fix
This commit is contained in:
@@ -8,7 +8,8 @@ const CAIRN_CACHE_TTL_MS = 5 * 60 * 1000;
|
|||||||
|
|
||||||
const paginatedQuerySchema = z.object({
|
const paginatedQuerySchema = z.object({
|
||||||
limit: z.number().int().min(1).max(100).optional(),
|
limit: z.number().int().min(1).max(100).optional(),
|
||||||
offset: z.number().int().min(0).optional()
|
offset: z.number().int().min(0).optional(),
|
||||||
|
since: z.string().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
const planIdSchema = z.object({ id: z.string().min(1) });
|
const planIdSchema = z.object({ id: z.string().min(1) });
|
||||||
@@ -187,10 +188,10 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
getUsers: cairnProcedure
|
getUsers: cairnProcedure
|
||||||
.input(paginatedQuerySchema)
|
.input(paginatedQuerySchema)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
const limit = input.limit ?? 50;
|
const limit = input.limit ?? 50;
|
||||||
const offset = input.offset ?? 0;
|
const offset = input.offset ?? 0;
|
||||||
const cacheKey = `cairn-users:${limit}:${offset}`;
|
const cacheKey = `cairn-users:${ctx.cairnUserId}:${limit}:${offset}:${input.since ?? ""}`;
|
||||||
|
|
||||||
const cached = await cache.get<{
|
const cached = await cache.get<{
|
||||||
users: Array<{ id: string; email: string | null }>;
|
users: Array<{ id: string; email: string | null }>;
|
||||||
@@ -201,9 +202,15 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
const sql = input.since
|
||||||
|
? "SELECT id, email FROM users WHERE id = ? AND updatedAt > ? ORDER BY createdAt DESC LIMIT ? OFFSET ?"
|
||||||
|
: "SELECT id, email FROM users WHERE id = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?";
|
||||||
|
const args = input.since
|
||||||
|
? [ctx.cairnUserId, input.since, limit, offset]
|
||||||
|
: [ctx.cairnUserId, limit, offset];
|
||||||
const result = await conn.execute({
|
const result = await conn.execute({
|
||||||
sql: "SELECT id, email FROM users ORDER BY createdAt DESC LIMIT ? OFFSET ?",
|
sql,
|
||||||
args: [limit, offset]
|
args
|
||||||
});
|
});
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
@@ -223,10 +230,10 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
getPlans: cairnProcedure
|
getPlans: cairnProcedure
|
||||||
.input(paginatedQuerySchema)
|
.input(paginatedQuerySchema)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
const limit = input.limit ?? 50;
|
const limit = input.limit ?? 50;
|
||||||
const offset = input.offset ?? 0;
|
const offset = input.offset ?? 0;
|
||||||
const cacheKey = `cairn-plans:${limit}:${offset}`;
|
const cacheKey = `cairn-plans:${ctx.cairnUserId}:${limit}:${offset}:${input.since ?? ""}`;
|
||||||
|
|
||||||
const cached = await cache.get<{
|
const cached = await cache.get<{
|
||||||
plans: Array<{ id: string; name: string; category: string }>;
|
plans: Array<{ id: string; name: string; category: string }>;
|
||||||
@@ -237,9 +244,15 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
const sql = input.since
|
||||||
|
? "SELECT id, name, category FROM workoutPlans WHERE userId = ? AND updatedAt > ? ORDER BY createdAt DESC LIMIT ? OFFSET ?"
|
||||||
|
: "SELECT id, name, category FROM workoutPlans WHERE userId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?";
|
||||||
|
const args = input.since
|
||||||
|
? [ctx.cairnUserId, input.since, limit, offset]
|
||||||
|
: [ctx.cairnUserId, limit, offset];
|
||||||
const result = await conn.execute({
|
const result = await conn.execute({
|
||||||
sql: "SELECT id, name, category FROM workoutPlans ORDER BY createdAt DESC LIMIT ? OFFSET ?",
|
sql,
|
||||||
args: [limit, offset]
|
args
|
||||||
});
|
});
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
@@ -263,10 +276,10 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
getWorkouts: cairnProcedure
|
getWorkouts: cairnProcedure
|
||||||
.input(paginatedQuerySchema)
|
.input(paginatedQuerySchema)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
const limit = input.limit ?? 50;
|
const limit = input.limit ?? 50;
|
||||||
const offset = input.offset ?? 0;
|
const offset = input.offset ?? 0;
|
||||||
const cacheKey = `cairn-workouts:${limit}:${offset}`;
|
const cacheKey = `cairn-workouts:${ctx.cairnUserId}:${limit}:${offset}:${input.since ?? ""}`;
|
||||||
|
|
||||||
const cached = await cache.get<{
|
const cached = await cache.get<{
|
||||||
workouts: Array<{ id: string; type: string; startDate: string }>;
|
workouts: Array<{ id: string; type: string; startDate: string }>;
|
||||||
@@ -277,9 +290,15 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
const sql = input.since
|
||||||
|
? "SELECT id, type, startDate FROM workouts WHERE userId = ? AND updatedAt > ? ORDER BY startDate DESC LIMIT ? OFFSET ?"
|
||||||
|
: "SELECT id, type, startDate FROM workouts WHERE userId = ? ORDER BY startDate DESC LIMIT ? OFFSET ?";
|
||||||
|
const args = input.since
|
||||||
|
? [ctx.cairnUserId, input.since, limit, offset]
|
||||||
|
: [ctx.cairnUserId, limit, offset];
|
||||||
const result = await conn.execute({
|
const result = await conn.execute({
|
||||||
sql: "SELECT id, type, startDate FROM workouts ORDER BY startDate DESC LIMIT ? OFFSET ?",
|
sql,
|
||||||
args: [limit, offset]
|
args
|
||||||
});
|
});
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
@@ -303,12 +322,16 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
createUser: cairnProcedure
|
createUser: cairnProcedure
|
||||||
.input(userInputSchema)
|
.input(userInputSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
if (input.id !== ctx.cairnUserId) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `INSERT INTO users (id, email, emailVerified, firstName, lastName, displayName, avatarUrl, provider, appleUserId, status)
|
sql: `INSERT INTO users (id, email, emailVerified, firstName, lastName, displayName, avatarUrl, provider, appleUserId, status)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET email = excluded.email, emailVerified = excluded.emailVerified, firstName = excluded.firstName, lastName = excluded.lastName, displayName = excluded.displayName, avatarUrl = excluded.avatarUrl, provider = excluded.provider, appleUserId = excluded.appleUserId, status = excluded.status, updatedAt = datetime('now')`,
|
||||||
args: [
|
args: [
|
||||||
input.id,
|
input.id,
|
||||||
input.email ?? null,
|
input.email ?? null,
|
||||||
@@ -334,7 +357,10 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
updateUser: cairnProcedure
|
updateUser: cairnProcedure
|
||||||
.input(userInputSchema)
|
.input(userInputSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
if (input.id !== ctx.cairnUserId) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
@@ -364,7 +390,10 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
deleteUser: cairnProcedure
|
deleteUser: cairnProcedure
|
||||||
.input(userIdSchema)
|
.input(userIdSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
if (input.id !== ctx.cairnUserId) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
@@ -383,7 +412,10 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
createWorkoutPlan: cairnProcedure
|
createWorkoutPlan: cairnProcedure
|
||||||
.input(workoutPlanSchema)
|
.input(workoutPlanSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
if (input.userId != ctx.cairnUserId) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
@@ -413,7 +445,10 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
updateWorkoutPlan: cairnProcedure
|
updateWorkoutPlan: cairnProcedure
|
||||||
.input(workoutPlanSchema)
|
.input(workoutPlanSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
if (input.userId != ctx.cairnUserId) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
@@ -441,9 +476,16 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
deleteWorkoutPlan: cairnProcedure
|
deleteWorkoutPlan: cairnProcedure
|
||||||
.input(planIdSchema)
|
.input(planIdSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
const check = await conn.execute({
|
||||||
|
sql: "SELECT userId FROM workoutPlans WHERE id = ?",
|
||||||
|
args: [input.id]
|
||||||
|
});
|
||||||
|
if (!check.rows.length || check.rows[0].userId !== ctx.cairnUserId) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: "DELETE FROM workoutPlans WHERE id = ?",
|
sql: "DELETE FROM workoutPlans WHERE id = ?",
|
||||||
args: [input.id]
|
args: [input.id]
|
||||||
@@ -460,9 +502,19 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
createPlanExercise: cairnProcedure
|
createPlanExercise: cairnProcedure
|
||||||
.input(planExerciseSchema)
|
.input(planExerciseSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
const planCheck = await conn.execute({
|
||||||
|
sql: "SELECT userId FROM workoutPlans WHERE id = ?",
|
||||||
|
args: [input.planId]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!planCheck.rows.length ||
|
||||||
|
planCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `INSERT INTO planExercises (id, planId, exerciseId, name, category, orderIndex, notes)
|
sql: `INSERT INTO planExercises (id, planId, exerciseId, name, category, orderIndex, notes)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||||
@@ -488,9 +540,19 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
updatePlanExercise: cairnProcedure
|
updatePlanExercise: cairnProcedure
|
||||||
.input(planExerciseSchema)
|
.input(planExerciseSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
const planCheck = await conn.execute({
|
||||||
|
sql: "SELECT userId FROM workoutPlans WHERE id = ?",
|
||||||
|
args: [input.planId]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!planCheck.rows.length ||
|
||||||
|
planCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `UPDATE planExercises SET exerciseId = ?, name = ?, category = ?, orderIndex = ?, notes = ? WHERE id = ?`,
|
sql: `UPDATE planExercises SET exerciseId = ?, name = ?, category = ?, orderIndex = ?, notes = ? WHERE id = ?`,
|
||||||
args: [
|
args: [
|
||||||
@@ -514,9 +576,19 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
deletePlanExercise: cairnProcedure
|
deletePlanExercise: cairnProcedure
|
||||||
.input(planExerciseIdSchema)
|
.input(planExerciseIdSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
const planCheck = await conn.execute({
|
||||||
|
sql: "SELECT workoutPlans.userId FROM workoutPlans INNER JOIN planExercises ON planExercises.planId = workoutPlans.id WHERE planExercises.id = ?",
|
||||||
|
args: [input.id]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!planCheck.rows.length ||
|
||||||
|
planCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: "DELETE FROM planExercises WHERE id = ?",
|
sql: "DELETE FROM planExercises WHERE id = ?",
|
||||||
args: [input.id]
|
args: [input.id]
|
||||||
@@ -533,9 +605,19 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
createPlanSet: cairnProcedure
|
createPlanSet: cairnProcedure
|
||||||
.input(planSetSchema)
|
.input(planSetSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
const planCheck = await conn.execute({
|
||||||
|
sql: "SELECT workoutPlans.userId FROM workoutPlans INNER JOIN planExercises ON planExercises.planId = workoutPlans.id WHERE planExercises.id = ?",
|
||||||
|
args: [input.planExerciseId]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!planCheck.rows.length ||
|
||||||
|
planCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `INSERT INTO planSets (id, planExerciseId, setNumber, reps, weight, durationSeconds, rpe, restAfterSeconds, isWarmup, isDropset, notes)
|
sql: `INSERT INTO planSets (id, planExerciseId, setNumber, reps, weight, durationSeconds, rpe, restAfterSeconds, isWarmup, isDropset, notes)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
@@ -565,9 +647,19 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
updatePlanSet: cairnProcedure
|
updatePlanSet: cairnProcedure
|
||||||
.input(planSetSchema)
|
.input(planSetSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
const planCheck = await conn.execute({
|
||||||
|
sql: "SELECT workoutPlans.userId FROM workoutPlans INNER JOIN planExercises ON planExercises.planId = workoutPlans.id INNER JOIN planSets ON planSets.planExerciseId = planExercises.id WHERE planSets.id = ?",
|
||||||
|
args: [input.id]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!planCheck.rows.length ||
|
||||||
|
planCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `UPDATE planSets SET setNumber = ?, reps = ?, weight = ?, durationSeconds = ?, rpe = ?, restAfterSeconds = ?, isWarmup = ?, isDropset = ?, notes = ? WHERE id = ?`,
|
sql: `UPDATE planSets SET setNumber = ?, reps = ?, weight = ?, durationSeconds = ?, rpe = ?, restAfterSeconds = ?, isWarmup = ?, isDropset = ?, notes = ? WHERE id = ?`,
|
||||||
args: [
|
args: [
|
||||||
@@ -595,9 +687,19 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
deletePlanSet: cairnProcedure
|
deletePlanSet: cairnProcedure
|
||||||
.input(planSetIdSchema)
|
.input(planSetIdSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
const planCheck = await conn.execute({
|
||||||
|
sql: "SELECT workoutPlans.userId FROM workoutPlans INNER JOIN planExercises ON planExercises.planId = workoutPlans.id INNER JOIN planSets ON planSets.planExerciseId = planExercises.id WHERE planSets.id = ?",
|
||||||
|
args: [input.id]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!planCheck.rows.length ||
|
||||||
|
planCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: "DELETE FROM planSets WHERE id = ?",
|
sql: "DELETE FROM planSets WHERE id = ?",
|
||||||
args: [input.id]
|
args: [input.id]
|
||||||
@@ -614,9 +716,19 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
createRoutePoint: cairnProcedure
|
createRoutePoint: cairnProcedure
|
||||||
.input(routePointSchema)
|
.input(routePointSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
const planCheck = await conn.execute({
|
||||||
|
sql: "SELECT userId FROM workoutPlans WHERE id = ?",
|
||||||
|
args: [input.planId]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!planCheck.rows.length ||
|
||||||
|
planCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `INSERT INTO routePoints (id, planId, latitude, longitude, orderIndex, isWaypoint)
|
sql: `INSERT INTO routePoints (id, planId, latitude, longitude, orderIndex, isWaypoint)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
@@ -641,9 +753,19 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
updateRoutePoint: cairnProcedure
|
updateRoutePoint: cairnProcedure
|
||||||
.input(routePointSchema)
|
.input(routePointSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
const planCheck = await conn.execute({
|
||||||
|
sql: "SELECT workoutPlans.userId FROM workoutPlans INNER JOIN routePoints ON routePoints.planId = workoutPlans.id WHERE routePoints.id = ?",
|
||||||
|
args: [input.id]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!planCheck.rows.length ||
|
||||||
|
planCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `UPDATE routePoints SET latitude = ?, longitude = ?, orderIndex = ?, isWaypoint = ? WHERE id = ?`,
|
sql: `UPDATE routePoints SET latitude = ?, longitude = ?, orderIndex = ?, isWaypoint = ? WHERE id = ?`,
|
||||||
args: [
|
args: [
|
||||||
@@ -666,9 +788,19 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
deleteRoutePoint: cairnProcedure
|
deleteRoutePoint: cairnProcedure
|
||||||
.input(routePointIdSchema)
|
.input(routePointIdSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
const planCheck = await conn.execute({
|
||||||
|
sql: "SELECT workoutPlans.userId FROM workoutPlans INNER JOIN routePoints ON routePoints.planId = workoutPlans.id WHERE routePoints.id = ?",
|
||||||
|
args: [input.id]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!planCheck.rows.length ||
|
||||||
|
planCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: "DELETE FROM routePoints WHERE id = ?",
|
sql: "DELETE FROM routePoints WHERE id = ?",
|
||||||
args: [input.id]
|
args: [input.id]
|
||||||
@@ -685,7 +817,10 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
createWorkout: cairnProcedure
|
createWorkout: cairnProcedure
|
||||||
.input(workoutSchema)
|
.input(workoutSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
if (input.userId != ctx.cairnUserId) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
@@ -724,7 +859,10 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
updateWorkout: cairnProcedure
|
updateWorkout: cairnProcedure
|
||||||
.input(workoutSchema)
|
.input(workoutSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
if (input.userId != ctx.cairnUserId) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
@@ -761,9 +899,16 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
deleteWorkout: cairnProcedure
|
deleteWorkout: cairnProcedure
|
||||||
.input(workoutIdSchema)
|
.input(workoutIdSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
const check = await conn.execute({
|
||||||
|
sql: "SELECT userId FROM workouts WHERE id = ?",
|
||||||
|
args: [input.id]
|
||||||
|
});
|
||||||
|
if (!check.rows.length || check.rows[0].userId !== ctx.cairnUserId) {
|
||||||
|
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: "DELETE FROM workouts WHERE id = ?",
|
sql: "DELETE FROM workouts WHERE id = ?",
|
||||||
args: [input.id]
|
args: [input.id]
|
||||||
@@ -811,12 +956,7 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `UPDATE heartRateSamples SET timestamp = ?, bpm = ?, source = ? WHERE id = ?`,
|
sql: `UPDATE heartRateSamples SET timestamp = ?, bpm = ?, source = ? WHERE id = ?`,
|
||||||
args: [
|
args: [input.timestamp, input.bpm, input.source ?? null, input.id]
|
||||||
input.timestamp,
|
|
||||||
input.bpm,
|
|
||||||
input.source ?? null,
|
|
||||||
input.id
|
|
||||||
]
|
|
||||||
});
|
});
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -1162,12 +1302,52 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
bulkUpsert: cairnProcedure
|
bulkUpsert: cairnProcedure
|
||||||
.input(bulkSchema)
|
.input(bulkSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
const conn = CairnConnectionFactory();
|
const conn = CairnConnectionFactory();
|
||||||
|
|
||||||
if (input.users?.length) {
|
if (input.users?.length) {
|
||||||
for (const user of input.users) {
|
for (const user of input.users) {
|
||||||
|
if (user.id !== ctx.cairnUserId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "FORBIDDEN",
|
||||||
|
message: "User mismatch"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await conn.execute({
|
||||||
|
sql: `INSERT INTO users (id, email, emailVerified, firstName, lastName, displayName, avatarUrl, provider, appleUserId, status)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET email = excluded.email, emailVerified = excluded.emailVerified, firstName = excluded.firstName, lastName = excluded.lastName, displayName = excluded.displayName, avatarUrl = excluded.avatarUrl, provider = excluded.provider, appleUserId = excluded.appleUserId, status = excluded.status, updatedAt = datetime('now')`,
|
||||||
|
args: [
|
||||||
|
user.id,
|
||||||
|
user.email ?? null,
|
||||||
|
user.emailVerified ?? 0,
|
||||||
|
user.firstName ?? null,
|
||||||
|
user.lastName ?? null,
|
||||||
|
user.displayName ?? null,
|
||||||
|
user.avatarUrl ?? null,
|
||||||
|
user.provider ?? null,
|
||||||
|
user.appleUserId ?? null,
|
||||||
|
user.status ?? "active"
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bulkUpsert: cairnProcedure
|
||||||
|
.input(bulkSchema)
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
try {
|
||||||
|
const conn = CairnConnectionFactory();
|
||||||
|
|
||||||
|
if (input.users?.length) {
|
||||||
|
for (const user of input.users) {
|
||||||
|
if (user.id !== ctx.cairnUserId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "FORBIDDEN",
|
||||||
|
message: "User mismatch"
|
||||||
|
});
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `INSERT INTO users (id, email, emailVerified, firstName, lastName, displayName, avatarUrl, provider, appleUserId, status)
|
sql: `INSERT INTO users (id, email, emailVerified, firstName, lastName, displayName, avatarUrl, provider, appleUserId, status)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
@@ -1212,6 +1392,12 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (input.workoutPlans?.length) {
|
if (input.workoutPlans?.length) {
|
||||||
for (const plan of input.workoutPlans) {
|
for (const plan of input.workoutPlans) {
|
||||||
|
if (plan.userId !== ctx.cairnUserId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "FORBIDDEN",
|
||||||
|
message: "User mismatch"
|
||||||
|
});
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `INSERT INTO workoutPlans (id, userId, name, description, category, difficulty, durationMinutes, type, isPublic)
|
sql: `INSERT INTO workoutPlans (id, userId, name, description, category, difficulty, durationMinutes, type, isPublic)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
@@ -1233,6 +1419,19 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (input.planExercises?.length) {
|
if (input.planExercises?.length) {
|
||||||
for (const planExercise of input.planExercises) {
|
for (const planExercise of input.planExercises) {
|
||||||
|
const planCheck = await conn.execute({
|
||||||
|
sql: "SELECT userId FROM workoutPlans WHERE id = ?",
|
||||||
|
args: [planExercise.planId]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!planCheck.rows.length ||
|
||||||
|
planCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "FORBIDDEN",
|
||||||
|
message: "User mismatch"
|
||||||
|
});
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `INSERT INTO planExercises (id, planId, exerciseId, name, category, orderIndex, notes)
|
sql: `INSERT INTO planExercises (id, planId, exerciseId, name, category, orderIndex, notes)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
@@ -1252,6 +1451,25 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (input.planSets?.length) {
|
if (input.planSets?.length) {
|
||||||
for (const planSet of input.planSets) {
|
for (const planSet of input.planSets) {
|
||||||
|
const planExerciseCheck = await conn.execute({
|
||||||
|
sql: "SELECT planId FROM planExercises WHERE id = ?",
|
||||||
|
args: [planSet.planExerciseId]
|
||||||
|
});
|
||||||
|
if (planExerciseCheck.rows.length) {
|
||||||
|
const planCheck = await conn.execute({
|
||||||
|
sql: "SELECT userId FROM workoutPlans WHERE id = ?",
|
||||||
|
args: [planExerciseCheck.rows[0].planId]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!planCheck.rows.length ||
|
||||||
|
planCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "FORBIDDEN",
|
||||||
|
message: "User mismatch"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `INSERT INTO planSets (id, planExerciseId, setNumber, reps, weight, durationSeconds, rpe, restAfterSeconds, isWarmup, isDropset, notes)
|
sql: `INSERT INTO planSets (id, planExerciseId, setNumber, reps, weight, durationSeconds, rpe, restAfterSeconds, isWarmup, isDropset, notes)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
@@ -1275,6 +1493,19 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (input.routePoints?.length) {
|
if (input.routePoints?.length) {
|
||||||
for (const point of input.routePoints) {
|
for (const point of input.routePoints) {
|
||||||
|
const planCheck = await conn.execute({
|
||||||
|
sql: "SELECT userId FROM workoutPlans WHERE id = ?",
|
||||||
|
args: [point.planId]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!planCheck.rows.length ||
|
||||||
|
planCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "FORBIDDEN",
|
||||||
|
message: "User mismatch"
|
||||||
|
});
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `INSERT INTO routePoints (id, planId, latitude, longitude, orderIndex, isWaypoint)
|
sql: `INSERT INTO routePoints (id, planId, latitude, longitude, orderIndex, isWaypoint)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
@@ -1293,6 +1524,12 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (input.workouts?.length) {
|
if (input.workouts?.length) {
|
||||||
for (const workout of input.workouts) {
|
for (const workout of input.workouts) {
|
||||||
|
if (workout.userId !== ctx.cairnUserId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "FORBIDDEN",
|
||||||
|
message: "User mismatch"
|
||||||
|
});
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `INSERT INTO workouts (id, userId, planId, type, name, startDate, endDate, durationSeconds, distanceMeters, calories, averageHeartRate, maxHeartRate, averagePace, elevationGain, status, source, healthKitUUID, notes)
|
sql: `INSERT INTO workouts (id, userId, planId, type, name, startDate, endDate, durationSeconds, distanceMeters, calories, averageHeartRate, maxHeartRate, averagePace, elevationGain, status, source, healthKitUUID, notes)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
@@ -1323,6 +1560,19 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (input.heartRateSamples?.length) {
|
if (input.heartRateSamples?.length) {
|
||||||
for (const sample of input.heartRateSamples) {
|
for (const sample of input.heartRateSamples) {
|
||||||
|
const workoutCheck = await conn.execute({
|
||||||
|
sql: "SELECT userId FROM workouts WHERE id = ?",
|
||||||
|
args: [sample.workoutId]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!workoutCheck.rows.length ||
|
||||||
|
workoutCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "FORBIDDEN",
|
||||||
|
message: "User mismatch"
|
||||||
|
});
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `INSERT INTO heartRateSamples (id, workoutId, timestamp, bpm, source)
|
sql: `INSERT INTO heartRateSamples (id, workoutId, timestamp, bpm, source)
|
||||||
VALUES (?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?)
|
||||||
@@ -1340,6 +1590,19 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (input.locationSamples?.length) {
|
if (input.locationSamples?.length) {
|
||||||
for (const sample of input.locationSamples) {
|
for (const sample of input.locationSamples) {
|
||||||
|
const workoutCheck = await conn.execute({
|
||||||
|
sql: "SELECT userId FROM workouts WHERE id = ?",
|
||||||
|
args: [sample.workoutId]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!workoutCheck.rows.length ||
|
||||||
|
workoutCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "FORBIDDEN",
|
||||||
|
message: "User mismatch"
|
||||||
|
});
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `INSERT INTO locationSamples (id, workoutId, timestamp, latitude, longitude, altitude, horizontalAccuracy, verticalAccuracy, speed, course)
|
sql: `INSERT INTO locationSamples (id, workoutId, timestamp, latitude, longitude, altitude, horizontalAccuracy, verticalAccuracy, speed, course)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
@@ -1362,6 +1625,19 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (input.workoutSplits?.length) {
|
if (input.workoutSplits?.length) {
|
||||||
for (const split of input.workoutSplits) {
|
for (const split of input.workoutSplits) {
|
||||||
|
const workoutCheck = await conn.execute({
|
||||||
|
sql: "SELECT userId FROM workouts WHERE id = ?",
|
||||||
|
args: [split.workoutId]
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!workoutCheck.rows.length ||
|
||||||
|
workoutCheck.rows[0].userId !== ctx.cairnUserId
|
||||||
|
) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "FORBIDDEN",
|
||||||
|
message: "User mismatch"
|
||||||
|
});
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `INSERT INTO workoutSplits (id, workoutId, splitNumber, distanceMeters, durationSeconds, startTimestamp, endTimestamp, averageHeartRate, averagePace, elevationGain, elevationLoss)
|
sql: `INSERT INTO workoutSplits (id, workoutId, splitNumber, distanceMeters, durationSeconds, startTimestamp, endTimestamp, averageHeartRate, averagePace, elevationGain, elevationLoss)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
@@ -1385,6 +1661,12 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (input.authProviders?.length) {
|
if (input.authProviders?.length) {
|
||||||
for (const provider of input.authProviders) {
|
for (const provider of input.authProviders) {
|
||||||
|
if (provider.userId !== ctx.cairnUserId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "FORBIDDEN",
|
||||||
|
message: "User mismatch"
|
||||||
|
});
|
||||||
|
}
|
||||||
await conn.execute({
|
await conn.execute({
|
||||||
sql: `INSERT INTO authProviders (id, userId, provider, providerUserId, email, displayName, avatarUrl)
|
sql: `INSERT INTO authProviders (id, userId, provider, providerUserId, email, displayName, avatarUrl)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
@@ -1402,6 +1684,16 @@ export const remoteDbRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed bulk upsert:", error);
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
|
message: "Failed to bulk upsert"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed bulk upsert:", error);
|
console.error("Failed bulk upsert:", error);
|
||||||
|
|||||||
Reference in New Issue
Block a user