openship 0.4.5 → 0.4.7

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 (314) hide show
  1. package/README.md +2 -24
  2. package/dist/bare-2HYDXQEH.js +23 -0
  3. package/dist/{bowser-L6PVH37V.js → bowser-O3AEA6AN.js} +3 -1
  4. package/dist/{chunk-YFLQGDN2.js → chunk-4TJC4JZQ.js} +18 -9
  5. package/dist/{chunk-JXQCFTDV.js → chunk-4Z5QO7N6.js} +4 -2
  6. package/dist/{chunk-JJVSOE4I.js → chunk-5MZ624VX.js} +2 -0
  7. package/dist/{chunk-TDFK6ISF.js → chunk-6APIPIJY.js} +6 -4
  8. package/dist/{chunk-3LCRNQ2C.js → chunk-6APULWSU.js} +151 -133
  9. package/dist/{chunk-FTBXJ2VA.js → chunk-6B5YCFFV.js} +6 -4
  10. package/dist/{chunk-MDFYN2QH.js → chunk-AVIB7CT7.js} +3 -1
  11. package/dist/{chunk-W7KGHYLV.js → chunk-DYPH4HCI.js} +2 -0
  12. package/dist/{chunk-5WI7ZXOC.js → chunk-EI575GHN.js} +4 -2
  13. package/dist/{chunk-Z6EKX6CX.js → chunk-FPRHYBY2.js} +2 -0
  14. package/dist/{chunk-FZJX6XP7.js → chunk-FRNAR33O.js} +4 -2
  15. package/dist/{chunk-XRWV5ZNR.js → chunk-FXTBHTWY.js} +360 -42
  16. package/dist/chunk-ILFETZOM.js +131 -0
  17. package/dist/{chunk-E3ZPCNPU.js → chunk-IMBD5PZ5.js} +251 -105
  18. package/dist/{chunk-VJTXRCTT.js → chunk-IPYPMI3S.js} +10 -6
  19. package/dist/{chunk-PARRPUXN.js → chunk-IXCHURVK.js} +5 -3
  20. package/dist/{chunk-YJAS5RQX.js → chunk-JYGD5WSN.js} +2 -0
  21. package/dist/{chunk-JWBXYO24.js → chunk-KI2EY3WB.js} +2 -0
  22. package/dist/{chunk-YVXF6GGR.js → chunk-L2VOGB2X.js} +5 -104
  23. package/dist/{chunk-K5OH5U7N.js → chunk-MZ2WEHHY.js} +5 -3
  24. package/dist/{chunk-SJBR527P.js → chunk-O6ZZF52V.js} +3 -1
  25. package/dist/{chunk-VLPJLXXB.js → chunk-OWED2GRM.js} +3 -1
  26. package/dist/{chunk-C4FOQJ63.js → chunk-OXWDY22G.js} +4 -2
  27. package/dist/{chunk-NPVJYR7M.js → chunk-Q2CIRAOK.js} +2 -0
  28. package/dist/chunk-R5BHFFAV.js +282 -0
  29. package/dist/{chunk-JYAGTMSJ.js → chunk-RPHRPFEH.js} +2 -0
  30. package/dist/{chunk-R74UB4X4.js → chunk-SJIFLLX4.js} +397 -85
  31. package/dist/{chunk-HYL4QYZM.js → chunk-TGIINPVM.js} +13 -2
  32. package/dist/{chunk-SYCZE6FQ.js → chunk-TYVME3XD.js} +7 -1
  33. package/dist/{chunk-UMYQALBH.js → chunk-UXWAVM4C.js} +1788 -1484
  34. package/dist/{chunk-HDV4NSA3.js → chunk-V6AN6XLZ.js} +16 -6
  35. package/dist/{chunk-3YCMJRDZ.js → chunk-VDMQJHPI.js} +36 -15
  36. package/dist/{chunk-RZPDJC2J.js → chunk-VFINNTUL.js} +20 -1
  37. package/dist/{chunk-65KKUJBR.js → chunk-VWPEKOH6.js} +4 -2
  38. package/dist/{chunk-GHXGCANU.js → chunk-W73X43R7.js} +4 -2
  39. package/dist/{chunk-7EYFCKZX.js → chunk-XFCDYZUQ.js} +8 -6
  40. package/dist/cloud-2KXREXMO.js +30 -0
  41. package/dist/{cloud-7IGG45CF.js → cloud-7QYOGF7F.js} +4 -2
  42. package/dist/detect-2XSCFFTF.js +44 -0
  43. package/dist/{dist-NQB6QGX3.js → dist-IQJ6H6UZ.js} +4 -2
  44. package/dist/{dist-es-DCR6UJMP.js → dist-es-5DY7K32L.js} +7 -5
  45. package/dist/{dist-es-TYYZ5KEF.js → dist-es-JSTDUS7Q.js} +7 -5
  46. package/dist/{dist-es-7EZIM4JU.js → dist-es-OSNJBLXB.js} +6 -4
  47. package/dist/{dist-es-6RSPR56Q.js → dist-es-RYHIPOTN.js} +15 -13
  48. package/dist/{dist-es-33Z3Y6VE.js → dist-es-VNKHQVIB.js} +5 -3
  49. package/dist/{dist-es-D6QQVWRZ.js → dist-es-WV4AMOII.js} +9 -7
  50. package/dist/{dist-es-GMEQINIE.js → dist-es-X3B5QYKB.js} +7 -5
  51. package/dist/docker-LODK5Y2S.js +16 -0
  52. package/dist/docker-edge-executor-VHW3JPJS.js +13 -0
  53. package/dist/edge-import-ZQ4Y5HEA.js +45 -0
  54. package/dist/ensure-container-edge-ED25XL3Z.js +31 -0
  55. package/dist/{event-streams-G7F6Q7QY.js → event-streams-EHRAKL75.js} +5 -3
  56. package/dist/{executor-O5NR7LMP.js → executor-YPT6RH6O.js} +9 -7
  57. package/dist/index.d.ts +2 -1
  58. package/dist/index.js +279 -245
  59. package/dist/{loadSso-LH753OSJ.js → loadSso-46FQRIZS.js} +11 -9
  60. package/dist/local-executor-V7U5VOZW.js +12 -0
  61. package/dist/nginx-XWJAOGS4.js +24 -0
  62. package/dist/{noop-6KC6HRDB.js → noop-FMMFG2EZ.js} +4 -2
  63. package/dist/{openresty-lua-KFCOJAG4.js → openresty-lua-4WLGDSER.js} +11 -3
  64. package/dist/server/engine/ChangeLog +2289 -0
  65. package/dist/server/engine/Documentations +5 -0
  66. package/dist/server/engine/LICENSE +674 -0
  67. package/dist/server/engine/README.md +34 -0
  68. package/dist/server/engine/conf/amavisd +150 -0
  69. package/dist/server/engine/conf/clamav +71 -0
  70. package/dist/server/engine/conf/core +632 -0
  71. package/dist/server/engine/conf/dovecot +151 -0
  72. package/dist/server/engine/conf/fail2ban +72 -0
  73. package/dist/server/engine/conf/global +698 -0
  74. package/dist/server/engine/conf/iredapd +52 -0
  75. package/dist/server/engine/conf/logwatch +28 -0
  76. package/dist/server/engine/conf/postfix +97 -0
  77. package/dist/server/engine/conf/postgresql +93 -0
  78. package/dist/server/engine/conf/spamassassin +39 -0
  79. package/dist/server/engine/dialog/config_via_dialog.sh +258 -0
  80. package/dist/server/engine/dialog/optional_components.sh +122 -0
  81. package/dist/server/engine/dialog/pgsql_config.sh +52 -0
  82. package/dist/server/engine/dialog/virtual_domain_config.sh +94 -0
  83. package/dist/server/engine/functions/amavisd.sh +267 -0
  84. package/dist/server/engine/functions/backend.sh +85 -0
  85. package/dist/server/engine/functions/clamav.sh +100 -0
  86. package/dist/server/engine/functions/cleanup.sh +393 -0
  87. package/dist/server/engine/functions/dovecot.sh +583 -0
  88. package/dist/server/engine/functions/fail2ban.sh +209 -0
  89. package/dist/server/engine/functions/iredapd.sh +356 -0
  90. package/dist/server/engine/functions/optional_components.sh +24 -0
  91. package/dist/server/engine/functions/packages.sh +522 -0
  92. package/dist/server/engine/functions/packages_freebsd.sh +179 -0
  93. package/dist/server/engine/functions/postfix.sh +457 -0
  94. package/dist/server/engine/functions/postgresql.sh +239 -0
  95. package/dist/server/engine/functions/spamassassin.sh +49 -0
  96. package/dist/server/engine/functions/system_accounts.sh +128 -0
  97. package/dist/server/engine/iRedMail.sh +144 -0
  98. package/dist/server/engine/pkgs/get_all.sh +338 -0
  99. package/dist/server/engine/pkgs/pkgs.freebsd.sha256 +3 -0
  100. package/dist/server/engine/pkgs/pkgs.openbsd.sha256 +6 -0
  101. package/dist/server/engine/pkgs/pkgs.sha256 +1 -0
  102. package/dist/server/engine/samples/amavisd/amavisd.conf +677 -0
  103. package/dist/server/engine/samples/amavisd/amavisd.mysql +278 -0
  104. package/dist/server/engine/samples/amavisd/amavisd.pgsql +253 -0
  105. package/dist/server/engine/samples/amavisd/amavisd.schema +942 -0
  106. package/dist/server/engine/samples/amavisd/default_spam_policy.sql +46 -0
  107. package/dist/server/engine/samples/dovecot/dovecot-2.4-mariadb.conf +758 -0
  108. package/dist/server/engine/samples/dovecot/dovecot-2.4-openldap.conf +767 -0
  109. package/dist/server/engine/samples/dovecot/dovecot-2.4-pgsql.conf +762 -0
  110. package/dist/server/engine/samples/dovecot/dovecot-last-login.conf +50 -0
  111. package/dist/server/engine/samples/dovecot/dovecot-ldap.conf +21 -0
  112. package/dist/server/engine/samples/dovecot/dovecot-share-folder.conf +22 -0
  113. package/dist/server/engine/samples/dovecot/dovecot-sql.conf +28 -0
  114. package/dist/server/engine/samples/dovecot/dovecot-used-quota.conf +13 -0
  115. package/dist/server/engine/samples/dovecot/dovecot.conf +484 -0
  116. package/dist/server/engine/samples/dovecot/dovecot.sieve +7 -0
  117. package/dist/server/engine/samples/dovecot/dovecot2-quota-warning.sh +38 -0
  118. package/dist/server/engine/samples/dovecot/sql/imap_share_folder.mysql +16 -0
  119. package/dist/server/engine/samples/dovecot/sql/last_login.mysql +12 -0
  120. package/dist/server/engine/samples/dovecot/sql/used_quota.mysql +26 -0
  121. package/dist/server/engine/samples/dovecot/systemd/override.conf +3 -0
  122. package/dist/server/engine/samples/fail2ban/action.d/banned_db.conf +9 -0
  123. package/dist/server/engine/samples/fail2ban/bin/fail2ban_banned_db +268 -0
  124. package/dist/server/engine/samples/fail2ban/fail2ban.local +17 -0
  125. package/dist/server/engine/samples/fail2ban/filter.d/dovecot.iredmail.conf +5 -0
  126. package/dist/server/engine/samples/fail2ban/filter.d/postfix-pregreet.iredmail.conf +16 -0
  127. package/dist/server/engine/samples/fail2ban/filter.d/postfix.iredmail.conf +18 -0
  128. package/dist/server/engine/samples/fail2ban/filter.d/roundcube.iredmail.conf +9 -0
  129. package/dist/server/engine/samples/fail2ban/jail.d/dovecot.local +8 -0
  130. package/dist/server/engine/samples/fail2ban/jail.d/nginx-http-auth.local +8 -0
  131. package/dist/server/engine/samples/fail2ban/jail.d/postfix-pregreet.local +9 -0
  132. package/dist/server/engine/samples/fail2ban/jail.d/postfix.local +8 -0
  133. package/dist/server/engine/samples/fail2ban/jail.d/roundcube.local +9 -0
  134. package/dist/server/engine/samples/fail2ban/jail.d/sogo.local +8 -0
  135. package/dist/server/engine/samples/fail2ban/jail.d/sshd.local +8 -0
  136. package/dist/server/engine/samples/fail2ban/jail.local +6 -0
  137. package/dist/server/engine/samples/fail2ban/openbsd/rc +27 -0
  138. package/dist/server/engine/samples/fail2ban/sql/fail2ban.mysql +49 -0
  139. package/dist/server/engine/samples/fail2ban/sql/fail2ban.pgsql +48 -0
  140. package/dist/server/engine/samples/firewall/firewalld/services/ssh.xml +6 -0
  141. package/dist/server/engine/samples/firewall/firewalld/zones/iredmail.xml +15 -0
  142. package/dist/server/engine/samples/firewall/iptables/ip6tables.init.debian +362 -0
  143. package/dist/server/engine/samples/firewall/iptables/ip6tables.rules +53 -0
  144. package/dist/server/engine/samples/firewall/iptables/iptables.init.debian +363 -0
  145. package/dist/server/engine/samples/firewall/iptables/iptables.rules +54 -0
  146. package/dist/server/engine/samples/firewall/nftables.conf +58 -0
  147. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/dovecot.conf +3 -0
  148. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/iredapd.conf +1 -0
  149. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/mlmmjadmin.conf +1 -0
  150. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/php-fpm.conf +2 -0
  151. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/slapd.conf +1 -0
  152. package/dist/server/engine/samples/freebsd/syslog.d/dovecot.conf +2 -0
  153. package/dist/server/engine/samples/freebsd/syslog.d/fail2ban.conf +2 -0
  154. package/dist/server/engine/samples/freebsd/syslog.d/iredapd.conf +2 -0
  155. package/dist/server/engine/samples/freebsd/syslog.d/mlmmjadmin.conf +2 -0
  156. package/dist/server/engine/samples/freebsd/syslog.d/php-fpm.conf +2 -0
  157. package/dist/server/engine/samples/freebsd/syslog.d/slapd.conf +2 -0
  158. package/dist/server/engine/samples/iredmail/iredmail.mysql +491 -0
  159. package/dist/server/engine/samples/iredmail/iredmail.pgsql +465 -0
  160. package/dist/server/engine/samples/iredmail/iredmail.schema +456 -0
  161. package/dist/server/engine/samples/iredmail/used_quota_triggers_dovecot_2.3.pgsql +40 -0
  162. package/dist/server/engine/samples/iredmail/used_quota_triggers_dovecot_2.4.pgsql +18 -0
  163. package/dist/server/engine/samples/logrotate/dovecot +13 -0
  164. package/dist/server/engine/samples/logrotate/iredapd +13 -0
  165. package/dist/server/engine/samples/logrotate/mlmmjadmin +13 -0
  166. package/dist/server/engine/samples/logrotate/openldap +19 -0
  167. package/dist/server/engine/samples/logrotate/php-fpm +13 -0
  168. package/dist/server/engine/samples/openbsd/ldapd.conf +58 -0
  169. package/dist/server/engine/samples/openbsd/pf.conf +28 -0
  170. package/dist/server/engine/samples/postfix/command_filter.pcre +3 -0
  171. package/dist/server/engine/samples/postfix/freebsd/mailer.conf +9 -0
  172. package/dist/server/engine/samples/postfix/helo_access.pcre +180 -0
  173. package/dist/server/engine/samples/postfix/ldap/catchall_maps.cf +11 -0
  174. package/dist/server/engine/samples/postfix/ldap/recipient_bcc_maps_domain.cf +11 -0
  175. package/dist/server/engine/samples/postfix/ldap/recipient_bcc_maps_user.cf +11 -0
  176. package/dist/server/engine/samples/postfix/ldap/relay_domains.cf +11 -0
  177. package/dist/server/engine/samples/postfix/ldap/sender_bcc_maps_domain.cf +11 -0
  178. package/dist/server/engine/samples/postfix/ldap/sender_bcc_maps_user.cf +11 -0
  179. package/dist/server/engine/samples/postfix/ldap/sender_dependent_relayhost_maps_domain.cf +11 -0
  180. package/dist/server/engine/samples/postfix/ldap/sender_dependent_relayhost_maps_user.cf +11 -0
  181. package/dist/server/engine/samples/postfix/ldap/sender_login_maps.cf +11 -0
  182. package/dist/server/engine/samples/postfix/ldap/transport_maps_domain.cf +11 -0
  183. package/dist/server/engine/samples/postfix/ldap/transport_maps_user.cf +11 -0
  184. package/dist/server/engine/samples/postfix/ldap/virtual_alias_maps.cf +11 -0
  185. package/dist/server/engine/samples/postfix/ldap/virtual_group_maps.cf +11 -0
  186. package/dist/server/engine/samples/postfix/ldap/virtual_group_members_maps.cf +11 -0
  187. package/dist/server/engine/samples/postfix/ldap/virtual_mailbox_domains.cf +11 -0
  188. package/dist/server/engine/samples/postfix/ldap/virtual_mailbox_maps.cf +12 -0
  189. package/dist/server/engine/samples/postfix/main.cf +282 -0
  190. package/dist/server/engine/samples/postfix/main.cf.amavisd +7 -0
  191. package/dist/server/engine/samples/postfix/main.cf.dovecot +8 -0
  192. package/dist/server/engine/samples/postfix/main.cf.ldap +39 -0
  193. package/dist/server/engine/samples/postfix/main.cf.mlmmj +5 -0
  194. package/dist/server/engine/samples/postfix/main.cf.mysql +39 -0
  195. package/dist/server/engine/samples/postfix/main.cf.pgsql +39 -0
  196. package/dist/server/engine/samples/postfix/main.cf.postscreen +30 -0
  197. package/dist/server/engine/samples/postfix/master.cf +87 -0
  198. package/dist/server/engine/samples/postfix/mysql/catchall_maps.cf +5 -0
  199. package/dist/server/engine/samples/postfix/mysql/domain_alias_catchall_maps.cf +5 -0
  200. package/dist/server/engine/samples/postfix/mysql/domain_alias_maps.cf +5 -0
  201. package/dist/server/engine/samples/postfix/mysql/recipient_bcc_maps_domain.cf +5 -0
  202. package/dist/server/engine/samples/postfix/mysql/recipient_bcc_maps_user.cf +5 -0
  203. package/dist/server/engine/samples/postfix/mysql/relay_domains.cf +5 -0
  204. package/dist/server/engine/samples/postfix/mysql/sender_bcc_maps_domain.cf +5 -0
  205. package/dist/server/engine/samples/postfix/mysql/sender_bcc_maps_user.cf +5 -0
  206. package/dist/server/engine/samples/postfix/mysql/sender_dependent_relayhost_maps.cf +6 -0
  207. package/dist/server/engine/samples/postfix/mysql/sender_login_maps.cf +5 -0
  208. package/dist/server/engine/samples/postfix/mysql/transport_maps_domain.cf +5 -0
  209. package/dist/server/engine/samples/postfix/mysql/transport_maps_maillist.cf +5 -0
  210. package/dist/server/engine/samples/postfix/mysql/transport_maps_user.cf +5 -0
  211. package/dist/server/engine/samples/postfix/mysql/virtual_alias_maps.cf +5 -0
  212. package/dist/server/engine/samples/postfix/mysql/virtual_mailbox_domains.cf +5 -0
  213. package/dist/server/engine/samples/postfix/mysql/virtual_mailbox_maps.cf +5 -0
  214. package/dist/server/engine/samples/postfix/pgsql/catchall_maps.cf +5 -0
  215. package/dist/server/engine/samples/postfix/pgsql/domain_alias_catchall_maps.cf +5 -0
  216. package/dist/server/engine/samples/postfix/pgsql/domain_alias_maps.cf +5 -0
  217. package/dist/server/engine/samples/postfix/pgsql/recipient_bcc_maps_domain.cf +5 -0
  218. package/dist/server/engine/samples/postfix/pgsql/recipient_bcc_maps_user.cf +5 -0
  219. package/dist/server/engine/samples/postfix/pgsql/relay_domains.cf +5 -0
  220. package/dist/server/engine/samples/postfix/pgsql/sender_bcc_maps_domain.cf +5 -0
  221. package/dist/server/engine/samples/postfix/pgsql/sender_bcc_maps_user.cf +5 -0
  222. package/dist/server/engine/samples/postfix/pgsql/sender_dependent_relayhost_maps.cf +6 -0
  223. package/dist/server/engine/samples/postfix/pgsql/sender_login_maps.cf +5 -0
  224. package/dist/server/engine/samples/postfix/pgsql/transport_maps_domain.cf +5 -0
  225. package/dist/server/engine/samples/postfix/pgsql/transport_maps_maillist.cf +5 -0
  226. package/dist/server/engine/samples/postfix/pgsql/transport_maps_user.cf +5 -0
  227. package/dist/server/engine/samples/postfix/pgsql/virtual_alias_maps.cf +5 -0
  228. package/dist/server/engine/samples/postfix/pgsql/virtual_mailbox_domains.cf +5 -0
  229. package/dist/server/engine/samples/postfix/pgsql/virtual_mailbox_maps.cf +5 -0
  230. package/dist/server/engine/samples/postfix/postscreen_access.cidr +6 -0
  231. package/dist/server/engine/samples/postgresql/sql/add_first_domain_and_user.sql +34 -0
  232. package/dist/server/engine/samples/postgresql/sql/grant_permissions.sql +14 -0
  233. package/dist/server/engine/samples/postgresql/sql/init_vmail_db.sql +15 -0
  234. package/dist/server/engine/samples/rsyslog.d/1-iredmail-dovecot.conf +23 -0
  235. package/dist/server/engine/samples/rsyslog.d/1-iredmail-fail2ban.conf +2 -0
  236. package/dist/server/engine/samples/rsyslog.d/1-iredmail-iredapd.conf +12 -0
  237. package/dist/server/engine/samples/rsyslog.d/1-iredmail-mlmmjadmin.conf +12 -0
  238. package/dist/server/engine/samples/rsyslog.d/1-iredmail-openldap.conf +2 -0
  239. package/dist/server/engine/samples/rsyslog.d/1-iredmail-phpfpm.conf +3 -0
  240. package/dist/server/engine/samples/spamassassin/local.cf +186 -0
  241. package/dist/server/engine/samples/spamassassin/razor.conf +1 -0
  242. package/dist/server/engine/samples/systemd/clamd.service.d/override.conf +2 -0
  243. package/dist/server/engine/samples/systemd/mariadb.service.d/override.conf +3 -0
  244. package/dist/server/engine/samples/systemd/slapd.service.d/override.conf +6 -0
  245. package/dist/server/engine/samples/yum/CentOS-Linux-AppStream.repo +6 -0
  246. package/dist/server/engine/samples/yum/CentOS-Linux-PowerTools.repo +6 -0
  247. package/dist/server/engine/samples/yum/CentOS-Stream-AppStream.repo +7 -0
  248. package/dist/server/engine/samples/yum/CentOS-Stream-PowerTools.repo +7 -0
  249. package/dist/server/engine/samples/yum/symas-openldap.repo +6 -0
  250. package/dist/server/engine/tools/add_enabled_service.py +60 -0
  251. package/dist/server/engine/tools/backup_mysql.sh +238 -0
  252. package/dist/server/engine/tools/backup_openldap.sh +230 -0
  253. package/dist/server/engine/tools/backup_pgsql.sh +217 -0
  254. package/dist/server/engine/tools/backup_sogo.sh +156 -0
  255. package/dist/server/engine/tools/create_mail_user_OpenLDAP.py +335 -0
  256. package/dist/server/engine/tools/create_mail_user_OpenLDAP.sh +287 -0
  257. package/dist/server/engine/tools/create_mail_user_SQL.sh +127 -0
  258. package/dist/server/engine/tools/enable_postscreen.sh +144 -0
  259. package/dist/server/engine/tools/fail2ban_unban_ip.sh +39 -0
  260. package/dist/server/engine/tools/find_sasl_login_ip.sh +20 -0
  261. package/dist/server/engine/tools/find_top_sasl_usernames.sh +28 -0
  262. package/dist/server/engine/tools/generate_ssl_keys.sh +85 -0
  263. package/dist/server/engine/tools/kill_high_cpu_sogo_process.sh +32 -0
  264. package/dist/server/engine/tools/ldap_assign_user_to_groups.py +151 -0
  265. package/dist/server/engine/tools/ldap_move_members_to_another_group.py +129 -0
  266. package/dist/server/engine/tools/migrate_sql_alias_table.py +177 -0
  267. package/dist/server/engine/update/0.9.8/amavisd.mysql +29 -0
  268. package/dist/server/engine/update/0.9.8/amavisd.pgsql +39 -0
  269. package/dist/server/engine/update/0.9.8/iredmail.mysql +53 -0
  270. package/dist/server/engine/update/0.9.8/iredmail.pgsql +61 -0
  271. package/dist/server/engine/update/0.9.9/iredmail.mysql +2 -0
  272. package/dist/server/engine/update/0.9.9/iredmail.pgsql +2 -0
  273. package/dist/server/engine/update/1.0/iredmail.mysql +2 -0
  274. package/dist/server/engine/update/1.0/iredmail.pgsql +2 -0
  275. package/dist/server/engine/update/1.2/last_login.mysql +16 -0
  276. package/dist/server/engine/update/1.4.0/iredmail.mysql +22 -0
  277. package/dist/server/engine/update/1.4.0/iredmail.pgsql +20 -0
  278. package/dist/server/engine/update/1.4.1/iredmail.mysql +3 -0
  279. package/dist/server/engine/update/1.4.1/iredmail.pgsql +3 -0
  280. package/dist/server/engine/update/1.4.1/sogo.mysql +11 -0
  281. package/dist/server/engine/update/1.4.1/sogo.pgsql +30 -0
  282. package/dist/server/engine/update/1.4.1/update-ldap.py +66 -0
  283. package/dist/server/engine/update/1.4.2/iredmail.mysql +9 -0
  284. package/dist/server/engine/update/1.4.2/iredmail.pgsql +9 -0
  285. package/dist/server/engine/update/1.4.2/sogo.pgsql +30 -0
  286. package/dist/server/engine/update/1.6.0/sogo_view.pgsql +27 -0
  287. package/dist/server/engine/update/1.6.3/iredmail.mysql +1 -0
  288. package/dist/server/engine/update/1.7.0/fail2ban.mysql +11 -0
  289. package/dist/server/engine/update/1.7.0/fail2ban.pgsql +12 -0
  290. package/dist/server/engine/update/1.7.1/amavisd.mysql +1 -0
  291. package/dist/server/engine/update/1.7.2/vmail.mysql +30 -0
  292. package/dist/server/engine/update/1.7.3/deleted_mailboxes.mysql +43 -0
  293. package/dist/server/engine/update/1.7.3/iredadmin.mysql +26 -0
  294. package/dist/server/engine/update/1.7.3/vmail.mysql +79 -0
  295. package/dist/server/engine/update/1.7.3/vmail.pgsql +9 -0
  296. package/dist/server/engine/update/README.md +1 -0
  297. package/dist/server/engine/update/ldap/README.md +40 -0
  298. package/dist/server/engine/update/ldap/update-ldap-dovecot-2.3.py +69 -0
  299. package/dist/server/index.js +55793 -53701
  300. package/dist/server/migrations/0071_gh_device_token.sql +3 -0
  301. package/dist/server/migrations/0072_gh_device_token_method.sql +1 -0
  302. package/dist/server/migrations/meta/_journal.json +14 -0
  303. package/dist/setup-2DMFIA74.js +20 -0
  304. package/dist/{signin-PVC7MBZJ.js → signin-ITOPKKUB.js} +11 -9
  305. package/dist/{sso-oidc-ZXY77BSL.js → sso-oidc-NON2PD7Y.js} +11 -9
  306. package/dist/{sts-LX5L4UU3.js → sts-HHFFSAY2.js} +10 -8
  307. package/package.json +1 -1
  308. package/dist/bare-CNSFDNSZ.js +0 -20
  309. package/dist/cloud-655YYCEW.js +0 -26
  310. package/dist/docker-UWQPDEGW.js +0 -14
  311. package/dist/docker-edge-executor-ZLAYE5FE.js +0 -11
  312. package/dist/edge-import-AFID2EN3.js +0 -41
  313. package/dist/nginx-M36GDYL7.js +0 -18
  314. package/dist/setup-ZXXB5S22.js +0 -16
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env sh
2
+ ":" //# ; exec "$(command -v bun || command -v node)" "$0" "$@"
2
3
  import { createRequire as __ospCreateRequire } from "node:module";
3
4
  const require = __ospCreateRequire(import.meta.url);
4
5
  import {
@@ -8,6 +9,9 @@ import {
8
9
  composeInternalToken,
9
10
  composeIsViableDefault,
10
11
  composePs,
12
+ composeRestart,
13
+ composeRunning,
14
+ composeStart,
11
15
  composeUninstall,
12
16
  composeUp,
13
17
  composeUpdate,
@@ -21,47 +25,56 @@ import {
21
25
  remoteSha,
22
26
  shortSha,
23
27
  sourceBuildDir
24
- } from "./chunk-E3ZPCNPU.js";
25
- import "./chunk-TDFK6ISF.js";
26
- import "./chunk-5WI7ZXOC.js";
27
- import "./chunk-PARRPUXN.js";
28
- import "./chunk-VLPJLXXB.js";
29
- import "./chunk-JJVSOE4I.js";
30
- import "./chunk-NPVJYR7M.js";
31
- import "./chunk-HYL4QYZM.js";
32
- import "./chunk-VJTXRCTT.js";
33
- import "./chunk-SJBR527P.js";
34
- import "./chunk-FZJX6XP7.js";
35
- import "./chunk-65KKUJBR.js";
36
- import "./chunk-GHXGCANU.js";
37
- import "./chunk-YJAS5RQX.js";
38
- import "./chunk-R74UB4X4.js";
39
- import "./chunk-3YCMJRDZ.js";
40
- import "./chunk-7EYFCKZX.js";
28
+ } from "./chunk-IMBD5PZ5.js";
29
+ import "./chunk-FRNAR33O.js";
30
+ import "./chunk-EI575GHN.js";
31
+ import "./chunk-6APIPIJY.js";
32
+ import "./chunk-IXCHURVK.js";
33
+ import "./chunk-OWED2GRM.js";
34
+ import "./chunk-VWPEKOH6.js";
35
+ import "./chunk-W73X43R7.js";
36
+ import "./chunk-TGIINPVM.js";
37
+ import "./chunk-XFCDYZUQ.js";
38
+ import "./chunk-AVIB7CT7.js";
39
+ import "./chunk-5MZ624VX.js";
40
+ import "./chunk-Q2CIRAOK.js";
41
+ import "./chunk-IPYPMI3S.js";
41
42
  import {
42
43
  beginEdgeTakeover,
44
+ collectProxyCerts,
43
45
  completeEdgeTakeover,
44
46
  detectInstalledProxy,
47
+ edgeProxy,
48
+ edgeProxyFor,
45
49
  foreignProxyOnEdge,
46
50
  importSites,
47
- ourEdgeContainerRunning,
48
51
  recoverInterruptedTakeover,
49
52
  rollbackEdgeTakeover,
50
53
  scanImportableSites
51
- } from "./chunk-UMYQALBH.js";
52
- import "./chunk-HDV4NSA3.js";
53
- import "./chunk-K5OH5U7N.js";
54
- import "./chunk-YVXF6GGR.js";
55
- import "./chunk-JXQCFTDV.js";
56
- import "./chunk-YFLQGDN2.js";
54
+ } from "./chunk-UXWAVM4C.js";
55
+ import "./chunk-O6ZZF52V.js";
56
+ import "./chunk-JYGD5WSN.js";
57
+ import "./chunk-SJIFLLX4.js";
58
+ import "./chunk-V6AN6XLZ.js";
59
+ import "./chunk-VDMQJHPI.js";
60
+ import "./chunk-MZ2WEHHY.js";
61
+ import "./chunk-L2VOGB2X.js";
62
+ import "./chunk-4Z5QO7N6.js";
63
+ import "./chunk-4TJC4JZQ.js";
64
+ import "./chunk-OXWDY22G.js";
65
+ import "./chunk-RPHRPFEH.js";
57
66
  import {
58
67
  LocalExecutor
59
- } from "./chunk-SYCZE6FQ.js";
60
- import "./chunk-C4FOQJ63.js";
61
- import "./chunk-JYAGTMSJ.js";
62
- import "./chunk-XRWV5ZNR.js";
63
- import "./chunk-Z6EKX6CX.js";
64
- import "./chunk-RZPDJC2J.js";
68
+ } from "./chunk-TYVME3XD.js";
69
+ import "./chunk-FXTBHTWY.js";
70
+ import "./chunk-FPRHYBY2.js";
71
+ import {
72
+ edgeCrashReason,
73
+ edgeIsServing,
74
+ ourEdgeContainerRunning
75
+ } from "./chunk-R5BHFFAV.js";
76
+ import "./chunk-ILFETZOM.js";
77
+ import "./chunk-VFINNTUL.js";
65
78
  import {
66
79
  CLOUD_DASHBOARD_URL,
67
80
  LOCAL_API_URL,
@@ -70,9 +83,8 @@ import {
70
83
  isValidEmail,
71
84
  parseOpenshipConfigJson,
72
85
  resolveCliUpdatePlan
73
- } from "./chunk-3LCRNQ2C.js";
74
- import "./chunk-MDFYN2QH.js";
75
- import "./chunk-JWBXYO24.js";
86
+ } from "./chunk-6APULWSU.js";
87
+ import "./chunk-KI2EY3WB.js";
76
88
 
77
89
  // src/index.ts
78
90
  import { Command as Command28 } from "commander";
@@ -474,7 +486,7 @@ import chalk6 from "chalk";
474
486
  import ora from "ora";
475
487
  import { spawn } from "child_process";
476
488
  import { randomBytes as randomBytes2 } from "crypto";
477
- import { createWriteStream as createWriteStream2, existsSync as existsSync7, mkdirSync as mkdirSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "fs";
489
+ import { createWriteStream as createWriteStream2, existsSync as existsSync7, mkdirSync as mkdirSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync7 } from "fs";
478
490
  import { dirname as dirname2, join as join8 } from "path";
479
491
  import { fileURLToPath } from "url";
480
492
 
@@ -1000,20 +1012,10 @@ async function resolvePorts(prefs) {
1000
1012
 
1001
1013
  // src/lib/edge-preflight.ts
1002
1014
  import { spawnSync as spawnSync3 } from "child_process";
1003
- import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "fs";
1015
+ import { existsSync as existsSync5, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "fs";
1004
1016
  import { join as join6 } from "path";
1005
1017
  import chalk5 from "chalk";
1006
1018
  import { isCancel, log, note, select } from "@clack/prompts";
1007
- function isSafePath(p) {
1008
- return /^\/[A-Za-z0-9._/-]+$/.test(p);
1009
- }
1010
- function readCertFile(p) {
1011
- try {
1012
- return readFileSync4(p, "utf8");
1013
- } catch {
1014
- return null;
1015
- }
1016
- }
1017
1019
  async function planAndApplyHostEdge(opts, overrides = {}) {
1018
1020
  const deps = { ...defaultDeps(), ...overrides };
1019
1021
  if (deps.platform !== "linux") return { proceed: true };
@@ -1031,7 +1033,7 @@ async function planAndApplyHostEdge(opts, overrides = {}) {
1031
1033
  const { sites, warnings } = await deps.importSites(executor, status);
1032
1034
  const action2 = await resolveAction(opts.edge, { status, owner, sites, warnings }, deps);
1033
1035
  if (action2 === "cancel") return { proceed: false };
1034
- const certPems = action2 === "migrate" ? collectCertPems(sites, deps.readCert) : void 0;
1036
+ const certPems = action2 === "migrate" ? await deps.collectCerts(executor, sites, { status }) : void 0;
1035
1037
  await deps.beginEdgeTakeover(
1036
1038
  executor,
1037
1039
  status,
@@ -1108,7 +1110,12 @@ async function offerStoppedProxyImport(executor, deps) {
1108
1110
  if (!await deps.confirmStoppedImport({ proxy, count: sites.length })) {
1109
1111
  return { proceed: true };
1110
1112
  }
1111
- return { proceed: true, action: "migrate", sites, certPems: collectCertPems(sites, readCertFile) };
1113
+ return {
1114
+ proceed: true,
1115
+ action: "migrate",
1116
+ sites,
1117
+ certPems: await deps.collectCerts(executor, sites, { proxy })
1118
+ };
1112
1119
  }
1113
1120
  async function completeHostEdge() {
1114
1121
  if (process.platform !== "linux") return;
@@ -1132,17 +1139,57 @@ async function resolveAction(flag, ctx, deps) {
1132
1139
  importable: ctx.sites.length
1133
1140
  });
1134
1141
  }
1135
- function collectCertPems(sites, readCert) {
1136
- const out = {};
1137
- for (const s of sites) {
1138
- if (!s.ssl || !s.tls) continue;
1139
- const { certPath, keyPath } = s.tls;
1140
- if (!isSafePath(certPath) || !isSafePath(keyPath)) continue;
1141
- const certPem = readCert(certPath);
1142
- const keyPem = readCert(keyPath);
1143
- if (certPem && keyPem) out[certPath] = { certPem, keyPem };
1142
+ async function collectCertsFromProxy(executor, sites, source) {
1143
+ const api = source.proxy ? edgeProxyFor(executor, source.proxy) : await edgeProxy(executor, source.status ? { status: source.status } : {});
1144
+ if (!api) return {};
1145
+ const { certPems, warnings } = await collectProxyCerts(api, sites);
1146
+ for (const w of warnings) console.log(chalk5.yellow(` ${w}`));
1147
+ return certPems;
1148
+ }
1149
+ function renderEdgeConflict(info3) {
1150
+ const { owner, sites, warnings } = info3;
1151
+ if (sites.length > 0) {
1152
+ const lines = sites.map((st) => {
1153
+ const host = (st.serverNames ?? []).join(", ") || "(no server_name)";
1154
+ const dest = st.target.kind === "static" ? `static: ${st.target.root}` : st.target.url;
1155
+ return `${chalk5.bold(host)} \u2192 ${chalk5.dim(dest)}${st.ssl ? chalk5.green(" [TLS]") : ""}`;
1156
+ });
1157
+ note(lines.join("\n"), `Detected ${sites.length} site${sites.length === 1 ? "" : "s"} on ${owner}`);
1144
1158
  }
1145
- return out;
1159
+ if (warnings.length > 0) {
1160
+ log.warn(`${warnings.length} config item${warnings.length === 1 ? "" : "s"} won't migrate automatically:`);
1161
+ for (const w of warnings.slice(0, 8)) log.message(chalk5.dim(`\u2022 ${w}`));
1162
+ }
1163
+ }
1164
+ async function confirmEdgeAction(info3) {
1165
+ const { owner, known, importable } = info3;
1166
+ const choice = await select({
1167
+ message: known ? `An existing reverse proxy (${owner}) is serving ports 80/443.` : `Ports 80/443 are in use by ${owner}, which we couldn't identify.`,
1168
+ options: [
1169
+ ...importable > 0 ? [
1170
+ {
1171
+ value: "migrate",
1172
+ label: `Migrate ${importable} site${importable === 1 ? "" : "s"} & take over`,
1173
+ hint: "import the existing sites into Openship's edge, then take 80/443"
1174
+ }
1175
+ ] : [],
1176
+ {
1177
+ value: "takeover",
1178
+ label: "Stop it & take over 80/443",
1179
+ hint: known ? "the existing sites stop being served" : "may interrupt a running service"
1180
+ },
1181
+ { value: "cancel", label: "Cancel \u2014 leave it running" }
1182
+ ],
1183
+ // Default to MIGRATE whenever there are sites we can import: it's the only
1184
+ // option that both gets Openship onto 80/443 and keeps the operator's
1185
+ // existing sites served, so it's the choice they almost always want and
1186
+ // the one that loses nothing on a reflexive Enter.
1187
+ // With nothing importable there's no safe default: a known proxy falls
1188
+ // back to cancel (blind Enter would stop sites that are being served),
1189
+ // and an unidentified port holder to takeover.
1190
+ initialValue: importable > 0 ? "migrate" : known ? "cancel" : "takeover"
1191
+ });
1192
+ return isCancel(choice) ? "cancel" : choice;
1146
1193
  }
1147
1194
  function defaultDeps() {
1148
1195
  return {
@@ -1172,61 +1219,20 @@ function defaultDeps() {
1172
1219
  });
1173
1220
  return !isCancel(take) && take === "import";
1174
1221
  },
1175
- readCert: readCertFile,
1176
- render: ({ owner, sites, warnings }) => {
1177
- if (sites.length > 0) {
1178
- const lines = sites.map((st) => {
1179
- const host = (st.serverNames ?? []).join(", ") || "(no server_name)";
1180
- const dest = st.target.kind === "static" ? `static: ${st.target.root}` : st.target.url;
1181
- return `${chalk5.bold(host)} \u2192 ${chalk5.dim(dest)}${st.ssl ? chalk5.green(" [TLS]") : ""}`;
1182
- });
1183
- note(lines.join("\n"), `Detected ${sites.length} site${sites.length === 1 ? "" : "s"} on ${owner}`);
1184
- }
1185
- if (warnings.length > 0) {
1186
- log.warn(`${warnings.length} config item${warnings.length === 1 ? "" : "s"} won't migrate automatically:`);
1187
- for (const w of warnings.slice(0, 8)) log.message(chalk5.dim(`\u2022 ${w}`));
1188
- }
1189
- },
1190
- confirm: async ({ owner, known, importable }) => {
1191
- const choice = await select({
1192
- message: known ? `An existing reverse proxy (${owner}) is serving ports 80/443.` : `Ports 80/443 are in use by ${owner}, which we couldn't identify.`,
1193
- options: [
1194
- ...importable > 0 ? [
1195
- {
1196
- value: "migrate",
1197
- label: `Migrate ${importable} site${importable === 1 ? "" : "s"} & take over`,
1198
- hint: "import the existing sites into Openship's edge, then take 80/443"
1199
- }
1200
- ] : [],
1201
- {
1202
- value: "takeover",
1203
- label: "Stop it & take over 80/443",
1204
- hint: known ? "the existing sites stop being served" : "may interrupt a running service"
1205
- },
1206
- { value: "cancel", label: "Cancel \u2014 leave it running" }
1207
- ],
1208
- // Default to MIGRATE whenever there are sites we can import: it's the only
1209
- // option that both gets Openship onto 80/443 and keeps the operator's
1210
- // existing sites served, so it's the choice they almost always want and
1211
- // the one that loses nothing on a reflexive Enter.
1212
- // With nothing importable there's no safe default: a known proxy falls
1213
- // back to cancel (blind Enter would stop sites that are being served),
1214
- // and an unidentified port holder to takeover.
1215
- initialValue: importable > 0 ? "migrate" : known ? "cancel" : "takeover"
1216
- });
1217
- return isCancel(choice) ? "cancel" : choice;
1218
- },
1222
+ collectCerts: collectCertsFromProxy,
1223
+ render: renderEdgeConflict,
1224
+ confirm: confirmEdgeAction,
1219
1225
  warn: (message) => console.log(chalk5.yellow(` ${message}`))
1220
1226
  };
1221
1227
  }
1222
1228
 
1223
1229
  // src/lib/loopback-api.ts
1224
- import { existsSync as existsSync6, readFileSync as readFileSync5, mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "fs";
1230
+ import { existsSync as existsSync6, readFileSync as readFileSync4, mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "fs";
1225
1231
  import { randomBytes } from "crypto";
1226
1232
  import { join as join7 } from "path";
1227
1233
  function ensureInternalToken() {
1228
1234
  const path2 = join7(OS_DIR, "internal-token");
1229
- if (existsSync6(path2)) return readFileSync5(path2, "utf8").trim();
1235
+ if (existsSync6(path2)) return readFileSync4(path2, "utf8").trim();
1230
1236
  mkdirSync7(OS_DIR, { recursive: true, mode: 448 });
1231
1237
  const token = randomBytes(32).toString("hex");
1232
1238
  writeFileSync6(path2, token, { mode: 384 });
@@ -1486,26 +1492,31 @@ async function headlessProvision(opts) {
1486
1492
  warnings.push(`Custom domain provisioning returned: ${res.data?.error || "failed"}`);
1487
1493
  }
1488
1494
  } else {
1495
+ const freeBody = (s) => ({
1496
+ domainType: "free",
1497
+ slug: s,
1498
+ publicHost: d.publicHost,
1499
+ dashPort,
1500
+ localEdge: d.localEdge,
1501
+ edgeTakeover: d.edgeTakeover,
1502
+ edgeMigrate: d.edgeMigrate
1503
+ });
1489
1504
  let slug = d.slug;
1490
- let res = await internalPost(
1491
- port,
1492
- "/api/system/self-register",
1493
- { domainType: "free", slug, publicHost: d.publicHost, dashPort },
1494
- token
1495
- );
1505
+ let res = await internalPost(port, "/api/system/self-register", freeBody(slug), token);
1496
1506
  for (let attempt = 0; !res.ok && isSlugTaken(res.data) && opts.onSlugTaken && attempt < 5; attempt++) {
1497
1507
  const next = await opts.onSlugTaken(slug);
1498
1508
  if (!next) break;
1499
1509
  slug = next;
1500
- res = await internalPost(
1501
- port,
1502
- "/api/system/self-register",
1503
- { domainType: "free", slug, publicHost: d.publicHost, dashPort },
1504
- token
1505
- );
1510
+ res = await internalPost(port, "/api/system/self-register", freeBody(slug), token);
1506
1511
  }
1507
1512
  domainRegistered = res.ok;
1508
1513
  liveUrl = res.data?.url;
1514
+ if (res.ok && res.data?.sessionId) {
1515
+ const edge = await drainProvisionStream(port, String(res.data.sessionId), token, (m) => log6(` ${m}`));
1516
+ if (!edge.completed && edge.detail) {
1517
+ warnings.push(`Local edge not fully ready: ${edge.detail} \u2014 it retries on next boot.`);
1518
+ }
1519
+ }
1509
1520
  if (!res.ok) {
1510
1521
  warnings.push(
1511
1522
  isSlugTaken(res.data) ? `Free domain not created: "${slug}.opsh.io" is already taken. Re-run and choose a different subdomain, or add one later in Settings \u2192 Domains.` : `Free .opsh.io domain not registered: ${res.data?.error || "failed"}. A free domain needs the box connected to Openship Cloud \u2014 connect it in Settings \u2192 Cloud, or use --domain-kind byo/custom.`
@@ -1526,6 +1537,12 @@ function normalizeUrl(raw) {
1526
1537
  return null;
1527
1538
  }
1528
1539
  }
1540
+ function effectivePublicUrl(flag) {
1541
+ if (flag) return normalizePublicUrl(flag);
1542
+ const saved = readInstanceUrl();
1543
+ if (!saved || /^https?:\/\/(localhost|127\.|\[?::1\]?)([:/]|$)/i.test(saved)) return void 0;
1544
+ return normalizePublicUrl(saved);
1545
+ }
1529
1546
  function normalizePublicUrl(raw) {
1530
1547
  const url = normalizeUrl(raw);
1531
1548
  if (!url) {
@@ -1541,7 +1558,7 @@ var DIST_DIR = dirname2(fileURLToPath(import.meta.url));
1541
1558
  var SERVER_DIR = join8(DIST_DIR, "server");
1542
1559
  function ensureAuthSecret() {
1543
1560
  const path2 = join8(OS_DIR, "auth-secret");
1544
- if (existsSync7(path2)) return readFileSync6(path2, "utf8").trim();
1561
+ if (existsSync7(path2)) return readFileSync5(path2, "utf8").trim();
1545
1562
  mkdirSync8(OS_DIR, { recursive: true, mode: 448 });
1546
1563
  const secret = randomBytes2(32).toString("hex");
1547
1564
  writeFileSync7(path2, secret, { mode: 384 });
@@ -1683,7 +1700,7 @@ async function runCompose(opts) {
1683
1700
  );
1684
1701
  process.exit(1);
1685
1702
  }
1686
- const publicUrl = opts.publicUrl ? normalizePublicUrl(opts.publicUrl) : void 0;
1703
+ const publicUrl = effectivePublicUrl(opts.publicUrl);
1687
1704
  const fromSource = sourceBuildDir();
1688
1705
  const spinner5 = ora(
1689
1706
  fromSource ? `Building Openship from ${fromSource} and starting the stack\u2026` : "Starting Openship via Docker Compose\u2026"
@@ -1693,8 +1710,10 @@ async function runCompose(opts) {
1693
1710
  dashboardPort: opts.dashboardPort,
1694
1711
  publicUrl,
1695
1712
  trustProxy: opts.trustProxy,
1696
- // commander maps `--no-host-control` to hostControl === false.
1697
- noHostControl: opts.hostControl === false
1713
+ // commander maps `--no-host-control` to hostControl === false. `undefined` when
1714
+ // the flag is absent, so a plain re-run keeps the install's original choice
1715
+ // instead of silently re-granting host control (see resolveEnvConfig).
1716
+ noHostControl: opts.hostControl === false ? true : void 0
1698
1717
  });
1699
1718
  if (!res.ok) {
1700
1719
  spinner5.fail("docker compose failed to start the stack");
@@ -1705,6 +1724,27 @@ async function runCompose(opts) {
1705
1724
  process.exit(1);
1706
1725
  }
1707
1726
  spinner5.succeed("Openship is running via Docker Compose.");
1727
+ if (edgePlan.action && !await edgeIsServing(new LocalExecutor())) {
1728
+ const reason = await edgeCrashReason(new LocalExecutor());
1729
+ console.error(
1730
+ chalk6.red(`
1731
+ Openship's edge is not serving :80${reason ? ` \u2014 ${reason}` : "."}`)
1732
+ );
1733
+ const restored = await rollbackHostEdge();
1734
+ console.error(
1735
+ restored ? chalk6.yellow(
1736
+ ` Your previous proxy has been restarted \u2014 the box is serving again, on it.
1737
+ Nothing was migrated. Fix the cause above, then re-run \`openship up\`.
1738
+ `
1739
+ ) : chalk6.red(
1740
+ ` AND your previous proxy could NOT be restarted automatically \u2014 the box is
1741
+ serving nothing right now. Start it by hand:
1742
+ docker stop openship-edge && sudo systemctl enable --now nginx
1743
+ `
1744
+ )
1745
+ );
1746
+ process.exit(1);
1747
+ }
1708
1748
  let importedOk = true;
1709
1749
  if (edgePlan.action === "migrate" && edgePlan.sites?.length) {
1710
1750
  const outcome = await importMigratedSites(res.apiPort, edgePlan.sites, edgePlan.certPems);
@@ -1742,7 +1782,7 @@ async function runFromSource(opts) {
1742
1782
  await runForeground(opts, src);
1743
1783
  }
1744
1784
  async function startService(opts, runOpts = {}) {
1745
- const publicUrl = opts.publicUrl ? normalizePublicUrl(opts.publicUrl) : void 0;
1785
+ const publicUrl = effectivePublicUrl(opts.publicUrl);
1746
1786
  if (opts.dryRun) {
1747
1787
  const p = preview({
1748
1788
  port: opts.port,
@@ -1841,7 +1881,7 @@ async function runForeground(opts, source) {
1841
1881
  });
1842
1882
  const port = String(resolved.api);
1843
1883
  const dashPort = String(resolved.dashboard);
1844
- const publicUrl = opts.publicUrl ? normalizePublicUrl(opts.publicUrl) : void 0;
1884
+ const publicUrl = effectivePublicUrl(opts.publicUrl);
1845
1885
  const managedEdge = Boolean(opts.managedEdge && publicUrl);
1846
1886
  const dataDir = opts.dataDir || join8(OS_DIR, "data");
1847
1887
  mkdirSync8(dataDir, { recursive: true });
@@ -1863,9 +1903,12 @@ async function runForeground(opts, source) {
1863
1903
  if (!source) {
1864
1904
  env.OPENSHIP_MIGRATIONS_DIR = join8(SERVER_DIR, "migrations");
1865
1905
  env.OPENSHIP_PGLITE_ASSETS_DIR = join8(SERVER_DIR, "pglite");
1906
+ const engineDir = join8(SERVER_DIR, "engine");
1907
+ if (existsSync7(engineDir)) env.MAIL_SERVER_ENGINE_DIR = engineDir;
1866
1908
  }
1867
1909
  env.OPENSHIP_REQUIRE_AUTH = "true";
1868
1910
  env.INTERNAL_TOKEN = ensureInternalToken();
1911
+ if (!env.OPENSHIP_AUTH_MODE) env.OPENSHIP_AUTH_MODE = "local";
1869
1912
  env.OPENSHIP_API_HOST = "127.0.0.1";
1870
1913
  env.OPENSHIP_DASHBOARD_PORT = dashPort;
1871
1914
  env.OPENSHIP_INSTANCE_LOG = instanceLogPath;
@@ -1953,7 +1996,7 @@ async function runForeground(opts, source) {
1953
1996
  const uiSpinner = ora("Preparing the dashboard\u2026").start();
1954
1997
  try {
1955
1998
  const bundle = await ensureDashboard({
1956
- tag: source ? "local" : opts.uiVersion || `v${"0.4.5"}`,
1999
+ tag: source ? "local" : opts.uiVersion || `v${"0.4.7"}`,
1957
2000
  onProgress: (received, total) => {
1958
2001
  if (total) {
1959
2002
  uiSpinner.text = `Downloading dashboard\u2026 ${Math.round(received / total * 100)}%`;
@@ -2222,7 +2265,7 @@ var initCommand = new Command7("init").description("Link the current directory t
2222
2265
 
2223
2266
  // src/commands/config.ts
2224
2267
  import { Command as Command8 } from "commander";
2225
- import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync9 } from "fs";
2268
+ import { existsSync as existsSync10, readFileSync as readFileSync6, writeFileSync as writeFileSync9 } from "fs";
2226
2269
  import { join as join10 } from "path";
2227
2270
  var SCHEMA_URL = "https://openship.io/openship.schema.json";
2228
2271
  var CONFIG_FILE = "openship.json";
@@ -2243,7 +2286,7 @@ function detectHints(dir) {
2243
2286
  const pkgPath = join10(dir, "package.json");
2244
2287
  if (existsSync10(pkgPath)) {
2245
2288
  try {
2246
- const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
2289
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
2247
2290
  const runner = hints.packageManager ?? "npm";
2248
2291
  const exec = runner === "npm" ? "npm run" : runner;
2249
2292
  if (pkg.scripts?.build) hints.buildCommand = `${exec} build`;
@@ -2279,7 +2322,7 @@ var validateCmd = new Command8("validate").description("Validate an openship.jso
2279
2322
  else err(`Not found: ${path2}`);
2280
2323
  process.exit(1);
2281
2324
  }
2282
- const { config, errors, warnings } = parseOpenshipConfigJson(readFileSync7(path2, "utf8"));
2325
+ const { config, errors, warnings } = parseOpenshipConfigJson(readFileSync6(path2, "utf8"));
2283
2326
  const valid = errors.length === 0 && config !== null;
2284
2327
  if (isJsonMode()) {
2285
2328
  printJson({ valid, errors, warnings });
@@ -2352,11 +2395,11 @@ var contextCommand = new Command9("context").alias("ctx").description("Manage co
2352
2395
  // src/commands/status.ts
2353
2396
  import { Command as Command10 } from "commander";
2354
2397
  import chalk9 from "chalk";
2355
- import { readFileSync as readFileSync8 } from "fs";
2398
+ import { readFileSync as readFileSync7 } from "fs";
2356
2399
  import { join as join11 } from "path";
2357
2400
  function readPorts() {
2358
2401
  try {
2359
- return JSON.parse(readFileSync8(join11(OS_DIR, "ports.json"), "utf8"));
2402
+ return JSON.parse(readFileSync7(join11(OS_DIR, "ports.json"), "utf8"));
2360
2403
  } catch {
2361
2404
  return {};
2362
2405
  }
@@ -2420,7 +2463,7 @@ import { intro, outro, note as note2, select as select3, log as log3, spinner as
2420
2463
 
2421
2464
  // src/lib/repair.ts
2422
2465
  import { spawnSync as spawnSync4 } from "child_process";
2423
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
2466
+ import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
2424
2467
  import { join as join13 } from "path";
2425
2468
  import chalk10 from "chalk";
2426
2469
  import { spinner, log as log2, select as select2, isCancel as isCancel3, cancel } from "@clack/prompts";
@@ -2534,7 +2577,7 @@ function ensure(value) {
2534
2577
  function storedPorts() {
2535
2578
  try {
2536
2579
  const p = join13(OS_DIR, "ports.json");
2537
- return existsSync12(p) ? JSON.parse(readFileSync9(p, "utf8")) : {};
2580
+ return existsSync12(p) ? JSON.parse(readFileSync8(p, "utf8")) : {};
2538
2581
  } catch {
2539
2582
  return {};
2540
2583
  }
@@ -2586,7 +2629,7 @@ function lastServiceError() {
2586
2629
  const p = join13(LOG_DIR2, name);
2587
2630
  if (!existsSync12(p)) continue;
2588
2631
  try {
2589
- const lines = readFileSync9(p, "utf8").trim().split("\n");
2632
+ const lines = readFileSync8(p, "utf8").trim().split("\n");
2590
2633
  const hit = [...lines].reverse().find((l) => /error|locked|aborted|malformed|corrupt|throw|cannot|EADDRINUSE/i.test(l));
2591
2634
  if (hit) return hit.trim().slice(0, 240);
2592
2635
  } catch {
@@ -3013,7 +3056,7 @@ import { execFileSync as execFileSync4 } from "child_process";
3013
3056
  import ora2 from "ora";
3014
3057
 
3015
3058
  // src/lib/project-link.ts
3016
- import { readFileSync as readFileSync10, existsSync as existsSync14 } from "fs";
3059
+ import { readFileSync as readFileSync9, existsSync as existsSync14 } from "fs";
3017
3060
  import { join as join14, dirname as dirname3, parse } from "path";
3018
3061
  var LINK_REL = join14(".openship", "project.json");
3019
3062
  function findProjectLinkPath(from = process.cwd()) {
@@ -3030,7 +3073,7 @@ function readProjectLink(from) {
3030
3073
  const path2 = findProjectLinkPath(from);
3031
3074
  if (!path2) return null;
3032
3075
  try {
3033
- return JSON.parse(readFileSync10(path2, "utf8"));
3076
+ return JSON.parse(readFileSync9(path2, "utf8"));
3034
3077
  } catch {
3035
3078
  return null;
3036
3079
  }
@@ -3038,7 +3081,7 @@ function readProjectLink(from) {
3038
3081
 
3039
3082
  // src/lib/folder-deploy.ts
3040
3083
  import { execFileSync as execFileSync3 } from "child_process";
3041
- import { readFileSync as readFileSync11, existsSync as existsSync15, rmSync as rmSync4 } from "fs";
3084
+ import { readFileSync as readFileSync10, existsSync as existsSync15, rmSync as rmSync4 } from "fs";
3042
3085
  import { tmpdir } from "os";
3043
3086
  import { join as join15, basename } from "path";
3044
3087
  function detectPackageManager(dir) {
@@ -3088,7 +3131,7 @@ async function deployFolder(opts) {
3088
3131
  );
3089
3132
  step("Uploading source");
3090
3133
  try {
3091
- const body = readFileSync11(tarball);
3134
+ const body = readFileSync10(tarball);
3092
3135
  const up = session.upload;
3093
3136
  const method = up.method || "POST";
3094
3137
  const res = /^https?:\/\//i.test(up.url) ? await fetch(up.url, { method, headers: up.headers, body }) : await apiRaw(`/${up.url.replace(/^\/+/, "")}`, { method, headers: up.headers, body });
@@ -4800,7 +4843,7 @@ var domainCommand = new Command17("domain").description("Manage custom domains,
4800
4843
  import { Command as Command18 } from "commander";
4801
4844
  import chalk15 from "chalk";
4802
4845
  import ora4 from "ora";
4803
- var INSTALLABLE = ["docker", "git", "openresty", "certbot", "rsync"];
4846
+ var INSTALLABLE = ["docker", "git", "edge", "rsync"];
4804
4847
  function guard(fn) {
4805
4848
  return async (...args) => {
4806
4849
  if (!getToken()) {
@@ -5094,7 +5137,7 @@ var serverCommand = server;
5094
5137
  // src/commands/system.ts
5095
5138
  import { Command as Command19 } from "commander";
5096
5139
  import ora5 from "ora";
5097
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
5140
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync10 } from "fs";
5098
5141
  import { createInterface as createInterface6 } from "readline/promises";
5099
5142
  import { stdin as input5, stdout as output5 } from "process";
5100
5143
  async function guarded(fn) {
@@ -5420,7 +5463,7 @@ dataTransferCommand.command("import").description("Import an instance export fil
5420
5463
  }
5421
5464
  let file;
5422
5465
  try {
5423
- file = JSON.parse(readFileSync12(opts.file, "utf8"));
5466
+ file = JSON.parse(readFileSync11(opts.file, "utf8"));
5424
5467
  } catch {
5425
5468
  err(`
5426
5469
  Could not read or parse ${opts.file}.
@@ -5771,7 +5814,7 @@ var mailCommand = new Command20("mail").description("Self-hosted mail server (iR
5771
5814
  // src/commands/backup.ts
5772
5815
  import { Command as Command21 } from "commander";
5773
5816
  import ora7 from "ora";
5774
- import { readFileSync as readFileSync13 } from "fs";
5817
+ import { readFileSync as readFileSync12 } from "fs";
5775
5818
  async function guard3(fn) {
5776
5819
  try {
5777
5820
  await fn();
@@ -6064,7 +6107,7 @@ destinationCmd.command("create").description("Create a backup destination").requ
6064
6107
  let sftpPrivateKey = opts.sftpPrivateKey;
6065
6108
  if (opts.sftpPrivateKeyFile) {
6066
6109
  try {
6067
- sftpPrivateKey = readFileSync13(opts.sftpPrivateKeyFile, "utf8");
6110
+ sftpPrivateKey = readFileSync12(opts.sftpPrivateKeyFile, "utf8");
6068
6111
  } catch {
6069
6112
  throw new Error(`Cannot read key file: ${opts.sftpPrivateKeyFile}`);
6070
6113
  }
@@ -6256,11 +6299,11 @@ var apiCommand = new Command23("api").description("Make an authenticated request
6256
6299
  import { Command as Command24 } from "commander";
6257
6300
  import chalk19 from "chalk";
6258
6301
  import { intro as intro2, outro as outro2, password as passwordPrompt, isCancel as isCancel4, cancel as cancel3, log as log4 } from "@clack/prompts";
6259
- import { readFileSync as readFileSync14 } from "fs";
6302
+ import { readFileSync as readFileSync13 } from "fs";
6260
6303
  import { join as join16 } from "path";
6261
6304
  function resolvedApiPort() {
6262
6305
  try {
6263
- return JSON.parse(readFileSync14(join16(OS_DIR, "ports.json"), "utf8")).api;
6306
+ return JSON.parse(readFileSync13(join16(OS_DIR, "ports.json"), "utf8")).api;
6264
6307
  } catch {
6265
6308
  return void 0;
6266
6309
  }
@@ -6314,7 +6357,7 @@ var resetAdminCommand = new Command24("reset-admin-password").description("Reset
6314
6357
 
6315
6358
  // src/commands/install.ts
6316
6359
  import { Command as Command25 } from "commander";
6317
- import { chmodSync as chmodSync2, existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync15, writeFileSync as writeFileSync11 } from "fs";
6360
+ import { chmodSync as chmodSync2, existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
6318
6361
  import { spawn as spawn2, spawnSync as spawnSync6 } from "child_process";
6319
6362
  import { homedir as homedir3 } from "os";
6320
6363
  import { join as join17 } from "path";
@@ -6451,7 +6494,7 @@ var installCommand = new Command25("install").description("Download and install
6451
6494
  try {
6452
6495
  let sidecarBody;
6453
6496
  if (existsSync16(sidecarPath) && !downloaded) {
6454
- sidecarBody = readFileSync15(sidecarPath, "utf8");
6497
+ sidecarBody = readFileSync14(sidecarPath, "utf8");
6455
6498
  } else {
6456
6499
  sidecarBody = await fetchSidecar(sidecarUrl);
6457
6500
  }
@@ -6562,7 +6605,7 @@ function detectPackageManager2(override) {
6562
6605
  var updateCommand = new Command26("update").description("Update the Openship CLI + bundled server to the latest release").option("--check", "Only report the current + latest version; don't install").option("--via <manager>", "Package manager to update with: bun | npm").option("--rebuild", "From-source installs: rebuild even if already at the remote tip").action(async (opts) => {
6563
6606
  const source = readSourceInstall();
6564
6607
  if (source) return runSourceUpdate(source, opts);
6565
- const current = "0.4.5";
6608
+ const current = "0.4.7";
6566
6609
  let latest;
6567
6610
  try {
6568
6611
  latest = (await resolveLatestTag()).replace(/^v/, "");
@@ -6597,13 +6640,13 @@ var updateCommand = new Command26("update").description("Update the Openship CLI
6597
6640
  return;
6598
6641
  }
6599
6642
  if (readInstallMethod() === "compose") {
6600
- const pulled = composeUpdate(latest);
6643
+ const applied = composeUpdate(latest);
6601
6644
  if (isJsonMode()) {
6602
- printJson({ updated: true, from: current, to: latest, via: pm, method: "compose", pulled });
6603
- } else if (pulled) {
6604
- ok(`Updated to v${latest} and pulled the new images \u2014 the compose stack is on the new version.`);
6645
+ printJson({ updated: true, from: current, to: latest, via: pm, method: "compose", applied });
6646
+ } else if (applied) {
6647
+ ok(`Updated to v${latest} \u2014 compose stack regenerated, images pulled, services recreated.`);
6605
6648
  } else {
6606
- err(`Updated the CLI to v${latest}, but \`docker compose pull\` failed. Run \`openship up\` to retry.`);
6649
+ err(`Updated the CLI to v${latest}, but bringing the compose stack up failed. Re-run \`openship update\`, or \`openship up\` to retry.`);
6607
6650
  process.exitCode = 1;
6608
6651
  }
6609
6652
  return;
@@ -6620,7 +6663,7 @@ var updateCommand = new Command26("update").description("Update the Openship CLI
6620
6663
 
6621
6664
  // src/commands/cache.ts
6622
6665
  import { Command as Command27 } from "commander";
6623
- import { existsSync as existsSync17, readdirSync as readdirSync2, readFileSync as readFileSync16, rmSync as rmSync5, statSync as statSync2 } from "fs";
6666
+ import { existsSync as existsSync17, readdirSync as readdirSync2, readFileSync as readFileSync15, rmSync as rmSync5, statSync as statSync2 } from "fs";
6624
6667
  import { join as join18 } from "path";
6625
6668
  function listAssets() {
6626
6669
  if (!existsSync17(RELEASES_DIR)) return [];
@@ -6669,7 +6712,7 @@ var verifyCmd2 = new Command27("verify").description("Re-hash cached assets and
6669
6712
  results.push({ tag: a.tag, asset: a.name, result: "no-sidecar" });
6670
6713
  continue;
6671
6714
  }
6672
- const expected = parseSha256(readFileSync16(`${a.path}.sha256`, "utf8"));
6715
+ const expected = parseSha256(readFileSync15(`${a.path}.sha256`, "utf8"));
6673
6716
  const actual = await hashFile(a.path);
6674
6717
  const okMatch = expected !== null && expected === actual;
6675
6718
  if (!okMatch) bad += 1;
@@ -6705,7 +6748,7 @@ var cacheCommand = new Command27("cache").description("Manage the local download
6705
6748
  import chalk20 from "chalk";
6706
6749
  import open2 from "open";
6707
6750
  import { createHash as createHash2, randomBytes as randomBytes3 } from "crypto";
6708
- import { existsSync as existsSync18, mkdirSync as mkdirSync12, readFileSync as readFileSync17, rmSync as rmSync6, writeFileSync as writeFileSync12 } from "fs";
6751
+ import { existsSync as existsSync18, mkdirSync as mkdirSync12, readFileSync as readFileSync16, rmSync as rmSync6, writeFileSync as writeFileSync12 } from "fs";
6709
6752
  import { join as join19 } from "path";
6710
6753
  import {
6711
6754
  intro as intro3,
@@ -6893,6 +6936,21 @@ ${pad("Login")}${opts.adminEmail} ${chalk20.dim("(email + password you set)")}
6893
6936
  );
6894
6937
  outro3(opts.byo ? chalk20.dim("Point your reverse proxy at the dashboard port above.") : chalk20.green("Happy shipping."));
6895
6938
  }
6939
+ async function promptLocalEdgeTakeover(port) {
6940
+ const pf = await internalPost(port, "/api/system/self-edge/preflight", {});
6941
+ const status = pf.ok ? pf.data?.status : void 0;
6942
+ if (!status || status.canProceedClean || !status.occupants?.length) {
6943
+ return { proceed: true, edgeMigrate: false, edgeTakeover: false };
6944
+ }
6945
+ const owner = status.occupants.map((o) => o.command ?? `port ${o.port}`).join(", ");
6946
+ const known = status.classification === "known";
6947
+ const sites = pf.ok && Array.isArray(pf.data?.sites) ? pf.data.sites : [];
6948
+ const warnings = pf.ok && Array.isArray(pf.data?.warnings) ? pf.data.warnings : [];
6949
+ renderEdgeConflict({ owner, sites, warnings });
6950
+ const action2 = await confirmEdgeAction({ owner, known, importable: sites.length });
6951
+ if (action2 === "cancel") return { proceed: false, edgeMigrate: false, edgeTakeover: false };
6952
+ return { proceed: true, edgeMigrate: action2 === "migrate", edgeTakeover: action2 === "takeover" };
6953
+ }
6896
6954
  async function runWizard() {
6897
6955
  const resuming = isSetupInProgress();
6898
6956
  intro3(`${chalk20.bgCyan(chalk20.black(" Openship "))}${chalk20.dim(" setup")}`);
@@ -7114,7 +7172,7 @@ async function runWizard() {
7114
7172
  }
7115
7173
  }
7116
7174
  }
7117
- const uiTag = `v${"0.4.5"}`;
7175
+ const uiTag = `v${"0.4.7"}`;
7118
7176
  if (method === "bare") {
7119
7177
  const dl = spinner4();
7120
7178
  dl.start("Pulling the Openship dist from GitHub");
@@ -7182,7 +7240,7 @@ async function runWizard() {
7182
7240
  const up = composeUp({
7183
7241
  publicUrl,
7184
7242
  trustProxy: behindProxy,
7185
- version: "0.4.5",
7243
+ version: "0.4.7",
7186
7244
  noHostControl: !allowHostControl
7187
7245
  });
7188
7246
  if (!up.ok) {
@@ -7319,93 +7377,49 @@ async function runWizard() {
7319
7377
  await internalPost(port, "/api/system/self-register", { domainType: "byo" });
7320
7378
  } else {
7321
7379
  cloudEmail = cloud.email;
7322
- let regSlug = domainPlan.slug;
7323
- while (true) {
7324
- const s2 = spinner4();
7325
- s2.start(`Registering ${chalk20.bold(`${regSlug}.opsh.io`)} with Openship Cloud`);
7326
- const res = await internalPost(port, "/api/system/self-register", {
7327
- domainType: "free",
7328
- slug: regSlug,
7329
- publicHost: domainPlan.publicHost,
7330
- dashPort: Number(started.dashPort)
7331
- });
7332
- if (res.ok && res.data?.url) {
7333
- liveUrl = res.data.url;
7334
- s2.stop(`Free domain live: ${res.data.url}`);
7335
- break;
7336
- }
7337
- s2.stop(`Couldn't register ${regSlug}.opsh.io: ${res.data?.error || "failed"}`, 1);
7338
- const next = ensure2(
7339
- await select4({
7340
- message: "Try a different subdomain?",
7341
- options: [
7342
- { value: "retry", label: "Pick another subdomain" },
7343
- { value: "skip", label: "Skip for now", hint: "log in on this server; add a domain later in Settings \u2192 Cloud" }
7344
- ]
7345
- })
7380
+ const edge = await promptLocalEdgeTakeover(port);
7381
+ if (!edge.proceed) {
7382
+ log5.warn(
7383
+ "Left the existing proxy on 80/443 running \u2014 a free domain forwards to :80 here, so it won't serve until Openship owns that port. Re-run setup and choose migrate / take over."
7346
7384
  );
7347
- if (next === "skip") break;
7348
- regSlug = ensure2(
7349
- await text({
7350
- message: "Choose your subdomain",
7351
- placeholder: "my-openship",
7352
- initialValue: regSlug,
7353
- validate: (v) => v && SLUG_RE2.test(v.trim().toLowerCase()) ? void 0 : "Lowercase letters, digits, hyphens"
7354
- })
7355
- ).trim().toLowerCase();
7356
7385
  }
7386
+ const result = await headlessProvision({
7387
+ port,
7388
+ dashPort: started.dashPort,
7389
+ token: provisionToken,
7390
+ method: "bare",
7391
+ inputs: {
7392
+ admin,
7393
+ domain: {
7394
+ kind: "free",
7395
+ slug: domainPlan.slug,
7396
+ publicHost: domainPlan.publicHost,
7397
+ // Bare needs a local :80 edge for Cloud to forward to; skip only when
7398
+ // the operator declined the takeover (their proxy keeps 80/443).
7399
+ localEdge: edge.proceed,
7400
+ edgeTakeover: edge.edgeTakeover,
7401
+ edgeMigrate: edge.edgeMigrate
7402
+ }
7403
+ },
7404
+ onLog: (msg) => log5.message(chalk20.dim(msg)),
7405
+ onSlugTaken: async (taken) => {
7406
+ log5.warn(`"${taken}.opsh.io" is already taken.`);
7407
+ const next = await text({
7408
+ message: "Choose a different subdomain (or leave empty to skip the free domain)",
7409
+ placeholder: "my-openship",
7410
+ validate: (v) => !v || SLUG_RE2.test(v.trim().toLowerCase()) ? void 0 : "Lowercase letters, digits, hyphens"
7411
+ });
7412
+ return isCancel5(next) || !next.trim() ? null : next.trim().toLowerCase();
7413
+ }
7414
+ });
7415
+ if (result.liveUrl) liveUrl = result.liveUrl;
7416
+ for (const w of result.warnings) log5.warn(w);
7357
7417
  }
7358
7418
  } else if (domainPlan.type === "custom") {
7359
- let edgeTakeover = false;
7360
- let edgeMigrate = false;
7361
- let proceedCustom = true;
7362
- const pf = await internalPost(port, "/api/system/self-edge/preflight", {});
7363
- const status = pf.ok ? pf.data?.status : void 0;
7364
- const importable = pf.ok && Array.isArray(pf.data?.sites) ? pf.data.sites.length : 0;
7365
- if (status && !status.canProceedClean && status.occupants?.length) {
7366
- const owner = status.occupants.map((o) => o.command ?? `port ${o.port}`).join(", ");
7367
- const known = status.classification === "known";
7368
- const sites = pf.ok && Array.isArray(pf.data?.sites) ? pf.data.sites : [];
7369
- if (sites.length > 0) {
7370
- const lines = sites.map((st) => {
7371
- const host = (st.serverNames ?? []).join(", ") || "(no server_name)";
7372
- const dest = st.target?.kind === "static" ? `static: ${st.target?.root ?? ""}` : st.target?.url ?? "";
7373
- return `${chalk20.bold(host)} \u2192 ${chalk20.dim(dest)}${st.ssl ? chalk20.green(" [TLS]") : ""}`;
7374
- });
7375
- note3(lines.join("\n"), `Detected ${sites.length} site${sites.length === 1 ? "" : "s"} on ${owner}`);
7376
- }
7377
- const warns = pf.ok && Array.isArray(pf.data?.warnings) ? pf.data.warnings : [];
7378
- if (warns.length > 0) {
7379
- log5.warn(`${warns.length} config item${warns.length === 1 ? "" : "s"} won't migrate automatically:`);
7380
- for (const w of warns.slice(0, 8)) log5.message(chalk20.dim(`\u2022 ${w}`));
7381
- }
7382
- const choice = ensure2(
7383
- await select4({
7384
- message: known ? `An existing reverse proxy (${owner}) is serving ports 80/443.` : `Ports 80/443 are in use by ${owner}, which we couldn't identify.`,
7385
- options: [
7386
- ...importable > 0 ? [{
7387
- value: "migrate",
7388
- label: `Migrate ${importable} site${importable === 1 ? "" : "s"} & take over`,
7389
- hint: "import the existing sites into Openship, then take 80/443"
7390
- }] : [],
7391
- {
7392
- value: "override",
7393
- label: "Stop it & take over 80/443",
7394
- hint: known ? "the existing sites stop being served" : "may interrupt a running service"
7395
- },
7396
- { value: "cancel", label: "Cancel \u2014 leave it running" }
7397
- ],
7398
- // Same default as the `up` preflight (edge-preflight.ts): migrate when
7399
- // there are importable sites — it takes 80/443 without dropping any of
7400
- // them. Otherwise a known proxy defaults to cancel (Enter must not stop
7401
- // sites that are being served) and an unidentified holder to takeover.
7402
- initialValue: importable > 0 ? "migrate" : known ? "cancel" : "override"
7403
- })
7404
- );
7405
- if (choice === "cancel") proceedCustom = false;
7406
- else if (choice === "migrate") edgeMigrate = true;
7407
- else edgeTakeover = true;
7408
- }
7419
+ const edge = await promptLocalEdgeTakeover(port);
7420
+ const proceedCustom = edge.proceed;
7421
+ const edgeTakeover = edge.edgeTakeover;
7422
+ const edgeMigrate = edge.edgeMigrate;
7409
7423
  if (!proceedCustom) {
7410
7424
  log5.warn(
7411
7425
  "Left the existing proxy on 80/443 running. Registering Openship without managed HTTPS \u2014 front it with your proxy, or re-run setup to take over."
@@ -7460,13 +7474,16 @@ async function runWizard() {
7460
7474
  function storedPorts2() {
7461
7475
  const p = join19(OS_DIR, "ports.json");
7462
7476
  try {
7463
- return existsSync18(p) ? JSON.parse(readFileSync17(p, "utf8")) : {};
7477
+ return existsSync18(p) ? JSON.parse(readFileSync16(p, "utf8")) : {};
7464
7478
  } catch {
7465
7479
  return {};
7466
7480
  }
7467
7481
  }
7468
7482
  async function runControl() {
7469
7483
  const svc = serviceStatus();
7484
+ const isCompose = readInstallMethod() === "compose";
7485
+ const running = isCompose ? composeRunning() : svc.running;
7486
+ const managerLabel = isCompose ? "docker compose" : svc.kind === "unsupported" ? "none" : svc.kind;
7470
7487
  const ports = storedPorts2();
7471
7488
  const apiPort2 = String(ports.api ?? 4e3);
7472
7489
  const dashUrl = `http://localhost:${ports.dashboard ?? 3001}`;
@@ -7475,10 +7492,10 @@ async function runControl() {
7475
7492
  intro3(`${chalk20.bgCyan(chalk20.black(" Openship "))}${chalk20.dim(" control")}`);
7476
7493
  note3(
7477
7494
  `${chalk20.dim("URL".padEnd(11))}${chalk20.bold(primaryUrl)}
7478
- ${chalk20.dim("Service".padEnd(11))}${svc.running ? chalk20.green("running") : chalk20.yellow("stopped")}
7495
+ ${chalk20.dim("Service".padEnd(11))}${running ? chalk20.green("running") : chalk20.yellow("stopped")}
7479
7496
  ${chalk20.dim("Dashboard".padEnd(11))}${dashUrl}
7480
7497
  ` + (ports.api ? `${chalk20.dim("API".padEnd(11))}http://localhost:${ports.api}
7481
- ` : "") + `${chalk20.dim("Manager".padEnd(11))}${svc.kind === "unsupported" ? "none" : svc.kind}`,
7498
+ ` : "") + `${chalk20.dim("Manager".padEnd(11))}${managerLabel}`,
7482
7499
  "Openship is already set up"
7483
7500
  );
7484
7501
  const corrupted = looksCorrupted();
@@ -7491,7 +7508,7 @@ ${chalk20.dim("Dashboard".padEnd(11))}${dashUrl}
7491
7508
  options: [
7492
7509
  ...corrupted ? [{ value: "repair", label: "Repair database", hint: "backup \u2192 heal \u2192 verify" }] : [],
7493
7510
  { value: "open", label: "Open the dashboard" },
7494
- svc.running ? { value: "restart", label: "Restart the service" } : { value: "start", label: "Start the service" },
7511
+ running ? { value: "restart", label: "Restart the service" } : { value: "start", label: "Start the service" },
7495
7512
  { value: "stop", label: "Stop the service", hint: "won't restart on boot" },
7496
7513
  ...corrupted ? [] : [{ value: "repair", label: "Repair database", hint: "backup \u2192 heal a corrupt DB" }],
7497
7514
  { value: "reset", label: "Reset admin password", hint: "sets a local email + password login" },
@@ -7511,15 +7528,31 @@ ${chalk20.dim("Dashboard".padEnd(11))}${dashUrl}
7511
7528
  });
7512
7529
  outro3(chalk20.dim(`Opening ${primaryUrl}`));
7513
7530
  return;
7514
- case "start":
7531
+ case "start": {
7532
+ if (isCompose) {
7533
+ const ok3 = composeStart();
7534
+ outro3(ok3 ? chalk20.green("Started.") : chalk20.yellow("Couldn't start the stack \u2014 run `openship up` to see the error."));
7535
+ return;
7536
+ }
7515
7537
  await startService({});
7516
7538
  return;
7539
+ }
7517
7540
  case "restart": {
7541
+ if (isCompose) {
7542
+ const ok3 = composeRestart();
7543
+ outro3(ok3 ? chalk20.green("Restarted.") : chalk20.yellow("Couldn't restart the stack."));
7544
+ return;
7545
+ }
7518
7546
  const r = restart();
7519
7547
  outro3(r.restarted ? chalk20.green("Restarted.") : chalk20.yellow(r.detail));
7520
7548
  return;
7521
7549
  }
7522
7550
  case "stop": {
7551
+ if (isCompose) {
7552
+ const ok3 = composeDown();
7553
+ outro3(ok3 ? chalk20.green("Stopped. Won't restart on boot.") : chalk20.yellow("Couldn't stop the stack."));
7554
+ return;
7555
+ }
7523
7556
  const r = stop();
7524
7557
  outro3(chalk20.green(`Stopped. ${chalk20.dim(r.detail)}`));
7525
7558
  return;
@@ -7544,10 +7577,11 @@ ${chalk20.dim("Dashboard".padEnd(11))}${dashUrl}
7544
7577
 
7545
7578
  // src/index.ts
7546
7579
  var program = new Command28();
7547
- program.name("openship").description("Openship CLI \u2014 install, run, and manage Openship from your terminal").version("0.4.5").option("--json", "Machine-readable JSON output (stdout data only)").hook("preAction", (thisCommand) => {
7580
+ program.name("openship").description("Openship CLI \u2014 install, run, and manage Openship from your terminal").version("0.4.7").option("--json", "Machine-readable JSON output (stdout data only)").hook("preAction", (thisCommand) => {
7548
7581
  if (thisCommand.opts().json) setJsonMode(true);
7549
7582
  }).action(async () => {
7550
- if (serviceStatus().installed && !isSetupInProgress()) await runControl();
7583
+ const installed = serviceStatus().installed || readInstallMethod() === "compose";
7584
+ if (installed && !isSetupInProgress()) await runControl();
7551
7585
  else await runWizard();
7552
7586
  });
7553
7587
  program.addCommand(upCommand);