appwrite-cli 13.0.0-rc.2 → 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 (718) hide show
  1. package/.github/workflows/{npm-publish.yml → publish.yml} +1 -1
  2. package/CHANGELOG.md +106 -101
  3. package/LICENSE.md +2 -2
  4. package/README.md +36 -59
  5. package/cli.ts +152 -0
  6. package/dist/bundle.cjs +4613 -3653
  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.map +1 -1
  16. package/dist/lib/client.js +7 -6
  17. package/dist/lib/client.js.map +1 -1
  18. package/dist/lib/commands/config.d.ts +562 -0
  19. package/dist/lib/commands/config.d.ts.map +1 -0
  20. package/dist/lib/commands/config.js +416 -0
  21. package/dist/lib/commands/config.js.map +1 -0
  22. package/dist/lib/commands/db.d.ts +34 -0
  23. package/dist/lib/commands/db.d.ts.map +1 -0
  24. package/dist/lib/commands/db.js +247 -0
  25. package/dist/lib/commands/db.js.map +1 -0
  26. package/dist/lib/commands/errors.d.ts +68 -0
  27. package/dist/lib/commands/errors.d.ts.map +1 -0
  28. package/dist/lib/commands/errors.js +72 -0
  29. package/dist/lib/commands/errors.js.map +1 -0
  30. package/dist/lib/commands/init.d.ts.map +1 -1
  31. package/dist/lib/commands/init.js +15 -14
  32. package/dist/lib/commands/init.js.map +1 -1
  33. package/dist/lib/commands/pull.d.ts +104 -2
  34. package/dist/lib/commands/pull.d.ts.map +1 -1
  35. package/dist/lib/commands/pull.js +470 -281
  36. package/dist/lib/commands/pull.js.map +1 -1
  37. package/dist/lib/commands/push.d.ts +106 -0
  38. package/dist/lib/commands/push.d.ts.map +1 -1
  39. package/dist/lib/commands/push.js +1257 -1628
  40. package/dist/lib/commands/push.js.map +1 -1
  41. package/dist/lib/commands/run.js +1 -1
  42. package/dist/lib/commands/run.js.map +1 -1
  43. package/dist/lib/commands/schema.d.ts +59 -0
  44. package/dist/lib/commands/schema.d.ts.map +1 -0
  45. package/dist/lib/commands/schema.js +86 -0
  46. package/dist/lib/commands/schema.js.map +1 -0
  47. package/dist/lib/commands/services/account.d.ts.map +1 -1
  48. package/dist/lib/commands/services/account.js +86 -64
  49. package/dist/lib/commands/services/account.js.map +1 -1
  50. package/dist/lib/commands/services/console.d.ts.map +1 -1
  51. package/dist/lib/commands/services/console.js +4 -17
  52. package/dist/lib/commands/services/console.js.map +1 -1
  53. package/dist/lib/commands/services/databases.d.ts.map +1 -1
  54. package/dist/lib/commands/services/databases.js +160 -152
  55. package/dist/lib/commands/services/databases.js.map +1 -1
  56. package/dist/lib/commands/services/functions.d.ts.map +1 -1
  57. package/dist/lib/commands/services/functions.js +41 -33
  58. package/dist/lib/commands/services/functions.js.map +1 -1
  59. package/dist/lib/commands/services/graphql.d.ts.map +1 -1
  60. package/dist/lib/commands/services/graphql.js +3 -3
  61. package/dist/lib/commands/services/graphql.js.map +1 -1
  62. package/dist/lib/commands/services/health.d.ts.map +1 -1
  63. package/dist/lib/commands/services/health.js +24 -24
  64. package/dist/lib/commands/services/health.js.map +1 -1
  65. package/dist/lib/commands/services/locale.d.ts.map +1 -1
  66. package/dist/lib/commands/services/locale.js +9 -9
  67. package/dist/lib/commands/services/locale.js.map +1 -1
  68. package/dist/lib/commands/services/messaging.d.ts.map +1 -1
  69. package/dist/lib/commands/services/messaging.js +59 -59
  70. package/dist/lib/commands/services/messaging.js.map +1 -1
  71. package/dist/lib/commands/services/migrations.d.ts.map +1 -1
  72. package/dist/lib/commands/services/migrations.js +19 -19
  73. package/dist/lib/commands/services/migrations.js.map +1 -1
  74. package/dist/lib/commands/services/project.d.ts.map +1 -1
  75. package/dist/lib/commands/services/project.js +8 -8
  76. package/dist/lib/commands/services/project.js.map +1 -1
  77. package/dist/lib/commands/services/projects.d.ts.map +1 -1
  78. package/dist/lib/commands/services/projects.js +64 -64
  79. package/dist/lib/commands/services/projects.js.map +1 -1
  80. package/dist/lib/commands/services/proxy.d.ts.map +1 -1
  81. package/dist/lib/commands/services/proxy.js +11 -11
  82. package/dist/lib/commands/services/proxy.js.map +1 -1
  83. package/dist/lib/commands/services/sites.d.ts.map +1 -1
  84. package/dist/lib/commands/services/sites.js +41 -33
  85. package/dist/lib/commands/services/sites.js.map +1 -1
  86. package/dist/lib/commands/services/storage.d.ts.map +1 -1
  87. package/dist/lib/commands/services/storage.js +38 -16
  88. package/dist/lib/commands/services/storage.js.map +1 -1
  89. package/dist/lib/commands/services/tables-db.d.ts +3 -0
  90. package/dist/lib/commands/services/tables-db.d.ts.map +1 -0
  91. package/dist/lib/commands/services/{tablesdb.js → tables-db.js} +171 -163
  92. package/dist/lib/commands/services/tables-db.js.map +1 -0
  93. package/dist/lib/commands/services/teams.d.ts.map +1 -1
  94. package/dist/lib/commands/services/teams.js +16 -16
  95. package/dist/lib/commands/services/teams.js.map +1 -1
  96. package/dist/lib/commands/services/tokens.d.ts.map +1 -1
  97. package/dist/lib/commands/services/tokens.js +6 -6
  98. package/dist/lib/commands/services/tokens.js.map +1 -1
  99. package/dist/lib/commands/services/users.d.ts.map +1 -1
  100. package/dist/lib/commands/services/users.js +54 -54
  101. package/dist/lib/commands/services/users.js.map +1 -1
  102. package/dist/lib/commands/services/vcs.d.ts.map +1 -1
  103. package/dist/lib/commands/services/vcs.js +13 -13
  104. package/dist/lib/commands/services/vcs.js.map +1 -1
  105. package/dist/lib/commands/types.d.ts.map +1 -1
  106. package/dist/lib/commands/types.js +1 -1
  107. package/dist/lib/commands/types.js.map +1 -1
  108. package/dist/lib/commands/update.d.ts.map +1 -1
  109. package/dist/lib/commands/update.js +9 -8
  110. package/dist/lib/commands/update.js.map +1 -1
  111. package/dist/lib/commands/utils/attributes.d.ts +47 -0
  112. package/dist/lib/commands/utils/attributes.d.ts.map +1 -0
  113. package/dist/lib/commands/utils/attributes.js +514 -0
  114. package/dist/lib/commands/utils/attributes.js.map +1 -0
  115. package/dist/lib/commands/utils/change-approval.d.ts +25 -0
  116. package/dist/lib/commands/utils/change-approval.d.ts.map +1 -0
  117. package/dist/lib/commands/utils/change-approval.js +129 -0
  118. package/dist/lib/commands/utils/change-approval.js.map +1 -0
  119. package/dist/lib/commands/utils/database-sync.d.ts +10 -0
  120. package/dist/lib/commands/utils/database-sync.d.ts.map +1 -0
  121. package/dist/lib/commands/utils/database-sync.js +136 -0
  122. package/dist/lib/commands/utils/database-sync.js.map +1 -0
  123. package/dist/lib/commands/utils/deployment.d.ts +34 -0
  124. package/dist/lib/commands/utils/deployment.d.ts.map +1 -0
  125. package/dist/lib/commands/utils/deployment.js +109 -0
  126. package/dist/lib/commands/utils/deployment.js.map +1 -0
  127. package/dist/lib/commands/utils/error-formatter.d.ts +19 -0
  128. package/dist/lib/commands/utils/error-formatter.d.ts.map +1 -0
  129. package/dist/lib/commands/utils/error-formatter.js +333 -0
  130. package/dist/lib/commands/utils/error-formatter.js.map +1 -0
  131. package/dist/lib/commands/utils/pools.d.ts +16 -0
  132. package/dist/lib/commands/utils/pools.d.ts.map +1 -0
  133. package/dist/lib/commands/utils/pools.js +198 -0
  134. package/dist/lib/commands/utils/pools.js.map +1 -0
  135. package/dist/lib/config.d.ts +27 -26
  136. package/dist/lib/config.d.ts.map +1 -1
  137. package/dist/lib/config.js +8 -45
  138. package/dist/lib/config.js.map +1 -1
  139. package/dist/lib/constants.d.ts +14 -0
  140. package/dist/lib/constants.d.ts.map +1 -0
  141. package/dist/lib/constants.js +19 -0
  142. package/dist/lib/constants.js.map +1 -0
  143. package/dist/lib/emulation/docker.d.ts +4 -4
  144. package/dist/lib/emulation/docker.d.ts.map +1 -1
  145. package/dist/lib/emulation/docker.js +80 -67
  146. package/dist/lib/emulation/docker.js.map +1 -1
  147. package/dist/lib/parser.d.ts.map +1 -1
  148. package/dist/lib/parser.js +11 -10
  149. package/dist/lib/parser.js.map +1 -1
  150. package/dist/lib/questions.d.ts.map +1 -1
  151. package/dist/lib/questions.js +20 -20
  152. package/dist/lib/questions.js.map +1 -1
  153. package/dist/lib/sdks.d.ts.map +1 -1
  154. package/dist/lib/sdks.js +10 -11
  155. package/dist/lib/sdks.js.map +1 -1
  156. package/dist/lib/types.d.ts +0 -214
  157. package/dist/lib/types.d.ts.map +1 -1
  158. package/dist/lib/utils.d.ts +3 -0
  159. package/dist/lib/utils.d.ts.map +1 -1
  160. package/dist/lib/utils.js +42 -2
  161. package/dist/lib/utils.js.map +1 -1
  162. package/dist/package.json +12 -11
  163. package/docs/examples/account/create-anonymous-session.md +1 -0
  164. package/docs/examples/account/create-email-password-session.md +3 -0
  165. package/docs/examples/account/create-email-token.md +3 -0
  166. package/docs/examples/account/create-email-verification.md +2 -0
  167. package/docs/examples/account/create-jwt.md +1 -0
  168. package/docs/examples/account/create-magic-url-token.md +3 -0
  169. package/docs/examples/account/create-mfa-authenticator.md +2 -0
  170. package/docs/examples/account/create-mfa-challenge.md +2 -0
  171. package/docs/examples/account/create-mfa-recovery-codes.md +1 -0
  172. package/docs/examples/account/create-o-auth-2-session.md +2 -0
  173. package/docs/examples/account/create-o-auth-2-token.md +2 -0
  174. package/docs/examples/account/create-phone-token.md +3 -0
  175. package/docs/examples/account/create-phone-verification.md +1 -0
  176. package/docs/examples/account/create-push-target.md +3 -0
  177. package/docs/examples/account/create-recovery.md +3 -0
  178. package/docs/examples/account/create-session.md +3 -0
  179. package/docs/examples/account/create-verification.md +2 -0
  180. package/docs/examples/account/create.md +4 -0
  181. package/docs/examples/account/delete-identity.md +2 -0
  182. package/docs/examples/account/delete-mfa-authenticator.md +2 -0
  183. package/docs/examples/account/delete-push-target.md +2 -0
  184. package/docs/examples/account/delete-session.md +2 -0
  185. package/docs/examples/account/delete-sessions.md +1 -0
  186. package/docs/examples/account/delete.md +1 -0
  187. package/docs/examples/account/get-mfa-recovery-codes.md +1 -0
  188. package/docs/examples/account/get-prefs.md +1 -0
  189. package/docs/examples/account/get-session.md +2 -0
  190. package/docs/examples/account/get.md +1 -0
  191. package/docs/examples/account/list-identities.md +1 -0
  192. package/docs/examples/account/list-logs.md +1 -0
  193. package/docs/examples/account/list-mfa-factors.md +1 -0
  194. package/docs/examples/account/list-sessions.md +1 -0
  195. package/docs/examples/account/update-email-verification.md +3 -0
  196. package/docs/examples/account/update-email.md +3 -0
  197. package/docs/examples/account/update-magic-url-session.md +3 -0
  198. package/docs/examples/account/update-mfa-authenticator.md +3 -0
  199. package/docs/examples/account/update-mfa-challenge.md +3 -0
  200. package/docs/examples/account/update-mfa-recovery-codes.md +1 -0
  201. package/docs/examples/account/update-mfa.md +2 -0
  202. package/docs/examples/account/update-name.md +2 -0
  203. package/docs/examples/account/update-password.md +2 -0
  204. package/docs/examples/account/update-phone-session.md +3 -0
  205. package/docs/examples/account/update-phone-verification.md +3 -0
  206. package/docs/examples/account/update-phone.md +3 -0
  207. package/docs/examples/account/update-prefs.md +2 -0
  208. package/docs/examples/account/update-push-target.md +3 -0
  209. package/docs/examples/account/update-recovery.md +4 -0
  210. package/docs/examples/account/update-session.md +2 -0
  211. package/docs/examples/account/update-status.md +1 -0
  212. package/docs/examples/account/update-verification.md +3 -0
  213. package/docs/examples/console/get-resource.md +3 -0
  214. package/docs/examples/console/variables.md +1 -0
  215. package/docs/examples/databases/create-boolean-attribute.md +5 -0
  216. package/docs/examples/databases/create-collection.md +4 -0
  217. package/docs/examples/databases/create-datetime-attribute.md +5 -0
  218. package/docs/examples/databases/create-document.md +5 -0
  219. package/docs/examples/databases/create-documents.md +4 -0
  220. package/docs/examples/databases/create-email-attribute.md +5 -0
  221. package/docs/examples/databases/create-enum-attribute.md +6 -0
  222. package/docs/examples/databases/create-float-attribute.md +5 -0
  223. package/docs/examples/databases/create-index.md +6 -0
  224. package/docs/examples/databases/create-integer-attribute.md +5 -0
  225. package/docs/examples/databases/create-ip-attribute.md +5 -0
  226. package/docs/examples/databases/create-line-attribute.md +5 -0
  227. package/docs/examples/databases/create-operations.md +2 -0
  228. package/docs/examples/databases/create-point-attribute.md +5 -0
  229. package/docs/examples/databases/create-polygon-attribute.md +5 -0
  230. package/docs/examples/databases/create-relationship-attribute.md +5 -0
  231. package/docs/examples/databases/create-string-attribute.md +6 -0
  232. package/docs/examples/databases/create-transaction.md +1 -0
  233. package/docs/examples/databases/create-url-attribute.md +5 -0
  234. package/docs/examples/databases/create.md +3 -0
  235. package/docs/examples/databases/decrement-document-attribute.md +5 -0
  236. package/docs/examples/databases/delete-attribute.md +4 -0
  237. package/docs/examples/databases/delete-collection.md +3 -0
  238. package/docs/examples/databases/delete-document.md +4 -0
  239. package/docs/examples/databases/delete-documents.md +3 -0
  240. package/docs/examples/databases/delete-index.md +4 -0
  241. package/docs/examples/databases/delete-transaction.md +2 -0
  242. package/docs/examples/databases/delete.md +2 -0
  243. package/docs/examples/databases/get-attribute.md +4 -0
  244. package/docs/examples/databases/get-collection-usage.md +3 -0
  245. package/docs/examples/databases/get-collection.md +3 -0
  246. package/docs/examples/databases/get-document.md +4 -0
  247. package/docs/examples/databases/get-index.md +4 -0
  248. package/docs/examples/databases/get-transaction.md +2 -0
  249. package/docs/examples/databases/get-usage.md +2 -0
  250. package/docs/examples/databases/get.md +2 -0
  251. package/docs/examples/databases/increment-document-attribute.md +5 -0
  252. package/docs/examples/databases/list-attributes.md +3 -0
  253. package/docs/examples/databases/list-collection-logs.md +3 -0
  254. package/docs/examples/databases/list-collections.md +2 -0
  255. package/docs/examples/databases/list-document-logs.md +4 -0
  256. package/docs/examples/databases/list-documents.md +3 -0
  257. package/docs/examples/databases/list-indexes.md +3 -0
  258. package/docs/examples/databases/list-logs.md +2 -0
  259. package/docs/examples/databases/list-transactions.md +1 -0
  260. package/docs/examples/databases/list-usage.md +1 -0
  261. package/docs/examples/databases/list.md +1 -0
  262. package/docs/examples/databases/update-boolean-attribute.md +6 -0
  263. package/docs/examples/databases/update-collection.md +4 -0
  264. package/docs/examples/databases/update-datetime-attribute.md +6 -0
  265. package/docs/examples/databases/update-document.md +4 -0
  266. package/docs/examples/databases/update-documents.md +3 -0
  267. package/docs/examples/databases/update-email-attribute.md +6 -0
  268. package/docs/examples/databases/update-enum-attribute.md +7 -0
  269. package/docs/examples/databases/update-float-attribute.md +6 -0
  270. package/docs/examples/databases/update-integer-attribute.md +6 -0
  271. package/docs/examples/databases/update-ip-attribute.md +6 -0
  272. package/docs/examples/databases/update-line-attribute.md +5 -0
  273. package/docs/examples/databases/update-point-attribute.md +5 -0
  274. package/docs/examples/databases/update-polygon-attribute.md +5 -0
  275. package/docs/examples/databases/update-relationship-attribute.md +4 -0
  276. package/docs/examples/databases/update-string-attribute.md +6 -0
  277. package/docs/examples/databases/update-transaction.md +2 -0
  278. package/docs/examples/databases/update-url-attribute.md +6 -0
  279. package/docs/examples/databases/update.md +3 -0
  280. package/docs/examples/databases/upsert-document.md +4 -0
  281. package/docs/examples/databases/upsert-documents.md +4 -0
  282. package/docs/examples/functions/create-deployment.md +4 -0
  283. package/docs/examples/functions/create-duplicate-deployment.md +3 -0
  284. package/docs/examples/functions/create-execution.md +2 -0
  285. package/docs/examples/functions/create-template-deployment.md +7 -0
  286. package/docs/examples/functions/create-variable.md +4 -0
  287. package/docs/examples/functions/create-vcs-deployment.md +4 -0
  288. package/docs/examples/functions/create.md +4 -0
  289. package/docs/examples/functions/delete-deployment.md +3 -0
  290. package/docs/examples/functions/delete-execution.md +3 -0
  291. package/docs/examples/functions/delete-variable.md +3 -0
  292. package/docs/examples/functions/delete.md +2 -0
  293. package/docs/examples/functions/get-deployment-download.md +3 -0
  294. package/docs/examples/functions/get-deployment.md +3 -0
  295. package/docs/examples/functions/get-execution.md +3 -0
  296. package/docs/examples/functions/get-template.md +2 -0
  297. package/docs/examples/functions/get-usage.md +2 -0
  298. package/docs/examples/functions/get-variable.md +3 -0
  299. package/docs/examples/functions/get.md +2 -0
  300. package/docs/examples/functions/list-deployments.md +2 -0
  301. package/docs/examples/functions/list-executions.md +2 -0
  302. package/docs/examples/functions/list-runtimes.md +1 -0
  303. package/docs/examples/functions/list-specifications.md +1 -0
  304. package/docs/examples/functions/list-templates.md +1 -0
  305. package/docs/examples/functions/list-usage.md +1 -0
  306. package/docs/examples/functions/list-variables.md +2 -0
  307. package/docs/examples/functions/list.md +1 -0
  308. package/docs/examples/functions/update-deployment-status.md +3 -0
  309. package/docs/examples/functions/update-function-deployment.md +3 -0
  310. package/docs/examples/functions/update-variable.md +4 -0
  311. package/docs/examples/functions/update.md +3 -0
  312. package/docs/examples/graphql/mutation.md +2 -0
  313. package/docs/examples/graphql/query.md +2 -0
  314. package/docs/examples/health/get-antivirus.md +1 -0
  315. package/docs/examples/health/get-cache.md +1 -0
  316. package/docs/examples/health/get-certificate.md +1 -0
  317. package/docs/examples/health/get-db.md +1 -0
  318. package/docs/examples/health/get-failed-jobs.md +2 -0
  319. package/docs/examples/health/get-pub-sub.md +1 -0
  320. package/docs/examples/health/get-queue-builds.md +1 -0
  321. package/docs/examples/health/get-queue-certificates.md +1 -0
  322. package/docs/examples/health/get-queue-databases.md +1 -0
  323. package/docs/examples/health/get-queue-deletes.md +1 -0
  324. package/docs/examples/health/get-queue-functions.md +1 -0
  325. package/docs/examples/health/get-queue-logs.md +1 -0
  326. package/docs/examples/health/get-queue-mails.md +1 -0
  327. package/docs/examples/health/get-queue-messaging.md +1 -0
  328. package/docs/examples/health/get-queue-migrations.md +1 -0
  329. package/docs/examples/health/get-queue-stats-resources.md +1 -0
  330. package/docs/examples/health/get-queue-usage.md +1 -0
  331. package/docs/examples/health/get-queue-webhooks.md +1 -0
  332. package/docs/examples/health/get-storage-local.md +1 -0
  333. package/docs/examples/health/get-storage.md +1 -0
  334. package/docs/examples/health/get-time.md +1 -0
  335. package/docs/examples/health/get.md +1 -0
  336. package/docs/examples/locale/get.md +1 -0
  337. package/docs/examples/locale/list-codes.md +1 -0
  338. package/docs/examples/locale/list-continents.md +1 -0
  339. package/docs/examples/locale/list-countries-eu.md +1 -0
  340. package/docs/examples/locale/list-countries-phones.md +1 -0
  341. package/docs/examples/locale/list-countries.md +1 -0
  342. package/docs/examples/locale/list-currencies.md +1 -0
  343. package/docs/examples/locale/list-languages.md +1 -0
  344. package/docs/examples/messaging/create-apns-provider.md +3 -0
  345. package/docs/examples/messaging/create-email.md +4 -0
  346. package/docs/examples/messaging/create-fcm-provider.md +3 -0
  347. package/docs/examples/messaging/create-mailgun-provider.md +3 -0
  348. package/docs/examples/messaging/create-msg-91-provider.md +3 -0
  349. package/docs/examples/messaging/create-push.md +2 -0
  350. package/docs/examples/messaging/create-resend-provider.md +3 -0
  351. package/docs/examples/messaging/create-sendgrid-provider.md +3 -0
  352. package/docs/examples/messaging/create-sms.md +3 -0
  353. package/docs/examples/messaging/create-smtp-provider.md +4 -0
  354. package/docs/examples/messaging/create-subscriber.md +4 -0
  355. package/docs/examples/messaging/create-telesign-provider.md +3 -0
  356. package/docs/examples/messaging/create-textmagic-provider.md +3 -0
  357. package/docs/examples/messaging/create-topic.md +3 -0
  358. package/docs/examples/messaging/create-twilio-provider.md +3 -0
  359. package/docs/examples/messaging/create-vonage-provider.md +3 -0
  360. package/docs/examples/messaging/delete-provider.md +2 -0
  361. package/docs/examples/messaging/delete-subscriber.md +3 -0
  362. package/docs/examples/messaging/delete-topic.md +2 -0
  363. package/docs/examples/messaging/delete.md +2 -0
  364. package/docs/examples/messaging/get-message.md +2 -0
  365. package/docs/examples/messaging/get-provider.md +2 -0
  366. package/docs/examples/messaging/get-subscriber.md +3 -0
  367. package/docs/examples/messaging/get-topic.md +2 -0
  368. package/docs/examples/messaging/list-message-logs.md +2 -0
  369. package/docs/examples/messaging/list-messages.md +1 -0
  370. package/docs/examples/messaging/list-provider-logs.md +2 -0
  371. package/docs/examples/messaging/list-providers.md +1 -0
  372. package/docs/examples/messaging/list-subscriber-logs.md +2 -0
  373. package/docs/examples/messaging/list-subscribers.md +2 -0
  374. package/docs/examples/messaging/list-targets.md +2 -0
  375. package/docs/examples/messaging/list-topic-logs.md +2 -0
  376. package/docs/examples/messaging/list-topics.md +1 -0
  377. package/docs/examples/messaging/update-apns-provider.md +2 -0
  378. package/docs/examples/messaging/update-email.md +2 -0
  379. package/docs/examples/messaging/update-fcm-provider.md +2 -0
  380. package/docs/examples/messaging/update-mailgun-provider.md +2 -0
  381. package/docs/examples/messaging/update-msg-91-provider.md +2 -0
  382. package/docs/examples/messaging/update-push.md +2 -0
  383. package/docs/examples/messaging/update-resend-provider.md +2 -0
  384. package/docs/examples/messaging/update-sendgrid-provider.md +2 -0
  385. package/docs/examples/messaging/update-sms.md +2 -0
  386. package/docs/examples/messaging/update-smtp-provider.md +2 -0
  387. package/docs/examples/messaging/update-telesign-provider.md +2 -0
  388. package/docs/examples/messaging/update-textmagic-provider.md +2 -0
  389. package/docs/examples/messaging/update-topic.md +2 -0
  390. package/docs/examples/messaging/update-twilio-provider.md +2 -0
  391. package/docs/examples/messaging/update-vonage-provider.md +2 -0
  392. package/docs/examples/migrations/create-appwrite-migration.md +5 -0
  393. package/docs/examples/migrations/create-csv-export.md +3 -0
  394. package/docs/examples/migrations/create-csv-import.md +4 -0
  395. package/docs/examples/migrations/create-firebase-migration.md +3 -0
  396. package/docs/examples/migrations/create-n-host-migration.md +8 -0
  397. package/docs/examples/migrations/create-supabase-migration.md +7 -0
  398. package/docs/examples/migrations/delete.md +2 -0
  399. package/docs/examples/migrations/get-appwrite-report.md +5 -0
  400. package/docs/examples/migrations/get-firebase-report.md +3 -0
  401. package/docs/examples/migrations/get-n-host-report.md +8 -0
  402. package/docs/examples/migrations/get-supabase-report.md +7 -0
  403. package/docs/examples/migrations/get.md +2 -0
  404. package/docs/examples/migrations/list.md +1 -0
  405. package/docs/examples/migrations/retry.md +2 -0
  406. package/docs/examples/project/create-variable.md +3 -0
  407. package/docs/examples/project/delete-variable.md +2 -0
  408. package/docs/examples/project/get-usage.md +3 -0
  409. package/docs/examples/project/get-variable.md +2 -0
  410. package/docs/examples/project/list-variables.md +1 -0
  411. package/docs/examples/project/update-variable.md +3 -0
  412. package/docs/examples/projects/create-dev-key.md +4 -0
  413. package/docs/examples/projects/create-jwt.md +3 -0
  414. package/docs/examples/projects/create-key.md +4 -0
  415. package/docs/examples/projects/create-platform.md +4 -0
  416. package/docs/examples/projects/create-smtp-test.md +6 -0
  417. package/docs/examples/projects/create-webhook.md +6 -0
  418. package/docs/examples/projects/create.md +4 -0
  419. package/docs/examples/projects/delete-dev-key.md +3 -0
  420. package/docs/examples/projects/delete-email-template.md +4 -0
  421. package/docs/examples/projects/delete-key.md +3 -0
  422. package/docs/examples/projects/delete-platform.md +3 -0
  423. package/docs/examples/projects/delete-sms-template.md +4 -0
  424. package/docs/examples/projects/delete-webhook.md +3 -0
  425. package/docs/examples/projects/delete.md +2 -0
  426. package/docs/examples/projects/get-dev-key.md +3 -0
  427. package/docs/examples/projects/get-email-template.md +4 -0
  428. package/docs/examples/projects/get-key.md +3 -0
  429. package/docs/examples/projects/get-platform.md +3 -0
  430. package/docs/examples/projects/get-sms-template.md +4 -0
  431. package/docs/examples/projects/get-webhook.md +3 -0
  432. package/docs/examples/projects/get.md +2 -0
  433. package/docs/examples/projects/list-dev-keys.md +2 -0
  434. package/docs/examples/projects/list-keys.md +2 -0
  435. package/docs/examples/projects/list-platforms.md +2 -0
  436. package/docs/examples/projects/list-webhooks.md +2 -0
  437. package/docs/examples/projects/list.md +1 -0
  438. package/docs/examples/projects/update-api-status-all.md +3 -0
  439. package/docs/examples/projects/update-api-status.md +4 -0
  440. package/docs/examples/projects/update-auth-duration.md +3 -0
  441. package/docs/examples/projects/update-auth-limit.md +3 -0
  442. package/docs/examples/projects/update-auth-password-dictionary.md +3 -0
  443. package/docs/examples/projects/update-auth-password-history.md +3 -0
  444. package/docs/examples/projects/update-auth-sessions-limit.md +3 -0
  445. package/docs/examples/projects/update-auth-status.md +4 -0
  446. package/docs/examples/projects/update-dev-key.md +5 -0
  447. package/docs/examples/projects/update-email-template.md +6 -0
  448. package/docs/examples/projects/update-key.md +5 -0
  449. package/docs/examples/projects/update-memberships-privacy.md +5 -0
  450. package/docs/examples/projects/update-mock-numbers.md +3 -0
  451. package/docs/examples/projects/update-o-auth-2.md +3 -0
  452. package/docs/examples/projects/update-personal-data-check.md +3 -0
  453. package/docs/examples/projects/update-platform.md +4 -0
  454. package/docs/examples/projects/update-service-status-all.md +3 -0
  455. package/docs/examples/projects/update-service-status.md +4 -0
  456. package/docs/examples/projects/update-session-alerts.md +3 -0
  457. package/docs/examples/projects/update-session-invalidation.md +3 -0
  458. package/docs/examples/projects/update-sms-template.md +5 -0
  459. package/docs/examples/projects/update-smtp.md +3 -0
  460. package/docs/examples/projects/update-team.md +3 -0
  461. package/docs/examples/projects/update-webhook-signature.md +3 -0
  462. package/docs/examples/projects/update-webhook.md +7 -0
  463. package/docs/examples/projects/update.md +3 -0
  464. package/docs/examples/proxy/create-api-rule.md +2 -0
  465. package/docs/examples/proxy/create-function-rule.md +3 -0
  466. package/docs/examples/proxy/create-redirect-rule.md +6 -0
  467. package/docs/examples/proxy/create-site-rule.md +3 -0
  468. package/docs/examples/proxy/delete-rule.md +2 -0
  469. package/docs/examples/proxy/get-rule.md +2 -0
  470. package/docs/examples/proxy/list-rules.md +1 -0
  471. package/docs/examples/proxy/update-rule-verification.md +2 -0
  472. package/docs/examples/sites/create-deployment.md +4 -0
  473. package/docs/examples/sites/create-duplicate-deployment.md +3 -0
  474. package/docs/examples/sites/create-template-deployment.md +7 -0
  475. package/docs/examples/sites/create-variable.md +4 -0
  476. package/docs/examples/sites/create-vcs-deployment.md +4 -0
  477. package/docs/examples/sites/create.md +5 -0
  478. package/docs/examples/sites/delete-deployment.md +3 -0
  479. package/docs/examples/sites/delete-log.md +3 -0
  480. package/docs/examples/sites/delete-variable.md +3 -0
  481. package/docs/examples/sites/delete.md +2 -0
  482. package/docs/examples/sites/get-deployment-download.md +3 -0
  483. package/docs/examples/sites/get-deployment.md +3 -0
  484. package/docs/examples/sites/get-log.md +3 -0
  485. package/docs/examples/sites/get-template.md +2 -0
  486. package/docs/examples/sites/get-usage.md +2 -0
  487. package/docs/examples/sites/get-variable.md +3 -0
  488. package/docs/examples/sites/get.md +2 -0
  489. package/docs/examples/sites/list-deployments.md +2 -0
  490. package/docs/examples/sites/list-frameworks.md +1 -0
  491. package/docs/examples/sites/list-logs.md +2 -0
  492. package/docs/examples/sites/list-specifications.md +1 -0
  493. package/docs/examples/sites/list-templates.md +1 -0
  494. package/docs/examples/sites/list-usage.md +1 -0
  495. package/docs/examples/sites/list-variables.md +2 -0
  496. package/docs/examples/sites/list.md +1 -0
  497. package/docs/examples/sites/update-deployment-status.md +3 -0
  498. package/docs/examples/sites/update-site-deployment.md +3 -0
  499. package/docs/examples/sites/update-variable.md +4 -0
  500. package/docs/examples/sites/update.md +4 -0
  501. package/docs/examples/storage/create-bucket.md +3 -0
  502. package/docs/examples/storage/create-file.md +4 -0
  503. package/docs/examples/storage/delete-bucket.md +2 -0
  504. package/docs/examples/storage/delete-file.md +3 -0
  505. package/docs/examples/storage/get-bucket-usage.md +2 -0
  506. package/docs/examples/storage/get-bucket.md +2 -0
  507. package/docs/examples/storage/get-file-download.md +3 -0
  508. package/docs/examples/storage/get-file-preview.md +3 -0
  509. package/docs/examples/storage/get-file-view.md +3 -0
  510. package/docs/examples/storage/get-file.md +3 -0
  511. package/docs/examples/storage/get-usage.md +1 -0
  512. package/docs/examples/storage/list-buckets.md +1 -0
  513. package/docs/examples/storage/list-files.md +2 -0
  514. package/docs/examples/storage/update-bucket.md +3 -0
  515. package/docs/examples/storage/update-file.md +3 -0
  516. package/docs/examples/tablesdb/create-boolean-column.md +5 -0
  517. package/docs/examples/tablesdb/create-datetime-column.md +5 -0
  518. package/docs/examples/tablesdb/create-email-column.md +5 -0
  519. package/docs/examples/tablesdb/create-enum-column.md +6 -0
  520. package/docs/examples/tablesdb/create-float-column.md +5 -0
  521. package/docs/examples/tablesdb/create-index.md +6 -0
  522. package/docs/examples/tablesdb/create-integer-column.md +5 -0
  523. package/docs/examples/tablesdb/create-ip-column.md +5 -0
  524. package/docs/examples/tablesdb/create-line-column.md +5 -0
  525. package/docs/examples/tablesdb/create-operations.md +2 -0
  526. package/docs/examples/tablesdb/create-point-column.md +5 -0
  527. package/docs/examples/tablesdb/create-polygon-column.md +5 -0
  528. package/docs/examples/tablesdb/create-relationship-column.md +5 -0
  529. package/docs/examples/tablesdb/create-row.md +5 -0
  530. package/docs/examples/tablesdb/create-rows.md +4 -0
  531. package/docs/examples/tablesdb/create-string-column.md +6 -0
  532. package/docs/examples/tablesdb/create-table.md +4 -0
  533. package/docs/examples/tablesdb/create-transaction.md +1 -0
  534. package/docs/examples/tablesdb/create-url-column.md +5 -0
  535. package/docs/examples/tablesdb/create.md +3 -0
  536. package/docs/examples/tablesdb/decrement-row-column.md +5 -0
  537. package/docs/examples/tablesdb/delete-column.md +4 -0
  538. package/docs/examples/tablesdb/delete-index.md +4 -0
  539. package/docs/examples/tablesdb/delete-row.md +4 -0
  540. package/docs/examples/tablesdb/delete-rows.md +3 -0
  541. package/docs/examples/tablesdb/delete-table.md +3 -0
  542. package/docs/examples/tablesdb/delete-transaction.md +2 -0
  543. package/docs/examples/tablesdb/delete.md +2 -0
  544. package/docs/examples/tablesdb/get-column.md +4 -0
  545. package/docs/examples/tablesdb/get-index.md +4 -0
  546. package/docs/examples/tablesdb/get-row.md +4 -0
  547. package/docs/examples/tablesdb/get-table-usage.md +3 -0
  548. package/docs/examples/tablesdb/get-table.md +3 -0
  549. package/docs/examples/tablesdb/get-transaction.md +2 -0
  550. package/docs/examples/tablesdb/get-usage.md +2 -0
  551. package/docs/examples/tablesdb/get.md +2 -0
  552. package/docs/examples/tablesdb/increment-row-column.md +5 -0
  553. package/docs/examples/tablesdb/list-columns.md +3 -0
  554. package/docs/examples/tablesdb/list-indexes.md +3 -0
  555. package/docs/examples/tablesdb/list-row-logs.md +4 -0
  556. package/docs/examples/tablesdb/list-rows.md +3 -0
  557. package/docs/examples/tablesdb/list-table-logs.md +3 -0
  558. package/docs/examples/tablesdb/list-tables.md +2 -0
  559. package/docs/examples/tablesdb/list-transactions.md +1 -0
  560. package/docs/examples/tablesdb/list-usage.md +1 -0
  561. package/docs/examples/tablesdb/list.md +1 -0
  562. package/docs/examples/tablesdb/update-boolean-column.md +6 -0
  563. package/docs/examples/tablesdb/update-datetime-column.md +6 -0
  564. package/docs/examples/tablesdb/update-email-column.md +6 -0
  565. package/docs/examples/tablesdb/update-enum-column.md +7 -0
  566. package/docs/examples/tablesdb/update-float-column.md +6 -0
  567. package/docs/examples/tablesdb/update-integer-column.md +6 -0
  568. package/docs/examples/tablesdb/update-ip-column.md +6 -0
  569. package/docs/examples/tablesdb/update-line-column.md +5 -0
  570. package/docs/examples/tablesdb/update-point-column.md +5 -0
  571. package/docs/examples/tablesdb/update-polygon-column.md +5 -0
  572. package/docs/examples/tablesdb/update-relationship-column.md +4 -0
  573. package/docs/examples/tablesdb/update-row.md +4 -0
  574. package/docs/examples/tablesdb/update-rows.md +3 -0
  575. package/docs/examples/tablesdb/update-string-column.md +6 -0
  576. package/docs/examples/tablesdb/update-table.md +4 -0
  577. package/docs/examples/tablesdb/update-transaction.md +2 -0
  578. package/docs/examples/tablesdb/update-url-column.md +6 -0
  579. package/docs/examples/tablesdb/update.md +3 -0
  580. package/docs/examples/tablesdb/upsert-row.md +4 -0
  581. package/docs/examples/tablesdb/upsert-rows.md +4 -0
  582. package/docs/examples/teams/create-membership.md +3 -0
  583. package/docs/examples/teams/create.md +3 -0
  584. package/docs/examples/teams/delete-membership.md +3 -0
  585. package/docs/examples/teams/delete.md +2 -0
  586. package/docs/examples/teams/get-membership.md +3 -0
  587. package/docs/examples/teams/get-prefs.md +2 -0
  588. package/docs/examples/teams/get.md +2 -0
  589. package/docs/examples/teams/list-logs.md +2 -0
  590. package/docs/examples/teams/list-memberships.md +2 -0
  591. package/docs/examples/teams/list.md +1 -0
  592. package/docs/examples/teams/update-membership-status.md +5 -0
  593. package/docs/examples/teams/update-membership.md +4 -0
  594. package/docs/examples/teams/update-name.md +3 -0
  595. package/docs/examples/teams/update-prefs.md +3 -0
  596. package/docs/examples/tokens/create-file-token.md +3 -0
  597. package/docs/examples/tokens/delete.md +2 -0
  598. package/docs/examples/tokens/get.md +2 -0
  599. package/docs/examples/tokens/list.md +3 -0
  600. package/docs/examples/tokens/update.md +2 -0
  601. package/docs/examples/users/create-argon-2-user.md +4 -0
  602. package/docs/examples/users/create-bcrypt-user.md +4 -0
  603. package/docs/examples/users/create-jwt.md +2 -0
  604. package/docs/examples/users/create-md-5-user.md +4 -0
  605. package/docs/examples/users/create-mfa-recovery-codes.md +2 -0
  606. package/docs/examples/users/create-ph-pass-user.md +4 -0
  607. package/docs/examples/users/create-scrypt-modified-user.md +7 -0
  608. package/docs/examples/users/create-scrypt-user.md +9 -0
  609. package/docs/examples/users/create-session.md +2 -0
  610. package/docs/examples/users/create-sha-user.md +4 -0
  611. package/docs/examples/users/create-target.md +5 -0
  612. package/docs/examples/users/create-token.md +2 -0
  613. package/docs/examples/users/create.md +2 -0
  614. package/docs/examples/users/delete-identity.md +2 -0
  615. package/docs/examples/users/delete-mfa-authenticator.md +3 -0
  616. package/docs/examples/users/delete-session.md +3 -0
  617. package/docs/examples/users/delete-sessions.md +2 -0
  618. package/docs/examples/users/delete-target.md +3 -0
  619. package/docs/examples/users/delete.md +2 -0
  620. package/docs/examples/users/get-mfa-recovery-codes.md +2 -0
  621. package/docs/examples/users/get-prefs.md +2 -0
  622. package/docs/examples/users/get-target.md +3 -0
  623. package/docs/examples/users/get-usage.md +1 -0
  624. package/docs/examples/users/get.md +2 -0
  625. package/docs/examples/users/list-identities.md +1 -0
  626. package/docs/examples/users/list-logs.md +2 -0
  627. package/docs/examples/users/list-memberships.md +2 -0
  628. package/docs/examples/users/list-mfa-factors.md +2 -0
  629. package/docs/examples/users/list-sessions.md +2 -0
  630. package/docs/examples/users/list-targets.md +2 -0
  631. package/docs/examples/users/list.md +1 -0
  632. package/docs/examples/users/update-email-verification.md +3 -0
  633. package/docs/examples/users/update-email.md +3 -0
  634. package/docs/examples/users/update-labels.md +3 -0
  635. package/docs/examples/users/update-mfa-recovery-codes.md +2 -0
  636. package/docs/examples/users/update-mfa.md +3 -0
  637. package/docs/examples/users/update-name.md +3 -0
  638. package/docs/examples/users/update-password.md +3 -0
  639. package/docs/examples/users/update-phone-verification.md +3 -0
  640. package/docs/examples/users/update-phone.md +3 -0
  641. package/docs/examples/users/update-prefs.md +3 -0
  642. package/docs/examples/users/update-status.md +3 -0
  643. package/docs/examples/users/update-target.md +3 -0
  644. package/docs/examples/vcs/create-repository-detection.md +4 -0
  645. package/docs/examples/vcs/create-repository.md +4 -0
  646. package/docs/examples/vcs/delete-installation.md +2 -0
  647. package/docs/examples/vcs/get-installation.md +2 -0
  648. package/docs/examples/vcs/get-repository-contents.md +3 -0
  649. package/docs/examples/vcs/get-repository.md +3 -0
  650. package/docs/examples/vcs/list-installations.md +1 -0
  651. package/docs/examples/vcs/list-repositories.md +3 -0
  652. package/docs/examples/vcs/list-repository-branches.md +3 -0
  653. package/docs/examples/vcs/update-external-deployments.md +4 -0
  654. package/index.ts +25 -165
  655. package/install.ps1 +2 -2
  656. package/install.sh +1 -1
  657. package/lib/client.ts +14 -6
  658. package/lib/commands/config.ts +494 -0
  659. package/lib/commands/db.ts +324 -0
  660. package/lib/commands/errors.ts +93 -0
  661. package/lib/commands/init.ts +25 -14
  662. package/lib/commands/pull.ts +683 -420
  663. package/lib/commands/push.ts +1677 -2404
  664. package/lib/commands/run.ts +1 -1
  665. package/lib/commands/schema.ts +122 -0
  666. package/lib/commands/services/account.ts +200 -385
  667. package/lib/commands/services/console.ts +13 -34
  668. package/lib/commands/services/databases.ts +298 -1215
  669. package/lib/commands/services/functions.ts +115 -434
  670. package/lib/commands/services/graphql.ts +5 -2
  671. package/lib/commands/services/health.ts +87 -149
  672. package/lib/commands/services/locale.ts +36 -33
  673. package/lib/commands/services/messaging.ts +180 -999
  674. package/lib/commands/services/migrations.ts +54 -226
  675. package/lib/commands/services/project.ts +19 -23
  676. package/lib/commands/services/projects.ts +182 -714
  677. package/lib/commands/services/proxy.ts +30 -58
  678. package/lib/commands/services/sites.ts +107 -379
  679. package/lib/commands/services/storage.ts +93 -302
  680. package/lib/commands/services/tables-db.ts +1150 -0
  681. package/lib/commands/services/teams.ts +48 -110
  682. package/lib/commands/services/tokens.ts +16 -28
  683. package/lib/commands/services/users.ts +133 -403
  684. package/lib/commands/services/vcs.ts +48 -151
  685. package/lib/commands/types.ts +1 -1
  686. package/lib/commands/update.ts +13 -22
  687. package/lib/commands/utils/attributes.ts +719 -0
  688. package/lib/commands/utils/change-approval.ts +186 -0
  689. package/lib/commands/utils/database-sync.ts +180 -0
  690. package/lib/commands/utils/deployment.ts +184 -0
  691. package/lib/commands/utils/error-formatter.ts +417 -0
  692. package/lib/commands/utils/pools.ts +355 -0
  693. package/lib/config.ts +46 -82
  694. package/lib/constants.ts +22 -0
  695. package/lib/emulation/docker.ts +101 -87
  696. package/lib/parser.ts +16 -11
  697. package/lib/questions.ts +20 -21
  698. package/lib/sdks.ts +15 -11
  699. package/lib/types.ts +0 -229
  700. package/lib/utils.ts +45 -4
  701. package/package.json +12 -11
  702. package/scoop/appwrite.config.json +29 -19
  703. package/.github/workflows/build-verify.yml +0 -71
  704. package/bun.lock +0 -625
  705. package/dist/lib/commands/services/avatars.d.ts +0 -3
  706. package/dist/lib/commands/services/avatars.d.ts.map +0 -1
  707. package/dist/lib/commands/services/avatars.js +0 -118
  708. package/dist/lib/commands/services/avatars.js.map +0 -1
  709. package/dist/lib/commands/services/tablesdb.d.ts +0 -3
  710. package/dist/lib/commands/services/tablesdb.d.ts.map +0 -1
  711. package/dist/lib/commands/services/tablesdb.js.map +0 -1
  712. package/dist/scripts/generate-commands.d.ts +0 -2
  713. package/dist/scripts/generate-commands.d.ts.map +0 -1
  714. package/dist/scripts/generate-commands.js +0 -398
  715. package/dist/scripts/generate-commands.js.map +0 -1
  716. package/lib/commands/services/avatars.ts +0 -400
  717. package/lib/commands/services/tablesdb.ts +0 -1928
  718. package/scripts/generate-commands.ts +0 -539
@@ -4,21 +4,23 @@ import chalk from "chalk";
4
4
  import inquirer from "inquirer";
5
5
  import { Command } from "commander";
6
6
  import ID from "../id.js";
7
+ import { EXECUTABLE_NAME } from "../constants.js";
7
8
  import {
8
9
  localConfig,
9
10
  globalConfig,
10
- KeysAttributes,
11
11
  KeysFunction,
12
12
  KeysSite,
13
- whitelistKeys,
14
13
  KeysTopics,
15
14
  KeysStorage,
16
15
  KeysTeams,
17
16
  KeysCollection,
18
17
  KeysTable,
19
18
  } from "../config.js";
20
- import { Spinner, SPINNER_ARC, SPINNER_DOTS } from "../spinner.js";
19
+ import type { SettingsType, ConfigType } from "./config.js";
20
+ import { createSettingsObject } from "../utils.js";
21
+ import { Spinner, SPINNER_DOTS } from "../spinner.js";
21
22
  import { paginate } from "../paginate.js";
23
+ import { pushDeployment } from "./utils/deployment.js";
22
24
  import {
23
25
  questionsPushBuckets,
24
26
  questionsPushTeams,
@@ -27,8 +29,6 @@ import {
27
29
  questionsGetEntrypoint,
28
30
  questionsPushCollections,
29
31
  questionsPushTables,
30
- questionPushChanges,
31
- questionPushChangesConfirmation,
32
32
  questionsPushMessagingTopics,
33
33
  questionsPushResources,
34
34
  } from "../questions.js";
@@ -55,44 +55,53 @@ import {
55
55
  getTeamsService,
56
56
  getProjectsService,
57
57
  } from "../services.js";
58
- import { ApiService, AuthMethod } from "@appwrite.io/console";
58
+ import { sdkForProject, sdkForConsole } from "../sdks.js";
59
+ import {
60
+ ApiService,
61
+ AuthMethod,
62
+ AppwriteException,
63
+ Client,
64
+ Query,
65
+ } from "@appwrite.io/console";
59
66
  import { checkDeployConditions } from "../utils.js";
67
+ import { Pools } from "./utils/pools.js";
68
+ import { Attributes, Collection } from "./utils/attributes.js";
69
+ import {
70
+ getConfirmation,
71
+ approveChanges,
72
+ getObjectChanges,
73
+ } from "./utils/change-approval.js";
74
+ import { checkAndApplyTablesDBChanges } from "./utils/database-sync.js";
60
75
 
61
- const STEP_SIZE = 100; // Resources
62
76
  const POLL_DEBOUNCE = 2000; // Milliseconds
63
77
  const POLL_DEFAULT_VALUE = 30;
64
-
65
- let pollMaxDebounces = POLL_DEFAULT_VALUE;
66
-
67
- const changeableKeys = [
68
- "status",
69
- "required",
70
- "xdefault",
71
- "elements",
72
- "min",
73
- "max",
74
- "default",
75
- "error",
76
- ];
77
-
78
- interface ObjectChange {
79
- group: string;
80
- setting: string;
81
- remote: string;
82
- local: string;
83
- }
84
-
85
- type ComparableValue = boolean | number | string | any[] | undefined;
86
-
87
- interface AttributeChange {
88
- key: string;
89
- attribute: any;
90
- reason: string;
91
- action: string;
92
- }
93
-
94
- interface PushResourcesOptions {
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;
95
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
+ };
96
105
  }
97
106
 
98
107
  interface PushSiteOptions {
@@ -109,1473 +118,890 @@ interface PushFunctionOptions {
109
118
  withVariables?: boolean;
110
119
  }
111
120
 
112
- interface TablesDBChangesResult {
113
- applied: boolean;
114
- resyncNeeded: boolean;
115
- }
116
-
117
121
  interface PushTableOptions {
118
122
  attempts?: number;
123
+ skipConfirmation?: boolean;
119
124
  }
120
125
 
121
- interface AwaitPools {
122
- wipeAttributes: (
123
- databaseId: string,
124
- collectionId: string,
125
- iteration?: number,
126
- ) => Promise<boolean>;
127
-
128
- wipeIndexes: (
129
- databaseId: string,
130
- collectionId: string,
131
- iteration?: number,
132
- ) => Promise<boolean>;
133
-
134
- deleteAttributes: (
135
- databaseId: string,
136
- collectionId: string,
137
- attributeKeys: any[],
138
- iteration?: number,
139
- ) => Promise<boolean>;
140
-
141
- expectAttributes: (
142
- databaseId: string,
143
- collectionId: string,
144
- attributeKeys: string[],
145
- iteration?: number,
146
- ) => Promise<boolean>;
147
-
148
- deleteIndexes: (
149
- databaseId: string,
150
- collectionId: string,
151
- indexesKeys: any[],
152
- iteration?: number,
153
- ) => Promise<boolean>;
154
-
155
- expectIndexes: (
156
- databaseId: string,
157
- collectionId: string,
158
- indexKeys: string[],
159
- iteration?: number,
160
- ) => Promise<boolean>;
161
- }
162
-
163
- const awaitPools: AwaitPools = {
164
- wipeAttributes: async (
165
- databaseId: string,
166
- collectionId: string,
167
- iteration: number = 1,
168
- ): Promise<boolean> => {
169
- if (iteration > pollMaxDebounces) {
170
- return false;
171
- }
126
+ export class Push {
127
+ private projectClient: Client;
128
+ private consoleClient: Client;
129
+ private silent: boolean;
172
130
 
173
- const databasesService = await getDatabasesService();
174
- const response = await databasesService.listAttributes(
175
- databaseId,
176
- collectionId,
177
- [JSON.stringify({ method: "limit", values: [1] })],
178
- );
179
- const { total } = response;
131
+ constructor(projectClient: Client, consoleClient: Client, silent = false) {
132
+ this.projectClient = projectClient;
133
+ this.consoleClient = consoleClient;
134
+ this.silent = silent;
135
+ }
180
136
 
181
- if (total === 0) {
182
- return true;
137
+ /**
138
+ * Log a message (respects silent mode)
139
+ */
140
+ private log(message: string): void {
141
+ if (!this.silent) {
142
+ log(message);
183
143
  }
144
+ }
184
145
 
185
- if (pollMaxDebounces === POLL_DEFAULT_VALUE) {
186
- let steps = Math.max(1, Math.ceil(total / STEP_SIZE));
187
- if (steps > 1 && iteration === 1) {
188
- pollMaxDebounces *= steps;
189
-
190
- log(
191
- "Found a large number of attributes, increasing timeout to " +
192
- (pollMaxDebounces * POLL_DEBOUNCE) / 1000 / 60 +
193
- " minutes",
194
- );
195
- }
146
+ /**
147
+ * Log a success message (respects silent mode)
148
+ */
149
+ private success(message: string): void {
150
+ if (!this.silent) {
151
+ success(message);
196
152
  }
153
+ }
197
154
 
198
- await new Promise((resolve) => setTimeout(resolve, POLL_DEBOUNCE));
199
-
200
- return await awaitPools.wipeAttributes(
201
- databaseId,
202
- collectionId,
203
- iteration + 1,
204
- );
205
- },
206
- wipeIndexes: async (
207
- databaseId: string,
208
- collectionId: string,
209
- iteration: number = 1,
210
- ): Promise<boolean> => {
211
- if (iteration > pollMaxDebounces) {
212
- return false;
155
+ /**
156
+ * Log a warning message (respects silent mode)
157
+ */
158
+ private warn(message: string): void {
159
+ if (!this.silent) {
160
+ warn(message);
213
161
  }
162
+ }
214
163
 
215
- const databasesService = await getDatabasesService();
216
- const response = await databasesService.listIndexes(
217
- databaseId,
218
- collectionId,
219
- [JSON.stringify({ method: "limit", values: [1] })],
220
- );
221
- const { total } = response;
222
-
223
- if (total === 0) {
224
- return true;
164
+ /**
165
+ * Log an error message (respects silent mode)
166
+ */
167
+ private error(message: string): void {
168
+ if (!this.silent) {
169
+ error(message);
225
170
  }
171
+ }
226
172
 
227
- if (pollMaxDebounces === POLL_DEFAULT_VALUE) {
228
- let steps = Math.max(1, Math.ceil(total / STEP_SIZE));
229
- if (steps > 1 && iteration === 1) {
230
- pollMaxDebounces *= steps;
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;
231
184
 
232
- log(
233
- "Found a large number of indexes, increasing timeout to " +
234
- (pollMaxDebounces * POLL_DEBOUNCE) / 1000 / 60 +
235
- " minutes",
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,
196
+ });
197
+ this.success(
198
+ `Successfully pushed ${chalk.bold("all")} project settings.`,
236
199
  );
200
+ results.settings = { success: true };
201
+ } catch (e: any) {
202
+ allErrors.push(e);
203
+ results.settings = { success: false, error: e.message };
237
204
  }
238
205
  }
239
206
 
240
- await new Promise((resolve) => setTimeout(resolve, POLL_DEBOUNCE));
241
-
242
- return await awaitPools.wipeIndexes(
243
- databaseId,
244
- collectionId,
245
- iteration + 1,
246
- );
247
- },
248
- deleteAttributes: async (
249
- databaseId: string,
250
- collectionId: string,
251
- attributeKeys: any[],
252
- iteration: number = 1,
253
- ): Promise<boolean> => {
254
- if (iteration > pollMaxDebounces) {
255
- return false;
256
- }
257
-
258
- if (pollMaxDebounces === POLL_DEFAULT_VALUE) {
259
- let steps = Math.max(1, Math.ceil(attributeKeys.length / STEP_SIZE));
260
- if (steps > 1 && iteration === 1) {
261
- pollMaxDebounces *= steps;
262
-
263
- log(
264
- "Found a large number of attributes to be deleted. Increasing timeout to " +
265
- (pollMaxDebounces * POLL_DEBOUNCE) / 1000 / 60 +
266
- " minutes",
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.`,
267
218
  );
219
+ results.buckets = result;
220
+ allErrors.push(...result.errors);
221
+ } catch (e: any) {
222
+ allErrors.push(e);
223
+ results.buckets = { successfullyPushed: 0, errors: [e] };
268
224
  }
269
225
  }
270
226
 
271
- const { attributes } = await paginate(
272
- async (args: any) => {
273
- const databasesService = await getDatabasesService();
274
- return await databasesService.listAttributes(
275
- args.databaseId,
276
- args.collectionId,
277
- args.queries || [],
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.`,
278
238
  );
279
- },
280
- {
281
- databaseId,
282
- collectionId,
283
- },
284
- 100,
285
- "attributes",
286
- );
287
-
288
- const ready = attributeKeys.filter((attribute: any) =>
289
- attributes.includes(attribute.key),
290
- );
291
-
292
- if (ready.length === 0) {
293
- return true;
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
+ }
294
245
  }
295
246
 
296
- await new Promise((resolve) => setTimeout(resolve, POLL_DEBOUNCE));
297
-
298
- return await awaitPools.expectAttributes(
299
- databaseId,
300
- collectionId,
301
- attributeKeys,
302
- iteration + 1,
303
- );
304
- },
305
- expectAttributes: async (
306
- databaseId: string,
307
- collectionId: string,
308
- attributeKeys: string[],
309
- iteration: number = 1,
310
- ): Promise<boolean> => {
311
- if (iteration > pollMaxDebounces) {
312
- return false;
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.`,
258
+ );
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
+ }
313
265
  }
314
266
 
315
- if (pollMaxDebounces === POLL_DEFAULT_VALUE) {
316
- let steps = Math.max(1, Math.ceil(attributeKeys.length / STEP_SIZE));
317
- if (steps > 1 && iteration === 1) {
318
- pollMaxDebounces *= steps;
319
-
320
- log(
321
- "Creating a large number of attributes, increasing timeout to " +
322
- (pollMaxDebounces * POLL_DEBOUNCE) / 1000 / 60 +
323
- " minutes",
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,
324
278
  );
279
+ this.success(
280
+ `Successfully pushed ${chalk.bold(result.successfullyPushed)} functions.`,
281
+ );
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
+ };
325
292
  }
326
293
  }
327
294
 
328
- const { attributes } = await paginate(
329
- async (args: any) => {
330
- const databasesService = await getDatabasesService();
331
- return await databasesService.listAttributes(
332
- args.databaseId,
333
- args.collectionId,
334
- args.queries || [],
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.`,
335
306
  );
336
- },
337
- {
338
- databaseId,
339
- collectionId,
340
- },
341
- 100,
342
- "attributes",
343
- );
344
-
345
- const ready = attributes
346
- .filter((attribute: any) => {
347
- if (attributeKeys.includes(attribute.key)) {
348
- if (["stuck", "failed"].includes(attribute.status)) {
349
- throw new Error(`Attribute '${attribute.key}' failed!`);
350
- }
351
-
352
- return attribute.status === "available";
353
- }
354
-
355
- return false;
356
- })
357
- .map((attribute: any) => attribute.key);
358
-
359
- if (ready.length === attributeKeys.length) {
360
- return true;
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
+ }
361
318
  }
362
319
 
363
- await new Promise((resolve) => setTimeout(resolve, POLL_DEBOUNCE));
364
-
365
- return await awaitPools.expectAttributes(
366
- databaseId,
367
- collectionId,
368
- attributeKeys,
369
- iteration + 1,
370
- );
371
- },
372
- deleteIndexes: async (
373
- databaseId: string,
374
- collectionId: string,
375
- indexesKeys: any[],
376
- iteration: number = 1,
377
- ): Promise<boolean> => {
378
- if (iteration > pollMaxDebounces) {
379
- return false;
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
+ }
380
341
  }
381
342
 
382
- if (pollMaxDebounces === POLL_DEFAULT_VALUE) {
383
- let steps = Math.max(1, Math.ceil(indexesKeys.length / STEP_SIZE));
384
- if (steps > 1 && iteration === 1) {
385
- pollMaxDebounces *= steps;
386
-
387
- log(
388
- "Found a large number of indexes to be deleted. Increasing timeout to " +
389
- (pollMaxDebounces * POLL_DEBOUNCE) / 1000 / 60 +
390
- " minutes",
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,
360
+ };
361
+ },
362
+ );
363
+ const result = await this.pushCollections(collectionsWithDbNames, {
364
+ skipConfirmation: options.skipConfirmation,
365
+ });
366
+ this.success(
367
+ `Successfully pushed ${chalk.bold(result.successfullyPushed)} collections.`,
391
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] };
392
374
  }
393
375
  }
394
376
 
395
- const { indexes } = await paginate(
396
- async (args: any) => {
397
- const databasesService = await getDatabasesService();
398
- return await databasesService.listIndexes(
399
- args.databaseId,
400
- args.collectionId,
401
- args.queries || [],
402
- );
403
- },
404
- {
405
- databaseId,
406
- collectionId,
407
- },
408
- 100,
409
- "indexes",
410
- );
377
+ return {
378
+ results,
379
+ errors: allErrors,
380
+ };
381
+ }
411
382
 
412
- const ready = indexesKeys.filter((index: any) =>
413
- indexes.includes(index.key),
414
- );
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 ?? {};
415
392
 
416
- if (ready.length === 0) {
417
- return true;
393
+ if (projectName) {
394
+ this.log("Applying project name ...");
395
+ await projectsService.update({
396
+ projectId: projectId,
397
+ name: projectName,
398
+ });
418
399
  }
419
400
 
420
- await new Promise((resolve) => setTimeout(resolve, POLL_DEBOUNCE));
421
-
422
- return await awaitPools.expectIndexes(
423
- databaseId,
424
- collectionId,
425
- indexesKeys,
426
- iteration + 1,
427
- );
428
- },
429
- expectIndexes: async (
430
- databaseId: string,
431
- collectionId: string,
432
- indexKeys: string[],
433
- iteration: number = 1,
434
- ): Promise<boolean> => {
435
- if (iteration > pollMaxDebounces) {
436
- return false;
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,
408
+ });
409
+ }
437
410
  }
438
411
 
439
- if (pollMaxDebounces === POLL_DEFAULT_VALUE) {
440
- let steps = Math.max(1, Math.ceil(indexKeys.length / STEP_SIZE));
441
- if (steps > 1 && iteration === 1) {
442
- pollMaxDebounces *= steps;
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,
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
+ }
443
448
 
444
- log(
445
- "Creating a large number of indexes, increasing timeout to " +
446
- (pollMaxDebounces * POLL_DEBOUNCE) / 1000 / 60 +
447
- " minutes",
448
- );
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
+ });
457
+ }
449
458
  }
450
459
  }
460
+ }
451
461
 
452
- const { indexes } = await paginate(
453
- async (args: any) => {
454
- const databasesService = await getDatabasesService();
455
- return await databasesService.listIndexes(
456
- args.databaseId,
457
- args.collectionId,
458
- args.queries || [],
459
- );
460
- },
461
- {
462
- databaseId,
463
- collectionId,
464
- },
465
- 100,
466
- "indexes",
467
- );
462
+ public async pushBuckets(buckets: any[]): Promise<{
463
+ successfullyPushed: number;
464
+ errors: any[];
465
+ }> {
466
+ let successfullyPushed = 0;
467
+ const errors: any[] = [];
468
468
 
469
- const ready = indexes
470
- .filter((index: any) => {
471
- if (indexKeys.includes(index.key)) {
472
- if (["stuck", "failed"].includes(index.status)) {
473
- throw new Error(`Index '${index.key}' failed!`);
474
- }
469
+ for (const bucket of buckets) {
470
+ try {
471
+ this.log(`Pushing bucket ${chalk.bold(bucket["name"])} ...`);
472
+ const storageService = await getStorageService(this.projectClient);
475
473
 
476
- return index.status === "available";
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
+ }
477
505
  }
478
506
 
479
- return false;
480
- })
481
- .map((index: any) => index.key);
482
-
483
- if (ready.length >= indexKeys.length) {
484
- return true;
507
+ successfullyPushed++;
508
+ } catch (e: any) {
509
+ errors.push(e);
510
+ this.error(`Failed to push bucket ${bucket["name"]}: ${e.message}`);
511
+ }
485
512
  }
486
513
 
487
- await new Promise((resolve) => setTimeout(resolve, POLL_DEBOUNCE));
488
-
489
- return await awaitPools.expectIndexes(
490
- databaseId,
491
- collectionId,
492
- indexKeys,
493
- iteration + 1,
494
- );
495
- },
496
- };
497
-
498
- const getConfirmation = async (): Promise<boolean> => {
499
- if (cliConfig.force) {
500
- return true;
514
+ return {
515
+ successfullyPushed,
516
+ errors,
517
+ };
501
518
  }
502
519
 
503
- async function fixConfirmation(): Promise<string> {
504
- const answers = await inquirer.prompt(questionPushChangesConfirmation);
505
- if (answers.changes !== "YES" && answers.changes !== "NO") {
506
- return await fixConfirmation();
507
- }
520
+ public async pushTeams(teams: any[]): Promise<{
521
+ successfullyPushed: number;
522
+ errors: any[];
523
+ }> {
524
+ let successfullyPushed = 0;
525
+ const errors: any[] = [];
508
526
 
509
- return answers.changes;
510
- }
527
+ for (const team of teams) {
528
+ try {
529
+ this.log(`Pushing team ${chalk.bold(team["name"])} ...`);
530
+ const teamsService = await getTeamsService(this.projectClient);
511
531
 
512
- let answers = await inquirer.prompt(questionPushChanges);
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
+ }
547
+ }
513
548
 
514
- if (answers.changes !== "YES" && answers.changes !== "NO") {
515
- answers.changes = await fixConfirmation();
516
- }
549
+ successfullyPushed++;
550
+ } catch (e: any) {
551
+ errors.push(e);
552
+ this.error(`Failed to push team ${team["name"]}: ${e.message}`);
553
+ }
554
+ }
517
555
 
518
- if (answers.changes === "YES") {
519
- return true;
556
+ return {
557
+ successfullyPushed,
558
+ errors,
559
+ };
520
560
  }
521
561
 
522
- warn("Skipping push action. Changes were not applied.");
523
- return false;
524
- };
525
- const isEmpty = (value: any): boolean =>
526
- value === null ||
527
- value === undefined ||
528
- (typeof value === "string" && value.trim().length === 0) ||
529
- (Array.isArray(value) && value.length === 0);
530
-
531
- const approveChanges = async (
532
- resource: any[],
533
- resourceGetFunction: Function,
534
- keys: Set<string>,
535
- resourceName: string,
536
- resourcePlural: string,
537
- skipKeys: string[] = [],
538
- secondId: string = "",
539
- secondResourceName: string = "",
540
- ): Promise<boolean> => {
541
- log("Checking for changes ...");
542
- const changes: any[] = [];
543
-
544
- await Promise.all(
545
- resource.map(async (localResource) => {
562
+ public async pushMessagingTopics(topics: any[]): Promise<{
563
+ successfullyPushed: number;
564
+ errors: any[];
565
+ }> {
566
+ let successfullyPushed = 0;
567
+ const errors: any[] = [];
568
+
569
+ for (const topic of topics) {
546
570
  try {
547
- const options: Record<string, any> = {
548
- [resourceName]: localResource["$id"],
549
- };
571
+ this.log(`Pushing topic ${chalk.bold(topic["name"])} ...`);
572
+ const messagingService = await getMessagingService(this.projectClient);
550
573
 
551
- if (secondId !== "" && secondResourceName !== "") {
552
- options[secondResourceName] = localResource[secondId];
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
+ }
553
591
  }
554
592
 
555
- const remoteResource = await resourceGetFunction(options);
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
+ }
599
+ }
600
+
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;
621
+
622
+ Spinner.start(false);
623
+ let successfullyPushed = 0;
624
+ let successfullyDeployed = 0;
625
+ const failedDeployments: any[] = [];
626
+ const errors: any[] = [];
556
627
 
557
- for (let [key, value] of Object.entries(
558
- whitelistKeys(remoteResource, keys),
559
- )) {
560
- if (skipKeys.includes(key)) {
561
- continue;
562
- }
628
+ await Promise.all(
629
+ functions.map(async (func: any) => {
630
+ let response: any = {};
631
+
632
+ const ignore = func.ignore ? "appwrite.config.json" : ".gitignore";
633
+ let functionExists = false;
634
+ let deploymentCreated = false;
635
+
636
+ const updaterRow = new Spinner({
637
+ status: "",
638
+ resource: func.name,
639
+ id: func["$id"],
640
+ end: `Ignoring using: ${ignore}`,
641
+ });
563
642
 
564
- if (isEmpty(value) && isEmpty(localResource[key])) {
565
- continue;
643
+ updaterRow.update({ status: "Getting" }).startSpinner(SPINNER_DOTS);
644
+ const functionsService = await getFunctionsService(this.projectClient);
645
+ try {
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`,
651
+ });
652
+ return;
566
653
  }
567
654
 
568
- if (Array.isArray(value) && Array.isArray(localResource[key])) {
569
- if (JSON.stringify(value) !== JSON.stringify(localResource[key])) {
570
- changes.push({
571
- id: localResource["$id"],
572
- key,
573
- remote: chalk.red((value as string[]).join("\n")),
574
- local: chalk.green(localResource[key].join("\n")),
575
- });
576
- }
577
- } else if (value !== localResource[key]) {
578
- changes.push({
579
- id: localResource["$id"],
580
- key,
581
- remote: chalk.red(value),
582
- local: chalk.green(localResource[key]),
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
+ });
674
+ } catch (e: any) {
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",
583
682
  });
683
+ return;
584
684
  }
585
685
  }
586
- } catch (e: any) {
587
- if (Number(e.code) !== 404) {
588
- throw e;
589
- }
590
- }
591
- }),
592
- );
593
686
 
594
- if (changes.length === 0) {
595
- return true;
596
- }
687
+ if (!functionExists) {
688
+ updaterRow
689
+ .update({ status: "Creating" })
690
+ .replaceSpinner(SPINNER_DOTS);
597
691
 
598
- drawTable(changes);
599
- if ((await getConfirmation()) === true) {
600
- return true;
601
- }
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
+ });
602
708
 
603
- success(`Successfully pushed 0 ${resourcePlural}.`);
604
- return false;
605
- };
709
+ let domain = "";
710
+ try {
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
+ }
606
720
 
607
- const getObjectChanges = <T extends Record<string, any>>(
608
- remote: T,
609
- local: T,
610
- index: keyof T,
611
- what: string,
612
- ): ObjectChange[] => {
613
- const changes: ObjectChange[] = [];
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;
727
+ }
614
728
 
615
- const remoteNested = remote[index];
616
- const localNested = local[index];
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
+ }
738
+ }
617
739
 
618
- if (
619
- remoteNested &&
620
- localNested &&
621
- typeof remoteNested === "object" &&
622
- !Array.isArray(remoteNested) &&
623
- typeof localNested === "object" &&
624
- !Array.isArray(localNested)
625
- ) {
626
- const remoteObj = remoteNested as Record<string, ComparableValue>;
627
- const localObj = localNested as Record<string, ComparableValue>;
740
+ if (withVariables) {
741
+ updaterRow
742
+ .update({ status: "Updating variables" })
743
+ .replaceSpinner(SPINNER_DOTS);
628
744
 
629
- for (const [service, status] of Object.entries(remoteObj)) {
630
- const localValue = localObj[service];
631
- let valuesEqual = false;
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
+ );
632
760
 
633
- if (Array.isArray(status) && Array.isArray(localValue)) {
634
- valuesEqual = JSON.stringify(status) === JSON.stringify(localValue);
635
- } else {
636
- valuesEqual = status === localValue;
637
- }
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
+ );
638
772
 
639
- if (!valuesEqual) {
640
- changes.push({
641
- group: what,
642
- setting: service,
643
- remote: chalk.red(String(status ?? "")),
644
- local: chalk.green(String(localValue ?? "")),
645
- });
646
- }
647
- }
648
- }
649
-
650
- return changes;
651
- };
652
-
653
- const createAttribute = async (
654
- databaseId: string,
655
- collectionId: string,
656
- attribute: any,
657
- ): Promise<any> => {
658
- const databasesService = await getDatabasesService();
659
- switch (attribute.type) {
660
- case "string":
661
- switch (attribute.format) {
662
- case "email":
663
- return databasesService.createEmailAttribute({
664
- databaseId,
665
- collectionId,
666
- key: attribute.key,
667
- required: attribute.required,
668
- xdefault: attribute.default,
669
- array: attribute.array,
670
- });
671
- case "url":
672
- return databasesService.createUrlAttribute({
673
- databaseId,
674
- collectionId,
675
- key: attribute.key,
676
- required: attribute.required,
677
- xdefault: attribute.default,
678
- array: attribute.array,
679
- });
680
- case "ip":
681
- return databasesService.createIpAttribute({
682
- databaseId,
683
- collectionId,
684
- key: attribute.key,
685
- required: attribute.required,
686
- xdefault: attribute.default,
687
- array: attribute.array,
688
- });
689
- case "enum":
690
- return databasesService.createEnumAttribute({
691
- databaseId,
692
- collectionId,
693
- key: attribute.key,
694
- elements: attribute.elements,
695
- required: attribute.required,
696
- xdefault: attribute.default,
697
- array: attribute.array,
698
- });
699
- default:
700
- return databasesService.createStringAttribute({
701
- databaseId,
702
- collectionId,
703
- key: attribute.key,
704
- size: attribute.size,
705
- required: attribute.required,
706
- xdefault: attribute.default,
707
- array: attribute.array,
708
- encrypt: attribute.encrypt,
709
- });
710
- }
711
- case "integer":
712
- return databasesService.createIntegerAttribute({
713
- databaseId,
714
- collectionId,
715
- key: attribute.key,
716
- required: attribute.required,
717
- min: attribute.min,
718
- max: attribute.max,
719
- xdefault: attribute.default,
720
- array: attribute.array,
721
- });
722
- case "double":
723
- return databasesService.createFloatAttribute({
724
- databaseId,
725
- collectionId,
726
- key: attribute.key,
727
- required: attribute.required,
728
- min: attribute.min,
729
- max: attribute.max,
730
- xdefault: attribute.default,
731
- array: attribute.array,
732
- });
733
- case "boolean":
734
- return databasesService.createBooleanAttribute({
735
- databaseId,
736
- collectionId,
737
- key: attribute.key,
738
- required: attribute.required,
739
- xdefault: attribute.default,
740
- array: attribute.array,
741
- });
742
- case "datetime":
743
- return databasesService.createDatetimeAttribute({
744
- databaseId,
745
- collectionId,
746
- key: attribute.key,
747
- required: attribute.required,
748
- xdefault: attribute.default,
749
- array: attribute.array,
750
- });
751
- case "relationship":
752
- return databasesService.createRelationshipAttribute({
753
- databaseId,
754
- collectionId,
755
- relatedCollectionId:
756
- attribute.relatedTable ?? attribute.relatedCollection,
757
- type: attribute.relationType,
758
- twoWay: attribute.twoWay,
759
- key: attribute.key,
760
- twoWayKey: attribute.twoWayKey,
761
- onDelete: attribute.onDelete,
762
- });
763
- case "point":
764
- return databasesService.createPointAttribute({
765
- databaseId,
766
- collectionId,
767
- key: attribute.key,
768
- required: attribute.required,
769
- xdefault: attribute.default,
770
- });
771
- case "linestring":
772
- return databasesService.createLineAttribute({
773
- databaseId,
774
- collectionId,
775
- key: attribute.key,
776
- required: attribute.required,
777
- xdefault: attribute.default,
778
- });
779
- case "polygon":
780
- return databasesService.createPolygonAttribute({
781
- databaseId,
782
- collectionId,
783
- key: attribute.key,
784
- required: attribute.required,
785
- xdefault: attribute.default,
786
- });
787
- default:
788
- throw new Error(`Unsupported attribute type: ${attribute.type}`);
789
- }
790
- };
791
-
792
- const updateAttribute = async (
793
- databaseId: string,
794
- collectionId: string,
795
- attribute: any,
796
- ): Promise<any> => {
797
- const databasesService = await getDatabasesService();
798
- switch (attribute.type) {
799
- case "string":
800
- switch (attribute.format) {
801
- case "email":
802
- return databasesService.updateEmailAttribute({
803
- databaseId,
804
- collectionId,
805
- key: attribute.key,
806
- required: attribute.required,
807
- xdefault: attribute.default,
808
- });
809
- case "url":
810
- return databasesService.updateUrlAttribute({
811
- databaseId,
812
- collectionId,
813
- key: attribute.key,
814
- required: attribute.required,
815
- xdefault: attribute.default,
816
- });
817
- case "ip":
818
- return databasesService.updateIpAttribute({
819
- databaseId,
820
- collectionId,
821
- key: attribute.key,
822
- required: attribute.required,
823
- xdefault: attribute.default,
824
- });
825
- case "enum":
826
- return databasesService.updateEnumAttribute({
827
- databaseId,
828
- collectionId,
829
- key: attribute.key,
830
- elements: attribute.elements,
831
- required: attribute.required,
832
- xdefault: attribute.default,
833
- });
834
- default:
835
- return databasesService.updateStringAttribute({
836
- databaseId,
837
- collectionId,
838
- key: attribute.key,
839
- required: attribute.required,
840
- xdefault: attribute.default,
841
- });
842
- }
843
- case "integer":
844
- return databasesService.updateIntegerAttribute({
845
- databaseId,
846
- collectionId,
847
- key: attribute.key,
848
- required: attribute.required,
849
- min: attribute.min,
850
- max: attribute.max,
851
- xdefault: attribute.default,
852
- });
853
- case "double":
854
- return databasesService.updateFloatAttribute({
855
- databaseId,
856
- collectionId,
857
- key: attribute.key,
858
- required: attribute.required,
859
- min: attribute.min,
860
- max: attribute.max,
861
- xdefault: attribute.default,
862
- });
863
- case "boolean":
864
- return databasesService.updateBooleanAttribute({
865
- databaseId,
866
- collectionId,
867
- key: attribute.key,
868
- required: attribute.required,
869
- xdefault: attribute.default,
870
- });
871
- case "datetime":
872
- return databasesService.updateDatetimeAttribute({
873
- databaseId,
874
- collectionId,
875
- key: attribute.key,
876
- required: attribute.required,
877
- xdefault: attribute.default,
878
- });
879
- case "relationship":
880
- return databasesService.updateRelationshipAttribute({
881
- databaseId,
882
- collectionId,
883
- key: attribute.key,
884
- onDelete: attribute.onDelete,
885
- });
886
- case "point":
887
- return databasesService.updatePointAttribute({
888
- databaseId,
889
- collectionId,
890
- key: attribute.key,
891
- required: attribute.required,
892
- xdefault: attribute.default,
893
- });
894
- case "linestring":
895
- return databasesService.updateLineAttribute({
896
- databaseId,
897
- collectionId,
898
- key: attribute.key,
899
- required: attribute.required,
900
- xdefault: attribute.default,
901
- });
902
- case "polygon":
903
- return databasesService.updatePolygonAttribute({
904
- databaseId,
905
- collectionId,
906
- key: attribute.key,
907
- required: attribute.required,
908
- xdefault: attribute.default,
909
- });
910
- default:
911
- throw new Error(`Unsupported attribute type: ${attribute.type}`);
912
- }
913
- };
914
- const deleteAttribute = async (
915
- collection: any,
916
- attribute: any,
917
- isIndex: boolean = false,
918
- ): Promise<void> => {
919
- log(
920
- `Deleting ${isIndex ? "index" : "attribute"} ${attribute.key} of ${collection.name} ( ${collection["$id"]} )`,
921
- );
922
-
923
- const databasesService = await getDatabasesService();
924
- if (isIndex) {
925
- await databasesService.deleteIndex(
926
- collection["databaseId"],
927
- collection["$id"],
928
- attribute.key,
929
- );
930
- return;
931
- }
932
-
933
- await databasesService.deleteAttribute(
934
- collection["databaseId"],
935
- collection["$id"],
936
- attribute.key,
937
- );
938
- };
939
-
940
- const isEqual = (a: any, b: any): boolean => {
941
- if (a === b) return true;
942
-
943
- if (a && b && typeof a === "object" && typeof b === "object") {
944
- if (
945
- a.constructor &&
946
- a.constructor.name === "BigNumber" &&
947
- b.constructor &&
948
- b.constructor.name === "BigNumber"
949
- ) {
950
- return a.eq(b);
951
- }
952
-
953
- if (typeof a.equals === "function") {
954
- return a.equals(b);
955
- }
956
-
957
- if (typeof a.eq === "function") {
958
- return a.eq(b);
959
- }
960
- }
961
-
962
- if (typeof a === "number" && typeof b === "number") {
963
- if (isNaN(a) && isNaN(b)) return true;
964
- if (!isFinite(a) && !isFinite(b)) return a === b;
965
- return Math.abs(a - b) < Number.EPSILON;
966
- }
967
-
968
- return false;
969
- };
970
-
971
- const compareAttribute = (
972
- remote: any,
973
- local: any,
974
- reason: string,
975
- key: string,
976
- ): string => {
977
- if (isEmpty(remote) && isEmpty(local)) {
978
- return reason;
979
- }
980
-
981
- if (Array.isArray(remote) && Array.isArray(local)) {
982
- if (JSON.stringify(remote) !== JSON.stringify(local)) {
983
- const bol = reason === "" ? "" : "\n";
984
- reason += `${bol}${key} changed from ${chalk.red(remote)} to ${chalk.green(local)}`;
985
- }
986
- } else if (!isEqual(remote, local)) {
987
- const bol = reason === "" ? "" : "\n";
988
- reason += `${bol}${key} changed from ${chalk.red(remote)} to ${chalk.green(local)}`;
989
- }
990
-
991
- return reason;
992
- };
993
-
994
- /**
995
- * Check if attribute non-changeable fields has been changed
996
- * If so return the differences as an object.
997
- */
998
- const checkAttributeChanges = (
999
- remote: any,
1000
- local: any,
1001
- collection: any,
1002
- recreating: boolean = true,
1003
- ): AttributeChange | undefined => {
1004
- if (local === undefined) {
1005
- return undefined;
1006
- }
1007
-
1008
- const keyName = `${chalk.yellow(local.key)} in ${collection.name} (${collection["$id"]})`;
1009
- const action = chalk.cyan(recreating ? "recreating" : "changing");
1010
- let reason = "";
1011
- let attribute = recreating ? remote : local;
1012
-
1013
- for (let key of Object.keys(remote)) {
1014
- if (!KeysAttributes.has(key)) {
1015
- continue;
1016
- }
1017
-
1018
- if (changeableKeys.includes(key)) {
1019
- if (!recreating) {
1020
- reason = compareAttribute(remote[key], local[key], reason, key);
1021
- }
1022
- continue;
1023
- }
1024
-
1025
- if (!recreating) {
1026
- continue;
1027
- }
1028
-
1029
- reason = compareAttribute(remote[key], local[key], reason, key);
1030
- }
1031
-
1032
- return reason === ""
1033
- ? undefined
1034
- : { key: keyName, attribute, reason, action };
1035
- };
1036
-
1037
- /**
1038
- * Check if attributes contain the given attribute
1039
- */
1040
- const attributesContains = (attribute: any, attributes: any[]): any =>
1041
- attributes.find((attr) => attr.key === attribute.key);
1042
-
1043
- const generateChangesObject = (
1044
- attribute: any,
1045
- collection: any,
1046
- isAdding: boolean,
1047
- ): AttributeChange => {
1048
- return {
1049
- key: `${chalk.yellow(attribute.key)} in ${collection.name} (${collection["$id"]})`,
1050
- attribute: attribute,
1051
- reason: isAdding
1052
- ? "Field isn't present on the remote server"
1053
- : "Field isn't present on the appwrite.config.json file",
1054
- action: isAdding ? chalk.green("adding") : chalk.red("deleting"),
1055
- };
1056
- };
1057
-
1058
- /**
1059
- * Filter deleted and recreated attributes,
1060
- * return list of attributes to create
1061
- */
1062
- const attributesToCreate = async (
1063
- remoteAttributes: any[],
1064
- localAttributes: any[],
1065
- collection: any,
1066
- isIndex: boolean = false,
1067
- ): Promise<any[]> => {
1068
- const deleting = remoteAttributes
1069
- .filter((attribute) => !attributesContains(attribute, localAttributes))
1070
- .map((attr) => generateChangesObject(attr, collection, false));
1071
- const adding = localAttributes
1072
- .filter((attribute) => !attributesContains(attribute, remoteAttributes))
1073
- .map((attr) => generateChangesObject(attr, collection, true));
1074
- const conflicts = remoteAttributes
1075
- .map((attribute) =>
1076
- checkAttributeChanges(
1077
- attribute,
1078
- attributesContains(attribute, localAttributes),
1079
- collection,
1080
- ),
1081
- )
1082
- .filter((attribute) => attribute !== undefined) as AttributeChange[];
1083
- const changes = remoteAttributes
1084
- .map((attribute) =>
1085
- checkAttributeChanges(
1086
- attribute,
1087
- attributesContains(attribute, localAttributes),
1088
- collection,
1089
- false,
1090
- ),
1091
- )
1092
- .filter((attribute) => attribute !== undefined)
1093
- .filter(
1094
- (attribute) =>
1095
- conflicts.filter((attr) => attribute!.key === attr.key).length !== 1,
1096
- ) as AttributeChange[];
1097
-
1098
- let changedAttributes: any[] = [];
1099
- const changing = [...deleting, ...adding, ...conflicts, ...changes];
1100
- if (changing.length === 0) {
1101
- return changedAttributes;
1102
- }
1103
-
1104
- log(
1105
- !cliConfig.force
1106
- ? "There are pending changes in your collection deployment"
1107
- : "List of applied changes",
1108
- );
1109
-
1110
- drawTable(
1111
- changing.map((change) => {
1112
- return { Key: change.key, Action: change.action, Reason: change.reason };
1113
- }),
1114
- );
1115
-
1116
- if (!cliConfig.force) {
1117
- if (deleting.length > 0 && !isIndex) {
1118
- console.log(
1119
- `${chalk.red("------------------------------------------------------")}`,
1120
- );
1121
- console.log(
1122
- `${chalk.red("| WARNING: Attribute deletion may cause loss of data |")}`,
1123
- );
1124
- console.log(
1125
- `${chalk.red("------------------------------------------------------")}`,
1126
- );
1127
- console.log();
1128
- }
1129
- if (conflicts.length > 0 && !isIndex) {
1130
- console.log(
1131
- `${chalk.red("--------------------------------------------------------")}`,
1132
- );
1133
- console.log(
1134
- `${chalk.red("| WARNING: Attribute recreation may cause loss of data |")}`,
1135
- );
1136
- console.log(
1137
- `${chalk.red("--------------------------------------------------------")}`,
1138
- );
1139
- console.log();
1140
- }
1141
-
1142
- if ((await getConfirmation()) !== true) {
1143
- return changedAttributes;
1144
- }
1145
- }
1146
-
1147
- if (conflicts.length > 0) {
1148
- changedAttributes = conflicts.map((change) => change.attribute);
1149
- await Promise.all(
1150
- changedAttributes.map((changed) =>
1151
- deleteAttribute(collection, changed, isIndex),
1152
- ),
1153
- );
1154
- remoteAttributes = remoteAttributes.filter(
1155
- (attribute) => !attributesContains(attribute, changedAttributes),
1156
- );
1157
- }
1158
-
1159
- if (changes.length > 0) {
1160
- changedAttributes = changes.map((change) => change.attribute);
1161
- await Promise.all(
1162
- changedAttributes.map((changed) =>
1163
- updateAttribute(collection["databaseId"], collection["$id"], changed),
1164
- ),
1165
- );
1166
- }
1167
-
1168
- const deletingAttributes = deleting.map((change) => change.attribute);
1169
- await Promise.all(
1170
- deletingAttributes.map((attribute) =>
1171
- deleteAttribute(collection, attribute, isIndex),
1172
- ),
1173
- );
1174
- const attributeKeys = [
1175
- ...remoteAttributes.map((attribute: any) => attribute.key),
1176
- ...deletingAttributes.map((attribute: any) => attribute.key),
1177
- ];
1178
-
1179
- if (attributeKeys.length) {
1180
- const deleteAttributesPoolStatus = await awaitPools.deleteAttributes(
1181
- collection["databaseId"],
1182
- collection["$id"],
1183
- attributeKeys,
1184
- );
1185
-
1186
- if (!deleteAttributesPoolStatus) {
1187
- throw new Error("Attribute deletion timed out.");
1188
- }
1189
- }
1190
-
1191
- return localAttributes.filter(
1192
- (attribute) => !attributesContains(attribute, remoteAttributes),
1193
- );
1194
- };
1195
-
1196
- const createIndexes = async (
1197
- indexes: any[],
1198
- collection: any,
1199
- ): Promise<void> => {
1200
- log(`Creating indexes ...`);
1201
-
1202
- const databasesService = await getDatabasesService();
1203
- for (let index of indexes) {
1204
- await databasesService.createIndex(
1205
- collection["databaseId"],
1206
- collection["$id"],
1207
- index.key,
1208
- index.type,
1209
- index.columns ?? index.attributes,
1210
- index.orders,
1211
- );
1212
- }
1213
-
1214
- const result = await awaitPools.expectIndexes(
1215
- collection["databaseId"],
1216
- collection["$id"],
1217
- indexes.map((index: any) => index.key),
1218
- );
1219
-
1220
- if (!result) {
1221
- throw new Error("Index creation timed out.");
1222
- }
1223
-
1224
- success(`Created ${indexes.length} indexes`);
1225
- };
1226
-
1227
- const createAttributes = async (
1228
- attributes: any[],
1229
- collection: any,
1230
- ): Promise<void> => {
1231
- for (let attribute of attributes) {
1232
- if (attribute.side !== "child") {
1233
- await createAttribute(
1234
- collection["databaseId"],
1235
- collection["$id"],
1236
- attribute,
1237
- );
1238
- }
1239
- }
1240
-
1241
- const result = await awaitPools.expectAttributes(
1242
- collection["databaseId"],
1243
- collection["$id"],
1244
- collection.attributes
1245
- .filter((attribute: any) => attribute.side !== "child")
1246
- .map((attribute: any) => attribute.key),
1247
- );
1248
-
1249
- if (!result) {
1250
- throw new Error(`Attribute creation timed out.`);
1251
- }
1252
-
1253
- success(`Created ${attributes.length} attributes`);
1254
- };
1255
-
1256
- const createColumns = async (columns: any[], table: any): Promise<void> => {
1257
- for (let column of columns) {
1258
- if (column.side !== "child") {
1259
- await createAttribute(table["databaseId"], table["$id"], column);
1260
- }
1261
- }
1262
-
1263
- const result = await awaitPools.expectAttributes(
1264
- table["databaseId"],
1265
- table["$id"],
1266
- table.columns
1267
- .filter((column: any) => column.side !== "child")
1268
- .map((column: any) => column.key),
1269
- );
1270
-
1271
- if (!result) {
1272
- throw new Error(`Column creation timed out.`);
1273
- }
1274
-
1275
- success(`Created ${columns.length} columns`);
1276
- };
1277
-
1278
- const pushResources = async ({
1279
- skipDeprecated = false,
1280
- }: PushResourcesOptions = {}): Promise<void> => {
1281
- const actions: Record<string, (options?: any) => Promise<void>> = {
1282
- settings: pushSettings,
1283
- functions: pushFunction,
1284
- sites: pushSite,
1285
- collections: pushCollection,
1286
- tables: pushTable,
1287
- buckets: pushBucket,
1288
- teams: pushTeam,
1289
- messages: pushMessagingTopic,
1290
- };
1291
-
1292
- if (skipDeprecated) {
1293
- delete actions.collections;
1294
- }
1295
-
1296
- if (cliConfig.all) {
1297
- for (let action of Object.values(actions)) {
1298
- await action();
1299
- }
1300
- } else {
1301
- const answers = await inquirer.prompt(questionsPushResources);
1302
-
1303
- const action = actions[answers.resource];
1304
- if (action !== undefined) {
1305
- await action();
1306
- }
1307
- }
1308
- };
1309
-
1310
- const pushSettings = async (): Promise<void> => {
1311
- checkDeployConditions(localConfig);
1312
-
1313
- try {
1314
- const projectsService = await getProjectsService();
1315
- let response = await projectsService.get(
1316
- localConfig.getProject().projectId,
1317
- );
1318
-
1319
- const remoteSettings = localConfig.createSettingsObject(response ?? {});
1320
- const localSettings = localConfig.getProject().projectSettings ?? {};
1321
-
1322
- log("Checking for changes ...");
1323
- const changes: any[] = [];
1324
-
1325
- changes.push(
1326
- ...getObjectChanges(remoteSettings, localSettings, "services", "Service"),
1327
- );
1328
- changes.push(
1329
- ...getObjectChanges(
1330
- remoteSettings["auth"] ?? {},
1331
- localSettings["auth"] ?? {},
1332
- "methods",
1333
- "Auth method",
1334
- ),
1335
- );
1336
- changes.push(
1337
- ...getObjectChanges(
1338
- remoteSettings["auth"] ?? {},
1339
- localSettings["auth"] ?? {},
1340
- "security",
1341
- "Auth security",
1342
- ),
1343
- );
1344
-
1345
- if (changes.length > 0) {
1346
- drawTable(changes);
1347
- if ((await getConfirmation()) !== true) {
1348
- success(`Successfully pushed 0 project settings.`);
1349
- return;
1350
- }
1351
- }
1352
- } catch (e) {}
1353
-
1354
- try {
1355
- log("Pushing project settings ...");
1356
-
1357
- const projectsService = await getProjectsService();
1358
- const projectId = localConfig.getProject().projectId;
1359
- const projectName = localConfig.getProject().projectName;
1360
- const settings = localConfig.getProject().projectSettings ?? {};
1361
-
1362
- if (projectName) {
1363
- log("Applying project name ...");
1364
- await projectsService.update(projectId, projectName);
1365
- }
1366
-
1367
- if (settings.services) {
1368
- log("Applying service statuses ...");
1369
- for (let [service, status] of Object.entries(settings.services)) {
1370
- await projectsService.updateServiceStatus(
1371
- projectId,
1372
- service as ApiService,
1373
- status,
1374
- );
1375
- }
1376
- }
1377
-
1378
- if (settings.auth) {
1379
- if (settings.auth.security) {
1380
- log("Applying auth security settings ...");
1381
- await projectsService.updateAuthDuration(
1382
- projectId,
1383
- settings.auth.security.duration,
1384
- );
1385
- await projectsService.updateAuthLimit(
1386
- projectId,
1387
- settings.auth.security.limit,
1388
- );
1389
- await projectsService.updateAuthSessionsLimit(
1390
- projectId,
1391
- settings.auth.security.sessionsLimit,
1392
- );
1393
- await projectsService.updateAuthPasswordDictionary(
1394
- projectId,
1395
- settings.auth.security.passwordDictionary,
1396
- );
1397
- await projectsService.updateAuthPasswordHistory(
1398
- projectId,
1399
- settings.auth.security.passwordHistory,
1400
- );
1401
- await projectsService.updatePersonalDataCheck(
1402
- projectId,
1403
- settings.auth.security.personalDataCheck,
1404
- );
1405
- await projectsService.updateSessionAlerts(
1406
- projectId,
1407
- settings.auth.security.sessionAlerts,
1408
- );
1409
- await projectsService.updateMockNumbers(
1410
- projectId,
1411
- settings.auth.security.mockNumbers,
1412
- );
1413
- }
1414
-
1415
- if (settings.auth.methods) {
1416
- log("Applying auth methods statuses ...");
1417
-
1418
- for (let [method, status] of Object.entries(settings.auth.methods)) {
1419
- await projectsService.updateAuthStatus(
1420
- projectId,
1421
- method as AuthMethod,
1422
- status,
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
+ );
783
+ }
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
+ }),
1423
799
  );
1424
800
  }
1425
- }
1426
- }
1427
801
 
1428
- success(`Successfully pushed ${chalk.bold("all")} project settings.`);
1429
- } catch (e) {
1430
- throw e;
1431
- }
1432
- };
1433
-
1434
- const pushSite = async ({
1435
- siteId,
1436
- async: asyncDeploy,
1437
- code,
1438
- withVariables,
1439
- }: PushSiteOptions = {}): Promise<void> => {
1440
- process.chdir(localConfig.configDirectoryPath);
1441
-
1442
- const siteIds: string[] = [];
1443
-
1444
- if (siteId) {
1445
- siteIds.push(siteId);
1446
- } else if (cliConfig.all) {
1447
- checkDeployConditions(localConfig);
1448
- const sites = localConfig.getSites();
1449
- siteIds.push(
1450
- ...sites.map((site: any) => {
1451
- return site.$id;
1452
- }),
1453
- );
1454
- }
1455
-
1456
- if (siteIds.length <= 0) {
1457
- const answers = await inquirer.prompt(questionsPushSites);
1458
- if (answers.sites) {
1459
- siteIds.push(...answers.sites);
1460
- }
1461
- }
1462
-
1463
- if (siteIds.length === 0) {
1464
- log("No sites found.");
1465
- hint(
1466
- "Use 'appwrite pull sites' to synchronize existing one, or use 'appwrite init site' to create a new one.",
1467
- );
1468
- return;
1469
- }
802
+ if (code === false) {
803
+ successfullyPushed++;
804
+ successfullyDeployed++;
805
+ updaterRow.update({ status: "Pushed" });
806
+ updaterRow.stopSpinner();
807
+ return;
808
+ }
1470
809
 
1471
- let sites = siteIds.map((id: string) => {
1472
- const sites = localConfig.getSites();
1473
- const site = sites.find((s: any) => s.$id === id);
810
+ try {
811
+ updaterRow.update({ status: "Pushing" }).replaceSpinner(SPINNER_DOTS);
812
+ const functionsServiceDeploy = await getFunctionsService(
813
+ this.projectClient,
814
+ );
1474
815
 
1475
- if (!site) {
1476
- throw new Error("Site '" + id + "' not found.");
1477
- }
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,
824
+ activate: true,
825
+ });
826
+ },
827
+ pollForStatus: false,
828
+ });
1478
829
 
1479
- return site;
1480
- });
830
+ response = result.deployment;
831
+ updaterRow.update({ status: "Pushed" });
1481
832
 
1482
- log("Validating sites ...");
1483
- // Validation is done BEFORE pushing so the deployment process can be run in async with progress update
1484
- for (let site of sites) {
1485
- if (!site.buildCommand) {
1486
- log(`Site ${site.name} is missing build command.`);
1487
- const answers = await inquirer.prompt(questionsGetEntrypoint);
1488
- site.buildCommand = answers.entrypoint;
1489
- localConfig.addSite(site);
1490
- }
1491
- }
833
+ deploymentCreated = true;
834
+ successfullyPushed++;
835
+ } catch (e: any) {
836
+ errors.push(e);
1492
837
 
1493
- if (
1494
- !(await approveChanges(
1495
- sites,
1496
- async (args: any) => {
1497
- const sitesService = await getSitesService();
1498
- return await sitesService.get({ siteId: args.siteId });
1499
- },
1500
- KeysSite,
1501
- "siteId",
1502
- "sites",
1503
- ["vars"],
1504
- ))
1505
- ) {
1506
- return;
1507
- }
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
+ }
850
+ }
1508
851
 
1509
- log("Pushing sites ...");
852
+ if (deploymentCreated && !asyncDeploy) {
853
+ try {
854
+ const deploymentId = response["$id"];
855
+ updaterRow.update({
856
+ status: "Deploying",
857
+ end: "Checking deployment status...",
858
+ });
1510
859
 
1511
- Spinner.start(false);
1512
- let successfullyPushed = 0;
1513
- let successfullyDeployed = 0;
1514
- const failedDeployments: any[] = [];
1515
- const errors: any[] = [];
1516
-
1517
- await Promise.all(
1518
- sites.map(async (site: any) => {
1519
- let response: any = {};
1520
-
1521
- const ignore = site.ignore ? "appwrite.config.json" : ".gitignore";
1522
- let siteExists = false;
1523
- let deploymentCreated = false;
1524
-
1525
- const updaterRow = new Spinner({
1526
- status: "",
1527
- resource: site.name,
1528
- id: site["$id"],
1529
- end: `Ignoring using: ${ignore}`,
1530
- });
860
+ const timeoutDeadline = Date.now() + DEPLOYMENT_TIMEOUT_MS;
861
+
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
+ }
1531
874
 
1532
- updaterRow.update({ status: "Getting" }).startSpinner(SPINNER_DOTS);
875
+ const functionsServicePoll = await getFunctionsService(
876
+ this.projectClient,
877
+ );
878
+ response = await functionsServicePoll.getDeployment({
879
+ functionId: func["$id"],
880
+ deploymentId: deploymentId,
881
+ });
1533
882
 
1534
- const sitesService = await getSitesService();
1535
- try {
1536
- response = await sitesService.get({ siteId: site["$id"] });
1537
- siteExists = true;
1538
- if (response.framework !== site.framework) {
1539
- updaterRow.fail({
1540
- errorMessage: `Framework mismatch! (local=${site.framework},remote=${response.framework}) Please delete remote site or update your appwrite.config.json`,
1541
- });
1542
- return;
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
+ ],
898
+ });
899
+
900
+ if (Number(res.total) === 1) {
901
+ url = `https://${res.rules[0].domain}`;
902
+ }
903
+
904
+ updaterRow.update({ status: "Deployed", end: url });
905
+
906
+ break;
907
+ } else if (status === "failed") {
908
+ failedDeployments.push({
909
+ name: func["name"],
910
+ $id: func["$id"],
911
+ deployment: response["$id"],
912
+ });
913
+ updaterRow.fail({ errorMessage: `Failed to deploy` });
914
+
915
+ break;
916
+ } else {
917
+ updaterRow.update({
918
+ status: "Deploying",
919
+ end: `Current status: ${status}`,
920
+ });
921
+ }
922
+
923
+ await new Promise((resolve) =>
924
+ setTimeout(resolve, POLL_DEBOUNCE),
925
+ );
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
+ }
1543
934
  }
1544
935
 
1545
- updaterRow.update({ status: "Updating" }).replaceSpinner(SPINNER_ARC);
1546
-
1547
- response = await sitesService.update({
1548
- siteId: site["$id"],
1549
- name: site.name,
1550
- framework: site.framework,
1551
- enabled: site.enabled,
1552
- logging: site.logging,
1553
- timeout: site.timeout,
1554
- installCommand: site.installCommand,
1555
- buildCommand: site.buildCommand,
1556
- outputDirectory: site.outputDirectory,
1557
- buildRuntime: site.buildRuntime,
1558
- adapter: site.adapter,
1559
- specification: site.specification,
936
+ updaterRow.stopSpinner();
937
+ }),
938
+ );
939
+
940
+ Spinner.stop();
941
+
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;
964
+
965
+ Spinner.start(false);
966
+ let successfullyPushed = 0;
967
+ let successfullyDeployed = 0;
968
+ const failedDeployments: any[] = [];
969
+ const errors: any[] = [];
970
+
971
+ await Promise.all(
972
+ sites.map(async (site: any) => {
973
+ let response: any = {};
974
+
975
+ const ignore = site.ignore ? "appwrite.config.json" : ".gitignore";
976
+ let siteExists = false;
977
+ let deploymentCreated = false;
978
+
979
+ const updaterRow = new Spinner({
980
+ status: "",
981
+ resource: site.name,
982
+ id: site["$id"],
983
+ end: `Ignoring using: ${ignore}`,
1560
984
  });
1561
- } catch (e: any) {
1562
- if (Number(e.code) === 404) {
1563
- siteExists = false;
1564
- } else {
1565
- errors.push(e);
1566
- updaterRow.fail({
1567
- errorMessage: e.message ?? "General error occurs please try again",
1568
- });
1569
- return;
1570
- }
1571
- }
1572
985
 
1573
- if (!siteExists) {
1574
- updaterRow.update({ status: "Creating" }).replaceSpinner(SPINNER_DOTS);
986
+ updaterRow.update({ status: "Getting" }).startSpinner(SPINNER_DOTS);
1575
987
 
988
+ const sitesService = await getSitesService(this.projectClient);
1576
989
  try {
1577
- response = await sitesService.create({
1578
- siteId: site.$id,
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`,
995
+ });
996
+ return;
997
+ }
998
+
999
+ updaterRow
1000
+ .update({ status: "Updating" })
1001
+ .replaceSpinner(SPINNER_DOTS);
1002
+
1003
+ response = await sitesService.update({
1004
+ siteId: site["$id"],
1579
1005
  name: site.name,
1580
1006
  framework: site.framework,
1581
1007
  enabled: site.enabled,
@@ -1588,617 +1014,774 @@ const pushSite = async ({
1588
1014
  adapter: site.adapter,
1589
1015
  specification: site.specification,
1590
1016
  });
1017
+ } catch (e: any) {
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",
1025
+ });
1026
+ return;
1027
+ }
1028
+ }
1029
+
1030
+ if (!siteExists) {
1031
+ updaterRow
1032
+ .update({ status: "Creating" })
1033
+ .replaceSpinner(SPINNER_DOTS);
1591
1034
 
1592
- let domain = "";
1593
1035
  try {
1594
- const consoleService = await getConsoleService();
1595
- const variables = await consoleService.variables();
1596
- domain = ID.unique() + "." + variables["_APP_DOMAIN_SITES"];
1597
- } catch (error) {
1598
- console.error("Error fetching console variables.");
1599
- throw error;
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,
1049
+ });
1050
+
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
+ }
1062
+
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
+ }
1070
+
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;
1600
1079
  }
1080
+ }
1081
+
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
+ );
1601
1110
 
1111
+ const envFileLocation = `${site["path"]}/.env`;
1112
+ let envVariables: Array<{ key: string; value: string }> = [];
1602
1113
  try {
1603
- const proxyService = await getProxyService();
1604
- const rule = await proxyService.createSiteRule(domain, site.$id);
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
+ }
1605
1122
  } catch (error) {
1606
- console.error("Error creating site rule.");
1607
- throw error;
1123
+ envVariables = [];
1608
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
+ }
1139
+
1140
+ if (code === false) {
1141
+ successfullyPushed++;
1142
+ successfullyDeployed++;
1143
+ updaterRow.update({ status: "Pushed" });
1144
+ updaterRow.stopSpinner();
1145
+ return;
1146
+ }
1147
+
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
+ });
1609
1166
 
1610
- updaterRow.update({ status: "Created" });
1167
+ response = result.deployment;
1168
+ updaterRow.update({ status: "Pushed" });
1169
+ deploymentCreated = true;
1170
+ successfullyPushed++;
1611
1171
  } catch (e: any) {
1612
1172
  errors.push(e);
1613
- updaterRow.fail({
1614
- errorMessage: e.message ?? "General error occurs please try again",
1615
- });
1616
- return;
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
+ }
1617
1186
  }
1618
- }
1619
1187
 
1620
- if (withVariables) {
1621
- updaterRow
1622
- .update({ status: "Creating variables" })
1623
- .replaceSpinner(SPINNER_ARC);
1188
+ if (deploymentCreated && !asyncDeploy) {
1189
+ try {
1190
+ const deploymentId = response["$id"];
1191
+ updaterRow.update({
1192
+ status: "Deploying",
1193
+ end: "Checking deployment status...",
1194
+ });
1624
1195
 
1625
- const sitesService = await getSitesService();
1626
- const { variables } = await paginate(
1627
- async (args: any) => {
1628
- return await sitesService.listVariables({ siteId: args.siteId });
1629
- },
1630
- {
1631
- siteId: site["$id"],
1632
- },
1633
- 100,
1634
- "variables",
1635
- );
1196
+ const timeoutDeadline = Date.now() + DEPLOYMENT_TIMEOUT_MS;
1197
+
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
+ }
1636
1210
 
1637
- await Promise.all(
1638
- variables.map(async (variable: any) => {
1639
- const sitesService = await getSitesService();
1640
- await sitesService.deleteVariable({
1641
- siteId: site["$id"],
1642
- variableId: variable["$id"],
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
+ });
1235
+
1236
+ if (Number(res.total) === 1) {
1237
+ url = `https://${res.rules[0].domain}`;
1238
+ }
1239
+
1240
+ updaterRow.update({ status: "Deployed", end: url });
1241
+
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` });
1250
+
1251
+ break;
1252
+ } else {
1253
+ updaterRow.update({
1254
+ status: "Deploying",
1255
+ end: `Current status: ${status}`,
1256
+ });
1257
+ }
1258
+
1259
+ await new Promise((resolve) =>
1260
+ setTimeout(resolve, POLL_DEBOUNCE),
1261
+ );
1262
+ }
1263
+ } catch (e: any) {
1264
+ errors.push(e);
1265
+ updaterRow.fail({
1266
+ errorMessage:
1267
+ e.message ?? "Unknown error occurred. Please try again",
1643
1268
  });
1644
- }),
1645
- );
1269
+ }
1270
+ }
1271
+
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);
1646
1297
 
1647
- const envFileLocation = `${site["path"]}/.env`;
1648
- let envVariables: Array<{ key: string; value: string }> = [];
1298
+ let tablesChanged = new Set();
1299
+ const errors: any[] = [];
1300
+
1301
+ // Parallel tables actions
1302
+ await Promise.all(
1303
+ tables.map(async (table: any) => {
1649
1304
  try {
1650
- if (fs.existsSync(envFileLocation)) {
1651
- const envObject = parseDotenv(
1652
- fs.readFileSync(envFileLocation, "utf8"),
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"],
1329
+ });
1330
+
1331
+ this.success(
1332
+ `Updated ${table.name} ( ${table["$id"]} ) - ${changes.join(", ")}`,
1653
1333
  );
1654
- envVariables = Object.entries(envObject || {}).map(
1655
- ([key, value]) => ({ key, value }),
1334
+ tablesChanged.add(table["$id"]);
1335
+ }
1336
+ table.remoteVersion = remoteTable;
1337
+
1338
+ table.isExisted = true;
1339
+ } catch (e: any) {
1340
+ if (Number(e.code) === 404) {
1341
+ this.log(
1342
+ `Table ${table.name} does not exist in the project. Creating ... `,
1656
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;
1657
1360
  }
1658
- } catch (error) {
1659
- // Handle parsing errors gracefully
1660
- envVariables = [];
1661
1361
  }
1662
- await Promise.all(
1663
- envVariables.map(async (variable) => {
1664
- const sitesService = await getSitesService();
1665
- await sitesService.createVariable({
1666
- siteId: site["$id"],
1667
- key: variable.key,
1668
- value: variable.value,
1669
- secret: false,
1670
- });
1671
- }),
1362
+ }),
1363
+ );
1364
+
1365
+ // Serialize attribute actions
1366
+ for (let table of tables) {
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,
1672
1381
  );
1673
- }
1674
1382
 
1675
- if (code === false) {
1676
- successfullyPushed++;
1677
- successfullyDeployed++;
1678
- updaterRow.update({ status: "Pushed" });
1679
- updaterRow.stopSpinner();
1680
- return;
1383
+ if (
1384
+ Array.isArray(columns) &&
1385
+ columns.length <= 0 &&
1386
+ Array.isArray(indexes) &&
1387
+ indexes.length <= 0
1388
+ ) {
1389
+ continue;
1390
+ }
1681
1391
  }
1682
1392
 
1683
- try {
1684
- updaterRow.update({ status: "Pushing" }).replaceSpinner(SPINNER_ARC);
1685
- const sitesService = await getSitesService();
1686
- response = await sitesService.createDeployment({
1687
- siteId: site["$id"],
1688
- installCommand: site.installCommand,
1689
- buildCommand: site.buildCommand,
1690
- outputDirectory: site.outputDirectory,
1691
- code: site.path,
1692
- activate: true,
1693
- });
1393
+ this.log(
1394
+ `Pushing table ${table.name} ( ${table["databaseId"]} - ${table["$id"]} ) attributes`,
1395
+ );
1694
1396
 
1695
- updaterRow.update({ status: "Pushed" });
1696
- deploymentCreated = true;
1697
- successfullyPushed++;
1698
- } catch (e: any) {
1397
+ try {
1398
+ await attributes.createColumns(columns, table as Collection);
1399
+ } catch (e) {
1699
1400
  errors.push(e);
1401
+ throw e;
1402
+ }
1700
1403
 
1701
- switch (e.code) {
1702
- case "ENOENT":
1703
- updaterRow.fail({
1704
- errorMessage: "Not found in the current directory. Skipping...",
1705
- });
1706
- break;
1707
- default:
1708
- updaterRow.fail({
1709
- errorMessage:
1710
- e.message ?? "An unknown error occurred. Please try again.",
1711
- });
1712
- }
1404
+ try {
1405
+ await attributes.createIndexes(indexes, table as Collection);
1406
+ } catch (e) {
1407
+ errors.push(e);
1408
+ throw e;
1713
1409
  }
1410
+ tablesChanged.add(table["$id"]);
1411
+ this.success(`Successfully pushed ${table.name} ( ${table["$id"]} )`);
1412
+ }
1413
+
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);
1430
+
1431
+ const errors: any[] = [];
1432
+
1433
+ const databases = Array.from(
1434
+ new Set(collections.map((collection: any) => collection["databaseId"])),
1435
+ );
1714
1436
 
1715
- if (deploymentCreated && !asyncDeploy) {
1437
+ // Parallel db actions
1438
+ await Promise.all(
1439
+ databases.map(async (databaseId: any) => {
1440
+ const databasesService = await getDatabasesService(this.projectClient);
1716
1441
  try {
1717
- const deploymentId = response["$id"];
1718
- updaterRow.update({
1719
- status: "Deploying",
1720
- end: "Checking deployment status...",
1721
- });
1722
- let pollChecks = 0;
1442
+ const database = await databasesService.get(databaseId);
1723
1443
 
1724
- while (true) {
1725
- const sitesService = await getSitesService();
1726
- response = await sitesService.getDeployment({
1727
- siteId: site["$id"],
1728
- deploymentId: deploymentId,
1729
- });
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;
1730
1449
 
1731
- const status = response["status"];
1732
- if (status === "ready") {
1733
- successfullyDeployed++;
1734
-
1735
- let url = "";
1736
- const proxyService = await getProxyService();
1737
- const res = await proxyService.listRules([
1738
- JSON.stringify({ method: "limit", values: [1] }),
1739
- JSON.stringify({
1740
- method: "equal",
1741
- attribute: "deploymentResourceType",
1742
- values: ["site"],
1743
- }),
1744
- JSON.stringify({
1745
- method: "equal",
1746
- attribute: "deploymentResourceId",
1747
- values: [site["$id"]],
1748
- }),
1749
- JSON.stringify({
1750
- method: "equal",
1751
- attribute: "trigger",
1752
- values: ["manual"],
1753
- }),
1754
- ]);
1755
-
1756
- if (Number(res.total) === 1) {
1757
- url = res.rules[0].domain;
1758
- }
1450
+ if (database.name !== localDatabaseName) {
1451
+ await databasesService.update(databaseId, localDatabaseName);
1759
1452
 
1760
- updaterRow.update({ status: "Deployed", end: url });
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 ...`);
1761
1458
 
1762
- break;
1763
- } else if (status === "failed") {
1764
- failedDeployments.push({
1765
- name: site["name"],
1766
- $id: site["$id"],
1767
- deployment: response["$id"],
1768
- });
1769
- updaterRow.fail({ errorMessage: `Failed to deploy` });
1459
+ const localDatabaseName =
1460
+ collections.find((c: any) => c.databaseId === databaseId)
1461
+ ?.databaseName ?? databaseId;
1770
1462
 
1771
- break;
1772
- } else {
1773
- updaterRow.update({
1774
- status: "Deploying",
1775
- end: `Current status: ${status}`,
1776
- });
1777
- }
1463
+ await databasesService.create(databaseId, localDatabaseName);
1464
+ } else {
1465
+ throw err;
1466
+ }
1467
+ }
1468
+ }),
1469
+ );
1778
1470
 
1779
- pollChecks++;
1780
- await new Promise((resolve) =>
1781
- setTimeout(resolve, POLL_DEBOUNCE * 1.5),
1471
+ // Parallel collection actions
1472
+ await Promise.all(
1473
+ collections.map(async (collection: any) => {
1474
+ try {
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`,
1782
1492
  );
1783
1493
  }
1494
+ collection.remoteVersion = remoteCollection;
1495
+
1496
+ collection.isExisted = true;
1784
1497
  } catch (e: any) {
1785
- errors.push(e);
1786
- updaterRow.fail({
1787
- errorMessage:
1788
- e.message ?? "Unknown error occurred. Please try again",
1789
- });
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
+ }
1790
1516
  }
1517
+ }),
1518
+ );
1519
+
1520
+ let numberOfCollections = 0;
1521
+ // Serialize attribute actions
1522
+ for (let collection of collections) {
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
+ );
1538
+
1539
+ if (
1540
+ Array.isArray(collectionAttributes) &&
1541
+ collectionAttributes.length <= 0 &&
1542
+ Array.isArray(indexes) &&
1543
+ indexes.length <= 0
1544
+ ) {
1545
+ continue;
1546
+ }
1547
+ }
1548
+
1549
+ this.log(
1550
+ `Pushing collection ${collection.name} ( ${collection["databaseId"]} - ${collection["$id"]} ) attributes`,
1551
+ );
1552
+
1553
+ try {
1554
+ await attributes.createAttributes(
1555
+ collectionAttributes,
1556
+ collection as Collection,
1557
+ );
1558
+ } catch (e) {
1559
+ errors.push(e);
1560
+ throw e;
1791
1561
  }
1792
1562
 
1793
- updaterRow.stopSpinner();
1794
- }),
1795
- );
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
+ );
1573
+ }
1574
+
1575
+ return {
1576
+ successfullyPushed: numberOfCollections,
1577
+ errors,
1578
+ };
1579
+ }
1580
+ }
1581
+
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);
1586
+ }
1587
+
1588
+ const pushResources = async ({
1589
+ skipDeprecated = false,
1590
+ }: {
1591
+ skipDeprecated?: boolean;
1592
+ } = {}): Promise<void> => {
1593
+ if (cliConfig.all) {
1594
+ checkDeployConditions(localConfig);
1595
+
1596
+ const pushInstance = await createPushInstance();
1597
+ const config = localConfig.getProject() as ConfigType;
1598
+
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
+ };
1615
+
1616
+ if (skipDeprecated) {
1617
+ delete actions.collections;
1618
+ }
1619
+
1620
+ const answers = await inquirer.prompt(questionsPushResources);
1621
+
1622
+ const action = actions[answers.resource];
1623
+ if (action !== undefined) {
1624
+ await action();
1625
+ }
1626
+ }
1627
+ };
1628
+
1629
+ const pushSettings = async (): Promise<void> => {
1630
+ checkDeployConditions(localConfig);
1796
1631
 
1797
- Spinner.stop();
1632
+ try {
1633
+ const projectsService = await getProjectsService();
1634
+ let response = await projectsService.get(
1635
+ localConfig.getProject().projectId,
1636
+ );
1798
1637
 
1799
- failedDeployments.forEach((failed) => {
1800
- const { name, deployment, $id } = failed;
1801
- const failUrl = `${globalConfig.getEndpoint().slice(0, -3)}/console/project-${localConfig.getProject().projectId}/sites/site-${$id}/deployments/deployment-${deployment}`;
1638
+ const remoteSettings = createSettingsObject(response);
1639
+ const localSettings = localConfig.getProject().projectSettings ?? {};
1802
1640
 
1803
- error(
1804
- `Deployment of ${name} has failed. Check at ${failUrl} for more details\n`,
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
+ ),
1805
1662
  );
1806
- });
1807
1663
 
1808
- if (!asyncDeploy) {
1809
- if (successfullyPushed === 0) {
1810
- error("No sites were pushed.");
1811
- } else if (successfullyDeployed !== successfullyPushed) {
1812
- warn(
1813
- `Successfully pushed ${successfullyDeployed} of ${successfullyPushed} sites`,
1814
- );
1815
- } else {
1816
- success(`Successfully pushed ${successfullyPushed} sites.`);
1664
+ if (changes.length > 0) {
1665
+ drawTable(changes);
1666
+ if ((await getConfirmation()) !== true) {
1667
+ success(`Successfully pushed 0 project settings.`);
1668
+ return;
1669
+ }
1817
1670
  }
1818
- } else {
1819
- success(`Successfully pushed ${successfullyPushed} sites.`);
1820
- }
1671
+ } catch (e) {}
1821
1672
 
1822
- if (cliConfig.verbose) {
1823
- errors.forEach((e) => {
1824
- console.error(e);
1673
+ try {
1674
+ log("Pushing project settings ...");
1675
+
1676
+ const pushInstance = await createPushInstance();
1677
+ const config = localConfig.getProject();
1678
+
1679
+ await pushInstance.pushSettings({
1680
+ projectId: config.projectId,
1681
+ projectName: config.projectName,
1682
+ settings: config.projectSettings,
1825
1683
  });
1684
+
1685
+ success(`Successfully pushed ${chalk.bold("all")} project settings.`);
1686
+ } catch (e) {
1687
+ throw e;
1826
1688
  }
1827
1689
  };
1828
1690
 
1829
- const pushFunction = async ({
1830
- functionId,
1691
+ const pushSite = async ({
1692
+ siteId,
1831
1693
  async: asyncDeploy,
1832
1694
  code,
1833
1695
  withVariables,
1834
- }: PushFunctionOptions = {}): Promise<void> => {
1696
+ }: PushSiteOptions = {}): Promise<void> => {
1835
1697
  process.chdir(localConfig.configDirectoryPath);
1836
1698
 
1837
- const functionIds: string[] = [];
1699
+ const siteIds: string[] = [];
1838
1700
 
1839
- if (functionId) {
1840
- functionIds.push(functionId);
1701
+ if (siteId) {
1702
+ siteIds.push(siteId);
1841
1703
  } else if (cliConfig.all) {
1842
1704
  checkDeployConditions(localConfig);
1843
- const functions = localConfig.getFunctions();
1844
- functionIds.push(
1845
- ...functions.map((func: any) => {
1846
- return func.$id;
1705
+ const sites = localConfig.getSites();
1706
+ siteIds.push(
1707
+ ...sites.map((site: any) => {
1708
+ return site.$id;
1847
1709
  }),
1848
1710
  );
1849
1711
  }
1850
1712
 
1851
- if (functionIds.length <= 0) {
1852
- const answers = await inquirer.prompt(questionsPushFunctions);
1853
- if (answers.functions) {
1854
- functionIds.push(...answers.functions);
1713
+ if (siteIds.length <= 0) {
1714
+ const answers = await inquirer.prompt(questionsPushSites);
1715
+ if (answers.sites) {
1716
+ siteIds.push(...answers.sites);
1855
1717
  }
1856
1718
  }
1857
1719
 
1858
- if (functionIds.length === 0) {
1859
- log("No functions found.");
1720
+ if (siteIds.length === 0) {
1721
+ log("No sites found.");
1860
1722
  hint(
1861
- "Use 'appwrite pull functions' to synchronize existing one, or use 'appwrite init function' to create a new one.",
1723
+ `Use '${EXECUTABLE_NAME} pull sites' to synchronize existing one, or use '${EXECUTABLE_NAME} init site' to create a new one.`,
1862
1724
  );
1863
1725
  return;
1864
1726
  }
1865
1727
 
1866
- let functions = functionIds.map((id: string) => {
1867
- const functions = localConfig.getFunctions();
1868
- const func = functions.find((f: any) => f.$id === id);
1728
+ let sites = siteIds.map((id: string) => {
1729
+ const sites = localConfig.getSites();
1730
+ const site = sites.find((s: any) => s.$id === id);
1869
1731
 
1870
- if (!func) {
1871
- throw new Error("Function '" + id + "' not found.");
1732
+ if (!site) {
1733
+ throw new Error("Site '" + id + "' not found.");
1872
1734
  }
1873
1735
 
1874
- return func;
1736
+ return site;
1875
1737
  });
1876
1738
 
1877
- log("Validating functions ...");
1739
+ log("Validating sites ...");
1878
1740
  // Validation is done BEFORE pushing so the deployment process can be run in async with progress update
1879
- for (let func of functions) {
1880
- if (!func.entrypoint) {
1881
- log(`Function ${func.name} is missing an entrypoint.`);
1741
+ for (let site of sites) {
1742
+ if (!site.buildCommand) {
1743
+ log(`Site ${site.name} is missing build command.`);
1882
1744
  const answers = await inquirer.prompt(questionsGetEntrypoint);
1883
- func.entrypoint = answers.entrypoint;
1884
- localConfig.addFunction(func);
1745
+ site.buildCommand = answers.entrypoint;
1746
+ localConfig.addSite(site);
1885
1747
  }
1886
1748
  }
1887
1749
 
1888
1750
  if (
1889
1751
  !(await approveChanges(
1890
- functions,
1752
+ sites,
1891
1753
  async (args: any) => {
1892
- const functionsService = await getFunctionsService();
1893
- return await functionsService.get({ functionId: args.functionId });
1754
+ const sitesService = await getSitesService();
1755
+ return await sitesService.get({ siteId: args.siteId });
1894
1756
  },
1895
- KeysFunction,
1896
- "functionId",
1897
- "functions",
1757
+ KeysSite,
1758
+ "siteId",
1759
+ "sites",
1898
1760
  ["vars"],
1899
1761
  ))
1900
1762
  ) {
1901
1763
  return;
1902
1764
  }
1903
1765
 
1904
- log("Pushing functions ...");
1905
-
1906
- Spinner.start(false);
1907
- let successfullyPushed = 0;
1908
- let successfullyDeployed = 0;
1909
- const failedDeployments: any[] = [];
1910
- const errors: any[] = [];
1911
-
1912
- await Promise.all(
1913
- functions.map(async (func: any) => {
1914
- let response: any = {};
1915
-
1916
- const ignore = func.ignore ? "appwrite.config.json" : ".gitignore";
1917
- let functionExists = false;
1918
- let deploymentCreated = false;
1919
-
1920
- const updaterRow = new Spinner({
1921
- status: "",
1922
- resource: func.name,
1923
- id: func["$id"],
1924
- end: `Ignoring using: ${ignore}`,
1925
- });
1926
-
1927
- updaterRow.update({ status: "Getting" }).startSpinner(SPINNER_DOTS);
1928
- const functionsService = await getFunctionsService();
1929
- try {
1930
- response = await functionsService.get({ functionId: func["$id"] });
1931
- functionExists = true;
1932
- if (response.runtime !== func.runtime) {
1933
- updaterRow.fail({
1934
- errorMessage: `Runtime mismatch! (local=${func.runtime},remote=${response.runtime}) Please delete remote function or update your appwrite.config.json`,
1935
- });
1936
- return;
1937
- }
1938
-
1939
- updaterRow.update({ status: "Updating" }).replaceSpinner(SPINNER_ARC);
1940
-
1941
- response = await functionsService.update({
1942
- functionId: func["$id"],
1943
- name: func.name,
1944
- runtime: func.runtime,
1945
- execute: func.execute,
1946
- events: func.events,
1947
- schedule: func.schedule,
1948
- timeout: func.timeout,
1949
- enabled: func.enabled,
1950
- logging: func.logging,
1951
- entrypoint: func.entrypoint,
1952
- commands: func.commands,
1953
- scopes: func.scopes,
1954
- specification: func.specification,
1955
- });
1956
- } catch (e: any) {
1957
- if (Number(e.code) === 404) {
1958
- functionExists = false;
1959
- } else {
1960
- errors.push(e);
1961
- updaterRow.fail({
1962
- errorMessage: e.message ?? "General error occurs please try again",
1963
- });
1964
- return;
1965
- }
1966
- }
1967
-
1968
- if (!functionExists) {
1969
- updaterRow.update({ status: "Creating" }).replaceSpinner(SPINNER_DOTS);
1970
-
1971
- try {
1972
- response = await functionsService.create({
1973
- functionId: func.$id,
1974
- name: func.name,
1975
- runtime: func.runtime,
1976
- execute: func.execute,
1977
- events: func.events,
1978
- schedule: func.schedule,
1979
- timeout: func.timeout,
1980
- enabled: func.enabled,
1981
- logging: func.logging,
1982
- entrypoint: func.entrypoint,
1983
- commands: func.commands,
1984
- scopes: func.scopes,
1985
- specification: func.specification,
1986
- });
1987
-
1988
- let domain = "";
1989
- try {
1990
- const consoleService = await getConsoleService();
1991
- const variables = await consoleService.variables();
1992
- domain = ID.unique() + "." + variables["_APP_DOMAIN_FUNCTIONS"];
1993
- } catch (error) {
1994
- console.error("Error fetching console variables.");
1995
- throw error;
1996
- }
1997
-
1998
- try {
1999
- const proxyService = await getProxyService();
2000
- const rule = await proxyService.createFunctionRule(
2001
- domain,
2002
- func.$id,
2003
- );
2004
- } catch (error) {
2005
- console.error("Error creating function rule.");
2006
- throw error;
2007
- }
2008
-
2009
- updaterRow.update({ status: "Created" });
2010
- } catch (e: any) {
2011
- errors.push(e);
2012
- updaterRow.fail({
2013
- errorMessage: e.message ?? "General error occurs please try again",
2014
- });
2015
- return;
2016
- }
2017
- }
2018
-
2019
- if (withVariables) {
2020
- updaterRow
2021
- .update({ status: "Updating variables" })
2022
- .replaceSpinner(SPINNER_ARC);
2023
-
2024
- const functionsService = await getFunctionsService();
2025
- const { variables } = await paginate(
2026
- async (args: any) => {
2027
- return await functionsService.listVariables({
2028
- functionId: args.functionId,
2029
- });
2030
- },
2031
- {
2032
- functionId: func["$id"],
2033
- },
2034
- 100,
2035
- "variables",
2036
- );
2037
-
2038
- await Promise.all(
2039
- variables.map(async (variable: any) => {
2040
- const functionsService = await getFunctionsService();
2041
- await functionsService.deleteVariable({
2042
- functionId: func["$id"],
2043
- variableId: variable["$id"],
2044
- });
2045
- }),
2046
- );
2047
-
2048
- const envFileLocation = `${func["path"]}/.env`;
2049
- let envVariables: Array<{ key: string; value: string }> = [];
2050
- try {
2051
- if (fs.existsSync(envFileLocation)) {
2052
- const envObject = parseDotenv(
2053
- fs.readFileSync(envFileLocation, "utf8"),
2054
- );
2055
- envVariables = Object.entries(envObject || {}).map(
2056
- ([key, value]) => ({ key, value }),
2057
- );
2058
- }
2059
- } catch (error) {
2060
- // Handle parsing errors gracefully
2061
- envVariables = [];
2062
- }
2063
- await Promise.all(
2064
- envVariables.map(async (variable) => {
2065
- const functionsService = await getFunctionsService();
2066
- await functionsService.createVariable({
2067
- functionId: func["$id"],
2068
- key: variable.key,
2069
- value: variable.value,
2070
- secret: false,
2071
- });
2072
- }),
2073
- );
2074
- }
2075
-
2076
- if (code === false) {
2077
- successfullyPushed++;
2078
- successfullyDeployed++;
2079
- updaterRow.update({ status: "Pushed" });
2080
- updaterRow.stopSpinner();
2081
- return;
2082
- }
2083
-
2084
- try {
2085
- updaterRow.update({ status: "Pushing" }).replaceSpinner(SPINNER_ARC);
2086
- const functionsService = await getFunctionsService();
2087
- response = await functionsService.createDeployment({
2088
- functionId: func["$id"],
2089
- entrypoint: func.entrypoint,
2090
- commands: func.commands,
2091
- code: func.path,
2092
- activate: true,
2093
- });
2094
-
2095
- updaterRow.update({ status: "Pushed" });
2096
- deploymentCreated = true;
2097
- successfullyPushed++;
2098
- } catch (e: any) {
2099
- errors.push(e);
2100
-
2101
- switch (e.code) {
2102
- case "ENOENT":
2103
- updaterRow.fail({
2104
- errorMessage: "Not found in the current directory. Skipping...",
2105
- });
2106
- break;
2107
- default:
2108
- updaterRow.fail({
2109
- errorMessage:
2110
- e.message ?? "An unknown error occurred. Please try again.",
2111
- });
2112
- }
2113
- }
2114
-
2115
- if (deploymentCreated && !asyncDeploy) {
2116
- try {
2117
- const deploymentId = response["$id"];
2118
- updaterRow.update({
2119
- status: "Deploying",
2120
- end: "Checking deployment status...",
2121
- });
2122
- let pollChecks = 0;
2123
-
2124
- while (true) {
2125
- const functionsService = await getFunctionsService();
2126
- response = await functionsService.getDeployment({
2127
- functionId: func["$id"],
2128
- deploymentId: deploymentId,
2129
- });
2130
-
2131
- const status = response["status"];
2132
- if (status === "ready") {
2133
- successfullyDeployed++;
2134
-
2135
- let url = "";
2136
- const proxyService = await getProxyService();
2137
- const res = await proxyService.listRules([
2138
- JSON.stringify({ method: "limit", values: [1] }),
2139
- JSON.stringify({
2140
- method: "equal",
2141
- attribute: "deploymentResourceType",
2142
- values: ["function"],
2143
- }),
2144
- JSON.stringify({
2145
- method: "equal",
2146
- attribute: "deploymentResourceId",
2147
- values: [func["$id"]],
2148
- }),
2149
- JSON.stringify({
2150
- method: "equal",
2151
- attribute: "trigger",
2152
- values: ["manual"],
2153
- }),
2154
- ]);
2155
-
2156
- if (Number(res.total) === 1) {
2157
- url = res.rules[0].domain;
2158
- }
2159
-
2160
- updaterRow.update({ status: "Deployed", end: url });
2161
-
2162
- break;
2163
- } else if (status === "failed") {
2164
- failedDeployments.push({
2165
- name: func["name"],
2166
- $id: func["$id"],
2167
- deployment: response["$id"],
2168
- });
2169
- updaterRow.fail({ errorMessage: `Failed to deploy` });
2170
-
2171
- break;
2172
- } else {
2173
- updaterRow.update({
2174
- status: "Deploying",
2175
- end: `Current status: ${status}`,
2176
- });
2177
- }
2178
-
2179
- pollChecks++;
2180
- await new Promise((resolve) =>
2181
- setTimeout(resolve, POLL_DEBOUNCE * 1.5),
2182
- );
2183
- }
2184
- } catch (e: any) {
2185
- errors.push(e);
2186
- updaterRow.fail({
2187
- errorMessage:
2188
- e.message ?? "Unknown error occurred. Please try again",
2189
- });
2190
- }
2191
- }
1766
+ log("Pushing sites ...");
2192
1767
 
2193
- updaterRow.stopSpinner();
2194
- }),
2195
- );
1768
+ const pushInstance = await createPushInstance();
1769
+ const result = await pushInstance.pushSites(sites, {
1770
+ async: asyncDeploy,
1771
+ code,
1772
+ withVariables,
1773
+ });
2196
1774
 
2197
- Spinner.stop();
1775
+ const {
1776
+ successfullyPushed,
1777
+ successfullyDeployed,
1778
+ failedDeployments,
1779
+ errors,
1780
+ } = result;
2198
1781
 
2199
1782
  failedDeployments.forEach((failed) => {
2200
1783
  const { name, deployment, $id } = failed;
2201
- const failUrl = `${globalConfig.getEndpoint().slice(0, -3)}/console/project-${localConfig.getProject().projectId}/functions/function-${$id}/deployment-${deployment}`;
1784
+ const failUrl = `${globalConfig.getEndpoint().slice(0, -3)}/console/project-${localConfig.getProject().projectId}/sites/site-${$id}/deployments/deployment-${deployment}`;
2202
1785
 
2203
1786
  error(
2204
1787
  `Deployment of ${name} has failed. Check at ${failUrl} for more details\n`,
@@ -2207,16 +1790,16 @@ const pushFunction = async ({
2207
1790
 
2208
1791
  if (!asyncDeploy) {
2209
1792
  if (successfullyPushed === 0) {
2210
- error("No functions were pushed.");
1793
+ error("No sites were pushed.");
2211
1794
  } else if (successfullyDeployed !== successfullyPushed) {
2212
1795
  warn(
2213
- `Successfully pushed ${successfullyDeployed} of ${successfullyPushed} functions`,
1796
+ `Successfully pushed ${successfullyDeployed} of ${successfullyPushed} sites`,
2214
1797
  );
2215
1798
  } else {
2216
- success(`Successfully pushed ${successfullyPushed} functions.`);
1799
+ success(`Successfully pushed ${successfullyPushed} sites.`);
2217
1800
  }
2218
1801
  } else {
2219
- success(`Successfully pushed ${successfullyPushed} functions.`);
1802
+ success(`Successfully pushed ${successfullyPushed} sites.`);
2220
1803
  }
2221
1804
 
2222
1805
  if (cliConfig.verbose) {
@@ -2226,194 +1809,140 @@ const pushFunction = async ({
2226
1809
  }
2227
1810
  };
2228
1811
 
2229
- const checkAndApplyTablesDBChanges =
2230
- async (): Promise<TablesDBChangesResult> => {
2231
- log("Checking for tablesDB changes ...");
1812
+ const pushFunction = async ({
1813
+ functionId,
1814
+ async: asyncDeploy,
1815
+ code,
1816
+ withVariables,
1817
+ }: PushFunctionOptions = {}): Promise<void> => {
1818
+ process.chdir(localConfig.configDirectoryPath);
1819
+
1820
+ const functionIds: string[] = [];
2232
1821
 
2233
- const localTablesDBs = localConfig.getTablesDBs();
2234
- const { databases: remoteTablesDBs } = await paginate(
2235
- async (args: any) => {
2236
- const tablesDBService = await getTablesDBService();
2237
- return await tablesDBService.list(args.queries || []);
2238
- },
2239
- {},
2240
- 100,
2241
- "databases",
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
+ }),
2242
1831
  );
1832
+ }
2243
1833
 
2244
- if (localTablesDBs.length === 0 && remoteTablesDBs.length === 0) {
2245
- return { applied: false, resyncNeeded: false };
2246
- }
2247
-
2248
- const changes: any[] = [];
2249
- const toCreate: any[] = [];
2250
- const toUpdate: any[] = [];
2251
- const toDelete: any[] = [];
2252
-
2253
- // Check for deletions - remote DBs that aren't in local config
2254
- for (const remoteDB of remoteTablesDBs) {
2255
- const localDB = localTablesDBs.find((db: any) => db.$id === remoteDB.$id);
2256
- if (!localDB) {
2257
- toDelete.push(remoteDB);
2258
- changes.push({
2259
- id: remoteDB.$id,
2260
- action: chalk.red("deleting"),
2261
- key: "Database",
2262
- remote: remoteDB.name,
2263
- local: "(deleted locally)",
2264
- });
2265
- }
1834
+ if (functionIds.length <= 0) {
1835
+ const answers = await inquirer.prompt(questionsPushFunctions);
1836
+ if (answers.functions) {
1837
+ functionIds.push(...answers.functions);
2266
1838
  }
1839
+ }
2267
1840
 
2268
- // Check for additions and updates
2269
- for (const localDB of localTablesDBs) {
2270
- const remoteDB = remoteTablesDBs.find(
2271
- (db: any) => db.$id === localDB.$id,
2272
- );
2273
-
2274
- if (!remoteDB) {
2275
- toCreate.push(localDB);
2276
- changes.push({
2277
- id: localDB.$id,
2278
- action: chalk.green("creating"),
2279
- key: "Database",
2280
- remote: "(does not exist)",
2281
- local: localDB.name,
2282
- });
2283
- } else {
2284
- let hasChanges = false;
2285
-
2286
- if (remoteDB.name !== localDB.name) {
2287
- hasChanges = true;
2288
- changes.push({
2289
- id: localDB.$id,
2290
- action: chalk.yellow("updating"),
2291
- key: "Name",
2292
- remote: remoteDB.name,
2293
- local: localDB.name,
2294
- });
2295
- }
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
+ }
2296
1848
 
2297
- if (remoteDB.enabled !== localDB.enabled) {
2298
- hasChanges = true;
2299
- changes.push({
2300
- id: localDB.$id,
2301
- action: chalk.yellow("updating"),
2302
- key: "Enabled",
2303
- remote: remoteDB.enabled,
2304
- local: localDB.enabled,
2305
- });
2306
- }
1849
+ let functions = functionIds.map((id: string) => {
1850
+ const functions = localConfig.getFunctions();
1851
+ const func = functions.find((f: any) => f.$id === id);
2307
1852
 
2308
- if (hasChanges) {
2309
- toUpdate.push(localDB);
2310
- }
2311
- }
1853
+ if (!func) {
1854
+ throw new Error("Function '" + id + "' not found.");
2312
1855
  }
2313
1856
 
2314
- if (changes.length === 0) {
2315
- return { applied: false, resyncNeeded: false };
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);
2316
1867
  }
1868
+ }
2317
1869
 
2318
- log("Found changes in tablesDB resource:");
2319
- drawTable(changes);
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
+ }
2320
1885
 
2321
- if (toDelete.length > 0) {
2322
- console.log(
2323
- `${chalk.red("------------------------------------------------------------------")}`,
2324
- );
2325
- console.log(
2326
- `${chalk.red("| WARNING: Database deletion will also delete all related tables |")}`,
2327
- );
2328
- console.log(
2329
- `${chalk.red("------------------------------------------------------------------")}`,
2330
- );
2331
- console.log();
2332
- }
1886
+ log("Pushing functions ...");
2333
1887
 
2334
- if ((await getConfirmation()) !== true) {
2335
- return { applied: false, resyncNeeded: false };
2336
- }
1888
+ const pushInstance = await createPushInstance();
1889
+ const result = await pushInstance.pushFunctions(functions, {
1890
+ async: asyncDeploy,
1891
+ code,
1892
+ withVariables,
1893
+ });
2337
1894
 
2338
- // Apply deletions first
2339
- let needsResync = false;
2340
- for (const db of toDelete) {
2341
- try {
2342
- log(`Deleting database ${db.name} ( ${db.$id} ) ...`);
2343
- const tablesDBService = await getTablesDBService();
2344
- await tablesDBService.delete(db.$id);
2345
- success(`Deleted ${db.name} ( ${db.$id} )`);
2346
- needsResync = true;
2347
- } catch (e: any) {
2348
- error(
2349
- `Failed to delete database ${db.name} ( ${db.$id} ): ${e.message}`,
2350
- );
2351
- throw new Error(
2352
- `Database sync failed during deletion of ${db.$id}. Some changes may have been applied.`,
2353
- );
2354
- }
2355
- }
1895
+ const {
1896
+ successfullyPushed,
1897
+ successfullyDeployed,
1898
+ failedDeployments,
1899
+ errors,
1900
+ } = result;
2356
1901
 
2357
- // Apply creations
2358
- for (const db of toCreate) {
2359
- try {
2360
- log(`Creating database ${db.name} ( ${db.$id} ) ...`);
2361
- const tablesDBService = await getTablesDBService();
2362
- await tablesDBService.create(db.$id, db.name, db.enabled);
2363
- success(`Created ${db.name} ( ${db.$id} )`);
2364
- } catch (e: any) {
2365
- error(
2366
- `Failed to create database ${db.name} ( ${db.$id} ): ${e.message}`,
2367
- );
2368
- throw new Error(
2369
- `Database sync failed during creation of ${db.$id}. Some changes may have been applied.`,
2370
- );
2371
- }
2372
- }
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}`;
2373
1905
 
2374
- // Apply updates
2375
- for (const db of toUpdate) {
2376
- try {
2377
- log(`Updating database ${db.name} ( ${db.$id} ) ...`);
2378
- const tablesDBService = await getTablesDBService();
2379
- await tablesDBService.update(db.$id, db.name, db.enabled);
2380
- success(`Updated ${db.name} ( ${db.$id} )`);
2381
- } catch (e: any) {
2382
- error(
2383
- `Failed to update database ${db.name} ( ${db.$id} ): ${e.message}`,
2384
- );
2385
- throw new Error(
2386
- `Database sync failed during update of ${db.$id}. Some changes may have been applied.`,
2387
- );
2388
- }
2389
- }
1906
+ error(
1907
+ `Deployment of ${name} has failed. Check at ${failUrl} for more details\n`,
1908
+ );
1909
+ });
2390
1910
 
2391
- if (toDelete.length === 0) {
2392
- console.log();
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.`);
2393
1920
  }
1921
+ } else {
1922
+ success(`Successfully pushed ${successfullyPushed} functions.`);
1923
+ }
2394
1924
 
2395
- return { applied: true, resyncNeeded: needsResync };
2396
- };
1925
+ if (cliConfig.verbose) {
1926
+ errors.forEach((e) => {
1927
+ console.error(e);
1928
+ });
1929
+ }
1930
+ };
2397
1931
 
2398
1932
  const pushTable = async ({
2399
1933
  attempts,
2400
1934
  }: PushTableOptions = {}): Promise<void> => {
2401
1935
  const tables: any[] = [];
2402
1936
 
2403
- if (attempts) {
2404
- pollMaxDebounces = attempts;
2405
- }
2406
-
2407
- const { applied: tablesDBApplied, resyncNeeded } =
2408
- await checkAndApplyTablesDBChanges();
1937
+ const { resyncNeeded } = await checkAndApplyTablesDBChanges();
2409
1938
  if (resyncNeeded) {
2410
- log("Resyncing configuration due to tablesDB deletions ...");
1939
+ log("Resyncing configuration due to tables deletions ...");
2411
1940
 
2412
1941
  const remoteTablesDBs = (
2413
1942
  await paginate(
2414
1943
  async (args: any) => {
2415
- const tablesDBService = await getTablesDBService();
2416
- return await tablesDBService.list(args.queries || []);
1944
+ const tablesService = await getTablesDBService();
1945
+ return await tablesService.list(args.queries || []);
2417
1946
  },
2418
1947
  {},
2419
1948
  100,
@@ -2448,8 +1977,8 @@ const pushTable = async ({
2448
1977
  try {
2449
1978
  const { tables: remoteTables } = await paginate(
2450
1979
  async (args: any) => {
2451
- const tablesDBService = await getTablesDBService();
2452
- return await tablesDBService.listTables(
1980
+ const tablesService = await getTablesDBService();
1981
+ return await tablesService.listTables(
2453
1982
  args.databaseId,
2454
1983
  args.queries || [],
2455
1984
  );
@@ -2496,8 +2025,8 @@ const pushTable = async ({
2496
2025
  log(
2497
2026
  `Deleting table ${table.name} ( ${table.$id} ) from database ${table.databaseName} ...`,
2498
2027
  );
2499
- const tablesDBService = await getTablesDBService();
2500
- await tablesDBService.deleteTable(table.databaseId, table.$id);
2028
+ const tablesService = await getTablesDBService();
2029
+ await tablesService.deleteTable(table.databaseId, table.$id);
2501
2030
  success(`Deleted ${table.name} ( ${table.$id} )`);
2502
2031
  } catch (e: any) {
2503
2032
  error(
@@ -2528,7 +2057,7 @@ const pushTable = async ({
2528
2057
  if (tables.length === 0) {
2529
2058
  log("No tables found.");
2530
2059
  hint(
2531
- "Use 'appwrite pull tables' to synchronize existing one, or use 'appwrite init table' to create a new one.",
2060
+ `Use '${EXECUTABLE_NAME} pull tables' to synchronize existing one, or use '${EXECUTABLE_NAME} init table' to create a new one.`,
2532
2061
  );
2533
2062
  return;
2534
2063
  }
@@ -2537,8 +2066,8 @@ const pushTable = async ({
2537
2066
  !(await approveChanges(
2538
2067
  tables,
2539
2068
  async (args: any) => {
2540
- const tablesDBService = await getTablesDBService();
2541
- return await tablesDBService.getTable(args.databaseId, args.tableId);
2069
+ const tablesService = await getTablesDBService();
2070
+ return await tablesService.getTable(args.databaseId, args.tableId);
2542
2071
  },
2543
2072
  KeysTable,
2544
2073
  "tableId",
@@ -2550,129 +2079,31 @@ const pushTable = async ({
2550
2079
  ) {
2551
2080
  return;
2552
2081
  }
2553
- let tablesChanged = new Set();
2554
-
2555
- // Parallel tables actions
2556
- await Promise.all(
2557
- tables.map(async (table: any) => {
2558
- try {
2559
- const tablesDBService = await getTablesDBService();
2560
- const remoteTable = await tablesDBService.getTable(
2561
- table["databaseId"],
2562
- table["$id"],
2563
- );
2564
-
2565
- const changes: string[] = [];
2566
- if (remoteTable.name !== table.name) changes.push("name");
2567
- if (remoteTable.rowSecurity !== table.rowSecurity)
2568
- changes.push("rowSecurity");
2569
- if (remoteTable.enabled !== table.enabled) changes.push("enabled");
2570
- if (
2571
- JSON.stringify(remoteTable["$permissions"]) !==
2572
- JSON.stringify(table["$permissions"])
2573
- )
2574
- changes.push("permissions");
2575
-
2576
- if (changes.length > 0) {
2577
- await tablesDBService.updateTable(
2578
- table["databaseId"],
2579
- table["$id"],
2580
- table.name,
2581
- table.rowSecurity,
2582
- table["$permissions"],
2583
- );
2584
-
2585
- success(
2586
- `Updated ${table.name} ( ${table["$id"]} ) - ${changes.join(", ")}`,
2587
- );
2588
- tablesChanged.add(table["$id"]);
2589
- }
2590
- table.remoteVersion = remoteTable;
2591
-
2592
- table.isExisted = true;
2593
- } catch (e: any) {
2594
- if (Number(e.code) === 404) {
2595
- log(
2596
- `Table ${table.name} does not exist in the project. Creating ... `,
2597
- );
2598
- const tablesDBService = await getTablesDBService();
2599
- await tablesDBService.createTable(
2600
- table["databaseId"],
2601
- table["$id"],
2602
- table.name,
2603
- table.rowSecurity,
2604
- table["$permissions"],
2605
- );
2606
-
2607
- success(`Created ${table.name} ( ${table["$id"]} )`);
2608
- tablesChanged.add(table["$id"]);
2609
- } else {
2610
- throw e;
2611
- }
2612
- }
2613
- }),
2614
- );
2615
-
2616
- // Serialize attribute actions
2617
- for (let table of tables) {
2618
- let columns = table.columns;
2619
- let indexes = table.indexes;
2620
2082
 
2621
- if (table.isExisted) {
2622
- columns = await attributesToCreate(
2623
- table.remoteVersion.columns,
2624
- table.columns,
2625
- table,
2626
- );
2627
- indexes = await attributesToCreate(
2628
- table.remoteVersion.indexes,
2629
- table.indexes,
2630
- table,
2631
- true,
2632
- );
2633
-
2634
- if (
2635
- Array.isArray(columns) &&
2636
- columns.length <= 0 &&
2637
- Array.isArray(indexes) &&
2638
- indexes.length <= 0
2639
- ) {
2640
- continue;
2641
- }
2642
- }
2083
+ log("Pushing tables ...");
2643
2084
 
2644
- log(
2645
- `Pushing table ${table.name} ( ${table["databaseId"]} - ${table["$id"]} ) attributes`,
2646
- );
2085
+ const pushInstance = await createPushInstance();
2086
+ const result = await pushInstance.pushTables(tables, { attempts });
2647
2087
 
2648
- try {
2649
- await createColumns(columns, table);
2650
- } catch (e) {
2651
- throw e;
2652
- }
2088
+ const { successfullyPushed, errors } = result;
2653
2089
 
2654
- try {
2655
- await createIndexes(indexes, table);
2656
- } catch (e) {
2657
- throw e;
2658
- }
2659
- tablesChanged.add(table["$id"]);
2660
- success(`Successfully pushed ${table.name} ( ${table["$id"]} )`);
2090
+ if (successfullyPushed === 0) {
2091
+ error("No tables were pushed.");
2092
+ } else {
2093
+ success(`Successfully pushed ${successfullyPushed} tables.`);
2661
2094
  }
2662
2095
 
2663
- success(`Successfully pushed ${tablesChanged.size} tables`);
2096
+ if (cliConfig.verbose) {
2097
+ errors.forEach((e) => console.error(e));
2098
+ }
2664
2099
  };
2665
2100
 
2666
- const pushCollection = async ({ attempts }): Promise<void> => {
2101
+ const pushCollection = async (): Promise<void> => {
2667
2102
  warn(
2668
- "appwrite push collection has been deprecated. Please consider using 'appwrite push tables' instead",
2103
+ `${EXECUTABLE_NAME} push collection has been deprecated. Please consider using '${EXECUTABLE_NAME} push tables' instead`,
2669
2104
  );
2670
2105
  const collections: any[] = [];
2671
2106
 
2672
- if (attempts) {
2673
- pollMaxDebounces = attempts;
2674
- }
2675
-
2676
2107
  if (cliConfig.all) {
2677
2108
  checkDeployConditions(localConfig);
2678
2109
  collections.push(...localConfig.getCollections());
@@ -2693,42 +2124,16 @@ const pushCollection = async ({ attempts }): Promise<void> => {
2693
2124
  if (collections.length === 0) {
2694
2125
  log("No collections found.");
2695
2126
  hint(
2696
- "Use 'appwrite pull collections' to synchronize existing one, or use 'appwrite init collection' to create a new one.",
2127
+ `Use '${EXECUTABLE_NAME} pull collections' to synchronize existing one, or use '${EXECUTABLE_NAME} init collection' to create a new one.`,
2697
2128
  );
2698
2129
  return;
2699
2130
  }
2700
2131
 
2701
- const databases = Array.from(
2702
- new Set(collections.map((collection: any) => collection["databaseId"])),
2703
- );
2704
-
2705
- // Parallel db actions
2706
- await Promise.all(
2707
- databases.map(async (databaseId: any) => {
2708
- const localDatabase = localConfig.getDatabase(databaseId);
2709
-
2710
- const databasesService = await getDatabasesService();
2711
- try {
2712
- const database = await databasesService.get(databaseId);
2713
-
2714
- if (database.name !== (localDatabase.name ?? databaseId)) {
2715
- await databasesService.update(
2716
- databaseId,
2717
- localDatabase.name ?? databaseId,
2718
- );
2719
-
2720
- success(`Updated ${localDatabase.name} ( ${databaseId} ) name`);
2721
- }
2722
- } catch (err) {
2723
- log(`Database ${databaseId} not found. Creating it now ...`);
2724
-
2725
- await databasesService.create(
2726
- databaseId,
2727
- localDatabase.name ?? databaseId,
2728
- );
2729
- }
2730
- }),
2731
- );
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
+ });
2732
2137
 
2733
2138
  if (
2734
2139
  !(await approveChanges(
@@ -2750,101 +2155,26 @@ const pushCollection = async ({ attempts }): Promise<void> => {
2750
2155
  ) {
2751
2156
  return;
2752
2157
  }
2753
- // Parallel collection actions
2754
- await Promise.all(
2755
- collections.map(async (collection: any) => {
2756
- try {
2757
- const databasesService = await getDatabasesService();
2758
- const remoteCollection = await databasesService.getCollection(
2759
- collection["databaseId"],
2760
- collection["$id"],
2761
- );
2762
-
2763
- if (remoteCollection.name !== collection.name) {
2764
- await databasesService.updateCollection(
2765
- collection["databaseId"],
2766
- collection["$id"],
2767
- collection.name,
2768
- );
2769
-
2770
- success(`Updated ${collection.name} ( ${collection["$id"]} ) name`);
2771
- }
2772
- collection.remoteVersion = remoteCollection;
2773
-
2774
- collection.isExisted = true;
2775
- } catch (e: any) {
2776
- if (Number(e.code) === 404) {
2777
- log(
2778
- `Collection ${collection.name} does not exist in the project. Creating ... `,
2779
- );
2780
- const databasesService = await getDatabasesService();
2781
- await databasesService.createCollection(
2782
- collection["databaseId"],
2783
- collection["$id"],
2784
- collection.name,
2785
- collection.documentSecurity,
2786
- collection["$permissions"],
2787
- );
2788
- } else {
2789
- throw e;
2790
- }
2791
- }
2792
- }),
2793
- );
2794
- let numberOfCollections = 0;
2795
- // Serialize attribute actions
2796
- for (let collection of collections) {
2797
- let attributes = collection.attributes;
2798
- let indexes = collection.indexes;
2799
-
2800
- if (collection.isExisted) {
2801
- attributes = await attributesToCreate(
2802
- collection.remoteVersion.attributes,
2803
- collection.attributes,
2804
- collection,
2805
- );
2806
- indexes = await attributesToCreate(
2807
- collection.remoteVersion.indexes,
2808
- collection.indexes,
2809
- collection,
2810
- true,
2811
- );
2812
2158
 
2813
- if (
2814
- Array.isArray(attributes) &&
2815
- attributes.length <= 0 &&
2816
- Array.isArray(indexes) &&
2817
- indexes.length <= 0
2818
- ) {
2819
- continue;
2820
- }
2821
- }
2159
+ log("Pushing collections ...");
2822
2160
 
2823
- log(
2824
- `Pushing collection ${collection.name} ( ${collection["databaseId"]} - ${collection["$id"]} ) attributes`,
2825
- );
2161
+ const pushInstance = await createPushInstance();
2162
+ const result = await pushInstance.pushCollections(collections);
2826
2163
 
2827
- try {
2828
- await createAttributes(attributes, collection);
2829
- } catch (e) {
2830
- throw e;
2831
- }
2164
+ const { successfullyPushed, errors } = result;
2832
2165
 
2833
- try {
2834
- await createIndexes(indexes, collection);
2835
- } catch (e) {
2836
- throw e;
2837
- }
2838
- numberOfCollections++;
2839
- success(`Successfully pushed ${collection.name} ( ${collection["$id"]} )`);
2166
+ if (successfullyPushed === 0) {
2167
+ error("No collections were pushed.");
2168
+ } else {
2169
+ success(`Successfully pushed ${successfullyPushed} collections.`);
2840
2170
  }
2841
2171
 
2842
- success(`Successfully pushed ${numberOfCollections} collections`);
2172
+ if (cliConfig.verbose) {
2173
+ errors.forEach((e) => console.error(e));
2174
+ }
2843
2175
  };
2844
2176
 
2845
2177
  const pushBucket = async (): Promise<void> => {
2846
- let response: any = {};
2847
-
2848
2178
  let bucketIds: string[] = [];
2849
2179
  const configBuckets = localConfig.getBuckets();
2850
2180
 
@@ -2863,7 +2193,7 @@ const pushBucket = async (): Promise<void> => {
2863
2193
  if (bucketIds.length === 0) {
2864
2194
  log("No buckets found.");
2865
2195
  hint(
2866
- "Use 'appwrite pull buckets' to synchronize existing one, or use 'appwrite init bucket' to create a new one.",
2196
+ `Use '${EXECUTABLE_NAME} pull buckets' to synchronize existing one, or use '${EXECUTABLE_NAME} init bucket' to create a new one.`,
2867
2197
  );
2868
2198
  return;
2869
2199
  }
@@ -2892,55 +2222,23 @@ const pushBucket = async (): Promise<void> => {
2892
2222
 
2893
2223
  log("Pushing buckets ...");
2894
2224
 
2895
- for (let bucket of buckets) {
2896
- log(`Pushing bucket ${chalk.bold(bucket["name"])} ...`);
2225
+ const pushInstance = await createPushInstance();
2226
+ const result = await pushInstance.pushBuckets(buckets);
2897
2227
 
2898
- const storageService = await getStorageService();
2899
- try {
2900
- response = await storageService.getBucket(bucket["$id"]);
2901
-
2902
- await storageService.updateBucket(
2903
- bucket["$id"],
2904
- bucket.name,
2905
- bucket["$permissions"],
2906
- bucket.fileSecurity,
2907
- bucket.enabled,
2908
- bucket.maximumFileSize,
2909
- bucket.allowedFileExtensions,
2910
- bucket.encryption,
2911
- bucket.antivirus,
2912
- bucket.compression,
2913
- );
2914
- } catch (e: any) {
2915
- if (Number(e.code) === 404) {
2916
- log(
2917
- `Bucket ${bucket.name} does not exist in the project. Creating ... `,
2918
- );
2228
+ const { successfullyPushed, errors } = result;
2919
2229
 
2920
- response = await storageService.createBucket(
2921
- bucket["$id"],
2922
- bucket.name,
2923
- bucket["$permissions"],
2924
- bucket.fileSecurity,
2925
- bucket.enabled,
2926
- bucket.maximumFileSize,
2927
- bucket.allowedFileExtensions,
2928
- bucket.compression,
2929
- bucket.encryption,
2930
- bucket.antivirus,
2931
- );
2932
- } else {
2933
- throw e;
2934
- }
2935
- }
2230
+ if (successfullyPushed === 0) {
2231
+ error("No buckets were pushed.");
2232
+ } else {
2233
+ success(`Successfully pushed ${successfullyPushed} buckets.`);
2936
2234
  }
2937
2235
 
2938
- success(`Successfully pushed ${buckets.length} buckets.`);
2236
+ if (cliConfig.verbose) {
2237
+ errors.forEach((e) => console.error(e));
2238
+ }
2939
2239
  };
2940
2240
 
2941
2241
  const pushTeam = async (): Promise<void> => {
2942
- let response: any = {};
2943
-
2944
2242
  let teamIds: string[] = [];
2945
2243
  const configTeams = localConfig.getTeams();
2946
2244
 
@@ -2959,7 +2257,7 @@ const pushTeam = async (): Promise<void> => {
2959
2257
  if (teamIds.length === 0) {
2960
2258
  log("No teams found.");
2961
2259
  hint(
2962
- "Use 'appwrite pull teams' to synchronize existing one, or use 'appwrite init team' to create a new one.",
2260
+ `Use '${EXECUTABLE_NAME} pull teams' to synchronize existing one, or use '${EXECUTABLE_NAME} init team' to create a new one.`,
2963
2261
  );
2964
2262
  return;
2965
2263
  }
@@ -2988,31 +2286,23 @@ const pushTeam = async (): Promise<void> => {
2988
2286
 
2989
2287
  log("Pushing teams ...");
2990
2288
 
2991
- for (let team of teams) {
2992
- log(`Pushing team ${chalk.bold(team["name"])} ...`);
2993
-
2994
- const teamsService = await getTeamsService();
2995
- try {
2996
- response = await teamsService.get(team["$id"]);
2289
+ const pushInstance = await createPushInstance();
2290
+ const result = await pushInstance.pushTeams(teams);
2997
2291
 
2998
- await teamsService.updateName(team["$id"], team.name);
2999
- } catch (e: any) {
3000
- if (Number(e.code) === 404) {
3001
- log(`Team ${team.name} does not exist in the project. Creating ... `);
2292
+ const { successfullyPushed, errors } = result;
3002
2293
 
3003
- response = await teamsService.create(team["$id"], team.name);
3004
- } else {
3005
- throw e;
3006
- }
3007
- }
2294
+ if (successfullyPushed === 0) {
2295
+ error("No teams were pushed.");
2296
+ } else {
2297
+ success(`Successfully pushed ${successfullyPushed} teams.`);
3008
2298
  }
3009
2299
 
3010
- success(`Successfully pushed ${teams.length} teams.`);
2300
+ if (cliConfig.verbose) {
2301
+ errors.forEach((e) => console.error(e));
2302
+ }
3011
2303
  };
3012
2304
 
3013
2305
  const pushMessagingTopic = async (): Promise<void> => {
3014
- let response: any = {};
3015
-
3016
2306
  let topicsIds: string[] = [];
3017
2307
  const configTopics = localConfig.getMessagingTopics();
3018
2308
 
@@ -3031,7 +2321,7 @@ const pushMessagingTopic = async (): Promise<void> => {
3031
2321
  if (topicsIds.length === 0) {
3032
2322
  log("No topics found.");
3033
2323
  hint(
3034
- "Use 'appwrite pull topics' to synchronize existing one, or use 'appwrite init topic' to create a new one.",
2324
+ `Use '${EXECUTABLE_NAME} pull topics' to synchronize existing one, or use '${EXECUTABLE_NAME} init topic' to create a new one.`,
3035
2325
  );
3036
2326
  return;
3037
2327
  }
@@ -3060,37 +2350,20 @@ const pushMessagingTopic = async (): Promise<void> => {
3060
2350
 
3061
2351
  log("Pushing topics ...");
3062
2352
 
3063
- for (let topic of topics) {
3064
- log(`Pushing topic ${chalk.bold(topic["name"])} ...`);
3065
-
3066
- const messagingService = await getMessagingService();
3067
- try {
3068
- response = await messagingService.getTopic(topic["$id"]);
3069
- log(`Topic ${topic.name} ( ${topic["$id"]} ) already exists.`);
2353
+ const pushInstance = await createPushInstance();
2354
+ const result = await pushInstance.pushMessagingTopics(topics);
3070
2355
 
3071
- await messagingService.updateTopic(
3072
- topic["$id"],
3073
- topic.name,
3074
- topic.subscribe,
3075
- );
3076
- } catch (e: any) {
3077
- if (Number(e.code) === 404) {
3078
- log(`Topic ${topic.name} does not exist in the project. Creating ... `);
3079
-
3080
- response = await messagingService.createTopic(
3081
- topic["$id"],
3082
- topic.name,
3083
- topic.subscribe,
3084
- );
2356
+ const { successfullyPushed, errors } = result;
3085
2357
 
3086
- success(`Created ${topic.name} ( ${topic["$id"]} )`);
3087
- } else {
3088
- throw e;
3089
- }
3090
- }
2358
+ if (successfullyPushed === 0) {
2359
+ error("No topics were pushed.");
2360
+ } else {
2361
+ success(`Successfully pushed ${successfullyPushed} topics.`);
3091
2362
  }
3092
2363
 
3093
- success(`Successfully pushed ${topics.length} topics.`);
2364
+ if (cliConfig.verbose) {
2365
+ errors.forEach((e) => console.error(e));
2366
+ }
3094
2367
  };
3095
2368
 
3096
2369
  export const push = new Command("push")
@@ -3173,11 +2446,11 @@ push
3173
2446
  .action(actionRunner(pushMessagingTopic));
3174
2447
 
3175
2448
  export const deploy = new Command("deploy")
3176
- .description("Removed. Use appwrite push instead")
2449
+ .description(`Removed. Use ${EXECUTABLE_NAME} push instead`)
3177
2450
  .action(
3178
2451
  actionRunner(async () => {
3179
2452
  warn(
3180
- "appwrite deploy has been removed. Please use 'appwrite push' instead",
2453
+ `${EXECUTABLE_NAME} deploy has been removed. Please use '${EXECUTABLE_NAME} push' instead`,
3181
2454
  );
3182
2455
  }),
3183
2456
  );