@codions/portta 0.0.0-develop.155.sha-e6e3870 → 0.0.0-develop.159.sha-0da77d5
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.
- package/dist/cli.js +128 -73
- package/dist/documentation.json +1 -1
- package/dist/host.js +1 -1
- package/dist/runtime/.env.example +30 -17
- package/dist/runtime/VERSION +1 -1
- package/dist/runtime/docker/compose/features/panel-public.yaml +6 -8
- package/dist/runtime/docker/compose/features/web.yaml +8 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -20141,6 +20141,26 @@ function isLoopbackAddress(address) {
|
|
|
20141
20141
|
function isWildcardAddress(address) {
|
|
20142
20142
|
return address === "0.0.0.0" || address === "::";
|
|
20143
20143
|
}
|
|
20144
|
+
function isLoopbackIp(address) {
|
|
20145
|
+
return address === "::1" || /^127(\.\d{1,3}){3}$/.test(address);
|
|
20146
|
+
}
|
|
20147
|
+
function localhostDnsVerdict(addresses) {
|
|
20148
|
+
const hint = "see docs/product/guides/local-development.md#how-localhost-resolves";
|
|
20149
|
+
if (addresses.length === 0) return check2("dns.local", "warn", "local DNS", "could not confirm *.localhost resolution", hint);
|
|
20150
|
+
const stray = addresses.filter((address) => !isLoopbackIp(address));
|
|
20151
|
+
return stray.length === 0 ? check2("dns.local", "pass", "local DNS", "*.localhost resolves to loopback") : check2("dns.local", "warn", "local DNS", `*.localhost resolves to ${stray.join(", ")}, not loopback`, hint);
|
|
20152
|
+
}
|
|
20153
|
+
function loopbackDomainVerdict(domain2, resolved2, bind) {
|
|
20154
|
+
if (!isLoopbackIp(resolved2)) return null;
|
|
20155
|
+
const served = isWildcardAddress(bind) || bind === resolved2 || isLoopbackAddress(bind) && resolved2 === "127.0.0.1";
|
|
20156
|
+
return served ? check2("domain.reachable", "pass", "project hostnames", `*.${domain2} resolves to loopback, served on ${bind}`) : check2(
|
|
20157
|
+
"domain.reachable",
|
|
20158
|
+
"warn",
|
|
20159
|
+
"project hostnames",
|
|
20160
|
+
`*.${domain2} resolves to ${resolved2}, but Traefik listens on ${bind}`,
|
|
20161
|
+
"point the wildcard at an address Traefik listens on; see docs/product/guides/local-domains.md"
|
|
20162
|
+
);
|
|
20163
|
+
}
|
|
20144
20164
|
function publishesSensitivePort(published) {
|
|
20145
20165
|
return SENSITIVE_PORTS.some((port) => published.includes(`0.0.0.0:`) && published.includes(`->${port}`));
|
|
20146
20166
|
}
|
|
@@ -22364,7 +22384,7 @@ var init_package = __esm({
|
|
|
22364
22384
|
"package.json"() {
|
|
22365
22385
|
package_default = {
|
|
22366
22386
|
name: "@codions/portta",
|
|
22367
|
-
version: "0.0.0-develop.
|
|
22387
|
+
version: "0.0.0-develop.159.sha-0da77d5",
|
|
22368
22388
|
description: "Machine-first CLI for a shared Docker development gateway",
|
|
22369
22389
|
license: "MIT",
|
|
22370
22390
|
type: "module",
|
|
@@ -22438,7 +22458,7 @@ var init_version = __esm({
|
|
|
22438
22458
|
"use strict";
|
|
22439
22459
|
init_package();
|
|
22440
22460
|
CLI_VERSION = package_default.version;
|
|
22441
|
-
CLI_BUILD_DATE = true ? "2026-09-
|
|
22461
|
+
CLI_BUILD_DATE = true ? "2026-09-17T17:01:02Z" : void 0;
|
|
22442
22462
|
}
|
|
22443
22463
|
});
|
|
22444
22464
|
|
|
@@ -89799,7 +89819,7 @@ function createDaemonTaskflowHost(context) {
|
|
|
89799
89819
|
port: hostListen(env).port,
|
|
89800
89820
|
workflowBuiltinsDir: taskflowAssets(env).workflowBuiltinsDir,
|
|
89801
89821
|
version: env.PORTTA_VERSION || "0.0.0-local",
|
|
89802
|
-
buildDate: true ? "2026-09-
|
|
89822
|
+
buildDate: true ? "2026-09-17T17:01:02Z" : (/* @__PURE__ */ new Date()).toISOString(),
|
|
89803
89823
|
environmentStore: createEnvironmentStore(paths3.database),
|
|
89804
89824
|
hostEnvironmentProvider: new HostEnvironmentProvider(),
|
|
89805
89825
|
devContainerProvider: new DevContainerProvider(),
|
|
@@ -100395,7 +100415,7 @@ var require_network = __commonJS({
|
|
|
100395
100415
|
var exec3 = __require("child_process").exec;
|
|
100396
100416
|
var execSync = __require("child_process").execSync;
|
|
100397
100417
|
var execFileSync2 = __require("child_process").execFileSync;
|
|
100398
|
-
var
|
|
100418
|
+
var readFileSync39 = __require("fs").readFileSync;
|
|
100399
100419
|
var fs = __require("fs");
|
|
100400
100420
|
var util2 = require_util2();
|
|
100401
100421
|
var _platform = process.platform;
|
|
@@ -100883,7 +100903,7 @@ var require_network = __commonJS({
|
|
|
100883
100903
|
return result;
|
|
100884
100904
|
}
|
|
100885
100905
|
try {
|
|
100886
|
-
const content =
|
|
100906
|
+
const content = readFileSync39(file2, { encoding: "utf8" });
|
|
100887
100907
|
const lines = content.split("\n").filter((l) => /iface|source/.test(l));
|
|
100888
100908
|
lines.forEach((line4) => {
|
|
100889
100909
|
const parts2 = line4.replace(/\s+/g, " ").trim().split(" ");
|
|
@@ -120141,7 +120161,7 @@ async function runtimeAction(action, options, command) {
|
|
|
120141
120161
|
init_src();
|
|
120142
120162
|
init_src();
|
|
120143
120163
|
init_context();
|
|
120144
|
-
import { chmodSync as chmodSync9, existsSync as existsSync38, mkdirSync as mkdirSync18, readFileSync as
|
|
120164
|
+
import { chmodSync as chmodSync9, existsSync as existsSync38, mkdirSync as mkdirSync18, readFileSync as readFileSync31, readdirSync as readdirSync8, rmSync as rmSync10 } from "node:fs";
|
|
120145
120165
|
import { join as join57 } from "node:path";
|
|
120146
120166
|
init_errors3();
|
|
120147
120167
|
init_version();
|
|
@@ -120763,35 +120783,9 @@ async function runDoctor(context) {
|
|
|
120763
120783
|
const socket = (proxy.mounts ?? []).find((mount) => mount.source.includes("docker.sock"));
|
|
120764
120784
|
add(!socket ? check2("proxy.socket", "warn", "docker socket mount", "could not determine mount mode") : socket.readWrite ? check2("proxy.socket", "fail", "docker socket mount", "mounted read-write", "mount /var/run/docker.sock read-only (:ro)") : check2("proxy.socket", "pass", "docker socket mount", "read-only"));
|
|
120765
120785
|
}
|
|
120766
|
-
|
|
120767
|
-
|
|
120768
|
-
|
|
120769
|
-
add(database.state === "running" ? check2("db.state", "pass", "panel database", "running") : config3.webEnabled ? check2("db.state", "fail", "panel database", `state=${database.state}; the panel refuses to start without it`, "portta web up") : check2("db.state", "warn", "panel database", `state=${database.state}; the panel is off, so nothing needs it`, "portta web up"));
|
|
120770
|
-
const dbPublished = publishedBindings(database);
|
|
120771
|
-
add(dbPublished ? check2(
|
|
120772
|
-
"db.exposure",
|
|
120773
|
-
"fail",
|
|
120774
|
-
"panel database exposure",
|
|
120775
|
-
`publishes host ports: ${dbPublished}`,
|
|
120776
|
-
"remove every ports entry from docker/compose/features/db.yaml and recreate the database container"
|
|
120777
|
-
) : check2("db.exposure", "pass", "panel database exposure", "no host ports published"));
|
|
120778
|
-
add(database.networks.includes(config3.network) ? check2(
|
|
120779
|
-
"db.network.shared",
|
|
120780
|
-
"fail",
|
|
120781
|
-
"panel database network",
|
|
120782
|
-
`attached to the shared HTTP network '${config3.network}'`,
|
|
120783
|
-
`detach it; the panel database belongs only on '${dbNetwork}'`
|
|
120784
|
-
) : check2("db.network.shared", "pass", "panel database network", "off the shared HTTP network"));
|
|
120785
|
-
const data = await networkFacts(dbNetwork);
|
|
120786
|
-
add(!data.exists ? check2("db.network.internal", "warn", "panel data network", "not created yet", "portta web up") : data.internal ? check2("db.network.internal", "pass", "panel data network", `${dbNetwork} (internal)`) : check2(
|
|
120787
|
-
"db.network.internal",
|
|
120788
|
-
"fail",
|
|
120789
|
-
"panel data network",
|
|
120790
|
-
`${dbNetwork} is not internal`,
|
|
120791
|
-
"recreate the panel database network from docker/compose/features/db.yaml"
|
|
120792
|
-
));
|
|
120793
|
-
} else if (config3.webEnabled) {
|
|
120794
|
-
add(check2("db.state", "warn", "panel database", "container not created; the panel will refuse to start without it", "portta web up"));
|
|
120786
|
+
if (config3.webEnabled) {
|
|
120787
|
+
const databaseFile = databaseFileFor(root2);
|
|
120788
|
+
add(existsSync33(databaseFile) ? check2("db.file", "pass", "panel database", databaseFile) : check2("db.file", "warn", "panel database", `${databaseFile} not created yet; the panel creates it on first boot`, "portta web up"));
|
|
120795
120789
|
}
|
|
120796
120790
|
const panelIsPublic = (env["PORTTA_WEB_EXPOSE"] || "local") === "public";
|
|
120797
120791
|
if (traefik && traefik.state === "running") {
|
|
@@ -121153,14 +121147,7 @@ async function domainChecks(config3, env) {
|
|
|
121153
121147
|
const domain2 = config3.domain;
|
|
121154
121148
|
const panelExpose = env["PORTTA_WEB_EXPOSE"] || "local";
|
|
121155
121149
|
if (domain2 === "localhost" || domain2.endsWith(".localhost")) {
|
|
121156
|
-
|
|
121157
|
-
checks.push(probe.length > 0 ? check2("dns.local", "pass", "local DNS", "*.localhost resolves to loopback") : check2(
|
|
121158
|
-
"dns.local",
|
|
121159
|
-
"warn",
|
|
121160
|
-
"local DNS",
|
|
121161
|
-
"could not confirm *.localhost resolution",
|
|
121162
|
-
"see docs/product/guides/local-development.md if hostnames do not resolve"
|
|
121163
|
-
));
|
|
121150
|
+
checks.push(localhostDnsVerdict(await resolveIpv4("portta-probe.localhost")));
|
|
121164
121151
|
if (panelExpose !== "local") {
|
|
121165
121152
|
checks.push(check2(
|
|
121166
121153
|
"domain.reachable",
|
|
@@ -121176,6 +121163,8 @@ async function domainChecks(config3, env) {
|
|
|
121176
121163
|
const resolved2 = (await resolveIpv4(`portta-probe.${domain2}`))[0];
|
|
121177
121164
|
if (!resolved2) {
|
|
121178
121165
|
checks.push(check2("dns.wildcard", "fail", "wildcard DNS", `*.${domain2} does not resolve`, "portta dns setup"));
|
|
121166
|
+
} else if (isLoopbackIp(resolved2)) {
|
|
121167
|
+
checks.push(check2("dns.wildcard", "pass", "wildcard DNS", `*.${domain2} -> ${resolved2} (this machine)`));
|
|
121179
121168
|
} else {
|
|
121180
121169
|
const expected = env["PORTTA_PUBLIC_IP"] ?? "";
|
|
121181
121170
|
checks.push(!expected ? check2("dns.wildcard", "pass", "wildcard DNS", `*.${domain2} -> ${resolved2}`) : resolved2 === expected ? check2("dns.wildcard", "pass", "wildcard DNS", `*.${domain2} -> ${resolved2} (this host)`) : check2(
|
|
@@ -121187,7 +121176,19 @@ async function domainChecks(config3, env) {
|
|
|
121187
121176
|
));
|
|
121188
121177
|
}
|
|
121189
121178
|
const bind = config3.bindAddress;
|
|
121190
|
-
|
|
121179
|
+
const loopback = resolved2 ? loopbackDomainVerdict(domain2, resolved2, bind) : null;
|
|
121180
|
+
if (loopback) {
|
|
121181
|
+
checks.push(loopback);
|
|
121182
|
+
if (panelExpose !== "local") {
|
|
121183
|
+
checks.push(check2(
|
|
121184
|
+
"domain.reachable.remote",
|
|
121185
|
+
"warn",
|
|
121186
|
+
"project hostnames",
|
|
121187
|
+
`*.${domain2} resolves to loopback, and the panel is reached from elsewhere`,
|
|
121188
|
+
"point the wildcard at this host, or portta config set domain.mode auto"
|
|
121189
|
+
));
|
|
121190
|
+
}
|
|
121191
|
+
} else if (isLoopbackAddress(bind)) {
|
|
121191
121192
|
const publicIp = env["PORTTA_PUBLIC_IP"] ?? "";
|
|
121192
121193
|
const fix = publicIp && isPrivateAddress(publicIp) ? `portta config set gateway.bindAddress ${publicIp} serves them on that network only` : "portta public enable exposes the HTTP services that opted in";
|
|
121193
121194
|
checks.push(check2(
|
|
@@ -121380,7 +121381,7 @@ async function reexecBuiltCli(root2) {
|
|
|
121380
121381
|
init_src();
|
|
121381
121382
|
init_context();
|
|
121382
121383
|
init_errors3();
|
|
121383
|
-
import { existsSync as existsSync37, readdirSync as readdirSync7 } from "node:fs";
|
|
121384
|
+
import { existsSync as existsSync37, readdirSync as readdirSync7, readFileSync as readFileSync30 } from "node:fs";
|
|
121384
121385
|
import { basename as basename19, join as join56 } from "node:path";
|
|
121385
121386
|
var PORTTA_DIRECTORY = ".portta";
|
|
121386
121387
|
var OVERLAY_FILE = "compose.portta.yaml";
|
|
@@ -121388,6 +121389,7 @@ var COMPOSE_FILE = "compose.yaml";
|
|
|
121388
121389
|
var PANEL_WAIT_MS2 = 12e4;
|
|
121389
121390
|
var PANEL_POLL_MS2 = 500;
|
|
121390
121391
|
var DEMO_DIRECTORY_PREFIX = "portta-demo-";
|
|
121392
|
+
var MANIFEST_FILE = "portta.example.json";
|
|
121391
121393
|
function demoDirectories(home2) {
|
|
121392
121394
|
if (!existsSync37(home2)) return [];
|
|
121393
121395
|
return readdirSync7(home2, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith(DEMO_DIRECTORY_PREFIX)).map((entry) => join56(home2, entry.name)).sort();
|
|
@@ -121410,6 +121412,48 @@ function demoComposeArgs(stack, action) {
|
|
|
121410
121412
|
if (action === "up") return ["compose", ...files, "up", "-d"];
|
|
121411
121413
|
return ["compose", ...files, "down", "-v"];
|
|
121412
121414
|
}
|
|
121415
|
+
function readDemoProject(stackDir) {
|
|
121416
|
+
const path7 = join56(stackDir, PORTTA_DIRECTORY, MANIFEST_FILE);
|
|
121417
|
+
if (!existsSync37(path7)) return null;
|
|
121418
|
+
try {
|
|
121419
|
+
const manifest = JSON.parse(readFileSync30(path7, "utf8"));
|
|
121420
|
+
const project2 = manifest.project;
|
|
121421
|
+
if (typeof project2?.["slug"] !== "string" || typeof project2["name"] !== "string") return null;
|
|
121422
|
+
const repositories = (Array.isArray(manifest.repositories) ? manifest.repositories : []).filter((repository) => typeof repository?.["name"] === "string" && typeof repository["relativePath"] === "string").map((repository) => ({
|
|
121423
|
+
name: repository["name"],
|
|
121424
|
+
role: typeof repository["role"] === "string" ? repository["role"] : null,
|
|
121425
|
+
relativePath: repository["relativePath"]
|
|
121426
|
+
}));
|
|
121427
|
+
return {
|
|
121428
|
+
slug: project2["slug"],
|
|
121429
|
+
name: project2["name"],
|
|
121430
|
+
description: typeof project2["description"] === "string" ? project2["description"] : null,
|
|
121431
|
+
relativePath: typeof project2["relativePath"] === "string" ? project2["relativePath"] : basename19(stackDir),
|
|
121432
|
+
repositories
|
|
121433
|
+
};
|
|
121434
|
+
} catch {
|
|
121435
|
+
return null;
|
|
121436
|
+
}
|
|
121437
|
+
}
|
|
121438
|
+
async function registerDemoProjects(command, stacks, token) {
|
|
121439
|
+
const projects2 = stacks.map((stack) => readDemoProject(stack.dir)).filter((project2) => project2 !== null);
|
|
121440
|
+
if (projects2.length === 0) return;
|
|
121441
|
+
const { client, output: output2 } = clientFor(command, { actor: void 0, actorKind: "human", ...token ? { token } : {} });
|
|
121442
|
+
for (const { repositories, ...project2 } of projects2) {
|
|
121443
|
+
const created = await client.answer("POST", "/projects", project2);
|
|
121444
|
+
if (created.ok) output2.progress(`registered Project ${project2.name} (${project2.slug})`);
|
|
121445
|
+
else if (created.status === 409) output2.progress(`Project ${project2.slug} already registered`);
|
|
121446
|
+
else {
|
|
121447
|
+
output2.warning(`could not register Project ${project2.slug}: the panel answered ${created.status}`);
|
|
121448
|
+
output2.hint(`portta projects create --slug ${project2.slug} --path ${project2.relativePath}`);
|
|
121449
|
+
continue;
|
|
121450
|
+
}
|
|
121451
|
+
for (const repository of repositories) {
|
|
121452
|
+
const added = await client.answer("POST", `/projects/${encodeURIComponent(project2.slug)}/repositories`, repository);
|
|
121453
|
+
if (!added.ok && added.status !== 409) output2.warning(`could not register repository ${repository.name} on ${project2.slug}: the panel answered ${added.status}`);
|
|
121454
|
+
}
|
|
121455
|
+
}
|
|
121456
|
+
}
|
|
121413
121457
|
function outputFor(command) {
|
|
121414
121458
|
return new Output(workGlobals(command));
|
|
121415
121459
|
}
|
|
@@ -121419,15 +121463,24 @@ function demoHome(command) {
|
|
|
121419
121463
|
const configured = context.env["PORTTA_PROJECTS_HOME"]?.trim() || defaultProjectsHome(userHome, typeof process.getuid === "function" && process.getuid() === 0);
|
|
121420
121464
|
return normalizeProjectsHome(configured, context.root, userHome);
|
|
121421
121465
|
}
|
|
121422
|
-
|
|
121466
|
+
function requireDemoStacks(command) {
|
|
121467
|
+
const home2 = demoHome(command);
|
|
121468
|
+
const stacks = findDemoStacks(home2);
|
|
121469
|
+
if (stacks.length > 0) return stacks;
|
|
121470
|
+
throw new PreconditionError(
|
|
121471
|
+
`--demo found no portta-demo-* Compose project in ${home2}`,
|
|
121472
|
+
"clone the example repositories there, or set PORTTA_PROJECTS_HOME in .env to the directory that holds them"
|
|
121473
|
+
);
|
|
121474
|
+
}
|
|
121475
|
+
async function demoStacksUp(command, token) {
|
|
121423
121476
|
const output2 = outputFor(command);
|
|
121424
|
-
const stacks =
|
|
121425
|
-
if (stacks.length === 0) return;
|
|
121477
|
+
const stacks = requireDemoStacks(command);
|
|
121426
121478
|
output2.step("demo stacks");
|
|
121427
121479
|
for (const stack of stacks) {
|
|
121428
121480
|
output2.progress(`starting ${stack.name}`);
|
|
121429
121481
|
await runProcess("docker", demoComposeArgs(stack, "up"), { cwd: stack.dir, stdio: "inherit" });
|
|
121430
121482
|
}
|
|
121483
|
+
await registerDemoProjects(command, stacks, token);
|
|
121431
121484
|
}
|
|
121432
121485
|
async function demoStacksDown(command) {
|
|
121433
121486
|
const output2 = outputFor(command);
|
|
@@ -121625,6 +121678,7 @@ async function upCommand(profile, options, command) {
|
|
|
121625
121678
|
if (options.localRelease) selectLocalRelease(gatewayContext({ profile: profile ?? globals8(command).profile }));
|
|
121626
121679
|
const context = gatewayContext({ profile: profile ?? globals8(command).profile });
|
|
121627
121680
|
if (options.localRelease) context.env["PORTTA_LOCAL_RELEASE"] = "true";
|
|
121681
|
+
if (options.demo) requireDemoStacks(command);
|
|
121628
121682
|
if (context.config.profile === "remote-public" && context.config.tcpEnabled) throw new RefusedError("TCP entrypoints must not run on the remote-public profile");
|
|
121629
121683
|
if (context.config.profile === "remote-public" && context.config.webEnabled && context.config.webExpose === "vpn") {
|
|
121630
121684
|
throw new RefusedError(
|
|
@@ -121683,6 +121737,7 @@ async function devCommand(profile, options, command) {
|
|
|
121683
121737
|
await reexecBuiltCli(existing.root);
|
|
121684
121738
|
return;
|
|
121685
121739
|
}
|
|
121740
|
+
if (options.demo) requireDemoStacks(command);
|
|
121686
121741
|
const needsBootstrap = !existsSync38(join57(existing.root, ".env"));
|
|
121687
121742
|
output2.progress(`dev runs: ${[
|
|
121688
121743
|
...options.reset ? ["wipe Portta-managed containers and volumes"] : [],
|
|
@@ -121704,7 +121759,7 @@ async function devCommand(profile, options, command) {
|
|
|
121704
121759
|
await waitForPanel(command);
|
|
121705
121760
|
const demoToken = await ensureDevDemoOwner(command);
|
|
121706
121761
|
await finishWebUp(panel, command, false, demoToken);
|
|
121707
|
-
if (options.demo) await demoStacksUp(command);
|
|
121762
|
+
if (options.demo) await demoStacksUp(command, demoToken);
|
|
121708
121763
|
await urlsCommand({}, command);
|
|
121709
121764
|
}
|
|
121710
121765
|
async function downCommand(options, command) {
|
|
@@ -121886,7 +121941,7 @@ function readAliases(root2) {
|
|
|
121886
121941
|
const path7 = join57(root2, "config/traefik/dynamic/portta-aliases.yaml");
|
|
121887
121942
|
if (!existsSync38(path7)) return [];
|
|
121888
121943
|
try {
|
|
121889
|
-
return parseAliases(
|
|
121944
|
+
return parseAliases(readFileSync31(path7, "utf8"));
|
|
121890
121945
|
} catch {
|
|
121891
121946
|
return [];
|
|
121892
121947
|
}
|
|
@@ -122039,7 +122094,7 @@ async function dnsSetup(options, command) {
|
|
|
122039
122094
|
|
|
122040
122095
|
// src/commands/host-service.ts
|
|
122041
122096
|
init_config2();
|
|
122042
|
-
import { chmodSync as chmodSync10, existsSync as existsSync39, mkdirSync as mkdirSync19, readFileSync as
|
|
122097
|
+
import { chmodSync as chmodSync10, existsSync as existsSync39, mkdirSync as mkdirSync19, readFileSync as readFileSync32, realpathSync as realpathSync7, unlinkSync as unlinkSync6, writeFileSync as writeFileSync15 } from "node:fs";
|
|
122043
122098
|
import { homedir as homedir10 } from "node:os";
|
|
122044
122099
|
import { dirname as dirname29, join as join59 } from "node:path";
|
|
122045
122100
|
init_context();
|
|
@@ -122120,7 +122175,7 @@ var LAUNCHD_ENV_ENTRY_RE = /<key>([^<]+)<\/key>\s*<string>([^<]*)<\/string>/g;
|
|
|
122120
122175
|
function readEnvVarsFromUnit(filePath, platform4) {
|
|
122121
122176
|
let text;
|
|
122122
122177
|
try {
|
|
122123
|
-
text =
|
|
122178
|
+
text = readFileSync32(filePath, "utf8");
|
|
122124
122179
|
} catch {
|
|
122125
122180
|
return {};
|
|
122126
122181
|
}
|
|
@@ -122357,7 +122412,7 @@ async function configGet(name, command) {
|
|
|
122357
122412
|
const global3 = globals10(command);
|
|
122358
122413
|
const context = gatewayContext({ profile: global3.profile });
|
|
122359
122414
|
const item = setting(name);
|
|
122360
|
-
if (SECRETS.has(item.key)) throw new RefusedError(`${name} is a secret and is never printed`, "portta
|
|
122415
|
+
if (SECRETS.has(item.key)) throw new RefusedError(`${name} is a secret and is never printed`, "portta doctor reports whether it is set");
|
|
122361
122416
|
const value3 = context.env[item.key] ?? "";
|
|
122362
122417
|
const output2 = new Output(global3);
|
|
122363
122418
|
if (output2.json) output2.data({ setting: name, variable: item.key, value: value3 });
|
|
@@ -122521,7 +122576,7 @@ async function configSet(name, value3, options, command) {
|
|
|
122521
122576
|
const output2 = new Output(global3);
|
|
122522
122577
|
const context = gatewayContext({ profile: global3.profile });
|
|
122523
122578
|
const item = setting(name);
|
|
122524
|
-
if (SECRETS.has(item.key)) throw new RefusedError(`${name} is a secret and is not set this way`, "portta web
|
|
122579
|
+
if (SECRETS.has(item.key)) throw new RefusedError(`${name} is a secret and is not set this way`, "edit .env; portta web up generates PORTTA_AUTH_SECRET when it is empty");
|
|
122525
122580
|
if (item.allowed && !item.allowed.includes(value3)) throw new UsageError(`${name} must be one of: ${item.allowed.join(", ")}`);
|
|
122526
122581
|
let values;
|
|
122527
122582
|
if (name === "panel.access") values = await setPanelAccess(context.root, value3, output2);
|
|
@@ -122644,7 +122699,7 @@ async function setupCommand(options, command) {
|
|
|
122644
122699
|
const envFile = join61(target, ".env");
|
|
122645
122700
|
prepareEnvFile(envFile);
|
|
122646
122701
|
record6("environment", "updated", "environment prepared from .env.example");
|
|
122647
|
-
for (const directory of ["state", "state/auth", "state/ssh", "state/runner", "state/access", "state/cloudflared", "state/git", "state/
|
|
122702
|
+
for (const directory of ["state", "state/auth", "state/ssh", "state/runner", "state/access", "state/cloudflared", "state/git", "state/metrics", "state/environment", "state/logs", "config/tls", "config/traefik/dynamic"]) mkdirSync20(join61(target, directory), { recursive: true });
|
|
122648
122703
|
for (const directory of ["state/auth", "state/ssh", "state/runner", "state/cloudflared"]) chmodSync11(join61(target, directory), 448);
|
|
122649
122704
|
record6("directories", "ok", "gateway state directories");
|
|
122650
122705
|
const context = gatewayContext({ root: target, profile });
|
|
@@ -122666,7 +122721,7 @@ async function setupCommand(options, command) {
|
|
|
122666
122721
|
// src/commands/auth.ts
|
|
122667
122722
|
init_context();
|
|
122668
122723
|
init_errors3();
|
|
122669
|
-
import { readFileSync as
|
|
122724
|
+
import { readFileSync as readFileSync33 } from "node:fs";
|
|
122670
122725
|
function globals12(command) {
|
|
122671
122726
|
return command.optsWithGlobals();
|
|
122672
122727
|
}
|
|
@@ -122681,7 +122736,7 @@ function panelUrlFor2(command) {
|
|
|
122681
122736
|
}
|
|
122682
122737
|
async function authBootstrap(options, command) {
|
|
122683
122738
|
if (!options.name || !options.email) throw new UsageError("--name and --email are required");
|
|
122684
|
-
const password = options.passwordStdin ?
|
|
122739
|
+
const password = options.passwordStdin ? readFileSync33(0, "utf8").trim() : "";
|
|
122685
122740
|
if (!password) throw new UsageError("the password is read from stdin", 'printf %s "$PASSWORD" | portta auth bootstrap --name \u2026 --email \u2026 --password-stdin');
|
|
122686
122741
|
if (password.length < 10) throw new UsageError("the password must be at least 10 characters");
|
|
122687
122742
|
const { client, output: output2 } = clientFor(command, { actor: void 0, actorKind: "human" });
|
|
@@ -122714,7 +122769,7 @@ async function authLogin(options, command) {
|
|
|
122714
122769
|
}
|
|
122715
122770
|
async function readSecret() {
|
|
122716
122771
|
if (!process.stdin.isTTY) {
|
|
122717
|
-
const piped =
|
|
122772
|
+
const piped = readFileSync33(0, "utf8").trim();
|
|
122718
122773
|
if (piped) return piped;
|
|
122719
122774
|
throw new UsageError("no token on stdin", "pass --token, or run this in a terminal");
|
|
122720
122775
|
}
|
|
@@ -122853,7 +122908,7 @@ init_src();
|
|
|
122853
122908
|
init_context();
|
|
122854
122909
|
init_errors3();
|
|
122855
122910
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
122856
|
-
import { chmodSync as chmodSync12, mkdirSync as mkdirSync21, readFileSync as
|
|
122911
|
+
import { chmodSync as chmodSync12, mkdirSync as mkdirSync21, readFileSync as readFileSync34 } from "node:fs";
|
|
122857
122912
|
import { dirname as dirname31, join as join62 } from "node:path";
|
|
122858
122913
|
function globals13(command) {
|
|
122859
122914
|
return command.optsWithGlobals();
|
|
@@ -122885,7 +122940,7 @@ async function protectHost(hostInput, options, command) {
|
|
|
122885
122940
|
const entryPoint = options.entrypoint ?? (gatewayContext({ profile: globals13(command).profile }).config.tlsEnabled ? "websecure" : "web");
|
|
122886
122941
|
if (!/^[A-Za-z0-9_-]+$/.test(entryPoint)) throw new UsageError(`invalid entrypoint: ${entryPoint}`);
|
|
122887
122942
|
const generated = options.passwordStdin !== true;
|
|
122888
|
-
const password = generated ? randomBytes8(20).toString("base64url").slice(0, 20).match(/.{1,5}/g).join("-") :
|
|
122943
|
+
const password = generated ? randomBytes8(20).toString("base64url").slice(0, 20).match(/.{1,5}/g).join("-") : readFileSync34(0, "utf8").trim();
|
|
122889
122944
|
if (!password) throw new UsageError("no password on stdin");
|
|
122890
122945
|
const target = paths(command);
|
|
122891
122946
|
const existing = readProtectionStore(target.store);
|
|
@@ -122938,11 +122993,11 @@ async function protectStatus(hostInput, command) {
|
|
|
122938
122993
|
init_src();
|
|
122939
122994
|
init_errors3();
|
|
122940
122995
|
init_context();
|
|
122941
|
-
import { readFileSync as
|
|
122996
|
+
import { readFileSync as readFileSync35 } from "node:fs";
|
|
122942
122997
|
import { randomBytes as randomBytes9 } from "node:crypto";
|
|
122943
122998
|
function passwordFrom(options) {
|
|
122944
122999
|
if (options.passwordStdin) {
|
|
122945
|
-
const password2 =
|
|
123000
|
+
const password2 = readFileSync35(0, "utf8").trim();
|
|
122946
123001
|
if (!password2) throw new UsageError("no password on stdin");
|
|
122947
123002
|
if (password2.length < 10) throw new UsageError("the password must be at least 10 characters");
|
|
122948
123003
|
return { password: password2, generated: false };
|
|
@@ -123335,7 +123390,7 @@ function paths2(root2) {
|
|
|
123335
123390
|
init_src();
|
|
123336
123391
|
init_context();
|
|
123337
123392
|
init_errors3();
|
|
123338
|
-
import { chmodSync as chmodSync14, copyFileSync as copyFileSync5, cpSync as cpSync2, existsSync as existsSync43, mkdirSync as mkdirSync23, mkdtempSync as mkdtempSync3, readFileSync as
|
|
123393
|
+
import { chmodSync as chmodSync14, copyFileSync as copyFileSync5, cpSync as cpSync2, existsSync as existsSync43, mkdirSync as mkdirSync23, mkdtempSync as mkdtempSync3, readFileSync as readFileSync36, rmSync as rmSync12, statSync as statSync12, writeFileSync as writeFileSync17 } from "node:fs";
|
|
123339
123394
|
import { hostname as hostname3, tmpdir as tmpdir5 } from "node:os";
|
|
123340
123395
|
import { dirname as dirname32, join as join65 } from "node:path";
|
|
123341
123396
|
function globals16(command) {
|
|
@@ -123398,7 +123453,7 @@ async function backupCommand(options, command) {
|
|
|
123398
123453
|
database = await dumpDatabase(context, join65(staging, "panel.db"));
|
|
123399
123454
|
if (!database) {
|
|
123400
123455
|
output2.warning("the panel database was not included");
|
|
123401
|
-
output2.hint("it
|
|
123456
|
+
output2.hint("it exists once the panel has started, and copying it needs sqlite3 on this host; --no-database silences this");
|
|
123402
123457
|
}
|
|
123403
123458
|
}
|
|
123404
123459
|
const previous = process.umask(63);
|
|
@@ -123447,7 +123502,7 @@ async function restoreCommand(archive, options, command) {
|
|
|
123447
123502
|
if (extracted.failed) throw new UsageError("that file is not a Portta backup");
|
|
123448
123503
|
const manifestPath2 = join65(staging, "portta-backup.json");
|
|
123449
123504
|
if (!existsSync43(manifestPath2)) throw new UsageError("that archive has no Portta manifest");
|
|
123450
|
-
const manifest = parseManifest(
|
|
123505
|
+
const manifest = parseManifest(readFileSync36(manifestPath2, "utf8"));
|
|
123451
123506
|
if (!manifest) throw new UsageError("that archive has an unreadable Portta manifest");
|
|
123452
123507
|
output2.progress(`restoring a backup taken from Portta ${manifest.portta}`);
|
|
123453
123508
|
if (!options.force && await containerRunning(`${context.env["PORTTA_PROJECT_NAME"] || "portta"}-traefik-1`)) {
|
|
@@ -128559,7 +128614,7 @@ async function buildCommand(command) {
|
|
|
128559
128614
|
|
|
128560
128615
|
// src/commands/remote.ts
|
|
128561
128616
|
init_src();
|
|
128562
|
-
import { existsSync as existsSync44, mkdirSync as mkdirSync24, readFileSync as
|
|
128617
|
+
import { existsSync as existsSync44, mkdirSync as mkdirSync24, readFileSync as readFileSync37, readdirSync as readdirSync10, rmSync as rmSync13, writeFileSync as writeFileSync18 } from "node:fs";
|
|
128563
128618
|
import { basename as basename20, join as join67 } from "node:path";
|
|
128564
128619
|
import { createServer as createServer3 } from "node:net";
|
|
128565
128620
|
import { randomBytes as randomBytes10 } from "node:crypto";
|
|
@@ -128770,7 +128825,7 @@ function readTunnels(root2) {
|
|
|
128770
128825
|
for (const name of readdirSync10(directory).filter((entry) => entry.endsWith(".json"))) {
|
|
128771
128826
|
const path7 = join67(directory, name);
|
|
128772
128827
|
try {
|
|
128773
|
-
const record6 = parseTunnelRecord(basename20(name, ".json"),
|
|
128828
|
+
const record6 = parseTunnelRecord(basename20(name, ".json"), readFileSync37(path7, "utf8"));
|
|
128774
128829
|
if (record6) records.push(record6);
|
|
128775
128830
|
} catch {
|
|
128776
128831
|
}
|
|
@@ -128904,7 +128959,7 @@ init_src();
|
|
|
128904
128959
|
init_src();
|
|
128905
128960
|
init_context();
|
|
128906
128961
|
init_errors3();
|
|
128907
|
-
import { chmodSync as chmodSync15, closeSync as closeSync3, existsSync as existsSync45, mkdirSync as mkdirSync25, openSync as openSync3, readFileSync as
|
|
128962
|
+
import { chmodSync as chmodSync15, closeSync as closeSync3, existsSync as existsSync45, mkdirSync as mkdirSync25, openSync as openSync3, readFileSync as readFileSync38, rmSync as rmSync14, writeFileSync as writeFileSync19 } from "node:fs";
|
|
128908
128963
|
import { ReadStream } from "node:tty";
|
|
128909
128964
|
import { join as join68 } from "node:path";
|
|
128910
128965
|
function globals20(command) {
|
|
@@ -129047,7 +129102,7 @@ async function tunnelSetup(options, command) {
|
|
|
129047
129102
|
let token;
|
|
129048
129103
|
if (options.tokenFile) {
|
|
129049
129104
|
if (!existsSync45(options.tokenFile)) throw new UsageError(`no such file: ${options.tokenFile}`);
|
|
129050
|
-
token =
|
|
129105
|
+
token = readFileSync38(options.tokenFile, "utf8").replace(/\s+/g, "");
|
|
129051
129106
|
} else {
|
|
129052
129107
|
token = await promptForToken();
|
|
129053
129108
|
}
|
|
@@ -129225,10 +129280,10 @@ describe3(program2.command("version"), "Print the installed version").action((_o
|
|
|
129225
129280
|
describe3(program2.command("setup"), "Install or update a gateway from the packaged runtime").option("--dir <path>", "gateway installation directory").option("--dry-run", "print the idempotent plan without changing anything").option("--skip-pull", "do not pull images").action(setupCommand);
|
|
129226
129281
|
describe3(program2.command("bootstrap"), "Prepare this installation and run diagnostics").option("--skip-pull", "do not pull component images").action(bootstrapCommand);
|
|
129227
129282
|
describe3(program2.command("build"), "Build every local Portta image for this checkout").action((_options, command) => buildCommand(command));
|
|
129228
|
-
describe3(program2.command("up [profile]"), "Start gateway components").option("--local-release", "use the locally built image for this checkout").option("--attach", "run in the foreground").option("--demo", "also start portta-demo-* projects from PORTTA_PROJECTS_HOME
|
|
129229
|
-
describe3(program2.command("dev [profile]"), "Start a checkout from local Dockerfiles, never the published images").option("--reset", "stop every Portta-managed stack, drop their volumes, and start as if this checkout were new").option("--demo", "also start portta-demo-* projects from PORTTA_PROJECTS_HOME
|
|
129283
|
+
describe3(program2.command("up [profile]"), "Start gateway components").option("--local-release", "use the locally built image for this checkout").option("--attach", "run in the foreground").option("--demo", "also start portta-demo-* projects from PORTTA_PROJECTS_HOME").action((profile, options, command) => hasComposeRuntime() ? runtimeAction("up", {}, command) : upCommand(profile, options, command));
|
|
129284
|
+
describe3(program2.command("dev [profile]"), "Start a checkout from local Dockerfiles, never the published images").option("--reset", "stop every Portta-managed stack, drop their volumes, and start as if this checkout were new").option("--demo", "also start portta-demo-* projects from PORTTA_PROJECTS_HOME").action((profile, options, command) => devCommand(profile, options, command));
|
|
129230
129285
|
describe3(program2.command("down"), "Stop the current Compose runtime, or gateway components; keep projects and data").option("--demo", "also stop portta-demo-* projects from PORTTA_PROJECTS_HOME and drop their volumes").action((options, command) => hasComposeRuntime() ? runtimeAction("down", {}, command) : downCommand(options, command));
|
|
129231
|
-
describe3(program2.command("reset"), "Stop every Portta-managed stack, drop their volumes, and restart this checkout as if it were new").option("--demo", "also recreate portta-demo-* projects from PORTTA_PROJECTS_HOME
|
|
129286
|
+
describe3(program2.command("reset"), "Stop every Portta-managed stack, drop their volumes, and restart this checkout as if it were new").option("--demo", "also recreate portta-demo-* projects from PORTTA_PROJECTS_HOME").action((options, command) => resetCommand(options, command));
|
|
129232
129287
|
describe3(program2.command("restart"), "Restart the current Compose runtime, or recreate gateway components").action((_options, command) => hasComposeRuntime() ? runtimeAction("restart", {}, command) : restartCommand(command));
|
|
129233
129288
|
describe3(program2.command("status"), "Show the current Compose runtime, or gateway status").action((_options, command) => hasComposeRuntime() ? runtimeAction("status", {}, command) : statusCommand(command));
|
|
129234
129289
|
describe3(program2.command("doctor"), "Run read-only host and gateway diagnostics").action((_options, command) => doctorCommand(command));
|
|
@@ -129297,7 +129352,7 @@ describe3(dns.command("check"), "Resolve a wildcard probe").action((_options, co
|
|
|
129297
129352
|
describe3(dns.command("status"), "Show DNS configuration without secrets").action((_options, command) => dnsStatus(command));
|
|
129298
129353
|
describe3(dns.command("setup"), "Plan or apply a Cloudflare wildcard record").option("--target <ip>").option("--dry-run").action(dnsSetup);
|
|
129299
129354
|
var web = describe3(program2.command("web"), "Run the optional administration panel").option("--local-release", "use the locally built image for this checkout");
|
|
129300
|
-
describe3(web.command("up"), "Enable and start the panel").option("--expose <scope>", "local, tailscale, public or
|
|
129355
|
+
describe3(web.command("up"), "Enable and start the panel").option("--expose <scope>", "local, tailscale, vpn, public or domain").option("--port <number>").option("--read-only").option("--writable").action((options, command) => webUp({ ...options, localRelease: command.optsWithGlobals().localRelease }, command));
|
|
129301
129356
|
describe3(web.command("dev"), "Start the panel with hot reload").option("--expose <scope>").option("--port <number>").option("--read-only").option("--writable").action((options, command) => webUp({ ...options, dev: true }, command));
|
|
129302
129357
|
describe3(web.command("down"), "Stop the panel only").action((_options, command) => webDown(command));
|
|
129303
129358
|
describe3(web.command("disable"), "Stop and disable the panel").action((_options, command) => webDisable(command));
|