diff --git a/pb_migrations/1781000000_team_members_to_auth.js b/pb_migrations/1781000000_team_members_to_auth.js index 7283571..57b91c7 100644 --- a/pb_migrations/1781000000_team_members_to_auth.js +++ b/pb_migrations/1781000000_team_members_to_auth.js @@ -19,7 +19,42 @@ // re-configure the provider from the PocketBase admin UI (Settings → Auth // providers) or ship a follow-up migration. migrate((app) => { - // 1. Drop the old PIN-based collection (takes the PIN records with it). + // 1. Detach relation fields that point at team_members. PocketBase refuses to + // delete a collection that other collections relate to, so before we can + // drop the old PIN-based team_members we convert those relations into plain + // text fields (the id is stored as text — consistent with how user_id is + // stored in test_results / on_demand_attempts). Per-user progress is reset + // anyway, so dropping the FK constraint here is acceptable. + const deps = [ + { name: "micro_learning_completions", relId: "rel_team_member_id", textId: "txt_mlc_team_member" }, + { name: "theme_session_completions", relId: "rel_tsc_team_member", textId: "txt_tsc_team_member" }, + ]; + for (const dep of deps) { + let c; + try { c = app.findCollectionByNameOrId(dep.name); } catch (_) { continue; } + // Drop any index that references the team_member_id column. + c.indexes = (c.indexes || []).filter((idx) => idx.indexOf("team_member_id") === -1); + // Replace the relation field with a text field of the same name. + c.fields.removeById(dep.relId); + c.fields.add(new Field({ + type: "text", + id: dep.textId, + name: "team_member_id", + required: false, + min: 0, + max: 0, + })); + app.save(c); + } + + // Recreate the unique (team_member_id, session_week) guard on the text column. + try { + const tsc = app.findCollectionByNameOrId("theme_session_completions"); + tsc.indexes.push("CREATE UNIQUE INDEX `idx_theme_session_completions_user_week` ON `theme_session_completions` (`team_member_id`, `session_week`)"); + app.save(tsc); + } catch (_) { /* collection missing — skip */ } + + // 2. Drop the old PIN-based team_members (now unreferenced). try { const old = app.findCollectionByNameOrId("team_members"); app.delete(old); @@ -28,6 +63,28 @@ migrate((app) => { } const tenant = $os.getenv("ENTRA_TENANT_ID") || "common"; + const clientId = $os.getenv("ENTRA_CLIENT_ID"); + const clientSecret = $os.getenv("ENTRA_CLIENT_SECRET"); + + // Only configure the OIDC provider when credentials are actually present. + // PocketBase rejects an enabled OAuth2 provider with an empty clientId / + // clientSecret, which would abort this migration and prevent PocketBase from + // starting at all (502 on every /api call). When the secrets are absent the + // collection is created without a provider; once the ENTRA_* secrets are set, + // configure the provider via the PocketBase admin UI + // (Settings → Auth providers → OIDC) or re-apply this migration. + const oidcProviders = (clientId && clientSecret) ? [ + { + name: "oidc", + clientId: clientId, + clientSecret: clientSecret, + authURL: "https://login.microsoftonline.com/" + tenant + "/oauth2/v2.0/authorize", + tokenURL: "https://login.microsoftonline.com/" + tenant + "/oauth2/v2.0/token", + userInfoURL: "https://graph.microsoft.com/oidc/userinfo", + displayName: "Microsoft", + pkce: true, + }, + ] : []; // 2. Recreate `team_members` as an auth collection (same name → all existing // `user_id` references in test_results, on_demand_attempts, @@ -56,7 +113,7 @@ migrate((app) => { mfa: { enabled: false, duration: 600, rule: "" }, oauth2: { - enabled: true, + enabled: oidcProviders.length > 0, // Map the OIDC userinfo claims onto our fields. mappedFields: { id: "", @@ -64,18 +121,7 @@ migrate((app) => { username: "", avatarURL: "", }, - providers: [ - { - name: "oidc", - clientId: $os.getenv("ENTRA_CLIENT_ID"), - clientSecret: $os.getenv("ENTRA_CLIENT_SECRET"), - authURL: "https://login.microsoftonline.com/" + tenant + "/oauth2/v2.0/authorize", - tokenURL: "https://login.microsoftonline.com/" + tenant + "/oauth2/v2.0/token", - userInfoURL: "https://graph.microsoft.com/oidc/userinfo", - displayName: "Microsoft", - pkce: true, - }, - ], + providers: oidcProviders, }, fields: [