added commentary
This commit is contained in:
@@ -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'),
|
||||
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()
|
||||
})
|
||||
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(),
|
||||
});
|
||||
|
||||
@@ -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, () => {
|
||||
|
||||
87
src/routes/commentary.route.js
Normal file
87
src/routes/commentary.route.js
Normal 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
17
validation/commentary.js
Normal 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(),
|
||||
});
|
||||
Reference in New Issue
Block a user