Add routes and fix mappers and migration scripts

This commit is contained in:
araemer 2026-02-22 08:26:20 +01:00
parent 58a581fbac
commit 801739ea30
11 changed files with 184 additions and 97 deletions

View file

@ -1,44 +1,78 @@
import { MigrationInterface, QueryRunner } from "typeorm";
/**
* Migration to convert role column from varchar to enum
* Converts the `role` column on the `user` table from varchar to a proper
* PostgreSQL enum type.
*
* File name should be: TIMESTAMP-ConvertRoleToEnum.ts
* Example: 1701234567890-ConvertRoleToEnum.ts
* Existing rows are safe: the varchar values 'user' and 'admin' inserted by
* CreateUserTable1661234567890 match the enum members exactly, so the USING
* cast succeeds without any data transformation.
*
* Place in: src/migrations/
* down() reverts cleanly by casting back to text and dropping the enum type.
*/
export class ConvertRoleToEnum1701234567890 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
// Create the enum type if it doesn't already exist
await queryRunner.query(`
DO $$ BEGIN
CREATE TYPE user_role_enum AS ENUM ('user', 'admin');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`);
DO $$ BEGIN
CREATE TYPE user_role_enum AS ENUM ('user', 'admin');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`);
// Ensure any existing role values that differ only by whitespace or
// casing are normalised before the cast
await queryRunner.query(`
ALTER TABLE "user"
ALTER COLUMN "role" TYPE user_role_enum
USING "role"::user_role_enum;
`);
UPDATE "user"
SET "role" = LOWER(TRIM("role"))
WHERE "role" IS NOT NULL;
`);
// Drop the varchar default before retyping — PostgreSQL cannot cast it
// automatically and will reject the ALTER COLUMN otherwise
await queryRunner.query(`
ALTER TABLE "user"
ALTER COLUMN "role" SET DEFAULT 'user';
`);
ALTER TABLE "user"
ALTER COLUMN "role" DROP DEFAULT;
`);
// Convert the column — existing 'user' and 'admin' values cast directly
await queryRunner.query(`
ALTER TABLE "user"
ALTER COLUMN "role" TYPE user_role_enum
USING "role"::user_role_enum;
`);
// Re-apply the default using the enum literal
await queryRunner.query(`
ALTER TABLE "user"
ALTER COLUMN "role" SET DEFAULT 'user'::user_role_enum;
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
// Drop the enum default first so the column can be retyped
await queryRunner.query(`
ALTER TABLE "user"
ALTER COLUMN "role" TYPE varchar
USING "role"::text;
`);
ALTER TABLE "user"
ALTER COLUMN "role" DROP DEFAULT;
`);
// Cast enum values back to plain text
await queryRunner.query(`
DROP TYPE IF EXISTS user_role_enum;
`);
ALTER TABLE "user"
ALTER COLUMN "role" TYPE varchar
USING "role"::text;
`);
// Restore the original varchar default
await queryRunner.query(`
ALTER TABLE "user"
ALTER COLUMN "role" SET DEFAULT 'user';
`);
// Drop the enum type — only safe once no column references it
await queryRunner.query(`
DROP TYPE IF EXISTS user_role_enum;
`);
}
}

View file

@ -1,4 +1,4 @@
import {MigrationInterface, QueryRunner, Table} from "typeorm";
import {MigrationInterface, QueryRunner, Table, TableUnique} from "typeorm";
/**
* Creates the `tag` table.
@ -15,6 +15,7 @@ export class CreateTagTable1771658108802 implements MigrationInterface {
type: "uuid",
isPrimary: true,
generationStrategy: "uuid",
isGenerated: true,
default: "uuid_generate_v4()",
},
{
@ -38,9 +39,20 @@ export class CreateTagTable1771658108802 implements MigrationInterface {
}),
true // ifNotExists
);
// Add a unique constraint on desciption
await queryRunner.createUniqueConstraint(
"tag",
new TableUnique({
columnNames: ["description"],
name: "UQ_tag_description",
})
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
// Drop the unique constraint first
await queryRunner.dropUniqueConstraint("tag", "UQ_tag_description");
await queryRunner.dropTable("tag", true);
}
}