@lacrous/omnicloud 0.2.22 → 0.2.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lacrous/omnicloud",
3
- "version": "0.2.22",
3
+ "version": "0.2.24",
4
4
  "description": "SDK and core building blocks for OmniCloud — self-hosted cloud storage powered by your Telegram account.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -48,9 +48,9 @@
48
48
  "prisma": "^6.2.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@omnicloud/core": "0.2.22",
52
- "@omnicloud/shared": "0.2.22",
53
- "@omnicloud/telegram": "0.2.22",
51
+ "@omnicloud/core": "0.2.24",
52
+ "@omnicloud/shared": "0.2.24",
53
+ "@omnicloud/telegram": "0.2.24",
54
54
  "@types/node": "^22.10.0",
55
55
  "esbuild": "0.25.12",
56
56
  "tsup": "^8.3.0",
package/server/index.js CHANGED
@@ -1760,11 +1760,11 @@ var FolderService = class {
1760
1760
  const fileIds = await this.collectFileIds(userId, subtree);
1761
1761
  const batchId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
1762
1762
  const stampedAt = /* @__PURE__ */ new Date();
1763
- await this.folders.updateMany(
1763
+ await this.folders.trashSubtree(
1764
1764
  subtree.map((folder) => folder.id),
1765
+ fileIds,
1765
1766
  { deletedAt: stampedAt, trashBatchId: batchId }
1766
1767
  );
1767
- await this.files.updateMany(fileIds, { deletedAt: stampedAt, trashBatchId: batchId });
1768
1768
  await this.activity.record({
1769
1769
  userId,
1770
1770
  action: "trash",
@@ -1799,14 +1799,11 @@ var FolderService = class {
1799
1799
  );
1800
1800
  const restorableFileIds = restorableFiles.map((file) => file.id);
1801
1801
  if (restorableFolders.length > 0) {
1802
- await this.folders.updateMany(
1802
+ await this.folders.restoreSubtree(
1803
1803
  restorableFolders.map((folder) => folder.id),
1804
- { deletedAt: null, trashBatchId: null }
1804
+ restorableFileIds,
1805
+ { rootId: id, reparent: { parentId } }
1805
1806
  );
1806
- await this.folders.update(id, { parentId });
1807
- }
1808
- if (restorableFileIds.length > 0) {
1809
- await this.files.updateMany(restorableFileIds, { deletedAt: null, trashBatchId: null });
1810
1807
  }
1811
1808
  await this.activity.record({
1812
1809
  userId,
@@ -1833,9 +1830,16 @@ var FolderService = class {
1833
1830
  );
1834
1831
  const engine = await this.engineFor(userId);
1835
1832
  const removedFileIds = [];
1833
+ const versionsByFile = await this.files.listVersionsForFiles(affected.map((file) => file.id));
1836
1834
  for (const file of affected) {
1837
1835
  try {
1838
- await engine.remove({ messageId: String(file.telegramMessageId) });
1836
+ const messageIds = /* @__PURE__ */ new Set([String(file.telegramMessageId)]);
1837
+ for (const version of versionsByFile.get(file.id) ?? []) {
1838
+ messageIds.add(String(version.telegramMessageId));
1839
+ }
1840
+ for (const messageId of messageIds) {
1841
+ await engine.remove({ messageId });
1842
+ }
1839
1843
  removedFileIds.push(file.id);
1840
1844
  } catch {
1841
1845
  }
@@ -2478,6 +2482,13 @@ function mapTelegramError(error, fallbackMessage) {
2478
2482
  case "CHANNEL_INVALID":
2479
2483
  case "CHAT_ID_INVALID":
2480
2484
  return new StorageNotInitializedError();
2485
+ // Permanent: the account cannot write to the channel. Retrying cannot help.
2486
+ case "CHAT_WRITE_FORBIDDEN":
2487
+ case "CHAT_ADMIN_REQUIRED":
2488
+ return new ForbiddenError("OmniCloud cannot write to the storage channel for this account");
2489
+ // Permanent: the stored channel reference is stale. Retrying with the same reference fails too.
2490
+ case "PEER_ID_INVALID":
2491
+ return new StorageNotInitializedError();
2481
2492
  case "MSG_ID_INVALID":
2482
2493
  case "MESSAGE_ID_INVALID":
2483
2494
  return new TelegramFileNotFoundError();
@@ -3784,6 +3795,44 @@ function createFolderRepo(prisma) {
3784
3795
  });
3785
3796
  return mapFolder(folder);
3786
3797
  },
3798
+ async restoreSubtree(folderIds, fileIds, options) {
3799
+ await prisma.$transaction(async (tx) => {
3800
+ if (folderIds.length > 0) {
3801
+ await tx.folder.updateMany({
3802
+ where: { id: { in: folderIds } },
3803
+ data: { deletedAt: null, trashBatchId: null }
3804
+ });
3805
+ }
3806
+ if (options.reparent) {
3807
+ await tx.folder.update({
3808
+ where: { id: options.rootId },
3809
+ data: { parentId: options.reparent.parentId }
3810
+ });
3811
+ }
3812
+ if (fileIds.length > 0) {
3813
+ await tx.file.updateMany({
3814
+ where: { id: { in: fileIds } },
3815
+ data: { deletedAt: null, trashBatchId: null }
3816
+ });
3817
+ }
3818
+ });
3819
+ },
3820
+ async trashSubtree(folderIds, fileIds, stamp) {
3821
+ await prisma.$transaction(async (tx) => {
3822
+ if (folderIds.length > 0) {
3823
+ await tx.folder.updateMany({
3824
+ where: { id: { in: folderIds } },
3825
+ data: { deletedAt: stamp.deletedAt, trashBatchId: stamp.trashBatchId }
3826
+ });
3827
+ }
3828
+ if (fileIds.length > 0) {
3829
+ await tx.file.updateMany({
3830
+ where: { id: { in: fileIds } },
3831
+ data: { deletedAt: stamp.deletedAt, trashBatchId: stamp.trashBatchId }
3832
+ });
3833
+ }
3834
+ });
3835
+ },
3787
3836
  async updateMany(ids, patch) {
3788
3837
  const result = await prisma.folder.updateMany({
3789
3838
  where: { id: { in: ids } },
@@ -5038,7 +5087,7 @@ function encryptionCheck(configured) {
5038
5087
  }
5039
5088
 
5040
5089
  // ../../apps/api/src/routes/storage.ts
5041
- var VERSION = "0.2.22";
5090
+ var VERSION = "0.2.24";
5042
5091
  function registerStorageRoutes(app2, container2) {
5043
5092
  app2.get("/api/health", async () => {
5044
5093
  const database = await probeDatabase(container2);