Files
access-manage/src/modules/permissions/permission.repository.ts
T

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]),
);
},
};