Files
code-center/src/auth/models/org.ts

203 lines
5.8 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 { 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);