Files
code-center/src/auth/models/org.ts
2025-12-04 10:31:37 +08:00

185 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { DataTypes, Model, Op, Sequelize } from 'sequelize';
import { useContextKey } from '@kevisual/context';
import { SyncOpts, User } from './user.ts';
type AddUserOpts = {
role: string;
};
export enum OrgRole {
admin = 'admin',
member = 'member',
owner = 'owner',
}
export class Org extends Model {
declare id: string;
declare username: string;
declare description: string;
declare users: { role: string; uid: string }[];
/**
* operateId 是真实操作者的id
* @param user
* @param opts
*/
async addUser(user: User, opts?: { operateId?: string; role: string; needPermission?: boolean; isAdmin?: boolean }) {
const hasUser = this.users.find((u) => u.uid === user.id);
if (hasUser) {
return;
}
if (user.type !== 'user') {
throw Error('Only user can be added to org');
}
if (opts?.needPermission) {
if (opts?.isAdmin) {
} else {
const adminUsers = this.users.filter((u) => u.role === 'admin' || u.role === 'owner');
const adminIds = adminUsers.map((u) => u.uid);
const hasPermission = adminIds.includes(opts.operateId);
if (!hasPermission) {
throw Error('No permission');
}
}
}
try {
await user.expireOrgs();
} catch (e) {
console.error('expireOrgs', e);
}
const users = [...this.users];
if (opts?.role === 'owner') {
const orgOwner = users.find((u) => u.role === 'owner');
if (opts.isAdmin) {
} else {
if (!opts.operateId) {
throw Error('operateId is required');
}
const owner = await User.findByPk(opts?.operateId);
if (!owner) {
throw Error('operateId is not found');
}
if (orgOwner?.uid !== owner.id) {
throw Error('No permission');
}
}
if (orgOwner) {
orgOwner.role = 'admin';
}
users.push({ role: 'owner', uid: user.id });
} else {
users.push({ role: opts?.role || 'member', uid: user.id });
}
await Org.update({ users }, { where: { id: this.id } });
}
/**
* operateId 是真实操作者的id
* @param user
* @param opts
*/
async removeUser(user: User, opts?: { operateId?: string; needPermission?: boolean; isAdmin?: boolean }) {
if (opts?.needPermission) {
if (opts?.isAdmin) {
} else {
const adminUsers = this.users.filter((u) => u.role === 'admin' || u.role === 'owner');
const adminIds = adminUsers.map((u) => u.uid);
const hasPermission = adminIds.includes(opts.operateId);
if (!hasPermission) {
throw Error('No permission');
}
}
}
await user.expireOrgs();
const users = this.users.filter((u) => u.uid !== user.id || u.role === 'owner');
await Org.update({ users }, { where: { id: this.id } });
}
/**
* operateId 是真实操作者的id
* @param user
* @param opts
*/
async getUsers(opts?: { operateId: string; needPermission?: boolean; isAdmin?: boolean }) {
const usersIds = this.users.map((u) => u.uid);
const orgUser = this.users;
if (opts?.needPermission) {
// 不在组织内或者不是管理员,如果需要权限,返回空
if (opts.isAdmin) {
} else {
const hasPermission = usersIds.includes(opts.operateId);
if (!hasPermission) {
return {
hasPermission: false,
users: [],
};
}
}
}
const _users = await User.findAll({
where: {
id: {
[Op.in]: usersIds,
},
},
});
const users = _users.map((u) => {
const role = orgUser.find((r) => r.uid === u.id)?.role;
return {
id: u.id,
username: u.username,
role: role,
};
});
return { users };
}
/**
* 检测用户是否在组织内且角色为role
* @param user
* @param opts
*/
async getInRole(userId: string, role = 'admin') {
const user = this.users.find((u) => u.uid === userId && u.role === role);
return !!user;
}
}
/**
* 组织模型在sequelize之后初始化
*/
export const OrgInit = async (newSequelize?: any, tableName?: string, sync?: SyncOpts) => {
const sequelize = useContextKey<Sequelize>('sequelize');
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: newSequelize || sequelize,
modelName: tableName || 'cf_org',
paranoid: true,
},
);
if (sync) {
await Org.sync({ alter: true, logging: false, ...sync }).catch((e) => {
console.error('Org sync', e);
});
return Org;
}
return Org;
};
export const OrgModel = useContextKey('OrgModel', () => Org);