diff --git a/package.json b/package.json index a6420f5..c6be52a 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "reload": "ssh light pm2 restart codecenter", "pub": "npm run build && npm run deploy && npm run reload", "start": "pm2 start dist/app.mjs --name codecenter", - "release": "node ./config/release/index.mjs" + "release": "node ./config/release/index.mjs", + "ssl": "ssl -L 6379:localhost:6379 light " }, "keywords": [], "types": "types/index.d.ts", @@ -45,6 +46,7 @@ "node-fetch": "^3.3.2", "p-queue": "^8.1.0", "pg": "^8.13.3", + "pm2": "^5.4.3", "rollup-plugin-esbuild": "^6.2.1", "semver": "^7.7.1", "sequelize": "^6.37.5", @@ -52,10 +54,10 @@ "strip-ansi": "^7.1.0", "tar": "^7.4.3", "uuid": "^11.1.0", - "pm2": "^5.4.3", "zod": "^3.24.2" }, "devDependencies": { + "@kevisual/code-center-module": "^0.0.9", "@kevisual/types": "^0.0.6", "@rollup/plugin-alias": "^5.1.1", "@rollup/plugin-commonjs": "^28.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fcde86c..3a91324 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -95,6 +95,9 @@ importers: specifier: ^3.24.2 version: 3.24.2 devDependencies: + '@kevisual/code-center-module': + specifier: ^0.0.9 + version: 0.0.9(@kevisual/auth@1.0.5)(@kevisual/router@0.0.7)(@kevisual/use-config@1.0.8)(ioredis@5.5.0)(pg@8.13.3)(sequelize@6.37.5(pg@8.13.3)) '@kevisual/types': specifier: ^0.0.6 version: 0.0.6 @@ -348,6 +351,16 @@ packages: '@kevisual/auth@1.0.5': resolution: {integrity: sha512-GwsLj7unKXi7lmMiIIgdig4LwwLiDJnOy15HHZR5gMbyK6s5/uJiMY5RXPB2+onGzTNDqFo/hXjsD2wkerHPVg==} + '@kevisual/code-center-module@0.0.9': + resolution: {integrity: sha512-ErHTYUlJGXmeBV5RJezKbBRmTYVBABV0SVvcMvbWVWPNc13Zo5RvM29vnzY6w3b6/cpnpWBi7Vx8WGFrcsMOkw==} + peerDependencies: + '@kevisual/auth': ^1.0.5 + '@kevisual/router': ^0.0.7 + '@kevisual/use-config': ^1.0.8 + ioredis: ^5.5.0 + pg: ^8.13.3 + sequelize: ^6.37.5 + '@kevisual/local-app-manager@0.1.9': resolution: {integrity: sha512-oppYUjvpPg/7gBw1h3dX63fhxB8MvGc74yGgIM4JifvSuOYLcZ9oZyauYV+5lQVh2tjJImOuUmRHnVXu1FWKDQ==} peerDependencies: @@ -2562,6 +2575,22 @@ snapshots: '@kevisual/auth@1.0.5': {} + '@kevisual/code-center-module@0.0.9(@kevisual/auth@1.0.5)(@kevisual/router@0.0.7)(@kevisual/use-config@1.0.8)(ioredis@5.5.0)(pg@8.13.3)(sequelize@6.37.5(pg@8.13.3))': + dependencies: + '@kevisual/auth': 1.0.5 + '@kevisual/router': 0.0.7 + '@kevisual/use-config': 1.0.8 + ioredis: 5.5.0 + nanoid: 5.1.2 + pg: 8.13.3 + sequelize: 6.37.5(pg@8.13.3) + socket.io: 4.8.1 + zod: 3.24.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + '@kevisual/local-app-manager@0.1.9(@kevisual/router@0.0.7)(@kevisual/types@0.0.6)(@kevisual/use-config@1.0.8)(pm2@5.4.3)': dependencies: '@kevisual/router': 0.0.7 diff --git a/src/models/org.ts b/src/models/org.ts index 8717f07..b16fb97 100644 --- a/src/models/org.ts +++ b/src/models/org.ts @@ -1,44 +1,48 @@ -import { DataTypes, Model, Sequelize } from 'sequelize'; -import { useContextKey } from '@kevisual/use-config/context'; -const sequelize = useContextKey('sequelize'); -export class Org extends Model { - declare id: string; - declare username: string; - declare description: string; - declare users: { role: string; uid: string }[]; -} +// import { DataTypes, Model, Sequelize } from 'sequelize'; +// import { useContextKey } from '@kevisual/use-config/context'; +// const sequelize = useContextKey('sequelize'); +// export class Org extends Model { +// declare id: string; +// declare username: string; +// declare description: string; +// declare users: { role: string; uid: string }[]; +// } -Org.init( - { - id: { - type: DataTypes.UUID, - primaryKey: true, - defaultValue: DataTypes.UUIDV4, - }, - username: { - type: DataTypes.STRING, - allowNull: false, - unique: true, - }, - description: { - type: DataTypes.STRING, - allowNull: true, - }, - users: { - type: DataTypes.JSONB, - allowNull: true, - defaultValue: [], - }, - }, - { - sequelize, - modelName: 'cf_org', - paranoid: true, - }, -); +// Org.init( +// { +// id: { +// type: DataTypes.UUID, +// primaryKey: true, +// defaultValue: DataTypes.UUIDV4, +// }, +// username: { +// type: DataTypes.STRING, +// allowNull: false, +// unique: true, +// }, +// description: { +// type: DataTypes.STRING, +// allowNull: true, +// }, +// users: { +// type: DataTypes.JSONB, +// allowNull: true, +// defaultValue: [], +// }, +// }, +// { +// sequelize, +// modelName: 'cf_org', +// paranoid: true, +// }, +// ); -Org.sync({ alter: true, logging: false }).catch((e) => { - console.error('Org sync', e); -}); +// Org.sync({ alter: true, logging: false }).catch((e) => { +// console.error('Org sync', e); +// }); -useContextKey('OrgModel', () => Org); +// useContextKey('OrgModel', () => Org); +import { Org, OrgInit } from '@kevisual/code-center-module/models'; +export { Org }; + +OrgInit(); diff --git a/src/models/user.ts b/src/models/user.ts index 68637cb..3dd4ae6 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -1,287 +1,293 @@ -import { useConfig } from '@kevisual/use-config'; -import { DataTypes, Model, Op, Sequelize } from 'sequelize'; -import { createToken, checkToken } from '@kevisual/auth'; -import { cryptPwd } from '@kevisual/auth'; -import { customRandom, nanoid, customAlphabet } from 'nanoid'; -import { CustomError } from '@kevisual/router'; -import { Org } from './org.ts'; +// import { useConfig } from '@kevisual/use-config'; +// import { DataTypes, Model, Op, Sequelize } from 'sequelize'; +// import { createToken, checkToken } from '@kevisual/auth'; +// import { cryptPwd } from '@kevisual/auth'; +// import { customRandom, nanoid, customAlphabet } from 'nanoid'; +// import { CustomError } from '@kevisual/router'; +// import { Org } from './org.ts'; -import { useContextKey } from '@kevisual/use-config/context'; -import { Redis } from 'ioredis'; -export const redis = useContextKey('redis'); -const sequelize = useContextKey('sequelize'); -const config = useConfig<{ tokenSecret: string }>(); +// import { useContextKey } from '@kevisual/use-config/context'; +// import { Redis } from 'ioredis'; +// export const redis = useContextKey('redis'); +// const sequelize = useContextKey('sequelize'); +// const config = useConfig<{ tokenSecret: string }>(); -type UserData = { - orgs?: string[]; -}; -export class User extends Model { - declare id: string; - declare username: string; - declare nickname: string; // 昵称 - declare alias: string; // 别名 - declare password: string; - declare salt: string; - declare needChangePassword: boolean; - declare description: string; - declare data: UserData; - declare type: string; // user | org | visitor - declare owner: string; - declare orgId: string; - declare email: string; - declare avatar: string; - tokenUser: any; - setTokenUser(tokenUser: any) { - this.tokenUser = tokenUser; - } - /** - * uid 是用于 orgId 的用户id 真实用户的id - * @param uid - * @returns - */ - async createToken(uid?: string, loginType?: 'default' | 'plugin' | 'month' | 'season' | 'year') { - const { id, username, type } = this; - let expireTime = 60 * 60 * 24 * 7; // 7 days - switch (loginType) { - case 'plugin': - expireTime = 60 * 60 * 24 * 30 * 12; // 365 days - break; - case 'month': - expireTime = 60 * 60 * 24 * 30; // 30 days - break; - case 'season': - expireTime = 60 * 60 * 24 * 30 * 3; // 90 days - break; - case 'year': - expireTime = 60 * 60 * 24 * 30 * 12; // 365 days - break; - } - const now = new Date().getTime(); - const token = await createToken({ id, username, uid, type }, config.tokenSecret); - return { token, expireTime: now + expireTime }; - } - static async verifyToken(token: string) { - const ct = await checkToken(token, config.tokenSecret); - const tokenUser = ct.payload; - return tokenUser; - } - static async createUser(username: string, password?: string, description?: string) { - const user = await User.findOne({ where: { username } }); - if (user) { - throw new CustomError('User already exists'); - } - const salt = nanoid(6); - let needChangePassword = !password; - password = password || '123456'; - const cPassword = cryptPwd(password, salt); - return await User.create({ username, password: cPassword, description, salt, needChangePassword }); - } - static async createOrg(username: string, owner: string, description?: string) { - const user = await User.findOne({ where: { username } }); - if (user) { - throw new CustomError('User already exists'); - } - const me = await User.findByPk(owner); - if (!me) { - throw new CustomError('Owner not found'); - } - if (me.type !== 'user') { - throw new CustomError('Owner type is not user'); - } - const org = await Org.create({ username, description, users: [{ uid: owner, role: 'owner' }] }); - const newUser = await User.create({ username, password: '', description, type: 'org', owner, orgId: org.id }); - // owner add - await redis.del(`user:${me.id}:orgs`); - return newUser; - } - createPassword(password: string) { - const salt = this.salt; - const cPassword = cryptPwd(password, salt); - this.password = cPassword; - return cPassword; - } - checkPassword(password: string) { - const salt = this.salt; - const cPassword = cryptPwd(password, salt); - return this.password === cPassword; - } - async getInfo() { - const orgs = await this.getOrgs(); - return { - id: this.id, - username: this.username, - nickname: this.nickname, - description: this.description, - needChangePassword: this.needChangePassword, - type: this.type, - avatar: this.avatar, - orgs, - }; - } - async getOrgs() { - let id = this.id; - if (this.type === 'org') { - if (this.tokenUser && this.tokenUser.uid) { - id = this.tokenUser.uid; - } else { - console.log('getOrgs', 'no uid', this.id, this.username); - throw new CustomError('Permission denied'); - } - } - const cache = await redis.get(`user:${id}:orgs`); - if (cache) { - return JSON.parse(cache) as string[]; - } - const orgs = await Org.findAll({ - order: [['updatedAt', 'DESC']], - where: { - users: { - [Op.contains]: [ - { - uid: id, - }, - ], - }, - }, - }); - const orgNames = orgs.map((org) => org.username); - if (orgNames.length > 0) { - await redis.set(`user:${id}:orgs`, JSON.stringify(orgNames), 'EX', 60 * 60); // 1 hour - } - return orgNames; - } - async expireOrgs() { - await redis.del(`user:${this.id}:orgs`); - } -} -User.init( - { - id: { - type: DataTypes.UUID, - primaryKey: true, - defaultValue: DataTypes.UUIDV4, - }, - username: { - type: DataTypes.STRING, - allowNull: false, - unique: true, - // 用户名或者手机号 - // 创建后避免修改的字段,当注册用户后,用户名注册则默认不能用手机号 - }, - nickname: { - type: DataTypes.TEXT, - allowNull: true, - }, - alias: { - type: DataTypes.TEXT, - allowNull: true, // 别名,网络请求的别名,需要唯一,不能和username重复 - }, - password: { - type: DataTypes.STRING, - allowNull: true, - }, - email: { - type: DataTypes.STRING, - allowNull: true, - }, - avatar: { - type: DataTypes.TEXT, - allowNull: true, - }, - salt: { - type: DataTypes.STRING, - allowNull: true, - }, - description: { - type: DataTypes.TEXT, - }, - type: { - type: DataTypes.STRING, - defaultValue: 'user', - }, - owner: { - type: DataTypes.UUID, - }, - orgId: { - type: DataTypes.UUID, - }, - needChangePassword: { - type: DataTypes.BOOLEAN, - defaultValue: false, - }, - data: { - type: DataTypes.JSON, - defaultValue: {}, - }, - }, - { - sequelize, - tableName: 'cf_user', // codeflow user - paranoid: true, - }, -); -User.sync({ alter: true, logging: false }) - .then((res) => { - initializeUser(); - }) - .catch((err) => { - console.error('Sync User error', err); - }); +// type UserData = { +// orgs?: string[]; +// }; +// export class User extends Model { +// declare id: string; +// declare username: string; +// declare nickname: string; // 昵称 +// declare alias: string; // 别名 +// declare password: string; +// declare salt: string; +// declare needChangePassword: boolean; +// declare description: string; +// declare data: UserData; +// declare type: string; // user | org | visitor +// declare owner: string; +// declare orgId: string; +// declare email: string; +// declare avatar: string; +// tokenUser: any; +// setTokenUser(tokenUser: any) { +// this.tokenUser = tokenUser; +// } +// /** +// * uid 是用于 orgId 的用户id 真实用户的id +// * @param uid +// * @returns +// */ +// async createToken(uid?: string, loginType?: 'default' | 'plugin' | 'month' | 'season' | 'year') { +// const { id, username, type } = this; +// let expireTime = 60 * 60 * 24 * 7; // 7 days +// switch (loginType) { +// case 'plugin': +// expireTime = 60 * 60 * 24 * 30 * 12; // 365 days +// break; +// case 'month': +// expireTime = 60 * 60 * 24 * 30; // 30 days +// break; +// case 'season': +// expireTime = 60 * 60 * 24 * 30 * 3; // 90 days +// break; +// case 'year': +// expireTime = 60 * 60 * 24 * 30 * 12; // 365 days +// break; +// } +// const now = new Date().getTime(); +// const token = await createToken({ id, username, uid, type }, config.tokenSecret); +// return { token, expireTime: now + expireTime }; +// } +// static async verifyToken(token: string) { +// const ct = await checkToken(token, config.tokenSecret); +// const tokenUser = ct.payload; +// return tokenUser; +// } +// static async createUser(username: string, password?: string, description?: string) { +// const user = await User.findOne({ where: { username } }); +// if (user) { +// throw new CustomError('User already exists'); +// } +// const salt = nanoid(6); +// let needChangePassword = !password; +// password = password || '123456'; +// const cPassword = cryptPwd(password, salt); +// return await User.create({ username, password: cPassword, description, salt, needChangePassword }); +// } +// static async createOrg(username: string, owner: string, description?: string) { +// const user = await User.findOne({ where: { username } }); +// if (user) { +// throw new CustomError('User already exists'); +// } +// const me = await User.findByPk(owner); +// if (!me) { +// throw new CustomError('Owner not found'); +// } +// if (me.type !== 'user') { +// throw new CustomError('Owner type is not user'); +// } +// const org = await Org.create({ username, description, users: [{ uid: owner, role: 'owner' }] }); +// const newUser = await User.create({ username, password: '', description, type: 'org', owner, orgId: org.id }); +// // owner add +// await redis.del(`user:${me.id}:orgs`); +// return newUser; +// } +// createPassword(password: string) { +// const salt = this.salt; +// const cPassword = cryptPwd(password, salt); +// this.password = cPassword; +// return cPassword; +// } +// checkPassword(password: string) { +// const salt = this.salt; +// const cPassword = cryptPwd(password, salt); +// return this.password === cPassword; +// } +// async getInfo() { +// const orgs = await this.getOrgs(); +// return { +// id: this.id, +// username: this.username, +// nickname: this.nickname, +// description: this.description, +// needChangePassword: this.needChangePassword, +// type: this.type, +// avatar: this.avatar, +// orgs, +// }; +// } +// async getOrgs() { +// let id = this.id; +// if (this.type === 'org') { +// if (this.tokenUser && this.tokenUser.uid) { +// id = this.tokenUser.uid; +// } else { +// console.log('getOrgs', 'no uid', this.id, this.username); +// throw new CustomError('Permission denied'); +// } +// } +// const cache = await redis.get(`user:${id}:orgs`); +// if (cache) { +// return JSON.parse(cache) as string[]; +// } +// const orgs = await Org.findAll({ +// order: [['updatedAt', 'DESC']], +// where: { +// users: { +// [Op.contains]: [ +// { +// uid: id, +// }, +// ], +// }, +// }, +// }); +// const orgNames = orgs.map((org) => org.username); +// if (orgNames.length > 0) { +// await redis.set(`user:${id}:orgs`, JSON.stringify(orgNames), 'EX', 60 * 60); // 1 hour +// } +// return orgNames; +// } +// async expireOrgs() { +// await redis.del(`user:${this.id}:orgs`); +// } +// } +// User.init( +// { +// id: { +// type: DataTypes.UUID, +// primaryKey: true, +// defaultValue: DataTypes.UUIDV4, +// }, +// username: { +// type: DataTypes.STRING, +// allowNull: false, +// unique: true, +// // 用户名或者手机号 +// // 创建后避免修改的字段,当注册用户后,用户名注册则默认不能用手机号 +// }, +// nickname: { +// type: DataTypes.TEXT, +// allowNull: true, +// }, +// alias: { +// type: DataTypes.TEXT, +// allowNull: true, // 别名,网络请求的别名,需要唯一,不能和username重复 +// defaultValue: '', +// }, +// password: { +// type: DataTypes.STRING, +// allowNull: true, +// }, +// email: { +// type: DataTypes.STRING, +// allowNull: true, +// }, +// avatar: { +// type: DataTypes.TEXT, +// allowNull: true, +// }, +// salt: { +// type: DataTypes.STRING, +// allowNull: true, +// }, +// description: { +// type: DataTypes.TEXT, +// }, +// type: { +// type: DataTypes.STRING, +// defaultValue: 'user', +// }, +// owner: { +// type: DataTypes.UUID, +// }, +// orgId: { +// type: DataTypes.UUID, +// }, +// needChangePassword: { +// type: DataTypes.BOOLEAN, +// defaultValue: false, +// }, +// data: { +// type: DataTypes.JSONB, +// defaultValue: {}, +// }, +// }, +// { +// sequelize, +// tableName: 'cf_user', // codeflow user +// paranoid: true, +// }, +// ); +// User.sync({ alter: true, logging: false }) +// .then((res) => { +// initializeUser(); +// }) +// .catch((err) => { +// console.error('Sync User error', err); +// }); -const letter = 'abcdefghijklmnopqrstuvwxyz'; -const custom = customAlphabet(letter, 6); -export const initializeUser = async (pwd = custom()) => { - const w = await User.findOne({ where: { username: 'root' }, logging: false }); - if (!w) { - const root = await User.createUser('root', pwd, '系统管理员'); - const org = await User.createOrg('admin', root.id, '管理员'); - console.info(' new Users name', root.username, org.username); - console.info('new Users root password', pwd); - console.info('new Users id', root.id, org.id); - const demo = await createDemoUser(); - return { - code: 200, - data: { root, org, pwd: pwd, demo }, - }; - } else { - return { - code: 500, - message: 'Users has been created', - }; - } -}; -export const createDemoUser = async (username = 'demo', pwd = custom()) => { - const u = await User.findOne({ where: { username }, logging: false }); - if (!u) { - const user = await User.createUser(username, pwd, 'demo'); - console.info('new Users name', user.username, pwd); - return { - code: 200, - data: { user, pwd: pwd }, - }; - } else { - console.info('Users has been created', u.username); - return { - code: 500, - message: 'Users has been created', - }; - } -}; -// initializeUser(); +// const letter = 'abcdefghijklmnopqrstuvwxyz'; +// const custom = customAlphabet(letter, 6); +// export const initializeUser = async (pwd = custom()) => { +// const w = await User.findOne({ where: { username: 'root' }, logging: false }); +// if (!w) { +// const root = await User.createUser('root', pwd, '系统管理员'); +// const org = await User.createOrg('admin', root.id, '管理员'); +// console.info(' new Users name', root.username, org.username); +// console.info('new Users root password', pwd); +// console.info('new Users id', root.id, org.id); +// const demo = await createDemoUser(); +// return { +// code: 200, +// data: { root, org, pwd: pwd, demo }, +// }; +// } else { +// return { +// code: 500, +// message: 'Users has been created', +// }; +// } +// }; +// export const createDemoUser = async (username = 'demo', pwd = custom()) => { +// const u = await User.findOne({ where: { username }, logging: false }); +// if (!u) { +// const user = await User.createUser(username, pwd, 'demo'); +// console.info('new Users name', user.username, pwd); +// return { +// code: 200, +// data: { user, pwd: pwd }, +// }; +// } else { +// console.info('Users has been created', u.username); +// return { +// code: 500, +// message: 'Users has been created', +// }; +// } +// }; +// // initializeUser(); -export class UserServices extends User { - static async loginByPhone(phone: string) { - let user = await User.findOne({ where: { username: phone } }); - let isNew = false; - if (!user) { - user = await User.createUser(phone, phone.slice(-6)); - isNew = true; - } - const token = await user.createToken(null, 'season'); - return { ...token, isNew }; - } - static initializeUser = initializeUser; - static createDemoUser = createDemoUser; -} +// export class UserServices extends User { +// static async loginByPhone(phone: string) { +// let user = await User.findOne({ where: { username: phone } }); +// let isNew = false; +// if (!user) { +// user = await User.createUser(phone, phone.slice(-6)); +// isNew = true; +// } +// const token = await user.createToken(null, 'season'); +// return { ...token, isNew }; +// } +// static initializeUser = initializeUser; +// static createDemoUser = createDemoUser; +// } -useContextKey('UserModel', () => UserServices); +// useContextKey('UserModel', () => UserServices); + +import { User, UserServices, UserInit } from '@kevisual/code-center-module/models'; +export { User, UserServices }; + +UserInit(); diff --git a/src/route.ts b/src/route.ts index 4219cc8..3598f8e 100644 --- a/src/route.ts +++ b/src/route.ts @@ -46,6 +46,46 @@ app } }) .addTo(app); +app + .route({ + path: 'auth-check', + key: 'admin', + id: 'check-auth-admin', + middleware: ['auth'], + }) + .define(async (ctx) => { + const tokenUser = ctx.state.tokenUser; + if (!tokenUser) { + ctx.throw(401, 'No User For authorized'); + } + try { + const user = await User.findOne({ + where: { + id: tokenUser.id, + }, + }); + if (!user) { + ctx.throw(404, 'user not found'); + } + user.setTokenUser(tokenUser); + const orgs = await user.getOrgs(); + if (orgs.includes('admin')) { + ctx.body = 'admin'; + ctx.state.tokenAdmin = { + id: user.id, + username: user.username, + orgs, + }; + return; + } + ctx.body = 'not admin'; + } catch (e) { + console.error(`auth-admin error`, e); + console.error('tokenUser', tokenUser?.id, tokenUser?.username, tokenUser?.uid); + ctx.throw(500, e.message); + } + }) + .addTo(app); app .route({ diff --git a/src/routes/container/list.ts b/src/routes/container/list.ts index a796993..f9a3860 100644 --- a/src/routes/container/list.ts +++ b/src/routes/container/list.ts @@ -60,20 +60,11 @@ add.run = async (ctx) => { }, }); await containerModel.save(); - if (containerModel.code || containerModel.source || containerModel.sourceType) { - ctx.emit?.('pageEdit', { - source: 'container', - data: containerModel.toJSON(), - operation: 'edit', - }); - } } } else { try { containerModel = await ContainerModel.create({ ...container, - source: '', - sourceType: '', uid: tokenUser.id, }); } catch (e) { @@ -127,25 +118,29 @@ app if (!key || !version || !fileName) { return; } - const uploadResult = await uploadMinioContainer({ - key, - tokenUser: ctx.state.tokenUser, - version: version, - code: container.code, - filePath: fileName, - saveHTML, - }); - await ctx.call({ - path: 'app', - key: 'uploadFiles', - payload: { - token, - data: { - appKey: key, - version, - files: uploadResult, + if (container.type === 'render-js') { + const uploadResult = await uploadMinioContainer({ + key, + tokenUser: ctx.state.tokenUser, + version: version, + code: container.code, + filePath: fileName, + saveHTML, + }); + await ctx.call({ + path: 'app', + key: 'uploadFiles', + payload: { + token, + data: { + appKey: key, + version, + files: uploadResult, + }, }, - }, - }); + }); + } else { + ctx.throw(500, 'container type not supported:' + container.type); + } }) .addTo(app); diff --git a/src/routes/container/models/index.ts b/src/routes/container/models/index.ts index aa607eb..a433d87 100644 --- a/src/routes/container/models/index.ts +++ b/src/routes/container/models/index.ts @@ -16,16 +16,23 @@ export type Container = Partial>; */ export class ContainerModel extends Model { declare id: string; + // 标题 declare title: string; + // 描述 declare description: string; + // 类型 declare type: string; + // 标签 declare tags: string[]; + // 代码 declare code: string; + // hash 值 declare hash: string; - declare source: string; - declare sourceType: string; + // 数据 declare data: ContainerData; + // 发布 declare publish: ContainerPublish; + // 用户 id declare uid: string; declare updatedAt: Date; declare createdAt: Date; @@ -45,11 +52,11 @@ ContainerModel.init( comment: 'id', }, title: { - type: DataTypes.STRING, + type: DataTypes.TEXT, defaultValue: '', }, description: { - type: DataTypes.STRING, + type: DataTypes.TEXT, defaultValue: '', }, tags: { @@ -57,8 +64,8 @@ ContainerModel.init( defaultValue: [], }, type: { - type: DataTypes.STRING, - defaultValue: '', + type: DataTypes.STRING, // 代码类型, html, js, render-js + defaultValue: 'render-js', }, code: { type: DataTypes.TEXT, @@ -68,14 +75,6 @@ ContainerModel.init( type: DataTypes.TEXT, defaultValue: '', }, - source: { - type: DataTypes.STRING, - defaultValue: '', - }, - sourceType: { - type: DataTypes.STRING, - defaultValue: '', - }, data: { type: DataTypes.JSON, defaultValue: {}, diff --git a/src/routes/user/index.ts b/src/routes/user/index.ts index 33618e9..1688a43 100644 --- a/src/routes/user/index.ts +++ b/src/routes/user/index.ts @@ -7,4 +7,6 @@ import './update.ts' import './init.ts' -import './web-login.ts' \ No newline at end of file +import './web-login.ts' + +import './org-user/list.ts' \ No newline at end of file diff --git a/src/routes/user/me.ts b/src/routes/user/me.ts index ea95d02..25a087d 100644 --- a/src/routes/user/me.ts +++ b/src/routes/user/me.ts @@ -72,6 +72,7 @@ app if (!user.checkPassword(password)) { ctx.throw(500, 'Password error'); } + user.expireOrgs(); const token = await user.createToken(null, loginType); createCookie(token, ctx); ctx.body = token; diff --git a/src/routes/user/org-user/index.ts b/src/routes/user/org-user/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/routes/user/org-user/list.ts b/src/routes/user/org-user/list.ts new file mode 100644 index 0000000..141045d --- /dev/null +++ b/src/routes/user/org-user/list.ts @@ -0,0 +1,55 @@ +import { app } from '@/app.ts'; +import { User } from '@/models/user.ts'; +import { Org } from '@/models/org.ts'; +// 获取组织的用户列表 +app + .route({ + path: 'org-user', + key: 'list', + middleware: ['auth'], + }) + .define(async (ctx) => { + const tokenUser = ctx.state.tokenUser; + const { orgId } = ctx.query; + const org = await Org.findByPk(orgId); + if (!org) { + ctx.throw('组织不存在'); + } + // const users = await user.getUsers(); + ctx.body = org; + }) + .addTo(app); + +app + .route({ + path: 'org-user', + key: 'operate', + middleware: ['check-auth-admin'], + }) + .define(async (ctx) => { + const tokenAdmin = ctx.state.tokenAdmin; + const tokenUser = ctx.state.tokenUser; + const data = ctx.query.data; + const { orgId, userId, action } = data; + const org = await Org.findByPk(orgId); + if (!org) { + ctx.throw('组织不存在'); + } + const user = await User.findByPk(userId); + if (!user) { + ctx.throw('用户不存在'); + } + if (user.type !== 'user') { + ctx.throw('用户类型错误'); + } + const operateId = tokenUser.uid || tokenUser.id; + if (action === 'add') { + await org.addUser(user, { needPermission: true, role: 'user', operateId, isAdmin: !!tokenAdmin }); + } else if (action === 'remove') { + await org.removeUser(user, { needPermission: true, operateId, isAdmin: !!tokenAdmin }); + } else { + ctx.throw('操作错误'); + } + ctx.body = 'ok'; + }) + .addTo(app); diff --git a/src/routes/user/org.ts b/src/routes/user/org.ts index 8e0f24d..b753942 100644 --- a/src/routes/user/org.ts +++ b/src/routes/user/org.ts @@ -1,7 +1,6 @@ import { app, sequelize } from '@/app.ts'; import { Org } from '@/models/org.ts'; import { User } from '@/models/user.ts'; -import { CustomError } from '@kevisual/router'; import { Op } from 'sequelize'; app @@ -35,18 +34,19 @@ app .route({ path: 'org', key: 'update', - middleware: ['auth'], + middleware: ['auth-admin'], }) .define(async (ctx) => { const tokenUser = ctx.state.tokenUser; + // username 为org的名字,在用户表中也是唯一的 const { username, description, id } = ctx.query.data; if (!username) { - throw new CustomError('username is required'); + ctx.throw('username is required'); } if (id) { const org = await Org.findByPk(id); if (!org) { - throw new CustomError('org not found'); + ctx.throw('org not found'); } org.description = description; await org.save(); @@ -62,11 +62,11 @@ app } const user = await User.findByPk(tokenUser.id); if (!user) { - throw new CustomError('user not found'); + ctx.throw('user not found'); } const orgs = await user.getOrgs(); if (!orgs.includes('admin')) { - throw new CustomError('Permission denied'); + ctx.throw('Permission denied'); } const newUser = await User.createOrg(username, tokenUser.id, description); ctx.body = { @@ -87,17 +87,17 @@ app const tokenUser = ctx.state.tokenUser; const id = ctx.query.id; if (!id) { - throw new CustomError('id is required'); + ctx.throw('id is required'); } const org = await Org.findByPk(id); if (!org) { - throw new CustomError('org not found'); + ctx.throw('org not found'); } const username = org.username; const users = org.users; const owner = users.find((u) => u.role === 'owner'); if (owner.uid !== tokenUser.id) { - throw new CustomError('Permission denied'); + ctx.throw('Permission denied'); } await org.destroy({ force: true }); const orgUser = await User.findOne({ @@ -118,36 +118,21 @@ app const tokenUser = ctx.state.tokenUser; const id = ctx.query.id; if (!id) { - throw new CustomError('id is required'); + ctx.throw('id is required'); } const org = await Org.findByPk(id); if (!org) { - throw new CustomError('org not found'); + ctx.throw('org not found'); } const usersIds = org.users; const me = usersIds.find((u) => u.uid === tokenUser.id); if (!me) { - throw new CustomError('Permission denied'); + ctx.throw('Permission denied'); } - const _users = await User.findAll({ - where: { - id: { - [Op.in]: usersIds.map((u) => u.uid), - }, - }, - }); - - const users = _users.map((u) => { - const role = usersIds.find((r) => r.uid === u.id)?.role; - return { - id: u.id, - username: u.username, - role: role, - }; - }); + const orgGetUser = await org.getUsers(); ctx.body = { org, - users, + users: orgGetUser.users, }; }) .addTo(app); diff --git a/src/scripts/container.ts b/src/scripts/container.ts new file mode 100644 index 0000000..dafc718 --- /dev/null +++ b/src/scripts/container.ts @@ -0,0 +1,21 @@ +import { ContainerModel } from '../routes/container/models/index.ts'; + +const main = async () => { + // await ContainerModel.update( + // { + // type: 'render-js', + // }, + // { + // where: { + // type: '', + // }, + // }, + // ); + const containers = await ContainerModel.findAll(); + for (const container of containers) { + console.log(container.id, container.type); + } + process.exit(0); +}; + +main(); diff --git a/src/scripts/sync-user.ts b/src/scripts/sync-user.ts index cb9a41d..7a9c3b6 100644 --- a/src/scripts/sync-user.ts +++ b/src/scripts/sync-user.ts @@ -4,6 +4,9 @@ import { User } from '../models/user.ts'; // console.log('sync user done'); // }); -User.findOne({ where: { username: 'admin' } }).then((user) => { - console.log('user', user); -}); \ No newline at end of file +// class UserChange extends User { +// static async syncUser() { +// await UserChange.sync({ alter: true, logging: false }); +// console.log('sync user done'); +// } +// }