feat: api v1 routes for calendar lists and notes
This commit is contained in:
@@ -7,6 +7,11 @@ import { getCurrentSession } from "@/lib/session";
|
||||
import { householdMembers } from "@/modules/_core/schema";
|
||||
import { calendarEvents, calendars } from "../schema";
|
||||
|
||||
export type ApiScope = {
|
||||
householdId: string;
|
||||
userId: string | null;
|
||||
};
|
||||
|
||||
export type CalendarDto = {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -32,6 +37,30 @@ const listEventsSchema = z.object({
|
||||
calendarIds: z.union([z.literal("all"), z.array(z.string().uuid())]),
|
||||
});
|
||||
|
||||
function calendarVisibilityFilter(scope: ApiScope) {
|
||||
if (scope.userId) {
|
||||
return or(eq(calendars.visibility, "household"), eq(calendars.ownerId, scope.userId));
|
||||
}
|
||||
return eq(calendars.visibility, "household");
|
||||
}
|
||||
|
||||
export async function canSeeCalendarForScope(scope: ApiScope, calendarId: string) {
|
||||
const [row] = await db
|
||||
.select({
|
||||
visibility: calendars.visibility,
|
||||
ownerId: calendars.ownerId,
|
||||
householdId: calendars.householdId,
|
||||
})
|
||||
.from(calendars)
|
||||
.where(eq(calendars.id, calendarId))
|
||||
.limit(1);
|
||||
|
||||
if (!row || row.householdId !== scope.householdId) return false;
|
||||
if (row.visibility === "household") return true;
|
||||
if (!scope.userId) return false;
|
||||
return row.ownerId === scope.userId;
|
||||
}
|
||||
|
||||
export async function canSeeCalendar(userId: string, calendarId: string) {
|
||||
const [row] = await db
|
||||
.select({
|
||||
@@ -56,8 +85,7 @@ export async function canSeeCalendar(userId: string, calendarId: string) {
|
||||
return row.memberUserId === userId;
|
||||
}
|
||||
|
||||
export async function listCalendars(): Promise<CalendarDto[]> {
|
||||
const { user, household } = await getCurrentSession();
|
||||
export async function listCalendarsForScope(scope: ApiScope): Promise<CalendarDto[]> {
|
||||
const rows = await db
|
||||
.select({
|
||||
id: calendars.id,
|
||||
@@ -67,12 +95,7 @@ export async function listCalendars(): Promise<CalendarDto[]> {
|
||||
ownerId: calendars.ownerId,
|
||||
})
|
||||
.from(calendars)
|
||||
.where(
|
||||
and(
|
||||
eq(calendars.householdId, household.id),
|
||||
or(eq(calendars.visibility, "household"), eq(calendars.ownerId, user.id)),
|
||||
),
|
||||
)
|
||||
.where(and(eq(calendars.householdId, scope.householdId), calendarVisibilityFilter(scope)))
|
||||
.orderBy(asc(calendars.name));
|
||||
|
||||
return rows.map((row) => ({
|
||||
@@ -81,13 +104,48 @@ export async function listCalendars(): Promise<CalendarDto[]> {
|
||||
}));
|
||||
}
|
||||
|
||||
export async function listEvents(input: {
|
||||
from: Date | string;
|
||||
to: Date | string;
|
||||
calendarIds: "all" | string[];
|
||||
}): Promise<CalendarEventDto[]> {
|
||||
export async function listCalendars(): Promise<CalendarDto[]> {
|
||||
const { user, household } = await getCurrentSession();
|
||||
return listCalendarsForScope({ householdId: household.id, userId: user.id });
|
||||
}
|
||||
|
||||
export async function getCalendarForScope(scope: ApiScope, id: string): Promise<CalendarDto> {
|
||||
const parsed = z.string().uuid().parse(id);
|
||||
const [row] = await db
|
||||
.select({
|
||||
id: calendars.id,
|
||||
name: calendars.name,
|
||||
color: calendars.color,
|
||||
visibility: calendars.visibility,
|
||||
ownerId: calendars.ownerId,
|
||||
householdId: calendars.householdId,
|
||||
})
|
||||
.from(calendars)
|
||||
.where(and(eq(calendars.id, parsed), eq(calendars.householdId, scope.householdId)))
|
||||
.limit(1);
|
||||
|
||||
if (!row) throw new Error("Calendar not found");
|
||||
if (!(await canSeeCalendarForScope(scope, row.id))) throw new Error("Calendar not found");
|
||||
|
||||
return {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
color: row.color,
|
||||
visibility: row.visibility as "private" | "household",
|
||||
ownerId: row.ownerId,
|
||||
};
|
||||
}
|
||||
|
||||
export async function listEventsForScope(
|
||||
scope: ApiScope,
|
||||
input: {
|
||||
from: Date | string;
|
||||
to: Date | string;
|
||||
calendarIds: "all" | string[];
|
||||
},
|
||||
): Promise<CalendarEventDto[]> {
|
||||
const parsed = listEventsSchema.parse(input);
|
||||
const visibleCalendars = await listCalendars();
|
||||
const visibleCalendars = await listCalendarsForScope(scope);
|
||||
const visibleIds = new Set(visibleCalendars.map((calendar) => calendar.id));
|
||||
const calendarIds =
|
||||
parsed.calendarIds === "all"
|
||||
@@ -120,6 +178,40 @@ export async function listEvents(input: {
|
||||
return rows.map(toEventDto);
|
||||
}
|
||||
|
||||
export async function listEvents(input: {
|
||||
from: Date | string;
|
||||
to: Date | string;
|
||||
calendarIds: "all" | string[];
|
||||
}): Promise<CalendarEventDto[]> {
|
||||
const { user, household } = await getCurrentSession();
|
||||
return listEventsForScope({ householdId: household.id, userId: user.id }, input);
|
||||
}
|
||||
|
||||
export async function getEventForScope(scope: ApiScope, id: string): Promise<CalendarEventDto> {
|
||||
const parsed = z.string().uuid().parse(id);
|
||||
const [row] = await db
|
||||
.select({
|
||||
id: calendarEvents.id,
|
||||
calendarId: calendarEvents.calendarId,
|
||||
title: calendarEvents.title,
|
||||
startAt: calendarEvents.startAt,
|
||||
endAt: calendarEvents.endAt,
|
||||
allDay: calendarEvents.allDay,
|
||||
location: calendarEvents.location,
|
||||
notes: calendarEvents.notes,
|
||||
householdId: calendars.householdId,
|
||||
})
|
||||
.from(calendarEvents)
|
||||
.innerJoin(calendars, eq(calendarEvents.calendarId, calendars.id))
|
||||
.where(and(eq(calendarEvents.id, parsed), eq(calendars.householdId, scope.householdId)))
|
||||
.limit(1);
|
||||
|
||||
if (!row) throw new Error("Event not found");
|
||||
if (!(await canSeeCalendarForScope(scope, row.calendarId))) throw new Error("Event not found");
|
||||
|
||||
return toEventDto(row);
|
||||
}
|
||||
|
||||
export async function searchCalendars(query: string, householdId: string) {
|
||||
const rows = await db
|
||||
.select({ id: calendars.id, name: calendars.name })
|
||||
|
||||
Reference in New Issue
Block a user