feat: basic playtime backend

This commit is contained in:
Huskydog9988
2025-05-27 12:30:20 -04:00
parent 21eec081ee
commit 4b009f1aca
5 changed files with 96 additions and 4 deletions

View File

@ -2,6 +2,7 @@ enum ClientCapabilities {
PeerAPI @map("peerAPI") // other clients can use the HTTP API to P2P with this client PeerAPI @map("peerAPI") // other clients can use the HTTP API to P2P with this client
UserStatus @map("userStatus") // this client can report this user's status (playing, online, etc etc) UserStatus @map("userStatus") // this client can report this user's status (playing, online, etc etc)
CloudSaves @map("cloudSaves") // ability to save to save slots CloudSaves @map("cloudSaves") // ability to save to save slots
TrackPlaytime @map("trackPlaytime") // ability to track user playtime
} }
// References a device // References a device

View File

@ -36,6 +36,7 @@ model Game {
saves SaveSlot[] saves SaveSlot[]
screenshots Screenshot[] screenshots Screenshot[]
tags Tag[] tags Tag[]
playtime Playtime[]
developers Company[] @relation(name: "developers") developers Company[] @relation(name: "developers")
publishers Company[] @relation(name: "publishers") publishers Company[] @relation(name: "publishers")
@ -124,6 +125,21 @@ model Screenshot {
@@index([userId]) @@index([userId])
} }
model Playtime {
gameId String
game Game @relation(fields: [gameId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
seconds Int // seconds user has spent playing the game
updatedAt DateTime @updatedAt @db.Timestamptz(6)
createdAt DateTime @default(now()) @db.Timestamptz(6)
@@id([gameId, userId])
@@index([userId])
}
model Company { model Company {
id String @id @default(uuid()) id String @id @default(uuid())

View File

@ -19,6 +19,7 @@ model User {
saves SaveSlot[] saves SaveSlot[]
screenshots Screenshot[] screenshots Screenshot[]
playtime Playtime[]
} }
model Notification { model Notification {

View File

@ -10,6 +10,7 @@ export enum InternalClientCapability {
PeerAPI = "peerAPI", PeerAPI = "peerAPI",
UserStatus = "userStatus", UserStatus = "userStatus",
CloudSaves = "cloudSaves", CloudSaves = "cloudSaves",
TrackPlaytime = "trackPlaytime",
} }
export const validCapabilities = Object.values(InternalClientCapability); export const validCapabilities = Object.values(InternalClientCapability);
@ -79,6 +80,7 @@ class CapabilityManager {
[InternalClientCapability.PeerAPI]: async () => true, [InternalClientCapability.PeerAPI]: async () => true,
[InternalClientCapability.UserStatus]: async () => true, // No requirements for user status [InternalClientCapability.UserStatus]: async () => true, // No requirements for user status
[InternalClientCapability.CloudSaves]: async () => true, // No requirements for cloud saves [InternalClientCapability.CloudSaves]: async () => true, // No requirements for cloud saves
[InternalClientCapability.TrackPlaytime]: async () => true,
}; };
async validateCapabilityConfiguration( async validateCapabilityConfiguration(
@ -160,6 +162,28 @@ class CapabilityManager {
}, },
}); });
}, },
[InternalClientCapability.TrackPlaytime]: async function () {
const currentClient = await prisma.client.findUnique({
where: { id: clientId },
select: {
capabilities: true,
},
});
if (!currentClient) throw new Error("Invalid client ID");
if (
currentClient.capabilities.includes(ClientCapabilities.TrackPlaytime)
)
return;
await prisma.client.update({
where: { id: clientId },
data: {
capabilities: {
push: ClientCapabilities.TrackPlaytime,
},
},
});
},
}; };
await upsertFunctions[capability](); await upsertFunctions[capability]();
} }

View File

@ -0,0 +1,50 @@
import prisma from "../db/database";
class PlaytimeManager {
/**
* Get a user's playtime on a game
* @param gameId
* @param userId
* @returns
*/
async get(gameId: string, userId: string) {
return await prisma.playtime.findUnique({
where: {
gameId_userId: {
gameId,
userId,
},
},
});
}
/**
* Add time to a user's playtime
* @param gameId
* @param userId
* @param seconds seconds played
*/
async add(gameId: string, userId: string, seconds: number) {
await prisma.playtime.upsert({
where: {
gameId_userId: {
gameId,
userId,
},
},
create: {
gameId,
userId,
seconds,
},
update: {
seconds: {
increment: seconds,
},
},
});
}
}
export const playtimeManager = new PlaytimeManager();
export default playtimeManager;