appwrite-cli 13.0.0-rc.1 → 13.0.0-rc.3

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 (430) hide show
  1. package/.github/workflows/publish.yml +68 -0
  2. package/CHANGELOG.md +10 -1
  3. package/LICENSE.md +1 -1
  4. package/README.md +3 -3
  5. package/cli.ts +152 -0
  6. package/dist/bundle.cjs +95813 -0
  7. package/dist/cli.d.ts +3 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +145 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/index.d.ts +10 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +7 -142
  14. package/dist/index.js.map +1 -1
  15. package/dist/lib/client.d.ts +2 -3
  16. package/dist/lib/client.d.ts.map +1 -1
  17. package/dist/lib/client.js +57 -45
  18. package/dist/lib/client.js.map +1 -1
  19. package/dist/lib/commands/config.d.ts +562 -0
  20. package/dist/lib/commands/config.d.ts.map +1 -0
  21. package/dist/lib/commands/config.js +416 -0
  22. package/dist/lib/commands/config.js.map +1 -0
  23. package/dist/lib/commands/db.d.ts +34 -0
  24. package/dist/lib/commands/db.d.ts.map +1 -0
  25. package/dist/lib/commands/db.js +247 -0
  26. package/dist/lib/commands/db.js.map +1 -0
  27. package/dist/lib/commands/errors.d.ts +68 -0
  28. package/dist/lib/commands/errors.d.ts.map +1 -0
  29. package/dist/lib/commands/errors.js +72 -0
  30. package/dist/lib/commands/errors.js.map +1 -0
  31. package/dist/lib/commands/generic.d.ts +2 -2
  32. package/dist/lib/commands/generic.d.ts.map +1 -1
  33. package/dist/lib/commands/generic.js +170 -157
  34. package/dist/lib/commands/generic.js.map +1 -1
  35. package/dist/lib/commands/init.d.ts +1 -1
  36. package/dist/lib/commands/init.d.ts.map +1 -1
  37. package/dist/lib/commands/init.js +201 -192
  38. package/dist/lib/commands/init.js.map +1 -1
  39. package/dist/lib/commands/pull.d.ts +105 -3
  40. package/dist/lib/commands/pull.d.ts.map +1 -1
  41. package/dist/lib/commands/pull.js +530 -370
  42. package/dist/lib/commands/pull.js.map +1 -1
  43. package/dist/lib/commands/push.d.ts +106 -0
  44. package/dist/lib/commands/push.d.ts.map +1 -1
  45. package/dist/lib/commands/push.js +1432 -1830
  46. package/dist/lib/commands/push.js.map +1 -1
  47. package/dist/lib/commands/run.d.ts +1 -1
  48. package/dist/lib/commands/run.d.ts.map +1 -1
  49. package/dist/lib/commands/run.js +129 -127
  50. package/dist/lib/commands/run.js.map +1 -1
  51. package/dist/lib/commands/schema.d.ts +59 -0
  52. package/dist/lib/commands/schema.d.ts.map +1 -0
  53. package/dist/lib/commands/schema.js +86 -0
  54. package/dist/lib/commands/schema.js.map +1 -0
  55. package/dist/lib/commands/services/account.d.ts +3 -0
  56. package/dist/lib/commands/services/account.d.ts.map +1 -0
  57. package/dist/lib/commands/services/account.js +328 -0
  58. package/dist/lib/commands/services/account.js.map +1 -0
  59. package/dist/lib/commands/services/console.d.ts +3 -0
  60. package/dist/lib/commands/services/console.d.ts.map +1 -0
  61. package/dist/lib/commands/services/console.js +28 -0
  62. package/dist/lib/commands/services/console.js.map +1 -0
  63. package/dist/lib/commands/services/databases.d.ts +3 -0
  64. package/dist/lib/commands/services/databases.d.ts.map +1 -0
  65. package/dist/lib/commands/services/databases.js +620 -0
  66. package/dist/lib/commands/services/databases.js.map +1 -0
  67. package/dist/lib/commands/services/functions.d.ts +3 -0
  68. package/dist/lib/commands/services/functions.d.ts.map +1 -0
  69. package/dist/lib/commands/services/functions.js +266 -0
  70. package/dist/lib/commands/services/functions.js.map +1 -0
  71. package/dist/lib/commands/services/graphql.d.ts +3 -0
  72. package/dist/lib/commands/services/graphql.d.ts.map +1 -0
  73. package/dist/lib/commands/services/graphql.js +28 -0
  74. package/dist/lib/commands/services/graphql.js.map +1 -0
  75. package/dist/lib/commands/services/health.d.ts +3 -0
  76. package/dist/lib/commands/services/health.d.ts.map +1 -0
  77. package/dist/lib/commands/services/health.js +123 -0
  78. package/dist/lib/commands/services/health.js.map +1 -0
  79. package/dist/lib/commands/services/locale.d.ts +3 -0
  80. package/dist/lib/commands/services/locale.d.ts.map +1 -0
  81. package/dist/lib/commands/services/locale.js +52 -0
  82. package/dist/lib/commands/services/locale.js.map +1 -0
  83. package/dist/lib/commands/services/messaging.d.ts +3 -0
  84. package/dist/lib/commands/services/messaging.d.ts.map +1 -0
  85. package/dist/lib/commands/services/messaging.js +505 -0
  86. package/dist/lib/commands/services/messaging.js.map +1 -0
  87. package/dist/lib/commands/services/migrations.d.ts +3 -0
  88. package/dist/lib/commands/services/migrations.d.ts.map +1 -0
  89. package/dist/lib/commands/services/migrations.js +135 -0
  90. package/dist/lib/commands/services/migrations.js.map +1 -0
  91. package/dist/lib/commands/services/project.d.ts +3 -0
  92. package/dist/lib/commands/services/project.d.ts.map +1 -0
  93. package/dist/lib/commands/services/project.js +54 -0
  94. package/dist/lib/commands/services/project.js.map +1 -0
  95. package/dist/lib/commands/services/projects.d.ts +3 -0
  96. package/dist/lib/commands/services/projects.d.ts.map +1 -0
  97. package/dist/lib/commands/services/projects.js +415 -0
  98. package/dist/lib/commands/services/projects.js.map +1 -0
  99. package/dist/lib/commands/services/proxy.d.ts +3 -0
  100. package/dist/lib/commands/services/proxy.d.ts.map +1 -0
  101. package/dist/lib/commands/services/proxy.js +68 -0
  102. package/dist/lib/commands/services/proxy.js.map +1 -0
  103. package/dist/lib/commands/services/sites.d.ts +3 -0
  104. package/dist/lib/commands/services/sites.d.ts.map +1 -0
  105. package/dist/lib/commands/services/sites.js +250 -0
  106. package/dist/lib/commands/services/sites.js.map +1 -0
  107. package/dist/lib/commands/services/storage.d.ts +3 -0
  108. package/dist/lib/commands/services/storage.d.ts.map +1 -0
  109. package/dist/lib/commands/services/storage.js +175 -0
  110. package/dist/lib/commands/services/storage.js.map +1 -0
  111. package/dist/lib/commands/services/tables-db.d.ts +3 -0
  112. package/dist/lib/commands/services/tables-db.d.ts.map +1 -0
  113. package/dist/lib/commands/services/tables-db.js +613 -0
  114. package/dist/lib/commands/services/tables-db.js.map +1 -0
  115. package/dist/lib/commands/services/teams.d.ts +3 -0
  116. package/dist/lib/commands/services/teams.d.ts.map +1 -0
  117. package/dist/lib/commands/services/teams.js +123 -0
  118. package/dist/lib/commands/services/teams.js.map +1 -0
  119. package/dist/lib/commands/services/tokens.d.ts +3 -0
  120. package/dist/lib/commands/services/tokens.d.ts.map +1 -0
  121. package/dist/lib/commands/services/tokens.js +49 -0
  122. package/dist/lib/commands/services/tokens.js.map +1 -0
  123. package/dist/lib/commands/services/users.d.ts +3 -0
  124. package/dist/lib/commands/services/users.d.ts.map +1 -0
  125. package/dist/lib/commands/services/users.js +312 -0
  126. package/dist/lib/commands/services/users.js.map +1 -0
  127. package/dist/lib/commands/services/vcs.d.ts +3 -0
  128. package/dist/lib/commands/services/vcs.d.ts.map +1 -0
  129. package/dist/lib/commands/services/vcs.js +87 -0
  130. package/dist/lib/commands/services/vcs.js.map +1 -0
  131. package/dist/lib/commands/types.d.ts +1 -1
  132. package/dist/lib/commands/types.d.ts.map +1 -1
  133. package/dist/lib/commands/types.js +53 -57
  134. package/dist/lib/commands/types.js.map +1 -1
  135. package/dist/lib/commands/update.d.ts +1 -1
  136. package/dist/lib/commands/update.d.ts.map +1 -1
  137. package/dist/lib/commands/update.js +69 -69
  138. package/dist/lib/commands/update.js.map +1 -1
  139. package/dist/lib/commands/utils/attributes.d.ts +47 -0
  140. package/dist/lib/commands/utils/attributes.d.ts.map +1 -0
  141. package/dist/lib/commands/utils/attributes.js +514 -0
  142. package/dist/lib/commands/utils/attributes.js.map +1 -0
  143. package/dist/lib/commands/utils/change-approval.d.ts +25 -0
  144. package/dist/lib/commands/utils/change-approval.d.ts.map +1 -0
  145. package/dist/lib/commands/utils/change-approval.js +129 -0
  146. package/dist/lib/commands/utils/change-approval.js.map +1 -0
  147. package/dist/lib/commands/utils/database-sync.d.ts +10 -0
  148. package/dist/lib/commands/utils/database-sync.d.ts.map +1 -0
  149. package/dist/lib/commands/utils/database-sync.js +136 -0
  150. package/dist/lib/commands/utils/database-sync.js.map +1 -0
  151. package/dist/lib/commands/utils/deployment.d.ts +34 -0
  152. package/dist/lib/commands/utils/deployment.d.ts.map +1 -0
  153. package/dist/lib/commands/utils/deployment.js +109 -0
  154. package/dist/lib/commands/utils/deployment.js.map +1 -0
  155. package/dist/lib/commands/utils/error-formatter.d.ts +19 -0
  156. package/dist/lib/commands/utils/error-formatter.d.ts.map +1 -0
  157. package/dist/lib/commands/utils/error-formatter.js +333 -0
  158. package/dist/lib/commands/utils/error-formatter.js.map +1 -0
  159. package/dist/lib/commands/utils/pools.d.ts +16 -0
  160. package/dist/lib/commands/utils/pools.d.ts.map +1 -0
  161. package/dist/lib/commands/utils/pools.js +198 -0
  162. package/dist/lib/commands/utils/pools.js.map +1 -0
  163. package/dist/lib/config.d.ts +41 -40
  164. package/dist/lib/config.d.ts.map +1 -1
  165. package/dist/lib/config.js +264 -239
  166. package/dist/lib/config.js.map +1 -1
  167. package/dist/lib/constants.d.ts +14 -0
  168. package/dist/lib/constants.d.ts.map +1 -0
  169. package/dist/lib/constants.js +19 -0
  170. package/dist/lib/constants.js.map +1 -0
  171. package/dist/lib/emulation/docker.d.ts +4 -12
  172. package/dist/lib/emulation/docker.d.ts.map +1 -1
  173. package/dist/lib/emulation/docker.js +159 -142
  174. package/dist/lib/emulation/docker.js.map +1 -1
  175. package/dist/lib/emulation/utils.d.ts +1 -1
  176. package/dist/lib/emulation/utils.d.ts.map +1 -1
  177. package/dist/lib/emulation/utils.js +55 -58
  178. package/dist/lib/emulation/utils.js.map +1 -1
  179. package/dist/lib/id.d.ts +1 -1
  180. package/dist/lib/id.d.ts.map +1 -1
  181. package/dist/lib/id.js +13 -18
  182. package/dist/lib/id.js.map +1 -1
  183. package/dist/lib/paginate.d.ts +3 -4
  184. package/dist/lib/paginate.d.ts.map +1 -1
  185. package/dist/lib/paginate.js +7 -10
  186. package/dist/lib/paginate.js.map +1 -1
  187. package/dist/lib/parser.d.ts +1 -1
  188. package/dist/lib/parser.d.ts.map +1 -1
  189. package/dist/lib/parser.js +92 -103
  190. package/dist/lib/parser.js.map +1 -1
  191. package/dist/lib/questions.d.ts +1 -1
  192. package/dist/lib/questions.d.ts.map +1 -1
  193. package/dist/lib/questions.js +381 -385
  194. package/dist/lib/questions.js.map +1 -1
  195. package/dist/lib/sdks.d.ts +1 -1
  196. package/dist/lib/sdks.d.ts.map +1 -1
  197. package/dist/lib/sdks.js +39 -30
  198. package/dist/lib/sdks.js.map +1 -1
  199. package/dist/lib/services.d.ts +13 -0
  200. package/dist/lib/services.d.ts.map +1 -0
  201. package/dist/lib/services.js +47 -0
  202. package/dist/lib/services.js.map +1 -0
  203. package/dist/lib/spinner.d.ts +1 -1
  204. package/dist/lib/spinner.d.ts.map +1 -1
  205. package/dist/lib/spinner.js +25 -27
  206. package/dist/lib/spinner.js.map +1 -1
  207. package/dist/lib/type-generation/attribute.d.ts +1 -1
  208. package/dist/lib/type-generation/attribute.d.ts.map +1 -1
  209. package/dist/lib/type-generation/attribute.js +14 -17
  210. package/dist/lib/type-generation/attribute.js.map +1 -1
  211. package/dist/lib/type-generation/languages/csharp.d.ts +1 -1
  212. package/dist/lib/type-generation/languages/csharp.d.ts.map +1 -1
  213. package/dist/lib/type-generation/languages/csharp.js +34 -34
  214. package/dist/lib/type-generation/languages/csharp.js.map +1 -1
  215. package/dist/lib/type-generation/languages/dart.d.ts +1 -1
  216. package/dist/lib/type-generation/languages/dart.d.ts.map +1 -1
  217. package/dist/lib/type-generation/languages/dart.js +57 -57
  218. package/dist/lib/type-generation/languages/dart.js.map +1 -1
  219. package/dist/lib/type-generation/languages/java.d.ts +1 -1
  220. package/dist/lib/type-generation/languages/java.d.ts.map +1 -1
  221. package/dist/lib/type-generation/languages/java.js +35 -35
  222. package/dist/lib/type-generation/languages/java.js.map +1 -1
  223. package/dist/lib/type-generation/languages/javascript.d.ts +1 -1
  224. package/dist/lib/type-generation/languages/javascript.d.ts.map +1 -1
  225. package/dist/lib/type-generation/languages/javascript.js +45 -44
  226. package/dist/lib/type-generation/languages/javascript.js.map +1 -1
  227. package/dist/lib/type-generation/languages/kotlin.d.ts +1 -1
  228. package/dist/lib/type-generation/languages/kotlin.d.ts.map +1 -1
  229. package/dist/lib/type-generation/languages/kotlin.js +35 -35
  230. package/dist/lib/type-generation/languages/kotlin.js.map +1 -1
  231. package/dist/lib/type-generation/languages/language.d.ts.map +1 -1
  232. package/dist/lib/type-generation/languages/language.js +32 -37
  233. package/dist/lib/type-generation/languages/language.js.map +1 -1
  234. package/dist/lib/type-generation/languages/php.d.ts +1 -1
  235. package/dist/lib/type-generation/languages/php.d.ts.map +1 -1
  236. package/dist/lib/type-generation/languages/php.js +34 -34
  237. package/dist/lib/type-generation/languages/php.js.map +1 -1
  238. package/dist/lib/type-generation/languages/swift.d.ts +1 -1
  239. package/dist/lib/type-generation/languages/swift.d.ts.map +1 -1
  240. package/dist/lib/type-generation/languages/swift.js +35 -35
  241. package/dist/lib/type-generation/languages/swift.js.map +1 -1
  242. package/dist/lib/type-generation/languages/typescript.d.ts +1 -1
  243. package/dist/lib/type-generation/languages/typescript.d.ts.map +1 -1
  244. package/dist/lib/type-generation/languages/typescript.js +49 -46
  245. package/dist/lib/type-generation/languages/typescript.js.map +1 -1
  246. package/dist/lib/types.d.ts +38 -108
  247. package/dist/lib/types.d.ts.map +1 -1
  248. package/dist/lib/types.js +1 -2
  249. package/dist/lib/utils.d.ts +3 -0
  250. package/dist/lib/utils.d.ts.map +1 -1
  251. package/dist/lib/utils.js +142 -98
  252. package/dist/lib/utils.js.map +1 -1
  253. package/dist/lib/validations.d.ts.map +1 -1
  254. package/dist/lib/validations.js +2 -6
  255. package/dist/lib/validations.js.map +1 -1
  256. package/dist/package.json +68 -0
  257. package/index.ts +25 -149
  258. package/install.ps1 +2 -2
  259. package/install.sh +1 -1
  260. package/lib/client.ts +261 -220
  261. package/lib/commands/config.ts +494 -0
  262. package/lib/commands/db.ts +324 -0
  263. package/lib/commands/errors.ts +93 -0
  264. package/lib/commands/generic.ts +371 -269
  265. package/lib/commands/init.ts +631 -519
  266. package/lib/commands/pull.ts +827 -453
  267. package/lib/commands/push.ts +2191 -2349
  268. package/lib/commands/run.ts +382 -302
  269. package/lib/commands/schema.ts +122 -0
  270. package/lib/commands/services/account.ts +647 -0
  271. package/lib/commands/services/console.ts +52 -0
  272. package/lib/commands/services/databases.ts +1163 -0
  273. package/lib/commands/services/functions.ts +536 -0
  274. package/lib/commands/services/graphql.ts +50 -0
  275. package/lib/commands/services/health.ts +260 -0
  276. package/lib/commands/services/locale.ts +102 -0
  277. package/lib/commands/services/messaging.ts +1052 -0
  278. package/lib/commands/services/migrations.ts +249 -0
  279. package/lib/commands/services/project.ts +112 -0
  280. package/lib/commands/services/projects.ts +785 -0
  281. package/lib/commands/services/proxy.ts +135 -0
  282. package/lib/commands/services/sites.ts +505 -0
  283. package/lib/commands/services/storage.ts +338 -0
  284. package/lib/commands/services/tables-db.ts +1150 -0
  285. package/lib/commands/services/teams.ts +232 -0
  286. package/lib/commands/services/tokens.ts +94 -0
  287. package/lib/commands/services/users.ts +616 -0
  288. package/lib/commands/services/vcs.ts +165 -0
  289. package/lib/commands/types.ts +145 -118
  290. package/lib/commands/update.ts +189 -159
  291. package/lib/commands/utils/attributes.ts +719 -0
  292. package/lib/commands/utils/change-approval.ts +186 -0
  293. package/lib/commands/utils/database-sync.ts +180 -0
  294. package/lib/commands/utils/deployment.ts +184 -0
  295. package/lib/commands/utils/error-formatter.ts +417 -0
  296. package/lib/commands/utils/pools.ts +355 -0
  297. package/lib/config.ts +766 -687
  298. package/lib/constants.ts +22 -0
  299. package/lib/emulation/docker.ts +277 -216
  300. package/lib/emulation/utils.ts +188 -174
  301. package/lib/id.ts +23 -23
  302. package/lib/paginate.ts +69 -55
  303. package/lib/parser.ts +220 -189
  304. package/lib/questions.ts +1024 -948
  305. package/lib/sdks.ts +84 -51
  306. package/lib/services.ts +72 -0
  307. package/lib/spinner.ts +112 -99
  308. package/lib/type-generation/attribute.ts +15 -14
  309. package/lib/type-generation/languages/csharp.ts +71 -60
  310. package/lib/type-generation/languages/dart.ts +106 -93
  311. package/lib/type-generation/languages/java.ts +69 -58
  312. package/lib/type-generation/languages/javascript.ts +84 -73
  313. package/lib/type-generation/languages/kotlin.ts +71 -60
  314. package/lib/type-generation/languages/language.ts +103 -95
  315. package/lib/type-generation/languages/php.ts +67 -56
  316. package/lib/type-generation/languages/swift.ts +71 -60
  317. package/lib/type-generation/languages/typescript.ts +93 -76
  318. package/lib/types.ts +50 -125
  319. package/lib/utils.ts +304 -233
  320. package/lib/validations.ts +17 -14
  321. package/package.json +31 -22
  322. package/scoop/appwrite.config.json +3 -3
  323. package/tsconfig.json +7 -13
  324. package/.github/workflows/autoclose.yml +0 -11
  325. package/.github/workflows/npm-publish.yml +0 -49
  326. package/dist/lib/commands/account.d.ts +0 -379
  327. package/dist/lib/commands/account.d.ts.map +0 -1
  328. package/dist/lib/commands/account.js +0 -1228
  329. package/dist/lib/commands/account.js.map +0 -1
  330. package/dist/lib/commands/console.d.ts +0 -20
  331. package/dist/lib/commands/console.d.ts.map +0 -1
  332. package/dist/lib/commands/console.js +0 -78
  333. package/dist/lib/commands/console.js.map +0 -1
  334. package/dist/lib/commands/databases.d.ts +0 -732
  335. package/dist/lib/commands/databases.d.ts.map +0 -1
  336. package/dist/lib/commands/databases.js +0 -2196
  337. package/dist/lib/commands/databases.js.map +0 -1
  338. package/dist/lib/commands/functions.d.ts +0 -310
  339. package/dist/lib/commands/functions.d.ts.map +0 -1
  340. package/dist/lib/commands/functions.js +0 -1100
  341. package/dist/lib/commands/functions.js.map +0 -1
  342. package/dist/lib/commands/graphql.d.ts +0 -19
  343. package/dist/lib/commands/graphql.d.ts.map +0 -1
  344. package/dist/lib/commands/graphql.js +0 -77
  345. package/dist/lib/commands/graphql.js.map +0 -1
  346. package/dist/lib/commands/health.d.ts +0 -153
  347. package/dist/lib/commands/health.d.ts.map +0 -1
  348. package/dist/lib/commands/health.js +0 -464
  349. package/dist/lib/commands/health.js.map +0 -1
  350. package/dist/lib/commands/locale.d.ts +0 -53
  351. package/dist/lib/commands/locale.d.ts.map +0 -1
  352. package/dist/lib/commands/locale.js +0 -165
  353. package/dist/lib/commands/locale.js.map +0 -1
  354. package/dist/lib/commands/messaging.d.ts +0 -588
  355. package/dist/lib/commands/messaging.d.ts.map +0 -1
  356. package/dist/lib/commands/messaging.js +0 -2042
  357. package/dist/lib/commands/messaging.js.map +0 -1
  358. package/dist/lib/commands/migrations.d.ts +0 -150
  359. package/dist/lib/commands/migrations.d.ts.map +0 -1
  360. package/dist/lib/commands/migrations.js +0 -524
  361. package/dist/lib/commands/migrations.js.map +0 -1
  362. package/dist/lib/commands/organizations.d.ts +0 -11
  363. package/dist/lib/commands/organizations.d.ts.map +0 -1
  364. package/dist/lib/commands/organizations.js +0 -31
  365. package/dist/lib/commands/organizations.js.map +0 -1
  366. package/dist/lib/commands/project.d.ts +0 -53
  367. package/dist/lib/commands/project.d.ts.map +0 -1
  368. package/dist/lib/commands/project.js +0 -176
  369. package/dist/lib/commands/project.js.map +0 -1
  370. package/dist/lib/commands/projects.d.ts +0 -516
  371. package/dist/lib/commands/projects.d.ts.map +0 -1
  372. package/dist/lib/commands/projects.js +0 -1590
  373. package/dist/lib/commands/projects.js.map +0 -1
  374. package/dist/lib/commands/proxy.d.ts +0 -71
  375. package/dist/lib/commands/proxy.d.ts.map +0 -1
  376. package/dist/lib/commands/proxy.js +0 -240
  377. package/dist/lib/commands/proxy.js.map +0 -1
  378. package/dist/lib/commands/sites.d.ts +0 -296
  379. package/dist/lib/commands/sites.d.ts.map +0 -1
  380. package/dist/lib/commands/sites.js +0 -1046
  381. package/dist/lib/commands/sites.js.map +0 -1
  382. package/dist/lib/commands/storage.d.ts +0 -170
  383. package/dist/lib/commands/storage.d.ts.map +0 -1
  384. package/dist/lib/commands/storage.js +0 -651
  385. package/dist/lib/commands/storage.js.map +0 -1
  386. package/dist/lib/commands/tables-db.d.ts +0 -728
  387. package/dist/lib/commands/tables-db.d.ts.map +0 -1
  388. package/dist/lib/commands/tables-db.js +0 -2198
  389. package/dist/lib/commands/tables-db.js.map +0 -1
  390. package/dist/lib/commands/teams.d.ts +0 -129
  391. package/dist/lib/commands/teams.d.ts.map +0 -1
  392. package/dist/lib/commands/teams.js +0 -403
  393. package/dist/lib/commands/teams.js.map +0 -1
  394. package/dist/lib/commands/tokens.d.ts +0 -48
  395. package/dist/lib/commands/tokens.d.ts.map +0 -1
  396. package/dist/lib/commands/tokens.js +0 -156
  397. package/dist/lib/commands/tokens.js.map +0 -1
  398. package/dist/lib/commands/users.d.ts +0 -382
  399. package/dist/lib/commands/users.d.ts.map +0 -1
  400. package/dist/lib/commands/users.js +0 -1195
  401. package/dist/lib/commands/users.js.map +0 -1
  402. package/dist/lib/commands/vcs.d.ts +0 -92
  403. package/dist/lib/commands/vcs.d.ts.map +0 -1
  404. package/dist/lib/commands/vcs.js +0 -276
  405. package/dist/lib/commands/vcs.js.map +0 -1
  406. package/dist/lib/exception.d.ts +0 -8
  407. package/dist/lib/exception.d.ts.map +0 -1
  408. package/dist/lib/exception.js +0 -16
  409. package/dist/lib/exception.js.map +0 -1
  410. package/lib/commands/account.ts +0 -1867
  411. package/lib/commands/console.ts +0 -112
  412. package/lib/commands/databases.ts +0 -3272
  413. package/lib/commands/functions.ts +0 -1587
  414. package/lib/commands/graphql.ts +0 -110
  415. package/lib/commands/health.ts +0 -753
  416. package/lib/commands/locale.ts +0 -270
  417. package/lib/commands/messaging.ts +0 -2878
  418. package/lib/commands/migrations.ts +0 -754
  419. package/lib/commands/organizations.ts +0 -46
  420. package/lib/commands/project.ts +0 -266
  421. package/lib/commands/projects.ts +0 -2370
  422. package/lib/commands/proxy.ts +0 -357
  423. package/lib/commands/sites.ts +0 -1514
  424. package/lib/commands/storage.ts +0 -919
  425. package/lib/commands/tables-db.ts +0 -3260
  426. package/lib/commands/teams.ts +0 -609
  427. package/lib/commands/tokens.ts +0 -232
  428. package/lib/commands/users.ts +0 -1804
  429. package/lib/commands/vcs.ts +0 -428
  430. package/lib/exception.ts +0 -20
@@ -1,1202 +1,623 @@
1
- import fs = require('fs');
2
- import path = require('path');
3
- import { parse as parseDotenv } from 'dotenv';
4
- import chalk from 'chalk';
1
+ import fs from "fs";
2
+ import { parse as parseDotenv } from "dotenv";
3
+ import chalk from "chalk";
5
4
  import inquirer from "inquirer";
6
- import JSONbig from "json-bigint";
7
5
  import { Command } from "commander";
8
- import ID from "../id";
9
- import { localConfig, globalConfig, KeysAttributes, KeysFunction, KeysSite, whitelistKeys, KeysTopics, KeysStorage, KeysTeams, KeysCollection, KeysTable } from "../config";
10
- import { Spinner, SPINNER_ARC, SPINNER_DOTS } from '../spinner';
11
- import { paginate } from '../paginate';
12
- import { questionsPushBuckets, questionsPushTeams, questionsPushFunctions, questionsPushSites, questionsGetEntrypoint, questionsPushCollections, questionsPushTables, questionPushChanges, questionPushChangesConfirmation, questionsPushMessagingTopics, questionsPushResources } from "../questions";
13
- import { cliConfig, actionRunner, success, warn, log, hint, error, commandDescriptions, drawTable } from "../parser";
14
- import { proxyCreateFunctionRule, proxyCreateSiteRule, proxyListRules } from './proxy';
15
- import { consoleVariables } from './console';
16
- import { sdkForConsole } from '../sdks';
17
- import { functionsGet, functionsCreate, functionsUpdate, functionsCreateDeployment, functionsGetDeployment, functionsListVariables, functionsDeleteVariable, functionsCreateVariable } from './functions';
18
- import { sitesGet, sitesCreate, sitesUpdate, sitesCreateDeployment, sitesGetDeployment, sitesCreateVariable, sitesListVariables, sitesDeleteVariable } from './sites';
6
+ import ID from "../id.js";
7
+ import { EXECUTABLE_NAME } from "../constants.js";
19
8
  import {
20
- databasesGet,
21
- databasesCreate,
22
- databasesUpdate,
23
- databasesCreateBooleanAttribute,
24
- databasesGetCollection,
25
- databasesCreateCollection,
26
- databasesCreateStringAttribute,
27
- databasesCreateIntegerAttribute,
28
- databasesCreateFloatAttribute,
29
- databasesCreateEmailAttribute,
30
- databasesCreateDatetimeAttribute,
31
- databasesCreateIndex,
32
- databasesCreateUrlAttribute,
33
- databasesCreateIpAttribute,
34
- databasesCreateEnumAttribute,
35
- databasesUpdateBooleanAttribute,
36
- databasesUpdateStringAttribute,
37
- databasesUpdateIntegerAttribute,
38
- databasesUpdateFloatAttribute,
39
- databasesUpdateEmailAttribute,
40
- databasesUpdateDatetimeAttribute,
41
- databasesUpdateUrlAttribute,
42
- databasesUpdateIpAttribute,
43
- databasesUpdateEnumAttribute,
44
- databasesUpdateRelationshipAttribute,
45
- databasesCreateRelationshipAttribute,
46
- databasesCreatePointAttribute,
47
- databasesUpdatePointAttribute,
48
- databasesCreateLineAttribute,
49
- databasesUpdateLineAttribute,
50
- databasesCreatePolygonAttribute,
51
- databasesUpdatePolygonAttribute,
52
- databasesDeleteAttribute,
53
- databasesDeleteIndex,
54
- databasesListAttributes,
55
- databasesListIndexes,
56
- databasesUpdateCollection
57
- } from "./databases";
9
+ localConfig,
10
+ globalConfig,
11
+ KeysFunction,
12
+ KeysSite,
13
+ KeysTopics,
14
+ KeysStorage,
15
+ KeysTeams,
16
+ KeysCollection,
17
+ KeysTable,
18
+ } from "../config.js";
19
+ import type { SettingsType, ConfigType } from "./config.js";
20
+ import { createSettingsObject } from "../utils.js";
21
+ import { Spinner, SPINNER_DOTS } from "../spinner.js";
22
+ import { paginate } from "../paginate.js";
23
+ import { pushDeployment } from "./utils/deployment.js";
58
24
  import {
59
- tablesDBCreate,
60
- tablesDBGet,
61
- tablesDBUpdate,
62
- tablesDBCreateTable,
63
- tablesDBGetTable,
64
- tablesDBUpdateTable,
65
- tablesDBList,
66
- tablesDBDelete,
67
- tablesDBListTables,
68
- tablesDBDeleteTable
69
- } from "./tables-db";
25
+ questionsPushBuckets,
26
+ questionsPushTeams,
27
+ questionsPushFunctions,
28
+ questionsPushSites,
29
+ questionsGetEntrypoint,
30
+ questionsPushCollections,
31
+ questionsPushTables,
32
+ questionsPushMessagingTopics,
33
+ questionsPushResources,
34
+ } from "../questions.js";
70
35
  import {
71
- storageGetBucket, storageUpdateBucket, storageCreateBucket
72
- } from "./storage";
36
+ cliConfig,
37
+ actionRunner,
38
+ success,
39
+ warn,
40
+ log,
41
+ hint,
42
+ error,
43
+ commandDescriptions,
44
+ drawTable,
45
+ } from "../parser.js";
73
46
  import {
74
- messagingGetTopic, messagingUpdateTopic, messagingCreateTopic
75
- } from "./messaging";
47
+ getProxyService,
48
+ getConsoleService,
49
+ getFunctionsService,
50
+ getSitesService,
51
+ getDatabasesService,
52
+ getTablesDBService,
53
+ getStorageService,
54
+ getMessagingService,
55
+ getTeamsService,
56
+ getProjectsService,
57
+ } from "../services.js";
58
+ import { sdkForProject, sdkForConsole } from "../sdks.js";
76
59
  import {
77
- teamsGet,
78
- teamsUpdateName,
79
- teamsCreate
80
- } from "./teams";
60
+ ApiService,
61
+ AuthMethod,
62
+ AppwriteException,
63
+ Client,
64
+ Query,
65
+ } from "@appwrite.io/console";
66
+ import { checkDeployConditions } from "../utils.js";
67
+ import { Pools } from "./utils/pools.js";
68
+ import { Attributes, Collection } from "./utils/attributes.js";
81
69
  import {
82
- projectsGet,
83
- projectsUpdate,
84
- projectsUpdateServiceStatus,
85
- projectsUpdateAuthStatus,
86
- projectsUpdateAuthDuration,
87
- projectsUpdateAuthLimit,
88
- projectsUpdateAuthSessionsLimit,
89
- projectsUpdateAuthPasswordDictionary,
90
- projectsUpdateAuthPasswordHistory,
91
- projectsUpdatePersonalDataCheck,
92
- projectsUpdateSessionAlerts,
93
- projectsUpdateMockNumbers,
94
- } from "./projects";
95
- import { checkDeployConditions } from '../utils';
96
-
97
- const JSONbigNative = JSONbig({ storeAsString: false });
98
-
99
- const STEP_SIZE = 100; // Resources
70
+ getConfirmation,
71
+ approveChanges,
72
+ getObjectChanges,
73
+ } from "./utils/change-approval.js";
74
+ import { checkAndApplyTablesDBChanges } from "./utils/database-sync.js";
75
+
100
76
  const POLL_DEBOUNCE = 2000; // Milliseconds
101
- const POLL_MAX_DEBOUNCE = 1800; // Times of POLL_DEBOUNCE (1 hour)
102
77
  const POLL_DEFAULT_VALUE = 30;
78
+ const DEPLOYMENT_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
79
+
80
+ export interface PushOptions {
81
+ all?: boolean;
82
+ settings?: boolean;
83
+ functions?: boolean;
84
+ sites?: boolean;
85
+ collections?: boolean;
86
+ tables?: boolean;
87
+ buckets?: boolean;
88
+ teams?: boolean;
89
+ topics?: boolean;
90
+ skipDeprecated?: boolean;
91
+ skipConfirmation?: boolean;
92
+ functionOptions?: {
93
+ async?: boolean;
94
+ code?: boolean;
95
+ withVariables?: boolean;
96
+ };
97
+ siteOptions?: {
98
+ async?: boolean;
99
+ code?: boolean;
100
+ withVariables?: boolean;
101
+ };
102
+ tableOptions?: {
103
+ attempts?: number;
104
+ };
105
+ }
103
106
 
104
- let pollMaxDebounces = POLL_DEFAULT_VALUE;
105
-
106
- const changeableKeys = ['status', 'required', 'xdefault', 'elements', 'min', 'max', 'default', 'error'];
107
+ interface PushSiteOptions {
108
+ siteId?: string;
109
+ async?: boolean;
110
+ code?: boolean;
111
+ withVariables?: boolean;
112
+ }
107
113
 
108
- interface AwaitPools {
109
- wipeAttributes: (databaseId: string, collectionId: string, iteration?: number) => Promise<boolean>;
110
- wipeIndexes: (databaseId: string, collectionId: string, iteration?: number) => Promise<boolean>;
111
- deleteAttributes: (databaseId: string, collectionId: string, attributeKeys: any[], iteration?: number) => Promise<boolean>;
112
- expectAttributes: (databaseId: string, collectionId: string, attributeKeys: string[], iteration?: number) => Promise<boolean>;
113
- deleteIndexes: (databaseId: string, collectionId: string, indexesKeys: any[], iteration?: number) => Promise<boolean>;
114
- expectIndexes: (databaseId: string, collectionId: string, indexKeys: string[], iteration?: number) => Promise<boolean>;
114
+ interface PushFunctionOptions {
115
+ functionId?: string;
116
+ async?: boolean;
117
+ code?: boolean;
118
+ withVariables?: boolean;
115
119
  }
116
120
 
117
- const awaitPools: AwaitPools = {
118
- wipeAttributes: async (databaseId: string, collectionId: string, iteration: number = 1): Promise<boolean> => {
119
- if (iteration > pollMaxDebounces) {
120
- return false;
121
- }
121
+ interface PushTableOptions {
122
+ attempts?: number;
123
+ skipConfirmation?: boolean;
124
+ }
122
125
 
123
- const { total } = await databasesListAttributes({
124
- databaseId,
125
- collectionId,
126
- queries: [JSON.stringify({ method: 'limit', values: [1] })],
127
- parseOutput: false
126
+ export class Push {
127
+ private projectClient: Client;
128
+ private consoleClient: Client;
129
+ private silent: boolean;
130
+
131
+ constructor(projectClient: Client, consoleClient: Client, silent = false) {
132
+ this.projectClient = projectClient;
133
+ this.consoleClient = consoleClient;
134
+ this.silent = silent;
135
+ }
136
+
137
+ /**
138
+ * Log a message (respects silent mode)
139
+ */
140
+ private log(message: string): void {
141
+ if (!this.silent) {
142
+ log(message);
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Log a success message (respects silent mode)
148
+ */
149
+ private success(message: string): void {
150
+ if (!this.silent) {
151
+ success(message);
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Log a warning message (respects silent mode)
157
+ */
158
+ private warn(message: string): void {
159
+ if (!this.silent) {
160
+ warn(message);
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Log an error message (respects silent mode)
166
+ */
167
+ private error(message: string): void {
168
+ if (!this.silent) {
169
+ error(message);
170
+ }
171
+ }
172
+
173
+ public async pushResources(
174
+ config: ConfigType,
175
+ options: PushOptions = { all: true, skipDeprecated: true },
176
+ ): Promise<{
177
+ results: Record<string, any>;
178
+ errors: any[];
179
+ }> {
180
+ const { skipDeprecated = true } = options;
181
+ const results: Record<string, any> = {};
182
+ const allErrors: any[] = [];
183
+ const shouldPushAll = options.all === true;
184
+
185
+ // Push settings
186
+ if (
187
+ (shouldPushAll || options.settings) &&
188
+ (config.projectName || config.settings)
189
+ ) {
190
+ try {
191
+ this.log("Pushing settings ...");
192
+ await this.pushSettings({
193
+ projectId: config.projectId,
194
+ projectName: config.projectName,
195
+ settings: config.settings,
128
196
  });
129
-
130
- if (total === 0) {
131
- return true;
132
- }
133
-
134
- if (pollMaxDebounces === POLL_DEFAULT_VALUE) {
135
- let steps = Math.max(1, Math.ceil(total / STEP_SIZE));
136
- if (steps > 1 && iteration === 1) {
137
- pollMaxDebounces *= steps;
138
-
139
- log('Found a large number of attributes, increasing timeout to ' + (pollMaxDebounces * POLL_DEBOUNCE / 1000 / 60) + ' minutes')
140
- }
141
- }
142
-
143
- await new Promise(resolve => setTimeout(resolve, POLL_DEBOUNCE));
144
-
145
- return await awaitPools.wipeAttributes(
146
- databaseId,
147
- collectionId,
148
- iteration + 1
197
+ this.success(
198
+ `Successfully pushed ${chalk.bold("all")} project settings.`,
149
199
  );
150
- },
151
- wipeIndexes: async (databaseId: string, collectionId: string, iteration: number = 1): Promise<boolean> => {
152
- if (iteration > pollMaxDebounces) {
153
- return false;
154
- }
155
-
156
- const { total } = await databasesListIndexes({
157
- databaseId,
158
- collectionId,
159
- queries: [JSON.stringify({ method: 'limit', values: [1] })],
160
- parseOutput: false
161
- });
162
-
163
- if (total === 0) {
164
- return true;
165
- }
166
-
167
- if (pollMaxDebounces === POLL_DEFAULT_VALUE) {
168
- let steps = Math.max(1, Math.ceil(total / STEP_SIZE));
169
- if (steps > 1 && iteration === 1) {
170
- pollMaxDebounces *= steps;
171
-
172
- log('Found a large number of indexes, increasing timeout to ' + (pollMaxDebounces * POLL_DEBOUNCE / 1000 / 60) + ' minutes')
173
- }
174
- }
175
-
176
- await new Promise(resolve => setTimeout(resolve, POLL_DEBOUNCE));
177
-
178
- return await awaitPools.wipeIndexes(
179
- databaseId,
180
- collectionId,
181
- iteration + 1
200
+ results.settings = { success: true };
201
+ } catch (e: any) {
202
+ allErrors.push(e);
203
+ results.settings = { success: false, error: e.message };
204
+ }
205
+ }
206
+
207
+ // Push buckets
208
+ if (
209
+ (shouldPushAll || options.buckets) &&
210
+ config.buckets &&
211
+ config.buckets.length > 0
212
+ ) {
213
+ try {
214
+ this.log("Pushing buckets ...");
215
+ const result = await this.pushBuckets(config.buckets);
216
+ this.success(
217
+ `Successfully pushed ${chalk.bold(result.successfullyPushed)} buckets.`,
182
218
  );
183
- },
184
- deleteAttributes: async (databaseId: string, collectionId: string, attributeKeys: any[], iteration: number = 1): Promise<boolean> => {
185
- if (iteration > pollMaxDebounces) {
186
- return false;
187
- }
188
-
189
- if (pollMaxDebounces === POLL_DEFAULT_VALUE) {
190
- let steps = Math.max(1, Math.ceil(attributeKeys.length / STEP_SIZE));
191
- if (steps > 1 && iteration === 1) {
192
- pollMaxDebounces *= steps;
193
-
194
- log('Found a large number of attributes to be deleted. Increasing timeout to ' + (pollMaxDebounces * POLL_DEBOUNCE / 1000 / 60) + ' minutes')
195
- }
196
- }
197
-
198
- const { attributes } = await paginate(databasesListAttributes, {
199
- databaseId,
200
- collectionId,
201
- parseOutput: false
202
- }, 100, 'attributes');
203
-
204
- const ready = attributeKeys.filter((attribute: any) => attributes.includes(attribute.key));
205
-
206
- if (ready.length === 0) {
207
- return true;
208
- }
209
-
210
- await new Promise(resolve => setTimeout(resolve, POLL_DEBOUNCE));
211
-
212
- return await awaitPools.expectAttributes(
213
- databaseId,
214
- collectionId,
215
- attributeKeys,
216
- iteration + 1
219
+ results.buckets = result;
220
+ allErrors.push(...result.errors);
221
+ } catch (e: any) {
222
+ allErrors.push(e);
223
+ results.buckets = { successfullyPushed: 0, errors: [e] };
224
+ }
225
+ }
226
+
227
+ // Push teams
228
+ if (
229
+ (shouldPushAll || options.teams) &&
230
+ config.teams &&
231
+ config.teams.length > 0
232
+ ) {
233
+ try {
234
+ this.log("Pushing teams ...");
235
+ const result = await this.pushTeams(config.teams);
236
+ this.success(
237
+ `Successfully pushed ${chalk.bold(result.successfullyPushed)} teams.`,
217
238
  );
218
- },
219
- expectAttributes: async (databaseId: string, collectionId: string, attributeKeys: string[], iteration: number = 1): Promise<boolean> => {
220
- if (iteration > pollMaxDebounces) {
221
- return false;
222
- }
223
-
224
- if (pollMaxDebounces === POLL_DEFAULT_VALUE) {
225
- let steps = Math.max(1, Math.ceil(attributeKeys.length / STEP_SIZE));
226
- if (steps > 1 && iteration === 1) {
227
- pollMaxDebounces *= steps;
228
-
229
- log('Creating a large number of attributes, increasing timeout to ' + (pollMaxDebounces * POLL_DEBOUNCE / 1000 / 60) + ' minutes')
230
- }
231
- }
232
-
233
- const { attributes } = await paginate(databasesListAttributes, {
234
- databaseId,
235
- collectionId,
236
- parseOutput: false
237
- }, 100, 'attributes');
238
-
239
- const ready = attributes
240
- .filter((attribute: any) => {
241
- if (attributeKeys.includes(attribute.key)) {
242
- if (['stuck', 'failed'].includes(attribute.status)) {
243
- throw new Error(`Attribute '${attribute.key}' failed!`);
244
- }
245
-
246
- return attribute.status === 'available';
247
- }
248
-
249
- return false;
250
- })
251
- .map((attribute: any) => attribute.key);
252
-
253
- if (ready.length === attributeKeys.length) {
254
- return true;
255
- }
256
-
257
- await new Promise(resolve => setTimeout(resolve, POLL_DEBOUNCE));
258
-
259
- return await awaitPools.expectAttributes(
260
- databaseId,
261
- collectionId,
262
- attributeKeys,
263
- iteration + 1
239
+ results.teams = result;
240
+ allErrors.push(...result.errors);
241
+ } catch (e: any) {
242
+ allErrors.push(e);
243
+ results.teams = { successfullyPushed: 0, errors: [e] };
244
+ }
245
+ }
246
+
247
+ // Push messaging topics
248
+ if (
249
+ (shouldPushAll || options.topics) &&
250
+ config.topics &&
251
+ config.topics.length > 0
252
+ ) {
253
+ try {
254
+ this.log("Pushing topics ...");
255
+ const result = await this.pushMessagingTopics(config.topics);
256
+ this.success(
257
+ `Successfully pushed ${chalk.bold(result.successfullyPushed)} topics.`,
264
258
  );
265
- },
266
- deleteIndexes: async (databaseId: string, collectionId: string, indexesKeys: any[], iteration: number = 1): Promise<boolean> => {
267
- if (iteration > pollMaxDebounces) {
268
- return false;
269
- }
270
-
271
- if (pollMaxDebounces === POLL_DEFAULT_VALUE) {
272
- let steps = Math.max(1, Math.ceil(indexesKeys.length / STEP_SIZE));
273
- if (steps > 1 && iteration === 1) {
274
- pollMaxDebounces *= steps;
275
-
276
- log('Found a large number of indexes to be deleted. Increasing timeout to ' + (pollMaxDebounces * POLL_DEBOUNCE / 1000 / 60) + ' minutes')
277
- }
278
- }
279
-
280
- const { indexes } = await paginate(databasesListIndexes, {
281
- databaseId,
282
- collectionId,
283
- parseOutput: false
284
- }, 100, 'indexes');
285
-
286
- const ready = indexesKeys.filter((index: any) => indexes.includes(index.key));
287
-
288
- if (ready.length === 0) {
289
- return true;
290
- }
291
-
292
- await new Promise(resolve => setTimeout(resolve, POLL_DEBOUNCE));
293
-
294
- return await awaitPools.expectIndexes(
295
- databaseId,
296
- collectionId,
297
- indexesKeys,
298
- iteration + 1
259
+ results.topics = result;
260
+ allErrors.push(...result.errors);
261
+ } catch (e: any) {
262
+ allErrors.push(e);
263
+ results.topics = { successfullyPushed: 0, errors: [e] };
264
+ }
265
+ }
266
+
267
+ // Push functions
268
+ if (
269
+ (shouldPushAll || options.functions) &&
270
+ config.functions &&
271
+ config.functions.length > 0
272
+ ) {
273
+ try {
274
+ this.log("Pushing functions ...");
275
+ const result = await this.pushFunctions(
276
+ config.functions,
277
+ options.functionOptions,
299
278
  );
300
- },
301
- expectIndexes: async (databaseId: string, collectionId: string, indexKeys: string[], iteration: number = 1): Promise<boolean> => {
302
- if (iteration > pollMaxDebounces) {
303
- return false;
304
- }
305
-
306
- if (pollMaxDebounces === POLL_DEFAULT_VALUE) {
307
- let steps = Math.max(1, Math.ceil(indexKeys.length / STEP_SIZE));
308
- if (steps > 1 && iteration === 1) {
309
- pollMaxDebounces *= steps;
310
-
311
- log('Creating a large number of indexes, increasing timeout to ' + (pollMaxDebounces * POLL_DEBOUNCE / 1000 / 60) + ' minutes')
312
- }
313
- }
314
-
315
- const { indexes } = await paginate(databasesListIndexes, {
316
- databaseId,
317
- collectionId,
318
- parseOutput: false
319
- }, 100, 'indexes');
320
-
321
- const ready = indexes
322
- .filter((index: any) => {
323
- if (indexKeys.includes(index.key)) {
324
- if (['stuck', 'failed'].includes(index.status)) {
325
- throw new Error(`Index '${index.key}' failed!`);
326
- }
327
-
328
- return index.status === 'available';
329
- }
330
-
331
- return false;
332
- })
333
- .map((index: any) => index.key);
334
-
335
- if (ready.length >= indexKeys.length) {
336
- return true;
337
- }
338
-
339
- await new Promise(resolve => setTimeout(resolve, POLL_DEBOUNCE));
340
-
341
- return await awaitPools.expectIndexes(
342
- databaseId,
343
- collectionId,
344
- indexKeys,
345
- iteration + 1
279
+ this.success(
280
+ `Successfully pushed ${chalk.bold(result.successfullyPushed)} functions.`,
346
281
  );
347
- },
348
- }
349
-
350
- const getConfirmation = async (): Promise<boolean> => {
351
- if (cliConfig.force) {
352
- return true;
353
- }
354
-
355
- async function fixConfirmation(): Promise<string> {
356
- const answers = await inquirer.prompt(questionPushChangesConfirmation);
357
- if (answers.changes !== 'YES' && answers.changes !== 'NO') {
358
- return await fixConfirmation();
359
- }
360
-
361
- return answers.changes;
362
- }
363
-
364
- let answers = await inquirer.prompt(questionPushChanges);
365
-
366
- if (answers.changes !== 'YES' && answers.changes !== 'NO') {
367
- answers.changes = await fixConfirmation();
368
- }
369
-
370
- if (answers.changes === 'YES') {
371
- return true;
372
- }
373
-
374
- warn('Skipping push action. Changes were not applied.');
375
- return false;
376
-
377
- };
378
- const isEmpty = (value: any): boolean => (value === null || value === undefined || (typeof value === "string" && value.trim().length === 0) || (Array.isArray(value) && value.length === 0));
379
-
380
- const approveChanges = async (resource: any[], resourceGetFunction: Function, keys: Set<string>, resourceName: string, resourcePlural: string, skipKeys: string[] = [], secondId: string = '', secondResourceName: string = ''): Promise<boolean> => {
381
- log('Checking for changes ...');
382
- const changes: any[] = [];
383
-
384
- await Promise.all(resource.map(async (localResource) => {
385
- try {
386
- const options: Record<string, any> = {
387
- [resourceName]: localResource['$id'],
388
- parseOutput: false,
282
+ results.functions = result;
283
+ allErrors.push(...result.errors);
284
+ } catch (e: any) {
285
+ allErrors.push(e);
286
+ results.functions = {
287
+ successfullyPushed: 0,
288
+ successfullyDeployed: 0,
289
+ failedDeployments: [],
290
+ errors: [e],
291
+ };
292
+ }
293
+ }
294
+
295
+ // Push sites
296
+ if (
297
+ (shouldPushAll || options.sites) &&
298
+ config.sites &&
299
+ config.sites.length > 0
300
+ ) {
301
+ try {
302
+ this.log("Pushing sites ...");
303
+ const result = await this.pushSites(config.sites, options.siteOptions);
304
+ this.success(
305
+ `Successfully pushed ${chalk.bold(result.successfullyPushed)} sites.`,
306
+ );
307
+ results.sites = result;
308
+ allErrors.push(...result.errors);
309
+ } catch (e: any) {
310
+ allErrors.push(e);
311
+ results.sites = {
312
+ successfullyPushed: 0,
313
+ successfullyDeployed: 0,
314
+ failedDeployments: [],
315
+ errors: [e],
316
+ };
317
+ }
318
+ }
319
+
320
+ // Push tables
321
+ if (
322
+ (shouldPushAll || options.tables) &&
323
+ config.tables &&
324
+ config.tables.length > 0
325
+ ) {
326
+ try {
327
+ this.log("Pushing tables ...");
328
+ const result = await this.pushTables(config.tables, {
329
+ attempts: options.tableOptions?.attempts,
330
+ skipConfirmation: options.skipConfirmation,
331
+ });
332
+ this.success(
333
+ `Successfully pushed ${chalk.bold(result.successfullyPushed)} tables.`,
334
+ );
335
+ results.tables = result;
336
+ allErrors.push(...result.errors);
337
+ } catch (e: any) {
338
+ allErrors.push(e);
339
+ results.tables = { successfullyPushed: 0, errors: [e] };
340
+ }
341
+ }
342
+
343
+ // Push collections (skipDeprecated only applies when pushing all, explicit collections option takes precedence)
344
+ if (
345
+ (options.collections || (shouldPushAll && !skipDeprecated)) &&
346
+ config.collections &&
347
+ config.collections.length > 0
348
+ ) {
349
+ try {
350
+ this.log("Pushing collections ...");
351
+ // Add database names to collections
352
+ const collectionsWithDbNames = config.collections.map(
353
+ (collection: any) => {
354
+ const database = config.databases?.find(
355
+ (db: any) => db.$id === collection.databaseId,
356
+ );
357
+ return {
358
+ ...collection,
359
+ databaseName: database?.name ?? collection.databaseId,
389
360
  };
390
-
391
- if (secondId !== '' && secondResourceName !== '') {
392
- options[secondResourceName] = localResource[secondId]
393
- }
394
-
395
- const remoteResource = await resourceGetFunction(options);
396
-
397
- for (let [key, value] of Object.entries(whitelistKeys(remoteResource, keys))) {
398
- if (skipKeys.includes(key)) {
399
- continue;
400
- }
401
-
402
- if (isEmpty(value) && isEmpty(localResource[key])) {
403
- continue;
404
- }
405
-
406
- if (Array.isArray(value) && Array.isArray(localResource[key])) {
407
- if (JSON.stringify(value) !== JSON.stringify(localResource[key])) {
408
- changes.push({
409
- id: localResource['$id'],
410
- key,
411
- remote: chalk.red((value as string[]).join('\n')),
412
- local: chalk.green(localResource[key].join('\n'))
413
- })
414
- }
415
- } else if (value !== localResource[key]) {
416
- changes.push({
417
- id: localResource['$id'],
418
- key,
419
- remote: chalk.red(value),
420
- local: chalk.green(localResource[key])
421
- })
422
- }
423
- }
424
- } catch (e: any) {
425
- if (Number(e.code) !== 404) {
426
- throw e;
427
- }
428
- }
429
- }));
430
-
431
- if (changes.length === 0) {
432
- return true;
433
- }
434
-
435
- drawTable(changes);
436
- if ((await getConfirmation()) === true) {
437
- return true;
438
- }
439
-
440
- success(`Successfully pushed 0 ${resourcePlural}.`);
441
- return false;
442
- }
443
-
444
- const getObjectChanges = (remote: any, local: any, index: string, what: string): any[] => {
445
- const changes: any[] = [];
446
-
447
- if (remote[index] && local[index]) {
448
- for (let [service, status] of Object.entries(remote[index])) {
449
- const localValue = local[index][service];
450
- let valuesEqual = false;
451
-
452
- if (Array.isArray(status) && Array.isArray(localValue)) {
453
- valuesEqual = JSON.stringify(status) === JSON.stringify(localValue);
454
- } else {
455
- valuesEqual = status === localValue;
456
- }
457
-
458
- if (!valuesEqual) {
459
- changes.push({ group: what, setting: service, remote: chalk.red(status), local: chalk.green(localValue) })
460
- }
461
- }
462
- }
463
-
464
- return changes;
465
- }
466
-
467
- const createAttribute = (databaseId: string, collectionId: string, attribute: any): Promise<any> => {
468
- switch (attribute.type) {
469
- case 'string':
470
- switch (attribute.format) {
471
- case 'email':
472
- return databasesCreateEmailAttribute({
473
- databaseId,
474
- collectionId,
475
- key: attribute.key,
476
- required: attribute.required,
477
- xdefault: attribute.default,
478
- array: attribute.array,
479
- parseOutput: false
480
- })
481
- case 'url':
482
- return databasesCreateUrlAttribute({
483
- databaseId,
484
- collectionId,
485
- key: attribute.key,
486
- required: attribute.required,
487
- xdefault: attribute.default,
488
- array: attribute.array,
489
- parseOutput: false
490
- })
491
- case 'ip':
492
- return databasesCreateIpAttribute({
493
- databaseId,
494
- collectionId,
495
- key: attribute.key,
496
- required: attribute.required,
497
- xdefault: attribute.default,
498
- array: attribute.array,
499
- parseOutput: false
500
- })
501
- case 'enum':
502
- return databasesCreateEnumAttribute({
503
- databaseId,
504
- collectionId,
505
- key: attribute.key,
506
- elements: attribute.elements,
507
- required: attribute.required,
508
- xdefault: attribute.default,
509
- array: attribute.array,
510
- parseOutput: false
511
- })
512
- default:
513
- return databasesCreateStringAttribute({
514
- databaseId,
515
- collectionId,
516
- key: attribute.key,
517
- size: attribute.size,
518
- required: attribute.required,
519
- xdefault: attribute.default,
520
- array: attribute.array,
521
- encrypt: attribute.encrypt,
522
- parseOutput: false
523
- })
524
-
525
- }
526
- case 'integer':
527
- return databasesCreateIntegerAttribute({
528
- databaseId,
529
- collectionId,
530
- key: attribute.key,
531
- required: attribute.required,
532
- min: attribute.min,
533
- max: attribute.max,
534
- xdefault: attribute.default,
535
- array: attribute.array,
536
- parseOutput: false
537
- })
538
- case 'double':
539
- return databasesCreateFloatAttribute({
540
- databaseId,
541
- collectionId,
542
- key: attribute.key,
543
- required: attribute.required,
544
- min: attribute.min,
545
- max: attribute.max,
546
- xdefault: attribute.default,
547
- array: attribute.array,
548
- parseOutput: false
549
- })
550
- case 'boolean':
551
- return databasesCreateBooleanAttribute({
552
- databaseId,
553
- collectionId,
554
- key: attribute.key,
555
- required: attribute.required,
556
- xdefault: attribute.default,
557
- array: attribute.array,
558
- parseOutput: false
559
- })
560
- case 'datetime':
561
- return databasesCreateDatetimeAttribute({
562
- databaseId,
563
- collectionId,
564
- key: attribute.key,
565
- required: attribute.required,
566
- xdefault: attribute.default,
567
- array: attribute.array,
568
- parseOutput: false
569
- })
570
- case 'relationship':
571
- return databasesCreateRelationshipAttribute({
572
- databaseId,
573
- collectionId,
574
- relatedCollectionId: attribute.relatedTable ?? attribute.relatedCollection,
575
- type: attribute.relationType,
576
- twoWay: attribute.twoWay,
577
- key: attribute.key,
578
- twoWayKey: attribute.twoWayKey,
579
- onDelete: attribute.onDelete,
580
- parseOutput: false
581
- })
582
- case 'point':
583
- return databasesCreatePointAttribute({
584
- databaseId,
585
- collectionId,
586
- key:attribute.key,
587
- required:attribute.required,
588
- xdefault:attribute.default,
589
- parseOutput:false
590
- })
591
- case 'linestring':
592
- return databasesCreateLineAttribute({
593
- databaseId,
594
- collectionId,
595
- key:attribute.key,
596
- required:attribute.required,
597
- xdefault:attribute.default,
598
- parseOutput:false
599
- })
600
- case 'polygon':
601
- return databasesCreatePolygonAttribute({
602
- databaseId,
603
- collectionId,
604
- key:attribute.key,
605
- required:attribute.required,
606
- xdefault:attribute.default,
607
- parseOutput:false
608
- })
609
- default:
610
- throw new Error(`Unsupported attribute type: ${attribute.type}`);
611
- }
612
- }
613
-
614
- const updateAttribute = (databaseId: string, collectionId: string, attribute: any): Promise<any> => {
615
- switch (attribute.type) {
616
- case 'string':
617
- switch (attribute.format) {
618
- case 'email':
619
- return databasesUpdateEmailAttribute({
620
- databaseId,
621
- collectionId,
622
- key: attribute.key,
623
- required: attribute.required,
624
- xdefault: attribute.default,
625
- array: attribute.array,
626
- parseOutput: false
627
- })
628
- case 'url':
629
- return databasesUpdateUrlAttribute({
630
- databaseId,
631
- collectionId,
632
- key: attribute.key,
633
- required: attribute.required,
634
- xdefault: attribute.default,
635
- array: attribute.array,
636
- parseOutput: false
637
- })
638
- case 'ip':
639
- return databasesUpdateIpAttribute({
640
- databaseId,
641
- collectionId,
642
- key: attribute.key,
643
- required: attribute.required,
644
- xdefault: attribute.default,
645
- array: attribute.array,
646
- parseOutput: false
647
- })
648
- case 'enum':
649
- return databasesUpdateEnumAttribute({
650
- databaseId,
651
- collectionId,
652
- key: attribute.key,
653
- elements: attribute.elements,
654
- required: attribute.required,
655
- xdefault: attribute.default,
656
- array: attribute.array,
657
- parseOutput: false
658
- })
659
- default:
660
- return databasesUpdateStringAttribute({
661
- databaseId,
662
- collectionId,
663
- key: attribute.key,
664
- size: attribute.size,
665
- required: attribute.required,
666
- xdefault: attribute.default,
667
- array: attribute.array,
668
- parseOutput: false
669
- })
670
-
671
- }
672
- case 'integer':
673
- return databasesUpdateIntegerAttribute({
674
- databaseId,
675
- collectionId,
676
- key: attribute.key,
677
- required: attribute.required,
678
- min: attribute.min,
679
- max: attribute.max,
680
- xdefault: attribute.default,
681
- array: attribute.array,
682
- parseOutput: false
683
- })
684
- case 'double':
685
- return databasesUpdateFloatAttribute({
686
- databaseId,
687
- collectionId,
688
- key: attribute.key,
689
- required: attribute.required,
690
- min: attribute.min,
691
- max: attribute.max,
692
- xdefault: attribute.default,
693
- array: attribute.array,
694
- parseOutput: false
695
- })
696
- case 'boolean':
697
- return databasesUpdateBooleanAttribute({
698
- databaseId,
699
- collectionId,
700
- key: attribute.key,
701
- required: attribute.required,
702
- xdefault: attribute.default,
703
- array: attribute.array,
704
- parseOutput: false
705
- })
706
- case 'datetime':
707
- return databasesUpdateDatetimeAttribute({
708
- databaseId,
709
- collectionId,
710
- key: attribute.key,
711
- required: attribute.required,
712
- xdefault: attribute.default,
713
- array: attribute.array,
714
- parseOutput: false
715
- })
716
- case 'relationship':
717
- return databasesUpdateRelationshipAttribute({
718
- databaseId,
719
- collectionId,
720
- relatedCollectionId: attribute.relatedTable ?? attribute.relatedCollection,
721
- type: attribute.relationType,
722
- twoWay: attribute.twoWay,
723
- key: attribute.key,
724
- twoWayKey: attribute.twoWayKey,
725
- onDelete: attribute.onDelete,
726
- parseOutput: false
727
- })
728
- case 'point':
729
- return databasesUpdatePointAttribute({
730
- databaseId,
731
- collectionId,
732
- key:attribute.key,
733
- required:attribute.required,
734
- xdefault:attribute.default,
735
- parseOutput:false
736
- })
737
- case 'linestring':
738
- return databasesUpdateLineAttribute({
739
- databaseId,
740
- collectionId,
741
- key:attribute.key,
742
- required:attribute.required,
743
- xdefault:attribute.default,
744
- parseOutput:false
745
- })
746
- case 'polygon':
747
- return databasesUpdatePolygonAttribute({
748
- databaseId,
749
- collectionId,
750
- key:attribute.key,
751
- required:attribute.required,
752
- xdefault:attribute.default,
753
- parseOutput:false
754
- })
755
- default:
756
- throw new Error(`Unsupported attribute type: ${attribute.type}`);
757
- }
758
- }
759
- const deleteAttribute = async (collection: any, attribute: any, isIndex: boolean = false): Promise<void> => {
760
- log(`Deleting ${isIndex ? 'index' : 'attribute'} ${attribute.key} of ${collection.name} ( ${collection['$id']} )`);
761
-
762
- if (isIndex) {
763
- await databasesDeleteIndex({
764
- databaseId: collection['databaseId'],
765
- collectionId: collection['$id'],
766
- key: attribute.key,
767
- parseOutput: false
361
+ },
362
+ );
363
+ const result = await this.pushCollections(collectionsWithDbNames, {
364
+ skipConfirmation: options.skipConfirmation,
768
365
  });
769
- return;
770
- }
771
-
772
- await databasesDeleteAttribute({
773
- databaseId: collection['databaseId'],
774
- collectionId: collection['$id'],
775
- key: attribute.key,
776
- parseOutput: false
777
- });
778
- }
779
-
780
- const isEqual = (a: any, b: any): boolean => {
781
- if (a === b) return true;
782
-
783
- if (a && b && typeof a === 'object' && typeof b === 'object') {
784
- if (a.constructor && a.constructor.name === 'BigNumber' &&
785
- b.constructor && b.constructor.name === 'BigNumber') {
786
- return a.eq(b);
787
- }
788
-
789
- if (typeof a.equals === 'function') {
790
- return a.equals(b);
791
- }
792
-
793
- if (typeof a.eq === 'function') {
794
- return a.eq(b);
795
- }
796
- }
797
-
798
- if (typeof a === 'number' && typeof b === 'number') {
799
- if (isNaN(a) && isNaN(b)) return true;
800
- if (!isFinite(a) && !isFinite(b)) return a === b;
801
- return Math.abs(a - b) < Number.EPSILON;
802
- }
803
-
804
- return false;
805
- };
806
-
807
- const compareAttribute = (remote: any, local: any, reason: string, key: string): string => {
808
- if (isEmpty(remote) && isEmpty(local)) {
809
- return reason;
810
- }
811
-
812
- if (Array.isArray(remote) && Array.isArray(local)) {
813
- if (JSON.stringify(remote) !== JSON.stringify(local)) {
814
- const bol = reason === '' ? '' : '\n';
815
- reason += `${bol}${key} changed from ${chalk.red(remote)} to ${chalk.green(local)}`;
816
- }
817
- } else if (!isEqual(remote, local)) {
818
- const bol = reason === '' ? '' : '\n';
819
- reason += `${bol}${key} changed from ${chalk.red(remote)} to ${chalk.green(local)}`;
820
- }
821
-
822
- return reason
823
- }
824
-
825
- interface AttributeChange {
826
- key: string;
827
- attribute: any;
828
- reason: string;
829
- action: string;
830
- }
831
-
832
- /**
833
- * Check if attribute non-changeable fields has been changed
834
- * If so return the differences as an object.
835
- */
836
- const checkAttributeChanges = (remote: any, local: any, collection: any, recreating: boolean = true): AttributeChange | undefined => {
837
- if (local === undefined) {
838
- return undefined;
839
- }
840
-
841
- const keyName = `${chalk.yellow(local.key)} in ${collection.name} (${collection['$id']})`;
842
- const action = chalk.cyan(recreating ? 'recreating' : 'changing');
843
- let reason = '';
844
- let attribute = recreating ? remote : local;
845
-
846
- for (let key of Object.keys(remote)) {
847
- if (!KeysAttributes.has(key)) {
848
- continue;
849
- }
850
-
851
- if (changeableKeys.includes(key)) {
852
- if (!recreating) {
853
- reason = compareAttribute(remote[key], local[key], reason, key)
854
- }
855
- continue;
856
- }
857
-
858
- if (!recreating) {
859
- continue;
860
- }
861
-
862
- reason = compareAttribute(remote[key], local[key], reason, key)
366
+ this.success(
367
+ `Successfully pushed ${chalk.bold(result.successfullyPushed)} collections.`,
368
+ );
369
+ results.collections = result;
370
+ allErrors.push(...result.errors);
371
+ } catch (e: any) {
372
+ allErrors.push(e);
373
+ results.collections = { successfullyPushed: 0, errors: [e] };
374
+ }
863
375
  }
864
376
 
865
- return reason === '' ? undefined : { key: keyName, attribute, reason, action };
866
- }
867
-
868
- /**
869
- * Check if attributes contain the given attribute
870
- */
871
- const attributesContains = (attribute: any, attributes: any[]): any => attributes.find((attr) => attr.key === attribute.key);
872
-
873
- const generateChangesObject = (attribute: any, collection: any, isAdding: boolean): AttributeChange => {
874
377
  return {
875
- key: `${chalk.yellow(attribute.key)} in ${collection.name} (${collection['$id']})`,
876
- attribute: attribute,
877
- reason: isAdding ? 'Field isn\'t present on the remote server' : 'Field isn\'t present on the appwrite.config.json file',
878
- action: isAdding ? chalk.green('adding') : chalk.red('deleting')
378
+ results,
379
+ errors: allErrors,
879
380
  };
880
- };
881
-
882
- /**
883
- * Filter deleted and recreated attributes,
884
- * return list of attributes to create
885
- */
886
- const attributesToCreate = async (remoteAttributes: any[], localAttributes: any[], collection: any, isIndex: boolean = false): Promise<any[]> => {
887
-
888
- const deleting = remoteAttributes.filter((attribute) => !attributesContains(attribute, localAttributes)).map((attr) => generateChangesObject(attr, collection, false));
889
- const adding = localAttributes.filter((attribute) => !attributesContains(attribute, remoteAttributes)).map((attr) => generateChangesObject(attr, collection, true));
890
- const conflicts = remoteAttributes.map((attribute) => checkAttributeChanges(attribute, attributesContains(attribute, localAttributes), collection)).filter(attribute => attribute !== undefined) as AttributeChange[];
891
- const changes = remoteAttributes.map((attribute) => checkAttributeChanges(attribute, attributesContains(attribute, localAttributes), collection, false))
892
- .filter(attribute => attribute !== undefined)
893
- .filter(attribute => conflicts.filter(attr => attribute!.key === attr.key).length !== 1) as AttributeChange[];
894
-
895
- let changedAttributes: any[] = [];
896
- const changing = [...deleting, ...adding, ...conflicts, ...changes]
897
- if (changing.length === 0) {
898
- return changedAttributes;
899
- }
900
-
901
- log(!cliConfig.force ? 'There are pending changes in your collection deployment' : 'List of applied changes');
902
-
903
- drawTable(changing.map((change) => {
904
- return { Key: change.key, Action: change.action, Reason: change.reason, };
905
- }));
906
-
907
- if (!cliConfig.force) {
908
- if (deleting.length > 0 && !isIndex) {
909
- console.log(`${chalk.red('------------------------------------------------------')}`);
910
- console.log(`${chalk.red('| WARNING: Attribute deletion may cause loss of data |')}`);
911
- console.log(`${chalk.red('------------------------------------------------------')}`);
912
- console.log();
913
- }
914
- if (conflicts.length > 0 && !isIndex) {
915
- console.log(`${chalk.red('--------------------------------------------------------')}`);
916
- console.log(`${chalk.red('| WARNING: Attribute recreation may cause loss of data |')}`);
917
- console.log(`${chalk.red('--------------------------------------------------------')}`);
918
- console.log();
919
- }
920
-
921
- if ((await getConfirmation()) !== true) {
922
- return changedAttributes;
923
- }
924
- }
925
-
926
- if (conflicts.length > 0) {
927
- changedAttributes = conflicts.map((change) => change.attribute);
928
- await Promise.all(changedAttributes.map((changed) => deleteAttribute(collection, changed, isIndex)));
929
- remoteAttributes = remoteAttributes.filter((attribute) => !attributesContains(attribute, changedAttributes))
930
- }
931
-
932
- if (changes.length > 0) {
933
- changedAttributes = changes.map((change) => change.attribute);
934
- await Promise.all(changedAttributes.map((changed) => updateAttribute(collection['databaseId'], collection['$id'], changed)));
935
- }
936
-
937
- const deletingAttributes = deleting.map((change) => change.attribute);
938
- await Promise.all(deletingAttributes.map((attribute) => deleteAttribute(collection, attribute, isIndex)));
939
- const attributeKeys = [...remoteAttributes.map((attribute: any) => attribute.key), ...deletingAttributes.map((attribute: any) => attribute.key)]
940
-
941
- if (attributeKeys.length) {
942
- const deleteAttributesPoolStatus = await awaitPools.deleteAttributes(collection['databaseId'], collection['$id'], attributeKeys);
943
-
944
- if (!deleteAttributesPoolStatus) {
945
- throw new Error("Attribute deletion timed out.");
946
- }
947
- }
948
-
949
- return localAttributes.filter((attribute) => !attributesContains(attribute, remoteAttributes));
950
- }
951
-
952
- const createIndexes = async (indexes: any[], collection: any): Promise<void> => {
953
- log(`Creating indexes ...`)
954
-
955
- for (let index of indexes) {
956
- await databasesCreateIndex({
957
- databaseId: collection['databaseId'],
958
- collectionId: collection['$id'],
959
- key: index.key,
960
- type: index.type,
961
- attributes: index.columns ?? index.attributes,
962
- orders: index.orders,
963
- parseOutput: false
381
+ }
382
+
383
+ public async pushSettings(config: {
384
+ projectId: string;
385
+ projectName?: string;
386
+ settings?: SettingsType;
387
+ }): Promise<void> {
388
+ const projectsService = await getProjectsService(this.consoleClient);
389
+ const projectId = config.projectId;
390
+ const projectName = config.projectName;
391
+ const settings = config.settings ?? {};
392
+
393
+ if (projectName) {
394
+ this.log("Applying project name ...");
395
+ await projectsService.update({
396
+ projectId: projectId,
397
+ name: projectName,
398
+ });
399
+ }
400
+
401
+ if (settings.services) {
402
+ this.log("Applying service statuses ...");
403
+ for (let [service, status] of Object.entries(settings.services)) {
404
+ await projectsService.updateServiceStatus({
405
+ projectId: projectId,
406
+ service: service as ApiService,
407
+ status: status,
964
408
  });
409
+ }
965
410
  }
966
411
 
967
- const result = await awaitPools.expectIndexes(
968
- collection['databaseId'],
969
- collection['$id'],
970
- indexes.map((index: any) => index.key)
971
- );
972
-
973
- if (!result) {
974
- throw new Error('Index creation timed out.');
975
- }
976
-
977
- success(`Created ${indexes.length} indexes`);
978
- }
979
-
980
- const createAttributes = async (attributes: any[], collection: any): Promise<void> => {
981
- for (let attribute of attributes) {
982
- if (attribute.side !== 'child') {
983
- await createAttribute(collection['databaseId'], collection['$id'], attribute);
984
- }
985
- }
986
-
987
- const result = await awaitPools.expectAttributes(
988
- collection['databaseId'],
989
- collection['$id'],
990
- collection.attributes.filter((attribute: any) => attribute.side !== 'child').map((attribute: any) => attribute.key)
991
- );
992
-
993
- if (!result) {
994
- throw new Error(`Attribute creation timed out.`);
995
- }
996
-
997
- success(`Created ${attributes.length} attributes`);
998
- }
999
-
1000
- const createColumns = async (columns: any[], table: any): Promise<void> => {
1001
- for (let column of columns) {
1002
- if (column.side !== 'child') {
1003
- await createAttribute(table['databaseId'], table['$id'], column);
1004
- }
1005
- }
1006
-
1007
- const result = await awaitPools.expectAttributes(
1008
- table['databaseId'],
1009
- table['$id'],
1010
- table.columns.filter((column: any) => column.side !== 'child').map((column: any) => column.key)
1011
- );
1012
-
1013
- if (!result) {
1014
- throw new Error(`Column creation timed out.`);
1015
- }
1016
-
1017
- success(`Created ${columns.length} columns`);
1018
- }
1019
-
1020
- const pushResources = async (): Promise<void> => {
1021
- const actions: Record<string, (options?: any) => Promise<void>> = {
1022
- settings: pushSettings,
1023
- functions: pushFunction,
1024
- sites: pushSite,
1025
- collections: pushCollection,
1026
- tables: pushTable,
1027
- buckets: pushBucket,
1028
- teams: pushTeam,
1029
- messages: pushMessagingTopic
1030
- }
1031
-
1032
- if (cliConfig.all) {
1033
- for (let action of Object.values(actions)) {
1034
- await action({ returnOnZero: true });
1035
- }
1036
- } else {
1037
- const answers = await inquirer.prompt(questionsPushResources[0]);
1038
-
1039
- const action = actions[answers.resource];
1040
- if (action !== undefined) {
1041
- await action({ returnOnZero: true });
1042
- }
1043
- }
1044
- };
1045
-
1046
- const pushSettings = async (): Promise<void> => {
1047
- checkDeployConditions(localConfig);
1048
-
1049
- try {
1050
- let response = await projectsGet({
1051
- parseOutput: false,
1052
- projectId: localConfig.getProject().projectId
412
+ if (settings.auth) {
413
+ if (settings.auth.security) {
414
+ this.log("Applying auth security settings ...");
415
+ await projectsService.updateAuthDuration({
416
+ projectId,
417
+ duration: settings.auth.security.duration,
1053
418
  });
419
+ await projectsService.updateAuthLimit({
420
+ projectId,
421
+ limit: settings.auth.security.limit,
422
+ });
423
+ await projectsService.updateAuthSessionsLimit({
424
+ projectId,
425
+ limit: settings.auth.security.sessionsLimit,
426
+ });
427
+ await projectsService.updateAuthPasswordDictionary({
428
+ projectId,
429
+ enabled: settings.auth.security.passwordDictionary,
430
+ });
431
+ await projectsService.updateAuthPasswordHistory({
432
+ projectId,
433
+ limit: settings.auth.security.passwordHistory,
434
+ });
435
+ await projectsService.updatePersonalDataCheck({
436
+ projectId,
437
+ enabled: settings.auth.security.personalDataCheck,
438
+ });
439
+ await projectsService.updateSessionAlerts({
440
+ projectId,
441
+ alerts: settings.auth.security.sessionAlerts,
442
+ });
443
+ await projectsService.updateMockNumbers({
444
+ projectId,
445
+ numbers: settings.auth.security.mockNumbers,
446
+ });
447
+ }
1054
448
 
1055
- const remoteSettings = localConfig.createSettingsObject(response ?? {});
1056
- const localSettings = localConfig.getProject().projectSettings ?? {};
1057
-
1058
- log('Checking for changes ...');
1059
- const changes: any[] = [];
1060
-
1061
- changes.push(...(getObjectChanges(remoteSettings, localSettings, 'services', 'Service')));
1062
- changes.push(...(getObjectChanges(remoteSettings['auth'] ?? {}, localSettings['auth'] ?? {}, 'methods', 'Auth method')));
1063
- changes.push(...(getObjectChanges(remoteSettings['auth'] ?? {}, localSettings['auth'] ?? {}, 'security', 'Auth security')));
1064
-
1065
- if (changes.length > 0) {
1066
- drawTable(changes);
1067
- if ((await getConfirmation()) !== true) {
1068
- success(`Successfully pushed 0 project settings.`);
1069
- return;
1070
- }
449
+ if (settings.auth.methods) {
450
+ this.log("Applying auth methods statuses ...");
451
+ for (let [method, status] of Object.entries(settings.auth.methods)) {
452
+ await projectsService.updateAuthStatus({
453
+ projectId,
454
+ method: method as AuthMethod,
455
+ status: status,
456
+ });
1071
457
  }
1072
- } catch (e) {
458
+ }
1073
459
  }
460
+ }
1074
461
 
1075
- try {
1076
- log("Pushing project settings ...");
1077
-
1078
- const projectId = localConfig.getProject().projectId;
1079
- const projectName = localConfig.getProject().projectName;
1080
- const settings = localConfig.getProject().projectSettings ?? {};
1081
-
1082
- if (projectName) {
1083
- log("Applying project name ...");
1084
- await projectsUpdate({
1085
- projectId,
1086
- name: projectName,
1087
- parseOutput: false
1088
- });
1089
- }
1090
-
1091
- if (settings.services) {
1092
- log("Applying service statuses ...");
1093
- for (let [service, status] of Object.entries(settings.services)) {
1094
- await projectsUpdateServiceStatus({
1095
- projectId,
1096
- service,
1097
- status,
1098
- parseOutput: false
1099
- });
1100
- }
1101
- }
1102
-
1103
- if (settings.auth) {
1104
- if (settings.auth.security) {
1105
- log("Applying auth security settings ...");
1106
- await projectsUpdateAuthDuration({ projectId, duration: settings.auth.security.duration, parseOutput: false });
1107
- await projectsUpdateAuthLimit({ projectId, limit: settings.auth.security.limit, parseOutput: false });
1108
- await projectsUpdateAuthSessionsLimit({ projectId, limit: settings.auth.security.sessionsLimit, parseOutput: false });
1109
- await projectsUpdateAuthPasswordDictionary({ projectId, enabled: settings.auth.security.passwordDictionary, parseOutput: false });
1110
- await projectsUpdateAuthPasswordHistory({ projectId, limit: settings.auth.security.passwordHistory, parseOutput: false });
1111
- await projectsUpdatePersonalDataCheck({ projectId, enabled: settings.auth.security.personalDataCheck, parseOutput: false });
1112
- await projectsUpdateSessionAlerts({ projectId, alerts: settings.auth.security.sessionAlerts, parseOutput: false });
1113
- await projectsUpdateMockNumbers({ projectId, numbers: settings.auth.security.mockNumbers, parseOutput: false });
1114
- }
462
+ public async pushBuckets(buckets: any[]): Promise<{
463
+ successfullyPushed: number;
464
+ errors: any[];
465
+ }> {
466
+ let successfullyPushed = 0;
467
+ const errors: any[] = [];
1115
468
 
1116
- if (settings.auth.methods) {
1117
- log("Applying auth methods statuses ...");
469
+ for (const bucket of buckets) {
470
+ try {
471
+ this.log(`Pushing bucket ${chalk.bold(bucket["name"])} ...`);
472
+ const storageService = await getStorageService(this.projectClient);
1118
473
 
1119
- for (let [method, status] of Object.entries(settings.auth.methods)) {
1120
- await projectsUpdateAuthStatus({
1121
- projectId,
1122
- method,
1123
- status,
1124
- parseOutput: false
1125
- });
1126
- }
1127
- }
474
+ try {
475
+ await storageService.getBucket(bucket["$id"]);
476
+ await storageService.updateBucket({
477
+ bucketId: bucket["$id"],
478
+ name: bucket.name,
479
+ permissions: bucket["$permissions"],
480
+ fileSecurity: bucket.fileSecurity,
481
+ enabled: bucket.enabled,
482
+ maximumFileSize: bucket.maximumFileSize,
483
+ allowedFileExtensions: bucket.allowedFileExtensions,
484
+ encryption: bucket.encryption,
485
+ antivirus: bucket.antivirus,
486
+ compression: bucket.compression,
487
+ });
488
+ } catch (e: unknown) {
489
+ if (e instanceof AppwriteException && Number(e.code) === 404) {
490
+ await storageService.createBucket({
491
+ bucketId: bucket["$id"],
492
+ name: bucket.name,
493
+ permissions: bucket["$permissions"],
494
+ fileSecurity: bucket.fileSecurity,
495
+ enabled: bucket.enabled,
496
+ maximumFileSize: bucket.maximumFileSize,
497
+ allowedFileExtensions: bucket.allowedFileExtensions,
498
+ compression: bucket.compression,
499
+ encryption: bucket.encryption,
500
+ antivirus: bucket.antivirus,
501
+ });
502
+ } else {
503
+ throw e;
504
+ }
1128
505
  }
1129
506
 
1130
- success(`Successfully pushed ${chalk.bold('all')} project settings.`);
1131
- } catch (e) {
1132
- throw e;
507
+ successfullyPushed++;
508
+ } catch (e: any) {
509
+ errors.push(e);
510
+ this.error(`Failed to push bucket ${bucket["name"]}: ${e.message}`);
511
+ }
1133
512
  }
1134
- }
1135
513
 
1136
- interface PushSiteOptions {
1137
- siteId?: string;
1138
- async?: boolean;
1139
- code?: boolean;
1140
- withVariables?: boolean;
1141
- returnOnZero?: boolean;
1142
- }
1143
-
1144
- const pushSite = async({ siteId, async: asyncDeploy, code, withVariables }: PushSiteOptions = { returnOnZero: false }): Promise<void> => {
1145
- process.chdir(localConfig.configDirectoryPath)
514
+ return {
515
+ successfullyPushed,
516
+ errors,
517
+ };
518
+ }
1146
519
 
1147
- const siteIds: string[] = [];
520
+ public async pushTeams(teams: any[]): Promise<{
521
+ successfullyPushed: number;
522
+ errors: any[];
523
+ }> {
524
+ let successfullyPushed = 0;
525
+ const errors: any[] = [];
1148
526
 
1149
- if(siteId) {
1150
- siteIds.push(siteId);
1151
- } else if (cliConfig.all) {
1152
- checkDeployConditions(localConfig);
1153
- const sites = localConfig.getSites();
1154
- siteIds.push(...sites.map((site: any) => {
1155
- return site.$id;
1156
- }));
1157
- }
527
+ for (const team of teams) {
528
+ try {
529
+ this.log(`Pushing team ${chalk.bold(team["name"])} ...`);
530
+ const teamsService = await getTeamsService(this.projectClient);
1158
531
 
1159
- if (siteIds.length <= 0) {
1160
- const answers = await inquirer.prompt(questionsPushSites[0]);
1161
- if (answers.sites) {
1162
- siteIds.push(...answers.sites);
532
+ try {
533
+ await teamsService.get(team["$id"]);
534
+ await teamsService.updateName({
535
+ teamId: team["$id"],
536
+ name: team.name,
537
+ });
538
+ } catch (e: unknown) {
539
+ if (e instanceof AppwriteException && Number(e.code) === 404) {
540
+ await teamsService.create({
541
+ teamId: team["$id"],
542
+ name: team.name,
543
+ });
544
+ } else {
545
+ throw e;
546
+ }
1163
547
  }
1164
- }
1165
548
 
1166
- if (siteIds.length === 0) {
1167
- log("No sites found.");
1168
- hint("Use 'appwrite pull sites' to synchronize existing one, or use 'appwrite init site' to create a new one.");
1169
- return;
549
+ successfullyPushed++;
550
+ } catch (e: any) {
551
+ errors.push(e);
552
+ this.error(`Failed to push team ${team["name"]}: ${e.message}`);
553
+ }
1170
554
  }
1171
555
 
1172
- let sites = siteIds.map((id: string) => {
1173
- const sites = localConfig.getSites();
1174
- const site = sites.find((s: any) => s.$id === id);
1175
-
1176
- if (!site) {
1177
- throw new Error("Site '" + id + "' not found.")
1178
- }
556
+ return {
557
+ successfullyPushed,
558
+ errors,
559
+ };
560
+ }
1179
561
 
1180
- return site;
1181
- });
562
+ public async pushMessagingTopics(topics: any[]): Promise<{
563
+ successfullyPushed: number;
564
+ errors: any[];
565
+ }> {
566
+ let successfullyPushed = 0;
567
+ const errors: any[] = [];
1182
568
 
1183
- log('Validating sites ...');
1184
- // Validation is done BEFORE pushing so the deployment process can be run in async with progress update
1185
- for (let site of sites) {
569
+ for (const topic of topics) {
570
+ try {
571
+ this.log(`Pushing topic ${chalk.bold(topic["name"])} ...`);
572
+ const messagingService = await getMessagingService(this.projectClient);
1186
573
 
1187
- if (!site.buildCommand) {
1188
- log(`Site ${site.name} is missing build command.`);
1189
- const answers = await inquirer.prompt(questionsGetEntrypoint)
1190
- site.buildCommand = answers.entrypoint;
1191
- localConfig.addSite(site);
574
+ try {
575
+ await messagingService.getTopic(topic["$id"]);
576
+ await messagingService.updateTopic({
577
+ topicId: topic["$id"],
578
+ name: topic.name,
579
+ subscribe: topic.subscribe,
580
+ });
581
+ } catch (e: unknown) {
582
+ if (e instanceof AppwriteException && Number(e.code) === 404) {
583
+ await messagingService.createTopic({
584
+ topicId: topic["$id"],
585
+ name: topic.name,
586
+ subscribe: topic.subscribe,
587
+ });
588
+ } else {
589
+ throw e;
590
+ }
1192
591
  }
1193
- }
1194
592
 
1195
- if (!(await approveChanges(sites, sitesGet, KeysSite, 'siteId', 'sites', ['vars']))) {
1196
- return;
593
+ this.success(`Pushed ${topic.name} ( ${topic["$id"]} )`);
594
+ successfullyPushed++;
595
+ } catch (e: any) {
596
+ errors.push(e);
597
+ this.error(`Failed to push topic ${topic["name"]}: ${e.message}`);
598
+ }
1197
599
  }
1198
600
 
1199
- log('Pushing sites ...');
601
+ return {
602
+ successfullyPushed,
603
+ errors,
604
+ };
605
+ }
606
+
607
+ public async pushFunctions(
608
+ functions: any[],
609
+ options: {
610
+ async?: boolean;
611
+ code?: boolean;
612
+ withVariables?: boolean;
613
+ } = {},
614
+ ): Promise<{
615
+ successfullyPushed: number;
616
+ successfullyDeployed: number;
617
+ failedDeployments: any[];
618
+ errors: any[];
619
+ }> {
620
+ const { async: asyncDeploy, code, withVariables } = options;
1200
621
 
1201
622
  Spinner.start(false);
1202
623
  let successfullyPushed = 0;
@@ -1204,1411 +625,1832 @@ const pushSite = async({ siteId, async: asyncDeploy, code, withVariables }: Push
1204
625
  const failedDeployments: any[] = [];
1205
626
  const errors: any[] = [];
1206
627
 
1207
- await Promise.all(sites.map(async (site: any) => {
628
+ await Promise.all(
629
+ functions.map(async (func: any) => {
1208
630
  let response: any = {};
1209
631
 
1210
- const ignore = site.ignore ? 'appwrite.config.json' : '.gitignore';
1211
- let siteExists = false;
632
+ const ignore = func.ignore ? "appwrite.config.json" : ".gitignore";
633
+ let functionExists = false;
1212
634
  let deploymentCreated = false;
1213
635
 
1214
- const updaterRow = new Spinner({ status: '', resource: site.name, id: site['$id'], end: `Ignoring using: ${ignore}` });
1215
-
1216
- updaterRow.update({ status: 'Getting' }).startSpinner(SPINNER_DOTS);
636
+ const updaterRow = new Spinner({
637
+ status: "",
638
+ resource: func.name,
639
+ id: func["$id"],
640
+ end: `Ignoring using: ${ignore}`,
641
+ });
1217
642
 
643
+ updaterRow.update({ status: "Getting" }).startSpinner(SPINNER_DOTS);
644
+ const functionsService = await getFunctionsService(this.projectClient);
1218
645
  try {
1219
- response = await sitesGet({
1220
- siteId: site['$id'],
1221
- parseOutput: false,
1222
- });
1223
- siteExists = true;
1224
- if (response.framework !== site.framework) {
1225
- updaterRow.fail({ errorMessage: `Framework mismatch! (local=${site.framework},remote=${response.framework}) Please delete remote site or update your appwrite.config.json` })
1226
- return;
1227
- }
1228
-
1229
- updaterRow.update({ status: 'Updating' }).replaceSpinner(SPINNER_ARC);
1230
-
1231
- response = await sitesUpdate({
1232
- siteId: site['$id'],
1233
- name: site.name,
1234
- framework: site.framework,
1235
- buildRuntime: site.buildRuntime,
1236
- specification: site.specification,
1237
- timeout: site.timeout,
1238
- enabled: site.enabled,
1239
- logging: site.logging,
1240
- adapter: site.adapter,
1241
- buildCommand: site.buildCommand,
1242
- installCommand: site.installCommand,
1243
- outputDirectory: site.outputDirectory,
1244
- fallbackFile: site.fallbackFile,
1245
- vars: JSON.stringify(response.vars),
1246
- parseOutput: false
646
+ response = await functionsService.get({ functionId: func["$id"] });
647
+ functionExists = true;
648
+ if (response.runtime !== func.runtime) {
649
+ updaterRow.fail({
650
+ errorMessage: `Runtime mismatch! (local=${func.runtime},remote=${response.runtime}) Please delete remote function or update your appwrite.config.json`,
1247
651
  });
652
+ return;
653
+ }
654
+
655
+ updaterRow
656
+ .update({ status: "Updating" })
657
+ .replaceSpinner(SPINNER_DOTS);
658
+
659
+ response = await functionsService.update({
660
+ functionId: func["$id"],
661
+ name: func.name,
662
+ runtime: func.runtime,
663
+ execute: func.execute,
664
+ events: func.events,
665
+ schedule: func.schedule,
666
+ timeout: func.timeout,
667
+ enabled: func.enabled,
668
+ logging: func.logging,
669
+ entrypoint: func.entrypoint,
670
+ commands: func.commands,
671
+ scopes: func.scopes,
672
+ specification: func.specification,
673
+ });
1248
674
  } catch (e: any) {
1249
-
1250
- if (Number(e.code) === 404) {
1251
- siteExists = false;
1252
- } else {
1253
- errors.push(e);
1254
- updaterRow.fail({ errorMessage: e.message ?? 'General error occurs please try again' });
1255
- return;
1256
- }
675
+ if (Number(e.code) === 404) {
676
+ functionExists = false;
677
+ } else {
678
+ errors.push(e);
679
+ updaterRow.fail({
680
+ errorMessage:
681
+ e.message ?? "General error occurs please try again",
682
+ });
683
+ return;
684
+ }
1257
685
  }
1258
686
 
1259
- if (!siteExists) {
1260
- updaterRow.update({ status: 'Creating' }).replaceSpinner(SPINNER_DOTS);
687
+ if (!functionExists) {
688
+ updaterRow
689
+ .update({ status: "Creating" })
690
+ .replaceSpinner(SPINNER_DOTS);
691
+
692
+ try {
693
+ response = await functionsService.create({
694
+ functionId: func.$id,
695
+ name: func.name,
696
+ runtime: func.runtime,
697
+ execute: func.execute,
698
+ events: func.events,
699
+ schedule: func.schedule,
700
+ timeout: func.timeout,
701
+ enabled: func.enabled,
702
+ logging: func.logging,
703
+ entrypoint: func.entrypoint,
704
+ commands: func.commands,
705
+ scopes: func.scopes,
706
+ specification: func.specification,
707
+ });
1261
708
 
709
+ let domain = "";
1262
710
  try {
1263
- response = await sitesCreate({
1264
- siteId: site.$id,
1265
- name: site.name,
1266
- framework: site.framework,
1267
- specification: site.specification,
1268
- buildRuntime: site.buildRuntime,
1269
- buildCommand: site.buildCommand,
1270
- installCommand: site.installCommand,
1271
- outputDirectory: site.outputDirectory,
1272
- fallbackFile: site.fallbackFile,
1273
- adapter: site.adapter,
1274
- timeout: site.timeout,
1275
- enabled: site.enabled,
1276
- logging: site.logging,
1277
- parseOutput: false
1278
- });
1279
-
1280
- let domain = '';
1281
- try {
1282
- const variables = await consoleVariables({ parseOutput: false, sdk: await sdkForConsole() });
1283
- domain = ID.unique() + '.' + variables['_APP_DOMAIN_SITES'];
1284
- } catch (error) {
1285
- console.error('Error fetching console variables.');
1286
- throw error;
1287
- }
1288
-
1289
- try {
1290
- const rule = await proxyCreateSiteRule(
1291
- {
1292
- domain: domain,
1293
- siteId: site.$id
1294
- }
1295
- );
1296
- } catch (error) {
1297
- console.error('Error creating site rule.');
1298
- throw error;
1299
- }
711
+ const consoleService = await getConsoleService(
712
+ this.consoleClient,
713
+ );
714
+ const variables = await consoleService.variables();
715
+ domain = ID.unique() + "." + variables["_APP_DOMAIN_FUNCTIONS"];
716
+ } catch (err) {
717
+ this.error("Error fetching console variables.");
718
+ throw err;
719
+ }
1300
720
 
1301
- updaterRow.update({ status: 'Created' });
1302
- } catch (e: any) {
1303
- errors.push(e)
1304
- updaterRow.fail({ errorMessage: e.message ?? 'General error occurs please try again' });
1305
- return;
721
+ try {
722
+ const proxyService = await getProxyService(this.projectClient);
723
+ await proxyService.createFunctionRule(domain, func.$id);
724
+ } catch (err) {
725
+ this.error("Error creating function rule.");
726
+ throw err;
1306
727
  }
728
+
729
+ updaterRow.update({ status: "Created" });
730
+ } catch (e: any) {
731
+ errors.push(e);
732
+ updaterRow.fail({
733
+ errorMessage:
734
+ e.message ?? "General error occurs please try again",
735
+ });
736
+ return;
737
+ }
1307
738
  }
1308
739
 
1309
740
  if (withVariables) {
1310
- updaterRow.update({ status: 'Creating variables' }).replaceSpinner(SPINNER_ARC);
1311
-
1312
- const { variables } = await paginate(sitesListVariables, {
1313
- siteId: site['$id'],
1314
- parseOutput: false
1315
- }, 100, 'variables');
1316
-
1317
- await Promise.all(variables.map(async (variable: any) => {
1318
- await sitesDeleteVariable({
1319
- siteId: site['$id'],
1320
- variableId: variable['$id'],
1321
- parseOutput: false
1322
- });
1323
- }));
1324
-
1325
- const envFileLocation = `${site['path']}/.env`;
1326
- let envVariables: Array<{key: string; value: string}> = [];
1327
- try {
1328
- if (fs.existsSync(envFileLocation)) {
1329
- const envObject = parseDotenv(fs.readFileSync(envFileLocation, 'utf8'));
1330
- envVariables = Object.entries(envObject || {}).map(([key, value]) => ({ key, value }));
1331
- }
1332
- } catch (error) {
1333
- // Handle parsing errors gracefully
1334
- envVariables = [];
741
+ updaterRow
742
+ .update({ status: "Updating variables" })
743
+ .replaceSpinner(SPINNER_DOTS);
744
+
745
+ const functionsServiceForVars = await getFunctionsService(
746
+ this.projectClient,
747
+ );
748
+ const { variables } = await paginate(
749
+ async (args: any) => {
750
+ return await functionsServiceForVars.listVariables({
751
+ functionId: args.functionId,
752
+ });
753
+ },
754
+ {
755
+ functionId: func["$id"],
756
+ },
757
+ 100,
758
+ "variables",
759
+ );
760
+
761
+ await Promise.all(
762
+ variables.map(async (variable: any) => {
763
+ const functionsServiceDel = await getFunctionsService(
764
+ this.projectClient,
765
+ );
766
+ await functionsServiceDel.deleteVariable({
767
+ functionId: func["$id"],
768
+ variableId: variable["$id"],
769
+ });
770
+ }),
771
+ );
772
+
773
+ const envFileLocation = `${func["path"]}/.env`;
774
+ let envVariables: Array<{ key: string; value: string }> = [];
775
+ try {
776
+ if (fs.existsSync(envFileLocation)) {
777
+ const envObject = parseDotenv(
778
+ fs.readFileSync(envFileLocation, "utf8"),
779
+ );
780
+ envVariables = Object.entries(envObject || {}).map(
781
+ ([key, value]) => ({ key, value }),
782
+ );
1335
783
  }
1336
- await Promise.all(envVariables.map(async (variable) => {
1337
- await sitesCreateVariable({
1338
- siteId: site['$id'],
1339
- key: variable.key,
1340
- value: variable.value,
1341
- parseOutput: false,
1342
- secret: false
1343
- });
1344
- }));
784
+ } catch (error) {
785
+ envVariables = [];
786
+ }
787
+ await Promise.all(
788
+ envVariables.map(async (variable) => {
789
+ const functionsServiceCreate = await getFunctionsService(
790
+ this.projectClient,
791
+ );
792
+ await functionsServiceCreate.createVariable({
793
+ functionId: func["$id"],
794
+ key: variable.key,
795
+ value: variable.value,
796
+ secret: false,
797
+ });
798
+ }),
799
+ );
1345
800
  }
1346
801
 
1347
802
  if (code === false) {
1348
- successfullyPushed++;
1349
- successfullyDeployed++;
1350
- updaterRow.update({ status: 'Pushed' });
1351
- updaterRow.stopSpinner();
1352
- return;
803
+ successfullyPushed++;
804
+ successfullyDeployed++;
805
+ updaterRow.update({ status: "Pushed" });
806
+ updaterRow.stopSpinner();
807
+ return;
1353
808
  }
1354
809
 
1355
810
  try {
1356
- updaterRow.update({ status: 'Pushing' }).replaceSpinner(SPINNER_ARC);
1357
- response = await sitesCreateDeployment({
1358
- siteId: site['$id'],
1359
- buildCommand: site.buildCommand,
1360
- installCommand: site.installCommand,
1361
- outputDirectory: site.outputDirectory,
1362
- fallbackFile: site.fallbackFile,
1363
- code: site.path,
811
+ updaterRow.update({ status: "Pushing" }).replaceSpinner(SPINNER_DOTS);
812
+ const functionsServiceDeploy = await getFunctionsService(
813
+ this.projectClient,
814
+ );
815
+
816
+ const result = await pushDeployment({
817
+ resourcePath: func.path,
818
+ createDeployment: async (codeFile) => {
819
+ return await functionsServiceDeploy.createDeployment({
820
+ functionId: func["$id"],
821
+ entrypoint: func.entrypoint,
822
+ commands: func.commands,
823
+ code: codeFile,
1364
824
  activate: true,
1365
- parseOutput: false
1366
- })
825
+ });
826
+ },
827
+ pollForStatus: false,
828
+ });
1367
829
 
1368
- updaterRow.update({ status: 'Pushed' });
1369
- deploymentCreated = true;
1370
- successfullyPushed++;
1371
- } catch (e: any) {
1372
- errors.push(e);
830
+ response = result.deployment;
831
+ updaterRow.update({ status: "Pushed" });
1373
832
 
1374
- switch (e.code) {
1375
- case 'ENOENT':
1376
- updaterRow.fail({ errorMessage: 'Not found in the current directory. Skipping...' })
1377
- break;
1378
- default:
1379
- updaterRow.fail({ errorMessage: e.message ?? 'An unknown error occurred. Please try again.' })
1380
- }
833
+ deploymentCreated = true;
834
+ successfullyPushed++;
835
+ } catch (e: any) {
836
+ errors.push(e);
837
+
838
+ switch (e.code) {
839
+ case "ENOENT":
840
+ updaterRow.fail({
841
+ errorMessage: "Not found in the current directory. Skipping...",
842
+ });
843
+ break;
844
+ default:
845
+ updaterRow.fail({
846
+ errorMessage:
847
+ e.message ?? "An unknown error occurred. Please try again.",
848
+ });
849
+ }
1381
850
  }
1382
851
 
1383
852
  if (deploymentCreated && !asyncDeploy) {
1384
- try {
1385
- const deploymentId = response['$id'];
1386
- updaterRow.update({ status: 'Deploying', end: 'Checking deployment status...' })
1387
- let pollChecks = 0;
1388
-
1389
- while (true) {
1390
- response = await sitesGetDeployment({
1391
- siteId: site['$id'],
1392
- deploymentId: deploymentId,
1393
- parseOutput: false
1394
- });
1395
-
1396
- const status = response['status'];
1397
- if (status === 'ready') {
1398
- successfullyDeployed++;
1399
-
1400
- let url = '';
1401
- const res = await proxyListRules({
1402
- parseOutput: false,
1403
- queries: [
1404
- JSON.stringify({ method: 'limit', values: [1] }),
1405
- JSON.stringify({ method: 'equal', "attribute": "deploymentResourceType", "values": ["site"] }),
1406
- JSON.stringify({ method: 'equal', "attribute": "deploymentResourceId", "values": [site['$id']] }),
1407
- JSON.stringify({ method: 'equal', "attribute": "trigger", "values": ["manual"] }),
1408
- ],
1409
- });
1410
-
1411
- if (Number(res.total) === 1) {
1412
- url = res.rules[0].domain;
1413
- }
1414
-
1415
- updaterRow.update({ status: 'Deployed', end: url });
1416
-
1417
- break;
1418
- } else if (status === 'failed') {
1419
- failedDeployments.push({ name: site['name'], $id: site['$id'], deployment: response['$id'] });
1420
- updaterRow.fail({ errorMessage: `Failed to deploy` });
1421
-
1422
- break;
1423
- } else {
1424
- updaterRow.update({ status: 'Deploying', end: `Current status: ${status}` })
1425
- }
1426
-
1427
- pollChecks++;
1428
- await new Promise(resolve => setTimeout(resolve, POLL_DEBOUNCE * 1.5));
1429
- }
1430
- } catch (e: any) {
1431
- errors.push(e);
1432
- updaterRow.fail({ errorMessage: e.message ?? 'Unknown error occurred. Please try again' })
1433
- }
1434
- }
1435
-
1436
- updaterRow.stopSpinner();
1437
- }));
1438
-
1439
- Spinner.stop();
1440
-
1441
- failedDeployments.forEach((failed) => {
1442
- const { name, deployment, $id } = failed;
1443
- const failUrl = `${globalConfig.getEndpoint().slice(0, -3)}/console/project-${localConfig.getProject().projectId}/sites/site-${$id}/deployments/deployment-${deployment}`;
1444
-
1445
- error(`Deployment of ${name} has failed. Check at ${failUrl} for more details\n`);
1446
- });
1447
-
1448
- if (!asyncDeploy) {
1449
- if (successfullyPushed === 0) {
1450
- error('No sites were pushed.');
1451
- } else if (successfullyDeployed !== successfullyPushed) {
1452
- warn(`Successfully pushed ${successfullyDeployed} of ${successfullyPushed} sites`)
1453
- } else {
1454
- success(`Successfully pushed ${successfullyPushed} sites.`);
1455
- }
1456
- } else {
1457
- success(`Successfully pushed ${successfullyPushed} sites.`);
1458
- }
1459
-
1460
- if (cliConfig.verbose) {
1461
- errors.forEach(e => {
1462
- console.error(e);
1463
- })
1464
- }
1465
- }
1466
-
1467
- interface PushFunctionOptions {
1468
- functionId?: string;
1469
- async?: boolean;
1470
- code?: boolean;
1471
- withVariables?: boolean;
1472
- returnOnZero?: boolean;
1473
- }
1474
-
1475
- const pushFunction = async ({ functionId, async: asyncDeploy, code, withVariables }: PushFunctionOptions = { returnOnZero: false }): Promise<void> => {
1476
- process.chdir(localConfig.configDirectoryPath)
1477
-
1478
- const functionIds: string[] = [];
1479
-
1480
- if (functionId) {
1481
- functionIds.push(functionId);
1482
- } else if (cliConfig.all) {
1483
- checkDeployConditions(localConfig);
1484
- const functions = localConfig.getFunctions();
1485
- functionIds.push(...functions.map((func: any) => {
1486
- return func.$id;
1487
- }));
1488
- }
1489
-
1490
- if (functionIds.length <= 0) {
1491
- const answers = await inquirer.prompt(questionsPushFunctions[0]);
1492
- if (answers.functions) {
1493
- functionIds.push(...answers.functions);
1494
- }
1495
- }
1496
-
1497
- if (functionIds.length === 0) {
1498
- log("No functions found.");
1499
- hint("Use 'appwrite pull functions' to synchronize existing one, or use 'appwrite init function' to create a new one.");
1500
- return;
1501
- }
1502
-
1503
- let functions = functionIds.map((id: string) => {
1504
- const functions = localConfig.getFunctions();
1505
- const func = functions.find((f: any) => f.$id === id);
1506
-
1507
- if (!func) {
1508
- throw new Error("Function '" + id + "' not found.")
1509
- }
1510
-
1511
- return func;
1512
- });
1513
-
1514
- log('Validating functions ...');
1515
- // Validation is done BEFORE pushing so the deployment process can be run in async with progress update
1516
- for (let func of functions) {
1517
-
1518
- if (!func.entrypoint) {
1519
- log(`Function ${func.name} is missing an entrypoint.`);
1520
- const answers = await inquirer.prompt(questionsGetEntrypoint)
1521
- func.entrypoint = answers.entrypoint;
1522
- localConfig.addFunction(func);
1523
- }
1524
- }
1525
-
1526
- if (!(await approveChanges(functions, functionsGet, KeysFunction, 'functionId', 'functions', ['vars']))) {
1527
- return;
1528
- }
1529
-
1530
- log('Pushing functions ...');
1531
-
1532
- Spinner.start(false);
1533
- let successfullyPushed = 0;
1534
- let successfullyDeployed = 0;
1535
- const failedDeployments: any[] = [];
1536
- const errors: any[] = [];
1537
-
1538
- await Promise.all(functions.map(async (func: any) => {
1539
- let response: any = {};
1540
-
1541
- const ignore = func.ignore ? 'appwrite.config.json' : '.gitignore';
1542
- let functionExists = false;
1543
- let deploymentCreated = false;
1544
-
1545
- const updaterRow = new Spinner({ status: '', resource: func.name, id: func['$id'], end: `Ignoring using: ${ignore}` });
1546
-
1547
- updaterRow.update({ status: 'Getting' }).startSpinner(SPINNER_DOTS);
1548
- try {
1549
- response = await functionsGet({
1550
- functionId: func['$id'],
1551
- parseOutput: false,
853
+ try {
854
+ const deploymentId = response["$id"];
855
+ updaterRow.update({
856
+ status: "Deploying",
857
+ end: "Checking deployment status...",
1552
858
  });
1553
- functionExists = true;
1554
- if (response.runtime !== func.runtime) {
1555
- updaterRow.fail({ errorMessage: `Runtime mismatch! (local=${func.runtime},remote=${response.runtime}) Please delete remote function or update your appwrite.config.json` })
1556
- return;
1557
- }
1558
859
 
1559
- updaterRow.update({ status: 'Updating' }).replaceSpinner(SPINNER_ARC);
1560
-
1561
- response = await functionsUpdate({
1562
- functionId: func['$id'],
1563
- name: func.name,
1564
- specification: func.specification,
1565
- execute: func.execute,
1566
- events: func.events,
1567
- schedule: func.schedule,
1568
- timeout: func.timeout,
1569
- enabled: func.enabled,
1570
- logging: func.logging,
1571
- entrypoint: func.entrypoint,
1572
- commands: func.commands,
1573
- scopes: func.scopes,
1574
- vars: JSON.stringify(response.vars),
1575
- parseOutput: false
1576
- });
1577
- } catch (e: any) {
1578
-
1579
- if (Number(e.code) === 404) {
1580
- functionExists = false;
1581
- } else {
1582
- errors.push(e);
1583
- updaterRow.fail({ errorMessage: e.message ?? 'General error occurs please try again' });
1584
- return;
1585
- }
1586
- }
1587
-
1588
- if (!functionExists) {
1589
- updaterRow.update({ status: 'Creating' }).replaceSpinner(SPINNER_DOTS);
860
+ const timeoutDeadline = Date.now() + DEPLOYMENT_TIMEOUT_MS;
1590
861
 
1591
- try {
1592
- response = await functionsCreate({
1593
- functionId: func.$id,
1594
- name: func.name,
1595
- runtime: func.runtime,
1596
- specification: func.specification,
1597
- execute: func.execute,
1598
- events: func.events,
1599
- schedule: func.schedule,
1600
- timeout: func.timeout,
1601
- enabled: func.enabled,
1602
- logging: func.logging,
1603
- entrypoint: func.entrypoint,
1604
- commands: func.commands,
1605
- scopes: func.scopes,
1606
- parseOutput: false
862
+ while (true) {
863
+ if (Date.now() > timeoutDeadline) {
864
+ failedDeployments.push({
865
+ name: func["name"],
866
+ $id: func["$id"],
867
+ deployment: deploymentId,
868
+ });
869
+ updaterRow.fail({
870
+ errorMessage: "Deployment timed out after 10 minutes",
871
+ });
872
+ break;
873
+ }
874
+
875
+ const functionsServicePoll = await getFunctionsService(
876
+ this.projectClient,
877
+ );
878
+ response = await functionsServicePoll.getDeployment({
879
+ functionId: func["$id"],
880
+ deploymentId: deploymentId,
881
+ });
882
+
883
+ const status = response["status"];
884
+ if (status === "ready") {
885
+ successfullyDeployed++;
886
+
887
+ let url = "";
888
+ const proxyServiceUrl = await getProxyService(
889
+ this.projectClient,
890
+ );
891
+ const res = await proxyServiceUrl.listRules({
892
+ queries: [
893
+ Query.limit(1),
894
+ Query.equal("deploymentResourceType", "function"),
895
+ Query.equal("deploymentResourceId", func["$id"]),
896
+ Query.equal("trigger", "manual"),
897
+ ],
1607
898
  });
1608
899
 
1609
- let domain = '';
1610
- try {
1611
- const variables = await consoleVariables({ parseOutput: false, sdk: await sdkForConsole() });
1612
- domain = ID.unique() + '.' + variables['_APP_DOMAIN_FUNCTIONS'];
1613
- } catch (error) {
1614
- console.error('Error fetching console variables.');
1615
- throw error;
900
+ if (Number(res.total) === 1) {
901
+ url = `https://${res.rules[0].domain}`;
1616
902
  }
1617
903
 
1618
- try {
1619
- const rule = await proxyCreateFunctionRule(
1620
- {
1621
- domain: domain,
1622
- functionId: func.$id
1623
- }
1624
- );
1625
- } catch (error) {
1626
- console.error('Error creating function rule.');
1627
- throw error;
1628
- }
904
+ updaterRow.update({ status: "Deployed", end: url });
1629
905
 
1630
- updaterRow.update({ status: 'Created' });
1631
- } catch (e: any) {
1632
- errors.push(e)
1633
- updaterRow.fail({ errorMessage: e.message ?? 'General error occurs please try again' });
1634
- return;
1635
- }
1636
- }
1637
-
1638
- if (withVariables) {
1639
- updaterRow.update({ status: 'Updating variables' }).replaceSpinner(SPINNER_ARC);
1640
-
1641
- const { variables } = await paginate(functionsListVariables, {
1642
- functionId: func['$id'],
1643
- parseOutput: false
1644
- }, 100, 'variables');
1645
-
1646
- await Promise.all(variables.map(async (variable: any) => {
1647
- await functionsDeleteVariable({
1648
- functionId: func['$id'],
1649
- variableId: variable['$id'],
1650
- parseOutput: false
906
+ break;
907
+ } else if (status === "failed") {
908
+ failedDeployments.push({
909
+ name: func["name"],
910
+ $id: func["$id"],
911
+ deployment: response["$id"],
1651
912
  });
1652
- }));
913
+ updaterRow.fail({ errorMessage: `Failed to deploy` });
1653
914
 
1654
- const envFileLocation = `${func['path']}/.env`;
1655
- let envVariables: Array<{key: string; value: string}> = [];
1656
- try {
1657
- if (fs.existsSync(envFileLocation)) {
1658
- const envObject = parseDotenv(fs.readFileSync(envFileLocation, 'utf8'));
1659
- envVariables = Object.entries(envObject || {}).map(([key, value]) => ({ key, value }));
1660
- }
1661
- } catch (error) {
1662
- // Handle parsing errors gracefully
1663
- envVariables = [];
1664
- }
1665
- await Promise.all(envVariables.map(async (variable) => {
1666
- await functionsCreateVariable({
1667
- functionId: func['$id'],
1668
- variableId: ID.unique(),
1669
- key: variable.key,
1670
- value: variable.value,
1671
- parseOutput: false,
1672
- secret: false
915
+ break;
916
+ } else {
917
+ updaterRow.update({
918
+ status: "Deploying",
919
+ end: `Current status: ${status}`,
1673
920
  });
1674
- }));
1675
- }
1676
-
1677
- if (code === false) {
1678
- successfullyPushed++;
1679
- successfullyDeployed++;
1680
- updaterRow.update({ status: 'Pushed' });
1681
- updaterRow.stopSpinner();
1682
- return;
1683
- }
1684
-
1685
- try {
1686
- updaterRow.update({ status: 'Pushing' }).replaceSpinner(SPINNER_ARC);
1687
- response = await functionsCreateDeployment({
1688
- functionId: func['$id'],
1689
- entrypoint: func.entrypoint,
1690
- commands: func.commands,
1691
- code: func.path,
1692
- activate: true,
1693
- parseOutput: false
1694
- })
1695
-
1696
- updaterRow.update({ status: 'Pushed' });
1697
- deploymentCreated = true;
1698
- successfullyPushed++;
1699
- } catch (e: any) {
1700
- errors.push(e);
921
+ }
1701
922
 
1702
- switch (e.code) {
1703
- case 'ENOENT':
1704
- updaterRow.fail({ errorMessage: 'Not found in the current directory. Skipping...' })
1705
- break;
1706
- default:
1707
- updaterRow.fail({ errorMessage: e.message ?? 'An unknown error occurred. Please try again.' })
1708
- }
1709
- }
1710
-
1711
- if (deploymentCreated && !asyncDeploy) {
1712
- try {
1713
- const deploymentId = response['$id'];
1714
- updaterRow.update({ status: 'Deploying', end: 'Checking deployment status...' })
1715
- let pollChecks = 0;
1716
-
1717
- while (true) {
1718
- response = await functionsGetDeployment({
1719
- functionId: func['$id'],
1720
- deploymentId: deploymentId,
1721
- parseOutput: false
1722
- });
1723
-
1724
- const status = response['status'];
1725
- if (status === 'ready') {
1726
- successfullyDeployed++;
1727
-
1728
- let url = '';
1729
- const res = await proxyListRules({
1730
- parseOutput: false,
1731
- queries: [
1732
- JSON.stringify({ method: 'limit', values: [1] }),
1733
- JSON.stringify({ method: 'equal', "attribute": "deploymentResourceType", "values": ["function"] }),
1734
- JSON.stringify({ method: 'equal', "attribute": "deploymentResourceId", "values": [func['$id']] }),
1735
- JSON.stringify({ method: 'equal', "attribute": "trigger", "values": ["manual"] }),
1736
- ],
1737
- });
1738
-
1739
- if (Number(res.total) === 1) {
1740
- url = res.rules[0].domain;
1741
- }
1742
-
1743
- updaterRow.update({ status: 'Deployed', end: url });
1744
-
1745
- break;
1746
- } else if (status === 'failed') {
1747
- failedDeployments.push({ name: func['name'], $id: func['$id'], deployment: response['$id'] });
1748
- updaterRow.fail({ errorMessage: `Failed to deploy` });
1749
-
1750
- break;
1751
- } else {
1752
- updaterRow.update({ status: 'Deploying', end: `Current status: ${status}` })
1753
- }
1754
-
1755
- pollChecks++;
1756
- await new Promise(resolve => setTimeout(resolve, POLL_DEBOUNCE * 1.5));
1757
- }
1758
- } catch (e: any) {
1759
- errors.push(e);
1760
- updaterRow.fail({ errorMessage: e.message ?? 'Unknown error occurred. Please try again' })
923
+ await new Promise((resolve) =>
924
+ setTimeout(resolve, POLL_DEBOUNCE),
925
+ );
1761
926
  }
927
+ } catch (e: any) {
928
+ errors.push(e);
929
+ updaterRow.fail({
930
+ errorMessage:
931
+ e.message ?? "Unknown error occurred. Please try again",
932
+ });
933
+ }
1762
934
  }
1763
935
 
1764
936
  updaterRow.stopSpinner();
1765
- }));
937
+ }),
938
+ );
1766
939
 
1767
940
  Spinner.stop();
1768
941
 
1769
- failedDeployments.forEach((failed) => {
1770
- const { name, deployment, $id } = failed;
1771
- const failUrl = `${globalConfig.getEndpoint().slice(0, -3)}/console/project-${localConfig.getProject().projectId}/functions/function-${$id}/deployment-${deployment}`;
1772
-
1773
- error(`Deployment of ${name} has failed. Check at ${failUrl} for more details\n`);
1774
- });
1775
-
1776
- if (!asyncDeploy) {
1777
- if (successfullyPushed === 0) {
1778
- error('No functions were pushed.');
1779
- } else if (successfullyDeployed !== successfullyPushed) {
1780
- warn(`Successfully pushed ${successfullyDeployed} of ${successfullyPushed} functions`)
1781
- } else {
1782
- success(`Successfully pushed ${successfullyPushed} functions.`);
1783
- }
1784
- } else {
1785
- success(`Successfully pushed ${successfullyPushed} functions.`);
1786
- }
1787
-
1788
- if (cliConfig.verbose) {
1789
- errors.forEach(e => {
1790
- console.error(e);
1791
- })
1792
- }
1793
- }
1794
-
1795
- interface TablesDBChangesResult {
1796
- applied: boolean;
1797
- resyncNeeded: boolean;
1798
- }
1799
-
1800
- const checkAndApplyTablesDBChanges = async (): Promise<TablesDBChangesResult> => {
1801
- log('Checking for tablesDB changes ...');
1802
-
1803
- const localTablesDBs = localConfig.getTablesDBs();
1804
- const { databases: remoteTablesDBs } = await paginate(tablesDBList, { parseOutput: false }, 100, 'databases');
1805
-
1806
- if (localTablesDBs.length === 0 && remoteTablesDBs.length === 0) {
1807
- return { applied: false, resyncNeeded: false };
1808
- }
1809
-
1810
- const changes: any[] = [];
1811
- const toCreate: any[] = [];
1812
- const toUpdate: any[] = [];
1813
- const toDelete: any[] = [];
1814
-
1815
- // Check for deletions - remote DBs that aren't in local config
1816
- for (const remoteDB of remoteTablesDBs) {
1817
- const localDB = localTablesDBs.find((db: any) => db.$id === remoteDB.$id);
1818
- if (!localDB) {
1819
- toDelete.push(remoteDB);
1820
- changes.push({
1821
- id: remoteDB.$id,
1822
- action: chalk.red('deleting'),
1823
- key: 'Database',
1824
- remote: remoteDB.name,
1825
- local: '(deleted locally)'
1826
- });
1827
- }
1828
- }
942
+ return {
943
+ successfullyPushed,
944
+ successfullyDeployed,
945
+ failedDeployments,
946
+ errors,
947
+ };
948
+ }
949
+
950
+ public async pushSites(
951
+ sites: any[],
952
+ options: {
953
+ async?: boolean;
954
+ code?: boolean;
955
+ withVariables?: boolean;
956
+ } = {},
957
+ ): Promise<{
958
+ successfullyPushed: number;
959
+ successfullyDeployed: number;
960
+ failedDeployments: any[];
961
+ errors: any[];
962
+ }> {
963
+ const { async: asyncDeploy, code, withVariables } = options;
1829
964
 
1830
- // Check for additions and updates
1831
- for (const localDB of localTablesDBs) {
1832
- const remoteDB = remoteTablesDBs.find((db: any) => db.$id === localDB.$id);
1833
-
1834
- if (!remoteDB) {
1835
- toCreate.push(localDB);
1836
- changes.push({
1837
- id: localDB.$id,
1838
- action: chalk.green('creating'),
1839
- key: 'Database',
1840
- remote: '(does not exist)',
1841
- local: localDB.name
1842
- });
1843
- } else {
1844
- let hasChanges = false;
1845
-
1846
- if (remoteDB.name !== localDB.name) {
1847
- hasChanges = true;
1848
- changes.push({
1849
- id: localDB.$id,
1850
- action: chalk.yellow('updating'),
1851
- key: 'Name',
1852
- remote: remoteDB.name,
1853
- local: localDB.name
1854
- });
1855
- }
1856
-
1857
- if (remoteDB.enabled !== localDB.enabled) {
1858
- hasChanges = true;
1859
- changes.push({
1860
- id: localDB.$id,
1861
- action: chalk.yellow('updating'),
1862
- key: 'Enabled',
1863
- remote: remoteDB.enabled,
1864
- local: localDB.enabled
1865
- });
1866
- }
1867
-
1868
- if (hasChanges) {
1869
- toUpdate.push(localDB);
1870
- }
1871
- }
1872
- }
965
+ Spinner.start(false);
966
+ let successfullyPushed = 0;
967
+ let successfullyDeployed = 0;
968
+ const failedDeployments: any[] = [];
969
+ const errors: any[] = [];
1873
970
 
1874
- if (changes.length === 0) {
1875
- console.log('No changes found in tablesDB resource');
1876
- console.log();
1877
- return { applied: false, resyncNeeded: false };
1878
- }
971
+ await Promise.all(
972
+ sites.map(async (site: any) => {
973
+ let response: any = {};
1879
974
 
1880
- log('Found changes in tablesDB resource:');
1881
- drawTable(changes);
975
+ const ignore = site.ignore ? "appwrite.config.json" : ".gitignore";
976
+ let siteExists = false;
977
+ let deploymentCreated = false;
1882
978
 
1883
- if (toDelete.length > 0) {
1884
- console.log(`${chalk.red('------------------------------------------------------------------')}`);
1885
- console.log(`${chalk.red('| WARNING: Database deletion will also delete all related tables |')}`);
1886
- console.log(`${chalk.red('------------------------------------------------------------------')}`);
1887
- console.log();
1888
- }
979
+ const updaterRow = new Spinner({
980
+ status: "",
981
+ resource: site.name,
982
+ id: site["$id"],
983
+ end: `Ignoring using: ${ignore}`,
984
+ });
1889
985
 
1890
- if ((await getConfirmation()) !== true) {
1891
- return { applied: false, resyncNeeded: false };
1892
- }
986
+ updaterRow.update({ status: "Getting" }).startSpinner(SPINNER_DOTS);
1893
987
 
1894
- // Apply deletions first
1895
- let needsResync = false;
1896
- for (const db of toDelete) {
988
+ const sitesService = await getSitesService(this.projectClient);
1897
989
  try {
1898
- log(`Deleting database ${db.name} ( ${db.$id} ) ...`);
1899
- await tablesDBDelete({
1900
- databaseId: db.$id,
1901
- parseOutput: false
990
+ response = await sitesService.get({ siteId: site["$id"] });
991
+ siteExists = true;
992
+ if (response.framework !== site.framework) {
993
+ updaterRow.fail({
994
+ errorMessage: `Framework mismatch! (local=${site.framework},remote=${response.framework}) Please delete remote site or update your appwrite.config.json`,
1902
995
  });
1903
- success(`Deleted ${db.name} ( ${db.$id} )`);
1904
- needsResync = true;
996
+ return;
997
+ }
998
+
999
+ updaterRow
1000
+ .update({ status: "Updating" })
1001
+ .replaceSpinner(SPINNER_DOTS);
1002
+
1003
+ response = await sitesService.update({
1004
+ siteId: site["$id"],
1005
+ name: site.name,
1006
+ framework: site.framework,
1007
+ enabled: site.enabled,
1008
+ logging: site.logging,
1009
+ timeout: site.timeout,
1010
+ installCommand: site.installCommand,
1011
+ buildCommand: site.buildCommand,
1012
+ outputDirectory: site.outputDirectory,
1013
+ buildRuntime: site.buildRuntime,
1014
+ adapter: site.adapter,
1015
+ specification: site.specification,
1016
+ });
1905
1017
  } catch (e: any) {
1906
- error(`Failed to delete database ${db.name} ( ${db.$id} ): ${e.message}`);
1907
- throw new Error(`Database sync failed during deletion of ${db.$id}. Some changes may have been applied.`);
1908
- }
1909
- }
1910
-
1911
- // Apply creations
1912
- for (const db of toCreate) {
1913
- try {
1914
- log(`Creating database ${db.name} ( ${db.$id} ) ...`);
1915
- await tablesDBCreate({
1916
- databaseId: db.$id,
1917
- name: db.name,
1918
- enabled: db.enabled,
1919
- parseOutput: false
1018
+ if (Number(e.code) === 404) {
1019
+ siteExists = false;
1020
+ } else {
1021
+ errors.push(e);
1022
+ updaterRow.fail({
1023
+ errorMessage:
1024
+ e.message ?? "General error occurs please try again",
1920
1025
  });
1921
- success(`Created ${db.name} ( ${db.$id} )`);
1922
- } catch (e: any) {
1923
- error(`Failed to create database ${db.name} ( ${db.$id} ): ${e.message}`);
1924
- throw new Error(`Database sync failed during creation of ${db.$id}. Some changes may have been applied.`);
1026
+ return;
1027
+ }
1925
1028
  }
1926
- }
1927
1029
 
1928
- // Apply updates
1929
- for (const db of toUpdate) {
1930
- try {
1931
- log(`Updating database ${db.name} ( ${db.$id} ) ...`);
1932
- await tablesDBUpdate({
1933
- databaseId: db.$id,
1934
- name: db.name,
1935
- enabled: db.enabled,
1936
- parseOutput: false
1030
+ if (!siteExists) {
1031
+ updaterRow
1032
+ .update({ status: "Creating" })
1033
+ .replaceSpinner(SPINNER_DOTS);
1034
+
1035
+ try {
1036
+ response = await sitesService.create({
1037
+ siteId: site.$id,
1038
+ name: site.name,
1039
+ framework: site.framework,
1040
+ enabled: site.enabled,
1041
+ logging: site.logging,
1042
+ timeout: site.timeout,
1043
+ installCommand: site.installCommand,
1044
+ buildCommand: site.buildCommand,
1045
+ outputDirectory: site.outputDirectory,
1046
+ buildRuntime: site.buildRuntime,
1047
+ adapter: site.adapter,
1048
+ specification: site.specification,
1937
1049
  });
1938
- success(`Updated ${db.name} ( ${db.$id} )`);
1939
- } catch (e: any) {
1940
- error(`Failed to update database ${db.name} ( ${db.$id} ): ${e.message}`);
1941
- throw new Error(`Database sync failed during update of ${db.$id}. Some changes may have been applied.`);
1942
- }
1943
- }
1944
1050
 
1945
- if (toDelete.length === 0){
1946
- console.log();
1947
- }
1051
+ let domain = "";
1052
+ try {
1053
+ const consoleService = await getConsoleService(
1054
+ this.consoleClient,
1055
+ );
1056
+ const variables = await consoleService.variables();
1057
+ domain = ID.unique() + "." + variables["_APP_DOMAIN_SITES"];
1058
+ } catch (err) {
1059
+ this.error("Error fetching console variables.");
1060
+ throw err;
1061
+ }
1948
1062
 
1949
- return { applied: true, resyncNeeded: needsResync };
1950
- };
1063
+ try {
1064
+ const proxyService = await getProxyService(this.projectClient);
1065
+ await proxyService.createSiteRule(domain, site.$id);
1066
+ } catch (err) {
1067
+ this.error("Error creating site rule.");
1068
+ throw err;
1069
+ }
1951
1070
 
1952
- interface PushTableOptions {
1953
- returnOnZero?: boolean;
1954
- attempts?: number;
1955
- }
1071
+ updaterRow.update({ status: "Created" });
1072
+ } catch (e: any) {
1073
+ errors.push(e);
1074
+ updaterRow.fail({
1075
+ errorMessage:
1076
+ e.message ?? "General error occurs please try again",
1077
+ });
1078
+ return;
1079
+ }
1080
+ }
1956
1081
 
1957
- const pushTable = async ({ returnOnZero, attempts }: PushTableOptions = { returnOnZero: false }): Promise<void> => {
1958
- const tables: any[] = [];
1082
+ if (withVariables) {
1083
+ updaterRow
1084
+ .update({ status: "Creating variables" })
1085
+ .replaceSpinner(SPINNER_DOTS);
1086
+
1087
+ const sitesServiceForVars = await getSitesService(this.projectClient);
1088
+ const { variables } = await paginate(
1089
+ async (args: any) => {
1090
+ return await sitesServiceForVars.listVariables({
1091
+ siteId: args.siteId,
1092
+ });
1093
+ },
1094
+ {
1095
+ siteId: site["$id"],
1096
+ },
1097
+ 100,
1098
+ "variables",
1099
+ );
1100
+
1101
+ await Promise.all(
1102
+ variables.map(async (variable: any) => {
1103
+ const sitesServiceDel = await getSitesService(this.projectClient);
1104
+ await sitesServiceDel.deleteVariable({
1105
+ siteId: site["$id"],
1106
+ variableId: variable["$id"],
1107
+ });
1108
+ }),
1109
+ );
1110
+
1111
+ const envFileLocation = `${site["path"]}/.env`;
1112
+ let envVariables: Array<{ key: string; value: string }> = [];
1113
+ try {
1114
+ if (fs.existsSync(envFileLocation)) {
1115
+ const envObject = parseDotenv(
1116
+ fs.readFileSync(envFileLocation, "utf8"),
1117
+ );
1118
+ envVariables = Object.entries(envObject || {}).map(
1119
+ ([key, value]) => ({ key, value }),
1120
+ );
1121
+ }
1122
+ } catch (error) {
1123
+ envVariables = [];
1124
+ }
1125
+ await Promise.all(
1126
+ envVariables.map(async (variable) => {
1127
+ const sitesServiceCreate = await getSitesService(
1128
+ this.projectClient,
1129
+ );
1130
+ await sitesServiceCreate.createVariable({
1131
+ siteId: site["$id"],
1132
+ key: variable.key,
1133
+ value: variable.value,
1134
+ secret: false,
1135
+ });
1136
+ }),
1137
+ );
1138
+ }
1959
1139
 
1960
- if (attempts) {
1961
- pollMaxDebounces = attempts;
1962
- }
1140
+ if (code === false) {
1141
+ successfullyPushed++;
1142
+ successfullyDeployed++;
1143
+ updaterRow.update({ status: "Pushed" });
1144
+ updaterRow.stopSpinner();
1145
+ return;
1146
+ }
1963
1147
 
1964
- const { applied: tablesDBApplied, resyncNeeded } = await checkAndApplyTablesDBChanges();
1965
- if (resyncNeeded) {
1966
- log('Resyncing configuration due to tablesDB deletions ...');
1148
+ try {
1149
+ updaterRow.update({ status: "Pushing" }).replaceSpinner(SPINNER_DOTS);
1150
+ const sitesServiceDeploy = await getSitesService(this.projectClient);
1151
+
1152
+ const result = await pushDeployment({
1153
+ resourcePath: site.path,
1154
+ createDeployment: async (codeFile) => {
1155
+ return await sitesServiceDeploy.createDeployment({
1156
+ siteId: site["$id"],
1157
+ installCommand: site.installCommand,
1158
+ buildCommand: site.buildCommand,
1159
+ outputDirectory: site.outputDirectory,
1160
+ code: codeFile,
1161
+ activate: true,
1162
+ });
1163
+ },
1164
+ pollForStatus: false,
1165
+ });
1166
+
1167
+ response = result.deployment;
1168
+ updaterRow.update({ status: "Pushed" });
1169
+ deploymentCreated = true;
1170
+ successfullyPushed++;
1171
+ } catch (e: any) {
1172
+ errors.push(e);
1173
+
1174
+ switch (e.code) {
1175
+ case "ENOENT":
1176
+ updaterRow.fail({
1177
+ errorMessage: "Not found in the current directory. Skipping...",
1178
+ });
1179
+ break;
1180
+ default:
1181
+ updaterRow.fail({
1182
+ errorMessage:
1183
+ e.message ?? "An unknown error occurred. Please try again.",
1184
+ });
1185
+ }
1186
+ }
1967
1187
 
1968
- const remoteTablesDBs = (await paginate(tablesDBList, { parseOutput: false }, 100, 'databases')).databases;
1969
- const localTablesDBs = localConfig.getTablesDBs();
1188
+ if (deploymentCreated && !asyncDeploy) {
1189
+ try {
1190
+ const deploymentId = response["$id"];
1191
+ updaterRow.update({
1192
+ status: "Deploying",
1193
+ end: "Checking deployment status...",
1194
+ });
1970
1195
 
1971
- const remoteDatabaseIds = new Set(remoteTablesDBs.map((db: any) => db.$id));
1972
- const localTables = localConfig.getTables();
1973
- const validTables = localTables.filter((table: any) => remoteDatabaseIds.has(table.databaseId));
1196
+ const timeoutDeadline = Date.now() + DEPLOYMENT_TIMEOUT_MS;
1974
1197
 
1975
- localConfig.set('tables', validTables);
1198
+ while (true) {
1199
+ if (Date.now() > timeoutDeadline) {
1200
+ failedDeployments.push({
1201
+ name: site["name"],
1202
+ $id: site["$id"],
1203
+ deployment: deploymentId,
1204
+ });
1205
+ updaterRow.fail({
1206
+ errorMessage: "Deployment timed out after 10 minutes",
1207
+ });
1208
+ break;
1209
+ }
1210
+
1211
+ const sitesServicePoll = await getSitesService(
1212
+ this.projectClient,
1213
+ );
1214
+ response = await sitesServicePoll.getDeployment({
1215
+ siteId: site["$id"],
1216
+ deploymentId: deploymentId,
1217
+ });
1218
+
1219
+ const status = response["status"];
1220
+ if (status === "ready") {
1221
+ successfullyDeployed++;
1222
+
1223
+ let url = "";
1224
+ const proxyServiceUrl = await getProxyService(
1225
+ this.projectClient,
1226
+ );
1227
+ const res = await proxyServiceUrl.listRules({
1228
+ queries: [
1229
+ Query.limit(1),
1230
+ Query.equal("deploymentResourceType", "site"),
1231
+ Query.equal("deploymentResourceId", site["$id"]),
1232
+ Query.equal("trigger", "manual"),
1233
+ ],
1234
+ });
1976
1235
 
1977
- const validTablesDBs = localTablesDBs.filter((db: any) => remoteDatabaseIds.has(db.$id));
1978
- localConfig.set('tablesDB', validTablesDBs);
1236
+ if (Number(res.total) === 1) {
1237
+ url = `https://${res.rules[0].domain}`;
1238
+ }
1979
1239
 
1980
- success('Configuration resynced successfully.');
1981
- console.log();
1982
- }
1240
+ updaterRow.update({ status: "Deployed", end: url });
1983
1241
 
1984
- log('Checking for deleted tables ...');
1985
- const localTablesDBs = localConfig.getTablesDBs();
1986
- const localTables = localConfig.getTables();
1987
- const tablesToDelete: any[] = [];
1242
+ break;
1243
+ } else if (status === "failed") {
1244
+ failedDeployments.push({
1245
+ name: site["name"],
1246
+ $id: site["$id"],
1247
+ deployment: response["$id"],
1248
+ });
1249
+ updaterRow.fail({ errorMessage: `Failed to deploy` });
1988
1250
 
1989
- for (const db of localTablesDBs) {
1990
- try {
1991
- const { tables: remoteTables } = await paginate(tablesDBListTables, {
1992
- databaseId: db.$id,
1993
- parseOutput: false
1994
- }, 100, 'tables');
1995
-
1996
- for (const remoteTable of remoteTables) {
1997
- const localTable = localTables.find((t: any) => t.$id === remoteTable.$id && t.databaseId === db.$id);
1998
- if (!localTable) {
1999
- tablesToDelete.push({
2000
- ...remoteTable,
2001
- databaseId: db.$id,
2002
- databaseName: db.name
2003
- });
2004
- }
2005
- }
2006
- } catch (e) {
2007
- // Skip if database doesn't exist or other errors
2008
- }
2009
- }
1251
+ break;
1252
+ } else {
1253
+ updaterRow.update({
1254
+ status: "Deploying",
1255
+ end: `Current status: ${status}`,
1256
+ });
1257
+ }
2010
1258
 
2011
- if (tablesToDelete.length > 0) {
2012
- log('Found tables that exist remotely but not locally:');
2013
- const deletionChanges = tablesToDelete.map((table: any) => ({
2014
- id: table.$id,
2015
- action: chalk.red('deleting'),
2016
- key: 'Table',
2017
- database: table.databaseName,
2018
- remote: table.name,
2019
- local: '(deleted locally)'
2020
- }));
2021
- drawTable(deletionChanges);
2022
-
2023
- if ((await getConfirmation()) === true) {
2024
- for (const table of tablesToDelete) {
2025
- try {
2026
- log(`Deleting table ${table.name} ( ${table.$id} ) from database ${table.databaseName} ...`);
2027
- await tablesDBDeleteTable({
2028
- databaseId: table.databaseId,
2029
- tableId: table.$id,
2030
- parseOutput: false
2031
- });
2032
- success(`Deleted ${table.name} ( ${table.$id} )`);
2033
- } catch (e: any) {
2034
- error(`Failed to delete table ${table.name} ( ${table.$id} ): ${e.message}`);
2035
- }
1259
+ await new Promise((resolve) =>
1260
+ setTimeout(resolve, POLL_DEBOUNCE),
1261
+ );
2036
1262
  }
2037
- }
2038
- } else {
2039
- console.log('No tables found to delete');
2040
- }
2041
- console.log();
2042
-
2043
- if (cliConfig.all) {
2044
- checkDeployConditions(localConfig);
2045
- tables.push(...localConfig.getTables());
2046
- } else {
2047
- const answers = await inquirer.prompt(questionsPushTables)
2048
- if (answers.tables) {
2049
- const configTables = new Map();
2050
- localConfig.getTables().forEach((c: any) => {
2051
- configTables.set(`${c['databaseId']}|${c['$id']}`, c);
1263
+ } catch (e: any) {
1264
+ errors.push(e);
1265
+ updaterRow.fail({
1266
+ errorMessage:
1267
+ e.message ?? "Unknown error occurred. Please try again",
2052
1268
  });
2053
- answers.tables.forEach((a: any) => {
2054
- const table = configTables.get(a);
2055
- tables.push(table);
2056
- })
1269
+ }
2057
1270
  }
2058
- }
2059
1271
 
2060
- if (tables.length === 0) {
2061
- log("No tables found.");
2062
- hint("Use 'appwrite pull tables' to synchronize existing one, or use 'appwrite init table' to create a new one.");
2063
- return;
2064
- }
1272
+ updaterRow.stopSpinner();
1273
+ }),
1274
+ );
1275
+
1276
+ Spinner.stop();
1277
+
1278
+ return {
1279
+ successfullyPushed,
1280
+ successfullyDeployed,
1281
+ failedDeployments,
1282
+ errors,
1283
+ };
1284
+ }
1285
+
1286
+ public async pushTables(
1287
+ tables: any[],
1288
+ options: PushTableOptions = {},
1289
+ ): Promise<{
1290
+ successfullyPushed: number;
1291
+ errors: any[];
1292
+ }> {
1293
+ const { attempts, skipConfirmation = false } = options;
1294
+ const pollMaxDebounces = attempts ?? POLL_DEFAULT_VALUE;
1295
+ const pools = new Pools(pollMaxDebounces);
1296
+ const attributes = new Attributes(pools, skipConfirmation);
2065
1297
 
2066
- if (!(await approveChanges(tables, tablesDBGetTable, KeysTable, 'tableId', 'tables', ['columns', 'indexes'], 'databaseId', 'databaseId'))) {
2067
- return;
2068
- }
2069
1298
  let tablesChanged = new Set();
1299
+ const errors: any[] = [];
2070
1300
 
2071
1301
  // Parallel tables actions
2072
- await Promise.all(tables.map(async (table: any) => {
1302
+ await Promise.all(
1303
+ tables.map(async (table: any) => {
2073
1304
  try {
2074
- const remoteTable = await tablesDBGetTable({
2075
- databaseId: table['databaseId'],
2076
- tableId: table['$id'],
2077
- parseOutput: false,
1305
+ const tablesService = await getTablesDBService(this.projectClient);
1306
+ const remoteTable = await tablesService.getTable({
1307
+ databaseId: table["databaseId"],
1308
+ tableId: table["$id"],
1309
+ });
1310
+
1311
+ const changes: string[] = [];
1312
+ if (remoteTable.name !== table.name) changes.push("name");
1313
+ if (remoteTable.rowSecurity !== table.rowSecurity)
1314
+ changes.push("rowSecurity");
1315
+ if (remoteTable.enabled !== table.enabled) changes.push("enabled");
1316
+ if (
1317
+ JSON.stringify(remoteTable["$permissions"]) !==
1318
+ JSON.stringify(table["$permissions"])
1319
+ )
1320
+ changes.push("permissions");
1321
+
1322
+ if (changes.length > 0) {
1323
+ await tablesService.updateTable({
1324
+ databaseId: table["databaseId"],
1325
+ tableId: table["$id"],
1326
+ name: table.name,
1327
+ rowSecurity: table.rowSecurity,
1328
+ permissions: table["$permissions"],
2078
1329
  });
2079
1330
 
2080
- const changes: string[] = [];
2081
- if (remoteTable.name !== table.name) changes.push('name');
2082
- if (remoteTable.rowSecurity !== table.rowSecurity) changes.push('rowSecurity');
2083
- if (remoteTable.enabled !== table.enabled) changes.push('enabled');
2084
- if (JSON.stringify(remoteTable['$permissions']) !== JSON.stringify(table['$permissions'])) changes.push('permissions');
2085
-
2086
- if (changes.length > 0) {
2087
- await tablesDBUpdateTable({
2088
- databaseId: table['databaseId'],
2089
- tableId: table['$id'],
2090
- name: table.name,
2091
- parseOutput: false,
2092
- rowSecurity: table.rowSecurity,
2093
- permissions: table['$permissions']
2094
- })
2095
-
2096
- success(`Updated ${table.name} ( ${table['$id']} ) - ${changes.join(', ')}`);
2097
- tablesChanged.add(table['$id']);
2098
- }
2099
- table.remoteVersion = remoteTable;
1331
+ this.success(
1332
+ `Updated ${table.name} ( ${table["$id"]} ) - ${changes.join(", ")}`,
1333
+ );
1334
+ tablesChanged.add(table["$id"]);
1335
+ }
1336
+ table.remoteVersion = remoteTable;
2100
1337
 
2101
- table.isExisted = true;
1338
+ table.isExisted = true;
2102
1339
  } catch (e: any) {
2103
- if (Number(e.code) === 404) {
2104
- log(`Table ${table.name} does not exist in the project. Creating ... `);
2105
- await tablesDBCreateTable({
2106
- databaseId: table['databaseId'],
2107
- tableId: table['$id'],
2108
- name: table.name,
2109
- rowSecurity: table.rowSecurity,
2110
- permissions: table['$permissions'],
2111
- parseOutput: false
2112
- })
2113
-
2114
- success(`Created ${table.name} ( ${table['$id']} )`);
2115
- tablesChanged.add(table['$id']);
2116
- } else {
2117
- throw e;
2118
- }
1340
+ if (Number(e.code) === 404) {
1341
+ this.log(
1342
+ `Table ${table.name} does not exist in the project. Creating ... `,
1343
+ );
1344
+ const tablesService = await getTablesDBService(this.projectClient);
1345
+ await tablesService.createTable({
1346
+ databaseId: table["databaseId"],
1347
+ tableId: table["$id"],
1348
+ name: table.name,
1349
+ rowSecurity: table.rowSecurity,
1350
+ permissions: table["$permissions"]
1351
+ ? [...table["$permissions"]]
1352
+ : undefined,
1353
+ });
1354
+
1355
+ this.success(`Created ${table.name} ( ${table["$id"]} )`);
1356
+ tablesChanged.add(table["$id"]);
1357
+ } else {
1358
+ errors.push(e);
1359
+ throw e;
1360
+ }
2119
1361
  }
2120
- }))
1362
+ }),
1363
+ );
2121
1364
 
2122
1365
  // Serialize attribute actions
2123
1366
  for (let table of tables) {
2124
- let columns = table.columns;
2125
- let indexes = table.indexes;
2126
-
2127
- if (table.isExisted) {
2128
- columns = await attributesToCreate(table.remoteVersion.columns, table.columns, table);
2129
- indexes = await attributesToCreate(table.remoteVersion.indexes, table.indexes, table, true);
2130
-
2131
- if ((Array.isArray(columns) && columns.length <= 0) && (Array.isArray(indexes) && indexes.length <= 0)) {
2132
- continue;
2133
- }
2134
- }
2135
-
2136
- log(`Pushing table ${table.name} ( ${table['databaseId']} - ${table['$id']} ) attributes`)
2137
-
2138
- try {
2139
- await createColumns(columns, table)
2140
- } catch (e) {
2141
- throw e;
2142
- }
1367
+ let columns = table.columns;
1368
+ let indexes = table.indexes;
1369
+
1370
+ if (table.isExisted) {
1371
+ columns = await attributes.attributesToCreate(
1372
+ table.remoteVersion.columns,
1373
+ table.columns,
1374
+ table as Collection,
1375
+ );
1376
+ indexes = await attributes.attributesToCreate(
1377
+ table.remoteVersion.indexes,
1378
+ table.indexes,
1379
+ table as Collection,
1380
+ true,
1381
+ );
2143
1382
 
2144
- try {
2145
- await createIndexes(indexes, table);
2146
- } catch (e) {
2147
- throw e;
1383
+ if (
1384
+ Array.isArray(columns) &&
1385
+ columns.length <= 0 &&
1386
+ Array.isArray(indexes) &&
1387
+ indexes.length <= 0
1388
+ ) {
1389
+ continue;
2148
1390
  }
2149
- tablesChanged.add(table['$id']);
2150
- success(`Successfully pushed ${table.name} ( ${table['$id']} )`);
2151
- }
2152
-
2153
- success(`Successfully pushed ${tablesChanged.size} tables`);
2154
- }
1391
+ }
2155
1392
 
2156
- interface PushCollectionOptions {
2157
- returnOnZero?: boolean;
2158
- attempts?: number;
2159
- }
1393
+ this.log(
1394
+ `Pushing table ${table.name} ( ${table["databaseId"]} - ${table["$id"]} ) attributes`,
1395
+ );
2160
1396
 
2161
- const pushCollection = async ({ returnOnZero, attempts }: PushCollectionOptions = { returnOnZero: false }): Promise<void> => {
2162
- warn("appwrite push collection has been deprecated. Please consider using 'appwrite push tables' instead");
2163
- const collections: any[] = [];
1397
+ try {
1398
+ await attributes.createColumns(columns, table as Collection);
1399
+ } catch (e) {
1400
+ errors.push(e);
1401
+ throw e;
1402
+ }
2164
1403
 
2165
- if (attempts) {
2166
- pollMaxDebounces = attempts;
1404
+ try {
1405
+ await attributes.createIndexes(indexes, table as Collection);
1406
+ } catch (e) {
1407
+ errors.push(e);
1408
+ throw e;
1409
+ }
1410
+ tablesChanged.add(table["$id"]);
1411
+ this.success(`Successfully pushed ${table.name} ( ${table["$id"]} )`);
2167
1412
  }
2168
1413
 
2169
- if (cliConfig.all) {
2170
- checkDeployConditions(localConfig);
2171
- collections.push(...localConfig.getCollections());
2172
- } else {
2173
- const answers = await inquirer.prompt(questionsPushCollections)
2174
- if (answers.collections) {
2175
- const configCollections = new Map();
2176
- localConfig.getCollections().forEach((c: any) => {
2177
- configCollections.set(`${c['databaseId']}|${c['$id']}`, c);
2178
- });
2179
- answers.collections.forEach((a: any) => {
2180
- const collection = configCollections.get(a);
2181
- collections.push(collection);
2182
- })
2183
- }
2184
- }
1414
+ return {
1415
+ successfullyPushed: tablesChanged.size,
1416
+ errors,
1417
+ };
1418
+ }
1419
+
1420
+ public async pushCollections(
1421
+ collections: any[],
1422
+ options: { skipConfirmation?: boolean } = {},
1423
+ ): Promise<{
1424
+ successfullyPushed: number;
1425
+ errors: any[];
1426
+ }> {
1427
+ const { skipConfirmation = false } = options;
1428
+ const pools = new Pools(POLL_DEFAULT_VALUE);
1429
+ const attributes = new Attributes(pools, skipConfirmation);
2185
1430
 
2186
- if (collections.length === 0) {
2187
- log("No collections found.");
2188
- hint("Use 'appwrite pull collections' to synchronize existing one, or use 'appwrite init collection' to create a new one.");
2189
- return;
2190
- }
1431
+ const errors: any[] = [];
2191
1432
 
2192
- const databases = Array.from(new Set(collections.map((collection: any) => collection['databaseId'])));
1433
+ const databases = Array.from(
1434
+ new Set(collections.map((collection: any) => collection["databaseId"])),
1435
+ );
2193
1436
 
2194
1437
  // Parallel db actions
2195
- await Promise.all(databases.map(async (databaseId: any) => {
2196
- const localDatabase = localConfig.getDatabase(databaseId);
2197
-
1438
+ await Promise.all(
1439
+ databases.map(async (databaseId: any) => {
1440
+ const databasesService = await getDatabasesService(this.projectClient);
2198
1441
  try {
2199
- const database = await databasesGet({
2200
- databaseId: databaseId,
2201
- parseOutput: false,
2202
- });
1442
+ const database = await databasesService.get(databaseId);
2203
1443
 
2204
- if (database.name !== (localDatabase.name ?? databaseId)) {
2205
- await databasesUpdate({
2206
- databaseId: databaseId,
2207
- name: localDatabase.name ?? databaseId,
2208
- parseOutput: false
2209
- })
1444
+ // Note: We can't get the local database name here since we don't have access to localConfig
1445
+ // This will need to be handled by the caller if needed
1446
+ const localDatabaseName =
1447
+ collections.find((c: any) => c.databaseId === databaseId)
1448
+ ?.databaseName ?? databaseId;
2210
1449
 
2211
- success(`Updated ${localDatabase.name} ( ${databaseId} ) name`);
2212
- }
2213
- } catch (err) {
2214
- log(`Database ${databaseId} not found. Creating it now ...`);
1450
+ if (database.name !== localDatabaseName) {
1451
+ await databasesService.update(databaseId, localDatabaseName);
2215
1452
 
2216
- await databasesCreate({
2217
- databaseId: databaseId,
2218
- name: localDatabase.name ?? databaseId,
2219
- parseOutput: false,
2220
- });
1453
+ this.success(`Updated ${localDatabaseName} ( ${databaseId} ) name`);
1454
+ }
1455
+ } catch (err: any) {
1456
+ if (Number(err.code) === 404) {
1457
+ this.log(`Database ${databaseId} not found. Creating it now ...`);
1458
+
1459
+ const localDatabaseName =
1460
+ collections.find((c: any) => c.databaseId === databaseId)
1461
+ ?.databaseName ?? databaseId;
1462
+
1463
+ await databasesService.create(databaseId, localDatabaseName);
1464
+ } else {
1465
+ throw err;
1466
+ }
2221
1467
  }
2222
- }));
1468
+ }),
1469
+ );
2223
1470
 
2224
- if (!(await approveChanges(collections, databasesGetCollection, KeysCollection, 'collectionId', 'collections', ['attributes', 'indexes'], 'databaseId', 'databaseId',))) {
2225
- return;
2226
- }
2227
1471
  // Parallel collection actions
2228
- await Promise.all(collections.map(async (collection: any) => {
1472
+ await Promise.all(
1473
+ collections.map(async (collection: any) => {
2229
1474
  try {
2230
- const remoteCollection = await databasesGetCollection({
2231
- databaseId: collection['databaseId'],
2232
- collectionId: collection['$id'],
2233
- parseOutput: false,
2234
- });
2235
-
2236
- if (remoteCollection.name !== collection.name) {
2237
- await databasesUpdateCollection({
2238
- databaseId: collection['databaseId'],
2239
- collectionId: collection['$id'],
2240
- name: collection.name,
2241
- parseOutput: false
2242
- })
2243
-
2244
- success(`Updated ${collection.name} ( ${collection['$id']} ) name`);
2245
- }
2246
- collection.remoteVersion = remoteCollection;
2247
-
2248
- collection.isExisted = true;
1475
+ const databasesService = await getDatabasesService(
1476
+ this.projectClient,
1477
+ );
1478
+ const remoteCollection = await databasesService.getCollection(
1479
+ collection["databaseId"],
1480
+ collection["$id"],
1481
+ );
1482
+
1483
+ if (remoteCollection.name !== collection.name) {
1484
+ await databasesService.updateCollection(
1485
+ collection["databaseId"],
1486
+ collection["$id"],
1487
+ collection.name,
1488
+ );
1489
+
1490
+ this.success(
1491
+ `Updated ${collection.name} ( ${collection["$id"]} ) name`,
1492
+ );
1493
+ }
1494
+ collection.remoteVersion = remoteCollection;
1495
+
1496
+ collection.isExisted = true;
2249
1497
  } catch (e: any) {
2250
- if (Number(e.code) === 404) {
2251
- log(`Collection ${collection.name} does not exist in the project. Creating ... `);
2252
- await databasesCreateCollection({
2253
- databaseId: collection['databaseId'],
2254
- collectionId: collection['$id'],
2255
- name: collection.name,
2256
- documentSecurity: collection.documentSecurity,
2257
- permissions: collection['$permissions'],
2258
- parseOutput: false
2259
- })
2260
- } else {
2261
- throw e;
2262
- }
1498
+ if (Number(e.code) === 404) {
1499
+ this.log(
1500
+ `Collection ${collection.name} does not exist in the project. Creating ... `,
1501
+ );
1502
+ const databasesService = await getDatabasesService(
1503
+ this.projectClient,
1504
+ );
1505
+ await databasesService.createCollection({
1506
+ databaseId: collection["databaseId"],
1507
+ collectionId: collection["$id"],
1508
+ name: collection.name,
1509
+ documentSecurity: collection.documentSecurity,
1510
+ permissions: collection["$permissions"],
1511
+ });
1512
+ } else {
1513
+ errors.push(e);
1514
+ throw e;
1515
+ }
2263
1516
  }
2264
- }))
1517
+ }),
1518
+ );
1519
+
2265
1520
  let numberOfCollections = 0;
2266
1521
  // Serialize attribute actions
2267
1522
  for (let collection of collections) {
2268
- let attributes = collection.attributes;
2269
- let indexes = collection.indexes;
2270
-
2271
- if (collection.isExisted) {
2272
- attributes = await attributesToCreate(collection.remoteVersion.attributes, collection.attributes, collection);
2273
- indexes = await attributesToCreate(collection.remoteVersion.indexes, collection.indexes, collection, true);
2274
-
2275
- if ((Array.isArray(attributes) && attributes.length <= 0) && (Array.isArray(indexes) && indexes.length <= 0)) {
2276
- continue;
2277
- }
1523
+ let collectionAttributes = collection.attributes;
1524
+ let indexes = collection.indexes;
1525
+
1526
+ if (collection.isExisted) {
1527
+ collectionAttributes = await attributes.attributesToCreate(
1528
+ collection.remoteVersion.attributes,
1529
+ collection.attributes,
1530
+ collection as Collection,
1531
+ );
1532
+ indexes = await attributes.attributesToCreate(
1533
+ collection.remoteVersion.indexes,
1534
+ collection.indexes,
1535
+ collection as Collection,
1536
+ true,
1537
+ );
2278
1538
 
1539
+ if (
1540
+ Array.isArray(collectionAttributes) &&
1541
+ collectionAttributes.length <= 0 &&
1542
+ Array.isArray(indexes) &&
1543
+ indexes.length <= 0
1544
+ ) {
1545
+ continue;
2279
1546
  }
1547
+ }
2280
1548
 
2281
- log(`Pushing collection ${collection.name} ( ${collection['databaseId']} - ${collection['$id']} ) attributes`)
1549
+ this.log(
1550
+ `Pushing collection ${collection.name} ( ${collection["databaseId"]} - ${collection["$id"]} ) attributes`,
1551
+ );
2282
1552
 
2283
- try {
2284
- await createAttributes(attributes, collection)
2285
- } catch (e) {
2286
- throw e;
2287
- }
1553
+ try {
1554
+ await attributes.createAttributes(
1555
+ collectionAttributes,
1556
+ collection as Collection,
1557
+ );
1558
+ } catch (e) {
1559
+ errors.push(e);
1560
+ throw e;
1561
+ }
2288
1562
 
2289
- try {
2290
- await createIndexes(indexes, collection);
2291
- } catch (e) {
2292
- throw e;
2293
- }
2294
- numberOfCollections++;
2295
- success(`Successfully pushed ${collection.name} ( ${collection['$id']} )`);
1563
+ try {
1564
+ await attributes.createIndexes(indexes, collection as Collection);
1565
+ } catch (e) {
1566
+ errors.push(e);
1567
+ throw e;
1568
+ }
1569
+ numberOfCollections++;
1570
+ this.success(
1571
+ `Successfully pushed ${collection.name} ( ${collection["$id"]} )`,
1572
+ );
2296
1573
  }
2297
1574
 
2298
- success(`Successfully pushed ${numberOfCollections} collections`);
1575
+ return {
1576
+ successfullyPushed: numberOfCollections,
1577
+ errors,
1578
+ };
1579
+ }
2299
1580
  }
2300
1581
 
2301
- interface PushBucketOptions {
2302
- returnOnZero?: boolean;
1582
+ async function createPushInstance(silent = false): Promise<Push> {
1583
+ const projectClient = await sdkForProject();
1584
+ const consoleClient = await sdkForConsole();
1585
+ return new Push(projectClient, consoleClient, silent);
2303
1586
  }
2304
1587
 
2305
- const pushBucket = async ({ returnOnZero }: PushBucketOptions = { returnOnZero: false }): Promise<void> => {
2306
- let response: any = {};
1588
+ const pushResources = async ({
1589
+ skipDeprecated = false,
1590
+ }: {
1591
+ skipDeprecated?: boolean;
1592
+ } = {}): Promise<void> => {
1593
+ if (cliConfig.all) {
1594
+ checkDeployConditions(localConfig);
2307
1595
 
2308
- let bucketIds: string[] = [];
2309
- const configBuckets = localConfig.getBuckets();
1596
+ const pushInstance = await createPushInstance();
1597
+ const config = localConfig.getProject() as ConfigType;
2310
1598
 
2311
- if (cliConfig.all) {
2312
- checkDeployConditions(localConfig);
2313
- bucketIds.push(...configBuckets.map((b: any) => b.$id));
2314
- }
1599
+ await pushInstance.pushResources(config, {
1600
+ skipDeprecated,
1601
+ functionOptions: { code: true, withVariables: false },
1602
+ siteOptions: { code: true, withVariables: false },
1603
+ });
1604
+ } else {
1605
+ const actions: Record<string, (options?: any) => Promise<void>> = {
1606
+ settings: pushSettings,
1607
+ functions: pushFunction,
1608
+ sites: pushSite,
1609
+ collections: pushCollection,
1610
+ tables: pushTable,
1611
+ buckets: pushBucket,
1612
+ teams: pushTeam,
1613
+ messages: pushMessagingTopic,
1614
+ };
2315
1615
 
2316
- if (bucketIds.length === 0) {
2317
- const answers = await inquirer.prompt(questionsPushBuckets[0])
2318
- if (answers.buckets) {
2319
- bucketIds.push(...answers.buckets);
2320
- }
1616
+ if (skipDeprecated) {
1617
+ delete actions.collections;
2321
1618
  }
2322
1619
 
2323
- if (bucketIds.length === 0) {
2324
- log("No buckets found.");
2325
- hint("Use 'appwrite pull buckets' to synchronize existing one, or use 'appwrite init bucket' to create a new one.");
2326
- return;
1620
+ const answers = await inquirer.prompt(questionsPushResources);
1621
+
1622
+ const action = actions[answers.resource];
1623
+ if (action !== undefined) {
1624
+ await action();
2327
1625
  }
1626
+ }
1627
+ };
2328
1628
 
2329
- let buckets: any[] = [];
1629
+ const pushSettings = async (): Promise<void> => {
1630
+ checkDeployConditions(localConfig);
2330
1631
 
2331
- for (const bucketId of bucketIds) {
2332
- const idBuckets = configBuckets.filter((b: any) => b.$id === bucketId);
2333
- buckets.push(...idBuckets);
2334
- }
1632
+ try {
1633
+ const projectsService = await getProjectsService();
1634
+ let response = await projectsService.get(
1635
+ localConfig.getProject().projectId,
1636
+ );
1637
+
1638
+ const remoteSettings = createSettingsObject(response);
1639
+ const localSettings = localConfig.getProject().projectSettings ?? {};
2335
1640
 
2336
- if (!(await approveChanges(buckets, storageGetBucket, KeysStorage, 'bucketId', 'buckets'))) {
1641
+ log("Checking for changes ...");
1642
+ const changes: any[] = [];
1643
+
1644
+ changes.push(
1645
+ ...getObjectChanges(remoteSettings, localSettings, "services", "Service"),
1646
+ );
1647
+ changes.push(
1648
+ ...getObjectChanges(
1649
+ remoteSettings["auth"] ?? {},
1650
+ localSettings["auth"] ?? {},
1651
+ "methods",
1652
+ "Auth method",
1653
+ ),
1654
+ );
1655
+ changes.push(
1656
+ ...getObjectChanges(
1657
+ remoteSettings["auth"] ?? {},
1658
+ localSettings["auth"] ?? {},
1659
+ "security",
1660
+ "Auth security",
1661
+ ),
1662
+ );
1663
+
1664
+ if (changes.length > 0) {
1665
+ drawTable(changes);
1666
+ if ((await getConfirmation()) !== true) {
1667
+ success(`Successfully pushed 0 project settings.`);
2337
1668
  return;
1669
+ }
2338
1670
  }
1671
+ } catch (e) {}
2339
1672
 
2340
- log('Pushing buckets ...');
1673
+ try {
1674
+ log("Pushing project settings ...");
2341
1675
 
2342
- for (let bucket of buckets) {
2343
- log(`Pushing bucket ${chalk.bold(bucket['name'])} ...`);
1676
+ const pushInstance = await createPushInstance();
1677
+ const config = localConfig.getProject();
2344
1678
 
2345
- try {
2346
- response = await storageGetBucket({
2347
- bucketId: bucket['$id'],
2348
- parseOutput: false,
2349
- })
2350
-
2351
- await storageUpdateBucket({
2352
- bucketId: bucket['$id'],
2353
- name: bucket.name,
2354
- permissions: bucket['$permissions'],
2355
- fileSecurity: bucket.fileSecurity,
2356
- enabled: bucket.enabled,
2357
- maximumFileSize: bucket.maximumFileSize,
2358
- allowedFileExtensions: bucket.allowedFileExtensions,
2359
- encryption: bucket.encryption,
2360
- antivirus: bucket.antivirus,
2361
- compression: bucket.compression,
2362
- parseOutput: false
2363
- });
2364
- } catch (e: any) {
2365
- if (Number(e.code) === 404) {
2366
- log(`Bucket ${bucket.name} does not exist in the project. Creating ... `);
2367
-
2368
- response = await storageCreateBucket({
2369
- bucketId: bucket['$id'],
2370
- name: bucket.name,
2371
- permissions: bucket['$permissions'],
2372
- fileSecurity: bucket.fileSecurity,
2373
- enabled: bucket.enabled,
2374
- maximumFileSize: bucket.maximumFileSize,
2375
- allowedFileExtensions: bucket.allowedFileExtensions,
2376
- compression: bucket.compression,
2377
- encryption: bucket.encryption,
2378
- antivirus: bucket.antivirus,
2379
- parseOutput: false
2380
- })
2381
- } else {
2382
- throw e;
2383
- }
2384
- }
2385
- }
1679
+ await pushInstance.pushSettings({
1680
+ projectId: config.projectId,
1681
+ projectName: config.projectName,
1682
+ settings: config.projectSettings,
1683
+ });
2386
1684
 
2387
- success(`Successfully pushed ${buckets.length} buckets.`);
2388
- }
1685
+ success(`Successfully pushed ${chalk.bold("all")} project settings.`);
1686
+ } catch (e) {
1687
+ throw e;
1688
+ }
1689
+ };
2389
1690
 
2390
- interface PushTeamOptions {
2391
- returnOnZero?: boolean;
2392
- }
1691
+ const pushSite = async ({
1692
+ siteId,
1693
+ async: asyncDeploy,
1694
+ code,
1695
+ withVariables,
1696
+ }: PushSiteOptions = {}): Promise<void> => {
1697
+ process.chdir(localConfig.configDirectoryPath);
2393
1698
 
2394
- const pushTeam = async ({ returnOnZero }: PushTeamOptions = { returnOnZero: false }): Promise<void> => {
2395
- let response: any = {};
1699
+ const siteIds: string[] = [];
2396
1700
 
2397
- let teamIds: string[] = [];
2398
- const configTeams = localConfig.getTeams();
1701
+ if (siteId) {
1702
+ siteIds.push(siteId);
1703
+ } else if (cliConfig.all) {
1704
+ checkDeployConditions(localConfig);
1705
+ const sites = localConfig.getSites();
1706
+ siteIds.push(
1707
+ ...sites.map((site: any) => {
1708
+ return site.$id;
1709
+ }),
1710
+ );
1711
+ }
2399
1712
 
2400
- if (cliConfig.all) {
2401
- checkDeployConditions(localConfig);
2402
- teamIds.push(...configTeams.map((t: any) => t.$id));
1713
+ if (siteIds.length <= 0) {
1714
+ const answers = await inquirer.prompt(questionsPushSites);
1715
+ if (answers.sites) {
1716
+ siteIds.push(...answers.sites);
2403
1717
  }
1718
+ }
2404
1719
 
2405
- if (teamIds.length === 0) {
2406
- const answers = await inquirer.prompt(questionsPushTeams[0])
2407
- if (answers.teams) {
2408
- teamIds.push(...answers.teams);
2409
- }
1720
+ if (siteIds.length === 0) {
1721
+ log("No sites found.");
1722
+ hint(
1723
+ `Use '${EXECUTABLE_NAME} pull sites' to synchronize existing one, or use '${EXECUTABLE_NAME} init site' to create a new one.`,
1724
+ );
1725
+ return;
1726
+ }
1727
+
1728
+ let sites = siteIds.map((id: string) => {
1729
+ const sites = localConfig.getSites();
1730
+ const site = sites.find((s: any) => s.$id === id);
1731
+
1732
+ if (!site) {
1733
+ throw new Error("Site '" + id + "' not found.");
1734
+ }
1735
+
1736
+ return site;
1737
+ });
1738
+
1739
+ log("Validating sites ...");
1740
+ // Validation is done BEFORE pushing so the deployment process can be run in async with progress update
1741
+ for (let site of sites) {
1742
+ if (!site.buildCommand) {
1743
+ log(`Site ${site.name} is missing build command.`);
1744
+ const answers = await inquirer.prompt(questionsGetEntrypoint);
1745
+ site.buildCommand = answers.entrypoint;
1746
+ localConfig.addSite(site);
1747
+ }
1748
+ }
1749
+
1750
+ if (
1751
+ !(await approveChanges(
1752
+ sites,
1753
+ async (args: any) => {
1754
+ const sitesService = await getSitesService();
1755
+ return await sitesService.get({ siteId: args.siteId });
1756
+ },
1757
+ KeysSite,
1758
+ "siteId",
1759
+ "sites",
1760
+ ["vars"],
1761
+ ))
1762
+ ) {
1763
+ return;
1764
+ }
1765
+
1766
+ log("Pushing sites ...");
1767
+
1768
+ const pushInstance = await createPushInstance();
1769
+ const result = await pushInstance.pushSites(sites, {
1770
+ async: asyncDeploy,
1771
+ code,
1772
+ withVariables,
1773
+ });
1774
+
1775
+ const {
1776
+ successfullyPushed,
1777
+ successfullyDeployed,
1778
+ failedDeployments,
1779
+ errors,
1780
+ } = result;
1781
+
1782
+ failedDeployments.forEach((failed) => {
1783
+ const { name, deployment, $id } = failed;
1784
+ const failUrl = `${globalConfig.getEndpoint().slice(0, -3)}/console/project-${localConfig.getProject().projectId}/sites/site-${$id}/deployments/deployment-${deployment}`;
1785
+
1786
+ error(
1787
+ `Deployment of ${name} has failed. Check at ${failUrl} for more details\n`,
1788
+ );
1789
+ });
1790
+
1791
+ if (!asyncDeploy) {
1792
+ if (successfullyPushed === 0) {
1793
+ error("No sites were pushed.");
1794
+ } else if (successfullyDeployed !== successfullyPushed) {
1795
+ warn(
1796
+ `Successfully pushed ${successfullyDeployed} of ${successfullyPushed} sites`,
1797
+ );
1798
+ } else {
1799
+ success(`Successfully pushed ${successfullyPushed} sites.`);
2410
1800
  }
1801
+ } else {
1802
+ success(`Successfully pushed ${successfullyPushed} sites.`);
1803
+ }
2411
1804
 
2412
- if (teamIds.length === 0) {
2413
- log("No teams found.");
2414
- hint("Use 'appwrite pull teams' to synchronize existing one, or use 'appwrite init team' to create a new one.");
2415
- return;
2416
- }
1805
+ if (cliConfig.verbose) {
1806
+ errors.forEach((e) => {
1807
+ console.error(e);
1808
+ });
1809
+ }
1810
+ };
1811
+
1812
+ const pushFunction = async ({
1813
+ functionId,
1814
+ async: asyncDeploy,
1815
+ code,
1816
+ withVariables,
1817
+ }: PushFunctionOptions = {}): Promise<void> => {
1818
+ process.chdir(localConfig.configDirectoryPath);
2417
1819
 
2418
- let teams: any[] = [];
1820
+ const functionIds: string[] = [];
2419
1821
 
2420
- for (const teamId of teamIds) {
2421
- const idTeams = configTeams.filter((t: any) => t.$id === teamId);
2422
- teams.push(...idTeams);
1822
+ if (functionId) {
1823
+ functionIds.push(functionId);
1824
+ } else if (cliConfig.all) {
1825
+ checkDeployConditions(localConfig);
1826
+ const functions = localConfig.getFunctions();
1827
+ functionIds.push(
1828
+ ...functions.map((func: any) => {
1829
+ return func.$id;
1830
+ }),
1831
+ );
1832
+ }
1833
+
1834
+ if (functionIds.length <= 0) {
1835
+ const answers = await inquirer.prompt(questionsPushFunctions);
1836
+ if (answers.functions) {
1837
+ functionIds.push(...answers.functions);
2423
1838
  }
1839
+ }
2424
1840
 
2425
- if (!(await approveChanges(teams, teamsGet, KeysTeams, 'teamId', 'teams'))) {
2426
- return;
1841
+ if (functionIds.length === 0) {
1842
+ log("No functions found.");
1843
+ hint(
1844
+ `Use '${EXECUTABLE_NAME} pull functions' to synchronize existing one, or use '${EXECUTABLE_NAME} init function' to create a new one.`,
1845
+ );
1846
+ return;
1847
+ }
1848
+
1849
+ let functions = functionIds.map((id: string) => {
1850
+ const functions = localConfig.getFunctions();
1851
+ const func = functions.find((f: any) => f.$id === id);
1852
+
1853
+ if (!func) {
1854
+ throw new Error("Function '" + id + "' not found.");
1855
+ }
1856
+
1857
+ return func;
1858
+ });
1859
+
1860
+ log("Validating functions ...");
1861
+ for (let func of functions) {
1862
+ if (!func.entrypoint) {
1863
+ log(`Function ${func.name} is missing an entrypoint.`);
1864
+ const answers = await inquirer.prompt(questionsGetEntrypoint);
1865
+ func.entrypoint = answers.entrypoint;
1866
+ localConfig.addFunction(func);
1867
+ }
1868
+ }
1869
+
1870
+ if (
1871
+ !(await approveChanges(
1872
+ functions,
1873
+ async (args: any) => {
1874
+ const functionsService = await getFunctionsService();
1875
+ return await functionsService.get({ functionId: args.functionId });
1876
+ },
1877
+ KeysFunction,
1878
+ "functionId",
1879
+ "functions",
1880
+ ["vars"],
1881
+ ))
1882
+ ) {
1883
+ return;
1884
+ }
1885
+
1886
+ log("Pushing functions ...");
1887
+
1888
+ const pushInstance = await createPushInstance();
1889
+ const result = await pushInstance.pushFunctions(functions, {
1890
+ async: asyncDeploy,
1891
+ code,
1892
+ withVariables,
1893
+ });
1894
+
1895
+ const {
1896
+ successfullyPushed,
1897
+ successfullyDeployed,
1898
+ failedDeployments,
1899
+ errors,
1900
+ } = result;
1901
+
1902
+ failedDeployments.forEach((failed) => {
1903
+ const { name, deployment, $id } = failed;
1904
+ const failUrl = `${globalConfig.getEndpoint().slice(0, -3)}/console/project-${localConfig.getProject().projectId}/functions/function-${$id}/deployment-${deployment}`;
1905
+
1906
+ error(
1907
+ `Deployment of ${name} has failed. Check at ${failUrl} for more details\n`,
1908
+ );
1909
+ });
1910
+
1911
+ if (!asyncDeploy) {
1912
+ if (successfullyPushed === 0) {
1913
+ error("No functions were pushed.");
1914
+ } else if (successfullyDeployed !== successfullyPushed) {
1915
+ warn(
1916
+ `Successfully pushed ${successfullyDeployed} of ${successfullyPushed} functions`,
1917
+ );
1918
+ } else {
1919
+ success(`Successfully pushed ${successfullyPushed} functions.`);
2427
1920
  }
1921
+ } else {
1922
+ success(`Successfully pushed ${successfullyPushed} functions.`);
1923
+ }
1924
+
1925
+ if (cliConfig.verbose) {
1926
+ errors.forEach((e) => {
1927
+ console.error(e);
1928
+ });
1929
+ }
1930
+ };
1931
+
1932
+ const pushTable = async ({
1933
+ attempts,
1934
+ }: PushTableOptions = {}): Promise<void> => {
1935
+ const tables: any[] = [];
1936
+
1937
+ const { resyncNeeded } = await checkAndApplyTablesDBChanges();
1938
+ if (resyncNeeded) {
1939
+ log("Resyncing configuration due to tables deletions ...");
1940
+
1941
+ const remoteTablesDBs = (
1942
+ await paginate(
1943
+ async (args: any) => {
1944
+ const tablesService = await getTablesDBService();
1945
+ return await tablesService.list(args.queries || []);
1946
+ },
1947
+ {},
1948
+ 100,
1949
+ "databases",
1950
+ )
1951
+ ).databases;
1952
+ const localTablesDBs = localConfig.getTablesDBs();
2428
1953
 
2429
- log('Pushing teams ...');
1954
+ const remoteDatabaseIds = new Set(remoteTablesDBs.map((db: any) => db.$id));
1955
+ const localTables = localConfig.getTables();
1956
+ const validTables = localTables.filter((table: any) =>
1957
+ remoteDatabaseIds.has(table.databaseId),
1958
+ );
1959
+
1960
+ localConfig.set("tables", validTables);
1961
+
1962
+ const validTablesDBs = localTablesDBs.filter((db: any) =>
1963
+ remoteDatabaseIds.has(db.$id),
1964
+ );
1965
+ localConfig.set("tablesDB", validTablesDBs);
1966
+
1967
+ success("Configuration resynced successfully.");
1968
+ console.log();
1969
+ }
1970
+
1971
+ log("Checking for deleted tables ...");
1972
+ const localTablesDBs = localConfig.getTablesDBs();
1973
+ const localTables = localConfig.getTables();
1974
+ const tablesToDelete: any[] = [];
2430
1975
 
2431
- for (let team of teams) {
2432
- log(`Pushing team ${chalk.bold(team['name'])} ...`);
1976
+ for (const db of localTablesDBs) {
1977
+ try {
1978
+ const { tables: remoteTables } = await paginate(
1979
+ async (args: any) => {
1980
+ const tablesService = await getTablesDBService();
1981
+ return await tablesService.listTables(
1982
+ args.databaseId,
1983
+ args.queries || [],
1984
+ );
1985
+ },
1986
+ {
1987
+ databaseId: db.$id,
1988
+ },
1989
+ 100,
1990
+ "tables",
1991
+ );
1992
+
1993
+ for (const remoteTable of remoteTables) {
1994
+ const localTable = localTables.find(
1995
+ (t: any) => t.$id === remoteTable.$id && t.databaseId === db.$id,
1996
+ );
1997
+ if (!localTable) {
1998
+ tablesToDelete.push({
1999
+ ...remoteTable,
2000
+ databaseId: db.$id,
2001
+ databaseName: db.name,
2002
+ });
2003
+ }
2004
+ }
2005
+ } catch (e) {
2006
+ // Skip if database doesn't exist or other errors
2007
+ }
2008
+ }
2009
+
2010
+ if (tablesToDelete.length > 0) {
2011
+ log("Found tables that exist remotely but not locally:");
2012
+ const deletionChanges = tablesToDelete.map((table: any) => ({
2013
+ id: table.$id,
2014
+ action: chalk.red("deleting"),
2015
+ key: "Table",
2016
+ database: table.databaseName,
2017
+ remote: table.name,
2018
+ local: "(deleted locally)",
2019
+ }));
2020
+ drawTable(deletionChanges);
2433
2021
 
2022
+ if ((await getConfirmation()) === true) {
2023
+ for (const table of tablesToDelete) {
2434
2024
  try {
2435
- response = await teamsGet({
2436
- teamId: team['$id'],
2437
- parseOutput: false,
2438
- })
2439
-
2440
- await teamsUpdateName({
2441
- teamId: team['$id'],
2442
- name: team.name,
2443
- parseOutput: false
2444
- });
2025
+ log(
2026
+ `Deleting table ${table.name} ( ${table.$id} ) from database ${table.databaseName} ...`,
2027
+ );
2028
+ const tablesService = await getTablesDBService();
2029
+ await tablesService.deleteTable(table.databaseId, table.$id);
2030
+ success(`Deleted ${table.name} ( ${table.$id} )`);
2445
2031
  } catch (e: any) {
2446
- if (Number(e.code) === 404) {
2447
- log(`Team ${team.name} does not exist in the project. Creating ... `);
2448
-
2449
- response = await teamsCreate({
2450
- teamId: team['$id'],
2451
- name: team.name,
2452
- parseOutput: false
2453
- })
2454
- } else {
2455
- throw e;
2456
- }
2032
+ error(
2033
+ `Failed to delete table ${table.name} ( ${table.$id} ): ${e.message}`,
2034
+ );
2457
2035
  }
2036
+ }
2458
2037
  }
2038
+ }
2459
2039
 
2460
- success(`Successfully pushed ${teams.length} teams.`);
2461
- }
2040
+ if (cliConfig.all) {
2041
+ checkDeployConditions(localConfig);
2042
+ tables.push(...localConfig.getTables());
2043
+ } else {
2044
+ const answers = await inquirer.prompt(questionsPushTables);
2045
+ if (answers.tables) {
2046
+ const configTables = new Map();
2047
+ localConfig.getTables().forEach((c: any) => {
2048
+ configTables.set(`${c["databaseId"]}|${c["$id"]}`, c);
2049
+ });
2050
+ answers.tables.forEach((a: any) => {
2051
+ const table = configTables.get(a);
2052
+ tables.push(table);
2053
+ });
2054
+ }
2055
+ }
2056
+
2057
+ if (tables.length === 0) {
2058
+ log("No tables found.");
2059
+ hint(
2060
+ `Use '${EXECUTABLE_NAME} pull tables' to synchronize existing one, or use '${EXECUTABLE_NAME} init table' to create a new one.`,
2061
+ );
2062
+ return;
2063
+ }
2064
+
2065
+ if (
2066
+ !(await approveChanges(
2067
+ tables,
2068
+ async (args: any) => {
2069
+ const tablesService = await getTablesDBService();
2070
+ return await tablesService.getTable(args.databaseId, args.tableId);
2071
+ },
2072
+ KeysTable,
2073
+ "tableId",
2074
+ "tables",
2075
+ ["columns", "indexes"],
2076
+ "databaseId",
2077
+ "databaseId",
2078
+ ))
2079
+ ) {
2080
+ return;
2081
+ }
2082
+
2083
+ log("Pushing tables ...");
2084
+
2085
+ const pushInstance = await createPushInstance();
2086
+ const result = await pushInstance.pushTables(tables, { attempts });
2087
+
2088
+ const { successfullyPushed, errors } = result;
2089
+
2090
+ if (successfullyPushed === 0) {
2091
+ error("No tables were pushed.");
2092
+ } else {
2093
+ success(`Successfully pushed ${successfullyPushed} tables.`);
2094
+ }
2095
+
2096
+ if (cliConfig.verbose) {
2097
+ errors.forEach((e) => console.error(e));
2098
+ }
2099
+ };
2462
2100
 
2463
- interface PushMessagingTopicOptions {
2464
- returnOnZero?: boolean;
2465
- }
2101
+ const pushCollection = async (): Promise<void> => {
2102
+ warn(
2103
+ `${EXECUTABLE_NAME} push collection has been deprecated. Please consider using '${EXECUTABLE_NAME} push tables' instead`,
2104
+ );
2105
+ const collections: any[] = [];
2466
2106
 
2467
- const pushMessagingTopic = async ({ returnOnZero }: PushMessagingTopicOptions = { returnOnZero: false }): Promise<void> => {
2468
- let response: any = {};
2107
+ if (cliConfig.all) {
2108
+ checkDeployConditions(localConfig);
2109
+ collections.push(...localConfig.getCollections());
2110
+ } else {
2111
+ const answers = await inquirer.prompt(questionsPushCollections);
2112
+ if (answers.collections) {
2113
+ const configCollections = new Map();
2114
+ localConfig.getCollections().forEach((c: any) => {
2115
+ configCollections.set(`${c["databaseId"]}|${c["$id"]}`, c);
2116
+ });
2117
+ answers.collections.forEach((a: any) => {
2118
+ const collection = configCollections.get(a);
2119
+ collections.push(collection);
2120
+ });
2121
+ }
2122
+ }
2123
+
2124
+ if (collections.length === 0) {
2125
+ log("No collections found.");
2126
+ hint(
2127
+ `Use '${EXECUTABLE_NAME} pull collections' to synchronize existing one, or use '${EXECUTABLE_NAME} init collection' to create a new one.`,
2128
+ );
2129
+ return;
2130
+ }
2131
+
2132
+ // Add database names to collections for the class method
2133
+ collections.forEach((collection: any) => {
2134
+ const localDatabase = localConfig.getDatabase(collection.databaseId);
2135
+ collection.databaseName = localDatabase.name ?? collection.databaseId;
2136
+ });
2137
+
2138
+ if (
2139
+ !(await approveChanges(
2140
+ collections,
2141
+ async (args: any) => {
2142
+ const databasesService = await getDatabasesService();
2143
+ return await databasesService.getCollection(
2144
+ args.databaseId,
2145
+ args.collectionId,
2146
+ );
2147
+ },
2148
+ KeysCollection,
2149
+ "collectionId",
2150
+ "collections",
2151
+ ["attributes", "indexes"],
2152
+ "databaseId",
2153
+ "databaseId",
2154
+ ))
2155
+ ) {
2156
+ return;
2157
+ }
2158
+
2159
+ log("Pushing collections ...");
2160
+
2161
+ const pushInstance = await createPushInstance();
2162
+ const result = await pushInstance.pushCollections(collections);
2163
+
2164
+ const { successfullyPushed, errors } = result;
2165
+
2166
+ if (successfullyPushed === 0) {
2167
+ error("No collections were pushed.");
2168
+ } else {
2169
+ success(`Successfully pushed ${successfullyPushed} collections.`);
2170
+ }
2171
+
2172
+ if (cliConfig.verbose) {
2173
+ errors.forEach((e) => console.error(e));
2174
+ }
2175
+ };
2469
2176
 
2470
- let topicsIds: string[] = [];
2471
- const configTopics = localConfig.getMessagingTopics();
2177
+ const pushBucket = async (): Promise<void> => {
2178
+ let bucketIds: string[] = [];
2179
+ const configBuckets = localConfig.getBuckets();
2472
2180
 
2473
- if (cliConfig.all) {
2474
- checkDeployConditions(localConfig);
2475
- topicsIds.push(...configTopics.map((b: any) => b.$id));
2476
- }
2181
+ if (cliConfig.all) {
2182
+ checkDeployConditions(localConfig);
2183
+ bucketIds.push(...configBuckets.map((b: any) => b.$id));
2184
+ }
2477
2185
 
2478
- if (topicsIds.length === 0) {
2479
- const answers = await inquirer.prompt(questionsPushMessagingTopics[0])
2480
- if (answers.topics) {
2481
- topicsIds.push(...answers.topics);
2482
- }
2186
+ if (bucketIds.length === 0) {
2187
+ const answers = await inquirer.prompt(questionsPushBuckets);
2188
+ if (answers.buckets) {
2189
+ bucketIds.push(...answers.buckets);
2483
2190
  }
2191
+ }
2484
2192
 
2485
- if (topicsIds.length === 0) {
2486
- log("No topics found.");
2487
- hint("Use 'appwrite pull topics' to synchronize existing one, or use 'appwrite init topic' to create a new one.");
2488
- return;
2489
- }
2193
+ if (bucketIds.length === 0) {
2194
+ log("No buckets found.");
2195
+ hint(
2196
+ `Use '${EXECUTABLE_NAME} pull buckets' to synchronize existing one, or use '${EXECUTABLE_NAME} init bucket' to create a new one.`,
2197
+ );
2198
+ return;
2199
+ }
2200
+
2201
+ let buckets: any[] = [];
2202
+
2203
+ for (const bucketId of bucketIds) {
2204
+ const idBuckets = configBuckets.filter((b: any) => b.$id === bucketId);
2205
+ buckets.push(...idBuckets);
2206
+ }
2207
+
2208
+ if (
2209
+ !(await approveChanges(
2210
+ buckets,
2211
+ async (args: any) => {
2212
+ const storageService = await getStorageService();
2213
+ return await storageService.getBucket(args.bucketId);
2214
+ },
2215
+ KeysStorage,
2216
+ "bucketId",
2217
+ "buckets",
2218
+ ))
2219
+ ) {
2220
+ return;
2221
+ }
2222
+
2223
+ log("Pushing buckets ...");
2224
+
2225
+ const pushInstance = await createPushInstance();
2226
+ const result = await pushInstance.pushBuckets(buckets);
2227
+
2228
+ const { successfullyPushed, errors } = result;
2229
+
2230
+ if (successfullyPushed === 0) {
2231
+ error("No buckets were pushed.");
2232
+ } else {
2233
+ success(`Successfully pushed ${successfullyPushed} buckets.`);
2234
+ }
2235
+
2236
+ if (cliConfig.verbose) {
2237
+ errors.forEach((e) => console.error(e));
2238
+ }
2239
+ };
2490
2240
 
2491
- let topics: any[] = [];
2241
+ const pushTeam = async (): Promise<void> => {
2242
+ let teamIds: string[] = [];
2243
+ const configTeams = localConfig.getTeams();
2492
2244
 
2493
- for (const topicId of topicsIds) {
2494
- const idTopic = configTopics.filter((b: any) => b.$id === topicId);
2495
- topics.push(...idTopic);
2496
- }
2245
+ if (cliConfig.all) {
2246
+ checkDeployConditions(localConfig);
2247
+ teamIds.push(...configTeams.map((t: any) => t.$id));
2248
+ }
2497
2249
 
2498
- if (!(await approveChanges(topics, messagingGetTopic, KeysTopics, 'topicId', 'topics'))) {
2499
- return;
2250
+ if (teamIds.length === 0) {
2251
+ const answers = await inquirer.prompt(questionsPushTeams);
2252
+ if (answers.teams) {
2253
+ teamIds.push(...answers.teams);
2500
2254
  }
2255
+ }
2501
2256
 
2502
- log('Pushing topics ...');
2257
+ if (teamIds.length === 0) {
2258
+ log("No teams found.");
2259
+ hint(
2260
+ `Use '${EXECUTABLE_NAME} pull teams' to synchronize existing one, or use '${EXECUTABLE_NAME} init team' to create a new one.`,
2261
+ );
2262
+ return;
2263
+ }
2264
+
2265
+ let teams: any[] = [];
2266
+
2267
+ for (const teamId of teamIds) {
2268
+ const idTeams = configTeams.filter((t: any) => t.$id === teamId);
2269
+ teams.push(...idTeams);
2270
+ }
2271
+
2272
+ if (
2273
+ !(await approveChanges(
2274
+ teams,
2275
+ async (args: any) => {
2276
+ const teamsService = await getTeamsService();
2277
+ return await teamsService.get(args.teamId);
2278
+ },
2279
+ KeysTeams,
2280
+ "teamId",
2281
+ "teams",
2282
+ ))
2283
+ ) {
2284
+ return;
2285
+ }
2286
+
2287
+ log("Pushing teams ...");
2288
+
2289
+ const pushInstance = await createPushInstance();
2290
+ const result = await pushInstance.pushTeams(teams);
2291
+
2292
+ const { successfullyPushed, errors } = result;
2293
+
2294
+ if (successfullyPushed === 0) {
2295
+ error("No teams were pushed.");
2296
+ } else {
2297
+ success(`Successfully pushed ${successfullyPushed} teams.`);
2298
+ }
2299
+
2300
+ if (cliConfig.verbose) {
2301
+ errors.forEach((e) => console.error(e));
2302
+ }
2303
+ };
2503
2304
 
2504
- for (let topic of topics) {
2505
- log(`Pushing topic ${chalk.bold(topic['name'])} ...`);
2305
+ const pushMessagingTopic = async (): Promise<void> => {
2306
+ let topicsIds: string[] = [];
2307
+ const configTopics = localConfig.getMessagingTopics();
2506
2308
 
2507
- try {
2508
- response = await messagingGetTopic({
2509
- topicId: topic['$id'],
2510
- parseOutput: false
2511
- })
2512
- log(`Topic ${topic.name} ( ${topic['$id']} ) already exists.`);
2513
-
2514
- await messagingUpdateTopic({
2515
- topicId: topic['$id'],
2516
- name: topic.name,
2517
- subscribe: topic.subscribe,
2518
- parseOutput: false
2519
- });
2520
- } catch (e: any) {
2521
- if (Number(e.code) === 404) {
2522
- log(`Topic ${topic.name} does not exist in the project. Creating ... `);
2523
-
2524
- response = await messagingCreateTopic({
2525
- topicId: topic['$id'],
2526
- name: topic.name,
2527
- subscribe: topic.subscribe,
2528
- parseOutput: false
2529
- })
2530
-
2531
- success(`Created ${topic.name} ( ${topic['$id']} )`);
2532
- } else {
2533
- throw e;
2534
- }
2535
- }
2309
+ if (cliConfig.all) {
2310
+ checkDeployConditions(localConfig);
2311
+ topicsIds.push(...configTopics.map((b: any) => b.$id));
2312
+ }
2313
+
2314
+ if (topicsIds.length === 0) {
2315
+ const answers = await inquirer.prompt(questionsPushMessagingTopics);
2316
+ if (answers.topics) {
2317
+ topicsIds.push(...answers.topics);
2536
2318
  }
2319
+ }
2537
2320
 
2538
- success(`Successfully pushed ${topics.length} topics.`);
2539
- }
2321
+ if (topicsIds.length === 0) {
2322
+ log("No topics found.");
2323
+ hint(
2324
+ `Use '${EXECUTABLE_NAME} pull topics' to synchronize existing one, or use '${EXECUTABLE_NAME} init topic' to create a new one.`,
2325
+ );
2326
+ return;
2327
+ }
2328
+
2329
+ let topics: any[] = [];
2330
+
2331
+ for (const topicId of topicsIds) {
2332
+ const idTopic = configTopics.filter((b: any) => b.$id === topicId);
2333
+ topics.push(...idTopic);
2334
+ }
2335
+
2336
+ if (
2337
+ !(await approveChanges(
2338
+ topics,
2339
+ async (args: any) => {
2340
+ const messagingService = await getMessagingService();
2341
+ return await messagingService.getTopic(args.topicId);
2342
+ },
2343
+ KeysTopics,
2344
+ "topicId",
2345
+ "topics",
2346
+ ))
2347
+ ) {
2348
+ return;
2349
+ }
2350
+
2351
+ log("Pushing topics ...");
2352
+
2353
+ const pushInstance = await createPushInstance();
2354
+ const result = await pushInstance.pushMessagingTopics(topics);
2355
+
2356
+ const { successfullyPushed, errors } = result;
2357
+
2358
+ if (successfullyPushed === 0) {
2359
+ error("No topics were pushed.");
2360
+ } else {
2361
+ success(`Successfully pushed ${successfullyPushed} topics.`);
2362
+ }
2363
+
2364
+ if (cliConfig.verbose) {
2365
+ errors.forEach((e) => console.error(e));
2366
+ }
2367
+ };
2540
2368
 
2541
2369
  export const push = new Command("push")
2542
- .description(commandDescriptions['push'])
2543
- .action(actionRunner(pushResources));
2370
+ .description(commandDescriptions["push"])
2371
+ .action(actionRunner(() => pushResources({ skipDeprecated: true })));
2544
2372
 
2545
2373
  push
2546
- .command("all")
2547
- .description("Push all resource.")
2548
- .action(actionRunner(() => {
2549
- cliConfig.all = true;
2550
- return pushResources();
2551
- }));
2374
+ .command("all")
2375
+ .description("Push all resource.")
2376
+ .action(
2377
+ actionRunner(() => {
2378
+ cliConfig.all = true;
2379
+ return pushResources({ skipDeprecated: true });
2380
+ }),
2381
+ );
2552
2382
 
2553
2383
  push
2554
- .command("settings")
2555
- .description("Push project name, services and auth settings")
2556
- .action(actionRunner(pushSettings));
2384
+ .command("settings")
2385
+ .description("Push project name, services and auth settings")
2386
+ .action(actionRunner(pushSettings));
2557
2387
 
2558
2388
  push
2559
- .command("function")
2560
- .alias("functions")
2561
- .description("Push functions in the current directory.")
2562
- .option(`-f, --function-id <function-id>`, `ID of function to run`)
2563
- .option(`-A, --async`, `Don't wait for functions deployments status`)
2564
- .option("--no-code", "Don't push the function's code")
2565
- .option("--with-variables", `Push function variables.`)
2566
- .action(actionRunner(pushFunction));
2389
+ .command("function")
2390
+ .alias("functions")
2391
+ .description("Push functions in the current directory.")
2392
+ .option(`-f, --function-id <function-id>`, `ID of function to run`)
2393
+ .option(`-A, --async`, `Don't wait for functions deployments status`)
2394
+ .option("--no-code", "Don't push the function's code")
2395
+ .option("--with-variables", `Push function variables.`)
2396
+ .action(actionRunner(pushFunction));
2567
2397
 
2568
2398
  push
2569
- .command("site")
2570
- .alias("sites")
2571
- .description("Push sites in the current directory.")
2572
- .option(`-f, --site-id <site-id>`, `ID of site to run`)
2573
- .option(`-A, --async`, `Don't wait for sites deployments status`)
2574
- .option("--no-code", "Don't push the site's code")
2575
- .option("--with-variables", `Push site variables.`)
2576
- .action(actionRunner(pushSite));
2399
+ .command("site")
2400
+ .alias("sites")
2401
+ .description("Push sites in the current directory.")
2402
+ .option(`-f, --site-id <site-id>`, `ID of site to run`)
2403
+ .option(`-A, --async`, `Don't wait for sites deployments status`)
2404
+ .option("--no-code", "Don't push the site's code")
2405
+ .option("--with-variables", `Push site variables.`)
2406
+ .action(actionRunner(pushSite));
2577
2407
 
2578
2408
  push
2579
- .command("collection")
2580
- .alias("collections")
2581
- .description("Push collections in the current project. (deprecated, please use 'push tables' instead)")
2582
- .option(`-a, --attempts <numberOfAttempts>`, `Max number of attempts before timing out. default: 30.`)
2583
- .action(actionRunner(pushCollection));
2409
+ .command("collection")
2410
+ .alias("collections")
2411
+ .description(
2412
+ "Push collections in the current project. (deprecated, please use 'push tables' instead)",
2413
+ )
2414
+ .option(
2415
+ `-a, --attempts <numberOfAttempts>`,
2416
+ `Max number of attempts before timing out. default: 30.`,
2417
+ )
2418
+ .action(actionRunner(pushCollection));
2584
2419
 
2585
2420
  push
2586
- .command("table")
2587
- .alias("tables")
2588
- .description("Push tables in the current project.")
2589
- .option(`-a, --attempts <numberOfAttempts>`, `Max number of attempts before timing out. default: 30.`)
2590
- .action(actionRunner(pushTable));
2421
+ .command("table")
2422
+ .alias("tables")
2423
+ .description("Push tables in the current project.")
2424
+ .option(
2425
+ `-a, --attempts <numberOfAttempts>`,
2426
+ `Max number of attempts before timing out. default: 30.`,
2427
+ )
2428
+ .action(actionRunner(pushTable));
2591
2429
 
2592
2430
  push
2593
- .command("bucket")
2594
- .alias("buckets")
2595
- .description("Push buckets in the current project.")
2596
- .action(actionRunner(pushBucket));
2431
+ .command("bucket")
2432
+ .alias("buckets")
2433
+ .description("Push buckets in the current project.")
2434
+ .action(actionRunner(pushBucket));
2597
2435
 
2598
2436
  push
2599
- .command("team")
2600
- .alias("teams")
2601
- .description("Push teams in the current project.")
2602
- .action(actionRunner(pushTeam));
2437
+ .command("team")
2438
+ .alias("teams")
2439
+ .description("Push teams in the current project.")
2440
+ .action(actionRunner(pushTeam));
2603
2441
 
2604
2442
  push
2605
- .command("topic")
2606
- .alias("topics")
2607
- .description("Push messaging topics in the current project.")
2608
- .action(actionRunner(pushMessagingTopic));
2443
+ .command("topic")
2444
+ .alias("topics")
2445
+ .description("Push messaging topics in the current project.")
2446
+ .action(actionRunner(pushMessagingTopic));
2609
2447
 
2610
2448
  export const deploy = new Command("deploy")
2611
- .description('Removed. Use appwrite push instead')
2612
- .action(actionRunner(async () => {
2613
- warn("appwrite deploy has been removed. Please use 'appwrite push' instead");
2614
- }));
2449
+ .description(`Removed. Use ${EXECUTABLE_NAME} push instead`)
2450
+ .action(
2451
+ actionRunner(async () => {
2452
+ warn(
2453
+ `${EXECUTABLE_NAME} deploy has been removed. Please use '${EXECUTABLE_NAME} push' instead`,
2454
+ );
2455
+ }),
2456
+ );