added commentary

This commit is contained in:
2026-02-07 20:38:56 -06:00
parent 07b3f0d741
commit 13e917183f
4 changed files with 131 additions and 20 deletions

View File

@@ -1,20 +1,26 @@
import {
serial,
pgTable,
text,
integer,
jsonb,
timestamp,
} from "drizzle-orm/pg-core";
import { matches } from "./matches.js";
import { serial, pgTable, text, integer, jsonb, timestamp} from "drizzle-orm/pg-core";
import {matches} from './matches'
export const commentary = pgTable('commentary', {
id: serial('id').primaryKey(),
matchId: integer('match_id').notNull().references(()=> matches.id),
minute: integer('minute'),
sequence: integer('sequence'),
export const commentary = pgTable("commentary", {
id: serial("id").primaryKey(),
matchId: integer("match_id")
.notNull()
.references(() => matches.id),
minute: integer("minute"),
sequence: integer("sequence"),
period: text("period"),
eventType: text('event_type'),
actor: text('actor'),
team: text('team'),
message: text('message').notNull(),
metadata: jsonb('metadata'),
tags: text('tags').array(),
createdAt: timestamp('created_at').notNull().defaultNow()
})
eventType: text("event_type"),
actor: text("actor"),
team: text("team"),
message: text("message").notNull(),
metadata: jsonb("metadata"),
tags: text("tags").array(),
createdAt: timestamp("created_at").notNull().defaultNow(),
});

View File

@@ -5,6 +5,7 @@ import http from "http";
import { matchRouter } from "./routes/matches.route.js";
import { attachWebsocketServer } from "./ws/server.js";
import { securityMiddleware } from "./utils/arkjet.js";
import { comRouter } from "./routes/commentary.route.js";
const PORT = process.env.PORT || 8081;
const HOST = process.env.HOST || "0.0.0.0";
@@ -23,9 +24,9 @@ app.get("/", (_, res) => {
app.use(securityMiddleware());
app.use("/matches", matchRouter);
app.use("/matches/:id/commentary", comRouter);
const { broadcastMatchCreated } = attachWebsocketServer(server);
app.locals.broadcastMatchCreated = broadcastMatchCreated;
server.listen(PORT, HOST, () => {

View File

@@ -0,0 +1,87 @@
import { Router } from "express";
import { matchIdParamSchema } from "../../validation/matches.js";
import {
createCommentarySchema,
listCommentaryQuerySchema,
} from "../../validation/commentary.js";
import { db } from "../db/db.js";
import { commentary } from "../db/schema/commentary.js";
import { desc, eq } from "drizzle-orm";
export const comRouter = Router({ mergeParams: true }); // this is something we want when we are passing the params from somwhere else in the route.
const MAX_LIMIT = 100;
comRouter.get("/", async (req, res) => {
const paramsResult = matchIdParamSchema.safeParse(req.params);
if (!paramsResult.success) {
return res
.status(400)
.json({ error: "Invalid match ID.", details: paramsResult.error.issues });
}
const queryResult = listCommentaryQuerySchema.safeParse(req.query);
if (!queryResult.success) {
return res.status(400).json({
error: "Invalid query.",
details: queryResult.error.issues,
});
}
try {
const { id: matchId } = paramsResult.data;
const { limit = 10 } = queryResult.data;
const safeLimit = Math.min(limit, MAX_LIMIT);
const results = await db
.select()
.from(commentary)
.where(eq(commentary.matchId, matchId))
.orderBy(desc(commentary.createdAt))
.limit(safeLimit);
res.status(200).json({ data: results });
} catch (e) {
console.error("Failed to fetch commentary: ", e);
res.status(500).json({ error: "Failed to fetch commentary." });
}
});
comRouter.post("/", async (req, res) => {
const paramsResult = matchIdParamSchema.safeParse(req.params);
if (!paramsResult.success) {
return res
.status(400)
.json({ error: "Invalid match ID.", details: paramsResult.error.issues });
}
const bodyResult = createCommentarySchema.safeParse(req.body);
if (!bodyResult.success) {
return res.status(400).json({
error: "Invalid commentary payload.",
details: bodyResult.error.issues,
});
}
try {
const { minutes, ...rest } = bodyResult.data;
const [result] = await db
.insert(commentary)
.values({
matchId: paramsResult.data.id,
minutes,
...rest,
})
.returning();
res.status(201).json({ data: result });
} catch (e) {
console.error("Failed to create commentary:", e);
res.status(500).json({ error: "Failed to create commentary." });
}
});

17
validation/commentary.js Normal file
View File

@@ -0,0 +1,17 @@
import { z } from "zod";
export const listCommentaryQuerySchema = z.object({
limit: z.coerce.number().int().positive().max(100).optional(),
});
export const createCommentarySchema = z.object({
minutes: z.number().int().nonnegative(),
sequence: z.number().int().optional(),
period: z.string().optional(),
eventType: z.string().optional(),
actor: z.string().optional(),
team: z.string().optional(),
message: z.string().min(1),
metadata: z.record(z.string(), z.any()).optional(),
tags: z.array(z.string()).optional(),
});