MCPcopy Create free account
hub / github.com/UsefulSoftwareCo/executor / seedOrgAndAdmin

Function seedOrgAndAdmin

apps/host-selfhost/src/auth/seed.ts:18–89  ·  view source on GitHub ↗
(
  auth: Auth,
  client: Client,
  config: SelfHostConfig,
)

Source from the content-addressed store, hash-verified

16// ---------------------------------------------------------------------------
17
18export const seedOrgAndAdmin = async (
19 auth: Auth,
20 client: Client,
21 config: SelfHostConfig,
22): Promise<{ organizationId: string; organizationName: string }> => {
23 // Idempotent: once the single organization exists, boot is past first-run.
24 // This instance is SINGLE-org, so adopt whatever organization exists rather
25 // than looking it up by slug — matching on slug would silently create a
26 // second org (forking the instance) the first boot after EXECUTOR_ORG_SLUG
27 // changes. A changed slug is a rename of the one org, applied here.
28 // oxlint-disable-next-line executor/no-double-cast -- boundary: the SELECT columns are the schema contract for the Better Auth `organization` row read off the libSQL client
29 const existingOrg = (
30 await client.execute({
31 sql: "SELECT id, name, slug FROM organization ORDER BY createdAt ASC LIMIT 1",
32 args: [],
33 })
34 ).rows[0] as unknown as { id: string; name: string; slug: string } | undefined;
35 if (existingOrg) {
36 if (existingOrg.slug !== config.orgSlug) {
37 await client.execute({
38 sql: "UPDATE organization SET slug = ? WHERE id = ?",
39 args: [config.orgSlug, existingOrg.id],
40 });
41 }
42 return { organizationId: existingOrg.id, organizationName: existingOrg.name };
43 }
44
45 // Headless bootstrap: when BOTH admin email and password are set, pre-create
46 // that admin as the org owner (CI / infra-as-code). Otherwise fall through to
47 // the turnkey path so the first browser visitor claims the instance.
48 if (config.bootstrapAdminEmail && config.bootstrapAdminPassword) {
49 // oxlint-disable-next-line executor/no-double-cast -- boundary: the SELECT column is the schema contract for the Better Auth `user` row read off the libSQL client
50 const existingUser = (
51 await client.execute({
52 sql: "SELECT id FROM user WHERE email = ?",
53 args: [config.bootstrapAdminEmail],
54 })
55 ).rows[0] as unknown as { id: string } | undefined;
56 let adminId = existingUser?.id;
57 if (!adminId) {
58 const created = await auth.api.createUser({
59 body: {
60 email: config.bootstrapAdminEmail,
61 password: config.bootstrapAdminPassword,
62 name: config.bootstrapAdminName,
63 role: "admin",
64 },
65 });
66 adminId = created.user.id;
67 }
68 // Pass userId so the org is created with no session and the admin becomes
69 // its owner (creates the membership row).
70 const org = await auth.api.createOrganization({
71 body: { name: config.organizationName, slug: config.orgSlug, userId: adminId },
72 });
73 if (!org) {
74 // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: org creation must succeed for a usable instance
75 throw new Error("Failed to create the bootstrap organization");

Callers 1

buildBetterAuthFunction · 0.90

Calls 2

toStringMethod · 0.80
executeMethod · 0.65

Tested by

no test coverage detected