173 lines
4.3 KiB
TypeScript
173 lines
4.3 KiB
TypeScript
import type { PoolConnection, RowDataPacket } from "mysql2/promise";
|
|
import { pool } from "../../db/pool";
|
|
|
|
type DbExecutor = typeof pool | PoolConnection;
|
|
|
|
export interface RolePermissionRecord {
|
|
roleId: number;
|
|
roleCode: string;
|
|
roleName: string;
|
|
roleDescription: string | null;
|
|
isSystem: boolean;
|
|
permissions: string[];
|
|
}
|
|
|
|
interface RolePermissionRow extends RowDataPacket {
|
|
role_id: number;
|
|
role_code: string;
|
|
role_name: string;
|
|
role_description: string | null;
|
|
is_system: number;
|
|
permission_code: string | null;
|
|
}
|
|
|
|
interface PermissionCodeRow extends RowDataPacket {
|
|
permission_code: string;
|
|
}
|
|
|
|
function toRolePermissionRecords(
|
|
rows: RolePermissionRow[],
|
|
): RolePermissionRecord[] {
|
|
const records = new Map<number, RolePermissionRecord>();
|
|
|
|
for (const row of rows) {
|
|
const record = records.get(row.role_id) ?? {
|
|
roleId: row.role_id,
|
|
roleCode: row.role_code,
|
|
roleName: row.role_name,
|
|
roleDescription: row.role_description,
|
|
isSystem: row.is_system === 1,
|
|
permissions: [],
|
|
};
|
|
|
|
if (row.permission_code) {
|
|
record.permissions.push(row.permission_code);
|
|
}
|
|
|
|
records.set(row.role_id, record);
|
|
}
|
|
|
|
return [...records.values()];
|
|
}
|
|
|
|
export const permissionRepository = {
|
|
async withTransaction<T>(
|
|
handler: (connection: PoolConnection) => Promise<T>,
|
|
): Promise<T> {
|
|
const connection = await pool.getConnection();
|
|
|
|
try {
|
|
await connection.beginTransaction();
|
|
const result = await handler(connection);
|
|
await connection.commit();
|
|
return result;
|
|
} catch (error) {
|
|
await connection.rollback();
|
|
throw error;
|
|
} finally {
|
|
connection.release();
|
|
}
|
|
},
|
|
|
|
async listRolePermissions(
|
|
db: DbExecutor = pool,
|
|
): Promise<RolePermissionRecord[]> {
|
|
const [rows] = await db.execute<RolePermissionRow[]>(
|
|
`
|
|
SELECT
|
|
r.id AS role_id,
|
|
r.code AS role_code,
|
|
r.name AS role_name,
|
|
r.description AS role_description,
|
|
r.is_system,
|
|
rp.permission_code
|
|
FROM roles r
|
|
LEFT JOIN role_permissions rp
|
|
ON rp.role_id = r.id AND rp.deleted_at IS NULL
|
|
WHERE r.deleted_at IS NULL
|
|
ORDER BY r.id ASC, rp.permission_code ASC
|
|
`,
|
|
);
|
|
|
|
return toRolePermissionRecords(rows);
|
|
},
|
|
|
|
async findRolePermissionsByRoleId(
|
|
roleId: number,
|
|
db: DbExecutor = pool,
|
|
): Promise<RolePermissionRecord | null> {
|
|
const [rows] = await db.execute<RolePermissionRow[]>(
|
|
`
|
|
SELECT
|
|
r.id AS role_id,
|
|
r.code AS role_code,
|
|
r.name AS role_name,
|
|
r.description AS role_description,
|
|
r.is_system,
|
|
rp.permission_code
|
|
FROM roles r
|
|
LEFT JOIN role_permissions rp
|
|
ON rp.role_id = r.id AND rp.deleted_at IS NULL
|
|
WHERE r.id = ?
|
|
AND r.deleted_at IS NULL
|
|
ORDER BY rp.permission_code ASC
|
|
`,
|
|
[roleId],
|
|
);
|
|
|
|
return toRolePermissionRecords(rows)[0] ?? null;
|
|
},
|
|
|
|
async findPermissionCodesByRoleCodes(roleCodes: string[]): Promise<string[]> {
|
|
if (roleCodes.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
const placeholders = roleCodes.map(() => "?").join(", ");
|
|
const [rows] = await pool.execute<PermissionCodeRow[]>(
|
|
`
|
|
SELECT DISTINCT rp.permission_code
|
|
FROM role_permissions rp
|
|
INNER JOIN roles r ON r.id = rp.role_id
|
|
WHERE r.code IN (${placeholders})
|
|
AND r.deleted_at IS NULL
|
|
AND rp.deleted_at IS NULL
|
|
ORDER BY rp.permission_code ASC
|
|
`,
|
|
roleCodes,
|
|
);
|
|
|
|
return rows.map((row) => row.permission_code);
|
|
},
|
|
|
|
async replaceRolePermissions(
|
|
roleId: number,
|
|
permissionCodes: string[],
|
|
db: DbExecutor = pool,
|
|
): Promise<void> {
|
|
await db.execute(
|
|
`
|
|
UPDATE role_permissions
|
|
SET deleted_at = CURRENT_TIMESTAMP(3)
|
|
WHERE role_id = ? AND deleted_at IS NULL
|
|
`,
|
|
[roleId],
|
|
);
|
|
|
|
if (permissionCodes.length === 0) {
|
|
return;
|
|
}
|
|
|
|
await db.execute(
|
|
`
|
|
INSERT INTO role_permissions (role_id, permission_code)
|
|
VALUES ${permissionCodes.map(() => "(?, ?)").join(", ")}
|
|
ON DUPLICATE KEY UPDATE
|
|
deleted_at = NULL,
|
|
created_at = CURRENT_TIMESTAMP(3)
|
|
`,
|
|
permissionCodes.flatMap((permissionCode) => [roleId, permissionCode]),
|
|
);
|
|
},
|
|
};
|