generated from tailored/app-template
	add permission check
This commit is contained in:
		@@ -27,6 +27,8 @@
 | 
				
			|||||||
  "packageManager": "pnpm@10.7.1",
 | 
					  "packageManager": "pnpm@10.7.1",
 | 
				
			||||||
  "type": "module",
 | 
					  "type": "module",
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "@kevisual/cache": "^0.0.2",
 | 
				
			||||||
 | 
					    "@kevisual/permission": "^0.0.1",
 | 
				
			||||||
    "@kevisual/router": "0.0.10"
 | 
					    "@kevisual/router": "0.0.10"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										31
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										31
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@@ -8,6 +8,12 @@ importers:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  .:
 | 
					  .:
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@kevisual/cache':
 | 
				
			||||||
 | 
					        specifier: ^0.0.2
 | 
				
			||||||
 | 
					        version: 0.0.2(rollup@4.39.0)(tslib@2.8.1)(typescript@5.8.2)
 | 
				
			||||||
 | 
					      '@kevisual/permission':
 | 
				
			||||||
 | 
					        specifier: ^0.0.1
 | 
				
			||||||
 | 
					        version: 0.0.1
 | 
				
			||||||
      '@kevisual/router':
 | 
					      '@kevisual/router':
 | 
				
			||||||
        specifier: 0.0.10
 | 
					        specifier: 0.0.10
 | 
				
			||||||
        version: 0.0.10
 | 
					        version: 0.0.10
 | 
				
			||||||
@@ -398,6 +404,9 @@ packages:
 | 
				
			|||||||
  '@kevisual/auth@1.0.5':
 | 
					  '@kevisual/auth@1.0.5':
 | 
				
			||||||
    resolution: {integrity: sha512-GwsLj7unKXi7lmMiIIgdig4LwwLiDJnOy15HHZR5gMbyK6s5/uJiMY5RXPB2+onGzTNDqFo/hXjsD2wkerHPVg==}
 | 
					    resolution: {integrity: sha512-GwsLj7unKXi7lmMiIIgdig4LwwLiDJnOy15HHZR5gMbyK6s5/uJiMY5RXPB2+onGzTNDqFo/hXjsD2wkerHPVg==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@kevisual/cache@0.0.2':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-2Cl5KF2Gi27uLfhO6CdTMFnRzx9vYnqevAo7d9ab3rOaqTgF8tLeAXglXyRbaWW3WUbHU2XaOb4r98uUsqIQQw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@kevisual/code-center-module@0.0.18':
 | 
					  '@kevisual/code-center-module@0.0.18':
 | 
				
			||||||
    resolution: {integrity: sha512-BfANmxLEO1AwVmqpa6VDgxk//YN8asf1r5jIPpyKDQm12kyyrYgHND9AgGCDRH8lvq6rYVe0svCZXD5b06UPWQ==}
 | 
					    resolution: {integrity: sha512-BfANmxLEO1AwVmqpa6VDgxk//YN8asf1r5jIPpyKDQm12kyyrYgHND9AgGCDRH8lvq6rYVe0svCZXD5b06UPWQ==}
 | 
				
			||||||
    peerDependencies:
 | 
					    peerDependencies:
 | 
				
			||||||
@@ -414,6 +423,9 @@ packages:
 | 
				
			|||||||
  '@kevisual/mark@0.0.7':
 | 
					  '@kevisual/mark@0.0.7':
 | 
				
			||||||
    resolution: {integrity: sha512-PiEEy4yvWEpixw76PzgrIWeNelzm+FrhtzFmqJU92o5GkgawaFwighcvIxqcVZRKeEFF4uvlTjFrGeQvXw6F4A==}
 | 
					    resolution: {integrity: sha512-PiEEy4yvWEpixw76PzgrIWeNelzm+FrhtzFmqJU92o5GkgawaFwighcvIxqcVZRKeEFF4uvlTjFrGeQvXw6F4A==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@kevisual/permission@0.0.1':
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-nSX2LzbPkU3YAMegbUFGU8tfmtFb7dcF5edqzm+gI6crcyCL1JzIB9HAYNEeEVIljLxuREwM/vVg9aFmF4cz9Q==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@kevisual/query@0.0.13':
 | 
					  '@kevisual/query@0.0.13':
 | 
				
			||||||
    resolution: {integrity: sha512-gSEIDiCvwSaLLAFZv4vam4wSrMsaCuQ3VGjE3kwRwZ8urlVH1TOA+NUO908A22p9m1Iij7Y1Q/JlfSJi2QzuKQ==}
 | 
					    resolution: {integrity: sha512-gSEIDiCvwSaLLAFZv4vam4wSrMsaCuQ3VGjE3kwRwZ8urlVH1TOA+NUO908A22p9m1Iij7Y1Q/JlfSJi2QzuKQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1747,6 +1759,9 @@ packages:
 | 
				
			|||||||
    resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
 | 
					    resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
 | 
				
			||||||
    engines: {node: '>=0.10.0'}
 | 
					    engines: {node: '>=0.10.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  idb-keyval@6.2.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ignore-by-default@1.0.1:
 | 
					  ignore-by-default@1.0.1:
 | 
				
			||||||
    resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
 | 
					    resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -3347,6 +3362,18 @@ snapshots:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  '@kevisual/auth@1.0.5': {}
 | 
					  '@kevisual/auth@1.0.5': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@kevisual/cache@0.0.2(rollup@4.39.0)(tslib@2.8.1)(typescript@5.8.2)':
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@rollup/plugin-commonjs': 28.0.3(rollup@4.39.0)
 | 
				
			||||||
 | 
					      '@rollup/plugin-node-resolve': 16.0.1(rollup@4.39.0)
 | 
				
			||||||
 | 
					      '@rollup/plugin-typescript': 12.1.2(rollup@4.39.0)(tslib@2.8.1)(typescript@5.8.2)
 | 
				
			||||||
 | 
					      idb-keyval: 6.2.1
 | 
				
			||||||
 | 
					      rollup-plugin-dts: 6.2.1(rollup@4.39.0)(typescript@5.8.2)
 | 
				
			||||||
 | 
					    transitivePeerDependencies:
 | 
				
			||||||
 | 
					      - rollup
 | 
				
			||||||
 | 
					      - tslib
 | 
				
			||||||
 | 
					      - typescript
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@kevisual/code-center-module@0.0.18(@kevisual/auth@1.0.5)(@kevisual/router@0.0.10)(@kevisual/use-config@1.0.10(dotenv@16.4.7))(ioredis@5.6.0)(pg@8.14.1)(sequelize@6.37.7(pg-hstore@2.3.4)(pg@8.14.1))':
 | 
					  '@kevisual/code-center-module@0.0.18(@kevisual/auth@1.0.5)(@kevisual/router@0.0.10)(@kevisual/use-config@1.0.10(dotenv@16.4.7))(ioredis@5.6.0)(pg@8.14.1)(sequelize@6.37.7(pg-hstore@2.3.4)(pg@8.14.1))':
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      '@kevisual/auth': 1.0.5
 | 
					      '@kevisual/auth': 1.0.5
 | 
				
			||||||
@@ -3393,6 +3420,8 @@ snapshots:
 | 
				
			|||||||
      - tedious
 | 
					      - tedious
 | 
				
			||||||
      - utf-8-validate
 | 
					      - utf-8-validate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  '@kevisual/permission@0.0.1': {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  '@kevisual/query@0.0.13(ws@8.18.1)(zod@3.24.2)':
 | 
					  '@kevisual/query@0.0.13(ws@8.18.1)(zod@3.24.2)':
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      openai: 4.91.1(ws@8.18.1)(zod@3.24.2)
 | 
					      openai: 4.91.1(ws@8.18.1)(zod@3.24.2)
 | 
				
			||||||
@@ -4882,6 +4911,8 @@ snapshots:
 | 
				
			|||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
      safer-buffer: 2.1.2
 | 
					      safer-buffer: 2.1.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  idb-keyval@6.2.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ignore-by-default@1.0.1: {}
 | 
					  ignore-by-default@1.0.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ignore@5.3.2: {}
 | 
					  ignore@5.3.2: {}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					import { Permission } from '@kevisual/permission';
 | 
				
			||||||
import CryptoJS from 'crypto-js';
 | 
					import CryptoJS from 'crypto-js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 加密函数
 | 
					// 加密函数
 | 
				
			||||||
@@ -73,6 +74,7 @@ export type AIConfig = {
 | 
				
			|||||||
  description?: string;
 | 
					  description?: string;
 | 
				
			||||||
  models: AIModel[];
 | 
					  models: AIModel[];
 | 
				
			||||||
  secretKeys: SecretKey[];
 | 
					  secretKeys: SecretKey[];
 | 
				
			||||||
 | 
					  permission?: Permission;
 | 
				
			||||||
  filter?: {
 | 
					  filter?: {
 | 
				
			||||||
    objectKey: string;
 | 
					    objectKey: string;
 | 
				
			||||||
    type: 'array' | 'object';
 | 
					    type: 'array' | 'object';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ import { app } from '@/app.ts';
 | 
				
			|||||||
import { ChatServices } from './services/chat-services.ts';
 | 
					import { ChatServices } from './services/chat-services.ts';
 | 
				
			||||||
import { ChatConfigServices } from './services/chat-config-srevices.ts';
 | 
					import { ChatConfigServices } from './services/chat-config-srevices.ts';
 | 
				
			||||||
import { AiChatHistoryModel } from './models/ai-chat-history.ts';
 | 
					import { AiChatHistoryModel } from './models/ai-chat-history.ts';
 | 
				
			||||||
 | 
					import { UserPermission } from '@kevisual/permission';
 | 
				
			||||||
app
 | 
					app
 | 
				
			||||||
  .route({
 | 
					  .route({
 | 
				
			||||||
    path: 'ai',
 | 
					    path: 'ai',
 | 
				
			||||||
@@ -21,19 +21,20 @@ app
 | 
				
			|||||||
      if (!aiChatHistory) {
 | 
					      if (!aiChatHistory) {
 | 
				
			||||||
        ctx.throw(400, 'aiChatHistory not found');
 | 
					        ctx.throw(400, 'aiChatHistory not found');
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (aiChatHistory.uid !== tokenUser.uid) {
 | 
					      if (aiChatHistory.uid !== tokenUser.id) {
 | 
				
			||||||
        ctx.throw(403, 'not permission');
 | 
					        ctx.throw(403, 'not permission');
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      username = username || aiChatHistory.username;
 | 
					      username = username || aiChatHistory.username || tokenUsername;
 | 
				
			||||||
      model = model || aiChatHistory.model;
 | 
					      model = model || aiChatHistory.model;
 | 
				
			||||||
      group = group || aiChatHistory.group;
 | 
					      group = group || aiChatHistory.group;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      username = username || tokenUsername;
 | 
					      username = username || tokenUsername;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    const isSelf = username === tokenUsername;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!Array.isArray(messages)) {
 | 
					    if (!Array.isArray(messages)) {
 | 
				
			||||||
      ctx.throw(400, 'chat messages is not array');
 | 
					      ctx.throw(400, 'chat messages is not array');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 初始化服务
 | 
					    // 初始化服务
 | 
				
			||||||
    const chatServices = await ChatServices.createServices({
 | 
					    const chatServices = await ChatServices.createServices({
 | 
				
			||||||
      owner: username,
 | 
					      owner: username,
 | 
				
			||||||
@@ -41,6 +42,15 @@ app
 | 
				
			|||||||
      group,
 | 
					      group,
 | 
				
			||||||
      username: tokenUsername,
 | 
					      username: tokenUsername,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    if (!isSelf && username !== 'root') {
 | 
				
			||||||
 | 
					      const aiConfig = chatServices.aiConfig;
 | 
				
			||||||
 | 
					      const permission = new UserPermission({ permission: aiConfig.permission, owner: username });
 | 
				
			||||||
 | 
					      const checkPermission = permission.checkPermissionSuccess({ username: tokenUsername, password: options.password });
 | 
				
			||||||
 | 
					      if (!checkPermission.success) {
 | 
				
			||||||
 | 
					        ctx.throw(403, checkPermission.message);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const chatConfigServices = new ChatConfigServices(username, tokenUsername);
 | 
					    const chatConfigServices = new ChatConfigServices(username, tokenUsername);
 | 
				
			||||||
    await chatConfigServices.checkUserCanChat(tokenUsername);
 | 
					    await chatConfigServices.checkUserCanChat(tokenUsername);
 | 
				
			||||||
    await chatServices.checkCanChat();
 | 
					    await chatServices.checkCanChat();
 | 
				
			||||||
@@ -72,6 +82,10 @@ app
 | 
				
			|||||||
      prompt_tokens: aiChatHistory.prompt_tokens + usage.prompt_tokens,
 | 
					      prompt_tokens: aiChatHistory.prompt_tokens + usage.prompt_tokens,
 | 
				
			||||||
      completion_tokens: aiChatHistory.completion_tokens + usage.completion_tokens,
 | 
					      completion_tokens: aiChatHistory.completion_tokens + usage.completion_tokens,
 | 
				
			||||||
      total_tokens: aiChatHistory.total_tokens + usage.total_tokens,
 | 
					      total_tokens: aiChatHistory.total_tokens + usage.total_tokens,
 | 
				
			||||||
 | 
					      version: aiChatHistory.version + 1,
 | 
				
			||||||
 | 
					      model: model,
 | 
				
			||||||
 | 
					      group: group,
 | 
				
			||||||
 | 
					      username: username,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    if (hook) {
 | 
					    if (hook) {
 | 
				
			||||||
      needUpdateData.data = {
 | 
					      needUpdateData.data = {
 | 
				
			||||||
@@ -126,19 +140,46 @@ app
 | 
				
			|||||||
  .define(async (ctx) => {
 | 
					  .define(async (ctx) => {
 | 
				
			||||||
    const username = ctx.query.username || 'root';
 | 
					    const username = ctx.query.username || 'root';
 | 
				
			||||||
    const tokenUser = ctx.state.tokenUser;
 | 
					    const tokenUser = ctx.state.tokenUser;
 | 
				
			||||||
 | 
					    const usernames = ctx.query.data?.usernames || [];
 | 
				
			||||||
 | 
					    const tokenUsername = tokenUser.username;
 | 
				
			||||||
    const isSameUser = username === tokenUser.username;
 | 
					    const isSameUser = username === tokenUser.username;
 | 
				
			||||||
    const configObject: Record<string, any> = {};
 | 
					    const configArray: any[] = [];
 | 
				
			||||||
    const services = new ChatConfigServices(username, tokenUser.username);
 | 
					    const services = new ChatConfigServices(username, tokenUser.username);
 | 
				
			||||||
    const res = await services.getChatConfig(true, ctx.query.token);
 | 
					    const res = await services.getChatConfig(true, ctx.query.token);
 | 
				
			||||||
    configObject[username] = res;
 | 
					    configArray.push({
 | 
				
			||||||
 | 
					      username,
 | 
				
			||||||
 | 
					      config: res,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
    if (!isSameUser) {
 | 
					    if (!isSameUser) {
 | 
				
			||||||
      const selfServices = new ChatConfigServices(tokenUser.username, tokenUser.username);
 | 
					      const selfServices = new ChatConfigServices(tokenUser.username, tokenUser.username);
 | 
				
			||||||
      const selfRes = await selfServices.getChatConfig(true, ctx.query.token);
 | 
					      const selfRes = await selfServices.getChatConfig(true, ctx.query.token);
 | 
				
			||||||
      configObject['self'] = selfRes;
 | 
					      configArray.push({
 | 
				
			||||||
    } else {
 | 
					        username: tokenUser.username,
 | 
				
			||||||
      configObject['self'] = res;
 | 
					        self: true,
 | 
				
			||||||
 | 
					        config: selfRes,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    ctx.body = configObject;
 | 
					    for (const username of usernames) {
 | 
				
			||||||
 | 
					      const services = new ChatConfigServices(username, tokenUser.username);
 | 
				
			||||||
 | 
					      const res = await services.getChatConfig(true, ctx.query.token);
 | 
				
			||||||
 | 
					      const aiConfig = services.aiConfig;
 | 
				
			||||||
 | 
					      const permission = new UserPermission({ permission: aiConfig.permission, owner: username });
 | 
				
			||||||
 | 
					      const checkPermission = permission.checkPermissionSuccess({ username: tokenUsername, password: '-----------------' });
 | 
				
			||||||
 | 
					      if (!checkPermission.success) {
 | 
				
			||||||
 | 
					        // ctx.throw(403, `[${username}] ${checkPermission.message}`);
 | 
				
			||||||
 | 
					        configArray.push({
 | 
				
			||||||
 | 
					          username,
 | 
				
			||||||
 | 
					          config: null,
 | 
				
			||||||
 | 
					          error: checkPermission.message,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        configArray.push({
 | 
				
			||||||
 | 
					          username,
 | 
				
			||||||
 | 
					          config: res,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ctx.body = configArray;
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
  .addTo(app);
 | 
					  .addTo(app);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -146,7 +187,7 @@ app
 | 
				
			|||||||
  .route({
 | 
					  .route({
 | 
				
			||||||
    path: 'ai',
 | 
					    path: 'ai',
 | 
				
			||||||
    key: 'get-chat-usage',
 | 
					    key: 'get-chat-usage',
 | 
				
			||||||
    description: '获取chat使用情况',
 | 
					    description: '获取chat使用情况, 只获取root的使用情况',
 | 
				
			||||||
    middleware: ['auth'],
 | 
					    middleware: ['auth'],
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
  .define(async (ctx) => {
 | 
					  .define(async (ctx) => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,12 +12,121 @@ app
 | 
				
			|||||||
    const tokenUser = ctx.state.tokenUser;
 | 
					    const tokenUser = ctx.state.tokenUser;
 | 
				
			||||||
    const aiChatList = await AiChatHistoryModel.findAll({
 | 
					    const aiChatList = await AiChatHistoryModel.findAll({
 | 
				
			||||||
      where: {
 | 
					      where: {
 | 
				
			||||||
        uid: tokenUser.uid,
 | 
					        uid: tokenUser.id,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      order: [['createdAt', 'DESC']],
 | 
					      order: [['updatedAt', 'DESC']],
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    ctx.body = {
 | 
					    ctx.body = {
 | 
				
			||||||
      list: aiChatList,
 | 
					      list: aiChatList,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
  .addTo(app);
 | 
					  .addTo(app);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app
 | 
				
			||||||
 | 
					  .route({
 | 
				
			||||||
 | 
					    path: 'ai',
 | 
				
			||||||
 | 
					    key: 'update-chat',
 | 
				
			||||||
 | 
					    description: '更新chat',
 | 
				
			||||||
 | 
					    middleware: ['auth'],
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  .define(async (ctx) => {
 | 
				
			||||||
 | 
					    const tokenUser = ctx.state.tokenUser;
 | 
				
			||||||
 | 
					    const uid = tokenUser.id;
 | 
				
			||||||
 | 
					    const { id, data, prompt_tokens, total_tokens, completion_tokens, createdAt, updatedAt, ...rest } = ctx.query.data || {};
 | 
				
			||||||
 | 
					    let aiChat: AiChatHistoryModel | null = null;
 | 
				
			||||||
 | 
					    if (id) {
 | 
				
			||||||
 | 
					      aiChat = await AiChatHistoryModel.findByPk(id);
 | 
				
			||||||
 | 
					      if (!aiChat) {
 | 
				
			||||||
 | 
					        ctx.throw(404, 'chat not found');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (aiChat.uid !== uid) {
 | 
				
			||||||
 | 
					        ctx.throw(403, 'no permission');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      await aiChat.update({ data: { ...aiChat.data, ...data }, ...rest, version: aiChat.version + 1 });
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      aiChat = await AiChatHistoryModel.create({
 | 
				
			||||||
 | 
					        ...rest,
 | 
				
			||||||
 | 
					        uid: uid,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ctx.body = aiChat;
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  .addTo(app);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app
 | 
				
			||||||
 | 
					  .route({
 | 
				
			||||||
 | 
					    path: 'ai',
 | 
				
			||||||
 | 
					    key: 'get-chat',
 | 
				
			||||||
 | 
					    description: '获取chat',
 | 
				
			||||||
 | 
					    middleware: ['auth'],
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  .define(async (ctx) => {
 | 
				
			||||||
 | 
					    const tokenUser = ctx.state.tokenUser;
 | 
				
			||||||
 | 
					    const { id } = ctx.query.data || {};
 | 
				
			||||||
 | 
					    if (!id) {
 | 
				
			||||||
 | 
					      ctx.throw(400, 'id is required');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const aiChat = await AiChatHistoryModel.findByPk(id);
 | 
				
			||||||
 | 
					    if (!aiChat) {
 | 
				
			||||||
 | 
					      ctx.throw(404, 'chat not found');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (aiChat.uid !== tokenUser.id) {
 | 
				
			||||||
 | 
					      ctx.throw(403, 'no permission');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ctx.body = aiChat;
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  .addTo(app);
 | 
				
			||||||
 | 
					app
 | 
				
			||||||
 | 
					  .route({
 | 
				
			||||||
 | 
					    path: 'ai',
 | 
				
			||||||
 | 
					    key: 'get-chat-version',
 | 
				
			||||||
 | 
					    description: '获取chat版本',
 | 
				
			||||||
 | 
					    middleware: ['auth'],
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  .define(async (ctx) => {
 | 
				
			||||||
 | 
					    const tokenUser = ctx.state.tokenUser;
 | 
				
			||||||
 | 
					    const { id } = ctx.query.data || {};
 | 
				
			||||||
 | 
					    if (!id) {
 | 
				
			||||||
 | 
					      ctx.throw(400, 'id is required');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const aiChat = await AiChatHistoryModel.findByPk(id);
 | 
				
			||||||
 | 
					    if (!aiChat) {
 | 
				
			||||||
 | 
					      ctx.throw(404, 'chat not found');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (aiChat.uid !== tokenUser.id) {
 | 
				
			||||||
 | 
					      ctx.throw(403, 'no permission');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ctx.body = {
 | 
				
			||||||
 | 
					      id: aiChat.id,
 | 
				
			||||||
 | 
					      version: aiChat.version,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  .addTo(app);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app
 | 
				
			||||||
 | 
					  .route({
 | 
				
			||||||
 | 
					    path: 'ai',
 | 
				
			||||||
 | 
					    key: 'delete-chat',
 | 
				
			||||||
 | 
					    description: '删除chat',
 | 
				
			||||||
 | 
					    middleware: ['auth'],
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  .define(async (ctx) => {
 | 
				
			||||||
 | 
					    const tokenUser = ctx.state.tokenUser;
 | 
				
			||||||
 | 
					    const { id } = ctx.query.data || {};
 | 
				
			||||||
 | 
					    if (!id) {
 | 
				
			||||||
 | 
					      ctx.throw(400, 'id is required');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const aiChat = await AiChatHistoryModel.findByPk(id);
 | 
				
			||||||
 | 
					    if (!aiChat) {
 | 
				
			||||||
 | 
					      ctx.throw(404, 'chat not found');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (aiChat.uid !== tokenUser.id) {
 | 
				
			||||||
 | 
					      ctx.throw(403, 'no permission');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    await aiChat.destroy();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ctx.body = {
 | 
				
			||||||
 | 
					      success: true,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  .addTo(app);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,8 @@ export class AiChatHistoryModel extends Model {
 | 
				
			|||||||
  declare total_tokens: number;
 | 
					  declare total_tokens: number;
 | 
				
			||||||
  declare completion_tokens: number;
 | 
					  declare completion_tokens: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  declare version: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  declare createdAt: Date;
 | 
					  declare createdAt: Date;
 | 
				
			||||||
  declare updatedAt: Date;
 | 
					  declare updatedAt: Date;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -47,14 +49,17 @@ AiChatHistoryModel.init(
 | 
				
			|||||||
    username: {
 | 
					    username: {
 | 
				
			||||||
      type: DataTypes.STRING,
 | 
					      type: DataTypes.STRING,
 | 
				
			||||||
      allowNull: false,
 | 
					      allowNull: false,
 | 
				
			||||||
 | 
					      defaultValue: '',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    model: {
 | 
					    model: {
 | 
				
			||||||
      type: DataTypes.STRING,
 | 
					      type: DataTypes.STRING,
 | 
				
			||||||
      allowNull: false,
 | 
					      allowNull: false,
 | 
				
			||||||
 | 
					      defaultValue: '',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    group: {
 | 
					    group: {
 | 
				
			||||||
      type: DataTypes.STRING,
 | 
					      type: DataTypes.STRING,
 | 
				
			||||||
      allowNull: false,
 | 
					      allowNull: false,
 | 
				
			||||||
 | 
					      defaultValue: '',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    title: {
 | 
					    title: {
 | 
				
			||||||
      type: DataTypes.STRING,
 | 
					      type: DataTypes.STRING,
 | 
				
			||||||
@@ -82,6 +87,10 @@ AiChatHistoryModel.init(
 | 
				
			|||||||
      type: DataTypes.JSONB,
 | 
					      type: DataTypes.JSONB,
 | 
				
			||||||
      defaultValue: {},
 | 
					      defaultValue: {},
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    version: {
 | 
				
			||||||
 | 
					      type: DataTypes.INTEGER,
 | 
				
			||||||
 | 
					      defaultValue: 0,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    uid: {
 | 
					    uid: {
 | 
				
			||||||
      type: DataTypes.UUID,
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
      allowNull: true,
 | 
					      allowNull: true,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,8 @@ export class ChatConfigServices {
 | 
				
			|||||||
  owner: string;
 | 
					  owner: string;
 | 
				
			||||||
  // 使用者
 | 
					  // 使用者
 | 
				
			||||||
  username: string;
 | 
					  username: string;
 | 
				
			||||||
 | 
					  aiConfig?: AIConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * username 是使用的模型的用户名,使用谁的模型
 | 
					   * username 是使用的模型的用户名,使用谁的模型
 | 
				
			||||||
   * @param username
 | 
					   * @param username
 | 
				
			||||||
@@ -50,6 +52,7 @@ export class ChatConfigServices {
 | 
				
			|||||||
      const cacheTime = 60 * 60 * 24 * 40; // 1天
 | 
					      const cacheTime = 60 * 60 * 24 * 40; // 1天
 | 
				
			||||||
      await redis.set(key, JSON.stringify(modelConfig), 'EX', cacheTime);
 | 
					      await redis.set(key, JSON.stringify(modelConfig), 'EX', cacheTime);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    this.aiConfig = modelConfig;
 | 
				
			||||||
    if (needClearSecret) {
 | 
					    if (needClearSecret) {
 | 
				
			||||||
      modelConfig = this.filterApiKey(modelConfig);
 | 
					      modelConfig = this.filterApiKey(modelConfig);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import { AIConfigParser, ProviderResult } from '@/provider/utils/parse-config.ts';
 | 
					import { AIConfig, AIConfigParser, ProviderResult } from '@/provider/utils/parse-config.ts';
 | 
				
			||||||
import { ProviderManager, ChatMessage, BaseChat, ChatMessageOptions } from '@/provider/index.ts';
 | 
					import { ProviderManager, ChatMessage, BaseChat, ChatMessageOptions } from '@/provider/index.ts';
 | 
				
			||||||
import { redis } from '@/modules/db.ts';
 | 
					import { redis } from '@/modules/db.ts';
 | 
				
			||||||
import { CustomError } from '@kevisual/router';
 | 
					import { CustomError } from '@kevisual/router';
 | 
				
			||||||
@@ -36,6 +36,7 @@ export class ChatServices {
 | 
				
			|||||||
   * 模型配置
 | 
					   * 模型配置
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  modelConfig?: ProviderResult;
 | 
					  modelConfig?: ProviderResult;
 | 
				
			||||||
 | 
					  aiConfig?: AIConfig;
 | 
				
			||||||
  chatProvider?: BaseChat;
 | 
					  chatProvider?: BaseChat;
 | 
				
			||||||
  constructor(opts: ChatServicesConfig) {
 | 
					  constructor(opts: ChatServicesConfig) {
 | 
				
			||||||
    this.owner = opts.owner;
 | 
					    this.owner = opts.owner;
 | 
				
			||||||
@@ -65,6 +66,7 @@ export class ChatServices {
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    that.modelConfig = { ...providerResult, apiKey };
 | 
					    that.modelConfig = { ...providerResult, apiKey };
 | 
				
			||||||
 | 
					    that.aiConfig = config;
 | 
				
			||||||
    return that.modelConfig;
 | 
					    return that.modelConfig;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -108,7 +110,7 @@ export class ChatServices {
 | 
				
			|||||||
  async createNewMessage(messages: ChastHistoryMessage[]) {
 | 
					  async createNewMessage(messages: ChastHistoryMessage[]) {
 | 
				
			||||||
    return messages.map((item) => {
 | 
					    return messages.map((item) => {
 | 
				
			||||||
      if (!item.id) {
 | 
					      if (!item.id) {
 | 
				
			||||||
        item.id = 'chat' + nanoid();
 | 
					        item.id = 'chat-' + nanoid();
 | 
				
			||||||
        item.createdAt = Date.now();
 | 
					        item.createdAt = Date.now();
 | 
				
			||||||
        item.updatedAt = Date.now();
 | 
					        item.updatedAt = Date.now();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user