v0.1.0 — Moon Base initial scaffold: auth, garden plant registry + log

This commit is contained in:
Bonna Moon
2026-06-15 16:14:48 -05:00
commit 99918fffbc
47 changed files with 2764 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
import NextAuth from "next-auth";
import { authOptions } from "@/lib/auth";
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

View File

@@ -0,0 +1,55 @@
import { NextResponse } from "next/server";
import { z } from "zod";
import { db } from "@/lib/db";
import { requireAuth } from "@/lib/auth";
import { PlantLogType } from "@prisma/client";
const createSchema = z.object({
type: z.nativeEnum(PlantLogType),
date: z.string().optional(),
notes: z.string().optional(),
quantity: z.string().optional(),
});
export async function POST(req: Request, { params }: { params: { id: string } }) {
try {
const session = await requireAuth();
const plant = await db.plant.findUnique({ where: { id: params.id }, select: { id: true } });
if (!plant) return NextResponse.json({ error: "Plant not found" }, { status: 404 });
const body = await req.json();
const data = createSchema.parse(body);
const log = await db.plantLog.create({
data: {
plantId: params.id,
type: data.type,
date: data.date ? new Date(data.date) : new Date(),
notes: data.notes?.trim() || null,
quantity: data.quantity?.trim() || null,
actorId: session.user.id,
},
});
await db.auditEvent.create({
data: {
action: "plant.log.add",
entityId: params.id,
actorId: session.user.id,
payload: { logId: log.id, type: log.type },
},
});
return NextResponse.json(log, { status: 201 });
} catch (err) {
if (err instanceof z.ZodError) {
return NextResponse.json({ error: err.issues }, { status: 400 });
}
if (err instanceof Error && err.message === "Unauthorized") {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
console.error(err);
return NextResponse.json({ error: "Server error" }, { status: 500 });
}
}

View File

@@ -0,0 +1,57 @@
import { NextResponse } from "next/server";
import { z } from "zod";
import { db } from "@/lib/db";
import { requireAuth } from "@/lib/auth";
import { PlantCategory } from "@prisma/client";
const createSchema = z.object({
commonName: z.string().min(1),
species: z.string().optional(),
variety: z.string().optional(),
category: z.nativeEnum(PlantCategory),
zone: z.string().optional(),
plantedAt: z.string().optional(), // ISO date string
source: z.string().optional(),
notes: z.string().optional(),
});
export async function POST(req: Request) {
try {
const session = await requireAuth();
const body = await req.json();
const data = createSchema.parse(body);
const plant = await db.plant.create({
data: {
commonName: data.commonName.trim(),
species: data.species?.trim() || null,
variety: data.variety?.trim() || null,
category: data.category,
zone: data.zone?.trim() || null,
plantedAt: data.plantedAt ? new Date(data.plantedAt) : null,
source: data.source?.trim() || null,
notes: data.notes?.trim() || null,
},
});
await db.auditEvent.create({
data: {
action: "plant.create",
entityId: plant.id,
actorId: session.user.id,
payload: { commonName: plant.commonName, category: plant.category },
},
});
return NextResponse.json(plant, { status: 201 });
} catch (err) {
if (err instanceof z.ZodError) {
return NextResponse.json({ error: err.issues }, { status: 400 });
}
if (err instanceof Error && err.message === "Unauthorized") {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
console.error(err);
return NextResponse.json({ error: "Server error" }, { status: 500 });
}
}