This commit is contained in:
Michael Freno
2026-01-24 19:31:14 -05:00
parent e6d5b40acd
commit d7c91ac6c5
6 changed files with 198 additions and 198 deletions

12
src/env/server.ts vendored
View File

@@ -31,9 +31,9 @@ const serverEnvSchema = z.object({
VITE_INFILL_ENDPOINT: z.string().min(1), VITE_INFILL_ENDPOINT: z.string().min(1),
INFILL_BEARER_TOKEN: z.string().min(1), INFILL_BEARER_TOKEN: z.string().min(1),
REDIS_URL: z.string().min(1), REDIS_URL: z.string().min(1),
CAIRN_DB_URL: z.string().min(1), NESSA_DB_URL: z.string().min(1),
CAIRN_DB_TOKEN: z.string().min(1), NESSA_DB_TOKEN: z.string().min(1),
CAIRN_JWT_SECRET: z.string().min(1) NESSA_JWT_SECRET: z.string().min(1)
}); });
export type ServerEnv = z.infer<typeof serverEnvSchema>; export type ServerEnv = z.infer<typeof serverEnvSchema>;
@@ -137,9 +137,9 @@ export const getMissingEnvVars = (): string[] => {
"VITE_GITHUB_CLIENT_ID", "VITE_GITHUB_CLIENT_ID",
"VITE_WEBSOCKET", "VITE_WEBSOCKET",
"REDIS_URL", "REDIS_URL",
"CAIRN_DB_URL", "NESSA_DB_URL",
"CAIRN_DB_TOKEN", "NESSA_DB_TOKEN",
"CAIRN_JWT_SECRET" "NESSA_JWT_SECRET"
]; ];
return requiredServerVars.filter((varName) => isMissingEnvVar(varName)); return requiredServerVars.filter((varName) => isMissingEnvVar(varName));

View File

@@ -11,7 +11,7 @@ import { postHistoryRouter } from "./routers/post-history";
import { infillRouter } from "./routers/infill"; import { infillRouter } from "./routers/infill";
import { accountRouter } from "./routers/account"; import { accountRouter } from "./routers/account";
import { downloadsRouter } from "./routers/downloads"; import { downloadsRouter } from "./routers/downloads";
import { cairnDbRouter } from "./routers/cairn"; import { nessaDbRouter } from "./routers/nessa";
import { appleNotificationsRouter } from "./routers/apple-notifications"; import { appleNotificationsRouter } from "./routers/apple-notifications";
import { createTRPCRouter, createTRPCContext, t } from "./utils"; import { createTRPCRouter, createTRPCContext, t } from "./utils";
import type { H3Event } from "h3"; import type { H3Event } from "h3";
@@ -31,7 +31,7 @@ export const appRouter = createTRPCRouter({
infill: infillRouter, infill: infillRouter,
account: accountRouter, account: accountRouter,
downloads: downloadsRouter, downloads: downloadsRouter,
cairnDb: cairnDbRouter, nessaDb: nessaDbRouter,
appleNotifications: appleNotificationsRouter appleNotifications: appleNotificationsRouter
}); });

View File

@@ -1,12 +1,12 @@
import { createTRPCRouter, cairnProcedure, publicProcedure } from "../utils"; import { createTRPCRouter, nessaProcedure, publicProcedure } from "../utils";
import { z } from "zod"; import { z } from "zod";
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
import { CairnConnectionFactory } from "~/server/database"; import { NessaConnectionFactory } from "~/server/database";
import { cache } from "~/server/cache"; import { cache } from "~/server/cache";
import { hashPassword, checkPasswordSafe } from "~/server/utils"; import { hashPassword, checkPasswordSafe } from "~/server/utils";
import { signCairnToken } from "~/server/cairn-auth"; import { signNessaToken } from "~/server/nessa-auth";
const CAIRN_CACHE_TTL_MS = 5 * 60 * 1000; const NESSA_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(),
@@ -185,14 +185,14 @@ const loginSchema = z.object({
password: z.string().min(1) password: z.string().min(1)
}); });
export const cairnDbRouter = createTRPCRouter({ export const nessaDbRouter = createTRPCRouter({
health: cairnProcedure.query(async () => { health: nessaProcedure.query(async () => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const result = await conn.execute("SELECT 1 as ok"); const result = await conn.execute("SELECT 1 as ok");
return { success: true, ok: result.rows.length > 0 }; return { success: true, ok: result.rows.length > 0 };
} catch (error) { } catch (error) {
console.error("Cairn remote DB health check failed:", error); console.error("Nessa remote DB health check failed:", error);
throw new TRPCError({ throw new TRPCError({
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",
message: "Remote database health check failed" message: "Remote database health check failed"
@@ -204,7 +204,7 @@ export const cairnDbRouter = createTRPCRouter({
.input(registerSchema) .input(registerSchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const existing = await conn.execute({ const existing = await conn.execute({
sql: "SELECT id FROM users WHERE email = ?", sql: "SELECT id FROM users WHERE email = ?",
args: [input.email] args: [input.email]
@@ -270,13 +270,13 @@ export const cairnDbRouter = createTRPCRouter({
] ]
}); });
const token = await signCairnToken(userId); const token = await signNessaToken(userId);
return { success: true, token, userId }; return { success: true, token, userId };
} catch (error) { } catch (error) {
if (error instanceof TRPCError) { if (error instanceof TRPCError) {
throw error; throw error;
} }
console.error("Failed to register Cairn user:", error); console.error("Failed to register Nessa user:", error);
throw new TRPCError({ throw new TRPCError({
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",
message: "Failed to register user" message: "Failed to register user"
@@ -286,7 +286,7 @@ export const cairnDbRouter = createTRPCRouter({
login: publicProcedure.input(loginSchema).mutation(async ({ input }) => { login: publicProcedure.input(loginSchema).mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const result = await conn.execute({ const result = await conn.execute({
sql: "SELECT userId, email, provider, providerUserId FROM authProviders WHERE email = ? AND provider IN ('email', 'password')", sql: "SELECT userId, email, provider, providerUserId FROM authProviders WHERE email = ? AND provider IN ('email', 'password')",
args: [input.email] args: [input.email]
@@ -333,7 +333,7 @@ export const cairnDbRouter = createTRPCRouter({
}); });
} }
const token = await signCairnToken(emailProvider.userId); const token = await signNessaToken(emailProvider.userId);
await conn.execute({ await conn.execute({
sql: "UPDATE users SET lastLoginAt = datetime('now'), updatedAt = datetime('now') WHERE id = ?", sql: "UPDATE users SET lastLoginAt = datetime('now'), updatedAt = datetime('now') WHERE id = ?",
args: [emailProvider.userId] args: [emailProvider.userId]
@@ -344,7 +344,7 @@ export const cairnDbRouter = createTRPCRouter({
if (error instanceof TRPCError) { if (error instanceof TRPCError) {
throw error; throw error;
} }
console.error("Failed to login Cairn user:", error); console.error("Failed to login Nessa user:", error);
throw new TRPCError({ throw new TRPCError({
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",
message: "Failed to login" message: "Failed to login"
@@ -352,12 +352,12 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
getUsers: cairnProcedure getUsers: nessaProcedure
.input(paginatedQuerySchema) .input(paginatedQuerySchema)
.query(async ({ input, ctx }) => { .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:${ctx.cairnUserId}:${limit}:${offset}:${input.since ?? ""}`; const cacheKey = `nessa-users:${ctx.nessaUserId}:${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 }>;
@@ -367,13 +367,13 @@ export const cairnDbRouter = createTRPCRouter({
} }
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const sql = input.since 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 = ? AND updatedAt > ? ORDER BY createdAt DESC LIMIT ? OFFSET ?"
: "SELECT id, email FROM users WHERE id = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?"; : "SELECT id, email FROM users WHERE id = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?";
const args = input.since const args = input.since
? [ctx.cairnUserId, input.since, limit, offset] ? [ctx.nessaUserId, input.since, limit, offset]
: [ctx.cairnUserId, limit, offset]; : [ctx.nessaUserId, limit, offset];
const result = await conn.execute({ const result = await conn.execute({
sql, sql,
args args
@@ -383,23 +383,23 @@ export const cairnDbRouter = createTRPCRouter({
users: result.rows as Array<{ id: string; email: string | null }> users: result.rows as Array<{ id: string; email: string | null }>
}; };
await cache.set(cacheKey, payload, CAIRN_CACHE_TTL_MS); await cache.set(cacheKey, payload, NESSA_CACHE_TTL_MS);
return payload; return payload;
} catch (error) { } catch (error) {
console.error("Failed to fetch Cairn users:", error); console.error("Failed to fetch Nessa users:", error);
throw new TRPCError({ throw new TRPCError({
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",
message: "Failed to fetch Cairn users" message: "Failed to fetch Nessa users"
}); });
} }
}), }),
getPlans: cairnProcedure getPlans: nessaProcedure
.input(paginatedQuerySchema) .input(paginatedQuerySchema)
.query(async ({ input, ctx }) => { .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:${ctx.cairnUserId}:${limit}:${offset}:${input.since ?? ""}`; const cacheKey = `nessa-plans:${ctx.nessaUserId}:${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 }>;
@@ -409,13 +409,13 @@ export const cairnDbRouter = createTRPCRouter({
} }
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const sql = input.since 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 = ? AND updatedAt > ? ORDER BY createdAt DESC LIMIT ? OFFSET ?"
: "SELECT id, name, category FROM workoutPlans WHERE userId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?"; : "SELECT id, name, category FROM workoutPlans WHERE userId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?";
const args = input.since const args = input.since
? [ctx.cairnUserId, input.since, limit, offset] ? [ctx.nessaUserId, input.since, limit, offset]
: [ctx.cairnUserId, limit, offset]; : [ctx.nessaUserId, limit, offset];
const result = await conn.execute({ const result = await conn.execute({
sql, sql,
args args
@@ -429,23 +429,23 @@ export const cairnDbRouter = createTRPCRouter({
}> }>
}; };
await cache.set(cacheKey, payload, CAIRN_CACHE_TTL_MS); await cache.set(cacheKey, payload, NESSA_CACHE_TTL_MS);
return payload; return payload;
} catch (error) { } catch (error) {
console.error("Failed to fetch Cairn plans:", error); console.error("Failed to fetch Nessa plans:", error);
throw new TRPCError({ throw new TRPCError({
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",
message: "Failed to fetch Cairn plans" message: "Failed to fetch Nessa plans"
}); });
} }
}), }),
getPlanDetails: cairnProcedure getPlanDetails: nessaProcedure
.input(paginatedQuerySchema) .input(paginatedQuerySchema)
.query(async ({ input, ctx }) => { .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-plan-details:${ctx.cairnUserId}:${limit}:${offset}:${input.since ?? ""}`; const cacheKey = `nessa-plan-details:${ctx.nessaUserId}:${limit}:${offset}:${input.since ?? ""}`;
const cached = await cache.get<{ const cached = await cache.get<{
plans: Array<{ plans: Array<{
@@ -500,13 +500,13 @@ export const cairnDbRouter = createTRPCRouter({
} }
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const planSql = input.since const planSql = input.since
? "SELECT id, userId, name, description, category, difficulty, durationMinutes, type, isPublic, createdAt, updatedAt FROM workoutPlans WHERE userId = ? AND updatedAt > ? ORDER BY createdAt DESC LIMIT ? OFFSET ?" ? "SELECT id, userId, name, description, category, difficulty, durationMinutes, type, isPublic, createdAt, updatedAt FROM workoutPlans WHERE userId = ? AND updatedAt > ? ORDER BY createdAt DESC LIMIT ? OFFSET ?"
: "SELECT id, userId, name, description, category, difficulty, durationMinutes, type, isPublic, createdAt, updatedAt FROM workoutPlans WHERE userId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?"; : "SELECT id, userId, name, description, category, difficulty, durationMinutes, type, isPublic, createdAt, updatedAt FROM workoutPlans WHERE userId = ? ORDER BY createdAt DESC LIMIT ? OFFSET ?";
const planArgs = input.since const planArgs = input.since
? [ctx.cairnUserId, input.since, limit, offset] ? [ctx.nessaUserId, input.since, limit, offset]
: [ctx.cairnUserId, limit, offset]; : [ctx.nessaUserId, limit, offset];
const planResult = await conn.execute({ const planResult = await conn.execute({
sql: planSql, sql: planSql,
args: planArgs args: planArgs
@@ -516,8 +516,8 @@ export const cairnDbRouter = createTRPCRouter({
? "SELECT id, planId, exerciseId, name, category, orderIndex, notes, createdAt FROM planExercises WHERE planId IN (SELECT id FROM workoutPlans WHERE userId = ? AND updatedAt > ?) ORDER BY orderIndex ASC" ? "SELECT id, planId, exerciseId, name, category, orderIndex, notes, createdAt FROM planExercises WHERE planId IN (SELECT id FROM workoutPlans WHERE userId = ? AND updatedAt > ?) ORDER BY orderIndex ASC"
: "SELECT id, planId, exerciseId, name, category, orderIndex, notes, createdAt FROM planExercises WHERE planId IN (SELECT id FROM workoutPlans WHERE userId = ?) ORDER BY orderIndex ASC"; : "SELECT id, planId, exerciseId, name, category, orderIndex, notes, createdAt FROM planExercises WHERE planId IN (SELECT id FROM workoutPlans WHERE userId = ?) ORDER BY orderIndex ASC";
const planExercisesArgs = input.since const planExercisesArgs = input.since
? [ctx.cairnUserId, input.since] ? [ctx.nessaUserId, input.since]
: [ctx.cairnUserId]; : [ctx.nessaUserId];
const planExercisesResult = await conn.execute({ const planExercisesResult = await conn.execute({
sql: planExercisesSql, sql: planExercisesSql,
args: planExercisesArgs args: planExercisesArgs
@@ -527,8 +527,8 @@ export const cairnDbRouter = createTRPCRouter({
? "SELECT id, planExerciseId, setNumber, reps, weight, durationSeconds, rpe, restAfterSeconds, isWarmup, isDropset, notes, createdAt FROM planSets WHERE planExerciseId IN (SELECT id FROM planExercises WHERE planId IN (SELECT id FROM workoutPlans WHERE userId = ? AND updatedAt > ?)) ORDER BY setNumber ASC" ? "SELECT id, planExerciseId, setNumber, reps, weight, durationSeconds, rpe, restAfterSeconds, isWarmup, isDropset, notes, createdAt FROM planSets WHERE planExerciseId IN (SELECT id FROM planExercises WHERE planId IN (SELECT id FROM workoutPlans WHERE userId = ? AND updatedAt > ?)) ORDER BY setNumber ASC"
: "SELECT id, planExerciseId, setNumber, reps, weight, durationSeconds, rpe, restAfterSeconds, isWarmup, isDropset, notes, createdAt FROM planSets WHERE planExerciseId IN (SELECT id FROM planExercises WHERE planId IN (SELECT id FROM workoutPlans WHERE userId = ?)) ORDER BY setNumber ASC"; : "SELECT id, planExerciseId, setNumber, reps, weight, durationSeconds, rpe, restAfterSeconds, isWarmup, isDropset, notes, createdAt FROM planSets WHERE planExerciseId IN (SELECT id FROM planExercises WHERE planId IN (SELECT id FROM workoutPlans WHERE userId = ?)) ORDER BY setNumber ASC";
const planSetsArgs = input.since const planSetsArgs = input.since
? [ctx.cairnUserId, input.since] ? [ctx.nessaUserId, input.since]
: [ctx.cairnUserId]; : [ctx.nessaUserId];
const planSetsResult = await conn.execute({ const planSetsResult = await conn.execute({
sql: planSetsSql, sql: planSetsSql,
args: planSetsArgs args: planSetsArgs
@@ -538,8 +538,8 @@ export const cairnDbRouter = createTRPCRouter({
? "SELECT id, planId, latitude, longitude, orderIndex, isWaypoint, createdAt FROM routePoints WHERE planId IN (SELECT id FROM workoutPlans WHERE userId = ? AND updatedAt > ?) ORDER BY orderIndex ASC" ? "SELECT id, planId, latitude, longitude, orderIndex, isWaypoint, createdAt FROM routePoints WHERE planId IN (SELECT id FROM workoutPlans WHERE userId = ? AND updatedAt > ?) ORDER BY orderIndex ASC"
: "SELECT id, planId, latitude, longitude, orderIndex, isWaypoint, createdAt FROM routePoints WHERE planId IN (SELECT id FROM workoutPlans WHERE userId = ?) ORDER BY orderIndex ASC"; : "SELECT id, planId, latitude, longitude, orderIndex, isWaypoint, createdAt FROM routePoints WHERE planId IN (SELECT id FROM workoutPlans WHERE userId = ?) ORDER BY orderIndex ASC";
const routePointsArgs = input.since const routePointsArgs = input.since
? [ctx.cairnUserId, input.since] ? [ctx.nessaUserId, input.since]
: [ctx.cairnUserId]; : [ctx.nessaUserId];
const routePointsResult = await conn.execute({ const routePointsResult = await conn.execute({
sql: routePointsSql, sql: routePointsSql,
args: routePointsArgs args: routePointsArgs
@@ -594,23 +594,23 @@ export const cairnDbRouter = createTRPCRouter({
}> }>
}; };
await cache.set(cacheKey, payload, CAIRN_CACHE_TTL_MS); await cache.set(cacheKey, payload, NESSA_CACHE_TTL_MS);
return payload; return payload;
} catch (error) { } catch (error) {
console.error("Failed to fetch Cairn plan details:", error); console.error("Failed to fetch Nessa plan details:", error);
throw new TRPCError({ throw new TRPCError({
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",
message: "Failed to fetch Cairn plan details" message: "Failed to fetch Nessa plan details"
}); });
} }
}), }),
getWorkouts: cairnProcedure getWorkouts: nessaProcedure
.input(paginatedQuerySchema) .input(paginatedQuerySchema)
.query(async ({ input, ctx }) => { .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:${ctx.cairnUserId}:${limit}:${offset}:${input.since ?? ""}`; const cacheKey = `nessa-workouts:${ctx.nessaUserId}:${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 }>;
@@ -620,13 +620,13 @@ export const cairnDbRouter = createTRPCRouter({
} }
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const sql = input.since 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 = ? AND updatedAt > ? ORDER BY startDate DESC LIMIT ? OFFSET ?"
: "SELECT id, type, startDate FROM workouts WHERE userId = ? ORDER BY startDate DESC LIMIT ? OFFSET ?"; : "SELECT id, type, startDate FROM workouts WHERE userId = ? ORDER BY startDate DESC LIMIT ? OFFSET ?";
const args = input.since const args = input.since
? [ctx.cairnUserId, input.since, limit, offset] ? [ctx.nessaUserId, input.since, limit, offset]
: [ctx.cairnUserId, limit, offset]; : [ctx.nessaUserId, limit, offset];
const result = await conn.execute({ const result = await conn.execute({
sql, sql,
args args
@@ -640,23 +640,23 @@ export const cairnDbRouter = createTRPCRouter({
}> }>
}; };
await cache.set(cacheKey, payload, CAIRN_CACHE_TTL_MS); await cache.set(cacheKey, payload, NESSA_CACHE_TTL_MS);
return payload; return payload;
} catch (error) { } catch (error) {
console.error("Failed to fetch Cairn workouts:", error); console.error("Failed to fetch Nessa workouts:", error);
throw new TRPCError({ throw new TRPCError({
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",
message: "Failed to fetch Cairn workouts" message: "Failed to fetch Nessa workouts"
}); });
} }
}), }),
getWorkoutDetails: cairnProcedure getWorkoutDetails: nessaProcedure
.input(paginatedQuerySchema) .input(paginatedQuerySchema)
.query(async ({ input, ctx }) => { .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-workout-details:${ctx.cairnUserId}:${limit}:${offset}:${input.since ?? ""}`; const cacheKey = `nessa-workout-details:${ctx.nessaUserId}:${limit}:${offset}:${input.since ?? ""}`;
const cached = await cache.get<{ const cached = await cache.get<{
workouts: Array<{ workouts: Array<{
@@ -720,13 +720,13 @@ export const cairnDbRouter = createTRPCRouter({
} }
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const workoutSql = input.since const workoutSql = input.since
? "SELECT id, userId, planId, type, name, startDate, endDate, durationSeconds, distanceMeters, calories, averageHeartRate, maxHeartRate, averagePace, elevationGain, status, source, healthKitUUID, notes, createdAt, updatedAt, syncedAt FROM workouts WHERE userId = ? AND updatedAt > ? ORDER BY startDate DESC LIMIT ? OFFSET ?" ? "SELECT id, userId, planId, type, name, startDate, endDate, durationSeconds, distanceMeters, calories, averageHeartRate, maxHeartRate, averagePace, elevationGain, status, source, healthKitUUID, notes, createdAt, updatedAt, syncedAt FROM workouts WHERE userId = ? AND updatedAt > ? ORDER BY startDate DESC LIMIT ? OFFSET ?"
: "SELECT id, userId, planId, type, name, startDate, endDate, durationSeconds, distanceMeters, calories, averageHeartRate, maxHeartRate, averagePace, elevationGain, status, source, healthKitUUID, notes, createdAt, updatedAt, syncedAt FROM workouts WHERE userId = ? ORDER BY startDate DESC LIMIT ? OFFSET ?"; : "SELECT id, userId, planId, type, name, startDate, endDate, durationSeconds, distanceMeters, calories, averageHeartRate, maxHeartRate, averagePace, elevationGain, status, source, healthKitUUID, notes, createdAt, updatedAt, syncedAt FROM workouts WHERE userId = ? ORDER BY startDate DESC LIMIT ? OFFSET ?";
const workoutArgs = input.since const workoutArgs = input.since
? [ctx.cairnUserId, input.since, limit, offset] ? [ctx.nessaUserId, input.since, limit, offset]
: [ctx.cairnUserId, limit, offset]; : [ctx.nessaUserId, limit, offset];
const workoutResult = await conn.execute({ const workoutResult = await conn.execute({
sql: workoutSql, sql: workoutSql,
args: workoutArgs args: workoutArgs
@@ -736,8 +736,8 @@ export const cairnDbRouter = createTRPCRouter({
? "SELECT id, workoutId, timestamp, bpm, source FROM heartRateSamples WHERE workoutId IN (SELECT id FROM workouts WHERE userId = ? AND updatedAt > ?) ORDER BY timestamp ASC" ? "SELECT id, workoutId, timestamp, bpm, source FROM heartRateSamples WHERE workoutId IN (SELECT id FROM workouts WHERE userId = ? AND updatedAt > ?) ORDER BY timestamp ASC"
: "SELECT id, workoutId, timestamp, bpm, source FROM heartRateSamples WHERE workoutId IN (SELECT id FROM workouts WHERE userId = ?) ORDER BY timestamp ASC"; : "SELECT id, workoutId, timestamp, bpm, source FROM heartRateSamples WHERE workoutId IN (SELECT id FROM workouts WHERE userId = ?) ORDER BY timestamp ASC";
const heartRateArgs = input.since const heartRateArgs = input.since
? [ctx.cairnUserId, input.since] ? [ctx.nessaUserId, input.since]
: [ctx.cairnUserId]; : [ctx.nessaUserId];
const heartRateResult = await conn.execute({ const heartRateResult = await conn.execute({
sql: heartRateSql, sql: heartRateSql,
args: heartRateArgs args: heartRateArgs
@@ -747,8 +747,8 @@ export const cairnDbRouter = createTRPCRouter({
? "SELECT id, workoutId, timestamp, latitude, longitude, altitude, horizontalAccuracy, verticalAccuracy, speed, course FROM locationSamples WHERE workoutId IN (SELECT id FROM workouts WHERE userId = ? AND updatedAt > ?) ORDER BY timestamp ASC" ? "SELECT id, workoutId, timestamp, latitude, longitude, altitude, horizontalAccuracy, verticalAccuracy, speed, course FROM locationSamples WHERE workoutId IN (SELECT id FROM workouts WHERE userId = ? AND updatedAt > ?) ORDER BY timestamp ASC"
: "SELECT id, workoutId, timestamp, latitude, longitude, altitude, horizontalAccuracy, verticalAccuracy, speed, course FROM locationSamples WHERE workoutId IN (SELECT id FROM workouts WHERE userId = ?) ORDER BY timestamp ASC"; : "SELECT id, workoutId, timestamp, latitude, longitude, altitude, horizontalAccuracy, verticalAccuracy, speed, course FROM locationSamples WHERE workoutId IN (SELECT id FROM workouts WHERE userId = ?) ORDER BY timestamp ASC";
const locationArgs = input.since const locationArgs = input.since
? [ctx.cairnUserId, input.since] ? [ctx.nessaUserId, input.since]
: [ctx.cairnUserId]; : [ctx.nessaUserId];
const locationResult = await conn.execute({ const locationResult = await conn.execute({
sql: locationSql, sql: locationSql,
args: locationArgs args: locationArgs
@@ -758,8 +758,8 @@ export const cairnDbRouter = createTRPCRouter({
? "SELECT id, workoutId, splitNumber, distanceMeters, durationSeconds, startTimestamp, endTimestamp, averageHeartRate, averagePace, elevationGain, elevationLoss FROM workoutSplits WHERE workoutId IN (SELECT id FROM workouts WHERE userId = ? AND updatedAt > ?) ORDER BY splitNumber ASC" ? "SELECT id, workoutId, splitNumber, distanceMeters, durationSeconds, startTimestamp, endTimestamp, averageHeartRate, averagePace, elevationGain, elevationLoss FROM workoutSplits WHERE workoutId IN (SELECT id FROM workouts WHERE userId = ? AND updatedAt > ?) ORDER BY splitNumber ASC"
: "SELECT id, workoutId, splitNumber, distanceMeters, durationSeconds, startTimestamp, endTimestamp, averageHeartRate, averagePace, elevationGain, elevationLoss FROM workoutSplits WHERE workoutId IN (SELECT id FROM workouts WHERE userId = ?) ORDER BY splitNumber ASC"; : "SELECT id, workoutId, splitNumber, distanceMeters, durationSeconds, startTimestamp, endTimestamp, averageHeartRate, averagePace, elevationGain, elevationLoss FROM workoutSplits WHERE workoutId IN (SELECT id FROM workouts WHERE userId = ?) ORDER BY splitNumber ASC";
const splitArgs = input.since const splitArgs = input.since
? [ctx.cairnUserId, input.since] ? [ctx.nessaUserId, input.since]
: [ctx.cairnUserId]; : [ctx.nessaUserId];
const splitResult = await conn.execute({ const splitResult = await conn.execute({
sql: splitSql, sql: splitSql,
args: splitArgs args: splitArgs
@@ -823,25 +823,25 @@ export const cairnDbRouter = createTRPCRouter({
}> }>
}; };
await cache.set(cacheKey, payload, CAIRN_CACHE_TTL_MS); await cache.set(cacheKey, payload, NESSA_CACHE_TTL_MS);
return payload; return payload;
} catch (error) { } catch (error) {
console.error("Failed to fetch Cairn workout details:", error); console.error("Failed to fetch Nessa workout details:", error);
throw new TRPCError({ throw new TRPCError({
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",
message: "Failed to fetch Cairn workout details" message: "Failed to fetch Nessa workout details"
}); });
} }
}), }),
createUser: cairnProcedure createUser: nessaProcedure
.input(userInputSchema) .input(userInputSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
if (input.id !== ctx.cairnUserId) { if (input.id !== ctx.nessaUserId) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
@@ -869,14 +869,14 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
updateUser: cairnProcedure updateUser: nessaProcedure
.input(userInputSchema) .input(userInputSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
if (input.id !== ctx.cairnUserId) { if (input.id !== ctx.nessaUserId) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
await conn.execute({ await conn.execute({
sql: `UPDATE users SET email = ?, emailVerified = ?, firstName = ?, lastName = ?, displayName = ?, avatarUrl = ?, provider = ?, appleUserId = ?, status = ?, updatedAt = datetime('now') WHERE id = ?`, sql: `UPDATE users SET email = ?, emailVerified = ?, firstName = ?, lastName = ?, displayName = ?, avatarUrl = ?, provider = ?, appleUserId = ?, status = ?, updatedAt = datetime('now') WHERE id = ?`,
args: [ args: [
@@ -902,14 +902,14 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
deleteUser: cairnProcedure deleteUser: nessaProcedure
.input(userIdSchema) .input(userIdSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
if (input.id !== ctx.cairnUserId) { if (input.id !== ctx.nessaUserId) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
await conn.execute({ await conn.execute({
sql: "DELETE FROM users WHERE id = ?", sql: "DELETE FROM users WHERE id = ?",
args: [input.id] args: [input.id]
@@ -924,14 +924,14 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
createWorkoutPlan: cairnProcedure createWorkoutPlan: nessaProcedure
.input(workoutPlanSchema) .input(workoutPlanSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
if (input.userId != ctx.cairnUserId) { if (input.userId != ctx.nessaUserId) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
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 (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -957,14 +957,14 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
updateWorkoutPlan: cairnProcedure updateWorkoutPlan: nessaProcedure
.input(workoutPlanSchema) .input(workoutPlanSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
if (input.userId != ctx.cairnUserId) { if (input.userId != ctx.nessaUserId) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
await conn.execute({ await conn.execute({
sql: `UPDATE workoutPlans SET name = ?, description = ?, category = ?, difficulty = ?, durationMinutes = ?, type = ?, isPublic = ?, updatedAt = datetime('now') WHERE id = ?`, sql: `UPDATE workoutPlans SET name = ?, description = ?, category = ?, difficulty = ?, durationMinutes = ?, type = ?, isPublic = ?, updatedAt = datetime('now') WHERE id = ?`,
args: [ args: [
@@ -988,16 +988,16 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
deleteWorkoutPlan: cairnProcedure deleteWorkoutPlan: nessaProcedure
.input(planIdSchema) .input(planIdSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const check = await conn.execute({ const check = await conn.execute({
sql: "SELECT userId FROM workoutPlans WHERE id = ?", sql: "SELECT userId FROM workoutPlans WHERE id = ?",
args: [input.id] args: [input.id]
}); });
if (!check.rows.length || check.rows[0].userId !== ctx.cairnUserId) { if (!check.rows.length || check.rows[0].userId !== ctx.nessaUserId) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
await conn.execute({ await conn.execute({
@@ -1014,18 +1014,18 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
createPlanExercise: cairnProcedure createPlanExercise: nessaProcedure
.input(planExerciseSchema) .input(planExerciseSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const planCheck = await conn.execute({ const planCheck = await conn.execute({
sql: "SELECT userId FROM workoutPlans WHERE id = ?", sql: "SELECT userId FROM workoutPlans WHERE id = ?",
args: [input.planId] args: [input.planId]
}); });
if ( if (
!planCheck.rows.length || !planCheck.rows.length ||
planCheck.rows[0].userId !== ctx.cairnUserId planCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
@@ -1052,18 +1052,18 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
updatePlanExercise: cairnProcedure updatePlanExercise: nessaProcedure
.input(planExerciseSchema) .input(planExerciseSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const planCheck = await conn.execute({ const planCheck = await conn.execute({
sql: "SELECT userId FROM workoutPlans WHERE id = ?", sql: "SELECT userId FROM workoutPlans WHERE id = ?",
args: [input.planId] args: [input.planId]
}); });
if ( if (
!planCheck.rows.length || !planCheck.rows.length ||
planCheck.rows[0].userId !== ctx.cairnUserId planCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
@@ -1088,18 +1088,18 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
deletePlanExercise: cairnProcedure deletePlanExercise: nessaProcedure
.input(planExerciseIdSchema) .input(planExerciseIdSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const planCheck = await conn.execute({ const planCheck = await conn.execute({
sql: "SELECT workoutPlans.userId FROM workoutPlans INNER JOIN planExercises ON planExercises.planId = workoutPlans.id WHERE planExercises.id = ?", sql: "SELECT workoutPlans.userId FROM workoutPlans INNER JOIN planExercises ON planExercises.planId = workoutPlans.id WHERE planExercises.id = ?",
args: [input.id] args: [input.id]
}); });
if ( if (
!planCheck.rows.length || !planCheck.rows.length ||
planCheck.rows[0].userId !== ctx.cairnUserId planCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
@@ -1117,18 +1117,18 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
createPlanSet: cairnProcedure createPlanSet: nessaProcedure
.input(planSetSchema) .input(planSetSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const planCheck = await conn.execute({ const planCheck = await conn.execute({
sql: "SELECT workoutPlans.userId FROM workoutPlans INNER JOIN planExercises ON planExercises.planId = workoutPlans.id WHERE planExercises.id = ?", sql: "SELECT workoutPlans.userId FROM workoutPlans INNER JOIN planExercises ON planExercises.planId = workoutPlans.id WHERE planExercises.id = ?",
args: [input.planExerciseId] args: [input.planExerciseId]
}); });
if ( if (
!planCheck.rows.length || !planCheck.rows.length ||
planCheck.rows[0].userId !== ctx.cairnUserId planCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
@@ -1159,18 +1159,18 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
updatePlanSet: cairnProcedure updatePlanSet: nessaProcedure
.input(planSetSchema) .input(planSetSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const planCheck = await conn.execute({ 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 = ?", 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] args: [input.id]
}); });
if ( if (
!planCheck.rows.length || !planCheck.rows.length ||
planCheck.rows[0].userId !== ctx.cairnUserId planCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
@@ -1199,18 +1199,18 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
deletePlanSet: cairnProcedure deletePlanSet: nessaProcedure
.input(planSetIdSchema) .input(planSetIdSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const planCheck = await conn.execute({ 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 = ?", 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] args: [input.id]
}); });
if ( if (
!planCheck.rows.length || !planCheck.rows.length ||
planCheck.rows[0].userId !== ctx.cairnUserId planCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
@@ -1228,18 +1228,18 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
createRoutePoint: cairnProcedure createRoutePoint: nessaProcedure
.input(routePointSchema) .input(routePointSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const planCheck = await conn.execute({ const planCheck = await conn.execute({
sql: "SELECT userId FROM workoutPlans WHERE id = ?", sql: "SELECT userId FROM workoutPlans WHERE id = ?",
args: [input.planId] args: [input.planId]
}); });
if ( if (
!planCheck.rows.length || !planCheck.rows.length ||
planCheck.rows[0].userId !== ctx.cairnUserId planCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
@@ -1265,18 +1265,18 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
updateRoutePoint: cairnProcedure updateRoutePoint: nessaProcedure
.input(routePointSchema) .input(routePointSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const planCheck = await conn.execute({ const planCheck = await conn.execute({
sql: "SELECT workoutPlans.userId FROM workoutPlans INNER JOIN routePoints ON routePoints.planId = workoutPlans.id WHERE routePoints.id = ?", sql: "SELECT workoutPlans.userId FROM workoutPlans INNER JOIN routePoints ON routePoints.planId = workoutPlans.id WHERE routePoints.id = ?",
args: [input.id] args: [input.id]
}); });
if ( if (
!planCheck.rows.length || !planCheck.rows.length ||
planCheck.rows[0].userId !== ctx.cairnUserId planCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
@@ -1300,18 +1300,18 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
deleteRoutePoint: cairnProcedure deleteRoutePoint: nessaProcedure
.input(routePointIdSchema) .input(routePointIdSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const planCheck = await conn.execute({ const planCheck = await conn.execute({
sql: "SELECT workoutPlans.userId FROM workoutPlans INNER JOIN routePoints ON routePoints.planId = workoutPlans.id WHERE routePoints.id = ?", sql: "SELECT workoutPlans.userId FROM workoutPlans INNER JOIN routePoints ON routePoints.planId = workoutPlans.id WHERE routePoints.id = ?",
args: [input.id] args: [input.id]
}); });
if ( if (
!planCheck.rows.length || !planCheck.rows.length ||
planCheck.rows[0].userId !== ctx.cairnUserId planCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
@@ -1329,14 +1329,14 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
createWorkout: cairnProcedure createWorkout: nessaProcedure
.input(workoutSchema) .input(workoutSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
if (input.userId != ctx.cairnUserId) { if (input.userId != ctx.nessaUserId) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -1371,14 +1371,14 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
updateWorkout: cairnProcedure updateWorkout: nessaProcedure
.input(workoutSchema) .input(workoutSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
if (input.userId != ctx.cairnUserId) { if (input.userId != ctx.nessaUserId) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
await conn.execute({ await conn.execute({
sql: `UPDATE workouts SET planId = ?, type = ?, name = ?, startDate = ?, endDate = ?, durationSeconds = ?, distanceMeters = ?, calories = ?, averageHeartRate = ?, maxHeartRate = ?, averagePace = ?, elevationGain = ?, status = ?, source = ?, healthKitUUID = ?, notes = ?, updatedAt = datetime('now') WHERE id = ?`, sql: `UPDATE workouts SET planId = ?, type = ?, name = ?, startDate = ?, endDate = ?, durationSeconds = ?, distanceMeters = ?, calories = ?, averageHeartRate = ?, maxHeartRate = ?, averagePace = ?, elevationGain = ?, status = ?, source = ?, healthKitUUID = ?, notes = ?, updatedAt = datetime('now') WHERE id = ?`,
args: [ args: [
@@ -1411,16 +1411,16 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
deleteWorkout: cairnProcedure deleteWorkout: nessaProcedure
.input(workoutIdSchema) .input(workoutIdSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
const check = await conn.execute({ const check = await conn.execute({
sql: "SELECT userId FROM workouts WHERE id = ?", sql: "SELECT userId FROM workouts WHERE id = ?",
args: [input.id] args: [input.id]
}); });
if (!check.rows.length || check.rows[0].userId !== ctx.cairnUserId) { if (!check.rows.length || check.rows[0].userId !== ctx.nessaUserId) {
throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" }); throw new TRPCError({ code: "FORBIDDEN", message: "User mismatch" });
} }
await conn.execute({ await conn.execute({
@@ -1437,11 +1437,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
createHeartRateSample: cairnProcedure createHeartRateSample: nessaProcedure
.input(heartRateSchema) .input(heartRateSchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
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 (?, ?, ?, ?, ?)`,
@@ -1463,11 +1463,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
updateHeartRateSample: cairnProcedure updateHeartRateSample: nessaProcedure
.input(heartRateSchema) .input(heartRateSchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
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: [input.timestamp, input.bpm, input.source ?? null, input.id] args: [input.timestamp, input.bpm, input.source ?? null, input.id]
@@ -1482,11 +1482,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
deleteHeartRateSample: cairnProcedure deleteHeartRateSample: nessaProcedure
.input(heartRateSchema.pick({ id: true })) .input(heartRateSchema.pick({ id: true }))
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
await conn.execute({ await conn.execute({
sql: "DELETE FROM heartRateSamples WHERE id = ?", sql: "DELETE FROM heartRateSamples WHERE id = ?",
args: [input.id] args: [input.id]
@@ -1501,11 +1501,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
createLocationSample: cairnProcedure createLocationSample: nessaProcedure
.input(locationSampleSchema) .input(locationSampleSchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -1532,11 +1532,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
updateLocationSample: cairnProcedure updateLocationSample: nessaProcedure
.input(locationSampleSchema) .input(locationSampleSchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
await conn.execute({ await conn.execute({
sql: `UPDATE locationSamples SET timestamp = ?, latitude = ?, longitude = ?, altitude = ?, horizontalAccuracy = ?, verticalAccuracy = ?, speed = ?, course = ? WHERE id = ?`, sql: `UPDATE locationSamples SET timestamp = ?, latitude = ?, longitude = ?, altitude = ?, horizontalAccuracy = ?, verticalAccuracy = ?, speed = ?, course = ? WHERE id = ?`,
args: [ args: [
@@ -1561,11 +1561,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
deleteLocationSample: cairnProcedure deleteLocationSample: nessaProcedure
.input(locationSampleSchema.pick({ id: true })) .input(locationSampleSchema.pick({ id: true }))
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
await conn.execute({ await conn.execute({
sql: "DELETE FROM locationSamples WHERE id = ?", sql: "DELETE FROM locationSamples WHERE id = ?",
args: [input.id] args: [input.id]
@@ -1580,11 +1580,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
createWorkoutSplit: cairnProcedure createWorkoutSplit: nessaProcedure
.input(workoutSplitSchema) .input(workoutSplitSchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -1612,11 +1612,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
updateWorkoutSplit: cairnProcedure updateWorkoutSplit: nessaProcedure
.input(workoutSplitSchema) .input(workoutSplitSchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
await conn.execute({ await conn.execute({
sql: `UPDATE workoutSplits SET splitNumber = ?, distanceMeters = ?, durationSeconds = ?, startTimestamp = ?, endTimestamp = ?, averageHeartRate = ?, averagePace = ?, elevationGain = ?, elevationLoss = ? WHERE id = ?`, sql: `UPDATE workoutSplits SET splitNumber = ?, distanceMeters = ?, durationSeconds = ?, startTimestamp = ?, endTimestamp = ?, averageHeartRate = ?, averagePace = ?, elevationGain = ?, elevationLoss = ? WHERE id = ?`,
args: [ args: [
@@ -1642,11 +1642,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
deleteWorkoutSplit: cairnProcedure deleteWorkoutSplit: nessaProcedure
.input(workoutSplitSchema.pick({ id: true })) .input(workoutSplitSchema.pick({ id: true }))
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
await conn.execute({ await conn.execute({
sql: "DELETE FROM workoutSplits WHERE id = ?", sql: "DELETE FROM workoutSplits WHERE id = ?",
args: [input.id] args: [input.id]
@@ -1661,11 +1661,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
createExerciseLibrary: cairnProcedure createExerciseLibrary: nessaProcedure
.input(exerciseLibrarySchema) .input(exerciseLibrarySchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
await conn.execute({ await conn.execute({
sql: `INSERT INTO exerciseLibrary (id, name, category, muscleGroups, equipment, instructions, defaultSets, defaultReps, defaultRestSeconds, notes) sql: `INSERT INTO exerciseLibrary (id, name, category, muscleGroups, equipment, instructions, defaultSets, defaultReps, defaultRestSeconds, notes)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -1692,11 +1692,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
updateExerciseLibrary: cairnProcedure updateExerciseLibrary: nessaProcedure
.input(exerciseLibrarySchema) .input(exerciseLibrarySchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
await conn.execute({ await conn.execute({
sql: `UPDATE exerciseLibrary SET name = ?, category = ?, muscleGroups = ?, equipment = ?, instructions = ?, defaultSets = ?, defaultReps = ?, defaultRestSeconds = ?, notes = ?, updatedAt = datetime('now') WHERE id = ?`, sql: `UPDATE exerciseLibrary SET name = ?, category = ?, muscleGroups = ?, equipment = ?, instructions = ?, defaultSets = ?, defaultReps = ?, defaultRestSeconds = ?, notes = ?, updatedAt = datetime('now') WHERE id = ?`,
args: [ args: [
@@ -1722,11 +1722,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
deleteExerciseLibrary: cairnProcedure deleteExerciseLibrary: nessaProcedure
.input(exerciseIdSchema) .input(exerciseIdSchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
await conn.execute({ await conn.execute({
sql: "DELETE FROM exerciseLibrary WHERE id = ?", sql: "DELETE FROM exerciseLibrary WHERE id = ?",
args: [input.id] args: [input.id]
@@ -1741,11 +1741,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
createAuthProvider: cairnProcedure createAuthProvider: nessaProcedure
.input(providerSchema) .input(providerSchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
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 (?, ?, ?, ?, ?, ?, ?)`,
@@ -1769,11 +1769,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
updateAuthProvider: cairnProcedure updateAuthProvider: nessaProcedure
.input(providerSchema) .input(providerSchema)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
await conn.execute({ await conn.execute({
sql: `UPDATE authProviders SET provider = ?, providerUserId = ?, email = ?, displayName = ?, avatarUrl = ?, lastUsedAt = datetime('now') WHERE id = ?`, sql: `UPDATE authProviders SET provider = ?, providerUserId = ?, email = ?, displayName = ?, avatarUrl = ?, lastUsedAt = datetime('now') WHERE id = ?`,
args: [ args: [
@@ -1795,11 +1795,11 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
deleteAuthProvider: cairnProcedure deleteAuthProvider: nessaProcedure
.input(providerSchema.pick({ id: true })) .input(providerSchema.pick({ id: true }))
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
await conn.execute({ await conn.execute({
sql: "DELETE FROM authProviders WHERE id = ?", sql: "DELETE FROM authProviders WHERE id = ?",
args: [input.id] args: [input.id]
@@ -1814,15 +1814,15 @@ export const cairnDbRouter = createTRPCRouter({
} }
}), }),
bulkUpsert: cairnProcedure bulkUpsert: nessaProcedure
.input(bulkSchema) .input(bulkSchema)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
try { try {
const conn = CairnConnectionFactory(); const conn = NessaConnectionFactory();
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) { if (user.id !== ctx.nessaUserId) {
throw new TRPCError({ throw new TRPCError({
code: "FORBIDDEN", code: "FORBIDDEN",
message: "User mismatch" message: "User mismatch"
@@ -1872,7 +1872,7 @@ export const cairnDbRouter = 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) { if (plan.userId !== ctx.nessaUserId) {
throw new TRPCError({ throw new TRPCError({
code: "FORBIDDEN", code: "FORBIDDEN",
message: "User mismatch" message: "User mismatch"
@@ -1905,7 +1905,7 @@ export const cairnDbRouter = createTRPCRouter({
}); });
if ( if (
!planCheck.rows.length || !planCheck.rows.length ||
planCheck.rows[0].userId !== ctx.cairnUserId planCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ throw new TRPCError({
code: "FORBIDDEN", code: "FORBIDDEN",
@@ -1942,7 +1942,7 @@ export const cairnDbRouter = createTRPCRouter({
}); });
if ( if (
!planCheck.rows.length || !planCheck.rows.length ||
planCheck.rows[0].userId !== ctx.cairnUserId planCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ throw new TRPCError({
code: "FORBIDDEN", code: "FORBIDDEN",
@@ -1979,7 +1979,7 @@ export const cairnDbRouter = createTRPCRouter({
}); });
if ( if (
!planCheck.rows.length || !planCheck.rows.length ||
planCheck.rows[0].userId !== ctx.cairnUserId planCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ throw new TRPCError({
code: "FORBIDDEN", code: "FORBIDDEN",
@@ -2004,7 +2004,7 @@ export const cairnDbRouter = 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) { if (workout.userId !== ctx.nessaUserId) {
throw new TRPCError({ throw new TRPCError({
code: "FORBIDDEN", code: "FORBIDDEN",
message: "User mismatch" message: "User mismatch"
@@ -2046,7 +2046,7 @@ export const cairnDbRouter = createTRPCRouter({
}); });
if ( if (
!workoutCheck.rows.length || !workoutCheck.rows.length ||
workoutCheck.rows[0].userId !== ctx.cairnUserId workoutCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ throw new TRPCError({
code: "FORBIDDEN", code: "FORBIDDEN",
@@ -2076,7 +2076,7 @@ export const cairnDbRouter = createTRPCRouter({
}); });
if ( if (
!workoutCheck.rows.length || !workoutCheck.rows.length ||
workoutCheck.rows[0].userId !== ctx.cairnUserId workoutCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ throw new TRPCError({
code: "FORBIDDEN", code: "FORBIDDEN",
@@ -2111,7 +2111,7 @@ export const cairnDbRouter = createTRPCRouter({
}); });
if ( if (
!workoutCheck.rows.length || !workoutCheck.rows.length ||
workoutCheck.rows[0].userId !== ctx.cairnUserId workoutCheck.rows[0].userId !== ctx.nessaUserId
) { ) {
throw new TRPCError({ throw new TRPCError({
code: "FORBIDDEN", code: "FORBIDDEN",
@@ -2141,7 +2141,7 @@ export const cairnDbRouter = 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) { if (provider.userId !== ctx.nessaUserId) {
throw new TRPCError({ throw new TRPCError({
code: "FORBIDDEN", code: "FORBIDDEN",
message: "User mismatch" message: "User mismatch"

View File

@@ -2,14 +2,14 @@ import { initTRPC, TRPCError } from "@trpc/server";
import type { APIEvent } from "@solidjs/start/server"; import type { APIEvent } from "@solidjs/start/server";
import { logVisit, enrichAnalyticsEntry } from "~/server/analytics"; import { logVisit, enrichAnalyticsEntry } from "~/server/analytics";
import { getRequestIP } from "vinxi/http"; import { getRequestIP } from "vinxi/http";
import { verifyCairnToken } from "~/server/cairn-auth"; import { verifyNessaToken } from "~/server/nessa-auth";
import { getAuthPayloadFromEvent } from "~/server/auth"; import { getAuthPayloadFromEvent } from "~/server/auth";
export type Context = { export type Context = {
event: APIEvent; event: APIEvent;
userId: string | null; userId: string | null;
isAdmin: boolean; isAdmin: boolean;
cairnUserId: string | null; nessaUserId: string | null;
}; };
/** Safely get a header value from either Fetch API Headers or Node.js IncomingHttpHeaders */ /** Safely get a header value from either Fetch API Headers or Node.js IncomingHttpHeaders */
@@ -59,14 +59,14 @@ async function createContextInner(event: APIEvent): Promise<Context> {
getHeader(event.request?.headers, "authorization") || getHeader(event.request?.headers, "authorization") ||
null; null;
let cairnUserId: string | null = null; let nessaUserId: string | null = null;
if (authHeader && authHeader.startsWith("Bearer ")) { if (authHeader && authHeader.startsWith("Bearer ")) {
const token = authHeader.replace("Bearer ", "").trim(); const token = authHeader.replace("Bearer ", "").trim();
try { try {
const payload = await verifyCairnToken(token); const payload = await verifyNessaToken(token);
cairnUserId = payload.sub; nessaUserId = payload.sub;
} catch (error) { } catch (error) {
console.error("Cairn JWT verification failed:", error); console.error("Nessa JWT verification failed:", error);
} }
} }
@@ -88,7 +88,7 @@ async function createContextInner(event: APIEvent): Promise<Context> {
event, event,
userId, userId,
isAdmin, isAdmin,
cairnUserId nessaUserId
}; };
} }
@@ -128,21 +128,21 @@ const enforceUserIsAdmin = t.middleware(({ ctx, next }) => {
}); });
}); });
const enforceCairnUser = t.middleware(({ ctx, next }) => { const enforceNessaUser = t.middleware(({ ctx, next }) => {
if (!ctx.cairnUserId) { if (!ctx.nessaUserId) {
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "Cairn authentication required" message: "Nessa authentication required"
}); });
} }
return next({ return next({
ctx: { ctx: {
...ctx, ...ctx,
cairnUserId: ctx.cairnUserId nessaUserId: ctx.nessaUserId
} }
}); });
}); });
export const protectedProcedure = t.procedure.use(enforceUserIsAuthed); export const protectedProcedure = t.procedure.use(enforceUserIsAuthed);
export const adminProcedure = t.procedure.use(enforceUserIsAdmin); export const adminProcedure = t.procedure.use(enforceUserIsAdmin);
export const cairnProcedure = t.procedure.use(enforceCairnUser); export const nessaProcedure = t.procedure.use(enforceNessaUser);

View File

@@ -14,7 +14,7 @@ import {
let mainDBConnection: ReturnType<typeof createClient> | null = null; let mainDBConnection: ReturnType<typeof createClient> | null = null;
let lineageDBConnection: ReturnType<typeof createClient> | null = null; let lineageDBConnection: ReturnType<typeof createClient> | null = null;
let cairnDBConnection: ReturnType<typeof createClient> | null = null; let nessaDBConnection: ReturnType<typeof createClient> | null = null;
export function ConnectionFactory() { export function ConnectionFactory() {
if (!mainDBConnection) { if (!mainDBConnection) {
@@ -38,15 +38,15 @@ export function LineageConnectionFactory() {
return lineageDBConnection; return lineageDBConnection;
} }
export function CairnConnectionFactory() { export function NessaConnectionFactory() {
if (!cairnDBConnection) { if (!nessaDBConnection) {
const config = { const config = {
url: env.CAIRN_DB_URL, url: env.NESSA_DB_URL,
authToken: env.CAIRN_DB_TOKEN authToken: env.NESSA_DB_TOKEN
}; };
cairnDBConnection = createClient(config); nessaDBConnection = createClient(config);
} }
return cairnDBConnection; return nessaDBConnection;
} }
export async function LineageDBInit() { export async function LineageDBInit() {

View File

@@ -1,24 +1,24 @@
import { SignJWT, jwtVerify } from "jose"; import { SignJWT, jwtVerify } from "jose";
import { env } from "~/env/server"; import { env } from "~/env/server";
const CAIRN_JWT_EXPIRY = "30d"; const NESSA_JWT_EXPIRY = "30d";
export type CairnAuthPayload = { export type NessaAuthPayload = {
sub: string; sub: string;
exp?: number; exp?: number;
iat?: number; iat?: number;
}; };
export async function verifyCairnToken( export async function verifyNessaToken(
token: string token: string
): Promise<CairnAuthPayload> { ): Promise<NessaAuthPayload> {
const secret = new TextEncoder().encode(env.CAIRN_JWT_SECRET); const secret = new TextEncoder().encode(env.NESSA_JWT_SECRET);
const { payload } = await jwtVerify(token, secret, { const { payload } = await jwtVerify(token, secret, {
algorithms: ["HS256"] algorithms: ["HS256"]
}); });
if (!payload.sub) { if (!payload.sub) {
throw new Error("Missing subject in Cairn JWT"); throw new Error("Missing subject in Nessa JWT");
} }
return { return {
@@ -28,12 +28,12 @@ export async function verifyCairnToken(
}; };
} }
export async function signCairnToken(userId: string): Promise<string> { export async function signNessaToken(userId: string): Promise<string> {
const secret = new TextEncoder().encode(env.CAIRN_JWT_SECRET); const secret = new TextEncoder().encode(env.NESSA_JWT_SECRET);
return new SignJWT({}) return new SignJWT({})
.setProtectedHeader({ alg: "HS256" }) .setProtectedHeader({ alg: "HS256" })
.setSubject(userId) .setSubject(userId)
.setIssuedAt() .setIssuedAt()
.setExpirationTime(CAIRN_JWT_EXPIRY) .setExpirationTime(NESSA_JWT_EXPIRY)
.sign(secret); .sign(secret);
} }