@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/index.js
CHANGED
|
@@ -57,6 +57,9 @@ function getRuntimeConfig() {
|
|
|
57
57
|
get DB_PATH() {
|
|
58
58
|
return process.env.DB_PATH;
|
|
59
59
|
},
|
|
60
|
+
get DATABASE_URL() {
|
|
61
|
+
return process.env.DATABASE_URL;
|
|
62
|
+
},
|
|
60
63
|
get PORT() {
|
|
61
64
|
return process.env.PORT || STATIC_CONFIG.DEFAULT_PORT;
|
|
62
65
|
},
|
|
@@ -68,16 +71,19 @@ function getRuntimeConfig() {
|
|
|
68
71
|
}
|
|
69
72
|
};
|
|
70
73
|
}
|
|
71
|
-
function
|
|
74
|
+
function validateDbConfig() {
|
|
72
75
|
const dbPath = process.env.DB_PATH;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
console.error("
|
|
76
|
+
const databaseUrl = process.env.DATABASE_URL;
|
|
77
|
+
if (!dbPath && !databaseUrl) {
|
|
78
|
+
console.error("Error: Either DB_PATH or DATABASE_URL environment variable is required");
|
|
79
|
+
console.error("Please set DB_PATH or DATABASE_URL in your .env file (config/.env or ~/.fileverse/.env) or run the CLI first");
|
|
76
80
|
process.exit(1);
|
|
77
81
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
fs.
|
|
82
|
+
if (dbPath && !databaseUrl) {
|
|
83
|
+
const dbDir = path2.dirname(dbPath.trim());
|
|
84
|
+
if (!fs.existsSync(dbDir)) {
|
|
85
|
+
fs.mkdirSync(dbDir, { recursive: true });
|
|
86
|
+
}
|
|
81
87
|
}
|
|
82
88
|
}
|
|
83
89
|
var config = {
|
|
@@ -106,6 +112,9 @@ var config = {
|
|
|
106
112
|
get DB_PATH() {
|
|
107
113
|
return process.env.DB_PATH;
|
|
108
114
|
},
|
|
115
|
+
get DATABASE_URL() {
|
|
116
|
+
return process.env.DATABASE_URL;
|
|
117
|
+
},
|
|
109
118
|
get PORT() {
|
|
110
119
|
return process.env.PORT || STATIC_CONFIG.DEFAULT_PORT;
|
|
111
120
|
},
|
|
@@ -198,71 +207,237 @@ var asyncHandlerArray = (resolvers) => {
|
|
|
198
207
|
return resolvers.map(asyncHandler);
|
|
199
208
|
};
|
|
200
209
|
|
|
201
|
-
// src/infra/database/
|
|
210
|
+
// src/infra/database/adapters/sqlite-adapter.ts
|
|
202
211
|
import Database from "better-sqlite3";
|
|
203
|
-
var
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
212
|
+
var SqliteAdapter = class {
|
|
213
|
+
db;
|
|
214
|
+
constructor(dbPath) {
|
|
215
|
+
this.db = new Database(dbPath, {
|
|
216
|
+
verbose: config.NODE_ENV === "development" ? (msg) => logger.debug(String(msg)) : void 0
|
|
217
|
+
});
|
|
218
|
+
this.db.pragma("journal_mode = WAL");
|
|
219
|
+
this.db.pragma("foreign_keys = ON");
|
|
220
|
+
this.db.prepare("SELECT 1").get();
|
|
221
|
+
logger.info(`SQLite database connected: ${dbPath}`);
|
|
207
222
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
223
|
+
async select(sql, params = []) {
|
|
224
|
+
const stmt = this.db.prepare(sql);
|
|
225
|
+
return stmt.all(params);
|
|
226
|
+
}
|
|
227
|
+
async selectOne(sql, params = []) {
|
|
228
|
+
const stmt = this.db.prepare(sql);
|
|
229
|
+
return stmt.get(params);
|
|
230
|
+
}
|
|
231
|
+
async execute(sql, params = []) {
|
|
232
|
+
const stmt = this.db.prepare(sql);
|
|
233
|
+
const result = stmt.run(params);
|
|
234
|
+
return {
|
|
235
|
+
changes: result.changes,
|
|
236
|
+
lastInsertRowid: result.lastInsertRowid
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
async transaction(callback) {
|
|
240
|
+
this.db.exec("SAVEPOINT txn");
|
|
241
|
+
try {
|
|
242
|
+
const result = await callback(this);
|
|
243
|
+
this.db.exec("RELEASE txn");
|
|
244
|
+
return result;
|
|
245
|
+
} catch (err) {
|
|
246
|
+
this.db.exec("ROLLBACK TO txn");
|
|
247
|
+
throw err;
|
|
211
248
|
}
|
|
212
|
-
return _DatabaseConnectionManager.instance;
|
|
213
249
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
250
|
+
async exec(sql) {
|
|
251
|
+
this.db.exec(sql);
|
|
252
|
+
}
|
|
253
|
+
async close() {
|
|
254
|
+
this.db.close();
|
|
255
|
+
logger.info("Database connection closed");
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// src/infra/database/adapters/pg-adapter.ts
|
|
260
|
+
import pg from "pg";
|
|
261
|
+
var { Pool } = pg;
|
|
262
|
+
var COLUMN_NAME_MAP = {
|
|
263
|
+
ddocid: "ddocId",
|
|
264
|
+
localversion: "localVersion",
|
|
265
|
+
onchainversion: "onchainVersion",
|
|
266
|
+
syncstatus: "syncStatus",
|
|
267
|
+
isdeleted: "isDeleted",
|
|
268
|
+
onchainfileid: "onChainFileId",
|
|
269
|
+
portaladdress: "portalAddress",
|
|
270
|
+
createdat: "createdAt",
|
|
271
|
+
updatedat: "updatedAt",
|
|
272
|
+
linkkey: "linkKey",
|
|
273
|
+
linkkeynonce: "linkKeyNonce",
|
|
274
|
+
commentkey: "commentKey",
|
|
275
|
+
portalseed: "portalSeed",
|
|
276
|
+
owneraddress: "ownerAddress",
|
|
277
|
+
apikeyseed: "apiKeySeed",
|
|
278
|
+
collaboratoraddress: "collaboratorAddress",
|
|
279
|
+
fileid: "fileId",
|
|
280
|
+
retrycount: "retryCount",
|
|
281
|
+
lasterror: "lastError",
|
|
282
|
+
lockedat: "lockedAt",
|
|
283
|
+
nextretryat: "nextRetryAt",
|
|
284
|
+
userophash: "userOpHash",
|
|
285
|
+
pendingpayload: "pendingPayload",
|
|
286
|
+
folderid: "folderId",
|
|
287
|
+
folderref: "folderRef",
|
|
288
|
+
foldername: "folderName",
|
|
289
|
+
metadataipfshash: "metadataIPFSHash",
|
|
290
|
+
contentipfshash: "contentIPFSHash",
|
|
291
|
+
lasttransactionhash: "lastTransactionHash",
|
|
292
|
+
lasttransactionblocknumber: "lastTransactionBlockNumber",
|
|
293
|
+
lasttransactionblocktimestamp: "lastTransactionBlockTimestamp",
|
|
294
|
+
created_at: "created_at",
|
|
295
|
+
updated_at: "updated_at"
|
|
296
|
+
};
|
|
297
|
+
function translateParams(sql) {
|
|
298
|
+
let idx = 0;
|
|
299
|
+
return sql.replace(/\?/g, () => `$${++idx}`);
|
|
300
|
+
}
|
|
301
|
+
function mapRow(row) {
|
|
302
|
+
const mapped = {};
|
|
303
|
+
for (const [key, value] of Object.entries(row)) {
|
|
304
|
+
const mappedKey = COLUMN_NAME_MAP[key] ?? key;
|
|
305
|
+
mapped[mappedKey] = value instanceof Date ? value.toISOString() : value;
|
|
306
|
+
}
|
|
307
|
+
return mapped;
|
|
308
|
+
}
|
|
309
|
+
var PgClientAdapter = class {
|
|
310
|
+
constructor(client) {
|
|
311
|
+
this.client = client;
|
|
312
|
+
}
|
|
313
|
+
async select(sql, params = []) {
|
|
314
|
+
const result = await this.client.query(translateParams(sql), params);
|
|
315
|
+
return result.rows.map((row) => mapRow(row));
|
|
316
|
+
}
|
|
317
|
+
async selectOne(sql, params = []) {
|
|
318
|
+
const result = await this.client.query(translateParams(sql), params);
|
|
319
|
+
return result.rows[0] ? mapRow(result.rows[0]) : void 0;
|
|
320
|
+
}
|
|
321
|
+
async execute(sql, params = []) {
|
|
322
|
+
const result = await this.client.query(translateParams(sql), params);
|
|
323
|
+
return { changes: result.rowCount ?? 0, lastInsertRowid: 0 };
|
|
324
|
+
}
|
|
325
|
+
async transaction(callback) {
|
|
326
|
+
await this.client.query("SAVEPOINT nested_txn");
|
|
327
|
+
try {
|
|
328
|
+
const result = await callback(this);
|
|
329
|
+
await this.client.query("RELEASE SAVEPOINT nested_txn");
|
|
330
|
+
return result;
|
|
331
|
+
} catch (err) {
|
|
332
|
+
await this.client.query("ROLLBACK TO SAVEPOINT nested_txn");
|
|
333
|
+
throw err;
|
|
224
334
|
}
|
|
225
|
-
|
|
335
|
+
}
|
|
336
|
+
async exec(sql) {
|
|
337
|
+
await this.client.query(sql);
|
|
226
338
|
}
|
|
227
339
|
async close() {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
var PgAdapter = class {
|
|
343
|
+
pool;
|
|
344
|
+
constructor(connectionString) {
|
|
345
|
+
const url2 = new URL(connectionString);
|
|
346
|
+
const isLocal = url2.hostname === "localhost" || url2.hostname === "127.0.0.1" || url2.hostname === "::1";
|
|
347
|
+
const sslDisabled = connectionString.includes("sslmode=disable");
|
|
348
|
+
const needsSsl = !isLocal && !sslDisabled;
|
|
349
|
+
this.pool = new Pool({
|
|
350
|
+
connectionString,
|
|
351
|
+
// pg requires password to be a string; local trust/peer auth uses empty string
|
|
352
|
+
password: url2.password || "",
|
|
353
|
+
max: 20,
|
|
354
|
+
idleTimeoutMillis: 3e4,
|
|
355
|
+
ssl: needsSsl ? { rejectUnauthorized: false } : void 0
|
|
356
|
+
});
|
|
357
|
+
logger.info(`PostgreSQL pool created (ssl: ${needsSsl ? "on" : "off"})`);
|
|
358
|
+
}
|
|
359
|
+
async select(sql, params = []) {
|
|
360
|
+
const result = await this.pool.query(translateParams(sql), params);
|
|
361
|
+
return result.rows.map((row) => mapRow(row));
|
|
362
|
+
}
|
|
363
|
+
async selectOne(sql, params = []) {
|
|
364
|
+
const result = await this.pool.query(translateParams(sql), params);
|
|
365
|
+
return result.rows[0] ? mapRow(result.rows[0]) : void 0;
|
|
366
|
+
}
|
|
367
|
+
async execute(sql, params = []) {
|
|
368
|
+
const result = await this.pool.query(translateParams(sql), params);
|
|
369
|
+
return { changes: result.rowCount ?? 0, lastInsertRowid: 0 };
|
|
370
|
+
}
|
|
371
|
+
async transaction(callback) {
|
|
372
|
+
const client = await this.pool.connect();
|
|
373
|
+
try {
|
|
374
|
+
await client.query("BEGIN");
|
|
375
|
+
const clientAdapter = new PgClientAdapter(client);
|
|
376
|
+
const result = await callback(clientAdapter);
|
|
377
|
+
await client.query("COMMIT");
|
|
378
|
+
return result;
|
|
379
|
+
} catch (err) {
|
|
380
|
+
await client.query("ROLLBACK");
|
|
381
|
+
throw err;
|
|
382
|
+
} finally {
|
|
383
|
+
client.release();
|
|
232
384
|
}
|
|
233
385
|
}
|
|
234
|
-
|
|
235
|
-
|
|
386
|
+
async exec(sql) {
|
|
387
|
+
await this.pool.query(sql);
|
|
388
|
+
}
|
|
389
|
+
async close() {
|
|
390
|
+
await this.pool.end();
|
|
391
|
+
logger.info("PostgreSQL pool closed");
|
|
236
392
|
}
|
|
237
393
|
};
|
|
238
|
-
|
|
394
|
+
|
|
395
|
+
// src/infra/database/connection.ts
|
|
396
|
+
var adapter = null;
|
|
397
|
+
async function getAdapter() {
|
|
398
|
+
if (adapter) return adapter;
|
|
399
|
+
const databaseUrl = config.DATABASE_URL;
|
|
400
|
+
const dbPath = config.DB_PATH;
|
|
401
|
+
if (databaseUrl) {
|
|
402
|
+
adapter = new PgAdapter(databaseUrl);
|
|
403
|
+
} else if (dbPath) {
|
|
404
|
+
adapter = new SqliteAdapter(dbPath);
|
|
405
|
+
} else {
|
|
406
|
+
throw new Error("Either DATABASE_URL or DB_PATH must be set");
|
|
407
|
+
}
|
|
408
|
+
return adapter;
|
|
409
|
+
}
|
|
410
|
+
function getAdapterSync() {
|
|
411
|
+
if (!adapter) {
|
|
412
|
+
throw new Error(
|
|
413
|
+
"Database adapter not initialized. Call getAdapter() at startup first."
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
return adapter;
|
|
417
|
+
}
|
|
418
|
+
async function closeAdapter() {
|
|
419
|
+
if (adapter) {
|
|
420
|
+
await adapter.close();
|
|
421
|
+
adapter = null;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
239
424
|
|
|
240
425
|
// src/domain/file/constants.ts
|
|
241
426
|
var DEFAULT_LIST_LIMIT = 10;
|
|
242
427
|
|
|
243
428
|
// src/infra/database/query-builder.ts
|
|
244
|
-
function getDb() {
|
|
245
|
-
return databaseConnectionManager.getConnection();
|
|
246
|
-
}
|
|
247
429
|
var QueryBuilder = class {
|
|
248
|
-
static select(sql, params = []) {
|
|
249
|
-
|
|
250
|
-
return stmt.all(params);
|
|
430
|
+
static async select(sql, params = []) {
|
|
431
|
+
return getAdapterSync().select(sql, params);
|
|
251
432
|
}
|
|
252
|
-
static selectOne(sql, params = []) {
|
|
253
|
-
|
|
254
|
-
return stmt.get(params);
|
|
433
|
+
static async selectOne(sql, params = []) {
|
|
434
|
+
return getAdapterSync().selectOne(sql, params);
|
|
255
435
|
}
|
|
256
|
-
static execute(sql, params = []) {
|
|
257
|
-
|
|
258
|
-
const result = stmt.run(params);
|
|
259
|
-
return {
|
|
260
|
-
changes: result.changes,
|
|
261
|
-
lastInsertRowid: result.lastInsertRowid
|
|
262
|
-
};
|
|
436
|
+
static async execute(sql, params = []) {
|
|
437
|
+
return getAdapterSync().execute(sql, params);
|
|
263
438
|
}
|
|
264
|
-
static transaction(callback) {
|
|
265
|
-
return
|
|
439
|
+
static async transaction(callback) {
|
|
440
|
+
return getAdapterSync().transaction(callback);
|
|
266
441
|
}
|
|
267
442
|
static paginate(sql, options = {}) {
|
|
268
443
|
let query = sql;
|
|
@@ -281,15 +456,6 @@ var QueryBuilder = class {
|
|
|
281
456
|
}
|
|
282
457
|
};
|
|
283
458
|
|
|
284
|
-
// src/infra/database/index.ts
|
|
285
|
-
function getDb2() {
|
|
286
|
-
return databaseConnectionManager.getConnection();
|
|
287
|
-
}
|
|
288
|
-
var closeDatabase = async () => {
|
|
289
|
-
await databaseConnectionManager.close();
|
|
290
|
-
};
|
|
291
|
-
var database_default = getDb2;
|
|
292
|
-
|
|
293
459
|
// src/infra/database/models/files.model.ts
|
|
294
460
|
import { uuidv7 } from "uuidv7";
|
|
295
461
|
var FilesModel = class {
|
|
@@ -323,15 +489,15 @@ var FilesModel = class {
|
|
|
323
489
|
link: fileRaw.link
|
|
324
490
|
};
|
|
325
491
|
}
|
|
326
|
-
static findAll(portalAddress, limit, skip) {
|
|
492
|
+
static async findAll(portalAddress, limit, skip) {
|
|
327
493
|
const whereClause = "isDeleted = 0 AND portalAddress = ?";
|
|
328
494
|
const params = [portalAddress];
|
|
329
495
|
const countSql = `
|
|
330
|
-
SELECT COUNT(*) as count
|
|
331
|
-
FROM ${this.TABLE}
|
|
496
|
+
SELECT COUNT(*) as count
|
|
497
|
+
FROM ${this.TABLE}
|
|
332
498
|
WHERE ${whereClause}
|
|
333
499
|
`;
|
|
334
|
-
const totalResult = QueryBuilder.selectOne(countSql, params);
|
|
500
|
+
const totalResult = await QueryBuilder.selectOne(countSql, params);
|
|
335
501
|
const total = totalResult?.count || 0;
|
|
336
502
|
const sql = `
|
|
337
503
|
SELECT *
|
|
@@ -344,51 +510,51 @@ var FilesModel = class {
|
|
|
344
510
|
orderBy: "createdAt",
|
|
345
511
|
orderDirection: "DESC"
|
|
346
512
|
});
|
|
347
|
-
const filesRaw = QueryBuilder.select(completeSql, params);
|
|
513
|
+
const filesRaw = await QueryBuilder.select(completeSql, params);
|
|
348
514
|
const files = filesRaw.map(this.parseFile);
|
|
349
515
|
const hasNext = skip !== void 0 && limit !== void 0 ? skip + limit < total : false;
|
|
350
516
|
return { files, total, hasNext };
|
|
351
517
|
}
|
|
352
|
-
static findById(_id, portalAddress) {
|
|
518
|
+
static async findById(_id, portalAddress) {
|
|
353
519
|
const sql = `
|
|
354
520
|
SELECT *
|
|
355
|
-
FROM ${this.TABLE}
|
|
521
|
+
FROM ${this.TABLE}
|
|
356
522
|
WHERE _id = ? AND isDeleted = 0 AND portalAddress = ?
|
|
357
523
|
`;
|
|
358
|
-
const result = QueryBuilder.selectOne(sql, [_id, portalAddress]);
|
|
524
|
+
const result = await QueryBuilder.selectOne(sql, [_id, portalAddress]);
|
|
359
525
|
return result ? this.parseFile(result) : void 0;
|
|
360
526
|
}
|
|
361
|
-
static findByIdIncludingDeleted(_id) {
|
|
527
|
+
static async findByIdIncludingDeleted(_id) {
|
|
362
528
|
const sql = `
|
|
363
529
|
SELECT *
|
|
364
|
-
FROM ${this.TABLE}
|
|
530
|
+
FROM ${this.TABLE}
|
|
365
531
|
WHERE _id = ?
|
|
366
532
|
`;
|
|
367
|
-
const result = QueryBuilder.selectOne(sql, [_id]);
|
|
533
|
+
const result = await QueryBuilder.selectOne(sql, [_id]);
|
|
368
534
|
return result ? this.parseFile(result) : void 0;
|
|
369
535
|
}
|
|
370
|
-
static findByIdExcludingDeleted(_id) {
|
|
536
|
+
static async findByIdExcludingDeleted(_id) {
|
|
371
537
|
const sql = `
|
|
372
538
|
SELECT *
|
|
373
|
-
FROM ${this.TABLE}
|
|
539
|
+
FROM ${this.TABLE}
|
|
374
540
|
WHERE _id = ? AND isDeleted = 0
|
|
375
541
|
`;
|
|
376
|
-
const result = QueryBuilder.selectOne(sql, [_id]);
|
|
542
|
+
const result = await QueryBuilder.selectOne(sql, [_id]);
|
|
377
543
|
return result ? this.parseFile(result) : void 0;
|
|
378
544
|
}
|
|
379
|
-
static findByDDocId(ddocId, portalAddress) {
|
|
545
|
+
static async findByDDocId(ddocId, portalAddress) {
|
|
380
546
|
const sql = `
|
|
381
547
|
SELECT *
|
|
382
|
-
FROM ${this.TABLE}
|
|
548
|
+
FROM ${this.TABLE}
|
|
383
549
|
WHERE ddocId = ? AND isDeleted = 0 AND portalAddress = ?
|
|
384
550
|
`;
|
|
385
|
-
const result = QueryBuilder.selectOne(sql, [ddocId, portalAddress]);
|
|
551
|
+
const result = await QueryBuilder.selectOne(sql, [ddocId, portalAddress]);
|
|
386
552
|
return result ? this.parseFile(result) : void 0;
|
|
387
553
|
}
|
|
388
|
-
static searchByTitle(searchTerm, portalAddress, limit, skip) {
|
|
554
|
+
static async searchByTitle(searchTerm, portalAddress, limit, skip) {
|
|
389
555
|
const sql = `
|
|
390
556
|
SELECT *
|
|
391
|
-
FROM ${this.TABLE}
|
|
557
|
+
FROM ${this.TABLE}
|
|
392
558
|
WHERE LOWER(title) LIKE LOWER(?) AND isDeleted = 0 AND portalAddress = ?
|
|
393
559
|
`;
|
|
394
560
|
const completeSql = QueryBuilder.paginate(sql, {
|
|
@@ -397,24 +563,24 @@ var FilesModel = class {
|
|
|
397
563
|
orderBy: "createdAt",
|
|
398
564
|
orderDirection: "DESC"
|
|
399
565
|
});
|
|
400
|
-
const filesRaw = QueryBuilder.select(completeSql, [`%${searchTerm}%`, portalAddress]);
|
|
566
|
+
const filesRaw = await QueryBuilder.select(completeSql, [`%${searchTerm}%`, portalAddress]);
|
|
401
567
|
return filesRaw.map(this.parseFile);
|
|
402
568
|
}
|
|
403
|
-
static create(input) {
|
|
569
|
+
static async create(input) {
|
|
404
570
|
const _id = uuidv7();
|
|
405
571
|
const sql = `
|
|
406
|
-
INSERT INTO ${this.TABLE}
|
|
407
|
-
(_id, title, content, ddocId, portalAddress)
|
|
572
|
+
INSERT INTO ${this.TABLE}
|
|
573
|
+
(_id, title, content, ddocId, portalAddress)
|
|
408
574
|
VALUES (?, ?, ?, ?, ?)
|
|
409
575
|
`;
|
|
410
|
-
QueryBuilder.execute(sql, [_id, input.title, input.content, input.ddocId, input.portalAddress]);
|
|
411
|
-
const created = this.findById(_id, input.portalAddress);
|
|
576
|
+
await QueryBuilder.execute(sql, [_id, input.title, input.content, input.ddocId, input.portalAddress]);
|
|
577
|
+
const created = await this.findById(_id, input.portalAddress);
|
|
412
578
|
if (!created) {
|
|
413
579
|
throw new Error("Failed to create file");
|
|
414
580
|
}
|
|
415
581
|
return created;
|
|
416
582
|
}
|
|
417
|
-
static update(_id, payload, portalAddress) {
|
|
583
|
+
static async update(_id, payload, portalAddress) {
|
|
418
584
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
419
585
|
const keys = [];
|
|
420
586
|
const values = [];
|
|
@@ -433,22 +599,22 @@ var FilesModel = class {
|
|
|
433
599
|
values.push(now, _id, portalAddress);
|
|
434
600
|
const updateChain = keys.join(", ");
|
|
435
601
|
const sql = `UPDATE ${this.TABLE} SET ${updateChain} WHERE _id = ? AND portalAddress = ?`;
|
|
436
|
-
QueryBuilder.execute(sql, values);
|
|
437
|
-
const updated = this.findById(_id, portalAddress);
|
|
602
|
+
await QueryBuilder.execute(sql, values);
|
|
603
|
+
const updated = await this.findById(_id, portalAddress);
|
|
438
604
|
if (!updated) {
|
|
439
605
|
throw new Error("Failed to update file");
|
|
440
606
|
}
|
|
441
607
|
return updated;
|
|
442
608
|
}
|
|
443
|
-
static softDelete(_id) {
|
|
609
|
+
static async softDelete(_id) {
|
|
444
610
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
445
611
|
const sql = `
|
|
446
|
-
UPDATE ${this.TABLE}
|
|
612
|
+
UPDATE ${this.TABLE}
|
|
447
613
|
SET isDeleted = 1, syncStatus = 'pending', updatedAt = ?
|
|
448
614
|
WHERE _id = ?
|
|
449
615
|
`;
|
|
450
|
-
QueryBuilder.execute(sql, [now, _id]);
|
|
451
|
-
const deleted = this.findByIdIncludingDeleted(_id);
|
|
616
|
+
await QueryBuilder.execute(sql, [now, _id]);
|
|
617
|
+
const deleted = await this.findByIdIncludingDeleted(_id);
|
|
452
618
|
if (!deleted) {
|
|
453
619
|
throw new Error("Failed to delete file");
|
|
454
620
|
}
|
|
@@ -460,22 +626,22 @@ var FilesModel = class {
|
|
|
460
626
|
import { uuidv7 as uuidv72 } from "uuidv7";
|
|
461
627
|
var PortalsModel = class {
|
|
462
628
|
static TABLE = "portals";
|
|
463
|
-
static findByPortalAddress(portalAddress) {
|
|
629
|
+
static async findByPortalAddress(portalAddress) {
|
|
464
630
|
const sql = `SELECT _id, portalAddress, portalSeed, ownerAddress, createdAt, updatedAt FROM ${this.TABLE} WHERE portalAddress = ?`;
|
|
465
631
|
return QueryBuilder.selectOne(sql, [portalAddress]);
|
|
466
632
|
}
|
|
467
|
-
static create(input) {
|
|
633
|
+
static async create(input) {
|
|
468
634
|
const _id = uuidv72();
|
|
469
635
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
470
636
|
const sql = `INSERT INTO ${this.TABLE} (_id, portalAddress, portalSeed, ownerAddress, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?)`;
|
|
471
|
-
QueryBuilder.execute(sql, [_id, input.portalAddress, input.portalSeed, input.ownerAddress, now, now]);
|
|
472
|
-
const created = this.findByPortalAddress(input.portalAddress);
|
|
637
|
+
await QueryBuilder.execute(sql, [_id, input.portalAddress, input.portalSeed, input.ownerAddress, now, now]);
|
|
638
|
+
const created = await this.findByPortalAddress(input.portalAddress);
|
|
473
639
|
if (!created) {
|
|
474
640
|
throw new Error("Failed to create portal");
|
|
475
641
|
}
|
|
476
642
|
return created;
|
|
477
643
|
}
|
|
478
|
-
static update(portalAddress, input) {
|
|
644
|
+
static async update(portalAddress, input) {
|
|
479
645
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
480
646
|
const keys = [];
|
|
481
647
|
const values = [];
|
|
@@ -490,15 +656,15 @@ var PortalsModel = class {
|
|
|
490
656
|
const updateChain = keys.join(", ");
|
|
491
657
|
const sql = `UPDATE ${this.TABLE} SET ${updateChain} WHERE portalAddress = ?`;
|
|
492
658
|
values.push(portalAddress);
|
|
493
|
-
QueryBuilder.execute(sql, values);
|
|
494
|
-
const updated = this.findByPortalAddress(portalAddress);
|
|
659
|
+
await QueryBuilder.execute(sql, values);
|
|
660
|
+
const updated = await this.findByPortalAddress(portalAddress);
|
|
495
661
|
if (!updated) {
|
|
496
662
|
throw new Error("Failed to update portal");
|
|
497
663
|
}
|
|
498
664
|
return updated;
|
|
499
665
|
}
|
|
500
|
-
static upsert(input) {
|
|
501
|
-
const existing = this.findByPortalAddress(input.portalAddress);
|
|
666
|
+
static async upsert(input) {
|
|
667
|
+
const existing = await this.findByPortalAddress(input.portalAddress);
|
|
502
668
|
if (existing) {
|
|
503
669
|
return this.update(input.portalAddress, {
|
|
504
670
|
portalSeed: input.portalSeed,
|
|
@@ -513,12 +679,12 @@ var PortalsModel = class {
|
|
|
513
679
|
import { uuidv7 as uuidv73 } from "uuidv7";
|
|
514
680
|
var ApiKeysModel = class {
|
|
515
681
|
static TABLE = "api_keys";
|
|
516
|
-
static create(input) {
|
|
682
|
+
static async create(input) {
|
|
517
683
|
const _id = uuidv73();
|
|
518
684
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
519
|
-
const sql = `INSERT INTO ${this.TABLE} (_id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt)
|
|
685
|
+
const sql = `INSERT INTO ${this.TABLE} (_id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt)
|
|
520
686
|
VALUES (?, ?, ?, ?, ?, ?)`;
|
|
521
|
-
const result = QueryBuilder.execute(sql, [
|
|
687
|
+
const result = await QueryBuilder.execute(sql, [
|
|
522
688
|
_id,
|
|
523
689
|
input.apiKeySeed,
|
|
524
690
|
input.name,
|
|
@@ -529,29 +695,29 @@ var ApiKeysModel = class {
|
|
|
529
695
|
if (result.changes === 0) {
|
|
530
696
|
throw new Error("Failed to create API key");
|
|
531
697
|
}
|
|
532
|
-
const created = this.findById(_id);
|
|
698
|
+
const created = await this.findById(_id);
|
|
533
699
|
if (!created) {
|
|
534
700
|
throw new Error("Failed to create API key");
|
|
535
701
|
}
|
|
536
702
|
return created;
|
|
537
703
|
}
|
|
538
|
-
static findById(_id) {
|
|
704
|
+
static async findById(_id) {
|
|
539
705
|
const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE _id = ? AND isDeleted = 0`;
|
|
540
706
|
return QueryBuilder.selectOne(sql, [_id]);
|
|
541
707
|
}
|
|
542
|
-
static findByCollaboratorAddress(collaboratorAddress) {
|
|
708
|
+
static async findByCollaboratorAddress(collaboratorAddress) {
|
|
543
709
|
const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE collaboratorAddress = ? AND isDeleted = 0 LIMIT 1`;
|
|
544
710
|
return QueryBuilder.selectOne(sql, [collaboratorAddress]);
|
|
545
711
|
}
|
|
546
|
-
static delete(_id) {
|
|
712
|
+
static async delete(_id) {
|
|
547
713
|
const sql = `UPDATE ${this.TABLE} SET isDeleted = 1 WHERE _id = ?`;
|
|
548
|
-
QueryBuilder.execute(sql, [_id]);
|
|
714
|
+
await QueryBuilder.execute(sql, [_id]);
|
|
549
715
|
}
|
|
550
|
-
static findByPortalAddress(portalAddress) {
|
|
716
|
+
static async findByPortalAddress(portalAddress) {
|
|
551
717
|
const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE portalAddress = ? AND isDeleted = 0`;
|
|
552
718
|
return QueryBuilder.selectOne(sql, [portalAddress]);
|
|
553
719
|
}
|
|
554
|
-
static findByApiKey(apiKey) {
|
|
720
|
+
static async findByApiKey(apiKey) {
|
|
555
721
|
const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE apiKeySeed = ? AND isDeleted = 0`;
|
|
556
722
|
return QueryBuilder.selectOne(sql, [apiKey]);
|
|
557
723
|
}
|
|
@@ -563,9 +729,9 @@ var FoldersModel = class {
|
|
|
563
729
|
/**
|
|
564
730
|
* List all folders with pagination
|
|
565
731
|
*/
|
|
566
|
-
static findAll(limit, skip) {
|
|
732
|
+
static async findAll(limit, skip) {
|
|
567
733
|
const countSql = `SELECT COUNT(*) as count FROM ${this.TABLE} WHERE isDeleted = 0`;
|
|
568
|
-
const totalResult = QueryBuilder.selectOne(countSql);
|
|
734
|
+
const totalResult = await QueryBuilder.selectOne(countSql);
|
|
569
735
|
const total = totalResult?.count || 0;
|
|
570
736
|
const sql = QueryBuilder.paginate(`SELECT * FROM ${this.TABLE} WHERE isDeleted = 0`, {
|
|
571
737
|
limit,
|
|
@@ -573,7 +739,8 @@ var FoldersModel = class {
|
|
|
573
739
|
orderBy: "created_at",
|
|
574
740
|
orderDirection: "DESC"
|
|
575
741
|
});
|
|
576
|
-
const
|
|
742
|
+
const foldersRaw = await QueryBuilder.select(sql);
|
|
743
|
+
const folders = foldersRaw.map((folderRaw) => ({
|
|
577
744
|
...folderRaw,
|
|
578
745
|
isDeleted: Boolean(folderRaw.isDeleted)
|
|
579
746
|
}));
|
|
@@ -584,9 +751,9 @@ var FoldersModel = class {
|
|
|
584
751
|
* Get a single folder by folderRef and folderId
|
|
585
752
|
* Includes ddocs array (as per API spec)
|
|
586
753
|
*/
|
|
587
|
-
static findByFolderRefAndId(folderRef, folderId) {
|
|
754
|
+
static async findByFolderRefAndId(folderRef, folderId) {
|
|
588
755
|
const sql = `SELECT * FROM ${this.TABLE} WHERE folderRef = ? AND folderId = ? AND isDeleted = 0`;
|
|
589
|
-
const folderRaw = QueryBuilder.selectOne(sql, [folderRef, folderId]);
|
|
756
|
+
const folderRaw = await QueryBuilder.selectOne(sql, [folderRef, folderId]);
|
|
590
757
|
if (!folderRaw) {
|
|
591
758
|
return void 0;
|
|
592
759
|
}
|
|
@@ -603,9 +770,9 @@ var FoldersModel = class {
|
|
|
603
770
|
/**
|
|
604
771
|
* Get folder by folderRef only
|
|
605
772
|
*/
|
|
606
|
-
static findByFolderRef(folderRef) {
|
|
773
|
+
static async findByFolderRef(folderRef) {
|
|
607
774
|
const sql = `SELECT * FROM ${this.TABLE} WHERE folderRef = ? AND isDeleted = 0 LIMIT 1`;
|
|
608
|
-
const folderRaw = QueryBuilder.selectOne(sql, [folderRef]);
|
|
775
|
+
const folderRaw = await QueryBuilder.selectOne(sql, [folderRef]);
|
|
609
776
|
if (!folderRaw) {
|
|
610
777
|
return void 0;
|
|
611
778
|
}
|
|
@@ -617,9 +784,9 @@ var FoldersModel = class {
|
|
|
617
784
|
/**
|
|
618
785
|
* Search folders by folderName (case-insensitive substring match)
|
|
619
786
|
*/
|
|
620
|
-
static searchByName(searchTerm, limit, skip) {
|
|
787
|
+
static async searchByName(searchTerm, limit, skip) {
|
|
621
788
|
const sql = QueryBuilder.paginate(
|
|
622
|
-
`SELECT * FROM ${this.TABLE}
|
|
789
|
+
`SELECT * FROM ${this.TABLE}
|
|
623
790
|
WHERE isDeleted = 0 AND LOWER(folderName) LIKE LOWER(?)`,
|
|
624
791
|
{
|
|
625
792
|
limit,
|
|
@@ -628,7 +795,7 @@ var FoldersModel = class {
|
|
|
628
795
|
orderDirection: "DESC"
|
|
629
796
|
}
|
|
630
797
|
);
|
|
631
|
-
const foldersRaw = QueryBuilder.select(sql, [`%${searchTerm}%`]);
|
|
798
|
+
const foldersRaw = await QueryBuilder.select(sql, [`%${searchTerm}%`]);
|
|
632
799
|
return foldersRaw.map((folderRaw) => ({
|
|
633
800
|
...folderRaw,
|
|
634
801
|
isDeleted: Boolean(folderRaw.isDeleted)
|
|
@@ -637,15 +804,15 @@ var FoldersModel = class {
|
|
|
637
804
|
/**
|
|
638
805
|
* Create a new folder
|
|
639
806
|
*/
|
|
640
|
-
static create(input) {
|
|
807
|
+
static async create(input) {
|
|
641
808
|
const _id = input._id || `folder_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
642
809
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
643
810
|
const sql = `INSERT INTO ${this.TABLE} (
|
|
644
811
|
_id, onchainFileId, folderId, folderRef, folderName, portalAddress, metadataIPFSHash,
|
|
645
|
-
contentIPFSHash, isDeleted, lastTransactionHash, lastTransactionBlockNumber,
|
|
812
|
+
contentIPFSHash, isDeleted, lastTransactionHash, lastTransactionBlockNumber,
|
|
646
813
|
lastTransactionBlockTimestamp, created_at, updated_at
|
|
647
814
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
|
648
|
-
QueryBuilder.execute(sql, [
|
|
815
|
+
await QueryBuilder.execute(sql, [
|
|
649
816
|
_id,
|
|
650
817
|
input.onchainFileId,
|
|
651
818
|
input.folderId,
|
|
@@ -663,7 +830,7 @@ var FoldersModel = class {
|
|
|
663
830
|
now
|
|
664
831
|
]);
|
|
665
832
|
const selectSql = `SELECT * FROM ${this.TABLE} WHERE folderRef = ? AND folderId = ? AND isDeleted = 0`;
|
|
666
|
-
const folderRaw = QueryBuilder.selectOne(selectSql, [input.folderRef, input.folderId]);
|
|
833
|
+
const folderRaw = await QueryBuilder.selectOne(selectSql, [input.folderRef, input.folderId]);
|
|
667
834
|
if (!folderRaw) {
|
|
668
835
|
throw new Error("Failed to create folder");
|
|
669
836
|
}
|
|
@@ -695,16 +862,16 @@ function onNewEvent(callback) {
|
|
|
695
862
|
var RETRY_DELAYS_MS = [5e3, 3e4, 12e4];
|
|
696
863
|
var EventsModel = class {
|
|
697
864
|
static TABLE = "events";
|
|
698
|
-
static create(input) {
|
|
865
|
+
static async create(input) {
|
|
699
866
|
const _id = uuidv74();
|
|
700
867
|
const timestamp = Date.now();
|
|
701
868
|
const status = "pending";
|
|
702
869
|
const sql = `
|
|
703
|
-
INSERT INTO ${this.TABLE}
|
|
704
|
-
(_id, type, timestamp, fileId, portalAddress, status, retryCount, lastError, lockedAt, nextRetryAt)
|
|
870
|
+
INSERT INTO ${this.TABLE}
|
|
871
|
+
(_id, type, timestamp, fileId, portalAddress, status, retryCount, lastError, lockedAt, nextRetryAt)
|
|
705
872
|
VALUES (?, ?, ?, ?, ?, ?, 0, NULL, NULL, NULL)
|
|
706
873
|
`;
|
|
707
|
-
QueryBuilder.execute(sql, [_id, input.type, timestamp, input.fileId, input.portalAddress, status]);
|
|
874
|
+
await QueryBuilder.execute(sql, [_id, input.type, timestamp, input.fileId, input.portalAddress, status]);
|
|
708
875
|
notifyNewEvent();
|
|
709
876
|
return {
|
|
710
877
|
_id,
|
|
@@ -719,22 +886,22 @@ var EventsModel = class {
|
|
|
719
886
|
nextRetryAt: null
|
|
720
887
|
};
|
|
721
888
|
}
|
|
722
|
-
static findById(_id) {
|
|
889
|
+
static async findById(_id) {
|
|
723
890
|
const sql = `SELECT * FROM ${this.TABLE} WHERE _id = ?`;
|
|
724
|
-
const row = QueryBuilder.selectOne(sql, [_id]);
|
|
891
|
+
const row = await QueryBuilder.selectOne(sql, [_id]);
|
|
725
892
|
return row ? this.parseEvent(row) : void 0;
|
|
726
893
|
}
|
|
727
|
-
static findNextPending() {
|
|
894
|
+
static async findNextPending() {
|
|
728
895
|
const sql = `
|
|
729
896
|
SELECT * FROM ${this.TABLE}
|
|
730
897
|
WHERE status = 'pending'
|
|
731
898
|
ORDER BY timestamp ASC
|
|
732
899
|
LIMIT 1
|
|
733
900
|
`;
|
|
734
|
-
const row = QueryBuilder.selectOne(sql, []);
|
|
901
|
+
const row = await QueryBuilder.selectOne(sql, []);
|
|
735
902
|
return row ? this.parseEvent(row) : void 0;
|
|
736
903
|
}
|
|
737
|
-
static findNextEligible(lockedFileIds) {
|
|
904
|
+
static async findNextEligible(lockedFileIds) {
|
|
738
905
|
const now = Date.now();
|
|
739
906
|
const exclusionClause = lockedFileIds.length > 0 ? `AND e1.fileId NOT IN (${lockedFileIds.map(() => "?").join(", ")})` : "";
|
|
740
907
|
const sql = `
|
|
@@ -752,29 +919,29 @@ var EventsModel = class {
|
|
|
752
919
|
LIMIT 1
|
|
753
920
|
`;
|
|
754
921
|
const params = [now, ...lockedFileIds];
|
|
755
|
-
const row = QueryBuilder.selectOne(sql, params);
|
|
922
|
+
const row = await QueryBuilder.selectOne(sql, params);
|
|
756
923
|
return row ? this.parseEvent(row) : void 0;
|
|
757
924
|
}
|
|
758
|
-
static markProcessing(_id) {
|
|
925
|
+
static async markProcessing(_id) {
|
|
759
926
|
const sql = `
|
|
760
927
|
UPDATE ${this.TABLE}
|
|
761
928
|
SET status = 'processing',
|
|
762
929
|
lockedAt = ?
|
|
763
930
|
WHERE _id = ?
|
|
764
931
|
`;
|
|
765
|
-
QueryBuilder.execute(sql, [Date.now(), _id]);
|
|
932
|
+
await QueryBuilder.execute(sql, [Date.now(), _id]);
|
|
766
933
|
}
|
|
767
|
-
static markProcessed(_id) {
|
|
934
|
+
static async markProcessed(_id) {
|
|
768
935
|
const sql = `
|
|
769
936
|
UPDATE ${this.TABLE}
|
|
770
937
|
SET status = 'processed',
|
|
771
938
|
lockedAt = NULL
|
|
772
939
|
WHERE _id = ?
|
|
773
940
|
`;
|
|
774
|
-
QueryBuilder.execute(sql, [_id]);
|
|
941
|
+
await QueryBuilder.execute(sql, [_id]);
|
|
775
942
|
}
|
|
776
|
-
static scheduleRetry(_id, errorMsg) {
|
|
777
|
-
const event = this.findById(_id);
|
|
943
|
+
static async scheduleRetry(_id, errorMsg) {
|
|
944
|
+
const event = await this.findById(_id);
|
|
778
945
|
if (!event) return;
|
|
779
946
|
const delay = RETRY_DELAYS_MS[Math.min(event.retryCount, RETRY_DELAYS_MS.length - 1)];
|
|
780
947
|
const nextRetryAt = Date.now() + delay;
|
|
@@ -787,9 +954,9 @@ var EventsModel = class {
|
|
|
787
954
|
lockedAt = NULL
|
|
788
955
|
WHERE _id = ?
|
|
789
956
|
`;
|
|
790
|
-
QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
|
|
957
|
+
await QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
|
|
791
958
|
}
|
|
792
|
-
static scheduleRetryAfter(_id, errorMsg, retryAfterMs) {
|
|
959
|
+
static async scheduleRetryAfter(_id, errorMsg, retryAfterMs) {
|
|
793
960
|
const nextRetryAt = Date.now() + retryAfterMs;
|
|
794
961
|
const sql = `
|
|
795
962
|
UPDATE ${this.TABLE}
|
|
@@ -799,9 +966,9 @@ var EventsModel = class {
|
|
|
799
966
|
lockedAt = NULL
|
|
800
967
|
WHERE _id = ?
|
|
801
968
|
`;
|
|
802
|
-
QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
|
|
969
|
+
await QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
|
|
803
970
|
}
|
|
804
|
-
static markFailed(_id, errorMsg) {
|
|
971
|
+
static async markFailed(_id, errorMsg) {
|
|
805
972
|
const sql = `
|
|
806
973
|
UPDATE ${this.TABLE}
|
|
807
974
|
SET status = 'failed',
|
|
@@ -809,9 +976,9 @@ var EventsModel = class {
|
|
|
809
976
|
lockedAt = NULL
|
|
810
977
|
WHERE _id = ?
|
|
811
978
|
`;
|
|
812
|
-
QueryBuilder.execute(sql, [errorMsg, _id]);
|
|
979
|
+
await QueryBuilder.execute(sql, [errorMsg, _id]);
|
|
813
980
|
}
|
|
814
|
-
static listFailed(portalAddress) {
|
|
981
|
+
static async listFailed(portalAddress) {
|
|
815
982
|
const portalClause = portalAddress != null ? "AND portalAddress = ?" : "";
|
|
816
983
|
const sql = `
|
|
817
984
|
SELECT * FROM ${this.TABLE}
|
|
@@ -820,10 +987,10 @@ var EventsModel = class {
|
|
|
820
987
|
ORDER BY timestamp ASC
|
|
821
988
|
`;
|
|
822
989
|
const params = portalAddress != null ? [portalAddress] : [];
|
|
823
|
-
const rows = QueryBuilder.select(sql, params);
|
|
990
|
+
const rows = await QueryBuilder.select(sql, params);
|
|
824
991
|
return rows.map((row) => this.parseEvent(row));
|
|
825
992
|
}
|
|
826
|
-
static resetFailedToPending(_id, portalAddress) {
|
|
993
|
+
static async resetFailedToPending(_id, portalAddress) {
|
|
827
994
|
const portalClause = portalAddress != null ? "AND portalAddress = ?" : "";
|
|
828
995
|
const sql = `
|
|
829
996
|
UPDATE ${this.TABLE}
|
|
@@ -837,13 +1004,13 @@ var EventsModel = class {
|
|
|
837
1004
|
${portalClause}
|
|
838
1005
|
`;
|
|
839
1006
|
const params = portalAddress != null ? [_id, portalAddress] : [_id];
|
|
840
|
-
const result = QueryBuilder.execute(sql, params);
|
|
1007
|
+
const result = await QueryBuilder.execute(sql, params);
|
|
841
1008
|
if (result.changes > 0) {
|
|
842
1009
|
notifyNewEvent();
|
|
843
1010
|
}
|
|
844
1011
|
return result.changes > 0;
|
|
845
1012
|
}
|
|
846
|
-
static resetAllFailedToPending(portalAddress) {
|
|
1013
|
+
static async resetAllFailedToPending(portalAddress) {
|
|
847
1014
|
const portalClause = portalAddress != null ? "AND portalAddress = ?" : "";
|
|
848
1015
|
const sql = `
|
|
849
1016
|
UPDATE ${this.TABLE}
|
|
@@ -856,13 +1023,13 @@ var EventsModel = class {
|
|
|
856
1023
|
${portalClause}
|
|
857
1024
|
`;
|
|
858
1025
|
const params = portalAddress != null ? [portalAddress] : [];
|
|
859
|
-
const result = QueryBuilder.execute(sql, params);
|
|
1026
|
+
const result = await QueryBuilder.execute(sql, params);
|
|
860
1027
|
if (result.changes > 0) {
|
|
861
1028
|
notifyNewEvent();
|
|
862
1029
|
}
|
|
863
1030
|
return result.changes;
|
|
864
1031
|
}
|
|
865
|
-
static resetStaleEvents(staleThreshold) {
|
|
1032
|
+
static async resetStaleEvents(staleThreshold) {
|
|
866
1033
|
const sql = `
|
|
867
1034
|
UPDATE ${this.TABLE}
|
|
868
1035
|
SET status = 'pending',
|
|
@@ -873,16 +1040,16 @@ var EventsModel = class {
|
|
|
873
1040
|
AND lockedAt IS NOT NULL
|
|
874
1041
|
AND lockedAt < ?
|
|
875
1042
|
`;
|
|
876
|
-
const result = QueryBuilder.execute(sql, [staleThreshold]);
|
|
1043
|
+
const result = await QueryBuilder.execute(sql, [staleThreshold]);
|
|
877
1044
|
return result.changes;
|
|
878
1045
|
}
|
|
879
|
-
static setEventPendingOp(_id, userOpHash, payload) {
|
|
1046
|
+
static async setEventPendingOp(_id, userOpHash, payload) {
|
|
880
1047
|
const sql = `UPDATE ${this.TABLE} SET userOpHash = ?, pendingPayload = ? WHERE _id = ?`;
|
|
881
|
-
QueryBuilder.execute(sql, [userOpHash, JSON.stringify(payload), _id]);
|
|
1048
|
+
await QueryBuilder.execute(sql, [userOpHash, JSON.stringify(payload), _id]);
|
|
882
1049
|
}
|
|
883
|
-
static clearEventPendingOp(_id) {
|
|
1050
|
+
static async clearEventPendingOp(_id) {
|
|
884
1051
|
const sql = `UPDATE ${this.TABLE} SET userOpHash = NULL, pendingPayload = NULL WHERE _id = ?`;
|
|
885
|
-
QueryBuilder.execute(sql, [_id]);
|
|
1052
|
+
await QueryBuilder.execute(sql, [_id]);
|
|
886
1053
|
}
|
|
887
1054
|
static parseEvent(row) {
|
|
888
1055
|
return {
|
|
@@ -2470,12 +2637,12 @@ var FileManager = class {
|
|
|
2470
2637
|
};
|
|
2471
2638
|
|
|
2472
2639
|
// src/domain/portal/publish.ts
|
|
2473
|
-
function getPortalData(fileId) {
|
|
2474
|
-
const file2 = FilesModel.findByIdIncludingDeleted(fileId);
|
|
2640
|
+
async function getPortalData(fileId) {
|
|
2641
|
+
const file2 = await FilesModel.findByIdIncludingDeleted(fileId);
|
|
2475
2642
|
if (!file2) {
|
|
2476
2643
|
throw new Error(`File with _id ${fileId} not found`);
|
|
2477
2644
|
}
|
|
2478
|
-
const portalDetails = PortalsModel.findByPortalAddress(file2.portalAddress);
|
|
2645
|
+
const portalDetails = await PortalsModel.findByPortalAddress(file2.portalAddress);
|
|
2479
2646
|
if (!portalDetails) {
|
|
2480
2647
|
throw new Error(`Portal with address ${file2.portalAddress} not found`);
|
|
2481
2648
|
}
|
|
@@ -2515,7 +2682,7 @@ var executeOperation = async (fileManager, file2, operation) => {
|
|
|
2515
2682
|
};
|
|
2516
2683
|
var handleExistingFileOp = async (fileId, operation) => {
|
|
2517
2684
|
try {
|
|
2518
|
-
const { file: file2, portalDetails, apiKey } = getPortalData(fileId);
|
|
2685
|
+
const { file: file2, portalDetails, apiKey } = await getPortalData(fileId);
|
|
2519
2686
|
const apiKeySeed = toUint8Array3(apiKey);
|
|
2520
2687
|
const { privateAccountKey, ucanSecret } = deriveCollaboratorKeys(apiKeySeed);
|
|
2521
2688
|
const fileManager = await createFileManager(
|
|
@@ -2531,7 +2698,7 @@ var handleExistingFileOp = async (fileId, operation) => {
|
|
|
2531
2698
|
}
|
|
2532
2699
|
};
|
|
2533
2700
|
var handleNewFileOp = async (fileId) => {
|
|
2534
|
-
const { file: file2, portalDetails, apiKey } = getPortalData(fileId);
|
|
2701
|
+
const { file: file2, portalDetails, apiKey } = await getPortalData(fileId);
|
|
2535
2702
|
const apiKeySeed = toUint8Array3(apiKey);
|
|
2536
2703
|
const { privateAccountKey, ucanSecret } = deriveCollaboratorKeys(apiKeySeed);
|
|
2537
2704
|
const fileManager = await createFileManager(
|
|
@@ -2543,7 +2710,7 @@ var handleNewFileOp = async (fileId) => {
|
|
|
2543
2710
|
return fileManager.submitAddFileTrx(file2);
|
|
2544
2711
|
};
|
|
2545
2712
|
var getProxyAuthParams = async (fileId) => {
|
|
2546
|
-
const { portalDetails, apiKey } = getPortalData(fileId);
|
|
2713
|
+
const { portalDetails, apiKey } = await getPortalData(fileId);
|
|
2547
2714
|
const apiKeySeed = toUint8Array3(apiKey);
|
|
2548
2715
|
const { privateAccountKey, ucanSecret } = deriveCollaboratorKeys(apiKeySeed);
|
|
2549
2716
|
const fileManager = await createFileManager(
|
|
@@ -2556,7 +2723,7 @@ var getProxyAuthParams = async (fileId) => {
|
|
|
2556
2723
|
};
|
|
2557
2724
|
|
|
2558
2725
|
// src/domain/portal/savePortal.ts
|
|
2559
|
-
function savePortal(input) {
|
|
2726
|
+
async function savePortal(input) {
|
|
2560
2727
|
if (!input.portalAddress || !input.portalSeed || !input.ownerAddress) {
|
|
2561
2728
|
throw new Error("portalAddress, portalSeed, and ownerAddress are required");
|
|
2562
2729
|
}
|
|
@@ -2564,11 +2731,11 @@ function savePortal(input) {
|
|
|
2564
2731
|
}
|
|
2565
2732
|
|
|
2566
2733
|
// src/domain/portal/saveApiKey.ts
|
|
2567
|
-
function addApiKey(input) {
|
|
2734
|
+
async function addApiKey(input) {
|
|
2568
2735
|
if (!input.apiKeySeed || !input.name || !input.collaboratorAddress || !input.portalAddress) {
|
|
2569
2736
|
throw new Error("apiKeySeed, name, collaboratorAddress, and portalAddress are required");
|
|
2570
2737
|
}
|
|
2571
|
-
const portal = PortalsModel.findByPortalAddress(input.portalAddress);
|
|
2738
|
+
const portal = await PortalsModel.findByPortalAddress(input.portalAddress);
|
|
2572
2739
|
if (!portal) {
|
|
2573
2740
|
throw new Error(`Portal with address ${input.portalAddress} does not exist`);
|
|
2574
2741
|
}
|
|
@@ -2632,7 +2799,7 @@ var processEvent = async (event) => {
|
|
|
2632
2799
|
return { success: false, error: errorMsg };
|
|
2633
2800
|
}
|
|
2634
2801
|
};
|
|
2635
|
-
var onTransactionSuccess = (fileId, file2, onChainFileId, pending) => {
|
|
2802
|
+
var onTransactionSuccess = async (fileId, file2, onChainFileId, pending) => {
|
|
2636
2803
|
const frontendUrl = getRuntimeConfig().FRONTEND_URL;
|
|
2637
2804
|
const payload = {
|
|
2638
2805
|
onchainVersion: file2.localVersion,
|
|
@@ -2643,14 +2810,14 @@ var onTransactionSuccess = (fileId, file2, onChainFileId, pending) => {
|
|
|
2643
2810
|
metadata: pending.metadata,
|
|
2644
2811
|
link: `${frontendUrl}/${file2.portalAddress}/${onChainFileId}#key=${pending.linkKey}`
|
|
2645
2812
|
};
|
|
2646
|
-
const updatedFile = FilesModel.update(fileId, payload, file2.portalAddress);
|
|
2813
|
+
const updatedFile = await FilesModel.update(fileId, payload, file2.portalAddress);
|
|
2647
2814
|
if (updatedFile.localVersion === updatedFile.onchainVersion) {
|
|
2648
|
-
FilesModel.update(fileId, { syncStatus: "synced" }, file2.portalAddress);
|
|
2815
|
+
await FilesModel.update(fileId, { syncStatus: "synced" }, file2.portalAddress);
|
|
2649
2816
|
}
|
|
2650
2817
|
};
|
|
2651
2818
|
var processCreateEvent = async (event) => {
|
|
2652
2819
|
const { fileId } = event;
|
|
2653
|
-
const file2 = FilesModel.findByIdIncludingDeleted(fileId);
|
|
2820
|
+
const file2 = await FilesModel.findByIdIncludingDeleted(fileId);
|
|
2654
2821
|
if (!file2) {
|
|
2655
2822
|
throw new Error(`File ${fileId} not found`);
|
|
2656
2823
|
}
|
|
@@ -2669,18 +2836,18 @@ var processCreateEvent = async (event) => {
|
|
|
2669
2836
|
timeout
|
|
2670
2837
|
);
|
|
2671
2838
|
if (!receipt2.success) {
|
|
2672
|
-
EventsModel.clearEventPendingOp(event._id);
|
|
2839
|
+
await EventsModel.clearEventPendingOp(event._id);
|
|
2673
2840
|
throw new Error(`User operation failed: ${receipt2.reason}`);
|
|
2674
2841
|
}
|
|
2675
2842
|
const onChainFileId2 = parseFileEventLog(receipt2.logs, "AddedFile", ADDED_FILE_EVENT);
|
|
2676
2843
|
const pending = JSON.parse(event.pendingPayload);
|
|
2677
|
-
onTransactionSuccess(fileId, file2, onChainFileId2, pending);
|
|
2678
|
-
EventsModel.clearEventPendingOp(event._id);
|
|
2844
|
+
await onTransactionSuccess(fileId, file2, onChainFileId2, pending);
|
|
2845
|
+
await EventsModel.clearEventPendingOp(event._id);
|
|
2679
2846
|
logger.info(`File ${file2.ddocId} created and published successfully (resumed from pending op)`);
|
|
2680
2847
|
return;
|
|
2681
2848
|
}
|
|
2682
2849
|
const result = await handleNewFileOp(fileId);
|
|
2683
|
-
EventsModel.setEventPendingOp(event._id, result.userOpHash, {
|
|
2850
|
+
await EventsModel.setEventPendingOp(event._id, result.userOpHash, {
|
|
2684
2851
|
linkKey: result.linkKey,
|
|
2685
2852
|
linkKeyNonce: result.linkKeyNonce,
|
|
2686
2853
|
commentKey: result.commentKey,
|
|
@@ -2694,22 +2861,22 @@ var processCreateEvent = async (event) => {
|
|
|
2694
2861
|
timeout
|
|
2695
2862
|
);
|
|
2696
2863
|
if (!receipt.success) {
|
|
2697
|
-
EventsModel.clearEventPendingOp(event._id);
|
|
2864
|
+
await EventsModel.clearEventPendingOp(event._id);
|
|
2698
2865
|
throw new Error(`User operation failed: ${receipt.reason}`);
|
|
2699
2866
|
}
|
|
2700
2867
|
const onChainFileId = parseFileEventLog(receipt.logs, "AddedFile", ADDED_FILE_EVENT);
|
|
2701
|
-
onTransactionSuccess(fileId, file2, onChainFileId, {
|
|
2868
|
+
await onTransactionSuccess(fileId, file2, onChainFileId, {
|
|
2702
2869
|
linkKey: result.linkKey,
|
|
2703
2870
|
linkKeyNonce: result.linkKeyNonce,
|
|
2704
2871
|
commentKey: result.commentKey,
|
|
2705
2872
|
metadata: result.metadata
|
|
2706
2873
|
});
|
|
2707
|
-
EventsModel.clearEventPendingOp(event._id);
|
|
2874
|
+
await EventsModel.clearEventPendingOp(event._id);
|
|
2708
2875
|
logger.info(`File ${file2.ddocId} created and published successfully`);
|
|
2709
2876
|
};
|
|
2710
2877
|
var processUpdateEvent = async (event) => {
|
|
2711
2878
|
const { fileId } = event;
|
|
2712
|
-
const file2 = FilesModel.findByIdExcludingDeleted(fileId);
|
|
2879
|
+
const file2 = await FilesModel.findByIdExcludingDeleted(fileId);
|
|
2713
2880
|
if (!file2) {
|
|
2714
2881
|
return;
|
|
2715
2882
|
}
|
|
@@ -2724,15 +2891,15 @@ var processUpdateEvent = async (event) => {
|
|
|
2724
2891
|
onchainVersion: file2.localVersion,
|
|
2725
2892
|
metadata: result.metadata
|
|
2726
2893
|
};
|
|
2727
|
-
const updatedFile = FilesModel.update(fileId, payload, file2.portalAddress);
|
|
2894
|
+
const updatedFile = await FilesModel.update(fileId, payload, file2.portalAddress);
|
|
2728
2895
|
if (updatedFile.localVersion === updatedFile.onchainVersion) {
|
|
2729
|
-
FilesModel.update(fileId, { syncStatus: "synced" }, file2.portalAddress);
|
|
2896
|
+
await FilesModel.update(fileId, { syncStatus: "synced" }, file2.portalAddress);
|
|
2730
2897
|
}
|
|
2731
2898
|
logger.info(`File ${file2.ddocId} updated and published successfully`);
|
|
2732
2899
|
};
|
|
2733
2900
|
var processDeleteEvent = async (event) => {
|
|
2734
2901
|
const { fileId } = event;
|
|
2735
|
-
const file2 = FilesModel.findByIdIncludingDeleted(fileId);
|
|
2902
|
+
const file2 = await FilesModel.findByIdIncludingDeleted(fileId);
|
|
2736
2903
|
if (!file2) {
|
|
2737
2904
|
return;
|
|
2738
2905
|
}
|
|
@@ -2753,7 +2920,7 @@ var processDeleteEvent = async (event) => {
|
|
|
2753
2920
|
payload.metadata = result.metadata;
|
|
2754
2921
|
payload.isDeleted = 1;
|
|
2755
2922
|
}
|
|
2756
|
-
FilesModel.update(fileId, payload, file2.portalAddress);
|
|
2923
|
+
await FilesModel.update(fileId, payload, file2.portalAddress);
|
|
2757
2924
|
logger.info(`File ${fileId} delete event processed (syncStatus set to synced)`);
|
|
2758
2925
|
};
|
|
2759
2926
|
|
|
@@ -2779,10 +2946,11 @@ var FileEventsWorker = class {
|
|
|
2779
2946
|
return;
|
|
2780
2947
|
}
|
|
2781
2948
|
this.isRunning = true;
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2949
|
+
this.recoverStaleEvents().then((staleCount) => {
|
|
2950
|
+
if (staleCount > 0) {
|
|
2951
|
+
logger.info(`Recovered ${staleCount} stale event(s)`);
|
|
2952
|
+
}
|
|
2953
|
+
});
|
|
2786
2954
|
this.signalCleanup = onNewEvent(() => {
|
|
2787
2955
|
this.pendingSignal = true;
|
|
2788
2956
|
this.wakeUp();
|
|
@@ -2811,10 +2979,10 @@ var FileEventsWorker = class {
|
|
|
2811
2979
|
let foundAny = false;
|
|
2812
2980
|
while (this.activeProcessors.size < this.concurrency && this.isRunning) {
|
|
2813
2981
|
const lockedFileIds = Array.from(this.activeProcessors.keys());
|
|
2814
|
-
const event = EventsModel.findNextEligible(lockedFileIds);
|
|
2982
|
+
const event = await EventsModel.findNextEligible(lockedFileIds);
|
|
2815
2983
|
if (!event) break;
|
|
2816
2984
|
foundAny = true;
|
|
2817
|
-
EventsModel.markProcessing(event._id);
|
|
2985
|
+
await EventsModel.markProcessing(event._id);
|
|
2818
2986
|
const processor = this.processEventWrapper(event);
|
|
2819
2987
|
this.activeProcessors.set(event.fileId, processor);
|
|
2820
2988
|
}
|
|
@@ -2825,33 +2993,33 @@ var FileEventsWorker = class {
|
|
|
2825
2993
|
try {
|
|
2826
2994
|
const result = await processEvent(event);
|
|
2827
2995
|
if (result.success) {
|
|
2828
|
-
EventsModel.markProcessed(event._id);
|
|
2996
|
+
await EventsModel.markProcessed(event._id);
|
|
2829
2997
|
} else {
|
|
2830
|
-
this.handleFailure(event, result.error);
|
|
2998
|
+
await this.handleFailure(event, result.error);
|
|
2831
2999
|
}
|
|
2832
3000
|
} catch (err) {
|
|
2833
|
-
this.handleFailure(event, err);
|
|
3001
|
+
await this.handleFailure(event, err);
|
|
2834
3002
|
} finally {
|
|
2835
3003
|
this.activeProcessors.delete(event.fileId);
|
|
2836
3004
|
}
|
|
2837
3005
|
}
|
|
2838
|
-
handleFailure(event, error48) {
|
|
3006
|
+
async handleFailure(event, error48) {
|
|
2839
3007
|
const errorMsg = error48 instanceof Error ? error48.message : String(error48);
|
|
2840
3008
|
if (error48 instanceof RateLimitError) {
|
|
2841
3009
|
const retryAfterMs = error48.retryAfterSeconds * 1e3;
|
|
2842
|
-
EventsModel.scheduleRetryAfter(event._id, errorMsg, retryAfterMs);
|
|
3010
|
+
await EventsModel.scheduleRetryAfter(event._id, errorMsg, retryAfterMs);
|
|
2843
3011
|
logger.warn(`Event ${event._id} rate limited; retry after ${error48.retryAfterSeconds}s`);
|
|
2844
3012
|
return;
|
|
2845
3013
|
}
|
|
2846
3014
|
if (event.retryCount < MAX_RETRIES) {
|
|
2847
|
-
EventsModel.scheduleRetry(event._id, errorMsg);
|
|
3015
|
+
await EventsModel.scheduleRetry(event._id, errorMsg);
|
|
2848
3016
|
logger.warn(`Event ${event._id} failed (retry ${event.retryCount + 1}/${MAX_RETRIES}): ${errorMsg}`);
|
|
2849
3017
|
} else {
|
|
2850
|
-
EventsModel.markFailed(event._id, errorMsg);
|
|
3018
|
+
await EventsModel.markFailed(event._id, errorMsg);
|
|
2851
3019
|
logger.error(`Event ${event._id} permanently failed after ${MAX_RETRIES} retries: ${errorMsg}`);
|
|
2852
3020
|
}
|
|
2853
3021
|
}
|
|
2854
|
-
recoverStaleEvents() {
|
|
3022
|
+
async recoverStaleEvents() {
|
|
2855
3023
|
const staleThreshold = Date.now() - STALE_THRESHOLD_MS;
|
|
2856
3024
|
return EventsModel.resetStaleEvents(staleThreshold);
|
|
2857
3025
|
}
|
|
@@ -2940,8 +3108,8 @@ CREATE TABLE IF NOT EXISTS files (
|
|
|
2940
3108
|
localVersion INTEGER NOT NULL DEFAULT 1,
|
|
2941
3109
|
onchainVersion INTEGER NOT NULL DEFAULT 0,
|
|
2942
3110
|
syncStatus TEXT NOT NULL DEFAULT 'pending',
|
|
2943
|
-
createdAt
|
|
2944
|
-
updatedAt
|
|
3111
|
+
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
3112
|
+
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
2945
3113
|
isDeleted INTEGER NOT NULL DEFAULT 0,
|
|
2946
3114
|
portalAddress TEXT NOT NULL,
|
|
2947
3115
|
metadata TEXT DEFAULT '{}',
|
|
@@ -2961,8 +3129,8 @@ CREATE TABLE IF NOT EXISTS portals (
|
|
|
2961
3129
|
portalAddress TEXT NOT NULL UNIQUE,
|
|
2962
3130
|
portalSeed TEXT NOT NULL UNIQUE,
|
|
2963
3131
|
ownerAddress TEXT NOT NULL,
|
|
2964
|
-
createdAt
|
|
2965
|
-
updatedAt
|
|
3132
|
+
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
3133
|
+
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
2966
3134
|
);
|
|
2967
3135
|
|
|
2968
3136
|
CREATE TABLE IF NOT EXISTS api_keys (
|
|
@@ -2971,7 +3139,7 @@ CREATE TABLE IF NOT EXISTS api_keys (
|
|
|
2971
3139
|
name TEXT NOT NULL,
|
|
2972
3140
|
collaboratorAddress TEXT NOT NULL UNIQUE,
|
|
2973
3141
|
portalAddress TEXT NOT NULL,
|
|
2974
|
-
createdAt
|
|
3142
|
+
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
2975
3143
|
isDeleted INTEGER NOT NULL DEFAULT 0
|
|
2976
3144
|
);
|
|
2977
3145
|
|
|
@@ -3007,16 +3175,16 @@ CREATE TABLE IF NOT EXISTS folders (
|
|
|
3007
3175
|
lastTransactionHash TEXT,
|
|
3008
3176
|
lastTransactionBlockNumber INTEGER NOT NULL,
|
|
3009
3177
|
lastTransactionBlockTimestamp INTEGER NOT NULL,
|
|
3010
|
-
created_at
|
|
3011
|
-
updated_at
|
|
3178
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
3179
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
3012
3180
|
);
|
|
3013
3181
|
CREATE INDEX IF NOT EXISTS idx_folders_folderRef_folderId ON folders(folderRef, folderId);
|
|
3014
3182
|
CREATE INDEX IF NOT EXISTS idx_folders_folderRef ON folders(folderRef);
|
|
3015
3183
|
CREATE INDEX IF NOT EXISTS idx_folders_created_at ON folders(created_at);
|
|
3016
3184
|
`;
|
|
3017
|
-
function runMigrations() {
|
|
3018
|
-
const
|
|
3019
|
-
|
|
3185
|
+
async function runMigrations() {
|
|
3186
|
+
const adapter2 = getAdapterSync();
|
|
3187
|
+
await adapter2.exec(STABLE_SCHEMA);
|
|
3020
3188
|
logger.debug("Database schema ready");
|
|
3021
3189
|
}
|
|
3022
3190
|
|
|
@@ -3054,16 +3222,16 @@ import { toUint8Array as toUint8Array5 } from "js-base64";
|
|
|
3054
3222
|
import { stringToBytes as stringToBytes2 } from "viem";
|
|
3055
3223
|
import { toAESKey as toAESKey2, aesDecrypt } from "@fileverse/crypto/webcrypto";
|
|
3056
3224
|
var SAVED_DATA_ENCRYPTION_KEY_INFO = "SAVED_DATA_ENCRYPTION_KEY";
|
|
3057
|
-
function initializeWithData(data) {
|
|
3225
|
+
async function initializeWithData(data) {
|
|
3058
3226
|
const { keyMaterial, appMaterial } = data;
|
|
3059
|
-
savePortal({
|
|
3227
|
+
await savePortal({
|
|
3060
3228
|
portalAddress: appMaterial.portalAddress,
|
|
3061
3229
|
portalSeed: appMaterial.portalSeed,
|
|
3062
3230
|
ownerAddress: appMaterial.ownerAddress
|
|
3063
3231
|
});
|
|
3064
|
-
const existingApiKey = ApiKeysModel.findByApiKey(keyMaterial.apiKeySeed);
|
|
3232
|
+
const existingApiKey = await ApiKeysModel.findByApiKey(keyMaterial.apiKeySeed);
|
|
3065
3233
|
if (!existingApiKey) {
|
|
3066
|
-
addApiKey({
|
|
3234
|
+
await addApiKey({
|
|
3067
3235
|
apiKeySeed: keyMaterial.apiKeySeed,
|
|
3068
3236
|
name: keyMaterial.name,
|
|
3069
3237
|
collaboratorAddress: keyMaterial.collaboratorAddress,
|
|
@@ -3096,7 +3264,7 @@ var initializeFromApiKey = async (apiKey) => {
|
|
|
3096
3264
|
logger.debug("API key data retrieved");
|
|
3097
3265
|
const keyMaterial = await decryptSavedData(apiKey, data.encryptedKeyMaterial);
|
|
3098
3266
|
const appMaterial = await decryptSavedData(apiKey, data.encryptedAppMaterial);
|
|
3099
|
-
const result = initializeWithData({ keyMaterial, appMaterial, id: data.id });
|
|
3267
|
+
const result = await initializeWithData({ keyMaterial, appMaterial, id: data.id });
|
|
3100
3268
|
logger.debug("Portal saved");
|
|
3101
3269
|
if (result.apiKeySaved) {
|
|
3102
3270
|
logger.debug("API key saved");
|
|
@@ -3121,10 +3289,10 @@ import { Router } from "express";
|
|
|
3121
3289
|
|
|
3122
3290
|
// src/domain/file/index.ts
|
|
3123
3291
|
import { generate } from "short-uuid";
|
|
3124
|
-
function listFiles(params) {
|
|
3292
|
+
async function listFiles(params) {
|
|
3125
3293
|
const { limit, skip, portalAddress } = params;
|
|
3126
3294
|
const effectiveLimit = limit || DEFAULT_LIST_LIMIT;
|
|
3127
|
-
const result = FilesModel.findAll(portalAddress, effectiveLimit, skip);
|
|
3295
|
+
const result = await FilesModel.findAll(portalAddress, effectiveLimit, skip);
|
|
3128
3296
|
const processedFiles = result.files.map((file2) => ({
|
|
3129
3297
|
ddocId: file2.ddocId,
|
|
3130
3298
|
link: file2.link,
|
|
@@ -3145,11 +3313,11 @@ function listFiles(params) {
|
|
|
3145
3313
|
hasNext: result.hasNext
|
|
3146
3314
|
};
|
|
3147
3315
|
}
|
|
3148
|
-
function getFile(ddocId, portalAddress) {
|
|
3316
|
+
async function getFile(ddocId, portalAddress) {
|
|
3149
3317
|
if (!ddocId) {
|
|
3150
3318
|
throw new Error("ddocId is required");
|
|
3151
3319
|
}
|
|
3152
|
-
const file2 = FilesModel.findByDDocId(ddocId, portalAddress);
|
|
3320
|
+
const file2 = await FilesModel.findByDDocId(ddocId, portalAddress);
|
|
3153
3321
|
if (!file2) {
|
|
3154
3322
|
return null;
|
|
3155
3323
|
}
|
|
@@ -3173,13 +3341,13 @@ var createFile = async (input) => {
|
|
|
3173
3341
|
throw new Error("title, content, and portalAddress are required");
|
|
3174
3342
|
}
|
|
3175
3343
|
const ddocId = generate();
|
|
3176
|
-
const file2 = FilesModel.create({
|
|
3344
|
+
const file2 = await FilesModel.create({
|
|
3177
3345
|
title: input.title,
|
|
3178
3346
|
content: input.content,
|
|
3179
3347
|
ddocId,
|
|
3180
3348
|
portalAddress: input.portalAddress
|
|
3181
3349
|
});
|
|
3182
|
-
EventsModel.create({ type: "create", fileId: file2._id, portalAddress: file2.portalAddress });
|
|
3350
|
+
await EventsModel.create({ type: "create", fileId: file2._id, portalAddress: file2.portalAddress });
|
|
3183
3351
|
return file2;
|
|
3184
3352
|
};
|
|
3185
3353
|
var updateFile = async (ddocId, payload, portalAddress) => {
|
|
@@ -3189,7 +3357,7 @@ var updateFile = async (ddocId, payload, portalAddress) => {
|
|
|
3189
3357
|
if (!payload.title && !payload.content) {
|
|
3190
3358
|
throw new Error("At least one field is required: Either provide title, content, or both");
|
|
3191
3359
|
}
|
|
3192
|
-
const existingFile = FilesModel.findByDDocId(ddocId, portalAddress);
|
|
3360
|
+
const existingFile = await FilesModel.findByDDocId(ddocId, portalAddress);
|
|
3193
3361
|
if (!existingFile) {
|
|
3194
3362
|
throw new Error(`File with ddocId ${ddocId} not found`);
|
|
3195
3363
|
}
|
|
@@ -3199,8 +3367,8 @@ var updateFile = async (ddocId, payload, portalAddress) => {
|
|
|
3199
3367
|
syncStatus: "pending"
|
|
3200
3368
|
// since the update is done in local db, it's not on the chain yet. hence pending
|
|
3201
3369
|
};
|
|
3202
|
-
const updatedFile = FilesModel.update(existingFile._id, updatePayload, portalAddress);
|
|
3203
|
-
EventsModel.create({ type: "update", fileId: updatedFile._id, portalAddress: updatedFile.portalAddress });
|
|
3370
|
+
const updatedFile = await FilesModel.update(existingFile._id, updatePayload, portalAddress);
|
|
3371
|
+
await EventsModel.create({ type: "update", fileId: updatedFile._id, portalAddress: updatedFile.portalAddress });
|
|
3204
3372
|
return {
|
|
3205
3373
|
ddocId: updatedFile.ddocId,
|
|
3206
3374
|
link: updatedFile.link,
|
|
@@ -3220,12 +3388,12 @@ var deleteFile = async (ddocId, portalAddress) => {
|
|
|
3220
3388
|
if (!ddocId) {
|
|
3221
3389
|
throw new Error("ddocId is required");
|
|
3222
3390
|
}
|
|
3223
|
-
const existingFile = FilesModel.findByDDocId(ddocId, portalAddress);
|
|
3391
|
+
const existingFile = await FilesModel.findByDDocId(ddocId, portalAddress);
|
|
3224
3392
|
if (!existingFile) {
|
|
3225
3393
|
throw new Error(`File with ddocId ${ddocId} not found`);
|
|
3226
3394
|
}
|
|
3227
|
-
const deletedFile = FilesModel.softDelete(existingFile._id);
|
|
3228
|
-
EventsModel.create({ type: "delete", fileId: deletedFile._id, portalAddress: deletedFile.portalAddress });
|
|
3395
|
+
const deletedFile = await FilesModel.softDelete(existingFile._id);
|
|
3396
|
+
await EventsModel.create({ type: "delete", fileId: deletedFile._id, portalAddress: deletedFile.portalAddress });
|
|
3229
3397
|
return deletedFile;
|
|
3230
3398
|
};
|
|
3231
3399
|
|
|
@@ -3280,7 +3448,7 @@ var listHandler = async (req, res) => {
|
|
|
3280
3448
|
if (!apiKey) {
|
|
3281
3449
|
throw new Error("API key is required");
|
|
3282
3450
|
}
|
|
3283
|
-
const apiKeyInfo = ApiKeysModel.findByApiKey(apiKey);
|
|
3451
|
+
const apiKeyInfo = await ApiKeysModel.findByApiKey(apiKey);
|
|
3284
3452
|
if (!apiKeyInfo) {
|
|
3285
3453
|
throw new Error("Invalid API key");
|
|
3286
3454
|
}
|
|
@@ -3288,7 +3456,7 @@ var listHandler = async (req, res) => {
|
|
|
3288
3456
|
if (!portalAddress) {
|
|
3289
3457
|
throw new Error("Portal address is required");
|
|
3290
3458
|
}
|
|
3291
|
-
const result = listFiles({ limit, skip, portalAddress });
|
|
3459
|
+
const result = await listFiles({ limit, skip, portalAddress });
|
|
3292
3460
|
res.json({
|
|
3293
3461
|
ddocs: result.ddocs,
|
|
3294
3462
|
total: result.total,
|
|
@@ -3301,7 +3469,7 @@ var getHandler = async (req, res) => {
|
|
|
3301
3469
|
if (!apiKey) {
|
|
3302
3470
|
throw new Error("API key is required");
|
|
3303
3471
|
}
|
|
3304
|
-
const apiKeyInfo = ApiKeysModel.findByApiKey(apiKey);
|
|
3472
|
+
const apiKeyInfo = await ApiKeysModel.findByApiKey(apiKey);
|
|
3305
3473
|
if (!apiKeyInfo) {
|
|
3306
3474
|
throw new Error("Invalid API key");
|
|
3307
3475
|
}
|
|
@@ -3313,7 +3481,7 @@ var getHandler = async (req, res) => {
|
|
|
3313
3481
|
return res.status(400).json({ error: "Missing required header: x-portal-address is required" });
|
|
3314
3482
|
}
|
|
3315
3483
|
try {
|
|
3316
|
-
const file2 = getFile(ddocId, portalAddress);
|
|
3484
|
+
const file2 = await getFile(ddocId, portalAddress);
|
|
3317
3485
|
if (!file2) {
|
|
3318
3486
|
return res.status(404).json({ error: "DDoc not found" });
|
|
3319
3487
|
}
|
|
@@ -3339,7 +3507,7 @@ var createHandler = async (req, res) => {
|
|
|
3339
3507
|
error: "Missing content: Either provide a file upload or fileContent text field"
|
|
3340
3508
|
});
|
|
3341
3509
|
}
|
|
3342
|
-
const apiKeyInfo = ApiKeysModel.findByApiKey(apiKey);
|
|
3510
|
+
const apiKeyInfo = await ApiKeysModel.findByApiKey(apiKey);
|
|
3343
3511
|
if (!apiKeyInfo) {
|
|
3344
3512
|
return res.status(400).json({ error: "Invalid API key" });
|
|
3345
3513
|
}
|
|
@@ -3384,7 +3552,7 @@ var updateHandler = async (req, res) => {
|
|
|
3384
3552
|
title: clientPayload.title,
|
|
3385
3553
|
content: clientPayload.content
|
|
3386
3554
|
};
|
|
3387
|
-
const apiKeyInfo = ApiKeysModel.findByApiKey(apiKeySeed);
|
|
3555
|
+
const apiKeyInfo = await ApiKeysModel.findByApiKey(apiKeySeed);
|
|
3388
3556
|
if (!apiKeyInfo) {
|
|
3389
3557
|
return res.status(400).json({ error: "Invalid API key" });
|
|
3390
3558
|
}
|
|
@@ -3405,7 +3573,7 @@ var deleteHandler = async (req, res) => {
|
|
|
3405
3573
|
if (!apiKey) {
|
|
3406
3574
|
return res.status(400).json({ error: "API key is required" });
|
|
3407
3575
|
}
|
|
3408
|
-
const apiKeyInfo = ApiKeysModel.findByApiKey(apiKey);
|
|
3576
|
+
const apiKeyInfo = await ApiKeysModel.findByApiKey(apiKey);
|
|
3409
3577
|
if (!apiKeyInfo) {
|
|
3410
3578
|
return res.status(400).json({ error: "Invalid API key" });
|
|
3411
3579
|
}
|
|
@@ -3446,9 +3614,9 @@ var ddocs_default = router;
|
|
|
3446
3614
|
import { Router as Router2 } from "express";
|
|
3447
3615
|
|
|
3448
3616
|
// src/domain/folder/listFolders.ts
|
|
3449
|
-
function listFolders(params) {
|
|
3617
|
+
async function listFolders(params) {
|
|
3450
3618
|
const { limit, skip } = params;
|
|
3451
|
-
const result = FoldersModel.findAll(limit, skip);
|
|
3619
|
+
const result = await FoldersModel.findAll(limit, skip);
|
|
3452
3620
|
return {
|
|
3453
3621
|
folders: result.folders,
|
|
3454
3622
|
total: result.total,
|
|
@@ -3457,11 +3625,11 @@ function listFolders(params) {
|
|
|
3457
3625
|
}
|
|
3458
3626
|
|
|
3459
3627
|
// src/domain/folder/getFolder.ts
|
|
3460
|
-
function getFolder(folderRef, folderId) {
|
|
3628
|
+
async function getFolder(folderRef, folderId) {
|
|
3461
3629
|
if (!folderRef || !folderId) {
|
|
3462
3630
|
throw new Error("folderRef and folderId are required");
|
|
3463
3631
|
}
|
|
3464
|
-
const folder = FoldersModel.findByFolderRefAndId(folderRef, folderId);
|
|
3632
|
+
const folder = await FoldersModel.findByFolderRefAndId(folderRef, folderId);
|
|
3465
3633
|
if (!folder) {
|
|
3466
3634
|
return null;
|
|
3467
3635
|
}
|
|
@@ -3469,7 +3637,7 @@ function getFolder(folderRef, folderId) {
|
|
|
3469
3637
|
}
|
|
3470
3638
|
|
|
3471
3639
|
// src/domain/folder/createFolder.ts
|
|
3472
|
-
function createFolder(input) {
|
|
3640
|
+
async function createFolder(input) {
|
|
3473
3641
|
if (!input.folderId) {
|
|
3474
3642
|
throw new Error("folderId is required");
|
|
3475
3643
|
}
|
|
@@ -3482,7 +3650,7 @@ function createFolder(input) {
|
|
|
3482
3650
|
if (!input.portalAddress) {
|
|
3483
3651
|
throw new Error("portalAddress is required");
|
|
3484
3652
|
}
|
|
3485
|
-
const existing = FoldersModel.findByFolderRefAndId(input.folderRef, input.folderId);
|
|
3653
|
+
const existing = await FoldersModel.findByFolderRefAndId(input.folderRef, input.folderId);
|
|
3486
3654
|
if (existing) {
|
|
3487
3655
|
throw new Error("Folder with this folderRef and folderId already exists");
|
|
3488
3656
|
}
|
|
@@ -3493,7 +3661,7 @@ function createFolder(input) {
|
|
|
3493
3661
|
var listHandler2 = async (req, res) => {
|
|
3494
3662
|
const limit = req.query.limit ? parseInt(req.query.limit, 10) : void 0;
|
|
3495
3663
|
const skip = req.query.skip ? parseInt(req.query.skip, 10) : void 0;
|
|
3496
|
-
const result = listFolders({ limit, skip });
|
|
3664
|
+
const result = await listFolders({ limit, skip });
|
|
3497
3665
|
res.json({
|
|
3498
3666
|
folders: result.folders,
|
|
3499
3667
|
total: result.total,
|
|
@@ -3509,7 +3677,7 @@ var getHandler2 = async (req, res) => {
|
|
|
3509
3677
|
return res.status(400).json({ error: "folderRef and folderId are required" });
|
|
3510
3678
|
}
|
|
3511
3679
|
try {
|
|
3512
|
-
const folder = getFolder(folderRef, folderId);
|
|
3680
|
+
const folder = await getFolder(folderRef, folderId);
|
|
3513
3681
|
if (!folder) {
|
|
3514
3682
|
return res.status(404).json({ error: "Folder not found" });
|
|
3515
3683
|
}
|
|
@@ -3539,7 +3707,7 @@ var createHandler2 = async (req, res) => {
|
|
|
3539
3707
|
error: "Missing required fields: lastTransactionBlockNumber and lastTransactionBlockTimestamp are required"
|
|
3540
3708
|
});
|
|
3541
3709
|
}
|
|
3542
|
-
const folder = createFolder(input);
|
|
3710
|
+
const folder = await createFolder(input);
|
|
3543
3711
|
res.status(201).json(folder);
|
|
3544
3712
|
} catch (error48) {
|
|
3545
3713
|
if (error48.message.includes("already exists")) {
|
|
@@ -3561,18 +3729,18 @@ var folders_default = router2;
|
|
|
3561
3729
|
import { Router as Router3 } from "express";
|
|
3562
3730
|
|
|
3563
3731
|
// src/domain/search/searchNodes.ts
|
|
3564
|
-
function searchNodes(params) {
|
|
3732
|
+
async function searchNodes(params) {
|
|
3565
3733
|
const { query, limit, skip, portalAddress } = params;
|
|
3566
3734
|
if (!query || query.trim().length === 0) {
|
|
3567
3735
|
return { nodes: [], total: 0, hasNext: false };
|
|
3568
3736
|
}
|
|
3569
|
-
const files = FilesModel.searchByTitle(query, portalAddress, limit, skip);
|
|
3737
|
+
const files = await FilesModel.searchByTitle(query, portalAddress, limit, skip);
|
|
3570
3738
|
const normalizedNodes = files.map((file2) => ({
|
|
3571
3739
|
type: "file",
|
|
3572
3740
|
...file2
|
|
3573
3741
|
}));
|
|
3574
3742
|
const countSql = `SELECT COUNT(*) as count FROM files WHERE LOWER(title) LIKE LOWER(?) AND isDeleted = 0 AND portalAddress = ?`;
|
|
3575
|
-
const totalResult = QueryBuilder.selectOne(countSql, [`%${query}%`, portalAddress]);
|
|
3743
|
+
const totalResult = await QueryBuilder.selectOne(countSql, [`%${query}%`, portalAddress]);
|
|
3576
3744
|
const total = totalResult?.count || 0;
|
|
3577
3745
|
const hasNext = skip !== void 0 && limit !== void 0 ? skip + limit < total : false;
|
|
3578
3746
|
return {
|
|
@@ -3590,7 +3758,8 @@ var searchHandler = async (req, res) => {
|
|
|
3590
3758
|
const runtimConfig = getRuntimeConfig();
|
|
3591
3759
|
const apiKey = runtimConfig.API_KEY;
|
|
3592
3760
|
if (!apiKey) throw new Error("API key is required");
|
|
3593
|
-
const
|
|
3761
|
+
const apiKeyInfo = await ApiKeysModel.findByApiKey(apiKey);
|
|
3762
|
+
const portalAddress = apiKeyInfo?.portalAddress;
|
|
3594
3763
|
if (!portalAddress) throw new Error("Portal address is required");
|
|
3595
3764
|
if (!query) {
|
|
3596
3765
|
return res.status(400).json({ error: 'Query parameter "q" is required' });
|
|
@@ -3598,7 +3767,7 @@ var searchHandler = async (req, res) => {
|
|
|
3598
3767
|
if (!portalAddress) {
|
|
3599
3768
|
return res.status(400).json({ error: "Missing required header: x-portal-address is required" });
|
|
3600
3769
|
}
|
|
3601
|
-
const result = searchNodes({ query, limit, skip, portalAddress });
|
|
3770
|
+
const result = await searchNodes({ query, limit, skip, portalAddress });
|
|
3602
3771
|
res.json({
|
|
3603
3772
|
nodes: result.nodes,
|
|
3604
3773
|
total: result.total,
|
|
@@ -3618,11 +3787,12 @@ import { Router as Router4 } from "express";
|
|
|
3618
3787
|
// src/interface/api/router/events/listFailed.ts
|
|
3619
3788
|
var listFailedHandler = async (req, res) => {
|
|
3620
3789
|
const apiKey = req.query.apiKey;
|
|
3621
|
-
const
|
|
3790
|
+
const apiKeyInfo = await ApiKeysModel.findByApiKey(apiKey);
|
|
3791
|
+
const portalAddress = apiKeyInfo?.portalAddress;
|
|
3622
3792
|
if (!portalAddress) {
|
|
3623
3793
|
return res.status(401).json({ error: "Invalid or missing API key" });
|
|
3624
3794
|
}
|
|
3625
|
-
const events = EventsModel.listFailed(portalAddress);
|
|
3795
|
+
const events = await EventsModel.listFailed(portalAddress);
|
|
3626
3796
|
res.json(events);
|
|
3627
3797
|
};
|
|
3628
3798
|
var listFailed_default = [listFailedHandler];
|
|
@@ -3630,12 +3800,13 @@ var listFailed_default = [listFailedHandler];
|
|
|
3630
3800
|
// src/interface/api/router/events/retryOne.ts
|
|
3631
3801
|
var retryOneHandler = async (req, res) => {
|
|
3632
3802
|
const apiKey = req.query.apiKey;
|
|
3633
|
-
const
|
|
3803
|
+
const apiKeyInfo = await ApiKeysModel.findByApiKey(apiKey);
|
|
3804
|
+
const portalAddress = apiKeyInfo?.portalAddress;
|
|
3634
3805
|
if (!portalAddress) {
|
|
3635
3806
|
return res.status(401).json({ error: "Invalid or missing API key" });
|
|
3636
3807
|
}
|
|
3637
3808
|
const _id = req.params.id;
|
|
3638
|
-
const updated = EventsModel.resetFailedToPending(_id, portalAddress);
|
|
3809
|
+
const updated = await EventsModel.resetFailedToPending(_id, portalAddress);
|
|
3639
3810
|
if (!updated) {
|
|
3640
3811
|
return res.status(404).json({ error: "Event not found or not in failed state" });
|
|
3641
3812
|
}
|
|
@@ -3646,11 +3817,12 @@ var retryOne_default = [retryOneHandler];
|
|
|
3646
3817
|
// src/interface/api/router/events/retryAllFailed.ts
|
|
3647
3818
|
var retryAllFailedHandler = async (req, res) => {
|
|
3648
3819
|
const apiKey = req.query.apiKey;
|
|
3649
|
-
const
|
|
3820
|
+
const apiKeyInfo = await ApiKeysModel.findByApiKey(apiKey);
|
|
3821
|
+
const portalAddress = apiKeyInfo?.portalAddress;
|
|
3650
3822
|
if (!portalAddress) {
|
|
3651
3823
|
return res.status(401).json({ error: "Invalid or missing API key" });
|
|
3652
3824
|
}
|
|
3653
|
-
const count = EventsModel.resetAllFailedToPending(portalAddress);
|
|
3825
|
+
const count = await EventsModel.resetAllFailedToPending(portalAddress);
|
|
3654
3826
|
res.json({ retried: count });
|
|
3655
3827
|
};
|
|
3656
3828
|
var retryAllFailed_default = [retryAllFailedHandler];
|
|
@@ -17739,8 +17911,9 @@ var port = parseInt(config.PORT || "8001", 10);
|
|
|
17739
17911
|
var ip = config.IP || "0.0.0.0";
|
|
17740
17912
|
var server;
|
|
17741
17913
|
var startServer = async () => {
|
|
17742
|
-
|
|
17743
|
-
|
|
17914
|
+
validateDbConfig();
|
|
17915
|
+
await getAdapter();
|
|
17916
|
+
await runMigrations();
|
|
17744
17917
|
const apiKey = config.API_KEY;
|
|
17745
17918
|
if (!apiKey) {
|
|
17746
17919
|
logger.error("API_KEY environment variable is not set");
|
|
@@ -17785,7 +17958,7 @@ var shutdown = async () => {
|
|
|
17785
17958
|
logger.error("Error closing worker:", error48);
|
|
17786
17959
|
}
|
|
17787
17960
|
try {
|
|
17788
|
-
await
|
|
17961
|
+
await closeAdapter();
|
|
17789
17962
|
logger.info("Database connection closed");
|
|
17790
17963
|
} catch (error48) {
|
|
17791
17964
|
logger.error("Error closing database:", error48);
|