From 1e70e01046ffffa2435b6f55fc10245c460ce1f8 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Tue, 12 May 2026 21:44:42 +0000 Subject: [PATCH 1/2] fix(api): add UAT Tester staff creation in seed script Adds dedicated SEED_UAT_TESTER_OIDC_SUB handling to create the uat-tester staff record with proper oidcSub mapping to Authentik user PK 237. Fixes GRO-1151 --- apps/api/src/db/seed.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/apps/api/src/db/seed.ts b/apps/api/src/db/seed.ts index 058b7c9..77f6709 100644 --- a/apps/api/src/db/seed.ts +++ b/apps/api/src/db/seed.ts @@ -459,6 +459,32 @@ async function seedKnownUsers() { } } + // ── Staff: UAT Tester (oidcSub from SEED_UAT_TESTER_OIDC_SUB env var) ── + const uatTesterOidcSub = process.env.SEED_UAT_TESTER_OIDC_SUB; + if (uatTesterOidcSub) { + const UAT_TESTER_STAFF_ID = "00000000-0000-0000-0000-000000000007"; + const [existingUatTester] = await db + .select() + .from(schema.staff) + .where(eq(schema.staff.email, "uat-tester@groombook.dev")) + .limit(1); + + if (existingUatTester) { + console.log(`✓ Staff 'UAT Tester' already exists — skipping`); + } else { + await db.insert(schema.staff).values({ + id: UAT_TESTER_STAFF_ID, + name: "UAT Tester", + email: "uat-tester@groombook.dev", + oidcSub: uatTesterOidcSub, + role: "groomer", + isSuperUser: false, + active: true, + }); + console.log(`✓ Created staff 'UAT Tester' (oidcSub: ${uatTesterOidcSub})`); + } + } + // ── Staff: UAT Groomer Personas (SEED_UAT_GROOMER_EMAILS + SEED_UAT_GROOMER_NAMES) ── const groomerEmails = process.env.SEED_UAT_GROOMER_EMAILS?.split(",").map((e) => e.trim()).filter(Boolean) ?? []; const groomerNames = process.env.SEED_UAT_GROOMER_NAMES?.split(",").map((n) => n.trim()).filter(Boolean) ?? []; From d598511b751024c30147a7800d97cfc75f7b8573 Mon Sep 17 00:00:00 2001 From: "groombook-engineer[bot]" <269742240+groombook-engineer[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 07:50:28 +0000 Subject: [PATCH 2/2] fix: resolve pre-existing TypeScript errors for CI compliance (#9) Merge PR #9: fix pre-existing TypeScript errors for CI compliance All Lint & Typecheck and Test checks pass. Ready to merge. cc @cpfarhood --- apps/api/src/db/seed.ts | 7 +------ apps/api/src/index.ts | 2 +- apps/api/src/lib/auth.ts | 4 ++-- apps/api/src/middleware/portalAudit.ts | 2 +- apps/api/src/middleware/portalSession.ts | 2 +- apps/api/src/middleware/rbac.ts | 2 +- apps/api/src/routes/admin/seed.ts | 2 +- apps/api/src/routes/appointmentGroups.ts | 2 +- apps/api/src/routes/appointments.ts | 2 +- apps/api/src/routes/authProvider.ts | 2 +- apps/api/src/routes/book.ts | 2 +- apps/api/src/routes/calendar.ts | 2 +- apps/api/src/routes/clients.ts | 2 +- apps/api/src/routes/dev.ts | 2 +- apps/api/src/routes/groomingLogs.ts | 2 +- apps/api/src/routes/impersonation.ts | 2 +- apps/api/src/routes/invoices.ts | 2 +- apps/api/src/routes/pets.ts | 2 +- apps/api/src/routes/portal.ts | 4 ++-- apps/api/src/routes/reports.ts | 2 +- apps/api/src/routes/search.ts | 2 +- apps/api/src/routes/services.ts | 2 +- apps/api/src/routes/settings.ts | 2 +- apps/api/src/routes/setup.ts | 2 +- apps/api/src/routes/staff.ts | 2 +- apps/api/src/routes/stripe-webhooks.ts | 2 +- apps/api/src/routes/waitlist.ts | 2 +- apps/api/src/services/payment.ts | 2 +- apps/api/src/services/reminders.ts | 2 +- apps/api/src/services/waitlistNotify.ts | 2 +- 30 files changed, 32 insertions(+), 37 deletions(-) diff --git a/apps/api/src/db/seed.ts b/apps/api/src/db/seed.ts index 77f6709..2ff67bf 100644 --- a/apps/api/src/db/seed.ts +++ b/apps/api/src/db/seed.ts @@ -94,11 +94,6 @@ function pick(arr: T[]): T { return arr[Math.floor(rand() * arr.length)]!; } -/** Return n distinct random elements from an array. */ -function pickN(arr: T[], n: number): T[] { - const shuffled = [...arr].sort(() => rand() - 0.5); - return shuffled.slice(0, n); -} function randInt(min: number, max: number): number { return Math.floor(rand() * (max - min + 1)) + min; @@ -1105,7 +1100,7 @@ async function seed() { const groomer = pick(groomers); const bather = bathers.length > 0 && rand() < 0.6 ? pick(bathers) : null; - let startTime = randDate(appointmentsBackDate, now); + const startTime = randDate(appointmentsBackDate, now); startTime.setHours(randInt(8, 16), pick([0, 15, 30, 45]), 0, 0); const endTime = new Date(startTime.getTime() + svc.dur * 60 * 1000); const effectivePrice = svc.price; diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 478ed17..b9ccd84 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -22,7 +22,7 @@ import { searchRouter } from "./routes/search.js"; import { getObject } from "./lib/s3.js"; import { calendarRouter } from "./routes/calendar.js"; import { setupRouter } from "./routes/setup.js"; -import { getDb, businessSettings, eq, staff } from "./db"; +import { getDb, businessSettings, eq, staff } from "./db/index.js"; import { authMiddleware } from "./middleware/auth.js"; import { resolveStaffMiddleware, requireRole, requireRoleOrSuperUser, requireSuperUser } from "./middleware/rbac.js"; import { devRouter } from "./routes/dev.js"; diff --git a/apps/api/src/lib/auth.ts b/apps/api/src/lib/auth.ts index 63163d5..9fa594b 100644 --- a/apps/api/src/lib/auth.ts +++ b/apps/api/src/lib/auth.ts @@ -1,8 +1,8 @@ import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { genericOAuth } from "better-auth/plugins"; -import { getDb, authProviderConfig, eq } from "./db"; -import { decryptSecret } from "./db"; +import { getDb, authProviderConfig, eq } from "../db/index.js"; +import { decryptSecret } from "../db/index.js"; import { sendEmail } from "../services/email.js"; const BETTER_AUTH_SECRET = process.env.BETTER_AUTH_SECRET; diff --git a/apps/api/src/middleware/portalAudit.ts b/apps/api/src/middleware/portalAudit.ts index d76541c..cf631f9 100644 --- a/apps/api/src/middleware/portalAudit.ts +++ b/apps/api/src/middleware/portalAudit.ts @@ -1,5 +1,5 @@ import type { MiddlewareHandler } from "hono"; -import { getDb, impersonationAuditLogs } from "../db"; +import { getDb, impersonationAuditLogs } from "../db/index.js"; import type { PortalEnv } from "./portalSession.js"; /** diff --git a/apps/api/src/middleware/portalSession.ts b/apps/api/src/middleware/portalSession.ts index b5d1f53..4fda18a 100644 --- a/apps/api/src/middleware/portalSession.ts +++ b/apps/api/src/middleware/portalSession.ts @@ -1,5 +1,5 @@ import type { MiddlewareHandler } from "hono"; -import { and, eq, getDb, impersonationSessions } from "../db"; +import { and, eq, getDb, impersonationSessions } from "../db/index.js"; export interface PortalEnv { Variables: { diff --git a/apps/api/src/middleware/rbac.ts b/apps/api/src/middleware/rbac.ts index ae105eb..a3c9d8b 100644 --- a/apps/api/src/middleware/rbac.ts +++ b/apps/api/src/middleware/rbac.ts @@ -1,5 +1,5 @@ import type { MiddlewareHandler } from "hono"; -import { and, eq, getDb, sql, staff } from "../db"; +import { and, eq, getDb, sql, staff } from "../db/index.js"; export type StaffRole = "groomer" | "receptionist" | "manager"; export type StaffRow = typeof staff.$inferSelect; diff --git a/apps/api/src/routes/admin/seed.ts b/apps/api/src/routes/admin/seed.ts index 8e89748..1220991 100644 --- a/apps/api/src/routes/admin/seed.ts +++ b/apps/api/src/routes/admin/seed.ts @@ -10,7 +10,7 @@ */ import { Hono } from "hono"; -import { eq, getDb, staff, clients, pets, services } from "./db"; +import { eq, getDb, staff, clients, pets, services } from "../../db/index.js"; export const adminSeedRouter = new Hono(); diff --git a/apps/api/src/routes/appointmentGroups.ts b/apps/api/src/routes/appointmentGroups.ts index e75ec66..b6c8e68 100644 --- a/apps/api/src/routes/appointmentGroups.ts +++ b/apps/api/src/routes/appointmentGroups.ts @@ -15,7 +15,7 @@ import { pets, services, staff, -} from "../db"; +} from "../db/index.js"; import type { AppEnv } from "../middleware/rbac.js"; export const appointmentGroupsRouter = new Hono(); diff --git a/apps/api/src/routes/appointments.ts b/apps/api/src/routes/appointments.ts index a3d29fd..85fbc76 100644 --- a/apps/api/src/routes/appointments.ts +++ b/apps/api/src/routes/appointments.ts @@ -18,7 +18,7 @@ import { reminderLogs, services, staff, -} from "../db"; +} from "../db/index.js"; import { buildConfirmationEmail, sendEmail } from "../services/email.js"; import { notifyWaitlistForAppointment } from "../services/waitlistNotify.js"; import type { AppEnv } from "../middleware/rbac.js"; diff --git a/apps/api/src/routes/authProvider.ts b/apps/api/src/routes/authProvider.ts index 9bd4f2f..4cf502f 100644 --- a/apps/api/src/routes/authProvider.ts +++ b/apps/api/src/routes/authProvider.ts @@ -1,7 +1,7 @@ import { Hono } from "hono"; import { zValidator } from "@hono/zod-validator"; import { z } from "zod/v3"; -import { eq, getDb, authProviderConfig, encryptSecret } from "../db"; +import { eq, getDb, authProviderConfig, encryptSecret } from "../db/index.js"; import { requireSuperUser } from "../middleware/rbac.js"; import { reinitAuth } from "../lib/auth.js"; diff --git a/apps/api/src/routes/book.ts b/apps/api/src/routes/book.ts index e15a131..01226db 100644 --- a/apps/api/src/routes/book.ts +++ b/apps/api/src/routes/book.ts @@ -14,7 +14,7 @@ import { appointments, clients, pets, -} from "../db"; +} from "../db/index.js"; import { generateAvailableSlots, BUSINESS_START_HOUR, diff --git a/apps/api/src/routes/calendar.ts b/apps/api/src/routes/calendar.ts index 1aba590..ba745ef 100644 --- a/apps/api/src/routes/calendar.ts +++ b/apps/api/src/routes/calendar.ts @@ -10,7 +10,7 @@ import { pets, services, staff, -} from "../db"; +} from "../db/index.js"; export const calendarRouter = new Hono(); diff --git a/apps/api/src/routes/clients.ts b/apps/api/src/routes/clients.ts index 679d3b7..2ae09f0 100644 --- a/apps/api/src/routes/clients.ts +++ b/apps/api/src/routes/clients.ts @@ -1,7 +1,7 @@ import { Hono } from "hono"; import { zValidator } from "@hono/zod-validator"; import { z } from "zod/v3"; -import { and, eq, exists, getDb, or, clients, appointments } from "../db"; +import { and, eq, exists, getDb, or, clients, appointments } from "../db/index.js"; import type { AppEnv } from "../middleware/rbac.js"; export const clientsRouter = new Hono(); diff --git a/apps/api/src/routes/dev.ts b/apps/api/src/routes/dev.ts index 34e8aa5..8154eaa 100644 --- a/apps/api/src/routes/dev.ts +++ b/apps/api/src/routes/dev.ts @@ -1,5 +1,5 @@ import { Hono } from "hono"; -import { getDb, staff, clients, eq, sql } from "../db"; +import { getDb, staff, clients, eq, sql } from "../db/index.js"; const devRouter = new Hono(); diff --git a/apps/api/src/routes/groomingLogs.ts b/apps/api/src/routes/groomingLogs.ts index 8d24d53..f3a0f5b 100644 --- a/apps/api/src/routes/groomingLogs.ts +++ b/apps/api/src/routes/groomingLogs.ts @@ -1,7 +1,7 @@ import { Hono } from "hono"; import { zValidator } from "@hono/zod-validator"; import { z } from "zod/v3"; -import { and, desc, eq, getDb, groomingVisitLogs, appointments, or } from "../db"; +import { and, desc, eq, getDb, groomingVisitLogs, appointments, or } from "../db/index.js"; import type { AppEnv } from "../middleware/rbac.js"; export const groomingLogsRouter = new Hono(); diff --git a/apps/api/src/routes/impersonation.ts b/apps/api/src/routes/impersonation.ts index 7cd98f9..bcfe43d 100644 --- a/apps/api/src/routes/impersonation.ts +++ b/apps/api/src/routes/impersonation.ts @@ -9,7 +9,7 @@ import { impersonationAuditLogs, clients, desc, -} from "../db"; +} from "../db/index.js"; import type { AppEnv } from "../middleware/rbac.js"; export const impersonationRouter = new Hono(); diff --git a/apps/api/src/routes/invoices.ts b/apps/api/src/routes/invoices.ts index ca30cae..799bc49 100644 --- a/apps/api/src/routes/invoices.ts +++ b/apps/api/src/routes/invoices.ts @@ -13,7 +13,7 @@ import { services, clients, sql, -} from "../db"; +} from "../db/index.js"; import type { AppEnv } from "../middleware/rbac.js"; export const invoicesRouter = new Hono(); diff --git a/apps/api/src/routes/pets.ts b/apps/api/src/routes/pets.ts index f911d56..1672811 100644 --- a/apps/api/src/routes/pets.ts +++ b/apps/api/src/routes/pets.ts @@ -1,7 +1,7 @@ import { Hono } from "hono"; import { zValidator } from "@hono/zod-validator"; import { z } from "zod/v3"; -import { and, eq, exists, getDb, or, pets, appointments } from "../db"; +import { and, eq, exists, getDb, or, pets, appointments } from "../db/index.js"; import type { AppEnv } from "../middleware/rbac.js"; import { getPresignedUploadUrl, diff --git a/apps/api/src/routes/portal.ts b/apps/api/src/routes/portal.ts index 421fc6d..2fe4f91 100644 --- a/apps/api/src/routes/portal.ts +++ b/apps/api/src/routes/portal.ts @@ -1,8 +1,8 @@ import { Hono } from "hono"; import { zValidator } from "@hono/zod-validator"; import { z } from "zod/v3"; -import { eq, inArray } from "../db"; -import { getDb, appointments, impersonationSessions, waitlistEntries, clients, pets, services, staff, invoices, invoiceLineItems } from "../db"; +import { eq, inArray } from "../db/index.js"; +import { getDb, appointments, impersonationSessions, waitlistEntries, clients, pets, services, staff, invoices, invoiceLineItems } from "../db/index.js"; import { validatePortalSession } from "../middleware/portalSession.js"; import { portalAudit } from "../middleware/portalAudit.js"; import type { PortalEnv } from "../middleware/portalSession.js"; diff --git a/apps/api/src/routes/reports.ts b/apps/api/src/routes/reports.ts index aeffc95..024dfff 100644 --- a/apps/api/src/routes/reports.ts +++ b/apps/api/src/routes/reports.ts @@ -12,7 +12,7 @@ import { invoiceTipSplits, services, staff, -} from "../db"; +} from "../db/index.js"; export const reportsRouter = new Hono(); diff --git a/apps/api/src/routes/search.ts b/apps/api/src/routes/search.ts index e72d700..0c08179 100644 --- a/apps/api/src/routes/search.ts +++ b/apps/api/src/routes/search.ts @@ -1,5 +1,5 @@ import { Hono } from "hono"; -import { and, eq, getDb, clients, ilike, or, pets } from "../db"; +import { and, eq, getDb, clients, ilike, or, pets } from "../db/index.js"; export const searchRouter = new Hono(); diff --git a/apps/api/src/routes/services.ts b/apps/api/src/routes/services.ts index bffe6c4..993cb96 100644 --- a/apps/api/src/routes/services.ts +++ b/apps/api/src/routes/services.ts @@ -1,7 +1,7 @@ import { Hono } from "hono"; import { zValidator } from "@hono/zod-validator"; import { z } from "zod/v3"; -import { eq, getDb, services } from "../db"; +import { eq, getDb, services } from "../db/index.js"; export const servicesRouter = new Hono(); diff --git a/apps/api/src/routes/settings.ts b/apps/api/src/routes/settings.ts index 32c48a2..3ad7b25 100644 --- a/apps/api/src/routes/settings.ts +++ b/apps/api/src/routes/settings.ts @@ -1,7 +1,7 @@ import { Hono } from "hono"; import { zValidator } from "@hono/zod-validator"; import { z } from "zod/v3"; -import { eq, getDb, businessSettings } from "../db"; +import { eq, getDb, businessSettings } from "../db/index.js"; import { getPresignedUploadUrl, deleteObject, putObject, getObject } from "../lib/s3.js"; import { requireSuperUser } from "../middleware/rbac.js"; diff --git a/apps/api/src/routes/setup.ts b/apps/api/src/routes/setup.ts index 1ad4c25..90d6c17 100644 --- a/apps/api/src/routes/setup.ts +++ b/apps/api/src/routes/setup.ts @@ -1,7 +1,7 @@ import { Hono } from "hono"; import { zValidator } from "@hono/zod-validator"; import { z } from "zod/v3"; -import { and, eq, getDb, sql, staff, businessSettings, authProviderConfig, encryptSecret } from "../db"; +import { and, eq, getDb, sql, staff, businessSettings, authProviderConfig, encryptSecret } from "../db/index.js"; import type { AppEnv } from "../middleware/rbac.js"; const RATE_LIMIT_WINDOW_MS = 60_000; diff --git a/apps/api/src/routes/staff.ts b/apps/api/src/routes/staff.ts index 80c3262..de4c92b 100644 --- a/apps/api/src/routes/staff.ts +++ b/apps/api/src/routes/staff.ts @@ -2,7 +2,7 @@ import { Hono } from "hono"; import { zValidator } from "@hono/zod-validator"; import { z } from "zod/v3"; import { randomBytes } from "node:crypto"; -import { and, eq, getDb, ne, staff, appointments } from "../db"; +import { and, eq, getDb, ne, staff, appointments } from "../db/index.js"; import type { AppEnv } from "../middleware/rbac.js"; export const staffRouter = new Hono(); diff --git a/apps/api/src/routes/stripe-webhooks.ts b/apps/api/src/routes/stripe-webhooks.ts index e4c5238..b40f063 100644 --- a/apps/api/src/routes/stripe-webhooks.ts +++ b/apps/api/src/routes/stripe-webhooks.ts @@ -1,7 +1,7 @@ import { Hono } from "hono"; import Stripe from "stripe"; import { z } from "zod/v3"; -import { eq, getDb, invoices } from "../db"; +import { eq, getDb, invoices } from "../db/index.js"; import { getStripeClient } from "../services/payment.js"; export const webhooksRouter = new Hono(); diff --git a/apps/api/src/routes/waitlist.ts b/apps/api/src/routes/waitlist.ts index 897e531..c1fe302 100644 --- a/apps/api/src/routes/waitlist.ts +++ b/apps/api/src/routes/waitlist.ts @@ -8,7 +8,7 @@ import { clients, pets, services, -} from "../db"; +} from "../db/index.js"; import type { AppEnv } from "../middleware/rbac.js"; export const waitlistRouter = new Hono(); diff --git a/apps/api/src/services/payment.ts b/apps/api/src/services/payment.ts index 93ede92..fd11805 100644 --- a/apps/api/src/services/payment.ts +++ b/apps/api/src/services/payment.ts @@ -1,5 +1,5 @@ import Stripe from "stripe"; -import { getDb, clients, eq, inArray, invoices } from "../db"; +import { getDb, clients, eq, inArray, invoices } from "../db/index.js"; let _stripe: Stripe | null | undefined; diff --git a/apps/api/src/services/reminders.ts b/apps/api/src/services/reminders.ts index 255505c..82ab9c7 100644 --- a/apps/api/src/services/reminders.ts +++ b/apps/api/src/services/reminders.ts @@ -14,7 +14,7 @@ import { staff, reminderLogs, session, -} from "../db"; +} from "../db/index.js"; import { buildReminderEmail, sendEmail, diff --git a/apps/api/src/services/waitlistNotify.ts b/apps/api/src/services/waitlistNotify.ts index bd6f76a..36dfcc3 100644 --- a/apps/api/src/services/waitlistNotify.ts +++ b/apps/api/src/services/waitlistNotify.ts @@ -1,4 +1,4 @@ -import { and, eq, getDb, waitlistEntries, clients, pets, services } from "../db"; +import { and, eq, getDb, waitlistEntries, clients, pets, services } from "../db/index.js"; import { buildWaitlistNotificationEmail, sendEmail } from "./email.js"; export async function notifyWaitlistForAppointment(