@devalade/shipnode 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/dist/cli/commands/backup.d.ts +14 -0
  2. package/dist/cli/commands/backup.d.ts.map +1 -0
  3. package/dist/cli/commands/backup.js +144 -0
  4. package/dist/cli/commands/backup.js.map +1 -0
  5. package/dist/cli/commands/ci.d.ts +5 -0
  6. package/dist/cli/commands/ci.d.ts.map +1 -0
  7. package/dist/cli/commands/ci.js +222 -0
  8. package/dist/cli/commands/ci.js.map +1 -0
  9. package/dist/cli/commands/cloudflare.d.ts +10 -0
  10. package/dist/cli/commands/cloudflare.d.ts.map +1 -0
  11. package/dist/cli/commands/cloudflare.js +166 -0
  12. package/dist/cli/commands/cloudflare.js.map +1 -0
  13. package/dist/cli/commands/config.d.ts +10 -0
  14. package/dist/cli/commands/config.d.ts.map +1 -0
  15. package/dist/cli/commands/config.js +99 -0
  16. package/dist/cli/commands/config.js.map +1 -0
  17. package/dist/cli/commands/deploy.d.ts +6 -0
  18. package/dist/cli/commands/deploy.d.ts.map +1 -0
  19. package/dist/cli/commands/deploy.js +95 -0
  20. package/dist/cli/commands/deploy.js.map +1 -0
  21. package/dist/cli/commands/doctor.d.ts +5 -0
  22. package/dist/cli/commands/doctor.d.ts.map +1 -0
  23. package/dist/cli/commands/doctor.js +86 -0
  24. package/dist/cli/commands/doctor.js.map +1 -0
  25. package/dist/cli/commands/eject.d.ts +2 -0
  26. package/dist/cli/commands/eject.d.ts.map +1 -0
  27. package/dist/cli/commands/eject.js +74 -0
  28. package/dist/cli/commands/eject.js.map +1 -0
  29. package/dist/cli/commands/env.d.ts +4 -0
  30. package/dist/cli/commands/env.d.ts.map +1 -0
  31. package/dist/cli/commands/env.js +50 -0
  32. package/dist/cli/commands/env.js.map +1 -0
  33. package/dist/cli/commands/harden.d.ts +4 -0
  34. package/dist/cli/commands/harden.d.ts.map +1 -0
  35. package/dist/cli/commands/harden.js +110 -0
  36. package/dist/cli/commands/harden.js.map +1 -0
  37. package/dist/cli/commands/help.d.ts +2 -0
  38. package/dist/cli/commands/help.d.ts.map +1 -0
  39. package/dist/cli/commands/help.js +99 -0
  40. package/dist/cli/commands/help.js.map +1 -0
  41. package/dist/cli/commands/init.d.ts +5 -0
  42. package/dist/cli/commands/init.d.ts.map +1 -0
  43. package/dist/cli/commands/init.js +246 -0
  44. package/dist/cli/commands/init.js.map +1 -0
  45. package/dist/cli/commands/logs.d.ts +5 -0
  46. package/dist/cli/commands/logs.d.ts.map +1 -0
  47. package/dist/cli/commands/logs.js +17 -0
  48. package/dist/cli/commands/logs.js.map +1 -0
  49. package/dist/cli/commands/metrics.d.ts +4 -0
  50. package/dist/cli/commands/metrics.d.ts.map +1 -0
  51. package/dist/cli/commands/metrics.js +21 -0
  52. package/dist/cli/commands/metrics.js.map +1 -0
  53. package/dist/cli/commands/migrate.d.ts +4 -0
  54. package/dist/cli/commands/migrate.d.ts.map +1 -0
  55. package/dist/cli/commands/migrate.js +71 -0
  56. package/dist/cli/commands/migrate.js.map +1 -0
  57. package/dist/cli/commands/restart.d.ts +4 -0
  58. package/dist/cli/commands/restart.d.ts.map +1 -0
  59. package/dist/cli/commands/restart.js +14 -0
  60. package/dist/cli/commands/restart.js.map +1 -0
  61. package/dist/cli/commands/rollback.d.ts +5 -0
  62. package/dist/cli/commands/rollback.d.ts.map +1 -0
  63. package/dist/cli/commands/rollback.js +49 -0
  64. package/dist/cli/commands/rollback.js.map +1 -0
  65. package/dist/cli/commands/run.d.ts +5 -0
  66. package/dist/cli/commands/run.d.ts.map +1 -0
  67. package/dist/cli/commands/run.js +69 -0
  68. package/dist/cli/commands/run.js.map +1 -0
  69. package/dist/cli/commands/setup.d.ts +4 -0
  70. package/dist/cli/commands/setup.d.ts.map +1 -0
  71. package/dist/cli/commands/setup.js +46 -0
  72. package/dist/cli/commands/setup.js.map +1 -0
  73. package/dist/cli/commands/status.d.ts +4 -0
  74. package/dist/cli/commands/status.d.ts.map +1 -0
  75. package/dist/cli/commands/status.js +49 -0
  76. package/dist/cli/commands/status.js.map +1 -0
  77. package/dist/cli/commands/stop.d.ts +4 -0
  78. package/dist/cli/commands/stop.d.ts.map +1 -0
  79. package/dist/cli/commands/stop.js +14 -0
  80. package/dist/cli/commands/stop.js.map +1 -0
  81. package/dist/cli/commands/unlock.d.ts +4 -0
  82. package/dist/cli/commands/unlock.d.ts.map +1 -0
  83. package/dist/cli/commands/unlock.js +27 -0
  84. package/dist/cli/commands/unlock.js.map +1 -0
  85. package/dist/cli/commands/upgrade.d.ts +2 -0
  86. package/dist/cli/commands/upgrade.d.ts.map +1 -0
  87. package/dist/cli/commands/upgrade.js +63 -0
  88. package/dist/cli/commands/upgrade.js.map +1 -0
  89. package/dist/cli/commands/user.d.ts +17 -0
  90. package/dist/cli/commands/user.d.ts.map +1 -0
  91. package/dist/cli/commands/user.js +98 -0
  92. package/dist/cli/commands/user.js.map +1 -0
  93. package/dist/cli/index.d.ts +3 -0
  94. package/dist/cli/index.d.ts.map +1 -0
  95. package/dist/cli/index.js +218 -0
  96. package/dist/cli/index.js.map +1 -0
  97. package/dist/cli/prompt.d.ts +2 -0
  98. package/dist/cli/prompt.d.ts.map +1 -0
  99. package/dist/cli/prompt.js +11 -0
  100. package/dist/cli/prompt.js.map +1 -0
  101. package/dist/cli/runner.d.ts +34 -0
  102. package/dist/cli/runner.d.ts.map +1 -0
  103. package/dist/cli/runner.js +44 -0
  104. package/dist/cli/runner.js.map +1 -0
  105. package/dist/cli/ui.d.ts +9 -0
  106. package/dist/cli/ui.d.ts.map +1 -0
  107. package/dist/cli/ui.js +26 -0
  108. package/dist/cli/ui.js.map +1 -0
  109. package/dist/config/assembly.d.ts +14 -0
  110. package/dist/config/assembly.d.ts.map +1 -0
  111. package/dist/config/assembly.js +62 -0
  112. package/dist/config/assembly.js.map +1 -0
  113. package/dist/config/builder.d.ts +33 -0
  114. package/dist/config/builder.d.ts.map +1 -0
  115. package/dist/config/builder.js +119 -0
  116. package/dist/config/builder.js.map +1 -0
  117. package/dist/config/loader.d.ts +3 -0
  118. package/dist/config/loader.d.ts.map +1 -0
  119. package/dist/config/loader.js +20 -0
  120. package/dist/config/loader.js.map +1 -0
  121. package/dist/config/schema.d.ts +416 -0
  122. package/dist/config/schema.d.ts.map +1 -0
  123. package/dist/config/schema.js +75 -0
  124. package/dist/config/schema.js.map +1 -0
  125. package/dist/domain/deploy/backend-strategy.d.ts +15 -0
  126. package/dist/domain/deploy/backend-strategy.d.ts.map +1 -0
  127. package/dist/domain/deploy/backend-strategy.js +110 -0
  128. package/dist/domain/deploy/backend-strategy.js.map +1 -0
  129. package/dist/domain/deploy/frontend-strategy.d.ts +13 -0
  130. package/dist/domain/deploy/frontend-strategy.d.ts.map +1 -0
  131. package/dist/domain/deploy/frontend-strategy.js +64 -0
  132. package/dist/domain/deploy/frontend-strategy.js.map +1 -0
  133. package/dist/domain/deploy/orchestrator.d.ts +34 -0
  134. package/dist/domain/deploy/orchestrator.d.ts.map +1 -0
  135. package/dist/domain/deploy/orchestrator.js +149 -0
  136. package/dist/domain/deploy/orchestrator.js.map +1 -0
  137. package/dist/domain/deploy/strategy.d.ts +44 -0
  138. package/dist/domain/deploy/strategy.d.ts.map +1 -0
  139. package/dist/domain/deploy/strategy.js +2 -0
  140. package/dist/domain/deploy/strategy.js.map +1 -0
  141. package/dist/domain/framework/detector.d.ts +7 -0
  142. package/dist/domain/framework/detector.d.ts.map +1 -0
  143. package/dist/domain/framework/detector.js +109 -0
  144. package/dist/domain/framework/detector.js.map +1 -0
  145. package/dist/domain/release/manager.d.ts +22 -0
  146. package/dist/domain/release/manager.d.ts.map +1 -0
  147. package/dist/domain/release/manager.js +100 -0
  148. package/dist/domain/release/manager.js.map +1 -0
  149. package/dist/domain/remote/executor.d.ts +16 -0
  150. package/dist/domain/remote/executor.d.ts.map +1 -0
  151. package/dist/domain/remote/executor.js +2 -0
  152. package/dist/domain/remote/executor.js.map +1 -0
  153. package/dist/domain/validation/ip.d.ts +5 -0
  154. package/dist/domain/validation/ip.d.ts.map +1 -0
  155. package/dist/domain/validation/ip.js +38 -0
  156. package/dist/domain/validation/ip.js.map +1 -0
  157. package/dist/index.d.ts +6 -0
  158. package/dist/index.d.ts.map +1 -0
  159. package/dist/index.js +4 -0
  160. package/dist/index.js.map +1 -0
  161. package/dist/infrastructure/ssh/connection.d.ts +19 -0
  162. package/dist/infrastructure/ssh/connection.d.ts.map +1 -0
  163. package/dist/infrastructure/ssh/connection.js +89 -0
  164. package/dist/infrastructure/ssh/connection.js.map +1 -0
  165. package/dist/services/caddy.service.d.ts +12 -0
  166. package/dist/services/caddy.service.d.ts.map +1 -0
  167. package/dist/services/caddy.service.js +56 -0
  168. package/dist/services/caddy.service.js.map +1 -0
  169. package/dist/services/deploy.service.d.ts +19 -0
  170. package/dist/services/deploy.service.d.ts.map +1 -0
  171. package/dist/services/deploy.service.js +53 -0
  172. package/dist/services/deploy.service.js.map +1 -0
  173. package/dist/services/health.service.d.ts +13 -0
  174. package/dist/services/health.service.d.ts.map +1 -0
  175. package/dist/services/health.service.js +38 -0
  176. package/dist/services/health.service.js.map +1 -0
  177. package/dist/shared/constants.d.ts +127 -0
  178. package/dist/shared/constants.d.ts.map +1 -0
  179. package/dist/shared/constants.js +85 -0
  180. package/dist/shared/constants.js.map +1 -0
  181. package/dist/shared/errors.d.ts +27 -0
  182. package/dist/shared/errors.d.ts.map +1 -0
  183. package/dist/shared/errors.js +53 -0
  184. package/dist/shared/errors.js.map +1 -0
  185. package/dist/shared/types.d.ts +116 -0
  186. package/dist/shared/types.d.ts.map +1 -0
  187. package/dist/shared/types.js +2 -0
  188. package/dist/shared/types.js.map +1 -0
  189. package/package.json +53 -0
@@ -0,0 +1,100 @@
1
+ import { LockError } from '../../shared/errors.js';
2
+ export class ReleaseManager {
3
+ executor;
4
+ remotePath;
5
+ keepReleases;
6
+ constructor(executor, remotePath, keepReleases) {
7
+ this.executor = executor;
8
+ this.remotePath = remotePath;
9
+ this.keepReleases = keepReleases;
10
+ }
11
+ async createReleasePath(timestamp) {
12
+ const releasePath = `${this.remotePath}/releases/${timestamp}`;
13
+ await this.executor.exec(`mkdir -p "${releasePath}"`);
14
+ return releasePath;
15
+ }
16
+ async setupReleaseStructure() {
17
+ await this.executor.exec(`mkdir -p "${this.remotePath}/releases"`);
18
+ await this.executor.exec(`mkdir -p "${this.remotePath}/shared"`);
19
+ await this.executor.exec(`mkdir -p "${this.remotePath}/.shipnode"`);
20
+ }
21
+ async switchSymlink(releasePath) {
22
+ const tmpLink = `${this.remotePath}/current.tmp`;
23
+ await this.executor.exec(`ln -sfn "${releasePath}" "${tmpLink}"`);
24
+ await this.executor.exec(`mv -Tf "${tmpLink}" "${this.remotePath}/current"`);
25
+ }
26
+ async recordRelease(record) {
27
+ const releasesFile = `${this.remotePath}/.shipnode/releases.json`;
28
+ const existingResult = await this.executor.exec(`cat "${releasesFile}" 2>/dev/null || echo '[]'`);
29
+ let releases = [];
30
+ try {
31
+ releases = JSON.parse(existingResult.stdout);
32
+ }
33
+ catch {
34
+ releases = [];
35
+ }
36
+ releases.push(record);
37
+ const b64 = Buffer.from(JSON.stringify(releases, null, 2)).toString('base64');
38
+ await this.executor.exec(`printf '%s' '${b64}' | base64 -d > "${releasesFile}"`);
39
+ }
40
+ async cleanupOldReleases() {
41
+ try {
42
+ const result = await this.executor.exec(`ls -1t "${this.remotePath}/releases/" 2>/dev/null | tail -n +${this.keepReleases + 1}`);
43
+ if (result.stdout) {
44
+ const oldReleases = result.stdout.split('\n').filter(Boolean);
45
+ for (const release of oldReleases) {
46
+ await this.executor.exec(`rm -rf "${this.remotePath}/releases/${release}"`);
47
+ }
48
+ }
49
+ }
50
+ catch {
51
+ // Cleanup is best-effort
52
+ }
53
+ }
54
+ async listReleases() {
55
+ const releasesFile = `${this.remotePath}/.shipnode/releases.json`;
56
+ try {
57
+ const result = await this.executor.exec(`cat "${releasesFile}" 2>/dev/null || echo '[]'`);
58
+ return JSON.parse(result.stdout);
59
+ }
60
+ catch {
61
+ return [];
62
+ }
63
+ }
64
+ }
65
+ export class DeployLock {
66
+ executor;
67
+ remotePath;
68
+ constructor(executor, remotePath) {
69
+ this.executor = executor;
70
+ this.remotePath = remotePath;
71
+ }
72
+ async acquire() {
73
+ const lockFile = `${this.remotePath}/.shipnode/deploy.lock`;
74
+ const staleAfterSeconds = 3600;
75
+ const result = await this.executor.exec(`mkdir -p "${this.remotePath}/.shipnode" && ` +
76
+ `if [ -f "${lockFile}" ]; then ` +
77
+ ` age=$(( $(date +%s) - $(stat -c %Y "${lockFile}") )); ` +
78
+ ` if [ "$age" -lt ${staleAfterSeconds} ]; then ` +
79
+ ` echo "LOCKED:$age"; exit 1; ` +
80
+ ` else ` +
81
+ ` rm -f "${lockFile}"; ` +
82
+ ` fi; ` +
83
+ `fi && ` +
84
+ `date -u +%Y-%m-%dT%H:%M:%SZ > "${lockFile}" && echo "OK"`);
85
+ if (result.stdout.startsWith('LOCKED:')) {
86
+ const age = result.stdout.split(':')[1];
87
+ throw new LockError(`Deployment already in progress (lock age: ${age}s). Run 'shipnode unlock' to clear.`);
88
+ }
89
+ if (result.exitCode !== 0) {
90
+ throw new LockError(result.stderr || 'Failed to acquire deployment lock');
91
+ }
92
+ }
93
+ async release() {
94
+ const lockFile = `${this.remotePath}/.shipnode/deploy.lock`;
95
+ await this.executor.exec(`rm -f "${lockFile}"`).catch(() => {
96
+ // Best-effort cleanup
97
+ });
98
+ }
99
+ }
100
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/domain/release/manager.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,OAAO,cAAc;IAEf;IACA;IACA;IAHV,YACU,QAAwB,EACxB,UAAkB,EAClB,YAAoB;QAFpB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,eAAU,GAAV,UAAU,CAAQ;QAClB,iBAAY,GAAZ,YAAY,CAAQ;IAC3B,CAAC;IAEJ,KAAK,CAAC,iBAAiB,CAAC,SAAiB;QACvC,MAAM,WAAW,GAAG,GAAG,IAAI,CAAC,UAAU,aAAa,SAAS,EAAE,CAAC;QAC/D,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,WAAW,GAAG,CAAC,CAAC;QACtD,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,qBAAqB;QACzB,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,UAAU,YAAY,CAAC,CAAC;QACnE,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,UAAU,UAAU,CAAC,CAAC;QACjE,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,UAAU,aAAa,CAAC,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,WAAmB;QACrC,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,UAAU,cAAc,CAAC;QACjD,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,WAAW,MAAM,OAAO,GAAG,CAAC,CAAC;QAClE,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,OAAO,MAAM,IAAI,CAAC,UAAU,WAAW,CAAC,CAAC;IAC/E,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,MAAqB;QACvC,MAAM,YAAY,GAAG,GAAG,IAAI,CAAC,UAAU,0BAA0B,CAAC;QAElE,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,YAAY,4BAA4B,CAAC,CAAC;QAClG,IAAI,QAAQ,GAAoB,EAAE,CAAC;QAEnC,IAAI,CAAC;YACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,QAAQ,GAAG,EAAE,CAAC;QAChB,CAAC;QAED,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAEtB,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC9E,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,gBAAgB,GAAG,oBAAoB,YAAY,GAAG,CAAC,CAAC;IACnF,CAAC;IAED,KAAK,CAAC,kBAAkB;QACtB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CACrC,WAAW,IAAI,CAAC,UAAU,sCAAsC,IAAI,CAAC,YAAY,GAAG,CAAC,EAAE,CACxF,CAAC;YAEF,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBAC9D,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;oBAClC,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,UAAU,aAAa,OAAO,GAAG,CAAC,CAAC;gBAC9E,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,yBAAyB;QAC3B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,YAAY,GAAG,GAAG,IAAI,CAAC,UAAU,0BAA0B,CAAC;QAClE,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,YAAY,4BAA4B,CAAC,CAAC;YAC1F,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF;AAED,MAAM,OAAO,UAAU;IAEX;IACA;IAFV,YACU,QAAwB,EACxB,UAAkB;QADlB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,eAAU,GAAV,UAAU,CAAQ;IACzB,CAAC;IAEJ,KAAK,CAAC,OAAO;QACX,MAAM,QAAQ,GAAG,GAAG,IAAI,CAAC,UAAU,wBAAwB,CAAC;QAC5D,MAAM,iBAAiB,GAAG,IAAI,CAAC;QAE/B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CACrC,aAAa,IAAI,CAAC,UAAU,iBAAiB;YAC7C,YAAY,QAAQ,YAAY;YAChC,yCAAyC,QAAQ,SAAS;YAC1D,qBAAqB,iBAAiB,WAAW;YACjD,kCAAkC;YAClC,SAAS;YACT,cAAc,QAAQ,KAAK;YAC3B,QAAQ;YACR,QAAQ;YACR,kCAAkC,QAAQ,gBAAgB,CAC3D,CAAC;QAEF,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACxC,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACxC,MAAM,IAAI,SAAS,CACjB,6CAA6C,GAAG,qCAAqC,CACtF,CAAC;QACJ,CAAC;QAED,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,SAAS,CAAC,MAAM,CAAC,MAAM,IAAI,mCAAmC,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,QAAQ,GAAG,GAAG,IAAI,CAAC,UAAU,wBAAwB,CAAC;QAC5D,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YACzD,sBAAsB;QACxB,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,16 @@
1
+ import type { ExecResult } from '../../shared/types.js';
2
+ /**
3
+ * A seam for executing commands on a remote host.
4
+ *
5
+ * Implementations satisfy this interface at the seam — callers do not
6
+ * know whether the underlying transport is SSH, a local shell, or a test double.
7
+ *
8
+ * The interface is intentionally narrow: a lot of behaviour (connection
9
+ * management, authentication, session reuse) hides behind `exec`.
10
+ */
11
+ export interface RemoteExecutor {
12
+ exec(command: string, options?: {
13
+ timeout?: number;
14
+ }): Promise<ExecResult>;
15
+ }
16
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../../src/domain/remote/executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAC5E"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../src/domain/remote/executor.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export declare function isValidIpOrHostname(input: string): boolean;
2
+ export declare function isValidPort(port: number): boolean;
3
+ export declare function isValidDomain(domain: string): boolean;
4
+ export declare function isValidPm2Name(name: string): boolean;
5
+ //# sourceMappingURL=ip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ip.d.ts","sourceRoot":"","sources":["../../../src/domain/validation/ip.ts"],"names":[],"mappings":"AAGA,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAc1D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAMrD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIpD"}
@@ -0,0 +1,38 @@
1
+ const IPV4_REGEX = /^([0-9]{1,3}\.){3}[0-9]{1,3}$/;
2
+ const HOSTNAME_LABEL_REGEX = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/;
3
+ export function isValidIpOrHostname(input) {
4
+ if (!input)
5
+ return false;
6
+ if (IPV4_REGEX.test(input)) {
7
+ const octets = input.split('.').map(Number);
8
+ return octets.every((o) => o >= 0 && o <= 255);
9
+ }
10
+ const labels = input.split('.');
11
+ if (labels.length < 1)
12
+ return false;
13
+ if (!labels.every((label) => HOSTNAME_LABEL_REGEX.test(label)))
14
+ return false;
15
+ const hasLetter = labels.some((label) => /[a-zA-Z]/.test(label));
16
+ return hasLetter;
17
+ }
18
+ export function isValidPort(port) {
19
+ return Number.isInteger(port) && port >= 1 && port <= 65535;
20
+ }
21
+ export function isValidDomain(domain) {
22
+ if (!domain)
23
+ return true;
24
+ if (/^https?:\/\//.test(domain))
25
+ return false;
26
+ const labels = domain.split('.');
27
+ if (labels.length < 2)
28
+ return false;
29
+ return labels.every((label) => HOSTNAME_LABEL_REGEX.test(label));
30
+ }
31
+ export function isValidPm2Name(name) {
32
+ if (!name)
33
+ return false;
34
+ if (name.length > 64)
35
+ return false;
36
+ return /^[a-zA-Z0-9_-]+$/.test(name);
37
+ }
38
+ //# sourceMappingURL=ip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ip.js","sourceRoot":"","sources":["../../../src/domain/validation/ip.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,GAAG,+BAA+B,CAAC;AACnD,MAAM,oBAAoB,GAAG,+CAA+C,CAAC;AAE7E,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IAEzB,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC5C,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACpC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAE7E,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACjE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACpC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,KAAK,CAAC;IACnC,OAAO,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { shipnode, defineConfig } from './config/builder.js';
2
+ export type { ShipnodeConfig, SshConfig, Pm2Config, BackendConfig, HealthCheckConfig, DatabaseConfig, HookContext, HookFn, AppType, PkgManager } from './shared/types.js';
3
+ export { loadConfig } from './config/loader.js';
4
+ export { detectFramework, detectPkgManager, parsePackageJson } from './domain/framework/detector.js';
5
+ export type { FrameworkDetectionResult } from './shared/types.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC7D,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,iBAAiB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC1K,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACrG,YAAY,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { shipnode, defineConfig } from './config/builder.js';
2
+ export { loadConfig } from './config/loader.js';
3
+ export { detectFramework, detectPkgManager, parsePackageJson } from './domain/framework/detector.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { SshConfig, ExecResult } from '../../shared/types.js';
2
+ import type { RemoteExecutor } from '../../domain/remote/executor.js';
3
+ export interface SshConnectionOptions {
4
+ onReady?: () => void;
5
+ onError?: (err: Error) => void;
6
+ }
7
+ export declare class SshConnection implements RemoteExecutor {
8
+ private client;
9
+ private connected;
10
+ constructor();
11
+ connect(config: SshConfig): Promise<void>;
12
+ exec(command: string, options?: {
13
+ timeout?: number;
14
+ }): Promise<ExecResult>;
15
+ testConnection(): Promise<boolean>;
16
+ disconnect(): void;
17
+ isConnected(): boolean;
18
+ }
19
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/ssh/connection.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CAChC;AAED,qBAAa,aAAc,YAAW,cAAc;IAClD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;;IAMpB,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAmCzC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IA0C1E,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IASxC,UAAU,IAAI,IAAI;IAKlB,WAAW,IAAI,OAAO;CAGvB"}
@@ -0,0 +1,89 @@
1
+ import { readFileSync } from 'fs';
2
+ import { Client } from 'ssh2';
3
+ import { SshError } from '../../shared/errors.js';
4
+ export class SshConnection {
5
+ client;
6
+ connected = false;
7
+ constructor() {
8
+ this.client = new Client();
9
+ }
10
+ async connect(config) {
11
+ const sshConfig = {
12
+ host: config.host,
13
+ port: config.port,
14
+ username: config.user,
15
+ readyTimeout: 30000,
16
+ };
17
+ if (config.identityFile) {
18
+ sshConfig.privateKey = readFileSync(config.identityFile);
19
+ }
20
+ if (config.proxyMode === 'cloudflare') {
21
+ throw new SshError('Cloudflare Access proxy requires external connector. Use ssh2-http-proxy-agent.');
22
+ }
23
+ return new Promise((resolve, reject) => {
24
+ this.client.on('ready', () => {
25
+ this.connected = true;
26
+ resolve();
27
+ });
28
+ this.client.on('error', (err) => {
29
+ this.connected = false;
30
+ reject(new SshError(`SSH connection failed: ${err.message}`));
31
+ });
32
+ this.client.on('close', () => {
33
+ this.connected = false;
34
+ });
35
+ this.client.connect(sshConfig);
36
+ });
37
+ }
38
+ async exec(command, options) {
39
+ if (!this.connected) {
40
+ throw new SshError('Not connected to SSH server');
41
+ }
42
+ return new Promise((resolve, reject) => {
43
+ this.client.exec(command, { pty: false }, (err, stream) => {
44
+ if (err) {
45
+ return reject(new SshError(`Failed to execute command: ${err.message}`));
46
+ }
47
+ let stdout = '';
48
+ let stderr = '';
49
+ let exitCode = 0;
50
+ stream.on('data', (data) => {
51
+ stdout += data.toString();
52
+ });
53
+ stream.stderr.on('data', (data) => {
54
+ stderr += data.toString();
55
+ });
56
+ stream.on('close', (code) => {
57
+ exitCode = code ?? 1;
58
+ resolve({ stdout: stdout.trim(), stderr: stderr.trim(), exitCode });
59
+ });
60
+ stream.on('error', (err) => {
61
+ reject(new SshError(`Stream error: ${err.message}`));
62
+ });
63
+ if (options?.timeout) {
64
+ setTimeout(() => {
65
+ stream.close();
66
+ reject(new SshError(`Command timed out after ${options.timeout}ms`, undefined, stderr));
67
+ }, options.timeout);
68
+ }
69
+ });
70
+ });
71
+ }
72
+ async testConnection() {
73
+ try {
74
+ const result = await this.exec('echo ok', { timeout: 5000 });
75
+ return result.exitCode === 0 && result.stdout === 'ok';
76
+ }
77
+ catch {
78
+ return false;
79
+ }
80
+ }
81
+ disconnect() {
82
+ this.client.end();
83
+ this.connected = false;
84
+ }
85
+ isConnected() {
86
+ return this.connected;
87
+ }
88
+ }
89
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../src/infrastructure/ssh/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,MAAM,EAAgC,MAAM,MAAM,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AASlD,MAAM,OAAO,aAAa;IAChB,MAAM,CAAS;IACf,SAAS,GAAG,KAAK,CAAC;IAE1B;QACE,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,MAAiB;QAC7B,MAAM,SAAS,GAAkB;YAC/B,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,QAAQ,EAAE,MAAM,CAAC,IAAI;YACrB,YAAY,EAAE,KAAK;SACpB,CAAC;QAEF,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACxB,SAAS,CAAC,UAAU,GAAG,YAAY,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,MAAM,CAAC,SAAS,KAAK,YAAY,EAAE,CAAC;YACtC,MAAM,IAAI,QAAQ,CAAC,iFAAiF,CAAC,CAAC;QACxG,CAAC;QAED,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBAC3B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;gBACtB,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC9B,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;gBACvB,MAAM,CAAC,IAAI,QAAQ,CAAC,0BAA0B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAChE,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBAC3B,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;YACzB,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAe,EAAE,OAA8B;QACxD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,QAAQ,CAAC,6BAA6B,CAAC,CAAC;QACpD,CAAC;QAED,OAAO,IAAI,OAAO,CAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACjD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,EAAE,MAAqB,EAAE,EAAE;gBACvE,IAAI,GAAG,EAAE,CAAC;oBACR,OAAO,MAAM,CAAC,IAAI,QAAQ,CAAC,8BAA8B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBAC3E,CAAC;gBAED,IAAI,MAAM,GAAG,EAAE,CAAC;gBAChB,IAAI,MAAM,GAAG,EAAE,CAAC;gBAChB,IAAI,QAAQ,GAAG,CAAC,CAAC;gBAEjB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;oBACjC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC5B,CAAC,CAAC,CAAC;gBAEH,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;oBACxC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC5B,CAAC,CAAC,CAAC;gBAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAwB,EAAE,EAAE;oBAC9C,QAAQ,GAAG,IAAI,IAAI,CAAC,CAAC;oBACrB,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACtE,CAAC,CAAC,CAAC;gBAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;oBAChC,MAAM,CAAC,IAAI,QAAQ,CAAC,iBAAiB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBACvD,CAAC,CAAC,CAAC;gBAEH,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;oBACrB,UAAU,CAAC,GAAG,EAAE;wBACd,MAAM,CAAC,KAAK,EAAE,CAAC;wBACf,MAAM,CAAC,IAAI,QAAQ,CAAC,2BAA2B,OAAO,CAAC,OAAO,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;oBAC1F,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;gBACtB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAC7D,OAAO,MAAM,CAAC,QAAQ,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,UAAU;QACR,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;QAClB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACzB,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ import type { ShipnodeConfig } from '../shared/types.js';
2
+ import type { RemoteExecutor } from '../domain/remote/executor.js';
3
+ export declare class CaddyService {
4
+ private executor;
5
+ private config;
6
+ constructor(executor: RemoteExecutor, config: ShipnodeConfig);
7
+ configureBackend(): Promise<void>;
8
+ configureFrontend(): Promise<void>;
9
+ private generateBackendCaddyfile;
10
+ private generateFrontendCaddyfile;
11
+ }
12
+ //# sourceMappingURL=caddy.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caddy.service.d.ts","sourceRoot":"","sources":["../../src/services/caddy.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE,qBAAa,YAAY;IAErB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,MAAM;gBADN,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,cAAc;IAG1B,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAYjC,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAexC,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,yBAAyB;CAclC"}
@@ -0,0 +1,56 @@
1
+ export class CaddyService {
2
+ executor;
3
+ config;
4
+ constructor(executor, config) {
5
+ this.executor = executor;
6
+ this.config = config;
7
+ }
8
+ async configureBackend() {
9
+ if (!this.config.domain || !this.config.pm2)
10
+ return;
11
+ const port = this.config.backend?.port ?? 3000;
12
+ const appName = this.config.pm2.name;
13
+ const caddyConfig = this.generateBackendCaddyfile(appName, port);
14
+ const escaped = caddyConfig.replace(/'/g, "'\"'\"'");
15
+ await this.executor.exec(`echo '${escaped}' > /etc/caddy/conf.d/${appName}.caddy`);
16
+ await this.executor.exec('systemctl reload caddy');
17
+ }
18
+ async configureFrontend() {
19
+ if (!this.config.domain)
20
+ return;
21
+ const servePath = this.config.zeroDowntime
22
+ ? `${this.config.remotePath}/current`
23
+ : this.config.remotePath;
24
+ const appName = this.config.remotePath.split('/').pop() ?? 'app';
25
+ const caddyConfig = this.generateFrontendCaddyfile(appName, servePath);
26
+ const escaped = caddyConfig.replace(/'/g, "'\"'\"'");
27
+ await this.executor.exec(`echo '${escaped}' > /etc/caddy/conf.d/${appName}.caddy`);
28
+ await this.executor.exec('systemctl reload caddy');
29
+ }
30
+ generateBackendCaddyfile(appName, port) {
31
+ return `${this.config.domain} {
32
+ reverse_proxy localhost:${port}
33
+
34
+ encode gzip
35
+
36
+ log {
37
+ output file /var/log/caddy/${appName}.log
38
+ }
39
+ }`;
40
+ }
41
+ generateFrontendCaddyfile(appName, servePath) {
42
+ return `${this.config.domain} {
43
+ root * ${servePath}
44
+ file_server
45
+
46
+ try_files {path} /index.html
47
+
48
+ encode gzip
49
+
50
+ log {
51
+ output file /var/log/caddy/${appName}.log
52
+ }
53
+ }`;
54
+ }
55
+ }
56
+ //# sourceMappingURL=caddy.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caddy.service.js","sourceRoot":"","sources":["../../src/services/caddy.service.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,YAAY;IAEb;IACA;IAFV,YACU,QAAwB,EACxB,MAAsB;QADtB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,WAAM,GAAN,MAAM,CAAgB;IAC7B,CAAC;IAEJ,KAAK,CAAC,gBAAgB;QACpB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG;YAAE,OAAO;QAEpD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,IAAI,IAAI,CAAC;QAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;QACrC,MAAM,WAAW,GAAG,IAAI,CAAC,wBAAwB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAEjE,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACrD,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,OAAO,yBAAyB,OAAO,QAAQ,CAAC,CAAC;QACnF,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,OAAO;QAEhC,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY;YACxC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,UAAU;YACrC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;QAE3B,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,KAAK,CAAC;QACjE,MAAM,WAAW,GAAG,IAAI,CAAC,yBAAyB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAEvE,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACrD,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,OAAO,yBAAyB,OAAO,QAAQ,CAAC,CAAC;QACnF,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACrD,CAAC;IAEO,wBAAwB,CAAC,OAAe,EAAE,IAAY;QAC5D,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM;8BACF,IAAI;;;;;qCAKG,OAAO;;EAE1C,CAAC;IACD,CAAC;IAEO,yBAAyB,CAAC,OAAe,EAAE,SAAiB;QAClE,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM;aACnB,SAAS;;;;;;;;qCAQe,OAAO;;EAE1C,CAAC;IACD,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ import type { ShipnodeConfig } from '../shared/types.js';
2
+ import type { RemoteExecutor } from '../domain/remote/executor.js';
3
+ /**
4
+ * DeployService is a thin facade over the orchestrator and strategy.
5
+ *
6
+ * It wires the infrastructure (executor, config, cwd) into the domain
7
+ * (orchestrator + strategy) and kicks off the deployment. All the
8
+ * heavy lifting lives in the orchestrator and strategy modules.
9
+ */
10
+ export declare class DeployService {
11
+ private cwd;
12
+ private config;
13
+ private orchestrator;
14
+ private backendStrategy;
15
+ private frontendStrategy;
16
+ constructor(executor: RemoteExecutor, config: ShipnodeConfig, cwd: string);
17
+ execute(skipBuild?: boolean): Promise<void>;
18
+ }
19
+ //# sourceMappingURL=deploy.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.service.d.ts","sourceRoot":"","sources":["../../src/services/deploy.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAQnE;;;;;;GAMG;AACH,qBAAa,aAAa;IAStB,OAAO,CAAC,GAAG;IARb,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,gBAAgB,CAAmB;gBAGzC,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,cAAc,EACd,GAAG,EAAE,MAAM;IAqBf,OAAO,CAAC,SAAS,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CAYhD"}
@@ -0,0 +1,53 @@
1
+ import { execa } from 'execa';
2
+ import { ReleaseManager, DeployLock } from '../domain/release/manager.js';
3
+ import { HealthCheckService } from '../services/health.service.js';
4
+ import { CaddyService } from '../services/caddy.service.js';
5
+ import { DeployOrchestrator } from '../domain/deploy/orchestrator.js';
6
+ import { BackendStrategy } from '../domain/deploy/backend-strategy.js';
7
+ import { FrontendStrategy } from '../domain/deploy/frontend-strategy.js';
8
+ /**
9
+ * DeployService is a thin facade over the orchestrator and strategy.
10
+ *
11
+ * It wires the infrastructure (executor, config, cwd) into the domain
12
+ * (orchestrator + strategy) and kicks off the deployment. All the
13
+ * heavy lifting lives in the orchestrator and strategy modules.
14
+ */
15
+ export class DeployService {
16
+ cwd;
17
+ config;
18
+ orchestrator;
19
+ backendStrategy;
20
+ frontendStrategy;
21
+ constructor(executor, config, cwd) {
22
+ this.cwd = cwd;
23
+ this.config = config;
24
+ const releases = new ReleaseManager(executor, config.remotePath, config.keepReleases);
25
+ const lock = new DeployLock(executor, config.remotePath);
26
+ const healthCheck = new HealthCheckService(executor, config);
27
+ const caddy = new CaddyService(executor, config);
28
+ this.orchestrator = new DeployOrchestrator(config, executor, releases, lock, healthCheck, caddy);
29
+ this.backendStrategy = new BackendStrategy(config, cwd);
30
+ this.frontendStrategy = new FrontendStrategy(config, cwd);
31
+ }
32
+ async execute(skipBuild = false) {
33
+ const gitCommit = await getGitCommit(this.cwd);
34
+ const strategy = this.config.app === 'backend'
35
+ ? this.backendStrategy
36
+ : this.frontendStrategy;
37
+ await this.orchestrator.deploy(strategy, {
38
+ cwd: this.cwd,
39
+ skipBuild,
40
+ gitCommit,
41
+ });
42
+ }
43
+ }
44
+ async function getGitCommit(cwd) {
45
+ try {
46
+ const result = await execa('git', ['rev-parse', '--short', 'HEAD'], { cwd });
47
+ return result.stdout.trim();
48
+ }
49
+ catch {
50
+ return undefined;
51
+ }
52
+ }
53
+ //# sourceMappingURL=deploy.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.service.js","sourceRoot":"","sources":["../../src/services/deploy.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAG9B,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAEzE;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IASd;IARF,MAAM,CAAiB;IACvB,YAAY,CAAqB;IACjC,eAAe,CAAkB;IACjC,gBAAgB,CAAmB;IAE3C,YACE,QAAwB,EACxB,MAAsB,EACd,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;QAEnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;QACtF,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QACzD,MAAM,WAAW,GAAG,IAAI,kBAAkB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC7D,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAEjD,IAAI,CAAC,YAAY,GAAG,IAAI,kBAAkB,CACxC,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,WAAW,EACX,KAAK,CACN,CAAC;QAEF,IAAI,CAAC,eAAe,GAAG,IAAI,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACxD,IAAI,CAAC,gBAAgB,GAAG,IAAI,gBAAgB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,SAAS,GAAG,KAAK;QAC7B,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,SAAS;YAC5C,CAAC,CAAC,IAAI,CAAC,eAAe;YACtB,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC;QAE1B,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,EAAE;YACvC,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,SAAS;YACT,SAAS;SACV,CAAC,CAAC;IACL,CAAC;CACF;AAED,KAAK,UAAU,YAAY,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7E,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { ShipnodeConfig } from '../shared/types.js';
2
+ import type { RemoteExecutor } from '../domain/remote/executor.js';
3
+ export declare class HealthCheckService {
4
+ private executor;
5
+ private config;
6
+ constructor(executor: RemoteExecutor, config: ShipnodeConfig);
7
+ perform(): Promise<{
8
+ attempts: number;
9
+ responseMs: number;
10
+ }>;
11
+ private sleep;
12
+ }
13
+ //# sourceMappingURL=health.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.service.d.ts","sourceRoot":"","sources":["../../src/services/health.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAGnE,qBAAa,kBAAkB;IAE3B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,MAAM;gBADN,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,cAAc;IAG1B,OAAO,IAAI,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAuClE,OAAO,CAAC,KAAK;CAGd"}
@@ -0,0 +1,38 @@
1
+ import { HealthCheckError } from '../shared/errors.js';
2
+ export class HealthCheckService {
3
+ executor;
4
+ config;
5
+ constructor(executor, config) {
6
+ this.executor = executor;
7
+ this.config = config;
8
+ }
9
+ async perform() {
10
+ if (!this.config.healthCheck.enabled) {
11
+ return { attempts: 0, responseMs: 0 };
12
+ }
13
+ const { path, timeout, retries, startupDelay } = this.config.healthCheck;
14
+ const port = this.config.backend?.port ?? 3000;
15
+ const url = `http://localhost:${port}${path}`;
16
+ // Wait for startup delay
17
+ await this.sleep(startupDelay * 1000);
18
+ let lastStatus = 0;
19
+ let lastResponseMs = 0;
20
+ for (let attempt = 1; attempt <= retries; attempt++) {
21
+ const result = await this.executor.exec(`start_time=$(date +%s%N); ` +
22
+ `status=$(curl -s -o /dev/null -w "%{http_code}" --max-time ${timeout} "${url}"); ` +
23
+ `end_time=$(date +%s%N); ` +
24
+ `echo "$status $(( (end_time - start_time) / 1000000 ))"`);
25
+ const parts = result.stdout.split(' ');
26
+ lastStatus = parseInt(parts[0], 10);
27
+ lastResponseMs = parseInt(parts[1], 10);
28
+ if (lastStatus >= 200 && lastStatus < 400) {
29
+ return { attempts: attempt, responseMs: lastResponseMs };
30
+ }
31
+ }
32
+ throw new HealthCheckError(`Health check failed after ${retries} attempts. Last status: ${lastStatus}`, retries, lastStatus);
33
+ }
34
+ sleep(ms) {
35
+ return new Promise((resolve) => setTimeout(resolve, ms));
36
+ }
37
+ }
38
+ //# sourceMappingURL=health.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.service.js","sourceRoot":"","sources":["../../src/services/health.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,OAAO,kBAAkB;IAEnB;IACA;IAFV,YACU,QAAwB,EACxB,MAAsB;QADtB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,WAAM,GAAN,MAAM,CAAgB;IAC7B,CAAC;IAEJ,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;YACrC,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;QACxC,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QACzE,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,IAAI,IAAI,CAAC;QAC/C,MAAM,GAAG,GAAG,oBAAoB,IAAI,GAAG,IAAI,EAAE,CAAC;QAE9C,yBAAyB;QACzB,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;QAEtC,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,cAAc,GAAG,CAAC,CAAC;QAEvB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;YACpD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CACrC,4BAA4B;gBAC5B,8DAA8D,OAAO,KAAK,GAAG,MAAM;gBACnF,0BAA0B;gBAC1B,yDAAyD,CAC1D,CAAC;YAEF,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACvC,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACpC,cAAc,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAExC,IAAI,UAAU,IAAI,GAAG,IAAI,UAAU,GAAG,GAAG,EAAE,CAAC;gBAC1C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC;YAC3D,CAAC;QACH,CAAC;QAED,MAAM,IAAI,gBAAgB,CACxB,6BAA6B,OAAO,2BAA2B,UAAU,EAAE,EAC3E,OAAO,EACP,UAAU,CACX,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,EAAU;QACtB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IAC3D,CAAC;CACF"}