@x12i/catalox 3.1.1 → 3.3.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.
- package/README.md +6 -1
- package/dist/src/catalox/backup-data.d.ts +1 -6
- package/dist/src/catalox/backup-data.d.ts.map +1 -1
- package/dist/src/catalox/backup-data.js +71 -418
- package/dist/src/catalox/backup-data.js.map +1 -1
- package/dist/src/catalox/catalox-bound.d.ts +1 -2
- package/dist/src/catalox/catalox-bound.d.ts.map +1 -1
- package/dist/src/catalox/catalox-bound.js +0 -3
- package/dist/src/catalox/catalox-bound.js.map +1 -1
- package/dist/src/catalox/catalox.d.ts +2 -7
- package/dist/src/catalox/catalox.d.ts.map +1 -1
- package/dist/src/catalox/catalox.js +2 -16
- package/dist/src/catalox/catalox.js.map +1 -1
- package/dist/src/catalox/create-catalox.d.ts.map +1 -1
- package/dist/src/catalox/create-catalox.js +4 -1
- package/dist/src/catalox/create-catalox.js.map +1 -1
- package/dist/src/catalox/index.d.ts +2 -2
- package/dist/src/catalox/index.d.ts.map +1 -1
- package/dist/src/catalox/index.js +2 -2
- package/dist/src/catalox/index.js.map +1 -1
- package/dist/src/catalox/record-history.d.ts.map +1 -1
- package/dist/src/catalox/record-history.js +34 -0
- package/dist/src/catalox/record-history.js.map +1 -1
- package/dist/src/catalox/restore-firestore-backup.d.ts +6 -3
- package/dist/src/catalox/restore-firestore-backup.d.ts.map +1 -1
- package/dist/src/catalox/restore-firestore-backup.js +3 -224
- package/dist/src/catalox/restore-firestore-backup.js.map +1 -1
- package/dist/src/cli/index.js +27 -66
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/contracts/backup.d.ts +2 -2
- package/dist/src/contracts/backup.d.ts.map +1 -1
- package/dist/src/contracts/index.d.ts +1 -1
- package/dist/src/contracts/index.d.ts.map +1 -1
- package/dist/src/contracts/index.js.map +1 -1
- package/dist/src/contracts/restore.d.ts +0 -39
- package/dist/src/contracts/restore.d.ts.map +1 -1
- package/dist/src/firebase/catalog-item-history-store.d.ts.map +1 -1
- package/dist/src/firebase/catalog-item-history-store.js +35 -6
- package/dist/src/firebase/catalog-item-history-store.js.map +1 -1
- package/package.json +6 -3
- package/dist/adapters/api/api-adapter.d.ts +0 -22
- package/dist/adapters/api/api-adapter.d.ts.map +0 -1
- package/dist/adapters/api/api-adapter.js +0 -60
- package/dist/adapters/api/api-adapter.js.map +0 -1
- package/dist/adapters/api/index.d.ts +0 -2
- package/dist/adapters/api/index.d.ts.map +0 -1
- package/dist/adapters/api/index.js +0 -2
- package/dist/adapters/api/index.js.map +0 -1
- package/dist/adapters/mongo/index.d.ts +0 -3
- package/dist/adapters/mongo/index.d.ts.map +0 -1
- package/dist/adapters/mongo/index.js +0 -3
- package/dist/adapters/mongo/index.js.map +0 -1
- package/dist/adapters/mongo/mongo-adapter.d.ts +0 -21
- package/dist/adapters/mongo/mongo-adapter.d.ts.map +0 -1
- package/dist/adapters/mongo/mongo-adapter.js +0 -57
- package/dist/adapters/mongo/mongo-adapter.js.map +0 -1
- package/dist/adapters/mongo/resolve-db.d.ts +0 -4
- package/dist/adapters/mongo/resolve-db.d.ts.map +0 -1
- package/dist/adapters/mongo/resolve-db.js +0 -10
- package/dist/adapters/mongo/resolve-db.js.map +0 -1
- package/dist/bindings/index.d.ts +0 -2
- package/dist/bindings/index.d.ts.map +0 -1
- package/dist/bindings/index.js +0 -3
- package/dist/bindings/index.js.map +0 -1
- package/dist/catalox/authorization.d.ts +0 -11
- package/dist/catalox/authorization.d.ts.map +0 -1
- package/dist/catalox/authorization.js +0 -37
- package/dist/catalox/authorization.js.map +0 -1
- package/dist/catalox/catalox.d.ts +0 -80
- package/dist/catalox/catalox.d.ts.map +0 -1
- package/dist/catalox/catalox.js +0 -492
- package/dist/catalox/catalox.js.map +0 -1
- package/dist/catalox/context.d.ts +0 -17
- package/dist/catalox/context.d.ts.map +0 -1
- package/dist/catalox/context.js +0 -21
- package/dist/catalox/context.js.map +0 -1
- package/dist/catalox/identity.d.ts +0 -7
- package/dist/catalox/identity.d.ts.map +0 -1
- package/dist/catalox/identity.js +0 -26
- package/dist/catalox/identity.js.map +0 -1
- package/dist/catalox/index.d.ts +0 -6
- package/dist/catalox/index.d.ts.map +0 -1
- package/dist/catalox/index.js +0 -6
- package/dist/catalox/index.js.map +0 -1
- package/dist/catalox/json-io.d.ts +0 -3
- package/dist/catalox/json-io.d.ts.map +0 -1
- package/dist/catalox/json-io.js +0 -7
- package/dist/catalox/json-io.js.map +0 -1
- package/dist/contracts/adapters.d.ts +0 -40
- package/dist/contracts/adapters.d.ts.map +0 -1
- package/dist/contracts/adapters.js +0 -2
- package/dist/contracts/adapters.js.map +0 -1
- package/dist/contracts/apps.d.ts +0 -11
- package/dist/contracts/apps.d.ts.map +0 -1
- package/dist/contracts/apps.js +0 -2
- package/dist/contracts/apps.js.map +0 -1
- package/dist/contracts/bindings.d.ts +0 -26
- package/dist/contracts/bindings.d.ts.map +0 -1
- package/dist/contracts/bindings.js +0 -2
- package/dist/contracts/bindings.js.map +0 -1
- package/dist/contracts/bootstrap.d.ts +0 -7
- package/dist/contracts/bootstrap.d.ts.map +0 -1
- package/dist/contracts/bootstrap.js +0 -2
- package/dist/contracts/bootstrap.js.map +0 -1
- package/dist/contracts/catalogs.d.ts +0 -51
- package/dist/contracts/catalogs.d.ts.map +0 -1
- package/dist/contracts/catalogs.js +0 -2
- package/dist/contracts/catalogs.js.map +0 -1
- package/dist/contracts/config.d.ts +0 -14
- package/dist/contracts/config.d.ts.map +0 -1
- package/dist/contracts/config.js +0 -2
- package/dist/contracts/config.js.map +0 -1
- package/dist/contracts/context.d.ts +0 -11
- package/dist/contracts/context.d.ts.map +0 -1
- package/dist/contracts/context.js +0 -2
- package/dist/contracts/context.js.map +0 -1
- package/dist/contracts/descriptors.d.ts +0 -73
- package/dist/contracts/descriptors.d.ts.map +0 -1
- package/dist/contracts/descriptors.js +0 -2
- package/dist/contracts/descriptors.js.map +0 -1
- package/dist/contracts/discovery.d.ts +0 -25
- package/dist/contracts/discovery.d.ts.map +0 -1
- package/dist/contracts/discovery.js +0 -2
- package/dist/contracts/discovery.js.map +0 -1
- package/dist/contracts/errors.d.ts +0 -32
- package/dist/contracts/errors.d.ts.map +0 -1
- package/dist/contracts/errors.js +0 -53
- package/dist/contracts/errors.js.map +0 -1
- package/dist/contracts/ids.d.ts +0 -7
- package/dist/contracts/ids.d.ts.map +0 -1
- package/dist/contracts/ids.js +0 -2
- package/dist/contracts/ids.js.map +0 -1
- package/dist/contracts/index.d.ts +0 -21
- package/dist/contracts/index.d.ts.map +0 -1
- package/dist/contracts/index.js +0 -2
- package/dist/contracts/index.js.map +0 -1
- package/dist/contracts/inputs.d.ts +0 -19
- package/dist/contracts/inputs.d.ts.map +0 -1
- package/dist/contracts/inputs.js +0 -2
- package/dist/contracts/inputs.js.map +0 -1
- package/dist/contracts/item-validation.d.ts +0 -11
- package/dist/contracts/item-validation.d.ts.map +0 -1
- package/dist/contracts/item-validation.js +0 -2
- package/dist/contracts/item-validation.js.map +0 -1
- package/dist/contracts/items.d.ts +0 -49
- package/dist/contracts/items.d.ts.map +0 -1
- package/dist/contracts/items.js +0 -2
- package/dist/contracts/items.js.map +0 -1
- package/dist/contracts/mappings.d.ts +0 -35
- package/dist/contracts/mappings.d.ts.map +0 -1
- package/dist/contracts/mappings.js +0 -2
- package/dist/contracts/mappings.js.map +0 -1
- package/dist/contracts/query.d.ts +0 -10
- package/dist/contracts/query.d.ts.map +0 -1
- package/dist/contracts/query.js +0 -2
- package/dist/contracts/query.js.map +0 -1
- package/dist/contracts/references.d.ts +0 -11
- package/dist/contracts/references.d.ts.map +0 -1
- package/dist/contracts/references.js +0 -2
- package/dist/contracts/references.js.map +0 -1
- package/dist/contracts/sync.d.ts +0 -7
- package/dist/contracts/sync.d.ts.map +0 -1
- package/dist/contracts/sync.js +0 -2
- package/dist/contracts/sync.js.map +0 -1
- package/dist/contracts/validation.d.ts +0 -12
- package/dist/contracts/validation.d.ts.map +0 -1
- package/dist/contracts/validation.js +0 -2
- package/dist/contracts/validation.js.map +0 -1
- package/dist/errors/index.d.ts +0 -2
- package/dist/errors/index.d.ts.map +0 -1
- package/dist/errors/index.js +0 -3
- package/dist/errors/index.js.map +0 -1
- package/dist/firebase/adapter-store.d.ts +0 -15
- package/dist/firebase/adapter-store.d.ts.map +0 -1
- package/dist/firebase/adapter-store.js +0 -18
- package/dist/firebase/adapter-store.js.map +0 -1
- package/dist/firebase/app-store.d.ts +0 -11
- package/dist/firebase/app-store.d.ts.map +0 -1
- package/dist/firebase/app-store.js +0 -18
- package/dist/firebase/app-store.js.map +0 -1
- package/dist/firebase/binding-store.d.ts +0 -13
- package/dist/firebase/binding-store.d.ts.map +0 -1
- package/dist/firebase/binding-store.js +0 -35
- package/dist/firebase/binding-store.js.map +0 -1
- package/dist/firebase/catalog-store.d.ts +0 -12
- package/dist/firebase/catalog-store.d.ts.map +0 -1
- package/dist/firebase/catalog-store.js +0 -22
- package/dist/firebase/catalog-store.js.map +0 -1
- package/dist/firebase/definition-store.d.ts +0 -39
- package/dist/firebase/definition-store.d.ts.map +0 -1
- package/dist/firebase/definition-store.js +0 -18
- package/dist/firebase/definition-store.js.map +0 -1
- package/dist/firebase/descriptor-store.d.ts +0 -11
- package/dist/firebase/descriptor-store.d.ts.map +0 -1
- package/dist/firebase/descriptor-store.js +0 -18
- package/dist/firebase/descriptor-store.js.map +0 -1
- package/dist/firebase/firestore-store.d.ts +0 -14
- package/dist/firebase/firestore-store.d.ts.map +0 -1
- package/dist/firebase/firestore-store.js +0 -15
- package/dist/firebase/firestore-store.js.map +0 -1
- package/dist/firebase/index.d.ts +0 -12
- package/dist/firebase/index.d.ts.map +0 -1
- package/dist/firebase/index.js +0 -12
- package/dist/firebase/index.js.map +0 -1
- package/dist/firebase/mapping-store.d.ts +0 -11
- package/dist/firebase/mapping-store.d.ts.map +0 -1
- package/dist/firebase/mapping-store.js +0 -18
- package/dist/firebase/mapping-store.js.map +0 -1
- package/dist/firebase/native-item-store.d.ts +0 -20
- package/dist/firebase/native-item-store.d.ts.map +0 -1
- package/dist/firebase/native-item-store.js +0 -55
- package/dist/firebase/native-item-store.js.map +0 -1
- package/dist/firebase/reference-store.d.ts +0 -17
- package/dist/firebase/reference-store.d.ts.map +0 -1
- package/dist/firebase/reference-store.js +0 -27
- package/dist/firebase/reference-store.js.map +0 -1
- package/dist/firebase/snapshot-store.d.ts +0 -11
- package/dist/firebase/snapshot-store.d.ts.map +0 -1
- package/dist/firebase/snapshot-store.js +0 -22
- package/dist/firebase/snapshot-store.js.map +0 -1
- package/dist/index.d.ts +0 -10
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -10
- package/dist/index.js.map +0 -1
- package/dist/mapping/execute-mapping.d.ts +0 -8
- package/dist/mapping/execute-mapping.d.ts.map +0 -1
- package/dist/mapping/execute-mapping.js +0 -78
- package/dist/mapping/execute-mapping.js.map +0 -1
- package/dist/mapping/helper-gap-report.d.ts +0 -7
- package/dist/mapping/helper-gap-report.d.ts.map +0 -1
- package/dist/mapping/helper-gap-report.js +0 -2
- package/dist/mapping/helper-gap-report.js.map +0 -1
- package/dist/mapping/index.d.ts +0 -4
- package/dist/mapping/index.d.ts.map +0 -1
- package/dist/mapping/index.js +0 -4
- package/dist/mapping/index.js.map +0 -1
- package/dist/mapping/validate-mapping.d.ts +0 -3
- package/dist/mapping/validate-mapping.d.ts.map +0 -1
- package/dist/mapping/validate-mapping.js +0 -32
- package/dist/mapping/validate-mapping.js.map +0 -1
- package/dist/test/integration/firestore.emulator.test.d.ts +0 -2
- package/dist/test/integration/firestore.emulator.test.d.ts.map +0 -1
- package/dist/test/integration/firestore.emulator.test.js +0 -117
- package/dist/test/integration/firestore.emulator.test.js.map +0 -1
- package/dist/test/integration/record-history.live.test.d.ts +0 -2
- package/dist/test/integration/record-history.live.test.d.ts.map +0 -1
- package/dist/test/integration/record-history.live.test.js +0 -126
- package/dist/test/integration/record-history.live.test.js.map +0 -1
- package/dist/test/unit/backup-timestamp.test.d.ts +0 -2
- package/dist/test/unit/backup-timestamp.test.d.ts.map +0 -1
- package/dist/test/unit/backup-timestamp.test.js +0 -11
- package/dist/test/unit/backup-timestamp.test.js.map +0 -1
- package/dist/test/unit/catalox-gcs-backup-export-manifest.test.d.ts +0 -2
- package/dist/test/unit/catalox-gcs-backup-export-manifest.test.d.ts.map +0 -1
- package/dist/test/unit/catalox-gcs-backup-export-manifest.test.js +0 -20
- package/dist/test/unit/catalox-gcs-backup-export-manifest.test.js.map +0 -1
- package/dist/test/unit/compact-catalog-filter.test.d.ts +0 -2
- package/dist/test/unit/compact-catalog-filter.test.d.ts.map +0 -1
- package/dist/test/unit/compact-catalog-filter.test.js +0 -18
- package/dist/test/unit/compact-catalog-filter.test.js.map +0 -1
- package/dist/test/unit/field-source-resolution.test.d.ts +0 -2
- package/dist/test/unit/field-source-resolution.test.d.ts.map +0 -1
- package/dist/test/unit/field-source-resolution.test.js +0 -45
- package/dist/test/unit/field-source-resolution.test.js.map +0 -1
- package/dist/test/unit/gcs-backup-run-folder.test.d.ts +0 -2
- package/dist/test/unit/gcs-backup-run-folder.test.d.ts.map +0 -1
- package/dist/test/unit/gcs-backup-run-folder.test.js +0 -10
- package/dist/test/unit/gcs-backup-run-folder.test.js.map +0 -1
- package/dist/test/unit/gcs-firestore-compare.test.d.ts +0 -2
- package/dist/test/unit/gcs-firestore-compare.test.d.ts.map +0 -1
- package/dist/test/unit/gcs-firestore-compare.test.js +0 -14
- package/dist/test/unit/gcs-firestore-compare.test.js.map +0 -1
- package/dist/test/unit/gcs-firestore-paths.test.d.ts +0 -2
- package/dist/test/unit/gcs-firestore-paths.test.d.ts.map +0 -1
- package/dist/test/unit/gcs-firestore-paths.test.js +0 -16
- package/dist/test/unit/gcs-firestore-paths.test.js.map +0 -1
- package/dist/test/unit/identity.test.d.ts +0 -2
- package/dist/test/unit/identity.test.d.ts.map +0 -1
- package/dist/test/unit/identity.test.js +0 -18
- package/dist/test/unit/identity.test.js.map +0 -1
- package/dist/test/unit/jsx-snippets.test.d.ts +0 -2
- package/dist/test/unit/jsx-snippets.test.d.ts.map +0 -1
- package/dist/test/unit/jsx-snippets.test.js +0 -35
- package/dist/test/unit/jsx-snippets.test.js.map +0 -1
- package/dist/test/unit/mapping.test.d.ts +0 -2
- package/dist/test/unit/mapping.test.d.ts.map +0 -1
- package/dist/test/unit/mapping.test.js +0 -19
- package/dist/test/unit/mapping.test.js.map +0 -1
- package/dist/test/unit/markdown-and-diagrams.test.d.ts +0 -2
- package/dist/test/unit/markdown-and-diagrams.test.d.ts.map +0 -1
- package/dist/test/unit/markdown-and-diagrams.test.js +0 -62
- package/dist/test/unit/markdown-and-diagrams.test.js.map +0 -1
- package/dist/test/unit/native-catalog-merge.test.d.ts +0 -2
- package/dist/test/unit/native-catalog-merge.test.d.ts.map +0 -1
- package/dist/test/unit/native-catalog-merge.test.js +0 -33
- package/dist/test/unit/native-catalog-merge.test.js.map +0 -1
- package/dist/test/unit/native-scope.test.d.ts +0 -2
- package/dist/test/unit/native-scope.test.d.ts.map +0 -1
- package/dist/test/unit/native-scope.test.js +0 -29
- package/dist/test/unit/native-scope.test.js.map +0 -1
- package/dist/test/unit/record-history-path.test.d.ts +0 -2
- package/dist/test/unit/record-history-path.test.d.ts.map +0 -1
- package/dist/test/unit/record-history-path.test.js +0 -24
- package/dist/test/unit/record-history-path.test.js.map +0 -1
- package/dist/test/unit/renderer-metadata-migration.test.d.ts +0 -2
- package/dist/test/unit/renderer-metadata-migration.test.d.ts.map +0 -1
- package/dist/test/unit/renderer-metadata-migration.test.js +0 -33
- package/dist/test/unit/renderer-metadata-migration.test.js.map +0 -1
- package/dist/test/unit/renderer-metadata.test.d.ts +0 -2
- package/dist/test/unit/renderer-metadata.test.d.ts.map +0 -1
- package/dist/test/unit/renderer-metadata.test.js +0 -10
- package/dist/test/unit/renderer-metadata.test.js.map +0 -1
- package/dist/test/unit/resolve-native-items-layout.test.d.ts +0 -2
- package/dist/test/unit/resolve-native-items-layout.test.d.ts.map +0 -1
- package/dist/test/unit/resolve-native-items-layout.test.js +0 -50
- package/dist/test/unit/resolve-native-items-layout.test.js.map +0 -1
- package/dist/test/unit/ui-spec-validation.test.d.ts +0 -2
- package/dist/test/unit/ui-spec-validation.test.d.ts.map +0 -1
- package/dist/test/unit/ui-spec-validation.test.js +0 -90
- package/dist/test/unit/ui-spec-validation.test.js.map +0 -1
- package/dist/validation/index.d.ts +0 -2
- package/dist/validation/index.d.ts.map +0 -1
- package/dist/validation/index.js +0 -3
- package/dist/validation/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -54,7 +54,9 @@ Scoping:
|
|
|
54
54
|
- You can scope the service account to **Firestore-related IAM roles** and to a specific **project**.
|
|
55
55
|
- You generally cannot scope an Admin SDK credential to only certain collections/documents via IAM the way client rules work.
|
|
56
56
|
|
|
57
|
-
To connect to a real Firebase project, initialize `firebase-admin` using `GOOGLE_APPLICATION_CREDENTIALS` (service account JSON) or your preferred Admin initialization strategy.
|
|
57
|
+
To connect to a real Firebase project, initialize `firebase-admin` using `GOOGLE_APPLICATION_CREDENTIALS` (service account JSON), workload identity, or your preferred Admin initialization strategy.
|
|
58
|
+
|
|
59
|
+
This repo’s CLI + live tests typically load `.env` via **`@x12i/env`** and support **`GOOGLE_SERVICE_ACCOUNT_BASE64`** as a convenient local/dev secret carrier. That value is picked up **automatically** by the supporting x12i tooling (including **`@x12i/helpers/gcs`**) when present—**upstream code does not need to plumb it through**; just set it in the environment.
|
|
58
60
|
|
|
59
61
|
Minimal example:
|
|
60
62
|
|
|
@@ -81,6 +83,8 @@ Example `.env` (do not commit secrets):
|
|
|
81
83
|
```bash
|
|
82
84
|
MONGO_URI=mongodb://127.0.0.1:27017
|
|
83
85
|
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
|
|
86
|
+
# Alternative (supported by this repo’s CLI/tests and x12i helpers): base64-encoded service account JSON
|
|
87
|
+
# GOOGLE_SERVICE_ACCOUNT_BASE64=...
|
|
84
88
|
# Or (used by the packaged CLI migration + live integration tests):
|
|
85
89
|
FIREBASE_SERVICE_ACCOUNT_PATH=/path/to/service-account.json
|
|
86
90
|
FIREBASE_PROJECT_ID=your-project-id
|
|
@@ -104,6 +108,7 @@ The `catalox` binary loads `.env` via `dotenv`. Use **`CATALOX_*`** for **Catalo
|
|
|
104
108
|
- **`CATALOX_USER_ID`** — Optional user id / actor for authz-sensitive CLI paths.
|
|
105
109
|
- **`CATALOX_MONGO_URI`** — If set, enables the Mongo catalog adapter in the CLI (wired via **`createCatalox`** in `src/catalox/create-catalox.ts`, called from `src/cli/index.ts`).
|
|
106
110
|
- **`CATALOX_RECORD_HISTORY_BUCKET`** — If set, **`createCatalox`** wires **`recordHistory`** (GCS payloads + **`catalogItemHistory`** on native writes; also required for **`catalox history …`** to persist new events).
|
|
111
|
+
- **`CATALOX_BUCKET`** — Default GCS bucket for Catalox storage features (used by the CLI and live tests; also used as a fallback for `CATALOX_RECORD_HISTORY_BUCKET` when wiring `recordHistory` via env).
|
|
107
112
|
- **`CATALOX_RECORD_HISTORY_PREFIX`** — Optional GCS prefix for record history (default `catalox-record-history/`).
|
|
108
113
|
- **`CATALOX_RECORD_HISTORY_FAIL_CLOSED`** — When **`1`**, failed history writes fail the parent Firestore mutation.
|
|
109
114
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Readable } from "node:stream";
|
|
2
2
|
import { type Firestore } from "firebase-admin/firestore";
|
|
3
|
-
import { type Db } from "mongodb";
|
|
4
3
|
import type { CatalogId } from "../contracts/ids.js";
|
|
5
4
|
import type { BackupDataInput, BackupDataResult } from "../contracts/backup.js";
|
|
6
5
|
import type { PruneGcsBackupRunsInput, PruneGcsBackupRunsResult } from "../contracts/restore.js";
|
|
@@ -29,8 +28,6 @@ export declare function discoverNativeCatalogIds(fs: Firestore, catalogs: Catalo
|
|
|
29
28
|
catalogIds?: CatalogId[];
|
|
30
29
|
appId?: string;
|
|
31
30
|
}): Promise<CatalogId[]>;
|
|
32
|
-
/** Delete Mongo `backupRuns` documents with createdAt older than cutoff ISO string. */
|
|
33
|
-
export declare function pruneMongoBackupRuns(db: Db, createdBeforeIso: string): Promise<number>;
|
|
34
31
|
/** Exported for tests: `{gcsPrefix|default}/{formatBackupTimestamp}/` without trailing slash (helpers client prefix). */
|
|
35
32
|
export declare function gcsBackupRunFolder(ts: string, gcsPrefix?: string): string;
|
|
36
33
|
/** Subset of `@x12i/helpers/gcs` client used by Catalox backup / restore / prune. */
|
|
@@ -57,6 +54,7 @@ export type CataloxHelpersGcsClient = {
|
|
|
57
54
|
deleteObject: (objectName: string) => Promise<void>;
|
|
58
55
|
};
|
|
59
56
|
export declare const CATALOX_GCS_BACKUP_MANIFEST_FILENAME = "catalox-backup-manifest.json";
|
|
57
|
+
export declare function defaultCataloxBucketName(): string;
|
|
60
58
|
/** Same as internal backup client: `prefix` is the logical root prepended to relative object names. */
|
|
61
59
|
export declare function createCataloxHelpersGcsClient(bucket: string, prefixWithoutTrailingSlash: string): CataloxHelpersGcsClient;
|
|
62
60
|
/** Delete every object under one backup run folder (`…/{timestamp}/…`). */
|
|
@@ -68,8 +66,5 @@ export declare function deleteCataloxGcsBackupRunObjects(bucket: string, runFold
|
|
|
68
66
|
* Folder names are sorted lexicographically (compatible with `formatBackupTimestamp` ISO-style tokens).
|
|
69
67
|
*/
|
|
70
68
|
export declare function pruneGcsBackupRuns(input: PruneGcsBackupRunsInput): Promise<PruneGcsBackupRunsResult>;
|
|
71
|
-
export declare function pruneFirebaseVersionedBackups(firestore: Firestore, keepLast: number): Promise<{
|
|
72
|
-
deleted: string[];
|
|
73
|
-
}>;
|
|
74
69
|
export declare function runBackupData(deps: BackupDataDeps, input: BackupDataInput): Promise<BackupDataResult>;
|
|
75
70
|
//# sourceMappingURL=backup-data.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backup-data.d.ts","sourceRoot":"","sources":["../../../src/catalox/backup-data.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"backup-data.d.ts","sourceRoot":"","sources":["../../../src/catalox/backup-data.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { Readable } from "node:stream";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
3
4
|
import { FieldPath, } from "firebase-admin/firestore";
|
|
4
|
-
import { MongoClient } from "mongodb";
|
|
5
5
|
import { CatalogStore } from "../firebase/catalog-store.js";
|
|
6
6
|
import { BindingStore } from "../firebase/binding-store.js";
|
|
7
7
|
import { FirestoreStore } from "../firebase/firestore-store.js";
|
|
8
8
|
import { flatNativeItemsCollectionRef, legacyNativeItemsCollectionRef, nativeItemsCollectionId, resolveNativeItemsLayout, } from "../firebase/catalog-data-paths.js";
|
|
9
9
|
import { collectionRefFromSlashPath } from "../firebase/firestore-collection-path.js";
|
|
10
|
-
const MONGO_BACKUP_DB = "catalox-backups";
|
|
11
10
|
export const BACKUP_METADATA_SOURCE_NAMES = [
|
|
12
11
|
"apps",
|
|
13
12
|
"catalogs",
|
|
@@ -39,24 +38,7 @@ export function firebaseLatestName(logical) {
|
|
|
39
38
|
export function firebaseVersionedName(ts, logical) {
|
|
40
39
|
return `${ts}__backup-${logical}`;
|
|
41
40
|
}
|
|
42
|
-
|
|
43
|
-
return `__tmp_backup_${logical}`;
|
|
44
|
-
}
|
|
45
|
-
function mongoVersionedName(ts, logical) {
|
|
46
|
-
return `${ts}__${logical}`;
|
|
47
|
-
}
|
|
48
|
-
function mongoNativeItemsCollectionName(catalogId) {
|
|
49
|
-
return `catalogData__${sanitizeMongoCollectionPart(catalogId)}`;
|
|
50
|
-
}
|
|
51
|
-
function mongoSnapshotCollectionName(catalogId) {
|
|
52
|
-
return `catalogSnapshots__${sanitizeMongoCollectionPart(catalogId)}`;
|
|
53
|
-
}
|
|
54
|
-
function mongoSnapshotRunCollectionName(catalogId, runId) {
|
|
55
|
-
return `catalogSnapshotsRun__${sanitizeMongoCollectionPart(catalogId)}__${sanitizeMongoCollectionPart(runId)}`;
|
|
56
|
-
}
|
|
57
|
-
function sanitizeMongoCollectionPart(s) {
|
|
58
|
-
return String(s).replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
59
|
-
}
|
|
41
|
+
// Mongo backup mode has been removed; use explicit Mongo export/import instead.
|
|
60
42
|
export function firebaseNativeLogical(catalogId) {
|
|
61
43
|
return `catalogData--${catalogId}`;
|
|
62
44
|
}
|
|
@@ -132,208 +114,26 @@ async function copyNativeCatalogToFirestoreBackup(fs, catalogId, destCollectionP
|
|
|
132
114
|
: flatNativeItemsCollectionRef(fs, catalogId);
|
|
133
115
|
await paginatedCopyFirestoreToFirestore(fs, src.path, destCollectionPath, counts, destCollectionPath);
|
|
134
116
|
}
|
|
135
|
-
async function mongoReplacePage(col, docs) {
|
|
136
|
-
const bulk = docs.map((d) => {
|
|
137
|
-
const payload = { ...d.data() };
|
|
138
|
-
delete payload._id;
|
|
139
|
-
return {
|
|
140
|
-
replaceOne: {
|
|
141
|
-
filter: { _id: d.id },
|
|
142
|
-
replacement: { _id: d.id, ...payload },
|
|
143
|
-
upsert: true,
|
|
144
|
-
},
|
|
145
|
-
};
|
|
146
|
-
});
|
|
147
|
-
if (bulk.length)
|
|
148
|
-
await col.bulkWrite(bulk);
|
|
149
|
-
}
|
|
150
|
-
async function paginatedCopyFirestoreToMongo(fs, sourceCollectionPath, db, destCollectionName, counts, countKey) {
|
|
151
|
-
const src = fs.collection(sourceCollectionPath);
|
|
152
|
-
const col = db.collection(destCollectionName);
|
|
153
|
-
let last;
|
|
154
|
-
let total = 0;
|
|
155
|
-
while (true) {
|
|
156
|
-
let q = src.orderBy(FieldPath.documentId()).limit(200);
|
|
157
|
-
if (last)
|
|
158
|
-
q = q.startAfter(last);
|
|
159
|
-
const snap = await q.get();
|
|
160
|
-
if (snap.empty)
|
|
161
|
-
break;
|
|
162
|
-
await mongoReplacePage(col, snap.docs);
|
|
163
|
-
total += snap.docs.length;
|
|
164
|
-
last = snap.docs[snap.docs.length - 1];
|
|
165
|
-
}
|
|
166
|
-
counts[countKey] = (counts[countKey] ?? 0) + total;
|
|
167
|
-
}
|
|
168
|
-
async function promoteMongoLatest(db, tmpName, latestName) {
|
|
169
|
-
const n = await db.collection(tmpName).countDocuments();
|
|
170
|
-
if (n === 0) {
|
|
171
|
-
await db.collection(latestName).drop().catch(() => undefined);
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
await db.collection(latestName).drop().catch(() => undefined);
|
|
175
|
-
await db.collection(tmpName).rename(latestName, { dropTarget: true });
|
|
176
|
-
}
|
|
177
|
-
async function backupMetadataMongo(fs, db, ts, counts, collectionsWritten, versionedWritten, issues) {
|
|
178
|
-
for (const name of BACKUP_METADATA_SOURCE_NAMES) {
|
|
179
|
-
const tmp = mongoTmpName(name);
|
|
180
|
-
const versioned = mongoVersionedName(ts, name);
|
|
181
|
-
try {
|
|
182
|
-
await db.collection(tmp).drop().catch(() => undefined);
|
|
183
|
-
await paginatedCopyFirestoreToMongo(fs, name, db, tmp, counts, tmp);
|
|
184
|
-
await db.collection(versioned).drop().catch(() => undefined);
|
|
185
|
-
await paginatedCopyFirestoreToMongo(fs, name, db, versioned, counts, versioned);
|
|
186
|
-
await promoteMongoLatest(db, tmp, name);
|
|
187
|
-
collectionsWritten.push(name);
|
|
188
|
-
versionedWritten.push(versioned);
|
|
189
|
-
}
|
|
190
|
-
catch (e) {
|
|
191
|
-
issues.push({
|
|
192
|
-
code: "mongo_metadata_copy_failed",
|
|
193
|
-
message: e instanceof Error ? e.message : String(e),
|
|
194
|
-
detail: { name },
|
|
195
|
-
});
|
|
196
|
-
throw e;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
async function backupMetadataFirebase(fs, ts, counts, collectionsWritten, versionedWritten, issues) {
|
|
201
|
-
for (const name of BACKUP_METADATA_SOURCE_NAMES) {
|
|
202
|
-
const tmp = firebaseTmpName(name);
|
|
203
|
-
const latest = firebaseLatestName(name);
|
|
204
|
-
const ver = firebaseVersionedName(ts, name);
|
|
205
|
-
try {
|
|
206
|
-
await deleteAllDocsInFirestoreCollection(fs, tmp);
|
|
207
|
-
await paginatedCopyFirestoreToFirestore(fs, name, tmp, counts, tmp);
|
|
208
|
-
await paginatedCopyFirestoreToFirestore(fs, name, ver, counts, ver);
|
|
209
|
-
await deleteAllDocsInFirestoreCollection(fs, latest);
|
|
210
|
-
await paginatedCopyFirestoreToFirestore(fs, tmp, latest, counts, latest);
|
|
211
|
-
await deleteAllDocsInFirestoreCollection(fs, tmp);
|
|
212
|
-
collectionsWritten.push(latest);
|
|
213
|
-
versionedWritten.push(ver);
|
|
214
|
-
}
|
|
215
|
-
catch (e) {
|
|
216
|
-
issues.push({
|
|
217
|
-
code: "firebase_metadata_copy_failed",
|
|
218
|
-
message: e instanceof Error ? e.message : String(e),
|
|
219
|
-
detail: { name },
|
|
220
|
-
});
|
|
221
|
-
throw e;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
async function backupSnapshotsFirebase(fs, ts, counts, collectionsWritten, versionedWritten, issues) {
|
|
226
|
-
const roots = await fs.collection("catalogSnapshots").listDocuments();
|
|
227
|
-
for (const pref of roots) {
|
|
228
|
-
const catalogId = pref.id;
|
|
229
|
-
const logical = firebaseSnapshotLogical(catalogId);
|
|
230
|
-
const itemsPath = pref.collection("items").path;
|
|
231
|
-
const tmp = firebaseTmpName(logical);
|
|
232
|
-
const latest = firebaseLatestName(logical);
|
|
233
|
-
const ver = firebaseVersionedName(ts, logical);
|
|
234
|
-
try {
|
|
235
|
-
await deleteAllDocsInFirestoreCollection(fs, tmp);
|
|
236
|
-
await paginatedCopyFirestoreToFirestore(fs, itemsPath, tmp, counts, tmp);
|
|
237
|
-
await paginatedCopyFirestoreToFirestore(fs, itemsPath, ver, counts, ver);
|
|
238
|
-
await deleteAllDocsInFirestoreCollection(fs, latest);
|
|
239
|
-
await paginatedCopyFirestoreToFirestore(fs, tmp, latest, counts, latest);
|
|
240
|
-
await deleteAllDocsInFirestoreCollection(fs, tmp);
|
|
241
|
-
collectionsWritten.push(latest);
|
|
242
|
-
versionedWritten.push(ver);
|
|
243
|
-
const runs = await pref.collection("runs").listDocuments();
|
|
244
|
-
for (const runRef of runs) {
|
|
245
|
-
const runId = runRef.id;
|
|
246
|
-
const runLogical = firebaseSnapshotRunLogical(catalogId, runId);
|
|
247
|
-
const runItemsPath = runRef.collection("items").path;
|
|
248
|
-
const rtmp = firebaseTmpName(runLogical);
|
|
249
|
-
const rlatest = firebaseLatestName(runLogical);
|
|
250
|
-
const rver = firebaseVersionedName(ts, runLogical);
|
|
251
|
-
await deleteAllDocsInFirestoreCollection(fs, rtmp);
|
|
252
|
-
await paginatedCopyFirestoreToFirestore(fs, runItemsPath, rtmp, counts, rtmp);
|
|
253
|
-
await paginatedCopyFirestoreToFirestore(fs, runItemsPath, rver, counts, rver);
|
|
254
|
-
await deleteAllDocsInFirestoreCollection(fs, rlatest);
|
|
255
|
-
await paginatedCopyFirestoreToFirestore(fs, rtmp, rlatest, counts, rlatest);
|
|
256
|
-
await deleteAllDocsInFirestoreCollection(fs, rtmp);
|
|
257
|
-
collectionsWritten.push(rlatest);
|
|
258
|
-
versionedWritten.push(rver);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
catch (e) {
|
|
262
|
-
issues.push({
|
|
263
|
-
code: "firebase_snapshot_copy_failed",
|
|
264
|
-
message: e instanceof Error ? e.message : String(e),
|
|
265
|
-
detail: { catalogId },
|
|
266
|
-
});
|
|
267
|
-
throw e;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
async function backupSnapshotsMongo(fs, db, ts, counts, collectionsWritten, versionedWritten, issues) {
|
|
272
|
-
const roots = await fs.collection("catalogSnapshots").listDocuments();
|
|
273
|
-
for (const pref of roots) {
|
|
274
|
-
const catalogId = pref.id;
|
|
275
|
-
const itemsPath = pref.collection("items").path;
|
|
276
|
-
const latestName = mongoSnapshotCollectionName(catalogId);
|
|
277
|
-
const versionedName = mongoVersionedName(ts, latestName);
|
|
278
|
-
const tmpName = mongoTmpName(latestName);
|
|
279
|
-
try {
|
|
280
|
-
await db.collection(tmpName).drop().catch(() => undefined);
|
|
281
|
-
await paginatedCopyFirestoreToMongo(fs, itemsPath, db, tmpName, counts, tmpName);
|
|
282
|
-
await db.collection(versionedName).drop().catch(() => undefined);
|
|
283
|
-
await paginatedCopyFirestoreToMongo(fs, itemsPath, db, versionedName, counts, versionedName);
|
|
284
|
-
await promoteMongoLatest(db, tmpName, latestName);
|
|
285
|
-
collectionsWritten.push(latestName);
|
|
286
|
-
versionedWritten.push(versionedName);
|
|
287
|
-
const runs = await pref.collection("runs").listDocuments();
|
|
288
|
-
for (const runRef of runs) {
|
|
289
|
-
const runId = runRef.id;
|
|
290
|
-
const runItemsPath = runRef.collection("items").path;
|
|
291
|
-
const runLatest = mongoSnapshotRunCollectionName(catalogId, runId);
|
|
292
|
-
const runVer = mongoVersionedName(ts, runLatest);
|
|
293
|
-
const runTmp = mongoTmpName(runLatest);
|
|
294
|
-
await db.collection(runTmp).drop().catch(() => undefined);
|
|
295
|
-
await paginatedCopyFirestoreToMongo(fs, runItemsPath, db, runTmp, counts, runTmp);
|
|
296
|
-
await db.collection(runVer).drop().catch(() => undefined);
|
|
297
|
-
await paginatedCopyFirestoreToMongo(fs, runItemsPath, db, runVer, counts, runVer);
|
|
298
|
-
await promoteMongoLatest(db, runTmp, runLatest);
|
|
299
|
-
collectionsWritten.push(runLatest);
|
|
300
|
-
versionedWritten.push(runVer);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
catch (e) {
|
|
304
|
-
issues.push({
|
|
305
|
-
code: "mongo_snapshot_copy_failed",
|
|
306
|
-
message: e instanceof Error ? e.message : String(e),
|
|
307
|
-
detail: { catalogId },
|
|
308
|
-
});
|
|
309
|
-
throw e;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
async function writeBackupManifestFirebase(fs, payload) {
|
|
314
|
-
await fs.collection("backup-backupRuns").add({
|
|
315
|
-
...payload,
|
|
316
|
-
createdAt: new Date().toISOString(),
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
async function writeBackupManifestMongo(db, payload) {
|
|
320
|
-
await db.collection("backupRuns").insertOne({
|
|
321
|
-
...payload,
|
|
322
|
-
createdAt: new Date().toISOString(),
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
/** Delete Mongo `backupRuns` documents with createdAt older than cutoff ISO string. */
|
|
326
|
-
export async function pruneMongoBackupRuns(db, createdBeforeIso) {
|
|
327
|
-
const res = await db.collection("backupRuns").deleteMany({
|
|
328
|
-
createdAt: { $lt: createdBeforeIso },
|
|
329
|
-
});
|
|
330
|
-
return res.deletedCount ?? 0;
|
|
331
|
-
}
|
|
332
117
|
/**
|
|
333
118
|
* Delete documents in oldest Firestore versioned backup collections matching `{ts}__backup-*`
|
|
334
119
|
* until at most `keepLast` such collections remain.
|
|
335
120
|
*/
|
|
336
121
|
const require = createRequire(import.meta.url);
|
|
122
|
+
let warnedDefaultBucket = false;
|
|
123
|
+
function warnMissingCataloxBucketEnv(feature) {
|
|
124
|
+
if (warnedDefaultBucket)
|
|
125
|
+
return;
|
|
126
|
+
warnedDefaultBucket = true;
|
|
127
|
+
try {
|
|
128
|
+
const { createLogxer } = require("@x12i/logxer");
|
|
129
|
+
const log = createLogxer({ packageName: "CATALOX", envPrefix: "CATALOX", debugNamespace: "catalox" });
|
|
130
|
+
log.warn(`${feature}: CATALOX_BUCKET is not set; defaulting to bucket "catalox". ` +
|
|
131
|
+
`Set CATALOX_BUCKET to an existing bucket to avoid unexpected writes or bucket-create failures.`);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// If logxer can't initialize for any reason, do not crash the caller for a warning.
|
|
135
|
+
}
|
|
136
|
+
}
|
|
337
137
|
function normalizeBackupGcsBasePrefix(prefix) {
|
|
338
138
|
const p = (prefix ?? "").trim();
|
|
339
139
|
if (!p)
|
|
@@ -347,6 +147,41 @@ export function gcsBackupRunFolder(ts, gcsPrefix) {
|
|
|
347
147
|
return `${base.replace(/\/+$/, "")}${base ? "/" : ""}${ts}`.replace(/\/+$/, "");
|
|
348
148
|
}
|
|
349
149
|
export const CATALOX_GCS_BACKUP_MANIFEST_FILENAME = "catalox-backup-manifest.json";
|
|
150
|
+
function resolveGoogleCredentialsForStorage() {
|
|
151
|
+
// Prefer base64 carrier used in this repo.
|
|
152
|
+
const b64 = String(process.env.GOOGLE_SERVICE_ACCOUNT_BASE64 ?? "").trim();
|
|
153
|
+
if (b64) {
|
|
154
|
+
const json = Buffer.from(b64, "base64").toString("utf8");
|
|
155
|
+
const creds = JSON.parse(json);
|
|
156
|
+
const pid = String(creds.project_id ?? "").trim();
|
|
157
|
+
return { credentials: creds, ...(pid ? { projectId: pid } : {}) };
|
|
158
|
+
}
|
|
159
|
+
const p = String(process.env.FIREBASE_SERVICE_ACCOUNT_PATH ?? "").trim();
|
|
160
|
+
if (p) {
|
|
161
|
+
const json = readFileSync(p, "utf8");
|
|
162
|
+
const creds = JSON.parse(json);
|
|
163
|
+
const pid = String(creds.project_id ?? "").trim();
|
|
164
|
+
return { credentials: creds, ...(pid ? { projectId: pid } : {}) };
|
|
165
|
+
}
|
|
166
|
+
return {};
|
|
167
|
+
}
|
|
168
|
+
async function ensureGcsBucketExists(bucketName) {
|
|
169
|
+
// This uses @google-cloud/storage directly because @x12i/helpers/gcs does not create buckets.
|
|
170
|
+
// Requires permissions: storage.buckets.get + storage.buckets.create (if missing).
|
|
171
|
+
const { Storage } = require("@google-cloud/storage");
|
|
172
|
+
const auth = resolveGoogleCredentialsForStorage();
|
|
173
|
+
const storage = auth.credentials != null
|
|
174
|
+
? new Storage({ credentials: auth.credentials, ...(auth.projectId ? { projectId: auth.projectId } : {}) })
|
|
175
|
+
: new Storage();
|
|
176
|
+
const b = storage.bucket(bucketName);
|
|
177
|
+
const [exists] = await b.exists();
|
|
178
|
+
if (exists)
|
|
179
|
+
return;
|
|
180
|
+
await storage.createBucket(bucketName);
|
|
181
|
+
}
|
|
182
|
+
export function defaultCataloxBucketName() {
|
|
183
|
+
return "catalox";
|
|
184
|
+
}
|
|
350
185
|
function createHelpersGcsClient(bucket, prefixWithoutTrailingSlash) {
|
|
351
186
|
const { createGcsClient } = require("@x12i/helpers/gcs");
|
|
352
187
|
return createGcsClient({
|
|
@@ -512,23 +347,36 @@ async function runBackupDataGcs(deps, input, nativeCatalogIds) {
|
|
|
512
347
|
const fs = deps.firestoreStore.firestore;
|
|
513
348
|
const ts = formatBackupTimestamp(input.versionTimestamp);
|
|
514
349
|
const includeSnapshots = Boolean(input.includeSnapshots);
|
|
515
|
-
const
|
|
516
|
-
|
|
350
|
+
const envBucket = typeof process !== "undefined" ? String(process.env.CATALOX_BUCKET ?? "").trim() : "";
|
|
351
|
+
const rawBucket = input.gcsBucket?.trim() || envBucket || defaultCataloxBucketName();
|
|
352
|
+
if (!input.gcsBucket?.trim() && !envBucket)
|
|
353
|
+
warnMissingCataloxBucketEnv("backupData(gcs)");
|
|
354
|
+
const bucket = rawBucket.trim();
|
|
355
|
+
const runFolder = gcsBackupRunFolder(ts, input.gcsPrefix);
|
|
356
|
+
try {
|
|
357
|
+
await ensureGcsBucketExists(bucket);
|
|
358
|
+
}
|
|
359
|
+
catch (e) {
|
|
517
360
|
return {
|
|
518
361
|
ok: false,
|
|
519
362
|
mode: "gcs",
|
|
520
|
-
databaseName:
|
|
363
|
+
databaseName: `gs://${bucket}/`,
|
|
521
364
|
backupTimestamp: new Date().toISOString(),
|
|
522
365
|
...(input.backupLabel != null ? { backupLabel: input.backupLabel } : {}),
|
|
523
366
|
includeSnapshots,
|
|
524
367
|
collectionsWritten: [],
|
|
525
368
|
versionedCollectionsWritten: [],
|
|
526
369
|
counts: {},
|
|
527
|
-
issues: [
|
|
370
|
+
issues: [
|
|
371
|
+
{
|
|
372
|
+
code: "gcs_bucket_ensure_failed",
|
|
373
|
+
message: e instanceof Error ? e.message : String(e),
|
|
374
|
+
detail: { bucket },
|
|
375
|
+
},
|
|
376
|
+
],
|
|
528
377
|
nativeItemSourceLayoutByCatalogId: {},
|
|
529
378
|
};
|
|
530
379
|
}
|
|
531
|
-
const runFolder = gcsBackupRunFolder(ts, input.gcsPrefix);
|
|
532
380
|
const gcs = createHelpersGcsClient(bucket, runFolder);
|
|
533
381
|
try {
|
|
534
382
|
versionedCollectionsWritten.push(runFolder);
|
|
@@ -605,22 +453,6 @@ async function runBackupDataGcs(deps, input, nativeCatalogIds) {
|
|
|
605
453
|
};
|
|
606
454
|
}
|
|
607
455
|
}
|
|
608
|
-
export async function pruneFirebaseVersionedBackups(firestore, keepLast) {
|
|
609
|
-
const cols = await firestore.listCollections();
|
|
610
|
-
const versioned = cols
|
|
611
|
-
.map((c) => c.id)
|
|
612
|
-
.filter((id) => /^\d{4}_\d{2}_\d{2}T.*__backup-/.test(id))
|
|
613
|
-
.sort();
|
|
614
|
-
if (versioned.length <= keepLast)
|
|
615
|
-
return { deleted: [] };
|
|
616
|
-
const toRemove = versioned.slice(0, versioned.length - keepLast);
|
|
617
|
-
const deleted = [];
|
|
618
|
-
for (const id of toRemove) {
|
|
619
|
-
await deleteAllDocsInFirestoreCollection(firestore, id);
|
|
620
|
-
deleted.push(id);
|
|
621
|
-
}
|
|
622
|
-
return { deleted };
|
|
623
|
-
}
|
|
624
456
|
export async function runBackupData(deps, input) {
|
|
625
457
|
const issues = [];
|
|
626
458
|
const collectionsWritten = [];
|
|
@@ -636,185 +468,6 @@ export async function runBackupData(deps, input) {
|
|
|
636
468
|
...(input.catalogIds != null && input.catalogIds.length ? { catalogIds: input.catalogIds } : {}),
|
|
637
469
|
...(input.appId != null && input.appId !== "" ? { appId: input.appId } : {}),
|
|
638
470
|
});
|
|
639
|
-
|
|
640
|
-
if (!input.mongoBackupUri?.trim()) {
|
|
641
|
-
return {
|
|
642
|
-
ok: false,
|
|
643
|
-
mode: "mongo",
|
|
644
|
-
databaseName: MONGO_BACKUP_DB,
|
|
645
|
-
backupTimestamp: new Date().toISOString(),
|
|
646
|
-
...(input.backupLabel != null ? { backupLabel: input.backupLabel } : {}),
|
|
647
|
-
includeSnapshots,
|
|
648
|
-
collectionsWritten: [],
|
|
649
|
-
versionedCollectionsWritten: [],
|
|
650
|
-
counts: {},
|
|
651
|
-
issues: [{ code: "missing_mongo_uri", message: "mongoBackupUri is required for mongo mode" }],
|
|
652
|
-
nativeItemSourceLayoutByCatalogId: {},
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
const client = new MongoClient(input.mongoBackupUri);
|
|
656
|
-
try {
|
|
657
|
-
await client.connect();
|
|
658
|
-
const db = client.db(MONGO_BACKUP_DB);
|
|
659
|
-
await backupMetadataMongo(fs, db, ts, counts, collectionsWritten, versionedCollectionsWritten, issues);
|
|
660
|
-
for (const catalogId of nativeCatalogIds) {
|
|
661
|
-
const layout = await resolveNativeItemsLayout(fs, catalogId);
|
|
662
|
-
nativeItemSourceLayoutByCatalogId[String(catalogId)] = layout;
|
|
663
|
-
const logical = mongoNativeItemsCollectionName(String(catalogId));
|
|
664
|
-
const tmp = mongoTmpName(logical);
|
|
665
|
-
const versioned = mongoVersionedName(ts, logical);
|
|
666
|
-
await db.collection(tmp).drop().catch(() => undefined);
|
|
667
|
-
await copyNativeCatalogToMongo(fs, db, catalogId, tmp, counts, layout);
|
|
668
|
-
await db.collection(versioned).drop().catch(() => undefined);
|
|
669
|
-
await copyNativeCatalogToMongo(fs, db, catalogId, versioned, counts, layout);
|
|
670
|
-
await promoteMongoLatest(db, tmp, logical);
|
|
671
|
-
collectionsWritten.push(logical);
|
|
672
|
-
versionedCollectionsWritten.push(versioned);
|
|
673
|
-
}
|
|
674
|
-
if (includeSnapshots) {
|
|
675
|
-
await backupSnapshotsMongo(fs, db, ts, counts, collectionsWritten, versionedCollectionsWritten, issues);
|
|
676
|
-
}
|
|
677
|
-
await writeBackupManifestMongo(db, {
|
|
678
|
-
backupTimestamp: new Date().toISOString(),
|
|
679
|
-
mode: "mongo",
|
|
680
|
-
backupLabel: input.backupLabel,
|
|
681
|
-
includeSnapshots,
|
|
682
|
-
collectionsWritten,
|
|
683
|
-
versionedCollectionsWritten,
|
|
684
|
-
counts,
|
|
685
|
-
issues,
|
|
686
|
-
nativeItemSourceLayoutByCatalogId,
|
|
687
|
-
});
|
|
688
|
-
return {
|
|
689
|
-
ok: true,
|
|
690
|
-
mode: "mongo",
|
|
691
|
-
databaseName: MONGO_BACKUP_DB,
|
|
692
|
-
backupTimestamp: new Date().toISOString(),
|
|
693
|
-
...(input.backupLabel != null ? { backupLabel: input.backupLabel } : {}),
|
|
694
|
-
includeSnapshots,
|
|
695
|
-
collectionsWritten,
|
|
696
|
-
versionedCollectionsWritten,
|
|
697
|
-
counts,
|
|
698
|
-
issues,
|
|
699
|
-
nativeItemSourceLayoutByCatalogId,
|
|
700
|
-
};
|
|
701
|
-
}
|
|
702
|
-
catch (e) {
|
|
703
|
-
issues.push({
|
|
704
|
-
code: "mongo_backup_failed",
|
|
705
|
-
message: e instanceof Error ? e.message : String(e),
|
|
706
|
-
});
|
|
707
|
-
return {
|
|
708
|
-
ok: false,
|
|
709
|
-
mode: "mongo",
|
|
710
|
-
databaseName: MONGO_BACKUP_DB,
|
|
711
|
-
backupTimestamp: new Date().toISOString(),
|
|
712
|
-
...(input.backupLabel != null ? { backupLabel: input.backupLabel } : {}),
|
|
713
|
-
includeSnapshots,
|
|
714
|
-
collectionsWritten,
|
|
715
|
-
versionedCollectionsWritten,
|
|
716
|
-
counts,
|
|
717
|
-
issues,
|
|
718
|
-
nativeItemSourceLayoutByCatalogId,
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
finally {
|
|
722
|
-
await client.close().catch(() => undefined);
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
if (input.mode === "gcs") {
|
|
726
|
-
return await runBackupDataGcs(deps, input, nativeCatalogIds);
|
|
727
|
-
}
|
|
728
|
-
if (input.mode !== "firebase") {
|
|
729
|
-
return {
|
|
730
|
-
ok: false,
|
|
731
|
-
mode: input.mode,
|
|
732
|
-
databaseName: "",
|
|
733
|
-
backupTimestamp: new Date().toISOString(),
|
|
734
|
-
...(input.backupLabel != null ? { backupLabel: input.backupLabel } : {}),
|
|
735
|
-
includeSnapshots,
|
|
736
|
-
collectionsWritten: [],
|
|
737
|
-
versionedCollectionsWritten: [],
|
|
738
|
-
counts: {},
|
|
739
|
-
issues: [
|
|
740
|
-
{
|
|
741
|
-
code: "unknown_backup_mode",
|
|
742
|
-
message: `Unsupported backup mode: ${String(input.mode)}`,
|
|
743
|
-
},
|
|
744
|
-
],
|
|
745
|
-
nativeItemSourceLayoutByCatalogId: {},
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
try {
|
|
749
|
-
await backupMetadataFirebase(fs, ts, counts, collectionsWritten, versionedCollectionsWritten, issues);
|
|
750
|
-
for (const catalogId of nativeCatalogIds) {
|
|
751
|
-
const layout = await resolveNativeItemsLayout(fs, catalogId);
|
|
752
|
-
nativeItemSourceLayoutByCatalogId[String(catalogId)] = layout;
|
|
753
|
-
const logical = firebaseNativeLogical(String(catalogId));
|
|
754
|
-
const tmp = firebaseTmpName(logical);
|
|
755
|
-
const latest = firebaseLatestName(logical);
|
|
756
|
-
const ver = firebaseVersionedName(ts, logical);
|
|
757
|
-
await deleteAllDocsInFirestoreCollection(fs, tmp);
|
|
758
|
-
await copyNativeCatalogToFirestoreBackup(fs, catalogId, tmp, counts, layout);
|
|
759
|
-
await copyNativeCatalogToFirestoreBackup(fs, catalogId, ver, counts, layout);
|
|
760
|
-
await deleteAllDocsInFirestoreCollection(fs, latest);
|
|
761
|
-
await paginatedCopyFirestoreToFirestore(fs, tmp, latest, counts, latest);
|
|
762
|
-
await deleteAllDocsInFirestoreCollection(fs, tmp);
|
|
763
|
-
collectionsWritten.push(latest);
|
|
764
|
-
versionedCollectionsWritten.push(ver);
|
|
765
|
-
}
|
|
766
|
-
if (includeSnapshots) {
|
|
767
|
-
await backupSnapshotsFirebase(fs, ts, counts, collectionsWritten, versionedCollectionsWritten, issues);
|
|
768
|
-
}
|
|
769
|
-
await writeBackupManifestFirebase(fs, {
|
|
770
|
-
backupTimestamp: new Date().toISOString(),
|
|
771
|
-
mode: "firebase",
|
|
772
|
-
backupLabel: input.backupLabel,
|
|
773
|
-
includeSnapshots,
|
|
774
|
-
collectionsWritten,
|
|
775
|
-
versionedCollectionsWritten,
|
|
776
|
-
counts,
|
|
777
|
-
issues,
|
|
778
|
-
nativeItemSourceLayoutByCatalogId,
|
|
779
|
-
});
|
|
780
|
-
return {
|
|
781
|
-
ok: true,
|
|
782
|
-
mode: "firebase",
|
|
783
|
-
databaseName: "(same-firestore-target)",
|
|
784
|
-
backupTimestamp: new Date().toISOString(),
|
|
785
|
-
...(input.backupLabel != null ? { backupLabel: input.backupLabel } : {}),
|
|
786
|
-
includeSnapshots,
|
|
787
|
-
collectionsWritten,
|
|
788
|
-
versionedCollectionsWritten,
|
|
789
|
-
counts,
|
|
790
|
-
issues,
|
|
791
|
-
nativeItemSourceLayoutByCatalogId,
|
|
792
|
-
};
|
|
793
|
-
}
|
|
794
|
-
catch (e) {
|
|
795
|
-
issues.push({
|
|
796
|
-
code: "firebase_backup_failed",
|
|
797
|
-
message: e instanceof Error ? e.message : String(e),
|
|
798
|
-
});
|
|
799
|
-
return {
|
|
800
|
-
ok: false,
|
|
801
|
-
mode: "firebase",
|
|
802
|
-
databaseName: "(same-firestore-target)",
|
|
803
|
-
backupTimestamp: new Date().toISOString(),
|
|
804
|
-
...(input.backupLabel != null ? { backupLabel: input.backupLabel } : {}),
|
|
805
|
-
includeSnapshots,
|
|
806
|
-
collectionsWritten,
|
|
807
|
-
versionedCollectionsWritten,
|
|
808
|
-
counts,
|
|
809
|
-
issues,
|
|
810
|
-
nativeItemSourceLayoutByCatalogId,
|
|
811
|
-
};
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
async function copyNativeCatalogToMongo(fs, db, catalogId, destCollection, counts, layout) {
|
|
815
|
-
const src = layout === "legacy"
|
|
816
|
-
? legacyNativeItemsCollectionRef(fs, catalogId)
|
|
817
|
-
: flatNativeItemsCollectionRef(fs, catalogId);
|
|
818
|
-
await paginatedCopyFirestoreToMongo(fs, src.path, db, destCollection, counts, destCollection);
|
|
471
|
+
return await runBackupDataGcs(deps, input, nativeCatalogIds);
|
|
819
472
|
}
|
|
820
473
|
//# sourceMappingURL=backup-data.js.map
|