forty-ends-melt/apps/server/router.ts

355 lines
9.9 KiB
TypeScript

import { z } from "zod";
import { router, publicProcedure } from "./trpc";
import * as db from "./taylordb/query-builder";
import {
StrengthExerciseOptions,
CaloriesTimeOfDayOptions,
CardioExerciseOptions,
CaloriesUnitOptions,
} from "./taylordb/types";
/**
* Main tRPC router with TaylorDB integration
* All procedures are type-safe and use the TaylorDB query builder
*/
export const appRouter = router({
// ============================================================================
// Example / Test Procedures
// ============================================================================
hello: publicProcedure
.input(z.object({ name: z.string().optional() }).optional())
.query(({ input }) => {
return {
message: `Hello ${input?.name || "from tRPC"}!`,
timestamp: new Date().toISOString(),
};
}),
health: publicProcedure.query(() => {
return {
status: "ok",
timestamp: new Date().toISOString(),
uptime: process.uptime(),
};
}),
// ============================================================================
// Weight Tracking
// ============================================================================
weight: {
getAll: publicProcedure.query(async () => {
return await db.getAllWeightRecords();
}),
getById: publicProcedure
.input(z.object({ id: z.number() }))
.query(async ({ input }) => {
return await db.getWeightRecordById(input.id);
}),
getByDateRange: publicProcedure
.input(
z.object({
startDate: z.string(),
endDate: z.string(),
})
)
.query(async ({ input }) => {
return await db.getWeightRecordsByDateRange(
input.startDate,
input.endDate
);
}),
create: publicProcedure
.input(
z.object({
date: z.string(),
weight: z.number().positive(),
name: z.string().optional(),
})
)
.mutation(async ({ input }) => {
return await db.createWeightRecord(input);
}),
update: publicProcedure
.input(
z.object({
id: z.number(),
weight: z.number().positive().optional(),
date: z.string().optional(),
name: z.string().optional(),
})
)
.mutation(async ({ input }) => {
const { id, ...data } = input;
return await db.updateWeightRecord(id, data);
}),
delete: publicProcedure
.input(z.object({ id: z.number() }))
.mutation(async ({ input }) => {
return await db.deleteWeightRecord(input.id);
}),
deleteMultiple: publicProcedure
.input(z.object({ ids: z.array(z.number()) }))
.mutation(async ({ input }) => {
return await db.deleteWeightRecords(input.ids);
}),
getStats: publicProcedure.query(async () => {
return await db.getWeightStats();
}),
},
// ============================================================================
// Goals Management
// ============================================================================
goals: {
getAll: publicProcedure.query(async () => {
return await db.getAllGoals();
}),
create: publicProcedure
.input(
z.object({
name: z.string().min(1),
value: z.string().min(1),
description: z.string().optional(),
})
)
.mutation(async ({ input }) => {
return await db.createGoal(input);
}),
update: publicProcedure
.input(
z.object({
id: z.number(),
name: z.string().optional(),
value: z.string().optional(),
description: z.string().optional(),
})
)
.mutation(async ({ input }) => {
const { id, ...data } = input;
return await db.updateGoal(id, data);
}),
delete: publicProcedure
.input(z.object({ id: z.number() }))
.mutation(async ({ input }) => {
return await db.deleteGoal(input.id);
}),
},
// ============================================================================
// Strength Training
// ============================================================================
strength: {
getAll: publicProcedure
.input(
z
.object({ exercise: z.enum(StrengthExerciseOptions).optional() })
.optional()
)
.query(async ({ input }) => {
return await db.getStrengthWorkouts(input?.exercise);
}),
create: publicProcedure
.input(
z.object({
exercise: z.enum(StrengthExerciseOptions),
reps: z.number().positive(),
weight: z.number().positive(),
date: z.string(),
name: z.string().optional(),
})
)
.mutation(async ({ input }) => {
return await db.createStrengthWorkout(input);
}),
update: publicProcedure
.input(
z.object({
id: z.number(),
exercise: z.enum(StrengthExerciseOptions).optional(),
reps: z.number().positive().optional(),
weight: z.number().positive().optional(),
date: z.string().optional(),
name: z.string().optional(),
})
)
.mutation(async ({ input }) => {
const { id, ...data } = input;
return await db.updateStrengthWorkout(id, data);
}),
delete: publicProcedure
.input(z.object({ id: z.number() }))
.mutation(async ({ input }) => {
return await db.deleteStrengthWorkout(input.id);
}),
},
// ============================================================================
// Cardio Exercise
// ============================================================================
cardio: {
getAll: publicProcedure.query(async () => {
return await db.getCardioExercises();
}),
create: publicProcedure
.input(
z.object({
exercise: z.enum(CardioExerciseOptions),
duration: z.number().positive(),
distance: z.number().positive(),
date: z.string(),
name: z.string().optional(),
})
)
.mutation(async ({ input }) => {
return await db.createCardioExercise(input);
}),
update: publicProcedure
.input(
z.object({
id: z.number(),
exercise: z.enum(CardioExerciseOptions).optional(),
duration: z.number().positive().optional(),
distance: z.number().positive().optional(),
date: z.string().optional(),
name: z.string().optional(),
})
)
.mutation(async ({ input }) => {
const { id, ...data } = input;
return await db.updateCardioExercise(id, data);
}),
delete: publicProcedure
.input(z.object({ id: z.number() }))
.mutation(async ({ input }) => {
return await db.deleteCardioExercise(input.id);
}),
},
// ============================================================================
// Calories/Nutrition Tracking
// ============================================================================
calories: {
getByTimeOfDay: publicProcedure
.input(z.object({ timeOfDay: z.enum(CaloriesTimeOfDayOptions) }))
.query(async ({ input }) => {
return await db.getCaloriesByTimeOfDay(input.timeOfDay);
}),
create: publicProcedure
.input(
z.object({
date: z.string(),
timeOfDay: z.enum(CaloriesTimeOfDayOptions),
mealName: z.string(),
mealIngredient: z.string(),
quantity: z.number().positive(),
unit: z.enum(CaloriesUnitOptions),
totalCalories: z.number(),
totalProtein: z.number(),
totalCarbs: z.number(),
totalFats: z.number(),
})
)
.mutation(async ({ input }) => {
return await db.createCalorieEntry(input);
}),
update: publicProcedure
.input(
z.object({
id: z.number(),
totalCalories: z.number().optional(),
totalProtein: z.number().optional(),
totalCarbs: z.number().optional(),
totalFats: z.number().optional(),
quantity: z.number().positive().optional(),
mealName: z.string().optional(),
})
)
.mutation(async ({ input }) => {
const { id, ...data } = input;
return await db.updateCalorieEntry(id, data);
}),
delete: publicProcedure
.input(z.object({ id: z.number() }))
.mutation(async ({ input }) => {
return await db.deleteCalorieEntry(input.id);
}),
getTotalForDate: publicProcedure
.input(z.object({ date: z.string() }))
.query(async ({ input }) => {
return await db.getTotalCaloriesForDate(input.date);
}),
},
// ============================================================================
// Settings
// ============================================================================
settings: {
getByName: publicProcedure
.input(z.object({ name: z.string() }))
.query(async ({ input }) => {
return await db.getSettingByName(input.name);
}),
create: publicProcedure
.input(
z.object({
name: z.string().min(1),
value: z.string(),
description: z.string().optional(),
})
)
.mutation(async ({ input }) => {
return await db.createSetting(input);
}),
update: publicProcedure
.input(
z.object({
id: z.number(),
value: z.string().optional(),
description: z.string().optional(),
})
)
.mutation(async ({ input }) => {
const { id, ...data } = input;
return await db.updateSetting(id, data);
}),
delete: publicProcedure
.input(z.object({ id: z.number() }))
.mutation(async ({ input }) => {
return await db.deleteSetting(input.id);
}),
},
});
// Export type definition of API
export type AppRouter = typeof appRouter;