generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } // User roles enum Role { USER ADMIN } // User authentication model User { id Int @id @default(autoincrement()) username String @unique email String @unique passwordHash String role Role @default(USER) resetToken String? // Password reset token resetTokenExpiry DateTime? // Token expiration time createdAt DateTime @default(now()) updatedAt DateTime @updatedAt sessions Session[] characters Character[] ownedGroups Group[] @relation("GroupOwner") } // User's OSRS characters (for task/unlock tracking) model Character { id Int @id @default(autoincrement()) userId Int user User @relation(fields: [userId], references: [id], onDelete: Cascade) rsn String // RuneScape Name isActive Boolean @default(false) // Synced data (stored as JSON) tasksData String? // JSON - task completion status unlocksData String? // JSON - unlock status notesData String? // JSON - user notes/planner data createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([userId, rsn]) @@index([userId]) } model Session { id String @id @default(uuid()) userId Int user User @relation(fields: [userId], references: [id], onDelete: Cascade) expiresAt DateTime createdAt DateTime @default(now()) @@index([userId]) } // Group tracking (for RuneLite plugin) model Group { id Int @id @default(autoincrement()) name String tokenHash String // Blake2b-256 hash of token version Int @default(1) ownerId Int? // Optional - user who owns/manages this group owner User? @relation("GroupOwner", fields: [ownerId], references: [id], onDelete: SetNull) createdAt DateTime @default(now()) members Member[] @@unique([name, tokenHash]) @@index([tokenHash]) @@index([ownerId]) } model Member { id Int @id @default(autoincrement()) groupId Int group Group @relation(fields: [groupId], references: [id], onDelete: Cascade) name String // Stats (HP, Prayer, Energy, World, etc.) - JSON array of 7 integers stats String? // JSON statsLastUpdate DateTime? // Coordinates (x, y, plane) - JSON array of 3 integers coordinates String? // JSON coordinatesLastUpdate DateTime? // Skills (24 skills) - JSON array of 24 integers skills String? // JSON skillsLastUpdate DateTime? // Quests - binary blob quests Bytes? questsLastUpdate DateTime? // Inventory (56 items) - JSON array of 56 integers inventory String? // JSON inventoryLastUpdate DateTime? // Equipment (28 slots) - JSON array of 28 integers equipment String? // JSON equipmentLastUpdate DateTime? // Rune pouch (8 runes) - JSON array of 8 integers runePouch String? // JSON runePouchLastUpdate DateTime? // Bank - JSON array (variable length) bank String? // JSON bankLastUpdate DateTime? // Seed vault - JSON array (variable length) seedVault String? // JSON seedVaultLastUpdate DateTime? // Interacting NPC interacting String? interactingLastUpdate DateTime? // Diary vars (62 integers) - JSON array diaryVars String? // JSON diaryVarsLastUpdate DateTime? // Overall last update lastUpdated DateTime? // Skills aggregation skillsDay SkillsDay[] skillsMonth SkillsMonth[] skillsYear SkillsYear[] // Collection log collectionLogs CollectionLog[] collectionLogsNew CollectionLogNew[] @@unique([groupId, name]) @@index([groupId]) } // Skills aggregation tables model SkillsDay { memberId Int member Member @relation(fields: [memberId], references: [id], onDelete: Cascade) time DateTime skills String // JSON array of 24 integers @@id([memberId, time]) } model SkillsMonth { memberId Int member Member @relation(fields: [memberId], references: [id], onDelete: Cascade) time DateTime skills String // JSON array of 24 integers @@id([memberId, time]) } model SkillsYear { memberId Int member Member @relation(fields: [memberId], references: [id], onDelete: Cascade) time DateTime skills String // JSON array of 24 integers @@id([memberId, time]) } // Aggregation tracking model AggregationInfo { type String @id lastAggregation DateTime @default(dbgenerated("'2000-01-01 00:00:00'")) } // Collection log tables model CollectionTab { id Int @id @default(autoincrement()) name String pages CollectionPage[] } model CollectionPage { id Int @id @default(autoincrement()) tabId Int tab CollectionTab @relation(fields: [tabId], references: [id], onDelete: Cascade) pageName String collectionLogs CollectionLog[] collectionLogsNew CollectionLogNew[] @@unique([tabId, pageName]) } model CollectionLog { memberId Int member Member @relation(fields: [memberId], references: [id], onDelete: Cascade) pageId Int page CollectionPage @relation(fields: [pageId], references: [id], onDelete: Cascade) items String? // JSON array of item IDs counts String? // JSON array of completion counts lastUpdated DateTime? @@id([memberId, pageId]) } model CollectionLogNew { memberId Int member Member @relation(fields: [memberId], references: [id], onDelete: Cascade) pageId Int page CollectionPage @relation(fields: [pageId], references: [id], onDelete: Cascade) newItems String? // JSON array of new item IDs lastUpdated DateTime? @@id([memberId, pageId]) } // Hiscores cache - stores fetched hiscores data model HiscoresCache { rsn String @id // RuneScape Name (lowercase for lookup) displayRsn String // Original case RSN for display skills String // JSON - skill data clues String // JSON - clue scroll data activities String // JSON - activities/bosses data leaguePoints Int @default(0) fetchedAt DateTime @default(now()) @@index([fetchedAt]) }