feat: update org operate
This commit is contained in:
parent
409067f13f
commit
d2280f6b89
@ -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",
|
||||
|
29
pnpm-lock.yaml
generated
29
pnpm-lock.yaml
generated
@ -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
|
||||
|
@ -1,44 +1,48 @@
|
||||
import { DataTypes, Model, Sequelize } from 'sequelize';
|
||||
import { useContextKey } from '@kevisual/use-config/context';
|
||||
const sequelize = useContextKey<Sequelize>('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>('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();
|
||||
|
@ -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>('redis');
|
||||
const sequelize = useContextKey<Sequelize>('sequelize');
|
||||
const config = useConfig<{ tokenSecret: string }>();
|
||||
// import { useContextKey } from '@kevisual/use-config/context';
|
||||
// import { Redis } from 'ioredis';
|
||||
// export const redis = useContextKey<Redis>('redis');
|
||||
// const sequelize = useContextKey<Sequelize>('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();
|
||||
|
40
src/route.ts
40
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({
|
||||
|
@ -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);
|
||||
|
@ -16,16 +16,23 @@ export type Container = Partial<InstanceType<typeof ContainerModel>>;
|
||||
*/
|
||||
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: {},
|
||||
|
@ -8,3 +8,5 @@ import './update.ts'
|
||||
import './init.ts'
|
||||
|
||||
import './web-login.ts'
|
||||
|
||||
import './org-user/list.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;
|
||||
|
0
src/routes/user/org-user/index.ts
Normal file
0
src/routes/user/org-user/index.ts
Normal file
55
src/routes/user/org-user/list.ts
Normal file
55
src/routes/user/org-user/list.ts
Normal 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);
|
@ -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);
|
||||
|
21
src/scripts/container.ts
Normal file
21
src/scripts/container.ts
Normal 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();
|
@ -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);
|
||||
});
|
||||
// class UserChange extends User {
|
||||
// static async syncUser() {
|
||||
// await UserChange.sync({ alter: true, logging: false });
|
||||
// console.log('sync user done');
|
||||
// }
|
||||
// }
|
||||
|
Loading…
x
Reference in New Issue
Block a user