landfall 0.5.1 → 0.5.2
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.mjs +1 -1
- package/dist/index.html +15 -4
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -815,6 +815,6 @@ Runs on 127.0.0.1 only; the printed link logs the browser in.`,options:{port:{ty
|
|
|
815
815
|
|
|
816
816
|
Without --packages/--images both are updated. Package upgrades cannot be undone;
|
|
817
817
|
image updates can (\`lf undo\` puts the old image back). --compose replaces the
|
|
818
|
-
compose plugin setup installed with the newest release (undoable too).`,options:{server:F,packages:{type:`boolean`,desc:`upgrade OS packages`},security:{type:`boolean`,desc:`only security updates (apt, dnf/yum); implies --packages`},reboot:{type:`boolean`,desc:`reboot afterwards if the upgrade needs it`},images:{type:`boolean`,desc:`pull newer images and recreate their containers`},compose:{type:`boolean`,desc:`update the compose plugin landfall installed to the newest release`}},async run(e,t){let{name:n,server:r}=H(t.server);if(t.compose){(e.length||t.packages||t.images)&&k(`--compose goes alone`);let i=await hr(r,`updating docker compose`),a=await Nr(i),o=Pr(a);if(!o.composeUpdate)return void(o.composeNote?E(o.composeNote):T(`compose ${a.compose} is the newest`));await N(`Update compose ${a.compose} to ${a.latestCompose} on ${n}?`)&&(w(`downloading docker compose`),await Fr(i),T(`compose updated (lf undo puts the old one back)`));return}let i=!t.packages&&!t.images&&!t.security&&!t.reboot;e.length&&t.packages&&k(`sites only apply to --images`);let a=!e.length&&(t.packages||i||t.security),o=t.images||i||e.length>0;if(a){let e=await hr(r,`upgrading packages`),i=await vr(e),a=t.security?i.updates.filter(e=>e.security):i.updates;i.mgr===`none`?D(`no supported package manager, skipping packages`):a.length?(Io({...i,updates:a},!1),t.security&&(i.mgr===`apk`||i.mgr===`zypper`)&&k(`${i.mgr} has no security-only upgrades here; drop --security`),await N(`Upgrade ${a.length} package${a.length===1?``:`s`} on ${n}?`)&&(w(`upgrading packages on ${n}`),await br(e,{only:t.security?a.map(e=>e.name):void 0,what:t.security?`${a.length} security packages`:`${a.length} packages`,reboot:t.reboot}),T(`packages upgraded`),t.reboot&&E(`rebooting if required; give it a minute`))):T(t.security?`no security updates`:`packages up to date`)}if(o){let t=await Lr(r);if(e.length){let r=e.filter(e=>!t.some(t=>t.name===e));r.length&&k(`no site or service ${r.join(`, `)} on ${n}`)}let i=e.length?t.filter(t=>e.includes(t.name)):t;for(let e of i.filter(e=>e.state!==`update`&&e.state!==`current`&&e.state!==`unknown`))E(C.dim(`${e.name}: ${e.note??e.state}`));let a=i.filter(t=>t.state===`update`||e.includes(t.name)&&t.state===`unknown`);a.length?(Lo(a),await N(`Pull and recreate ${a.map(e=>e.name).join(`, `)}?`)&&(w(`updating images`),await Rr(r,a),T(`images updated (lf undo puts the old ones back)`))):T(`images up to date`)}}});var Vo=`0.5.
|
|
818
|
+
compose plugin setup installed with the newest release (undoable too).`,options:{server:F,packages:{type:`boolean`,desc:`upgrade OS packages`},security:{type:`boolean`,desc:`only security updates (apt, dnf/yum); implies --packages`},reboot:{type:`boolean`,desc:`reboot afterwards if the upgrade needs it`},images:{type:`boolean`,desc:`pull newer images and recreate their containers`},compose:{type:`boolean`,desc:`update the compose plugin landfall installed to the newest release`}},async run(e,t){let{name:n,server:r}=H(t.server);if(t.compose){(e.length||t.packages||t.images)&&k(`--compose goes alone`);let i=await hr(r,`updating docker compose`),a=await Nr(i),o=Pr(a);if(!o.composeUpdate)return void(o.composeNote?E(o.composeNote):T(`compose ${a.compose} is the newest`));await N(`Update compose ${a.compose} to ${a.latestCompose} on ${n}?`)&&(w(`downloading docker compose`),await Fr(i),T(`compose updated (lf undo puts the old one back)`));return}let i=!t.packages&&!t.images&&!t.security&&!t.reboot;e.length&&t.packages&&k(`sites only apply to --images`);let a=!e.length&&(t.packages||i||t.security),o=t.images||i||e.length>0;if(a){let e=await hr(r,`upgrading packages`),i=await vr(e),a=t.security?i.updates.filter(e=>e.security):i.updates;i.mgr===`none`?D(`no supported package manager, skipping packages`):a.length?(Io({...i,updates:a},!1),t.security&&(i.mgr===`apk`||i.mgr===`zypper`)&&k(`${i.mgr} has no security-only upgrades here; drop --security`),await N(`Upgrade ${a.length} package${a.length===1?``:`s`} on ${n}?`)&&(w(`upgrading packages on ${n}`),await br(e,{only:t.security?a.map(e=>e.name):void 0,what:t.security?`${a.length} security packages`:`${a.length} packages`,reboot:t.reboot}),T(`packages upgraded`),t.reboot&&E(`rebooting if required; give it a minute`))):T(t.security?`no security updates`:`packages up to date`)}if(o){let t=await Lr(r);if(e.length){let r=e.filter(e=>!t.some(t=>t.name===e));r.length&&k(`no site or service ${r.join(`, `)} on ${n}`)}let i=e.length?t.filter(t=>e.includes(t.name)):t;for(let e of i.filter(e=>e.state!==`update`&&e.state!==`current`&&e.state!==`unknown`))E(C.dim(`${e.name}: ${e.note??e.state}`));let a=i.filter(t=>t.state===`update`||e.includes(t.name)&&t.state===`unknown`);a.length?(Lo(a),await N(`Pull and recreate ${a.map(e=>e.name).join(`, `)}?`)&&(w(`updating images`),await Rr(r,a),T(`images updated (lf undo puts the old ones back)`))):T(`images up to date`)}}});var Vo=`0.5.2`;const Ho=[Eo,No,jo,Li,Ri,zi,qi,Wi,Vi,Hi,Ui,Ki,Gi,Ji,yo,bo,xo,zo,Bo,Xa,Za,ro,ao,lo,to,Qa,eo,no,Oi,ki,ji,Mi,Ni,Ai,Pi,ma,ha,ga,fa,pa,va,_a,Fa,Ia,So,Po],Uo={ls:`sites list`,sites:`sites list`,servers:`server list`,"server ls":`server list`,"server ssh":`ssh`,"sites logs":`logs`,"sites env":`env`,"sites rollback":`rollback`,"sites restart":`restart`,"sites stop":`stop`,"sites start":`start`,"sites ls":`sites list`,"sites remove":`sites rm`,"server remove":`server rm`,uninstall:`server uninstall`,"traefik remove":`traefik rm`,traefik:`traefik status`,firewall:`firewall status`,"firewall remove":`firewall rm`,log:`history`,revert:`undo`,web:`ui`,info:`server info`,"sites domains":`domains`,"sites auth":`auth`,"sites open":`open`,"sites tunnel":`tunnel`,upgrade:`update`},Wo=new Map(Ho.map(e=>[e.name,e]));function Go(e){for(let t of[2,1]){let n=e.slice(0,t).join(` `),r=Wo.get(Uo[n]??n);if(r&&e.length>=t)return{cmd:r,rest:e.slice(t)}}return{rest:e}}function Ko(){let e=[[`Servers`,[`setup`,`server add`,`server list`,`server info`,`server config`,`server domain`,`server use`,`server rm`,`server uninstall`,`ssh`,`keygen`]],[`Traefik`,[`traefik setup`,`traefik status`,`traefik dashboard`,`traefik logs`,`traefik restart`,`traefik rm`]],[`Firewall`,[`firewall status`,`firewall enable`,`firewall disable`,`firewall allow`,`firewall deny`,`firewall update`,`firewall rm`]],[`Sites`,[`deploy`,`sites list`,`sites info`,`domains`,`auth`,`open`,`tunnel`,`logs`,`env`,`rollback`,`restart`,`stop`,`start`,`sites rm`]],[`Updates`,[`updates`,`update`]],[`History`,[`history`,`undo`]],[`Dashboard`,[`ui`]]],t=[`${C.bold(`landfall`)} ${C.dim(Vo)} — set up servers and deploy sites behind Traefik + HTTPS`,``,`${C.bold(`Usage:`)} lf <command> [options] ${C.dim(`lf <command> --help for details`)}`,` lf ui ${C.dim(`web dashboard in your browser`)}`];for(let[n,r]of e){t.push(``,C.bold(n));for(let e of r){let n=Wo.get(e);t.push(` ${(e+(n.args?` `+n.args:``)).padEnd(32)} ${n.summary.split(`
|
|
819
819
|
`)[0]}`)}}t.push(``,C.bold(`Quick start`),` lf setup vps --check ${C.dim(`# what is on vps, what setup would change`)}`,` lf setup vps ${C.dim("# docker + traefik on a server you reach with `ssh vps`")}`,` lf setup box --host 203.0.113.7 ${C.dim("# fresh box (root password once) → `ssh box` works")}`,` lf deploy ./dist -d example.com -s vps ${C.dim(`# publish a static site with HTTPS`)}`,` lf deploy ${C.dim(`# redeploy using ./.landfall.json`)}`,` lf deploy ./dist -y ${C.dim(`# no domain yet: serve at <name>.<ip>.sslip.io`)}`,` lf auth my-site --user anna ${C.dim(`# put a password in front of a site`)}`,` lf updates -s vps && lf update -s vps ${C.dim(`# pending OS/image updates, then apply them`)}`,` lf ls ${C.dim(`# what runs where`)}`,` lf history -s vps && lf undo -s vps ${C.dim(`# what landfall changed, and revert the last of it`)}`,``,C.dim(`Servers are ssh targets: any ~/.ssh/config alias works as --server.`)),console.log(t.join(`
|
|
820
820
|
`))}async function qo(e){if(!e.length||e[0]===`help`||e[0]===`-h`||e[0]===`--help`)return Ko(),0;if(e[0]===`-v`||e[0]===`--version`||e[0]===`version`)return console.log(Vo),0;let{cmd:t,rest:n}=Go(e);if(!t)return console.error(`${C.red(`error:`)} unknown command "${e.join(` `)}"\n`),Ko(),2;let{values:r,positionals:i,rest:a}=me(t,n);if(r.help)return console.log(he(t)),0;j(!!r.yes),gt.cmd=ge(t,i,r);let o=await t.run(i,r,a);return typeof o==`number`?o:0}async function Jo(e){try{return await qo(e)}catch(e){return e instanceof O?(console.error(`${C.red(`error:`)} ${e.message}`),/--force\b/.test(e.message)&&oe({force:e.message})):console.error(e),1}}Jo(process.argv.slice(2)).then(e=>process.stdout.write(``,()=>process.exit(e)));export{H as A,ye as C,z as D,ve as E,C as M,pe as N,V as O,G as S,Te as T,Ge as _,uo as a,U as b,Ua as c,za as d,Yn as f,at as g,ht as h,po as i,L as j,Be as k,Ba as l,Nn as m,fo as n,mo as o,jn as p,go as r,ho as s,Vo as t,Ra as u,q as v,we as w,W as x,K as y};
|
package/dist/index.html
CHANGED
|
@@ -1022,7 +1022,6 @@ function deployDialog(det, o = {}) {
|
|
|
1022
1022
|
if (!n) return invalid("name", "Give the site a name");
|
|
1023
1023
|
if (!v.server) return invalid("server", "Pick a server");
|
|
1024
1024
|
const domains = v.domains.split(/[\s,]+/).filter(Boolean);
|
|
1025
|
-
if (v.noDomain && domains.length) return invalid("domains", "No domain is ticked below: clear these, or untick it");
|
|
1026
1025
|
const argv = ["deploy", "-y", ...sv(v.server), "-n", n, ...(v.noDomain ? ["--no-domain"] : domains.flatMap((d) => ["-d", d]))];
|
|
1027
1026
|
if (v.build?.trim() && v.build.trim() !== p?.build) argv.push("--build", v.build.trim());
|
|
1028
1027
|
if (v.spa && !p?.spa) argv.push("--spa");
|
|
@@ -1040,6 +1039,16 @@ function deployDialog(det, o = {}) {
|
|
|
1040
1039
|
run(argv, { server: v.server, cwd: det.path, after: openAfter });
|
|
1041
1040
|
},
|
|
1042
1041
|
});
|
|
1042
|
+
noDomainToggle();
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
/** The open dialog's "no domain" box greys out its domains field: ticked, the site gets none. */
|
|
1046
|
+
function noDomainToggle() {
|
|
1047
|
+
const box = $("#f-noDomain"), input = $("#f-domains");
|
|
1048
|
+
if (!box || !input) return;
|
|
1049
|
+
const sync = () => (input.disabled = box.checked);
|
|
1050
|
+
box.addEventListener("change", sync);
|
|
1051
|
+
sync();
|
|
1043
1052
|
}
|
|
1044
1053
|
|
|
1045
1054
|
/** After a deploy: offer the address it printed. */
|
|
@@ -1179,20 +1188,22 @@ function domainsDialog(n, m) {
|
|
|
1179
1188
|
text: `Each domain needs an A record pointing at ${ips.join(", ") || "the server"}. Traefik fetches the certificate once it resolves.`,
|
|
1180
1189
|
fields: [
|
|
1181
1190
|
{ name: "domains", label: "Domains (space separated)", value: m.domains.join(" ") },
|
|
1191
|
+
{ name: "noDomain", label: "No domain: reach it through a tunnel only", type: "check", value: !m.domains.length, hint: "Nothing is served on the internet; the site keeps running. Open via tunnel reaches it." },
|
|
1182
1192
|
...(main && !m.domains.includes(main) ? [{ name: "apex", label: `Also serve ${main}`, type: "check", value: false, hint: servedByOther(n, m.name, main) ? `${servedByOther(n, m.name, main)} serves it now and hands it over.` : "The main domain itself." }] : []),
|
|
1183
1193
|
...(free && !m.domains.includes(free) ? [{ name: "free", label: main ? `Also add ${free}` : `Also add the free address ${free}`, type: "check", value: false, hint: main ? `Under the main domain: works once *.${main} points at the server.` : "Works right away, no DNS setup." }] : []),
|
|
1184
1194
|
],
|
|
1185
1195
|
ok: "Save",
|
|
1186
1196
|
onOk: (v) => {
|
|
1187
|
-
const domains = v.domains.split(/[\s,]+/).filter(Boolean);
|
|
1188
|
-
if (v.free) domains.push(free);
|
|
1189
|
-
if (v.apex) domains.push(main);
|
|
1197
|
+
const domains = v.noDomain ? [] : v.domains.split(/[\s,]+/).filter(Boolean);
|
|
1198
|
+
if (v.free && !v.noDomain) domains.push(free);
|
|
1199
|
+
if (v.apex && !v.noDomain) domains.push(main);
|
|
1190
1200
|
// None left: the site keeps running, unrouted.
|
|
1191
1201
|
const args = domains.length ? domains.flatMap((d) => ["--set", d]) : m.domains.flatMap((d) => ["--rm", d]);
|
|
1192
1202
|
if (!args.length) return;
|
|
1193
1203
|
run(["domains", m.name, ...sv(n), ...args, "-y"], { server: n });
|
|
1194
1204
|
},
|
|
1195
1205
|
});
|
|
1206
|
+
noDomainToggle();
|
|
1196
1207
|
}
|
|
1197
1208
|
|
|
1198
1209
|
function authDialog(n, m) {
|