Download superlog/apps/api/src/org-context.ts from smlflg/Experimente: direct link, hf CLI and curl.
- Browser
- Download file 3.82 kB
-
https://huggingface.co/smlflg/Experimente/resolve/main/superlog/apps/api/src/org-context.ts
- Command line
-
hf download hf://smlflg/Experimente/superlog/apps/api/src/org-context.ts
-
curl -L -o org-context.ts https://huggingface.co/smlflg/Experimente/resolve/main/superlog/apps/api/src/org-context.ts
3.82 kB
| import { db, schema } from "@superlog/db"; | |
| import { and, eq } from "drizzle-orm"; | |
| // Org context resolution backed by Better Auth's session + organization | |
| // plugin. Replaces the old Clerk-driven resolveActiveOrgContext in | |
| // clerk-orgs.ts: identity now comes from our local `users` + `org_members` | |
| // tables, so this is a pure DB read with no upstream sync calls. | |
| type SyncedUser = typeof schema.users.$inferSelect; | |
| type SyncedOrg = typeof schema.orgs.$inferSelect; | |
| type SyncedProject = typeof schema.projects.$inferSelect; | |
| type ResolveActiveOrgOptions = { | |
| userId: string; | |
| preferredOrgId?: string | null; | |
| preferredProjectId?: string | null; | |
| }; | |
| export type ActiveOrgContext = { | |
| user: SyncedUser; | |
| org: SyncedOrg; | |
| project: SyncedProject; | |
| }; | |
| export type MaybeActiveOrgContext = | |
| | ActiveOrgContext | |
| | { user: SyncedUser; org: null; project: null }; | |
| async function ensureProjectForOrg(orgId: string): Promise<SyncedProject> { | |
| const existing = await db.query.projects.findFirst({ | |
| where: eq(schema.projects.orgId, orgId), | |
| }); | |
| if (existing) return existing; | |
| const inserted = await db | |
| .insert(schema.projects) | |
| .values({ orgId, name: "Default", slug: "default" }) | |
| .returning(); | |
| const project = inserted[0]; | |
| if (!project) throw new Error("failed to create default project"); | |
| await db | |
| .insert(schema.projectAutomationSettings) | |
| .values({ projectId: project.id }) | |
| .onConflictDoNothing({ target: schema.projectAutomationSettings.projectId }); | |
| return project; | |
| } | |
| async function resolveProjectForOrg( | |
| orgId: string, | |
| preferredProjectId: string | null, | |
| ): Promise<SyncedProject> { | |
| if (preferredProjectId) { | |
| const candidate = await db.query.projects.findFirst({ | |
| where: and(eq(schema.projects.id, preferredProjectId), eq(schema.projects.orgId, orgId)), | |
| }); | |
| if (candidate) return candidate; | |
| } | |
| return ensureProjectForOrg(orgId); | |
| } | |
| // Returns the user's active org + project, or `org: null, project: null` when | |
| // the user hasn't created their first org yet. Use this on endpoints that | |
| // must keep working for pre-org users (currently only /api/me — every other | |
| // endpoint requires an org and uses resolveActiveOrgContext). | |
| export async function resolveMaybeActiveOrgContext( | |
| options: ResolveActiveOrgOptions, | |
| ): Promise<MaybeActiveOrgContext> { | |
| const user = await db.query.users.findFirst({ where: eq(schema.users.id, options.userId) }); | |
| if (!user) throw new Error(`user ${options.userId} not found`); | |
| const preferredProjectId = options.preferredProjectId ?? user.activeProjectId ?? null; | |
| if (options.preferredOrgId) { | |
| const membership = await db.query.orgMembers.findFirst({ | |
| where: and( | |
| eq(schema.orgMembers.userId, options.userId), | |
| eq(schema.orgMembers.orgId, options.preferredOrgId), | |
| ), | |
| }); | |
| if (membership) { | |
| const org = await db.query.orgs.findFirst({ where: eq(schema.orgs.id, membership.orgId) }); | |
| if (org) { | |
| const project = await resolveProjectForOrg(org.id, preferredProjectId); | |
| return { user, org, project }; | |
| } | |
| } | |
| } | |
| const firstMembership = await db.query.orgMembers.findFirst({ | |
| where: eq(schema.orgMembers.userId, options.userId), | |
| }); | |
| if (!firstMembership) return { user, org: null, project: null }; | |
| const org = await db.query.orgs.findFirst({ where: eq(schema.orgs.id, firstMembership.orgId) }); | |
| if (!org) throw new Error("org membership references missing org"); | |
| const project = await resolveProjectForOrg(org.id, preferredProjectId); | |
| return { user, org, project }; | |
| } | |
| export async function resolveActiveOrgContext( | |
| options: ResolveActiveOrgOptions, | |
| ): Promise<ActiveOrgContext> { | |
| const ctx = await resolveMaybeActiveOrgContext(options); | |
| if (!ctx.org) throw new Error("no organization access"); | |
| return ctx; | |
| } | |