Merge origin/dev into fix/gro-1749-uat-seed-sync

Resolve conflict in apps/api/src/routes/admin/seed.ts:
- Keep UAT_CLIENT with address (not stripped by GRO-1743)
- Keep weightKg in UAT_PETS (from both branches)
- Use origin/dev coatType as const (typed properly)
- Use existingPet naming (from origin/dev)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Barcode Betty
2026-05-25 17:45:25 +00:00
5 changed files with 24 additions and 13 deletions
+4
View File
@@ -96,6 +96,7 @@ jobs:
file: Dockerfile file: Dockerfile
target: runner target: runner
push: true push: true
provenance: false
tags: | tags: |
git.farh.net/groombook/api:${{ steps.version.outputs.tag }} git.farh.net/groombook/api:${{ steps.version.outputs.tag }}
${{ github.ref == 'refs/heads/main' && 'git.farh.net/groombook/api:latest' || '' }} ${{ github.ref == 'refs/heads/main' && 'git.farh.net/groombook/api:latest' || '' }}
@@ -110,6 +111,7 @@ jobs:
file: Dockerfile file: Dockerfile
target: migrate target: migrate
push: true push: true
provenance: false
tags: | tags: |
git.farh.net/groombook/migrate:${{ steps.version.outputs.tag }} git.farh.net/groombook/migrate:${{ steps.version.outputs.tag }}
${{ github.ref == 'refs/heads/main' && 'git.farh.net/groombook/migrate:latest' || '' }} ${{ github.ref == 'refs/heads/main' && 'git.farh.net/groombook/migrate:latest' || '' }}
@@ -124,6 +126,7 @@ jobs:
file: Dockerfile file: Dockerfile
target: seed target: seed
push: true push: true
provenance: false
tags: | tags: |
git.farh.net/groombook/seed:${{ steps.version.outputs.tag }} git.farh.net/groombook/seed:${{ steps.version.outputs.tag }}
${{ github.ref == 'refs/heads/main' && 'git.farh.net/groombook/seed:latest' || '' }} ${{ github.ref == 'refs/heads/main' && 'git.farh.net/groombook/seed:latest' || '' }}
@@ -138,6 +141,7 @@ jobs:
file: Dockerfile file: Dockerfile
target: reset target: reset
push: true push: true
provenance: false
tags: | tags: |
git.farh.net/groombook/reset:${{ steps.version.outputs.tag }} git.farh.net/groombook/reset:${{ steps.version.outputs.tag }}
${{ github.ref == 'refs/heads/main' && 'git.farh.net/groombook/reset:latest' || '' }} ${{ github.ref == 'refs/heads/main' && 'git.farh.net/groombook/reset:latest' || '' }}
+6 -6
View File
@@ -45,8 +45,8 @@ const UAT_CLIENT = {
}; };
const UAT_PETS = [ const UAT_PETS = [
{ name: "Bella", species: "Dog", breed: "Poodle", coatType: "curly", weightKg: "20.00" }, { name: "Bella", species: "Dog", breed: "Poodle", coatType: "curly" as const, weightKg: "20.00" },
{ name: "Max", species: "Dog", breed: "Labrador Retriever", coatType: "short", weightKg: "30.00" }, { name: "Max", species: "Dog", breed: "Labrador Retriever", coatType: "short" as const, weightKg: "30.00" },
]; ];
const DEMO_SERVICES = [ const DEMO_SERVICES = [
@@ -164,11 +164,11 @@ adminSeedRouter.post("/", async (c) => {
.where(eq(pets.clientId, uatClientId)); .where(eq(pets.clientId, uatClientId));
for (const uatPet of UAT_PETS) { for (const uatPet of UAT_PETS) {
const existing = existingUatPets.find( const existingPet = existingUatPets.find(
(p) => p.name === uatPet.name && p.species === uatPet.species (p) => p.name === uatPet.name && p.species === uatPet.species
); );
if (existing) { if (existingPet) {
results.push(`Pet '${uatPet.name}' already exists for UAT Customer (id: ${existing.id})`); results.push(`Pet '${uatPet.name}' already exists for UAT Customer (id: ${existingPet.id})`);
} else { } else {
const [created] = await db const [created] = await db
.insert(pets) .insert(pets)
@@ -177,7 +177,7 @@ adminSeedRouter.post("/", async (c) => {
name: uatPet.name, name: uatPet.name,
species: uatPet.species, species: uatPet.species,
breed: uatPet.breed, breed: uatPet.breed,
coatType: uatPet.coatType as any, coatType: uatPet.coatType,
weightKg: uatPet.weightKg, weightKg: uatPet.weightKg,
dateOfBirth: new Date("2019-01-01T00:00:00Z"), dateOfBirth: new Date("2019-01-01T00:00:00Z"),
}) })
+1 -1
View File
@@ -12,7 +12,7 @@ export function getDb() {
if (_db) return _db; if (_db) return _db;
const url = process.env.DATABASE_URL; const url = process.env.DATABASE_URL;
if (!url) throw new Error("DATABASE_URL is not set"); if (!url) throw new Error("DATABASE_URL is not set");
const client = postgres(url, { max: 10 }); const client = postgres(url, { max: 10, connect_timeout: 5 });
_db = drizzle(client, { schema }); _db = drizzle(client, { schema });
return _db; return _db;
} }
+9 -4
View File
@@ -59,6 +59,9 @@ app.use(
); );
// Health check — no auth required, registered on app at full path before auth middleware // Health check — no auth required, registered on app at full path before auth middleware
// /health: used by Dockerfile HEALTHCHECK and K8s readinessProbe/livenessProbe (port 3000 direct)
app.get("/health", (c) => c.json({ status: "ok" }));
// /api/health: used by Gateway HTTPRoute (/api/* → API pod)
app.get("/api/health", (c) => c.json({ status: "ok" })); app.get("/api/health", (c) => c.json({ status: "ok" }));
// Public booking routes — no auth required, must be registered before auth middleware // Public booking routes — no auth required, must be registered before auth middleware
@@ -282,14 +285,16 @@ startReminderScheduler();
function shutdown() { function shutdown() {
console.log("Shutting down gracefully..."); console.log("Shutting down gracefully...");
// SIGTERM/SIGINT → server.close() → callback → process.exit(0)
// If graceful close takes >8s, force-exit to avoid being killed undrained
setTimeout(() => {
console.error("Graceful close timeout — forcing exit");
process.exit(1);
}, 8_000);
server.close(() => { server.close(() => {
console.log("HTTP server closed"); console.log("HTTP server closed");
process.exit(0); process.exit(0);
}); });
setTimeout(() => {
console.error("Forced shutdown after timeout");
process.exit(1);
}, 10_000);
} }
process.on("SIGTERM", shutdown); process.on("SIGTERM", shutdown);
+3 -1
View File
@@ -186,7 +186,9 @@ export async function initAuth(): Promise<void> {
const discoveryUrlStr = `${providerConfig.issuerUrl}/.well-known/openid-configuration`; const discoveryUrlStr = `${providerConfig.issuerUrl}/.well-known/openid-configuration`;
let oidcConfig: Record<string, string> = {}; let oidcConfig: Record<string, string> = {};
try { try {
const discoveryRes = await fetch(discoveryUrlStr); const discoveryRes = await fetch(discoveryUrlStr, {
signal: AbortSignal.timeout(5000),
});
if (discoveryRes.ok) { if (discoveryRes.ok) {
const discovery = await discoveryRes.json() as { const discovery = await discoveryRes.json() as {
authorization_endpoint?: string; authorization_endpoint?: string;