feat: add kv-code dependency and integrate kv-code editor in settings

- Added @kevisual/kv-code dependency to package.json
- Integrated kv-code editor in the FirstLogin and Config components
- Updated layout to include ToastContainer for notifications
- Implemented saveConfig functionality in store with success notifications
- Created detailed configuration documentation in 10-config.md
- Added typings.d.ts for type definitions
This commit is contained in:
2025-12-19 13:10:45 +08:00
parent 91fdd6abc3
commit fb2b2d4d6f
9 changed files with 636 additions and 52 deletions

View File

@@ -24,6 +24,7 @@
"@astrojs/sitemap": "^3.6.0",
"@astrojs/vue": "^5.1.3",
"@kevisual/context": "^0.0.4",
"@kevisual/kv-code": "^0.0.4",
"@kevisual/query": "^0.0.32",
"@kevisual/query-login": "^0.0.7",
"@kevisual/registry": "^0.0.1",

View File

@@ -1,6 +1,11 @@
import { use, useEffect, useState } from "react";
import { Layout } from "./layout"
import { useStore } from "./store";
import '@kevisual/kv-code/kv-code.js'
const link = {
loginDocs: '../docs/01-login-first/',
settingDocs: '../../docs/10-config/',
}
export const FirstLogin = () => {
const store = useStore();
const [username, setUsername] = useState('')
@@ -23,7 +28,7 @@ export const FirstLogin = () => {
<h1 className='text-2xl font-bold text-black text-center'></h1>
<blockquote className="text-gray-500 p-4 mt-4">
<a className="text-gray-700 mx-1" href="https://kevisual.cn">kevisual</a> "全局设置"
<a className="text-gray-700 mx-1 underline" href="../docs/01-login-first/"></a>
<a className="text-gray-700 mx-1 underline" href={link.loginDocs}></a>
</blockquote>
<form className='space-y-4 mt-8'>
<div>
@@ -34,6 +39,7 @@ export const FirstLogin = () => {
onChange={e => setUsername(e.target.value)}
value={username}
placeholder='请输入账号'
autoComplete="username"
className='w-full px-4 py-2 border-2 border-black bg-white text-black placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-black focus:border-transparent'
/>
</div>
@@ -45,6 +51,7 @@ export const FirstLogin = () => {
onChange={e => setPassword(e.target.value)}
value={password}
placeholder='请输入密码'
autoComplete='current-password'
className='w-full px-4 py-2 border-2 border-black bg-white text-black placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-black focus:border-transparent'
/>
</div>
@@ -63,15 +70,33 @@ export const FirstLogin = () => {
export const Config = () => {
const store = useStore();
const [code, setCode] = useState('');
useEffect(() => {
store.initAdmin();
}, []);
const onSaveConfig = async () => {
console.log('onSaveConfig', code);
const parsedCode = JSON.parse(code);
await store.saveConfig(parsedCode);
}
return (
<Layout>
<div className="p-4">
<pre className="bg-gray-100 p-4 rounded-lg overflow-x-auto">
{JSON.stringify(store.config, null, 2)}
</pre>
<div className="p-4 flex flex-col h-full">
<div className="mb-4 flex justify-between items-center">
<h2 className="text-xl font-bold text-black" >
<a href={link.settingDocs} target="_blank" rel="noreferrer" className="underline">
</a>
</h2>
<button
onClick={onSaveConfig}
className="px-6 py-2 bg-black text-white font-medium hover:bg-gray-800 active:bg-gray-900 transition-colors focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2">
</button>
</div>
<kv-code-editor className="flex-1" value={JSON.stringify(store.config, null, 2)} onChange={(e) => {
setCode(e.nativeEvent?.detail?.value);
}} type="json"></kv-code-editor>
</div>
</Layout>
)

View File

@@ -1,4 +1,5 @@
import { Nav } from './nav'
import { toast, ToastContainer } from 'react-toastify'
export const Layout = (props) => {
return (
@@ -17,6 +18,7 @@ export const Layout = (props) => {
</div>
<main className="flex-1 p-6 bg-gray-50">{props.children}</main>
</div>
<ToastContainer />
</div>
)
}

View File

@@ -1,11 +1,12 @@
import { query, queryLogin } from '@/modules/query';
import { create } from 'zustand';
import { toast } from 'react-toastify';
type SettingState = {
username?: string;
initAdmin: () => any;
login: (username: string, password: string) => any;
config?: any;
saveConfig: any;
}
export const useStore = create<SettingState>((set => ({
username: undefined,
@@ -33,10 +34,22 @@ export const useStore = create<SettingState>((set => ({
if (res.code === 200) {
set({ username });
const setToken = await queryLogin.setLoginToken(res.data)
console.log('setToken', setToken);
toast.success('登录成功');
}
console.log('login res', res);
return res;
},
saveConfig: async (config: any) => {
const res = await query.post({
path: 'config',
key: 'set',
data: config
})
if (res.code === 200) {
set({ config })
toast.success('配置保存成功');
}
console.log('saveConfig res', res);
return res;
}
})));

View File

@@ -0,0 +1,302 @@
---
title: '配置项介绍'
description: 'Assistant 应用配置项完整说明文档包括应用信息、代理、服务器、认证、AI等各项配置详解'
tags: ['config', 'configuration', 'settings', 'assistant']
createdAt: '2025-12-18'
---
# 配置项介绍
本文档详细介绍 Assistant 应用的所有配置项。配置文件通常为 JSON 格式,用于定制应用的行为和功能。
## app - 应用信息
应用的基本标识信息。
```json
{
"app": {
"id": "my-assistant-001",
"url": "https://my-app.example.com"
}
}
```
- **id**: `string` - 应用唯一标识符,用于识别具体设备或应用实例
- **url**: `string` - 应用访问地址
## token - 访问令牌
```json
{
"token": "your-access-token"
}
```
- **token**: `string` - 用于身份验证的访问令牌
## registry - 注册中心
```json
{
"registry": "https://kevisual.cn"
}
```
- **registry**: `string` - 注册中心地址,默认为 `https://kevisual.cn`
## proxy - 前端代理配置
前端路由代理配置,用于将特定路径转发到目标服务器。
```json
{
"proxy": [
{
"path": "/root/home",
"target": "https://kevisual.cn",
"pathname": "/root/home"
}
]
}
```
- **proxy**: `ProxyInfo[]` - 代理配置数组
- **path**: `string` - 匹配的路径前缀
- **target**: `string` - 目标服务器地址
- **pathname**: `string` - 转发到目标服务器的路径
示例:访问 `/root/home` 会被转发到 `https://kevisual.cn/root/home`
## api - API代理配置
专门用于API请求的代理配置。
```json
{
"api": {
"proxy": [
{
"path": "/api",
"target": "https://api.example.com"
},
{
"path": "/v1",
"target": "https://api-v1.example.com"
}
]
}
}
```
- **api.proxy**: `ProxyInfo[]` - API代理配置数组配置方式同 `proxy`
## description - 应用描述
```json
{
"description": "我的助手应用"
}
```
- **description**: `string` - 应用的描述信息
## server - 服务器配置
配置本地服务器的监听地址和端口。
```json
{
"server": {
"path": "127.0.0.1",
"port": 3000
}
}
```
- **server.path**: `string` - 服务器监听地址,默认 `127.0.0.1`
- **server.port**: `number` - 服务器监听端口号
## share - 远程访问配置
配置应用是否可被远程调用。
```json
{
"share": {
"url": "https://kevisual.cn/ws/proxy",
"enabled": true
}
}
```
- **share.url**: `string` - 远程应用代理地址
- **share.enabled**: `boolean` - 是否启用远程访问功能
## watch - 文件监听配置
配置是否监听 pages 目录下的文件变化。
```json
{
"watch": {
"enabled": true
}
}
```
- **watch.enabled**: `boolean` - 是否启用文件监听
## home - 首页路径
```json
{
"home": "/root/home"
}
```
- **home**: `string` - 访问根路径 `/` 时自动重定向的首页地址
## ai - AI功能配置
启用和配置本地AI代理功能。
```json
{
"ai": {
"enabled": true,
"provider": "DeepSeek",
"apiKey": "your-api-key",
"model": "deepseek-chat"
}
}
```
- **ai.enabled**: `boolean` - 是否启用AI功能
- **ai.provider**: `string` - AI提供商可选 `'DeepSeek'` | `'Custom'` 或其他自定义值
- **ai.apiKey**: `string` - API密钥
- **ai.model**: `string` - 使用的模型名称
## scripts - 自定义脚本
定义自定义脚本命令,在应用启动时执行。
```json
{
"scripts": {
"start": "node server.js",
"build": "npm run build",
"custom": "echo 'Hello World'"
}
}
```
- **scripts**: `Record<string, string>` - 键值对形式的脚本配置
- key: 脚本名称
- value: 要执行的命令
## auth - 认证和权限配置
配置应用的认证和访问权限策略。
```json
{
"auth": {
"share": "protected"
}
}
```
- **auth**: `AuthPermission` - 认证权限配置对象
- **share**: 共享访问模式
- `"protected"` - 需要认证才能访问(默认)
- `"public"` - 公开访问,无需认证
- `"private"` - 私有访问,完全禁止外部访问
> **说明**: `share` 配置影响 pages 目录下页面的对外共享权限
## https - HTTPS证书配置
配置HTTPS服务和证书。
```json
{
"https": {
"type": "https",
"keyPath": "/path/to/private.key",
"certPath": "/path/to/certificate.crt"
}
}
```
- **https.type**: `'https' | 'http'` - 服务协议类型,默认 `'http'`
- **https.keyPath**: `string` - SSL证书私钥文件路径
- **https.certPath**: `string` - SSL证书文件路径
> **注意**: 通常不需要配置HTTPS可以通过反向代理如Nginx实现HTTPS访问
## 完整配置示例
```json
{
"app": {
"id": "assistant-prod-001",
"url": "https://app.example.com"
},
"token": "your-secure-token",
"registry": "https://kevisual.cn",
"proxy": [
{
"path": "/root/home",
"target": "https://kevisual.cn",
"pathname": "/root/home"
}
],
"api": {
"proxy": [
{
"path": "/api",
"target": "https://api.example.com"
}
]
},
"description": "生产环境助手应用",
"server": {
"path": "0.0.0.0",
"port": 3000
},
"share": {
"url": "https://kevisual.cn/ws/proxy",
"enabled": true
},
"watch": {
"enabled": true
},
"home": "/root/home",
"ai": {
"enabled": true,
"provider": "DeepSeek",
"apiKey": "sk-xxx",
"model": "deepseek-chat"
},
"scripts": {
"setup": "npm install",
"dev": "npm run dev"
},
"auth": {
"share": "protected"
}
}
```
## 配置文件位置
配置文件通常位于项目根目录,文件名为 `kevisual.json` 或其他约定名称。
## 最佳实践
1. **安全性**: 不要在配置文件中硬编码敏感信息(如 token、apiKey建议使用环境变量
2. **端口选择**: 确保选择的端口未被占用
3. **代理配置**: 合理配置代理路径,避免路径冲突
4. **HTTPS**: 生产环境建议使用反向代理配置HTTPS而非直接在应用中配置
5. **权限控制**: 根据实际需求选择合适的 `auth.share` 模式

View File

@@ -1,47 +1,9 @@
---
// import { query } from '@/modules/query.ts';
console.log('Hello from index.astro');
import '../styles/global.css';
import Html from '@/components/html.astro';
---
<html lang='en'>
<head>
<title>My Homepage</title>
</head>
<body>
<h1 onclick="{onClick}">Welcome to my website!</h1>
<div class='bg-amber-50 w-20 h-20 rounded-full'></div>
<div id='root'></div>
<script type='importmap' data-vite-ignore is:inline>
{
"imports": {
"react": "https://esm.sh/react@19.1.0",
"react-dom": "https://esm.sh/react-dom@19.1.0/client.js",
"react-toastify": "https://esm.sh/react-toastify@11.0.5"
}
}
</script>
<script type='module' data-vite-ignore is:inline>
import { Button, message } from 'https://esm.sh/antd?standalone';
import React from 'react';
import { ToastContainer, toast } from 'react-toastify';
import { createRoot } from 'react-dom';
setTimeout(() => {
toast.loading('Hello from index.astro');
window.toast = toast;
console.log('message', toast);
}, 1000);
console.log('Hello from index.astro', Button);
const root = document.getElementById('root');
const render = createRoot(root);
const App = () => {
const button = React.createElement(Button, null, 'Hello');
const messageEl = React.createElement(ToastContainer, null, 'Hello');
const wrapperMessage = React.createElement('div', null, [button, messageEl]);
return wrapperMessage;
};
// render.render(React.createElement(Button, null, 'Hello'), root);
render.render(App(), root);
</script>
</body>
</html>
<Html>
<main>
cli-center
</main>
</Html>

View File

@@ -13,6 +13,8 @@
},
"include": [
"src/**/*",
"typings.d.ts",
"@kevisual/kv-code/typings.d.ts",
"agent/**/*"
],
}

0
cli-center/typings.d.ts vendored Normal file
View File