appwrite-utils-cli 1.5.2 → 1.6.1
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 +479 -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 +209 -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 +274 -1563
- 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/utilsController.js
CHANGED
@@ -5,16 +5,18 @@ import { UsersController } from "./users/methods.js";
|
|
5
5
|
import { AppwriteToX } from "./migrations/appwriteToX.js";
|
6
6
|
import { ImportController } from "./migrations/importController.js";
|
7
7
|
import { ImportDataActions } from "./migrations/importDataActions.js";
|
8
|
-
import {
|
8
|
+
import { ensureDatabasesExist, wipeOtherDatabases, ensureCollectionsExist, } from "./databases/setup.js";
|
9
9
|
import { createOrUpdateCollections, wipeDatabase, generateSchemas, fetchAllCollections, wipeCollection, } from "./collections/methods.js";
|
10
10
|
import { wipeAllTables, wipeTableRows } from "./collections/methods.js";
|
11
|
-
import { backupDatabase, ensureDatabaseConfigBucketsExist,
|
11
|
+
import { backupDatabase, ensureDatabaseConfigBucketsExist, wipeDocumentStorage, } from "./storage/methods.js";
|
12
12
|
import path from "path";
|
13
13
|
import { converterFunctions, validationRules, } from "appwrite-utils";
|
14
14
|
import { afterImportActions } from "./migrations/afterImportActions.js";
|
15
15
|
import { transferDatabaseLocalToLocal, transferDatabaseLocalToRemote, transferStorageLocalToLocal, transferStorageLocalToRemote, transferUsersLocalToRemote, } from "./migrations/transfer.js";
|
16
|
-
import { getClient } from "./utils/getClientFromConfig.js";
|
16
|
+
import { getClient, getClientWithAuth } from "./utils/getClientFromConfig.js";
|
17
17
|
import { getAdapterFromConfig } from "./utils/getClientFromConfig.js";
|
18
|
+
import { hasSessionAuth, findSessionByEndpointAndProject, isValidSessionCookie } from "./utils/sessionAuth.js";
|
19
|
+
import { createSessionPreservation } from "./utils/loadConfigs.js";
|
18
20
|
import { fetchAllDatabases } from "./databases/methods.js";
|
19
21
|
import { listFunctions, updateFunctionSpecifications, } from "./functions/methods.js";
|
20
22
|
import chalk from "chalk";
|
@@ -24,6 +26,7 @@ import { configureLogging, updateLogger } from "./shared/logging.js";
|
|
24
26
|
import { MessageFormatter, Messages } from "./shared/messageFormatter.js";
|
25
27
|
import { SchemaGenerator } from "./shared/schemaGenerator.js";
|
26
28
|
import { findYamlConfig } from "./config/yamlConfig.js";
|
29
|
+
import { validateCollectionsTablesConfig, reportValidationResults, validateWithStrictMode } from "./config/configValidation.js";
|
27
30
|
export class UtilsController {
|
28
31
|
appwriteFolderPath;
|
29
32
|
appwriteConfigPath;
|
@@ -31,9 +34,14 @@ export class UtilsController {
|
|
31
34
|
appwriteServer;
|
32
35
|
database;
|
33
36
|
storage;
|
37
|
+
adapter;
|
34
38
|
converterDefinitions = converterFunctions;
|
35
39
|
validityRuleDefinitions = validationRules;
|
36
40
|
afterImportActionsDefinitions = afterImportActions;
|
41
|
+
// Session preservation fields
|
42
|
+
sessionCookie;
|
43
|
+
authMethod;
|
44
|
+
sessionMetadata;
|
37
45
|
constructor(currentUserDir, directConfig) {
|
38
46
|
const basePath = currentUserDir;
|
39
47
|
if (directConfig) {
|
@@ -46,19 +54,29 @@ export class UtilsController {
|
|
46
54
|
MessageFormatter.error("Appwrite project is required", undefined, { prefix: "Config" });
|
47
55
|
hasErrors = true;
|
48
56
|
}
|
49
|
-
|
50
|
-
|
57
|
+
// Check authentication: either API key or session auth is required
|
58
|
+
const hasValidSession = directConfig.appwriteEndpoint && directConfig.appwriteProject &&
|
59
|
+
hasSessionAuth(directConfig.appwriteEndpoint, directConfig.appwriteProject);
|
60
|
+
if (!directConfig.appwriteKey && !hasValidSession) {
|
61
|
+
MessageFormatter.error("Authentication required: provide an API key or login with 'appwrite login'", undefined, { prefix: "Config" });
|
51
62
|
hasErrors = true;
|
52
63
|
}
|
64
|
+
else if (!directConfig.appwriteKey && hasValidSession) {
|
65
|
+
MessageFormatter.info("Using session authentication (no API key required)", { prefix: "Auth" });
|
66
|
+
}
|
67
|
+
else if (directConfig.appwriteKey && hasValidSession) {
|
68
|
+
MessageFormatter.info("API key provided, session authentication also available", { prefix: "Auth" });
|
69
|
+
}
|
53
70
|
if (!hasErrors) {
|
54
71
|
// Only set config if we have all required fields
|
55
72
|
this.appwriteFolderPath = basePath;
|
56
73
|
this.config = {
|
57
74
|
appwriteEndpoint: directConfig.appwriteEndpoint,
|
58
75
|
appwriteProject: directConfig.appwriteProject,
|
59
|
-
appwriteKey: directConfig.appwriteKey,
|
76
|
+
appwriteKey: directConfig.appwriteKey || "",
|
60
77
|
appwriteClient: null,
|
61
78
|
apiMode: "auto", // Default to auto-detect for dual API support
|
79
|
+
authMethod: "auto", // Default to auto-detect authentication method
|
62
80
|
enableBackups: false,
|
63
81
|
backupInterval: 0,
|
64
82
|
backupRetention: 0,
|
@@ -66,7 +84,6 @@ export class UtilsController {
|
|
66
84
|
enableMockData: false,
|
67
85
|
documentBucketId: "",
|
68
86
|
usersCollectionName: "",
|
69
|
-
useMigrations: true,
|
70
87
|
databases: [],
|
71
88
|
buckets: [],
|
72
89
|
functions: [],
|
@@ -89,17 +106,26 @@ export class UtilsController {
|
|
89
106
|
this.appwriteFolderPath = appwriteConfigFound; // For YAML configs, findAppwriteConfig already returns the correct directory
|
90
107
|
}
|
91
108
|
}
|
92
|
-
async init() {
|
109
|
+
async init(options = {}) {
|
110
|
+
const { validate = false, strictMode = false, useSession = false, sessionCookie } = options;
|
93
111
|
if (!this.config) {
|
94
112
|
if (this.appwriteFolderPath && this.appwriteConfigPath) {
|
95
113
|
MessageFormatter.progress("Loading config from file...", { prefix: "Config" });
|
96
114
|
try {
|
97
|
-
const { config, actualConfigPath } = await loadConfigWithPath(this.appwriteFolderPath);
|
115
|
+
const { config, actualConfigPath, validation } = await loadConfigWithPath(this.appwriteFolderPath, { validate, strictMode, reportValidation: false });
|
98
116
|
this.config = config;
|
99
117
|
MessageFormatter.info(`Loaded config from: ${actualConfigPath}`, { prefix: "Config" });
|
118
|
+
// Report validation results if validation was requested
|
119
|
+
if (validation && validate) {
|
120
|
+
reportValidationResults(validation, { verbose: false });
|
121
|
+
// In strict mode, throw if validation fails
|
122
|
+
if (strictMode && !validation.isValid) {
|
123
|
+
throw new Error(`Configuration validation failed in strict mode. Found ${validation.errors.length} validation errors.`);
|
124
|
+
}
|
125
|
+
}
|
100
126
|
}
|
101
127
|
catch (error) {
|
102
|
-
MessageFormatter.error("Failed to load config from file", undefined, { prefix: "Config" });
|
128
|
+
MessageFormatter.error("Failed to load config from file", error instanceof Error ? error : undefined, { prefix: "Config" });
|
103
129
|
return;
|
104
130
|
}
|
105
131
|
}
|
@@ -113,23 +139,39 @@ export class UtilsController {
|
|
113
139
|
configureLogging(this.config.logging);
|
114
140
|
updateLogger();
|
115
141
|
}
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
.setKey(this.config.appwriteKey);
|
142
|
+
// Use enhanced client with session authentication support
|
143
|
+
// Pass session cookie from options if provided
|
144
|
+
const clientSessionCookie = sessionCookie || this.sessionCookie;
|
145
|
+
this.appwriteServer = getClientWithAuth(this.config.appwriteEndpoint, this.config.appwriteProject, this.config.appwriteKey || undefined, clientSessionCookie);
|
121
146
|
this.database = new Databases(this.appwriteServer);
|
122
147
|
this.storage = new Storage(this.appwriteServer);
|
123
148
|
this.config.appwriteClient = this.appwriteServer;
|
149
|
+
// Initialize adapter with version detection
|
150
|
+
try {
|
151
|
+
const { adapter, apiMode } = await getAdapterFromConfig(this.config, false, clientSessionCookie);
|
152
|
+
this.adapter = adapter;
|
153
|
+
MessageFormatter.info(`Database adapter initialized (apiMode: ${apiMode})`, {
|
154
|
+
prefix: "Adapter"
|
155
|
+
});
|
156
|
+
}
|
157
|
+
catch (error) {
|
158
|
+
MessageFormatter.warning('Database adapter initialization failed - some features may not work', { prefix: "Adapter" });
|
159
|
+
}
|
160
|
+
// Extract and store session information after successful authentication
|
161
|
+
this.extractSessionInfo();
|
124
162
|
}
|
125
163
|
async reloadConfig() {
|
126
164
|
if (!this.appwriteFolderPath) {
|
127
165
|
MessageFormatter.error("Failed to get appwriteFolderPath", undefined, { prefix: "Controller" });
|
128
166
|
return;
|
129
167
|
}
|
130
|
-
|
168
|
+
// Preserve session authentication during config reload
|
169
|
+
const preserveAuth = this.createSessionPreservationOptions();
|
170
|
+
this.config = await loadConfig(this.appwriteFolderPath, {
|
171
|
+
preserveAuth
|
172
|
+
});
|
131
173
|
if (!this.config) {
|
132
|
-
|
174
|
+
MessageFormatter.error("Failed to load config", undefined, { prefix: "Controller" });
|
133
175
|
return;
|
134
176
|
}
|
135
177
|
// Configure logging based on updated config
|
@@ -137,22 +179,24 @@ export class UtilsController {
|
|
137
179
|
configureLogging(this.config.logging);
|
138
180
|
updateLogger();
|
139
181
|
}
|
140
|
-
|
141
|
-
this.appwriteServer
|
142
|
-
.setEndpoint(this.config.appwriteEndpoint)
|
143
|
-
.setProject(this.config.appwriteProject)
|
144
|
-
.setKey(this.config.appwriteKey);
|
182
|
+
// Use enhanced client with session authentication support, passing preserved session
|
183
|
+
this.appwriteServer = getClientWithAuth(this.config.appwriteEndpoint, this.config.appwriteProject, this.config.appwriteKey || undefined, this.sessionCookie);
|
145
184
|
this.database = new Databases(this.appwriteServer);
|
146
185
|
this.storage = new Storage(this.appwriteServer);
|
147
186
|
this.config.appwriteClient = this.appwriteServer;
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
MessageFormatter.
|
153
|
-
|
187
|
+
// Re-initialize adapter with version detection after config reload
|
188
|
+
try {
|
189
|
+
const { adapter, apiMode } = await getAdapterFromConfig(this.config, false, this.sessionCookie);
|
190
|
+
this.adapter = adapter;
|
191
|
+
MessageFormatter.info(`Database adapter re-initialized (apiMode: ${apiMode})`, {
|
192
|
+
prefix: "Adapter"
|
193
|
+
});
|
154
194
|
}
|
155
|
-
|
195
|
+
catch (error) {
|
196
|
+
MessageFormatter.warning('Database adapter re-initialization failed - some features may not work', { prefix: "Adapter" });
|
197
|
+
}
|
198
|
+
// Re-extract session information after reload
|
199
|
+
this.extractSessionInfo();
|
156
200
|
}
|
157
201
|
async ensureDatabaseConfigBucketsExist(databases = []) {
|
158
202
|
await this.init();
|
@@ -172,7 +216,6 @@ export class UtilsController {
|
|
172
216
|
MessageFormatter.error("Config not initialized", undefined, { prefix: "Controller" });
|
173
217
|
return;
|
174
218
|
}
|
175
|
-
await this.setupMigrationDatabase();
|
176
219
|
await this.ensureDatabaseConfigBucketsExist(databases);
|
177
220
|
await ensureDatabasesExist(this.config, databases);
|
178
221
|
}
|
@@ -204,29 +247,29 @@ export class UtilsController {
|
|
204
247
|
MessageFormatter.error("Database not initialized", undefined, { prefix: "Controller" });
|
205
248
|
return;
|
206
249
|
}
|
207
|
-
await wipeOtherDatabases(this.database, databasesToKeep
|
250
|
+
await wipeOtherDatabases(this.database, databasesToKeep);
|
208
251
|
}
|
209
252
|
async wipeUsers() {
|
210
253
|
await this.init();
|
211
254
|
if (!this.config || !this.database) {
|
212
|
-
|
255
|
+
MessageFormatter.error("Config or database not initialized", undefined, { prefix: "Controller" });
|
213
256
|
return;
|
214
257
|
}
|
215
258
|
const usersController = new UsersController(this.config, this.database);
|
216
259
|
await usersController.wipeUsers();
|
217
260
|
}
|
218
|
-
async backupDatabase(database) {
|
261
|
+
async backupDatabase(database, format = 'json') {
|
219
262
|
await this.init();
|
220
263
|
if (!this.database || !this.storage || !this.config) {
|
221
|
-
|
264
|
+
MessageFormatter.error("Database, storage, or config not initialized", undefined, { prefix: "Controller" });
|
222
265
|
return;
|
223
266
|
}
|
224
|
-
await backupDatabase(this.config, this.database, database.$id, this.storage);
|
267
|
+
await backupDatabase(this.config, this.database, database.$id, this.storage, format);
|
225
268
|
}
|
226
269
|
async listAllFunctions() {
|
227
270
|
await this.init();
|
228
271
|
if (!this.appwriteServer) {
|
229
|
-
|
272
|
+
MessageFormatter.error("Appwrite server not initialized", undefined, { prefix: "Controller" });
|
230
273
|
return [];
|
231
274
|
}
|
232
275
|
const { functions } = await listFunctions(this.appwriteServer, [
|
@@ -241,7 +284,7 @@ export class UtilsController {
|
|
241
284
|
}
|
242
285
|
const functionsDir = findFunctionsDir(this.appwriteFolderPath);
|
243
286
|
if (!functionsDir) {
|
244
|
-
|
287
|
+
MessageFormatter.error("Failed to find functions directory", undefined, { prefix: "Controller" });
|
245
288
|
return new Map();
|
246
289
|
}
|
247
290
|
const functionDirMap = new Map();
|
@@ -263,14 +306,14 @@ export class UtilsController {
|
|
263
306
|
async deployFunction(functionName, functionPath, functionConfig) {
|
264
307
|
await this.init();
|
265
308
|
if (!this.appwriteServer) {
|
266
|
-
|
309
|
+
MessageFormatter.error("Appwrite server not initialized", undefined, { prefix: "Controller" });
|
267
310
|
return;
|
268
311
|
}
|
269
312
|
if (!functionConfig) {
|
270
313
|
functionConfig = this.config?.functions?.find((f) => f.name === functionName);
|
271
314
|
}
|
272
315
|
if (!functionConfig) {
|
273
|
-
|
316
|
+
MessageFormatter.error(`Function ${functionName} not found in config`, undefined, { prefix: "Controller" });
|
274
317
|
return;
|
275
318
|
}
|
276
319
|
await deployLocalFunction(this.appwriteServer, functionName, functionConfig, functionPath);
|
@@ -278,7 +321,7 @@ export class UtilsController {
|
|
278
321
|
async syncFunctions() {
|
279
322
|
await this.init();
|
280
323
|
if (!this.appwriteServer) {
|
281
|
-
|
324
|
+
MessageFormatter.error("Appwrite server not initialized", undefined, { prefix: "Controller" });
|
282
325
|
return;
|
283
326
|
}
|
284
327
|
const localFunctions = this.config?.functions || [];
|
@@ -296,7 +339,7 @@ export class UtilsController {
|
|
296
339
|
if (!this.database || !this.config)
|
297
340
|
throw new Error("Database not initialized");
|
298
341
|
try {
|
299
|
-
const { adapter, apiMode } = await getAdapterFromConfig(this.config);
|
342
|
+
const { adapter, apiMode } = await getAdapterFromConfig(this.config, false, this.sessionCookie);
|
300
343
|
if (apiMode === 'tablesdb') {
|
301
344
|
await wipeAllTables(adapter, database.$id);
|
302
345
|
}
|
@@ -339,7 +382,7 @@ export class UtilsController {
|
|
339
382
|
if (!this.database || !this.config)
|
340
383
|
throw new Error("Database not initialized");
|
341
384
|
try {
|
342
|
-
const { adapter, apiMode } = await getAdapterFromConfig(this.config);
|
385
|
+
const { adapter, apiMode } = await getAdapterFromConfig(this.config, false, this.sessionCookie);
|
343
386
|
if (apiMode === 'tablesdb') {
|
344
387
|
await wipeTableRows(adapter, database.$id, collection.$id);
|
345
388
|
}
|
@@ -362,8 +405,6 @@ export class UtilsController {
|
|
362
405
|
if (!this.database || !this.config)
|
363
406
|
throw new Error("Database or config not initialized");
|
364
407
|
for (const database of databases) {
|
365
|
-
if (!this.config.useMigrations && database.$id === "migrations")
|
366
|
-
continue;
|
367
408
|
await this.createOrUpdateCollections(database, undefined, collections);
|
368
409
|
}
|
369
410
|
}
|
@@ -374,10 +415,24 @@ export class UtilsController {
|
|
374
415
|
await createOrUpdateCollections(this.database, database.$id, this.config, deletedCollections, collections);
|
375
416
|
}
|
376
417
|
async generateSchemas() {
|
377
|
-
|
418
|
+
// Schema generation doesn't need Appwrite connection, just config
|
378
419
|
if (!this.config) {
|
379
|
-
|
380
|
-
|
420
|
+
if (this.appwriteFolderPath && this.appwriteConfigPath) {
|
421
|
+
MessageFormatter.progress("Loading config from file...", { prefix: "Config" });
|
422
|
+
try {
|
423
|
+
const { config, actualConfigPath } = await loadConfigWithPath(this.appwriteFolderPath, { validate: false, strictMode: false, reportValidation: false });
|
424
|
+
this.config = config;
|
425
|
+
MessageFormatter.info(`Loaded config from: ${actualConfigPath}`, { prefix: "Config" });
|
426
|
+
}
|
427
|
+
catch (error) {
|
428
|
+
MessageFormatter.error("Failed to load config from file", error instanceof Error ? error : undefined, { prefix: "Config" });
|
429
|
+
return;
|
430
|
+
}
|
431
|
+
}
|
432
|
+
else {
|
433
|
+
MessageFormatter.error("No configuration available", undefined, { prefix: "Controller" });
|
434
|
+
return;
|
435
|
+
}
|
381
436
|
}
|
382
437
|
if (!this.appwriteFolderPath) {
|
383
438
|
MessageFormatter.error("Failed to get appwriteFolderPath", undefined, { prefix: "Controller" });
|
@@ -442,12 +497,10 @@ export class UtilsController {
|
|
442
497
|
const allDatabases = await fetchAllDatabases(this.database);
|
443
498
|
databases = allDatabases;
|
444
499
|
}
|
445
|
-
// Ensure DBs exist
|
500
|
+
// Ensure DBs exist
|
446
501
|
await this.ensureDatabasesExist(databases);
|
447
502
|
await this.ensureDatabaseConfigBucketsExist(databases);
|
448
|
-
|
449
|
-
const dbsForCollections = databases.filter((db) => (this.config?.useMigrations ?? true) ? db.name.toLowerCase() !== "migrations" : true);
|
450
|
-
await this.createOrUpdateCollectionsForDatabases(dbsForCollections, collections);
|
503
|
+
await this.createOrUpdateCollectionsForDatabases(databases, collections);
|
451
504
|
}
|
452
505
|
getAppwriteFolderPath() {
|
453
506
|
return this.appwriteFolderPath;
|
@@ -458,14 +511,14 @@ export class UtilsController {
|
|
458
511
|
let sourceDatabases = [];
|
459
512
|
let targetDatabases = [];
|
460
513
|
if (!sourceClient) {
|
461
|
-
|
514
|
+
MessageFormatter.error("Source database not initialized", undefined, { prefix: "Controller" });
|
462
515
|
return;
|
463
516
|
}
|
464
517
|
if (options.isRemote) {
|
465
518
|
if (!options.transferEndpoint ||
|
466
519
|
!options.transferProject ||
|
467
520
|
!options.transferKey) {
|
468
|
-
|
521
|
+
MessageFormatter.error("Remote transfer options are missing", undefined, { prefix: "Controller" });
|
469
522
|
return;
|
470
523
|
}
|
471
524
|
const remoteClient = getClient(options.transferEndpoint, options.transferProject, options.transferKey);
|
@@ -482,7 +535,7 @@ export class UtilsController {
|
|
482
535
|
const fromDb = sourceDatabases.find((db) => db.$id === options.fromDb.$id);
|
483
536
|
const targetDb = targetDatabases.find((db) => db.$id === options.targetDb.$id);
|
484
537
|
if (!fromDb || !targetDb) {
|
485
|
-
|
538
|
+
MessageFormatter.error("Source or target database not found", undefined, { prefix: "Controller" });
|
486
539
|
return;
|
487
540
|
}
|
488
541
|
if (options.isRemote && targetClient) {
|
@@ -494,10 +547,10 @@ export class UtilsController {
|
|
494
547
|
}
|
495
548
|
if (options.transferUsers) {
|
496
549
|
if (!options.isRemote) {
|
497
|
-
|
550
|
+
MessageFormatter.warning("User transfer is only supported for remote transfers. Skipping...", { prefix: "Controller" });
|
498
551
|
}
|
499
552
|
else if (!this.appwriteServer) {
|
500
|
-
|
553
|
+
MessageFormatter.error("Appwrite server not initialized", undefined, { prefix: "Controller" });
|
501
554
|
return;
|
502
555
|
}
|
503
556
|
else {
|
@@ -543,4 +596,81 @@ export class UtilsController {
|
|
543
596
|
await updateFunctionSpecifications(this.appwriteServer, functionId, specification);
|
544
597
|
MessageFormatter.success(`Successfully updated function specifications for ${functionId} to ${specification}`, { prefix: "Functions" });
|
545
598
|
}
|
599
|
+
/**
|
600
|
+
* Validates the current configuration for collections/tables conflicts
|
601
|
+
*/
|
602
|
+
async validateConfiguration(strictMode = false) {
|
603
|
+
await this.init();
|
604
|
+
if (!this.config) {
|
605
|
+
throw new Error("Configuration not loaded");
|
606
|
+
}
|
607
|
+
MessageFormatter.progress("Validating configuration...", { prefix: "Validation" });
|
608
|
+
const validation = strictMode
|
609
|
+
? validateWithStrictMode(this.config, strictMode)
|
610
|
+
: validateCollectionsTablesConfig(this.config);
|
611
|
+
reportValidationResults(validation, { verbose: true });
|
612
|
+
if (validation.isValid) {
|
613
|
+
MessageFormatter.success("Configuration validation passed", { prefix: "Validation" });
|
614
|
+
}
|
615
|
+
else {
|
616
|
+
MessageFormatter.error(`Configuration validation failed with ${validation.errors.length} errors`, undefined, { prefix: "Validation" });
|
617
|
+
}
|
618
|
+
return validation;
|
619
|
+
}
|
620
|
+
/**
|
621
|
+
* Extract session information from the current authenticated client
|
622
|
+
* This preserves session context for use across config reloads and adapter operations
|
623
|
+
*/
|
624
|
+
extractSessionInfo() {
|
625
|
+
if (!this.config) {
|
626
|
+
return;
|
627
|
+
}
|
628
|
+
// Try to extract session from current config first
|
629
|
+
if (this.config.sessionCookie && isValidSessionCookie(this.config.sessionCookie)) {
|
630
|
+
this.sessionCookie = this.config.sessionCookie;
|
631
|
+
this.authMethod = "session";
|
632
|
+
this.sessionMetadata = this.config.sessionMetadata;
|
633
|
+
MessageFormatter.debug("Extracted session from config", { prefix: "Session" });
|
634
|
+
return;
|
635
|
+
}
|
636
|
+
// Fall back to finding session from Appwrite CLI preferences
|
637
|
+
const sessionAuth = findSessionByEndpointAndProject(this.config.appwriteEndpoint, this.config.appwriteProject);
|
638
|
+
if (sessionAuth && isValidSessionCookie(sessionAuth.sessionCookie)) {
|
639
|
+
this.sessionCookie = sessionAuth.sessionCookie;
|
640
|
+
this.authMethod = "session";
|
641
|
+
this.sessionMetadata = {
|
642
|
+
email: sessionAuth.email
|
643
|
+
};
|
644
|
+
MessageFormatter.debug(`Extracted session from CLI preferences for ${sessionAuth.email || 'unknown user'}`, { prefix: "Session" });
|
645
|
+
return;
|
646
|
+
}
|
647
|
+
// No session found, using API key authentication
|
648
|
+
if (this.config.appwriteKey) {
|
649
|
+
this.authMethod = "apikey";
|
650
|
+
this.sessionCookie = undefined;
|
651
|
+
this.sessionMetadata = undefined;
|
652
|
+
MessageFormatter.debug("Using API key authentication", { prefix: "Session" });
|
653
|
+
}
|
654
|
+
}
|
655
|
+
/**
|
656
|
+
* Create session preservation options for passing to loadConfig
|
657
|
+
* Returns current session state to maintain authentication across config reloads
|
658
|
+
*/
|
659
|
+
createSessionPreservationOptions() {
|
660
|
+
if (!this.sessionCookie || !isValidSessionCookie(this.sessionCookie)) {
|
661
|
+
return undefined;
|
662
|
+
}
|
663
|
+
return createSessionPreservation(this.sessionCookie, this.sessionMetadata?.email, this.sessionMetadata?.expiresAt);
|
664
|
+
}
|
665
|
+
/**
|
666
|
+
* Get current session information for debugging/logging purposes
|
667
|
+
*/
|
668
|
+
getSessionInfo() {
|
669
|
+
return {
|
670
|
+
hasSession: !!this.sessionCookie && isValidSessionCookie(this.sessionCookie),
|
671
|
+
authMethod: this.authMethod,
|
672
|
+
email: this.sessionMetadata?.email,
|
673
|
+
expiresAt: this.sessionMetadata?.expiresAt
|
674
|
+
};
|
675
|
+
}
|
546
676
|
}
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "appwrite-utils-cli",
|
3
3
|
"description": "Appwrite Utility Functions to help with database management, data conversion, data import, migrations, and much more. Meant to be used as a CLI tool, I do not recommend installing this in frontend environments.",
|
4
|
-
"version": "1.
|
4
|
+
"version": "1.6.1",
|
5
5
|
"main": "src/main.ts",
|
6
6
|
"type": "module",
|
7
7
|
"repository": {
|
@@ -27,13 +27,17 @@
|
|
27
27
|
"build": "bun run tsc",
|
28
28
|
"start": "tsx --no-cache src/main.ts",
|
29
29
|
"deploy": "bun run build && npm publish --access public",
|
30
|
+
"test": "jest",
|
31
|
+
"test:watch": "jest --watch",
|
32
|
+
"test:coverage": "jest --coverage",
|
33
|
+
"test:ci": "jest --ci --coverage --watchAll=false",
|
30
34
|
"postinstall": "echo 'This package is intended for CLI use only and should not be added as a dependency in other projects.'"
|
31
35
|
},
|
32
36
|
"dependencies": {
|
33
37
|
"@types/inquirer": "^9.0.8",
|
34
38
|
"@types/json-schema": "^7.0.15",
|
35
39
|
"@types/yargs": "^17.0.33",
|
36
|
-
"appwrite-utils": "^1.
|
40
|
+
"appwrite-utils": "^1.6.1",
|
37
41
|
"chalk": "^5.4.1",
|
38
42
|
"cli-progress": "^3.12.0",
|
39
43
|
"commander": "^12.1.0",
|
@@ -41,6 +45,7 @@
|
|
41
45
|
"ignore": "^6.0.2",
|
42
46
|
"inquirer": "^9.3.7",
|
43
47
|
"js-yaml": "^4.1.0",
|
48
|
+
"jszip": "^3.10.1",
|
44
49
|
"luxon": "^3.6.1",
|
45
50
|
"nanostores": "^0.10.3",
|
46
51
|
"node-appwrite": "^17",
|
@@ -54,10 +59,15 @@
|
|
54
59
|
"zod": "^4.0.0"
|
55
60
|
},
|
56
61
|
"devDependencies": {
|
62
|
+
"@jest/globals": "^29.7.0",
|
57
63
|
"@types/cli-progress": "^3.11.6",
|
64
|
+
"@types/jest": "^29.5.12",
|
58
65
|
"@types/js-yaml": "^4.0.9",
|
66
|
+
"@types/jszip": "^3.4.1",
|
59
67
|
"@types/lodash": "^4.17.18",
|
60
68
|
"@types/luxon": "^3.6.2",
|
69
|
+
"jest": "^29.7.0",
|
70
|
+
"ts-jest": "^29.1.2",
|
61
71
|
"typescript": "^5.8.3"
|
62
72
|
}
|
63
73
|
}
|