feat: update org operate

This commit is contained in:
熊潇 2025-02-28 13:35:13 +08:00
parent 409067f13f
commit d2280f6b89
14 changed files with 542 additions and 400 deletions

View File

@ -16,7 +16,8 @@
"reload": "ssh light pm2 restart codecenter", "reload": "ssh light pm2 restart codecenter",
"pub": "npm run build && npm run deploy && npm run reload", "pub": "npm run build && npm run deploy && npm run reload",
"start": "pm2 start dist/app.mjs --name codecenter", "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": [], "keywords": [],
"types": "types/index.d.ts", "types": "types/index.d.ts",
@ -45,6 +46,7 @@
"node-fetch": "^3.3.2", "node-fetch": "^3.3.2",
"p-queue": "^8.1.0", "p-queue": "^8.1.0",
"pg": "^8.13.3", "pg": "^8.13.3",
"pm2": "^5.4.3",
"rollup-plugin-esbuild": "^6.2.1", "rollup-plugin-esbuild": "^6.2.1",
"semver": "^7.7.1", "semver": "^7.7.1",
"sequelize": "^6.37.5", "sequelize": "^6.37.5",
@ -52,10 +54,10 @@
"strip-ansi": "^7.1.0", "strip-ansi": "^7.1.0",
"tar": "^7.4.3", "tar": "^7.4.3",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"pm2": "^5.4.3",
"zod": "^3.24.2" "zod": "^3.24.2"
}, },
"devDependencies": { "devDependencies": {
"@kevisual/code-center-module": "^0.0.9",
"@kevisual/types": "^0.0.6", "@kevisual/types": "^0.0.6",
"@rollup/plugin-alias": "^5.1.1", "@rollup/plugin-alias": "^5.1.1",
"@rollup/plugin-commonjs": "^28.0.2", "@rollup/plugin-commonjs": "^28.0.2",

29
pnpm-lock.yaml generated
View File

@ -95,6 +95,9 @@ importers:
specifier: ^3.24.2 specifier: ^3.24.2
version: 3.24.2 version: 3.24.2
devDependencies: 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': '@kevisual/types':
specifier: ^0.0.6 specifier: ^0.0.6
version: 0.0.6 version: 0.0.6
@ -348,6 +351,16 @@ 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/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': '@kevisual/local-app-manager@0.1.9':
resolution: {integrity: sha512-oppYUjvpPg/7gBw1h3dX63fhxB8MvGc74yGgIM4JifvSuOYLcZ9oZyauYV+5lQVh2tjJImOuUmRHnVXu1FWKDQ==} resolution: {integrity: sha512-oppYUjvpPg/7gBw1h3dX63fhxB8MvGc74yGgIM4JifvSuOYLcZ9oZyauYV+5lQVh2tjJImOuUmRHnVXu1FWKDQ==}
peerDependencies: peerDependencies:
@ -2562,6 +2575,22 @@ snapshots:
'@kevisual/auth@1.0.5': {} '@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)': '@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: dependencies:
'@kevisual/router': 0.0.7 '@kevisual/router': 0.0.7

View File

@ -1,44 +1,48 @@
import { DataTypes, Model, Sequelize } from 'sequelize'; // import { DataTypes, Model, Sequelize } from 'sequelize';
import { useContextKey } from '@kevisual/use-config/context'; // import { useContextKey } from '@kevisual/use-config/context';
const sequelize = useContextKey<Sequelize>('sequelize'); // const sequelize = useContextKey<Sequelize>('sequelize');
export class Org extends Model { // export class Org extends Model {
declare id: string; // declare id: string;
declare username: string; // declare username: string;
declare description: string; // declare description: string;
declare users: { role: string; uid: string }[]; // declare users: { role: string; uid: string }[];
} // }
Org.init( // Org.init(
{ // {
id: { // id: {
type: DataTypes.UUID, // type: DataTypes.UUID,
primaryKey: true, // primaryKey: true,
defaultValue: DataTypes.UUIDV4, // defaultValue: DataTypes.UUIDV4,
}, // },
username: { // username: {
type: DataTypes.STRING, // type: DataTypes.STRING,
allowNull: false, // allowNull: false,
unique: true, // unique: true,
}, // },
description: { // description: {
type: DataTypes.STRING, // type: DataTypes.STRING,
allowNull: true, // allowNull: true,
}, // },
users: { // users: {
type: DataTypes.JSONB, // type: DataTypes.JSONB,
allowNull: true, // allowNull: true,
defaultValue: [], // defaultValue: [],
}, // },
}, // },
{ // {
sequelize, // sequelize,
modelName: 'cf_org', // modelName: 'cf_org',
paranoid: true, // paranoid: true,
}, // },
); // );
Org.sync({ alter: true, logging: false }).catch((e) => { // Org.sync({ alter: true, logging: false }).catch((e) => {
console.error('Org sync', e); // console.error('Org sync', e);
}); // });
useContextKey('OrgModel', () => Org); // useContextKey('OrgModel', () => Org);
import { Org, OrgInit } from '@kevisual/code-center-module/models';
export { Org };
OrgInit();

View File

@ -1,287 +1,293 @@
import { useConfig } from '@kevisual/use-config'; // import { useConfig } from '@kevisual/use-config';
import { DataTypes, Model, Op, Sequelize } from 'sequelize'; // import { DataTypes, Model, Op, Sequelize } from 'sequelize';
import { createToken, checkToken } from '@kevisual/auth'; // import { createToken, checkToken } from '@kevisual/auth';
import { cryptPwd } from '@kevisual/auth'; // import { cryptPwd } from '@kevisual/auth';
import { customRandom, nanoid, customAlphabet } from 'nanoid'; // import { customRandom, nanoid, customAlphabet } from 'nanoid';
import { CustomError } from '@kevisual/router'; // import { CustomError } from '@kevisual/router';
import { Org } from './org.ts'; // import { Org } from './org.ts';
import { useContextKey } from '@kevisual/use-config/context'; // import { useContextKey } from '@kevisual/use-config/context';
import { Redis } from 'ioredis'; // import { Redis } from 'ioredis';
export const redis = useContextKey<Redis>('redis'); // export const redis = useContextKey<Redis>('redis');
const sequelize = useContextKey<Sequelize>('sequelize'); // const sequelize = useContextKey<Sequelize>('sequelize');
const config = useConfig<{ tokenSecret: string }>(); // const config = useConfig<{ tokenSecret: string }>();
type UserData = { // type UserData = {
orgs?: string[]; // orgs?: string[];
}; // };
export class User extends Model { // export class User extends Model {
declare id: string; // declare id: string;
declare username: string; // declare username: string;
declare nickname: string; // 昵称 // declare nickname: string; // 昵称
declare alias: string; // 别名 // declare alias: string; // 别名
declare password: string; // declare password: string;
declare salt: string; // declare salt: string;
declare needChangePassword: boolean; // declare needChangePassword: boolean;
declare description: string; // declare description: string;
declare data: UserData; // declare data: UserData;
declare type: string; // user | org | visitor // declare type: string; // user | org | visitor
declare owner: string; // declare owner: string;
declare orgId: string; // declare orgId: string;
declare email: string; // declare email: string;
declare avatar: string; // declare avatar: string;
tokenUser: any; // tokenUser: any;
setTokenUser(tokenUser: any) { // setTokenUser(tokenUser: any) {
this.tokenUser = tokenUser; // this.tokenUser = tokenUser;
} // }
/** // /**
* uid orgId id id // * uid 是用于 orgId 的用户id 真实用户的id
* @param uid // * @param uid
* @returns // * @returns
*/ // */
async createToken(uid?: string, loginType?: 'default' | 'plugin' | 'month' | 'season' | 'year') { // async createToken(uid?: string, loginType?: 'default' | 'plugin' | 'month' | 'season' | 'year') {
const { id, username, type } = this; // const { id, username, type } = this;
let expireTime = 60 * 60 * 24 * 7; // 7 days // let expireTime = 60 * 60 * 24 * 7; // 7 days
switch (loginType) { // switch (loginType) {
case 'plugin': // case 'plugin':
expireTime = 60 * 60 * 24 * 30 * 12; // 365 days // expireTime = 60 * 60 * 24 * 30 * 12; // 365 days
break; // break;
case 'month': // case 'month':
expireTime = 60 * 60 * 24 * 30; // 30 days // expireTime = 60 * 60 * 24 * 30; // 30 days
break; // break;
case 'season': // case 'season':
expireTime = 60 * 60 * 24 * 30 * 3; // 90 days // expireTime = 60 * 60 * 24 * 30 * 3; // 90 days
break; // break;
case 'year': // case 'year':
expireTime = 60 * 60 * 24 * 30 * 12; // 365 days // expireTime = 60 * 60 * 24 * 30 * 12; // 365 days
break; // break;
} // }
const now = new Date().getTime(); // const now = new Date().getTime();
const token = await createToken({ id, username, uid, type }, config.tokenSecret); // const token = await createToken({ id, username, uid, type }, config.tokenSecret);
return { token, expireTime: now + expireTime }; // return { token, expireTime: now + expireTime };
} // }
static async verifyToken(token: string) { // static async verifyToken(token: string) {
const ct = await checkToken(token, config.tokenSecret); // const ct = await checkToken(token, config.tokenSecret);
const tokenUser = ct.payload; // const tokenUser = ct.payload;
return tokenUser; // return tokenUser;
} // }
static async createUser(username: string, password?: string, description?: string) { // static async createUser(username: string, password?: string, description?: string) {
const user = await User.findOne({ where: { username } }); // const user = await User.findOne({ where: { username } });
if (user) { // if (user) {
throw new CustomError('User already exists'); // throw new CustomError('User already exists');
} // }
const salt = nanoid(6); // const salt = nanoid(6);
let needChangePassword = !password; // let needChangePassword = !password;
password = password || '123456'; // password = password || '123456';
const cPassword = cryptPwd(password, salt); // const cPassword = cryptPwd(password, salt);
return await User.create({ username, password: cPassword, description, salt, needChangePassword }); // return await User.create({ username, password: cPassword, description, salt, needChangePassword });
} // }
static async createOrg(username: string, owner: string, description?: string) { // static async createOrg(username: string, owner: string, description?: string) {
const user = await User.findOne({ where: { username } }); // const user = await User.findOne({ where: { username } });
if (user) { // if (user) {
throw new CustomError('User already exists'); // throw new CustomError('User already exists');
} // }
const me = await User.findByPk(owner); // const me = await User.findByPk(owner);
if (!me) { // if (!me) {
throw new CustomError('Owner not found'); // throw new CustomError('Owner not found');
} // }
if (me.type !== 'user') { // if (me.type !== 'user') {
throw new CustomError('Owner type is not user'); // throw new CustomError('Owner type is not user');
} // }
const org = await Org.create({ username, description, users: [{ uid: owner, role: 'owner' }] }); // 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 }); // const newUser = await User.create({ username, password: '', description, type: 'org', owner, orgId: org.id });
// owner add // // owner add
await redis.del(`user:${me.id}:orgs`); // await redis.del(`user:${me.id}:orgs`);
return newUser; // return newUser;
} // }
createPassword(password: string) { // createPassword(password: string) {
const salt = this.salt; // const salt = this.salt;
const cPassword = cryptPwd(password, salt); // const cPassword = cryptPwd(password, salt);
this.password = cPassword; // this.password = cPassword;
return cPassword; // return cPassword;
} // }
checkPassword(password: string) { // checkPassword(password: string) {
const salt = this.salt; // const salt = this.salt;
const cPassword = cryptPwd(password, salt); // const cPassword = cryptPwd(password, salt);
return this.password === cPassword; // return this.password === cPassword;
} // }
async getInfo() { // async getInfo() {
const orgs = await this.getOrgs(); // const orgs = await this.getOrgs();
return { // return {
id: this.id, // id: this.id,
username: this.username, // username: this.username,
nickname: this.nickname, // nickname: this.nickname,
description: this.description, // description: this.description,
needChangePassword: this.needChangePassword, // needChangePassword: this.needChangePassword,
type: this.type, // type: this.type,
avatar: this.avatar, // avatar: this.avatar,
orgs, // orgs,
}; // };
} // }
async getOrgs() { // async getOrgs() {
let id = this.id; // let id = this.id;
if (this.type === 'org') { // if (this.type === 'org') {
if (this.tokenUser && this.tokenUser.uid) { // if (this.tokenUser && this.tokenUser.uid) {
id = this.tokenUser.uid; // id = this.tokenUser.uid;
} else { // } else {
console.log('getOrgs', 'no uid', this.id, this.username); // console.log('getOrgs', 'no uid', this.id, this.username);
throw new CustomError('Permission denied'); // throw new CustomError('Permission denied');
} // }
} // }
const cache = await redis.get(`user:${id}:orgs`); // const cache = await redis.get(`user:${id}:orgs`);
if (cache) { // if (cache) {
return JSON.parse(cache) as string[]; // return JSON.parse(cache) as string[];
} // }
const orgs = await Org.findAll({ // const orgs = await Org.findAll({
order: [['updatedAt', 'DESC']], // order: [['updatedAt', 'DESC']],
where: { // where: {
users: { // users: {
[Op.contains]: [ // [Op.contains]: [
{ // {
uid: id, // uid: id,
}, // },
], // ],
}, // },
}, // },
}); // });
const orgNames = orgs.map((org) => org.username); // const orgNames = orgs.map((org) => org.username);
if (orgNames.length > 0) { // if (orgNames.length > 0) {
await redis.set(`user:${id}:orgs`, JSON.stringify(orgNames), 'EX', 60 * 60); // 1 hour // await redis.set(`user:${id}:orgs`, JSON.stringify(orgNames), 'EX', 60 * 60); // 1 hour
} // }
return orgNames; // return orgNames;
} // }
async expireOrgs() { // async expireOrgs() {
await redis.del(`user:${this.id}:orgs`); // await redis.del(`user:${this.id}:orgs`);
} // }
} // }
User.init( // User.init(
{ // {
id: { // id: {
type: DataTypes.UUID, // type: DataTypes.UUID,
primaryKey: true, // primaryKey: true,
defaultValue: DataTypes.UUIDV4, // defaultValue: DataTypes.UUIDV4,
}, // },
username: { // username: {
type: DataTypes.STRING, // type: DataTypes.STRING,
allowNull: false, // allowNull: false,
unique: true, // unique: true,
// 用户名或者手机号 // // 用户名或者手机号
// 创建后避免修改的字段,当注册用户后,用户名注册则默认不能用手机号 // // 创建后避免修改的字段,当注册用户后,用户名注册则默认不能用手机号
}, // },
nickname: { // nickname: {
type: DataTypes.TEXT, // type: DataTypes.TEXT,
allowNull: true, // allowNull: true,
}, // },
alias: { // alias: {
type: DataTypes.TEXT, // type: DataTypes.TEXT,
allowNull: true, // 别名网络请求的别名需要唯一不能和username重复 // allowNull: true, // 别名网络请求的别名需要唯一不能和username重复
}, // defaultValue: '',
password: { // },
type: DataTypes.STRING, // password: {
allowNull: true, // type: DataTypes.STRING,
}, // allowNull: true,
email: { // },
type: DataTypes.STRING, // email: {
allowNull: true, // type: DataTypes.STRING,
}, // allowNull: true,
avatar: { // },
type: DataTypes.TEXT, // avatar: {
allowNull: true, // type: DataTypes.TEXT,
}, // allowNull: true,
salt: { // },
type: DataTypes.STRING, // salt: {
allowNull: true, // type: DataTypes.STRING,
}, // allowNull: true,
description: { // },
type: DataTypes.TEXT, // description: {
}, // type: DataTypes.TEXT,
type: { // },
type: DataTypes.STRING, // type: {
defaultValue: 'user', // type: DataTypes.STRING,
}, // defaultValue: 'user',
owner: { // },
type: DataTypes.UUID, // owner: {
}, // type: DataTypes.UUID,
orgId: { // },
type: DataTypes.UUID, // orgId: {
}, // type: DataTypes.UUID,
needChangePassword: { // },
type: DataTypes.BOOLEAN, // needChangePassword: {
defaultValue: false, // type: DataTypes.BOOLEAN,
}, // defaultValue: false,
data: { // },
type: DataTypes.JSON, // data: {
defaultValue: {}, // type: DataTypes.JSONB,
}, // defaultValue: {},
}, // },
{ // },
sequelize, // {
tableName: 'cf_user', // codeflow user // sequelize,
paranoid: true, // tableName: 'cf_user', // codeflow user
}, // paranoid: true,
); // },
User.sync({ alter: true, logging: false }) // );
.then((res) => { // User.sync({ alter: true, logging: false })
initializeUser(); // .then((res) => {
}) // initializeUser();
.catch((err) => { // })
console.error('Sync User error', err); // .catch((err) => {
}); // console.error('Sync User error', err);
// });
const letter = 'abcdefghijklmnopqrstuvwxyz'; // const letter = 'abcdefghijklmnopqrstuvwxyz';
const custom = customAlphabet(letter, 6); // const custom = customAlphabet(letter, 6);
export const initializeUser = async (pwd = custom()) => { // export const initializeUser = async (pwd = custom()) => {
const w = await User.findOne({ where: { username: 'root' }, logging: false }); // const w = await User.findOne({ where: { username: 'root' }, logging: false });
if (!w) { // if (!w) {
const root = await User.createUser('root', pwd, '系统管理员'); // const root = await User.createUser('root', pwd, '系统管理员');
const org = await User.createOrg('admin', root.id, '管理员'); // const org = await User.createOrg('admin', root.id, '管理员');
console.info(' new Users name', root.username, org.username); // console.info(' new Users name', root.username, org.username);
console.info('new Users root password', pwd); // console.info('new Users root password', pwd);
console.info('new Users id', root.id, org.id); // console.info('new Users id', root.id, org.id);
const demo = await createDemoUser(); // const demo = await createDemoUser();
return { // return {
code: 200, // code: 200,
data: { root, org, pwd: pwd, demo }, // data: { root, org, pwd: pwd, demo },
}; // };
} else { // } else {
return { // return {
code: 500, // code: 500,
message: 'Users has been created', // message: 'Users has been created',
}; // };
} // }
}; // };
export const createDemoUser = async (username = 'demo', pwd = custom()) => { // export const createDemoUser = async (username = 'demo', pwd = custom()) => {
const u = await User.findOne({ where: { username }, logging: false }); // const u = await User.findOne({ where: { username }, logging: false });
if (!u) { // if (!u) {
const user = await User.createUser(username, pwd, 'demo'); // const user = await User.createUser(username, pwd, 'demo');
console.info('new Users name', user.username, pwd); // console.info('new Users name', user.username, pwd);
return { // return {
code: 200, // code: 200,
data: { user, pwd: pwd }, // data: { user, pwd: pwd },
}; // };
} else { // } else {
console.info('Users has been created', u.username); // console.info('Users has been created', u.username);
return { // return {
code: 500, // code: 500,
message: 'Users has been created', // message: 'Users has been created',
}; // };
} // }
}; // };
// initializeUser(); // // initializeUser();
export class UserServices extends User { // export class UserServices extends User {
static async loginByPhone(phone: string) { // static async loginByPhone(phone: string) {
let user = await User.findOne({ where: { username: phone } }); // let user = await User.findOne({ where: { username: phone } });
let isNew = false; // let isNew = false;
if (!user) { // if (!user) {
user = await User.createUser(phone, phone.slice(-6)); // user = await User.createUser(phone, phone.slice(-6));
isNew = true; // isNew = true;
} // }
const token = await user.createToken(null, 'season'); // const token = await user.createToken(null, 'season');
return { ...token, isNew }; // return { ...token, isNew };
} // }
static initializeUser = initializeUser; // static initializeUser = initializeUser;
static createDemoUser = createDemoUser; // static createDemoUser = createDemoUser;
} // }
useContextKey('UserModel', () => UserServices); // useContextKey('UserModel', () => UserServices);
import { User, UserServices, UserInit } from '@kevisual/code-center-module/models';
export { User, UserServices };
UserInit();

View File

@ -46,6 +46,46 @@ app
} }
}) })
.addTo(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 app
.route({ .route({

View File

@ -60,20 +60,11 @@ add.run = async (ctx) => {
}, },
}); });
await containerModel.save(); await containerModel.save();
if (containerModel.code || containerModel.source || containerModel.sourceType) {
ctx.emit?.('pageEdit', {
source: 'container',
data: containerModel.toJSON(),
operation: 'edit',
});
}
} }
} else { } else {
try { try {
containerModel = await ContainerModel.create({ containerModel = await ContainerModel.create({
...container, ...container,
source: '',
sourceType: '',
uid: tokenUser.id, uid: tokenUser.id,
}); });
} catch (e) { } catch (e) {
@ -127,25 +118,29 @@ app
if (!key || !version || !fileName) { if (!key || !version || !fileName) {
return; return;
} }
const uploadResult = await uploadMinioContainer({ if (container.type === 'render-js') {
key, const uploadResult = await uploadMinioContainer({
tokenUser: ctx.state.tokenUser, key,
version: version, tokenUser: ctx.state.tokenUser,
code: container.code, version: version,
filePath: fileName, code: container.code,
saveHTML, filePath: fileName,
}); saveHTML,
await ctx.call({ });
path: 'app', await ctx.call({
key: 'uploadFiles', path: 'app',
payload: { key: 'uploadFiles',
token, payload: {
data: { token,
appKey: key, data: {
version, appKey: key,
files: uploadResult, version,
files: uploadResult,
},
}, },
}, });
}); } else {
ctx.throw(500, 'container type not supported:' + container.type);
}
}) })
.addTo(app); .addTo(app);

View File

@ -16,16 +16,23 @@ export type Container = Partial<InstanceType<typeof ContainerModel>>;
*/ */
export class ContainerModel extends Model { export class ContainerModel extends Model {
declare id: string; declare id: string;
// 标题
declare title: string; declare title: string;
// 描述
declare description: string; declare description: string;
// 类型
declare type: string; declare type: string;
// 标签
declare tags: string[]; declare tags: string[];
// 代码
declare code: string; declare code: string;
// hash 值
declare hash: string; declare hash: string;
declare source: string; // 数据
declare sourceType: string;
declare data: ContainerData; declare data: ContainerData;
// 发布
declare publish: ContainerPublish; declare publish: ContainerPublish;
// 用户 id
declare uid: string; declare uid: string;
declare updatedAt: Date; declare updatedAt: Date;
declare createdAt: Date; declare createdAt: Date;
@ -45,11 +52,11 @@ ContainerModel.init(
comment: 'id', comment: 'id',
}, },
title: { title: {
type: DataTypes.STRING, type: DataTypes.TEXT,
defaultValue: '', defaultValue: '',
}, },
description: { description: {
type: DataTypes.STRING, type: DataTypes.TEXT,
defaultValue: '', defaultValue: '',
}, },
tags: { tags: {
@ -57,8 +64,8 @@ ContainerModel.init(
defaultValue: [], defaultValue: [],
}, },
type: { type: {
type: DataTypes.STRING, type: DataTypes.STRING, // 代码类型, html, js, render-js
defaultValue: '', defaultValue: 'render-js',
}, },
code: { code: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
@ -68,14 +75,6 @@ ContainerModel.init(
type: DataTypes.TEXT, type: DataTypes.TEXT,
defaultValue: '', defaultValue: '',
}, },
source: {
type: DataTypes.STRING,
defaultValue: '',
},
sourceType: {
type: DataTypes.STRING,
defaultValue: '',
},
data: { data: {
type: DataTypes.JSON, type: DataTypes.JSON,
defaultValue: {}, defaultValue: {},

View File

@ -8,3 +8,5 @@ import './update.ts'
import './init.ts' import './init.ts'
import './web-login.ts' import './web-login.ts'
import './org-user/list.ts'

View File

@ -72,6 +72,7 @@ app
if (!user.checkPassword(password)) { if (!user.checkPassword(password)) {
ctx.throw(500, 'Password error'); ctx.throw(500, 'Password error');
} }
user.expireOrgs();
const token = await user.createToken(null, loginType); const token = await user.createToken(null, loginType);
createCookie(token, ctx); createCookie(token, ctx);
ctx.body = token; ctx.body = token;

View File

View File

@ -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);

View File

@ -1,7 +1,6 @@
import { app, sequelize } from '@/app.ts'; import { app, sequelize } from '@/app.ts';
import { Org } from '@/models/org.ts'; import { Org } from '@/models/org.ts';
import { User } from '@/models/user.ts'; import { User } from '@/models/user.ts';
import { CustomError } from '@kevisual/router';
import { Op } from 'sequelize'; import { Op } from 'sequelize';
app app
@ -35,18 +34,19 @@ app
.route({ .route({
path: 'org', path: 'org',
key: 'update', key: 'update',
middleware: ['auth'], middleware: ['auth-admin'],
}) })
.define(async (ctx) => { .define(async (ctx) => {
const tokenUser = ctx.state.tokenUser; const tokenUser = ctx.state.tokenUser;
// username 为org的名字在用户表中也是唯一的
const { username, description, id } = ctx.query.data; const { username, description, id } = ctx.query.data;
if (!username) { if (!username) {
throw new CustomError('username is required'); ctx.throw('username is required');
} }
if (id) { if (id) {
const org = await Org.findByPk(id); const org = await Org.findByPk(id);
if (!org) { if (!org) {
throw new CustomError('org not found'); ctx.throw('org not found');
} }
org.description = description; org.description = description;
await org.save(); await org.save();
@ -62,11 +62,11 @@ app
} }
const user = await User.findByPk(tokenUser.id); const user = await User.findByPk(tokenUser.id);
if (!user) { if (!user) {
throw new CustomError('user not found'); ctx.throw('user not found');
} }
const orgs = await user.getOrgs(); const orgs = await user.getOrgs();
if (!orgs.includes('admin')) { if (!orgs.includes('admin')) {
throw new CustomError('Permission denied'); ctx.throw('Permission denied');
} }
const newUser = await User.createOrg(username, tokenUser.id, description); const newUser = await User.createOrg(username, tokenUser.id, description);
ctx.body = { ctx.body = {
@ -87,17 +87,17 @@ app
const tokenUser = ctx.state.tokenUser; const tokenUser = ctx.state.tokenUser;
const id = ctx.query.id; const id = ctx.query.id;
if (!id) { if (!id) {
throw new CustomError('id is required'); ctx.throw('id is required');
} }
const org = await Org.findByPk(id); const org = await Org.findByPk(id);
if (!org) { if (!org) {
throw new CustomError('org not found'); ctx.throw('org not found');
} }
const username = org.username; const username = org.username;
const users = org.users; const users = org.users;
const owner = users.find((u) => u.role === 'owner'); const owner = users.find((u) => u.role === 'owner');
if (owner.uid !== tokenUser.id) { if (owner.uid !== tokenUser.id) {
throw new CustomError('Permission denied'); ctx.throw('Permission denied');
} }
await org.destroy({ force: true }); await org.destroy({ force: true });
const orgUser = await User.findOne({ const orgUser = await User.findOne({
@ -118,36 +118,21 @@ app
const tokenUser = ctx.state.tokenUser; const tokenUser = ctx.state.tokenUser;
const id = ctx.query.id; const id = ctx.query.id;
if (!id) { if (!id) {
throw new CustomError('id is required'); ctx.throw('id is required');
} }
const org = await Org.findByPk(id); const org = await Org.findByPk(id);
if (!org) { if (!org) {
throw new CustomError('org not found'); ctx.throw('org not found');
} }
const usersIds = org.users; const usersIds = org.users;
const me = usersIds.find((u) => u.uid === tokenUser.id); const me = usersIds.find((u) => u.uid === tokenUser.id);
if (!me) { if (!me) {
throw new CustomError('Permission denied'); ctx.throw('Permission denied');
} }
const _users = await User.findAll({ const orgGetUser = await org.getUsers();
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,
};
});
ctx.body = { ctx.body = {
org, org,
users, users: orgGetUser.users,
}; };
}) })
.addTo(app); .addTo(app);

21
src/scripts/container.ts Normal file
View File

@ -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();

View File

@ -4,6 +4,9 @@ import { User } from '../models/user.ts';
// console.log('sync user done'); // console.log('sync user done');
// }); // });
User.findOne({ where: { username: 'admin' } }).then((user) => { // class UserChange extends User {
console.log('user', user); // static async syncUser() {
}); // await UserChange.sync({ alter: true, logging: false });
// console.log('sync user done');
// }
// }