- Implemented forgot password and reset password routes in the backend. - Added email sending capabilities using Nodemailer for password reset requests. - Created ResetPassword page in the frontend for users to reset their passwords. - Updated user model to include reset token and expiry fields. - Integrated hiscores API with caching mechanism for improved performance. - Enhanced authentication modal to include forgot password option. - Updated environment configuration for SMTP settings.
231 lines
6.4 KiB
Plaintext
231 lines
6.4 KiB
Plaintext
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])
|
|
}
|