AI Implementation feature(867): Admin Area Players Management (#56)
This commit was merged in pull request #56.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
/**
|
||||
* Deployment-wide safety net for the "always retain at least one admin"
|
||||
* invariant. Even if a future code path bypasses
|
||||
* `UsersService.applyLastAdminSafeMutation`, the database itself aborts
|
||||
* the offending UPDATE/DELETE with a clear `LAST_ADMIN` error message.
|
||||
*
|
||||
* Triggers fire before the row mutation, so the offending statement never
|
||||
* commits. The service-level wrapper still rolls back with 409 LAST_ADMIN
|
||||
* in normal operation; the triggers are the belt-and-braces guard for
|
||||
* cross-instance / out-of-band mistakes.
|
||||
*
|
||||
* Forward-only.
|
||||
*/
|
||||
export class AddUserLastAdminTriggers1700000000800 implements MigrationInterface {
|
||||
name = 'AddUserLastAdminTriggers1700000000800';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// UPDATE trigger: blocks any role mutation that would leave the system
|
||||
// with zero admins. We use WHEN to short-circuit so the trigger body
|
||||
// only fires when (a) the old role was admin, (b) the new role is no
|
||||
// longer admin, and (c) no other admin row exists.
|
||||
await queryRunner.query(`
|
||||
CREATE TRIGGER IF NOT EXISTS "trg_user_last_admin_update"
|
||||
BEFORE UPDATE OF "role" ON "user"
|
||||
FOR EACH ROW
|
||||
WHEN OLD."role" = 'admin' AND NEW."role" <> 'admin'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM "user" AS "other"
|
||||
WHERE "other"."role" = 'admin' AND "other"."id" <> OLD."id"
|
||||
)
|
||||
BEGIN
|
||||
SELECT RAISE(ABORT, 'LAST_ADMIN');
|
||||
END;
|
||||
`);
|
||||
|
||||
// DELETE trigger: blocks deletion of the only remaining admin row.
|
||||
await queryRunner.query(`
|
||||
CREATE TRIGGER IF NOT EXISTS "trg_user_last_admin_delete"
|
||||
BEFORE DELETE ON "user"
|
||||
FOR EACH ROW
|
||||
WHEN OLD."role" = 'admin'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM "user" AS "other"
|
||||
WHERE "other"."role" = 'admin' AND "other"."id" <> OLD."id"
|
||||
)
|
||||
BEGIN
|
||||
SELECT RAISE(ABORT, 'LAST_ADMIN');
|
||||
END;
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
// Forward-only.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user