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