karin-plugin-qgroup-file2openlist 0.0.27 → 0.0.28

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.
@@ -6,12 +6,12 @@ import "../chunk-PBBZ5KAD.js";
6
6
  import "../chunk-QB3GSENE.js";
7
7
  import {
8
8
  handleGroupFileUploadedAutoBackup
9
- } from "../chunk-R4MRR5TI.js";
10
- import "../chunk-DDV3OZXB.js";
9
+ } from "../chunk-ZXZMBJN3.js";
10
+ import "../chunk-AAKFRFSO.js";
11
11
  import {
12
12
  backupOpenListToOpenListCore,
13
13
  formatErrorMessage
14
- } from "../chunk-GDVU2T6R.js";
14
+ } from "../chunk-HSFFULJR.js";
15
15
  import {
16
16
  config
17
17
  } from "../chunk-DA4U55JC.js";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  exportGroupFilesToDisk,
3
3
  syncGroupFilesToOpenListCore
4
- } from "../chunk-R4MRR5TI.js";
5
- import "../chunk-DDV3OZXB.js";
4
+ } from "../chunk-ZXZMBJN3.js";
5
+ import "../chunk-AAKFRFSO.js";
6
6
  import {
7
7
  formatErrorMessage,
8
8
  normalizePosixPath
9
- } from "../chunk-GDVU2T6R.js";
9
+ } from "../chunk-HSFFULJR.js";
10
10
  import {
11
11
  config
12
12
  } from "../chunk-DA4U55JC.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  handleGroupSyncConfigCommand
3
- } from "../chunk-P5FZDWLZ.js";
4
- import "../chunk-R4MRR5TI.js";
5
- import "../chunk-DDV3OZXB.js";
6
- import "../chunk-GDVU2T6R.js";
3
+ } from "../chunk-H2TAMQ7I.js";
4
+ import "../chunk-ZXZMBJN3.js";
5
+ import "../chunk-AAKFRFSO.js";
6
+ import "../chunk-HSFFULJR.js";
7
7
  import "../chunk-DA4U55JC.js";
8
8
  import "../chunk-IZS467MR.js";
9
9
 
@@ -5,7 +5,7 @@ import {
5
5
  resolveOpltMapping,
6
6
  withOpltUser,
7
7
  writeOpltData
8
- } from "../chunk-5HRWYTEM.js";
8
+ } from "../chunk-GAVQ23XT.js";
9
9
  import {
10
10
  buildActionCard,
11
11
  replyImages
@@ -20,7 +20,7 @@ import {
20
20
  normalizePosixPath,
21
21
  readJsonSafe,
22
22
  writeJsonSafe
23
- } from "../chunk-GDVU2T6R.js";
23
+ } from "../chunk-HSFFULJR.js";
24
24
  import {
25
25
  config
26
26
  } from "../chunk-DA4U55JC.js";
@@ -4,14 +4,14 @@ import {
4
4
  import "../chunk-QB3GSENE.js";
5
5
  import {
6
6
  readGroupSyncState
7
- } from "../chunk-DDV3OZXB.js";
7
+ } from "../chunk-AAKFRFSO.js";
8
8
  import {
9
9
  backupOpenListToOpenListCore,
10
10
  formatErrorMessage,
11
11
  normalizePosixPath,
12
12
  readJsonSafe,
13
13
  writeJsonSafe
14
- } from "../chunk-GDVU2T6R.js";
14
+ } from "../chunk-HSFFULJR.js";
15
15
  import "../chunk-DA4U55JC.js";
16
16
  import {
17
17
  dir
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  runNightlyOpltBackup
3
- } from "../chunk-5HRWYTEM.js";
3
+ } from "../chunk-GAVQ23XT.js";
4
4
  import {
5
5
  runNightlyGroupBackup
6
- } from "../chunk-P5FZDWLZ.js";
7
- import "../chunk-R4MRR5TI.js";
8
- import "../chunk-DDV3OZXB.js";
9
- import "../chunk-GDVU2T6R.js";
6
+ } from "../chunk-H2TAMQ7I.js";
7
+ import "../chunk-ZXZMBJN3.js";
8
+ import "../chunk-AAKFRFSO.js";
9
+ import "../chunk-HSFFULJR.js";
10
10
  import {
11
11
  config
12
12
  } from "../chunk-DA4U55JC.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  readJsonSafe,
3
3
  writeJsonSafe
4
- } from "./chunk-GDVU2T6R.js";
4
+ } from "./chunk-HSFFULJR.js";
5
5
  import {
6
6
  dir
7
7
  } from "./chunk-IZS467MR.js";
@@ -4,7 +4,7 @@ import {
4
4
  normalizePosixPath,
5
5
  readJsonSafe,
6
6
  writeJsonSafe
7
- } from "./chunk-GDVU2T6R.js";
7
+ } from "./chunk-HSFFULJR.js";
8
8
  import {
9
9
  config
10
10
  } from "./chunk-DA4U55JC.js";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  syncGroupFilesToOpenListCore
3
- } from "./chunk-R4MRR5TI.js";
3
+ } from "./chunk-ZXZMBJN3.js";
4
4
  import {
5
5
  normalizePosixPath,
6
6
  readJsonSafe,
7
7
  writeJsonSafe
8
- } from "./chunk-GDVU2T6R.js";
8
+ } from "./chunk-HSFFULJR.js";
9
9
  import {
10
10
  config
11
11
  } from "./chunk-DA4U55JC.js";
@@ -43,11 +43,59 @@ var formatErrorMessage = (error) => {
43
43
  var isAbortError = (error) => {
44
44
  return Boolean(error && typeof error === "object" && "name" in error && error.name === "AbortError");
45
45
  };
46
+ var drainResponseBody = async (res) => {
47
+ try {
48
+ const body = res.body;
49
+ if (!body) return;
50
+ const reader = body.getReader();
51
+ while (true) {
52
+ const { done } = await reader.read();
53
+ if (done) break;
54
+ }
55
+ } catch {
56
+ try {
57
+ await res.body?.cancel();
58
+ } catch {
59
+ }
60
+ }
61
+ };
46
62
  var fetchTextSafely = async (res, maxLen = 500) => {
47
63
  try {
48
- const text = await res.text();
49
- return text.slice(0, maxLen);
64
+ const maxBytes = Math.max(0, Math.floor(maxLen) || 0);
65
+ if (!maxBytes) {
66
+ await res.body?.cancel();
67
+ return "";
68
+ }
69
+ const body = res.body;
70
+ if (!body) return "";
71
+ const reader = body.getReader();
72
+ const chunks = [];
73
+ let total = 0;
74
+ while (total < maxBytes) {
75
+ const { value, done } = await reader.read();
76
+ if (done) break;
77
+ if (!value || value.byteLength <= 0) continue;
78
+ const remaining = maxBytes - total;
79
+ if (value.byteLength <= remaining) {
80
+ chunks.push(value);
81
+ total += value.byteLength;
82
+ continue;
83
+ }
84
+ chunks.push(value.slice(0, remaining));
85
+ total += remaining;
86
+ break;
87
+ }
88
+ try {
89
+ await reader.cancel();
90
+ } catch {
91
+ }
92
+ if (!chunks.length) return "";
93
+ return Buffer.concat(chunks.map((c) => Buffer.from(c))).toString("utf8");
50
94
  } catch {
95
+ try {
96
+ await res.body?.cancel();
97
+ } catch {
98
+ }
51
99
  return "";
52
100
  }
53
101
  };
@@ -548,6 +596,7 @@ var downloadAndUploadByOpenListApiPut = async (params) => {
548
596
  const controller = new AbortController();
549
597
  const timer = setTimeout(() => controller.abort(), timeoutMs);
550
598
  let tmpFilePath;
599
+ let uploadFileStream;
551
600
  try {
552
601
  let downloadRes;
553
602
  try {
@@ -585,7 +634,8 @@ var downloadAndUploadByOpenListApiPut = async (params) => {
585
634
  await streamToFile(Readable.fromWeb(downloadRes.body), tmpFilePath, { signal: controller.signal });
586
635
  const stat = await fs3.promises.stat(tmpFilePath);
587
636
  headers["Content-Length"] = String(stat.size);
588
- body = fs3.createReadStream(tmpFilePath);
637
+ uploadFileStream = fs3.createReadStream(tmpFilePath);
638
+ body = uploadFileStream;
589
639
  } else {
590
640
  body = Readable.fromWeb(downloadRes.body);
591
641
  }
@@ -619,6 +669,15 @@ var downloadAndUploadByOpenListApiPut = async (params) => {
619
669
  throw error;
620
670
  } finally {
621
671
  clearTimeout(timer);
672
+ if (uploadFileStream) {
673
+ await new Promise((resolve) => {
674
+ try {
675
+ uploadFileStream.close(() => resolve());
676
+ } catch {
677
+ resolve();
678
+ }
679
+ });
680
+ }
622
681
  if (tmpFilePath) await safeUnlink(tmpFilePath);
623
682
  }
624
683
  });
@@ -659,49 +718,6 @@ var isRetryableWebDavError = (error) => {
659
718
  return /ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|ECONNREFUSED|UND_ERR|socket hang up/i.test(msg);
660
719
  };
661
720
  var webdavMkcolOk = (status) => status === 201 || status === 405;
662
- var webdavPropfindListNames = async (params) => {
663
- const { davBaseUrl, auth, dirPath, timeoutMs } = params;
664
- const controller = new AbortController();
665
- const timer = setTimeout(() => controller.abort(), timeoutMs);
666
- try {
667
- const url = `${davBaseUrl}${encodePathForUrl(dirPath)}`;
668
- const body = `<?xml version="1.0" encoding="utf-8" ?>
669
- <d:propfind xmlns:d="DAV:">
670
- <d:prop>
671
- <d:displayname />
672
- </d:prop>
673
- </d:propfind>`;
674
- const res = await fetch(url, {
675
- method: "PROPFIND",
676
- headers: {
677
- Authorization: auth,
678
- Depth: "1",
679
- "Content-Type": "application/xml; charset=utf-8"
680
- },
681
- body,
682
- redirect: "follow",
683
- signal: controller.signal
684
- });
685
- if (!res.ok) return /* @__PURE__ */ new Set();
686
- const text = await res.text();
687
- if (!text) return /* @__PURE__ */ new Set();
688
- const names = /* @__PURE__ */ new Set();
689
- const hrefRegex = /<d:href>([^<]+)<\/d:href>/gi;
690
- let match;
691
- while (match = hrefRegex.exec(text)) {
692
- const href = match[1] ?? "";
693
- const decoded = decodeURIComponent(href);
694
- const cleaned = decoded.replace(/\/+$/, "");
695
- const base = cleaned.split("/").filter(Boolean).pop();
696
- if (base) names.add(base);
697
- }
698
- return names;
699
- } catch {
700
- return /* @__PURE__ */ new Set();
701
- } finally {
702
- clearTimeout(timer);
703
- }
704
- };
705
721
  var webdavPropfindListEntries = async (params) => {
706
722
  const { davBaseUrl, auth, dirPath, timeoutMs } = params;
707
723
  const controller = new AbortController();
@@ -729,7 +745,13 @@ var webdavPropfindListEntries = async (params) => {
729
745
  redirect: "follow",
730
746
  signal: controller.signal
731
747
  });
732
- if (!res.ok) return [];
748
+ if (!res.ok) {
749
+ try {
750
+ await res.body?.cancel();
751
+ } catch {
752
+ }
753
+ return [];
754
+ }
733
755
  const text = await res.text();
734
756
  if (!text) return [];
735
757
  const out = [];
@@ -786,6 +808,7 @@ var copyWebDavToWebDav = async (params) => {
786
808
  const controller = new AbortController();
787
809
  const timer = setTimeout(() => controller.abort(), timeoutMs);
788
810
  let tmpFilePath;
811
+ let uploadFileStream;
789
812
  try {
790
813
  const sourceUrl = `${sourceDavBaseUrl}${encodePathForUrl(sourcePath)}`;
791
814
  const targetUrl = `${targetDavBaseUrl}${encodePathForUrl(targetPath)}`;
@@ -824,7 +847,8 @@ var copyWebDavToWebDav = async (params) => {
824
847
  await streamToFile(Readable2.fromWeb(downloadRes.body), tmpFilePath, { signal: controller.signal });
825
848
  const stat = await fs4.promises.stat(tmpFilePath);
826
849
  headers["Content-Length"] = String(stat.size);
827
- body = fs4.createReadStream(tmpFilePath);
850
+ uploadFileStream = fs4.createReadStream(tmpFilePath);
851
+ body = uploadFileStream;
828
852
  } else {
829
853
  body = Readable2.fromWeb(downloadRes.body);
830
854
  }
@@ -845,6 +869,7 @@ var copyWebDavToWebDav = async (params) => {
845
869
  const body = await fetchTextSafely(putRes);
846
870
  throw new Error(`\u76EE\u6807\u7AEF\u5199\u5165\u5931\u8D25: ${putRes.status} ${putRes.statusText}${body ? ` - ${body}` : ""}`);
847
871
  }
872
+ await drainResponseBody(putRes);
848
873
  } catch (error) {
849
874
  try {
850
875
  controller.abort();
@@ -853,6 +878,15 @@ var copyWebDavToWebDav = async (params) => {
853
878
  throw error;
854
879
  } finally {
855
880
  clearTimeout(timer);
881
+ if (uploadFileStream) {
882
+ await new Promise((resolve) => {
883
+ try {
884
+ uploadFileStream.close(() => resolve());
885
+ } catch {
886
+ resolve();
887
+ }
888
+ });
889
+ }
856
890
  if (tmpFilePath) await safeUnlink(tmpFilePath);
857
891
  }
858
892
  });
@@ -893,6 +927,7 @@ var createWebDavDirEnsurer = (davBaseUrl, auth, timeoutMs) => {
893
927
  const body = await fetchTextSafely(res);
894
928
  throw new Error(`MKCOL \u5931\u8D25: ${current} -> ${res.status} ${res.statusText}${body ? ` - ${body}` : ""}`);
895
929
  }
930
+ await drainResponseBody(res);
896
931
  } finally {
897
932
  clearTimeout(timer);
898
933
  }
@@ -914,6 +949,7 @@ var downloadAndUploadByWebDav = async (params) => {
914
949
  const controller = new AbortController();
915
950
  const timer = setTimeout(() => controller.abort(), timeoutMs);
916
951
  let tmpFilePath;
952
+ let uploadFileStream;
917
953
  try {
918
954
  let downloadRes;
919
955
  try {
@@ -949,7 +985,8 @@ var downloadAndUploadByWebDav = async (params) => {
949
985
  await streamToFile(Readable2.fromWeb(downloadRes.body), tmpFilePath, { signal: controller.signal });
950
986
  const stat = await fs4.promises.stat(tmpFilePath);
951
987
  headers["Content-Length"] = String(stat.size);
952
- bodyStream = fs4.createReadStream(tmpFilePath);
988
+ uploadFileStream = fs4.createReadStream(tmpFilePath);
989
+ bodyStream = uploadFileStream;
953
990
  } else {
954
991
  bodyStream = Readable2.fromWeb(downloadRes.body);
955
992
  }
@@ -973,6 +1010,7 @@ var downloadAndUploadByWebDav = async (params) => {
973
1010
  const hint = putRes.status === 401 ? "\uFF08\u8D26\u53F7/\u5BC6\u7801\u9519\u8BEF\uFF0C\u6216\u672A\u5F00\u542F WebDAV\uFF09" : putRes.status === 403 ? "\uFF08\u6CA1\u6709 WebDAV \u7BA1\u7406/\u5199\u5165\u6743\u9650\uFF0C\u6216\u76EE\u6807\u76EE\u5F55\u4E0D\u53EF\u5199/\u4E0D\u5728\u7528\u6237\u53EF\u8BBF\u95EE\u8303\u56F4\uFF09" : "";
974
1011
  throw new Error(`\u4E0A\u4F20\u5931\u8D25: ${putRes.status} ${putRes.statusText}${hint}${body ? ` - ${body}` : ""}`);
975
1012
  }
1013
+ await drainResponseBody(putRes);
976
1014
  } catch (error) {
977
1015
  try {
978
1016
  controller.abort();
@@ -981,6 +1019,15 @@ var downloadAndUploadByWebDav = async (params) => {
981
1019
  throw error;
982
1020
  } finally {
983
1021
  clearTimeout(timer);
1022
+ if (uploadFileStream) {
1023
+ await new Promise((resolve) => {
1024
+ try {
1025
+ uploadFileStream.close(() => resolve());
1026
+ } catch {
1027
+ resolve();
1028
+ }
1029
+ });
1030
+ }
984
1031
  if (tmpFilePath) await safeUnlink(tmpFilePath);
985
1032
  }
986
1033
  });
@@ -1381,7 +1428,7 @@ export {
1381
1428
  ensureDir,
1382
1429
  readJsonSafe,
1383
1430
  writeJsonSafe,
1384
- webdavPropfindListNames,
1431
+ webdavHeadExists,
1385
1432
  createWebDavDirEnsurer,
1386
1433
  downloadAndUploadByWebDav,
1387
1434
  runWithConcurrency,
@@ -2,7 +2,7 @@ import {
2
2
  readGroupSyncState,
3
3
  withGroupSyncLock,
4
4
  writeGroupSyncState
5
- } from "./chunk-DDV3OZXB.js";
5
+ } from "./chunk-AAKFRFSO.js";
6
6
  import {
7
7
  buildOpenListAuthHeader,
8
8
  buildOpenListDavBaseUrl,
@@ -15,8 +15,8 @@ import {
15
15
  runWithAdaptiveConcurrency,
16
16
  runWithConcurrency,
17
17
  safePathSegment,
18
- webdavPropfindListNames
19
- } from "./chunk-GDVU2T6R.js";
18
+ webdavHeadExists
19
+ } from "./chunk-HSFFULJR.js";
20
20
  import {
21
21
  config,
22
22
  time
@@ -461,24 +461,18 @@ var syncGroupFilesToOpenListCore = async (params) => {
461
461
  return !ok;
462
462
  }) : candidates;
463
463
  if (mode === "incremental" && needSync.length) {
464
- const dirs = Array.from(new Set(
465
- needSync.map(({ remotePath }) => normalizePosixPath(path2.posix.dirname(remotePath)))
466
- ));
467
- const namesByDir = /* @__PURE__ */ new Map();
468
- await runWithConcurrency(dirs, 3, async (dirPath) => {
469
- const names = await webdavPropfindListNames({
464
+ const existsResults = new Array(needSync.length).fill(false);
465
+ const existsConcurrency = 10;
466
+ await runWithConcurrency(needSync, existsConcurrency, async ({ remotePath }, index) => {
467
+ existsResults[index] = await webdavHeadExists({
470
468
  davBaseUrl,
471
469
  auth,
472
- dirPath,
470
+ filePath: remotePath,
473
471
  timeoutMs: webdavTimeoutMs
474
472
  });
475
- namesByDir.set(dirPath, names);
476
473
  });
477
- needSync = needSync.filter(({ remotePath }) => {
478
- const dirPath = normalizePosixPath(path2.posix.dirname(remotePath));
479
- const base = path2.posix.basename(remotePath);
480
- const names = namesByDir.get(dirPath);
481
- if (names && names.has(base)) {
474
+ needSync = needSync.filter((_it, index) => {
475
+ if (existsResults[index]) {
482
476
  skipped++;
483
477
  return false;
484
478
  }
@@ -687,13 +681,15 @@ var handleGroupFileUploadedAutoBackup = (e) => {
687
681
  const normalized = direct ? normalizeGroupFileRelativePath(direct) : "";
688
682
  if (normalized) item.path = normalized;
689
683
  const bot = e?.bot;
690
- if (bot && !item.path.includes("/")) {
684
+ const resolvePathEnabled = typeof targetCfg?.uploadBackupResolvePath === "boolean" ? Boolean(targetCfg.uploadBackupResolvePath) : true;
685
+ const resolvePathMaxFolders = typeof targetCfg?.uploadBackupResolvePathMaxFolders === "number" ? Math.max(1, Math.floor(Number(targetCfg.uploadBackupResolvePathMaxFolders)) || 1) : 4e3;
686
+ if (resolvePathEnabled && bot && !item.path.includes("/")) {
691
687
  try {
692
688
  const found = await locateGroupFileByIdWithRetry(bot, groupId, fid, {
693
689
  retries: 3,
694
690
  delayMs: 1200,
695
691
  timeoutMs: 15e3,
696
- maxFolders: 4e3,
692
+ maxFolders: resolvePathMaxFolders,
697
693
  expectedName: name,
698
694
  expectedSize: size
699
695
  });
@@ -705,6 +701,8 @@ var handleGroupFileUploadedAutoBackup = (e) => {
705
701
  } catch (error) {
706
702
  logger3.debug(`[\u7FA4\u4E0A\u4F20\u5907\u4EFD][${groupId}] \u83B7\u53D6\u7FA4\u5185\u6587\u4EF6\u8DEF\u5F84\u5931\u8D25\uFF0C\u5C06\u9000\u5316\u4E3A\u6839\u76EE\u5F55: ${formatErrorMessage(error)}`);
707
703
  }
704
+ } else if (!resolvePathEnabled && bot && !item.path.includes("/")) {
705
+ logger3.debug(`[\u7FA4\u4E0A\u4F20\u5907\u4EFD][${groupId}] uploadBackupResolvePath=false\uFF0C\u5DF2\u8DF3\u8FC7\u7FA4\u6587\u4EF6\u5939\u8DEF\u5F84\u89E3\u6790\uFF1A${name}`);
708
706
  }
709
707
  }
710
708
  const resolveUrl = async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karin-plugin-qgroup-file2openlist",
3
- "version": "0.0.27",
3
+ "version": "0.0.28",
4
4
  "author": "429",
5
5
  "type": "module",
6
6
  "description": "karin plugin for QGroupFile backup",