@njdamstra/appwrite-utils-cli 1.8.9
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 +19 -0
- package/README.md +1133 -0
- package/dist/adapters/AdapterFactory.d.ts +94 -0
- package/dist/adapters/AdapterFactory.js +405 -0
- package/dist/adapters/DatabaseAdapter.d.ts +233 -0
- package/dist/adapters/DatabaseAdapter.js +50 -0
- package/dist/adapters/LegacyAdapter.d.ts +50 -0
- package/dist/adapters/LegacyAdapter.js +612 -0
- package/dist/adapters/TablesDBAdapter.d.ts +45 -0
- package/dist/adapters/TablesDBAdapter.js +571 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.js +12 -0
- 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 +166 -0
- package/dist/cli/commands/databaseCommands.d.ts +13 -0
- package/dist/cli/commands/databaseCommands.js +554 -0
- package/dist/cli/commands/functionCommands.d.ts +7 -0
- package/dist/cli/commands/functionCommands.js +330 -0
- package/dist/cli/commands/schemaCommands.d.ts +7 -0
- package/dist/cli/commands/schemaCommands.js +169 -0
- package/dist/cli/commands/storageCommands.d.ts +5 -0
- package/dist/cli/commands/storageCommands.js +143 -0
- package/dist/cli/commands/transferCommands.d.ts +5 -0
- package/dist/cli/commands/transferCommands.js +384 -0
- package/dist/collections/attributes.d.ts +13 -0
- package/dist/collections/attributes.js +1364 -0
- package/dist/collections/indexes.d.ts +12 -0
- package/dist/collections/indexes.js +217 -0
- package/dist/collections/methods.d.ts +19 -0
- package/dist/collections/methods.js +682 -0
- package/dist/collections/tableOperations.d.ts +86 -0
- package/dist/collections/tableOperations.js +434 -0
- package/dist/collections/transferOperations.d.ts +8 -0
- package/dist/collections/transferOperations.js +412 -0
- package/dist/collections/wipeOperations.d.ts +16 -0
- package/dist/collections/wipeOperations.js +233 -0
- package/dist/config/ConfigManager.d.ts +445 -0
- package/dist/config/ConfigManager.js +625 -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/index.d.ts +8 -0
- package/dist/config/index.js +7 -0
- package/dist/config/services/ConfigDiscoveryService.d.ts +126 -0
- package/dist/config/services/ConfigDiscoveryService.js +374 -0
- package/dist/config/services/ConfigLoaderService.d.ts +129 -0
- package/dist/config/services/ConfigLoaderService.js +540 -0
- package/dist/config/services/ConfigMergeService.d.ts +208 -0
- package/dist/config/services/ConfigMergeService.js +308 -0
- package/dist/config/services/ConfigValidationService.d.ts +214 -0
- package/dist/config/services/ConfigValidationService.js +310 -0
- package/dist/config/services/SessionAuthService.d.ts +225 -0
- package/dist/config/services/SessionAuthService.js +456 -0
- package/dist/config/services/__tests__/ConfigMergeService.test.d.ts +1 -0
- package/dist/config/services/__tests__/ConfigMergeService.test.js +271 -0
- package/dist/config/services/index.d.ts +13 -0
- package/dist/config/services/index.js +10 -0
- package/dist/config/yamlConfig.d.ts +722 -0
- package/dist/config/yamlConfig.js +702 -0
- package/dist/databases/methods.d.ts +6 -0
- package/dist/databases/methods.js +35 -0
- package/dist/databases/setup.d.ts +5 -0
- package/dist/databases/setup.js +45 -0
- package/dist/examples/yamlTerminologyExample.d.ts +42 -0
- package/dist/examples/yamlTerminologyExample.js +272 -0
- package/dist/functions/deployments.d.ts +4 -0
- package/dist/functions/deployments.js +146 -0
- package/dist/functions/fnConfigDiscovery.d.ts +3 -0
- package/dist/functions/fnConfigDiscovery.js +108 -0
- package/dist/functions/methods.d.ts +16 -0
- package/dist/functions/methods.js +162 -0
- package/dist/functions/pathResolution.d.ts +37 -0
- package/dist/functions/pathResolution.js +185 -0
- package/dist/functions/templates/count-docs-in-collection/README.md +54 -0
- package/dist/functions/templates/count-docs-in-collection/src/main.ts +159 -0
- package/dist/functions/templates/count-docs-in-collection/src/request.ts +9 -0
- package/dist/functions/templates/hono-typescript/README.md +286 -0
- package/dist/functions/templates/hono-typescript/src/adapters/request.ts +74 -0
- package/dist/functions/templates/hono-typescript/src/adapters/response.ts +106 -0
- package/dist/functions/templates/hono-typescript/src/app.ts +180 -0
- package/dist/functions/templates/hono-typescript/src/context.ts +103 -0
- package/dist/functions/templates/hono-typescript/src/index.ts +54 -0
- package/dist/functions/templates/hono-typescript/src/middleware/appwrite.ts +119 -0
- package/dist/functions/templates/typescript-node/README.md +32 -0
- package/dist/functions/templates/typescript-node/src/context.ts +103 -0
- package/dist/functions/templates/typescript-node/src/index.ts +29 -0
- package/dist/functions/templates/uv/README.md +31 -0
- package/dist/functions/templates/uv/pyproject.toml +30 -0
- package/dist/functions/templates/uv/src/__init__.py +0 -0
- package/dist/functions/templates/uv/src/context.py +125 -0
- package/dist/functions/templates/uv/src/index.py +46 -0
- package/dist/init.d.ts +2 -0
- package/dist/init.js +57 -0
- package/dist/interactiveCLI.d.ts +31 -0
- package/dist/interactiveCLI.js +898 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +1172 -0
- package/dist/migrations/afterImportActions.d.ts +17 -0
- package/dist/migrations/afterImportActions.js +306 -0
- package/dist/migrations/appwriteToX.d.ts +211 -0
- package/dist/migrations/appwriteToX.js +491 -0
- package/dist/migrations/comprehensiveTransfer.d.ts +147 -0
- package/dist/migrations/comprehensiveTransfer.js +1317 -0
- package/dist/migrations/dataLoader.d.ts +753 -0
- package/dist/migrations/dataLoader.js +1250 -0
- package/dist/migrations/importController.d.ts +23 -0
- package/dist/migrations/importController.js +268 -0
- package/dist/migrations/importDataActions.d.ts +50 -0
- package/dist/migrations/importDataActions.js +230 -0
- package/dist/migrations/relationships.d.ts +29 -0
- package/dist/migrations/relationships.js +204 -0
- package/dist/migrations/services/DataTransformationService.d.ts +55 -0
- package/dist/migrations/services/DataTransformationService.js +158 -0
- package/dist/migrations/services/FileHandlerService.d.ts +75 -0
- package/dist/migrations/services/FileHandlerService.js +236 -0
- package/dist/migrations/services/ImportOrchestrator.d.ts +97 -0
- package/dist/migrations/services/ImportOrchestrator.js +485 -0
- package/dist/migrations/services/RateLimitManager.d.ts +138 -0
- package/dist/migrations/services/RateLimitManager.js +279 -0
- package/dist/migrations/services/RelationshipResolver.d.ts +120 -0
- package/dist/migrations/services/RelationshipResolver.js +332 -0
- package/dist/migrations/services/UserMappingService.d.ts +109 -0
- package/dist/migrations/services/UserMappingService.js +277 -0
- package/dist/migrations/services/ValidationService.d.ts +74 -0
- package/dist/migrations/services/ValidationService.js +260 -0
- package/dist/migrations/transfer.d.ts +26 -0
- package/dist/migrations/transfer.js +608 -0
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +131 -0
- package/dist/migrations/yaml/YamlImportConfigLoader.js +383 -0
- package/dist/migrations/yaml/YamlImportIntegration.d.ts +93 -0
- package/dist/migrations/yaml/YamlImportIntegration.js +341 -0
- package/dist/migrations/yaml/generateImportSchemas.d.ts +30 -0
- package/dist/migrations/yaml/generateImportSchemas.js +1327 -0
- package/dist/schemas/authUser.d.ts +24 -0
- package/dist/schemas/authUser.js +17 -0
- package/dist/setup.d.ts +2 -0
- package/dist/setup.js +5 -0
- package/dist/setupCommands.d.ts +58 -0
- package/dist/setupCommands.js +490 -0
- package/dist/setupController.d.ts +9 -0
- package/dist/setupController.js +34 -0
- package/dist/shared/attributeMapper.d.ts +20 -0
- package/dist/shared/attributeMapper.js +203 -0
- 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.d.ts +75 -0
- package/dist/shared/confirmationDialogs.js +236 -0
- package/dist/shared/errorUtils.d.ts +54 -0
- package/dist/shared/errorUtils.js +95 -0
- package/dist/shared/functionManager.d.ts +48 -0
- package/dist/shared/functionManager.js +336 -0
- package/dist/shared/indexManager.d.ts +24 -0
- package/dist/shared/indexManager.js +151 -0
- package/dist/shared/jsonSchemaGenerator.d.ts +50 -0
- package/dist/shared/jsonSchemaGenerator.js +290 -0
- package/dist/shared/logging.d.ts +61 -0
- package/dist/shared/logging.js +116 -0
- package/dist/shared/messageFormatter.d.ts +39 -0
- package/dist/shared/messageFormatter.js +162 -0
- package/dist/shared/migrationHelpers.d.ts +61 -0
- package/dist/shared/migrationHelpers.js +145 -0
- package/dist/shared/operationLogger.d.ts +10 -0
- package/dist/shared/operationLogger.js +12 -0
- package/dist/shared/operationQueue.d.ts +40 -0
- package/dist/shared/operationQueue.js +311 -0
- 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/progressManager.d.ts +62 -0
- package/dist/shared/progressManager.js +215 -0
- package/dist/shared/pydanticModelGenerator.d.ts +17 -0
- package/dist/shared/pydanticModelGenerator.js +615 -0
- package/dist/shared/relationshipExtractor.d.ts +56 -0
- package/dist/shared/relationshipExtractor.js +138 -0
- package/dist/shared/schemaGenerator.d.ts +40 -0
- package/dist/shared/schemaGenerator.js +556 -0
- package/dist/shared/selectionDialogs.d.ts +214 -0
- package/dist/shared/selectionDialogs.js +544 -0
- package/dist/storage/backupCompression.d.ts +20 -0
- package/dist/storage/backupCompression.js +67 -0
- package/dist/storage/methods.d.ts +32 -0
- package/dist/storage/methods.js +472 -0
- package/dist/storage/schemas.d.ts +842 -0
- package/dist/storage/schemas.js +175 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.js +3 -0
- package/dist/users/methods.d.ts +16 -0
- package/dist/users/methods.js +277 -0
- package/dist/utils/ClientFactory.d.ts +87 -0
- package/dist/utils/ClientFactory.js +212 -0
- package/dist/utils/configDiscovery.d.ts +78 -0
- package/dist/utils/configDiscovery.js +472 -0
- package/dist/utils/configMigration.d.ts +1 -0
- package/dist/utils/configMigration.js +261 -0
- package/dist/utils/constantsGenerator.d.ts +31 -0
- package/dist/utils/constantsGenerator.js +321 -0
- package/dist/utils/dataConverters.d.ts +46 -0
- package/dist/utils/dataConverters.js +139 -0
- package/dist/utils/directoryUtils.d.ts +22 -0
- package/dist/utils/directoryUtils.js +59 -0
- package/dist/utils/getClientFromConfig.d.ts +39 -0
- package/dist/utils/getClientFromConfig.js +199 -0
- package/dist/utils/helperFunctions.d.ts +63 -0
- package/dist/utils/helperFunctions.js +156 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/loadConfigs.d.ts +50 -0
- package/dist/utils/loadConfigs.js +358 -0
- 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.d.ts +2 -0
- package/dist/utils/retryFailedPromises.js +23 -0
- package/dist/utils/sessionAuth.d.ts +48 -0
- package/dist/utils/sessionAuth.js +164 -0
- package/dist/utils/setupFiles.d.ts +4 -0
- package/dist/utils/setupFiles.js +1192 -0
- package/dist/utils/typeGuards.d.ts +35 -0
- package/dist/utils/typeGuards.js +57 -0
- package/dist/utils/validationRules.d.ts +43 -0
- package/dist/utils/validationRules.js +42 -0
- package/dist/utils/versionDetection.d.ts +58 -0
- package/dist/utils/versionDetection.js +251 -0
- package/dist/utils/yamlConverter.d.ts +100 -0
- package/dist/utils/yamlConverter.js +428 -0
- package/dist/utils/yamlLoader.d.ts +70 -0
- package/dist/utils/yamlLoader.js +267 -0
- package/dist/utilsController.d.ts +106 -0
- package/dist/utilsController.js +863 -0
- package/package.json +75 -0
- package/scripts/copy-templates.ts +23 -0
- package/src/adapters/AdapterFactory.ts +510 -0
- package/src/adapters/DatabaseAdapter.ts +306 -0
- package/src/adapters/LegacyAdapter.ts +841 -0
- package/src/adapters/TablesDBAdapter.ts +773 -0
- package/src/adapters/index.ts +37 -0
- 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 +201 -0
- package/src/cli/commands/databaseCommands.ts +749 -0
- package/src/cli/commands/functionCommands.ts +418 -0
- package/src/cli/commands/schemaCommands.ts +200 -0
- package/src/cli/commands/storageCommands.ts +152 -0
- package/src/cli/commands/transferCommands.ts +457 -0
- package/src/collections/attributes.ts +2054 -0
- package/src/collections/attributes.ts.backup +1555 -0
- package/src/collections/indexes.ts +352 -0
- package/src/collections/methods.ts +745 -0
- package/src/collections/tableOperations.ts +506 -0
- package/src/collections/transferOperations.ts +590 -0
- package/src/collections/wipeOperations.ts +346 -0
- package/src/config/ConfigManager.ts +808 -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/index.ts +10 -0
- package/src/config/services/ConfigDiscoveryService.ts +463 -0
- package/src/config/services/ConfigLoaderService.ts +740 -0
- package/src/config/services/ConfigMergeService.ts +388 -0
- package/src/config/services/ConfigValidationService.ts +394 -0
- package/src/config/services/SessionAuthService.ts +565 -0
- package/src/config/services/__tests__/ConfigMergeService.test.ts +351 -0
- package/src/config/services/index.ts +29 -0
- package/src/config/yamlConfig.ts +761 -0
- package/src/databases/methods.ts +49 -0
- package/src/databases/setup.ts +77 -0
- package/src/examples/yamlTerminologyExample.ts +346 -0
- package/src/functions/deployments.ts +220 -0
- package/src/functions/fnConfigDiscovery.ts +103 -0
- package/src/functions/methods.ts +271 -0
- package/src/functions/pathResolution.ts +227 -0
- package/src/functions/templates/count-docs-in-collection/README.md +54 -0
- package/src/functions/templates/count-docs-in-collection/src/main.ts +159 -0
- package/src/functions/templates/count-docs-in-collection/src/request.ts +9 -0
- package/src/functions/templates/hono-typescript/README.md +286 -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/typescript-node/README.md +32 -0
- package/src/functions/templates/typescript-node/src/context.ts +103 -0
- package/src/functions/templates/typescript-node/src/index.ts +29 -0
- package/src/functions/templates/uv/README.md +31 -0
- package/src/functions/templates/uv/pyproject.toml +30 -0
- package/src/functions/templates/uv/src/__init__.py +0 -0
- package/src/functions/templates/uv/src/context.py +125 -0
- package/src/functions/templates/uv/src/index.py +46 -0
- package/src/init.ts +62 -0
- package/src/interactiveCLI.ts +1136 -0
- package/src/main.ts +1661 -0
- package/src/migrations/afterImportActions.ts +580 -0
- package/src/migrations/appwriteToX.ts +664 -0
- package/src/migrations/comprehensiveTransfer.ts +2285 -0
- package/src/migrations/dataLoader.ts +1702 -0
- package/src/migrations/importController.ts +428 -0
- package/src/migrations/importDataActions.ts +315 -0
- package/src/migrations/relationships.ts +334 -0
- package/src/migrations/services/DataTransformationService.ts +196 -0
- package/src/migrations/services/FileHandlerService.ts +311 -0
- package/src/migrations/services/ImportOrchestrator.ts +666 -0
- package/src/migrations/services/RateLimitManager.ts +363 -0
- package/src/migrations/services/RelationshipResolver.ts +461 -0
- package/src/migrations/services/UserMappingService.ts +345 -0
- package/src/migrations/services/ValidationService.ts +349 -0
- package/src/migrations/transfer.ts +1068 -0
- package/src/migrations/yaml/YamlImportConfigLoader.ts +439 -0
- package/src/migrations/yaml/YamlImportIntegration.ts +446 -0
- package/src/migrations/yaml/generateImportSchemas.ts +1354 -0
- package/src/schemas/authUser.ts +23 -0
- package/src/setup.ts +8 -0
- package/src/setupCommands.ts +603 -0
- package/src/setupController.ts +43 -0
- package/src/shared/attributeMapper.ts +229 -0
- package/src/shared/backupMetadataSchema.ts +93 -0
- package/src/shared/backupTracking.ts +211 -0
- package/src/shared/confirmationDialogs.ts +327 -0
- package/src/shared/errorUtils.ts +110 -0
- package/src/shared/functionManager.ts +525 -0
- package/src/shared/indexManager.ts +254 -0
- package/src/shared/jsonSchemaGenerator.ts +383 -0
- package/src/shared/logging.ts +149 -0
- package/src/shared/messageFormatter.ts +208 -0
- package/src/shared/migrationHelpers.ts +232 -0
- package/src/shared/operationLogger.ts +20 -0
- package/src/shared/operationQueue.ts +377 -0
- package/src/shared/operationsTable.ts +338 -0
- package/src/shared/operationsTableSchema.ts +60 -0
- package/src/shared/progressManager.ts +278 -0
- package/src/shared/pydanticModelGenerator.ts +618 -0
- package/src/shared/relationshipExtractor.ts +214 -0
- package/src/shared/schemaGenerator.ts +644 -0
- package/src/shared/selectionDialogs.ts +749 -0
- package/src/storage/backupCompression.ts +88 -0
- package/src/storage/methods.ts +698 -0
- package/src/storage/schemas.ts +205 -0
- package/src/types/node-appwrite-tablesdb.d.ts +44 -0
- package/src/types.ts +9 -0
- package/src/users/methods.ts +359 -0
- package/src/utils/ClientFactory.ts +240 -0
- package/src/utils/configDiscovery.ts +557 -0
- package/src/utils/configMigration.ts +348 -0
- package/src/utils/constantsGenerator.ts +369 -0
- package/src/utils/dataConverters.ts +159 -0
- package/src/utils/directoryUtils.ts +61 -0
- package/src/utils/getClientFromConfig.ts +257 -0
- package/src/utils/helperFunctions.ts +228 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/loadConfigs.ts +449 -0
- package/src/utils/pathResolvers.ts +81 -0
- package/src/utils/projectConfig.ts +299 -0
- package/src/utils/retryFailedPromises.ts +29 -0
- package/src/utils/sessionAuth.ts +230 -0
- package/src/utils/setupFiles.ts +1238 -0
- package/src/utils/typeGuards.ts +65 -0
- package/src/utils/validationRules.ts +88 -0
- package/src/utils/versionDetection.ts +292 -0
- package/src/utils/yamlConverter.ts +542 -0
- package/src/utils/yamlLoader.ts +371 -0
- package/src/utilsController.ts +1203 -0
- 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/tsconfig.json +44 -0
|
@@ -0,0 +1,863 @@
|
|
|
1
|
+
import { Client, Databases, Query, Storage, Users, } from "node-appwrite";
|
|
2
|
+
import {} from "@njdamstra/appwrite-utils";
|
|
3
|
+
import { findAppwriteConfig, findFunctionsDir, } from "./utils/loadConfigs.js";
|
|
4
|
+
import { normalizeFunctionName, validateFunctionDirectory } from './functions/pathResolution.js';
|
|
5
|
+
import { UsersController } from "./users/methods.js";
|
|
6
|
+
import { AppwriteToX } from "./migrations/appwriteToX.js";
|
|
7
|
+
import { ImportController } from "./migrations/importController.js";
|
|
8
|
+
import { ImportDataActions } from "./migrations/importDataActions.js";
|
|
9
|
+
import { ensureDatabasesExist, wipeOtherDatabases, ensureCollectionsExist, } from "./databases/setup.js";
|
|
10
|
+
import { createOrUpdateCollections, createOrUpdateCollectionsViaAdapter, wipeDatabase, generateSchemas, fetchAllCollections, wipeCollection, } from "./collections/methods.js";
|
|
11
|
+
import { wipeAllTables, wipeTableRows } from "./collections/methods.js";
|
|
12
|
+
import { backupDatabase, ensureDatabaseConfigBucketsExist, wipeDocumentStorage, } from "./storage/methods.js";
|
|
13
|
+
import path from "path";
|
|
14
|
+
import { converterFunctions, validationRules, } from "@njdamstra/appwrite-utils";
|
|
15
|
+
import { afterImportActions } from "./migrations/afterImportActions.js";
|
|
16
|
+
import { transferDatabaseLocalToLocal, transferDatabaseLocalToRemote, transferStorageLocalToLocal, transferStorageLocalToRemote, transferUsersLocalToRemote, } from "./migrations/transfer.js";
|
|
17
|
+
import { getClient, getClientWithAuth } from "./utils/getClientFromConfig.js";
|
|
18
|
+
import { getAdapterFromConfig } from "./utils/getClientFromConfig.js";
|
|
19
|
+
import { hasSessionAuth, findSessionByEndpointAndProject, isValidSessionCookie } from "./utils/sessionAuth.js";
|
|
20
|
+
import { fetchAllDatabases } from "./databases/methods.js";
|
|
21
|
+
import { listFunctions, updateFunctionSpecifications, } from "./functions/methods.js";
|
|
22
|
+
import chalk from "chalk";
|
|
23
|
+
import { deployLocalFunction } from "./functions/deployments.js";
|
|
24
|
+
import fs from "node:fs";
|
|
25
|
+
import { configureLogging, updateLogger, logger } from "./shared/logging.js";
|
|
26
|
+
import { MessageFormatter, Messages } from "./shared/messageFormatter.js";
|
|
27
|
+
import { SchemaGenerator } from "./shared/schemaGenerator.js";
|
|
28
|
+
import { findYamlConfig } from "./config/yamlConfig.js";
|
|
29
|
+
import { createImportSchemas } from "./migrations/yaml/generateImportSchemas.js";
|
|
30
|
+
import { validateCollectionsTablesConfig, reportValidationResults, validateWithStrictMode } from "./config/configValidation.js";
|
|
31
|
+
import { ConfigManager } from "./config/ConfigManager.js";
|
|
32
|
+
import { ClientFactory } from "./utils/ClientFactory.js";
|
|
33
|
+
export class UtilsController {
|
|
34
|
+
// ──────────────────────────────────────────────────
|
|
35
|
+
// SINGLETON PATTERN
|
|
36
|
+
// ──────────────────────────────────────────────────
|
|
37
|
+
static instance = null;
|
|
38
|
+
isInitialized = false;
|
|
39
|
+
/**
|
|
40
|
+
* Get the UtilsController singleton instance
|
|
41
|
+
*/
|
|
42
|
+
static getInstance(currentUserDir, directConfig) {
|
|
43
|
+
// Clear instance if currentUserDir has changed
|
|
44
|
+
if (UtilsController.instance &&
|
|
45
|
+
UtilsController.instance.currentUserDir !== currentUserDir) {
|
|
46
|
+
logger.debug(`Clearing singleton: currentUserDir changed from ${UtilsController.instance.currentUserDir} to ${currentUserDir}`, { prefix: "UtilsController" });
|
|
47
|
+
UtilsController.clearInstance();
|
|
48
|
+
}
|
|
49
|
+
// Clear instance if directConfig endpoint or project has changed
|
|
50
|
+
if (UtilsController.instance && directConfig) {
|
|
51
|
+
const existingConfig = UtilsController.instance.config;
|
|
52
|
+
if (existingConfig) {
|
|
53
|
+
const endpointChanged = directConfig.appwriteEndpoint &&
|
|
54
|
+
existingConfig.appwriteEndpoint !== directConfig.appwriteEndpoint;
|
|
55
|
+
const projectChanged = directConfig.appwriteProject &&
|
|
56
|
+
existingConfig.appwriteProject !== directConfig.appwriteProject;
|
|
57
|
+
if (endpointChanged || projectChanged) {
|
|
58
|
+
logger.debug("Clearing singleton: endpoint or project changed", { prefix: "UtilsController" });
|
|
59
|
+
UtilsController.clearInstance();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!UtilsController.instance) {
|
|
64
|
+
UtilsController.instance = new UtilsController(currentUserDir, directConfig);
|
|
65
|
+
}
|
|
66
|
+
return UtilsController.instance;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Clear the singleton instance (useful for testing)
|
|
70
|
+
*/
|
|
71
|
+
static clearInstance() {
|
|
72
|
+
UtilsController.instance = null;
|
|
73
|
+
}
|
|
74
|
+
// ──────────────────────────────────────────────────
|
|
75
|
+
// INSTANCE FIELDS
|
|
76
|
+
// ──────────────────────────────────────────────────
|
|
77
|
+
appwriteFolderPath;
|
|
78
|
+
appwriteConfigPath;
|
|
79
|
+
currentUserDir;
|
|
80
|
+
config;
|
|
81
|
+
appwriteServer;
|
|
82
|
+
database;
|
|
83
|
+
storage;
|
|
84
|
+
adapter;
|
|
85
|
+
converterDefinitions = converterFunctions;
|
|
86
|
+
validityRuleDefinitions = validationRules;
|
|
87
|
+
afterImportActionsDefinitions = afterImportActions;
|
|
88
|
+
constructor(currentUserDir, directConfig) {
|
|
89
|
+
this.currentUserDir = currentUserDir;
|
|
90
|
+
const basePath = currentUserDir;
|
|
91
|
+
if (directConfig) {
|
|
92
|
+
let hasErrors = false;
|
|
93
|
+
if (!directConfig.appwriteEndpoint) {
|
|
94
|
+
MessageFormatter.error("Appwrite endpoint is required", undefined, { prefix: "Config" });
|
|
95
|
+
hasErrors = true;
|
|
96
|
+
}
|
|
97
|
+
if (!directConfig.appwriteProject) {
|
|
98
|
+
MessageFormatter.error("Appwrite project is required", undefined, { prefix: "Config" });
|
|
99
|
+
hasErrors = true;
|
|
100
|
+
}
|
|
101
|
+
// Check authentication: either API key or session auth is required
|
|
102
|
+
const hasValidSession = directConfig.appwriteEndpoint && directConfig.appwriteProject &&
|
|
103
|
+
hasSessionAuth(directConfig.appwriteEndpoint, directConfig.appwriteProject);
|
|
104
|
+
if (!directConfig.appwriteKey && !hasValidSession) {
|
|
105
|
+
MessageFormatter.error("Authentication required: provide an API key or login with 'appwrite login'", undefined, { prefix: "Config" });
|
|
106
|
+
hasErrors = true;
|
|
107
|
+
}
|
|
108
|
+
else if (!directConfig.appwriteKey && hasValidSession) {
|
|
109
|
+
MessageFormatter.info("Using session authentication (no API key required)", { prefix: "Auth" });
|
|
110
|
+
}
|
|
111
|
+
else if (directConfig.appwriteKey && hasValidSession) {
|
|
112
|
+
MessageFormatter.info("API key provided, session authentication also available", { prefix: "Auth" });
|
|
113
|
+
}
|
|
114
|
+
if (!hasErrors) {
|
|
115
|
+
// Only set config if we have all required fields
|
|
116
|
+
this.appwriteFolderPath = basePath;
|
|
117
|
+
this.config = {
|
|
118
|
+
appwriteEndpoint: directConfig.appwriteEndpoint,
|
|
119
|
+
appwriteProject: directConfig.appwriteProject,
|
|
120
|
+
appwriteKey: directConfig.appwriteKey || "",
|
|
121
|
+
appwriteClient: null,
|
|
122
|
+
apiMode: "auto", // Default to auto-detect for dual API support
|
|
123
|
+
authMethod: "auto", // Default to auto-detect authentication method
|
|
124
|
+
enableBackups: false,
|
|
125
|
+
backupInterval: 0,
|
|
126
|
+
backupRetention: 0,
|
|
127
|
+
enableBackupCleanup: false,
|
|
128
|
+
enableMockData: false,
|
|
129
|
+
documentBucketId: "",
|
|
130
|
+
usersCollectionName: "",
|
|
131
|
+
databases: [],
|
|
132
|
+
buckets: [],
|
|
133
|
+
functions: [],
|
|
134
|
+
logging: {
|
|
135
|
+
enabled: false,
|
|
136
|
+
level: "info",
|
|
137
|
+
console: false,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// Try to find config file
|
|
144
|
+
const appwriteConfigFound = findAppwriteConfig(basePath);
|
|
145
|
+
if (!appwriteConfigFound) {
|
|
146
|
+
MessageFormatter.warning("No appwriteConfig.ts found and no direct configuration provided", { prefix: "Config" });
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
this.appwriteConfigPath = appwriteConfigFound;
|
|
150
|
+
this.appwriteFolderPath = appwriteConfigFound; // For YAML configs, findAppwriteConfig already returns the correct directory
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async init(options = {}) {
|
|
154
|
+
const { validate = false, strictMode = false } = options;
|
|
155
|
+
const configManager = ConfigManager.getInstance();
|
|
156
|
+
// Load config if not already loaded
|
|
157
|
+
if (!configManager.hasConfig()) {
|
|
158
|
+
await configManager.loadConfig({
|
|
159
|
+
configDir: this.currentUserDir,
|
|
160
|
+
validate,
|
|
161
|
+
strictMode,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
const config = configManager.getConfig();
|
|
165
|
+
// Configure logging based on config
|
|
166
|
+
if (config.logging) {
|
|
167
|
+
configureLogging(config.logging);
|
|
168
|
+
updateLogger();
|
|
169
|
+
}
|
|
170
|
+
// Create client and adapter (session already in config from ConfigManager)
|
|
171
|
+
const { client, adapter } = await ClientFactory.createFromConfig(config);
|
|
172
|
+
this.appwriteServer = client;
|
|
173
|
+
this.adapter = adapter;
|
|
174
|
+
this.config = config;
|
|
175
|
+
// Update config.apiMode from adapter if it's auto or not set
|
|
176
|
+
if (adapter && (!config.apiMode || config.apiMode === 'auto')) {
|
|
177
|
+
this.config.apiMode = adapter.getApiMode();
|
|
178
|
+
logger.debug(`Updated config.apiMode from adapter during init: ${this.config.apiMode}`, { prefix: "UtilsController" });
|
|
179
|
+
}
|
|
180
|
+
this.database = new Databases(this.appwriteServer);
|
|
181
|
+
this.storage = new Storage(this.appwriteServer);
|
|
182
|
+
this.config.appwriteClient = this.appwriteServer;
|
|
183
|
+
// Log only on FIRST initialization to avoid spam
|
|
184
|
+
if (!this.isInitialized) {
|
|
185
|
+
const apiMode = adapter.getApiMode();
|
|
186
|
+
const configApiMode = this.config.apiMode;
|
|
187
|
+
MessageFormatter.info(`Database adapter initialized (apiMode: ${apiMode}, config.apiMode: ${configApiMode})`, { prefix: "Adapter" });
|
|
188
|
+
this.isInitialized = true;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
logger.debug("Adapter reused from cache", { prefix: "UtilsController" });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async reloadConfig() {
|
|
195
|
+
const configManager = ConfigManager.getInstance();
|
|
196
|
+
// Session preservation is automatic in ConfigManager
|
|
197
|
+
const config = await configManager.reloadConfig();
|
|
198
|
+
// Configure logging based on updated config
|
|
199
|
+
if (config.logging) {
|
|
200
|
+
configureLogging(config.logging);
|
|
201
|
+
updateLogger();
|
|
202
|
+
}
|
|
203
|
+
// Recreate client and adapter
|
|
204
|
+
const { client, adapter } = await ClientFactory.createFromConfig(config);
|
|
205
|
+
this.appwriteServer = client;
|
|
206
|
+
this.adapter = adapter;
|
|
207
|
+
this.config = config;
|
|
208
|
+
this.database = new Databases(this.appwriteServer);
|
|
209
|
+
this.storage = new Storage(this.appwriteServer);
|
|
210
|
+
this.config.appwriteClient = this.appwriteServer;
|
|
211
|
+
logger.debug("Config reloaded, adapter refreshed", { prefix: "UtilsController" });
|
|
212
|
+
}
|
|
213
|
+
async ensureDatabaseConfigBucketsExist(databases = []) {
|
|
214
|
+
await this.init();
|
|
215
|
+
if (!this.storage) {
|
|
216
|
+
MessageFormatter.error("Storage not initialized", undefined, { prefix: "Controller" });
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (!this.config) {
|
|
220
|
+
MessageFormatter.error("Config not initialized", undefined, { prefix: "Controller" });
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
await ensureDatabaseConfigBucketsExist(this.storage, this.config, databases);
|
|
224
|
+
}
|
|
225
|
+
async ensureDatabasesExist(databases) {
|
|
226
|
+
await this.init();
|
|
227
|
+
if (!this.config) {
|
|
228
|
+
MessageFormatter.error("Config not initialized", undefined, { prefix: "Controller" });
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
await this.ensureDatabaseConfigBucketsExist(databases);
|
|
232
|
+
await ensureDatabasesExist(this.config, databases);
|
|
233
|
+
}
|
|
234
|
+
async ensureCollectionsExist(database, collections) {
|
|
235
|
+
await this.init();
|
|
236
|
+
if (!this.config) {
|
|
237
|
+
MessageFormatter.error("Config not initialized", undefined, { prefix: "Controller" });
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
await ensureCollectionsExist(this.config, database, collections);
|
|
241
|
+
}
|
|
242
|
+
async getDatabasesByIds(ids) {
|
|
243
|
+
await this.init();
|
|
244
|
+
if (!this.database) {
|
|
245
|
+
MessageFormatter.error("Database not initialized", undefined, { prefix: "Controller" });
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (ids.length === 0)
|
|
249
|
+
return [];
|
|
250
|
+
const dbs = await this.database.list([
|
|
251
|
+
Query.limit(500),
|
|
252
|
+
Query.equal("$id", ids),
|
|
253
|
+
]);
|
|
254
|
+
return dbs.databases;
|
|
255
|
+
}
|
|
256
|
+
async fetchAllBuckets() {
|
|
257
|
+
await this.init();
|
|
258
|
+
if (!this.storage) {
|
|
259
|
+
MessageFormatter.warning("Storage not initialized - buckets will be empty", { prefix: "Controller" });
|
|
260
|
+
return { buckets: [] };
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
const result = await this.storage.listBuckets([
|
|
264
|
+
Query.limit(1000) // Increase limit to get all buckets
|
|
265
|
+
]);
|
|
266
|
+
MessageFormatter.success(`Found ${result.buckets.length} buckets`, { prefix: "Controller" });
|
|
267
|
+
return result;
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
MessageFormatter.error(`Failed to fetch buckets: ${error.message || error}`, error instanceof Error ? error : undefined, { prefix: "Controller" });
|
|
271
|
+
return { buckets: [] };
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async wipeOtherDatabases(databasesToKeep) {
|
|
275
|
+
await this.init();
|
|
276
|
+
if (!this.database) {
|
|
277
|
+
MessageFormatter.error("Database not initialized", undefined, { prefix: "Controller" });
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
await wipeOtherDatabases(this.database, databasesToKeep);
|
|
281
|
+
}
|
|
282
|
+
async wipeUsers() {
|
|
283
|
+
await this.init();
|
|
284
|
+
if (!this.config || !this.database) {
|
|
285
|
+
MessageFormatter.error("Config or database not initialized", undefined, { prefix: "Controller" });
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const usersController = new UsersController(this.config, this.database);
|
|
289
|
+
await usersController.wipeUsers();
|
|
290
|
+
}
|
|
291
|
+
async backupDatabase(database, format = 'json') {
|
|
292
|
+
await this.init();
|
|
293
|
+
if (!this.database || !this.storage || !this.config) {
|
|
294
|
+
MessageFormatter.error("Database, storage, or config not initialized", undefined, { prefix: "Controller" });
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
await backupDatabase(this.config, this.database, database.$id, this.storage, format);
|
|
298
|
+
}
|
|
299
|
+
async listAllFunctions() {
|
|
300
|
+
await this.init();
|
|
301
|
+
if (!this.appwriteServer) {
|
|
302
|
+
MessageFormatter.error("Appwrite server not initialized", undefined, { prefix: "Controller" });
|
|
303
|
+
return [];
|
|
304
|
+
}
|
|
305
|
+
const { functions } = await listFunctions(this.appwriteServer, [
|
|
306
|
+
Query.limit(1000),
|
|
307
|
+
]);
|
|
308
|
+
return functions;
|
|
309
|
+
}
|
|
310
|
+
async findFunctionDirectories() {
|
|
311
|
+
if (!this.appwriteFolderPath) {
|
|
312
|
+
MessageFormatter.error("Failed to get appwriteFolderPath", undefined, { prefix: "Controller" });
|
|
313
|
+
return new Map();
|
|
314
|
+
}
|
|
315
|
+
const functionsDir = findFunctionsDir(this.appwriteFolderPath);
|
|
316
|
+
if (!functionsDir) {
|
|
317
|
+
MessageFormatter.error("Failed to find functions directory", undefined, { prefix: "Controller" });
|
|
318
|
+
return new Map();
|
|
319
|
+
}
|
|
320
|
+
const functionDirMap = new Map();
|
|
321
|
+
const entries = fs.readdirSync(functionsDir, { withFileTypes: true });
|
|
322
|
+
for (const entry of entries) {
|
|
323
|
+
if (entry.isDirectory()) {
|
|
324
|
+
const functionPath = path.join(functionsDir, entry.name);
|
|
325
|
+
// Validate it's a function directory
|
|
326
|
+
if (!validateFunctionDirectory(functionPath)) {
|
|
327
|
+
continue; // Skip invalid directories
|
|
328
|
+
}
|
|
329
|
+
// Match with config functions using normalized names
|
|
330
|
+
if (this.config?.functions) {
|
|
331
|
+
const normalizedEntryName = normalizeFunctionName(entry.name);
|
|
332
|
+
const matchingFunc = this.config.functions.find((f) => normalizeFunctionName(f.name) === normalizedEntryName);
|
|
333
|
+
if (matchingFunc) {
|
|
334
|
+
functionDirMap.set(matchingFunc.name, functionPath);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return functionDirMap;
|
|
340
|
+
}
|
|
341
|
+
async deployFunction(functionName, functionPath, functionConfig) {
|
|
342
|
+
await this.init();
|
|
343
|
+
if (!this.appwriteServer) {
|
|
344
|
+
MessageFormatter.error("Appwrite server not initialized", undefined, { prefix: "Controller" });
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (!functionConfig) {
|
|
348
|
+
functionConfig = this.config?.functions?.find((f) => f.name === functionName);
|
|
349
|
+
}
|
|
350
|
+
if (!functionConfig) {
|
|
351
|
+
MessageFormatter.error(`Function ${functionName} not found in config`, undefined, { prefix: "Controller" });
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
await deployLocalFunction(this.appwriteServer, functionName, functionConfig, functionPath);
|
|
355
|
+
}
|
|
356
|
+
async syncFunctions() {
|
|
357
|
+
await this.init();
|
|
358
|
+
if (!this.appwriteServer) {
|
|
359
|
+
MessageFormatter.error("Appwrite server not initialized", undefined, { prefix: "Controller" });
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const localFunctions = this.config?.functions || [];
|
|
363
|
+
const remoteFunctions = await listFunctions(this.appwriteServer, [
|
|
364
|
+
Query.limit(1000),
|
|
365
|
+
]);
|
|
366
|
+
for (const localFunction of localFunctions) {
|
|
367
|
+
MessageFormatter.progress(`Syncing function ${localFunction.name}...`, { prefix: "Functions" });
|
|
368
|
+
await this.deployFunction(localFunction.name);
|
|
369
|
+
}
|
|
370
|
+
MessageFormatter.success("All functions synchronized successfully!", { prefix: "Functions" });
|
|
371
|
+
}
|
|
372
|
+
async wipeDatabase(database, wipeBucket = false) {
|
|
373
|
+
await this.init();
|
|
374
|
+
if (!this.database || !this.config)
|
|
375
|
+
throw new Error("Database not initialized");
|
|
376
|
+
try {
|
|
377
|
+
// Session is already in config from ConfigManager
|
|
378
|
+
const { adapter, apiMode } = await getAdapterFromConfig(this.config, false);
|
|
379
|
+
if (apiMode === 'tablesdb') {
|
|
380
|
+
await wipeAllTables(adapter, database.$id);
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
await wipeDatabase(this.database, database.$id);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
catch {
|
|
387
|
+
await wipeDatabase(this.database, database.$id);
|
|
388
|
+
}
|
|
389
|
+
if (wipeBucket) {
|
|
390
|
+
await this.wipeBucketFromDatabase(database);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
async wipeBucketFromDatabase(database) {
|
|
394
|
+
// Check configured bucket in database config
|
|
395
|
+
const configuredBucket = this.config?.databases?.find((db) => db.$id === database.$id)?.bucket;
|
|
396
|
+
if (configuredBucket?.$id) {
|
|
397
|
+
await this.wipeDocumentStorage(configuredBucket.$id);
|
|
398
|
+
}
|
|
399
|
+
// Also check for document bucket ID pattern
|
|
400
|
+
if (this.config?.documentBucketId) {
|
|
401
|
+
const documentBucketId = `${this.config.documentBucketId}_${database.$id
|
|
402
|
+
.toLowerCase()
|
|
403
|
+
.trim()
|
|
404
|
+
.replace(/\s+/g, "")}`;
|
|
405
|
+
try {
|
|
406
|
+
await this.wipeDocumentStorage(documentBucketId);
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
// Ignore if bucket doesn't exist
|
|
410
|
+
if (error?.type !== "storage_bucket_not_found") {
|
|
411
|
+
throw error;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
async wipeCollection(database, collection) {
|
|
417
|
+
await this.init();
|
|
418
|
+
if (!this.database || !this.config)
|
|
419
|
+
throw new Error("Database not initialized");
|
|
420
|
+
try {
|
|
421
|
+
// Session is already in config from ConfigManager
|
|
422
|
+
const { adapter, apiMode } = await getAdapterFromConfig(this.config, false);
|
|
423
|
+
if (apiMode === 'tablesdb') {
|
|
424
|
+
await wipeTableRows(adapter, database.$id, collection.$id);
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
await wipeCollection(this.database, database.$id, collection.$id);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
await wipeCollection(this.database, database.$id, collection.$id);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
async wipeDocumentStorage(bucketId) {
|
|
435
|
+
await this.init();
|
|
436
|
+
if (!this.storage)
|
|
437
|
+
throw new Error("Storage not initialized");
|
|
438
|
+
await wipeDocumentStorage(this.storage, bucketId);
|
|
439
|
+
}
|
|
440
|
+
async createOrUpdateCollectionsForDatabases(databases, collections = []) {
|
|
441
|
+
await this.init();
|
|
442
|
+
if (!this.database || !this.config)
|
|
443
|
+
throw new Error("Database or config not initialized");
|
|
444
|
+
for (const database of databases) {
|
|
445
|
+
await this.createOrUpdateCollections(database, undefined, collections);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
async createOrUpdateCollections(database, deletedCollections, collections = []) {
|
|
449
|
+
await this.init();
|
|
450
|
+
if (!this.database || !this.config)
|
|
451
|
+
throw new Error("Database or config not initialized");
|
|
452
|
+
// Ensure apiMode is properly set from adapter
|
|
453
|
+
if (this.adapter && (!this.config.apiMode || this.config.apiMode === 'auto')) {
|
|
454
|
+
this.config.apiMode = this.adapter.getApiMode();
|
|
455
|
+
logger.debug(`Updated config.apiMode from adapter: ${this.config.apiMode}`, { prefix: "UtilsController" });
|
|
456
|
+
}
|
|
457
|
+
// Ensure we don't carry state between databases in a multi-db push
|
|
458
|
+
// This resets processed sets and name->id mapping per database
|
|
459
|
+
try {
|
|
460
|
+
const { clearProcessingState } = await import('./shared/operationQueue.js');
|
|
461
|
+
clearProcessingState();
|
|
462
|
+
}
|
|
463
|
+
catch { }
|
|
464
|
+
// Always prefer adapter path for unified behavior. LegacyAdapter internally translates when needed.
|
|
465
|
+
if (this.adapter) {
|
|
466
|
+
logger.debug("Using adapter for createOrUpdateCollections (unified path)", {
|
|
467
|
+
prefix: "UtilsController",
|
|
468
|
+
apiMode: this.adapter.getApiMode()
|
|
469
|
+
});
|
|
470
|
+
await createOrUpdateCollectionsViaAdapter(this.adapter, database.$id, this.config, deletedCollections, collections);
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
// Fallback if adapter is unavailable for some reason
|
|
474
|
+
logger.debug("Adapter unavailable, falling back to legacy Databases path", { prefix: "UtilsController" });
|
|
475
|
+
await createOrUpdateCollections(this.database, database.$id, this.config, deletedCollections, collections);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
async generateSchemas() {
|
|
479
|
+
// Schema generation doesn't need Appwrite connection, just config
|
|
480
|
+
if (!this.config) {
|
|
481
|
+
MessageFormatter.progress("Loading config from ConfigManager...", { prefix: "Config" });
|
|
482
|
+
try {
|
|
483
|
+
const configManager = ConfigManager.getInstance();
|
|
484
|
+
// Load config if not already loaded
|
|
485
|
+
if (!configManager.hasConfig()) {
|
|
486
|
+
await configManager.loadConfig({
|
|
487
|
+
configDir: this.currentUserDir,
|
|
488
|
+
validate: false,
|
|
489
|
+
strictMode: false,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
this.config = configManager.getConfig();
|
|
493
|
+
MessageFormatter.info("Config loaded successfully from ConfigManager", { prefix: "Config" });
|
|
494
|
+
}
|
|
495
|
+
catch (error) {
|
|
496
|
+
MessageFormatter.error("Failed to load config", error instanceof Error ? error : undefined, { prefix: "Config" });
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (!this.appwriteFolderPath) {
|
|
501
|
+
MessageFormatter.error("Failed to get appwriteFolderPath", undefined, { prefix: "Controller" });
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
await generateSchemas(this.config, this.appwriteFolderPath);
|
|
505
|
+
}
|
|
506
|
+
async importData(options = {}) {
|
|
507
|
+
await this.init();
|
|
508
|
+
if (!this.database) {
|
|
509
|
+
MessageFormatter.error("Database not initialized", undefined, { prefix: "Controller" });
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
if (!this.storage) {
|
|
513
|
+
MessageFormatter.error("Storage not initialized", undefined, { prefix: "Controller" });
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
if (!this.config) {
|
|
517
|
+
MessageFormatter.error("Config not initialized", undefined, { prefix: "Controller" });
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
if (!this.appwriteFolderPath) {
|
|
521
|
+
MessageFormatter.error("Failed to get appwriteFolderPath", undefined, { prefix: "Controller" });
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
const importDataActions = new ImportDataActions(this.database, this.storage, this.config, this.converterDefinitions, this.validityRuleDefinitions, this.afterImportActionsDefinitions);
|
|
525
|
+
const importController = new ImportController(this.config, this.database, this.storage, this.appwriteFolderPath, importDataActions, options, options.databases);
|
|
526
|
+
await importController.run(options.collections);
|
|
527
|
+
}
|
|
528
|
+
async synchronizeConfigurations(databases, config, databaseSelections, bucketSelections) {
|
|
529
|
+
await this.init();
|
|
530
|
+
if (!this.storage) {
|
|
531
|
+
MessageFormatter.error("Storage not initialized", undefined, { prefix: "Controller" });
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const configToUse = config || this.config;
|
|
535
|
+
if (!configToUse) {
|
|
536
|
+
MessageFormatter.error("Config not initialized", undefined, { prefix: "Controller" });
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
if (!this.appwriteFolderPath) {
|
|
540
|
+
MessageFormatter.error("Failed to get appwriteFolderPath", undefined, { prefix: "Controller" });
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
// If selections are provided, filter the databases accordingly
|
|
544
|
+
let filteredDatabases = databases;
|
|
545
|
+
if (databaseSelections && databaseSelections.length > 0) {
|
|
546
|
+
// Convert selections to Models.Database format
|
|
547
|
+
filteredDatabases = [];
|
|
548
|
+
const allDatabases = databases ? databases : await fetchAllDatabases(this.database);
|
|
549
|
+
for (const selection of databaseSelections) {
|
|
550
|
+
const database = allDatabases.find(db => db.$id === selection.databaseId);
|
|
551
|
+
if (database) {
|
|
552
|
+
filteredDatabases.push(database);
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
MessageFormatter.warning(`Database with ID ${selection.databaseId} not found`, { prefix: "Controller" });
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
MessageFormatter.info(`Syncing ${filteredDatabases.length} selected databases out of ${allDatabases.length} available`, { prefix: "Controller" });
|
|
559
|
+
}
|
|
560
|
+
const appwriteToX = new AppwriteToX(configToUse, this.appwriteFolderPath, this.storage);
|
|
561
|
+
await appwriteToX.toSchemas(filteredDatabases);
|
|
562
|
+
// Update the controller's config with the synchronized collections
|
|
563
|
+
this.config = appwriteToX.updatedConfig;
|
|
564
|
+
// Write the updated config back to disk
|
|
565
|
+
const generator = new SchemaGenerator(this.config, this.appwriteFolderPath);
|
|
566
|
+
const yamlConfigPath = findYamlConfig(this.appwriteFolderPath);
|
|
567
|
+
const isYamlProject = !!yamlConfigPath;
|
|
568
|
+
await generator.updateConfig(this.config, isYamlProject);
|
|
569
|
+
// Regenerate JSON schemas to reflect any table terminology fixes
|
|
570
|
+
try {
|
|
571
|
+
MessageFormatter.progress("Regenerating JSON schemas...", { prefix: "Sync" });
|
|
572
|
+
await createImportSchemas(this.appwriteFolderPath);
|
|
573
|
+
MessageFormatter.success("JSON schemas regenerated successfully", { prefix: "Sync" });
|
|
574
|
+
}
|
|
575
|
+
catch (error) {
|
|
576
|
+
// Log error but don't fail the sync process
|
|
577
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
578
|
+
MessageFormatter.warning(`Failed to regenerate JSON schemas, but sync completed: ${errorMessage}`, { prefix: "Sync" });
|
|
579
|
+
logger.warn("Schema regeneration failed during sync:", error);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
async selectivePull(databaseSelections, bucketSelections) {
|
|
583
|
+
await this.init();
|
|
584
|
+
if (!this.database) {
|
|
585
|
+
MessageFormatter.error("Database not initialized", undefined, { prefix: "Controller" });
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
MessageFormatter.progress("Starting selective pull (Appwrite → local config)...", { prefix: "Controller" });
|
|
589
|
+
// Convert database selections to Models.Database format
|
|
590
|
+
const selectedDatabases = [];
|
|
591
|
+
for (const dbSelection of databaseSelections) {
|
|
592
|
+
// Get the full database object from the controller
|
|
593
|
+
const databases = await fetchAllDatabases(this.database);
|
|
594
|
+
const database = databases.find(db => db.$id === dbSelection.databaseId);
|
|
595
|
+
if (database) {
|
|
596
|
+
selectedDatabases.push(database);
|
|
597
|
+
MessageFormatter.info(`Selected database: ${database.name} (${database.$id})`, { prefix: "Controller" });
|
|
598
|
+
// Log selected tables for this database
|
|
599
|
+
if (dbSelection.tableIds && dbSelection.tableIds.length > 0) {
|
|
600
|
+
MessageFormatter.info(` Tables: ${dbSelection.tableIds.join(', ')}`, { prefix: "Controller" });
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
MessageFormatter.warning(`Database with ID ${dbSelection.databaseId} not found`, { prefix: "Controller" });
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (selectedDatabases.length === 0) {
|
|
608
|
+
MessageFormatter.warning("No valid databases selected for pull", { prefix: "Controller" });
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
// Log bucket selections if provided
|
|
612
|
+
if (bucketSelections && bucketSelections.length > 0) {
|
|
613
|
+
MessageFormatter.info(`Selected ${bucketSelections.length} buckets:`, { prefix: "Controller" });
|
|
614
|
+
for (const bucketSelection of bucketSelections) {
|
|
615
|
+
const dbInfo = bucketSelection.databaseId ? ` (DB: ${bucketSelection.databaseId})` : '';
|
|
616
|
+
MessageFormatter.info(` - ${bucketSelection.bucketName} (${bucketSelection.bucketId})${dbInfo}`, { prefix: "Controller" });
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
// Perform selective sync using the enhanced synchronizeConfigurations method
|
|
620
|
+
await this.synchronizeConfigurations(selectedDatabases, this.config, databaseSelections, bucketSelections);
|
|
621
|
+
MessageFormatter.success("Selective pull completed successfully! Remote config pulled to local.", { prefix: "Controller" });
|
|
622
|
+
}
|
|
623
|
+
async selectivePush(databaseSelections, bucketSelections) {
|
|
624
|
+
await this.init();
|
|
625
|
+
if (!this.database) {
|
|
626
|
+
MessageFormatter.error("Database not initialized", undefined, { prefix: "Controller" });
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
// Always reload config from disk so pushes use current local YAML/Ts definitions
|
|
630
|
+
try {
|
|
631
|
+
await this.reloadConfig();
|
|
632
|
+
MessageFormatter.info("Reloaded config from disk for push", { prefix: "Controller" });
|
|
633
|
+
}
|
|
634
|
+
catch (e) {
|
|
635
|
+
// Non-fatal; continue with existing config
|
|
636
|
+
MessageFormatter.warning("Could not reload config; continuing with current in-memory config", { prefix: "Controller" });
|
|
637
|
+
}
|
|
638
|
+
MessageFormatter.progress("Starting selective push (local config → Appwrite)...", { prefix: "Controller" });
|
|
639
|
+
// Convert database selections to Models.Database format
|
|
640
|
+
const selectedDatabases = [];
|
|
641
|
+
for (const dbSelection of databaseSelections) {
|
|
642
|
+
// Get the full database object from the controller
|
|
643
|
+
const databases = await fetchAllDatabases(this.database);
|
|
644
|
+
const database = databases.find(db => db.$id === dbSelection.databaseId);
|
|
645
|
+
if (database) {
|
|
646
|
+
selectedDatabases.push(database);
|
|
647
|
+
MessageFormatter.info(`Selected database: ${database.name} (${database.$id})`, { prefix: "Controller" });
|
|
648
|
+
// Log selected tables for this database
|
|
649
|
+
if (dbSelection.tableIds && dbSelection.tableIds.length > 0) {
|
|
650
|
+
MessageFormatter.info(` Tables: ${dbSelection.tableIds.join(', ')}`, { prefix: "Controller" });
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
MessageFormatter.warning(`Database with ID ${dbSelection.databaseId} not found`, { prefix: "Controller" });
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
if (selectedDatabases.length === 0) {
|
|
658
|
+
MessageFormatter.warning("No valid databases selected for push", { prefix: "Controller" });
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
// Log bucket selections if provided
|
|
662
|
+
if (bucketSelections && bucketSelections.length > 0) {
|
|
663
|
+
MessageFormatter.info(`Selected ${bucketSelections.length} buckets:`, { prefix: "Controller" });
|
|
664
|
+
for (const bucketSelection of bucketSelections) {
|
|
665
|
+
const dbInfo = bucketSelection.databaseId ? ` (DB: ${bucketSelection.databaseId})` : '';
|
|
666
|
+
MessageFormatter.info(` - ${bucketSelection.bucketName} (${bucketSelection.bucketId})${dbInfo}`, { prefix: "Controller" });
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
// PUSH OPERATION: Push local configuration to Appwrite
|
|
670
|
+
// Build database-specific collection mappings from databaseSelections
|
|
671
|
+
const databaseCollectionsMap = new Map();
|
|
672
|
+
// Get all collections/tables from config (they're at the root level, not nested in databases)
|
|
673
|
+
const allCollections = this.config?.collections || this.config?.tables || [];
|
|
674
|
+
// Create database-specific collection mapping to preserve relationships
|
|
675
|
+
for (const dbSelection of databaseSelections) {
|
|
676
|
+
const collectionsForDatabase = [];
|
|
677
|
+
MessageFormatter.info(`Processing collections for database: ${dbSelection.databaseId}`, { prefix: "Controller" });
|
|
678
|
+
// Filter collections that were selected for THIS specific database
|
|
679
|
+
for (const collection of allCollections) {
|
|
680
|
+
const collectionId = collection.$id || collection.id;
|
|
681
|
+
// Check if this collection was selected for THIS database
|
|
682
|
+
if (dbSelection.tableIds.includes(collectionId)) {
|
|
683
|
+
collectionsForDatabase.push(collection);
|
|
684
|
+
const source = collection._isFromTablesDir ? 'tables/' : 'collections/';
|
|
685
|
+
MessageFormatter.info(` - Selected collection: ${collection.name || collectionId} for database ${dbSelection.databaseId} [source: ${source}]`, { prefix: "Controller" });
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
databaseCollectionsMap.set(dbSelection.databaseId, collectionsForDatabase);
|
|
689
|
+
MessageFormatter.info(`Database ${dbSelection.databaseId}: ${collectionsForDatabase.length} collections selected`, { prefix: "Controller" });
|
|
690
|
+
}
|
|
691
|
+
// Calculate total collections for logging
|
|
692
|
+
const totalSelectedCollections = Array.from(databaseCollectionsMap.values())
|
|
693
|
+
.reduce((total, collections) => total + collections.length, 0);
|
|
694
|
+
MessageFormatter.info(`Pushing ${totalSelectedCollections} selected tables/collections to ${databaseCollectionsMap.size} databases`, { prefix: "Controller" });
|
|
695
|
+
// Ensure databases exist
|
|
696
|
+
await this.ensureDatabasesExist(selectedDatabases);
|
|
697
|
+
await this.ensureDatabaseConfigBucketsExist(selectedDatabases);
|
|
698
|
+
// Create/update collections with database-specific context
|
|
699
|
+
for (const database of selectedDatabases) {
|
|
700
|
+
const collectionsForThisDatabase = databaseCollectionsMap.get(database.$id) || [];
|
|
701
|
+
if (collectionsForThisDatabase.length > 0) {
|
|
702
|
+
MessageFormatter.info(`Pushing ${collectionsForThisDatabase.length} collections to database ${database.$id} (${database.name})`, { prefix: "Controller" });
|
|
703
|
+
await this.createOrUpdateCollections(database, undefined, collectionsForThisDatabase);
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
MessageFormatter.info(`No collections selected for database ${database.$id} (${database.name})`, { prefix: "Controller" });
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
MessageFormatter.success("Selective push completed successfully! Local config pushed to Appwrite.", { prefix: "Controller" });
|
|
710
|
+
}
|
|
711
|
+
async syncDb(databases = [], collections = []) {
|
|
712
|
+
await this.init();
|
|
713
|
+
if (!this.database) {
|
|
714
|
+
MessageFormatter.error("Database not initialized", undefined, { prefix: "Controller" });
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
if (databases.length === 0) {
|
|
718
|
+
const allDatabases = await fetchAllDatabases(this.database);
|
|
719
|
+
databases = allDatabases;
|
|
720
|
+
}
|
|
721
|
+
// Ensure DBs exist
|
|
722
|
+
await this.ensureDatabasesExist(databases);
|
|
723
|
+
await this.ensureDatabaseConfigBucketsExist(databases);
|
|
724
|
+
await this.createOrUpdateCollectionsForDatabases(databases, collections);
|
|
725
|
+
}
|
|
726
|
+
getAppwriteFolderPath() {
|
|
727
|
+
return this.appwriteFolderPath;
|
|
728
|
+
}
|
|
729
|
+
async transferData(options) {
|
|
730
|
+
let sourceClient = this.database;
|
|
731
|
+
let targetClient;
|
|
732
|
+
let sourceDatabases = [];
|
|
733
|
+
let targetDatabases = [];
|
|
734
|
+
if (!sourceClient) {
|
|
735
|
+
MessageFormatter.error("Source database not initialized", undefined, { prefix: "Controller" });
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (options.isRemote) {
|
|
739
|
+
if (!options.transferEndpoint ||
|
|
740
|
+
!options.transferProject ||
|
|
741
|
+
!options.transferKey) {
|
|
742
|
+
MessageFormatter.error("Remote transfer options are missing", undefined, { prefix: "Controller" });
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
const remoteClient = getClient(options.transferEndpoint, options.transferProject, options.transferKey);
|
|
746
|
+
targetClient = new Databases(remoteClient);
|
|
747
|
+
sourceDatabases = await fetchAllDatabases(sourceClient);
|
|
748
|
+
targetDatabases = await fetchAllDatabases(targetClient);
|
|
749
|
+
}
|
|
750
|
+
else {
|
|
751
|
+
targetClient = sourceClient;
|
|
752
|
+
sourceDatabases = targetDatabases = await fetchAllDatabases(sourceClient);
|
|
753
|
+
}
|
|
754
|
+
// Always perform database transfer if databases are specified
|
|
755
|
+
if (options.fromDb && options.targetDb) {
|
|
756
|
+
const fromDb = sourceDatabases.find((db) => db.$id === options.fromDb.$id);
|
|
757
|
+
const targetDb = targetDatabases.find((db) => db.$id === options.targetDb.$id);
|
|
758
|
+
if (!fromDb || !targetDb) {
|
|
759
|
+
MessageFormatter.error("Source or target database not found", undefined, { prefix: "Controller" });
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
if (options.isRemote && targetClient) {
|
|
763
|
+
await transferDatabaseLocalToRemote(sourceClient, options.transferEndpoint, options.transferProject, options.transferKey, fromDb.$id, targetDb.$id);
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
await transferDatabaseLocalToLocal(sourceClient, fromDb.$id, targetDb.$id);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
if (options.transferUsers) {
|
|
770
|
+
if (!options.isRemote) {
|
|
771
|
+
MessageFormatter.warning("User transfer is only supported for remote transfers. Skipping...", { prefix: "Controller" });
|
|
772
|
+
}
|
|
773
|
+
else if (!this.appwriteServer) {
|
|
774
|
+
MessageFormatter.error("Appwrite server not initialized", undefined, { prefix: "Controller" });
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
MessageFormatter.progress("Starting user transfer...", { prefix: "Transfer" });
|
|
779
|
+
const localUsers = new Users(this.appwriteServer);
|
|
780
|
+
await transferUsersLocalToRemote(localUsers, options.transferEndpoint, options.transferProject, options.transferKey);
|
|
781
|
+
MessageFormatter.success("User transfer completed", { prefix: "Transfer" });
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
// Handle storage transfer
|
|
785
|
+
if (this.storage && (options.sourceBucket || options.fromDb)) {
|
|
786
|
+
const sourceBucketId = options.sourceBucket?.$id ||
|
|
787
|
+
(options.fromDb &&
|
|
788
|
+
this.config?.documentBucketId &&
|
|
789
|
+
`${this.config.documentBucketId}_${options.fromDb.$id
|
|
790
|
+
.toLowerCase()
|
|
791
|
+
.trim()
|
|
792
|
+
.replace(/\s+/g, "")}`);
|
|
793
|
+
const targetBucketId = options.targetBucket?.$id ||
|
|
794
|
+
(options.targetDb &&
|
|
795
|
+
this.config?.documentBucketId &&
|
|
796
|
+
`${this.config.documentBucketId}_${options.targetDb.$id
|
|
797
|
+
.toLowerCase()
|
|
798
|
+
.trim()
|
|
799
|
+
.replace(/\s+/g, "")}`);
|
|
800
|
+
if (sourceBucketId && targetBucketId) {
|
|
801
|
+
MessageFormatter.progress(`Starting storage transfer from ${sourceBucketId} to ${targetBucketId}`, { prefix: "Transfer" });
|
|
802
|
+
if (options.isRemote) {
|
|
803
|
+
await transferStorageLocalToRemote(this.storage, options.transferEndpoint, options.transferProject, options.transferKey, sourceBucketId, targetBucketId);
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
await transferStorageLocalToLocal(this.storage, sourceBucketId, targetBucketId);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
MessageFormatter.success("Transfer completed", { prefix: "Transfer" });
|
|
811
|
+
}
|
|
812
|
+
async updateFunctionSpecifications(functionId, specification) {
|
|
813
|
+
await this.init();
|
|
814
|
+
if (!this.appwriteServer)
|
|
815
|
+
throw new Error("Appwrite server not initialized");
|
|
816
|
+
MessageFormatter.progress(`Updating function specifications for ${functionId} to ${specification}`, { prefix: "Functions" });
|
|
817
|
+
await updateFunctionSpecifications(this.appwriteServer, functionId, specification);
|
|
818
|
+
MessageFormatter.success(`Successfully updated function specifications for ${functionId} to ${specification}`, { prefix: "Functions" });
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Validates the current configuration for collections/tables conflicts
|
|
822
|
+
*/
|
|
823
|
+
async validateConfiguration(strictMode = false) {
|
|
824
|
+
await this.init();
|
|
825
|
+
if (!this.config) {
|
|
826
|
+
throw new Error("Configuration not loaded");
|
|
827
|
+
}
|
|
828
|
+
MessageFormatter.progress("Validating configuration...", { prefix: "Validation" });
|
|
829
|
+
const validation = strictMode
|
|
830
|
+
? validateWithStrictMode(this.config, strictMode)
|
|
831
|
+
: validateCollectionsTablesConfig(this.config);
|
|
832
|
+
reportValidationResults(validation, { verbose: true });
|
|
833
|
+
if (validation.isValid) {
|
|
834
|
+
MessageFormatter.success("Configuration validation passed", { prefix: "Validation" });
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
MessageFormatter.error(`Configuration validation failed with ${validation.errors.length} errors`, undefined, { prefix: "Validation" });
|
|
838
|
+
}
|
|
839
|
+
return validation;
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Get current session information for debugging/logging purposes
|
|
843
|
+
* Delegates to ConfigManager for session info
|
|
844
|
+
*/
|
|
845
|
+
async getSessionInfo() {
|
|
846
|
+
const configManager = ConfigManager.getInstance();
|
|
847
|
+
try {
|
|
848
|
+
const authStatus = await configManager.getAuthStatus();
|
|
849
|
+
return {
|
|
850
|
+
hasSession: authStatus.hasValidSession,
|
|
851
|
+
authMethod: authStatus.authMethod,
|
|
852
|
+
email: authStatus.sessionInfo?.email,
|
|
853
|
+
expiresAt: authStatus.sessionInfo?.expiresAt
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
catch (error) {
|
|
857
|
+
// If config not loaded, return empty status
|
|
858
|
+
return {
|
|
859
|
+
hasSession: false
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|