feat(16-03): wire userId from context into all route handlers
- Extract userId via c.get('userId') in every route handler
- Pass userId to all service function calls as second argument
- Update settings routes to use composite key [userId, key]
- Update Env type to include userId in Variables
- Auth routes pass userId to API key management functions
This commit is contained in:
@@ -10,7 +10,7 @@ import {
|
||||
listApiKeys,
|
||||
} from "../services/auth.service.ts";
|
||||
|
||||
type Env = { Variables: { db?: any } };
|
||||
type Env = { Variables: { db?: any; userId?: number } };
|
||||
|
||||
const createKeySchema = z.object({ name: z.string().min(1) });
|
||||
|
||||
@@ -33,7 +33,8 @@ app.get("/me", async (c) => {
|
||||
|
||||
app.get("/keys", requireAuth, async (c) => {
|
||||
const db = c.get("db");
|
||||
const keys = await listApiKeys(db);
|
||||
const userId = c.get("userId")!;
|
||||
const keys = await listApiKeys(db, userId);
|
||||
return c.json(keys);
|
||||
});
|
||||
|
||||
@@ -43,8 +44,9 @@ app.post(
|
||||
zValidator("json", createKeySchema),
|
||||
async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const { name } = c.req.valid("json");
|
||||
const result = await createApiKey(db, name);
|
||||
const result = await createApiKey(db, userId, name);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
@@ -60,9 +62,10 @@ app.post(
|
||||
|
||||
app.delete("/keys/:id", requireAuth, async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (!id) return c.json({ error: "Invalid key ID" }, 400);
|
||||
await deleteApiKey(db, id);
|
||||
await deleteApiKey(db, userId, id);
|
||||
return c.json({ ok: true });
|
||||
});
|
||||
|
||||
|
||||
@@ -12,20 +12,22 @@ import {
|
||||
updateCategory,
|
||||
} from "../services/category.service.ts";
|
||||
|
||||
type Env = { Variables: { db?: any } };
|
||||
type Env = { Variables: { db?: any; userId?: number } };
|
||||
|
||||
const app = new Hono<Env>();
|
||||
|
||||
app.get("/", async (c) => {
|
||||
const db = c.get("db");
|
||||
const cats = await getAllCategories(db);
|
||||
const userId = c.get("userId")!;
|
||||
const cats = await getAllCategories(db, userId);
|
||||
return c.json(cats);
|
||||
});
|
||||
|
||||
app.post("/", zValidator("json", createCategorySchema), async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const data = c.req.valid("json");
|
||||
const cat = await createCategory(db, data);
|
||||
const cat = await createCategory(db, userId, data);
|
||||
return c.json(cat, 201);
|
||||
});
|
||||
|
||||
@@ -34,10 +36,11 @@ app.put(
|
||||
zValidator("json", updateCategorySchema.omit({ id: true })),
|
||||
async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (!id) return c.json({ error: "Invalid category ID" }, 400);
|
||||
const data = c.req.valid("json");
|
||||
const cat = await updateCategory(db, id, data);
|
||||
const cat = await updateCategory(db, userId, id, data);
|
||||
if (!cat) return c.json({ error: "Category not found" }, 404);
|
||||
return c.json(cat);
|
||||
},
|
||||
@@ -45,9 +48,10 @@ app.put(
|
||||
|
||||
app.delete("/:id", async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (!id) return c.json({ error: "Invalid category ID" }, 400);
|
||||
const result = await deleteCategory(db, id);
|
||||
const result = await deleteCategory(db, userId, id);
|
||||
|
||||
if (!result.success) {
|
||||
if (result.error === "Cannot delete the Uncategorized category") {
|
||||
|
||||
@@ -14,13 +14,14 @@ import {
|
||||
updateItem,
|
||||
} from "../services/item.service.ts";
|
||||
|
||||
type Env = { Variables: { db?: any } };
|
||||
type Env = { Variables: { db?: any; userId?: number } };
|
||||
|
||||
const app = new Hono<Env>();
|
||||
|
||||
app.get("/export", async (c) => {
|
||||
const db = c.get("db");
|
||||
const csv = await exportItemsCsv(db);
|
||||
const userId = c.get("userId")!;
|
||||
const csv = await exportItemsCsv(db, userId);
|
||||
c.header("Content-Type", "text/csv");
|
||||
c.header("Content-Disposition", 'attachment; filename="gearbox-export.csv"');
|
||||
return c.body(csv);
|
||||
@@ -28,6 +29,7 @@ app.get("/export", async (c) => {
|
||||
|
||||
app.post("/import", async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const body = await c.req.parseBody();
|
||||
// Accept either "file" (direct) or "image" (via apiUpload helper)
|
||||
const file = body.file ?? body.image;
|
||||
@@ -35,29 +37,32 @@ app.post("/import", async (c) => {
|
||||
return c.json({ error: "No CSV file provided" }, 400);
|
||||
}
|
||||
const content = await file.text();
|
||||
const result = await importItemsCsv(db, content);
|
||||
const result = await importItemsCsv(db, userId, content);
|
||||
return c.json(result);
|
||||
});
|
||||
|
||||
app.get("/", async (c) => {
|
||||
const db = c.get("db");
|
||||
const items = await getAllItems(db);
|
||||
const userId = c.get("userId")!;
|
||||
const items = await getAllItems(db, userId);
|
||||
return c.json(items);
|
||||
});
|
||||
|
||||
app.get("/:id", async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (!id) return c.json({ error: "Invalid item ID" }, 400);
|
||||
const item = await getItemById(db, id);
|
||||
const item = await getItemById(db, userId, id);
|
||||
if (!item) return c.json({ error: "Item not found" }, 404);
|
||||
return c.json(item);
|
||||
});
|
||||
|
||||
app.post("/", zValidator("json", createItemSchema), async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const data = c.req.valid("json");
|
||||
const item = await createItem(db, data);
|
||||
const item = await createItem(db, userId, data);
|
||||
return c.json(item, 201);
|
||||
});
|
||||
|
||||
@@ -66,10 +71,11 @@ app.put(
|
||||
zValidator("json", updateItemSchema.omit({ id: true })),
|
||||
async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (!id) return c.json({ error: "Invalid item ID" }, 400);
|
||||
const data = c.req.valid("json");
|
||||
const item = await updateItem(db, id, data);
|
||||
const item = await updateItem(db, userId, id, data);
|
||||
if (!item) return c.json({ error: "Item not found" }, 404);
|
||||
return c.json(item);
|
||||
},
|
||||
@@ -77,18 +83,20 @@ app.put(
|
||||
|
||||
app.post("/:id/duplicate", async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (!id) return c.json({ error: "Invalid item ID" }, 400);
|
||||
const newItem = await duplicateItem(db, id);
|
||||
const newItem = await duplicateItem(db, userId, id);
|
||||
if (!newItem) return c.json({ error: "Item not found" }, 404);
|
||||
return c.json(newItem, 201);
|
||||
});
|
||||
|
||||
app.delete("/:id", async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (!id) return c.json({ error: "Invalid item ID" }, 400);
|
||||
const deleted = await deleteItem(db, id);
|
||||
const deleted = await deleteItem(db, userId, id);
|
||||
if (!deleted) return c.json({ error: "Item not found" }, 404);
|
||||
|
||||
// Clean up image file if exists
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { Hono } from "hono";
|
||||
import { settings } from "../../db/schema.ts";
|
||||
|
||||
type Env = { Variables: { db?: any } };
|
||||
type Env = { Variables: { db?: any; userId?: number } };
|
||||
|
||||
const app = new Hono<Env>();
|
||||
|
||||
app.get("/:key", async (c) => {
|
||||
const database = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const key = c.req.param("key");
|
||||
const [row] = await database
|
||||
.select()
|
||||
.from(settings)
|
||||
.where(eq(settings.key, key));
|
||||
.where(and(eq(settings.userId, userId), eq(settings.key, key)));
|
||||
if (!row) return c.json({ error: "Setting not found" }, 404);
|
||||
return c.json(row);
|
||||
});
|
||||
|
||||
app.put("/:key", async (c) => {
|
||||
const database = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const key = c.req.param("key");
|
||||
const body = await c.req.json<{ value: string }>();
|
||||
|
||||
@@ -28,13 +30,16 @@ app.put("/:key", async (c) => {
|
||||
|
||||
await database
|
||||
.insert(settings)
|
||||
.values({ key, value: body.value })
|
||||
.onConflictDoUpdate({ target: settings.key, set: { value: body.value } });
|
||||
.values({ userId, key, value: body.value })
|
||||
.onConflictDoUpdate({
|
||||
target: [settings.userId, settings.key],
|
||||
set: { value: body.value },
|
||||
});
|
||||
|
||||
const [row] = await database
|
||||
.select()
|
||||
.from(settings)
|
||||
.where(eq(settings.key, key));
|
||||
.where(and(eq(settings.userId, userId), eq(settings.key, key)));
|
||||
return c.json(row);
|
||||
});
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
updateSetup,
|
||||
} from "../services/setup.service.ts";
|
||||
|
||||
type Env = { Variables: { db?: any } };
|
||||
type Env = { Variables: { db?: any; userId?: number } };
|
||||
|
||||
const app = new Hono<Env>();
|
||||
|
||||
@@ -26,41 +26,46 @@ const app = new Hono<Env>();
|
||||
|
||||
app.get("/", async (c) => {
|
||||
const db = c.get("db");
|
||||
const setups = await getAllSetups(db);
|
||||
const userId = c.get("userId")!;
|
||||
const setups = await getAllSetups(db, userId);
|
||||
return c.json(setups);
|
||||
});
|
||||
|
||||
app.post("/", zValidator("json", createSetupSchema), async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const data = c.req.valid("json");
|
||||
const setup = await createSetup(db, data);
|
||||
const setup = await createSetup(db, userId, data);
|
||||
return c.json(setup, 201);
|
||||
});
|
||||
|
||||
app.get("/:id", async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (!id) return c.json({ error: "Invalid setup ID" }, 400);
|
||||
const setup = await getSetupWithItems(db, id);
|
||||
const setup = await getSetupWithItems(db, userId, id);
|
||||
if (!setup) return c.json({ error: "Setup not found" }, 404);
|
||||
return c.json(setup);
|
||||
});
|
||||
|
||||
app.put("/:id", zValidator("json", updateSetupSchema), async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (!id) return c.json({ error: "Invalid setup ID" }, 400);
|
||||
const data = c.req.valid("json");
|
||||
const setup = await updateSetup(db, id, data);
|
||||
const setup = await updateSetup(db, userId, id, data);
|
||||
if (!setup) return c.json({ error: "Setup not found" }, 404);
|
||||
return c.json(setup);
|
||||
});
|
||||
|
||||
app.delete("/:id", async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (!id) return c.json({ error: "Invalid setup ID" }, 400);
|
||||
const deleted = await deleteSetup(db, id);
|
||||
const deleted = await deleteSetup(db, userId, id);
|
||||
if (!deleted) return c.json({ error: "Setup not found" }, 404);
|
||||
return c.json({ success: true });
|
||||
});
|
||||
@@ -69,14 +74,15 @@ app.delete("/:id", async (c) => {
|
||||
|
||||
app.put("/:id/items", zValidator("json", syncSetupItemsSchema), async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (!id) return c.json({ error: "Invalid setup ID" }, 400);
|
||||
const { itemIds } = c.req.valid("json");
|
||||
|
||||
const setup = await getSetupWithItems(db, id);
|
||||
const setup = await getSetupWithItems(db, userId, id);
|
||||
if (!setup) return c.json({ error: "Setup not found" }, 404);
|
||||
|
||||
await syncSetupItems(db, id, itemIds);
|
||||
await syncSetupItems(db, userId, id, itemIds);
|
||||
return c.json({ success: true });
|
||||
});
|
||||
|
||||
@@ -85,21 +91,23 @@ app.patch(
|
||||
zValidator("json", updateClassificationSchema),
|
||||
async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const setupId = parseId(c.req.param("id"));
|
||||
const itemId = parseId(c.req.param("itemId"));
|
||||
if (!setupId || !itemId) return c.json({ error: "Invalid ID" }, 400);
|
||||
const { classification } = c.req.valid("json");
|
||||
await updateItemClassification(db, setupId, itemId, classification);
|
||||
await updateItemClassification(db, userId, setupId, itemId, classification);
|
||||
return c.json({ success: true });
|
||||
},
|
||||
);
|
||||
|
||||
app.delete("/:id/items/:itemId", async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const setupId = parseId(c.req.param("id"));
|
||||
const itemId = parseId(c.req.param("itemId"));
|
||||
if (!setupId || !itemId) return c.json({ error: "Invalid ID" }, 400);
|
||||
await removeSetupItem(db, setupId, itemId);
|
||||
await removeSetupItem(db, userId, setupId, itemId);
|
||||
return c.json({ success: true });
|
||||
});
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
updateThread,
|
||||
} from "../services/thread.service.ts";
|
||||
|
||||
type Env = { Variables: { db?: any } };
|
||||
type Env = { Variables: { db?: any; userId?: number } };
|
||||
|
||||
const app = new Hono<Env>();
|
||||
|
||||
@@ -32,42 +32,47 @@ const app = new Hono<Env>();
|
||||
|
||||
app.get("/", async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const includeResolved = c.req.query("includeResolved") === "true";
|
||||
const threads = await getAllThreads(db, includeResolved);
|
||||
const threads = await getAllThreads(db, userId, includeResolved);
|
||||
return c.json(threads);
|
||||
});
|
||||
|
||||
app.post("/", zValidator("json", createThreadSchema), async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const data = c.req.valid("json");
|
||||
const thread = await createThread(db, data);
|
||||
const thread = await createThread(db, userId, data);
|
||||
return c.json(thread, 201);
|
||||
});
|
||||
|
||||
app.get("/:id", async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (!id) return c.json({ error: "Invalid thread ID" }, 400);
|
||||
const thread = await getThreadWithCandidates(db, id);
|
||||
const thread = await getThreadWithCandidates(db, userId, id);
|
||||
if (!thread) return c.json({ error: "Thread not found" }, 404);
|
||||
return c.json(thread);
|
||||
});
|
||||
|
||||
app.put("/:id", zValidator("json", updateThreadSchema), async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (!id) return c.json({ error: "Invalid thread ID" }, 400);
|
||||
const data = c.req.valid("json");
|
||||
const thread = await updateThread(db, id, data);
|
||||
const thread = await updateThread(db, userId, id, data);
|
||||
if (!thread) return c.json({ error: "Thread not found" }, 404);
|
||||
return c.json(thread);
|
||||
});
|
||||
|
||||
app.delete("/:id", async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const id = parseId(c.req.param("id"));
|
||||
if (!id) return c.json({ error: "Invalid thread ID" }, 400);
|
||||
const deleted = await deleteThread(db, id);
|
||||
const deleted = await deleteThread(db, userId, id);
|
||||
if (!deleted) return c.json({ error: "Thread not found" }, 404);
|
||||
|
||||
// Clean up candidate image files
|
||||
@@ -89,15 +94,16 @@ app.post(
|
||||
zValidator("json", createCandidateSchema),
|
||||
async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const threadId = parseId(c.req.param("id"));
|
||||
if (!threadId) return c.json({ error: "Invalid thread ID" }, 400);
|
||||
|
||||
// Verify thread exists
|
||||
const thread = await getThreadWithCandidates(db, threadId);
|
||||
const thread = await getThreadWithCandidates(db, userId, threadId);
|
||||
if (!thread) return c.json({ error: "Thread not found" }, 404);
|
||||
|
||||
const data = c.req.valid("json");
|
||||
const candidate = await createCandidate(db, threadId, data);
|
||||
const candidate = await createCandidate(db, userId, threadId, data);
|
||||
return c.json(candidate, 201);
|
||||
},
|
||||
);
|
||||
@@ -107,10 +113,11 @@ app.put(
|
||||
zValidator("json", updateCandidateSchema),
|
||||
async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const candidateId = parseId(c.req.param("candidateId"));
|
||||
if (!candidateId) return c.json({ error: "Invalid candidate ID" }, 400);
|
||||
const data = c.req.valid("json");
|
||||
const candidate = await updateCandidate(db, candidateId, data);
|
||||
const candidate = await updateCandidate(db, userId, candidateId, data);
|
||||
if (!candidate) return c.json({ error: "Candidate not found" }, 404);
|
||||
return c.json(candidate);
|
||||
},
|
||||
@@ -118,9 +125,10 @@ app.put(
|
||||
|
||||
app.delete("/:threadId/candidates/:candidateId", async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const candidateId = parseId(c.req.param("candidateId"));
|
||||
if (!candidateId) return c.json({ error: "Invalid candidate ID" }, 400);
|
||||
const deleted = await deleteCandidate(db, candidateId);
|
||||
const deleted = await deleteCandidate(db, userId, candidateId);
|
||||
if (!deleted) return c.json({ error: "Candidate not found" }, 404);
|
||||
|
||||
// Clean up image file if exists
|
||||
@@ -142,10 +150,11 @@ app.patch(
|
||||
zValidator("json", reorderCandidatesSchema),
|
||||
async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const threadId = parseId(c.req.param("id"));
|
||||
if (!threadId) return c.json({ error: "Invalid thread ID" }, 400);
|
||||
const { orderedIds } = c.req.valid("json");
|
||||
const result = await reorderCandidates(db, threadId, orderedIds);
|
||||
const result = await reorderCandidates(db, userId, threadId, orderedIds);
|
||||
if (!result.success) return c.json({ error: result.error }, 400);
|
||||
return c.json({ success: true });
|
||||
},
|
||||
@@ -155,11 +164,12 @@ app.patch(
|
||||
|
||||
app.post("/:id/resolve", zValidator("json", resolveThreadSchema), async (c) => {
|
||||
const db = c.get("db");
|
||||
const userId = c.get("userId")!;
|
||||
const threadId = parseId(c.req.param("id"));
|
||||
if (!threadId) return c.json({ error: "Invalid thread ID" }, 400);
|
||||
const { candidateId } = c.req.valid("json");
|
||||
|
||||
const result = await resolveThread(db, threadId, candidateId);
|
||||
const result = await resolveThread(db, userId, threadId, candidateId);
|
||||
if (!result.success) {
|
||||
return c.json({ error: result.error }, 400);
|
||||
}
|
||||
|
||||
@@ -4,14 +4,15 @@ import {
|
||||
getGlobalTotals,
|
||||
} from "../services/totals.service.ts";
|
||||
|
||||
type Env = { Variables: { db?: any } };
|
||||
type Env = { Variables: { db?: any; userId?: number } };
|
||||
|
||||
const app = new Hono<Env>();
|
||||
|
||||
app.get("/", async (c) => {
|
||||
const db = c.get("db");
|
||||
const categoryTotals = await getCategoryTotals(db);
|
||||
const globalTotals = await getGlobalTotals(db);
|
||||
const userId = c.get("userId")!;
|
||||
const categoryTotals = await getCategoryTotals(db, userId);
|
||||
const globalTotals = await getGlobalTotals(db, userId);
|
||||
return c.json({ categories: categoryTotals, global: globalTotals });
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user