appwrite-utils-cli 1.9.7 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (376) hide show
  1. package/README.md +1004 -1004
  2. package/dist/adapters/index.d.ts +7 -8
  3. package/dist/adapters/index.js +7 -9
  4. package/dist/backups/operations/bucketBackup.js +2 -2
  5. package/dist/backups/operations/collectionBackup.d.ts +1 -1
  6. package/dist/backups/operations/collectionBackup.js +3 -3
  7. package/dist/backups/operations/comprehensiveBackup.d.ts +1 -1
  8. package/dist/backups/operations/comprehensiveBackup.js +2 -2
  9. package/dist/backups/tracking/centralizedTracking.d.ts +1 -1
  10. package/dist/backups/tracking/centralizedTracking.js +2 -2
  11. package/dist/cli/commands/configCommands.js +51 -7
  12. package/dist/cli/commands/databaseCommands.js +156 -104
  13. package/dist/cli/commands/functionCommands.js +3 -3
  14. package/dist/cli/commands/importFileCommands.d.ts +7 -0
  15. package/dist/cli/commands/importFileCommands.js +674 -0
  16. package/dist/cli/commands/schemaCommands.js +3 -3
  17. package/dist/cli/commands/storageCommands.js +2 -3
  18. package/dist/cli/commands/transferCommands.js +3 -5
  19. package/dist/collections/{attributes.d.ts → columns.d.ts} +1 -1
  20. package/dist/collections/{attributes.js → columns.js} +15 -9
  21. package/dist/collections/indexes.js +1 -3
  22. package/dist/collections/methods.d.ts +1 -1
  23. package/dist/collections/methods.js +38 -20
  24. package/dist/collections/tableOperations.d.ts +1 -0
  25. package/dist/collections/tableOperations.js +30 -11
  26. package/dist/collections/transferOperations.d.ts +1 -1
  27. package/dist/collections/transferOperations.js +3 -4
  28. package/dist/collections/wipeOperations.d.ts +4 -3
  29. package/dist/collections/wipeOperations.js +112 -39
  30. package/dist/databases/methods.js +2 -2
  31. package/dist/databases/setup.js +2 -2
  32. package/dist/examples/yamlTerminologyExample.js +2 -2
  33. package/dist/functions/deployments.d.ts +1 -1
  34. package/dist/functions/deployments.js +6 -6
  35. package/dist/functions/fnConfigDiscovery.js +2 -2
  36. package/dist/functions/methods.js +2 -2
  37. package/dist/functions/templates/count-docs-in-collection/README.md +53 -53
  38. package/dist/functions/templates/count-docs-in-collection/src/main.ts +159 -159
  39. package/dist/functions/templates/count-docs-in-collection/src/request.ts +8 -8
  40. package/dist/functions/templates/hono-typescript/README.md +285 -285
  41. package/dist/functions/templates/hono-typescript/src/adapters/request.ts +73 -73
  42. package/dist/functions/templates/hono-typescript/src/adapters/response.ts +105 -105
  43. package/dist/functions/templates/hono-typescript/src/app.ts +179 -179
  44. package/dist/functions/templates/hono-typescript/src/context.ts +102 -102
  45. package/{src/functions/templates/hono-typescript/src/index.ts → dist/functions/templates/hono-typescript/src/main.ts} +53 -53
  46. package/dist/functions/templates/hono-typescript/src/middleware/appwrite.ts +118 -118
  47. package/dist/functions/templates/typescript-node/README.md +31 -31
  48. package/dist/functions/templates/typescript-node/src/context.ts +102 -102
  49. package/dist/functions/templates/typescript-node/src/{index.ts → main.ts} +29 -29
  50. package/dist/functions/templates/uv/README.md +30 -30
  51. package/dist/functions/templates/uv/pyproject.toml +29 -29
  52. package/dist/functions/templates/uv/src/context.py +124 -124
  53. package/dist/functions/templates/uv/src/{index.py → main.py} +45 -45
  54. package/dist/init.js +1 -1
  55. package/dist/interactiveCLI.d.ts +6 -1
  56. package/dist/interactiveCLI.js +79 -25
  57. package/dist/main.js +125 -180
  58. package/dist/migrations/afterImportActions.js +2 -3
  59. package/dist/migrations/appwriteToX.d.ts +1 -1
  60. package/dist/migrations/appwriteToX.js +10 -8
  61. package/dist/migrations/comprehensiveTransfer.js +3 -5
  62. package/dist/migrations/dataLoader.d.ts +4 -2
  63. package/dist/migrations/dataLoader.js +42 -20
  64. package/dist/migrations/importController.d.ts +2 -0
  65. package/dist/migrations/importController.js +39 -24
  66. package/dist/migrations/importDataActions.js +3 -3
  67. package/dist/migrations/relationships.js +1 -2
  68. package/dist/migrations/services/DataTransformationService.js +2 -2
  69. package/dist/migrations/services/FileHandlerService.js +1 -1
  70. package/dist/migrations/services/ImportOrchestrator.d.ts +2 -0
  71. package/dist/migrations/services/ImportOrchestrator.js +15 -7
  72. package/dist/migrations/services/RateLimitManager.js +1 -1
  73. package/dist/migrations/services/RelationshipResolver.js +1 -1
  74. package/dist/migrations/services/UserMappingService.js +1 -1
  75. package/dist/migrations/services/ValidationService.js +1 -1
  76. package/dist/migrations/transfer.d.ts +8 -4
  77. package/dist/migrations/transfer.js +108 -55
  78. package/dist/migrations/yaml/YamlImportConfigLoader.js +52 -52
  79. package/dist/migrations/yaml/YamlImportIntegration.js +2 -2
  80. package/dist/migrations/yaml/generateImportSchemas.js +174 -174
  81. package/dist/setupCommands.d.ts +1 -1
  82. package/dist/setupCommands.js +5 -6
  83. package/dist/setupController.js +1 -1
  84. package/dist/shared/backupTracking.d.ts +1 -1
  85. package/dist/shared/backupTracking.js +2 -2
  86. package/dist/shared/confirmationDialogs.js +1 -1
  87. package/dist/shared/migrationHelpers.d.ts +1 -1
  88. package/dist/shared/migrationHelpers.js +4 -4
  89. package/dist/shared/operationQueue.d.ts +1 -1
  90. package/dist/shared/operationQueue.js +3 -4
  91. package/dist/shared/operationsTable.d.ts +1 -1
  92. package/dist/shared/operationsTable.js +35 -34
  93. package/dist/shared/operationsTableSchema.d.ts +3 -3
  94. package/dist/shared/operationsTableSchema.js +2 -2
  95. package/dist/shared/progressManager.js +1 -1
  96. package/dist/shared/selectionDialogs.d.ts +6 -0
  97. package/dist/shared/selectionDialogs.js +56 -12
  98. package/dist/storage/methods.d.ts +12 -0
  99. package/dist/storage/methods.js +92 -89
  100. package/dist/storage/schemas.d.ts +2 -2
  101. package/dist/tables/indexManager.d.ts +1 -1
  102. package/dist/tables/indexManager.js +2 -2
  103. package/dist/types.d.ts +2 -2
  104. package/dist/types.js +1 -1
  105. package/dist/users/methods.js +2 -3
  106. package/dist/utils/configMigration.js +1 -1
  107. package/dist/utils/index.d.ts +1 -1
  108. package/dist/utils/index.js +1 -1
  109. package/dist/utils/loadConfigs.d.ts +2 -2
  110. package/dist/utils/loadConfigs.js +6 -7
  111. package/dist/utils/setupFiles.js +139 -141
  112. package/dist/utilsController.d.ts +16 -9
  113. package/dist/utilsController.js +93 -68
  114. package/package.json +9 -3
  115. package/.appwrite/.yaml_schemas/appwrite-config.schema.json +0 -380
  116. package/.appwrite/.yaml_schemas/collection.schema.json +0 -255
  117. package/.appwrite/collections/Categories.yaml +0 -182
  118. package/.appwrite/collections/ExampleCollection.yaml +0 -36
  119. package/.appwrite/collections/Posts.yaml +0 -227
  120. package/.appwrite/collections/Users.yaml +0 -149
  121. package/.appwrite/config.yaml +0 -109
  122. package/.appwrite/import/README.md +0 -148
  123. package/.appwrite/import/categories-import.yaml +0 -129
  124. package/.appwrite/import/posts-import.yaml +0 -208
  125. package/.appwrite/import/users-import.yaml +0 -130
  126. package/.appwrite/importData/categories.json +0 -194
  127. package/.appwrite/importData/posts.json +0 -270
  128. package/.appwrite/importData/users.json +0 -220
  129. package/.appwrite/schemas/categories.json +0 -128
  130. package/.appwrite/schemas/exampleCollection.json +0 -52
  131. package/.appwrite/schemas/posts.json +0 -173
  132. package/.appwrite/schemas/users.json +0 -125
  133. package/CHANGELOG.md +0 -35
  134. package/CONFIG_TODO.md +0 -1189
  135. package/SELECTION_DIALOGS.md +0 -146
  136. package/SERVICE_IMPLEMENTATION_REPORT.md +0 -462
  137. package/dist/adapters/AdapterFactory.d.ts +0 -94
  138. package/dist/adapters/AdapterFactory.js +0 -420
  139. package/dist/adapters/DatabaseAdapter.d.ts +0 -243
  140. package/dist/adapters/DatabaseAdapter.js +0 -50
  141. package/dist/adapters/LegacyAdapter.d.ts +0 -50
  142. package/dist/adapters/LegacyAdapter.js +0 -615
  143. package/dist/adapters/TablesDBAdapter.d.ts +0 -45
  144. package/dist/adapters/TablesDBAdapter.js +0 -611
  145. package/dist/config/ConfigManager.d.ts +0 -450
  146. package/dist/config/ConfigManager.js +0 -650
  147. package/dist/config/configMigration.d.ts +0 -87
  148. package/dist/config/configMigration.js +0 -390
  149. package/dist/config/configValidation.d.ts +0 -66
  150. package/dist/config/configValidation.js +0 -358
  151. package/dist/config/index.d.ts +0 -8
  152. package/dist/config/index.js +0 -7
  153. package/dist/config/services/ConfigDiscoveryService.d.ts +0 -122
  154. package/dist/config/services/ConfigDiscoveryService.js +0 -322
  155. package/dist/config/services/ConfigLoaderService.d.ts +0 -129
  156. package/dist/config/services/ConfigLoaderService.js +0 -535
  157. package/dist/config/services/ConfigMergeService.d.ts +0 -208
  158. package/dist/config/services/ConfigMergeService.js +0 -308
  159. package/dist/config/services/ConfigValidationService.d.ts +0 -214
  160. package/dist/config/services/ConfigValidationService.js +0 -310
  161. package/dist/config/services/SessionAuthService.d.ts +0 -225
  162. package/dist/config/services/SessionAuthService.js +0 -456
  163. package/dist/config/services/__tests__/ConfigMergeService.test.d.ts +0 -1
  164. package/dist/config/services/__tests__/ConfigMergeService.test.js +0 -271
  165. package/dist/config/services/index.d.ts +0 -13
  166. package/dist/config/services/index.js +0 -10
  167. package/dist/config/yamlConfig.d.ts +0 -722
  168. package/dist/config/yamlConfig.js +0 -702
  169. package/dist/functions/pathResolution.d.ts +0 -37
  170. package/dist/functions/pathResolution.js +0 -185
  171. package/dist/functions/templates/count-docs-in-collection/package.json +0 -25
  172. package/dist/functions/templates/count-docs-in-collection/tsconfig.json +0 -28
  173. package/dist/functions/templates/hono-typescript/package.json +0 -26
  174. package/dist/functions/templates/hono-typescript/src/index.ts +0 -54
  175. package/dist/functions/templates/hono-typescript/tsconfig.json +0 -20
  176. package/dist/functions/templates/typescript-node/package.json +0 -25
  177. package/dist/functions/templates/typescript-node/tsconfig.json +0 -28
  178. package/dist/shared/attributeMapper.d.ts +0 -20
  179. package/dist/shared/attributeMapper.js +0 -203
  180. package/dist/shared/errorUtils.d.ts +0 -54
  181. package/dist/shared/errorUtils.js +0 -95
  182. package/dist/shared/functionManager.d.ts +0 -48
  183. package/dist/shared/functionManager.js +0 -348
  184. package/dist/shared/jsonSchemaGenerator.d.ts +0 -50
  185. package/dist/shared/jsonSchemaGenerator.js +0 -290
  186. package/dist/shared/logging.d.ts +0 -61
  187. package/dist/shared/logging.js +0 -116
  188. package/dist/shared/messageFormatter.d.ts +0 -39
  189. package/dist/shared/messageFormatter.js +0 -162
  190. package/dist/shared/pydanticModelGenerator.d.ts +0 -17
  191. package/dist/shared/pydanticModelGenerator.js +0 -615
  192. package/dist/shared/schemaGenerator.d.ts +0 -40
  193. package/dist/shared/schemaGenerator.js +0 -556
  194. package/dist/utils/ClientFactory.d.ts +0 -87
  195. package/dist/utils/ClientFactory.js +0 -212
  196. package/dist/utils/configDiscovery.d.ts +0 -78
  197. package/dist/utils/configDiscovery.js +0 -472
  198. package/dist/utils/constantsGenerator.d.ts +0 -31
  199. package/dist/utils/constantsGenerator.js +0 -321
  200. package/dist/utils/dataConverters.d.ts +0 -46
  201. package/dist/utils/dataConverters.js +0 -139
  202. package/dist/utils/directoryUtils.d.ts +0 -22
  203. package/dist/utils/directoryUtils.js +0 -59
  204. package/dist/utils/getClientFromConfig.d.ts +0 -39
  205. package/dist/utils/getClientFromConfig.js +0 -199
  206. package/dist/utils/helperFunctions.d.ts +0 -63
  207. package/dist/utils/helperFunctions.js +0 -156
  208. package/dist/utils/pathResolvers.d.ts +0 -53
  209. package/dist/utils/pathResolvers.js +0 -72
  210. package/dist/utils/projectConfig.d.ts +0 -122
  211. package/dist/utils/projectConfig.js +0 -206
  212. package/dist/utils/retryFailedPromises.d.ts +0 -2
  213. package/dist/utils/retryFailedPromises.js +0 -23
  214. package/dist/utils/sessionAuth.d.ts +0 -48
  215. package/dist/utils/sessionAuth.js +0 -164
  216. package/dist/utils/typeGuards.d.ts +0 -35
  217. package/dist/utils/typeGuards.js +0 -57
  218. package/dist/utils/validationRules.d.ts +0 -43
  219. package/dist/utils/validationRules.js +0 -42
  220. package/dist/utils/versionDetection.d.ts +0 -58
  221. package/dist/utils/versionDetection.js +0 -251
  222. package/dist/utils/yamlConverter.d.ts +0 -100
  223. package/dist/utils/yamlConverter.js +0 -428
  224. package/dist/utils/yamlLoader.d.ts +0 -70
  225. package/dist/utils/yamlLoader.js +0 -267
  226. package/scripts/copy-templates.ts +0 -23
  227. package/src/adapters/AdapterFactory.ts +0 -529
  228. package/src/adapters/DatabaseAdapter.ts +0 -319
  229. package/src/adapters/LegacyAdapter.ts +0 -844
  230. package/src/adapters/TablesDBAdapter.ts +0 -823
  231. package/src/adapters/index.ts +0 -37
  232. package/src/backups/operations/bucketBackup.ts +0 -277
  233. package/src/backups/operations/collectionBackup.ts +0 -310
  234. package/src/backups/operations/comprehensiveBackup.ts +0 -342
  235. package/src/backups/schemas/bucketManifest.ts +0 -78
  236. package/src/backups/schemas/comprehensiveManifest.ts +0 -76
  237. package/src/backups/tracking/centralizedTracking.ts +0 -352
  238. package/src/cli/commands/configCommands.ts +0 -201
  239. package/src/cli/commands/databaseCommands.ts +0 -879
  240. package/src/cli/commands/functionCommands.ts +0 -418
  241. package/src/cli/commands/schemaCommands.ts +0 -200
  242. package/src/cli/commands/storageCommands.ts +0 -152
  243. package/src/cli/commands/transferCommands.ts +0 -457
  244. package/src/collections/attributes.ts +0 -2020
  245. package/src/collections/attributes.ts.backup +0 -1555
  246. package/src/collections/indexes.ts +0 -352
  247. package/src/collections/methods.ts +0 -700
  248. package/src/collections/tableOperations.ts +0 -521
  249. package/src/collections/transferOperations.ts +0 -590
  250. package/src/collections/wipeOperations.ts +0 -346
  251. package/src/config/ConfigManager.ts +0 -849
  252. package/src/config/README.md +0 -274
  253. package/src/config/configMigration.ts +0 -575
  254. package/src/config/configValidation.ts +0 -445
  255. package/src/config/index.ts +0 -10
  256. package/src/config/services/ConfigDiscoveryService.ts +0 -410
  257. package/src/config/services/ConfigLoaderService.ts +0 -732
  258. package/src/config/services/ConfigMergeService.ts +0 -388
  259. package/src/config/services/ConfigValidationService.ts +0 -394
  260. package/src/config/services/SessionAuthService.ts +0 -565
  261. package/src/config/services/__tests__/ConfigMergeService.test.ts +0 -351
  262. package/src/config/services/index.ts +0 -29
  263. package/src/config/yamlConfig.ts +0 -761
  264. package/src/databases/methods.ts +0 -49
  265. package/src/databases/setup.ts +0 -77
  266. package/src/examples/yamlTerminologyExample.ts +0 -346
  267. package/src/functions/deployments.ts +0 -220
  268. package/src/functions/fnConfigDiscovery.ts +0 -103
  269. package/src/functions/methods.ts +0 -284
  270. package/src/functions/pathResolution.ts +0 -227
  271. package/src/functions/templates/count-docs-in-collection/README.md +0 -54
  272. package/src/functions/templates/count-docs-in-collection/package.json +0 -25
  273. package/src/functions/templates/count-docs-in-collection/src/main.ts +0 -159
  274. package/src/functions/templates/count-docs-in-collection/src/request.ts +0 -9
  275. package/src/functions/templates/count-docs-in-collection/tsconfig.json +0 -28
  276. package/src/functions/templates/hono-typescript/README.md +0 -286
  277. package/src/functions/templates/hono-typescript/package.json +0 -26
  278. package/src/functions/templates/hono-typescript/src/adapters/request.ts +0 -74
  279. package/src/functions/templates/hono-typescript/src/adapters/response.ts +0 -106
  280. package/src/functions/templates/hono-typescript/src/app.ts +0 -180
  281. package/src/functions/templates/hono-typescript/src/context.ts +0 -103
  282. package/src/functions/templates/hono-typescript/src/middleware/appwrite.ts +0 -119
  283. package/src/functions/templates/hono-typescript/tsconfig.json +0 -20
  284. package/src/functions/templates/typescript-node/README.md +0 -32
  285. package/src/functions/templates/typescript-node/package.json +0 -25
  286. package/src/functions/templates/typescript-node/src/context.ts +0 -103
  287. package/src/functions/templates/typescript-node/src/index.ts +0 -29
  288. package/src/functions/templates/typescript-node/tsconfig.json +0 -28
  289. package/src/functions/templates/uv/README.md +0 -31
  290. package/src/functions/templates/uv/pyproject.toml +0 -30
  291. package/src/functions/templates/uv/src/__init__.py +0 -0
  292. package/src/functions/templates/uv/src/context.py +0 -125
  293. package/src/functions/templates/uv/src/index.py +0 -46
  294. package/src/init.ts +0 -62
  295. package/src/interactiveCLI.ts +0 -1136
  296. package/src/main.ts +0 -1670
  297. package/src/migrations/afterImportActions.ts +0 -580
  298. package/src/migrations/appwriteToX.ts +0 -664
  299. package/src/migrations/comprehensiveTransfer.ts +0 -2285
  300. package/src/migrations/dataLoader.ts +0 -1702
  301. package/src/migrations/importController.ts +0 -428
  302. package/src/migrations/importDataActions.ts +0 -315
  303. package/src/migrations/relationships.ts +0 -334
  304. package/src/migrations/services/DataTransformationService.ts +0 -196
  305. package/src/migrations/services/FileHandlerService.ts +0 -311
  306. package/src/migrations/services/ImportOrchestrator.ts +0 -666
  307. package/src/migrations/services/RateLimitManager.ts +0 -363
  308. package/src/migrations/services/RelationshipResolver.ts +0 -461
  309. package/src/migrations/services/UserMappingService.ts +0 -345
  310. package/src/migrations/services/ValidationService.ts +0 -349
  311. package/src/migrations/transfer.ts +0 -1068
  312. package/src/migrations/yaml/YamlImportConfigLoader.ts +0 -439
  313. package/src/migrations/yaml/YamlImportIntegration.ts +0 -446
  314. package/src/migrations/yaml/generateImportSchemas.ts +0 -1354
  315. package/src/schemas/authUser.ts +0 -23
  316. package/src/setup.ts +0 -8
  317. package/src/setupCommands.ts +0 -603
  318. package/src/setupController.ts +0 -43
  319. package/src/shared/attributeMapper.ts +0 -229
  320. package/src/shared/backupMetadataSchema.ts +0 -93
  321. package/src/shared/backupTracking.ts +0 -211
  322. package/src/shared/confirmationDialogs.ts +0 -327
  323. package/src/shared/errorUtils.ts +0 -110
  324. package/src/shared/functionManager.ts +0 -537
  325. package/src/shared/jsonSchemaGenerator.ts +0 -383
  326. package/src/shared/logging.ts +0 -149
  327. package/src/shared/messageFormatter.ts +0 -208
  328. package/src/shared/migrationHelpers.ts +0 -232
  329. package/src/shared/operationLogger.ts +0 -20
  330. package/src/shared/operationQueue.ts +0 -377
  331. package/src/shared/operationsTable.ts +0 -338
  332. package/src/shared/operationsTableSchema.ts +0 -60
  333. package/src/shared/progressManager.ts +0 -278
  334. package/src/shared/pydanticModelGenerator.ts +0 -618
  335. package/src/shared/relationshipExtractor.ts +0 -214
  336. package/src/shared/schemaGenerator.ts +0 -644
  337. package/src/shared/selectionDialogs.ts +0 -749
  338. package/src/storage/backupCompression.ts +0 -88
  339. package/src/storage/methods.ts +0 -698
  340. package/src/storage/schemas.ts +0 -205
  341. package/src/tables/indexManager.ts +0 -409
  342. package/src/types/node-appwrite-tablesdb.d.ts +0 -44
  343. package/src/types.ts +0 -9
  344. package/src/users/methods.ts +0 -359
  345. package/src/utils/ClientFactory.ts +0 -240
  346. package/src/utils/configDiscovery.ts +0 -557
  347. package/src/utils/configMigration.ts +0 -348
  348. package/src/utils/constantsGenerator.ts +0 -369
  349. package/src/utils/dataConverters.ts +0 -159
  350. package/src/utils/directoryUtils.ts +0 -61
  351. package/src/utils/getClientFromConfig.ts +0 -257
  352. package/src/utils/helperFunctions.ts +0 -228
  353. package/src/utils/index.ts +0 -2
  354. package/src/utils/loadConfigs.ts +0 -449
  355. package/src/utils/pathResolvers.ts +0 -81
  356. package/src/utils/projectConfig.ts +0 -340
  357. package/src/utils/retryFailedPromises.ts +0 -29
  358. package/src/utils/sessionAuth.ts +0 -230
  359. package/src/utils/setupFiles.ts +0 -1238
  360. package/src/utils/typeGuards.ts +0 -65
  361. package/src/utils/validationRules.ts +0 -88
  362. package/src/utils/versionDetection.ts +0 -292
  363. package/src/utils/yamlConverter.ts +0 -542
  364. package/src/utils/yamlLoader.ts +0 -371
  365. package/src/utilsController.ts +0 -1213
  366. package/tests/README.md +0 -497
  367. package/tests/adapters/AdapterFactory.test.ts +0 -277
  368. package/tests/integration/syncOperations.test.ts +0 -463
  369. package/tests/jest.config.js +0 -25
  370. package/tests/migration/configMigration.test.ts +0 -546
  371. package/tests/setup.ts +0 -62
  372. package/tests/testUtils.ts +0 -340
  373. package/tests/utils/loadConfigs.test.ts +0 -350
  374. package/tests/validation/configValidation.test.ts +0 -412
  375. package/tmp-sync-test/.appwrite/collections/TestCollection.yaml +0 -7
  376. package/tsconfig.json +0 -44
@@ -1,1213 +0,0 @@
1
- import {
2
- Client,
3
- Databases,
4
- Query,
5
- Storage,
6
- Users,
7
- type Models,
8
- } from "node-appwrite";
9
- import {
10
- type AppwriteConfig,
11
- type AppwriteFunction,
12
- type Specification,
13
- } from "appwrite-utils";
14
- import {
15
- findAppwriteConfig,
16
- findFunctionsDir,
17
- } from "./utils/loadConfigs.js";
18
- import { normalizeFunctionName, validateFunctionDirectory } from './functions/pathResolution.js';
19
- import { UsersController } from "./users/methods.js";
20
- import { AppwriteToX } from "./migrations/appwriteToX.js";
21
- import { ImportController } from "./migrations/importController.js";
22
- import { ImportDataActions } from "./migrations/importDataActions.js";
23
- import {
24
- ensureDatabasesExist,
25
- wipeOtherDatabases,
26
- ensureCollectionsExist,
27
- } from "./databases/setup.js";
28
- import {
29
- createOrUpdateCollections,
30
- createOrUpdateCollectionsViaAdapter,
31
- wipeDatabase,
32
- generateSchemas,
33
- fetchAllCollections,
34
- wipeCollection,
35
- } from "./collections/methods.js";
36
- import { wipeAllTables, wipeTableRows } from "./collections/methods.js";
37
- import {
38
- backupDatabase,
39
- ensureDatabaseConfigBucketsExist,
40
- wipeDocumentStorage,
41
- } from "./storage/methods.js";
42
- import path from "path";
43
- import {
44
- type AfterImportActions,
45
- type ConverterFunctions,
46
- converterFunctions,
47
- validationRules,
48
- type ValidationRules,
49
- } from "appwrite-utils";
50
- import { afterImportActions } from "./migrations/afterImportActions.js";
51
- import {
52
- transferDatabaseLocalToLocal,
53
- transferDatabaseLocalToRemote,
54
- transferStorageLocalToLocal,
55
- transferStorageLocalToRemote,
56
- transferUsersLocalToRemote,
57
- type TransferOptions,
58
- } from "./migrations/transfer.js";
59
- import { getClient, getClientWithAuth } from "./utils/getClientFromConfig.js";
60
- import { getAdapterFromConfig } from "./utils/getClientFromConfig.js";
61
- import type { DatabaseAdapter } from './adapters/DatabaseAdapter.js';
62
- import { hasSessionAuth, findSessionByEndpointAndProject, isValidSessionCookie, type SessionAuthInfo } from "./utils/sessionAuth.js";
63
- import { fetchAllDatabases } from "./databases/methods.js";
64
- import {
65
- listFunctions,
66
- updateFunctionSpecifications,
67
- } from "./functions/methods.js";
68
- import chalk from "chalk";
69
- import { deployLocalFunction } from "./functions/deployments.js";
70
- import fs from "node:fs";
71
- import { configureLogging, updateLogger, logger } from "./shared/logging.js";
72
- import { MessageFormatter, Messages } from "./shared/messageFormatter.js";
73
- import { SchemaGenerator } from "./shared/schemaGenerator.js";
74
- import { findYamlConfig } from "./config/yamlConfig.js";
75
- import { createImportSchemas } from "./migrations/yaml/generateImportSchemas.js";
76
- import {
77
- validateCollectionsTablesConfig,
78
- reportValidationResults,
79
- validateWithStrictMode,
80
- type ValidationResult
81
- } from "./config/configValidation.js";
82
- import { ConfigManager } from "./config/ConfigManager.js";
83
- import { ClientFactory } from "./utils/ClientFactory.js";
84
- import type { DatabaseSelection, BucketSelection } from "./shared/selectionDialogs.js";
85
- import { clearProcessingState, processQueue } from "./shared/operationQueue.js";
86
-
87
- export interface SetupOptions {
88
- databases?: Models.Database[];
89
- collections?: string[];
90
- doBackup?: boolean;
91
- wipeDatabase?: boolean;
92
- wipeCollections?: boolean;
93
- wipeDocumentStorage?: boolean;
94
- wipeUsers?: boolean;
95
- transferUsers?: boolean;
96
- generateSchemas?: boolean;
97
- importData?: boolean;
98
- checkDuplicates?: boolean;
99
- shouldWriteFile?: boolean;
100
- }
101
-
102
- export class UtilsController {
103
- // ──────────────────────────────────────────────────
104
- // SINGLETON PATTERN
105
- // ──────────────────────────────────────────────────
106
- private static instance: UtilsController | null = null;
107
- private isInitialized: boolean = false;
108
-
109
- /**
110
- * Get the UtilsController singleton instance
111
- */
112
- public static getInstance(
113
- currentUserDir: string,
114
- directConfig?: {
115
- appwriteEndpoint?: string;
116
- appwriteProject?: string;
117
- appwriteKey?: string;
118
- }
119
- ): UtilsController {
120
- // Clear instance if currentUserDir has changed
121
- if (UtilsController.instance &&
122
- UtilsController.instance.currentUserDir !== currentUserDir) {
123
- logger.debug(`Clearing singleton: currentUserDir changed from ${UtilsController.instance.currentUserDir} to ${currentUserDir}`, { prefix: "UtilsController" });
124
- UtilsController.clearInstance();
125
- }
126
-
127
- // Clear instance if directConfig endpoint or project has changed
128
- if (UtilsController.instance && directConfig) {
129
- const existingConfig = UtilsController.instance.config;
130
- if (existingConfig) {
131
- const endpointChanged = directConfig.appwriteEndpoint &&
132
- existingConfig.appwriteEndpoint !== directConfig.appwriteEndpoint;
133
- const projectChanged = directConfig.appwriteProject &&
134
- existingConfig.appwriteProject !== directConfig.appwriteProject;
135
-
136
- if (endpointChanged || projectChanged) {
137
- logger.debug("Clearing singleton: endpoint or project changed", { prefix: "UtilsController" });
138
- UtilsController.clearInstance();
139
- }
140
- }
141
- }
142
-
143
- if (!UtilsController.instance) {
144
- UtilsController.instance = new UtilsController(currentUserDir, directConfig);
145
- }
146
-
147
- return UtilsController.instance;
148
- }
149
-
150
- /**
151
- * Clear the singleton instance (useful for testing)
152
- */
153
- public static clearInstance(): void {
154
- UtilsController.instance = null;
155
- }
156
-
157
- // ──────────────────────────────────────────────────
158
- // INSTANCE FIELDS
159
- // ──────────────────────────────────────────────────
160
- private appwriteFolderPath?: string;
161
- private appwriteConfigPath?: string;
162
- private currentUserDir: string;
163
- public config?: AppwriteConfig;
164
- public appwriteServer?: Client;
165
- public database?: Databases;
166
- public storage?: Storage;
167
- public adapter?: DatabaseAdapter;
168
- public converterDefinitions: ConverterFunctions = converterFunctions;
169
- public validityRuleDefinitions: ValidationRules = validationRules;
170
- public afterImportActionsDefinitions: AfterImportActions = afterImportActions;
171
-
172
- constructor(
173
- currentUserDir: string,
174
- directConfig?: {
175
- appwriteEndpoint?: string;
176
- appwriteProject?: string;
177
- appwriteKey?: string;
178
- }
179
- ) {
180
- this.currentUserDir = currentUserDir;
181
- const basePath = currentUserDir;
182
-
183
- if (directConfig) {
184
- let hasErrors = false;
185
- if (!directConfig.appwriteEndpoint) {
186
- MessageFormatter.error("Appwrite endpoint is required", undefined, { prefix: "Config" });
187
- hasErrors = true;
188
- }
189
- if (!directConfig.appwriteProject) {
190
- MessageFormatter.error("Appwrite project is required", undefined, { prefix: "Config" });
191
- hasErrors = true;
192
- }
193
- // Check authentication: either API key or session auth is required
194
- const hasValidSession = directConfig.appwriteEndpoint && directConfig.appwriteProject &&
195
- hasSessionAuth(directConfig.appwriteEndpoint, directConfig.appwriteProject);
196
-
197
- if (!directConfig.appwriteKey && !hasValidSession) {
198
- MessageFormatter.error(
199
- "Authentication required: provide an API key or login with 'appwrite login'",
200
- undefined,
201
- { prefix: "Config" }
202
- );
203
- hasErrors = true;
204
- } else if (!directConfig.appwriteKey && hasValidSession) {
205
- MessageFormatter.info("Using session authentication (no API key required)", { prefix: "Auth" });
206
- } else if (directConfig.appwriteKey && hasValidSession) {
207
- MessageFormatter.info("API key provided, session authentication also available", { prefix: "Auth" });
208
- }
209
- if (!hasErrors) {
210
- // Only set config if we have all required fields
211
- this.appwriteFolderPath = basePath;
212
- this.config = {
213
- appwriteEndpoint: directConfig.appwriteEndpoint!,
214
- appwriteProject: directConfig.appwriteProject!,
215
- appwriteKey: directConfig.appwriteKey || "",
216
- appwriteClient: null,
217
- apiMode: "auto", // Default to auto-detect for dual API support
218
- authMethod: "auto", // Default to auto-detect authentication method
219
- enableBackups: false,
220
- backupInterval: 0,
221
- backupRetention: 0,
222
- enableBackupCleanup: false,
223
- enableMockData: false,
224
- documentBucketId: "",
225
- usersCollectionName: "",
226
- databases: [],
227
- buckets: [],
228
- functions: [],
229
- logging: {
230
- enabled: false,
231
- level: "info",
232
- console: false,
233
- },
234
- };
235
- }
236
- } else {
237
- // Try to find config file
238
- const appwriteConfigFound = findAppwriteConfig(basePath);
239
- if (!appwriteConfigFound) {
240
- MessageFormatter.warning(
241
- "No appwriteConfig.ts found and no direct configuration provided",
242
- { prefix: "Config" }
243
- );
244
- return;
245
- }
246
- this.appwriteConfigPath = appwriteConfigFound;
247
- this.appwriteFolderPath = appwriteConfigFound; // For YAML configs, findAppwriteConfig already returns the correct directory
248
- }
249
- }
250
-
251
- async init(options: { validate?: boolean; strictMode?: boolean; useSession?: boolean; sessionCookie?: string; preferJson?: boolean } = {}) {
252
- const { validate = false, strictMode = false, preferJson = false } = options;
253
- const configManager = ConfigManager.getInstance();
254
-
255
- // Load config if not already loaded
256
- if (!configManager.hasConfig()) {
257
- await configManager.loadConfig({
258
- configDir: this.currentUserDir,
259
- validate,
260
- strictMode,
261
- preferJson,
262
- });
263
- }
264
-
265
- const config = configManager.getConfig();
266
-
267
- // Configure logging based on config
268
- if (config.logging) {
269
- configureLogging(config.logging);
270
- updateLogger();
271
- }
272
-
273
- // Create client and adapter (session already in config from ConfigManager)
274
- const { client, adapter } = await ClientFactory.createFromConfig(config);
275
-
276
- this.appwriteServer = client;
277
- this.adapter = adapter;
278
- this.config = config;
279
-
280
- // Update config.apiMode from adapter if it's auto or not set
281
- if (adapter && (!config.apiMode || config.apiMode === 'auto')) {
282
- this.config.apiMode = adapter.getApiMode();
283
- logger.debug(`Updated config.apiMode from adapter during init: ${this.config.apiMode}`, { prefix: "UtilsController" });
284
- }
285
-
286
- this.database = new Databases(this.appwriteServer);
287
- this.storage = new Storage(this.appwriteServer);
288
- this.config.appwriteClient = this.appwriteServer;
289
-
290
- // Log only on FIRST initialization to avoid spam
291
- if (!this.isInitialized) {
292
- const apiMode = adapter.getApiMode();
293
- const configApiMode = this.config.apiMode;
294
- MessageFormatter.info(`Database adapter initialized (apiMode: ${apiMode}, config.apiMode: ${configApiMode})`, { prefix: "Adapter" });
295
- this.isInitialized = true;
296
- } else {
297
- logger.debug("Adapter reused from cache", { prefix: "UtilsController" });
298
- }
299
- }
300
-
301
- async reloadConfig() {
302
- const configManager = ConfigManager.getInstance();
303
-
304
- // Session preservation is automatic in ConfigManager
305
- const config = await configManager.reloadConfig();
306
-
307
- // Configure logging based on updated config
308
- if (config.logging) {
309
- configureLogging(config.logging);
310
- updateLogger();
311
- }
312
-
313
- // Recreate client and adapter
314
- const { client, adapter } = await ClientFactory.createFromConfig(config);
315
-
316
- this.appwriteServer = client;
317
- this.adapter = adapter;
318
- this.config = config;
319
- this.database = new Databases(this.appwriteServer);
320
- this.storage = new Storage(this.appwriteServer);
321
- this.config.appwriteClient = this.appwriteServer;
322
-
323
- logger.debug("Config reloaded, adapter refreshed", { prefix: "UtilsController" });
324
- }
325
-
326
-
327
- async ensureDatabaseConfigBucketsExist(databases: Models.Database[] = []) {
328
- await this.init();
329
- if (!this.storage) {
330
- MessageFormatter.error("Storage not initialized", undefined, { prefix: "Controller" });
331
- return;
332
- }
333
- if (!this.config) {
334
- MessageFormatter.error("Config not initialized", undefined, { prefix: "Controller" });
335
- return;
336
- }
337
- await ensureDatabaseConfigBucketsExist(
338
- this.storage,
339
- this.config,
340
- databases
341
- );
342
- }
343
-
344
- async ensureDatabasesExist(databases?: Models.Database[]) {
345
- await this.init();
346
- if (!this.config) {
347
- MessageFormatter.error("Config not initialized", undefined, { prefix: "Controller" });
348
- return;
349
- }
350
- await this.ensureDatabaseConfigBucketsExist(databases);
351
- await ensureDatabasesExist(this.config, databases);
352
- }
353
-
354
- async ensureCollectionsExist(
355
- database: Models.Database,
356
- collections?: Models.Collection[]
357
- ) {
358
- await this.init();
359
- if (!this.config) {
360
- MessageFormatter.error("Config not initialized", undefined, { prefix: "Controller" });
361
- return;
362
- }
363
- await ensureCollectionsExist(this.config, database, collections);
364
- }
365
-
366
- async getDatabasesByIds(ids: string[]) {
367
- await this.init();
368
- if (!this.database) {
369
- MessageFormatter.error("Database not initialized", undefined, { prefix: "Controller" });
370
- return;
371
- }
372
- if (ids.length === 0) return [];
373
- const dbs = await this.database.list([
374
- Query.limit(500),
375
- Query.equal("$id", ids),
376
- ]);
377
- return dbs.databases;
378
- }
379
-
380
- async fetchAllBuckets(): Promise<{ buckets: Models.Bucket[] }> {
381
- await this.init();
382
- if (!this.storage) {
383
- MessageFormatter.warning("Storage not initialized - buckets will be empty", { prefix: "Controller" });
384
- return { buckets: [] };
385
- }
386
-
387
- try {
388
- const result = await this.storage.listBuckets([
389
- Query.limit(1000) // Increase limit to get all buckets
390
- ]);
391
-
392
- MessageFormatter.success(`Found ${result.buckets.length} buckets`, { prefix: "Controller" });
393
- return result;
394
- } catch (error: any) {
395
- MessageFormatter.error(`Failed to fetch buckets: ${error.message || error}`, error instanceof Error ? error : undefined, { prefix: "Controller" });
396
- return { buckets: [] };
397
- }
398
- }
399
-
400
- async wipeOtherDatabases(databasesToKeep: Models.Database[]) {
401
- await this.init();
402
- if (!this.database) {
403
- MessageFormatter.error("Database not initialized", undefined, { prefix: "Controller" });
404
- return;
405
- }
406
- await wipeOtherDatabases(this.database, databasesToKeep);
407
- }
408
-
409
- async wipeUsers() {
410
- await this.init();
411
- if (!this.config || !this.database) {
412
- MessageFormatter.error("Config or database not initialized", undefined, { prefix: "Controller" });
413
- return;
414
- }
415
- const usersController = new UsersController(this.config, this.database);
416
- await usersController.wipeUsers();
417
- }
418
-
419
- async backupDatabase(database: Models.Database, format: 'json' | 'zip' = 'json') {
420
- await this.init();
421
- if (!this.database || !this.storage || !this.config) {
422
- MessageFormatter.error("Database, storage, or config not initialized", undefined, { prefix: "Controller" });
423
- return;
424
- }
425
- await backupDatabase(
426
- this.config,
427
- this.database,
428
- database.$id,
429
- this.storage,
430
- format
431
- );
432
- }
433
-
434
- async listAllFunctions() {
435
- await this.init();
436
- if (!this.appwriteServer) {
437
- MessageFormatter.error("Appwrite server not initialized", undefined, { prefix: "Controller" });
438
- return [];
439
- }
440
- const { functions } = await listFunctions(this.appwriteServer, [
441
- Query.limit(1000),
442
- ]);
443
- return functions;
444
- }
445
-
446
- async findFunctionDirectories() {
447
- if (!this.appwriteFolderPath) {
448
- MessageFormatter.error("Failed to get appwriteFolderPath", undefined, { prefix: "Controller" });
449
- return new Map();
450
- }
451
- const functionsDir = findFunctionsDir(this.appwriteFolderPath);
452
- if (!functionsDir) {
453
- MessageFormatter.error("Failed to find functions directory", undefined, { prefix: "Controller" });
454
- return new Map();
455
- }
456
-
457
- const functionDirMap = new Map<string, string>();
458
- const entries = fs.readdirSync(functionsDir, { withFileTypes: true });
459
-
460
- for (const entry of entries) {
461
- if (entry.isDirectory()) {
462
- const functionPath = path.join(functionsDir, entry.name);
463
-
464
- // Validate it's a function directory
465
- if (!validateFunctionDirectory(functionPath)) {
466
- continue; // Skip invalid directories
467
- }
468
-
469
- // Match with config functions using normalized names
470
- if (this.config?.functions) {
471
- const normalizedEntryName = normalizeFunctionName(entry.name);
472
- const matchingFunc = this.config.functions.find(
473
- (f) => normalizeFunctionName(f.name) === normalizedEntryName
474
- );
475
- if (matchingFunc) {
476
- functionDirMap.set(matchingFunc.name, functionPath);
477
- }
478
- }
479
- }
480
- }
481
- return functionDirMap;
482
- }
483
-
484
- async deployFunction(
485
- functionName: string,
486
- functionPath?: string,
487
- functionConfig?: AppwriteFunction
488
- ) {
489
- await this.init();
490
- if (!this.appwriteServer) {
491
- MessageFormatter.error("Appwrite server not initialized", undefined, { prefix: "Controller" });
492
- return;
493
- }
494
-
495
- if (!functionConfig) {
496
- functionConfig = this.config?.functions?.find(
497
- (f) => f.name === functionName
498
- );
499
- }
500
- if (!functionConfig) {
501
- MessageFormatter.error(`Function ${functionName} not found in config`, undefined, { prefix: "Controller" });
502
- return;
503
- }
504
-
505
- await deployLocalFunction(
506
- this.appwriteServer,
507
- functionName,
508
- functionConfig,
509
- functionPath
510
- );
511
- }
512
-
513
- async syncFunctions() {
514
- await this.init();
515
- if (!this.appwriteServer) {
516
- MessageFormatter.error("Appwrite server not initialized", undefined, { prefix: "Controller" });
517
- return;
518
- }
519
-
520
- const localFunctions = this.config?.functions || [];
521
- const remoteFunctions = await listFunctions(this.appwriteServer, [
522
- Query.limit(1000),
523
- ]);
524
-
525
- for (const localFunction of localFunctions) {
526
- MessageFormatter.progress(`Syncing function ${localFunction.name}...`, { prefix: "Functions" });
527
- await this.deployFunction(localFunction.name);
528
- }
529
-
530
- MessageFormatter.success("All functions synchronized successfully!", { prefix: "Functions" });
531
- }
532
-
533
- async wipeDatabase(database: Models.Database, wipeBucket: boolean = false) {
534
- await this.init();
535
- if (!this.database || !this.config) throw new Error("Database not initialized");
536
- try {
537
- // Session is already in config from ConfigManager
538
- const { adapter, apiMode } = await getAdapterFromConfig(this.config, false);
539
- if (apiMode === 'tablesdb') {
540
- await wipeAllTables(adapter, database.$id);
541
- } else {
542
- await wipeDatabase(this.database, database.$id);
543
- }
544
- } catch {
545
- await wipeDatabase(this.database, database.$id);
546
- }
547
- if (wipeBucket) {
548
- await this.wipeBucketFromDatabase(database);
549
- }
550
- }
551
-
552
- async wipeBucketFromDatabase(database: Models.Database) {
553
- // Check configured bucket in database config
554
- const configuredBucket = this.config?.databases?.find(
555
- (db) => db.$id === database.$id
556
- )?.bucket;
557
- if (configuredBucket?.$id) {
558
- await this.wipeDocumentStorage(configuredBucket.$id);
559
- }
560
-
561
- // Also check for document bucket ID pattern
562
- if (this.config?.documentBucketId) {
563
- const documentBucketId = `${this.config.documentBucketId}_${database.$id
564
- .toLowerCase()
565
- .trim()
566
- .replace(/\s+/g, "")}`;
567
- try {
568
- await this.wipeDocumentStorage(documentBucketId);
569
- } catch (error: any) {
570
- // Ignore if bucket doesn't exist
571
- if (error?.type !== "storage_bucket_not_found") {
572
- throw error;
573
- }
574
- }
575
- }
576
- }
577
-
578
- async wipeCollection(
579
- database: Models.Database,
580
- collection: Models.Collection
581
- ) {
582
- await this.init();
583
- if (!this.database || !this.config) throw new Error("Database not initialized");
584
- try {
585
- // Session is already in config from ConfigManager
586
- const { adapter, apiMode } = await getAdapterFromConfig(this.config, false);
587
- if (apiMode === 'tablesdb') {
588
- await wipeTableRows(adapter, database.$id, collection.$id);
589
- } else {
590
- await wipeCollection(this.database, database.$id, collection.$id);
591
- }
592
- } catch {
593
- await wipeCollection(this.database, database.$id, collection.$id);
594
- }
595
- }
596
-
597
- async wipeDocumentStorage(bucketId: string) {
598
- await this.init();
599
- if (!this.storage) throw new Error("Storage not initialized");
600
- await wipeDocumentStorage(this.storage, bucketId);
601
- }
602
-
603
- async createOrUpdateCollectionsForDatabases(
604
- databases: Models.Database[],
605
- collections: Models.Collection[] = []
606
- ) {
607
- await this.init();
608
- if (!this.database || !this.config)
609
- throw new Error("Database or config not initialized");
610
- for (const database of databases) {
611
- await this.createOrUpdateCollections(database, undefined, collections);
612
- }
613
- }
614
-
615
- async createOrUpdateCollections(
616
- database: Models.Database,
617
- deletedCollections?: { collectionId: string; collectionName: string }[],
618
- collections: Models.Collection[] = []
619
- ) {
620
- await this.init();
621
- if (!this.database || !this.config)
622
- throw new Error("Database or config not initialized");
623
-
624
- // Ensure apiMode is properly set from adapter
625
- if (this.adapter && (!this.config.apiMode || this.config.apiMode === 'auto')) {
626
- this.config.apiMode = this.adapter.getApiMode();
627
- logger.debug(`Updated config.apiMode from adapter: ${this.config.apiMode}`, { prefix: "UtilsController" });
628
- }
629
-
630
- // Ensure we don't carry state between databases in a multi-db push
631
- // This resets processed sets and name->id mapping per database
632
- try {
633
- clearProcessingState();
634
- } catch {}
635
-
636
- // Always prefer adapter path for unified behavior. LegacyAdapter internally translates when needed.
637
- if (this.adapter) {
638
- logger.debug("Using adapter for createOrUpdateCollections (unified path)", {
639
- prefix: "UtilsController",
640
- apiMode: this.adapter.getApiMode()
641
- });
642
- await createOrUpdateCollectionsViaAdapter(
643
- this.adapter,
644
- database.$id,
645
- this.config,
646
- deletedCollections,
647
- collections
648
- );
649
- } else {
650
- // Fallback if adapter is unavailable for some reason
651
- logger.debug("Adapter unavailable, falling back to legacy Databases path", { prefix: "UtilsController" });
652
- await createOrUpdateCollections(
653
- this.database,
654
- database.$id,
655
- this.config,
656
- deletedCollections,
657
- collections
658
- );
659
- }
660
-
661
- // Safety net: Process any remaining queued operations to complete relationship sync
662
- try {
663
- MessageFormatter.info(`🔄 Processing final operation queue for database ${database.$id}`, { prefix: "UtilsController" });
664
- await processQueue(this.adapter || this.database!, database.$id);
665
- MessageFormatter.info(`✅ Operation queue processing completed`, { prefix: "UtilsController" });
666
- } catch (error) {
667
- MessageFormatter.error(`Failed to process operation queue`, error instanceof Error ? error : new Error(String(error)), { prefix: 'UtilsController' });
668
- }
669
- }
670
-
671
- async generateSchemas() {
672
- // Schema generation doesn't need Appwrite connection, just config
673
- if (!this.config) {
674
- MessageFormatter.progress("Loading config from ConfigManager...", { prefix: "Config" });
675
- try {
676
- const configManager = ConfigManager.getInstance();
677
-
678
- // Load config if not already loaded
679
- if (!configManager.hasConfig()) {
680
- await configManager.loadConfig({
681
- configDir: this.currentUserDir,
682
- validate: false,
683
- strictMode: false,
684
- });
685
- }
686
-
687
- this.config = configManager.getConfig();
688
- MessageFormatter.info("Config loaded successfully from ConfigManager", { prefix: "Config" });
689
- } catch (error) {
690
- MessageFormatter.error("Failed to load config", error instanceof Error ? error : undefined, { prefix: "Config" });
691
- return;
692
- }
693
- }
694
-
695
- if (!this.appwriteFolderPath) {
696
- MessageFormatter.error("Failed to get appwriteFolderPath", undefined, { prefix: "Controller" });
697
- return;
698
- }
699
-
700
- await generateSchemas(this.config, this.appwriteFolderPath);
701
- }
702
-
703
- async importData(options: SetupOptions = {}) {
704
- await this.init();
705
- if (!this.database) {
706
- MessageFormatter.error("Database not initialized", undefined, { prefix: "Controller" });
707
- return;
708
- }
709
- if (!this.storage) {
710
- MessageFormatter.error("Storage not initialized", undefined, { prefix: "Controller" });
711
- return;
712
- }
713
- if (!this.config) {
714
- MessageFormatter.error("Config not initialized", undefined, { prefix: "Controller" });
715
- return;
716
- }
717
- if (!this.appwriteFolderPath) {
718
- MessageFormatter.error("Failed to get appwriteFolderPath", undefined, { prefix: "Controller" });
719
- return;
720
- }
721
-
722
- const importDataActions = new ImportDataActions(
723
- this.database,
724
- this.storage,
725
- this.config,
726
- this.converterDefinitions,
727
- this.validityRuleDefinitions,
728
- this.afterImportActionsDefinitions
729
- );
730
-
731
- const importController = new ImportController(
732
- this.config,
733
- this.database,
734
- this.storage,
735
- this.appwriteFolderPath,
736
- importDataActions,
737
- options,
738
- options.databases
739
- );
740
- await importController.run(options.collections);
741
- }
742
-
743
- async synchronizeConfigurations(
744
- databases?: Models.Database[],
745
- config?: AppwriteConfig,
746
- databaseSelections?: DatabaseSelection[],
747
- bucketSelections?: BucketSelection[]
748
- ) {
749
- await this.init();
750
- if (!this.storage) {
751
- MessageFormatter.error("Storage not initialized", undefined, { prefix: "Controller" });
752
- return;
753
- }
754
- const configToUse = config || this.config;
755
- if (!configToUse) {
756
- MessageFormatter.error("Config not initialized", undefined, { prefix: "Controller" });
757
- return;
758
- }
759
- if (!this.appwriteFolderPath) {
760
- MessageFormatter.error("Failed to get appwriteFolderPath", undefined, { prefix: "Controller" });
761
- return;
762
- }
763
-
764
- // If selections are provided, filter the databases accordingly
765
- let filteredDatabases = databases;
766
- if (databaseSelections && databaseSelections.length > 0) {
767
- // Convert selections to Models.Database format
768
- filteredDatabases = [];
769
- const allDatabases = databases ? databases : await fetchAllDatabases(this.database!);
770
-
771
- for (const selection of databaseSelections) {
772
- const database = allDatabases.find(db => db.$id === selection.databaseId);
773
- if (database) {
774
- filteredDatabases.push(database);
775
- } else {
776
- MessageFormatter.warning(`Database with ID ${selection.databaseId} not found`, { prefix: "Controller" });
777
- }
778
- }
779
-
780
- MessageFormatter.info(`Syncing ${filteredDatabases.length} selected databases out of ${allDatabases.length} available`, { prefix: "Controller" });
781
- }
782
-
783
- const appwriteToX = new AppwriteToX(
784
- configToUse,
785
- this.appwriteFolderPath,
786
- this.storage
787
- );
788
- await appwriteToX.toSchemas(filteredDatabases);
789
-
790
- // Update the controller's config with the synchronized collections
791
- this.config = appwriteToX.updatedConfig;
792
-
793
- // Write the updated config back to disk
794
- const generator = new SchemaGenerator(this.config, this.appwriteFolderPath);
795
- const yamlConfigPath = findYamlConfig(this.appwriteFolderPath);
796
- const isYamlProject = !!yamlConfigPath;
797
- await generator.updateConfig(this.config, isYamlProject);
798
-
799
- // Regenerate JSON schemas to reflect any table terminology fixes
800
- try {
801
- MessageFormatter.progress("Regenerating JSON schemas...", { prefix: "Sync" });
802
- await createImportSchemas(this.appwriteFolderPath);
803
- MessageFormatter.success("JSON schemas regenerated successfully", { prefix: "Sync" });
804
- } catch (error) {
805
- // Log error but don't fail the sync process
806
- const errorMessage = error instanceof Error ? error.message : String(error);
807
- MessageFormatter.warning(
808
- `Failed to regenerate JSON schemas, but sync completed: ${errorMessage}`,
809
- { prefix: "Sync" }
810
- );
811
- logger.warn("Schema regeneration failed during sync:", error);
812
- }
813
- }
814
-
815
- async selectivePull(
816
- databaseSelections: DatabaseSelection[],
817
- bucketSelections: BucketSelection[]
818
- ): Promise<void> {
819
- await this.init();
820
- if (!this.database) {
821
- MessageFormatter.error("Database not initialized", undefined, { prefix: "Controller" });
822
- return;
823
- }
824
-
825
- MessageFormatter.progress("Starting selective pull (Appwrite → local config)...", { prefix: "Controller" });
826
-
827
- // Convert database selections to Models.Database format
828
- const selectedDatabases: Models.Database[] = [];
829
-
830
- for (const dbSelection of databaseSelections) {
831
- // Get the full database object from the controller
832
- const databases = await fetchAllDatabases(this.database);
833
- const database = databases.find(db => db.$id === dbSelection.databaseId);
834
-
835
- if (database) {
836
- selectedDatabases.push(database);
837
- MessageFormatter.info(`Selected database: ${database.name} (${database.$id})`, { prefix: "Controller" });
838
-
839
- // Log selected tables for this database
840
- if (dbSelection.tableIds && dbSelection.tableIds.length > 0) {
841
- MessageFormatter.info(` Tables: ${dbSelection.tableIds.join(', ')}`, { prefix: "Controller" });
842
- }
843
- } else {
844
- MessageFormatter.warning(`Database with ID ${dbSelection.databaseId} not found`, { prefix: "Controller" });
845
- }
846
- }
847
-
848
- if (selectedDatabases.length === 0) {
849
- MessageFormatter.warning("No valid databases selected for pull", { prefix: "Controller" });
850
- return;
851
- }
852
-
853
- // Log bucket selections if provided
854
- if (bucketSelections && bucketSelections.length > 0) {
855
- MessageFormatter.info(`Selected ${bucketSelections.length} buckets:`, { prefix: "Controller" });
856
- for (const bucketSelection of bucketSelections) {
857
- const dbInfo = bucketSelection.databaseId ? ` (DB: ${bucketSelection.databaseId})` : '';
858
- MessageFormatter.info(` - ${bucketSelection.bucketName} (${bucketSelection.bucketId})${dbInfo}`, { prefix: "Controller" });
859
- }
860
- }
861
-
862
- // Perform selective sync using the enhanced synchronizeConfigurations method
863
- await this.synchronizeConfigurations(selectedDatabases, this.config, databaseSelections, bucketSelections);
864
-
865
- MessageFormatter.success("Selective pull completed successfully! Remote config pulled to local.", { prefix: "Controller" });
866
- }
867
-
868
- async selectivePush(
869
- databaseSelections: DatabaseSelection[],
870
- bucketSelections: BucketSelection[]
871
- ): Promise<void> {
872
- await this.init();
873
- if (!this.database) {
874
- MessageFormatter.error("Database not initialized", undefined, { prefix: "Controller" });
875
- return;
876
- }
877
-
878
- // Always reload config from disk so pushes use current local YAML/Ts definitions
879
- try {
880
- await this.reloadConfig();
881
- MessageFormatter.info("Reloaded config from disk for push", { prefix: "Controller" });
882
- } catch (e) {
883
- // Non-fatal; continue with existing config
884
- MessageFormatter.warning("Could not reload config; continuing with current in-memory config", { prefix: "Controller" });
885
- }
886
-
887
- MessageFormatter.progress("Starting selective push (local config → Appwrite)...", { prefix: "Controller" });
888
-
889
- // Convert database selections to Models.Database format
890
- const selectedDatabases: Models.Database[] = [];
891
-
892
- for (const dbSelection of databaseSelections) {
893
- // Get the full database object from the controller
894
- const databases = await fetchAllDatabases(this.database);
895
- const database = databases.find(db => db.$id === dbSelection.databaseId);
896
-
897
- if (database) {
898
- selectedDatabases.push(database);
899
- MessageFormatter.info(`Selected database: ${database.name} (${database.$id})`, { prefix: "Controller" });
900
-
901
- // Log selected tables for this database
902
- if (dbSelection.tableIds && dbSelection.tableIds.length > 0) {
903
- MessageFormatter.info(` Tables: ${dbSelection.tableIds.join(', ')}`, { prefix: "Controller" });
904
- }
905
- } else {
906
- MessageFormatter.warning(`Database with ID ${dbSelection.databaseId} not found`, { prefix: "Controller" });
907
- }
908
- }
909
-
910
- if (selectedDatabases.length === 0) {
911
- MessageFormatter.warning("No valid databases selected for push", { prefix: "Controller" });
912
- return;
913
- }
914
-
915
- // Log bucket selections if provided
916
- if (bucketSelections && bucketSelections.length > 0) {
917
- MessageFormatter.info(`Selected ${bucketSelections.length} buckets:`, { prefix: "Controller" });
918
- for (const bucketSelection of bucketSelections) {
919
- const dbInfo = bucketSelection.databaseId ? ` (DB: ${bucketSelection.databaseId})` : '';
920
- MessageFormatter.info(` - ${bucketSelection.bucketName} (${bucketSelection.bucketId})${dbInfo}`, { prefix: "Controller" });
921
- }
922
- }
923
-
924
- // PUSH OPERATION: Push local configuration to Appwrite
925
- // Build database-specific collection mappings from databaseSelections
926
- const databaseCollectionsMap = new Map<string, any[]>();
927
-
928
- // Get all collections/tables from config (they're at the root level, not nested in databases)
929
- const allCollections = this.config?.collections || this.config?.tables || [];
930
-
931
- // Create database-specific collection mapping to preserve relationships
932
- for (const dbSelection of databaseSelections) {
933
- const collectionsForDatabase: any[] = [];
934
-
935
- MessageFormatter.info(`Processing collections for database: ${dbSelection.databaseId}`, { prefix: "Controller" });
936
-
937
- // Filter collections that were selected for THIS specific database
938
- for (const collection of allCollections) {
939
- const collectionId = collection.$id || (collection as any).id;
940
-
941
- // Check if this collection was selected for THIS database
942
- if (dbSelection.tableIds.includes(collectionId)) {
943
- collectionsForDatabase.push(collection);
944
- const source = (collection as any)._isFromTablesDir ? 'tables/' : 'collections/';
945
- MessageFormatter.info(` - Selected collection: ${collection.name || collectionId} for database ${dbSelection.databaseId} [source: ${source}]`, { prefix: "Controller" });
946
- }
947
- }
948
-
949
- databaseCollectionsMap.set(dbSelection.databaseId, collectionsForDatabase);
950
- MessageFormatter.info(`Database ${dbSelection.databaseId}: ${collectionsForDatabase.length} collections selected`, { prefix: "Controller" });
951
- }
952
-
953
- // Calculate total collections for logging
954
- const totalSelectedCollections = Array.from(databaseCollectionsMap.values())
955
- .reduce((total, collections) => total + collections.length, 0);
956
-
957
- MessageFormatter.info(`Pushing ${totalSelectedCollections} selected tables/collections to ${databaseCollectionsMap.size} databases`, { prefix: "Controller" });
958
-
959
- // Ensure databases exist
960
- await this.ensureDatabasesExist(selectedDatabases);
961
- await this.ensureDatabaseConfigBucketsExist(selectedDatabases);
962
-
963
- // Create/update collections with database-specific context
964
- for (const database of selectedDatabases) {
965
- const collectionsForThisDatabase = databaseCollectionsMap.get(database.$id) || [];
966
- if (collectionsForThisDatabase.length > 0) {
967
- MessageFormatter.info(`Pushing ${collectionsForThisDatabase.length} collections to database ${database.$id} (${database.name})`, { prefix: "Controller" });
968
- await this.createOrUpdateCollections(database, undefined, collectionsForThisDatabase);
969
- } else {
970
- MessageFormatter.info(`No collections selected for database ${database.$id} (${database.name})`, { prefix: "Controller" });
971
- }
972
- }
973
-
974
- MessageFormatter.success("Selective push completed successfully! Local config pushed to Appwrite.", { prefix: "Controller" });
975
- }
976
-
977
- async syncDb(
978
- databases: Models.Database[] = [],
979
- collections: Models.Collection[] = []
980
- ) {
981
- await this.init();
982
- if (!this.database) {
983
- MessageFormatter.error("Database not initialized", undefined, { prefix: "Controller" });
984
- return;
985
- }
986
- if (databases.length === 0) {
987
- const allDatabases = await fetchAllDatabases(this.database);
988
- databases = allDatabases;
989
- }
990
- // Ensure DBs exist
991
- await this.ensureDatabasesExist(databases);
992
- await this.ensureDatabaseConfigBucketsExist(databases);
993
-
994
- await this.createOrUpdateCollectionsForDatabases(databases, collections);
995
- }
996
-
997
- getAppwriteFolderPath() {
998
- return this.appwriteFolderPath;
999
- }
1000
-
1001
- async transferData(options: TransferOptions): Promise<void> {
1002
- let sourceClient = this.database;
1003
- let targetClient: Databases | undefined;
1004
- let sourceDatabases: Models.Database[] = [];
1005
- let targetDatabases: Models.Database[] = [];
1006
-
1007
- if (!sourceClient) {
1008
- MessageFormatter.error("Source database not initialized", undefined, { prefix: "Controller" });
1009
- return;
1010
- }
1011
-
1012
- if (options.isRemote) {
1013
- if (
1014
- !options.transferEndpoint ||
1015
- !options.transferProject ||
1016
- !options.transferKey
1017
- ) {
1018
- MessageFormatter.error("Remote transfer options are missing", undefined, { prefix: "Controller" });
1019
- return;
1020
- }
1021
-
1022
- const remoteClient = getClient(
1023
- options.transferEndpoint,
1024
- options.transferProject,
1025
- options.transferKey
1026
- );
1027
-
1028
- targetClient = new Databases(remoteClient);
1029
- sourceDatabases = await fetchAllDatabases(sourceClient);
1030
- targetDatabases = await fetchAllDatabases(targetClient);
1031
- } else {
1032
- targetClient = sourceClient;
1033
- sourceDatabases = targetDatabases = await fetchAllDatabases(sourceClient);
1034
- }
1035
-
1036
- // Always perform database transfer if databases are specified
1037
- if (options.fromDb && options.targetDb) {
1038
- const fromDb = sourceDatabases.find(
1039
- (db) => db.$id === options.fromDb!.$id
1040
- );
1041
- const targetDb = targetDatabases.find(
1042
- (db) => db.$id === options.targetDb!.$id
1043
- );
1044
-
1045
- if (!fromDb || !targetDb) {
1046
- MessageFormatter.error("Source or target database not found", undefined, { prefix: "Controller" });
1047
- return;
1048
- }
1049
-
1050
- if (options.isRemote && targetClient) {
1051
- await transferDatabaseLocalToRemote(
1052
- sourceClient,
1053
- options.transferEndpoint!,
1054
- options.transferProject!,
1055
- options.transferKey!,
1056
- fromDb.$id,
1057
- targetDb.$id
1058
- );
1059
- } else {
1060
- await transferDatabaseLocalToLocal(
1061
- sourceClient,
1062
- fromDb.$id,
1063
- targetDb.$id
1064
- );
1065
- }
1066
- }
1067
-
1068
- if (options.transferUsers) {
1069
- if (!options.isRemote) {
1070
- MessageFormatter.warning(
1071
- "User transfer is only supported for remote transfers. Skipping...",
1072
- { prefix: "Controller" }
1073
- );
1074
- } else if (!this.appwriteServer) {
1075
- MessageFormatter.error("Appwrite server not initialized", undefined, { prefix: "Controller" });
1076
- return;
1077
- } else {
1078
- MessageFormatter.progress("Starting user transfer...", { prefix: "Transfer" });
1079
- const localUsers = new Users(this.appwriteServer);
1080
- await transferUsersLocalToRemote(
1081
- localUsers,
1082
- options.transferEndpoint!,
1083
- options.transferProject!,
1084
- options.transferKey!
1085
- );
1086
- MessageFormatter.success("User transfer completed", { prefix: "Transfer" });
1087
- }
1088
- }
1089
-
1090
- // Handle storage transfer
1091
- if (this.storage && (options.sourceBucket || options.fromDb)) {
1092
- const sourceBucketId =
1093
- options.sourceBucket?.$id ||
1094
- (options.fromDb &&
1095
- this.config?.documentBucketId &&
1096
- `${this.config.documentBucketId}_${options.fromDb.$id
1097
- .toLowerCase()
1098
- .trim()
1099
- .replace(/\s+/g, "")}`);
1100
-
1101
- const targetBucketId =
1102
- options.targetBucket?.$id ||
1103
- (options.targetDb &&
1104
- this.config?.documentBucketId &&
1105
- `${this.config.documentBucketId}_${options.targetDb.$id
1106
- .toLowerCase()
1107
- .trim()
1108
- .replace(/\s+/g, "")}`);
1109
-
1110
- if (sourceBucketId && targetBucketId) {
1111
- MessageFormatter.progress(
1112
- `Starting storage transfer from ${sourceBucketId} to ${targetBucketId}`,
1113
- { prefix: "Transfer" }
1114
- );
1115
-
1116
- if (options.isRemote) {
1117
- await transferStorageLocalToRemote(
1118
- this.storage,
1119
- options.transferEndpoint!,
1120
- options.transferProject!,
1121
- options.transferKey!,
1122
- sourceBucketId,
1123
- targetBucketId
1124
- );
1125
- } else {
1126
- await transferStorageLocalToLocal(
1127
- this.storage,
1128
- sourceBucketId,
1129
- targetBucketId
1130
- );
1131
- }
1132
- }
1133
- }
1134
-
1135
- MessageFormatter.success("Transfer completed", { prefix: "Transfer" });
1136
- }
1137
-
1138
- async updateFunctionSpecifications(
1139
- functionId: string,
1140
- specification: Specification
1141
- ) {
1142
- await this.init();
1143
- if (!this.appwriteServer)
1144
- throw new Error("Appwrite server not initialized");
1145
- MessageFormatter.progress(
1146
- `Updating function specifications for ${functionId} to ${specification}`,
1147
- { prefix: "Functions" }
1148
- );
1149
- await updateFunctionSpecifications(
1150
- this.appwriteServer,
1151
- functionId,
1152
- specification
1153
- );
1154
- MessageFormatter.success(
1155
- `Successfully updated function specifications for ${functionId} to ${specification}`,
1156
- { prefix: "Functions" }
1157
- );
1158
- }
1159
-
1160
- /**
1161
- * Validates the current configuration for collections/tables conflicts
1162
- */
1163
- async validateConfiguration(strictMode: boolean = false): Promise<ValidationResult> {
1164
- await this.init();
1165
- if (!this.config) {
1166
- throw new Error("Configuration not loaded");
1167
- }
1168
-
1169
- MessageFormatter.progress("Validating configuration...", { prefix: "Validation" });
1170
-
1171
- const validation = strictMode
1172
- ? validateWithStrictMode(this.config, strictMode)
1173
- : validateCollectionsTablesConfig(this.config);
1174
-
1175
- reportValidationResults(validation, { verbose: true });
1176
-
1177
- if (validation.isValid) {
1178
- MessageFormatter.success("Configuration validation passed", { prefix: "Validation" });
1179
- } else {
1180
- MessageFormatter.error(`Configuration validation failed with ${validation.errors.length} errors`, undefined, { prefix: "Validation" });
1181
- }
1182
-
1183
- return validation;
1184
- }
1185
-
1186
- /**
1187
- * Get current session information for debugging/logging purposes
1188
- * Delegates to ConfigManager for session info
1189
- */
1190
- public async getSessionInfo(): Promise<{
1191
- hasSession: boolean;
1192
- authMethod?: string;
1193
- email?: string;
1194
- expiresAt?: string;
1195
- }> {
1196
- const configManager = ConfigManager.getInstance();
1197
-
1198
- try {
1199
- const authStatus = await configManager.getAuthStatus();
1200
- return {
1201
- hasSession: authStatus.hasValidSession,
1202
- authMethod: authStatus.authMethod,
1203
- email: authStatus.sessionInfo?.email,
1204
- expiresAt: authStatus.sessionInfo?.expiresAt
1205
- };
1206
- } catch (error) {
1207
- // If config not loaded, return empty status
1208
- return {
1209
- hasSession: false
1210
- };
1211
- }
1212
- }
1213
- }