Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b3b2113daa | |||
| 8c184dddbd | |||
| 4e8c66f3ca | |||
| ea28095434 | |||
| 3b9c72c2c4 | |||
| 62dfc7776b | |||
| 68df697cf3 | |||
| 174d1c667b | |||
| 9fe6e15012 | |||
| 002e6575ba | |||
| f9c679b392 | |||
| 7b2b533c16 | |||
| 55894c6ff2 |
@@ -78,6 +78,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
with:
|
||||||
|
driver-opts: network=host
|
||||||
|
|
||||||
- name: Log in to Gitea Container Registry
|
- name: Log in to Gitea Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
|
|||||||
@@ -21,6 +21,14 @@ GroomBook API is a Hono-based REST service (TypeScript/Node.js) powering the pet
|
|||||||
|
|
||||||
## Test Cases
|
## Test Cases
|
||||||
|
|
||||||
|
### 4.0 Health Check
|
||||||
|
|
||||||
|
| # | Scenario | Steps | Expected |
|
||||||
|
|---|----------|-------|----------|
|
||||||
|
| TC-API-0.1 | Unauthenticated health check | GET /api/health | 200 OK, `{"status":"ok"}` |
|
||||||
|
|
||||||
|
> **Note (GRO-1544):** Health endpoint registered on `api` basePath before auth middleware at `/api/health`. The old path `/health` was incorrect (routed to web pod via HTTPRoute `/*` rule).
|
||||||
|
|
||||||
### 4.1 Authentication
|
### 4.1 Authentication
|
||||||
|
|
||||||
| # | Scenario | Steps | Expected |
|
| # | Scenario | Steps | Expected |
|
||||||
|
|||||||
@@ -6,8 +6,10 @@
|
|||||||
CREATE TYPE "pet_size_category" AS ENUM ('small', 'medium', 'large', 'xlarge');
|
CREATE TYPE "pet_size_category" AS ENUM ('small', 'medium', 'large', 'xlarge');
|
||||||
CREATE TYPE "coat_type" AS ENUM ('smooth', 'double', 'wire', 'curly', 'long', 'hairless');
|
CREATE TYPE "coat_type" AS ENUM ('smooth', 'double', 'wire', 'curly', 'long', 'hairless');
|
||||||
|
|
||||||
-- ─── Alter pets columns to use new enums ─────────────────────────────────────
|
-- ─── Add columns to pets if missing, then cast to enums ──────────────────────
|
||||||
|
|
||||||
|
ALTER TABLE "pets" ADD COLUMN IF NOT EXISTS "coat_type" text;
|
||||||
|
ALTER TABLE "pets" ADD COLUMN IF NOT EXISTS "pet_size_category" text;
|
||||||
ALTER TABLE "pets" ALTER COLUMN "coat_type" TYPE "coat_type" USING "coat_type"::text::"coat_type";
|
ALTER TABLE "pets" ALTER COLUMN "coat_type" TYPE "coat_type" USING "coat_type"::text::"coat_type";
|
||||||
ALTER TABLE "pets" ALTER COLUMN "pet_size_category" TYPE "pet_size_category" USING "pet_size_category"::text::"pet_size_category";
|
ALTER TABLE "pets" ALTER COLUMN "pet_size_category" TYPE "pet_size_category" USING "pet_size_category"::text::"pet_size_category";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
-- no-op: journal entry exists but no schema change was needed
|
||||||
@@ -105,6 +105,10 @@ export function buildPet(overrides: Partial<PetRow> & { clientId: string }): Pet
|
|||||||
photoKey: null,
|
photoKey: null,
|
||||||
photoUploadedAt: null,
|
photoUploadedAt: null,
|
||||||
image: null,
|
image: null,
|
||||||
|
temperamentScore: null,
|
||||||
|
temperamentFlags: [],
|
||||||
|
medicalAlerts: [],
|
||||||
|
preferredCuts: [],
|
||||||
createdAt: new Date("2025-01-01T00:00:00Z"),
|
createdAt: new Date("2025-01-01T00:00:00Z"),
|
||||||
updatedAt: new Date("2025-01-01T00:00:00Z"),
|
updatedAt: new Date("2025-01-01T00:00:00Z"),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
unique,
|
unique,
|
||||||
uuid,
|
uuid,
|
||||||
} from "drizzle-orm/pg-core";
|
} from "drizzle-orm/pg-core";
|
||||||
|
import type { MedicalAlert } from "@groombook/types";
|
||||||
|
|
||||||
// ─── Enums ────────────────────────────────────────────────────────────────────
|
// ─── Enums ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -164,6 +165,10 @@ export const pets = pgTable(
|
|||||||
specialCareNotes: text("special_care_notes"),
|
specialCareNotes: text("special_care_notes"),
|
||||||
coatType: coatTypeEnum("coat_type"),
|
coatType: coatTypeEnum("coat_type"),
|
||||||
petSizeCategory: petSizeCategoryEnum("pet_size_category"),
|
petSizeCategory: petSizeCategoryEnum("pet_size_category"),
|
||||||
|
temperamentScore: integer("temperament_score"),
|
||||||
|
temperamentFlags: jsonb("temperament_flags").$type<string[]>().default([]),
|
||||||
|
medicalAlerts: jsonb("medical_alerts").$type<MedicalAlert[]>().default([]),
|
||||||
|
preferredCuts: jsonb("preferred_cuts").$type<string[]>().default([]),
|
||||||
customFields: jsonb("custom_fields").$type<Record<string, string>>().notNull().default({}),
|
customFields: jsonb("custom_fields").$type<Record<string, string>>().notNull().default({}),
|
||||||
photoKey: text("photo_key"),
|
photoKey: text("photo_key"),
|
||||||
photoUploadedAt: timestamp("photo_uploaded_at"),
|
photoUploadedAt: timestamp("photo_uploaded_at"),
|
||||||
|
|||||||
+2
-2
@@ -58,8 +58,8 @@ app.use(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Health check (no auth required)
|
// Health check — no auth required, registered on app at full path before auth middleware
|
||||||
app.get("/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
|
||||||
app.route("/api/book", bookRouter);
|
app.route("/api/book", bookRouter);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ if (process.env.AUTH_DISABLED === "true") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const authMiddleware: MiddlewareHandler = async (c, next) => {
|
export const authMiddleware: MiddlewareHandler = async (c, next) => {
|
||||||
if (c.req.path.startsWith("/api/auth/")) {
|
if (c.req.path.startsWith("/api/auth/") || c.req.path === "/api/health") {
|
||||||
await next();
|
await next();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user