@fileverse/api 0.0.1 → 0.0.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/cli/index.js +287 -94
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/index.js +355 -189
- package/dist/commands/index.js.map +1 -1
- package/dist/index.js +428 -255
- package/dist/index.js.map +1 -1
- package/dist/worker.js +367 -196
- package/dist/worker.js.map +1 -1
- package/package.json +3 -1
package/dist/worker.js
CHANGED
|
@@ -43,6 +43,9 @@ function getRuntimeConfig() {
|
|
|
43
43
|
get DB_PATH() {
|
|
44
44
|
return process.env.DB_PATH;
|
|
45
45
|
},
|
|
46
|
+
get DATABASE_URL() {
|
|
47
|
+
return process.env.DATABASE_URL;
|
|
48
|
+
},
|
|
46
49
|
get PORT() {
|
|
47
50
|
return process.env.PORT || STATIC_CONFIG.DEFAULT_PORT;
|
|
48
51
|
},
|
|
@@ -54,16 +57,19 @@ function getRuntimeConfig() {
|
|
|
54
57
|
}
|
|
55
58
|
};
|
|
56
59
|
}
|
|
57
|
-
function
|
|
60
|
+
function validateDbConfig() {
|
|
58
61
|
const dbPath = process.env.DB_PATH;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
console.error("
|
|
62
|
+
const databaseUrl = process.env.DATABASE_URL;
|
|
63
|
+
if (!dbPath && !databaseUrl) {
|
|
64
|
+
console.error("Error: Either DB_PATH or DATABASE_URL environment variable is required");
|
|
65
|
+
console.error("Please set DB_PATH or DATABASE_URL in your .env file (config/.env or ~/.fileverse/.env) or run the CLI first");
|
|
62
66
|
process.exit(1);
|
|
63
67
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
fs.
|
|
68
|
+
if (dbPath && !databaseUrl) {
|
|
69
|
+
const dbDir = path.dirname(dbPath.trim());
|
|
70
|
+
if (!fs.existsSync(dbDir)) {
|
|
71
|
+
fs.mkdirSync(dbDir, { recursive: true });
|
|
72
|
+
}
|
|
67
73
|
}
|
|
68
74
|
}
|
|
69
75
|
var config = {
|
|
@@ -92,6 +98,9 @@ var config = {
|
|
|
92
98
|
get DB_PATH() {
|
|
93
99
|
return process.env.DB_PATH;
|
|
94
100
|
},
|
|
101
|
+
get DATABASE_URL() {
|
|
102
|
+
return process.env.DATABASE_URL;
|
|
103
|
+
},
|
|
95
104
|
get PORT() {
|
|
96
105
|
return process.env.PORT || STATIC_CONFIG.DEFAULT_PORT;
|
|
97
106
|
},
|
|
@@ -178,71 +187,231 @@ var logger = {
|
|
|
178
187
|
child: pinoInstance.child.bind(pinoInstance)
|
|
179
188
|
};
|
|
180
189
|
|
|
181
|
-
// src/infra/database/
|
|
190
|
+
// src/infra/database/adapters/sqlite-adapter.ts
|
|
182
191
|
import Database from "better-sqlite3";
|
|
183
|
-
var
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
192
|
+
var SqliteAdapter = class {
|
|
193
|
+
db;
|
|
194
|
+
constructor(dbPath) {
|
|
195
|
+
this.db = new Database(dbPath, {
|
|
196
|
+
verbose: config.NODE_ENV === "development" ? (msg) => logger.debug(String(msg)) : void 0
|
|
197
|
+
});
|
|
198
|
+
this.db.pragma("journal_mode = WAL");
|
|
199
|
+
this.db.pragma("foreign_keys = ON");
|
|
200
|
+
this.db.prepare("SELECT 1").get();
|
|
201
|
+
logger.info(`SQLite database connected: ${dbPath}`);
|
|
202
|
+
}
|
|
203
|
+
async select(sql, params = []) {
|
|
204
|
+
const stmt = this.db.prepare(sql);
|
|
205
|
+
return stmt.all(params);
|
|
206
|
+
}
|
|
207
|
+
async selectOne(sql, params = []) {
|
|
208
|
+
const stmt = this.db.prepare(sql);
|
|
209
|
+
return stmt.get(params);
|
|
210
|
+
}
|
|
211
|
+
async execute(sql, params = []) {
|
|
212
|
+
const stmt = this.db.prepare(sql);
|
|
213
|
+
const result = stmt.run(params);
|
|
214
|
+
return {
|
|
215
|
+
changes: result.changes,
|
|
216
|
+
lastInsertRowid: result.lastInsertRowid
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
async transaction(callback) {
|
|
220
|
+
this.db.exec("SAVEPOINT txn");
|
|
221
|
+
try {
|
|
222
|
+
const result = await callback(this);
|
|
223
|
+
this.db.exec("RELEASE txn");
|
|
224
|
+
return result;
|
|
225
|
+
} catch (err) {
|
|
226
|
+
this.db.exec("ROLLBACK TO txn");
|
|
227
|
+
throw err;
|
|
191
228
|
}
|
|
192
|
-
return _DatabaseConnectionManager.instance;
|
|
193
229
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
230
|
+
async exec(sql) {
|
|
231
|
+
this.db.exec(sql);
|
|
232
|
+
}
|
|
233
|
+
async close() {
|
|
234
|
+
this.db.close();
|
|
235
|
+
logger.info("Database connection closed");
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// src/infra/database/adapters/pg-adapter.ts
|
|
240
|
+
import pg from "pg";
|
|
241
|
+
var { Pool } = pg;
|
|
242
|
+
var COLUMN_NAME_MAP = {
|
|
243
|
+
ddocid: "ddocId",
|
|
244
|
+
localversion: "localVersion",
|
|
245
|
+
onchainversion: "onchainVersion",
|
|
246
|
+
syncstatus: "syncStatus",
|
|
247
|
+
isdeleted: "isDeleted",
|
|
248
|
+
onchainfileid: "onChainFileId",
|
|
249
|
+
portaladdress: "portalAddress",
|
|
250
|
+
createdat: "createdAt",
|
|
251
|
+
updatedat: "updatedAt",
|
|
252
|
+
linkkey: "linkKey",
|
|
253
|
+
linkkeynonce: "linkKeyNonce",
|
|
254
|
+
commentkey: "commentKey",
|
|
255
|
+
portalseed: "portalSeed",
|
|
256
|
+
owneraddress: "ownerAddress",
|
|
257
|
+
apikeyseed: "apiKeySeed",
|
|
258
|
+
collaboratoraddress: "collaboratorAddress",
|
|
259
|
+
fileid: "fileId",
|
|
260
|
+
retrycount: "retryCount",
|
|
261
|
+
lasterror: "lastError",
|
|
262
|
+
lockedat: "lockedAt",
|
|
263
|
+
nextretryat: "nextRetryAt",
|
|
264
|
+
userophash: "userOpHash",
|
|
265
|
+
pendingpayload: "pendingPayload",
|
|
266
|
+
folderid: "folderId",
|
|
267
|
+
folderref: "folderRef",
|
|
268
|
+
foldername: "folderName",
|
|
269
|
+
metadataipfshash: "metadataIPFSHash",
|
|
270
|
+
contentipfshash: "contentIPFSHash",
|
|
271
|
+
lasttransactionhash: "lastTransactionHash",
|
|
272
|
+
lasttransactionblocknumber: "lastTransactionBlockNumber",
|
|
273
|
+
lasttransactionblocktimestamp: "lastTransactionBlockTimestamp",
|
|
274
|
+
created_at: "created_at",
|
|
275
|
+
updated_at: "updated_at"
|
|
276
|
+
};
|
|
277
|
+
function translateParams(sql) {
|
|
278
|
+
let idx = 0;
|
|
279
|
+
return sql.replace(/\?/g, () => `$${++idx}`);
|
|
280
|
+
}
|
|
281
|
+
function mapRow(row) {
|
|
282
|
+
const mapped = {};
|
|
283
|
+
for (const [key, value] of Object.entries(row)) {
|
|
284
|
+
const mappedKey = COLUMN_NAME_MAP[key] ?? key;
|
|
285
|
+
mapped[mappedKey] = value instanceof Date ? value.toISOString() : value;
|
|
286
|
+
}
|
|
287
|
+
return mapped;
|
|
288
|
+
}
|
|
289
|
+
var PgClientAdapter = class {
|
|
290
|
+
constructor(client) {
|
|
291
|
+
this.client = client;
|
|
292
|
+
}
|
|
293
|
+
async select(sql, params = []) {
|
|
294
|
+
const result = await this.client.query(translateParams(sql), params);
|
|
295
|
+
return result.rows.map((row) => mapRow(row));
|
|
296
|
+
}
|
|
297
|
+
async selectOne(sql, params = []) {
|
|
298
|
+
const result = await this.client.query(translateParams(sql), params);
|
|
299
|
+
return result.rows[0] ? mapRow(result.rows[0]) : void 0;
|
|
300
|
+
}
|
|
301
|
+
async execute(sql, params = []) {
|
|
302
|
+
const result = await this.client.query(translateParams(sql), params);
|
|
303
|
+
return { changes: result.rowCount ?? 0, lastInsertRowid: 0 };
|
|
304
|
+
}
|
|
305
|
+
async transaction(callback) {
|
|
306
|
+
await this.client.query("SAVEPOINT nested_txn");
|
|
307
|
+
try {
|
|
308
|
+
const result = await callback(this);
|
|
309
|
+
await this.client.query("RELEASE SAVEPOINT nested_txn");
|
|
310
|
+
return result;
|
|
311
|
+
} catch (err) {
|
|
312
|
+
await this.client.query("ROLLBACK TO SAVEPOINT nested_txn");
|
|
313
|
+
throw err;
|
|
204
314
|
}
|
|
205
|
-
|
|
315
|
+
}
|
|
316
|
+
async exec(sql) {
|
|
317
|
+
await this.client.query(sql);
|
|
206
318
|
}
|
|
207
319
|
async close() {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
var PgAdapter = class {
|
|
323
|
+
pool;
|
|
324
|
+
constructor(connectionString) {
|
|
325
|
+
const url = new URL(connectionString);
|
|
326
|
+
const isLocal = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1";
|
|
327
|
+
const sslDisabled = connectionString.includes("sslmode=disable");
|
|
328
|
+
const needsSsl = !isLocal && !sslDisabled;
|
|
329
|
+
this.pool = new Pool({
|
|
330
|
+
connectionString,
|
|
331
|
+
// pg requires password to be a string; local trust/peer auth uses empty string
|
|
332
|
+
password: url.password || "",
|
|
333
|
+
max: 20,
|
|
334
|
+
idleTimeoutMillis: 3e4,
|
|
335
|
+
ssl: needsSsl ? { rejectUnauthorized: false } : void 0
|
|
336
|
+
});
|
|
337
|
+
logger.info(`PostgreSQL pool created (ssl: ${needsSsl ? "on" : "off"})`);
|
|
338
|
+
}
|
|
339
|
+
async select(sql, params = []) {
|
|
340
|
+
const result = await this.pool.query(translateParams(sql), params);
|
|
341
|
+
return result.rows.map((row) => mapRow(row));
|
|
342
|
+
}
|
|
343
|
+
async selectOne(sql, params = []) {
|
|
344
|
+
const result = await this.pool.query(translateParams(sql), params);
|
|
345
|
+
return result.rows[0] ? mapRow(result.rows[0]) : void 0;
|
|
346
|
+
}
|
|
347
|
+
async execute(sql, params = []) {
|
|
348
|
+
const result = await this.pool.query(translateParams(sql), params);
|
|
349
|
+
return { changes: result.rowCount ?? 0, lastInsertRowid: 0 };
|
|
350
|
+
}
|
|
351
|
+
async transaction(callback) {
|
|
352
|
+
const client = await this.pool.connect();
|
|
353
|
+
try {
|
|
354
|
+
await client.query("BEGIN");
|
|
355
|
+
const clientAdapter = new PgClientAdapter(client);
|
|
356
|
+
const result = await callback(clientAdapter);
|
|
357
|
+
await client.query("COMMIT");
|
|
358
|
+
return result;
|
|
359
|
+
} catch (err) {
|
|
360
|
+
await client.query("ROLLBACK");
|
|
361
|
+
throw err;
|
|
362
|
+
} finally {
|
|
363
|
+
client.release();
|
|
212
364
|
}
|
|
213
365
|
}
|
|
214
|
-
|
|
215
|
-
|
|
366
|
+
async exec(sql) {
|
|
367
|
+
await this.pool.query(sql);
|
|
368
|
+
}
|
|
369
|
+
async close() {
|
|
370
|
+
await this.pool.end();
|
|
371
|
+
logger.info("PostgreSQL pool closed");
|
|
216
372
|
}
|
|
217
373
|
};
|
|
218
|
-
|
|
374
|
+
|
|
375
|
+
// src/infra/database/connection.ts
|
|
376
|
+
var adapter = null;
|
|
377
|
+
async function getAdapter() {
|
|
378
|
+
if (adapter) return adapter;
|
|
379
|
+
const databaseUrl = config.DATABASE_URL;
|
|
380
|
+
const dbPath = config.DB_PATH;
|
|
381
|
+
if (databaseUrl) {
|
|
382
|
+
adapter = new PgAdapter(databaseUrl);
|
|
383
|
+
} else if (dbPath) {
|
|
384
|
+
adapter = new SqliteAdapter(dbPath);
|
|
385
|
+
} else {
|
|
386
|
+
throw new Error("Either DATABASE_URL or DB_PATH must be set");
|
|
387
|
+
}
|
|
388
|
+
return adapter;
|
|
389
|
+
}
|
|
390
|
+
function getAdapterSync() {
|
|
391
|
+
if (!adapter) {
|
|
392
|
+
throw new Error(
|
|
393
|
+
"Database adapter not initialized. Call getAdapter() at startup first."
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
return adapter;
|
|
397
|
+
}
|
|
219
398
|
|
|
220
399
|
// src/domain/file/constants.ts
|
|
221
400
|
var DEFAULT_LIST_LIMIT = 10;
|
|
222
401
|
|
|
223
402
|
// src/infra/database/query-builder.ts
|
|
224
|
-
function getDb() {
|
|
225
|
-
return databaseConnectionManager.getConnection();
|
|
226
|
-
}
|
|
227
403
|
var QueryBuilder = class {
|
|
228
|
-
static select(sql, params = []) {
|
|
229
|
-
|
|
230
|
-
return stmt.all(params);
|
|
404
|
+
static async select(sql, params = []) {
|
|
405
|
+
return getAdapterSync().select(sql, params);
|
|
231
406
|
}
|
|
232
|
-
static selectOne(sql, params = []) {
|
|
233
|
-
|
|
234
|
-
return stmt.get(params);
|
|
407
|
+
static async selectOne(sql, params = []) {
|
|
408
|
+
return getAdapterSync().selectOne(sql, params);
|
|
235
409
|
}
|
|
236
|
-
static execute(sql, params = []) {
|
|
237
|
-
|
|
238
|
-
const result = stmt.run(params);
|
|
239
|
-
return {
|
|
240
|
-
changes: result.changes,
|
|
241
|
-
lastInsertRowid: result.lastInsertRowid
|
|
242
|
-
};
|
|
410
|
+
static async execute(sql, params = []) {
|
|
411
|
+
return getAdapterSync().execute(sql, params);
|
|
243
412
|
}
|
|
244
|
-
static transaction(callback) {
|
|
245
|
-
return
|
|
413
|
+
static async transaction(callback) {
|
|
414
|
+
return getAdapterSync().transaction(callback);
|
|
246
415
|
}
|
|
247
416
|
static paginate(sql, options = {}) {
|
|
248
417
|
let query = sql;
|
|
@@ -261,12 +430,6 @@ var QueryBuilder = class {
|
|
|
261
430
|
}
|
|
262
431
|
};
|
|
263
432
|
|
|
264
|
-
// src/infra/database/index.ts
|
|
265
|
-
function getDb2() {
|
|
266
|
-
return databaseConnectionManager.getConnection();
|
|
267
|
-
}
|
|
268
|
-
var database_default = getDb2;
|
|
269
|
-
|
|
270
433
|
// src/infra/database/models/files.model.ts
|
|
271
434
|
import { uuidv7 } from "uuidv7";
|
|
272
435
|
var FilesModel = class {
|
|
@@ -300,15 +463,15 @@ var FilesModel = class {
|
|
|
300
463
|
link: fileRaw.link
|
|
301
464
|
};
|
|
302
465
|
}
|
|
303
|
-
static findAll(portalAddress, limit, skip) {
|
|
466
|
+
static async findAll(portalAddress, limit, skip) {
|
|
304
467
|
const whereClause = "isDeleted = 0 AND portalAddress = ?";
|
|
305
468
|
const params = [portalAddress];
|
|
306
469
|
const countSql = `
|
|
307
|
-
SELECT COUNT(*) as count
|
|
308
|
-
FROM ${this.TABLE}
|
|
470
|
+
SELECT COUNT(*) as count
|
|
471
|
+
FROM ${this.TABLE}
|
|
309
472
|
WHERE ${whereClause}
|
|
310
473
|
`;
|
|
311
|
-
const totalResult = QueryBuilder.selectOne(countSql, params);
|
|
474
|
+
const totalResult = await QueryBuilder.selectOne(countSql, params);
|
|
312
475
|
const total = totalResult?.count || 0;
|
|
313
476
|
const sql = `
|
|
314
477
|
SELECT *
|
|
@@ -321,51 +484,51 @@ var FilesModel = class {
|
|
|
321
484
|
orderBy: "createdAt",
|
|
322
485
|
orderDirection: "DESC"
|
|
323
486
|
});
|
|
324
|
-
const filesRaw = QueryBuilder.select(completeSql, params);
|
|
487
|
+
const filesRaw = await QueryBuilder.select(completeSql, params);
|
|
325
488
|
const files = filesRaw.map(this.parseFile);
|
|
326
489
|
const hasNext = skip !== void 0 && limit !== void 0 ? skip + limit < total : false;
|
|
327
490
|
return { files, total, hasNext };
|
|
328
491
|
}
|
|
329
|
-
static findById(_id, portalAddress) {
|
|
492
|
+
static async findById(_id, portalAddress) {
|
|
330
493
|
const sql = `
|
|
331
494
|
SELECT *
|
|
332
|
-
FROM ${this.TABLE}
|
|
495
|
+
FROM ${this.TABLE}
|
|
333
496
|
WHERE _id = ? AND isDeleted = 0 AND portalAddress = ?
|
|
334
497
|
`;
|
|
335
|
-
const result = QueryBuilder.selectOne(sql, [_id, portalAddress]);
|
|
498
|
+
const result = await QueryBuilder.selectOne(sql, [_id, portalAddress]);
|
|
336
499
|
return result ? this.parseFile(result) : void 0;
|
|
337
500
|
}
|
|
338
|
-
static findByIdIncludingDeleted(_id) {
|
|
501
|
+
static async findByIdIncludingDeleted(_id) {
|
|
339
502
|
const sql = `
|
|
340
503
|
SELECT *
|
|
341
|
-
FROM ${this.TABLE}
|
|
504
|
+
FROM ${this.TABLE}
|
|
342
505
|
WHERE _id = ?
|
|
343
506
|
`;
|
|
344
|
-
const result = QueryBuilder.selectOne(sql, [_id]);
|
|
507
|
+
const result = await QueryBuilder.selectOne(sql, [_id]);
|
|
345
508
|
return result ? this.parseFile(result) : void 0;
|
|
346
509
|
}
|
|
347
|
-
static findByIdExcludingDeleted(_id) {
|
|
510
|
+
static async findByIdExcludingDeleted(_id) {
|
|
348
511
|
const sql = `
|
|
349
512
|
SELECT *
|
|
350
|
-
FROM ${this.TABLE}
|
|
513
|
+
FROM ${this.TABLE}
|
|
351
514
|
WHERE _id = ? AND isDeleted = 0
|
|
352
515
|
`;
|
|
353
|
-
const result = QueryBuilder.selectOne(sql, [_id]);
|
|
516
|
+
const result = await QueryBuilder.selectOne(sql, [_id]);
|
|
354
517
|
return result ? this.parseFile(result) : void 0;
|
|
355
518
|
}
|
|
356
|
-
static findByDDocId(ddocId, portalAddress) {
|
|
519
|
+
static async findByDDocId(ddocId, portalAddress) {
|
|
357
520
|
const sql = `
|
|
358
521
|
SELECT *
|
|
359
|
-
FROM ${this.TABLE}
|
|
522
|
+
FROM ${this.TABLE}
|
|
360
523
|
WHERE ddocId = ? AND isDeleted = 0 AND portalAddress = ?
|
|
361
524
|
`;
|
|
362
|
-
const result = QueryBuilder.selectOne(sql, [ddocId, portalAddress]);
|
|
525
|
+
const result = await QueryBuilder.selectOne(sql, [ddocId, portalAddress]);
|
|
363
526
|
return result ? this.parseFile(result) : void 0;
|
|
364
527
|
}
|
|
365
|
-
static searchByTitle(searchTerm, portalAddress, limit, skip) {
|
|
528
|
+
static async searchByTitle(searchTerm, portalAddress, limit, skip) {
|
|
366
529
|
const sql = `
|
|
367
530
|
SELECT *
|
|
368
|
-
FROM ${this.TABLE}
|
|
531
|
+
FROM ${this.TABLE}
|
|
369
532
|
WHERE LOWER(title) LIKE LOWER(?) AND isDeleted = 0 AND portalAddress = ?
|
|
370
533
|
`;
|
|
371
534
|
const completeSql = QueryBuilder.paginate(sql, {
|
|
@@ -374,24 +537,24 @@ var FilesModel = class {
|
|
|
374
537
|
orderBy: "createdAt",
|
|
375
538
|
orderDirection: "DESC"
|
|
376
539
|
});
|
|
377
|
-
const filesRaw = QueryBuilder.select(completeSql, [`%${searchTerm}%`, portalAddress]);
|
|
540
|
+
const filesRaw = await QueryBuilder.select(completeSql, [`%${searchTerm}%`, portalAddress]);
|
|
378
541
|
return filesRaw.map(this.parseFile);
|
|
379
542
|
}
|
|
380
|
-
static create(input) {
|
|
543
|
+
static async create(input) {
|
|
381
544
|
const _id = uuidv7();
|
|
382
545
|
const sql = `
|
|
383
|
-
INSERT INTO ${this.TABLE}
|
|
384
|
-
(_id, title, content, ddocId, portalAddress)
|
|
546
|
+
INSERT INTO ${this.TABLE}
|
|
547
|
+
(_id, title, content, ddocId, portalAddress)
|
|
385
548
|
VALUES (?, ?, ?, ?, ?)
|
|
386
549
|
`;
|
|
387
|
-
QueryBuilder.execute(sql, [_id, input.title, input.content, input.ddocId, input.portalAddress]);
|
|
388
|
-
const created = this.findById(_id, input.portalAddress);
|
|
550
|
+
await QueryBuilder.execute(sql, [_id, input.title, input.content, input.ddocId, input.portalAddress]);
|
|
551
|
+
const created = await this.findById(_id, input.portalAddress);
|
|
389
552
|
if (!created) {
|
|
390
553
|
throw new Error("Failed to create file");
|
|
391
554
|
}
|
|
392
555
|
return created;
|
|
393
556
|
}
|
|
394
|
-
static update(_id, payload, portalAddress) {
|
|
557
|
+
static async update(_id, payload, portalAddress) {
|
|
395
558
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
396
559
|
const keys = [];
|
|
397
560
|
const values = [];
|
|
@@ -410,22 +573,22 @@ var FilesModel = class {
|
|
|
410
573
|
values.push(now, _id, portalAddress);
|
|
411
574
|
const updateChain = keys.join(", ");
|
|
412
575
|
const sql = `UPDATE ${this.TABLE} SET ${updateChain} WHERE _id = ? AND portalAddress = ?`;
|
|
413
|
-
QueryBuilder.execute(sql, values);
|
|
414
|
-
const updated = this.findById(_id, portalAddress);
|
|
576
|
+
await QueryBuilder.execute(sql, values);
|
|
577
|
+
const updated = await this.findById(_id, portalAddress);
|
|
415
578
|
if (!updated) {
|
|
416
579
|
throw new Error("Failed to update file");
|
|
417
580
|
}
|
|
418
581
|
return updated;
|
|
419
582
|
}
|
|
420
|
-
static softDelete(_id) {
|
|
583
|
+
static async softDelete(_id) {
|
|
421
584
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
422
585
|
const sql = `
|
|
423
|
-
UPDATE ${this.TABLE}
|
|
586
|
+
UPDATE ${this.TABLE}
|
|
424
587
|
SET isDeleted = 1, syncStatus = 'pending', updatedAt = ?
|
|
425
588
|
WHERE _id = ?
|
|
426
589
|
`;
|
|
427
|
-
QueryBuilder.execute(sql, [now, _id]);
|
|
428
|
-
const deleted = this.findByIdIncludingDeleted(_id);
|
|
590
|
+
await QueryBuilder.execute(sql, [now, _id]);
|
|
591
|
+
const deleted = await this.findByIdIncludingDeleted(_id);
|
|
429
592
|
if (!deleted) {
|
|
430
593
|
throw new Error("Failed to delete file");
|
|
431
594
|
}
|
|
@@ -437,22 +600,22 @@ var FilesModel = class {
|
|
|
437
600
|
import { uuidv7 as uuidv72 } from "uuidv7";
|
|
438
601
|
var PortalsModel = class {
|
|
439
602
|
static TABLE = "portals";
|
|
440
|
-
static findByPortalAddress(portalAddress) {
|
|
603
|
+
static async findByPortalAddress(portalAddress) {
|
|
441
604
|
const sql = `SELECT _id, portalAddress, portalSeed, ownerAddress, createdAt, updatedAt FROM ${this.TABLE} WHERE portalAddress = ?`;
|
|
442
605
|
return QueryBuilder.selectOne(sql, [portalAddress]);
|
|
443
606
|
}
|
|
444
|
-
static create(input) {
|
|
607
|
+
static async create(input) {
|
|
445
608
|
const _id = uuidv72();
|
|
446
609
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
447
610
|
const sql = `INSERT INTO ${this.TABLE} (_id, portalAddress, portalSeed, ownerAddress, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?)`;
|
|
448
|
-
QueryBuilder.execute(sql, [_id, input.portalAddress, input.portalSeed, input.ownerAddress, now, now]);
|
|
449
|
-
const created = this.findByPortalAddress(input.portalAddress);
|
|
611
|
+
await QueryBuilder.execute(sql, [_id, input.portalAddress, input.portalSeed, input.ownerAddress, now, now]);
|
|
612
|
+
const created = await this.findByPortalAddress(input.portalAddress);
|
|
450
613
|
if (!created) {
|
|
451
614
|
throw new Error("Failed to create portal");
|
|
452
615
|
}
|
|
453
616
|
return created;
|
|
454
617
|
}
|
|
455
|
-
static update(portalAddress, input) {
|
|
618
|
+
static async update(portalAddress, input) {
|
|
456
619
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
457
620
|
const keys = [];
|
|
458
621
|
const values = [];
|
|
@@ -467,15 +630,15 @@ var PortalsModel = class {
|
|
|
467
630
|
const updateChain = keys.join(", ");
|
|
468
631
|
const sql = `UPDATE ${this.TABLE} SET ${updateChain} WHERE portalAddress = ?`;
|
|
469
632
|
values.push(portalAddress);
|
|
470
|
-
QueryBuilder.execute(sql, values);
|
|
471
|
-
const updated = this.findByPortalAddress(portalAddress);
|
|
633
|
+
await QueryBuilder.execute(sql, values);
|
|
634
|
+
const updated = await this.findByPortalAddress(portalAddress);
|
|
472
635
|
if (!updated) {
|
|
473
636
|
throw new Error("Failed to update portal");
|
|
474
637
|
}
|
|
475
638
|
return updated;
|
|
476
639
|
}
|
|
477
|
-
static upsert(input) {
|
|
478
|
-
const existing = this.findByPortalAddress(input.portalAddress);
|
|
640
|
+
static async upsert(input) {
|
|
641
|
+
const existing = await this.findByPortalAddress(input.portalAddress);
|
|
479
642
|
if (existing) {
|
|
480
643
|
return this.update(input.portalAddress, {
|
|
481
644
|
portalSeed: input.portalSeed,
|
|
@@ -510,16 +673,16 @@ function onNewEvent(callback) {
|
|
|
510
673
|
var RETRY_DELAYS_MS = [5e3, 3e4, 12e4];
|
|
511
674
|
var EventsModel = class {
|
|
512
675
|
static TABLE = "events";
|
|
513
|
-
static create(input) {
|
|
676
|
+
static async create(input) {
|
|
514
677
|
const _id = uuidv74();
|
|
515
678
|
const timestamp = Date.now();
|
|
516
679
|
const status = "pending";
|
|
517
680
|
const sql = `
|
|
518
|
-
INSERT INTO ${this.TABLE}
|
|
519
|
-
(_id, type, timestamp, fileId, portalAddress, status, retryCount, lastError, lockedAt, nextRetryAt)
|
|
681
|
+
INSERT INTO ${this.TABLE}
|
|
682
|
+
(_id, type, timestamp, fileId, portalAddress, status, retryCount, lastError, lockedAt, nextRetryAt)
|
|
520
683
|
VALUES (?, ?, ?, ?, ?, ?, 0, NULL, NULL, NULL)
|
|
521
684
|
`;
|
|
522
|
-
QueryBuilder.execute(sql, [_id, input.type, timestamp, input.fileId, input.portalAddress, status]);
|
|
685
|
+
await QueryBuilder.execute(sql, [_id, input.type, timestamp, input.fileId, input.portalAddress, status]);
|
|
523
686
|
notifyNewEvent();
|
|
524
687
|
return {
|
|
525
688
|
_id,
|
|
@@ -534,22 +697,22 @@ var EventsModel = class {
|
|
|
534
697
|
nextRetryAt: null
|
|
535
698
|
};
|
|
536
699
|
}
|
|
537
|
-
static findById(_id) {
|
|
700
|
+
static async findById(_id) {
|
|
538
701
|
const sql = `SELECT * FROM ${this.TABLE} WHERE _id = ?`;
|
|
539
|
-
const row = QueryBuilder.selectOne(sql, [_id]);
|
|
702
|
+
const row = await QueryBuilder.selectOne(sql, [_id]);
|
|
540
703
|
return row ? this.parseEvent(row) : void 0;
|
|
541
704
|
}
|
|
542
|
-
static findNextPending() {
|
|
705
|
+
static async findNextPending() {
|
|
543
706
|
const sql = `
|
|
544
707
|
SELECT * FROM ${this.TABLE}
|
|
545
708
|
WHERE status = 'pending'
|
|
546
709
|
ORDER BY timestamp ASC
|
|
547
710
|
LIMIT 1
|
|
548
711
|
`;
|
|
549
|
-
const row = QueryBuilder.selectOne(sql, []);
|
|
712
|
+
const row = await QueryBuilder.selectOne(sql, []);
|
|
550
713
|
return row ? this.parseEvent(row) : void 0;
|
|
551
714
|
}
|
|
552
|
-
static findNextEligible(lockedFileIds) {
|
|
715
|
+
static async findNextEligible(lockedFileIds) {
|
|
553
716
|
const now = Date.now();
|
|
554
717
|
const exclusionClause = lockedFileIds.length > 0 ? `AND e1.fileId NOT IN (${lockedFileIds.map(() => "?").join(", ")})` : "";
|
|
555
718
|
const sql = `
|
|
@@ -567,29 +730,29 @@ var EventsModel = class {
|
|
|
567
730
|
LIMIT 1
|
|
568
731
|
`;
|
|
569
732
|
const params = [now, ...lockedFileIds];
|
|
570
|
-
const row = QueryBuilder.selectOne(sql, params);
|
|
733
|
+
const row = await QueryBuilder.selectOne(sql, params);
|
|
571
734
|
return row ? this.parseEvent(row) : void 0;
|
|
572
735
|
}
|
|
573
|
-
static markProcessing(_id) {
|
|
736
|
+
static async markProcessing(_id) {
|
|
574
737
|
const sql = `
|
|
575
738
|
UPDATE ${this.TABLE}
|
|
576
739
|
SET status = 'processing',
|
|
577
740
|
lockedAt = ?
|
|
578
741
|
WHERE _id = ?
|
|
579
742
|
`;
|
|
580
|
-
QueryBuilder.execute(sql, [Date.now(), _id]);
|
|
743
|
+
await QueryBuilder.execute(sql, [Date.now(), _id]);
|
|
581
744
|
}
|
|
582
|
-
static markProcessed(_id) {
|
|
745
|
+
static async markProcessed(_id) {
|
|
583
746
|
const sql = `
|
|
584
747
|
UPDATE ${this.TABLE}
|
|
585
748
|
SET status = 'processed',
|
|
586
749
|
lockedAt = NULL
|
|
587
750
|
WHERE _id = ?
|
|
588
751
|
`;
|
|
589
|
-
QueryBuilder.execute(sql, [_id]);
|
|
752
|
+
await QueryBuilder.execute(sql, [_id]);
|
|
590
753
|
}
|
|
591
|
-
static scheduleRetry(_id, errorMsg) {
|
|
592
|
-
const event = this.findById(_id);
|
|
754
|
+
static async scheduleRetry(_id, errorMsg) {
|
|
755
|
+
const event = await this.findById(_id);
|
|
593
756
|
if (!event) return;
|
|
594
757
|
const delay = RETRY_DELAYS_MS[Math.min(event.retryCount, RETRY_DELAYS_MS.length - 1)];
|
|
595
758
|
const nextRetryAt = Date.now() + delay;
|
|
@@ -602,9 +765,9 @@ var EventsModel = class {
|
|
|
602
765
|
lockedAt = NULL
|
|
603
766
|
WHERE _id = ?
|
|
604
767
|
`;
|
|
605
|
-
QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
|
|
768
|
+
await QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
|
|
606
769
|
}
|
|
607
|
-
static scheduleRetryAfter(_id, errorMsg, retryAfterMs) {
|
|
770
|
+
static async scheduleRetryAfter(_id, errorMsg, retryAfterMs) {
|
|
608
771
|
const nextRetryAt = Date.now() + retryAfterMs;
|
|
609
772
|
const sql = `
|
|
610
773
|
UPDATE ${this.TABLE}
|
|
@@ -614,9 +777,9 @@ var EventsModel = class {
|
|
|
614
777
|
lockedAt = NULL
|
|
615
778
|
WHERE _id = ?
|
|
616
779
|
`;
|
|
617
|
-
QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
|
|
780
|
+
await QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
|
|
618
781
|
}
|
|
619
|
-
static markFailed(_id, errorMsg) {
|
|
782
|
+
static async markFailed(_id, errorMsg) {
|
|
620
783
|
const sql = `
|
|
621
784
|
UPDATE ${this.TABLE}
|
|
622
785
|
SET status = 'failed',
|
|
@@ -624,9 +787,9 @@ var EventsModel = class {
|
|
|
624
787
|
lockedAt = NULL
|
|
625
788
|
WHERE _id = ?
|
|
626
789
|
`;
|
|
627
|
-
QueryBuilder.execute(sql, [errorMsg, _id]);
|
|
790
|
+
await QueryBuilder.execute(sql, [errorMsg, _id]);
|
|
628
791
|
}
|
|
629
|
-
static listFailed(portalAddress) {
|
|
792
|
+
static async listFailed(portalAddress) {
|
|
630
793
|
const portalClause = portalAddress != null ? "AND portalAddress = ?" : "";
|
|
631
794
|
const sql = `
|
|
632
795
|
SELECT * FROM ${this.TABLE}
|
|
@@ -635,10 +798,10 @@ var EventsModel = class {
|
|
|
635
798
|
ORDER BY timestamp ASC
|
|
636
799
|
`;
|
|
637
800
|
const params = portalAddress != null ? [portalAddress] : [];
|
|
638
|
-
const rows = QueryBuilder.select(sql, params);
|
|
801
|
+
const rows = await QueryBuilder.select(sql, params);
|
|
639
802
|
return rows.map((row) => this.parseEvent(row));
|
|
640
803
|
}
|
|
641
|
-
static resetFailedToPending(_id, portalAddress) {
|
|
804
|
+
static async resetFailedToPending(_id, portalAddress) {
|
|
642
805
|
const portalClause = portalAddress != null ? "AND portalAddress = ?" : "";
|
|
643
806
|
const sql = `
|
|
644
807
|
UPDATE ${this.TABLE}
|
|
@@ -652,13 +815,13 @@ var EventsModel = class {
|
|
|
652
815
|
${portalClause}
|
|
653
816
|
`;
|
|
654
817
|
const params = portalAddress != null ? [_id, portalAddress] : [_id];
|
|
655
|
-
const result = QueryBuilder.execute(sql, params);
|
|
818
|
+
const result = await QueryBuilder.execute(sql, params);
|
|
656
819
|
if (result.changes > 0) {
|
|
657
820
|
notifyNewEvent();
|
|
658
821
|
}
|
|
659
822
|
return result.changes > 0;
|
|
660
823
|
}
|
|
661
|
-
static resetAllFailedToPending(portalAddress) {
|
|
824
|
+
static async resetAllFailedToPending(portalAddress) {
|
|
662
825
|
const portalClause = portalAddress != null ? "AND portalAddress = ?" : "";
|
|
663
826
|
const sql = `
|
|
664
827
|
UPDATE ${this.TABLE}
|
|
@@ -671,13 +834,13 @@ var EventsModel = class {
|
|
|
671
834
|
${portalClause}
|
|
672
835
|
`;
|
|
673
836
|
const params = portalAddress != null ? [portalAddress] : [];
|
|
674
|
-
const result = QueryBuilder.execute(sql, params);
|
|
837
|
+
const result = await QueryBuilder.execute(sql, params);
|
|
675
838
|
if (result.changes > 0) {
|
|
676
839
|
notifyNewEvent();
|
|
677
840
|
}
|
|
678
841
|
return result.changes;
|
|
679
842
|
}
|
|
680
|
-
static resetStaleEvents(staleThreshold) {
|
|
843
|
+
static async resetStaleEvents(staleThreshold) {
|
|
681
844
|
const sql = `
|
|
682
845
|
UPDATE ${this.TABLE}
|
|
683
846
|
SET status = 'pending',
|
|
@@ -688,16 +851,16 @@ var EventsModel = class {
|
|
|
688
851
|
AND lockedAt IS NOT NULL
|
|
689
852
|
AND lockedAt < ?
|
|
690
853
|
`;
|
|
691
|
-
const result = QueryBuilder.execute(sql, [staleThreshold]);
|
|
854
|
+
const result = await QueryBuilder.execute(sql, [staleThreshold]);
|
|
692
855
|
return result.changes;
|
|
693
856
|
}
|
|
694
|
-
static setEventPendingOp(_id, userOpHash, payload) {
|
|
857
|
+
static async setEventPendingOp(_id, userOpHash, payload) {
|
|
695
858
|
const sql = `UPDATE ${this.TABLE} SET userOpHash = ?, pendingPayload = ? WHERE _id = ?`;
|
|
696
|
-
QueryBuilder.execute(sql, [userOpHash, JSON.stringify(payload), _id]);
|
|
859
|
+
await QueryBuilder.execute(sql, [userOpHash, JSON.stringify(payload), _id]);
|
|
697
860
|
}
|
|
698
|
-
static clearEventPendingOp(_id) {
|
|
861
|
+
static async clearEventPendingOp(_id) {
|
|
699
862
|
const sql = `UPDATE ${this.TABLE} SET userOpHash = NULL, pendingPayload = NULL WHERE _id = ?`;
|
|
700
|
-
QueryBuilder.execute(sql, [_id]);
|
|
863
|
+
await QueryBuilder.execute(sql, [_id]);
|
|
701
864
|
}
|
|
702
865
|
static parseEvent(row) {
|
|
703
866
|
return {
|
|
@@ -2285,12 +2448,12 @@ var FileManager = class {
|
|
|
2285
2448
|
};
|
|
2286
2449
|
|
|
2287
2450
|
// src/domain/portal/publish.ts
|
|
2288
|
-
function getPortalData(fileId) {
|
|
2289
|
-
const file = FilesModel.findByIdIncludingDeleted(fileId);
|
|
2451
|
+
async function getPortalData(fileId) {
|
|
2452
|
+
const file = await FilesModel.findByIdIncludingDeleted(fileId);
|
|
2290
2453
|
if (!file) {
|
|
2291
2454
|
throw new Error(`File with _id ${fileId} not found`);
|
|
2292
2455
|
}
|
|
2293
|
-
const portalDetails = PortalsModel.findByPortalAddress(file.portalAddress);
|
|
2456
|
+
const portalDetails = await PortalsModel.findByPortalAddress(file.portalAddress);
|
|
2294
2457
|
if (!portalDetails) {
|
|
2295
2458
|
throw new Error(`Portal with address ${file.portalAddress} not found`);
|
|
2296
2459
|
}
|
|
@@ -2330,7 +2493,7 @@ var executeOperation = async (fileManager, file, operation) => {
|
|
|
2330
2493
|
};
|
|
2331
2494
|
var handleExistingFileOp = async (fileId, operation) => {
|
|
2332
2495
|
try {
|
|
2333
|
-
const { file, portalDetails, apiKey } = getPortalData(fileId);
|
|
2496
|
+
const { file, portalDetails, apiKey } = await getPortalData(fileId);
|
|
2334
2497
|
const apiKeySeed = toUint8Array3(apiKey);
|
|
2335
2498
|
const { privateAccountKey, ucanSecret } = deriveCollaboratorKeys(apiKeySeed);
|
|
2336
2499
|
const fileManager = await createFileManager(
|
|
@@ -2346,7 +2509,7 @@ var handleExistingFileOp = async (fileId, operation) => {
|
|
|
2346
2509
|
}
|
|
2347
2510
|
};
|
|
2348
2511
|
var handleNewFileOp = async (fileId) => {
|
|
2349
|
-
const { file, portalDetails, apiKey } = getPortalData(fileId);
|
|
2512
|
+
const { file, portalDetails, apiKey } = await getPortalData(fileId);
|
|
2350
2513
|
const apiKeySeed = toUint8Array3(apiKey);
|
|
2351
2514
|
const { privateAccountKey, ucanSecret } = deriveCollaboratorKeys(apiKeySeed);
|
|
2352
2515
|
const fileManager = await createFileManager(
|
|
@@ -2358,7 +2521,7 @@ var handleNewFileOp = async (fileId) => {
|
|
|
2358
2521
|
return fileManager.submitAddFileTrx(file);
|
|
2359
2522
|
};
|
|
2360
2523
|
var getProxyAuthParams = async (fileId) => {
|
|
2361
|
-
const { portalDetails, apiKey } = getPortalData(fileId);
|
|
2524
|
+
const { portalDetails, apiKey } = await getPortalData(fileId);
|
|
2362
2525
|
const apiKeySeed = toUint8Array3(apiKey);
|
|
2363
2526
|
const { privateAccountKey, ucanSecret } = deriveCollaboratorKeys(apiKeySeed);
|
|
2364
2527
|
const fileManager = await createFileManager(
|
|
@@ -2427,7 +2590,7 @@ var processEvent = async (event) => {
|
|
|
2427
2590
|
return { success: false, error: errorMsg };
|
|
2428
2591
|
}
|
|
2429
2592
|
};
|
|
2430
|
-
var onTransactionSuccess = (fileId, file, onChainFileId, pending) => {
|
|
2593
|
+
var onTransactionSuccess = async (fileId, file, onChainFileId, pending) => {
|
|
2431
2594
|
const frontendUrl = getRuntimeConfig().FRONTEND_URL;
|
|
2432
2595
|
const payload = {
|
|
2433
2596
|
onchainVersion: file.localVersion,
|
|
@@ -2438,14 +2601,14 @@ var onTransactionSuccess = (fileId, file, onChainFileId, pending) => {
|
|
|
2438
2601
|
metadata: pending.metadata,
|
|
2439
2602
|
link: `${frontendUrl}/${file.portalAddress}/${onChainFileId}#key=${pending.linkKey}`
|
|
2440
2603
|
};
|
|
2441
|
-
const updatedFile = FilesModel.update(fileId, payload, file.portalAddress);
|
|
2604
|
+
const updatedFile = await FilesModel.update(fileId, payload, file.portalAddress);
|
|
2442
2605
|
if (updatedFile.localVersion === updatedFile.onchainVersion) {
|
|
2443
|
-
FilesModel.update(fileId, { syncStatus: "synced" }, file.portalAddress);
|
|
2606
|
+
await FilesModel.update(fileId, { syncStatus: "synced" }, file.portalAddress);
|
|
2444
2607
|
}
|
|
2445
2608
|
};
|
|
2446
2609
|
var processCreateEvent = async (event) => {
|
|
2447
2610
|
const { fileId } = event;
|
|
2448
|
-
const file = FilesModel.findByIdIncludingDeleted(fileId);
|
|
2611
|
+
const file = await FilesModel.findByIdIncludingDeleted(fileId);
|
|
2449
2612
|
if (!file) {
|
|
2450
2613
|
throw new Error(`File ${fileId} not found`);
|
|
2451
2614
|
}
|
|
@@ -2464,18 +2627,18 @@ var processCreateEvent = async (event) => {
|
|
|
2464
2627
|
timeout
|
|
2465
2628
|
);
|
|
2466
2629
|
if (!receipt2.success) {
|
|
2467
|
-
EventsModel.clearEventPendingOp(event._id);
|
|
2630
|
+
await EventsModel.clearEventPendingOp(event._id);
|
|
2468
2631
|
throw new Error(`User operation failed: ${receipt2.reason}`);
|
|
2469
2632
|
}
|
|
2470
2633
|
const onChainFileId2 = parseFileEventLog(receipt2.logs, "AddedFile", ADDED_FILE_EVENT);
|
|
2471
2634
|
const pending = JSON.parse(event.pendingPayload);
|
|
2472
|
-
onTransactionSuccess(fileId, file, onChainFileId2, pending);
|
|
2473
|
-
EventsModel.clearEventPendingOp(event._id);
|
|
2635
|
+
await onTransactionSuccess(fileId, file, onChainFileId2, pending);
|
|
2636
|
+
await EventsModel.clearEventPendingOp(event._id);
|
|
2474
2637
|
logger.info(`File ${file.ddocId} created and published successfully (resumed from pending op)`);
|
|
2475
2638
|
return;
|
|
2476
2639
|
}
|
|
2477
2640
|
const result = await handleNewFileOp(fileId);
|
|
2478
|
-
EventsModel.setEventPendingOp(event._id, result.userOpHash, {
|
|
2641
|
+
await EventsModel.setEventPendingOp(event._id, result.userOpHash, {
|
|
2479
2642
|
linkKey: result.linkKey,
|
|
2480
2643
|
linkKeyNonce: result.linkKeyNonce,
|
|
2481
2644
|
commentKey: result.commentKey,
|
|
@@ -2489,22 +2652,22 @@ var processCreateEvent = async (event) => {
|
|
|
2489
2652
|
timeout
|
|
2490
2653
|
);
|
|
2491
2654
|
if (!receipt.success) {
|
|
2492
|
-
EventsModel.clearEventPendingOp(event._id);
|
|
2655
|
+
await EventsModel.clearEventPendingOp(event._id);
|
|
2493
2656
|
throw new Error(`User operation failed: ${receipt.reason}`);
|
|
2494
2657
|
}
|
|
2495
2658
|
const onChainFileId = parseFileEventLog(receipt.logs, "AddedFile", ADDED_FILE_EVENT);
|
|
2496
|
-
onTransactionSuccess(fileId, file, onChainFileId, {
|
|
2659
|
+
await onTransactionSuccess(fileId, file, onChainFileId, {
|
|
2497
2660
|
linkKey: result.linkKey,
|
|
2498
2661
|
linkKeyNonce: result.linkKeyNonce,
|
|
2499
2662
|
commentKey: result.commentKey,
|
|
2500
2663
|
metadata: result.metadata
|
|
2501
2664
|
});
|
|
2502
|
-
EventsModel.clearEventPendingOp(event._id);
|
|
2665
|
+
await EventsModel.clearEventPendingOp(event._id);
|
|
2503
2666
|
logger.info(`File ${file.ddocId} created and published successfully`);
|
|
2504
2667
|
};
|
|
2505
2668
|
var processUpdateEvent = async (event) => {
|
|
2506
2669
|
const { fileId } = event;
|
|
2507
|
-
const file = FilesModel.findByIdExcludingDeleted(fileId);
|
|
2670
|
+
const file = await FilesModel.findByIdExcludingDeleted(fileId);
|
|
2508
2671
|
if (!file) {
|
|
2509
2672
|
return;
|
|
2510
2673
|
}
|
|
@@ -2519,15 +2682,15 @@ var processUpdateEvent = async (event) => {
|
|
|
2519
2682
|
onchainVersion: file.localVersion,
|
|
2520
2683
|
metadata: result.metadata
|
|
2521
2684
|
};
|
|
2522
|
-
const updatedFile = FilesModel.update(fileId, payload, file.portalAddress);
|
|
2685
|
+
const updatedFile = await FilesModel.update(fileId, payload, file.portalAddress);
|
|
2523
2686
|
if (updatedFile.localVersion === updatedFile.onchainVersion) {
|
|
2524
|
-
FilesModel.update(fileId, { syncStatus: "synced" }, file.portalAddress);
|
|
2687
|
+
await FilesModel.update(fileId, { syncStatus: "synced" }, file.portalAddress);
|
|
2525
2688
|
}
|
|
2526
2689
|
logger.info(`File ${file.ddocId} updated and published successfully`);
|
|
2527
2690
|
};
|
|
2528
2691
|
var processDeleteEvent = async (event) => {
|
|
2529
2692
|
const { fileId } = event;
|
|
2530
|
-
const file = FilesModel.findByIdIncludingDeleted(fileId);
|
|
2693
|
+
const file = await FilesModel.findByIdIncludingDeleted(fileId);
|
|
2531
2694
|
if (!file) {
|
|
2532
2695
|
return;
|
|
2533
2696
|
}
|
|
@@ -2548,7 +2711,7 @@ var processDeleteEvent = async (event) => {
|
|
|
2548
2711
|
payload.metadata = result.metadata;
|
|
2549
2712
|
payload.isDeleted = 1;
|
|
2550
2713
|
}
|
|
2551
|
-
FilesModel.update(fileId, payload, file.portalAddress);
|
|
2714
|
+
await FilesModel.update(fileId, payload, file.portalAddress);
|
|
2552
2715
|
logger.info(`File ${fileId} delete event processed (syncStatus set to synced)`);
|
|
2553
2716
|
};
|
|
2554
2717
|
|
|
@@ -2565,8 +2728,8 @@ var FileEventsWorker = class {
|
|
|
2565
2728
|
signalCleanup = null;
|
|
2566
2729
|
pendingSignal = false;
|
|
2567
2730
|
wakeResolver = null;
|
|
2568
|
-
constructor(
|
|
2569
|
-
this.concurrency =
|
|
2731
|
+
constructor(concurrency = DEFAULT_CONCURRENCY) {
|
|
2732
|
+
this.concurrency = concurrency;
|
|
2570
2733
|
}
|
|
2571
2734
|
start() {
|
|
2572
2735
|
if (this.isRunning) {
|
|
@@ -2574,10 +2737,11 @@ var FileEventsWorker = class {
|
|
|
2574
2737
|
return;
|
|
2575
2738
|
}
|
|
2576
2739
|
this.isRunning = true;
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2740
|
+
this.recoverStaleEvents().then((staleCount) => {
|
|
2741
|
+
if (staleCount > 0) {
|
|
2742
|
+
logger.info(`Recovered ${staleCount} stale event(s)`);
|
|
2743
|
+
}
|
|
2744
|
+
});
|
|
2581
2745
|
this.signalCleanup = onNewEvent(() => {
|
|
2582
2746
|
this.pendingSignal = true;
|
|
2583
2747
|
this.wakeUp();
|
|
@@ -2606,10 +2770,10 @@ var FileEventsWorker = class {
|
|
|
2606
2770
|
let foundAny = false;
|
|
2607
2771
|
while (this.activeProcessors.size < this.concurrency && this.isRunning) {
|
|
2608
2772
|
const lockedFileIds = Array.from(this.activeProcessors.keys());
|
|
2609
|
-
const event = EventsModel.findNextEligible(lockedFileIds);
|
|
2773
|
+
const event = await EventsModel.findNextEligible(lockedFileIds);
|
|
2610
2774
|
if (!event) break;
|
|
2611
2775
|
foundAny = true;
|
|
2612
|
-
EventsModel.markProcessing(event._id);
|
|
2776
|
+
await EventsModel.markProcessing(event._id);
|
|
2613
2777
|
const processor = this.processEventWrapper(event);
|
|
2614
2778
|
this.activeProcessors.set(event.fileId, processor);
|
|
2615
2779
|
}
|
|
@@ -2620,33 +2784,33 @@ var FileEventsWorker = class {
|
|
|
2620
2784
|
try {
|
|
2621
2785
|
const result = await processEvent(event);
|
|
2622
2786
|
if (result.success) {
|
|
2623
|
-
EventsModel.markProcessed(event._id);
|
|
2787
|
+
await EventsModel.markProcessed(event._id);
|
|
2624
2788
|
} else {
|
|
2625
|
-
this.handleFailure(event, result.error);
|
|
2789
|
+
await this.handleFailure(event, result.error);
|
|
2626
2790
|
}
|
|
2627
2791
|
} catch (err) {
|
|
2628
|
-
this.handleFailure(event, err);
|
|
2792
|
+
await this.handleFailure(event, err);
|
|
2629
2793
|
} finally {
|
|
2630
2794
|
this.activeProcessors.delete(event.fileId);
|
|
2631
2795
|
}
|
|
2632
2796
|
}
|
|
2633
|
-
handleFailure(event, error) {
|
|
2797
|
+
async handleFailure(event, error) {
|
|
2634
2798
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2635
2799
|
if (error instanceof RateLimitError) {
|
|
2636
2800
|
const retryAfterMs = error.retryAfterSeconds * 1e3;
|
|
2637
|
-
EventsModel.scheduleRetryAfter(event._id, errorMsg, retryAfterMs);
|
|
2801
|
+
await EventsModel.scheduleRetryAfter(event._id, errorMsg, retryAfterMs);
|
|
2638
2802
|
logger.warn(`Event ${event._id} rate limited; retry after ${error.retryAfterSeconds}s`);
|
|
2639
2803
|
return;
|
|
2640
2804
|
}
|
|
2641
2805
|
if (event.retryCount < MAX_RETRIES) {
|
|
2642
|
-
EventsModel.scheduleRetry(event._id, errorMsg);
|
|
2806
|
+
await EventsModel.scheduleRetry(event._id, errorMsg);
|
|
2643
2807
|
logger.warn(`Event ${event._id} failed (retry ${event.retryCount + 1}/${MAX_RETRIES}): ${errorMsg}`);
|
|
2644
2808
|
} else {
|
|
2645
|
-
EventsModel.markFailed(event._id, errorMsg);
|
|
2809
|
+
await EventsModel.markFailed(event._id, errorMsg);
|
|
2646
2810
|
logger.error(`Event ${event._id} permanently failed after ${MAX_RETRIES} retries: ${errorMsg}`);
|
|
2647
2811
|
}
|
|
2648
2812
|
}
|
|
2649
|
-
recoverStaleEvents() {
|
|
2813
|
+
async recoverStaleEvents() {
|
|
2650
2814
|
const staleThreshold = Date.now() - STALE_THRESHOLD_MS;
|
|
2651
2815
|
return EventsModel.resetStaleEvents(staleThreshold);
|
|
2652
2816
|
}
|
|
@@ -2696,18 +2860,18 @@ var FileEventsWorker = class {
|
|
|
2696
2860
|
return this.activeProcessors.size;
|
|
2697
2861
|
}
|
|
2698
2862
|
};
|
|
2699
|
-
function createWorker(
|
|
2700
|
-
return new FileEventsWorker(
|
|
2863
|
+
function createWorker(concurrency = DEFAULT_CONCURRENCY) {
|
|
2864
|
+
return new FileEventsWorker(concurrency);
|
|
2701
2865
|
}
|
|
2702
2866
|
|
|
2703
2867
|
// src/appWorker.ts
|
|
2704
2868
|
var DEFAULT_CONCURRENCY2 = 5;
|
|
2705
2869
|
var worker = null;
|
|
2706
|
-
function startWorker(
|
|
2870
|
+
function startWorker(concurrency = DEFAULT_CONCURRENCY2) {
|
|
2707
2871
|
if (worker?.isActive()) {
|
|
2708
2872
|
return;
|
|
2709
2873
|
}
|
|
2710
|
-
worker = createWorker(
|
|
2874
|
+
worker = createWorker(concurrency);
|
|
2711
2875
|
worker.start();
|
|
2712
2876
|
}
|
|
2713
2877
|
async function closeWorker() {
|
|
@@ -2738,8 +2902,8 @@ CREATE TABLE IF NOT EXISTS files (
|
|
|
2738
2902
|
localVersion INTEGER NOT NULL DEFAULT 1,
|
|
2739
2903
|
onchainVersion INTEGER NOT NULL DEFAULT 0,
|
|
2740
2904
|
syncStatus TEXT NOT NULL DEFAULT 'pending',
|
|
2741
|
-
createdAt
|
|
2742
|
-
updatedAt
|
|
2905
|
+
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
2906
|
+
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
2743
2907
|
isDeleted INTEGER NOT NULL DEFAULT 0,
|
|
2744
2908
|
portalAddress TEXT NOT NULL,
|
|
2745
2909
|
metadata TEXT DEFAULT '{}',
|
|
@@ -2759,8 +2923,8 @@ CREATE TABLE IF NOT EXISTS portals (
|
|
|
2759
2923
|
portalAddress TEXT NOT NULL UNIQUE,
|
|
2760
2924
|
portalSeed TEXT NOT NULL UNIQUE,
|
|
2761
2925
|
ownerAddress TEXT NOT NULL,
|
|
2762
|
-
createdAt
|
|
2763
|
-
updatedAt
|
|
2926
|
+
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
2927
|
+
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
2764
2928
|
);
|
|
2765
2929
|
|
|
2766
2930
|
CREATE TABLE IF NOT EXISTS api_keys (
|
|
@@ -2769,7 +2933,7 @@ CREATE TABLE IF NOT EXISTS api_keys (
|
|
|
2769
2933
|
name TEXT NOT NULL,
|
|
2770
2934
|
collaboratorAddress TEXT NOT NULL UNIQUE,
|
|
2771
2935
|
portalAddress TEXT NOT NULL,
|
|
2772
|
-
createdAt
|
|
2936
|
+
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
2773
2937
|
isDeleted INTEGER NOT NULL DEFAULT 0
|
|
2774
2938
|
);
|
|
2775
2939
|
|
|
@@ -2805,32 +2969,39 @@ CREATE TABLE IF NOT EXISTS folders (
|
|
|
2805
2969
|
lastTransactionHash TEXT,
|
|
2806
2970
|
lastTransactionBlockNumber INTEGER NOT NULL,
|
|
2807
2971
|
lastTransactionBlockTimestamp INTEGER NOT NULL,
|
|
2808
|
-
created_at
|
|
2809
|
-
updated_at
|
|
2972
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
2973
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
2810
2974
|
);
|
|
2811
2975
|
CREATE INDEX IF NOT EXISTS idx_folders_folderRef_folderId ON folders(folderRef, folderId);
|
|
2812
2976
|
CREATE INDEX IF NOT EXISTS idx_folders_folderRef ON folders(folderRef);
|
|
2813
2977
|
CREATE INDEX IF NOT EXISTS idx_folders_created_at ON folders(created_at);
|
|
2814
2978
|
`;
|
|
2815
|
-
function runMigrations() {
|
|
2816
|
-
const
|
|
2817
|
-
|
|
2979
|
+
async function runMigrations() {
|
|
2980
|
+
const adapter2 = getAdapterSync();
|
|
2981
|
+
await adapter2.exec(STABLE_SCHEMA);
|
|
2818
2982
|
logger.debug("Database schema ready");
|
|
2819
2983
|
}
|
|
2820
2984
|
|
|
2821
2985
|
// src/worker.ts
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2986
|
+
async function main() {
|
|
2987
|
+
validateDbConfig();
|
|
2988
|
+
await getAdapter();
|
|
2989
|
+
await runMigrations();
|
|
2990
|
+
const concurrency = parseInt(process.env.WORKER_CONCURRENCY || "5", 10);
|
|
2991
|
+
startWorker(concurrency);
|
|
2992
|
+
setTimeout(() => {
|
|
2993
|
+
if (isWorkerActive()) {
|
|
2994
|
+
logger.info("File events worker started and active");
|
|
2995
|
+
return;
|
|
2996
|
+
}
|
|
2997
|
+
logger.error("Worker failed to start");
|
|
2998
|
+
process.exit(1);
|
|
2999
|
+
}, 100);
|
|
3000
|
+
}
|
|
3001
|
+
main().catch((error) => {
|
|
3002
|
+
logger.error("Failed to start worker:", error);
|
|
2832
3003
|
process.exit(1);
|
|
2833
|
-
}
|
|
3004
|
+
});
|
|
2834
3005
|
var shutdown = async () => {
|
|
2835
3006
|
logger.info("Shutting down worker gracefully...");
|
|
2836
3007
|
await closeWorker();
|