Skip to content

Commit 73c51ca

Browse files
committed
feat: gate org-creation API behind a flag and complete create-org field parity
1 parent a9ca647 commit 73c51ca

4 files changed

Lines changed: 49 additions & 6 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@trigger.dev/core": patch
3+
---
4+
5+
Add typed schemas for the create-organization management API endpoint.

apps/webapp/app/env.server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ const EnvironmentSchema = z
113113
// agent dark; flip to "1" to enable it for everyone at GA. Per-org overrides
114114
// (org featureFlags) win regardless.
115115
DASHBOARD_AGENT_ENABLED: z.string().default("0"),
116+
// Gates the create-org management API endpoint (default off).
117+
ORG_CREATION_API_ENABLED: z.string().default("0"),
116118
// "1" gives admins/impersonators an everywhere-preview (default off),
117119
// separate from the per-org rollout flag above.
118120
DASHBOARD_AGENT_ADMIN_PREVIEW: z.string().default("0"),

apps/webapp/app/routes/api.v1.orgs.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { json } from "@remix-run/server-runtime";
22
import type { GetOrgsResponseBody } from "@trigger.dev/core/v3";
3-
import { z } from "zod";
3+
import { CreateOrgRequestBody } from "@trigger.dev/core/v3";
44
import { prisma } from "~/db.server";
5+
import { env } from "~/env.server";
56
import { createOrganization } from "~/models/organization.server";
67
import {
78
createActionPATApiRoute,
89
createLoaderPATApiRoute,
910
} from "~/services/routeBuilders/apiBuilder.server";
11+
import { extractDomain, faviconUrl } from "~/utils/favicon";
1012

1113
// Identity-only: lists the caller's own orgs, so no authorization gate.
1214
export const loader = createLoaderPATApiRoute({}, async ({ authentication }) => {
@@ -35,11 +37,6 @@ export const loader = createLoaderPATApiRoute({}, async ({ authentication }) =>
3537
return json(result);
3638
});
3739

38-
const CreateOrgRequestBody = z.object({
39-
title: z.string().min(1),
40-
companySize: z.string().optional(),
41-
});
42-
4340
// No org exists yet, so no authorization gate; any authenticated user can
4441
// create an org and becomes its ADMIN.
4542
export const action = createActionPATApiRoute(
@@ -48,10 +45,34 @@ export const action = createActionPATApiRoute(
4845
body: CreateOrgRequestBody,
4946
},
5047
async ({ body, authentication }) => {
48+
if (env.ORG_CREATION_API_ENABLED !== "1") {
49+
return json({ error: "Not found" }, { status: 404 });
50+
}
51+
52+
// Mirror the dashboard: stash companyUrl/companySize as onboarding data and
53+
// derive the org avatar from the company domain's favicon.
54+
const onboardingData: Record<string, string> = {};
55+
if (body.companyUrl) {
56+
onboardingData.companyUrl = body.companyUrl;
57+
}
58+
if (body.companySize) {
59+
onboardingData.companySize = body.companySize;
60+
}
61+
62+
let avatar: { type: "image"; url: string } | undefined;
63+
if (body.companyUrl) {
64+
const domain = extractDomain(body.companyUrl);
65+
if (domain) {
66+
avatar = { type: "image", url: faviconUrl(domain) };
67+
}
68+
}
69+
5170
const organization = await createOrganization({
5271
title: body.title,
5372
companySize: body.companySize ?? null,
5473
userId: authentication.userId,
74+
onboardingData: Object.keys(onboardingData).length > 0 ? onboardingData : undefined,
75+
avatar,
5576
});
5677

5778
return json(

packages/core/src/v3/schemas/api.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,21 @@ export const GetOrgsResponseBody = z.array(
6767

6868
export type GetOrgsResponseBody = z.infer<typeof GetOrgsResponseBody>;
6969

70+
export const CreateOrgRequestBody = z.object({
71+
title: z.string().trim().min(3).max(50),
72+
companySize: z.string().optional(),
73+
companyUrl: z.string().optional(),
74+
});
75+
export type CreateOrgRequestBody = z.infer<typeof CreateOrgRequestBody>;
76+
77+
export const CreateOrgResponseBody = z.object({
78+
id: z.string(),
79+
title: z.string(),
80+
slug: z.string(),
81+
createdAt: z.coerce.date(),
82+
});
83+
export type CreateOrgResponseBody = z.infer<typeof CreateOrgResponseBody>;
84+
7085
export const CreateProjectRequestBody = z.object({
7186
name: z
7287
.string()

0 commit comments

Comments
 (0)