fix: add error handling and safe JSON parsing to route handlers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
268997a009
commit
2812d81979
5 changed files with 118 additions and 64 deletions
|
|
@ -6,14 +6,20 @@ const router = Router();
|
|||
|
||||
// GET /api/campaigns
|
||||
router.get("/", async (_req, res) => {
|
||||
try {
|
||||
const [rows] = await db.execute<RowDataPacket[]>(
|
||||
"SELECT * FROM campaigns ORDER BY created_at DESC"
|
||||
);
|
||||
res.json(rows);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: "Internal server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/campaigns
|
||||
router.post("/", async (req, res) => {
|
||||
try {
|
||||
const { name } = req.body;
|
||||
if (!name?.trim()) {
|
||||
res.status(400).json({ error: "Campaign name is required" });
|
||||
|
|
@ -28,10 +34,15 @@ router.post("/", async (req, res) => {
|
|||
[result.insertId]
|
||||
);
|
||||
res.status(201).json(rows[0]);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: "Internal server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// GET /api/campaigns/:id
|
||||
router.get("/:id", async (req, res) => {
|
||||
try {
|
||||
const [rows] = await db.execute<RowDataPacket[]>(
|
||||
"SELECT * FROM campaigns WHERE id = ?",
|
||||
[req.params.id]
|
||||
|
|
@ -41,10 +52,15 @@ router.get("/:id", async (req, res) => {
|
|||
return;
|
||||
}
|
||||
res.json(rows[0]);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: "Internal server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE /api/campaigns/:id
|
||||
router.delete("/:id", async (req, res) => {
|
||||
try {
|
||||
const [result] = await db.execute<ResultSetHeader>(
|
||||
"DELETE FROM campaigns WHERE id = ?",
|
||||
[req.params.id]
|
||||
|
|
@ -54,6 +70,10 @@ router.delete("/:id", async (req, res) => {
|
|||
return;
|
||||
}
|
||||
res.status(204).end();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: "Internal server error" });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,26 @@
|
|||
import { Router } from "express";
|
||||
import type { RowDataPacket } from "mysql2";
|
||||
import db from "../db.js";
|
||||
import { parseJson } from "../utils/parseJson.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// GET /api/game-items
|
||||
router.get("/", async (_req, res) => {
|
||||
try {
|
||||
const [rows] = await db.execute<RowDataPacket[]>(
|
||||
"SELECT * FROM game_items ORDER BY type, name"
|
||||
);
|
||||
const parsed = rows.map((item) => ({
|
||||
...item,
|
||||
effects: JSON.parse(item.effects as string),
|
||||
properties: JSON.parse(item.properties as string),
|
||||
effects: parseJson(item.effects),
|
||||
properties: parseJson(item.properties),
|
||||
}));
|
||||
res.json(parsed);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: "Internal server error" });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,25 @@
|
|||
import { Router } from "express";
|
||||
import type { RowDataPacket } from "mysql2";
|
||||
import db from "../db.js";
|
||||
import { parseJson } from "../utils/parseJson.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// GET /api/game-talents
|
||||
router.get("/", async (_req, res) => {
|
||||
try {
|
||||
const [rows] = await db.execute<RowDataPacket[]>(
|
||||
"SELECT * FROM game_talents ORDER BY source, name"
|
||||
);
|
||||
const parsed = rows.map((t) => ({
|
||||
...t,
|
||||
effect: JSON.parse(t.effect as string),
|
||||
effect: parseJson(t.effect),
|
||||
}));
|
||||
res.json(parsed);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: "Internal server error" });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
|
|
|||
|
|
@ -1,23 +1,33 @@
|
|||
import { Router } from "express";
|
||||
import type { ParamsDictionary } from "express-serve-static-core";
|
||||
import type { RowDataPacket } from "mysql2";
|
||||
import db from "../db.js";
|
||||
import { parseJson } from "../utils/parseJson.js";
|
||||
|
||||
type CampaignParams = ParamsDictionary & { campaignId: string };
|
||||
|
||||
const router = Router({ mergeParams: true });
|
||||
|
||||
router.get("/", async (req: any, res) => {
|
||||
const { campaignId } = req.params as { campaignId: string };
|
||||
// GET /api/campaigns/:campaignId/rolls
|
||||
router.get<CampaignParams>("/", async (req, res) => {
|
||||
try {
|
||||
const { campaignId } = req.params;
|
||||
const [rows] = await db.execute<RowDataPacket[]>(
|
||||
"SELECT * FROM roll_log WHERE campaign_id = ? ORDER BY created_at DESC LIMIT 50",
|
||||
[campaignId]
|
||||
);
|
||||
const parsed = rows.map((r) => ({
|
||||
...r,
|
||||
rolls: JSON.parse(r.rolls as string),
|
||||
rolls: (parseJson(r.rolls) as unknown) as unknown[],
|
||||
advantage: r.advantage === 1,
|
||||
disadvantage: r.disadvantage === 1,
|
||||
nat20: r.nat20 === 1,
|
||||
}));
|
||||
res.json(parsed);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: "Internal server error" });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
|
|
|||
10
server/src/utils/parseJson.ts
Normal file
10
server/src/utils/parseJson.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
export function parseJson(val: unknown): Record<string, unknown> {
|
||||
if (typeof val === "string") {
|
||||
try {
|
||||
return JSON.parse(val);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return (val as Record<string, unknown>) ?? {};
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue