Loading packages/server/prisma/dbml/schema.dbml +15 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ Table User { FactionMember FactionMember [not null] Ban Ban AuditLog AuditLog [not null] IPAddress IPAddress [not null] } Table Instance { Loading @@ -32,6 +33,18 @@ Table Instance { Ban Ban } Table IPAddress { ip String [not null] userSub String [not null] lastUsedAt DateTime [not null] createdAt DateTime [default: `now()`, not null] user User [not null] indexes { (ip, userSub) [pk] } } Table PaletteColor { id Int [pk, increment] name String [not null] Loading Loading @@ -145,6 +158,8 @@ Enum AuditLogAction { USER_UNADMIN } Ref: IPAddress.userSub > User.sub Ref: Pixel.userId > User.sub Ref: FactionMember.sub > User.sub Loading packages/server/prisma/migrations/20240714153412_add_ip_address_table/migration.sql 0 → 100644 +12 −0 Original line number Diff line number Diff line -- CreateTable CREATE TABLE "IPAddress" ( "ip" TEXT NOT NULL, "userSub" TEXT NOT NULL, "lastUsedAt" TIMESTAMP(3) NOT NULL, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "IPAddress_pkey" PRIMARY KEY ("ip","userSub") ); -- AddForeignKey ALTER TABLE "IPAddress" ADD CONSTRAINT "IPAddress_userSub_fkey" FOREIGN KEY ("userSub") REFERENCES "User"("sub") ON DELETE RESTRICT ON UPDATE CASCADE; packages/server/prisma/schema.prisma +13 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ model User { FactionMember FactionMember[] Ban Ban? AuditLog AuditLog[] IPAddress IPAddress[] } model Instance { Loading @@ -47,6 +48,18 @@ model Instance { Ban Ban? } model IPAddress { ip String userSub String lastUsedAt DateTime createdAt DateTime @default(now()) user User @relation(fields: [userSub], references: [sub]) @@id([ip, userSub]) } model PaletteColor { id Int @id @default(autoincrement()) name String Loading packages/server/src/api/admin.ts +55 −0 Original line number Diff line number Diff line Loading @@ -417,6 +417,61 @@ app.put("/canvas/fill", async (req, res) => { res.json({ success: true, auditLog }); }); /** * Get ip address info * * @query address IP address */ app.get("/ip", async (req, res) => { if (typeof req.query.address !== "string") { return res.status(400).json({ success: false, error: "missing ?address=" }); } const ip: string = req.query.address; const results = await prisma.iPAddress.findMany({ select: { userSub: true, createdAt: true, lastUsedAt: true, }, where: { ip, }, }); res.json({ success: true, results }); }); /** * Get all of a user's IP addresses * * @param :sub User ID */ app.get("/user/:sub/ips", async (req, res) => { let user: User; try { user = await User.fromSub(req.params.sub); } catch (e) { if (e instanceof UserNotFound) { res.status(404).json({ success: false, error: "User not found" }); } else { Logger.error(`/user/${req.params.sub}/ips Error ` + (e as any)?.message); res.status(500).json({ success: false, error: "Internal error" }); } return; } const ips = await prisma.iPAddress.findMany({ where: { userSub: user.sub, }, }); res.json({ success: true, ips }); }); /** * Create or ban a user * Loading packages/server/src/lib/SocketServer.ts +11 −0 Original line number Diff line number Diff line Loading @@ -164,6 +164,17 @@ export class SocketServer { ); user?.sockets.add(socket); let ip = socket.handshake.address; if (process.env.NODE_ENV === "production") { if (typeof socket.handshake.headers["x-forwarded-for"] === "string") { ip = socket.handshake.headers["x-forwarded-for"]; } else { ip = socket.handshake.headers["x-forwarded-for"]?.[0] || ip; } } user?.trackIP(ip); Logger.debug("handleConnection " + user?.sockets.size); socket.emit("clearCanvasChunks"); Loading Loading
packages/server/prisma/dbml/schema.dbml +15 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ Table User { FactionMember FactionMember [not null] Ban Ban AuditLog AuditLog [not null] IPAddress IPAddress [not null] } Table Instance { Loading @@ -32,6 +33,18 @@ Table Instance { Ban Ban } Table IPAddress { ip String [not null] userSub String [not null] lastUsedAt DateTime [not null] createdAt DateTime [default: `now()`, not null] user User [not null] indexes { (ip, userSub) [pk] } } Table PaletteColor { id Int [pk, increment] name String [not null] Loading Loading @@ -145,6 +158,8 @@ Enum AuditLogAction { USER_UNADMIN } Ref: IPAddress.userSub > User.sub Ref: Pixel.userId > User.sub Ref: FactionMember.sub > User.sub Loading
packages/server/prisma/migrations/20240714153412_add_ip_address_table/migration.sql 0 → 100644 +12 −0 Original line number Diff line number Diff line -- CreateTable CREATE TABLE "IPAddress" ( "ip" TEXT NOT NULL, "userSub" TEXT NOT NULL, "lastUsedAt" TIMESTAMP(3) NOT NULL, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "IPAddress_pkey" PRIMARY KEY ("ip","userSub") ); -- AddForeignKey ALTER TABLE "IPAddress" ADD CONSTRAINT "IPAddress_userSub_fkey" FOREIGN KEY ("userSub") REFERENCES "User"("sub") ON DELETE RESTRICT ON UPDATE CASCADE;
packages/server/prisma/schema.prisma +13 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ model User { FactionMember FactionMember[] Ban Ban? AuditLog AuditLog[] IPAddress IPAddress[] } model Instance { Loading @@ -47,6 +48,18 @@ model Instance { Ban Ban? } model IPAddress { ip String userSub String lastUsedAt DateTime createdAt DateTime @default(now()) user User @relation(fields: [userSub], references: [sub]) @@id([ip, userSub]) } model PaletteColor { id Int @id @default(autoincrement()) name String Loading
packages/server/src/api/admin.ts +55 −0 Original line number Diff line number Diff line Loading @@ -417,6 +417,61 @@ app.put("/canvas/fill", async (req, res) => { res.json({ success: true, auditLog }); }); /** * Get ip address info * * @query address IP address */ app.get("/ip", async (req, res) => { if (typeof req.query.address !== "string") { return res.status(400).json({ success: false, error: "missing ?address=" }); } const ip: string = req.query.address; const results = await prisma.iPAddress.findMany({ select: { userSub: true, createdAt: true, lastUsedAt: true, }, where: { ip, }, }); res.json({ success: true, results }); }); /** * Get all of a user's IP addresses * * @param :sub User ID */ app.get("/user/:sub/ips", async (req, res) => { let user: User; try { user = await User.fromSub(req.params.sub); } catch (e) { if (e instanceof UserNotFound) { res.status(404).json({ success: false, error: "User not found" }); } else { Logger.error(`/user/${req.params.sub}/ips Error ` + (e as any)?.message); res.status(500).json({ success: false, error: "Internal error" }); } return; } const ips = await prisma.iPAddress.findMany({ where: { userSub: user.sub, }, }); res.json({ success: true, ips }); }); /** * Create or ban a user * Loading
packages/server/src/lib/SocketServer.ts +11 −0 Original line number Diff line number Diff line Loading @@ -164,6 +164,17 @@ export class SocketServer { ); user?.sockets.add(socket); let ip = socket.handshake.address; if (process.env.NODE_ENV === "production") { if (typeof socket.handshake.headers["x-forwarded-for"] === "string") { ip = socket.handshake.headers["x-forwarded-for"]; } else { ip = socket.handshake.headers["x-forwarded-for"]?.[0] || ip; } } user?.trackIP(ip); Logger.debug("handleConnection " + user?.sockets.size); socket.emit("clearCanvasChunks"); Loading