@saltcorn/admin-models 1.7.0-alpha.0 → 1.7.0-beta.0

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.
Files changed (69) hide show
  1. package/cjs/models/backup-retention.cjs +18 -0
  2. package/cjs/models/file-globs.cjs +18 -0
  3. package/cjs/models/zip-extract.cjs +18 -0
  4. package/cjs/models/zip-format.cjs +18 -0
  5. package/cjs/models/zip-worker.cjs +18 -0
  6. package/cjs/models/zip-writer.cjs +18 -0
  7. package/cjs/models/zip.cjs +18 -0
  8. package/dist/index.d.ts +2 -2
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/models/backup-retention.d.ts +57 -0
  11. package/dist/models/backup-retention.d.ts.map +1 -0
  12. package/dist/models/backup-retention.js +108 -0
  13. package/dist/models/backup-retention.js.map +1 -0
  14. package/dist/models/backup.d.ts +4 -1
  15. package/dist/models/backup.d.ts.map +1 -1
  16. package/dist/models/backup.js +364 -302
  17. package/dist/models/backup.js.map +1 -1
  18. package/dist/models/file-globs.d.ts +34 -0
  19. package/dist/models/file-globs.d.ts.map +1 -0
  20. package/dist/models/file-globs.js +47 -0
  21. package/dist/models/file-globs.js.map +1 -0
  22. package/dist/models/pack.d.ts +1 -1
  23. package/dist/models/pack.d.ts.map +1 -1
  24. package/dist/models/pack.js +26 -1
  25. package/dist/models/pack.js.map +1 -1
  26. package/dist/models/tenant.d.ts.map +1 -1
  27. package/dist/models/tenant.js +2 -0
  28. package/dist/models/tenant.js.map +1 -1
  29. package/dist/models/zip-extract.d.ts +14 -0
  30. package/dist/models/zip-extract.d.ts.map +1 -0
  31. package/dist/models/zip-extract.js +110 -0
  32. package/dist/models/zip-extract.js.map +1 -0
  33. package/dist/models/zip-format.d.ts +57 -0
  34. package/dist/models/zip-format.d.ts.map +1 -0
  35. package/dist/models/zip-format.js +106 -0
  36. package/dist/models/zip-format.js.map +1 -0
  37. package/dist/models/zip-worker.d.ts +2 -0
  38. package/dist/models/zip-worker.d.ts.map +1 -0
  39. package/dist/models/zip-worker.js +177 -0
  40. package/dist/models/zip-worker.js.map +1 -0
  41. package/dist/models/zip-writer.d.ts +75 -0
  42. package/dist/models/zip-writer.d.ts.map +1 -0
  43. package/dist/models/zip-writer.js +321 -0
  44. package/dist/models/zip-writer.js.map +1 -0
  45. package/dist/models/zip.d.ts +101 -0
  46. package/dist/models/zip.d.ts.map +1 -0
  47. package/dist/models/zip.js +211 -0
  48. package/dist/models/zip.js.map +1 -0
  49. package/dist/tests/backup-retention.test.d.ts +2 -0
  50. package/dist/tests/backup-retention.test.d.ts.map +1 -0
  51. package/dist/tests/backup-retention.test.js +134 -0
  52. package/dist/tests/backup-retention.test.js.map +1 -0
  53. package/dist/tests/backup.test.js +186 -2
  54. package/dist/tests/backup.test.js.map +1 -1
  55. package/dist/tests/file-globs.test.d.ts +2 -0
  56. package/dist/tests/file-globs.test.d.ts.map +1 -0
  57. package/dist/tests/file-globs.test.js +66 -0
  58. package/dist/tests/file-globs.test.js.map +1 -0
  59. package/dist/tests/pack.test.js +14 -3
  60. package/dist/tests/pack.test.js.map +1 -1
  61. package/dist/tests/random.test.js +3 -3
  62. package/dist/tests/random.test.js.map +1 -1
  63. package/dist/tests/tenant.test.js +7 -7
  64. package/dist/tests/tenant.test.js.map +1 -1
  65. package/dist/tests/zip.test.d.ts +2 -0
  66. package/dist/tests/zip.test.d.ts.map +1 -0
  67. package/dist/tests/zip.test.js +319 -0
  68. package/dist/tests/zip.test.js.map +1 -0
  69. package/package.json +15 -9
@@ -0,0 +1,18 @@
1
+ // CommonJS interop shim — AUTO-GENERATED, DO NOT EDIT.
2
+ // See gen-cjs-shims.cjs.
3
+ "use strict";
4
+ const m = require("../../dist/models/backup-retention.js");
5
+ const e = m && m.__esModule && "default" in m ? m.default : m;
6
+ module.exports = e;
7
+ if (e !== m && e && (typeof e === "object" || typeof e === "function")) {
8
+ for (const k in m) {
9
+ if (k === "default" || k === "__esModule" || k in e) continue;
10
+ try {
11
+ Object.defineProperty(e, k, {
12
+ get: () => m[k],
13
+ enumerable: true,
14
+ configurable: true,
15
+ });
16
+ } catch {}
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ // CommonJS interop shim — AUTO-GENERATED, DO NOT EDIT.
2
+ // See gen-cjs-shims.cjs.
3
+ "use strict";
4
+ const m = require("../../dist/models/file-globs.js");
5
+ const e = m && m.__esModule && "default" in m ? m.default : m;
6
+ module.exports = e;
7
+ if (e !== m && e && (typeof e === "object" || typeof e === "function")) {
8
+ for (const k in m) {
9
+ if (k === "default" || k === "__esModule" || k in e) continue;
10
+ try {
11
+ Object.defineProperty(e, k, {
12
+ get: () => m[k],
13
+ enumerable: true,
14
+ configurable: true,
15
+ });
16
+ } catch {}
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ // CommonJS interop shim — AUTO-GENERATED, DO NOT EDIT.
2
+ // See gen-cjs-shims.cjs.
3
+ "use strict";
4
+ const m = require("../../dist/models/zip-extract.js");
5
+ const e = m && m.__esModule && "default" in m ? m.default : m;
6
+ module.exports = e;
7
+ if (e !== m && e && (typeof e === "object" || typeof e === "function")) {
8
+ for (const k in m) {
9
+ if (k === "default" || k === "__esModule" || k in e) continue;
10
+ try {
11
+ Object.defineProperty(e, k, {
12
+ get: () => m[k],
13
+ enumerable: true,
14
+ configurable: true,
15
+ });
16
+ } catch {}
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ // CommonJS interop shim — AUTO-GENERATED, DO NOT EDIT.
2
+ // See gen-cjs-shims.cjs.
3
+ "use strict";
4
+ const m = require("../../dist/models/zip-format.js");
5
+ const e = m && m.__esModule && "default" in m ? m.default : m;
6
+ module.exports = e;
7
+ if (e !== m && e && (typeof e === "object" || typeof e === "function")) {
8
+ for (const k in m) {
9
+ if (k === "default" || k === "__esModule" || k in e) continue;
10
+ try {
11
+ Object.defineProperty(e, k, {
12
+ get: () => m[k],
13
+ enumerable: true,
14
+ configurable: true,
15
+ });
16
+ } catch {}
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ // CommonJS interop shim — AUTO-GENERATED, DO NOT EDIT.
2
+ // See gen-cjs-shims.cjs.
3
+ "use strict";
4
+ const m = require("../../dist/models/zip-worker.js");
5
+ const e = m && m.__esModule && "default" in m ? m.default : m;
6
+ module.exports = e;
7
+ if (e !== m && e && (typeof e === "object" || typeof e === "function")) {
8
+ for (const k in m) {
9
+ if (k === "default" || k === "__esModule" || k in e) continue;
10
+ try {
11
+ Object.defineProperty(e, k, {
12
+ get: () => m[k],
13
+ enumerable: true,
14
+ configurable: true,
15
+ });
16
+ } catch {}
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ // CommonJS interop shim — AUTO-GENERATED, DO NOT EDIT.
2
+ // See gen-cjs-shims.cjs.
3
+ "use strict";
4
+ const m = require("../../dist/models/zip-writer.js");
5
+ const e = m && m.__esModule && "default" in m ? m.default : m;
6
+ module.exports = e;
7
+ if (e !== m && e && (typeof e === "object" || typeof e === "function")) {
8
+ for (const k in m) {
9
+ if (k === "default" || k === "__esModule" || k in e) continue;
10
+ try {
11
+ Object.defineProperty(e, k, {
12
+ get: () => m[k],
13
+ enumerable: true,
14
+ configurable: true,
15
+ });
16
+ } catch {}
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ // CommonJS interop shim — AUTO-GENERATED, DO NOT EDIT.
2
+ // See gen-cjs-shims.cjs.
3
+ "use strict";
4
+ const m = require("../../dist/models/zip.js");
5
+ const e = m && m.__esModule && "default" in m ? m.default : m;
6
+ module.exports = e;
7
+ if (e !== m && e && (typeof e === "object" || typeof e === "function")) {
8
+ for (const k in m) {
9
+ if (k === "default" || k === "__esModule" || k in e) continue;
10
+ try {
11
+ Object.defineProperty(e, k, {
12
+ get: () => m[k],
13
+ enumerable: true,
14
+ configurable: true,
15
+ });
16
+ } catch {}
17
+ }
18
+ }
package/dist/index.d.ts CHANGED
@@ -2,13 +2,13 @@
2
2
  * This is the backup module
3
3
  */
4
4
  export declare namespace backup {
5
- const create_backup: (fnm?: string) => Promise<string>, restore: (fnm: string, loadAndSaveNewPlugin: (plugin: import("@saltcorn/data/models/plugin").default) => void, restore_first_user?: boolean, password?: string) => Promise<string | void>, create_csv_from_rows: (rows: import("@saltcorn/db-common").Row[], fnm: string) => Promise<void>;
5
+ const create_backup: (fnm?: string) => Promise<string>, restore: (fnm: string, loadAndSaveNewPlugin: (plugin: import("@saltcorn/data/models/plugin").default) => void, restore_first_user?: boolean, password?: string, onLog?: (msg: string) => void) => Promise<string | void>, create_csv_from_rows: (rows: import("@saltcorn/db-common").Row[], fnm: string) => Promise<void>;
6
6
  }
7
7
  /**
8
8
  * This is the pack module
9
9
  */
10
10
  export declare namespace pack {
11
- const table_pack: (nameOrTable: string | import("@saltcorn/data/models/table").default) => Promise<import("@saltcorn/types/model-abstracts/abstract_table").TablePack>, view_pack: (name: string | import("@saltcorn/data/models/view").default) => Promise<import("@saltcorn/types/model-abstracts/abstract_view").ViewPack>, plugin_pack: (name: string) => Promise<import("@saltcorn/types/model-abstracts/abstract_plugin").PluginPack>, page_pack: (name: string | import("@saltcorn/data/models/page").default) => Promise<import("@saltcorn/types/model-abstracts/abstract_page").PagePack>, page_group_pack: (name: string) => Promise<import("@saltcorn/types/model-abstracts/abstract_page_group").PageGroupPack>, role_pack: (role: string) => Promise<import("@saltcorn/types/model-abstracts/abstract_role").RolePack>, library_pack: (name: string) => Promise<import("@saltcorn/types/model-abstracts/abstract_library").LibraryPack>, trigger_pack: (name: string | import("@saltcorn/data/models/trigger").default) => Promise<import("@saltcorn/types/model-abstracts/abstract_trigger").TriggerPack>, model_pack: (name: string, tableName: string) => Promise<import("@saltcorn/types/model-abstracts/abstract_model").ModelPack>, model_instance_pack: (instanceName: string, modelName: string, tableName: string) => Promise<import("@saltcorn/types/model-abstracts/abstract_model_instance").ModelInstancePack>, install_pack: (pack: import("@saltcorn/types/base_types").Pack, name: string | undefined, loadAndSaveNewPlugin: (arg0: import("@saltcorn/data/models/plugin").default) => void, bare_tables?: boolean) => Promise<void>, fetch_available_packs: () => Promise<Array<{
11
+ const table_pack: (nameOrTable: string | import("@saltcorn/data/models/table").default) => Promise<import("@saltcorn/types/model-abstracts/abstract_table").TablePack>, view_pack: (name: string | import("@saltcorn/data/models/view").default) => Promise<import("@saltcorn/types/model-abstracts/abstract_view").ViewPack>, plugin_pack: (name: string) => Promise<import("@saltcorn/types/model-abstracts/abstract_plugin").PluginPack>, page_pack: (name: string | import("@saltcorn/data/models/page").default) => Promise<import("@saltcorn/types/model-abstracts/abstract_page").PagePack>, page_group_pack: (name: string) => Promise<import("@saltcorn/types/model-abstracts/abstract_page_group").PageGroupPack>, role_pack: (role: string) => Promise<import("@saltcorn/types/model-abstracts/abstract_role").RolePack>, library_pack: (name: string) => Promise<import("@saltcorn/types/model-abstracts/abstract_library").LibraryPack>, trigger_pack: (name: string | import("@saltcorn/data/models/trigger").default) => Promise<import("@saltcorn/types/model-abstracts/abstract_trigger").TriggerPack>, model_pack: (name: string, tableName: string) => Promise<import("@saltcorn/types/model-abstracts/abstract_model").ModelPack>, model_instance_pack: (instanceName: string, modelName: string, tableName: string) => Promise<import("@saltcorn/types/model-abstracts/abstract_model_instance").ModelInstancePack>, install_pack: (pack: import("@saltcorn/types/base_types").Pack, name: string | undefined, loadAndSaveNewPlugin: (arg0: import("@saltcorn/data/models/plugin").default) => void, bare_tables?: boolean, onLog?: (msg: string) => void) => Promise<void>, fetch_available_packs: () => Promise<Array<{
12
12
  name: string;
13
13
  description: string;
14
14
  }>>, fetch_pack_by_name: (name: string) => Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,yBAAiB,MAAM,CAAC;IACf,MAAQ,aAAa,qCAAE,OAAO,oLAAE,oBAAoB,2EAAiB,CAAC;CAC9E;AAGD;;GAEG;AACH,yBAAiB,IAAI,CAAC;IACb,MACL,UAAU,wJACV,SAAS,8IACT,WAAW,mGACX,SAAS,8IACT,eAAe,0GACf,SAAS,+FACT,YAAY,qGACZ,YAAY,uJACZ,UAAU,oHACV,mBAAmB,gKACnB,YAAY,6MACZ,qBAAqB;;;SACrB,kBAAkB;;;eAClB,gBAAgB;aAuBu1Z,CAAC;eAAiB,CAAC;QAtB13Z,cAAc,qFACd,WAAW;;;gBAqB0kgB,CAAC;gBAAoB,CAAC;;uBApB/lgB,CAAC;CAChB;AAGD;;GAEG;AACH,yBAAiB,MAAM,CAAC;IACf,MACL,aAAa;;;oBAWqnJ,CAAC;;uBAA8E,CAAC;yBAVltJ,YAAY,mGACZ,cAAc,qDACd,aAAa,2BACb,gBAAgB,sDAChB,eAAe,yBACf,YAAY,kCACZ,UAAU,4CACV,oBAAoB;;;;uBACN,CAAC;CAClB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,yBAAiB,MAAM,CAAC;IACf,MAAQ,aAAa,qCAAE,OAAO,mNAAE,oBAAoB,2EAAiB,CAAC;CAC9E;AAGD;;GAEG;AACH,yBAAiB,IAAI,CAAC;IACb,MACL,UAAU,wJACV,SAAS,8IACT,WAAW,mGACX,SAAS,8IACT,eAAe,0GACf,SAAS,+FACT,YAAY,qGACZ,YAAY,uJACZ,UAAU,oHACV,mBAAmB,gKACnB,YAAY,4OACZ,qBAAqB;;;SACrB,kBAAkB;;;eAClB,gBAAgB;aAuBu1Z,CAAC;eAAiB,CAAC;QAtB13Z,cAAc,qFACd,WAAW;;;gBAqB0kgB,CAAC;gBAAoB,CAAC;;uBApB/lgB,CAAC;CAChB;AAGD;;GAEG;AACH,yBAAiB,MAAM,CAAC;IACf,MACL,aAAa;;;oBAWqnJ,CAAC;;uBAA8E,CAAC;yBAVltJ,YAAY,mGACZ,cAAc,qDACd,aAAa,2BACb,gBAAgB,sDAChB,eAAe,yBACf,YAAY,kCACZ,UAAU,4CACV,oBAAoB;;;;uBACN,CAAC;CAClB"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Tiered (GFS) backup retention classifier.
3
+ *
4
+ * Given a list of backup entries and a keep-policy, returns the entries
5
+ * that should be DELETED. Mirrors the semantics of `restic forget`:
6
+ * for each tier (daily / weekly / monthly / yearly), the newest backup
7
+ * in each of the most recent N buckets is kept. An entry survives if any
8
+ * tier keeps it. `keep_min` is a safety net that guarantees a minimum
9
+ * number of retained backups regardless of age.
10
+ *
11
+ * Pure and I/O-free: no Saltcorn imports, so it is unit-testable without
12
+ * a database or a backup destination.
13
+ * @category saltcorn-admin-models
14
+ * @module models/backup-retention
15
+ */
16
+ export type BackupEntry = {
17
+ /** Object key (S3) or filename (local / SFTP) */
18
+ key: string;
19
+ /** Backup timestamp (LastModified / file mtime) */
20
+ date: Date;
21
+ };
22
+ export type RetentionPolicy = {
23
+ keep_daily: number;
24
+ keep_weekly: number;
25
+ keep_monthly: number;
26
+ keep_yearly: number;
27
+ keep_min: number;
28
+ };
29
+ /**
30
+ * Returns the subset of `entries` that should be deleted under `policy`.
31
+ * Entries not returned are retained.
32
+ * @param entries backup entries (any order)
33
+ * @param policy how many backups to keep per tier
34
+ * @returns the deletion candidates
35
+ */
36
+ export declare const selectBackupsToDelete: (entries: BackupEntry[], policy: RetentionPolicy) => BackupEntry[];
37
+ /**
38
+ * Group entries by tenant so each tenant gets its own GFS ladder.
39
+ *
40
+ * Backup filenames follow `${prefix}${site_or_tenant}-${date}.zip`
41
+ * (see create_backup()). The grouping key is the basename with the
42
+ * trailing `-<date>.zip` stripped; entries that do not match the
43
+ * pattern fall into their full basename as a conservative default
44
+ * (they are then subject to keep_min within their own group).
45
+ * @param entries backup entries
46
+ * @returns entries grouped by tenant key
47
+ */
48
+ export declare const groupByTenant: (entries: BackupEntry[]) => Map<string, BackupEntry[]>;
49
+ /**
50
+ * Convenience wrapper: apply the policy per tenant group and return
51
+ * the union of deletion candidates.
52
+ * @param entries backup entries across all tenants
53
+ * @param policy how many backups to keep per tier
54
+ * @returns the deletion candidates
55
+ */
56
+ export declare const selectBackupsToDeleteGrouped: (entries: BackupEntry[], policy: RetentionPolicy) => BackupEntry[];
57
+ //# sourceMappingURL=backup-retention.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-retention.d.ts","sourceRoot":"","sources":["../../models/backup-retention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,WAAW,GAAG;IACxB,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,IAAI,EAAE,IAAI,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AA8BF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAChC,SAAS,WAAW,EAAE,EACtB,QAAQ,eAAe,KACtB,WAAW,EAwCb,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,GACxB,SAAS,WAAW,EAAE,KACrB,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAU3B,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GACvC,SAAS,WAAW,EAAE,EACtB,QAAQ,eAAe,KACtB,WAAW,EAKb,CAAC"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Tiered (GFS) backup retention classifier.
3
+ *
4
+ * Given a list of backup entries and a keep-policy, returns the entries
5
+ * that should be DELETED. Mirrors the semantics of `restic forget`:
6
+ * for each tier (daily / weekly / monthly / yearly), the newest backup
7
+ * in each of the most recent N buckets is kept. An entry survives if any
8
+ * tier keeps it. `keep_min` is a safety net that guarantees a minimum
9
+ * number of retained backups regardless of age.
10
+ *
11
+ * Pure and I/O-free: no Saltcorn imports, so it is unit-testable without
12
+ * a database or a backup destination.
13
+ * @category saltcorn-admin-models
14
+ * @module models/backup-retention
15
+ */
16
+ /** ISO-8601 week number (1..53), week starts Monday. */
17
+ const isoWeek = (d) => {
18
+ const date = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
19
+ const dayNum = date.getUTCDay() || 7; // Sun=7
20
+ date.setUTCDate(date.getUTCDate() + 4 - dayNum); // nearest Thursday
21
+ const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1));
22
+ const week = Math.ceil(((date.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
23
+ return `${date.getUTCFullYear()}-W${String(week).padStart(2, "0")}`;
24
+ };
25
+ const dayBucket = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
26
+ const monthBucket = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`;
27
+ const yearBucket = (d) => `${d.getFullYear()}`;
28
+ /**
29
+ * Returns the subset of `entries` that should be deleted under `policy`.
30
+ * Entries not returned are retained.
31
+ * @param entries backup entries (any order)
32
+ * @param policy how many backups to keep per tier
33
+ * @returns the deletion candidates
34
+ */
35
+ export const selectBackupsToDelete = (entries, policy) => {
36
+ if (entries.length === 0)
37
+ return [];
38
+ // newest first
39
+ const sorted = [...entries].sort((a, b) => b.date.getTime() - a.date.getTime());
40
+ const tiers = [
41
+ { remaining: policy.keep_daily, bucketOf: dayBucket, seen: new Set() },
42
+ { remaining: policy.keep_weekly, bucketOf: isoWeek, seen: new Set() },
43
+ { remaining: policy.keep_monthly, bucketOf: monthBucket, seen: new Set() },
44
+ { remaining: policy.keep_yearly, bucketOf: yearBucket, seen: new Set() },
45
+ ];
46
+ const kept = new Set();
47
+ for (const entry of sorted) {
48
+ for (const tier of tiers) {
49
+ if (tier.remaining <= 0)
50
+ continue;
51
+ const bucket = tier.bucketOf(entry.date);
52
+ if (tier.seen.has(bucket))
53
+ continue;
54
+ // newest entry of a fresh bucket in a tier with capacity → keep
55
+ tier.seen.add(bucket);
56
+ tier.remaining--;
57
+ kept.add(entry);
58
+ // NOTE: no `break` — a single backup may satisfy several tiers
59
+ // at once (e.g. newest daily is also newest weekly/monthly).
60
+ }
61
+ }
62
+ // Safety net: never fall below keep_min retained backups
63
+ if (policy.keep_min > 0 && kept.size < policy.keep_min) {
64
+ for (const entry of sorted) {
65
+ if (kept.size >= policy.keep_min)
66
+ break;
67
+ kept.add(entry);
68
+ }
69
+ }
70
+ return sorted.filter((e) => !kept.has(e));
71
+ };
72
+ /**
73
+ * Group entries by tenant so each tenant gets its own GFS ladder.
74
+ *
75
+ * Backup filenames follow `${prefix}${site_or_tenant}-${date}.zip`
76
+ * (see create_backup()). The grouping key is the basename with the
77
+ * trailing `-<date>.zip` stripped; entries that do not match the
78
+ * pattern fall into their full basename as a conservative default
79
+ * (they are then subject to keep_min within their own group).
80
+ * @param entries backup entries
81
+ * @returns entries grouped by tenant key
82
+ */
83
+ export const groupByTenant = (entries) => {
84
+ const groups = new Map();
85
+ for (const entry of entries) {
86
+ const base = entry.key.split("/").pop() || entry.key;
87
+ const m = base.match(/^(.*)-\d{4}-\d{1,2}-\d{1,2}.*\.zip$/);
88
+ const groupKey = m ? m[1] : base;
89
+ if (!groups.has(groupKey))
90
+ groups.set(groupKey, []);
91
+ groups.get(groupKey).push(entry);
92
+ }
93
+ return groups;
94
+ };
95
+ /**
96
+ * Convenience wrapper: apply the policy per tenant group and return
97
+ * the union of deletion candidates.
98
+ * @param entries backup entries across all tenants
99
+ * @param policy how many backups to keep per tier
100
+ * @returns the deletion candidates
101
+ */
102
+ export const selectBackupsToDeleteGrouped = (entries, policy) => {
103
+ const out = [];
104
+ for (const [, group] of groupByTenant(entries))
105
+ out.push(...selectBackupsToDelete(group, policy));
106
+ return out;
107
+ };
108
+ //# sourceMappingURL=backup-retention.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-retention.js","sourceRoot":"","sources":["../../models/backup-retention.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,11 @@
1
1
  import Plugin from "@saltcorn/data/models/plugin";
2
+ import { Readable } from "stream";
2
3
  import { Row } from "@saltcorn/db-common";
3
4
  declare const _default: {
4
5
  create_backup: (fnm?: string) => Promise<string>;
5
- restore: (fnm: string, loadAndSaveNewPlugin: (plugin: Plugin) => void, restore_first_user?: boolean, password?: string) => Promise<string | void>;
6
+ backup_to_stream: <T>(consume: (body: Readable) => Promise<T>) => Promise<T>;
7
+ backup_file_name: () => string;
8
+ restore: (fnm: string, loadAndSaveNewPlugin: (plugin: Plugin) => void, restore_first_user?: boolean, password?: string, onLog?: (msg: string) => void) => Promise<string | void>;
6
9
  create_csv_from_rows: (rows: Row[], fnm: string) => Promise<void>;
7
10
  auto_backup_now: () => Promise<void>;
8
11
  create_pack_json: (withEventLog?: boolean, forSnapshot?: boolean) => Promise<object>;
@@ -1 +1 @@
1
- {"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../models/backup.ts"],"names":[],"mappings":"AAaA,OAAO,MAAM,MAAM,8BAA8B,CAAC;AA2DlD,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;;0BA0VP,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;mBA6RpD,MAAM,wBACW,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,uBACzB,OAAO,aACjB,MAAM,KAChB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;iCA9ejB,GAAG,EAAE,OACN,MAAM,KACV,OAAO,CAAC,IAAI,CAAC;;sCAxIA,OAAO,gBACR,OAAO,KACnB,OAAO,CAAC,MAAM,CAAC;mBA6XX,MAAM,OACN,MAAM,aACA,MAAM,KAChB,OAAO,CAAC,IAAI,CAAC;;AAskBhB,wBAOE"}
1
+ {"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../models/backup.ts"],"names":[],"mappings":"AAaA,OAAO,MAAM,MAAM,8BAA8B,CAAC;AAgBlD,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAkDlC,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;;0BAwZP,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;uBAhC3B,CAAC,WACtB,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,KACtC,OAAO,CAAC,CAAC,CAAC;4BAtDgB,MAAM;mBA4T5B,MAAM,wBACW,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,uBACzB,OAAO,aACjB,MAAM,UACT,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,KAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;iCAvejB,GAAG,EAAE,OACN,MAAM,KACV,OAAO,CAAC,IAAI,CAAC;;sCAzJA,OAAO,gBACR,OAAO,KACnB,OAAO,CAAC,MAAM,CAAC;mBA6ZX,MAAM,OACN,MAAM,aACA,MAAM,KAChB,OAAO,CAAC,IAAI,CAAC;;AAonBhB,wBASE"}