203 lines
5.8 KiB
TypeScript
203 lines
5.8 KiB
TypeScript
import { useContextKey } from '@kevisual/context';
|
||
import { User } from './user.ts';
|
||
import { db } from '../../modules/db.ts';
|
||
import { cfOrgs, cfUser } from '../../db/drizzle/schema.ts';
|
||
import { eq, inArray, sql, InferSelectModel, InferInsertModel } from 'drizzle-orm';
|
||
|
||
const orgsTable = cfOrgs;
|
||
const usersTable = cfUser;
|
||
|
||
export enum OrgRole {
|
||
admin = 'admin',
|
||
member = 'member',
|
||
owner = 'owner',
|
||
}
|
||
|
||
export type OrgUser = {
|
||
role: string;
|
||
uid: string;
|
||
};
|
||
|
||
export type OrgSelect = InferSelectModel<typeof cfOrgs>;
|
||
export type OrgInsert = InferInsertModel<typeof cfOrgs>;
|
||
|
||
export class Org {
|
||
id: string;
|
||
username: string;
|
||
description: string;
|
||
users: OrgUser[];
|
||
|
||
constructor(data: OrgSelect) {
|
||
Object.assign(this, data);
|
||
}
|
||
/**
|
||
* 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 db.update(orgsTable).set({ users }).where(eq(orgsTable.id, this.id));
|
||
this.users = users;
|
||
}
|
||
/**
|
||
* 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 db.update(orgsTable).set({ users }).where(eq(orgsTable.id, this.id));
|
||
this.users = users;
|
||
}
|
||
/**
|
||
* 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 db.select().from(usersTable).where(inArray(usersTable.id, 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;
|
||
}
|
||
|
||
/**
|
||
* 根据主键查找
|
||
*/
|
||
static async findByPk(id: string): Promise<Org | null> {
|
||
const orgs = await db.select().from(orgsTable).where(eq(orgsTable.id, id)).limit(1);
|
||
return orgs.length > 0 ? new Org(orgs[0]) : null;
|
||
}
|
||
|
||
/**
|
||
* 根据条件查找一个
|
||
*/
|
||
static async findOne(where: { username?: string; id?: string }): Promise<Org | null> {
|
||
let query = db.select().from(orgsTable);
|
||
|
||
if (where.username) {
|
||
query = query.where(eq(orgsTable.username, where.username)) as any;
|
||
} else if (where.id) {
|
||
query = query.where(eq(orgsTable.id, where.id)) as any;
|
||
}
|
||
|
||
const orgs = await query.limit(1);
|
||
return orgs.length > 0 ? new Org(orgs[0]) : null;
|
||
}
|
||
|
||
/**
|
||
* 创建组织
|
||
*/
|
||
static async create(data: { username: string; description?: string; users: OrgUser[] }): Promise<Org> {
|
||
const insertData: any = {
|
||
username: data.username,
|
||
users: data.users,
|
||
};
|
||
|
||
if (data.description !== undefined && data.description !== null) {
|
||
insertData.description = data.description;
|
||
}
|
||
|
||
const inserted = await db.insert(orgsTable).values(insertData).returning();
|
||
return new Org(inserted[0]);
|
||
}
|
||
|
||
/**
|
||
* 更新组织
|
||
*/
|
||
static async update(data: Partial<OrgInsert>, where: { id: string }) {
|
||
await db.update(orgsTable).set(data).where(eq(orgsTable.id, where.id));
|
||
}
|
||
}
|
||
|
||
export const OrgModel = useContextKey('OrgModel', () => Org);
|