import { createContext, redirect, type LoaderFunctionArgs } from 'react-router' import { requestIdContext, userContext } from '@repo/auth/context' import { request as req } from '@repo/util/request' import { CACHE_NAME, CACHE_TTL_SECONDS } from '@/conf' import type { Address } from '@/routes/_.$orgid.enrollments.buy/review' export type Subscription = { billing_day: number payment_method: 'PIX' | 'BANK_SLIP' | 'MANUAL' } export type Workspace = { id: string name: string cnpj: string } export type WorkspaceContextProps = { activeWorkspace: Workspace workspaces: Workspace[] subscription: Subscription | null address: Address | null test_mode: boolean blocked: boolean } type HttpResponse = { items: { sk: string name: string cnpj: string }[] preferred_org_id?: string } export const workspaceContext = createContext() export const workspaceMiddleware = async ( { params, request, context }: LoaderFunctionArgs, next: () => Promise ): Promise => { const orgId = params.orgid const requestId = context.get(requestIdContext) const user = context.get(userContext)! const cacheKey = buildWorkspaceCacheKey(request, user.sub, orgId) const cached = await getWorkspaceFromCache(cacheKey) if (cached) { context.set(workspaceContext, cached) return next() } console.log(`[${new Date().toISOString()}] [${requestId}] Cache miss`) const r = await req({ url: `/users/${user.sub}/orgs?limit=25`, request, context }) if (!r.ok) { throw new Response(await r.text(), { status: r.status }) } const { items, preferred_org_id } = (await r.json()) as HttpResponse const workspaces: Workspace[] = items.map(({ sk, name, cnpj }) => { const [, id] = sk.split('#') return { id, name, cnpj } }) const activeWorkspace = workspaces.find(({ id }) => id === orgId) if (!activeWorkspace) { const url = new URL(request.url) const fallback = preferred_org_id ? (workspaces.find(({ id }) => id === preferred_org_id) ?? workspaces[0]) : workspaces[0] if (!fallback) { throw new Response(null, { status: 403 }) } throw redirect(`/${fallback.id}${url.pathname}`) } const org = (await req({ url: `/orgs/${activeWorkspace.id}`, request, context }).then((r) => r.json())) as any const workspace: WorkspaceContextProps = { activeWorkspace, workspaces, subscription: org?.['subscription'] || null, address: org?.['address'] || null, test_mode: 'test_mode' in org, blocked: 'subscription_frozen' in org } context.set(workspaceContext, workspace) saveToCache(cacheKey, workspace) return await next() } function buildWorkspaceCacheKey( request: Request, userId: string, orgId?: string ) { const url = new URL(request.url) url.pathname = `/__cache/workspace/${userId}/${orgId ?? 'none'}` url.search = '' return new Request(url.toString(), { method: 'GET' }) } async function getWorkspaceFromCache( key: Request ): Promise { const cache: Cache = await caches.open(CACHE_NAME) const cached: Response | undefined = await cache.match(key) if (!cached) { return null } return (await cached.json()) as WorkspaceContextProps } async function saveToCache( key: Request, workspace: WorkspaceContextProps ): Promise { const cache: Cache = await caches.open(CACHE_NAME) const response = new Response(JSON.stringify(workspace), { headers: { 'Content-Type': 'application/json', 'Cache-Control': `public, max-age=${CACHE_TTL_SECONDS}` } }) await cache.put(key, response) }