Loading Dockerfile +4 −0 Original line number Diff line number Diff line Loading @@ -107,9 +107,13 @@ ENV PORT 3000 ENV NODE_ENV production ENV SERVE_CLIENT /home/node/app/packages/client ENV SERVE_ADMIN /home/node/app/packages/admin ENV PIXEL_LOG_PATH /home/node/app/pixel.log VOLUME /home/node/app/pixel.log EXPOSE 3000 # profiler port, only used if profiler is explicity running EXPOSE 9229 ENTRYPOINT [ "/bin/sh" ] CMD [ "./docker-start.sh" ] No newline at end of file packages/server/src/api/admin.ts +38 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ import { InstanceNotFound, } from "../models/Instance"; import { AuditLog } from "../models/AuditLog"; import { LogMan } from "../lib/LogMan"; const app = Router(); const Logger = getLogger("HTTP/ADMIN"); Loading Loading @@ -50,6 +51,25 @@ app.get("/check", (req, res) => { res.send({ success: true }); }); // TODO: Delete before merge app.get("/log", (req, res) => { const user = "grant@grants.cafe"; for (let i = 0; i < 100; i++) { LogMan.log("pixel_place", user, { x: 0, y: 0, hex: "ABC123" }); LogMan.log("pixel_undo", user, { x: 0, y: 0, hex: "FFFFFF" }); LogMan.log("mod_fill", user, { from: [0, 0], to: [1, 1], hex: "000000" }); LogMan.log("mod_override", user, { x: 0, y: 0, hex: "111111" }); LogMan.log("mod_rollback", user, { x: 0, y: 0, hex: "222222" }); LogMan.log("mod_rollback_undo", user, { x: 0, y: 0, hex: "333333" }); LogMan.log("canvas_size", { width: 100, height: 100 }); LogMan.log("canvas_freeze", {}); LogMan.log("canvas_unfreeze", {}); } res.send("ok"); }); app.get("/canvas/size", async (req, res) => { const config = Canvas.getCanvasConfig(); Loading Loading @@ -86,6 +106,11 @@ app.post("/canvas/size", async (req, res) => { } await Canvas.setSize(width, height); // we log this here because Canvas#setSize is ran at launch // this is currently the only way the size is changed is via the API LogMan.log("canvas_size", { width, height }); const user = (await User.fromAuthSession(req.session.user!))!; const auditLog = AuditLog.Factory(user.sub) .doing("CANVAS_SIZE") Loading @@ -111,6 +136,9 @@ app.get("/canvas/freeze", async (req, res) => { app.post("/canvas/freeze", async (req, res) => { await Canvas.setFrozen(true); // same reason as canvas size changes, we log this here because #setFrozen is ran at startup LogMan.log("canvas_freeze", {}); const user = (await User.fromAuthSession(req.session.user!))!; const auditLog = AuditLog.Factory(user.sub) .doing("CANVAS_FREEZE") Loading @@ -129,6 +157,9 @@ app.post("/canvas/freeze", async (req, res) => { app.delete("/canvas/freeze", async (req, res) => { await Canvas.setFrozen(false); // same reason as canvas size changes, we log this here because #setFrozen is ran at startup LogMan.log("canvas_unfreeze", {}); const user = (await User.fromAuthSession(req.session.user!))!; const auditLog = AuditLog.Factory(user.sub) .doing("CANVAS_UNFREEZE") Loading Loading @@ -272,6 +303,13 @@ app.put("/canvas/undo", async (req, res) => { ? paletteColors.find((p) => p.hex === coveredPixel.color)?.id || -1 : -1, }); // TODO: this spams the log, it would be nicer if it combined LogMan.log("mod_rollback", user_sub, { x: pixel.pixel.x, y: pixel.pixel.y, hex: coveredPixel?.color, }); break; } case "rejected": Loading packages/server/src/index.ts +4 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,10 @@ if (!process.env.INHIBIT_LOGIN) { } } if (!process.env.PIXEL_LOG_PATH) { Logger.warn("PIXEL_LOG_PATH is not defined, defaulting to packages/server"); } // run startup tasks, all of these need to be completed to serve Promise.all([ Redis.getClient(), Loading packages/server/src/lib/Canvas.ts +14 −1 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import { SocketServer } from "./SocketServer"; import { getLogger } from "./Logger"; import { Pixel } from "@prisma/client"; import { CanvasWorker } from "../workers/worker"; import { LogMan } from "./LogMan"; const Logger = getLogger("CANVAS"); Loading Loading @@ -182,7 +183,7 @@ class Canvas { x: pixel.x, y: pixel.y, createdAt: { lt: pixel.createdAt }, deletedAt: null, deletedAt: null, // undone pixels will have this set }, orderBy: { createdAt: "desc" }, take: 1, Loading @@ -198,6 +199,11 @@ class Canvas { }); } LogMan.log("pixel_undo", pixel.userId, { x: pixel.x, y: pixel.y, hex: coveringPixel?.color, }); return coveringPixel; } Loading Loading @@ -392,6 +398,8 @@ class Canvas { hex, })) ); LogMan.log("mod_fill", user.sub, { from: start, to: end, hex }); } async setPixel( Loading Loading @@ -430,6 +438,11 @@ class Canvas { await this.updateCanvasRedisAtPos(x, y); Logger.info(`${user.sub} placed pixel at (${x}, ${y})`); LogMan.log(isModAction ? "mod_override" : "pixel_place", user.sub, { x, y, hex, }); } /** Loading packages/server/src/lib/LogMan.ts 0 → 100644 +85 −0 Original line number Diff line number Diff line import { PixelLogger } from "./Logger"; interface UserEvents { pixel_place: { x: number; y: number; hex: string }; pixel_undo: { x: number; y: number; hex?: string }; mod_fill: { from: [x: number, y: number]; to: [x: number, y: number]; hex: string; }; mod_override: { x: number; y: number; hex: string }; mod_rollback: { x: number; y: number; hex?: string }; mod_rollback_undo: { x: number; y: number; hex?: string }; } interface SystemEvents { canvas_size: { width: number; height: number }; canvas_freeze: {}; canvas_unfreeze: {}; } /** * Handle logs that should be written to a text file * * This could be used as an EventEmitter in the future, but as of right now * it just adds typing to logging of these events * * TODO: better name, this one is not it * * @see #57 */ class LogMan_ { log<EventName extends keyof SystemEvents>( event: EventName, data: SystemEvents[EventName] ): void; log<EventName extends keyof UserEvents>( event: EventName, user: string, data: UserEvents[EventName] ): void; log<EventName extends keyof UserEvents | keyof SystemEvents>( event: EventName, ...params: EventName extends keyof UserEvents ? [user: string, data: UserEvents[EventName]] : EventName extends keyof SystemEvents ? [data: SystemEvents[EventName]] : never ): void { let parts: string[] = []; if (params.length === 2) { // user event let user = params[0] as string; parts.push(user, event); if (event === "mod_fill") { // this event format has a different line format let data: UserEvents["mod_fill"] = params[1] as any; parts.push(data.from.join(","), data.to.join(","), data.hex); } else { let data: UserEvents[Exclude<keyof UserEvents, "mod_fill">] = params[1] as any; parts.push(...[data.x, data.y, data.hex || "unset"].map((a) => a + "")); } } else { // system event parts.push("system", event); switch (event) { case "canvas_size": let data: SystemEvents["canvas_size"] = params[0] as any; let { width, height } = data; parts.push(width + "", height + ""); break; } } PixelLogger.info(parts.join("\t")); } } export const LogMan = new LogMan_(); Loading
Dockerfile +4 −0 Original line number Diff line number Diff line Loading @@ -107,9 +107,13 @@ ENV PORT 3000 ENV NODE_ENV production ENV SERVE_CLIENT /home/node/app/packages/client ENV SERVE_ADMIN /home/node/app/packages/admin ENV PIXEL_LOG_PATH /home/node/app/pixel.log VOLUME /home/node/app/pixel.log EXPOSE 3000 # profiler port, only used if profiler is explicity running EXPOSE 9229 ENTRYPOINT [ "/bin/sh" ] CMD [ "./docker-start.sh" ] No newline at end of file
packages/server/src/api/admin.ts +38 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ import { InstanceNotFound, } from "../models/Instance"; import { AuditLog } from "../models/AuditLog"; import { LogMan } from "../lib/LogMan"; const app = Router(); const Logger = getLogger("HTTP/ADMIN"); Loading Loading @@ -50,6 +51,25 @@ app.get("/check", (req, res) => { res.send({ success: true }); }); // TODO: Delete before merge app.get("/log", (req, res) => { const user = "grant@grants.cafe"; for (let i = 0; i < 100; i++) { LogMan.log("pixel_place", user, { x: 0, y: 0, hex: "ABC123" }); LogMan.log("pixel_undo", user, { x: 0, y: 0, hex: "FFFFFF" }); LogMan.log("mod_fill", user, { from: [0, 0], to: [1, 1], hex: "000000" }); LogMan.log("mod_override", user, { x: 0, y: 0, hex: "111111" }); LogMan.log("mod_rollback", user, { x: 0, y: 0, hex: "222222" }); LogMan.log("mod_rollback_undo", user, { x: 0, y: 0, hex: "333333" }); LogMan.log("canvas_size", { width: 100, height: 100 }); LogMan.log("canvas_freeze", {}); LogMan.log("canvas_unfreeze", {}); } res.send("ok"); }); app.get("/canvas/size", async (req, res) => { const config = Canvas.getCanvasConfig(); Loading Loading @@ -86,6 +106,11 @@ app.post("/canvas/size", async (req, res) => { } await Canvas.setSize(width, height); // we log this here because Canvas#setSize is ran at launch // this is currently the only way the size is changed is via the API LogMan.log("canvas_size", { width, height }); const user = (await User.fromAuthSession(req.session.user!))!; const auditLog = AuditLog.Factory(user.sub) .doing("CANVAS_SIZE") Loading @@ -111,6 +136,9 @@ app.get("/canvas/freeze", async (req, res) => { app.post("/canvas/freeze", async (req, res) => { await Canvas.setFrozen(true); // same reason as canvas size changes, we log this here because #setFrozen is ran at startup LogMan.log("canvas_freeze", {}); const user = (await User.fromAuthSession(req.session.user!))!; const auditLog = AuditLog.Factory(user.sub) .doing("CANVAS_FREEZE") Loading @@ -129,6 +157,9 @@ app.post("/canvas/freeze", async (req, res) => { app.delete("/canvas/freeze", async (req, res) => { await Canvas.setFrozen(false); // same reason as canvas size changes, we log this here because #setFrozen is ran at startup LogMan.log("canvas_unfreeze", {}); const user = (await User.fromAuthSession(req.session.user!))!; const auditLog = AuditLog.Factory(user.sub) .doing("CANVAS_UNFREEZE") Loading Loading @@ -272,6 +303,13 @@ app.put("/canvas/undo", async (req, res) => { ? paletteColors.find((p) => p.hex === coveredPixel.color)?.id || -1 : -1, }); // TODO: this spams the log, it would be nicer if it combined LogMan.log("mod_rollback", user_sub, { x: pixel.pixel.x, y: pixel.pixel.y, hex: coveredPixel?.color, }); break; } case "rejected": Loading
packages/server/src/index.ts +4 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,10 @@ if (!process.env.INHIBIT_LOGIN) { } } if (!process.env.PIXEL_LOG_PATH) { Logger.warn("PIXEL_LOG_PATH is not defined, defaulting to packages/server"); } // run startup tasks, all of these need to be completed to serve Promise.all([ Redis.getClient(), Loading
packages/server/src/lib/Canvas.ts +14 −1 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import { SocketServer } from "./SocketServer"; import { getLogger } from "./Logger"; import { Pixel } from "@prisma/client"; import { CanvasWorker } from "../workers/worker"; import { LogMan } from "./LogMan"; const Logger = getLogger("CANVAS"); Loading Loading @@ -182,7 +183,7 @@ class Canvas { x: pixel.x, y: pixel.y, createdAt: { lt: pixel.createdAt }, deletedAt: null, deletedAt: null, // undone pixels will have this set }, orderBy: { createdAt: "desc" }, take: 1, Loading @@ -198,6 +199,11 @@ class Canvas { }); } LogMan.log("pixel_undo", pixel.userId, { x: pixel.x, y: pixel.y, hex: coveringPixel?.color, }); return coveringPixel; } Loading Loading @@ -392,6 +398,8 @@ class Canvas { hex, })) ); LogMan.log("mod_fill", user.sub, { from: start, to: end, hex }); } async setPixel( Loading Loading @@ -430,6 +438,11 @@ class Canvas { await this.updateCanvasRedisAtPos(x, y); Logger.info(`${user.sub} placed pixel at (${x}, ${y})`); LogMan.log(isModAction ? "mod_override" : "pixel_place", user.sub, { x, y, hex, }); } /** Loading
packages/server/src/lib/LogMan.ts 0 → 100644 +85 −0 Original line number Diff line number Diff line import { PixelLogger } from "./Logger"; interface UserEvents { pixel_place: { x: number; y: number; hex: string }; pixel_undo: { x: number; y: number; hex?: string }; mod_fill: { from: [x: number, y: number]; to: [x: number, y: number]; hex: string; }; mod_override: { x: number; y: number; hex: string }; mod_rollback: { x: number; y: number; hex?: string }; mod_rollback_undo: { x: number; y: number; hex?: string }; } interface SystemEvents { canvas_size: { width: number; height: number }; canvas_freeze: {}; canvas_unfreeze: {}; } /** * Handle logs that should be written to a text file * * This could be used as an EventEmitter in the future, but as of right now * it just adds typing to logging of these events * * TODO: better name, this one is not it * * @see #57 */ class LogMan_ { log<EventName extends keyof SystemEvents>( event: EventName, data: SystemEvents[EventName] ): void; log<EventName extends keyof UserEvents>( event: EventName, user: string, data: UserEvents[EventName] ): void; log<EventName extends keyof UserEvents | keyof SystemEvents>( event: EventName, ...params: EventName extends keyof UserEvents ? [user: string, data: UserEvents[EventName]] : EventName extends keyof SystemEvents ? [data: SystemEvents[EventName]] : never ): void { let parts: string[] = []; if (params.length === 2) { // user event let user = params[0] as string; parts.push(user, event); if (event === "mod_fill") { // this event format has a different line format let data: UserEvents["mod_fill"] = params[1] as any; parts.push(data.from.join(","), data.to.join(","), data.hex); } else { let data: UserEvents[Exclude<keyof UserEvents, "mod_fill">] = params[1] as any; parts.push(...[data.x, data.y, data.hex || "unset"].map((a) => a + "")); } } else { // system event parts.push("system", event); switch (event) { case "canvas_size": let data: SystemEvents["canvas_size"] = params[0] as any; let { width, height } = data; parts.push(width + "", height + ""); break; } } PixelLogger.info(parts.join("\t")); } } export const LogMan = new LogMan_();