mirror of
https://github.com/rajnandan1/kener.git
synced 2026-06-23 04:10:22 +00:00
95 lines
3.1 KiB
TypeScript
95 lines
3.1 KiB
TypeScript
import type { Knex } from "knex";
|
|
|
|
// Maps the legacy users.role string to the new roles.id value.
|
|
// The old default was "user"; everything unmapped falls back to "member".
|
|
const ROLE_MAP: Record<string, string> = {
|
|
admin: "admin",
|
|
editor: "editor",
|
|
member: "member",
|
|
user: "member",
|
|
};
|
|
|
|
export async function up(knex: Knex): Promise<void> {
|
|
const hasColumn = await knex.schema.hasColumn("users", "role");
|
|
if (!hasColumn) return;
|
|
|
|
// 1. Ensure the three target roles exist so FK inserts succeed.
|
|
// Seeds will reconcile permissions later; we only need the rows.
|
|
const rolesToEnsure = [
|
|
{ id: "admin", role_name: "Administrator" },
|
|
{ id: "editor", role_name: "Editor" },
|
|
{ id: "member", role_name: "Member" },
|
|
];
|
|
for (const role of rolesToEnsure) {
|
|
const exists = await knex("roles").where("id", role.id).first();
|
|
if (!exists) {
|
|
await knex("roles").insert({
|
|
id: role.id,
|
|
role_name: role.role_name,
|
|
readonly: 1,
|
|
status: "ACTIVE",
|
|
created_at: knex.fn.now(),
|
|
updated_at: knex.fn.now(),
|
|
});
|
|
}
|
|
}
|
|
|
|
// 2. Read users.role into memory BEFORE dropping the column.
|
|
// On SQLite, dropColumn recreates the table (create → copy → drop → rename),
|
|
// which can discard DML inserts to tables with FKs pointing at users.
|
|
const users: Array<{ id: number; role: string }> = await knex("users").select("id", "role");
|
|
|
|
// 3. Drop the column first.
|
|
await knex.schema.alterTable("users", (table) => {
|
|
table.dropColumn("role");
|
|
});
|
|
|
|
// 4. Now populate users_roles from the in-memory snapshot.
|
|
for (const user of users) {
|
|
const newRoleId = ROLE_MAP[user.role] ?? "member";
|
|
|
|
const alreadyAssigned = await knex("users_roles").where({ roles_id: newRoleId, users_id: user.id }).first();
|
|
|
|
if (!alreadyAssigned) {
|
|
await knex("users_roles").insert({
|
|
roles_id: newRoleId,
|
|
users_id: user.id,
|
|
created_at: knex.fn.now(),
|
|
updated_at: knex.fn.now(),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reverse map: pick the highest-precedence role when backfilling.
|
|
const REVERSE_ROLE_PRECEDENCE: string[] = ["admin", "editor", "member"];
|
|
|
|
export async function down(knex: Knex): Promise<void> {
|
|
const hasColumn = await knex.schema.hasColumn("users", "role");
|
|
if (!hasColumn) {
|
|
await knex.schema.alterTable("users", (table) => {
|
|
table.string("role").defaultTo("member");
|
|
});
|
|
}
|
|
|
|
// Backfill users.role from users_roles using deterministic precedence
|
|
const assignments: Array<{ users_id: number; roles_id: string }> = await knex("users_roles").select(
|
|
"users_id",
|
|
"roles_id",
|
|
);
|
|
|
|
// Group roles by user
|
|
const userRolesMap = new Map<number, string[]>();
|
|
for (const row of assignments) {
|
|
const list = userRolesMap.get(row.users_id) || [];
|
|
list.push(row.roles_id);
|
|
userRolesMap.set(row.users_id, list);
|
|
}
|
|
|
|
// Pick highest-precedence role for each user
|
|
for (const [userId, roleIds] of userRolesMap) {
|
|
const bestRole = REVERSE_ROLE_PRECEDENCE.find((r) => roleIds.includes(r)) || roleIds[0] || "member";
|
|
await knex("users").where("id", userId).update({ role: bestRole });
|
|
}
|
|
}
|