import { Redis } from "../db.ts"; import Dictionary from "./usernames.ts"; import { Config } from "../config.ts"; import { AuthType } from "./users.ts"; import * as JsonWebToken from "@gz/jwt"; const config = Config.getConfig(); interface ProfileInitOptions { username: string } interface AccountExport { accountId: number, profileImage: string, isJunior: boolean, platforms: number, username: string, displayName: string } export type ProfileTokenFormat = { iss: string; sub: number; nbf: number; iat: number; exp: number; typ: AuthType; } class Profile { static async getUniqueId() { let id = Math.round(Math.random() * Math.pow(2, 31)); while ((await Redis.Database.exists(Redis.buildKey(Redis.KeyGroups.Profiles.Root, id.toString(), Redis.KeyGroups.Profiles.Username))) >= 1) id = await this.getUniqueId(); return id; } static async byName(name: string) { const id = await Redis.Database.get(Redis.buildKey(Redis.KeyGroups.Profile_Usernames, name)); if (id == null) return null; else return new Profile(parseInt(id, 10)); } static async getUniqueUsername() { let username = `${Dictionary.Adjectives[Math.floor(Math.random() * Dictionary.Adjectives.length)]}${Dictionary.Nouns[Math.floor(Math.random() * Dictionary.Nouns.length)]}${Math.round(Math.random() * 10000)}` while ((await Profile.byName(username)) !== null) username = await this.getUniqueUsername(); return username; } static async init(options?: ProfileInitOptions) { const optionsSpecified = typeof options !== 'undefined'; const newId = await this.getUniqueId(); const newUsername = optionsSpecified ? options.username : await this.getUniqueUsername(); await Redis.Database.set(Redis.buildKey(Redis.KeyGroups.Profile_Usernames, newUsername), newId); await Redis.Database.set(Redis.buildKey(Redis.KeyGroups.Profiles.Root, newId.toString(), Redis.KeyGroups.Profiles.Username), newUsername); return new Profile(newId); } // surely this can be written better static getExportAccount(id: number): Promise { return new Promise((resolve, _reject) => { Redis.Database.get(Redis.buildKey(Redis.KeyGroups.Profiles.Root, id.toString(), Redis.KeyGroups.Profiles.Username)).then(val => { if (val == null) resolve(null); else { const promises = { profileImage: Redis.Database.get(Redis.buildKey(Redis.KeyGroups.Profiles.Root, id.toString(), Redis.KeyGroups.Profiles.ProfileImage)), isJunior: Redis.Database.get(Redis.buildKey(Redis.KeyGroups.Profiles.Root, id.toString(), Redis.KeyGroups.Profiles.Junior)), platforms: Redis.Database.get(Redis.buildKey(Redis.KeyGroups.Profiles.Root, id.toString(), Redis.KeyGroups.Profiles.Platforms)), displayName: Redis.Database.get(Redis.buildKey(Redis.KeyGroups.Profiles.Root, id.toString(), Redis.KeyGroups.Profiles.DisplayName)), username: Redis.Database.get(Redis.buildKey(Redis.KeyGroups.Profiles.Root, id.toString(), Redis.KeyGroups.Profiles.Username)), } Promise.all(Object.values(promises)).then((values) => { resolve({ accountId: id, profileImage: values[0] == null ? "DefaultProfileImage" : values[0], isJunior: values[1] == null ? false : JSON.parse(values[1]), platforms: values[2] == null ? 1 : JSON.parse(values[2]), displayName: values[3] == null ? (values[4] == null ? "DATABASEERROR" : values[4]) : values[3], username: values[4] == null ? "DATABASEERROR" : values[4], }); }); } }); }); } static async getExportAccountsBulk(ids: number[]) { const accs = await Promise.all(ids.map(val => this.getExportAccount(val))); return accs.filter(val => val !== null); } #id: number; constructor(id: number) { this.#id = id; } getId() { return this.#id; } async export() { return await Profile.getExportAccount(this.#id); } async getToken() { const payload: ProfileTokenFormat = { iss: config.web.publichost, sub: this.#id, nbf: Math.round(Date.now() / 1000) - 200, iat: Math.round(Date.now() / 1000), exp: Math.round(Date.now() / 1000) + (config.auth.timeout * 60 * 60), typ: AuthType.Web }; return await JsonWebToken.encode(payload, config.auth.secret, { algorithm: "HS512" }); } } export default Profile;