@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/commands/index.js
CHANGED
|
@@ -48,6 +48,9 @@ function getRuntimeConfig() {
|
|
|
48
48
|
get DB_PATH() {
|
|
49
49
|
return process.env.DB_PATH;
|
|
50
50
|
},
|
|
51
|
+
get DATABASE_URL() {
|
|
52
|
+
return process.env.DATABASE_URL;
|
|
53
|
+
},
|
|
51
54
|
get PORT() {
|
|
52
55
|
return process.env.PORT || STATIC_CONFIG.DEFAULT_PORT;
|
|
53
56
|
},
|
|
@@ -85,6 +88,9 @@ var config = {
|
|
|
85
88
|
get DB_PATH() {
|
|
86
89
|
return process.env.DB_PATH;
|
|
87
90
|
},
|
|
91
|
+
get DATABASE_URL() {
|
|
92
|
+
return process.env.DATABASE_URL;
|
|
93
|
+
},
|
|
88
94
|
get PORT() {
|
|
89
95
|
return process.env.PORT || STATIC_CONFIG.DEFAULT_PORT;
|
|
90
96
|
},
|
|
@@ -171,9 +177,43 @@ var logger = {
|
|
|
171
177
|
child: pinoInstance.child.bind(pinoInstance)
|
|
172
178
|
};
|
|
173
179
|
|
|
174
|
-
// src/infra/database/
|
|
180
|
+
// src/infra/database/adapters/sqlite-adapter.ts
|
|
175
181
|
import Database from "better-sqlite3";
|
|
176
182
|
|
|
183
|
+
// src/domain/file/constants.ts
|
|
184
|
+
var DEFAULT_LIST_LIMIT = 10;
|
|
185
|
+
|
|
186
|
+
// src/infra/database/query-builder.ts
|
|
187
|
+
var QueryBuilder = class {
|
|
188
|
+
static async select(sql, params = []) {
|
|
189
|
+
return getAdapterSync().select(sql, params);
|
|
190
|
+
}
|
|
191
|
+
static async selectOne(sql, params = []) {
|
|
192
|
+
return getAdapterSync().selectOne(sql, params);
|
|
193
|
+
}
|
|
194
|
+
static async execute(sql, params = []) {
|
|
195
|
+
return getAdapterSync().execute(sql, params);
|
|
196
|
+
}
|
|
197
|
+
static async transaction(callback) {
|
|
198
|
+
return getAdapterSync().transaction(callback);
|
|
199
|
+
}
|
|
200
|
+
static paginate(sql, options = {}) {
|
|
201
|
+
let query = sql;
|
|
202
|
+
if (options.orderBy) {
|
|
203
|
+
query += ` ORDER BY ${options.orderBy} ${options.orderDirection || "ASC"}`;
|
|
204
|
+
}
|
|
205
|
+
const hasOffset = (options.offset ?? 0) > 0;
|
|
206
|
+
const limit = options.limit ?? (hasOffset ? DEFAULT_LIST_LIMIT : void 0);
|
|
207
|
+
if (limit) {
|
|
208
|
+
query += ` LIMIT ${limit}`;
|
|
209
|
+
}
|
|
210
|
+
if (hasOffset) {
|
|
211
|
+
query += ` OFFSET ${options.offset}`;
|
|
212
|
+
}
|
|
213
|
+
return query;
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
177
217
|
// src/infra/database/models/files.model.ts
|
|
178
218
|
import { uuidv7 } from "uuidv7";
|
|
179
219
|
var FilesModel = class {
|
|
@@ -207,15 +247,15 @@ var FilesModel = class {
|
|
|
207
247
|
link: fileRaw.link
|
|
208
248
|
};
|
|
209
249
|
}
|
|
210
|
-
static findAll(portalAddress, limit, skip) {
|
|
250
|
+
static async findAll(portalAddress, limit, skip) {
|
|
211
251
|
const whereClause = "isDeleted = 0 AND portalAddress = ?";
|
|
212
252
|
const params = [portalAddress];
|
|
213
253
|
const countSql = `
|
|
214
|
-
SELECT COUNT(*) as count
|
|
215
|
-
FROM ${this.TABLE}
|
|
254
|
+
SELECT COUNT(*) as count
|
|
255
|
+
FROM ${this.TABLE}
|
|
216
256
|
WHERE ${whereClause}
|
|
217
257
|
`;
|
|
218
|
-
const totalResult = QueryBuilder.selectOne(countSql, params);
|
|
258
|
+
const totalResult = await QueryBuilder.selectOne(countSql, params);
|
|
219
259
|
const total = totalResult?.count || 0;
|
|
220
260
|
const sql = `
|
|
221
261
|
SELECT *
|
|
@@ -228,51 +268,51 @@ var FilesModel = class {
|
|
|
228
268
|
orderBy: "createdAt",
|
|
229
269
|
orderDirection: "DESC"
|
|
230
270
|
});
|
|
231
|
-
const filesRaw = QueryBuilder.select(completeSql, params);
|
|
271
|
+
const filesRaw = await QueryBuilder.select(completeSql, params);
|
|
232
272
|
const files = filesRaw.map(this.parseFile);
|
|
233
273
|
const hasNext = skip !== void 0 && limit !== void 0 ? skip + limit < total : false;
|
|
234
274
|
return { files, total, hasNext };
|
|
235
275
|
}
|
|
236
|
-
static findById(_id, portalAddress) {
|
|
276
|
+
static async findById(_id, portalAddress) {
|
|
237
277
|
const sql = `
|
|
238
278
|
SELECT *
|
|
239
|
-
FROM ${this.TABLE}
|
|
279
|
+
FROM ${this.TABLE}
|
|
240
280
|
WHERE _id = ? AND isDeleted = 0 AND portalAddress = ?
|
|
241
281
|
`;
|
|
242
|
-
const result = QueryBuilder.selectOne(sql, [_id, portalAddress]);
|
|
282
|
+
const result = await QueryBuilder.selectOne(sql, [_id, portalAddress]);
|
|
243
283
|
return result ? this.parseFile(result) : void 0;
|
|
244
284
|
}
|
|
245
|
-
static findByIdIncludingDeleted(_id) {
|
|
285
|
+
static async findByIdIncludingDeleted(_id) {
|
|
246
286
|
const sql = `
|
|
247
287
|
SELECT *
|
|
248
|
-
FROM ${this.TABLE}
|
|
288
|
+
FROM ${this.TABLE}
|
|
249
289
|
WHERE _id = ?
|
|
250
290
|
`;
|
|
251
|
-
const result = QueryBuilder.selectOne(sql, [_id]);
|
|
291
|
+
const result = await QueryBuilder.selectOne(sql, [_id]);
|
|
252
292
|
return result ? this.parseFile(result) : void 0;
|
|
253
293
|
}
|
|
254
|
-
static findByIdExcludingDeleted(_id) {
|
|
294
|
+
static async findByIdExcludingDeleted(_id) {
|
|
255
295
|
const sql = `
|
|
256
296
|
SELECT *
|
|
257
|
-
FROM ${this.TABLE}
|
|
297
|
+
FROM ${this.TABLE}
|
|
258
298
|
WHERE _id = ? AND isDeleted = 0
|
|
259
299
|
`;
|
|
260
|
-
const result = QueryBuilder.selectOne(sql, [_id]);
|
|
300
|
+
const result = await QueryBuilder.selectOne(sql, [_id]);
|
|
261
301
|
return result ? this.parseFile(result) : void 0;
|
|
262
302
|
}
|
|
263
|
-
static findByDDocId(ddocId, portalAddress) {
|
|
303
|
+
static async findByDDocId(ddocId, portalAddress) {
|
|
264
304
|
const sql = `
|
|
265
305
|
SELECT *
|
|
266
|
-
FROM ${this.TABLE}
|
|
306
|
+
FROM ${this.TABLE}
|
|
267
307
|
WHERE ddocId = ? AND isDeleted = 0 AND portalAddress = ?
|
|
268
308
|
`;
|
|
269
|
-
const result = QueryBuilder.selectOne(sql, [ddocId, portalAddress]);
|
|
309
|
+
const result = await QueryBuilder.selectOne(sql, [ddocId, portalAddress]);
|
|
270
310
|
return result ? this.parseFile(result) : void 0;
|
|
271
311
|
}
|
|
272
|
-
static searchByTitle(searchTerm, portalAddress, limit, skip) {
|
|
312
|
+
static async searchByTitle(searchTerm, portalAddress, limit, skip) {
|
|
273
313
|
const sql = `
|
|
274
314
|
SELECT *
|
|
275
|
-
FROM ${this.TABLE}
|
|
315
|
+
FROM ${this.TABLE}
|
|
276
316
|
WHERE LOWER(title) LIKE LOWER(?) AND isDeleted = 0 AND portalAddress = ?
|
|
277
317
|
`;
|
|
278
318
|
const completeSql = QueryBuilder.paginate(sql, {
|
|
@@ -281,24 +321,24 @@ var FilesModel = class {
|
|
|
281
321
|
orderBy: "createdAt",
|
|
282
322
|
orderDirection: "DESC"
|
|
283
323
|
});
|
|
284
|
-
const filesRaw = QueryBuilder.select(completeSql, [`%${searchTerm}%`, portalAddress]);
|
|
324
|
+
const filesRaw = await QueryBuilder.select(completeSql, [`%${searchTerm}%`, portalAddress]);
|
|
285
325
|
return filesRaw.map(this.parseFile);
|
|
286
326
|
}
|
|
287
|
-
static create(input) {
|
|
327
|
+
static async create(input) {
|
|
288
328
|
const _id = uuidv7();
|
|
289
329
|
const sql = `
|
|
290
|
-
INSERT INTO ${this.TABLE}
|
|
291
|
-
(_id, title, content, ddocId, portalAddress)
|
|
330
|
+
INSERT INTO ${this.TABLE}
|
|
331
|
+
(_id, title, content, ddocId, portalAddress)
|
|
292
332
|
VALUES (?, ?, ?, ?, ?)
|
|
293
333
|
`;
|
|
294
|
-
QueryBuilder.execute(sql, [_id, input.title, input.content, input.ddocId, input.portalAddress]);
|
|
295
|
-
const created = this.findById(_id, input.portalAddress);
|
|
334
|
+
await QueryBuilder.execute(sql, [_id, input.title, input.content, input.ddocId, input.portalAddress]);
|
|
335
|
+
const created = await this.findById(_id, input.portalAddress);
|
|
296
336
|
if (!created) {
|
|
297
337
|
throw new Error("Failed to create file");
|
|
298
338
|
}
|
|
299
339
|
return created;
|
|
300
340
|
}
|
|
301
|
-
static update(_id, payload, portalAddress) {
|
|
341
|
+
static async update(_id, payload, portalAddress) {
|
|
302
342
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
303
343
|
const keys = [];
|
|
304
344
|
const values = [];
|
|
@@ -317,22 +357,22 @@ var FilesModel = class {
|
|
|
317
357
|
values.push(now, _id, portalAddress);
|
|
318
358
|
const updateChain = keys.join(", ");
|
|
319
359
|
const sql = `UPDATE ${this.TABLE} SET ${updateChain} WHERE _id = ? AND portalAddress = ?`;
|
|
320
|
-
QueryBuilder.execute(sql, values);
|
|
321
|
-
const updated = this.findById(_id, portalAddress);
|
|
360
|
+
await QueryBuilder.execute(sql, values);
|
|
361
|
+
const updated = await this.findById(_id, portalAddress);
|
|
322
362
|
if (!updated) {
|
|
323
363
|
throw new Error("Failed to update file");
|
|
324
364
|
}
|
|
325
365
|
return updated;
|
|
326
366
|
}
|
|
327
|
-
static softDelete(_id) {
|
|
367
|
+
static async softDelete(_id) {
|
|
328
368
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
329
369
|
const sql = `
|
|
330
|
-
UPDATE ${this.TABLE}
|
|
370
|
+
UPDATE ${this.TABLE}
|
|
331
371
|
SET isDeleted = 1, syncStatus = 'pending', updatedAt = ?
|
|
332
372
|
WHERE _id = ?
|
|
333
373
|
`;
|
|
334
|
-
QueryBuilder.execute(sql, [now, _id]);
|
|
335
|
-
const deleted = this.findByIdIncludingDeleted(_id);
|
|
374
|
+
await QueryBuilder.execute(sql, [now, _id]);
|
|
375
|
+
const deleted = await this.findByIdIncludingDeleted(_id);
|
|
336
376
|
if (!deleted) {
|
|
337
377
|
throw new Error("Failed to delete file");
|
|
338
378
|
}
|
|
@@ -347,12 +387,12 @@ import { uuidv7 as uuidv72 } from "uuidv7";
|
|
|
347
387
|
import { uuidv7 as uuidv73 } from "uuidv7";
|
|
348
388
|
var ApiKeysModel = class {
|
|
349
389
|
static TABLE = "api_keys";
|
|
350
|
-
static create(input) {
|
|
390
|
+
static async create(input) {
|
|
351
391
|
const _id = uuidv73();
|
|
352
392
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
353
|
-
const sql = `INSERT INTO ${this.TABLE} (_id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt)
|
|
393
|
+
const sql = `INSERT INTO ${this.TABLE} (_id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt)
|
|
354
394
|
VALUES (?, ?, ?, ?, ?, ?)`;
|
|
355
|
-
const result = QueryBuilder.execute(sql, [
|
|
395
|
+
const result = await QueryBuilder.execute(sql, [
|
|
356
396
|
_id,
|
|
357
397
|
input.apiKeySeed,
|
|
358
398
|
input.name,
|
|
@@ -363,29 +403,29 @@ var ApiKeysModel = class {
|
|
|
363
403
|
if (result.changes === 0) {
|
|
364
404
|
throw new Error("Failed to create API key");
|
|
365
405
|
}
|
|
366
|
-
const created = this.findById(_id);
|
|
406
|
+
const created = await this.findById(_id);
|
|
367
407
|
if (!created) {
|
|
368
408
|
throw new Error("Failed to create API key");
|
|
369
409
|
}
|
|
370
410
|
return created;
|
|
371
411
|
}
|
|
372
|
-
static findById(_id) {
|
|
412
|
+
static async findById(_id) {
|
|
373
413
|
const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE _id = ? AND isDeleted = 0`;
|
|
374
414
|
return QueryBuilder.selectOne(sql, [_id]);
|
|
375
415
|
}
|
|
376
|
-
static findByCollaboratorAddress(collaboratorAddress) {
|
|
416
|
+
static async findByCollaboratorAddress(collaboratorAddress) {
|
|
377
417
|
const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE collaboratorAddress = ? AND isDeleted = 0 LIMIT 1`;
|
|
378
418
|
return QueryBuilder.selectOne(sql, [collaboratorAddress]);
|
|
379
419
|
}
|
|
380
|
-
static delete(_id) {
|
|
420
|
+
static async delete(_id) {
|
|
381
421
|
const sql = `UPDATE ${this.TABLE} SET isDeleted = 1 WHERE _id = ?`;
|
|
382
|
-
QueryBuilder.execute(sql, [_id]);
|
|
422
|
+
await QueryBuilder.execute(sql, [_id]);
|
|
383
423
|
}
|
|
384
|
-
static findByPortalAddress(portalAddress) {
|
|
424
|
+
static async findByPortalAddress(portalAddress) {
|
|
385
425
|
const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE portalAddress = ? AND isDeleted = 0`;
|
|
386
426
|
return QueryBuilder.selectOne(sql, [portalAddress]);
|
|
387
427
|
}
|
|
388
|
-
static findByApiKey(apiKey) {
|
|
428
|
+
static async findByApiKey(apiKey) {
|
|
389
429
|
const sql = `SELECT _id, apiKeySeed, name, collaboratorAddress, portalAddress, createdAt, isDeleted FROM ${this.TABLE} WHERE apiKeySeed = ? AND isDeleted = 0`;
|
|
390
430
|
return QueryBuilder.selectOne(sql, [apiKey]);
|
|
391
431
|
}
|
|
@@ -408,16 +448,16 @@ function notifyNewEvent() {
|
|
|
408
448
|
var RETRY_DELAYS_MS = [5e3, 3e4, 12e4];
|
|
409
449
|
var EventsModel = class {
|
|
410
450
|
static TABLE = "events";
|
|
411
|
-
static create(input) {
|
|
451
|
+
static async create(input) {
|
|
412
452
|
const _id = uuidv74();
|
|
413
453
|
const timestamp = Date.now();
|
|
414
454
|
const status = "pending";
|
|
415
455
|
const sql = `
|
|
416
|
-
INSERT INTO ${this.TABLE}
|
|
417
|
-
(_id, type, timestamp, fileId, portalAddress, status, retryCount, lastError, lockedAt, nextRetryAt)
|
|
456
|
+
INSERT INTO ${this.TABLE}
|
|
457
|
+
(_id, type, timestamp, fileId, portalAddress, status, retryCount, lastError, lockedAt, nextRetryAt)
|
|
418
458
|
VALUES (?, ?, ?, ?, ?, ?, 0, NULL, NULL, NULL)
|
|
419
459
|
`;
|
|
420
|
-
QueryBuilder.execute(sql, [_id, input.type, timestamp, input.fileId, input.portalAddress, status]);
|
|
460
|
+
await QueryBuilder.execute(sql, [_id, input.type, timestamp, input.fileId, input.portalAddress, status]);
|
|
421
461
|
notifyNewEvent();
|
|
422
462
|
return {
|
|
423
463
|
_id,
|
|
@@ -432,22 +472,22 @@ var EventsModel = class {
|
|
|
432
472
|
nextRetryAt: null
|
|
433
473
|
};
|
|
434
474
|
}
|
|
435
|
-
static findById(_id) {
|
|
475
|
+
static async findById(_id) {
|
|
436
476
|
const sql = `SELECT * FROM ${this.TABLE} WHERE _id = ?`;
|
|
437
|
-
const row = QueryBuilder.selectOne(sql, [_id]);
|
|
477
|
+
const row = await QueryBuilder.selectOne(sql, [_id]);
|
|
438
478
|
return row ? this.parseEvent(row) : void 0;
|
|
439
479
|
}
|
|
440
|
-
static findNextPending() {
|
|
480
|
+
static async findNextPending() {
|
|
441
481
|
const sql = `
|
|
442
482
|
SELECT * FROM ${this.TABLE}
|
|
443
483
|
WHERE status = 'pending'
|
|
444
484
|
ORDER BY timestamp ASC
|
|
445
485
|
LIMIT 1
|
|
446
486
|
`;
|
|
447
|
-
const row = QueryBuilder.selectOne(sql, []);
|
|
487
|
+
const row = await QueryBuilder.selectOne(sql, []);
|
|
448
488
|
return row ? this.parseEvent(row) : void 0;
|
|
449
489
|
}
|
|
450
|
-
static findNextEligible(lockedFileIds) {
|
|
490
|
+
static async findNextEligible(lockedFileIds) {
|
|
451
491
|
const now = Date.now();
|
|
452
492
|
const exclusionClause = lockedFileIds.length > 0 ? `AND e1.fileId NOT IN (${lockedFileIds.map(() => "?").join(", ")})` : "";
|
|
453
493
|
const sql = `
|
|
@@ -465,29 +505,29 @@ var EventsModel = class {
|
|
|
465
505
|
LIMIT 1
|
|
466
506
|
`;
|
|
467
507
|
const params = [now, ...lockedFileIds];
|
|
468
|
-
const row = QueryBuilder.selectOne(sql, params);
|
|
508
|
+
const row = await QueryBuilder.selectOne(sql, params);
|
|
469
509
|
return row ? this.parseEvent(row) : void 0;
|
|
470
510
|
}
|
|
471
|
-
static markProcessing(_id) {
|
|
511
|
+
static async markProcessing(_id) {
|
|
472
512
|
const sql = `
|
|
473
513
|
UPDATE ${this.TABLE}
|
|
474
514
|
SET status = 'processing',
|
|
475
515
|
lockedAt = ?
|
|
476
516
|
WHERE _id = ?
|
|
477
517
|
`;
|
|
478
|
-
QueryBuilder.execute(sql, [Date.now(), _id]);
|
|
518
|
+
await QueryBuilder.execute(sql, [Date.now(), _id]);
|
|
479
519
|
}
|
|
480
|
-
static markProcessed(_id) {
|
|
520
|
+
static async markProcessed(_id) {
|
|
481
521
|
const sql = `
|
|
482
522
|
UPDATE ${this.TABLE}
|
|
483
523
|
SET status = 'processed',
|
|
484
524
|
lockedAt = NULL
|
|
485
525
|
WHERE _id = ?
|
|
486
526
|
`;
|
|
487
|
-
QueryBuilder.execute(sql, [_id]);
|
|
527
|
+
await QueryBuilder.execute(sql, [_id]);
|
|
488
528
|
}
|
|
489
|
-
static scheduleRetry(_id, errorMsg) {
|
|
490
|
-
const event = this.findById(_id);
|
|
529
|
+
static async scheduleRetry(_id, errorMsg) {
|
|
530
|
+
const event = await this.findById(_id);
|
|
491
531
|
if (!event) return;
|
|
492
532
|
const delay = RETRY_DELAYS_MS[Math.min(event.retryCount, RETRY_DELAYS_MS.length - 1)];
|
|
493
533
|
const nextRetryAt = Date.now() + delay;
|
|
@@ -500,9 +540,9 @@ var EventsModel = class {
|
|
|
500
540
|
lockedAt = NULL
|
|
501
541
|
WHERE _id = ?
|
|
502
542
|
`;
|
|
503
|
-
QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
|
|
543
|
+
await QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
|
|
504
544
|
}
|
|
505
|
-
static scheduleRetryAfter(_id, errorMsg, retryAfterMs) {
|
|
545
|
+
static async scheduleRetryAfter(_id, errorMsg, retryAfterMs) {
|
|
506
546
|
const nextRetryAt = Date.now() + retryAfterMs;
|
|
507
547
|
const sql = `
|
|
508
548
|
UPDATE ${this.TABLE}
|
|
@@ -512,9 +552,9 @@ var EventsModel = class {
|
|
|
512
552
|
lockedAt = NULL
|
|
513
553
|
WHERE _id = ?
|
|
514
554
|
`;
|
|
515
|
-
QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
|
|
555
|
+
await QueryBuilder.execute(sql, [errorMsg, nextRetryAt, _id]);
|
|
516
556
|
}
|
|
517
|
-
static markFailed(_id, errorMsg) {
|
|
557
|
+
static async markFailed(_id, errorMsg) {
|
|
518
558
|
const sql = `
|
|
519
559
|
UPDATE ${this.TABLE}
|
|
520
560
|
SET status = 'failed',
|
|
@@ -522,9 +562,9 @@ var EventsModel = class {
|
|
|
522
562
|
lockedAt = NULL
|
|
523
563
|
WHERE _id = ?
|
|
524
564
|
`;
|
|
525
|
-
QueryBuilder.execute(sql, [errorMsg, _id]);
|
|
565
|
+
await QueryBuilder.execute(sql, [errorMsg, _id]);
|
|
526
566
|
}
|
|
527
|
-
static listFailed(portalAddress) {
|
|
567
|
+
static async listFailed(portalAddress) {
|
|
528
568
|
const portalClause = portalAddress != null ? "AND portalAddress = ?" : "";
|
|
529
569
|
const sql = `
|
|
530
570
|
SELECT * FROM ${this.TABLE}
|
|
@@ -533,10 +573,10 @@ var EventsModel = class {
|
|
|
533
573
|
ORDER BY timestamp ASC
|
|
534
574
|
`;
|
|
535
575
|
const params = portalAddress != null ? [portalAddress] : [];
|
|
536
|
-
const rows = QueryBuilder.select(sql, params);
|
|
576
|
+
const rows = await QueryBuilder.select(sql, params);
|
|
537
577
|
return rows.map((row) => this.parseEvent(row));
|
|
538
578
|
}
|
|
539
|
-
static resetFailedToPending(_id, portalAddress) {
|
|
579
|
+
static async resetFailedToPending(_id, portalAddress) {
|
|
540
580
|
const portalClause = portalAddress != null ? "AND portalAddress = ?" : "";
|
|
541
581
|
const sql = `
|
|
542
582
|
UPDATE ${this.TABLE}
|
|
@@ -550,13 +590,13 @@ var EventsModel = class {
|
|
|
550
590
|
${portalClause}
|
|
551
591
|
`;
|
|
552
592
|
const params = portalAddress != null ? [_id, portalAddress] : [_id];
|
|
553
|
-
const result = QueryBuilder.execute(sql, params);
|
|
593
|
+
const result = await QueryBuilder.execute(sql, params);
|
|
554
594
|
if (result.changes > 0) {
|
|
555
595
|
notifyNewEvent();
|
|
556
596
|
}
|
|
557
597
|
return result.changes > 0;
|
|
558
598
|
}
|
|
559
|
-
static resetAllFailedToPending(portalAddress) {
|
|
599
|
+
static async resetAllFailedToPending(portalAddress) {
|
|
560
600
|
const portalClause = portalAddress != null ? "AND portalAddress = ?" : "";
|
|
561
601
|
const sql = `
|
|
562
602
|
UPDATE ${this.TABLE}
|
|
@@ -569,13 +609,13 @@ var EventsModel = class {
|
|
|
569
609
|
${portalClause}
|
|
570
610
|
`;
|
|
571
611
|
const params = portalAddress != null ? [portalAddress] : [];
|
|
572
|
-
const result = QueryBuilder.execute(sql, params);
|
|
612
|
+
const result = await QueryBuilder.execute(sql, params);
|
|
573
613
|
if (result.changes > 0) {
|
|
574
614
|
notifyNewEvent();
|
|
575
615
|
}
|
|
576
616
|
return result.changes;
|
|
577
617
|
}
|
|
578
|
-
static resetStaleEvents(staleThreshold) {
|
|
618
|
+
static async resetStaleEvents(staleThreshold) {
|
|
579
619
|
const sql = `
|
|
580
620
|
UPDATE ${this.TABLE}
|
|
581
621
|
SET status = 'pending',
|
|
@@ -586,16 +626,16 @@ var EventsModel = class {
|
|
|
586
626
|
AND lockedAt IS NOT NULL
|
|
587
627
|
AND lockedAt < ?
|
|
588
628
|
`;
|
|
589
|
-
const result = QueryBuilder.execute(sql, [staleThreshold]);
|
|
629
|
+
const result = await QueryBuilder.execute(sql, [staleThreshold]);
|
|
590
630
|
return result.changes;
|
|
591
631
|
}
|
|
592
|
-
static setEventPendingOp(_id, userOpHash, payload) {
|
|
632
|
+
static async setEventPendingOp(_id, userOpHash, payload) {
|
|
593
633
|
const sql = `UPDATE ${this.TABLE} SET userOpHash = ?, pendingPayload = ? WHERE _id = ?`;
|
|
594
|
-
QueryBuilder.execute(sql, [userOpHash, JSON.stringify(payload), _id]);
|
|
634
|
+
await QueryBuilder.execute(sql, [userOpHash, JSON.stringify(payload), _id]);
|
|
595
635
|
}
|
|
596
|
-
static clearEventPendingOp(_id) {
|
|
636
|
+
static async clearEventPendingOp(_id) {
|
|
597
637
|
const sql = `UPDATE ${this.TABLE} SET userOpHash = NULL, pendingPayload = NULL WHERE _id = ?`;
|
|
598
|
-
QueryBuilder.execute(sql, [_id]);
|
|
638
|
+
await QueryBuilder.execute(sql, [_id]);
|
|
599
639
|
}
|
|
600
640
|
static parseEvent(row) {
|
|
601
641
|
return {
|
|
@@ -701,96 +741,219 @@ var Reporter = class {
|
|
|
701
741
|
};
|
|
702
742
|
var reporter_default = new Reporter();
|
|
703
743
|
|
|
704
|
-
// src/infra/database/
|
|
705
|
-
var
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
}
|
|
716
|
-
getConnection() {
|
|
717
|
-
if (!this.db) {
|
|
718
|
-
const dbPath = config.DB_PATH;
|
|
719
|
-
this.db = new Database(dbPath, {
|
|
720
|
-
verbose: config.NODE_ENV === "development" ? (msg) => logger.debug(String(msg)) : void 0
|
|
721
|
-
});
|
|
722
|
-
this.db.pragma("journal_mode = WAL");
|
|
723
|
-
this.db.pragma("foreign_keys = ON");
|
|
724
|
-
this.db.prepare("SELECT 1").get();
|
|
725
|
-
logger.info(`SQLite database connected: ${dbPath}`);
|
|
726
|
-
}
|
|
727
|
-
return this.db;
|
|
728
|
-
}
|
|
729
|
-
async close() {
|
|
730
|
-
if (this.db) {
|
|
731
|
-
this.db.close();
|
|
732
|
-
this.db = null;
|
|
733
|
-
logger.info("Database connection closed");
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
isConnected() {
|
|
737
|
-
return this.db !== null && this.db.open;
|
|
744
|
+
// src/infra/database/adapters/sqlite-adapter.ts
|
|
745
|
+
var SqliteAdapter = class {
|
|
746
|
+
db;
|
|
747
|
+
constructor(dbPath) {
|
|
748
|
+
this.db = new Database(dbPath, {
|
|
749
|
+
verbose: config.NODE_ENV === "development" ? (msg) => logger.debug(String(msg)) : void 0
|
|
750
|
+
});
|
|
751
|
+
this.db.pragma("journal_mode = WAL");
|
|
752
|
+
this.db.pragma("foreign_keys = ON");
|
|
753
|
+
this.db.prepare("SELECT 1").get();
|
|
754
|
+
logger.info(`SQLite database connected: ${dbPath}`);
|
|
738
755
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
// src/domain/file/constants.ts
|
|
743
|
-
var DEFAULT_LIST_LIMIT = 10;
|
|
744
|
-
|
|
745
|
-
// src/infra/database/query-builder.ts
|
|
746
|
-
function getDb() {
|
|
747
|
-
return databaseConnectionManager.getConnection();
|
|
748
|
-
}
|
|
749
|
-
var QueryBuilder = class {
|
|
750
|
-
static select(sql, params = []) {
|
|
751
|
-
const stmt = getDb().prepare(sql);
|
|
756
|
+
async select(sql, params = []) {
|
|
757
|
+
const stmt = this.db.prepare(sql);
|
|
752
758
|
return stmt.all(params);
|
|
753
759
|
}
|
|
754
|
-
|
|
755
|
-
const stmt =
|
|
760
|
+
async selectOne(sql, params = []) {
|
|
761
|
+
const stmt = this.db.prepare(sql);
|
|
756
762
|
return stmt.get(params);
|
|
757
763
|
}
|
|
758
|
-
|
|
759
|
-
const stmt =
|
|
764
|
+
async execute(sql, params = []) {
|
|
765
|
+
const stmt = this.db.prepare(sql);
|
|
760
766
|
const result = stmt.run(params);
|
|
761
767
|
return {
|
|
762
768
|
changes: result.changes,
|
|
763
769
|
lastInsertRowid: result.lastInsertRowid
|
|
764
770
|
};
|
|
765
771
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
772
|
+
async transaction(callback) {
|
|
773
|
+
this.db.exec("SAVEPOINT txn");
|
|
774
|
+
try {
|
|
775
|
+
const result = await callback(this);
|
|
776
|
+
this.db.exec("RELEASE txn");
|
|
777
|
+
return result;
|
|
778
|
+
} catch (err) {
|
|
779
|
+
this.db.exec("ROLLBACK TO txn");
|
|
780
|
+
throw err;
|
|
773
781
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
782
|
+
}
|
|
783
|
+
async exec(sql) {
|
|
784
|
+
this.db.exec(sql);
|
|
785
|
+
}
|
|
786
|
+
async close() {
|
|
787
|
+
this.db.close();
|
|
788
|
+
logger.info("Database connection closed");
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
// src/infra/database/adapters/pg-adapter.ts
|
|
793
|
+
import pg from "pg";
|
|
794
|
+
var { Pool } = pg;
|
|
795
|
+
var COLUMN_NAME_MAP = {
|
|
796
|
+
ddocid: "ddocId",
|
|
797
|
+
localversion: "localVersion",
|
|
798
|
+
onchainversion: "onchainVersion",
|
|
799
|
+
syncstatus: "syncStatus",
|
|
800
|
+
isdeleted: "isDeleted",
|
|
801
|
+
onchainfileid: "onChainFileId",
|
|
802
|
+
portaladdress: "portalAddress",
|
|
803
|
+
createdat: "createdAt",
|
|
804
|
+
updatedat: "updatedAt",
|
|
805
|
+
linkkey: "linkKey",
|
|
806
|
+
linkkeynonce: "linkKeyNonce",
|
|
807
|
+
commentkey: "commentKey",
|
|
808
|
+
portalseed: "portalSeed",
|
|
809
|
+
owneraddress: "ownerAddress",
|
|
810
|
+
apikeyseed: "apiKeySeed",
|
|
811
|
+
collaboratoraddress: "collaboratorAddress",
|
|
812
|
+
fileid: "fileId",
|
|
813
|
+
retrycount: "retryCount",
|
|
814
|
+
lasterror: "lastError",
|
|
815
|
+
lockedat: "lockedAt",
|
|
816
|
+
nextretryat: "nextRetryAt",
|
|
817
|
+
userophash: "userOpHash",
|
|
818
|
+
pendingpayload: "pendingPayload",
|
|
819
|
+
folderid: "folderId",
|
|
820
|
+
folderref: "folderRef",
|
|
821
|
+
foldername: "folderName",
|
|
822
|
+
metadataipfshash: "metadataIPFSHash",
|
|
823
|
+
contentipfshash: "contentIPFSHash",
|
|
824
|
+
lasttransactionhash: "lastTransactionHash",
|
|
825
|
+
lasttransactionblocknumber: "lastTransactionBlockNumber",
|
|
826
|
+
lasttransactionblocktimestamp: "lastTransactionBlockTimestamp",
|
|
827
|
+
created_at: "created_at",
|
|
828
|
+
updated_at: "updated_at"
|
|
829
|
+
};
|
|
830
|
+
function translateParams(sql) {
|
|
831
|
+
let idx = 0;
|
|
832
|
+
return sql.replace(/\?/g, () => `$${++idx}`);
|
|
833
|
+
}
|
|
834
|
+
function mapRow(row) {
|
|
835
|
+
const mapped = {};
|
|
836
|
+
for (const [key, value] of Object.entries(row)) {
|
|
837
|
+
const mappedKey = COLUMN_NAME_MAP[key] ?? key;
|
|
838
|
+
mapped[mappedKey] = value instanceof Date ? value.toISOString() : value;
|
|
839
|
+
}
|
|
840
|
+
return mapped;
|
|
841
|
+
}
|
|
842
|
+
var PgClientAdapter = class {
|
|
843
|
+
constructor(client) {
|
|
844
|
+
this.client = client;
|
|
845
|
+
}
|
|
846
|
+
async select(sql, params = []) {
|
|
847
|
+
const result = await this.client.query(translateParams(sql), params);
|
|
848
|
+
return result.rows.map((row) => mapRow(row));
|
|
849
|
+
}
|
|
850
|
+
async selectOne(sql, params = []) {
|
|
851
|
+
const result = await this.client.query(translateParams(sql), params);
|
|
852
|
+
return result.rows[0] ? mapRow(result.rows[0]) : void 0;
|
|
853
|
+
}
|
|
854
|
+
async execute(sql, params = []) {
|
|
855
|
+
const result = await this.client.query(translateParams(sql), params);
|
|
856
|
+
return { changes: result.rowCount ?? 0, lastInsertRowid: 0 };
|
|
857
|
+
}
|
|
858
|
+
async transaction(callback) {
|
|
859
|
+
await this.client.query("SAVEPOINT nested_txn");
|
|
860
|
+
try {
|
|
861
|
+
const result = await callback(this);
|
|
862
|
+
await this.client.query("RELEASE SAVEPOINT nested_txn");
|
|
863
|
+
return result;
|
|
864
|
+
} catch (err) {
|
|
865
|
+
await this.client.query("ROLLBACK TO SAVEPOINT nested_txn");
|
|
866
|
+
throw err;
|
|
778
867
|
}
|
|
779
|
-
|
|
780
|
-
|
|
868
|
+
}
|
|
869
|
+
async exec(sql) {
|
|
870
|
+
await this.client.query(sql);
|
|
871
|
+
}
|
|
872
|
+
async close() {
|
|
873
|
+
}
|
|
874
|
+
};
|
|
875
|
+
var PgAdapter = class {
|
|
876
|
+
pool;
|
|
877
|
+
constructor(connectionString) {
|
|
878
|
+
const url = new URL(connectionString);
|
|
879
|
+
const isLocal = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1";
|
|
880
|
+
const sslDisabled = connectionString.includes("sslmode=disable");
|
|
881
|
+
const needsSsl = !isLocal && !sslDisabled;
|
|
882
|
+
this.pool = new Pool({
|
|
883
|
+
connectionString,
|
|
884
|
+
// pg requires password to be a string; local trust/peer auth uses empty string
|
|
885
|
+
password: url.password || "",
|
|
886
|
+
max: 20,
|
|
887
|
+
idleTimeoutMillis: 3e4,
|
|
888
|
+
ssl: needsSsl ? { rejectUnauthorized: false } : void 0
|
|
889
|
+
});
|
|
890
|
+
logger.info(`PostgreSQL pool created (ssl: ${needsSsl ? "on" : "off"})`);
|
|
891
|
+
}
|
|
892
|
+
async select(sql, params = []) {
|
|
893
|
+
const result = await this.pool.query(translateParams(sql), params);
|
|
894
|
+
return result.rows.map((row) => mapRow(row));
|
|
895
|
+
}
|
|
896
|
+
async selectOne(sql, params = []) {
|
|
897
|
+
const result = await this.pool.query(translateParams(sql), params);
|
|
898
|
+
return result.rows[0] ? mapRow(result.rows[0]) : void 0;
|
|
899
|
+
}
|
|
900
|
+
async execute(sql, params = []) {
|
|
901
|
+
const result = await this.pool.query(translateParams(sql), params);
|
|
902
|
+
return { changes: result.rowCount ?? 0, lastInsertRowid: 0 };
|
|
903
|
+
}
|
|
904
|
+
async transaction(callback) {
|
|
905
|
+
const client = await this.pool.connect();
|
|
906
|
+
try {
|
|
907
|
+
await client.query("BEGIN");
|
|
908
|
+
const clientAdapter = new PgClientAdapter(client);
|
|
909
|
+
const result = await callback(clientAdapter);
|
|
910
|
+
await client.query("COMMIT");
|
|
911
|
+
return result;
|
|
912
|
+
} catch (err) {
|
|
913
|
+
await client.query("ROLLBACK");
|
|
914
|
+
throw err;
|
|
915
|
+
} finally {
|
|
916
|
+
client.release();
|
|
781
917
|
}
|
|
782
|
-
|
|
918
|
+
}
|
|
919
|
+
async exec(sql) {
|
|
920
|
+
await this.pool.query(sql);
|
|
921
|
+
}
|
|
922
|
+
async close() {
|
|
923
|
+
await this.pool.end();
|
|
924
|
+
logger.info("PostgreSQL pool closed");
|
|
783
925
|
}
|
|
784
926
|
};
|
|
785
927
|
|
|
786
|
-
// src/infra/database/
|
|
787
|
-
|
|
788
|
-
|
|
928
|
+
// src/infra/database/connection.ts
|
|
929
|
+
var adapter = null;
|
|
930
|
+
async function getAdapter() {
|
|
931
|
+
if (adapter) return adapter;
|
|
932
|
+
const databaseUrl = config.DATABASE_URL;
|
|
933
|
+
const dbPath = config.DB_PATH;
|
|
934
|
+
if (databaseUrl) {
|
|
935
|
+
adapter = new PgAdapter(databaseUrl);
|
|
936
|
+
} else if (dbPath) {
|
|
937
|
+
adapter = new SqliteAdapter(dbPath);
|
|
938
|
+
} else {
|
|
939
|
+
throw new Error("Either DATABASE_URL or DB_PATH must be set");
|
|
940
|
+
}
|
|
941
|
+
return adapter;
|
|
942
|
+
}
|
|
943
|
+
function getAdapterSync() {
|
|
944
|
+
if (!adapter) {
|
|
945
|
+
throw new Error(
|
|
946
|
+
"Database adapter not initialized. Call getAdapter() at startup first."
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
return adapter;
|
|
950
|
+
}
|
|
951
|
+
async function closeAdapter() {
|
|
952
|
+
if (adapter) {
|
|
953
|
+
await adapter.close();
|
|
954
|
+
adapter = null;
|
|
955
|
+
}
|
|
789
956
|
}
|
|
790
|
-
var closeDatabase = async () => {
|
|
791
|
-
await databaseConnectionManager.close();
|
|
792
|
-
};
|
|
793
|
-
var database_default = getDb2;
|
|
794
957
|
|
|
795
958
|
// src/infra/database/migrations/index.ts
|
|
796
959
|
var STABLE_SCHEMA = `
|
|
@@ -802,8 +965,8 @@ CREATE TABLE IF NOT EXISTS files (
|
|
|
802
965
|
localVersion INTEGER NOT NULL DEFAULT 1,
|
|
803
966
|
onchainVersion INTEGER NOT NULL DEFAULT 0,
|
|
804
967
|
syncStatus TEXT NOT NULL DEFAULT 'pending',
|
|
805
|
-
createdAt
|
|
806
|
-
updatedAt
|
|
968
|
+
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
969
|
+
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
807
970
|
isDeleted INTEGER NOT NULL DEFAULT 0,
|
|
808
971
|
portalAddress TEXT NOT NULL,
|
|
809
972
|
metadata TEXT DEFAULT '{}',
|
|
@@ -823,8 +986,8 @@ CREATE TABLE IF NOT EXISTS portals (
|
|
|
823
986
|
portalAddress TEXT NOT NULL UNIQUE,
|
|
824
987
|
portalSeed TEXT NOT NULL UNIQUE,
|
|
825
988
|
ownerAddress TEXT NOT NULL,
|
|
826
|
-
createdAt
|
|
827
|
-
updatedAt
|
|
989
|
+
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
990
|
+
updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
828
991
|
);
|
|
829
992
|
|
|
830
993
|
CREATE TABLE IF NOT EXISTS api_keys (
|
|
@@ -833,7 +996,7 @@ CREATE TABLE IF NOT EXISTS api_keys (
|
|
|
833
996
|
name TEXT NOT NULL,
|
|
834
997
|
collaboratorAddress TEXT NOT NULL UNIQUE,
|
|
835
998
|
portalAddress TEXT NOT NULL,
|
|
836
|
-
createdAt
|
|
999
|
+
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
837
1000
|
isDeleted INTEGER NOT NULL DEFAULT 0
|
|
838
1001
|
);
|
|
839
1002
|
|
|
@@ -869,16 +1032,16 @@ CREATE TABLE IF NOT EXISTS folders (
|
|
|
869
1032
|
lastTransactionHash TEXT,
|
|
870
1033
|
lastTransactionBlockNumber INTEGER NOT NULL,
|
|
871
1034
|
lastTransactionBlockTimestamp INTEGER NOT NULL,
|
|
872
|
-
created_at
|
|
873
|
-
updated_at
|
|
1035
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
1036
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
874
1037
|
);
|
|
875
1038
|
CREATE INDEX IF NOT EXISTS idx_folders_folderRef_folderId ON folders(folderRef, folderId);
|
|
876
1039
|
CREATE INDEX IF NOT EXISTS idx_folders_folderRef ON folders(folderRef);
|
|
877
1040
|
CREATE INDEX IF NOT EXISTS idx_folders_created_at ON folders(created_at);
|
|
878
1041
|
`;
|
|
879
|
-
function runMigrations() {
|
|
880
|
-
const
|
|
881
|
-
|
|
1042
|
+
async function runMigrations() {
|
|
1043
|
+
const adapter2 = getAdapterSync();
|
|
1044
|
+
await adapter2.exec(STABLE_SCHEMA);
|
|
882
1045
|
logger.debug("Database schema ready");
|
|
883
1046
|
}
|
|
884
1047
|
|
|
@@ -888,10 +1051,10 @@ import Table from "cli-table3";
|
|
|
888
1051
|
|
|
889
1052
|
// src/domain/file/index.ts
|
|
890
1053
|
import { generate } from "short-uuid";
|
|
891
|
-
function listFiles(params) {
|
|
1054
|
+
async function listFiles(params) {
|
|
892
1055
|
const { limit, skip, portalAddress } = params;
|
|
893
1056
|
const effectiveLimit = limit || DEFAULT_LIST_LIMIT;
|
|
894
|
-
const result = FilesModel.findAll(portalAddress, effectiveLimit, skip);
|
|
1057
|
+
const result = await FilesModel.findAll(portalAddress, effectiveLimit, skip);
|
|
895
1058
|
const processedFiles = result.files.map((file) => ({
|
|
896
1059
|
ddocId: file.ddocId,
|
|
897
1060
|
link: file.link,
|
|
@@ -912,11 +1075,11 @@ function listFiles(params) {
|
|
|
912
1075
|
hasNext: result.hasNext
|
|
913
1076
|
};
|
|
914
1077
|
}
|
|
915
|
-
function getFile(ddocId, portalAddress) {
|
|
1078
|
+
async function getFile(ddocId, portalAddress) {
|
|
916
1079
|
if (!ddocId) {
|
|
917
1080
|
throw new Error("ddocId is required");
|
|
918
1081
|
}
|
|
919
|
-
const file = FilesModel.findByDDocId(ddocId, portalAddress);
|
|
1082
|
+
const file = await FilesModel.findByDDocId(ddocId, portalAddress);
|
|
920
1083
|
if (!file) {
|
|
921
1084
|
return null;
|
|
922
1085
|
}
|
|
@@ -940,13 +1103,13 @@ var createFile = async (input) => {
|
|
|
940
1103
|
throw new Error("title, content, and portalAddress are required");
|
|
941
1104
|
}
|
|
942
1105
|
const ddocId = generate();
|
|
943
|
-
const file = FilesModel.create({
|
|
1106
|
+
const file = await FilesModel.create({
|
|
944
1107
|
title: input.title,
|
|
945
1108
|
content: input.content,
|
|
946
1109
|
ddocId,
|
|
947
1110
|
portalAddress: input.portalAddress
|
|
948
1111
|
});
|
|
949
|
-
EventsModel.create({ type: "create", fileId: file._id, portalAddress: file.portalAddress });
|
|
1112
|
+
await EventsModel.create({ type: "create", fileId: file._id, portalAddress: file.portalAddress });
|
|
950
1113
|
return file;
|
|
951
1114
|
};
|
|
952
1115
|
var updateFile = async (ddocId, payload, portalAddress) => {
|
|
@@ -956,7 +1119,7 @@ var updateFile = async (ddocId, payload, portalAddress) => {
|
|
|
956
1119
|
if (!payload.title && !payload.content) {
|
|
957
1120
|
throw new Error("At least one field is required: Either provide title, content, or both");
|
|
958
1121
|
}
|
|
959
|
-
const existingFile = FilesModel.findByDDocId(ddocId, portalAddress);
|
|
1122
|
+
const existingFile = await FilesModel.findByDDocId(ddocId, portalAddress);
|
|
960
1123
|
if (!existingFile) {
|
|
961
1124
|
throw new Error(`File with ddocId ${ddocId} not found`);
|
|
962
1125
|
}
|
|
@@ -966,8 +1129,8 @@ var updateFile = async (ddocId, payload, portalAddress) => {
|
|
|
966
1129
|
syncStatus: "pending"
|
|
967
1130
|
// since the update is done in local db, it's not on the chain yet. hence pending
|
|
968
1131
|
};
|
|
969
|
-
const updatedFile = FilesModel.update(existingFile._id, updatePayload, portalAddress);
|
|
970
|
-
EventsModel.create({ type: "update", fileId: updatedFile._id, portalAddress: updatedFile.portalAddress });
|
|
1132
|
+
const updatedFile = await FilesModel.update(existingFile._id, updatePayload, portalAddress);
|
|
1133
|
+
await EventsModel.create({ type: "update", fileId: updatedFile._id, portalAddress: updatedFile.portalAddress });
|
|
971
1134
|
return {
|
|
972
1135
|
ddocId: updatedFile.ddocId,
|
|
973
1136
|
link: updatedFile.link,
|
|
@@ -987,12 +1150,12 @@ var deleteFile = async (ddocId, portalAddress) => {
|
|
|
987
1150
|
if (!ddocId) {
|
|
988
1151
|
throw new Error("ddocId is required");
|
|
989
1152
|
}
|
|
990
|
-
const existingFile = FilesModel.findByDDocId(ddocId, portalAddress);
|
|
1153
|
+
const existingFile = await FilesModel.findByDDocId(ddocId, portalAddress);
|
|
991
1154
|
if (!existingFile) {
|
|
992
1155
|
throw new Error(`File with ddocId ${ddocId} not found`);
|
|
993
1156
|
}
|
|
994
|
-
const deletedFile = FilesModel.softDelete(existingFile._id);
|
|
995
|
-
EventsModel.create({ type: "delete", fileId: deletedFile._id, portalAddress: deletedFile.portalAddress });
|
|
1157
|
+
const deletedFile = await FilesModel.softDelete(existingFile._id);
|
|
1158
|
+
await EventsModel.create({ type: "delete", fileId: deletedFile._id, portalAddress: deletedFile.portalAddress });
|
|
996
1159
|
return deletedFile;
|
|
997
1160
|
};
|
|
998
1161
|
|
|
@@ -1078,14 +1241,14 @@ var listCommand = new Command().name("list").description("List all ddocs").optio
|
|
|
1078
1241
|
const runtimeConfig = getRuntimeConfig();
|
|
1079
1242
|
const apiKey = runtimeConfig.API_KEY;
|
|
1080
1243
|
validateApiKey(apiKey);
|
|
1081
|
-
const portalAddress = ApiKeysModel.findByApiKey(apiKey)?.portalAddress;
|
|
1244
|
+
const portalAddress = (await ApiKeysModel.findByApiKey(apiKey))?.portalAddress;
|
|
1082
1245
|
if (!portalAddress) throw new Error("Portal address is required");
|
|
1083
1246
|
const params = {
|
|
1084
1247
|
limit: options.limit,
|
|
1085
1248
|
skip: options.skip,
|
|
1086
1249
|
portalAddress
|
|
1087
1250
|
};
|
|
1088
|
-
const result = listFiles(params);
|
|
1251
|
+
const result = await listFiles(params);
|
|
1089
1252
|
if (result.ddocs.length === 0) {
|
|
1090
1253
|
console.log("No ddocs found.");
|
|
1091
1254
|
return;
|
|
@@ -1147,9 +1310,9 @@ var getCommand = new Command2().name("get").description("Get a ddoc by its ID").
|
|
|
1147
1310
|
const runtimeConfig = getRuntimeConfig();
|
|
1148
1311
|
const apiKey = runtimeConfig.API_KEY;
|
|
1149
1312
|
validateApiKey(apiKey);
|
|
1150
|
-
const portalAddress = ApiKeysModel.findByApiKey(apiKey)?.portalAddress;
|
|
1313
|
+
const portalAddress = (await ApiKeysModel.findByApiKey(apiKey))?.portalAddress;
|
|
1151
1314
|
if (!portalAddress) throw new Error("Portal address is required");
|
|
1152
|
-
const file = getFile(ddocId, portalAddress);
|
|
1315
|
+
const file = await getFile(ddocId, portalAddress);
|
|
1153
1316
|
if (!file) {
|
|
1154
1317
|
console.error(`Ddoc with ID "${ddocId}" not found.`);
|
|
1155
1318
|
return;
|
|
@@ -1214,7 +1377,7 @@ var createCommand = new Command3().name("create").description("Create a new ddoc
|
|
|
1214
1377
|
const runtimeConfig = getRuntimeConfig();
|
|
1215
1378
|
const apiKey = runtimeConfig.API_KEY;
|
|
1216
1379
|
validateApiKey(apiKey);
|
|
1217
|
-
const portalAddress = ApiKeysModel.findByApiKey(apiKey)?.portalAddress;
|
|
1380
|
+
const portalAddress = (await ApiKeysModel.findByApiKey(apiKey))?.portalAddress;
|
|
1218
1381
|
if (!portalAddress) throw new Error("Portal address is required");
|
|
1219
1382
|
const content = fs2.readFileSync(filepath, "utf-8");
|
|
1220
1383
|
if (!content || content.trim().length === 0) {
|
|
@@ -1310,9 +1473,9 @@ var updateCommand = new Command4().name("update").description("Update an existin
|
|
|
1310
1473
|
const runtimeConfig = getRuntimeConfig();
|
|
1311
1474
|
const apiKey = runtimeConfig.API_KEY;
|
|
1312
1475
|
validateApiKey(apiKey);
|
|
1313
|
-
const portalAddress = ApiKeysModel.findByApiKey(apiKey)?.portalAddress;
|
|
1476
|
+
const portalAddress = (await ApiKeysModel.findByApiKey(apiKey))?.portalAddress;
|
|
1314
1477
|
if (!portalAddress) throw new Error("Portal address is required");
|
|
1315
|
-
const file = getFile(ddocId, portalAddress);
|
|
1478
|
+
const file = await getFile(ddocId, portalAddress);
|
|
1316
1479
|
if (!file) {
|
|
1317
1480
|
throw new Error(`ddoc with ${ddocId} not found.`);
|
|
1318
1481
|
}
|
|
@@ -1366,7 +1529,7 @@ var deleteCommand = new Command5().name("delete").description("Delete one or mor
|
|
|
1366
1529
|
const runtimeConfig = getRuntimeConfig();
|
|
1367
1530
|
const apiKey = runtimeConfig.API_KEY;
|
|
1368
1531
|
validateApiKey(apiKey);
|
|
1369
|
-
const portalAddress = ApiKeysModel.findByApiKey(apiKey)?.portalAddress;
|
|
1532
|
+
const portalAddress = (await ApiKeysModel.findByApiKey(apiKey))?.portalAddress;
|
|
1370
1533
|
if (!portalAddress) throw new Error("Portal address is required");
|
|
1371
1534
|
for (const ddocId of ddocIds) {
|
|
1372
1535
|
try {
|
|
@@ -1390,9 +1553,9 @@ var downloadCommand = new Command6().name("download").description("Download a dd
|
|
|
1390
1553
|
const runtimeConfig = getRuntimeConfig();
|
|
1391
1554
|
const apiKey = runtimeConfig.API_KEY;
|
|
1392
1555
|
validateApiKey(apiKey);
|
|
1393
|
-
const portalAddress = ApiKeysModel.findByApiKey(apiKey)?.portalAddress;
|
|
1556
|
+
const portalAddress = (await ApiKeysModel.findByApiKey(apiKey))?.portalAddress;
|
|
1394
1557
|
if (!portalAddress) throw new Error("Portal address is required");
|
|
1395
|
-
const file = getFile(ddocId, portalAddress);
|
|
1558
|
+
const file = await getFile(ddocId, portalAddress);
|
|
1396
1559
|
if (!file) {
|
|
1397
1560
|
console.error(`Ddoc with ID "${ddocId}" not found.`);
|
|
1398
1561
|
return;
|
|
@@ -1418,9 +1581,9 @@ var viewCommand = new Command7().name("view").description("View content preview
|
|
|
1418
1581
|
const runtimeConfig = getRuntimeConfig();
|
|
1419
1582
|
const apiKey = runtimeConfig.API_KEY;
|
|
1420
1583
|
validateApiKey(apiKey);
|
|
1421
|
-
const portalAddress = ApiKeysModel.findByApiKey(apiKey)?.portalAddress;
|
|
1584
|
+
const portalAddress = (await ApiKeysModel.findByApiKey(apiKey))?.portalAddress;
|
|
1422
1585
|
if (!portalAddress) throw new Error("Portal address is required");
|
|
1423
|
-
const file = getFile(ddocId, portalAddress);
|
|
1586
|
+
const file = await getFile(ddocId, portalAddress);
|
|
1424
1587
|
if (!file) {
|
|
1425
1588
|
console.error(`Ddoc with ID "${ddocId}" not found.`);
|
|
1426
1589
|
return;
|
|
@@ -1462,7 +1625,7 @@ var MAX_ERROR_LEN = 60;
|
|
|
1462
1625
|
var eventsCommand = new Command8().name("events").description("Worker event operations (list failed, retry)");
|
|
1463
1626
|
eventsCommand.command("list-failed").description("List all failed events").action(async () => {
|
|
1464
1627
|
try {
|
|
1465
|
-
const events = EventsModel.listFailed();
|
|
1628
|
+
const events = await EventsModel.listFailed();
|
|
1466
1629
|
if (events.length === 0) {
|
|
1467
1630
|
console.log("No failed events.");
|
|
1468
1631
|
return;
|
|
@@ -1495,7 +1658,7 @@ Failed events (${events.length}):
|
|
|
1495
1658
|
});
|
|
1496
1659
|
eventsCommand.command("retry <eventId>").description("Retry a single failed event by ID").action(async (eventId) => {
|
|
1497
1660
|
try {
|
|
1498
|
-
const updated = EventsModel.resetFailedToPending(eventId);
|
|
1661
|
+
const updated = await EventsModel.resetFailedToPending(eventId);
|
|
1499
1662
|
if (updated) {
|
|
1500
1663
|
console.log(`Event ${eventId} reset to pending. Worker will pick it up.`);
|
|
1501
1664
|
} else {
|
|
@@ -1510,7 +1673,7 @@ eventsCommand.command("retry <eventId>").description("Retry a single failed even
|
|
|
1510
1673
|
});
|
|
1511
1674
|
eventsCommand.command("retry-all").description("Retry all failed events").action(async () => {
|
|
1512
1675
|
try {
|
|
1513
|
-
const count = EventsModel.resetAllFailedToPending();
|
|
1676
|
+
const count = await EventsModel.resetAllFailedToPending();
|
|
1514
1677
|
console.log(`Reset ${count} failed event(s) to pending. Worker will pick them up.`);
|
|
1515
1678
|
} catch (error) {
|
|
1516
1679
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -1521,8 +1684,7 @@ eventsCommand.command("retry-all").description("Retry all failed events").action
|
|
|
1521
1684
|
|
|
1522
1685
|
// src/commands/index.ts
|
|
1523
1686
|
logger.level = "error";
|
|
1524
|
-
|
|
1525
|
-
var program = new Command9().name("ddctl").description("CLI tool to manage your ddocs").version("0.0.1").addHelpText("beforeAll", "\n").addHelpText("afterAll", "\n");
|
|
1687
|
+
var program = new Command9().name("ddctl").description("CLI tool to manage your ddocs").version("0.0.2").addHelpText("beforeAll", "\n").addHelpText("afterAll", "\n");
|
|
1526
1688
|
program.addCommand(listCommand);
|
|
1527
1689
|
program.addCommand(getCommand);
|
|
1528
1690
|
program.addCommand(createCommand);
|
|
@@ -1531,10 +1693,14 @@ program.addCommand(deleteCommand);
|
|
|
1531
1693
|
program.addCommand(downloadCommand);
|
|
1532
1694
|
program.addCommand(viewCommand);
|
|
1533
1695
|
program.addCommand(eventsCommand);
|
|
1534
|
-
|
|
1696
|
+
(async () => {
|
|
1697
|
+
await getAdapter();
|
|
1698
|
+
await runMigrations();
|
|
1699
|
+
await program.parseAsync();
|
|
1700
|
+
})().then(async () => {
|
|
1535
1701
|
try {
|
|
1536
1702
|
await closeWorker();
|
|
1537
|
-
await
|
|
1703
|
+
await closeAdapter();
|
|
1538
1704
|
} catch (error) {
|
|
1539
1705
|
}
|
|
1540
1706
|
process.exit(0);
|