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

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

View File

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