diff --git a/package.json b/package.json index d5c5bc6..95c6161 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "nanoid": "^5.0.7", "neo4j-driver": "^5.25.0", "neode": "^0.4.9", + "node-fetch": "^3.3.2", "ollama": "^0.5.9", "pg": "^8.13.0", "semver": "^7.6.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 83f6398..a580083 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,6 +70,9 @@ importers: neode: specifier: ^0.4.9 version: 0.4.9 + node-fetch: + specifier: ^3.3.2 + version: 3.3.2 ollama: specifier: ^0.5.9 version: 0.5.9 @@ -1670,6 +1673,10 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + data-view-buffer@1.0.1: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} @@ -1895,6 +1902,10 @@ packages: picomatch: optional: true + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1939,6 +1950,10 @@ packages: resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} engines: {node: '>= 12.20'} + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -2508,6 +2523,10 @@ packages: encoding: optional: true + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-releases@2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} @@ -3215,6 +3234,10 @@ packages: web-encoding@1.1.5: resolution: {integrity: sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==} + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + web-streams-polyfill@4.0.0-beta.3: resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} engines: {node: '>= 14'} @@ -4960,6 +4983,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + data-uri-to-buffer@4.0.1: {} + data-view-buffer@1.0.1: dependencies: call-bind: 1.0.7 @@ -5257,6 +5282,11 @@ snapshots: optionalDependencies: picomatch: 2.3.1 + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -5313,6 +5343,10 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 4.0.0-beta.3 + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 @@ -5860,6 +5894,12 @@ snapshots: dependencies: whatwg-url: 5.0.0 + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + node-releases@2.0.18: {} nodemon@3.1.7: @@ -6612,6 +6652,8 @@ snapshots: optionalDependencies: '@zxing/text-encoding': 0.9.0 + web-streams-polyfill@3.3.3: {} + web-streams-polyfill@4.0.0-beta.3: {} webidl-conversions@3.0.1: {} diff --git a/src/routes/agent/list.ts b/src/routes/agent/list.ts index d26f498..8507336 100644 --- a/src/routes/agent/list.ts +++ b/src/routes/agent/list.ts @@ -4,7 +4,11 @@ import { CustomError } from '@abearxiong/router'; import { agentManger } from '@kevisual/ai-lang'; import { v4 } from 'uuid'; app - .route('agent', 'list') + .route({ + path: 'agent', + key: 'list', + middleware: ['auth'], + }) .define(async (ctx) => { const agents = await AiAgent.findAll({ order: [['updatedAt', 'DESC']], diff --git a/src/routes/chat-history/list.ts b/src/routes/chat-history/list.ts index 4ad45a7..9f9e165 100644 --- a/src/routes/chat-history/list.ts +++ b/src/routes/chat-history/list.ts @@ -7,6 +7,7 @@ app .route({ path: 'chat-history', key: 'list', + middleware: ['auth'], }) .define(async (ctx) => { const chatPrompt = await ChatHistory.findAll({ diff --git a/src/routes/chat-history/session-list.ts b/src/routes/chat-history/session-list.ts index 666ceb7..105de84 100644 --- a/src/routes/chat-history/session-list.ts +++ b/src/routes/chat-history/session-list.ts @@ -6,6 +6,7 @@ app .route({ path: 'chat-session', key: 'list', + middleware: ['auth'], }) .define(async (ctx) => { const chatSession = await ChatSession.findAll({ diff --git a/src/routes/container/list.ts b/src/routes/container/list.ts index f78ef84..5ef89a2 100644 --- a/src/routes/container/list.ts +++ b/src/routes/container/list.ts @@ -40,8 +40,10 @@ app const add = app.route({ path: 'container', key: 'update', + middleware: ['auth'], }); add.run = async (ctx) => { + const tokenUser = ctx.state.tokenUser; const data = ctx.query.data; const _data: Container = { @@ -72,6 +74,7 @@ add.run = async (ctx) => { ...container, source: '', sourceType: '', + uid: tokenUser.id, }); } catch (e) { console.log('error', e); diff --git a/src/routes/github/index.ts b/src/routes/github/index.ts new file mode 100644 index 0000000..83ec5cd --- /dev/null +++ b/src/routes/github/index.ts @@ -0,0 +1 @@ +import './list.ts'; diff --git a/src/routes/github/lib/get-token.ts b/src/routes/github/lib/get-token.ts new file mode 100644 index 0000000..e006a22 --- /dev/null +++ b/src/routes/github/lib/get-token.ts @@ -0,0 +1,51 @@ +import fetch from 'node-fetch'; +import { useConfig } from '@abearxiong/use-config'; + +type GithubConfig = { + clientId: string; + clientSecret: string; + redirect_uri: string; +}; +const { github } = useConfig<{ github: GithubConfig }>(); +// 获取 GitHub access_token 的函数 +async function getGithubToken(github: GithubConfig, code) { + const { clientId, clientSecret, redirect_uri } = github; + // 设置请求 URL 和参数 + const tokenUrl = 'https://github.com/login/oauth/access_token'; + const params = { + client_id: clientId, + client_secret: clientSecret, + code: code, + redirect_uri: redirect_uri, + }; + + try { + // 发送 POST 请求获取 access_token + const response = await fetch(tokenUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + body: JSON.stringify(params), + }); + + // 解析响应 JSON + const data: any = await response.json(); + + if (data.access_token) { + console.log('Access Token:', data.access_token); + return data.access_token; + } else { + console.error('Error:', data.error || 'Failed to get access token'); + return null; + } + } catch (error) { + console.error('Error fetching access token:', error); + return null; + } +} + +export const getAccessToken = async (code?: string) => { + return getGithubToken(github, code); +}; diff --git a/src/routes/github/list.ts b/src/routes/github/list.ts new file mode 100644 index 0000000..6dab482 --- /dev/null +++ b/src/routes/github/list.ts @@ -0,0 +1,48 @@ +import { app } from '@/app.ts'; +import { CustomError } from '@abearxiong/router'; +import { getAccessToken } from './lib/get-token.ts'; +import { GithubModel } from './models/github.ts'; + +app + .route({ + path: 'github', + key: 'token', + middleware: ['auth'], + }) + .define(async (ctx) => { + const tokenUser = ctx.state.tokenUser; + const github = await GithubModel.findOne({ + where: { + uid: tokenUser.id, + }, + logging: false, + }); + if (github) { + ctx.body = { + githubToken: github.githubToken, + }; + return; + } + const { code } = ctx.query; + if (!code) { + throw new CustomError(400, 'code is required'); + } + try { + console.log('get access token from github, code:', code); + const token = await getAccessToken(code); + if (!token) { + throw new CustomError(500, 'Failed to get access token'); + } + await GithubModel.create({ + uid: tokenUser.id, + githubToken: token, + }); + ctx.body = { + githubToken: token, + }; + } catch (e) { + console.error('get access token from github error:', e); + throw new CustomError(500, 'Failed to get access token'); + } + }) + .addTo(app); diff --git a/src/routes/github/models/github.ts b/src/routes/github/models/github.ts new file mode 100644 index 0000000..550977b --- /dev/null +++ b/src/routes/github/models/github.ts @@ -0,0 +1,45 @@ +import { sequelize } from '../../../modules/sequelize.ts'; +import { DataTypes, Model } from 'sequelize'; + +export type Github = Partial>; + +/** + * 用户代码容器 + */ +export class GithubModel extends Model { + declare id: string; + declare title: string; + declare githubToken: string; + declare uid: string; +} +GithubModel.init( + { + id: { + type: DataTypes.UUID, + primaryKey: true, + defaultValue: DataTypes.UUIDV4, + comment: 'id', + }, + title: { + type: DataTypes.STRING, + defaultValue: '', + }, + githubToken: { + type: DataTypes.STRING, + defaultValue: '', + }, + uid: { + type: DataTypes.UUID, + allowNull: true, + }, + }, + { + sequelize, + tableName: 'kv_github', + paranoid: true, + }, +); + +GithubModel.sync({ alter: true, logging: false }).catch((e) => { + console.error('GithubModel sync', e); +}); diff --git a/src/routes/index.ts b/src/routes/index.ts index afc80cd..b68d478 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -13,3 +13,5 @@ import './user/index.ts'; import './chat-prompt/index.ts'; import './chat-history/index.ts'; + +import './github/index.ts'; diff --git a/src/routes/page/list.ts b/src/routes/page/list.ts index 6c76cf7..a81779e 100644 --- a/src/routes/page/list.ts +++ b/src/routes/page/list.ts @@ -47,9 +47,11 @@ app .route({ path: 'page', key: 'update', + middleware: ['auth'], }) .define(async (ctx) => { const { data, id, title, type, description } = ctx.query.data; + const tokenUser = ctx.state.tokenUser; if (id) { const page = await PageModel.findByPk(id); if (page) { @@ -59,7 +61,7 @@ app throw new CustomError('page not found'); } } else if (data) { - const page = await PageModel.create({ data, title, type, description }); + const page = await PageModel.create({ data, title, type, description, uid: tokenUser.id }); ctx.body = page; } })