import z from "zod"; import Command from "../commands/command.ts"; import { ServerContentBase } from "../ContentBase.ts"; import { transformCheckEnum } from "../../util/validators.ts"; import { sign } from "@hono/hono/jwt"; import { CachedLogin, DbCachedLogin, PlatformMask, PlatformType, TokenFormat, TokenType } from "./types.ts"; import type Profile from "../profiles/profile.ts"; import { getNetConfig } from "../../net.ts"; export const steamAuthTicketSchema = z.object({ Ticket: z.string().min(256), AppId: z.literal("471710") }); const netConfig = getNetConfig(); export class PlatformsManager extends ServerContentBase { static platformsKey = "platforms"; #constructPlatformKey(...keys: (string | number | undefined)[]) { return [PlatformsManager.platformsKey, ...keys.filter(val => typeof val == 'string')]; } async getToken(prof: Profile, type: TokenType) { const secret = Deno.env.get('SECRET'); if (!secret) throw new Error("No SECRET in env. Did you forget to set it?"); const exp = type == TokenType.Access ? Math.round(Date.now() / 1000) + 21_600 : Math.round(Date.now() / 1000) + 31_556_952; const token: TokenFormat = { typ: type, sub: prof.getId(), role: await prof.getRole(), iss: `${netConfig.securePublicHost ? "https" : "http"}://${netConfig.publicHost}/auth`, iat: Math.round(Date.now() / 1000) - 5, exp } return await sign(JSON.parse(JSON.stringify(token)), secret); } async updateLastLoginTime(platform: PlatformType, platformId: string, accountId: number) { const key = this.#constructPlatformKey(platform, platformId); const set = await this.kv.getKv().get>(key); if (set.value) { const existing = set.value.values().toArray().find(val => val.accountId == accountId); if (!existing) return; existing.lastLoginTime = new Date(); await this.kv.getKv().set(key, set.value); } } async addCachedLogin(platform: PlatformType, platformId: string, accountId: number) { const key = this.#constructPlatformKey(platform, platformId); const set = await this.kv.getKv().get>(key); if (set.value) { const existing = set.value.values().toArray().find(val => val.accountId == accountId); if (!existing) { set.value.add({ accountId, lastLoginTime: new Date(), requirePassword: false }); await this.kv.getKv().set(key, set.value); } return set.value.values().toArray(); } else { const newSet = new Set([{ accountId, lastLoginTime: new Date(), requirePassword: false }]); await this.kv.getKv().set(key, newSet); return newSet.values().toArray(); }; } async getCachedLogins(platform: PlatformType, platformId: string, format: true): Promise async getCachedLogins(platform: PlatformType, platformId: string, format: false): Promise async getCachedLogins(platform: PlatformType, platformId: string, format?: boolean) { const set = await this.kv.getKv().get>(this.#constructPlatformKey(platform, platformId)); if (set.value && format) return set.value.values().toArray().map(val => ({ platform, platformId, accountId: val.accountId, lastLoginTime: val.lastLoginTime, requirePassword: val.requirePassword } as CachedLogin)); else if (set.value) return set.value.values().toArray(); else return []; } async deleteCachedLogin(platform: PlatformType, platformId: string, accountId: number) { const key = this.#constructPlatformKey(platform, platformId); const set = await this.kv.getKv().get>(key); if (set.value) { const existing = set.value.values().toArray().find(val => val.accountId == accountId); if (existing) { set.value.delete(existing); await this.kv.getKv().set(key, set.value); } return set.value.values().toArray(); } else return null; } getPlatformMask(value: number) { const err = new Error("Invalid mask"); if (typeof value !== 'number' || !Number.isInteger(value)) throw err; if (value === PlatformMask.All) { return [PlatformMask.All]; } return Object.values(PlatformMask) .filter(v => typeof v === "number" && v !== PlatformMask.None && v !== PlatformMask.All) .filter(v => (value & (v as number)) === v) as PlatformMask[]; } buildPlatformMask(...flags: PlatformMask[]) { const err = new Error("Invalid mask"); if (!flags.length) throw err; for (const flag of flags) if (typeof flag !== 'number' || !Object.values(PlatformMask).includes(flag)) throw err; return flags.reduce((mask, flag) => mask | flag, 0); } override start() { this.server.Commands.addRootCommand(new Command({ key: ['platforms', 'pm', 'platformmanager', 'platformanager'], subcommands: [ new Command({ key: ['get', 'g', 'list', 'l'], exec: async (type: PlatformType, platformId: string) => { return await this.getCachedLogins(type, platformId, false); }, zod: z.tuple([ z.coerce.number().transform(transformCheckEnum(PlatformType)), z.string().min(4) ]), help: 'List all cachedlogins for platformId: ' }), new Command({ key: ['set', 's', 'add', 'a'], exec: async (type: PlatformType, platformId: string, accountId: number) => { return await this.addCachedLogin(type, platformId, accountId); }, zod: z.tuple([ z.coerce.number().transform(transformCheckEnum(PlatformType)), z.string(), z.coerce.number() ]), help: 'Add a cachedlogin for platformId: , , ' }), new Command({ key: ['del', 'd', 'rem', 'remove', 'r'], exec: async (type: PlatformType, platformId: string, accountId: number) => { return await this.deleteCachedLogin(type, platformId, accountId); }, zod: z.tuple([ z.coerce.number().transform(transformCheckEnum(PlatformType)), z.string(), z.coerce.number() ]), help: 'Remove a cachedlogin for platformId: , , ' }) ] })); } }