omegga 1.9.0 → 1.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/util/time.js CHANGED
@@ -27,7 +27,9 @@ function parseDuration(str) {
27
27
  return total;
28
28
  }
29
29
  function parseBrickadiaTime(str) {
30
+ if (typeof str !== "string") return NaN;
30
31
  const [date, time] = str.split("-");
32
+ if (!date || !time) return NaN;
31
33
  return (/* @__PURE__ */ new Date(
32
34
  date.replace(/\./g, "-") + "T" + time.replace(/\./g, ":") + "Z"
33
35
  )).getTime();
@@ -1 +1 @@
1
- {"version":3,"file":"time.js","sources":["../../src/util/time.ts"],"sourcesContent":["import _debounce from 'lodash/debounce';\n\nexport interface DebouncedFunc<T extends (...args: any[]) => any> {\n /**\n * Call the original function, but applying the debounce rules.\n *\n * If the debounced function can be run immediately, this calls it and returns its return\n * value.\n *\n * Otherwise, it returns the return value of the last invocation, or undefined if the debounced\n * function was not invoked yet.\n */\n (...args: Parameters<T>): ReturnType<T> | undefined;\n\n /**\n * Throw away any pending invocation of the debounced function.\n */\n cancel(): void;\n\n /**\n * If there is a pending invocation of the debounced function, invoke it immediately and return\n * its return value.\n *\n * Otherwise, return the value from the last invocation, or undefined if the debounced function\n * was never invoked.\n */\n flush(): ReturnType<T> | undefined;\n}\nexport interface DebouncedFuncLeading<T extends (...args: any[]) => any>\n extends DebouncedFunc<T> {\n (...args: Parameters<T>): ReturnType<T>;\n flush(): ReturnType<T>;\n}\nexport interface DebounceSettings {\n leading?: boolean | undefined;\n maxWait?: number | undefined;\n trailing?: boolean | undefined;\n}\nexport interface DebounceSettingsLeading extends DebounceSettings {\n leading: true;\n}\nexport const debounce:\n | (<T extends (...args: any) => any>(\n func: T,\n wait: number | undefined,\n options?: DebounceSettingsLeading,\n ) => DebouncedFuncLeading<T>)\n | (<T extends (...args: any) => any>(\n func: T,\n wait?: number,\n options?: DebounceSettings,\n ) => DebouncedFunc<T>) = _debounce;\n\nconst UNIT_CONVERSION: Record<string, number> = {\n ms: 1, // milliseconds\n s: 1000, // seconds\n m: 60 * 1000, // minutes\n h: 60 * 60 * 1000, // hours\n d: 24 * 60 * 60 * 1000, // days\n w: 7 * 24 * 60 * 60 * 1000, // weeks\n};\n\n// sum duration from a string like '1d 4h 3m 25s 30ms'\nexport function parseDuration(str: string) {\n let total = 0;\n\n // split string by spaces (0m 35s) -> [0m, 35s]\n const chunks = str.split(' ');\n\n // parse each chunk\n for (const chunk of chunks) {\n // extract the duration and unit from the chunk\n const [, duration, unit] = chunk.match(/^(\\d+)(\\w+)$/) || [];\n\n // ignore invalid chunks\n if (!duration || !unit || !UNIT_CONVERSION[unit]) continue;\n\n // add to the total the duration in MS\n total += Number(duration) * UNIT_CONVERSION[unit];\n }\n\n return total;\n}\n\n// parse brickadia's time format (YYYY.MM.DD-HH-MM-SS) into a time object\nexport function parseBrickadiaTime(str: string) {\n const [date, time] = str.split('-');\n return new Date(\n date.replace(/\\./g, '-') + 'T' + time.replace(/\\./g, ':') + 'Z',\n ).getTime();\n}\n"],"names":[],"mappings":";;;AAyCO,MAAM,WAUgB;AAE7B,MAAM,kBAA0C;AAAA,EAC9C,IAAI;AAAA;AAAA,EACJ,GAAG;AAAA;AAAA,EACH,GAAG,KAAK;AAAA;AAAA,EACR,GAAG,KAAK,KAAK;AAAA;AAAA,EACb,GAAG,KAAK,KAAK,KAAK;AAAA;AAAA,EAClB,GAAG,IAAI,KAAK,KAAK,KAAK;AAAA;AACxB;AAGO,SAAS,cAAc,KAAa;AACzC,MAAI,QAAQ;AAGZ,QAAM,SAAS,IAAI,MAAM,GAAG;AAG5B,aAAW,SAAS,QAAQ;AAE1B,UAAM,CAAA,EAAG,UAAU,IAAI,IAAI,MAAM,MAAM,cAAc,KAAK,CAAA;AAG1D,QAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,gBAAgB,IAAI,EAAG;AAGlD,aAAS,OAAO,QAAQ,IAAI,gBAAgB,IAAI;AAAA,EAClD;AAEA,SAAO;AACT;AAGO,SAAS,mBAAmB,KAAa;AAC9C,QAAM,CAAC,MAAM,IAAI,IAAI,IAAI,MAAM,GAAG;AAClC,UAAO,oBAAI;AAAA,IACT,KAAK,QAAQ,OAAO,GAAG,IAAI,MAAM,KAAK,QAAQ,OAAO,GAAG,IAAI;AAAA,EAAA,GAC5D,QAAA;AACJ;;;;"}
1
+ {"version":3,"file":"time.js","sources":["../../src/util/time.ts"],"sourcesContent":["import _debounce from 'lodash/debounce';\n\nexport interface DebouncedFunc<T extends (...args: any[]) => any> {\n /**\n * Call the original function, but applying the debounce rules.\n *\n * If the debounced function can be run immediately, this calls it and returns its return\n * value.\n *\n * Otherwise, it returns the return value of the last invocation, or undefined if the debounced\n * function was not invoked yet.\n */\n (...args: Parameters<T>): ReturnType<T> | undefined;\n\n /**\n * Throw away any pending invocation of the debounced function.\n */\n cancel(): void;\n\n /**\n * If there is a pending invocation of the debounced function, invoke it immediately and return\n * its return value.\n *\n * Otherwise, return the value from the last invocation, or undefined if the debounced function\n * was never invoked.\n */\n flush(): ReturnType<T> | undefined;\n}\nexport interface DebouncedFuncLeading<T extends (...args: any[]) => any>\n extends DebouncedFunc<T> {\n (...args: Parameters<T>): ReturnType<T>;\n flush(): ReturnType<T>;\n}\nexport interface DebounceSettings {\n leading?: boolean | undefined;\n maxWait?: number | undefined;\n trailing?: boolean | undefined;\n}\nexport interface DebounceSettingsLeading extends DebounceSettings {\n leading: true;\n}\nexport const debounce:\n | (<T extends (...args: any) => any>(\n func: T,\n wait: number | undefined,\n options?: DebounceSettingsLeading,\n ) => DebouncedFuncLeading<T>)\n | (<T extends (...args: any) => any>(\n func: T,\n wait?: number,\n options?: DebounceSettings,\n ) => DebouncedFunc<T>) = _debounce;\n\nconst UNIT_CONVERSION: Record<string, number> = {\n ms: 1, // milliseconds\n s: 1000, // seconds\n m: 60 * 1000, // minutes\n h: 60 * 60 * 1000, // hours\n d: 24 * 60 * 60 * 1000, // days\n w: 7 * 24 * 60 * 60 * 1000, // weeks\n};\n\n// sum duration from a string like '1d 4h 3m 25s 30ms'\nexport function parseDuration(str: string) {\n let total = 0;\n\n // split string by spaces (0m 35s) -> [0m, 35s]\n const chunks = str.split(' ');\n\n // parse each chunk\n for (const chunk of chunks) {\n // extract the duration and unit from the chunk\n const [, duration, unit] = chunk.match(/^(\\d+)(\\w+)$/) || [];\n\n // ignore invalid chunks\n if (!duration || !unit || !UNIT_CONVERSION[unit]) continue;\n\n // add to the total the duration in MS\n total += Number(duration) * UNIT_CONVERSION[unit];\n }\n\n return total;\n}\n\n// parse brickadia's time format (YYYY.MM.DD-HH.MM.SS) into an epoch ms value.\n// returns NaN for malformed/invalid input (e.g. a missing time part, or a date\n// Brickadia has overflowed past a valid range) instead of throwing. Callers\n// must guard with Number.isFinite before persisting, since NaN bound to a\n// NOT NULL integer column is stored as NULL and violates the constraint.\nexport function parseBrickadiaTime(str: string) {\n if (typeof str !== 'string') return NaN;\n const [date, time] = str.split('-');\n if (!date || !time) return NaN;\n return new Date(\n date.replace(/\\./g, '-') + 'T' + time.replace(/\\./g, ':') + 'Z',\n ).getTime();\n}\n"],"names":[],"mappings":";;;AAyCO,MAAM,WAUgB;AAE7B,MAAM,kBAA0C;AAAA,EAC9C,IAAI;AAAA;AAAA,EACJ,GAAG;AAAA;AAAA,EACH,GAAG,KAAK;AAAA;AAAA,EACR,GAAG,KAAK,KAAK;AAAA;AAAA,EACb,GAAG,KAAK,KAAK,KAAK;AAAA;AAAA,EAClB,GAAG,IAAI,KAAK,KAAK,KAAK;AAAA;AACxB;AAGO,SAAS,cAAc,KAAa;AACzC,MAAI,QAAQ;AAGZ,QAAM,SAAS,IAAI,MAAM,GAAG;AAG5B,aAAW,SAAS,QAAQ;AAE1B,UAAM,CAAA,EAAG,UAAU,IAAI,IAAI,MAAM,MAAM,cAAc,KAAK,CAAA;AAG1D,QAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,gBAAgB,IAAI,EAAG;AAGlD,aAAS,OAAO,QAAQ,IAAI,gBAAgB,IAAI;AAAA,EAClD;AAEA,SAAO;AACT;AAOO,SAAS,mBAAmB,KAAa;AAC9C,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,CAAC,MAAM,IAAI,IAAI,IAAI,MAAM,GAAG;AAClC,MAAI,CAAC,QAAQ,CAAC,KAAM,QAAO;AAC3B,UAAO,oBAAI;AAAA,IACT,KAAK,QAAQ,OAAO,GAAG,IAAI,MAAM,KAAK,QAAQ,OAAO,GAAG,IAAI;AAAA,EAAA,GAC5D,QAAA;AACJ;;;;"}
@@ -667,8 +667,10 @@ class Database extends EventEmitter {
667
667
  this.db.insert(schema.banHistory).values({
668
668
  banned: entry.banned,
669
669
  bannerId: entry.bannerId ?? "",
670
- created: typeof entry.created === "number" ? entry.created : 0,
671
- expires: typeof entry.expires === "number" ? entry.expires : 0,
670
+ // Number.isFinite (not typeof) so NaN from parseBrickadiaTime becomes
671
+ // 0, binding NaN to a NOT NULL integer column stores NULL and throws
672
+ created: Number.isFinite(entry.created) ? entry.created : 0,
673
+ expires: Number.isFinite(entry.expires) ? entry.expires : 0,
672
674
  reason: entry.reason ?? ""
673
675
  }).onConflictDoNothing().run();
674
676
  }
@@ -1 +1 @@
1
- {"version":3,"file":"database.js","sources":["../../../src/webserver/backend/database.ts"],"sourcesContent":["import { IServerConfig } from '@config/types';\nimport * as schema from '@/db/schema';\nimport type Omegga from '@omegga/server';\nimport { explode } from '@util/pattern';\nimport { parseBrickadiaTime } from '@util/time';\nimport bcrypt from 'bcryptjs';\nimport type BetterSqlite3 from 'better-sqlite3';\nimport chokidar from 'chokidar';\nimport {\n and,\n count,\n desc,\n eq,\n inArray,\n lt,\n gt,\n or,\n sql,\n asc,\n type SQL,\n} from 'drizzle-orm';\nimport type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';\nimport EventEmitter from 'events';\nimport { randomUUID } from 'node:crypto';\nimport path from 'path';\nimport Calendar from './calendar';\nimport { serverEvents } from './events';\nimport {\n EMPTY_PERMISSIONS,\n RootLevel,\n type PermissionSet,\n} from './permissions';\nimport type {\n IPlayer,\n IStoreAutoRestartConfig,\n IStoreBanHistory,\n IStoreDefaultPermissions,\n IStoreKickHistory,\n IStoreRole,\n IStoreUser,\n IUserHistory,\n IWebAuthnCredential,\n} from './types';\n\n// TODO: online users graph\n// TODO: player online times\n// TODO: bricks over time graph\n// TODO: minute server status for metrics\n// TODO: chat messages per min/hour/day check\n\n// generate a punchcard (days x week)\nconst createPunchcard = (): number[][] =>\n Array.from({ length: 7 }).map(() =>\n Array.from<number>({ length: 24 }).fill(0),\n );\n\n// the database keeps track of metrics for omegga\nexport default class Database extends EventEmitter {\n options: IServerConfig;\n omegga: Omegga;\n sqlite: BetterSqlite3.Database;\n db: BetterSQLite3Database;\n defaultPermissionsCache: IStoreDefaultPermissions | null = null;\n rolesCache: (IStoreRole & { id: string })[] | null = null;\n calendar: Calendar;\n private serverInstance: { id: string; date: number } | null = null;\n\n constructor(\n options: IServerConfig,\n omegga: Omegga,\n sqlite: BetterSqlite3.Database,\n db: BetterSQLite3Database,\n ) {\n super();\n this.options = options;\n this.omegga = omegga;\n this.sqlite = sqlite;\n this.db = db;\n this.calendar = new Calendar();\n }\n\n // get the running instance id of this omegga\n async getInstanceId() {\n if (this.serverInstance) return this.serverInstance.id;\n\n const id = randomUUID();\n this.db\n .insert(schema.serverInstances)\n .values({ id, date: Date.now() })\n .run();\n this.serverInstance = { id, date: Date.now() };\n\n // create a calendar of all previous chat messages\n const dates = this.db\n .select({ created: schema.chatLogs.created })\n .from(schema.chatLogs)\n .all();\n for (const d of dates) {\n this.calendar.addDate(d.created);\n }\n\n const watcher = chokidar.watch(\n path.join(this.omegga.configPath, 'BanList.json'),\n { persistent: false },\n );\n watcher\n .on('add', () => setTimeout(this.syncBanList.bind(this), 500))\n .on('change', () =>\n setTimeout(() => {\n this.syncBanList();\n this.emit('update.bans');\n }, 500),\n );\n setTimeout(this.syncBanList.bind(this), 500);\n\n const handleKick = (kicked: IPlayer, kicker: IPlayer, reason: string) => {\n // time the event listener should expire\n const eventExpire = Date.now() + 100;\n // detect a single leave\n this.omegga.once('leave', async leavingPlayer => {\n const now = Date.now();\n // if a player leaves more than 100ms from the kick command, it's bugged\n if (now > eventExpire) return;\n if (leavingPlayer.id === kicked.id) {\n this.upsertKickHistory({\n kicked: kicked.id,\n kickerId: kicker.id,\n created: now,\n reason,\n });\n }\n });\n };\n\n // for a5 is based on the chat event\n this.omegga.on('kick', (kicked, kicker, reason) => {\n const kickedPlayer = this.omegga.findPlayerByName(kicked);\n const kickerPlayer = this.omegga.findPlayerByName(kicker);\n // couldn't find kicked or kicker player\n if (!kickedPlayer || !kickerPlayer) return;\n handleKick(kickedPlayer, kickerPlayer, reason);\n });\n\n return id;\n }\n\n // synchronize the ban list with the one in the database\n syncBanList() {\n const banList = this.omegga.getBanList()?.banList;\n if (!banList) return;\n\n const tx = this.sqlite.transaction(() => {\n // upsert all bans\n for (const banned in banList) {\n this.upsertBanHistory({\n banned,\n bannerId: banList[banned].bannerId ?? '',\n created: parseBrickadiaTime(banList[banned].created) ?? 0,\n expires: parseBrickadiaTime(banList[banned].expires) ?? 0,\n reason: banList[banned].reason,\n });\n }\n });\n tx();\n }\n\n // determine if this user would be the first user (admin user)\n async isFirstUser() {\n const result = this.db.select({ count: count() }).from(schema.users).get();\n return (result?.count ?? 0) === 0;\n }\n\n // hash a password\n async hash(password: string) {\n return await bcrypt.hash(password, 10);\n }\n\n // create an admin user account, username can be blank (no password)\n async createAdminUser(username: string, password: string) {\n const h = await this.hash(password);\n const id = randomUUID();\n // create an owner user\n this.db\n .insert(schema.users)\n .values({\n id,\n created: Date.now(),\n lastOnline: Date.now(),\n username,\n hash: h,\n isOwner: true,\n roles: [],\n permissions: EMPTY_PERMISSIONS,\n totpEnabled: false,\n passkeys: [],\n recoveryCodes: [],\n playerId: '',\n })\n .run();\n return this.findUserById(id);\n }\n\n // determine if a user with this name exists\n async userExists(username: string) {\n const result = this.db\n .select({ count: count() })\n .from(schema.users)\n .where(eq(schema.users.username, username))\n .get();\n return (result?.count ?? 0) > 0;\n }\n\n // create a regular user account\n async createUser(username: string, password: string) {\n if (await this.userExists(username)) throw new Error('user already exists');\n const h = await this.hash(password);\n const id = randomUUID();\n this.db\n .insert(schema.users)\n .values({\n id,\n created: Date.now(),\n lastOnline: 0,\n username,\n hash: h,\n isOwner: false,\n roles: [],\n permissions: EMPTY_PERMISSIONS,\n totpEnabled: false,\n passkeys: [],\n recoveryCodes: [],\n playerId: '',\n })\n .run();\n return this.findUserById(id);\n }\n\n // set a user's password\n async userPasswd(username: string, password: string) {\n const h = await this.hash(password);\n if (!(await this.userExists(username)))\n throw new Error('user does not exist');\n this.db\n .update(schema.users)\n .set({ hash: h })\n .where(eq(schema.users.username, username))\n .run();\n }\n\n async banUser(username: string, banned: boolean) {\n this.db\n .update(schema.users)\n .set({ isBanned: banned })\n .where(eq(schema.users.username, username))\n .run();\n if (banned) serverEvents.emit('userInvalidated', username);\n return 1;\n }\n\n async deleteUser(username: string) {\n this.db\n .delete(schema.users)\n .where(eq(schema.users.username, username))\n .run();\n serverEvents.emit('userInvalidated', username);\n return 1;\n }\n\n async setUserPermissions(username: string, permissions: PermissionSet) {\n this.db\n .update(schema.users)\n .set({ permissions: permissions })\n .where(eq(schema.users.username, username))\n .run();\n }\n\n async getDefaultPermissions(): Promise<IStoreDefaultPermissions> {\n if (this.defaultPermissionsCache) return this.defaultPermissionsCache;\n const row = this.db\n .select()\n .from(schema.serverConfig)\n .where(eq(schema.serverConfig.key, 'defaultPermissions'))\n .get();\n if (!row) {\n const defaults: IStoreDefaultPermissions = {\n type: 'defaultPermissions',\n root: RootLevel.Read,\n domains: {},\n scopes: {},\n };\n this.db\n .insert(schema.serverConfig)\n .values({ key: 'defaultPermissions', value: defaults })\n .run();\n this.defaultPermissionsCache = defaults;\n return defaults;\n }\n const val = row.value as any;\n const result: IStoreDefaultPermissions = {\n type: 'defaultPermissions',\n root: val.root ?? RootLevel.Off,\n domains: val.domains ?? {},\n scopes: val.scopes ?? {},\n };\n this.defaultPermissionsCache = result;\n return result;\n }\n\n async setDefaultPermissions(\n permissions: Omit<IStoreDefaultPermissions, 'type'>,\n ) {\n const value = {\n root: permissions.root,\n domains: permissions.domains,\n scopes: permissions.scopes,\n };\n this.db\n .insert(schema.serverConfig)\n .values({ key: 'defaultPermissions', value })\n .onConflictDoUpdate({\n target: schema.serverConfig.key,\n set: { value },\n })\n .run();\n this.defaultPermissionsCache = null;\n }\n\n // get a user from credentials\n async authUser(username: string, password: string) {\n const row = this.db\n .select()\n .from(schema.users)\n .where(eq(schema.users.username, username))\n .get();\n // user not found\n if (!row || row.isBanned) return null;\n // make sure the user's password hash is valid\n if (await bcrypt.compare(password, row.hash)) {\n this.db\n .update(schema.users)\n .set({ lastOnline: Date.now() })\n .where(eq(schema.users.id, row.id))\n .run();\n // update last online status\n return this._toStoreUser(row);\n }\n // wrong password\n return null;\n }\n\n // find a user by object id\n async findUserById(id: string) {\n const row = this.db\n .select()\n .from(schema.users)\n .where(\n or(\n // the owner has no username, so everyone is the owner\n and(eq(schema.users.username, ''), eq(schema.users.isOwner, true)),\n // the user exists and has an id\n eq(schema.users.id, id),\n ),\n )\n .get();\n if (!row) return null;\n return this._toStoreUser(row);\n }\n\n async findUserByUsername(\n username: string,\n ): Promise<(IStoreUser & { id: string }) | null> {\n const row = this.db\n .select()\n .from(schema.users)\n .where(eq(schema.users.username, username))\n .get();\n if (!row) return null;\n return this._toStoreUser(row);\n }\n\n async findUserByPasskeyId(credentialId: string) {\n const rows = this.db\n .select()\n .from(schema.users)\n .where(\n sql`${schema.users.passkeys} LIKE ${'%\"id\":\"' + credentialId + '\"%'}`,\n )\n .all();\n for (const row of rows) {\n const passkeys = (row.passkeys ?? []) as IWebAuthnCredential[];\n if (passkeys.some(p => p.id === credentialId)) {\n return this._toStoreUser(row);\n }\n }\n return null;\n }\n\n updateUserLastOnline(id: string) {\n this.db\n .update(schema.users)\n .set({ lastOnline: Date.now() })\n .where(eq(schema.users.id, id))\n .run();\n }\n\n updateOwnerCredentials(id: string, username: string, hash: string) {\n this.db\n .update(schema.users)\n .set({ username, hash })\n .where(eq(schema.users.id, id))\n .run();\n }\n\n addUserRole(userId: string, roleId: string) {\n const row = this.db\n .select({ roles: schema.users.roles })\n .from(schema.users)\n .where(eq(schema.users.id, userId))\n .get();\n if (!row) return;\n const roles = (row.roles ?? []) as string[];\n if (!roles.includes(roleId)) {\n roles.push(roleId);\n this.db\n .update(schema.users)\n .set({ roles })\n .where(eq(schema.users.id, userId))\n .run();\n }\n }\n\n removeUserRole(userId: string, roleId: string) {\n const row = this.db\n .select({ roles: schema.users.roles })\n .from(schema.users)\n .where(eq(schema.users.id, userId))\n .get();\n if (!row) return;\n const roles = ((row.roles ?? []) as string[]).filter(r => r !== roleId);\n this.db\n .update(schema.users)\n .set({ roles })\n .where(eq(schema.users.id, userId))\n .run();\n }\n\n async setUserTotp(username: string, secret: string, enabled: boolean) {\n this.db\n .update(schema.users)\n .set({ totpSecret: secret, totpEnabled: enabled })\n .where(eq(schema.users.username, username))\n .run();\n }\n\n async disableUserTotp(username: string) {\n this.db\n .update(schema.users)\n .set({ totpEnabled: false, totpSecret: null })\n .where(eq(schema.users.username, username))\n .run();\n }\n\n async addPasskey(username: string, credential: IWebAuthnCredential) {\n const row = this.db\n .select({ id: schema.users.id, passkeys: schema.users.passkeys })\n .from(schema.users)\n .where(eq(schema.users.username, username))\n .get();\n if (!row) return;\n const passkeys = [\n ...((row.passkeys ?? []) as IWebAuthnCredential[]),\n credential,\n ];\n this.db\n .update(schema.users)\n .set({ passkeys })\n .where(eq(schema.users.id, row.id))\n .run();\n }\n\n async removePasskey(username: string, credentialId: string) {\n const row = this.db\n .select({ id: schema.users.id, passkeys: schema.users.passkeys })\n .from(schema.users)\n .where(eq(schema.users.username, username))\n .get();\n if (!row) return;\n const passkeys = ((row.passkeys ?? []) as IWebAuthnCredential[]).filter(\n p => p.id !== credentialId,\n );\n this.db\n .update(schema.users)\n .set({ passkeys })\n .where(eq(schema.users.id, row.id))\n .run();\n }\n\n async updatePasskeyCounter(\n username: string,\n credentialId: string,\n counter: number,\n ) {\n const row = this.db\n .select({ id: schema.users.id, passkeys: schema.users.passkeys })\n .from(schema.users)\n .where(eq(schema.users.username, username))\n .get();\n if (!row) return;\n const passkeys = ((row.passkeys ?? []) as IWebAuthnCredential[]).map(p =>\n p.id === credentialId ? { ...p, counter, lastUsed: Date.now() } : p,\n );\n this.db\n .update(schema.users)\n .set({ passkeys })\n .where(eq(schema.users.id, row.id))\n .run();\n }\n\n async setRecoveryCodes(username: string, hashedCodes: string[]) {\n this.db\n .update(schema.users)\n .set({ recoveryCodes: hashedCodes })\n .where(eq(schema.users.username, username))\n .run();\n }\n\n async removeRecoveryCode(username: string, hashedCode: string) {\n const row = this.db\n .select({\n id: schema.users.id,\n recoveryCodes: schema.users.recoveryCodes,\n })\n .from(schema.users)\n .where(eq(schema.users.username, username))\n .get();\n if (!row) return;\n const codes = ((row.recoveryCodes ?? []) as string[]).filter(\n c => c !== hashedCode,\n );\n this.db\n .update(schema.users)\n .set({ recoveryCodes: codes })\n .where(eq(schema.users.id, row.id))\n .run();\n }\n\n async resetUserMfa(username: string) {\n this.db\n .update(schema.users)\n .set({\n totpEnabled: false,\n totpSecret: null,\n passkeys: [],\n recoveryCodes: [],\n })\n .where(eq(schema.users.username, username))\n .run();\n }\n\n async getAllRoles(): Promise<(IStoreRole & { id: string })[]> {\n if (this.rolesCache) return this.rolesCache;\n const rows = this.db\n .select()\n .from(schema.webRoles)\n .orderBy(desc(schema.webRoles.order))\n .all();\n const roles = rows.map(r => ({\n type: 'webRole' as const,\n id: r.id,\n name: r.name,\n description: r.description,\n order: r.order,\n permissions: r.permissions as PermissionSet,\n }));\n this.rolesCache = roles;\n return roles;\n }\n\n invalidateRolesCache() {\n this.rolesCache = null;\n this.defaultPermissionsCache = null;\n }\n\n async getRole(id: string): Promise<(IStoreRole & { id: string }) | null> {\n const row = this.db\n .select()\n .from(schema.webRoles)\n .where(eq(schema.webRoles.id, id))\n .get();\n if (!row) return null;\n return {\n type: 'webRole',\n id: row.id,\n name: row.name,\n description: row.description,\n order: row.order,\n permissions: row.permissions as PermissionSet,\n };\n }\n\n async createRole(\n name: string,\n description: string,\n permissions: PermissionSet,\n ): Promise<IStoreRole & { id: string }> {\n const id = randomUUID();\n const tx = this.sqlite.transaction(() => {\n this.db\n .update(schema.webRoles)\n .set({ order: sql`${schema.webRoles.order} + 1` })\n .run();\n this.db\n .insert(schema.webRoles)\n .values({\n id,\n name,\n description,\n order: 1,\n permissions: permissions,\n })\n .run();\n });\n tx();\n this.invalidateRolesCache();\n return {\n type: 'webRole',\n id,\n name,\n description,\n order: 1,\n permissions: permissions,\n };\n }\n\n async updateRole(\n id: string,\n updates: Partial<Pick<IStoreRole, 'name' | 'description' | 'order'>> & {\n permissions?: PermissionSet;\n },\n ): Promise<void> {\n const $set: Record<string, any> = {};\n if (updates.name !== undefined) $set.name = updates.name;\n if (updates.description !== undefined)\n $set.description = updates.description;\n if (updates.order !== undefined) $set.order = updates.order;\n if (updates.permissions !== undefined)\n $set.permissions = updates.permissions;\n if (Object.keys($set).length > 0) {\n this.db\n .update(schema.webRoles)\n .set($set)\n .where(eq(schema.webRoles.id, id))\n .run();\n }\n this.invalidateRolesCache();\n }\n\n async deleteRole(id: string): Promise<string> {\n const role = await this.getRole(id);\n if (!role) return 'role not found';\n\n const tx = this.sqlite.transaction(() => {\n this.db.delete(schema.webRoles).where(eq(schema.webRoles.id, id)).run();\n const usersWithRole = this.db\n .select({ id: schema.users.id, roles: schema.users.roles })\n .from(schema.users)\n .all();\n for (const u of usersWithRole) {\n const roles = ((u.roles ?? []) as string[]).filter(r => r !== id);\n if (roles.length !== ((u.roles ?? []) as string[]).length) {\n this.db\n .update(schema.users)\n .set({ roles })\n .where(eq(schema.users.id, u.id))\n .run();\n }\n }\n });\n tx();\n this.invalidateRolesCache();\n return '';\n }\n\n async reorderRoles(entries: { id: string; order: number }[]): Promise<void> {\n const tx = this.sqlite.transaction(() => {\n for (const { id, order } of entries) {\n this.db\n .update(schema.webRoles)\n .set({ order })\n .where(eq(schema.webRoles.id, id))\n .run();\n }\n });\n tx();\n this.invalidateRolesCache();\n }\n\n async getUserRolePermissions(user: IStoreUser): Promise<PermissionSet[]> {\n const defaults = await this.getDefaultPermissions();\n const defaultPerms: PermissionSet = {\n root: defaults.root,\n domains: defaults.domains,\n scopes: defaults.scopes,\n };\n const allRoles = await this.getAllRoles();\n const userRoles = allRoles.filter(r => user.roles.includes(r.id));\n return [defaultPerms, ...userRoles.map(r => r.permissions)];\n }\n\n async getUserAssignedRoles(\n user: IStoreUser,\n ): Promise<(IStoreRole & { id: string })[]> {\n const allRoles = await this.getAllRoles();\n return allRoles.filter(r => user.roles.includes(r.id));\n }\n\n // add a chat message to the chat log store\n async addChatLog(\n action: 'msg' | 'server' | 'leave' | 'join',\n user: IPlayer,\n message?: string,\n ) {\n this.calendar.addDate(Date.now());\n const row = this.db\n .insert(schema.chatLogs)\n .values({\n created: Date.now(),\n instanceId: await this.getInstanceId(),\n action,\n user,\n ...(message ? { message } : {}),\n })\n .returning()\n .get();\n return {\n type: 'chat' as const,\n _id: String(row.id),\n id: row.id,\n created: row.created,\n instanceId: row.instanceId,\n action: row.action,\n user: row.user,\n message: row.message,\n };\n }\n\n // get recent chat activity\n async getChats({\n count: limit = 50,\n sameServer,\n before,\n after,\n }: {\n count?: number;\n sameServer?: boolean;\n before?: number;\n after?: number;\n } = {}) {\n const conditions = [];\n if (sameServer) {\n conditions.push(\n eq(schema.chatLogs.instanceId, await this.getInstanceId()),\n );\n }\n if (before) {\n conditions.push(lt(schema.chatLogs.created, before));\n } else if (after) {\n conditions.push(gt(schema.chatLogs.created, after));\n } else {\n conditions.push(lt(schema.chatLogs.created, Date.now()));\n }\n\n const orderDir = !before && after ? asc : desc;\n const rows = this.db\n .select()\n .from(schema.chatLogs)\n .where(conditions.length > 1 ? and(...conditions) : conditions[0])\n .orderBy(orderDir(schema.chatLogs.created))\n .limit(limit)\n .all();\n\n return rows.map(r => ({\n type: 'chat' as const,\n _id: String(r.id),\n created: r.created,\n instanceId: r.instanceId,\n action: r.action,\n user: r.user,\n message: r.message,\n }));\n }\n\n // get paginated players\n async getPlayers({\n count: limit = 50,\n search = '',\n page = 0,\n sort = 'name',\n direction = 1,\n limitId = undefined,\n }: {\n count?: number;\n search?: string;\n page?: number;\n sort?: string;\n direction?: number;\n limitId?: string[];\n } = {}) {\n const pattern = search.length > 0 ? explode(search) : null;\n const patternSource = pattern?.source;\n\n // an empty filter (e.g. \"banned\" with no active bans) matches nothing\n if (limitId && limitId.length === 0)\n return { pages: 0, total: 0, players: [] };\n\n const conditions: SQL[] = [];\n\n if (limitId) {\n conditions.push(inArray(schema.playerHistory.id, limitId));\n }\n\n if (patternSource) {\n // match name history values via json_each so the pattern can't hit\n // JSON keys or date timestamps\n conditions.push(sql`(\n ${schema.playerHistory.id} = ${search}\n OR ${schema.playerHistory.name} REGEXP ${patternSource}\n OR ${schema.playerHistory.displayName} REGEXP ${patternSource}\n OR EXISTS (\n SELECT 1 FROM json_each(${schema.playerHistory.nameHistory})\n WHERE json_each.type = 'object'\n AND (json_extract(json_each.value, '$.name') REGEXP ${patternSource}\n OR json_extract(json_each.value, '$.displayName') REGEXP ${patternSource})\n )\n )`);\n }\n\n const baseWhere = conditions.length > 0 ? and(...conditions) : undefined;\n\n const sortCol =\n sort === 'lastSeen'\n ? schema.playerHistory.lastSeen\n : sort === 'created'\n ? schema.playerHistory.created\n : sort === 'heartbeats'\n ? schema.playerHistory.heartbeats\n : sort === 'sessions'\n ? schema.playerHistory.sessions\n : sql`${schema.playerHistory.name} COLLATE NOCASE`;\n const orderDir = direction === 1 ? asc : desc;\n\n const totalResult = this.db\n .select({ count: count() })\n .from(schema.playerHistory)\n .where(baseWhere)\n .get();\n const players = this.db\n .select()\n .from(schema.playerHistory)\n .where(baseWhere)\n .orderBy(orderDir(sortCol))\n .limit(limit)\n .offset(limit * page)\n .all();\n\n const total = totalResult?.count ?? 0;\n const result = players.map(p => this._toUserHistory(p));\n\n // exact match detection\n if (search.length > 0 && page === 0) {\n const exactRows = this.db\n .select()\n .from(schema.playerHistory)\n .where(\n or(\n eq(schema.playerHistory.id, search),\n eq(schema.playerHistory.name, search),\n eq(schema.playerHistory.displayName, search),\n ),\n )\n .all();\n if (exactRows.length > 0) {\n for (const res of exactRows) {\n const idx = result.findIndex(p => p.id === res.id);\n if (idx >= 0) result.splice(idx, 1);\n }\n result.splice(0, 0, ...exactRows.map(r => this._toUserHistory(r)));\n }\n }\n\n return { pages: Math.ceil(total / limit), total, players: result };\n }\n\n // get paginated users\n async getUsers({\n count: limit = 50,\n search = '',\n page = 0,\n sort = 'name',\n direction = 1,\n } = {}) {\n const pattern = search.length > 0 ? explode(search) : null;\n\n let baseWhere = sql`1=1`;\n if (pattern) {\n baseWhere = sql`(\n ${schema.users.playerId} = ${search}\n OR ${schema.users.username} REGEXP ${pattern.source}\n )`;\n }\n\n const sortCol =\n sort === 'lastOnline'\n ? schema.users.lastOnline\n : sort === 'created'\n ? schema.users.created\n : sql`${schema.users.username} COLLATE NOCASE`;\n const orderDir = direction === 1 ? asc : desc;\n\n const totalResult = this.db\n .select({ count: count() })\n .from(schema.users)\n .where(baseWhere)\n .get();\n const users = this.db\n .select()\n .from(schema.users)\n .where(baseWhere)\n .orderBy(orderDir(sortCol))\n .limit(limit)\n .offset(limit * page)\n .all();\n\n return {\n pages: Math.ceil((totalResult?.count ?? 0) / limit),\n total: totalResult?.count ?? 0,\n users: users.map(u => this._toStoreUser(u)),\n };\n }\n\n // get an individual player, ban and kick history, and notes\n async getPlayer(id: string) {\n const player = this.db\n .select()\n .from(schema.playerHistory)\n .where(eq(schema.playerHistory.id, id))\n .get();\n if (!player) return null;\n\n const banHist = this.db\n .select()\n .from(schema.banHistory)\n .where(eq(schema.banHistory.banned, id))\n .orderBy(desc(schema.banHistory.created))\n .limit(25)\n .all();\n\n const kickHist = this.db\n .select()\n .from(schema.kickHistory)\n .where(eq(schema.kickHistory.kicked, id))\n .orderBy(desc(schema.kickHistory.created))\n .limit(25)\n .all();\n\n const noteRows = this.db\n .select()\n .from(schema.playerNotes)\n .where(eq(schema.playerNotes.playerId, id))\n .limit(25)\n .all();\n\n return {\n ...this._toUserHistory(player),\n banHistory: banHist.map(b => ({\n type: 'banHistory' as const,\n _id: String(b.id),\n banned: b.banned,\n bannerId: b.bannerId,\n created: b.created,\n expires: b.expires,\n reason: b.reason,\n })),\n kickHistory: kickHist.map(k => ({\n type: 'kickHistory' as const,\n _id: String(k.id),\n kicked: k.kicked,\n kickerId: k.kickerId,\n created: k.created,\n reason: k.reason,\n })),\n notes: noteRows.map(n => ({\n type: 'note' as const,\n _id: String(n.id),\n id: n.playerId,\n note: n.note,\n })),\n };\n }\n\n // add a user to the visit history, returns true if this is a first visit\n async addVisit(user: Required<IPlayer>) {\n const now = Date.now();\n // resolve the instance id before opening the transaction — the tx callback\n // must stay synchronous, and this is the only await in the method\n const instanceId = await this.getInstanceId();\n\n // read-modify-write the row atomically so two near-simultaneous joins for\n // the same id can't both insert (PK conflict) or clobber each other's\n // nameHistory/counter updates from a shared stale snapshot\n const visit = this.sqlite.transaction(() => {\n const existing = this.db\n .select()\n .from(schema.playerHistory)\n .where(eq(schema.playerHistory.id, user.id))\n .get();\n\n if (!existing) {\n this.db\n .insert(schema.playerHistory)\n .values({\n id: user.id,\n name: user.name,\n displayName: user.displayName,\n nameHistory: [\n { name: user.name, displayName: user.displayName, date: now },\n ],\n ips: [],\n created: now,\n lastSeen: now,\n lastInstanceId: instanceId,\n heartbeats: 0,\n sessions: 1,\n instances: 1,\n })\n .run();\n return true;\n }\n\n const nameHistory = (existing.nameHistory ?? []) as {\n name: string;\n displayName: string;\n date: number;\n }[];\n const hasName = nameHistory.some(\n h => h.name === user.name && h.displayName === user.displayName,\n );\n if (!hasName) {\n nameHistory.push({\n name: user.name,\n displayName: user.displayName,\n date: now,\n });\n }\n\n this.db\n .update(schema.playerHistory)\n .set({\n lastSeen: now,\n name: user.name,\n displayName: user.displayName,\n lastInstanceId: instanceId,\n nameHistory,\n sessions: sql`${schema.playerHistory.sessions} + ${existing.lastSeen < now - 60 * 60 * 1000 ? 1 : 0}`,\n instances: sql`${schema.playerHistory.instances} + ${existing.lastInstanceId !== instanceId ? 1 : 0}`,\n })\n .where(eq(schema.playerHistory.id, existing.id))\n .run();\n return false;\n });\n\n return visit();\n }\n\n // use data from minutely heartbeats to fuel metrics\n async addHeartbeat(data: {\n bricks: number;\n players: string[];\n ips: Record<string, string>;\n }) {\n const now = Date.now();\n const tx = this.sqlite.transaction(() => {\n this.db\n .insert(schema.heartbeats)\n .values({\n created: now,\n bricks: data.bricks,\n players: data.players,\n })\n .run();\n\n if (data.players.length > 0) {\n // one bulk UPDATE for the shared lastSeen/heartbeats bump\n this.db\n .update(schema.playerHistory)\n .set({\n lastSeen: now,\n heartbeats: sql`${schema.playerHistory.heartbeats} + 1`,\n })\n .where(inArray(schema.playerHistory.id, data.players))\n .run();\n\n const players = this.db\n .select()\n .from(schema.playerHistory)\n .where(inArray(schema.playerHistory.id, data.players))\n .all();\n\n for (const p of players) {\n const ip = data.ips[p.id];\n if (ip && !((p.ips ?? []) as string[]).includes(ip)) {\n const ips = [...((p.ips ?? []) as string[]), ip];\n this.db\n .update(schema.playerHistory)\n .set({ ips })\n .where(eq(schema.playerHistory.id, p.id))\n .run();\n }\n }\n }\n });\n tx();\n }\n\n upsertBanHistory(entry: Omit<IStoreBanHistory, 'type'>) {\n this.db\n .insert(schema.banHistory)\n .values({\n banned: entry.banned,\n bannerId: entry.bannerId ?? '',\n created: typeof entry.created === 'number' ? entry.created : 0,\n expires: typeof entry.expires === 'number' ? entry.expires : 0,\n reason: entry.reason ?? '',\n })\n .onConflictDoNothing()\n .run();\n }\n\n upsertKickHistory(entry: Omit<IStoreKickHistory, 'type'>) {\n this.db\n .insert(schema.kickHistory)\n .values({\n kicked: entry.kicked,\n kickerId: entry.kickerId ?? '',\n created: entry.created ?? 0,\n reason: entry.reason ?? '',\n })\n .onConflictDoNothing()\n .run();\n }\n\n async getAutoRestartConfig(): Promise<IStoreAutoRestartConfig> {\n const row = this.db\n .select()\n .from(schema.serverConfig)\n .where(eq(schema.serverConfig.key, 'autoRestartConfig'))\n .get();\n if (!row) {\n const config: IStoreAutoRestartConfig = {\n type: 'autoRestartConfig',\n maxUptime: 48,\n maxUptimeEnabled: false,\n emptyUptime: 24,\n emptyUptimeEnabled: false,\n dailyHour: 2,\n dailyHourEnabled: false,\n autoUpdateEnabled: true,\n autoUpdateIntervalMins: 60,\n announcementEnabled: true,\n playersEnabled: true,\n saveWorld: true,\n crashRestartEnabled: true,\n };\n this.db\n .insert(schema.serverConfig)\n .values({ key: 'autoRestartConfig', value: config })\n .run();\n return config;\n }\n const config = row.value as IStoreAutoRestartConfig;\n config.type = 'autoRestartConfig';\n config.autoUpdateEnabled ??= true;\n config.autoUpdateIntervalMins ??= 60;\n config.saveWorld ??= true;\n config.crashRestartEnabled ??= true;\n return config;\n }\n\n async setAutoRestartConfig(config: IStoreAutoRestartConfig): Promise<void> {\n config.maxUptime = Math.round(Math.max(1, Math.min(config.maxUptime, 168)));\n config.emptyUptime = Math.round(\n Math.max(1, Math.min(config.emptyUptime, 168)),\n );\n config.dailyHour = Math.round(Math.max(0, Math.min(config.dailyHour, 23)));\n config.autoUpdateIntervalMins = Math.round(\n Math.max(10, Math.min(config.autoUpdateIntervalMins ?? 60, Infinity)),\n );\n config.autoUpdateEnabled ??= true;\n this.db\n .insert(schema.serverConfig)\n .values({ key: 'autoRestartConfig', value: config })\n .onConflictDoUpdate({\n target: schema.serverConfig.key,\n set: { value: config },\n })\n .run();\n }\n\n // update player online-time punchcard\n async updatePlayerPunchcard(numNewPlayers: number) {\n const now = new Date();\n const time = now.getTime();\n const hour = now.getUTCHours();\n const day = now.getUTCDay();\n const month = now.getUTCMonth();\n const year = now.getUTCFullYear();\n\n const card = this.db\n .select()\n .from(schema.punchcards)\n .where(\n and(\n eq(schema.punchcards.kind, 'playerCount'),\n eq(schema.punchcards.month, month),\n eq(schema.punchcards.year, year),\n ),\n )\n .get();\n\n if (!card) {\n const punchcard = createPunchcard();\n punchcard[day][hour] = numNewPlayers;\n this.db\n .insert(schema.punchcards)\n .values({\n kind: 'playerCount',\n created: time,\n updated: time,\n month,\n year,\n punchcard,\n })\n .run();\n } else {\n const punchcard = card.punchcard as number[][];\n punchcard[day][hour] += numNewPlayers;\n this.db\n .update(schema.punchcards)\n .set({ punchcard, updated: time })\n .where(eq(schema.punchcards.id, card.id))\n .run();\n }\n }\n\n private _toStoreUser(\n row: typeof schema.users.$inferSelect,\n ): IStoreUser & { id: string; _id: string } {\n const user: IStoreUser & { id: string; _id: string } = {\n type: 'user',\n id: row.id,\n _id: row.id,\n created: row.created,\n lastOnline: row.lastOnline,\n username: row.username,\n hash: row.hash,\n isOwner: row.isOwner,\n roles: (row.roles ?? []) as string[],\n playerId: row.playerId,\n isBanned: row.isBanned,\n permissions:\n (row.permissions as PermissionSet | null) ?? EMPTY_PERMISSIONS,\n totpSecret: row.totpSecret ?? undefined,\n totpEnabled: row.totpEnabled,\n passkeys: (row.passkeys ?? []) as IWebAuthnCredential[],\n recoveryCodes: (row.recoveryCodes ?? []) as string[],\n };\n return user;\n }\n\n private _toUserHistory(\n row: typeof schema.playerHistory.$inferSelect,\n ): IUserHistory & { _id: string } {\n return {\n type: 'userHistory',\n _id: row.id,\n id: row.id,\n name: row.name,\n displayName: row.displayName,\n nameHistory: (row.nameHistory ?? []) as {\n displayName: string;\n name: string;\n date: number;\n }[],\n ips: (row.ips ?? []) as string[],\n created: row.created,\n lastSeen: row.lastSeen,\n lastInstanceId: row.lastInstanceId,\n heartbeats: row.heartbeats,\n sessions: row.sessions,\n instances: row.instances,\n };\n }\n}\n"],"names":["Calendar","randomUUID","schema.serverInstances","schema.chatLogs","parseBrickadiaTime","count","schema.users","EMPTY_PERMISSIONS","eq","serverEvents","permissions","schema.serverConfig","RootLevel","or","and","sql","schema.webRoles","desc","lt","gt","asc","pattern","explode","inArray","schema.playerHistory","schema.banHistory","schema.kickHistory","schema.playerNotes","schema.heartbeats","config","time","schema.punchcards"],"mappings":"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"}
1
+ {"version":3,"file":"database.js","sources":["../../../src/webserver/backend/database.ts"],"sourcesContent":["import { IServerConfig } from '@config/types';\nimport * as schema from '@/db/schema';\nimport type Omegga from '@omegga/server';\nimport { explode } from '@util/pattern';\nimport { parseBrickadiaTime } from '@util/time';\nimport bcrypt from 'bcryptjs';\nimport type BetterSqlite3 from 'better-sqlite3';\nimport chokidar from 'chokidar';\nimport {\n and,\n count,\n desc,\n eq,\n inArray,\n lt,\n gt,\n or,\n sql,\n asc,\n type SQL,\n} from 'drizzle-orm';\nimport type { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';\nimport EventEmitter from 'events';\nimport { randomUUID } from 'node:crypto';\nimport path from 'path';\nimport Calendar from './calendar';\nimport { serverEvents } from './events';\nimport {\n EMPTY_PERMISSIONS,\n RootLevel,\n type PermissionSet,\n} from './permissions';\nimport type {\n IPlayer,\n IStoreAutoRestartConfig,\n IStoreBanHistory,\n IStoreDefaultPermissions,\n IStoreKickHistory,\n IStoreRole,\n IStoreUser,\n IUserHistory,\n IWebAuthnCredential,\n} from './types';\n\n// TODO: online users graph\n// TODO: player online times\n// TODO: bricks over time graph\n// TODO: minute server status for metrics\n// TODO: chat messages per min/hour/day check\n\n// generate a punchcard (days x week)\nconst createPunchcard = (): number[][] =>\n Array.from({ length: 7 }).map(() =>\n Array.from<number>({ length: 24 }).fill(0),\n );\n\n// the database keeps track of metrics for omegga\nexport default class Database extends EventEmitter {\n options: IServerConfig;\n omegga: Omegga;\n sqlite: BetterSqlite3.Database;\n db: BetterSQLite3Database;\n defaultPermissionsCache: IStoreDefaultPermissions | null = null;\n rolesCache: (IStoreRole & { id: string })[] | null = null;\n calendar: Calendar;\n private serverInstance: { id: string; date: number } | null = null;\n\n constructor(\n options: IServerConfig,\n omegga: Omegga,\n sqlite: BetterSqlite3.Database,\n db: BetterSQLite3Database,\n ) {\n super();\n this.options = options;\n this.omegga = omegga;\n this.sqlite = sqlite;\n this.db = db;\n this.calendar = new Calendar();\n }\n\n // get the running instance id of this omegga\n async getInstanceId() {\n if (this.serverInstance) return this.serverInstance.id;\n\n const id = randomUUID();\n this.db\n .insert(schema.serverInstances)\n .values({ id, date: Date.now() })\n .run();\n this.serverInstance = { id, date: Date.now() };\n\n // create a calendar of all previous chat messages\n const dates = this.db\n .select({ created: schema.chatLogs.created })\n .from(schema.chatLogs)\n .all();\n for (const d of dates) {\n this.calendar.addDate(d.created);\n }\n\n const watcher = chokidar.watch(\n path.join(this.omegga.configPath, 'BanList.json'),\n { persistent: false },\n );\n watcher\n .on('add', () => setTimeout(this.syncBanList.bind(this), 500))\n .on('change', () =>\n setTimeout(() => {\n this.syncBanList();\n this.emit('update.bans');\n }, 500),\n );\n setTimeout(this.syncBanList.bind(this), 500);\n\n const handleKick = (kicked: IPlayer, kicker: IPlayer, reason: string) => {\n // time the event listener should expire\n const eventExpire = Date.now() + 100;\n // detect a single leave\n this.omegga.once('leave', async leavingPlayer => {\n const now = Date.now();\n // if a player leaves more than 100ms from the kick command, it's bugged\n if (now > eventExpire) return;\n if (leavingPlayer.id === kicked.id) {\n this.upsertKickHistory({\n kicked: kicked.id,\n kickerId: kicker.id,\n created: now,\n reason,\n });\n }\n });\n };\n\n // for a5 is based on the chat event\n this.omegga.on('kick', (kicked, kicker, reason) => {\n const kickedPlayer = this.omegga.findPlayerByName(kicked);\n const kickerPlayer = this.omegga.findPlayerByName(kicker);\n // couldn't find kicked or kicker player\n if (!kickedPlayer || !kickerPlayer) return;\n handleKick(kickedPlayer, kickerPlayer, reason);\n });\n\n return id;\n }\n\n // synchronize the ban list with the one in the database\n syncBanList() {\n const banList = this.omegga.getBanList()?.banList;\n if (!banList) return;\n\n const tx = this.sqlite.transaction(() => {\n // upsert all bans\n for (const banned in banList) {\n this.upsertBanHistory({\n banned,\n bannerId: banList[banned].bannerId ?? '',\n created: parseBrickadiaTime(banList[banned].created) ?? 0,\n expires: parseBrickadiaTime(banList[banned].expires) ?? 0,\n reason: banList[banned].reason,\n });\n }\n });\n tx();\n }\n\n // determine if this user would be the first user (admin user)\n async isFirstUser() {\n const result = this.db.select({ count: count() }).from(schema.users).get();\n return (result?.count ?? 0) === 0;\n }\n\n // hash a password\n async hash(password: string) {\n return await bcrypt.hash(password, 10);\n }\n\n // create an admin user account, username can be blank (no password)\n async createAdminUser(username: string, password: string) {\n const h = await this.hash(password);\n const id = randomUUID();\n // create an owner user\n this.db\n .insert(schema.users)\n .values({\n id,\n created: Date.now(),\n lastOnline: Date.now(),\n username,\n hash: h,\n isOwner: true,\n roles: [],\n permissions: EMPTY_PERMISSIONS,\n totpEnabled: false,\n passkeys: [],\n recoveryCodes: [],\n playerId: '',\n })\n .run();\n return this.findUserById(id);\n }\n\n // determine if a user with this name exists\n async userExists(username: string) {\n const result = this.db\n .select({ count: count() })\n .from(schema.users)\n .where(eq(schema.users.username, username))\n .get();\n return (result?.count ?? 0) > 0;\n }\n\n // create a regular user account\n async createUser(username: string, password: string) {\n if (await this.userExists(username)) throw new Error('user already exists');\n const h = await this.hash(password);\n const id = randomUUID();\n this.db\n .insert(schema.users)\n .values({\n id,\n created: Date.now(),\n lastOnline: 0,\n username,\n hash: h,\n isOwner: false,\n roles: [],\n permissions: EMPTY_PERMISSIONS,\n totpEnabled: false,\n passkeys: [],\n recoveryCodes: [],\n playerId: '',\n })\n .run();\n return this.findUserById(id);\n }\n\n // set a user's password\n async userPasswd(username: string, password: string) {\n const h = await this.hash(password);\n if (!(await this.userExists(username)))\n throw new Error('user does not exist');\n this.db\n .update(schema.users)\n .set({ hash: h })\n .where(eq(schema.users.username, username))\n .run();\n }\n\n async banUser(username: string, banned: boolean) {\n this.db\n .update(schema.users)\n .set({ isBanned: banned })\n .where(eq(schema.users.username, username))\n .run();\n if (banned) serverEvents.emit('userInvalidated', username);\n return 1;\n }\n\n async deleteUser(username: string) {\n this.db\n .delete(schema.users)\n .where(eq(schema.users.username, username))\n .run();\n serverEvents.emit('userInvalidated', username);\n return 1;\n }\n\n async setUserPermissions(username: string, permissions: PermissionSet) {\n this.db\n .update(schema.users)\n .set({ permissions: permissions })\n .where(eq(schema.users.username, username))\n .run();\n }\n\n async getDefaultPermissions(): Promise<IStoreDefaultPermissions> {\n if (this.defaultPermissionsCache) return this.defaultPermissionsCache;\n const row = this.db\n .select()\n .from(schema.serverConfig)\n .where(eq(schema.serverConfig.key, 'defaultPermissions'))\n .get();\n if (!row) {\n const defaults: IStoreDefaultPermissions = {\n type: 'defaultPermissions',\n root: RootLevel.Read,\n domains: {},\n scopes: {},\n };\n this.db\n .insert(schema.serverConfig)\n .values({ key: 'defaultPermissions', value: defaults })\n .run();\n this.defaultPermissionsCache = defaults;\n return defaults;\n }\n const val = row.value as any;\n const result: IStoreDefaultPermissions = {\n type: 'defaultPermissions',\n root: val.root ?? RootLevel.Off,\n domains: val.domains ?? {},\n scopes: val.scopes ?? {},\n };\n this.defaultPermissionsCache = result;\n return result;\n }\n\n async setDefaultPermissions(\n permissions: Omit<IStoreDefaultPermissions, 'type'>,\n ) {\n const value = {\n root: permissions.root,\n domains: permissions.domains,\n scopes: permissions.scopes,\n };\n this.db\n .insert(schema.serverConfig)\n .values({ key: 'defaultPermissions', value })\n .onConflictDoUpdate({\n target: schema.serverConfig.key,\n set: { value },\n })\n .run();\n this.defaultPermissionsCache = null;\n }\n\n // get a user from credentials\n async authUser(username: string, password: string) {\n const row = this.db\n .select()\n .from(schema.users)\n .where(eq(schema.users.username, username))\n .get();\n // user not found\n if (!row || row.isBanned) return null;\n // make sure the user's password hash is valid\n if (await bcrypt.compare(password, row.hash)) {\n this.db\n .update(schema.users)\n .set({ lastOnline: Date.now() })\n .where(eq(schema.users.id, row.id))\n .run();\n // update last online status\n return this._toStoreUser(row);\n }\n // wrong password\n return null;\n }\n\n // find a user by object id\n async findUserById(id: string) {\n const row = this.db\n .select()\n .from(schema.users)\n .where(\n or(\n // the owner has no username, so everyone is the owner\n and(eq(schema.users.username, ''), eq(schema.users.isOwner, true)),\n // the user exists and has an id\n eq(schema.users.id, id),\n ),\n )\n .get();\n if (!row) return null;\n return this._toStoreUser(row);\n }\n\n async findUserByUsername(\n username: string,\n ): Promise<(IStoreUser & { id: string }) | null> {\n const row = this.db\n .select()\n .from(schema.users)\n .where(eq(schema.users.username, username))\n .get();\n if (!row) return null;\n return this._toStoreUser(row);\n }\n\n async findUserByPasskeyId(credentialId: string) {\n const rows = this.db\n .select()\n .from(schema.users)\n .where(\n sql`${schema.users.passkeys} LIKE ${'%\"id\":\"' + credentialId + '\"%'}`,\n )\n .all();\n for (const row of rows) {\n const passkeys = (row.passkeys ?? []) as IWebAuthnCredential[];\n if (passkeys.some(p => p.id === credentialId)) {\n return this._toStoreUser(row);\n }\n }\n return null;\n }\n\n updateUserLastOnline(id: string) {\n this.db\n .update(schema.users)\n .set({ lastOnline: Date.now() })\n .where(eq(schema.users.id, id))\n .run();\n }\n\n updateOwnerCredentials(id: string, username: string, hash: string) {\n this.db\n .update(schema.users)\n .set({ username, hash })\n .where(eq(schema.users.id, id))\n .run();\n }\n\n addUserRole(userId: string, roleId: string) {\n const row = this.db\n .select({ roles: schema.users.roles })\n .from(schema.users)\n .where(eq(schema.users.id, userId))\n .get();\n if (!row) return;\n const roles = (row.roles ?? []) as string[];\n if (!roles.includes(roleId)) {\n roles.push(roleId);\n this.db\n .update(schema.users)\n .set({ roles })\n .where(eq(schema.users.id, userId))\n .run();\n }\n }\n\n removeUserRole(userId: string, roleId: string) {\n const row = this.db\n .select({ roles: schema.users.roles })\n .from(schema.users)\n .where(eq(schema.users.id, userId))\n .get();\n if (!row) return;\n const roles = ((row.roles ?? []) as string[]).filter(r => r !== roleId);\n this.db\n .update(schema.users)\n .set({ roles })\n .where(eq(schema.users.id, userId))\n .run();\n }\n\n async setUserTotp(username: string, secret: string, enabled: boolean) {\n this.db\n .update(schema.users)\n .set({ totpSecret: secret, totpEnabled: enabled })\n .where(eq(schema.users.username, username))\n .run();\n }\n\n async disableUserTotp(username: string) {\n this.db\n .update(schema.users)\n .set({ totpEnabled: false, totpSecret: null })\n .where(eq(schema.users.username, username))\n .run();\n }\n\n async addPasskey(username: string, credential: IWebAuthnCredential) {\n const row = this.db\n .select({ id: schema.users.id, passkeys: schema.users.passkeys })\n .from(schema.users)\n .where(eq(schema.users.username, username))\n .get();\n if (!row) return;\n const passkeys = [\n ...((row.passkeys ?? []) as IWebAuthnCredential[]),\n credential,\n ];\n this.db\n .update(schema.users)\n .set({ passkeys })\n .where(eq(schema.users.id, row.id))\n .run();\n }\n\n async removePasskey(username: string, credentialId: string) {\n const row = this.db\n .select({ id: schema.users.id, passkeys: schema.users.passkeys })\n .from(schema.users)\n .where(eq(schema.users.username, username))\n .get();\n if (!row) return;\n const passkeys = ((row.passkeys ?? []) as IWebAuthnCredential[]).filter(\n p => p.id !== credentialId,\n );\n this.db\n .update(schema.users)\n .set({ passkeys })\n .where(eq(schema.users.id, row.id))\n .run();\n }\n\n async updatePasskeyCounter(\n username: string,\n credentialId: string,\n counter: number,\n ) {\n const row = this.db\n .select({ id: schema.users.id, passkeys: schema.users.passkeys })\n .from(schema.users)\n .where(eq(schema.users.username, username))\n .get();\n if (!row) return;\n const passkeys = ((row.passkeys ?? []) as IWebAuthnCredential[]).map(p =>\n p.id === credentialId ? { ...p, counter, lastUsed: Date.now() } : p,\n );\n this.db\n .update(schema.users)\n .set({ passkeys })\n .where(eq(schema.users.id, row.id))\n .run();\n }\n\n async setRecoveryCodes(username: string, hashedCodes: string[]) {\n this.db\n .update(schema.users)\n .set({ recoveryCodes: hashedCodes })\n .where(eq(schema.users.username, username))\n .run();\n }\n\n async removeRecoveryCode(username: string, hashedCode: string) {\n const row = this.db\n .select({\n id: schema.users.id,\n recoveryCodes: schema.users.recoveryCodes,\n })\n .from(schema.users)\n .where(eq(schema.users.username, username))\n .get();\n if (!row) return;\n const codes = ((row.recoveryCodes ?? []) as string[]).filter(\n c => c !== hashedCode,\n );\n this.db\n .update(schema.users)\n .set({ recoveryCodes: codes })\n .where(eq(schema.users.id, row.id))\n .run();\n }\n\n async resetUserMfa(username: string) {\n this.db\n .update(schema.users)\n .set({\n totpEnabled: false,\n totpSecret: null,\n passkeys: [],\n recoveryCodes: [],\n })\n .where(eq(schema.users.username, username))\n .run();\n }\n\n async getAllRoles(): Promise<(IStoreRole & { id: string })[]> {\n if (this.rolesCache) return this.rolesCache;\n const rows = this.db\n .select()\n .from(schema.webRoles)\n .orderBy(desc(schema.webRoles.order))\n .all();\n const roles = rows.map(r => ({\n type: 'webRole' as const,\n id: r.id,\n name: r.name,\n description: r.description,\n order: r.order,\n permissions: r.permissions as PermissionSet,\n }));\n this.rolesCache = roles;\n return roles;\n }\n\n invalidateRolesCache() {\n this.rolesCache = null;\n this.defaultPermissionsCache = null;\n }\n\n async getRole(id: string): Promise<(IStoreRole & { id: string }) | null> {\n const row = this.db\n .select()\n .from(schema.webRoles)\n .where(eq(schema.webRoles.id, id))\n .get();\n if (!row) return null;\n return {\n type: 'webRole',\n id: row.id,\n name: row.name,\n description: row.description,\n order: row.order,\n permissions: row.permissions as PermissionSet,\n };\n }\n\n async createRole(\n name: string,\n description: string,\n permissions: PermissionSet,\n ): Promise<IStoreRole & { id: string }> {\n const id = randomUUID();\n const tx = this.sqlite.transaction(() => {\n this.db\n .update(schema.webRoles)\n .set({ order: sql`${schema.webRoles.order} + 1` })\n .run();\n this.db\n .insert(schema.webRoles)\n .values({\n id,\n name,\n description,\n order: 1,\n permissions: permissions,\n })\n .run();\n });\n tx();\n this.invalidateRolesCache();\n return {\n type: 'webRole',\n id,\n name,\n description,\n order: 1,\n permissions: permissions,\n };\n }\n\n async updateRole(\n id: string,\n updates: Partial<Pick<IStoreRole, 'name' | 'description' | 'order'>> & {\n permissions?: PermissionSet;\n },\n ): Promise<void> {\n const $set: Record<string, any> = {};\n if (updates.name !== undefined) $set.name = updates.name;\n if (updates.description !== undefined)\n $set.description = updates.description;\n if (updates.order !== undefined) $set.order = updates.order;\n if (updates.permissions !== undefined)\n $set.permissions = updates.permissions;\n if (Object.keys($set).length > 0) {\n this.db\n .update(schema.webRoles)\n .set($set)\n .where(eq(schema.webRoles.id, id))\n .run();\n }\n this.invalidateRolesCache();\n }\n\n async deleteRole(id: string): Promise<string> {\n const role = await this.getRole(id);\n if (!role) return 'role not found';\n\n const tx = this.sqlite.transaction(() => {\n this.db.delete(schema.webRoles).where(eq(schema.webRoles.id, id)).run();\n const usersWithRole = this.db\n .select({ id: schema.users.id, roles: schema.users.roles })\n .from(schema.users)\n .all();\n for (const u of usersWithRole) {\n const roles = ((u.roles ?? []) as string[]).filter(r => r !== id);\n if (roles.length !== ((u.roles ?? []) as string[]).length) {\n this.db\n .update(schema.users)\n .set({ roles })\n .where(eq(schema.users.id, u.id))\n .run();\n }\n }\n });\n tx();\n this.invalidateRolesCache();\n return '';\n }\n\n async reorderRoles(entries: { id: string; order: number }[]): Promise<void> {\n const tx = this.sqlite.transaction(() => {\n for (const { id, order } of entries) {\n this.db\n .update(schema.webRoles)\n .set({ order })\n .where(eq(schema.webRoles.id, id))\n .run();\n }\n });\n tx();\n this.invalidateRolesCache();\n }\n\n async getUserRolePermissions(user: IStoreUser): Promise<PermissionSet[]> {\n const defaults = await this.getDefaultPermissions();\n const defaultPerms: PermissionSet = {\n root: defaults.root,\n domains: defaults.domains,\n scopes: defaults.scopes,\n };\n const allRoles = await this.getAllRoles();\n const userRoles = allRoles.filter(r => user.roles.includes(r.id));\n return [defaultPerms, ...userRoles.map(r => r.permissions)];\n }\n\n async getUserAssignedRoles(\n user: IStoreUser,\n ): Promise<(IStoreRole & { id: string })[]> {\n const allRoles = await this.getAllRoles();\n return allRoles.filter(r => user.roles.includes(r.id));\n }\n\n // add a chat message to the chat log store\n async addChatLog(\n action: 'msg' | 'server' | 'leave' | 'join',\n user: IPlayer,\n message?: string,\n ) {\n this.calendar.addDate(Date.now());\n const row = this.db\n .insert(schema.chatLogs)\n .values({\n created: Date.now(),\n instanceId: await this.getInstanceId(),\n action,\n user,\n ...(message ? { message } : {}),\n })\n .returning()\n .get();\n return {\n type: 'chat' as const,\n _id: String(row.id),\n id: row.id,\n created: row.created,\n instanceId: row.instanceId,\n action: row.action,\n user: row.user,\n message: row.message,\n };\n }\n\n // get recent chat activity\n async getChats({\n count: limit = 50,\n sameServer,\n before,\n after,\n }: {\n count?: number;\n sameServer?: boolean;\n before?: number;\n after?: number;\n } = {}) {\n const conditions = [];\n if (sameServer) {\n conditions.push(\n eq(schema.chatLogs.instanceId, await this.getInstanceId()),\n );\n }\n if (before) {\n conditions.push(lt(schema.chatLogs.created, before));\n } else if (after) {\n conditions.push(gt(schema.chatLogs.created, after));\n } else {\n conditions.push(lt(schema.chatLogs.created, Date.now()));\n }\n\n const orderDir = !before && after ? asc : desc;\n const rows = this.db\n .select()\n .from(schema.chatLogs)\n .where(conditions.length > 1 ? and(...conditions) : conditions[0])\n .orderBy(orderDir(schema.chatLogs.created))\n .limit(limit)\n .all();\n\n return rows.map(r => ({\n type: 'chat' as const,\n _id: String(r.id),\n created: r.created,\n instanceId: r.instanceId,\n action: r.action,\n user: r.user,\n message: r.message,\n }));\n }\n\n // get paginated players\n async getPlayers({\n count: limit = 50,\n search = '',\n page = 0,\n sort = 'name',\n direction = 1,\n limitId = undefined,\n }: {\n count?: number;\n search?: string;\n page?: number;\n sort?: string;\n direction?: number;\n limitId?: string[];\n } = {}) {\n const pattern = search.length > 0 ? explode(search) : null;\n const patternSource = pattern?.source;\n\n // an empty filter (e.g. \"banned\" with no active bans) matches nothing\n if (limitId && limitId.length === 0)\n return { pages: 0, total: 0, players: [] };\n\n const conditions: SQL[] = [];\n\n if (limitId) {\n conditions.push(inArray(schema.playerHistory.id, limitId));\n }\n\n if (patternSource) {\n // match name history values via json_each so the pattern can't hit\n // JSON keys or date timestamps\n conditions.push(sql`(\n ${schema.playerHistory.id} = ${search}\n OR ${schema.playerHistory.name} REGEXP ${patternSource}\n OR ${schema.playerHistory.displayName} REGEXP ${patternSource}\n OR EXISTS (\n SELECT 1 FROM json_each(${schema.playerHistory.nameHistory})\n WHERE json_each.type = 'object'\n AND (json_extract(json_each.value, '$.name') REGEXP ${patternSource}\n OR json_extract(json_each.value, '$.displayName') REGEXP ${patternSource})\n )\n )`);\n }\n\n const baseWhere = conditions.length > 0 ? and(...conditions) : undefined;\n\n const sortCol =\n sort === 'lastSeen'\n ? schema.playerHistory.lastSeen\n : sort === 'created'\n ? schema.playerHistory.created\n : sort === 'heartbeats'\n ? schema.playerHistory.heartbeats\n : sort === 'sessions'\n ? schema.playerHistory.sessions\n : sql`${schema.playerHistory.name} COLLATE NOCASE`;\n const orderDir = direction === 1 ? asc : desc;\n\n const totalResult = this.db\n .select({ count: count() })\n .from(schema.playerHistory)\n .where(baseWhere)\n .get();\n const players = this.db\n .select()\n .from(schema.playerHistory)\n .where(baseWhere)\n .orderBy(orderDir(sortCol))\n .limit(limit)\n .offset(limit * page)\n .all();\n\n const total = totalResult?.count ?? 0;\n const result = players.map(p => this._toUserHistory(p));\n\n // exact match detection\n if (search.length > 0 && page === 0) {\n const exactRows = this.db\n .select()\n .from(schema.playerHistory)\n .where(\n or(\n eq(schema.playerHistory.id, search),\n eq(schema.playerHistory.name, search),\n eq(schema.playerHistory.displayName, search),\n ),\n )\n .all();\n if (exactRows.length > 0) {\n for (const res of exactRows) {\n const idx = result.findIndex(p => p.id === res.id);\n if (idx >= 0) result.splice(idx, 1);\n }\n result.splice(0, 0, ...exactRows.map(r => this._toUserHistory(r)));\n }\n }\n\n return { pages: Math.ceil(total / limit), total, players: result };\n }\n\n // get paginated users\n async getUsers({\n count: limit = 50,\n search = '',\n page = 0,\n sort = 'name',\n direction = 1,\n } = {}) {\n const pattern = search.length > 0 ? explode(search) : null;\n\n let baseWhere = sql`1=1`;\n if (pattern) {\n baseWhere = sql`(\n ${schema.users.playerId} = ${search}\n OR ${schema.users.username} REGEXP ${pattern.source}\n )`;\n }\n\n const sortCol =\n sort === 'lastOnline'\n ? schema.users.lastOnline\n : sort === 'created'\n ? schema.users.created\n : sql`${schema.users.username} COLLATE NOCASE`;\n const orderDir = direction === 1 ? asc : desc;\n\n const totalResult = this.db\n .select({ count: count() })\n .from(schema.users)\n .where(baseWhere)\n .get();\n const users = this.db\n .select()\n .from(schema.users)\n .where(baseWhere)\n .orderBy(orderDir(sortCol))\n .limit(limit)\n .offset(limit * page)\n .all();\n\n return {\n pages: Math.ceil((totalResult?.count ?? 0) / limit),\n total: totalResult?.count ?? 0,\n users: users.map(u => this._toStoreUser(u)),\n };\n }\n\n // get an individual player, ban and kick history, and notes\n async getPlayer(id: string) {\n const player = this.db\n .select()\n .from(schema.playerHistory)\n .where(eq(schema.playerHistory.id, id))\n .get();\n if (!player) return null;\n\n const banHist = this.db\n .select()\n .from(schema.banHistory)\n .where(eq(schema.banHistory.banned, id))\n .orderBy(desc(schema.banHistory.created))\n .limit(25)\n .all();\n\n const kickHist = this.db\n .select()\n .from(schema.kickHistory)\n .where(eq(schema.kickHistory.kicked, id))\n .orderBy(desc(schema.kickHistory.created))\n .limit(25)\n .all();\n\n const noteRows = this.db\n .select()\n .from(schema.playerNotes)\n .where(eq(schema.playerNotes.playerId, id))\n .limit(25)\n .all();\n\n return {\n ...this._toUserHistory(player),\n banHistory: banHist.map(b => ({\n type: 'banHistory' as const,\n _id: String(b.id),\n banned: b.banned,\n bannerId: b.bannerId,\n created: b.created,\n expires: b.expires,\n reason: b.reason,\n })),\n kickHistory: kickHist.map(k => ({\n type: 'kickHistory' as const,\n _id: String(k.id),\n kicked: k.kicked,\n kickerId: k.kickerId,\n created: k.created,\n reason: k.reason,\n })),\n notes: noteRows.map(n => ({\n type: 'note' as const,\n _id: String(n.id),\n id: n.playerId,\n note: n.note,\n })),\n };\n }\n\n // add a user to the visit history, returns true if this is a first visit\n async addVisit(user: Required<IPlayer>) {\n const now = Date.now();\n // resolve the instance id before opening the transaction — the tx callback\n // must stay synchronous, and this is the only await in the method\n const instanceId = await this.getInstanceId();\n\n // read-modify-write the row atomically so two near-simultaneous joins for\n // the same id can't both insert (PK conflict) or clobber each other's\n // nameHistory/counter updates from a shared stale snapshot\n const visit = this.sqlite.transaction(() => {\n const existing = this.db\n .select()\n .from(schema.playerHistory)\n .where(eq(schema.playerHistory.id, user.id))\n .get();\n\n if (!existing) {\n this.db\n .insert(schema.playerHistory)\n .values({\n id: user.id,\n name: user.name,\n displayName: user.displayName,\n nameHistory: [\n { name: user.name, displayName: user.displayName, date: now },\n ],\n ips: [],\n created: now,\n lastSeen: now,\n lastInstanceId: instanceId,\n heartbeats: 0,\n sessions: 1,\n instances: 1,\n })\n .run();\n return true;\n }\n\n const nameHistory = (existing.nameHistory ?? []) as {\n name: string;\n displayName: string;\n date: number;\n }[];\n const hasName = nameHistory.some(\n h => h.name === user.name && h.displayName === user.displayName,\n );\n if (!hasName) {\n nameHistory.push({\n name: user.name,\n displayName: user.displayName,\n date: now,\n });\n }\n\n this.db\n .update(schema.playerHistory)\n .set({\n lastSeen: now,\n name: user.name,\n displayName: user.displayName,\n lastInstanceId: instanceId,\n nameHistory,\n sessions: sql`${schema.playerHistory.sessions} + ${existing.lastSeen < now - 60 * 60 * 1000 ? 1 : 0}`,\n instances: sql`${schema.playerHistory.instances} + ${existing.lastInstanceId !== instanceId ? 1 : 0}`,\n })\n .where(eq(schema.playerHistory.id, existing.id))\n .run();\n return false;\n });\n\n return visit();\n }\n\n // use data from minutely heartbeats to fuel metrics\n async addHeartbeat(data: {\n bricks: number;\n players: string[];\n ips: Record<string, string>;\n }) {\n const now = Date.now();\n const tx = this.sqlite.transaction(() => {\n this.db\n .insert(schema.heartbeats)\n .values({\n created: now,\n bricks: data.bricks,\n players: data.players,\n })\n .run();\n\n if (data.players.length > 0) {\n // one bulk UPDATE for the shared lastSeen/heartbeats bump\n this.db\n .update(schema.playerHistory)\n .set({\n lastSeen: now,\n heartbeats: sql`${schema.playerHistory.heartbeats} + 1`,\n })\n .where(inArray(schema.playerHistory.id, data.players))\n .run();\n\n const players = this.db\n .select()\n .from(schema.playerHistory)\n .where(inArray(schema.playerHistory.id, data.players))\n .all();\n\n for (const p of players) {\n const ip = data.ips[p.id];\n if (ip && !((p.ips ?? []) as string[]).includes(ip)) {\n const ips = [...((p.ips ?? []) as string[]), ip];\n this.db\n .update(schema.playerHistory)\n .set({ ips })\n .where(eq(schema.playerHistory.id, p.id))\n .run();\n }\n }\n }\n });\n tx();\n }\n\n upsertBanHistory(entry: Omit<IStoreBanHistory, 'type'>) {\n this.db\n .insert(schema.banHistory)\n .values({\n banned: entry.banned,\n bannerId: entry.bannerId ?? '',\n // Number.isFinite (not typeof) so NaN from parseBrickadiaTime becomes\n // 0, binding NaN to a NOT NULL integer column stores NULL and throws\n created: Number.isFinite(entry.created) ? entry.created : 0,\n expires: Number.isFinite(entry.expires) ? entry.expires : 0,\n reason: entry.reason ?? '',\n })\n .onConflictDoNothing()\n .run();\n }\n\n upsertKickHistory(entry: Omit<IStoreKickHistory, 'type'>) {\n this.db\n .insert(schema.kickHistory)\n .values({\n kicked: entry.kicked,\n kickerId: entry.kickerId ?? '',\n created: entry.created ?? 0,\n reason: entry.reason ?? '',\n })\n .onConflictDoNothing()\n .run();\n }\n\n async getAutoRestartConfig(): Promise<IStoreAutoRestartConfig> {\n const row = this.db\n .select()\n .from(schema.serverConfig)\n .where(eq(schema.serverConfig.key, 'autoRestartConfig'))\n .get();\n if (!row) {\n const config: IStoreAutoRestartConfig = {\n type: 'autoRestartConfig',\n maxUptime: 48,\n maxUptimeEnabled: false,\n emptyUptime: 24,\n emptyUptimeEnabled: false,\n dailyHour: 2,\n dailyHourEnabled: false,\n autoUpdateEnabled: true,\n autoUpdateIntervalMins: 60,\n announcementEnabled: true,\n playersEnabled: true,\n saveWorld: true,\n crashRestartEnabled: true,\n };\n this.db\n .insert(schema.serverConfig)\n .values({ key: 'autoRestartConfig', value: config })\n .run();\n return config;\n }\n const config = row.value as IStoreAutoRestartConfig;\n config.type = 'autoRestartConfig';\n config.autoUpdateEnabled ??= true;\n config.autoUpdateIntervalMins ??= 60;\n config.saveWorld ??= true;\n config.crashRestartEnabled ??= true;\n return config;\n }\n\n async setAutoRestartConfig(config: IStoreAutoRestartConfig): Promise<void> {\n config.maxUptime = Math.round(Math.max(1, Math.min(config.maxUptime, 168)));\n config.emptyUptime = Math.round(\n Math.max(1, Math.min(config.emptyUptime, 168)),\n );\n config.dailyHour = Math.round(Math.max(0, Math.min(config.dailyHour, 23)));\n config.autoUpdateIntervalMins = Math.round(\n Math.max(10, Math.min(config.autoUpdateIntervalMins ?? 60, Infinity)),\n );\n config.autoUpdateEnabled ??= true;\n this.db\n .insert(schema.serverConfig)\n .values({ key: 'autoRestartConfig', value: config })\n .onConflictDoUpdate({\n target: schema.serverConfig.key,\n set: { value: config },\n })\n .run();\n }\n\n // update player online-time punchcard\n async updatePlayerPunchcard(numNewPlayers: number) {\n const now = new Date();\n const time = now.getTime();\n const hour = now.getUTCHours();\n const day = now.getUTCDay();\n const month = now.getUTCMonth();\n const year = now.getUTCFullYear();\n\n const card = this.db\n .select()\n .from(schema.punchcards)\n .where(\n and(\n eq(schema.punchcards.kind, 'playerCount'),\n eq(schema.punchcards.month, month),\n eq(schema.punchcards.year, year),\n ),\n )\n .get();\n\n if (!card) {\n const punchcard = createPunchcard();\n punchcard[day][hour] = numNewPlayers;\n this.db\n .insert(schema.punchcards)\n .values({\n kind: 'playerCount',\n created: time,\n updated: time,\n month,\n year,\n punchcard,\n })\n .run();\n } else {\n const punchcard = card.punchcard as number[][];\n punchcard[day][hour] += numNewPlayers;\n this.db\n .update(schema.punchcards)\n .set({ punchcard, updated: time })\n .where(eq(schema.punchcards.id, card.id))\n .run();\n }\n }\n\n private _toStoreUser(\n row: typeof schema.users.$inferSelect,\n ): IStoreUser & { id: string; _id: string } {\n const user: IStoreUser & { id: string; _id: string } = {\n type: 'user',\n id: row.id,\n _id: row.id,\n created: row.created,\n lastOnline: row.lastOnline,\n username: row.username,\n hash: row.hash,\n isOwner: row.isOwner,\n roles: (row.roles ?? []) as string[],\n playerId: row.playerId,\n isBanned: row.isBanned,\n permissions:\n (row.permissions as PermissionSet | null) ?? EMPTY_PERMISSIONS,\n totpSecret: row.totpSecret ?? undefined,\n totpEnabled: row.totpEnabled,\n passkeys: (row.passkeys ?? []) as IWebAuthnCredential[],\n recoveryCodes: (row.recoveryCodes ?? []) as string[],\n };\n return user;\n }\n\n private _toUserHistory(\n row: typeof schema.playerHistory.$inferSelect,\n ): IUserHistory & { _id: string } {\n return {\n type: 'userHistory',\n _id: row.id,\n id: row.id,\n name: row.name,\n displayName: row.displayName,\n nameHistory: (row.nameHistory ?? []) as {\n displayName: string;\n name: string;\n date: number;\n }[],\n ips: (row.ips ?? []) as string[],\n created: row.created,\n lastSeen: row.lastSeen,\n lastInstanceId: row.lastInstanceId,\n heartbeats: row.heartbeats,\n sessions: row.sessions,\n instances: row.instances,\n };\n }\n}\n"],"names":["Calendar","randomUUID","schema.serverInstances","schema.chatLogs","parseBrickadiaTime","count","schema.users","EMPTY_PERMISSIONS","eq","serverEvents","permissions","schema.serverConfig","RootLevel","or","and","sql","schema.webRoles","desc","lt","gt","asc","pattern","explode","inArray","schema.playerHistory","schema.banHistory","schema.kickHistory","schema.playerNotes","schema.heartbeats","config","time","schema.punchcards"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omegga",
3
- "version": "1.9.0",
3
+ "version": "1.9.2",
4
4
  "description": "Brickadia server installer, updater, manager, wrapper, configurator, and automator",
5
5
  "engines": {
6
6
  "node": ">=23.0.0"
@@ -21,7 +21,7 @@
21
21
  "better-sqlite3": "^12.2.0",
22
22
  "better-sqlite3-session-store": "^0.1.0",
23
23
  "body-parser": "^1.19.0",
24
- "brs-js": "^3.2.1",
24
+ "brs-js": "^3.2.4",
25
25
  "child_process": "^1.0.2",
26
26
  "chokidar": "^3.4.2",
27
27
  "colors": "^1.4.0",
package/public/app.html CHANGED
@@ -812,7 +812,6 @@ a,
812
812
  box-sizing: border-box;
813
813
  opacity: 0;
814
814
  border-radius: 12px;
815
- overflow: hidden;
816
815
  }
817
816
  .modal.visible {
818
817
  opacity: 1;
@@ -821,9 +820,25 @@ a,
821
820
  .modal .modal-content {
822
821
  position: relative;
823
822
  }
824
- .modal .popout-inputs .dropdown,
825
- .modal .popout-inputs .input {
826
- margin: 8px;
823
+ .modal .header {
824
+ border-top-left-radius: 12px;
825
+ border-top-right-radius: 12px;
826
+ }
827
+ .modal .footer {
828
+ border-bottom-left-radius: 12px;
829
+ border-bottom-right-radius: 12px;
830
+ }
831
+ .modal .popout-inputs {
832
+ display: flex;
833
+ flex-direction: column;
834
+ gap: 8px;
835
+ padding: 8px;
836
+ }
837
+ .modal .popout-inputs .ban-duration {
838
+ gap: 8px;
839
+ }
840
+ .modal .popout-inputs .ban-duration .input {
841
+ flex: 1;
827
842
  }
828
843
  .nav-header {
829
844
  color: white;
@@ -1681,12 +1696,6 @@ html.keyboard-open .bottom-nav {
1681
1696
  overflow: hidden;
1682
1697
  }
1683
1698
  }
1684
- .player-inspector-container .popout-inputs.ban > .input {
1685
- margin-bottom: 0 !important;
1686
- }
1687
- .player-inspector-container .popout-inputs .ban-duration .input {
1688
- margin-right: 0 !important;
1689
- }
1690
1699
  .player-inspector-container .popout-content.compact {
1691
1700
  padding: 8px !important;
1692
1701
  padding-top: 0 !important;
@@ -2802,7 +2811,7 @@ html.keyboard-open .bottom-nav {
2802
2811
  }
2803
2812
  </style>
2804
2813
  <script type="importmap">{"imports":{"react":"./assets/shared/react-iXLZ_Bod.js","react-dom":"./assets/shared/react-dom-8Rcmh_FG.js","react/jsx-runtime":"./assets/shared/react_jsx-runtime-BwhiLcBy.js","@nanostores/react":"./assets/shared/@nanostores_react-DJPF1Eqx.js","nanostores":"./assets/shared/nanostores-BFqqIY_c.js","@tabler/icons-react":"./assets/shared/@tabler_icons-react-B4130bhe.js","@hooks":"./assets/shared/@hooks-CiJaUL6_.js","wouter":"./assets/shared/wouter-CUC1caxu.js","@utils":"./assets/shared/@utils-BA2UvC9c.js","@components":"./assets/shared/@components-VkA0DIz6.js"}}</script>
2805
- <script type="module" crossorigin src="/assets/app-CHjgCOH4.js"></script>
2814
+ <script type="module" crossorigin src="/assets/app-DvPQUQnE.js"></script>
2806
2815
  <link rel="modulepreload" crossorigin href="/assets/index-D0OPOe3j.js">
2807
2816
  <link rel="modulepreload" crossorigin href="/assets/index-CtCtK5BX.js">
2808
2817
  <link rel="modulepreload" crossorigin href="/assets/client-BT1QYH0O.js">