2024-11-16 16:24:23 +11:00
|
|
|
model User {
|
|
|
|
|
id String @id @default(uuid())
|
|
|
|
|
username String @unique
|
|
|
|
|
admin Boolean @default(false)
|
2025-03-14 12:22:08 +11:00
|
|
|
enabled Boolean @default(true)
|
2024-11-16 16:24:23 +11:00
|
|
|
|
2025-05-08 19:42:46 -04:00
|
|
|
email String
|
|
|
|
|
displayName String
|
|
|
|
|
profilePictureObjectId String // Object
|
2024-11-16 16:24:23 +11:00
|
|
|
|
2026-01-13 15:32:39 +11:00
|
|
|
authMecs LinkedAuthMec[]
|
|
|
|
|
mfas LinkedMFAMec[]
|
|
|
|
|
|
2025-01-19 16:46:43 +11:00
|
|
|
clients Client[]
|
2024-11-16 19:41:19 +11:00
|
|
|
notifications Notification[]
|
2025-01-19 16:46:43 +11:00
|
|
|
collections Collection[]
|
2025-03-10 11:39:45 +11:00
|
|
|
articles Article[]
|
2025-02-04 13:15:34 +11:00
|
|
|
|
2025-04-03 19:15:33 -04:00
|
|
|
tokens APIToken[]
|
|
|
|
|
sessions Session[]
|
2025-04-01 21:08:57 +11:00
|
|
|
|
2025-05-08 11:38:09 -04:00
|
|
|
saves SaveSlot[]
|
|
|
|
|
screenshots Screenshot[]
|
2025-05-27 12:30:20 -04:00
|
|
|
playtime Playtime[]
|
community: M5 -- mods catalog and achievement definitions/display
Adds the M5 slice of the community layer: Mod/ModVersion/ModDependency/
ModInstallation/ModInstalledFile/AchievementDefinition (migration
m5_mods_achievements). Gods/Users are referenced as plain soft columns
(gameId/uploaderId/identityId), not Prisma relations, following the
concurrent-edit-avoidance pattern devices.prisma established -- content.prisma
and user.prisma only grow a comment each.
Mods: syncs from script-library/scripts/archive-mod.py's _index/catalog.json
(read-only bind mount, homelab-compose/drop-stack/docker-compose.yml) into
Mod/ModVersion/ModDependency, resolves dependency refs into real Mod rows
where archived, and exposes a topologically-ordered install-plan resolver
with cycle/conflict/unresolved-dependency detection
(GET /mods/:id/versions/:v/plan). The install manifest
(ModInstallation/ModInstalledFile) records created/overwrote/skipped per
file so an uninstall plan is exact, computed server-side from what was
recorded, applied by the caller. Downloads stream directly off the mount
(GET /mods/:id/versions/:v/download) since cdn.quasarke.net has no
file_server wired to /mnt/local24/game-mods yet -- documented as a known gap
in server/internal/community/README.md, deliberately not fixed by editing
the live Caddyfile. A manual add form covers the DESIGN.md open-question #4
fallback (no upload write path exists).
Achievements: imports gbe_fork's archived generate_emu_config output
(/mnt/local24/game-tools/goldberg/schemas, also read-only mounted) via
goldbergAdapter.ts, copying icons into Drop's own object store
(objectHandler.createFromSource, same mechanism as Steam cover art) so
nothing is hotlinked. Display joins AchievementDefinition against M2's
already-landed AchievementUnlock by (steamAppId, apiName), exactly the join
key M2 designed it around -- no stub needed, M2's table was already in the
tree. Verified against real fixtures: appid 245170 (Skullgirls) importing
45/45 definitions with 90 icons, and 274190 (Broforce) importing 17/17.
Pages: /community/mods, /community/mods/:slug, /community/mods/new,
/community/achievements, /community/games/:gameId (achievement grid +
per-title mods, completion %). Small additive nav links on /community's
index rather than touching the actively-changing shared game-detail page.
2026-08-03 01:16:25 -04:00
|
|
|
|
|
|
|
|
// See content.prisma's Game model for why M5 (and future milestones) do
|
|
|
|
|
// NOT add a relation array here -- Mod/ModInstallation reference User.id
|
|
|
|
|
// as a plain, unenforced-by-FK column, same pattern as devices.prisma.
|
community: M3 -- friends, presence, chat
Friendship (request/accept/decline/remove, self-request and crossing-request
handling, DB-level pair uniqueness via a hand-written expression index),
ChatRoom/ChatMessage/ChatReadState backed by Nitro's built-in websocket
(server/api/v1/community/ws.get.ts) behind a ChatTransport seam
(server/internal/community/chatTransport.ts) with a REST send fallback.
Mid-milestone product redirect: chat's primary job is coordinating the
persistent-world servers (EQEmu/WoW/DAoC), not per-title discussion --
ChatRoomKind gets a first-class `server` case and chatService.ts ships a
static KNOWN_SERVERS registry keyed the same way M4's GameServer will be,
once that lands (gameServerId reserved for that wiring-up pass).
Presence has no new table -- "online" is a live websocket registry
(presenceRuntime.ts), "playing X" reads through an isolated query module
(presenceService.ts) that degrades to an empty, clearly-labeled result if
PlaySession's shape doesn't match what it expects.
Pages: /community/friends, /community/chat, plus a friends/servers strip on
/community itself. Migration 20260803120000_m3_friends_chat.
2026-08-03 01:27:55 -04:00
|
|
|
|
|
|
|
|
// --- M3: friends, presence, chat ---
|
|
|
|
|
friendshipsSent Friendship[] @relation("FriendshipRequester")
|
|
|
|
|
friendshipsReceived Friendship[] @relation("FriendshipAddressee")
|
|
|
|
|
chatRoomsCreated ChatRoom[] @relation("ChatRoomCreatedBy")
|
|
|
|
|
chatMessages ChatMessage[] @relation("ChatMessageSender")
|
|
|
|
|
chatReadStates ChatReadState[] @relation("ChatReadState")
|
2024-11-16 19:41:19 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model Notification {
|
|
|
|
|
id String @id @default(uuid())
|
|
|
|
|
|
2025-05-14 15:40:55 -04:00
|
|
|
nonce String?
|
2024-11-16 19:41:19 +11:00
|
|
|
|
|
|
|
|
userId String
|
2025-06-08 05:49:11 +01:00
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
2025-05-15 14:55:05 +10:00
|
|
|
acls String[]
|
2024-11-16 19:41:19 +11:00
|
|
|
|
2025-01-19 16:46:43 +11:00
|
|
|
created DateTime @default(now())
|
2024-11-16 19:41:19 +11:00
|
|
|
title String
|
|
|
|
|
description String
|
|
|
|
|
actions String[]
|
|
|
|
|
|
|
|
|
|
read Boolean @default(false)
|
2025-05-14 15:40:55 -04:00
|
|
|
|
|
|
|
|
@@unique([userId, nonce])
|
2024-11-16 19:41:19 +11:00
|
|
|
}
|