openship 0.6.1 → 0.6.6

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 (67) hide show
  1. package/README.md +7 -2
  2. package/dist/{bare-IWS2QHJ3.js → bare-OXJVW3AK.js} +11 -9
  3. package/dist/{chunk-VZ2ZHPS2.js → chunk-2YM3NI2E.js} +3 -9
  4. package/dist/chunk-4I2BM2DT.js +68 -0
  5. package/dist/{chunk-2NZSUXOB.js → chunk-5DMWSTOM.js} +310 -1459
  6. package/dist/{chunk-3FYIXVX5.js → chunk-77QDF6UD.js} +581 -122
  7. package/dist/{chunk-IOT5ZX23.js → chunk-A4GXYE4L.js} +449 -383
  8. package/dist/chunk-CBLD2HS5.js +428 -0
  9. package/dist/chunk-ELGRMZOW.js +25 -0
  10. package/dist/{chunk-TWJCLD4W.js → chunk-FE7ZZQSE.js} +5 -5
  11. package/dist/{chunk-3NZICQHF.js → chunk-HKRQXPTI.js} +31 -8
  12. package/dist/{chunk-IXM5245F.js → chunk-LFE5KQ7Y.js} +553 -538
  13. package/dist/{chunk-VDDMADOF.js → chunk-LH2PLRQO.js} +16 -2
  14. package/dist/{chunk-EMXI4UCU.js → chunk-LUKYPVJ4.js} +29 -10
  15. package/dist/{chunk-UC7YIEHT.js → chunk-MLYSEZQB.js} +352 -47
  16. package/dist/chunk-NK5N5LXN.js +1681 -0
  17. package/dist/{chunk-RRJJGVKE.js → chunk-OBPKYXQL.js} +75 -17
  18. package/dist/{chunk-6ODUBKUB.js → chunk-PCPVTH6M.js} +78 -88
  19. package/dist/chunk-Q7WGTQQ5.js +88 -0
  20. package/dist/{chunk-EW6KB2J7.js → chunk-QVW76WDL.js} +440 -74
  21. package/dist/{chunk-YIPMDGG6.js → chunk-RSR7PFIQ.js} +79 -23
  22. package/dist/{chunk-3LDWWY7U.js → chunk-SM26HDS2.js} +41 -7
  23. package/dist/{chunk-OTLSJYAC.js → chunk-UJ6JZM65.js} +5185 -341
  24. package/dist/chunk-UVNMQM7W.js +444 -0
  25. package/dist/chunk-VTGPEC6B.js +308 -0
  26. package/dist/chunk-WVAML4H6.js +2145 -0
  27. package/dist/{chunk-JIMUYEKN.js → chunk-YIMOCZBZ.js} +1 -1
  28. package/dist/chunk-YMMQRE5J.js +292 -0
  29. package/dist/cloud-75VIT4KV.js +37 -0
  30. package/dist/{detect-ALF4GXRX.js → detect-OIE5V3VZ.js} +3 -3
  31. package/dist/{docker-QECQJ5P4.js → docker-VTR2GK5Y.js} +16 -8
  32. package/dist/{docker-edge-executor-62N64MGS.js → docker-edge-executor-22IBXNYJ.js} +5 -5
  33. package/dist/{edge-import-SAYTRJLO.js → edge-import-WJUB4M7F.js} +27 -23
  34. package/dist/edge-preflight-GBNCPNVU.js +42 -0
  35. package/dist/{ensure-container-edge-CFSCPGZK.js → ensure-container-edge-KPI2CVXJ.js} +9 -8
  36. package/dist/{executor-PT6UEJ5M.js → executor-7LXXZPBR.js} +15 -5
  37. package/dist/host-channel-preflight-KE6OONYK.js +50 -0
  38. package/dist/index.js +3783 -4221
  39. package/dist/{nginx-YOEEFBTE.js → nginx-KUFMLMAC.js} +13 -5
  40. package/dist/node-bootstrap.js +241 -0
  41. package/dist/node-entry.js +52 -1
  42. package/dist/{openresty-lua-WGBO2M3Z.js → openresty-lua-ATTH35BE.js} +8 -2
  43. package/dist/privilege-I4W3ZNZK.js +19 -0
  44. package/dist/server/engine/samples/amavisd/amavisd.conf +25 -0
  45. package/dist/server/engine/samples/fail2ban/jail.d/sshd.local +4 -1
  46. package/dist/server/engine/samples/postfix/master.cf +14 -0
  47. package/dist/server/index.js +77659 -61909
  48. package/dist/server/migrations/0098_instance_product_mode.sql +12 -0
  49. package/dist/server/migrations/0099_mail_webmail_project.sql +24 -0
  50. package/dist/server/migrations/0100_server_ssh_private_key.sql +11 -0
  51. package/dist/server/migrations/0101_mail_resume_step.sql +12 -0
  52. package/dist/server/migrations/0102_project_workload_axes.sql +19 -0
  53. package/dist/server/migrations/0103_dns_credential.sql +41 -0
  54. package/dist/server/migrations/0104_instance_host_control.sql +15 -0
  55. package/dist/server/migrations/0105_mail_inbound_rules.sql +80 -0
  56. package/dist/server/migrations/0106_mcp_call_tracking.sql +23 -0
  57. package/dist/server/migrations/0107_build_minute_metering_indexes.sql +20 -0
  58. package/dist/server/migrations/0108_credential.sql +68 -0
  59. package/dist/server/migrations/meta/_journal.json +77 -0
  60. package/dist/{setup-M2ZDGH3O.js → setup-XGLGUUFW.js} +10 -9
  61. package/dist/ssh-client-JBMA7SN4.js +28 -0
  62. package/dist/up-K4BV7466.js +57 -0
  63. package/package.json +4 -2
  64. package/dist/chunk-IW2UKBVF.js +0 -144
  65. package/dist/chunk-ZCFVHA25.js +0 -89
  66. package/dist/cloud-M2YLWEXO.js +0 -31
  67. /package/dist/{dist-es-JGBPABJI.js → dist-es-AXFRS7PS.js} +0 -0
@@ -2,12 +2,22 @@
2
2
  ":" //# ; exec "$(command -v node || command -v bun)" "$0" "$@"
3
3
  import { createRequire as __ospCreateRequire } from "node:module";
4
4
  const require = __ospCreateRequire(import.meta.url);
5
+ import {
6
+ envOps,
7
+ opScript
8
+ } from "./chunk-YMMQRE5J.js";
9
+ import {
10
+ resolveEnvironment
11
+ } from "./chunk-UVNMQM7W.js";
5
12
  import {
6
13
  sq
7
14
  } from "./chunk-ZFJXPCQP.js";
8
15
  import {
16
+ SYSTEM,
17
+ answered,
18
+ refused,
9
19
  safeErrorMessage
10
- } from "./chunk-OTLSJYAC.js";
20
+ } from "./chunk-UJ6JZM65.js";
11
21
 
12
22
  // ../../packages/adapters/src/infra/openresty-lua.ts
13
23
  import { createHash, randomBytes } from "crypto";
@@ -126,6 +136,40 @@ function edgeRealIpConf() {
126
136
  }
127
137
  return lines.join("\n") + "\n";
128
138
  }
139
+ var CLOUD_EDGE_REAL_IP_HEADER = "X-Real-IP";
140
+ function isCloudFrontedHost(hostname) {
141
+ const host = hostname.trim().toLowerCase().replace(/\.+$/, "");
142
+ const suffix = `.${SYSTEM.DOMAINS.CLOUD_DOMAIN.toLowerCase()}`;
143
+ return host.length > suffix.length && host.endsWith(suffix);
144
+ }
145
+ function cloudEdgeRealIpConf(indent = " ") {
146
+ const lines = [
147
+ "# Openship Cloud's edge fronts this free host, so the peer is the edge and the",
148
+ "# visitor is named in X-Real-IP (which that front OVERWRITES). The http-scope block",
149
+ "# reads CF-Connecting-IP for Cloudflare-fronted custom domains, and there is one",
150
+ "# real_ip_header per scope \u2014 hence this override.",
151
+ "#",
152
+ "# Trust is scoped to this VHOST instead of to a peer list, deliberately. The Cloud",
153
+ "# edge reaches origins from its own address, which this box has no way to know and",
154
+ "# which Cloudflare's ranges (the visitor\u2192edge leg) do not contain \u2014 so a peer list",
155
+ "# here silently ignores the header and every free-domain visitor stays the edge.",
156
+ "#",
157
+ "# What bounds it is the server_name: this block exists only inside a <slug>.opsh.io",
158
+ "# vhost, a hostname whose only legitimate path is that edge. Someone who reaches this",
159
+ "# box directly on :80 with a forged Host AND a forged X-Real-IP can choose their own",
160
+ "# apparent address for THIS vhost \u2014 accepted, because the alternative is the address",
161
+ "# being wrong for every visitor all the time. Custom domains keep the peer-anchored",
162
+ "# http-scope block; do not widen this to them.",
163
+ `real_ip_header ${CLOUD_EDGE_REAL_IP_HEADER};`,
164
+ // OFF, unlike the http-scope block: the Cloud edge OVERWRITES X-Real-IP with a single
165
+ // address, so there is no list to walk, and "skip trusted addresses from the right"
166
+ // has no meaning once every address is trusted.
167
+ "real_ip_recursive off;",
168
+ "set_real_ip_from 0.0.0.0/0;",
169
+ "set_real_ip_from ::/0;"
170
+ ];
171
+ return lines.map((l) => `${indent}${l}`).join("\n") + "\n";
172
+ }
129
173
 
130
174
  // ../../packages/adapters/src/infra/edge-not-found.ts
131
175
  var EDGE_UNROUTED_SENTINEL = "openship-edge-unrouted";
@@ -204,6 +248,7 @@ var OPENRESTY_MGMT_PORT = 9145;
204
248
  var OPENRESTY_SITE_LUALIB = OPENRESTY_LUA_DIR.replace(/\/[^/]+$/, "");
205
249
  var EDGE_LUA_PACKAGE_PATH = `${OPENRESTY_SITE_LUALIB}/?.lua;;`;
206
250
  var EDGE_SERVER_NAMES_HASH_BUCKET_SIZE = 128;
251
+ var EDGE_CLIENT_MAX_BODY_SIZE = "50m";
207
252
  var EDGE_SHARED_DICTS = [
208
253
  /** Analytics counters. */
209
254
  { name: "analytics", size: "256m" },
@@ -266,6 +311,9 @@ var EDGE_CONTAINER_MOUNTS = [
266
311
  { host: `${EDGE_HOST_STATE_DIR}/acme`, container: "/var/www/acme" },
267
312
  { host: "/opt/openship/static", container: "/opt/openship/static" }
268
313
  ];
314
+ var EDGE_SAME_PATH_MOUNTS = EDGE_CONTAINER_MOUNTS.filter(
315
+ (m) => m.host === m.container
316
+ ).map((m) => m.host.replace(/\/+$/, ""));
269
317
  var EDGE_HOST_PATHS = {
270
318
  bin: OPENRESTY_DEFAULT_PATHS.bin,
271
319
  confPath: OPENRESTY_DEFAULT_PATHS.confPath,
@@ -393,6 +441,12 @@ async function ensureOpenRestyConfig(executor, paths) {
393
441
  );
394
442
  }
395
443
  }
444
+ const hasBodySize = await executor.exec(`grep -qE '^[[:space:]]*client_max_body_size' ${paths.confPath}`).then(() => true).catch(() => false);
445
+ if (!hasBodySize) {
446
+ await executor.exec(
447
+ `sed -i '/http *{/a \\ client_max_body_size ${EDGE_CLIENT_MAX_BODY_SIZE};' ${paths.confPath}`
448
+ );
449
+ }
396
450
  }
397
451
  function MINIMAL_NGINX_CONF(confDir, sitesDir) {
398
452
  return `# Auto-generated by Openship - safe to extend
@@ -409,6 +463,9 @@ http {
409
463
  # \`nginx -t\` outright for any server_name past ~63 chars, refusing the reload
410
464
  # and wedging every route on the box, not just the long one.
411
465
  server_names_hash_bucket_size ${EDGE_SERVER_NAMES_HASH_BUCKET_SIZE};
466
+ # See EDGE_CLIENT_MAX_BODY_SIZE \u2014 nginx's 1 MB default 413s an upload before the app
467
+ # ever sees it. A project's own value overrides this (server beats http).
468
+ client_max_body_size ${EDGE_CLIENT_MAX_BODY_SIZE};
412
469
  include ${sitesDir}/*.conf;
413
470
  }
414
471
  `;
@@ -455,7 +512,7 @@ var MANAGEMENT_BLOCK = `# Openship internal management - analytics & live-log st
455
512
  ${EDGE_MGMT_SERVER_BLOCK}
456
513
  `;
457
514
  var ACME_HTTP01_PORT = 49180;
458
- var ACME_CHALLENGE_LOCATION = ` location /.well-known/acme-challenge/ {
515
+ var ACME_CHALLENGE_LOCATION = ` location ^~ /.well-known/acme-challenge/ {
459
516
  proxy_pass http://127.0.0.1:${ACME_HTTP01_PORT};
460
517
  proxy_set_header Host $host;
461
518
  }`;
@@ -466,7 +523,7 @@ var EDGE_CHALLENGE_HOST_DIR = `${EDGE_HOST_STATE_DIR}/acme/oblien${EDGE_CHALLENG
466
523
  /\/$/,
467
524
  ""
468
525
  );
469
- var EDGE_CHALLENGE_LOCATION = ` location ${EDGE_CHALLENGE_URL_PREFIX} {
526
+ var EDGE_CHALLENGE_LOCATION = ` location ^~ ${EDGE_CHALLENGE_URL_PREFIX} {
470
527
  root ${EDGE_CHALLENGE_ROOT};
471
528
  default_type text/plain;
472
529
  try_files $uri =404;
@@ -600,27 +657,22 @@ async function ensureLuaScripts(executor, paths) {
600
657
  }
601
658
  }
602
659
  async function installGeoDeps(executor) {
603
- try {
604
- const hasPkg = async (cmd) => {
605
- try {
606
- await executor.exec(`command -v ${cmd}`);
607
- return true;
608
- } catch {
609
- return false;
610
- }
611
- };
612
- if (await hasPkg("apt-get")) {
613
- await executor.exec(
614
- "apt-get update -qq && apt-get install -y -qq libmaxminddb0 libmaxminddb-dev"
660
+ const profile = await resolveEnvironment(executor).catch((err) => safeErrorMessage(err));
661
+ const install = typeof profile === "string" ? refused(`the host profile could not be read: ${profile}`) : envOps(profile).pkgInstallVariants({
662
+ apt: answered(["libmaxminddb0", "libmaxminddb-dev"]),
663
+ dnf: answered(["libmaxminddb", "libmaxminddb-devel"]),
664
+ yum: answered(["libmaxminddb", "libmaxminddb-devel"]),
665
+ apk: answered(["libmaxminddb"]),
666
+ brew: refused("the bare edge is Linux-only, so Openship has no macOS package for it.")
667
+ });
668
+ if (install.supported) {
669
+ await executor.exec(opScript(install.value)).catch((err) => {
670
+ console.warn(
671
+ `[openresty] libmaxminddb install failed \u2014 geo lookups return nil: ${safeErrorMessage(err)}`
615
672
  );
616
- } else if (await hasPkg("dnf")) {
617
- await executor.exec("dnf install -y libmaxminddb libmaxminddb-devel");
618
- } else if (await hasPkg("yum")) {
619
- await executor.exec("yum install -y libmaxminddb libmaxminddb-devel");
620
- } else if (await hasPkg("apk")) {
621
- await executor.exec("apk add --no-cache libmaxminddb");
622
- }
623
- } catch {
673
+ });
674
+ } else {
675
+ console.warn(`[openresty] libmaxminddb not installed \u2014 geo lookups return nil: ${install.reason}`);
624
676
  }
625
677
  try {
626
678
  const exists = await executor.exists(GEOIP_DB_PATH);
@@ -665,12 +717,15 @@ async function deployLuaScripts(executor, paths) {
665
717
  }
666
718
 
667
719
  export {
720
+ isCloudFrontedHost,
721
+ cloudEdgeRealIpConf,
668
722
  OPENRESTY_LUA_DIR,
669
723
  LUA_LOGGER_PATH,
670
724
  RULES_GUARD_PATH,
671
725
  OPENRESTY_MGMT_PORT,
672
726
  EDGE_LUA_PACKAGE_PATH,
673
727
  EDGE_SERVER_NAMES_HASH_BUCKET_SIZE,
728
+ EDGE_CLIENT_MAX_BODY_SIZE,
674
729
  EDGE_SHARED_DICTS,
675
730
  parseNginxVersion,
676
731
  OPENRESTY_DEFAULT_PATHS,
@@ -679,6 +734,7 @@ export {
679
734
  EDGE_DEFAULT_CERT_CN,
680
735
  EDGE_HOST_STATE_DIR,
681
736
  EDGE_CONTAINER_MOUNTS,
737
+ EDGE_SAME_PATH_MOUNTS,
682
738
  EDGE_HOST_PATHS,
683
739
  detectOpenRestyPaths,
684
740
  buildReloadCommand,
@@ -2,6 +2,44 @@
2
2
  ":" //# ; exec "$(command -v node || command -v bun)" "$0" "$@"
3
3
  import { createRequire as __ospCreateRequire } from "node:module";
4
4
  const require = __ospCreateRequire(import.meta.url);
5
+ import {
6
+ formatDuration,
7
+ isRemoteConnectionError,
8
+ safeErrorMessage,
9
+ systemDebug
10
+ } from "./chunk-UJ6JZM65.js";
11
+
12
+ // ../../packages/adapters/src/system/probe-exec.ts
13
+ async function tryExec(executor, command, opts) {
14
+ try {
15
+ return opts ? await executor.exec(command, opts) : await executor.exec(command);
16
+ } catch {
17
+ return null;
18
+ }
19
+ }
20
+ async function probeExec(executor, command, scope) {
21
+ const startedAt = Date.now();
22
+ systemDebug(scope, `exec:start ${command}`);
23
+ try {
24
+ const result = await executor.exec(command, { timeout: 1e4 });
25
+ systemDebug(scope, `exec:ok ${command} (${formatDuration(startedAt)})`);
26
+ return { output: result.trim(), error: null };
27
+ } catch (err) {
28
+ const msg = safeErrorMessage(err);
29
+ if (isRemoteConnectionError(err)) {
30
+ systemDebug(scope, `exec:abort ${command} (${formatDuration(startedAt)}) ${msg}`);
31
+ throw err;
32
+ }
33
+ systemDebug(scope, `exec:fail ${command} (${formatDuration(startedAt)}) ${msg}`);
34
+ return { output: "", error: msg || `\`${command}\` failed` };
35
+ }
36
+ }
37
+ function withReason(message, error) {
38
+ if (!error) return message;
39
+ const first = error.split("\n").map((line) => line.trim()).find(Boolean);
40
+ if (!first) return message;
41
+ return `${message} \u2014 ${first.length > 200 ? `${first.slice(0, 199)}\u2026` : first}`;
42
+ }
5
43
 
6
44
  // ../../packages/adapters/src/system/port-listen.ts
7
45
  var PROC_NET_TCP_CMD = "cat /proc/net/tcp 2>/dev/null; cat /proc/net/tcp6 2>/dev/null; true";
@@ -75,13 +113,6 @@ async function waitForPortFree(executor, port, opts = {}) {
75
113
  }
76
114
 
77
115
  // ../../packages/adapters/src/runtime/port-conflict.ts
78
- async function tryExec(executor, command) {
79
- try {
80
- return await executor.exec(command);
81
- } catch {
82
- return null;
83
- }
84
- }
85
116
  async function resolveSystemdUnit(executor, pid) {
86
117
  const cgroup = await tryExec(executor, `cat /proc/${pid}/cgroup 2>/dev/null || true`);
87
118
  const unitMatch = cgroup?.match(/(?:^|\/)([^/\n]+\.service)(?:$|\n|\/)/m) ?? cgroup?.match(/(?:^|\/)([^/\n]+\.service)(?:$|\n|\/)/m);
@@ -144,6 +175,9 @@ async function describeProcess(executor, pid) {
144
175
  }
145
176
 
146
177
  export {
178
+ tryExec,
179
+ probeExec,
180
+ withReason,
147
181
  waitForPortListening,
148
182
  waitForPortFree,
149
183
  probeListeningPort,