omegga 1.6.0 → 1.7.0

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.
Files changed (105) hide show
  1. package/.claude/settings.local.json +9 -2
  2. package/CHANGELOG.md +22 -1
  3. package/dist/cli/terminal.js +5 -1
  4. package/dist/cli/terminal.js.map +1 -1
  5. package/dist/db/connection.js +31 -0
  6. package/dist/db/connection.js.map +1 -0
  7. package/dist/db/migrate.js +20 -0
  8. package/dist/db/migrate.js.map +1 -0
  9. package/dist/db/migrations/0000_long_killer_shrike.sql +111 -0
  10. package/dist/db/migrations/meta/0000_snapshot.json +737 -0
  11. package/dist/db/migrations/meta/_journal.json +13 -0
  12. package/dist/db/nedbImport.js +344 -0
  13. package/dist/db/nedbImport.js.map +1 -0
  14. package/dist/db/plugin-migrations/0000_faulty_tyger_tiger.sql +11 -0
  15. package/dist/db/plugin-migrations/meta/0000_snapshot.json +81 -0
  16. package/dist/db/plugin-migrations/meta/_journal.json +13 -0
  17. package/dist/db/pluginSchema.js +19 -0
  18. package/dist/db/pluginSchema.js.map +1 -0
  19. package/dist/db/schema.js +174 -0
  20. package/dist/db/schema.js.map +1 -0
  21. package/dist/omegga/matchers/version.js +30 -13
  22. package/dist/omegga/matchers/version.js.map +1 -1
  23. package/dist/omegga/plugin.js +47 -49
  24. package/dist/omegga/plugin.js.map +1 -1
  25. package/dist/omegga/server.js.map +1 -1
  26. package/dist/softconfig.js +9 -21
  27. package/dist/softconfig.js.map +1 -1
  28. package/dist/updater/steam.js +1 -1
  29. package/dist/updater/steam.js.map +1 -1
  30. package/dist/util/brdb.js +84 -2
  31. package/dist/util/brdb.js.map +1 -1
  32. package/dist/webserver/backend/database.js +533 -533
  33. package/dist/webserver/backend/database.js.map +1 -1
  34. package/dist/webserver/backend/index.js +19 -8
  35. package/dist/webserver/backend/index.js.map +1 -1
  36. package/dist/webserver/backend/metrics.js +7 -5
  37. package/dist/webserver/backend/metrics.js.map +1 -1
  38. package/dist/webserver/backend/permissions.js +69 -20
  39. package/dist/webserver/backend/permissions.js.map +1 -1
  40. package/dist/webserver/backend/roleHierarchy.js +106 -0
  41. package/dist/webserver/backend/roleHierarchy.js.map +1 -0
  42. package/dist/webserver/backend/router/player.js +13 -16
  43. package/dist/webserver/backend/router/player.js.map +1 -1
  44. package/dist/webserver/backend/router/role.js +195 -7
  45. package/dist/webserver/backend/router/role.js.map +1 -1
  46. package/dist/webserver/backend/router/server.js +32 -2
  47. package/dist/webserver/backend/router/server.js.map +1 -1
  48. package/dist/webserver/backend/router/session.js +3 -5
  49. package/dist/webserver/backend/router/session.js.map +1 -1
  50. package/dist/webserver/backend/router/user.js +89 -42
  51. package/dist/webserver/backend/router/user.js.map +1 -1
  52. package/dist/webserver/backend/scopes.js +73 -44
  53. package/dist/webserver/backend/scopes.js.map +1 -1
  54. package/dist/webserver/backend/trpc.js +3 -6
  55. package/dist/webserver/backend/trpc.js.map +1 -1
  56. package/doc/permissions.md +83 -12
  57. package/drizzle.config.ts +7 -0
  58. package/drizzle.plugin.config.ts +7 -0
  59. package/package.json +7 -2
  60. package/public/app.html +415 -32
  61. package/public/assets/IconWorld-CmdphDgp.js +2 -0
  62. package/public/assets/IconWorld-CmdphDgp.js.map +1 -0
  63. package/public/assets/IconWorldPlus-DKSZjbqh.js +2 -0
  64. package/public/assets/IconWorldPlus-DKSZjbqh.js.map +1 -0
  65. package/public/assets/app-BiBsxujC.js +7 -0
  66. package/public/assets/app-BiBsxujC.js.map +1 -0
  67. package/public/assets/{auth-CEYiizOt.js → auth-nn5rDnJ9.js} +2 -2
  68. package/public/assets/{auth-CEYiizOt.js.map → auth-nn5rDnJ9.js.map} +1 -1
  69. package/public/assets/favicon-DYc6c67x.ico +0 -0
  70. package/public/assets/{index-DEL-PD_8.js → index-2cgXLhrt.js} +2 -2
  71. package/public/assets/{index-DEL-PD_8.js.map → index-2cgXLhrt.js.map} +1 -1
  72. package/public/assets/index-5EkjXNMi.js +12 -0
  73. package/public/assets/index-5EkjXNMi.js.map +1 -0
  74. package/public/assets/index-B2DCW1zi.js +2 -0
  75. package/public/assets/index-B2DCW1zi.js.map +1 -0
  76. package/public/assets/logo-Dl-LlTow.webp +0 -0
  77. package/public/assets/shared/@components-VkA0DIz6.js +2 -0
  78. package/public/assets/shared/@components-VkA0DIz6.js.map +1 -0
  79. package/public/assets/shared/@hooks-CiJaUL6_.js +2 -0
  80. package/public/assets/shared/@hooks-CiJaUL6_.js.map +1 -0
  81. package/public/assets/shared/@tabler_icons-react-B4130bhe.js +3 -0
  82. package/public/assets/shared/{@tabler_icons-react-DrVFX3uL.js.map → @tabler_icons-react-B4130bhe.js.map} +1 -1
  83. package/public/assets/shared/{wouter-CNISJ5i6.js → wouter-CUC1caxu.js} +2 -2
  84. package/public/assets/shared/wouter-CUC1caxu.js.map +1 -0
  85. package/public/assets/user-XF1UyJMA.js +2 -0
  86. package/public/assets/user-XF1UyJMA.js.map +1 -0
  87. package/public/auth.html +163 -16
  88. package/public/assets/IconWorld-XmQNg6JI.js +0 -2
  89. package/public/assets/IconWorld-XmQNg6JI.js.map +0 -1
  90. package/public/assets/IconWorldPlus-CxGGv2eD.js +0 -2
  91. package/public/assets/IconWorldPlus-CxGGv2eD.js.map +0 -1
  92. package/public/assets/app-DMr_-wik.js +0 -7
  93. package/public/assets/app-DMr_-wik.js.map +0 -1
  94. package/public/assets/index-D_STNela.js +0 -2
  95. package/public/assets/index-D_STNela.js.map +0 -1
  96. package/public/assets/index-Dkh3HiBz.js +0 -4
  97. package/public/assets/index-Dkh3HiBz.js.map +0 -1
  98. package/public/assets/shared/@components-CgsSYMe6.js +0 -2
  99. package/public/assets/shared/@components-CgsSYMe6.js.map +0 -1
  100. package/public/assets/shared/@hooks-DT93N7wd.js +0 -2
  101. package/public/assets/shared/@hooks-DT93N7wd.js.map +0 -1
  102. package/public/assets/shared/@tabler_icons-react-DrVFX3uL.js +0 -3
  103. package/public/assets/shared/wouter-CNISJ5i6.js.map +0 -1
  104. package/public/assets/useHasScope-BOdFktko.js +0 -2
  105. package/public/assets/useHasScope-BOdFktko.js.map +0 -1
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": "7",
3
+ "dialect": "sqlite",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "6",
8
+ "when": 1781405588199,
9
+ "tag": "0000_long_killer_shrike",
10
+ "breakpoints": true
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,344 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const logger = require("../logger.js");
4
+ const softconfig = require("../softconfig.js");
5
+ const permissions = require("../webserver/backend/permissions.js");
6
+ const drizzleOrm = require("drizzle-orm");
7
+ const betterSqlite3 = require("drizzle-orm/better-sqlite3");
8
+ const fs = require("fs");
9
+ const Datastore = require("nedb-promises");
10
+ const path = require("path");
11
+ const connection = require("./connection.js");
12
+ const migrate = require("./migrate.js");
13
+ const schema = require("./schema.js");
14
+ const pluginSchema = require("./pluginSchema.js");
15
+ const MARKER_FILE = "nedb-imported.marker";
16
+ const BATCH_SIZE = 500;
17
+ const NEDB_FILES = {
18
+ users: "users.db",
19
+ chat: "chat.db",
20
+ players: "players.db",
21
+ status: "status.db",
22
+ server: "store.db",
23
+ plugins: "plugins.db"
24
+ };
25
+ function isLegacyPermissions(p) {
26
+ if (p.root === "unset") return true;
27
+ for (const v of Object.values(p.domains ?? {}))
28
+ if (v === "unset") return true;
29
+ for (const v of Object.values(p.scopes ?? {}))
30
+ if (typeof v === "string") return true;
31
+ return false;
32
+ }
33
+ function normalizeLegacyPermissions(p) {
34
+ const root = p.root === "all" || p.root === "read" ? p.root : "off";
35
+ const domains = {};
36
+ for (const [k, v] of Object.entries(p.domains ?? {})) {
37
+ if (v !== "unset" && v !== "none") domains[k] = v;
38
+ }
39
+ const scopes = {};
40
+ for (const [k, v] of Object.entries(p.scopes ?? {})) {
41
+ if (v === "enabled" || v === true) scopes[k] = true;
42
+ else if (v === "disabled" || v === false) scopes[k] = false;
43
+ }
44
+ return { root, domains, scopes };
45
+ }
46
+ function migrateUserPermissions(user) {
47
+ const p = user.permissions;
48
+ if (!p || typeof p !== "object") return user;
49
+ const stored = isLegacyPermissions(p) ? normalizeLegacyPermissions(p) : p;
50
+ return { ...user, permissions: permissions.decodePermissions(stored) };
51
+ }
52
+ function migrateDefaultPermissions(doc) {
53
+ if (!doc) return doc;
54
+ const stored = isLegacyPermissions(doc) ? normalizeLegacyPermissions(doc) : doc;
55
+ return { ...doc, ...permissions.decodePermissions(stored) };
56
+ }
57
+ function sanitizeNameHistory(raw) {
58
+ if (!Array.isArray(raw)) return [];
59
+ return raw.filter((h) => h && typeof h === "object").map((h) => ({
60
+ name: String(h.name ?? ""),
61
+ displayName: String(h.displayName ?? ""),
62
+ date: typeof h.date === "number" ? h.date : 0
63
+ }));
64
+ }
65
+ async function loadNedb(filepath) {
66
+ if (!fs.existsSync(filepath)) return [];
67
+ const store = Datastore.create({ filename: filepath, autoload: true });
68
+ return await store.find({});
69
+ }
70
+ function eachBatch(docs, fn) {
71
+ for (let i = 0; i < docs.length; i += BATCH_SIZE) {
72
+ fn(docs.slice(i, i + BATCH_SIZE));
73
+ }
74
+ }
75
+ const IMPORT_FLAG_KEY = "nedbImported";
76
+ const importsInFlight = /* @__PURE__ */ new Map();
77
+ function importNedbIfNeeded(dataPath, mainSqlite, mainDb) {
78
+ const key = path.resolve(dataPath);
79
+ let promise = importsInFlight.get(key);
80
+ if (!promise) {
81
+ promise = importNedb(dataPath, mainSqlite, mainDb).finally(
82
+ () => importsInFlight.delete(key)
83
+ );
84
+ importsInFlight.set(key, promise);
85
+ }
86
+ return promise;
87
+ }
88
+ async function importNedb(dataPath, mainSqlite, mainDb) {
89
+ const markerPath = path.join(dataPath, MARKER_FILE);
90
+ if (fs.existsSync(markerPath)) return;
91
+ const filePath = (f) => path.join(dataPath, f);
92
+ const mainNedbFiles = [
93
+ NEDB_FILES.users,
94
+ NEDB_FILES.chat,
95
+ NEDB_FILES.players,
96
+ NEDB_FILES.status,
97
+ NEDB_FILES.server
98
+ ];
99
+ const hasMainNedb = mainNedbFiles.some((f) => fs.existsSync(filePath(f)));
100
+ const pluginPath = filePath(NEDB_FILES.plugins);
101
+ const hasPluginNedb = fs.existsSync(pluginPath);
102
+ if (!hasMainNedb && !hasPluginNedb) {
103
+ fs.writeFileSync(markerPath, (/* @__PURE__ */ new Date()).toISOString());
104
+ return;
105
+ }
106
+ let ownConnection = false;
107
+ if (!mainSqlite || !mainDb) {
108
+ ownConnection = true;
109
+ mainSqlite = connection.openDb(filePath(softconfig.default.MAIN_DB));
110
+ mainDb = betterSqlite3.drizzle(mainSqlite);
111
+ migrate.runMigrations(mainDb);
112
+ }
113
+ try {
114
+ logger.default.log(">>".green, "Importing NeDB data into SQLite...");
115
+ const counts = {};
116
+ const users = (await loadNedb(filePath(NEDB_FILES.users))).filter((d) => d.type === "user").map(migrateUserPermissions);
117
+ const chatMessages = (await loadNedb(filePath(NEDB_FILES.chat))).filter((d) => d.type === "chat").sort((a, b) => (a.created ?? 0) - (b.created ?? 0));
118
+ const playerDocs = await loadNedb(filePath(NEDB_FILES.players));
119
+ const histories = playerDocs.filter((d) => d.type === "userHistory");
120
+ const bans = playerDocs.filter((d) => d.type === "banHistory");
121
+ const kicks = playerDocs.filter((d) => d.type === "kickHistory");
122
+ const notes = playerDocs.filter((d) => d.type === "note");
123
+ const statusDocs = await loadNedb(filePath(NEDB_FILES.status));
124
+ const hbDocs = statusDocs.filter((d) => d.type === "heartbeat").sort((a, b) => (a.created ?? 0) - (b.created ?? 0));
125
+ const pcDocs = statusDocs.filter((d) => d.type === "punchcard");
126
+ const serverDocs = await loadNedb(filePath(NEDB_FILES.server));
127
+ const instances = serverDocs.filter((d) => d.type === "app:start");
128
+ const roles = serverDocs.filter((d) => d.type === "webRole");
129
+ const rawDefaultPerms = serverDocs.find(
130
+ (d) => d.type === "defaultPermissions"
131
+ );
132
+ const autoRestart = serverDocs.find(
133
+ (d) => d.type === "autoRestartConfig"
134
+ );
135
+ const seenUsernames = /* @__PURE__ */ new Set();
136
+ for (const u of users) {
137
+ const name = u.username ?? "";
138
+ if (seenUsernames.has(name))
139
+ logger.default.error(
140
+ "!>".red,
141
+ `Duplicate username ${JSON.stringify(name)} in legacy data; user ${u._id} will not be imported`
142
+ );
143
+ else seenUsernames.add(name);
144
+ }
145
+ if (hasPluginNedb) {
146
+ const pluginSqlite = connection.openDb(filePath(softconfig.default.PLUGINS_DB));
147
+ try {
148
+ const pluginDb = betterSqlite3.drizzle(pluginSqlite);
149
+ migrate.runPluginMigrations(pluginDb);
150
+ const pluginDocs = await loadNedb(pluginPath);
151
+ const storeDocs = pluginDocs.filter(
152
+ (d) => d.type === "store" && d.value != null
153
+ );
154
+ const skipped = pluginDocs.filter((d) => d.type === "store").length - storeDocs.length;
155
+ if (skipped > 0)
156
+ logger.default.log(
157
+ ":>".yellow,
158
+ `Skipping ${skipped} plugin store entr${skipped === 1 ? "y" : "ies"} with no value`
159
+ );
160
+ const configDocs = pluginDocs.filter((d) => d.type === "config");
161
+ const pluginTx = pluginSqlite.transaction(() => {
162
+ eachBatch(
163
+ storeDocs,
164
+ (batch) => pluginDb.insert(pluginSchema.pluginStore).values(
165
+ batch.map((s) => ({
166
+ plugin: s.plugin ?? "",
167
+ key: s.key ?? "",
168
+ value: s.value
169
+ }))
170
+ ).onConflictDoNothing().run()
171
+ );
172
+ eachBatch(
173
+ configDocs,
174
+ (batch) => pluginDb.insert(pluginSchema.pluginConfig).values(
175
+ batch.map((c) => ({
176
+ plugin: c.plugin ?? "",
177
+ value: c.value ?? {}
178
+ }))
179
+ ).onConflictDoNothing().run()
180
+ );
181
+ });
182
+ pluginTx();
183
+ if (storeDocs.length) counts.pluginStoreItems = storeDocs.length;
184
+ if (configDocs.length) counts.pluginConfigs = configDocs.length;
185
+ } finally {
186
+ pluginSqlite.close();
187
+ }
188
+ }
189
+ const alreadyImported = mainDb.select().from(schema.serverConfig).where(drizzleOrm.eq(schema.serverConfig.key, IMPORT_FLAG_KEY)).get();
190
+ if (!alreadyImported && hasMainNedb) {
191
+ const tx = mainSqlite.transaction(() => {
192
+ eachBatch(
193
+ users,
194
+ (batch) => mainDb.insert(schema.users).values(
195
+ batch.map((u) => ({
196
+ id: u._id,
197
+ created: u.created ?? Date.now(),
198
+ lastOnline: u.lastOnline ?? 0,
199
+ username: u.username ?? "",
200
+ hash: u.hash ?? "",
201
+ isOwner: !!u.isOwner,
202
+ roles: u.roles ?? [],
203
+ playerId: u.playerId ?? "",
204
+ isBanned: !!u.isBanned,
205
+ permissions: u.permissions ?? null,
206
+ totpSecret: u.totpSecret ?? null,
207
+ totpEnabled: !!u.totpEnabled,
208
+ passkeys: u.passkeys ?? [],
209
+ recoveryCodes: u.recoveryCodes ?? []
210
+ }))
211
+ ).onConflictDoNothing().run()
212
+ );
213
+ if (users.length) counts.users = users.length;
214
+ eachBatch(
215
+ chatMessages,
216
+ (batch) => mainDb.insert(schema.chatLogs).values(
217
+ batch.map((c) => ({
218
+ created: c.created ?? 0,
219
+ instanceId: c.instanceId ?? "",
220
+ action: c.action ?? "msg",
221
+ user: c.user ?? {},
222
+ message: c.message ?? null
223
+ }))
224
+ ).run()
225
+ );
226
+ if (chatMessages.length) counts.chatMessages = chatMessages.length;
227
+ eachBatch(
228
+ histories,
229
+ (batch) => mainDb.insert(schema.playerHistory).values(
230
+ batch.map((p) => ({
231
+ id: p.id,
232
+ name: p.name ?? "",
233
+ displayName: p.displayName ?? "",
234
+ nameHistory: sanitizeNameHistory(p.nameHistory),
235
+ ips: Array.isArray(p.ips) ? p.ips : [],
236
+ created: p.created ?? 0,
237
+ lastSeen: p.lastSeen ?? 0,
238
+ lastInstanceId: p.lastInstanceId ?? "",
239
+ heartbeats: p.heartbeats ?? 0,
240
+ sessions: p.sessions ?? 0,
241
+ instances: p.instances ?? 0
242
+ }))
243
+ ).onConflictDoNothing().run()
244
+ );
245
+ if (histories.length) counts.players = histories.length;
246
+ for (const b of bans) {
247
+ mainDb.insert(schema.banHistory).values({
248
+ banned: b.banned ?? "",
249
+ bannerId: b.bannerId ?? "",
250
+ created: typeof b.created === "number" ? b.created : 0,
251
+ expires: typeof b.expires === "number" ? b.expires : 0,
252
+ reason: b.reason ?? ""
253
+ }).onConflictDoNothing().run();
254
+ }
255
+ if (bans.length) counts.bans = bans.length;
256
+ for (const k of kicks) {
257
+ mainDb.insert(schema.kickHistory).values({
258
+ kicked: k.kicked ?? "",
259
+ kickerId: k.kickerId ?? "",
260
+ created: k.created ?? 0,
261
+ reason: k.reason ?? ""
262
+ }).onConflictDoNothing().run();
263
+ }
264
+ if (kicks.length) counts.kicks = kicks.length;
265
+ eachBatch(
266
+ notes,
267
+ (batch) => mainDb.insert(schema.playerNotes).values(
268
+ batch.map((n) => ({
269
+ playerId: n.id ?? "",
270
+ note: n.note ?? ""
271
+ }))
272
+ ).run()
273
+ );
274
+ if (notes.length) counts.notes = notes.length;
275
+ eachBatch(
276
+ hbDocs,
277
+ (batch) => mainDb.insert(schema.heartbeats).values(
278
+ batch.map((h) => ({
279
+ created: h.created ?? 0,
280
+ bricks: h.bricks ?? 0,
281
+ players: Array.isArray(h.players) ? h.players : []
282
+ }))
283
+ ).run()
284
+ );
285
+ if (hbDocs.length) counts.heartbeats = hbDocs.length;
286
+ for (const p of pcDocs) {
287
+ mainDb.insert(schema.punchcards).values({
288
+ kind: p.kind ?? "playerCount",
289
+ created: p.created ?? 0,
290
+ updated: p.updated ?? 0,
291
+ month: p.month ?? 0,
292
+ year: p.year ?? 0,
293
+ punchcard: p.punchcard ?? []
294
+ }).onConflictDoNothing().run();
295
+ }
296
+ if (pcDocs.length) counts.punchcards = pcDocs.length;
297
+ eachBatch(
298
+ instances,
299
+ (batch) => mainDb.insert(schema.serverInstances).values(batch.map((i) => ({ id: i._id, date: i.date ?? 0 }))).onConflictDoNothing().run()
300
+ );
301
+ if (instances.length) counts.serverInstances = instances.length;
302
+ eachBatch(
303
+ roles,
304
+ (batch) => mainDb.insert(schema.webRoles).values(
305
+ batch.map((r) => ({
306
+ id: r._id,
307
+ name: r.name ?? "",
308
+ description: r.description ?? "",
309
+ order: r.order ?? 0,
310
+ permissions: permissions.decodePermissions(r.permissions)
311
+ }))
312
+ ).onConflictDoNothing().run()
313
+ );
314
+ if (roles.length) counts.roles = roles.length;
315
+ const defaultPerms = migrateDefaultPermissions(rawDefaultPerms);
316
+ if (defaultPerms) {
317
+ mainDb.insert(schema.serverConfig).values({
318
+ key: "defaultPermissions",
319
+ value: {
320
+ root: defaultPerms.root,
321
+ domains: defaultPerms.domains,
322
+ scopes: defaultPerms.scopes
323
+ }
324
+ }).onConflictDoNothing().run();
325
+ }
326
+ if (autoRestart) {
327
+ const { _id: _, type: __, ...config } = autoRestart;
328
+ mainDb.insert(schema.serverConfig).values({ key: "autoRestartConfig", value: config }).onConflictDoNothing().run();
329
+ }
330
+ mainDb.insert(schema.serverConfig).values({ key: IMPORT_FLAG_KEY, value: (/* @__PURE__ */ new Date()).toISOString() }).onConflictDoNothing().run();
331
+ });
332
+ tx();
333
+ }
334
+ fs.writeFileSync(markerPath, (/* @__PURE__ */ new Date()).toISOString());
335
+ const parts = Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(", ");
336
+ logger.default.log(">>".green, `Imported ${parts || "no records"}`);
337
+ logger.default.log(">>".green, "NeDB import complete. Original files preserved.");
338
+ } finally {
339
+ if (ownConnection) mainSqlite.close();
340
+ }
341
+ }
342
+ exports.NEDB_FILES = NEDB_FILES;
343
+ exports.importNedbIfNeeded = importNedbIfNeeded;
344
+ //# sourceMappingURL=nedbImport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nedbImport.js","sources":["../../src/db/nedbImport.ts"],"sourcesContent":["import Logger from '@/logger';\nimport soft from '@/softconfig';\nimport {\n decodePermissions,\n type StoredPermissionSet,\n} from '@webserver/backend/permissions';\nimport type BetterSqlite3 from 'better-sqlite3';\nimport { eq } from 'drizzle-orm';\nimport {\n drizzle,\n type BetterSQLite3Database,\n} from 'drizzle-orm/better-sqlite3';\nimport fs from 'fs';\nimport Datastore from 'nedb-promises';\nimport path from 'path';\nimport { openDb } from './connection';\nimport { runMigrations, runPluginMigrations } from './migrate';\nimport * as mainSchema from './schema';\nimport * as pluginSchema from './pluginSchema';\n\nconst MARKER_FILE = 'nedb-imported.marker';\nconst BATCH_SIZE = 500;\n\n// legacy NeDB database filenames; the one-time import is their only consumer\nexport const NEDB_FILES = {\n users: 'users.db',\n chat: 'chat.db',\n players: 'players.db',\n status: 'status.db',\n server: 'store.db',\n plugins: 'plugins.db',\n} as const;\n\n// Apply the NeDB data migrations that the old doMigrations() would have run.\n// Users v1: convert old NeDB permission format to PermissionSet.\n// Server v1: re-encode defaultPermissions from old format.\n// NeDB stored scope keys colon-encoded ('chat:send'); runtime lookups use\n// dotted keys, so every path below must run decodePermissions.\n\n// the v1 format used 'unset' sentinels and 'enabled'/'disabled' scope strings,\n// none of which are valid v2 values. detect by the presence of any sentinel so\n// an empty-scopes v1 doc (which a scopes-only check would miss) is still\n// normalized, while a valid v2 'none' domain is preserved.\nfunction isLegacyPermissions(p: any): boolean {\n if (p.root === 'unset') return true;\n for (const v of Object.values(p.domains ?? {}))\n if (v === 'unset') return true;\n for (const v of Object.values(p.scopes ?? {}))\n if (typeof v === 'string') return true;\n return false;\n}\n\n// normalize a v1 permission doc to a v2 StoredPermissionSet (still colon-keyed)\nfunction normalizeLegacyPermissions(p: any): StoredPermissionSet {\n const root = p.root === 'all' || p.root === 'read' ? p.root : 'off';\n const domains: Record<string, string> = {};\n for (const [k, v] of Object.entries(p.domains ?? {})) {\n if (v !== 'unset' && v !== 'none') domains[k] = v as string;\n }\n const scopes: Record<string, boolean> = {};\n for (const [k, v] of Object.entries(p.scopes ?? {})) {\n if (v === 'enabled' || v === true) scopes[k] = true;\n else if (v === 'disabled' || v === false) scopes[k] = false;\n }\n return { root, domains, scopes } as StoredPermissionSet;\n}\n\nfunction migrateUserPermissions(user: any): any {\n const p = user.permissions;\n if (!p || typeof p !== 'object') return user;\n const stored = isLegacyPermissions(p) ? normalizeLegacyPermissions(p) : p;\n return { ...user, permissions: decodePermissions(stored) };\n}\n\nfunction migrateDefaultPermissions(doc: any): any {\n if (!doc) return doc;\n const stored = isLegacyPermissions(doc)\n ? normalizeLegacyPermissions(doc)\n : doc;\n return { ...doc, ...decodePermissions(stored) };\n}\n\n// legacy nameHistory may predate the {name,displayName,date} object shape (older\n// docs stored bare strings) or be hand-edited; coerce to well-formed objects so\n// the json_each/REGEXP player search can't hit malformed JSON or null keys\nfunction sanitizeNameHistory(raw: any): {\n name: string;\n displayName: string;\n date: number;\n}[] {\n if (!Array.isArray(raw)) return [];\n return raw\n .filter(h => h && typeof h === 'object')\n .map(h => ({\n name: String(h.name ?? ''),\n displayName: String(h.displayName ?? ''),\n date: typeof h.date === 'number' ? h.date : 0,\n }));\n}\n\nasync function loadNedb<T = any>(filepath: string): Promise<T[]> {\n if (!fs.existsSync(filepath)) return [];\n const store = Datastore.create({ filename: filepath, autoload: true });\n return (await store.find({})) as T[];\n}\n\n// invoke fn on BATCH_SIZE-sized slices of docs (keeps multi-row inserts under\n// SQLite's bound-parameter limit). NOT wrapped in its own transaction — callers\n// run all batches inside one outer transaction for atomicity.\nfunction eachBatch(docs: any[], fn: (docs: any[]) => void) {\n for (let i = 0; i < docs.length; i += BATCH_SIZE) {\n fn(docs.slice(i, i + BATCH_SIZE));\n }\n}\n\n// key in server_config marking the main-DB import as committed. checked\n// in-band with the data so a crash between commit and marker-file write can't\n// cause a re-import (which would duplicate the append-only tables).\nconst IMPORT_FLAG_KEY = 'nedbImported';\n\n// coalesce concurrent import calls for the same data dir (the webserver and the\n// plugin loader both bootstrap it). entries are dropped once settled, so this is\n// purely a concurrency guard — repeat suppression is the marker file / DB flag.\nconst importsInFlight = new Map<string, Promise<void>>();\n\nexport function importNedbIfNeeded(\n dataPath: string,\n mainSqlite?: BetterSqlite3.Database,\n mainDb?: BetterSQLite3Database,\n): Promise<void> {\n const key = path.resolve(dataPath);\n let promise = importsInFlight.get(key);\n if (!promise) {\n promise = importNedb(dataPath, mainSqlite, mainDb).finally(() =>\n importsInFlight.delete(key),\n );\n importsInFlight.set(key, promise);\n }\n return promise;\n}\n\nasync function importNedb(\n dataPath: string,\n mainSqlite?: BetterSqlite3.Database,\n mainDb?: BetterSQLite3Database,\n) {\n const markerPath = path.join(dataPath, MARKER_FILE);\n if (fs.existsSync(markerPath)) return;\n\n const filePath = (f: string) => path.join(dataPath, f);\n const mainNedbFiles = [\n NEDB_FILES.users,\n NEDB_FILES.chat,\n NEDB_FILES.players,\n NEDB_FILES.status,\n NEDB_FILES.server,\n ];\n const hasMainNedb = mainNedbFiles.some(f => fs.existsSync(filePath(f)));\n const pluginPath = filePath(NEDB_FILES.plugins);\n const hasPluginNedb = fs.existsSync(pluginPath);\n if (!hasMainNedb && !hasPluginNedb) {\n fs.writeFileSync(markerPath, new Date().toISOString());\n return;\n }\n\n // open a dedicated main-db connection when the caller doesn't have one\n // (the plugin loader bootstraps the import without a webserver)\n let ownConnection = false;\n if (!mainSqlite || !mainDb) {\n ownConnection = true;\n mainSqlite = openDb(filePath(soft.MAIN_DB));\n mainDb = drizzle(mainSqlite);\n runMigrations(mainDb);\n }\n\n try {\n Logger.log('>>'.green, 'Importing NeDB data into SQLite...');\n const counts: Record<string, number> = {};\n\n // ---- Load phase: read every NeDB file up front (async). All writes happen\n // afterwards inside a single synchronous transaction so a partial failure\n // rolls back cleanly and a retry can't double-import append-only tables. ----\n const users = (await loadNedb(filePath(NEDB_FILES.users)))\n .filter((d: any) => d.type === 'user')\n .map(migrateUserPermissions);\n const chatMessages = (await loadNedb(filePath(NEDB_FILES.chat)))\n .filter((d: any) => d.type === 'chat')\n .sort((a: any, b: any) => (a.created ?? 0) - (b.created ?? 0));\n const playerDocs = await loadNedb(filePath(NEDB_FILES.players));\n const histories = playerDocs.filter((d: any) => d.type === 'userHistory');\n const bans = playerDocs.filter((d: any) => d.type === 'banHistory');\n const kicks = playerDocs.filter((d: any) => d.type === 'kickHistory');\n const notes = playerDocs.filter((d: any) => d.type === 'note');\n const statusDocs = await loadNedb(filePath(NEDB_FILES.status));\n const hbDocs = statusDocs\n .filter((d: any) => d.type === 'heartbeat')\n .sort((a: any, b: any) => (a.created ?? 0) - (b.created ?? 0));\n const pcDocs = statusDocs.filter((d: any) => d.type === 'punchcard');\n const serverDocs = await loadNedb(filePath(NEDB_FILES.server));\n const instances = serverDocs.filter((d: any) => d.type === 'app:start');\n const roles = serverDocs.filter((d: any) => d.type === 'webRole');\n const rawDefaultPerms = serverDocs.find(\n (d: any) => d.type === 'defaultPermissions',\n );\n const autoRestart = serverDocs.find(\n (d: any) => d.type === 'autoRestartConfig',\n );\n\n // warn about user rows that will be dropped by the new UNIQUE(username):\n // NeDB had no such constraint, so legacy data can hold duplicates\n const seenUsernames = new Set<string>();\n for (const u of users) {\n const name = u.username ?? '';\n if (seenUsernames.has(name))\n Logger.error(\n '!>'.red,\n `Duplicate username ${JSON.stringify(name)} in legacy data; user ${u._id} will not be imported`,\n );\n else seenUsernames.add(name);\n }\n\n // ---- Plugins: a separate connection/DB, imported first and idempotently\n // (onConflictDoNothing against real unique constraints), so re-running after\n // a main-DB failure is a safe no-op. ----\n if (hasPluginNedb) {\n const pluginSqlite = openDb(filePath(soft.PLUGINS_DB));\n try {\n const pluginDb = drizzle(pluginSqlite);\n runPluginMigrations(pluginDb);\n const pluginDocs = await loadNedb(pluginPath);\n // NeDB allowed store.set(key, null/undefined); the new value column is\n // NOT NULL, so skip those rows\n const storeDocs = pluginDocs.filter(\n (d: any) => d.type === 'store' && d.value != null,\n );\n const skipped =\n pluginDocs.filter((d: any) => d.type === 'store').length -\n storeDocs.length;\n if (skipped > 0)\n Logger.log(\n ':>'.yellow,\n `Skipping ${skipped} plugin store entr${skipped === 1 ? 'y' : 'ies'} with no value`,\n );\n const configDocs = pluginDocs.filter((d: any) => d.type === 'config');\n\n const pluginTx = pluginSqlite.transaction(() => {\n eachBatch(storeDocs, batch =>\n pluginDb\n .insert(pluginSchema.pluginStore)\n .values(\n batch.map((s: any) => ({\n plugin: s.plugin ?? '',\n key: s.key ?? '',\n value: s.value,\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n eachBatch(configDocs, batch =>\n pluginDb\n .insert(pluginSchema.pluginConfig)\n .values(\n batch.map((c: any) => ({\n plugin: c.plugin ?? '',\n value: c.value ?? {},\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n });\n pluginTx();\n if (storeDocs.length) counts.pluginStoreItems = storeDocs.length;\n if (configDocs.length) counts.pluginConfigs = configDocs.length;\n } finally {\n pluginSqlite.close();\n }\n }\n\n // ---- Main DB: one atomic transaction. The completion flag is written in\n // the same transaction as the data, so either both commit or neither does —\n // a crash before the marker file is written can't trigger a re-import. ----\n const alreadyImported = mainDb\n .select()\n .from(mainSchema.serverConfig)\n .where(eq(mainSchema.serverConfig.key, IMPORT_FLAG_KEY))\n .get();\n\n if (!alreadyImported && hasMainNedb) {\n const tx = mainSqlite.transaction(() => {\n eachBatch(users, batch =>\n mainDb\n .insert(mainSchema.users)\n .values(\n batch.map((u: any) => ({\n id: u._id,\n created: u.created ?? Date.now(),\n lastOnline: u.lastOnline ?? 0,\n username: u.username ?? '',\n hash: u.hash ?? '',\n isOwner: !!u.isOwner,\n roles: u.roles ?? [],\n playerId: u.playerId ?? '',\n isBanned: !!u.isBanned,\n permissions: u.permissions ?? null,\n totpSecret: u.totpSecret ?? null,\n totpEnabled: !!u.totpEnabled,\n passkeys: u.passkeys ?? [],\n recoveryCodes: u.recoveryCodes ?? [],\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n if (users.length) counts.users = users.length;\n\n eachBatch(chatMessages, batch =>\n mainDb\n .insert(mainSchema.chatLogs)\n .values(\n batch.map((c: any) => ({\n created: c.created ?? 0,\n instanceId: c.instanceId ?? '',\n action: c.action ?? 'msg',\n user: c.user ?? {},\n message: c.message ?? null,\n })),\n )\n .run(),\n );\n if (chatMessages.length) counts.chatMessages = chatMessages.length;\n\n eachBatch(histories, batch =>\n mainDb\n .insert(mainSchema.playerHistory)\n .values(\n batch.map((p: any) => ({\n id: p.id,\n name: p.name ?? '',\n displayName: p.displayName ?? '',\n nameHistory: sanitizeNameHistory(p.nameHistory),\n ips: Array.isArray(p.ips) ? p.ips : [],\n created: p.created ?? 0,\n lastSeen: p.lastSeen ?? 0,\n lastInstanceId: p.lastInstanceId ?? '',\n heartbeats: p.heartbeats ?? 0,\n sessions: p.sessions ?? 0,\n instances: p.instances ?? 0,\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n if (histories.length) counts.players = histories.length;\n\n for (const b of bans) {\n mainDb\n .insert(mainSchema.banHistory)\n .values({\n banned: b.banned ?? '',\n bannerId: b.bannerId ?? '',\n created: typeof b.created === 'number' ? b.created : 0,\n expires: typeof b.expires === 'number' ? b.expires : 0,\n reason: b.reason ?? '',\n })\n .onConflictDoNothing()\n .run();\n }\n if (bans.length) counts.bans = bans.length;\n\n for (const k of kicks) {\n mainDb\n .insert(mainSchema.kickHistory)\n .values({\n kicked: k.kicked ?? '',\n kickerId: k.kickerId ?? '',\n created: k.created ?? 0,\n reason: k.reason ?? '',\n })\n .onConflictDoNothing()\n .run();\n }\n if (kicks.length) counts.kicks = kicks.length;\n\n eachBatch(notes, batch =>\n mainDb\n .insert(mainSchema.playerNotes)\n .values(\n batch.map((n: any) => ({\n playerId: n.id ?? '',\n note: n.note ?? '',\n })),\n )\n .run(),\n );\n if (notes.length) counts.notes = notes.length;\n\n eachBatch(hbDocs, batch =>\n mainDb\n .insert(mainSchema.heartbeats)\n .values(\n batch.map((h: any) => ({\n created: h.created ?? 0,\n bricks: h.bricks ?? 0,\n players: Array.isArray(h.players) ? h.players : [],\n })),\n )\n .run(),\n );\n if (hbDocs.length) counts.heartbeats = hbDocs.length;\n\n for (const p of pcDocs) {\n mainDb\n .insert(mainSchema.punchcards)\n .values({\n kind: p.kind ?? 'playerCount',\n created: p.created ?? 0,\n updated: p.updated ?? 0,\n month: p.month ?? 0,\n year: p.year ?? 0,\n punchcard: p.punchcard ?? [],\n })\n .onConflictDoNothing()\n .run();\n }\n if (pcDocs.length) counts.punchcards = pcDocs.length;\n\n eachBatch(instances, batch =>\n mainDb\n .insert(mainSchema.serverInstances)\n .values(batch.map((i: any) => ({ id: i._id, date: i.date ?? 0 })))\n .onConflictDoNothing()\n .run(),\n );\n if (instances.length) counts.serverInstances = instances.length;\n\n eachBatch(roles, batch =>\n mainDb\n .insert(mainSchema.webRoles)\n .values(\n batch.map((r: any) => ({\n id: r._id,\n name: r.name ?? '',\n description: r.description ?? '',\n order: r.order ?? 0,\n permissions: decodePermissions(r.permissions),\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n if (roles.length) counts.roles = roles.length;\n\n const defaultPerms = migrateDefaultPermissions(rawDefaultPerms);\n if (defaultPerms) {\n mainDb\n .insert(mainSchema.serverConfig)\n .values({\n key: 'defaultPermissions',\n value: {\n root: defaultPerms.root,\n domains: defaultPerms.domains,\n scopes: defaultPerms.scopes,\n },\n })\n .onConflictDoNothing()\n .run();\n }\n\n if (autoRestart) {\n const { _id: _, type: __, ...config } = autoRestart;\n mainDb\n .insert(mainSchema.serverConfig)\n .values({ key: 'autoRestartConfig', value: config })\n .onConflictDoNothing()\n .run();\n }\n\n // commit the completion flag atomically with the data above\n mainDb\n .insert(mainSchema.serverConfig)\n .values({ key: IMPORT_FLAG_KEY, value: new Date().toISOString() })\n .onConflictDoNothing()\n .run();\n });\n tx();\n }\n\n // marker file enables the cheap fast-path skip on subsequent boots\n fs.writeFileSync(markerPath, new Date().toISOString());\n\n const parts = Object.entries(counts)\n .map(([k, v]) => `${v} ${k}`)\n .join(', ');\n Logger.log('>>'.green, `Imported ${parts || 'no records'}`);\n Logger.log('>>'.green, 'NeDB import complete. Original files preserved.');\n } finally {\n if (ownConnection) mainSqlite.close();\n }\n}\n"],"names":["decodePermissions","openDb","soft","drizzle","runMigrations","Logger","runPluginMigrations","pluginSchema.pluginStore","pluginSchema.pluginConfig","mainSchema.serverConfig","eq","mainSchema.users","mainSchema.chatLogs","mainSchema.playerHistory","mainSchema.banHistory","mainSchema.kickHistory","mainSchema.playerNotes","mainSchema.heartbeats","mainSchema.punchcards","mainSchema.serverInstances","mainSchema.webRoles"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ CREATE TABLE `plugin_config` (
2
+ `plugin` text PRIMARY KEY NOT NULL,
3
+ `value` text NOT NULL
4
+ );
5
+ --> statement-breakpoint
6
+ CREATE TABLE `plugin_store` (
7
+ `plugin` text NOT NULL,
8
+ `key` text NOT NULL,
9
+ `value` text NOT NULL,
10
+ PRIMARY KEY(`plugin`, `key`)
11
+ );
@@ -0,0 +1,81 @@
1
+ {
2
+ "version": "6",
3
+ "dialect": "sqlite",
4
+ "id": "46dda145-1426-43d2-9a30-fe324e4c0a31",
5
+ "prevId": "00000000-0000-0000-0000-000000000000",
6
+ "tables": {
7
+ "plugin_config": {
8
+ "name": "plugin_config",
9
+ "columns": {
10
+ "plugin": {
11
+ "name": "plugin",
12
+ "type": "text",
13
+ "primaryKey": true,
14
+ "notNull": true,
15
+ "autoincrement": false
16
+ },
17
+ "value": {
18
+ "name": "value",
19
+ "type": "text",
20
+ "primaryKey": false,
21
+ "notNull": true,
22
+ "autoincrement": false
23
+ }
24
+ },
25
+ "indexes": {},
26
+ "foreignKeys": {},
27
+ "compositePrimaryKeys": {},
28
+ "uniqueConstraints": {},
29
+ "checkConstraints": {}
30
+ },
31
+ "plugin_store": {
32
+ "name": "plugin_store",
33
+ "columns": {
34
+ "plugin": {
35
+ "name": "plugin",
36
+ "type": "text",
37
+ "primaryKey": false,
38
+ "notNull": true,
39
+ "autoincrement": false
40
+ },
41
+ "key": {
42
+ "name": "key",
43
+ "type": "text",
44
+ "primaryKey": false,
45
+ "notNull": true,
46
+ "autoincrement": false
47
+ },
48
+ "value": {
49
+ "name": "value",
50
+ "type": "text",
51
+ "primaryKey": false,
52
+ "notNull": true,
53
+ "autoincrement": false
54
+ }
55
+ },
56
+ "indexes": {},
57
+ "foreignKeys": {},
58
+ "compositePrimaryKeys": {
59
+ "plugin_store_plugin_key_pk": {
60
+ "columns": [
61
+ "plugin",
62
+ "key"
63
+ ],
64
+ "name": "plugin_store_plugin_key_pk"
65
+ }
66
+ },
67
+ "uniqueConstraints": {},
68
+ "checkConstraints": {}
69
+ }
70
+ },
71
+ "views": {},
72
+ "enums": {},
73
+ "_meta": {
74
+ "schemas": {},
75
+ "tables": {},
76
+ "columns": {}
77
+ },
78
+ "internal": {
79
+ "indexes": {}
80
+ }
81
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": "7",
3
+ "dialect": "sqlite",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "6",
8
+ "when": 1779848290531,
9
+ "tag": "0000_faulty_tyger_tiger",
10
+ "breakpoints": true
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const sqliteCore = require("drizzle-orm/sqlite-core");
4
+ const pluginStore = sqliteCore.sqliteTable(
5
+ "plugin_store",
6
+ {
7
+ plugin: sqliteCore.text("plugin").notNull(),
8
+ key: sqliteCore.text("key").notNull(),
9
+ value: sqliteCore.text("value", { mode: "json" }).notNull().$type()
10
+ },
11
+ (table) => [sqliteCore.primaryKey({ columns: [table.plugin, table.key] })]
12
+ );
13
+ const pluginConfig = sqliteCore.sqliteTable("plugin_config", {
14
+ plugin: sqliteCore.text("plugin").primaryKey(),
15
+ value: sqliteCore.text("value", { mode: "json" }).notNull().$type()
16
+ });
17
+ exports.pluginConfig = pluginConfig;
18
+ exports.pluginStore = pluginStore;
19
+ //# sourceMappingURL=pluginSchema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pluginSchema.js","sources":["../../src/db/pluginSchema.ts"],"sourcesContent":["import { primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core';\n\nexport const pluginStore = sqliteTable(\n 'plugin_store',\n {\n plugin: text('plugin').notNull(),\n key: text('key').notNull(),\n value: text('value', { mode: 'json' }).notNull().$type<unknown>(),\n },\n table => [primaryKey({ columns: [table.plugin, table.key] })],\n);\n\nexport const pluginConfig = sqliteTable('plugin_config', {\n plugin: text('plugin').primaryKey(),\n value: text('value', { mode: 'json' })\n .notNull()\n .$type<Record<string, unknown>>(),\n});\n"],"names":["sqliteTable","text","primaryKey"],"mappings":";;;AAEO,MAAM,cAAcA,WAAAA;AAAAA,EACzB;AAAA,EACA;AAAA,IACE,QAAQC,WAAAA,KAAK,QAAQ,EAAE,QAAA;AAAA,IACvB,KAAKA,WAAAA,KAAK,KAAK,EAAE,QAAA;AAAA,IACjB,OAAOA,WAAAA,KAAK,SAAS,EAAE,MAAM,QAAQ,EAAE,QAAA,EAAU,MAAA;AAAA,EAAe;AAAA,EAElE,CAAA,UAAS,CAACC,WAAAA,WAAW,EAAE,SAAS,CAAC,MAAM,QAAQ,MAAM,GAAG,GAAG,CAAC;AAC9D;AAEO,MAAM,eAAeF,WAAAA,YAAY,iBAAiB;AAAA,EACvD,QAAQC,WAAAA,KAAK,QAAQ,EAAE,WAAA;AAAA,EACvB,OAAOA,WAAAA,KAAK,SAAS,EAAE,MAAM,QAAQ,EAClC,QAAA,EACA,MAAA;AACL,CAAC;;;"}