Skip to content

Commit 030d06b

Browse files
committed
feat: add session-auth resource route for org feature flags
1 parent 2d480a5 commit 030d06b

1 file changed

Lines changed: 100 additions & 0 deletions

File tree

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/server-runtime";
2+
import { json } from "@remix-run/server-runtime";
3+
import { z } from "zod";
4+
import { prisma } from "~/db.server";
5+
import { requireUser } from "~/services/session.server";
6+
import {
7+
flags as getGlobalFlags,
8+
validatePartialFeatureFlags,
9+
getAllFlagControlTypes,
10+
} from "~/v3/featureFlags.server";
11+
12+
const ParamsSchema = z.object({
13+
orgId: z.string(),
14+
});
15+
16+
export async function loader({ request, params }: LoaderFunctionArgs) {
17+
const user = await requireUser(request);
18+
if (!user.admin) {
19+
throw new Response("Unauthorized", { status: 403 });
20+
}
21+
22+
const { orgId } = ParamsSchema.parse(params);
23+
24+
const organization = await prisma.organization.findUnique({
25+
where: { id: orgId },
26+
select: {
27+
id: true,
28+
title: true,
29+
slug: true,
30+
featureFlags: true,
31+
},
32+
});
33+
34+
if (!organization) {
35+
throw new Response("Organization not found", { status: 404 });
36+
}
37+
38+
const orgFlagsResult = organization.featureFlags
39+
? validatePartialFeatureFlags(organization.featureFlags as Record<string, unknown>)
40+
: ({ success: false } as const);
41+
42+
const orgFlags = orgFlagsResult.success ? orgFlagsResult.data : {};
43+
const globalFlags = await getGlobalFlags();
44+
const controlTypes = getAllFlagControlTypes();
45+
46+
return json({
47+
org: {
48+
id: organization.id,
49+
title: organization.title,
50+
slug: organization.slug,
51+
},
52+
orgFlags,
53+
globalFlags,
54+
controlTypes,
55+
});
56+
}
57+
58+
export async function action({ request, params }: ActionFunctionArgs) {
59+
const user = await requireUser(request);
60+
if (!user.admin) {
61+
throw new Response("Unauthorized", { status: 403 });
62+
}
63+
64+
const { orgId } = ParamsSchema.parse(params);
65+
66+
const organization = await prisma.organization.findUnique({
67+
where: { id: orgId },
68+
select: { id: true },
69+
});
70+
71+
if (!organization) {
72+
throw new Response("Organization not found", { status: 404 });
73+
}
74+
75+
const body = await request.json();
76+
77+
// body is the full overrides object (or null to clear all)
78+
if (body === null || (typeof body === "object" && Object.keys(body).length === 0)) {
79+
await prisma.organization.update({
80+
where: { id: orgId },
81+
data: { featureFlags: null },
82+
});
83+
return json({ success: true });
84+
}
85+
86+
const validationResult = validatePartialFeatureFlags(body as Record<string, unknown>);
87+
if (!validationResult.success) {
88+
return json(
89+
{ error: "Invalid feature flags", details: validationResult.error.issues },
90+
{ status: 400 }
91+
);
92+
}
93+
94+
await prisma.organization.update({
95+
where: { id: orgId },
96+
data: { featureFlags: validationResult.data },
97+
});
98+
99+
return json({ success: true });
100+
}

0 commit comments

Comments
 (0)