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
|
// GET /api/campaigns
|
||||||
router.get("/", async (_req, res) => {
|
router.get("/", async (_req, res) => {
|
||||||
|
try {
|
||||||
const [rows] = await db.execute<RowDataPacket[]>(
|
const [rows] = await db.execute<RowDataPacket[]>(
|
||||||
"SELECT * FROM campaigns ORDER BY created_at DESC"
|
"SELECT * FROM campaigns ORDER BY created_at DESC"
|
||||||
);
|
);
|
||||||
res.json(rows);
|
res.json(rows);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).json({ error: "Internal server error" });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// POST /api/campaigns
|
// POST /api/campaigns
|
||||||
router.post("/", async (req, res) => {
|
router.post("/", async (req, res) => {
|
||||||
|
try {
|
||||||
const { name } = req.body;
|
const { name } = req.body;
|
||||||
if (!name?.trim()) {
|
if (!name?.trim()) {
|
||||||
res.status(400).json({ error: "Campaign name is required" });
|
res.status(400).json({ error: "Campaign name is required" });
|
||||||
|
|
@ -28,10 +34,15 @@ router.post("/", async (req, res) => {
|
||||||
[result.insertId]
|
[result.insertId]
|
||||||
);
|
);
|
||||||
res.status(201).json(rows[0]);
|
res.status(201).json(rows[0]);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).json({ error: "Internal server error" });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// GET /api/campaigns/:id
|
// GET /api/campaigns/:id
|
||||||
router.get("/:id", async (req, res) => {
|
router.get("/:id", async (req, res) => {
|
||||||
|
try {
|
||||||
const [rows] = await db.execute<RowDataPacket[]>(
|
const [rows] = await db.execute<RowDataPacket[]>(
|
||||||
"SELECT * FROM campaigns WHERE id = ?",
|
"SELECT * FROM campaigns WHERE id = ?",
|
||||||
[req.params.id]
|
[req.params.id]
|
||||||
|
|
@ -41,10 +52,15 @@ router.get("/:id", async (req, res) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
res.json(rows[0]);
|
res.json(rows[0]);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).json({ error: "Internal server error" });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// DELETE /api/campaigns/:id
|
// DELETE /api/campaigns/:id
|
||||||
router.delete("/:id", async (req, res) => {
|
router.delete("/:id", async (req, res) => {
|
||||||
|
try {
|
||||||
const [result] = await db.execute<ResultSetHeader>(
|
const [result] = await db.execute<ResultSetHeader>(
|
||||||
"DELETE FROM campaigns WHERE id = ?",
|
"DELETE FROM campaigns WHERE id = ?",
|
||||||
[req.params.id]
|
[req.params.id]
|
||||||
|
|
@ -54,6 +70,10 @@ router.delete("/:id", async (req, res) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
res.status(204).end();
|
res.status(204).end();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).json({ error: "Internal server error" });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,26 @@
|
||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
import type { RowDataPacket } from "mysql2";
|
import type { RowDataPacket } from "mysql2";
|
||||||
import db from "../db.js";
|
import db from "../db.js";
|
||||||
|
import { parseJson } from "../utils/parseJson.js";
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
|
// GET /api/game-items
|
||||||
router.get("/", async (_req, res) => {
|
router.get("/", async (_req, res) => {
|
||||||
|
try {
|
||||||
const [rows] = await db.execute<RowDataPacket[]>(
|
const [rows] = await db.execute<RowDataPacket[]>(
|
||||||
"SELECT * FROM game_items ORDER BY type, name"
|
"SELECT * FROM game_items ORDER BY type, name"
|
||||||
);
|
);
|
||||||
const parsed = rows.map((item) => ({
|
const parsed = rows.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
effects: JSON.parse(item.effects as string),
|
effects: parseJson(item.effects),
|
||||||
properties: JSON.parse(item.properties as string),
|
properties: parseJson(item.properties),
|
||||||
}));
|
}));
|
||||||
res.json(parsed);
|
res.json(parsed);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).json({ error: "Internal server error" });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,25 @@
|
||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
import type { RowDataPacket } from "mysql2";
|
import type { RowDataPacket } from "mysql2";
|
||||||
import db from "../db.js";
|
import db from "../db.js";
|
||||||
|
import { parseJson } from "../utils/parseJson.js";
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
|
// GET /api/game-talents
|
||||||
router.get("/", async (_req, res) => {
|
router.get("/", async (_req, res) => {
|
||||||
|
try {
|
||||||
const [rows] = await db.execute<RowDataPacket[]>(
|
const [rows] = await db.execute<RowDataPacket[]>(
|
||||||
"SELECT * FROM game_talents ORDER BY source, name"
|
"SELECT * FROM game_talents ORDER BY source, name"
|
||||||
);
|
);
|
||||||
const parsed = rows.map((t) => ({
|
const parsed = rows.map((t) => ({
|
||||||
...t,
|
...t,
|
||||||
effect: JSON.parse(t.effect as string),
|
effect: parseJson(t.effect),
|
||||||
}));
|
}));
|
||||||
res.json(parsed);
|
res.json(parsed);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).json({ error: "Internal server error" });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,33 @@
|
||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
|
import type { ParamsDictionary } from "express-serve-static-core";
|
||||||
import type { RowDataPacket } from "mysql2";
|
import type { RowDataPacket } from "mysql2";
|
||||||
import db from "../db.js";
|
import db from "../db.js";
|
||||||
|
import { parseJson } from "../utils/parseJson.js";
|
||||||
|
|
||||||
|
type CampaignParams = ParamsDictionary & { campaignId: string };
|
||||||
|
|
||||||
const router = Router({ mergeParams: true });
|
const router = Router({ mergeParams: true });
|
||||||
|
|
||||||
router.get("/", async (req: any, res) => {
|
// GET /api/campaigns/:campaignId/rolls
|
||||||
const { campaignId } = req.params as { campaignId: string };
|
router.get<CampaignParams>("/", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { campaignId } = req.params;
|
||||||
const [rows] = await db.execute<RowDataPacket[]>(
|
const [rows] = await db.execute<RowDataPacket[]>(
|
||||||
"SELECT * FROM roll_log WHERE campaign_id = ? ORDER BY created_at DESC LIMIT 50",
|
"SELECT * FROM roll_log WHERE campaign_id = ? ORDER BY created_at DESC LIMIT 50",
|
||||||
[campaignId]
|
[campaignId]
|
||||||
);
|
);
|
||||||
const parsed = rows.map((r) => ({
|
const parsed = rows.map((r) => ({
|
||||||
...r,
|
...r,
|
||||||
rolls: JSON.parse(r.rolls as string),
|
rolls: (parseJson(r.rolls) as unknown) as unknown[],
|
||||||
advantage: r.advantage === 1,
|
advantage: r.advantage === 1,
|
||||||
disadvantage: r.disadvantage === 1,
|
disadvantage: r.disadvantage === 1,
|
||||||
nat20: r.nat20 === 1,
|
nat20: r.nat20 === 1,
|
||||||
}));
|
}));
|
||||||
res.json(parsed);
|
res.json(parsed);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).json({ error: "Internal server error" });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
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