@stoker-platform/cli 0.5.100 → 0.5.102
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/init-files/.env/.env +1 -0
- package/init-files/bin/build.js +4 -1
- package/init-files/functions/src/index.ts +20 -0
- package/lib/package.json +4 -4
- package/lib/src/deploy/firestore-ttl/deployTTLs.js +4 -0
- package/lib/src/deploy/live-update/liveUpdate.js +3 -2
- package/lib/src/deploy/maintenance/activateMaintenanceMode.js +2 -2
- package/lib/src/deploy/maintenance/disableMaintenanceMode.js +2 -2
- package/lib/src/deploy/maintenance/setDeploymentStatus.js +2 -2
- package/lib/src/deploy/rules-indexes/generateFirestoreIndexes.js +2 -1
- package/lib/src/migration/firestore/operations/deleteField.js +3 -2
- package/lib/src/ops/auditDenormalized.js +2 -2
- package/lib/src/ops/auditPermissions.js +3 -4
- package/lib/src/ops/auditRelations.js +2 -2
- package/lib/src/ops/explainPreloadQueries.js +3 -2
- package/lib/src/ops/getUserPermissions.js +2 -2
- package/lib/src/ops/getUserRecord.js +2 -2
- package/lib/src/project/addProject.js +58 -31
- package/lib/src/project/addTenant.js +2 -2
- package/lib/src/project/deleteTenant.js +3 -3
- package/lib/src/utils/getCliFirestore.js +8 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
package/init-files/.env/.env
CHANGED
package/init-files/bin/build.js
CHANGED
|
@@ -4,6 +4,7 @@ import { fileURLToPath } from "url"
|
|
|
4
4
|
import { dirname, join } from "path"
|
|
5
5
|
import { readFile, writeFile, readdir } from "fs/promises"
|
|
6
6
|
import { existsSync, cpSync, rmSync } from "fs"
|
|
7
|
+
import { getFirestoreDatabaseId } from "@stoker-platform/utils"
|
|
7
8
|
import dotenv from "dotenv"
|
|
8
9
|
|
|
9
10
|
const envDir = join(process.cwd(), ".env")
|
|
@@ -101,6 +102,7 @@ try {
|
|
|
101
102
|
"SMTP_CONNECTION_URI=",
|
|
102
103
|
"firebaseextensions.v1beta.function/location=",
|
|
103
104
|
"DATABASE_REGION=",
|
|
105
|
+
"DATABASE=",
|
|
104
106
|
]
|
|
105
107
|
const filteredLines = extensionEnvFileLines.filter(
|
|
106
108
|
(line) => !linesToRemove.some((removeStr) => line.startsWith(removeStr)),
|
|
@@ -113,6 +115,7 @@ try {
|
|
|
113
115
|
filteredLines.push(`DEFAULT_REPLY_TO=${mailSender.split("=")[1].replace(/^"|"$/g, "")}`)
|
|
114
116
|
filteredLines.push(`SMTP_CONNECTION_URI=${mailSmtpConnectionUri.split("=")[1].replace(/^"|"$/g, "")}`)
|
|
115
117
|
filteredLines.push(`DATABASE_REGION=${databaseRegion.split("=")[1].replace(/^"|"$/g, "")}`)
|
|
118
|
+
filteredLines.push(`DATABASE=${getFirestoreDatabaseId(process.env.FB_FIRESTORE_EDITION, process.env.GCP_PROJECT)}`)
|
|
116
119
|
await writeFile(join(__dirname, "..", "extensions", "firestore-send-email.env"), filteredLines.join("\n"))
|
|
117
120
|
|
|
118
121
|
// Create functions .env file with filtered environment variables
|
|
@@ -122,7 +125,7 @@ try {
|
|
|
122
125
|
const projectSpecificEnvFile = join(envDir, `.env.${process.env.GCP_PROJECT}`)
|
|
123
126
|
|
|
124
127
|
let envContent = ""
|
|
125
|
-
const envPattern = /^(FB_FUNCTIONS_|FB_AI_REGION|STOKER_|ADMIN_)/
|
|
128
|
+
const envPattern = /^(FB_FIRESTORE_EDITION|FB_FUNCTIONS_|FB_AI_REGION|STOKER_|ADMIN_)/
|
|
126
129
|
|
|
127
130
|
if (existsSync(projectEnvFile)) {
|
|
128
131
|
const projectEnvContent = await readFile(projectEnvFile, "utf8")
|
|
@@ -39,6 +39,7 @@ import {defineSecret} from "firebase-functions/params";
|
|
|
39
39
|
import {readFileSync} from "fs";
|
|
40
40
|
import {
|
|
41
41
|
getPathCollections,
|
|
42
|
+
getFirestoreTriggerDatabase,
|
|
42
43
|
roleHasOperationAccess,
|
|
43
44
|
} from "@stoker-platform/utils";
|
|
44
45
|
import {
|
|
@@ -67,6 +68,12 @@ const consumeAppCheckToken =
|
|
|
67
68
|
process.env.FB_FUNCTIONS_CONSUME_APP_CHECK_TOKEN === "true";
|
|
68
69
|
const v1Region = process.env.FB_FUNCTIONS_V1_REGION ||
|
|
69
70
|
process.env.FB_FUNCTIONS_REGION;
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
72
|
+
const webAppConfig = JSON.parse(process.env.STOKER_FB_WEB_APP_CONFIG!);
|
|
73
|
+
const projectId = webAppConfig.projectId;
|
|
74
|
+
const firestoreDatabase =
|
|
75
|
+
getFirestoreTriggerDatabase(process.env.FB_FIRESTORE_EDITION, projectId);
|
|
76
|
+
|
|
70
77
|
|
|
71
78
|
const ai = genkit({
|
|
72
79
|
plugins: [vertexAI({
|
|
@@ -83,6 +90,7 @@ export const stoker: any = {};
|
|
|
83
90
|
|
|
84
91
|
stoker["notifications"] = onDocumentCreated({
|
|
85
92
|
document: "tenants/{tenantId}/Inbox/{messageId}",
|
|
93
|
+
database: firestoreDatabase,
|
|
86
94
|
}, (event) => {
|
|
87
95
|
return messageNotifications(
|
|
88
96
|
event,
|
|
@@ -182,6 +190,7 @@ Object.values(schema.collections).forEach((collectionSchema) => {
|
|
|
182
190
|
stoker[`validatefields${collectionNameLower}`] =
|
|
183
191
|
onDocumentWritten({
|
|
184
192
|
document,
|
|
193
|
+
database: firestoreDatabase,
|
|
185
194
|
retry: true,
|
|
186
195
|
}, (event) => {
|
|
187
196
|
return validateFields(
|
|
@@ -197,6 +206,7 @@ Object.values(schema.collections).forEach((collectionSchema) => {
|
|
|
197
206
|
stoker[`verifywritelog${collectionNameLower}`] =
|
|
198
207
|
onDocumentWritten({
|
|
199
208
|
document,
|
|
209
|
+
database: firestoreDatabase,
|
|
200
210
|
retry: true,
|
|
201
211
|
}, (event) => {
|
|
202
212
|
return verifyWriteLog(event, collectionSchema);
|
|
@@ -216,6 +226,7 @@ Object.values(schema.collections).forEach((collectionSchema) => {
|
|
|
216
226
|
stoker[`fulltextsearch${collectionNameLower}`] =
|
|
217
227
|
onDocumentWritten({
|
|
218
228
|
document,
|
|
229
|
+
database: firestoreDatabase,
|
|
219
230
|
retry: true,
|
|
220
231
|
secrets: [algoliaAdminKey],
|
|
221
232
|
}, (event) => {
|
|
@@ -234,6 +245,7 @@ Object.values(schema.collections).forEach((collectionSchema) => {
|
|
|
234
245
|
stoker[`autoincrement${collectionNameLower}`] =
|
|
235
246
|
onDocumentWritten({
|
|
236
247
|
document,
|
|
248
|
+
database: firestoreDatabase,
|
|
237
249
|
retry: true,
|
|
238
250
|
}, (event) => {
|
|
239
251
|
return autoIncrement(event, collectionSchema, schema);
|
|
@@ -243,6 +255,7 @@ Object.values(schema.collections).forEach((collectionSchema) => {
|
|
|
243
255
|
stoker[`validatedenormalized${collectionNameLower}`] =
|
|
244
256
|
onDocumentWritten({
|
|
245
257
|
document,
|
|
258
|
+
database: firestoreDatabase,
|
|
246
259
|
retry: true,
|
|
247
260
|
}, (event) => {
|
|
248
261
|
return validateDenormalized(event, collectionSchema, schema);
|
|
@@ -258,6 +271,7 @@ Object.values(schema.collections).forEach((collectionSchema) => {
|
|
|
258
271
|
stoker[`includefields${collectionNameLower}`] =
|
|
259
272
|
onDocumentUpdated({
|
|
260
273
|
document,
|
|
274
|
+
database: firestoreDatabase,
|
|
261
275
|
retry: true,
|
|
262
276
|
}, (event) => {
|
|
263
277
|
return updateIncludeFields(event, collectionSchema, schema);
|
|
@@ -273,6 +287,7 @@ Object.values(schema.collections).forEach((collectionSchema) => {
|
|
|
273
287
|
}`] =
|
|
274
288
|
onDocumentWritten({
|
|
275
289
|
document,
|
|
290
|
+
database: firestoreDatabase,
|
|
276
291
|
retry: true,
|
|
277
292
|
}, (event) => {
|
|
278
293
|
return validateRelations(
|
|
@@ -295,6 +310,7 @@ Object.values(schema.collections).forEach((collectionSchema) => {
|
|
|
295
310
|
}`] =
|
|
296
311
|
onDocumentDeleted({
|
|
297
312
|
document,
|
|
313
|
+
database: firestoreDatabase,
|
|
298
314
|
retry: true,
|
|
299
315
|
}, (event) => {
|
|
300
316
|
return removeRelations(
|
|
@@ -312,6 +328,7 @@ Object.values(schema.collections).forEach((collectionSchema) => {
|
|
|
312
328
|
stoker[`uniquedelete${collectionNameLower}`] =
|
|
313
329
|
onDocumentWritten({
|
|
314
330
|
document,
|
|
331
|
+
database: firestoreDatabase,
|
|
315
332
|
retry: true,
|
|
316
333
|
}, (event) => {
|
|
317
334
|
return uniqueDelete(
|
|
@@ -324,6 +341,7 @@ Object.values(schema.collections).forEach((collectionSchema) => {
|
|
|
324
341
|
stoker[`deletefiles${collectionNameLower}`] =
|
|
325
342
|
onDocumentDeleted({
|
|
326
343
|
document,
|
|
344
|
+
database: firestoreDatabase,
|
|
327
345
|
retry: true,
|
|
328
346
|
}, (event) => {
|
|
329
347
|
return deleteFiles(
|
|
@@ -336,6 +354,7 @@ Object.values(schema.collections).forEach((collectionSchema) => {
|
|
|
336
354
|
stoker[`embedding${collectionNameLower}`] =
|
|
337
355
|
onDocumentWritten({
|
|
338
356
|
document,
|
|
357
|
+
database: firestoreDatabase,
|
|
339
358
|
retry: true,
|
|
340
359
|
}, (event) => {
|
|
341
360
|
return writeEmbedding(
|
|
@@ -401,6 +420,7 @@ if (process.env.STOKER_SMS_ENABLED === "true") {
|
|
|
401
420
|
const twilioPhoneNumber = defineSecret("TWILIO_PHONE_NUMBER");
|
|
402
421
|
stoker["sendmessage"] = onDocumentCreated({
|
|
403
422
|
document: "system_messages/{messageId}",
|
|
423
|
+
database: firestoreDatabase,
|
|
404
424
|
retry: true,
|
|
405
425
|
secrets: [twilioAccountSid, twilioAuthToken, twilioPhoneNumber],
|
|
406
426
|
}, (event) => {
|
package/lib/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stoker-platform/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.101",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"main": "./lib/src/main.js",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"@google-cloud/secret-manager": "^6.1.2",
|
|
25
25
|
"@google-cloud/storage": "^7.19.0",
|
|
26
26
|
"@inquirer/prompts": "^8.5.2",
|
|
27
|
-
"@stoker-platform/node-client": "0.5.
|
|
28
|
-
"@stoker-platform/types": "0.5.
|
|
29
|
-
"@stoker-platform/utils": "0.5.
|
|
27
|
+
"@stoker-platform/node-client": "0.5.67",
|
|
28
|
+
"@stoker-platform/types": "0.5.46",
|
|
29
|
+
"@stoker-platform/utils": "0.5.58",
|
|
30
30
|
"algoliasearch": "^5.53.0",
|
|
31
31
|
"commander": "^15.0.0",
|
|
32
32
|
"cross-spawn": "^7.0.6",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { runChildProcess, initializeFirebase } from "@stoker-platform/node-client";
|
|
2
|
+
import { getFirestoreDatabaseId } from "@stoker-platform/utils";
|
|
2
3
|
import spawn from "cross-spawn";
|
|
3
4
|
import { generateSchema } from "../schema/generateSchema.js";
|
|
4
5
|
import { lintSchema } from "../../lint/lintSchema.js";
|
|
@@ -23,6 +24,7 @@ export const deployTTLs = async () => {
|
|
|
23
24
|
ttl,
|
|
24
25
|
`--collection-group=${collectionName}`,
|
|
25
26
|
"--enable-ttl",
|
|
27
|
+
`--database=${getFirestoreDatabaseId(process.env.FB_FIRESTORE_EDITION, projectId)}`,
|
|
26
28
|
`--project=${projectId}`,
|
|
27
29
|
"--quiet",
|
|
28
30
|
"--async",
|
|
@@ -41,6 +43,7 @@ export const deployTTLs = async () => {
|
|
|
41
43
|
ttl,
|
|
42
44
|
`--collection-group=${collectionName}`,
|
|
43
45
|
"--disable-ttl",
|
|
46
|
+
`--database=${getFirestoreDatabaseId(process.env.FB_FIRESTORE_EDITION, projectId)}`,
|
|
44
47
|
`--project=${projectId}`,
|
|
45
48
|
"--quiet",
|
|
46
49
|
"--async",
|
|
@@ -61,6 +64,7 @@ export const deployTTLs = async () => {
|
|
|
61
64
|
"ttls",
|
|
62
65
|
"list",
|
|
63
66
|
`--collection-group=${collection}`,
|
|
67
|
+
`--database=${getFirestoreDatabaseId(process.env.FB_FIRESTORE_EDITION, projectId)}`,
|
|
64
68
|
`--project=${projectId}`,
|
|
65
69
|
]);
|
|
66
70
|
let stdout = "";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { fetchCurrentSchema, initializeFirebase } from "@stoker-platform/node-client";
|
|
2
|
-
import {
|
|
2
|
+
import { FieldValue } from "firebase-admin/firestore";
|
|
3
|
+
import { getCLIFirestore } from "../../utils/getCLIFirestore.js";
|
|
3
4
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
5
|
export const liveUpdate = async (options) => {
|
|
5
6
|
await initializeFirebase();
|
|
6
|
-
const db =
|
|
7
|
+
const db = getCLIFirestore();
|
|
7
8
|
const batch = db.batch();
|
|
8
9
|
const currentSchema = await fetchCurrentSchema();
|
|
9
10
|
const deployId = db.collection("system_deployment").doc("latest_deploy").collection("deploy_history").doc().id;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { initializeFirebase } from "@stoker-platform/node-client";
|
|
2
|
-
import {
|
|
2
|
+
import { getCLIFirestore } from "../../utils/getCLIFirestore.js";
|
|
3
3
|
export const activateMaintenanceMode = async () => {
|
|
4
4
|
await initializeFirebase();
|
|
5
|
-
const db =
|
|
5
|
+
const db = getCLIFirestore();
|
|
6
6
|
await db.collection("system_deployment").doc("maintenance_mode").set({ active: true });
|
|
7
7
|
console.info("MAINTENANCE MODE ENGAGED");
|
|
8
8
|
process.exit();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { initializeFirebase } from "@stoker-platform/node-client";
|
|
2
|
-
import {
|
|
2
|
+
import { getCLIFirestore } from "../../utils/getCLIFirestore.js";
|
|
3
3
|
export const disableMaintenanceMode = async () => {
|
|
4
4
|
await initializeFirebase();
|
|
5
|
-
const db =
|
|
5
|
+
const db = getCLIFirestore();
|
|
6
6
|
await db.collection("system_deployment").doc("maintenance_mode").set({ active: false });
|
|
7
7
|
console.info("MAINTENANCE MODE DISENGAGED");
|
|
8
8
|
process.exit();
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { initializeFirebase } from "@stoker-platform/node-client";
|
|
2
|
-
import {
|
|
2
|
+
import { getCLIFirestore } from "../../utils/getCLIFirestore.js";
|
|
3
3
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
4
|
export const setDeploymentStatus = async (status) => {
|
|
5
5
|
await initializeFirebase();
|
|
6
|
-
const db =
|
|
6
|
+
const db = getCLIFirestore();
|
|
7
7
|
if (!["idle", "in_progress"].includes(status)) {
|
|
8
8
|
throw new Error("Invalid deployment status");
|
|
9
9
|
}
|
|
@@ -11,11 +11,12 @@ export const generateFirestoreIndexes = async () => {
|
|
|
11
11
|
const __dirname = dirname(__filename);
|
|
12
12
|
const schema = await generateSchema();
|
|
13
13
|
await lintSchema(true);
|
|
14
|
+
const edition = process.env.FB_FIRESTORE_EDITION || "enterprise";
|
|
14
15
|
const indexesResponse = await fetch(process.env.URL_FIRESTORE_INDEXES, {
|
|
15
16
|
headers: {
|
|
16
17
|
"Content-Type": "application/json",
|
|
17
18
|
},
|
|
18
|
-
body: JSON.stringify({ schema }),
|
|
19
|
+
body: JSON.stringify({ schema, edition }),
|
|
19
20
|
method: "POST",
|
|
20
21
|
});
|
|
21
22
|
const { indexes, message, error } = await indexesResponse.json();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FieldValue } from "firebase-admin/firestore";
|
|
2
2
|
import { appendFileSync } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
|
+
import { getCLIFirestore } from "../../../utils/getCLIFirestore.js";
|
|
4
5
|
export const deleteField = async (currentSchema, lastSchema) => {
|
|
5
6
|
const deletedFields = [];
|
|
6
7
|
const currentSchemaKeys = Object.keys(currentSchema.collections);
|
|
@@ -19,7 +20,7 @@ export const deleteField = async (currentSchema, lastSchema) => {
|
|
|
19
20
|
});
|
|
20
21
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
21
22
|
const filePath = join(process.cwd(), ".migration", process.env.GCP_PROJECT, `v${currentSchema.version.toString()}`);
|
|
22
|
-
const db =
|
|
23
|
+
const db = getCLIFirestore();
|
|
23
24
|
const bulkWriter = db.bulkWriter();
|
|
24
25
|
for (const field of deletedFields) {
|
|
25
26
|
const [collection, fieldName] = field.split(".");
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { fetchCurrentSchema, initializeStoker } from "@stoker-platform/node-client";
|
|
2
2
|
import { getDependencyIndexFields, getLowercaseFields, getRoleGroups, getSingleFieldRelations, isDependencyField, isRelationField, } from "@stoker-platform/utils";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { getCLIFirestore } from "../utils/getCLIFirestore.js";
|
|
4
5
|
import isEqual from "lodash/isEqual.js";
|
|
5
6
|
import isEmpty from "lodash/isEmpty.js";
|
|
6
|
-
import { getFirestore } from "firebase-admin/firestore";
|
|
7
7
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
8
|
export const auditDenormalized = async (options) => {
|
|
9
9
|
await initializeStoker(options.mode || "production", options.tenant, join(process.cwd(), "lib", "main.js"), join(process.cwd(), "lib", "collections"));
|
|
10
10
|
const schema = await fetchCurrentSchema();
|
|
11
|
-
const db =
|
|
11
|
+
const db = getCLIFirestore();
|
|
12
12
|
for (const [collectionName, collectionSchema] of Object.entries(schema.collections)) {
|
|
13
13
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
14
|
const collectionData = {};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { fetchCurrentSchema, initializeStoker } from "@stoker-platform/node-client";
|
|
2
|
-
import { getFirestore } from "firebase-admin/firestore";
|
|
3
2
|
import { join } from "node:path";
|
|
3
|
+
import { getCLIFirestore } from "../utils/getCLIFirestore.js";
|
|
4
4
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
5
|
export const auditPermissions = async (options) => {
|
|
6
6
|
await initializeStoker(options.mode || "production", options.tenant, join(process.cwd(), "lib", "main.js"), join(process.cwd(), "lib", "collections"));
|
|
7
7
|
const schema = await fetchCurrentSchema();
|
|
8
|
-
const db =
|
|
9
|
-
const dbMain = getFirestore();
|
|
8
|
+
const db = getCLIFirestore();
|
|
10
9
|
const mismatches = [];
|
|
11
10
|
const permissions = await db.collection("tenants").doc(options.tenant).collection("system_user_permissions").get();
|
|
12
11
|
for (const authCollection of Object.values(schema.collections)) {
|
|
@@ -79,7 +78,7 @@ export const auditPermissions = async (options) => {
|
|
|
79
78
|
}
|
|
80
79
|
console.log(mismatches.join("\n\n"));
|
|
81
80
|
if (options.email && mismatches.length > 0) {
|
|
82
|
-
await
|
|
81
|
+
await db.collection("system_mail").add({
|
|
83
82
|
to: options.email,
|
|
84
83
|
message: {
|
|
85
84
|
subject: `Stoker Permissions Audit`,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { fetchCurrentSchema, getFirestorePathRef, initializeStoker } from "@stoker-platform/node-client";
|
|
2
|
-
import { getFirestore } from "firebase-admin/firestore";
|
|
3
2
|
import { join } from "node:path";
|
|
3
|
+
import { getCLIFirestore } from "../utils/getCLIFirestore.js";
|
|
4
4
|
import isEqual from "lodash/isEqual.js";
|
|
5
5
|
import { getField, getLowercaseFields, getSingleFieldRelations } from "@stoker-platform/utils";
|
|
6
6
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
7
|
export const auditRelations = async (options) => {
|
|
8
8
|
await initializeStoker(options.mode || "production", options.tenant, join(process.cwd(), "lib", "main.js"), join(process.cwd(), "lib", "collections"));
|
|
9
9
|
const schema = await fetchCurrentSchema();
|
|
10
|
-
const db =
|
|
10
|
+
const db = getCLIFirestore();
|
|
11
11
|
const tenantPrefix = `tenants/${options.tenant}`;
|
|
12
12
|
const recordByDocumentPath = new Map();
|
|
13
13
|
const collectionDocsByName = new Map();
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { fetchCurrentSchema, getCollectionRefs, initializeStoker } from "@stoker-platform/node-client";
|
|
2
2
|
import { tryPromise, getRange } from "@stoker-platform/utils";
|
|
3
|
-
import { Filter
|
|
3
|
+
import { Filter } from "firebase-admin/firestore";
|
|
4
4
|
import { join } from "path";
|
|
5
|
+
import { getCLIFirestore } from "../utils/getCLIFirestore.js";
|
|
5
6
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
7
|
export const explainPreloadQueries = async (options) => {
|
|
7
8
|
const { getGlobalConfigModule } = await initializeStoker("production", options.tenant, join(process.cwd(), "lib", "main.js"), join(process.cwd(), "lib", "collections"));
|
|
8
9
|
const globalConfig = getGlobalConfigModule();
|
|
9
10
|
const schema = await fetchCurrentSchema();
|
|
10
|
-
const db =
|
|
11
|
+
const db = getCLIFirestore();
|
|
11
12
|
const permissionsSnapshot = await db
|
|
12
13
|
.collection("tenants")
|
|
13
14
|
.doc(options.tenant)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { initializeFirebase } from "@stoker-platform/node-client";
|
|
2
|
-
import {
|
|
2
|
+
import { getCLIFirestore } from "../utils/getCLIFirestore.js";
|
|
3
3
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
4
|
export const getUserPermissions = async (options) => {
|
|
5
5
|
await initializeFirebase();
|
|
6
|
-
const db =
|
|
6
|
+
const db = getCLIFirestore();
|
|
7
7
|
const usersRef = await db
|
|
8
8
|
.collection("tenants")
|
|
9
9
|
.doc(options.tenant)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { initializeFirebase } from "@stoker-platform/node-client";
|
|
2
|
-
import {
|
|
2
|
+
import { getCLIFirestore } from "../utils/getCLIFirestore.js";
|
|
3
3
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
4
|
export const getUserRecord = async (options) => {
|
|
5
5
|
await initializeFirebase();
|
|
6
|
-
const db =
|
|
6
|
+
const db = getCLIFirestore();
|
|
7
7
|
const usersRef = await db
|
|
8
8
|
.collection("tenants")
|
|
9
9
|
.doc(options.tenant)
|
|
@@ -2,7 +2,7 @@ import { runChildProcess } from "@stoker-platform/node-client";
|
|
|
2
2
|
import { writeFile, unlink, readFile } from "fs/promises";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import dotenv from "dotenv";
|
|
5
|
-
import { retryOperation } from "@stoker-platform/utils";
|
|
5
|
+
import { retryOperation, getFirestoreDatabaseId } from "@stoker-platform/utils";
|
|
6
6
|
import { SecretManagerServiceClient } from "@google-cloud/secret-manager";
|
|
7
7
|
import { addTenant } from "./addTenant.js";
|
|
8
8
|
import { existsSync } from "fs";
|
|
@@ -277,20 +277,26 @@ export const addProject = async (options) => {
|
|
|
277
277
|
}
|
|
278
278
|
if (getProgress() < 10) {
|
|
279
279
|
await new Promise((resolve) => setTimeout(resolve, 10000));
|
|
280
|
+
const args = [
|
|
281
|
+
"firestore",
|
|
282
|
+
"databases",
|
|
283
|
+
"create",
|
|
284
|
+
`--location=${process.env.FB_FIRESTORE_REGION}`,
|
|
285
|
+
"--type=firestore-native",
|
|
286
|
+
process.env.FB_FIRESTORE_EDITION ? `--edition=${process.env.FB_FIRESTORE_EDITION}` : "--edition=enterprise",
|
|
287
|
+
"--delete-protection",
|
|
288
|
+
options.pitr && process.env.FB_FIRESTORE_ENABLE_PITR !== "false" ? "--enable-pitr" : "--no-enable-pitr",
|
|
289
|
+
`--project=${projectId}`,
|
|
290
|
+
"--quiet",
|
|
291
|
+
];
|
|
292
|
+
if ((process.env.FB_FIRESTORE_EDITION || "enterprise") === "enterprise") {
|
|
293
|
+
args.push(`--database=${projectId}`);
|
|
294
|
+
args.push("--enable-firestore-data-access");
|
|
295
|
+
args.push("--enable-realtime-updates");
|
|
296
|
+
args.push("--concurrency-mode=pessimistic");
|
|
297
|
+
}
|
|
280
298
|
await retryOperation(async () => {
|
|
281
|
-
await runChildProcess("gcloud",
|
|
282
|
-
"firestore",
|
|
283
|
-
"databases",
|
|
284
|
-
"create",
|
|
285
|
-
`--location=${process.env.FB_FIRESTORE_REGION}`,
|
|
286
|
-
"--type=firestore-native",
|
|
287
|
-
"--delete-protection",
|
|
288
|
-
options.pitr && process.env.FB_FIRESTORE_ENABLE_PITR !== "false"
|
|
289
|
-
? "--enable-pitr"
|
|
290
|
-
: "--no-enable-pitr",
|
|
291
|
-
`--project=${projectId}`,
|
|
292
|
-
"--quiet",
|
|
293
|
-
]).catch(() => {
|
|
299
|
+
await runChildProcess("gcloud", args).catch(() => {
|
|
294
300
|
throw new Error("Error creating Firestore database.");
|
|
295
301
|
});
|
|
296
302
|
}, [], undefined, 5000);
|
|
@@ -313,7 +319,7 @@ export const addProject = async (options) => {
|
|
|
313
319
|
: process.env.FB_FIRESTORE_BACKUP_RETENTION
|
|
314
320
|
? `--retention=${process.env.FB_FIRESTORE_BACKUP_RETENTION}`
|
|
315
321
|
: "--retention=7d",
|
|
316
|
-
|
|
322
|
+
`--database=${getFirestoreDatabaseId(process.env.FB_FIRESTORE_EDITION, projectId)}`,
|
|
317
323
|
`--project=${projectId}`,
|
|
318
324
|
"--quiet",
|
|
319
325
|
];
|
|
@@ -432,6 +438,18 @@ export const addProject = async (options) => {
|
|
|
432
438
|
await runChildProcess("attrib", ["-H", firebasercPath]);
|
|
433
439
|
}
|
|
434
440
|
await runChildProcess("firebase", ["target:apply", "storage", "default", projectId, "--project", projectId]);
|
|
441
|
+
if ((process.env.FB_FIRESTORE_EDITION || "enterprise") === "enterprise") {
|
|
442
|
+
const firebaseJsonPath = join(process.cwd(), "firebase.json");
|
|
443
|
+
const firebaseJson = JSON.parse(await readFile(firebaseJsonPath, "utf8"));
|
|
444
|
+
firebaseJson.firestore = [
|
|
445
|
+
{
|
|
446
|
+
database: projectId,
|
|
447
|
+
rules: "firebase-rules/firestore.rules",
|
|
448
|
+
indexes: "firebase-rules/firestore.indexes.json",
|
|
449
|
+
},
|
|
450
|
+
];
|
|
451
|
+
await writeFile(firebaseJsonPath, JSON.stringify(firebaseJson, null, 4), "utf8");
|
|
452
|
+
}
|
|
435
453
|
await updateProjectData(17);
|
|
436
454
|
}
|
|
437
455
|
if (getProgress() < 18) {
|
|
@@ -750,23 +768,31 @@ export const addProject = async (options) => {
|
|
|
750
768
|
if (getProgress() < 33) {
|
|
751
769
|
const externalSecrets = JSON.parse(process.env.EXTERNAL_SECRETS || "{}");
|
|
752
770
|
for (const [secretName, secretValue] of Object.entries(externalSecrets)) {
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
771
|
+
try {
|
|
772
|
+
const [externalSecret] = await secretManager.createSecret({
|
|
773
|
+
parent: `projects/${projectId}`,
|
|
774
|
+
secret: {
|
|
775
|
+
name: secretName,
|
|
776
|
+
replication: {
|
|
777
|
+
automatic: {},
|
|
778
|
+
},
|
|
759
779
|
},
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
|
|
780
|
+
secretId: secretName,
|
|
781
|
+
});
|
|
782
|
+
const [secretVersion] = await secretManager.addSecretVersion({
|
|
783
|
+
parent: externalSecret.name,
|
|
784
|
+
payload: {
|
|
785
|
+
data: Buffer.from(secretValue, "utf8"),
|
|
786
|
+
},
|
|
787
|
+
});
|
|
788
|
+
console.log(secretVersion);
|
|
789
|
+
}
|
|
790
|
+
catch (error) {
|
|
791
|
+
if (error?.code === 6) {
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
throw error;
|
|
795
|
+
}
|
|
770
796
|
}
|
|
771
797
|
await updateProjectData(33);
|
|
772
798
|
}
|
|
@@ -951,6 +977,7 @@ STOKER_FB_ENABLE_APP_CHECK=${process.env.FB_ENABLE_APP_CHECK}
|
|
|
951
977
|
STOKER_FB_APP_CHECK_KEY="${recaptchaKeyId}"
|
|
952
978
|
STOKER_ALGOLIA_ID="${process.env.ALGOLIA_ID || ""}"
|
|
953
979
|
STOKER_FB_FUNCTIONS_REGION="${process.env.FB_FUNCTIONS_REGION}"
|
|
980
|
+
STOKER_FB_FIRESTORE_EDITION="${process.env.FB_FIRESTORE_EDITION || "enterprise"}"
|
|
954
981
|
FB_DATABASE="${projectId}-default-rtdb"
|
|
955
982
|
FB_FIRESTORE_EXPORT_BUCKET="${projectId}-export"`;
|
|
956
983
|
if (process.env.SENTRY_DSN && !options.development) {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { input } from "@inquirer/prompts";
|
|
2
2
|
import { fetchCurrentSchema, initializeFirebase, initializeStoker } from "@stoker-platform/node-client";
|
|
3
|
-
import { getFirestore } from "firebase-admin/firestore";
|
|
4
3
|
import { join } from "path";
|
|
5
4
|
import { retryOperation, isRelationField } from "@stoker-platform/utils";
|
|
6
5
|
import { addRecordPrompt } from "./addRecordPrompt.js";
|
|
6
|
+
import { getCLIFirestore } from "../utils/getCLIFirestore.js";
|
|
7
7
|
export const addTenant = async () => {
|
|
8
8
|
await initializeFirebase();
|
|
9
|
-
const db =
|
|
9
|
+
const db = getCLIFirestore();
|
|
10
10
|
const doc = await db.collection("tenants").add({});
|
|
11
11
|
const tenantId = doc.id;
|
|
12
12
|
await initializeStoker("production", tenantId, join(process.cwd(), "lib", "main.js"), join(process.cwd(), "lib", "collections"));
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { fetchCurrentSchema, initializeFirebase } from "@stoker-platform/node-client";
|
|
2
2
|
import { getAuth } from "firebase-admin/auth";
|
|
3
|
-
import { getFirestore } from "firebase-admin/firestore";
|
|
4
|
-
import { getStorage } from "firebase-admin/storage";
|
|
5
3
|
import { getApp } from "firebase-admin/app";
|
|
4
|
+
import { getStorage } from "firebase-admin/storage";
|
|
5
|
+
import { getCLIFirestore } from "../utils/getCLIFirestore.js";
|
|
6
6
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7
7
|
export const deleteTenant = async (options) => {
|
|
8
8
|
await initializeFirebase();
|
|
9
9
|
const app = getApp();
|
|
10
10
|
const auth = getAuth();
|
|
11
|
-
const db =
|
|
11
|
+
const db = getCLIFirestore();
|
|
12
12
|
const storage = getStorage();
|
|
13
13
|
const schema = await fetchCurrentSchema();
|
|
14
14
|
const users = await db.collection("tenants").doc(options.tenant).collection("system_user_permissions").get();
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { getApp } from "firebase-admin/app";
|
|
2
|
+
import { getFirestore } from "firebase-admin/firestore";
|
|
3
|
+
import { getFirestoreDatabaseId } from "@stoker-platform/utils";
|
|
4
|
+
export const getCLIFirestore = (app) => {
|
|
5
|
+
return getFirestore(app || getApp(),
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
7
|
+
getFirestoreDatabaseId(process.env.FB_FIRESTORE_EDITION, process.env.GCP_PROJECT));
|
|
8
|
+
};
|
package/lib/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/main.ts","../src/data/exporttobigquery.ts","../src/data/seeddata.ts","../src/deploy/deployproject.ts","../src/deploy/cloud-functions/getfunctionsdata.ts","../src/deploy/firestore-export/exportfirestoredata.ts","../src/deploy/firestore-ttl/deployttls.ts","../src/deploy/live-update/liveupdate.ts","../src/deploy/maintenance/activatemaintenancemode.ts","../src/deploy/maintenance/disablemaintenancemode.ts","../src/deploy/maintenance/setdeploymentstatus.ts","../src/deploy/rules-indexes/generatefirestoreindexes.ts","../src/deploy/rules-indexes/generatefirestorerules.ts","../src/deploy/rules-indexes/generatestoragerules.ts","../src/deploy/schema/applyschema.ts","../src/deploy/schema/generateschema.ts","../src/deploy/schema/persistschema.ts","../src/deploy/schema/updateliveschema.ts","../src/lint/lintschema.ts","../src/lint/securityreport.ts","../src/migration/migrateall.ts","../src/migration/firestore/migratefirestore.ts","../src/migration/firestore/operations/deletefield.ts","../src/ops/auditdenormalized.ts","../src/ops/auditpermissions.ts","../src/ops/auditrelations.ts","../src/ops/explainpreloadqueries.ts","../src/ops/getuser.ts","../src/ops/getuserpermissions.ts","../src/ops/getuserrecord.ts","../src/ops/listprojects.ts","../src/ops/setusercollection.ts","../src/ops/setuserdocument.ts","../src/ops/setuserrole.ts","../src/project/addproject.ts","../src/project/addrecord.ts","../src/project/addrecordprompt.ts","../src/project/addtenant.ts","../src/project/buildwebapp.ts","../src/project/customdomain.ts","../src/project/deleteproject.ts","../src/project/deleterecord.ts","../src/project/deletetenant.ts","../src/project/getone.ts","../src/project/getsome.ts","../src/project/initproject.ts","../src/project/prepareemulatordata.ts","../src/project/setproject.ts","../src/project/startemulators.ts","../src/project/updaterecord.ts","../src/types/generatetypes.ts"],"version":"6.0.3"}
|
|
1
|
+
{"root":["../src/main.ts","../src/data/exporttobigquery.ts","../src/data/seeddata.ts","../src/deploy/deployproject.ts","../src/deploy/cloud-functions/getfunctionsdata.ts","../src/deploy/firestore-export/exportfirestoredata.ts","../src/deploy/firestore-ttl/deployttls.ts","../src/deploy/live-update/liveupdate.ts","../src/deploy/maintenance/activatemaintenancemode.ts","../src/deploy/maintenance/disablemaintenancemode.ts","../src/deploy/maintenance/setdeploymentstatus.ts","../src/deploy/rules-indexes/generatefirestoreindexes.ts","../src/deploy/rules-indexes/generatefirestorerules.ts","../src/deploy/rules-indexes/generatestoragerules.ts","../src/deploy/schema/applyschema.ts","../src/deploy/schema/generateschema.ts","../src/deploy/schema/persistschema.ts","../src/deploy/schema/updateliveschema.ts","../src/lint/lintschema.ts","../src/lint/securityreport.ts","../src/migration/migrateall.ts","../src/migration/firestore/migratefirestore.ts","../src/migration/firestore/operations/deletefield.ts","../src/ops/auditdenormalized.ts","../src/ops/auditpermissions.ts","../src/ops/auditrelations.ts","../src/ops/explainpreloadqueries.ts","../src/ops/getuser.ts","../src/ops/getuserpermissions.ts","../src/ops/getuserrecord.ts","../src/ops/listprojects.ts","../src/ops/setusercollection.ts","../src/ops/setuserdocument.ts","../src/ops/setuserrole.ts","../src/project/addproject.ts","../src/project/addrecord.ts","../src/project/addrecordprompt.ts","../src/project/addtenant.ts","../src/project/buildwebapp.ts","../src/project/customdomain.ts","../src/project/deleteproject.ts","../src/project/deleterecord.ts","../src/project/deletetenant.ts","../src/project/getone.ts","../src/project/getsome.ts","../src/project/initproject.ts","../src/project/prepareemulatordata.ts","../src/project/setproject.ts","../src/project/startemulators.ts","../src/project/updaterecord.ts","../src/types/generatetypes.ts","../src/utils/getclifirestore.ts"],"version":"6.0.3"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stoker-platform/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.102",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"main": "./lib/src/main.js",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"@google-cloud/secret-manager": "^6.1.2",
|
|
25
25
|
"@google-cloud/storage": "^7.19.0",
|
|
26
26
|
"@inquirer/prompts": "^8.5.2",
|
|
27
|
-
"@stoker-platform/node-client": "0.5.
|
|
28
|
-
"@stoker-platform/types": "0.5.
|
|
29
|
-
"@stoker-platform/utils": "0.5.
|
|
27
|
+
"@stoker-platform/node-client": "0.5.68",
|
|
28
|
+
"@stoker-platform/types": "0.5.47",
|
|
29
|
+
"@stoker-platform/utils": "0.5.59",
|
|
30
30
|
"algoliasearch": "^5.53.0",
|
|
31
31
|
"commander": "^15.0.0",
|
|
32
32
|
"cross-spawn": "^7.0.6",
|