From e8318d577b3188a916ffa7311ffa0307fa0114a8 Mon Sep 17 00:00:00 2001 From: Nicolas Meienberger Date: Thu, 29 Jan 2026 08:31:44 +0100 Subject: [PATCH] fix: user missing their org on startup --- app/server/modules/lifecycle/migrations.ts | 3 +- .../migrations/00003-assign-organization.ts | 68 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 app/server/modules/lifecycle/migrations/00003-assign-organization.ts diff --git a/app/server/modules/lifecycle/migrations.ts b/app/server/modules/lifecycle/migrations.ts index ddc333be..a4a49a03 100644 --- a/app/server/modules/lifecycle/migrations.ts +++ b/app/server/modules/lifecycle/migrations.ts @@ -1,6 +1,7 @@ import { logger } from "../../utils/logger"; import { v00001 } from "./migrations/00001-retag-snapshots"; import { v00002 } from "./migrations/00002-isolate-restic-passwords"; +import { v00003 } from "./migrations/00003-assign-organization"; import { sql } from "drizzle-orm"; import { eq } from "drizzle-orm"; import { appMetadataTable, usersTable } from "../../db/schema"; @@ -38,7 +39,7 @@ type MigrationEntity = { dependsOn?: string[]; }; -const registry: MigrationEntity[] = [v00001, v00002]; +const registry: MigrationEntity[] = [v00001, v00002, v00003]; export const runMigrations = async () => { const userCount = await db.select({ count: sql`count(*)` }).from(usersTable); diff --git a/app/server/modules/lifecycle/migrations/00003-assign-organization.ts b/app/server/modules/lifecycle/migrations/00003-assign-organization.ts new file mode 100644 index 00000000..5b381101 --- /dev/null +++ b/app/server/modules/lifecycle/migrations/00003-assign-organization.ts @@ -0,0 +1,68 @@ +import { eq } from "drizzle-orm"; +import { db } from "../../../db/db"; +import { member, usersTable } from "../../../db/schema"; +import { logger } from "../../../utils/logger"; +import { toMessage } from "~/server/utils/errors"; + +const execute = async () => { + const errors: Array<{ name: string; error: string }> = []; + + try { + const allUsers = await db.query.usersTable.findMany({ where: eq(usersTable.role, "admin") }); + const allOrganizations = await db.query.organization.findMany({}); + + if (allUsers.length === 0) { + logger.info("No users found, skipping migration"); + return { success: true, errors }; + } + + if (allOrganizations.length === 0) { + logger.info("No organizations found, skipping migration"); + return { success: true, errors }; + } + + if (allUsers.length !== 1) { + logger.info(`Found ${allUsers.length} users, expected exactly 1, skipping migration`); + return { success: true, errors }; + } + + if (allOrganizations.length !== 1) { + logger.info(`Found ${allOrganizations.length} organizations, expected exactly 1, skipping migration`); + return { success: true, errors }; + } + + const user = allUsers[0]; + const org = allOrganizations[0]; + + const existingMembers = await db.query.member.findMany({ + where: (memberTable, { eq }) => eq(memberTable.userId, user.id), + }); + + if (existingMembers.length > 0) { + logger.info(`User ${user.username} already belongs to organization(s), skipping migration`); + return { success: true, errors }; + } + + logger.info(`Assigning user ${user.username} to organization ${org.name}`); + + await db.insert(member).values({ + id: crypto.randomUUID(), + organizationId: org.id, + userId: user.id, + role: "owner", + createdAt: new Date(), + }); + + logger.info(`Successfully assigned user ${user.username} to organization ${org.name}`); + } catch (err) { + errors.push({ name: "assign-organization", error: toMessage(err) }); + } + + return { success: errors.length === 0, errors }; +}; + +export const v00003 = { + execute, + id: "00003-assign-organization", + type: "maintenance" as const, +};