v0.14.0 — Animals & Pets
Livestock and household pets as leaves on the Location tree, mirroring Garden. - Animal model (species/breed/sex/birthdate/location, soft-delete) + AnimalLog (feeding, vet, weight, medication, breeding, acquired, died); Task.animalId for recurring care reminders - API-first /api/v1/animals (CRUD) + logs, animals:read/write scopes; animals service shared with the coming MCP - Animals pages: list grouped by species with auto-computed age, detail with facts + latest weight + care log; add/edit/remove + log entry. Nav entry. Backup/restore cover the new tables - Pure age.ts (newborn/days/months/years) with tests; verified end-to-end against a running server (create, vet + weigh-in logs, read-back) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
15
src/app/api/v1/animals/[id]/logs/route.ts
Normal file
15
src/app/api/v1/animals/[id]/logs/route.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { authenticateRequest, handleApiError } from "@/lib/api/authenticate";
|
||||
import { animalLogInput } from "@/lib/api/schemas";
|
||||
import { addAnimalLog } from "@/lib/services/animals";
|
||||
|
||||
// POST /api/v1/animals/:id/logs
|
||||
export async function POST(req: Request, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const ctx = await authenticateRequest(req, "animals:write");
|
||||
const input = animalLogInput.parse(await req.json());
|
||||
return NextResponse.json(await addAnimalLog(ctx, params.id, input), { status: 201 });
|
||||
} catch (err) {
|
||||
return handleApiError(err);
|
||||
}
|
||||
}
|
||||
33
src/app/api/v1/animals/[id]/route.ts
Normal file
33
src/app/api/v1/animals/[id]/route.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { authenticateRequest, handleApiError } from "@/lib/api/authenticate";
|
||||
import { animalUpdateInput } from "@/lib/api/schemas";
|
||||
import { getAnimal, updateAnimal, deleteAnimal } from "@/lib/services/animals";
|
||||
|
||||
export async function GET(req: Request, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const ctx = await authenticateRequest(req, "animals:read");
|
||||
return NextResponse.json(await getAnimal(ctx, params.id));
|
||||
} catch (err) {
|
||||
return handleApiError(err);
|
||||
}
|
||||
}
|
||||
|
||||
export async function PATCH(req: Request, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const ctx = await authenticateRequest(req, "animals:write");
|
||||
const input = animalUpdateInput.parse(await req.json());
|
||||
return NextResponse.json(await updateAnimal(ctx, params.id, input));
|
||||
} catch (err) {
|
||||
return handleApiError(err);
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE(req: Request, { params }: { params: { id: string } }) {
|
||||
try {
|
||||
const ctx = await authenticateRequest(req, "animals:write");
|
||||
await deleteAnimal(ctx, params.id);
|
||||
return NextResponse.json({ ok: true });
|
||||
} catch (err) {
|
||||
return handleApiError(err);
|
||||
}
|
||||
}
|
||||
28
src/app/api/v1/animals/route.ts
Normal file
28
src/app/api/v1/animals/route.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { authenticateRequest, handleApiError } from "@/lib/api/authenticate";
|
||||
import { animalCreateInput } from "@/lib/api/schemas";
|
||||
import { listAnimals, createAnimal } from "@/lib/services/animals";
|
||||
|
||||
// GET /api/v1/animals?q=&species=
|
||||
export async function GET(req: Request) {
|
||||
try {
|
||||
const ctx = await authenticateRequest(req, "animals:read");
|
||||
const sp = new URL(req.url).searchParams;
|
||||
return NextResponse.json(
|
||||
await listAnimals(ctx, { q: sp.get("q") ?? undefined, species: sp.get("species") ?? undefined }),
|
||||
);
|
||||
} catch (err) {
|
||||
return handleApiError(err);
|
||||
}
|
||||
}
|
||||
|
||||
// POST /api/v1/animals
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const ctx = await authenticateRequest(req, "animals:write");
|
||||
const input = animalCreateInput.parse(await req.json());
|
||||
return NextResponse.json(await createAnimal(ctx, input), { status: 201 });
|
||||
} catch (err) {
|
||||
return handleApiError(err);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user