feat: user org and fix bugs

This commit is contained in:
2024-10-08 03:30:01 +08:00
parent 3c7ef0d6e4
commit 54e3ccb3ff
10 changed files with 422 additions and 113 deletions

42
src/models/org.ts Normal file
View File

@@ -0,0 +1,42 @@
import { sequelize } from '../modules/sequelize.ts';
import { DataTypes, Model } from '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.sync({ alter: true, logging: false }).catch((e) => {
console.error('Org sync', e);
});

View File

@@ -1,13 +1,18 @@
import { useConfig } from '@abearxiong/use-config';
import { sequelize } from '@/modules/sequelize.ts';
import { DataTypes, Model } from 'sequelize';
import { DataTypes, Model, Op } from 'sequelize';
import { createToken, checkToken } from '@abearxiong/auth/token';
import { cryptPwd } from '@abearxiong/auth';
import { nanoid } from 'nanoid';
import { CustomError } from '@abearxiong/router';
import { Org } from './org.ts';
import { redis } from '@/app.ts';
const config = useConfig<{ tokenSecret: string }>();
type UserData = {
orgs?: string[];
};
export class User extends Model {
declare id: string;
declare username: string;
@@ -15,12 +20,16 @@ export class User extends Model {
declare salt: string;
declare needChangePassword: boolean;
declare description: string;
declare data: any;
async createToken() {
declare data: UserData;
declare type: string; // user | org
declare owner: string;
declare orgId: string;
declare email: string;
async createToken(uid?: string) {
const { id, username } = this;
const expireTime = 60 * 60 * 24 * 7; // 7 days
const now = new Date().getTime();
const token = await createToken({ id, username }, config.tokenSecret);
const token = await createToken({ id, username, uid }, config.tokenSecret);
return { token, expireTime: now + expireTime };
}
static async verifyToken(token: string) {
@@ -28,8 +37,8 @@ export class User extends Model {
const tokenUser = ct.payload;
return tokenUser;
}
static createUser(username: string, password?: string, description?: string) {
const user = User.findOne({ where: { username } });
static async createUser(username: string, password?: string, description?: string) {
const user = await User.findOne({ where: { username } });
if (user) {
throw new CustomError('User already exists');
}
@@ -37,7 +46,25 @@ export class User extends Model {
let needChangePassword = !password;
password = password || '123456';
const cPassword = cryptPwd(password, salt);
return 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) {
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;
@@ -45,6 +72,44 @@ export class User extends Model {
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,
description: this.description,
needChangePassword: this.needChangePassword,
type: this.type,
orgs,
};
}
async getOrgs() {
const id = this.id;
const cache = await redis.get(`user:${id}:orgs`);
if (cache) {
return JSON.parse(cache);
}
const orgs = await Org.findAll({
order: [['updatedAt', 'DESC']],
where: {
users: {
[Op.contains]: [
{
uid: id,
},
],
},
},
});
const orgNames = orgs.map((org) => org.username);
await redis.set(`user:${id}:orgs`, JSON.stringify(orgNames), 'EX', 60 * 60); // 1 hour
return orgNames;
}
}
User.init(
{
@@ -60,15 +125,29 @@ User.init(
},
password: {
type: DataTypes.STRING,
allowNull: false,
allowNull: true,
},
email: {
type: DataTypes.STRING,
allowNull: true,
},
salt: {
type: DataTypes.STRING,
allowNull: false,
allowNull: true,
},
description: {
type: DataTypes.STRING,
},
type: {
type: DataTypes.STRING,
defaultValue: 'user',
},
owner: {
type: DataTypes.UUID,
},
orgId: {
type: DataTypes.UUID,
},
needChangePassword: {
type: DataTypes.BOOLEAN,
defaultValue: false,