diff --git a/app/features/authentication/routes/login.tsx b/app/features/authentication/routes/login.tsx index ac9f149..eca4cd8 100644 --- a/app/features/authentication/routes/login.tsx +++ b/app/features/authentication/routes/login.tsx @@ -23,6 +23,9 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { + // Vérifier que le sous-domaine correspond à une assemblée existante + await resolveCongregationFromRequest(request) + const shouldStartSetup = await needSetupProcess() if (shouldStartSetup) { return redirect(process.env.MULTI_TENANT === 'true' ? '/register' : '/setup') diff --git a/app/features/authentication/routes/password-forgot.tsx b/app/features/authentication/routes/password-forgot.tsx index 83f639b..10ac675 100644 --- a/app/features/authentication/routes/password-forgot.tsx +++ b/app/features/authentication/routes/password-forgot.tsx @@ -3,7 +3,7 @@ import { data, Form, Link, redirect } from 'react-router' import { createPasswordResetToken } from '~/features/authentication/server/invalidate-user-password.server' import { sendResetUserPasswordEmail } from '~/features/authentication/server/send-reset-user-password-email.server' import { commitSession, getSession } from '~/features/authentication/server/session.server' -import { getBrandingName, resolveCongregation } from '~/shared/libs/congregation.server' +import { getBrandingName, resolveCongregation, resolveCongregationFromRequest } from '~/shared/libs/congregation.server' import { unscopedDb as db } from '~/shared/libs/db.server' import { Alert, AlertDescription } from '~/shared/ui/alert' import { Button } from '~/shared/ui/button' @@ -18,6 +18,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request }: Route.LoaderArgs) { + await resolveCongregationFromRequest(request) + const session = await getSession(request.headers.get('Cookie')) const brandingName = await getBrandingName(request) diff --git a/app/features/authentication/routes/password-reset.tsx b/app/features/authentication/routes/password-reset.tsx index 3d92047..cea7cd9 100644 --- a/app/features/authentication/routes/password-reset.tsx +++ b/app/features/authentication/routes/password-reset.tsx @@ -1,6 +1,6 @@ import { Form, redirect } from 'react-router' -import { getBrandingName } from '~/shared/libs/congregation.server' +import { getBrandingName, resolveCongregationFromRequest } from '~/shared/libs/congregation.server' import { consumePasswordResetToken, verifyPasswordResetToken, @@ -19,6 +19,8 @@ export const meta: Route.MetaFunction = () => { } export async function loader({ request, params }: Route.LoaderArgs) { + await resolveCongregationFromRequest(request) + const user = await verifyPasswordResetToken(params.userHash ?? '') if (user == null) { diff --git a/app/features/authorization/server/verify-role.server.test.ts b/app/features/authorization/server/verify-role.server.test.ts index 965bfe6..d5524e9 100644 --- a/app/features/authorization/server/verify-role.server.test.ts +++ b/app/features/authorization/server/verify-role.server.test.ts @@ -11,6 +11,7 @@ vi.mock('~/shared/libs/db.server', () => ({ }, congregationContext: { getStore: vi.fn(), + enterWith: vi.fn(), }, })) diff --git a/app/features/authorization/server/verify-role.server.ts b/app/features/authorization/server/verify-role.server.ts index 6567702..dfecd95 100644 --- a/app/features/authorization/server/verify-role.server.ts +++ b/app/features/authorization/server/verify-role.server.ts @@ -31,6 +31,8 @@ export async function verifyRole(request: Request, roleKey: Role) { }) if (adminRole != null) { + // Restaurer le contexte ALS — les requêtes unscopedDb via l'adaptateur pg le cassent + congregationContext.enterWith({ congregationId }) return true } @@ -42,5 +44,7 @@ export async function verifyRole(request: Request, roleKey: Role) { }, }) + // Restaurer le contexte ALS — les requêtes unscopedDb via l'adaptateur pg le cassent + congregationContext.enterWith({ congregationId }) return role != null } diff --git a/app/shared/libs/db.server.ts b/app/shared/libs/db.server.ts index 5dd4279..ea10bb7 100644 --- a/app/shared/libs/db.server.ts +++ b/app/shared/libs/db.server.ts @@ -46,12 +46,15 @@ const UPDATE_OPERATIONS = new Set(['update', 'updateMany', 'upsert', 'delete', ' // Tenant-scoped client — auto-injects congregationId on all scoped models const db = unscopedDb.$extends({ query: { - $allOperations({ model, operation, args, query }) { + async $allOperations({ model, operation, args, query }) { if (!model || !SCOPED_MODELS.has(model)) return query(args) const ctx = congregationContext.getStore() if (!ctx) { - return query(args) + throw new Error( + `Congregation context is required for ${model}.${operation} but was not set. ` + + 'Use unscopedDb for global operations or ensure verifySession() was called.', + ) } const { congregationId } = ctx @@ -75,7 +78,13 @@ const db = unscopedDb.$extends({ } } - return query(args) + const result = await query(args) + + // Restaurer le contexte ALS après la requête — l'adaptateur pg de Prisma 7 + // peut casser la propagation d'AsyncLocalStorage lors des opérations async. + congregationContext.enterWith(ctx) + + return result }, }, })