appwrite-utils-cli 1.5.2 → 1.6.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/CHANGELOG.md +199 -0
- package/README.md +251 -29
- package/dist/adapters/AdapterFactory.d.ts +10 -3
- package/dist/adapters/AdapterFactory.js +213 -17
- package/dist/adapters/TablesDBAdapter.js +60 -17
- package/dist/backups/operations/bucketBackup.d.ts +19 -0
- package/dist/backups/operations/bucketBackup.js +197 -0
- package/dist/backups/operations/collectionBackup.d.ts +30 -0
- package/dist/backups/operations/collectionBackup.js +201 -0
- package/dist/backups/operations/comprehensiveBackup.d.ts +25 -0
- package/dist/backups/operations/comprehensiveBackup.js +238 -0
- package/dist/backups/schemas/bucketManifest.d.ts +93 -0
- package/dist/backups/schemas/bucketManifest.js +33 -0
- package/dist/backups/schemas/comprehensiveManifest.d.ts +108 -0
- package/dist/backups/schemas/comprehensiveManifest.js +32 -0
- package/dist/backups/tracking/centralizedTracking.d.ts +34 -0
- package/dist/backups/tracking/centralizedTracking.js +274 -0
- package/dist/cli/commands/configCommands.d.ts +8 -0
- package/dist/cli/commands/configCommands.js +160 -0
- package/dist/cli/commands/databaseCommands.d.ts +13 -0
- package/dist/cli/commands/databaseCommands.js +478 -0
- package/dist/cli/commands/functionCommands.d.ts +7 -0
- package/dist/cli/commands/functionCommands.js +289 -0
- package/dist/cli/commands/schemaCommands.d.ts +7 -0
- package/dist/cli/commands/schemaCommands.js +134 -0
- package/dist/cli/commands/transferCommands.d.ts +5 -0
- package/dist/cli/commands/transferCommands.js +384 -0
- package/dist/collections/attributes.d.ts +5 -4
- package/dist/collections/attributes.js +539 -246
- package/dist/collections/indexes.js +39 -37
- package/dist/collections/methods.d.ts +2 -16
- package/dist/collections/methods.js +90 -538
- package/dist/collections/transferOperations.d.ts +7 -0
- package/dist/collections/transferOperations.js +331 -0
- package/dist/collections/wipeOperations.d.ts +16 -0
- package/dist/collections/wipeOperations.js +328 -0
- package/dist/config/configMigration.d.ts +87 -0
- package/dist/config/configMigration.js +390 -0
- package/dist/config/configValidation.d.ts +66 -0
- package/dist/config/configValidation.js +358 -0
- package/dist/config/yamlConfig.d.ts +455 -1
- package/dist/config/yamlConfig.js +145 -52
- package/dist/databases/methods.js +3 -2
- package/dist/databases/setup.d.ts +1 -2
- package/dist/databases/setup.js +9 -87
- package/dist/examples/yamlTerminologyExample.d.ts +42 -0
- package/dist/examples/yamlTerminologyExample.js +269 -0
- package/dist/functions/deployments.js +11 -10
- package/dist/functions/methods.d.ts +1 -1
- package/dist/functions/methods.js +5 -4
- package/dist/init.js +9 -9
- package/dist/interactiveCLI.d.ts +8 -17
- package/dist/interactiveCLI.js +181 -1172
- package/dist/main.js +364 -21
- package/dist/migrations/afterImportActions.js +22 -30
- package/dist/migrations/appwriteToX.js +71 -25
- package/dist/migrations/dataLoader.js +35 -26
- package/dist/migrations/importController.js +29 -30
- package/dist/migrations/relationships.js +13 -12
- package/dist/migrations/services/ImportOrchestrator.js +16 -19
- package/dist/migrations/transfer.js +46 -46
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +3 -1
- package/dist/migrations/yaml/YamlImportConfigLoader.js +6 -3
- package/dist/migrations/yaml/YamlImportIntegration.d.ts +9 -3
- package/dist/migrations/yaml/YamlImportIntegration.js +22 -11
- package/dist/migrations/yaml/generateImportSchemas.d.ts +14 -1
- package/dist/migrations/yaml/generateImportSchemas.js +736 -7
- package/dist/schemas/authUser.d.ts +1 -1
- package/dist/setupController.js +3 -2
- package/dist/shared/backupMetadataSchema.d.ts +94 -0
- package/dist/shared/backupMetadataSchema.js +38 -0
- package/dist/shared/backupTracking.d.ts +18 -0
- package/dist/shared/backupTracking.js +176 -0
- package/dist/shared/confirmationDialogs.js +15 -15
- package/dist/shared/errorUtils.d.ts +54 -0
- package/dist/shared/errorUtils.js +95 -0
- package/dist/shared/functionManager.js +20 -19
- package/dist/shared/indexManager.js +12 -11
- package/dist/shared/jsonSchemaGenerator.js +10 -26
- package/dist/shared/logging.d.ts +51 -0
- package/dist/shared/logging.js +70 -0
- package/dist/shared/messageFormatter.d.ts +2 -0
- package/dist/shared/messageFormatter.js +10 -0
- package/dist/shared/migrationHelpers.d.ts +6 -16
- package/dist/shared/migrationHelpers.js +24 -21
- package/dist/shared/operationLogger.d.ts +8 -1
- package/dist/shared/operationLogger.js +11 -24
- package/dist/shared/operationQueue.d.ts +28 -1
- package/dist/shared/operationQueue.js +268 -66
- package/dist/shared/operationsTable.d.ts +26 -0
- package/dist/shared/operationsTable.js +286 -0
- package/dist/shared/operationsTableSchema.d.ts +48 -0
- package/dist/shared/operationsTableSchema.js +35 -0
- package/dist/shared/relationshipExtractor.d.ts +56 -0
- package/dist/shared/relationshipExtractor.js +138 -0
- package/dist/shared/schemaGenerator.d.ts +19 -1
- package/dist/shared/schemaGenerator.js +56 -75
- package/dist/storage/backupCompression.d.ts +20 -0
- package/dist/storage/backupCompression.js +67 -0
- package/dist/storage/methods.d.ts +16 -2
- package/dist/storage/methods.js +98 -14
- package/dist/users/methods.js +9 -8
- package/dist/utils/configDiscovery.d.ts +78 -0
- package/dist/utils/configDiscovery.js +430 -0
- package/dist/utils/directoryUtils.d.ts +22 -0
- package/dist/utils/directoryUtils.js +59 -0
- package/dist/utils/getClientFromConfig.d.ts +17 -8
- package/dist/utils/getClientFromConfig.js +162 -17
- package/dist/utils/helperFunctions.d.ts +16 -2
- package/dist/utils/helperFunctions.js +19 -5
- package/dist/utils/loadConfigs.d.ts +34 -9
- package/dist/utils/loadConfigs.js +236 -316
- package/dist/utils/pathResolvers.d.ts +53 -0
- package/dist/utils/pathResolvers.js +72 -0
- package/dist/utils/projectConfig.d.ts +119 -0
- package/dist/utils/projectConfig.js +171 -0
- package/dist/utils/retryFailedPromises.js +4 -2
- package/dist/utils/sessionAuth.d.ts +48 -0
- package/dist/utils/sessionAuth.js +164 -0
- package/dist/utils/sessionPreservationExample.d.ts +1666 -0
- package/dist/utils/sessionPreservationExample.js +101 -0
- package/dist/utils/setupFiles.js +301 -41
- package/dist/utils/typeGuards.d.ts +35 -0
- package/dist/utils/typeGuards.js +57 -0
- package/dist/utils/versionDetection.js +145 -9
- package/dist/utils/yamlConverter.d.ts +53 -3
- package/dist/utils/yamlConverter.js +232 -13
- package/dist/utils/yamlLoader.d.ts +70 -0
- package/dist/utils/yamlLoader.js +263 -0
- package/dist/utilsController.d.ts +36 -3
- package/dist/utilsController.js +186 -56
- package/package.json +12 -2
- package/src/adapters/AdapterFactory.ts +263 -35
- package/src/adapters/TablesDBAdapter.ts +225 -36
- package/src/backups/operations/bucketBackup.ts +277 -0
- package/src/backups/operations/collectionBackup.ts +310 -0
- package/src/backups/operations/comprehensiveBackup.ts +342 -0
- package/src/backups/schemas/bucketManifest.ts +78 -0
- package/src/backups/schemas/comprehensiveManifest.ts +76 -0
- package/src/backups/tracking/centralizedTracking.ts +352 -0
- package/src/cli/commands/configCommands.ts +194 -0
- package/src/cli/commands/databaseCommands.ts +635 -0
- package/src/cli/commands/functionCommands.ts +379 -0
- package/src/cli/commands/schemaCommands.ts +163 -0
- package/src/cli/commands/transferCommands.ts +457 -0
- package/src/collections/attributes.ts +900 -621
- package/src/collections/attributes.ts.backup +1555 -0
- package/src/collections/indexes.ts +116 -114
- package/src/collections/methods.ts +295 -968
- package/src/collections/transferOperations.ts +516 -0
- package/src/collections/wipeOperations.ts +501 -0
- package/src/config/README.md +274 -0
- package/src/config/configMigration.ts +575 -0
- package/src/config/configValidation.ts +445 -0
- package/src/config/yamlConfig.ts +168 -55
- package/src/databases/methods.ts +3 -2
- package/src/databases/setup.ts +11 -138
- package/src/examples/yamlTerminologyExample.ts +341 -0
- package/src/functions/deployments.ts +14 -12
- package/src/functions/methods.ts +11 -11
- package/src/functions/templates/hono-typescript/README.md +286 -0
- package/src/functions/templates/hono-typescript/package.json +26 -0
- package/src/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
- package/src/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
- package/src/functions/templates/hono-typescript/src/app.ts +180 -0
- package/src/functions/templates/hono-typescript/src/context.ts +103 -0
- package/src/functions/templates/hono-typescript/src/index.ts +54 -0
- package/src/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
- package/src/functions/templates/hono-typescript/tsconfig.json +20 -0
- package/src/functions/templates/typescript-node/package.json +2 -1
- package/src/functions/templates/typescript-node/src/context.ts +103 -0
- package/src/functions/templates/typescript-node/src/index.ts +18 -12
- package/src/functions/templates/uv/pyproject.toml +1 -0
- package/src/functions/templates/uv/src/context.py +125 -0
- package/src/functions/templates/uv/src/index.py +35 -5
- package/src/init.ts +9 -11
- package/src/interactiveCLI.ts +278 -1596
- package/src/main.ts +418 -24
- package/src/migrations/afterImportActions.ts +71 -44
- package/src/migrations/appwriteToX.ts +100 -34
- package/src/migrations/dataLoader.ts +48 -34
- package/src/migrations/importController.ts +44 -39
- package/src/migrations/relationships.ts +28 -18
- package/src/migrations/services/ImportOrchestrator.ts +24 -27
- package/src/migrations/transfer.ts +159 -121
- package/src/migrations/yaml/YamlImportConfigLoader.ts +11 -4
- package/src/migrations/yaml/YamlImportIntegration.ts +47 -20
- package/src/migrations/yaml/generateImportSchemas.ts +751 -12
- package/src/setupController.ts +3 -2
- package/src/shared/backupMetadataSchema.ts +93 -0
- package/src/shared/backupTracking.ts +211 -0
- package/src/shared/confirmationDialogs.ts +19 -19
- package/src/shared/errorUtils.ts +110 -0
- package/src/shared/functionManager.ts +21 -20
- package/src/shared/indexManager.ts +12 -11
- package/src/shared/jsonSchemaGenerator.ts +38 -52
- package/src/shared/logging.ts +75 -0
- package/src/shared/messageFormatter.ts +14 -1
- package/src/shared/migrationHelpers.ts +45 -38
- package/src/shared/operationLogger.ts +11 -36
- package/src/shared/operationQueue.ts +322 -93
- package/src/shared/operationsTable.ts +338 -0
- package/src/shared/operationsTableSchema.ts +60 -0
- package/src/shared/relationshipExtractor.ts +214 -0
- package/src/shared/schemaGenerator.ts +179 -219
- package/src/storage/backupCompression.ts +88 -0
- package/src/storage/methods.ts +131 -34
- package/src/users/methods.ts +11 -9
- package/src/utils/configDiscovery.ts +502 -0
- package/src/utils/directoryUtils.ts +61 -0
- package/src/utils/getClientFromConfig.ts +205 -22
- package/src/utils/helperFunctions.ts +23 -5
- package/src/utils/loadConfigs.ts +313 -345
- package/src/utils/pathResolvers.ts +81 -0
- package/src/utils/projectConfig.ts +299 -0
- package/src/utils/retryFailedPromises.ts +4 -2
- package/src/utils/sessionAuth.ts +230 -0
- package/src/utils/setupFiles.ts +322 -54
- package/src/utils/typeGuards.ts +65 -0
- package/src/utils/versionDetection.ts +218 -64
- package/src/utils/yamlConverter.ts +296 -13
- package/src/utils/yamlLoader.ts +364 -0
- package/src/utilsController.ts +314 -110
- package/tests/README.md +497 -0
- package/tests/adapters/AdapterFactory.test.ts +277 -0
- package/tests/integration/syncOperations.test.ts +463 -0
- package/tests/jest.config.js +25 -0
- package/tests/migration/configMigration.test.ts +546 -0
- package/tests/setup.ts +62 -0
- package/tests/testUtils.ts +340 -0
- package/tests/utils/loadConfigs.test.ts +350 -0
- package/tests/validation/configValidation.test.ts +412 -0
- package/src/utils/schemaStrings.ts +0 -517
package/dist/main.js
CHANGED
@@ -14,6 +14,47 @@ import { listSpecifications } from "./functions/methods.js";
|
|
14
14
|
import { MessageFormatter } from "./shared/messageFormatter.js";
|
15
15
|
import { ConfirmationDialogs } from "./shared/confirmationDialogs.js";
|
16
16
|
import path from "path";
|
17
|
+
import fs from "fs";
|
18
|
+
import { loadAppwriteProjectConfig, findAppwriteProjectConfig, projectConfigToAppwriteConfig } from "./utils/projectConfig.js";
|
19
|
+
import { hasSessionAuth, getAvailableSessions, getAuthenticationStatus } from "./utils/sessionAuth.js";
|
20
|
+
import { findYamlConfig, loadYamlConfigWithSession } from "./config/yamlConfig.js";
|
21
|
+
/**
|
22
|
+
* Checks if the migration from collections to tables should be allowed
|
23
|
+
* Returns an object with:
|
24
|
+
* - allowed: boolean indicating if migration should proceed
|
25
|
+
* - reason: string explaining why migration was blocked (if not allowed)
|
26
|
+
*/
|
27
|
+
function checkMigrationConditions(configPath) {
|
28
|
+
const collectionsPath = path.join(configPath, "collections");
|
29
|
+
const tablesPath = path.join(configPath, "tables");
|
30
|
+
// Check if collections/ folder exists
|
31
|
+
if (!fs.existsSync(collectionsPath)) {
|
32
|
+
return {
|
33
|
+
allowed: false,
|
34
|
+
reason: "No collections/ folder found. Migration requires existing collections to migrate."
|
35
|
+
};
|
36
|
+
}
|
37
|
+
// Check if collections/ folder has YAML files
|
38
|
+
const collectionFiles = fs.readdirSync(collectionsPath).filter(file => file.endsWith(".yaml") || file.endsWith(".yml"));
|
39
|
+
if (collectionFiles.length === 0) {
|
40
|
+
return {
|
41
|
+
allowed: false,
|
42
|
+
reason: "No YAML files found in collections/ folder. Migration requires existing collection YAML files."
|
43
|
+
};
|
44
|
+
}
|
45
|
+
// Check if tables/ folder exists and has YAML files
|
46
|
+
if (fs.existsSync(tablesPath)) {
|
47
|
+
const tableFiles = fs.readdirSync(tablesPath).filter(file => file.endsWith(".yaml") || file.endsWith(".yml"));
|
48
|
+
if (tableFiles.length > 0) {
|
49
|
+
return {
|
50
|
+
allowed: false,
|
51
|
+
reason: `Tables folder already exists with ${tableFiles.length} YAML file(s). Migration appears to have already been completed.`
|
52
|
+
};
|
53
|
+
}
|
54
|
+
}
|
55
|
+
// All conditions met
|
56
|
+
return { allowed: true };
|
57
|
+
}
|
17
58
|
const argv = yargs(hideBin(process.argv))
|
18
59
|
.option("config", {
|
19
60
|
type: "string",
|
@@ -29,9 +70,9 @@ const argv = yargs(hideBin(process.argv))
|
|
29
70
|
description: "Comma-separated list of database IDs to target (e.g., 'db1,db2,db3')",
|
30
71
|
})
|
31
72
|
.option("collectionIds", {
|
32
|
-
alias: ["collIds"],
|
73
|
+
alias: ["collIds", "tableIds", "tables"],
|
33
74
|
type: "string",
|
34
|
-
description: "Comma-separated list of collection IDs to target (e.g., 'users,posts')",
|
75
|
+
description: "Comma-separated list of collection/table IDs to target (e.g., 'users,posts')",
|
35
76
|
})
|
36
77
|
.option("bucketIds", {
|
37
78
|
type: "string",
|
@@ -43,7 +84,7 @@ const argv = yargs(hideBin(process.argv))
|
|
43
84
|
})
|
44
85
|
.option("wipeCollections", {
|
45
86
|
type: "boolean",
|
46
|
-
description: "⚠️ DESTRUCTIVE: Wipe specific collections (requires --collectionIds)",
|
87
|
+
description: "⚠️ DESTRUCTIVE: Wipe specific collections/tables (requires --collectionIds or --tableIds)",
|
47
88
|
})
|
48
89
|
.option("transferUsers", {
|
49
90
|
type: "boolean",
|
@@ -60,6 +101,31 @@ const argv = yargs(hideBin(process.argv))
|
|
60
101
|
.option("backup", {
|
61
102
|
type: "boolean",
|
62
103
|
description: "Create a complete backup of your databases and collections",
|
104
|
+
})
|
105
|
+
.option("backupFormat", {
|
106
|
+
type: "string",
|
107
|
+
choices: ["json", "zip"],
|
108
|
+
default: "json",
|
109
|
+
description: "Backup file format (json or zip)",
|
110
|
+
})
|
111
|
+
.option("listBackups", {
|
112
|
+
type: "boolean",
|
113
|
+
description: "List all backups for databases",
|
114
|
+
})
|
115
|
+
.option("comprehensiveBackup", {
|
116
|
+
alias: ["comprehensive", "backup-all"],
|
117
|
+
type: "boolean",
|
118
|
+
description: "🚀 Create comprehensive backup of ALL databases and ALL storage buckets",
|
119
|
+
})
|
120
|
+
.option("trackingDatabaseId", {
|
121
|
+
alias: ["tracking-db"],
|
122
|
+
type: "string",
|
123
|
+
description: "Database ID to use for centralized backup tracking (interactive prompt if not specified)",
|
124
|
+
})
|
125
|
+
.option("parallelDownloads", {
|
126
|
+
type: "number",
|
127
|
+
default: 10,
|
128
|
+
description: "Number of parallel file downloads for bucket backups (default: 10)",
|
63
129
|
})
|
64
130
|
.option("writeData", {
|
65
131
|
type: "boolean",
|
@@ -174,25 +240,130 @@ const argv = yargs(hideBin(process.argv))
|
|
174
240
|
type: "string",
|
175
241
|
description: "Output directory for generated constants files (default: config-folder/constants)",
|
176
242
|
default: "auto",
|
243
|
+
})
|
244
|
+
.option("migrateCollectionsToTables", {
|
245
|
+
alias: ["migrate-collections"],
|
246
|
+
type: "boolean",
|
247
|
+
description: "Migrate collections to tables format for TablesDB API compatibility",
|
248
|
+
})
|
249
|
+
.option("useSession", {
|
250
|
+
alias: ["session"],
|
251
|
+
type: "boolean",
|
252
|
+
description: "Use Appwrite CLI session authentication instead of API key",
|
253
|
+
})
|
254
|
+
.option("sessionCookie", {
|
255
|
+
type: "string",
|
256
|
+
description: "Explicit session cookie to use for authentication",
|
177
257
|
})
|
178
258
|
.parse();
|
179
259
|
async function main() {
|
180
260
|
const startTime = Date.now();
|
181
261
|
const operationStats = {};
|
262
|
+
// Early session detection for better user guidance
|
263
|
+
const availableSessions = getAvailableSessions();
|
264
|
+
let hasAnyValidSessions = availableSessions.length > 0;
|
182
265
|
if (argv.it) {
|
183
266
|
const cli = new InteractiveCLI(process.cwd());
|
184
267
|
await cli.run();
|
185
268
|
}
|
186
269
|
else {
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
270
|
+
// Enhanced config creation with session and project file support
|
271
|
+
let directConfig = undefined;
|
272
|
+
// Show authentication status on startup if no config provided
|
273
|
+
if (!argv.config && !argv.endpoint && !argv.projectId && !argv.apiKey && !argv.useSession && !argv.sessionCookie) {
|
274
|
+
if (hasAnyValidSessions) {
|
275
|
+
MessageFormatter.info(`Found ${availableSessions.length} available session(s)`, { prefix: "Auth" });
|
276
|
+
availableSessions.forEach(session => {
|
277
|
+
MessageFormatter.info(` \u2022 ${session.projectId} (${session.email || 'unknown'}) at ${session.endpoint}`, { prefix: "Auth" });
|
278
|
+
});
|
279
|
+
MessageFormatter.info("Use --session to enable session authentication", { prefix: "Auth" });
|
280
|
+
}
|
281
|
+
else {
|
282
|
+
MessageFormatter.info("No active Appwrite sessions found", { prefix: "Auth" });
|
283
|
+
MessageFormatter.info("\u2022 Run 'appwrite login' to authenticate with session", { prefix: "Auth" });
|
284
|
+
MessageFormatter.info("\u2022 Or provide --apiKey for API key authentication", { prefix: "Auth" });
|
285
|
+
}
|
286
|
+
}
|
287
|
+
// Priority 1: Check for appwrite.json project configuration
|
288
|
+
const projectConfigPath = findAppwriteProjectConfig(process.cwd());
|
289
|
+
if (projectConfigPath) {
|
290
|
+
const projectConfig = loadAppwriteProjectConfig(projectConfigPath);
|
291
|
+
if (projectConfig) {
|
292
|
+
directConfig = projectConfigToAppwriteConfig(projectConfig);
|
293
|
+
MessageFormatter.info(`Loaded project configuration from ${projectConfigPath}`, { prefix: "CLI" });
|
294
|
+
}
|
295
|
+
}
|
296
|
+
// Priority 2: CLI arguments override project config
|
297
|
+
if (argv.endpoint || argv.projectId || argv.apiKey || argv.useSession || argv.sessionCookie) {
|
298
|
+
directConfig = {
|
299
|
+
...directConfig,
|
300
|
+
appwriteEndpoint: argv.endpoint || directConfig?.appwriteEndpoint,
|
301
|
+
appwriteProject: argv.projectId || directConfig?.appwriteProject,
|
302
|
+
appwriteKey: argv.apiKey || directConfig?.appwriteKey,
|
303
|
+
};
|
304
|
+
}
|
305
|
+
// Priority 3: Session authentication support with improved detection
|
306
|
+
let sessionAuthAvailable = false;
|
307
|
+
if (directConfig?.appwriteEndpoint && directConfig?.appwriteProject) {
|
308
|
+
sessionAuthAvailable = hasSessionAuth(directConfig.appwriteEndpoint, directConfig.appwriteProject);
|
309
|
+
}
|
310
|
+
if (argv.useSession || argv.sessionCookie) {
|
311
|
+
if (argv.sessionCookie) {
|
312
|
+
// Explicit session cookie provided
|
313
|
+
MessageFormatter.info("Using explicit session cookie for authentication", { prefix: "Auth" });
|
314
|
+
}
|
315
|
+
else if (sessionAuthAvailable) {
|
316
|
+
MessageFormatter.info("Session authentication detected and will be used", { prefix: "Auth" });
|
317
|
+
}
|
318
|
+
else {
|
319
|
+
MessageFormatter.warning("Session authentication requested but no valid session found", { prefix: "Auth" });
|
320
|
+
const availableSessions = getAvailableSessions();
|
321
|
+
if (availableSessions.length > 0) {
|
322
|
+
MessageFormatter.info(`Available sessions: ${availableSessions.map(s => `${s.projectId} (${s.email || 'unknown'})`).join(", ")}`, { prefix: "Auth" });
|
323
|
+
MessageFormatter.info("Use --session flag to enable session authentication", { prefix: "Auth" });
|
324
|
+
}
|
325
|
+
else {
|
326
|
+
MessageFormatter.warning("No Appwrite CLI sessions found. Please run 'appwrite login' first.", { prefix: "Auth" });
|
327
|
+
}
|
328
|
+
MessageFormatter.error("Session authentication requested but not available", undefined, { prefix: "Auth" });
|
329
|
+
return; // Exit early if session auth was requested but not available
|
330
|
+
}
|
331
|
+
}
|
332
|
+
else if (sessionAuthAvailable && !argv.apiKey) {
|
333
|
+
// Auto-detect session authentication when no API key is provided
|
334
|
+
MessageFormatter.info("Session authentication detected - no API key required", { prefix: "Auth" });
|
335
|
+
MessageFormatter.info("Use --session flag to explicitly enable session authentication", { prefix: "Auth" });
|
336
|
+
}
|
337
|
+
// Enhanced session authentication support:
|
338
|
+
// 1. If session auth is explicitly requested via flags, use it
|
339
|
+
// 2. If no API key is provided but sessions are available, offer to use session auth
|
340
|
+
// 3. Auto-detect session authentication when possible
|
341
|
+
let finalDirectConfig = directConfig;
|
342
|
+
if ((argv.useSession || argv.sessionCookie) &&
|
343
|
+
(!directConfig || !directConfig.appwriteEndpoint || !directConfig.appwriteProject)) {
|
344
|
+
// Don't pass incomplete directConfig - let UtilsController load YAML config normally
|
345
|
+
finalDirectConfig = null;
|
346
|
+
}
|
347
|
+
else if (finalDirectConfig && !finalDirectConfig.appwriteKey && !argv.useSession && !argv.sessionCookie) {
|
348
|
+
// Auto-detect session authentication when no API key provided
|
349
|
+
if (sessionAuthAvailable) {
|
350
|
+
MessageFormatter.info("No API key provided, but session authentication is available", { prefix: "Auth" });
|
351
|
+
MessageFormatter.info("Automatically using session authentication (add --session to suppress this message)", { prefix: "Auth" });
|
352
|
+
// Implicitly enable session authentication
|
353
|
+
argv.useSession = true;
|
354
|
+
}
|
355
|
+
}
|
356
|
+
// Create controller with session authentication support
|
357
|
+
const controller = new UtilsController(process.cwd(), finalDirectConfig);
|
358
|
+
// Pass session authentication options to the controller
|
359
|
+
const initOptions = {};
|
360
|
+
if (argv.useSession || argv.sessionCookie) {
|
361
|
+
initOptions.useSession = true;
|
362
|
+
if (argv.sessionCookie) {
|
363
|
+
initOptions.sessionCookie = argv.sessionCookie;
|
364
|
+
}
|
365
|
+
}
|
366
|
+
await controller.init(initOptions);
|
196
367
|
if (argv.setup) {
|
197
368
|
await setupDirsFiles(false, process.cwd());
|
198
369
|
return;
|
@@ -229,11 +400,114 @@ async function main() {
|
|
229
400
|
MessageFormatter.success(`Constants generated in ${outputDir}`, { prefix: "Constants" });
|
230
401
|
return;
|
231
402
|
}
|
403
|
+
if (argv.migrateCollectionsToTables) {
|
404
|
+
try {
|
405
|
+
if (!controller.config) {
|
406
|
+
MessageFormatter.error("No Appwrite configuration found", undefined, { prefix: "Migration" });
|
407
|
+
return;
|
408
|
+
}
|
409
|
+
// Get the config path from the controller or use .appwrite in current directory
|
410
|
+
let configPath = controller.getAppwriteFolderPath();
|
411
|
+
if (!configPath) {
|
412
|
+
// Try .appwrite in current directory
|
413
|
+
const defaultPath = path.join(process.cwd(), ".appwrite");
|
414
|
+
if (fs.existsSync(defaultPath)) {
|
415
|
+
configPath = defaultPath;
|
416
|
+
}
|
417
|
+
else {
|
418
|
+
MessageFormatter.error("Could not determine configuration folder path", undefined, { prefix: "Migration" });
|
419
|
+
MessageFormatter.info("Make sure you have a .appwrite/ folder in your current directory", { prefix: "Migration" });
|
420
|
+
return;
|
421
|
+
}
|
422
|
+
}
|
423
|
+
// Check if migration conditions are met
|
424
|
+
const migrationCheck = checkMigrationConditions(configPath);
|
425
|
+
if (!migrationCheck.allowed) {
|
426
|
+
MessageFormatter.error(`Migration not allowed: ${migrationCheck.reason}`, undefined, { prefix: "Migration" });
|
427
|
+
MessageFormatter.info("Migration requirements:", { prefix: "Migration" });
|
428
|
+
MessageFormatter.info(" • Configuration must be loaded (use --config or have .appwrite/ folder)", { prefix: "Migration" });
|
429
|
+
MessageFormatter.info(" • collections/ folder must exist with YAML files", { prefix: "Migration" });
|
430
|
+
MessageFormatter.info(" • tables/ folder must not exist or be empty", { prefix: "Migration" });
|
431
|
+
return;
|
432
|
+
}
|
433
|
+
const { migrateCollectionsToTables } = await import("./config/configMigration.js");
|
434
|
+
MessageFormatter.info("Starting collections to tables migration...", { prefix: "Migration" });
|
435
|
+
const result = migrateCollectionsToTables(controller.config, {
|
436
|
+
strategy: "full_migration",
|
437
|
+
validateResult: true,
|
438
|
+
dryRun: false
|
439
|
+
});
|
440
|
+
if (result.success) {
|
441
|
+
operationStats.migratedCollections = result.changes.length;
|
442
|
+
MessageFormatter.success("Collections migration completed successfully", { prefix: "Migration" });
|
443
|
+
}
|
444
|
+
else {
|
445
|
+
MessageFormatter.error(`Migration failed: ${result.errors.join(", ")}`, undefined, { prefix: "Migration" });
|
446
|
+
process.exit(1);
|
447
|
+
}
|
448
|
+
}
|
449
|
+
catch (error) {
|
450
|
+
MessageFormatter.error("Migration failed", error instanceof Error ? error : new Error(String(error)), { prefix: "Migration" });
|
451
|
+
process.exit(1);
|
452
|
+
}
|
453
|
+
return;
|
454
|
+
}
|
232
455
|
if (!controller.config) {
|
233
|
-
|
456
|
+
// Provide better guidance based on available authentication methods
|
457
|
+
const availableSessions = getAvailableSessions();
|
458
|
+
if (availableSessions.length > 0) {
|
459
|
+
MessageFormatter.error("No Appwrite configuration found", undefined, { prefix: "CLI" });
|
460
|
+
MessageFormatter.info("Available authentication options:", { prefix: "Auth" });
|
461
|
+
MessageFormatter.info("• Session authentication: Add --session flag", { prefix: "Auth" });
|
462
|
+
MessageFormatter.info("• API key authentication: Add --apiKey YOUR_API_KEY", { prefix: "Auth" });
|
463
|
+
MessageFormatter.info(`• Available sessions: ${availableSessions.map(s => `${s.projectId} (${s.email || 'unknown'})`).join(", ")}`, { prefix: "Auth" });
|
464
|
+
}
|
465
|
+
else {
|
466
|
+
MessageFormatter.error("No Appwrite configuration found", undefined, { prefix: "CLI" });
|
467
|
+
MessageFormatter.info("Authentication options:", { prefix: "Auth" });
|
468
|
+
MessageFormatter.info("• Login with Appwrite CLI: Run 'appwrite login' then use --session flag", { prefix: "Auth" });
|
469
|
+
MessageFormatter.info("• Use API key: Add --apiKey YOUR_API_KEY", { prefix: "Auth" });
|
470
|
+
MessageFormatter.info("• Create config file: Run with --setup to initialize project configuration", { prefix: "Auth" });
|
471
|
+
}
|
234
472
|
return;
|
235
473
|
}
|
236
474
|
const parsedArgv = argv;
|
475
|
+
// List backups if requested
|
476
|
+
if (parsedArgv.listBackups) {
|
477
|
+
const { AdapterFactory } = await import("./adapters/AdapterFactory.js");
|
478
|
+
const { listBackups } = await import("./shared/backupTracking.js");
|
479
|
+
if (!controller.config) {
|
480
|
+
MessageFormatter.error("No Appwrite configuration found", undefined, { prefix: "Backups" });
|
481
|
+
return;
|
482
|
+
}
|
483
|
+
const { adapter } = await AdapterFactory.create({
|
484
|
+
appwriteEndpoint: controller.config.appwriteEndpoint,
|
485
|
+
appwriteProject: controller.config.appwriteProject,
|
486
|
+
appwriteKey: controller.config.appwriteKey
|
487
|
+
});
|
488
|
+
const databases = parsedArgv.dbIds
|
489
|
+
? await controller.getDatabasesByIds(parsedArgv.dbIds.split(","))
|
490
|
+
: await fetchAllDatabases(controller.database);
|
491
|
+
if (!databases || databases.length === 0) {
|
492
|
+
MessageFormatter.info("No databases found", { prefix: "Backups" });
|
493
|
+
return;
|
494
|
+
}
|
495
|
+
for (const db of databases) {
|
496
|
+
const backups = await listBackups(adapter, db.$id);
|
497
|
+
MessageFormatter.info(`\nBackups for database: ${db.name} (${db.$id})`, { prefix: "Backups" });
|
498
|
+
if (backups.length === 0) {
|
499
|
+
MessageFormatter.info(" No backups found", { prefix: "Backups" });
|
500
|
+
}
|
501
|
+
else {
|
502
|
+
backups.forEach((backup, index) => {
|
503
|
+
const date = new Date(backup.$createdAt).toLocaleString();
|
504
|
+
const size = MessageFormatter.formatBytes(backup.sizeBytes);
|
505
|
+
MessageFormatter.info(` ${index + 1}. ${date} - ${backup.format.toUpperCase()} - ${size} - ${backup.collections} collections, ${backup.documents} documents`, { prefix: "Backups" });
|
506
|
+
});
|
507
|
+
}
|
508
|
+
}
|
509
|
+
return;
|
510
|
+
}
|
237
511
|
const options = {
|
238
512
|
databases: parsedArgv.dbIds
|
239
513
|
? await controller.getDatabasesByIds(parsedArgv.dbIds.split(","))
|
@@ -261,13 +535,14 @@ async function main() {
|
|
261
535
|
}
|
262
536
|
await controller.updateFunctionSpecifications(parsedArgv.functionId, parsedArgv.specification);
|
263
537
|
}
|
264
|
-
// Add default databases if not specified
|
265
|
-
|
538
|
+
// Add default databases if not specified (only if we need them for operations)
|
539
|
+
const needsDatabases = options.doBackup || options.wipeDatabase ||
|
540
|
+
options.wipeDocumentStorage || options.wipeUsers ||
|
541
|
+
options.wipeCollections || options.importData ||
|
542
|
+
parsedArgv.sync || parsedArgv.transfer;
|
543
|
+
if (needsDatabases && (!options.databases || options.databases.length === 0)) {
|
266
544
|
const allDatabases = await fetchAllDatabases(controller.database);
|
267
|
-
options.databases = allDatabases
|
268
|
-
const useMigrations = controller.config?.useMigrations ?? true;
|
269
|
-
return useMigrations || db.name.toLowerCase() !== "migrations";
|
270
|
-
});
|
545
|
+
options.databases = allDatabases;
|
271
546
|
}
|
272
547
|
// Add default collections if not specified
|
273
548
|
if (!options.collections || options.collections.length === 0) {
|
@@ -278,10 +553,78 @@ async function main() {
|
|
278
553
|
options.collections = [];
|
279
554
|
}
|
280
555
|
}
|
556
|
+
// Comprehensive backup (all databases + all buckets)
|
557
|
+
if (parsedArgv.comprehensiveBackup) {
|
558
|
+
const { comprehensiveBackup } = await import("./backups/operations/comprehensiveBackup.js");
|
559
|
+
const { AdapterFactory } = await import("./adapters/AdapterFactory.js");
|
560
|
+
// Get tracking database ID (interactive prompt if not specified)
|
561
|
+
let trackingDatabaseId = parsedArgv.trackingDatabaseId;
|
562
|
+
if (!trackingDatabaseId) {
|
563
|
+
// Fetch all databases for selection
|
564
|
+
const allDatabases = await fetchAllDatabases(controller.database);
|
565
|
+
if (allDatabases.length === 0) {
|
566
|
+
MessageFormatter.error("No databases found. Cannot create comprehensive backup without a tracking database.", undefined, { prefix: "Backup" });
|
567
|
+
return;
|
568
|
+
}
|
569
|
+
if (allDatabases.length === 1) {
|
570
|
+
trackingDatabaseId = allDatabases[0].$id;
|
571
|
+
MessageFormatter.info(`Using only available database for tracking: ${allDatabases[0].name} (${trackingDatabaseId})`, { prefix: "Backup" });
|
572
|
+
}
|
573
|
+
else {
|
574
|
+
// Interactive selection
|
575
|
+
const inquirer = (await import("inquirer")).default;
|
576
|
+
const answer = await inquirer.prompt([{
|
577
|
+
type: 'list',
|
578
|
+
name: 'trackingDb',
|
579
|
+
message: 'Select database to store backup tracking metadata:',
|
580
|
+
choices: allDatabases.map(db => ({
|
581
|
+
name: `${db.name} (${db.$id})`,
|
582
|
+
value: db.$id
|
583
|
+
}))
|
584
|
+
}]);
|
585
|
+
trackingDatabaseId = answer.trackingDb;
|
586
|
+
}
|
587
|
+
}
|
588
|
+
// Ensure trackingDatabaseId is defined before proceeding
|
589
|
+
if (!trackingDatabaseId) {
|
590
|
+
throw new Error('Tracking database ID is required for comprehensive backup');
|
591
|
+
}
|
592
|
+
MessageFormatter.info(`Using tracking database: ${trackingDatabaseId}`, { prefix: "Backup" });
|
593
|
+
// Create adapter for backup tracking
|
594
|
+
const { adapter } = await AdapterFactory.create({
|
595
|
+
appwriteEndpoint: controller.config.appwriteEndpoint,
|
596
|
+
appwriteProject: controller.config.appwriteProject,
|
597
|
+
appwriteKey: controller.config.appwriteKey,
|
598
|
+
sessionCookie: controller.config.sessionCookie
|
599
|
+
});
|
600
|
+
const result = await comprehensiveBackup(controller.config, controller.database, controller.storage, adapter, {
|
601
|
+
trackingDatabaseId,
|
602
|
+
backupFormat: parsedArgv.backupFormat || 'zip',
|
603
|
+
parallelDownloads: parsedArgv.parallelDownloads || 10,
|
604
|
+
onProgress: (message) => {
|
605
|
+
MessageFormatter.info(message, { prefix: "Backup" });
|
606
|
+
}
|
607
|
+
});
|
608
|
+
operationStats.comprehensiveBackup = 1;
|
609
|
+
operationStats.databasesBackedUp = result.databaseBackups.length;
|
610
|
+
operationStats.bucketsBackedUp = result.bucketBackups.length;
|
611
|
+
operationStats.totalBackupSize = result.totalSizeBytes;
|
612
|
+
if (result.status === 'completed') {
|
613
|
+
MessageFormatter.success(`Comprehensive backup completed successfully (ID: ${result.backupId})`, { prefix: "Backup" });
|
614
|
+
}
|
615
|
+
else if (result.status === 'partial') {
|
616
|
+
MessageFormatter.warning(`Comprehensive backup completed with errors (ID: ${result.backupId})`, { prefix: "Backup" });
|
617
|
+
result.errors.forEach(err => MessageFormatter.warning(err, { prefix: "Backup" }));
|
618
|
+
}
|
619
|
+
else {
|
620
|
+
MessageFormatter.error(`Comprehensive backup failed (ID: ${result.backupId})`, undefined, { prefix: "Backup" });
|
621
|
+
result.errors.forEach(err => MessageFormatter.error(err, undefined, { prefix: "Backup" }));
|
622
|
+
}
|
623
|
+
}
|
281
624
|
if (options.doBackup && options.databases) {
|
282
|
-
MessageFormatter.info(`Creating backups for ${options.databases.length} database(s)`, { prefix: "Backup" });
|
625
|
+
MessageFormatter.info(`Creating backups for ${options.databases.length} database(s) in ${parsedArgv.backupFormat} format`, { prefix: "Backup" });
|
283
626
|
for (const db of options.databases) {
|
284
|
-
await controller.backupDatabase(db);
|
627
|
+
await controller.backupDatabase(db, parsedArgv.backupFormat || 'json');
|
285
628
|
}
|
286
629
|
operationStats.backups = options.databases.length;
|
287
630
|
MessageFormatter.success(`Backup completed for ${options.databases.length} database(s)`, { prefix: "Backup" });
|
@@ -1,26 +1,18 @@
|
|
1
|
-
import { Databases, Storage, Query, ID,
|
1
|
+
import { Databases, Storage, Query, ID, Compression, } from "node-appwrite";
|
2
2
|
import { InputFile } from "node-appwrite/file";
|
3
3
|
import path from "path";
|
4
4
|
import fs from "fs";
|
5
5
|
import os from "os";
|
6
6
|
import { logger } from "../shared/logging.js";
|
7
7
|
import { tryAwaitWithRetry, } from "appwrite-utils";
|
8
|
+
import { getClientFromConfig } from "../utils/getClientFromConfig.js";
|
9
|
+
import { MessageFormatter } from "../shared/messageFormatter.js";
|
8
10
|
export const getDatabaseFromConfig = (config) => {
|
9
|
-
|
10
|
-
config.appwriteClient = new Client()
|
11
|
-
.setEndpoint(config.appwriteEndpoint)
|
12
|
-
.setProject(config.appwriteProject)
|
13
|
-
.setKey(config.appwriteKey);
|
14
|
-
}
|
11
|
+
getClientFromConfig(config); // Sets config.appwriteClient if missing
|
15
12
|
return new Databases(config.appwriteClient);
|
16
13
|
};
|
17
14
|
export const getStorageFromConfig = (config) => {
|
18
|
-
|
19
|
-
config.appwriteClient = new Client()
|
20
|
-
.setEndpoint(config.appwriteEndpoint)
|
21
|
-
.setProject(config.appwriteProject)
|
22
|
-
.setKey(config.appwriteKey);
|
23
|
-
}
|
15
|
+
getClientFromConfig(config); // Sets config.appwriteClient if missing
|
24
16
|
return new Storage(config.appwriteClient);
|
25
17
|
};
|
26
18
|
export const afterImportActions = {
|
@@ -30,7 +22,7 @@ export const afterImportActions = {
|
|
30
22
|
await tryAwaitWithRetry(async () => await db.updateDocument(dbId, collId, docId, data));
|
31
23
|
}
|
32
24
|
catch (error) {
|
33
|
-
|
25
|
+
MessageFormatter.error("Error updating document", error instanceof Error ? error : new Error(String(error)), { prefix: "Import" });
|
34
26
|
}
|
35
27
|
},
|
36
28
|
checkAndUpdateFieldInDocument: async (config, dbId, collId, docId, fieldName, oldFieldValue, newFieldValue) => {
|
@@ -44,7 +36,7 @@ export const afterImportActions = {
|
|
44
36
|
}
|
45
37
|
}
|
46
38
|
catch (error) {
|
47
|
-
|
39
|
+
MessageFormatter.error("Error updating document", error instanceof Error ? error : new Error(String(error)), { prefix: "Import" });
|
48
40
|
}
|
49
41
|
},
|
50
42
|
setFieldFromOtherCollectionDocument: async (config, dbId, collIdOrName, docId, fieldName, otherCollIdOrName, otherDocId, otherFieldName) => {
|
@@ -76,10 +68,10 @@ export const afterImportActions = {
|
|
76
68
|
[fieldName]: valueToSet,
|
77
69
|
}));
|
78
70
|
}
|
79
|
-
|
71
|
+
MessageFormatter.success(`Field ${fieldName} updated successfully in document ${docId}`, { prefix: "Import" });
|
80
72
|
}
|
81
73
|
catch (error) {
|
82
|
-
|
74
|
+
MessageFormatter.error("Error setting field from other collection document", error instanceof Error ? error : new Error(String(error)), { prefix: "Import" });
|
83
75
|
}
|
84
76
|
},
|
85
77
|
/**
|
@@ -136,11 +128,11 @@ export const afterImportActions = {
|
|
136
128
|
? { [fieldName]: documentIds }
|
137
129
|
: { [fieldName]: documentIds[0] };
|
138
130
|
await tryAwaitWithRetry(async () => await db.updateDocument(dbId, targetCollectionId, docId, updatePayload));
|
139
|
-
|
131
|
+
MessageFormatter.success(`Field ${fieldName} updated successfully in document ${docId} with ${documentIds.length} document IDs`, { prefix: "Import" });
|
140
132
|
}
|
141
133
|
}
|
142
134
|
catch (error) {
|
143
|
-
|
135
|
+
MessageFormatter.error("Error setting field from other collection documents", error instanceof Error ? error : new Error(String(error)), { prefix: "Import" });
|
144
136
|
}
|
145
137
|
},
|
146
138
|
setTargetFieldFromOtherCollectionDocumentsByMatchingField: async (config, dbId, collIdOrName, docId, fieldName, otherCollIdOrName, matchingFieldName, matchingFieldValue, targetField) => {
|
@@ -190,11 +182,11 @@ export const afterImportActions = {
|
|
190
182
|
? { [fieldName]: targetFieldValues }
|
191
183
|
: { [fieldName]: targetFieldValues[0] };
|
192
184
|
await tryAwaitWithRetry(async () => await db.updateDocument(dbId, targetCollectionId, docId, updatePayload));
|
193
|
-
|
185
|
+
MessageFormatter.success(`Field ${fieldName} updated successfully in document ${docId} with values from field ${targetField}`, { prefix: "Import" });
|
194
186
|
}
|
195
187
|
}
|
196
188
|
catch (error) {
|
197
|
-
|
189
|
+
MessageFormatter.error("Error setting field from other collection documents", error instanceof Error ? error : new Error(String(error)), { prefix: "Import" });
|
198
190
|
}
|
199
191
|
},
|
200
192
|
createOrGetBucket: async (config, bucketName, bucketId, permissions, fileSecurity, enabled, maxFileSize, allowedExtensions, compression, encryption, antivirus) => {
|
@@ -217,7 +209,7 @@ export const afterImportActions = {
|
|
217
209
|
}
|
218
210
|
}
|
219
211
|
catch (error) {
|
220
|
-
|
212
|
+
MessageFormatter.error("Error creating or getting bucket", error instanceof Error ? error : new Error(String(error)), { prefix: "Import" });
|
221
213
|
}
|
222
214
|
},
|
223
215
|
createFileAndUpdateField: async (config, dbId, collId, docId, fieldName, bucketId, filePath, fileName) => {
|
@@ -231,12 +223,12 @@ export const afterImportActions = {
|
|
231
223
|
// `Processing field ${fieldName} in collection ${collId} for document ${docId} in database ${dbId} in bucket ${bucketId} with path ${filePath} and name ${fileName}...`
|
232
224
|
// );
|
233
225
|
if (filePath.length === 0 || fileName.length === 0) {
|
234
|
-
|
226
|
+
MessageFormatter.error(`File path or name is empty for field ${fieldName} in collection ${collId}, skipping...`, undefined, { prefix: "Import" });
|
235
227
|
return;
|
236
228
|
}
|
237
229
|
let isArray = false;
|
238
230
|
if (!attribute) {
|
239
|
-
|
231
|
+
MessageFormatter.warning(`Field ${fieldName} not found in collection ${collId}, weird, skipping...`, { prefix: "Import" });
|
240
232
|
return;
|
241
233
|
}
|
242
234
|
else if (attribute.array === true) {
|
@@ -260,7 +252,7 @@ export const afterImportActions = {
|
|
260
252
|
// Download the file using fetch
|
261
253
|
const response = await tryAwaitWithRetry(async () => await fetch(filePath));
|
262
254
|
if (!response.ok)
|
263
|
-
|
255
|
+
MessageFormatter.error(`Failed to fetch ${filePath}: ${response.statusText} for document ${docId} with field ${fieldName}`, undefined, { prefix: "Import" });
|
264
256
|
// Use arrayBuffer if buffer is not available
|
265
257
|
const arrayBuffer = await response.arrayBuffer();
|
266
258
|
const buffer = Buffer.from(arrayBuffer);
|
@@ -269,7 +261,7 @@ export const afterImportActions = {
|
|
269
261
|
const inputFile = InputFile.fromPath(tempFilePath, fileName);
|
270
262
|
// Use the full file name (with extension) for creating the file
|
271
263
|
const file = await tryAwaitWithRetry(async () => await storage.createFile(bucketId, ID.unique(), inputFile));
|
272
|
-
|
264
|
+
MessageFormatter.success(`Created file from URL: ${file.$id}`, { prefix: "Import" });
|
273
265
|
// After uploading, adjust the updateData based on whether the field is an array or not
|
274
266
|
if (isArray) {
|
275
267
|
updateData = [...updateData, file.$id]; // Append the new file ID
|
@@ -287,7 +279,7 @@ export const afterImportActions = {
|
|
287
279
|
const files = fs.readdirSync(filePath);
|
288
280
|
const fileFullName = files.find((file) => file.includes(fileName));
|
289
281
|
if (!fileFullName) {
|
290
|
-
|
282
|
+
MessageFormatter.error(`File starting with '${fileName}' not found in '${filePath}'`, undefined, { prefix: "Import" });
|
291
283
|
return;
|
292
284
|
}
|
293
285
|
const pathToFile = path.join(filePath, fileFullName);
|
@@ -302,13 +294,13 @@ export const afterImportActions = {
|
|
302
294
|
tryAwaitWithRetry(async () => await db.updateDocument(dbId, collId, doc.$id, {
|
303
295
|
[fieldName]: updateData,
|
304
296
|
}));
|
305
|
-
|
297
|
+
MessageFormatter.success(`Created file from path: ${file.$id}`, { prefix: "Import" });
|
306
298
|
}
|
307
299
|
}
|
308
300
|
catch (error) {
|
309
301
|
logger.error(`Error creating file and updating field, params were:\ndbId: ${dbId}, collId: ${collId}, docId: ${docId}, fieldName: ${fieldName}, filePath: ${filePath}, fileName: ${fileName}\n\nError: ${error}`);
|
310
|
-
|
311
|
-
|
302
|
+
MessageFormatter.error("Error creating file and updating field", error instanceof Error ? error : new Error(String(error)), { prefix: "Import" });
|
303
|
+
MessageFormatter.info(`Params were: dbId: ${dbId}, collId: ${collId}, docId: ${docId}, fieldName: ${fieldName}, filePath: ${filePath}, fileName: ${fileName}`, { prefix: "Import" });
|
312
304
|
}
|
313
305
|
},
|
314
306
|
};
|