Skip to content

Commit 84c717c

Browse files
committed
fix(webapp): migrate Sessions routes to new createLoaderApiRoute auth shape
The auth-consolidation commit on main rewrote createLoaderApiRoute's authorization contract: - `resource` returns a typed RbacResource ({ type, id }) instead of an untyped record ({ deployments: 'current' } / { sessions: [...] }) - multi-resource auth uses `anyResource(...)` instead of an array literal - `superScopes` is no longer a field — super-scopes are resolved at the JWT ability layer - the `findResource` resolver must return `T | undefined`, not `T | null` Our Sessions PR added two routes (api.v1.deployments.current.ts and realtime.v1.sessions.$session.$io.records.ts) using the pre-consolidation shape. The rebase had no textual conflict because the files didn't exist on main, but typecheck fails because the new contract doesn't accept the old shape. Migrate both routes to match.
1 parent 1020060 commit 84c717c

2 files changed

Lines changed: 8 additions & 6 deletions

File tree

apps/webapp/app/routes/api.v1.deployments.current.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ export const loader = createLoaderApiRoute(
88
corsStrategy: "none",
99
authorization: {
1010
action: "read",
11-
resource: () => ({ deployments: "current" }),
12-
superScopes: ["read:deployments", "read:all", "admin"],
11+
resource: () => ({ type: "deployments", id: "current" }),
1312
},
1413
findResource: async (_params, auth) => {
1514
const promotion = await $replica.workerDeploymentPromotion.findFirst({
@@ -35,7 +34,7 @@ export const loader = createLoaderApiRoute(
3534
},
3635
});
3736

38-
return promotion?.deployment ?? null;
37+
return promotion?.deployment ?? undefined;
3938
},
4039
},
4140
async ({ resource: deployment }) => {

apps/webapp/app/routes/realtime.v1.sessions.$session.$io.records.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
resolveSessionByIdOrExternalId,
99
} from "~/services/realtime/sessions.server";
1010
import { getRealtimeStreamInstance } from "~/services/realtime/v1StreamsGlobal.server";
11-
import { createLoaderApiRoute } from "~/services/routeBuilders/apiBuilder.server";
11+
import { anyResource, createLoaderApiRoute } from "~/services/routeBuilders/apiBuilder.server";
1212

1313
const ParamsSchema = z.object({
1414
session: z.string(),
@@ -58,15 +58,18 @@ const loader = createLoaderApiRoute(
5858
},
5959
authorization: {
6060
action: "read",
61+
// Multi-key: the channel is addressable by the URL key, the row's
62+
// friendlyId, and (if set) externalId. Type-level `read:sessions`
63+
// matches any of them; `read:all` / `admin` bypass via the JWT
64+
// ability's wildcard branches.
6165
resource: ({ row, addressingKey }) => {
6266
const ids = new Set<string>([addressingKey]);
6367
if (row) {
6468
ids.add(row.friendlyId);
6569
if (row.externalId) ids.add(row.externalId);
6670
}
67-
return { sessions: [...ids] };
71+
return anyResource([...ids].map((id) => ({ type: "sessions", id })));
6872
},
69-
superScopes: ["read:sessions", "read:all", "admin"],
7073
},
7174
},
7275
async ({ params, authentication, resource, searchParams }) => {

0 commit comments

Comments
 (0)