@envsync-cloud/deploy-cli 0.8.8 → 0.8.10

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 (2) hide show
  1. package/dist/index.js +97 -6
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4334,6 +4334,7 @@ function domainMap(rootDomain) {
4334
4334
  landing: rootDomain,
4335
4335
  app: `app.${rootDomain}`,
4336
4336
  api: `api.${rootDomain}`,
4337
+ manage_api: `manage-api.${rootDomain}`,
4337
4338
  auth: `auth.${rootDomain}`,
4338
4339
  obs: `obs.${rootDomain}`,
4339
4340
  mail: `mail.${rootDomain}`,
@@ -4397,6 +4398,8 @@ function buildRuntimeEnv2(config, generated) {
4397
4398
  const hosts = domainMap(config.domain.root_domain);
4398
4399
  const bucketName = "envsync-bucket";
4399
4400
  const oss = isOssConfig(config);
4401
+ const enterprise = !oss;
4402
+ const license = config.license ?? {};
4400
4403
  return {
4401
4404
  NODE_ENV: "production",
4402
4405
  ENVSYNC_EDITION: oss ? "oss" : "enterprise",
@@ -4409,8 +4412,14 @@ function buildRuntimeEnv2(config, generated) {
4409
4412
  ENVSYNC_LICENSE_CERT_PATH: oss ? "" : "/etc/envsync/license/enterprise-cert.pem",
4410
4413
  ENVSYNC_LICENSE_KEY_PATH: oss ? "" : "/etc/envsync/license/enterprise-key.pem",
4411
4414
  ENVSYNC_LICENSE_ROOT_CA_CERT_PATH: oss ? "" : "/etc/envsync/license/root-ca.pem",
4415
+ ENVSYNC_LICENSE_SERVER_URL: enterprise ? license.server_url ?? "" : "",
4416
+ ENVSYNC_LICENSE_KEY: enterprise ? license.key ?? "" : "",
4417
+ ENVSYNC_INSTALL_FINGERPRINT: enterprise ? license.install_fingerprint ?? "" : "",
4418
+ ENVSYNC_LICENSE_LEASE_TTL_SECONDS: String(license.lease_ttl_seconds ?? 300),
4419
+ ENVSYNC_STACK_NAME: config.services.stack_name,
4412
4420
  DB_AUTO_MIGRATE: "false",
4413
4421
  PORT: `${config.services.api_port}`,
4422
+ MANAGEMENT_API_PORT: `${config.services.management_api_port}`,
4414
4423
  DATABASE_HOST: "postgres",
4415
4424
  DATABASE_PORT: "5432",
4416
4425
  DATABASE_USER: "postgres",
@@ -4452,6 +4461,7 @@ function buildRuntimeEnv2(config, generated) {
4452
4461
  OPENFGA_STORE_ID: generated.openfga.store_id,
4453
4462
  OPENFGA_MODEL_ID: generated.openfga.model_id,
4454
4463
  OPENFGA_DB_PASSWORD: generated.secrets.openfga_db_password,
4464
+ MANAGEMENT_API_URL: oss ? "" : publicHttpsUrl(config, hosts.manage_api),
4455
4465
  CLICKSTACK_OPERATOR_EMAIL: generated.clickstack.operator_email,
4456
4466
  CLICKSTACK_OPERATOR_PASSWORD: generated.clickstack.operator_password,
4457
4467
  CLICKSTACK_ACCESS_KEY: generated.clickstack.access_key,
@@ -4541,6 +4551,7 @@ function renderTraefikDynamicConfig(config, generated) {
4541
4551
  const activeSlot = generated.deployment.active_slot;
4542
4552
  const apiServiceName = generated.deployment.maintenance_mode ? "envsync-api-maintenance" : "envsync-api";
4543
4553
  const landingEnabled = !isOssConfig(config);
4554
+ const managementEnabled = !isOssConfig(config);
4544
4555
  const otelAllowedOrigins = [
4545
4556
  ...landingEnabled ? publicHttpsOriginVariants(config, hosts.landing) : [],
4546
4557
  ...publicHttpsOriginVariants(config, hosts.app)
@@ -4602,6 +4613,16 @@ function renderTraefikDynamicConfig(config, generated) {
4602
4613
  " loadBalancer:",
4603
4614
  " servers:",
4604
4615
  " - url: http://api_maintenance:8080",
4616
+ ...managementEnabled ? [
4617
+ " envsync-management-api:",
4618
+ " loadBalancer:",
4619
+ " healthCheck:",
4620
+ " path: /health",
4621
+ " interval: 5s",
4622
+ " timeout: 3s",
4623
+ " servers:",
4624
+ ` - url: http://envsync-management-api:${config.services.management_api_port}`
4625
+ ] : [],
4605
4626
  ...landingEnabled ? [
4606
4627
  " landing:",
4607
4628
  " loadBalancer:",
@@ -4629,6 +4650,14 @@ function renderTraefikDynamicConfig(config, generated) {
4629
4650
  " tls:",
4630
4651
  " certResolver: letsencrypt"
4631
4652
  ] : [],
4653
+ ...managementEnabled ? [
4654
+ " management-api-router:",
4655
+ ` rule: Host(\`${hosts.manage_api}\`)`,
4656
+ " service: envsync-management-api",
4657
+ " entryPoints: [websecure]",
4658
+ " tls:",
4659
+ " certResolver: letsencrypt"
4660
+ ] : [],
4632
4661
  " web-router:",
4633
4662
  ` rule: Host(\`${hosts.app}\`)`,
4634
4663
  " service: web",
@@ -4707,10 +4736,12 @@ function renderFrontendRuntimeConfig(config, generated) {
4707
4736
  const hosts = domainMap(config.domain.root_domain);
4708
4737
  const otelEndpoint = publicHttpsUrl(config, hosts.obs);
4709
4738
  const activeReleaseVersion = generated.deployment.slots[generated.deployment.active_slot].release_version || config.release.version;
4739
+ const managementApiUrl = config.edition === "enterprise" ? publicHttpsUrl(config, hosts.manage_api) : "";
4710
4740
  return `window.__ENVSYNC_RUNTIME_CONFIG__ = ${JSON.stringify({
4711
4741
  apiBaseUrl: publicHttpsUrl(config, hosts.api),
4712
4742
  appBaseUrl: publicHttpsUrl(config, hosts.app),
4713
4743
  authBaseUrl: publicHttpsUrl(config, hosts.auth),
4744
+ managementApiUrl,
4714
4745
  keycloakRealm: config.auth.keycloak_realm,
4715
4746
  webClientId: config.auth.web_client_id,
4716
4747
  apiDocsUrl: publicHttpsUrl(config, hosts.api, "/docs"),
@@ -4772,6 +4803,7 @@ function renderStack(config, runtimeEnv, generated, mode, paths) {
4772
4803
  const includeRuntimeInfra = mode !== "base";
4773
4804
  const includeAppServices = mode === "full";
4774
4805
  const landingEnabled = !isOssConfig(config);
4806
+ const managementEnabled = !isOssConfig(config);
4775
4807
  const apiLicenseVolume = landingEnabled ? "\n volumes:\n - /etc/envsync/license:/etc/envsync/license:ro" : "";
4776
4808
  const deployment = createSteadyApiDeploymentState(config, generated);
4777
4809
  const stackName = config.services.stack_name;
@@ -4983,6 +5015,17 @@ ${renderEnvList({
4983
5015
  - source: otel_agent_conf
4984
5016
  target: /etc/otel-agent.yaml
4985
5017
  networks: [envsync]
5018
+ ${includeRuntimeInfra && managementEnabled ? `
5019
+
5020
+ envsync-management-api:
5021
+ image: ${config.images.management_api}
5022
+ environment:
5023
+ ${renderEnvList({
5024
+ ...apiEnvironment,
5025
+ PORT: `${config.services.management_api_port}`
5026
+ })}
5027
+ ${apiLicenseVolume}
5028
+ networks: [envsync]` : ""}
4986
5029
  ${includeAppServices ? `
4987
5030
 
4988
5031
  api_maintenance:
@@ -5117,6 +5160,8 @@ var LICENSE_BUNDLE_FILE = path3.join(LICENSE_ROOT, "enterprise-license-bundle.js
5117
5160
  var LICENSE_CERT_FILE = path3.join(LICENSE_ROOT, "enterprise-cert.pem");
5118
5161
  var LICENSE_KEY_FILE = path3.join(LICENSE_ROOT, "enterprise-key.pem");
5119
5162
  var LICENSE_ROOT_CA_FILE = path3.join(LICENSE_ROOT, "root-ca.pem");
5163
+ var LICENSE_FILE_MODE = 420;
5164
+ var LICENSE_DIR_MODE = 493;
5120
5165
  var VERSIONS_LOCK = path3.join(DEPLOY_ROOT, "versions.lock.json");
5121
5166
  var STACK_FILE = path3.join(DEPLOY_ROOT, "docker-stack.yaml");
5122
5167
  var BOOTSTRAP_BASE_STACK_FILE = path3.join(DEPLOY_ROOT, "docker-stack.bootstrap.base.yaml");
@@ -5218,6 +5263,7 @@ var DEFAULT_SOURCE_REPO_URL = "https://github.com/EnvSync-Cloud/envsync.git";
5218
5263
  var DEFAULT_ENTERPRISE_LICENSE_SERVER_URL = "https://license.envsync.cloud";
5219
5264
  var MANAGED_VERSIONED_IMAGE_PREFIXES = {
5220
5265
  api: "ghcr.io/envsync-cloud/envsync-api:",
5266
+ management_api: "ghcr.io/envsync-cloud/envsync-management-api:",
5221
5267
  keycloak: "envsync-keycloak:",
5222
5268
  web: "ghcr.io/envsync-cloud/envsync-web-static:",
5223
5269
  landing: "ghcr.io/envsync-cloud/envsync-landing-static:"
@@ -5333,6 +5379,27 @@ function writeFileMaybe(target, content, mode) {
5333
5379
  }
5334
5380
  writeFile(target, content, mode);
5335
5381
  }
5382
+ function ensureEnterpriseLicenseFilesReadable() {
5383
+ if (exists2(LICENSE_ROOT)) {
5384
+ try {
5385
+ fs3.chmodSync(LICENSE_ROOT, LICENSE_DIR_MODE);
5386
+ } catch {
5387
+ throw new Error(
5388
+ `Cannot update directory mode for ${LICENSE_ROOT}. Fix by running: chmod ${LICENSE_DIR_MODE.toString(8)} ${LICENSE_ROOT}`
5389
+ );
5390
+ }
5391
+ }
5392
+ for (const filePath of [LICENSE_BUNDLE_FILE, LICENSE_CERT_FILE, LICENSE_KEY_FILE, LICENSE_ROOT_CA_FILE]) {
5393
+ if (!exists2(filePath)) continue;
5394
+ try {
5395
+ fs3.chmodSync(filePath, LICENSE_FILE_MODE);
5396
+ } catch {
5397
+ throw new Error(
5398
+ `Cannot update file mode for ${filePath}. Fix by running: chmod ${LICENSE_FILE_MODE.toString(8)} ${filePath}`
5399
+ );
5400
+ }
5401
+ }
5402
+ }
5336
5403
  function exists2(target) {
5337
5404
  return fs3.existsSync(target);
5338
5405
  }
@@ -5424,6 +5491,7 @@ function domainMap2(rootDomain) {
5424
5491
  landing: rootDomain,
5425
5492
  app: `app.${rootDomain}`,
5426
5493
  api: `api.${rootDomain}`,
5494
+ manage_api: `manage-api.${rootDomain}`,
5427
5495
  auth: `auth.${rootDomain}`,
5428
5496
  obs: `obs.${rootDomain}`,
5429
5497
  mail: `mail.${rootDomain}`,
@@ -5599,6 +5667,7 @@ function versionedImages(version) {
5599
5667
  assertSemverVersion(version);
5600
5668
  return {
5601
5669
  api: `ghcr.io/envsync-cloud/envsync-api:${version}`,
5670
+ management_api: `ghcr.io/envsync-cloud/envsync-management-api:${version}`,
5602
5671
  keycloak: `envsync-keycloak:${version}`,
5603
5672
  web: `ghcr.io/envsync-cloud/envsync-web-static:${version}`,
5604
5673
  landing: `ghcr.io/envsync-cloud/envsync-landing-static:${version}`
@@ -5664,6 +5733,7 @@ function normalizeConfig(raw) {
5664
5733
  },
5665
5734
  images: {
5666
5735
  api: !raw.images?.api || isManagedVersionedImage(raw.images.api, "api") ? derivedImages.api : raw.images.api,
5736
+ management_api: !raw.images?.management_api || isManagedVersionedImage(raw.images.management_api, "management_api") ? derivedImages.management_api : raw.images.management_api,
5667
5737
  keycloak: !raw.images?.keycloak || isManagedVersionedImage(raw.images.keycloak, "keycloak") ? derivedImages.keycloak : raw.images.keycloak,
5668
5738
  web: !raw.images?.web || isManagedVersionedImage(raw.images.web, "web") ? derivedImages.web : raw.images.web,
5669
5739
  landing: !raw.images?.landing || isManagedVersionedImage(raw.images.landing, "landing") ? derivedImages.landing : raw.images.landing,
@@ -5674,6 +5744,7 @@ function normalizeConfig(raw) {
5674
5744
  services: {
5675
5745
  stack_name: stackName,
5676
5746
  api_port: requireDefined(raw.services?.api_port, "services.api_port"),
5747
+ management_api_port: raw.services?.management_api_port ?? 4001,
5677
5748
  public_http_port: raw.services?.public_http_port ?? 80,
5678
5749
  public_https_port: raw.services?.public_https_port ?? 443,
5679
5750
  clickstack_ui_port: requireDefined(raw.services?.clickstack_ui_port, "services.clickstack_ui_port"),
@@ -5726,6 +5797,7 @@ function normalizeConfig(raw) {
5726
5797
  key: raw.license.key,
5727
5798
  install_fingerprint: raw.license.install_fingerprint ?? deterministicInstallFingerprint(rootDomain, stackName),
5728
5799
  certificate_bundle_file: raw.license.certificate_bundle_file,
5800
+ lease_ttl_seconds: raw.license.lease_ttl_seconds,
5729
5801
  certificate_validity_days: raw.license.certificate_validity_days
5730
5802
  } : raw.edition === "oss" ? void 0 : {
5731
5803
  install_fingerprint: deterministicInstallFingerprint(rootDomain, stackName),
@@ -7148,6 +7220,7 @@ async function cmdSetup() {
7148
7220
  domain: { root_domain: rootDomain, acme_email: acmeEmail },
7149
7221
  images: {
7150
7222
  api: releaseImages.api,
7223
+ management_api: releaseImages.management_api,
7151
7224
  keycloak: releaseImages.keycloak,
7152
7225
  web: releaseImages.web,
7153
7226
  landing: releaseImages.landing,
@@ -7158,6 +7231,7 @@ async function cmdSetup() {
7158
7231
  services: {
7159
7232
  stack_name: "envsync",
7160
7233
  api_port: 4e3,
7234
+ management_api_port: 4001,
7161
7235
  public_http_port: 80,
7162
7236
  public_https_port: 443,
7163
7237
  clickstack_ui_port: 8080,
@@ -7308,6 +7382,9 @@ async function cmdDeploy() {
7308
7382
  logSection("Deploy");
7309
7383
  const { config, generated } = loadState();
7310
7384
  logReleaseContext(config);
7385
+ if (!isOssConfig2(config)) {
7386
+ ensureEnterpriseLicenseFilesReadable();
7387
+ }
7311
7388
  assertSwarmManager();
7312
7389
  assertBootstrapState(generated);
7313
7390
  if (!currentOptions.dryRun) {
@@ -7658,6 +7735,9 @@ async function cmdUpgrade(targetVersion) {
7658
7735
  ref: `v${desiredVersion}`
7659
7736
  };
7660
7737
  logReleaseContext(config);
7738
+ if (!isOssConfig2(config) && !currentOptions.dryRun) {
7739
+ ensureEnterpriseLicenseFilesReadable();
7740
+ }
7661
7741
  config.images = {
7662
7742
  ...config.images,
7663
7743
  ...versionedImages(desiredVersion)
@@ -7749,16 +7829,25 @@ async function requestEnterpriseLicenseCertificate(config, route) {
7749
7829
  return parseEnterpriseLicenseBundle(body.bundle);
7750
7830
  }
7751
7831
  function writeEnterpriseLicenseBundle(bundle) {
7752
- writeFileMaybe(LICENSE_BUNDLE_FILE, JSON.stringify(bundle, null, 2) + "\n", 384);
7753
- writeFileMaybe(LICENSE_CERT_FILE, bundle.certificate_pem.endsWith("\n") ? bundle.certificate_pem : `${bundle.certificate_pem}
7754
- `, 384);
7755
- writeFileMaybe(LICENSE_KEY_FILE, bundle.private_key_pem.endsWith("\n") ? bundle.private_key_pem : `${bundle.private_key_pem}
7756
- `, 384);
7832
+ writeFileMaybe(LICENSE_BUNDLE_FILE, JSON.stringify(bundle, null, 2) + "\n", LICENSE_FILE_MODE);
7833
+ writeFileMaybe(
7834
+ LICENSE_CERT_FILE,
7835
+ bundle.certificate_pem.endsWith("\n") ? bundle.certificate_pem : `${bundle.certificate_pem}
7836
+ `,
7837
+ LICENSE_FILE_MODE
7838
+ );
7839
+ writeFileMaybe(
7840
+ LICENSE_KEY_FILE,
7841
+ bundle.private_key_pem.endsWith("\n") ? bundle.private_key_pem : `${bundle.private_key_pem}
7842
+ `,
7843
+ LICENSE_FILE_MODE
7844
+ );
7757
7845
  writeFileMaybe(LICENSE_ROOT_CA_FILE, bundle.root_ca_pem.endsWith("\n") ? bundle.root_ca_pem : `${bundle.root_ca_pem}
7758
- `, 420);
7846
+ `, LICENSE_FILE_MODE);
7759
7847
  }
7760
7848
  async function ensureEnterpriseCertificateBundle(config) {
7761
7849
  if (isOssConfig2(config)) return;
7850
+ ensureEnterpriseLicenseFilesReadable();
7762
7851
  if (exists2(LICENSE_BUNDLE_FILE) && exists2(LICENSE_CERT_FILE) && exists2(LICENSE_KEY_FILE) && exists2(LICENSE_ROOT_CA_FILE)) {
7763
7852
  validateEnterpriseLicenseBundleFiles(config, false);
7764
7853
  return;
@@ -7817,6 +7906,7 @@ async function cmdLicense(action = "validate-cert") {
7817
7906
  logInfo("OSS deployments do not require license verification.");
7818
7907
  return;
7819
7908
  }
7909
+ ensureEnterpriseLicenseFilesReadable();
7820
7910
  if (action === "issue-cert" || action === "renew-cert") {
7821
7911
  const route = action === "renew-cert" ? "renew" : "issue";
7822
7912
  if (currentOptions.dryRun) {
@@ -8058,6 +8148,7 @@ async function cmdRestore(archivePath, autoDeploy = false) {
8058
8148
  if (exists2(restoredLicenseRoot)) {
8059
8149
  fs3.rmSync(LICENSE_ROOT, { recursive: true, force: true });
8060
8150
  fs3.cpSync(restoredLicenseRoot, LICENSE_ROOT, { recursive: true });
8151
+ ensureEnterpriseLicenseFilesReadable();
8061
8152
  }
8062
8153
  const config = loadConfig();
8063
8154
  for (const volume of STACK_VOLUMES) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envsync-cloud/deploy-cli",
3
- "version": "0.8.8",
3
+ "version": "0.8.10",
4
4
  "description": "CLI for self-hosted EnvSync deployment on Docker Swarm",
5
5
  "type": "module",
6
6
  "bin": {