feat: api v1 garden bangs routes and openapi
This commit is contained in:
@@ -3,65 +3,71 @@
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { z } from "zod";
|
||||
import type { ApiAuthContext } from "@/lib/api-auth";
|
||||
import { db } from "@/lib/db";
|
||||
import { getCurrentSession } from "@/lib/session";
|
||||
import { logActivity } from "@/modules/_core/activity";
|
||||
import { logActivityForScope } from "@/modules/_core/activity";
|
||||
import { bangEvents } from "../schema";
|
||||
|
||||
const dateString = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Must be YYYY-MM-DD");
|
||||
|
||||
const addBangInput = z.object({
|
||||
occurredOn: dateString.optional(),
|
||||
});
|
||||
|
||||
const updateBangInput = z.object({
|
||||
id: z.string().uuid(),
|
||||
occurredOn: dateString,
|
||||
});
|
||||
import { addBangInput, updateBangInput } from "./schemas";
|
||||
|
||||
const deleteBangInput = z.object({
|
||||
id: z.string().uuid(),
|
||||
});
|
||||
|
||||
function toScope(ctx: ApiAuthContext) {
|
||||
return { householdId: ctx.householdId, userId: ctx.userId };
|
||||
}
|
||||
|
||||
function todayString(): string {
|
||||
const d = new Date();
|
||||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
export async function addBang(input: z.input<typeof addBangInput> = {}) {
|
||||
export async function addBangForScope(
|
||||
scope: ApiAuthContext,
|
||||
input: z.input<typeof addBangInput> = {},
|
||||
) {
|
||||
const parsed = addBangInput.parse(input);
|
||||
const { user, household } = await getCurrentSession();
|
||||
|
||||
const occurredOn = parsed.occurredOn ?? todayString();
|
||||
|
||||
const [bang] = await db
|
||||
.insert(bangEvents)
|
||||
.values({
|
||||
householdId: household.id,
|
||||
recordedBy: user.id,
|
||||
householdId: scope.householdId,
|
||||
recordedBy: scope.userId,
|
||||
occurredOn,
|
||||
})
|
||||
.returning();
|
||||
|
||||
if (!bang) throw new Error("Bang was not recorded");
|
||||
|
||||
await logActivity({
|
||||
await logActivityForScope(toScope(scope), {
|
||||
entityType: "bangs.event",
|
||||
entityId: bang.id,
|
||||
action: "create",
|
||||
payload: { occurredOn },
|
||||
});
|
||||
|
||||
revalidatePath("/");
|
||||
revalidatePath("/d/[slug]", "page");
|
||||
|
||||
return bang;
|
||||
}
|
||||
|
||||
export async function updateBang(input: z.input<typeof updateBangInput>) {
|
||||
const parsed = updateBangInput.parse(input);
|
||||
const { household } = await getCurrentSession();
|
||||
await assertCanAccessBang(parsed.id, household.id);
|
||||
export async function addBang(input: z.input<typeof addBangInput> = {}) {
|
||||
const { user, household } = await getCurrentSession();
|
||||
const bang = await addBangForScope(
|
||||
{ householdId: household.id, userId: user.id, role: null },
|
||||
input,
|
||||
);
|
||||
revalidatePath("/");
|
||||
revalidatePath("/d/[slug]", "page");
|
||||
return bang;
|
||||
}
|
||||
|
||||
export async function updateBangForScope(
|
||||
scope: ApiAuthContext,
|
||||
input: { id: string } & z.input<typeof updateBangInput>,
|
||||
) {
|
||||
const parsed = z.object({ id: z.string().uuid() }).and(updateBangInput).parse(input);
|
||||
await assertCanAccessBang(parsed.id, scope.householdId);
|
||||
|
||||
const [bang] = await db
|
||||
.update(bangEvents)
|
||||
@@ -71,23 +77,31 @@ export async function updateBang(input: z.input<typeof updateBangInput>) {
|
||||
|
||||
if (!bang) throw new Error("Bang was not updated");
|
||||
|
||||
await logActivity({
|
||||
await logActivityForScope(toScope(scope), {
|
||||
entityType: "bangs.event",
|
||||
entityId: bang.id,
|
||||
action: "update",
|
||||
payload: { occurredOn: parsed.occurredOn },
|
||||
});
|
||||
|
||||
revalidatePath("/");
|
||||
revalidatePath("/d/[slug]", "page");
|
||||
|
||||
return bang;
|
||||
}
|
||||
|
||||
export async function deleteBang(input: z.input<typeof deleteBangInput>) {
|
||||
export async function updateBang(input: { id: string } & z.input<typeof updateBangInput>) {
|
||||
const parsed = z.object({ id: z.string().uuid() }).and(updateBangInput).parse(input);
|
||||
const { household, user } = await getCurrentSession();
|
||||
const bang = await updateBangForScope(
|
||||
{ householdId: household.id, userId: user.id, role: null },
|
||||
parsed,
|
||||
);
|
||||
revalidatePath("/");
|
||||
revalidatePath("/d/[slug]", "page");
|
||||
return bang;
|
||||
}
|
||||
|
||||
export async function deleteBangForScope(scope: ApiAuthContext, input: { id: string }) {
|
||||
const parsed = deleteBangInput.parse(input);
|
||||
const { household } = await getCurrentSession();
|
||||
await assertCanAccessBang(parsed.id, household.id);
|
||||
await assertCanAccessBang(parsed.id, scope.householdId);
|
||||
|
||||
const [existing] = await db
|
||||
.select({ occurredOn: bangEvents.occurredOn })
|
||||
@@ -95,7 +109,7 @@ export async function deleteBang(input: z.input<typeof deleteBangInput>) {
|
||||
.where(eq(bangEvents.id, parsed.id))
|
||||
.limit(1);
|
||||
|
||||
await logActivity({
|
||||
await logActivityForScope(toScope(scope), {
|
||||
entityType: "bangs.event",
|
||||
entityId: parsed.id,
|
||||
action: "delete",
|
||||
@@ -103,7 +117,12 @@ export async function deleteBang(input: z.input<typeof deleteBangInput>) {
|
||||
});
|
||||
|
||||
await db.delete(bangEvents).where(eq(bangEvents.id, parsed.id));
|
||||
}
|
||||
|
||||
export async function deleteBang(input: z.input<typeof deleteBangInput>) {
|
||||
const parsed = deleteBangInput.parse(input);
|
||||
const { household, user } = await getCurrentSession();
|
||||
await deleteBangForScope({ householdId: household.id, userId: user.id, role: null }, parsed);
|
||||
revalidatePath("/");
|
||||
revalidatePath("/d/[slug]", "page");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user