landfall 0.4.0 → 0.4.1
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 +9 -2
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -769,6 +769,6 @@ Runs on 127.0.0.1 only; the printed link logs the browser in.`,options:{port:{ty
|
|
|
769
769
|
|
|
770
770
|
Without --packages/--images both are updated. Package upgrades cannot be undone;
|
|
771
771
|
image updates can (\`lf undo\` puts the old image back). --compose replaces the
|
|
772
|
-
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)`},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}=V(t.server);if(t.compose){(e.length||t.packages||t.images)&&D(`--compose goes alone`);let i=await Hn(r,`updating docker compose`),a=await or(i),o=sr(a);if(!o.composeUpdate)return void(o.composeNote?w(o.composeNote):C(`compose ${a.compose} is the newest`));await j(`Update compose ${a.compose} to ${a.latestCompose} on ${n}?`)&&(S(`downloading docker compose`),await cr(i),C(`compose updated (lf undo puts the old one back)`));return}let i=!t.packages&&!t.images;e.length&&t.packages&&D(`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 Hn(r,`upgrading packages`),i=await Gn(e),a=t.security?i.updates.filter(e=>e.security):i.updates;i.mgr===`none`?T(`no supported package manager, skipping packages`):a.length?(Aa({...i,updates:a},!1),t.security&&i.mgr===`apk`&&D(`apk has no security-only upgrades; drop --security`),await j(`Upgrade ${a.length} package${a.length===1?``:`s`} on ${n}?`)&&(S(`upgrading packages on ${n}`),await qn(e,{only:t.security&&i.mgr===`apt-get`?a.map(e=>e.name):void 0,what:t.security?`${a.length} security packages`:`${a.length} packages`,reboot:t.reboot}),C(`packages upgraded`),t.reboot&&w(`rebooting if required; give it a minute`))):C(t.security?`no security updates`:`packages up to date`)}if(o){let t=await ur(r);if(e.length){let r=e.filter(e=>!t.some(t=>t.name===e));r.length&&D(`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`))w(x.dim(`${e.name}: ${e.note??e.state}`));let a=i.filter(t=>t.state===`update`||e.includes(t.name)&&t.state===`unknown`);a.length?(ja(a),await j(`Pull and recreate ${a.map(e=>e.name).join(`, `)}?`)&&(S(`updating images`),await dr(r,a),C(`images updated (lf undo puts the old ones back)`))):C(`images up to date`)}}});var Fa=`0.4.
|
|
772
|
+
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)`},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}=V(t.server);if(t.compose){(e.length||t.packages||t.images)&&D(`--compose goes alone`);let i=await Hn(r,`updating docker compose`),a=await or(i),o=sr(a);if(!o.composeUpdate)return void(o.composeNote?w(o.composeNote):C(`compose ${a.compose} is the newest`));await j(`Update compose ${a.compose} to ${a.latestCompose} on ${n}?`)&&(S(`downloading docker compose`),await cr(i),C(`compose updated (lf undo puts the old one back)`));return}let i=!t.packages&&!t.images;e.length&&t.packages&&D(`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 Hn(r,`upgrading packages`),i=await Gn(e),a=t.security?i.updates.filter(e=>e.security):i.updates;i.mgr===`none`?T(`no supported package manager, skipping packages`):a.length?(Aa({...i,updates:a},!1),t.security&&i.mgr===`apk`&&D(`apk has no security-only upgrades; drop --security`),await j(`Upgrade ${a.length} package${a.length===1?``:`s`} on ${n}?`)&&(S(`upgrading packages on ${n}`),await qn(e,{only:t.security&&i.mgr===`apt-get`?a.map(e=>e.name):void 0,what:t.security?`${a.length} security packages`:`${a.length} packages`,reboot:t.reboot}),C(`packages upgraded`),t.reboot&&w(`rebooting if required; give it a minute`))):C(t.security?`no security updates`:`packages up to date`)}if(o){let t=await ur(r);if(e.length){let r=e.filter(e=>!t.some(t=>t.name===e));r.length&&D(`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`))w(x.dim(`${e.name}: ${e.note??e.state}`));let a=i.filter(t=>t.state===`update`||e.includes(t.name)&&t.state===`unknown`);a.length?(ja(a),await j(`Pull and recreate ${a.map(e=>e.name).join(`, `)}?`)&&(S(`updating images`),await dr(r,a),C(`images updated (lf undo puts the old ones back)`))):C(`images up to date`)}}});var Fa=`0.4.1`;const Ia=[Ti,Ni,Ai,Yr,Xr,Zr,ai,ni,$r,ei,ti,ii,ri,ci,li,ui,Na,Pa,ma,ha,xa,Ca,Da,ya,ga,va,ba,Vr,Hr,Wr,Gr,Kr,Ur,na,ra,ia,ea,ta,oa,aa,fa,pa,xi,Oa],La={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`,upgrade:`update`},Ra=new Map(Ia.map(e=>[e.name,e]));function za(e){for(let t of[2,1]){let n=e.slice(0,t).join(` `),r=Ra.get(La[n]??n);if(r&&e.length>=t)return{cmd:r,rest:e.slice(t)}}return{rest:e}}function Ba(){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 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`,`logs`,`env`,`rollback`,`restart`,`stop`,`start`,`sites rm`]],[`Updates`,[`updates`,`update`]],[`History`,[`history`,`undo`]],[`Dashboard`,[`ui`]]],t=[`${x.bold(`landfall`)} ${x.dim(Fa)} — set up servers and deploy sites behind Traefik + HTTPS`,``,`${x.bold(`Usage:`)} lf <command> [options] ${x.dim(`lf <command> --help for details`)}`,` lf ui ${x.dim(`web dashboard in your browser`)}`];for(let[n,r]of e){t.push(``,x.bold(n));for(let e of r){let n=Ra.get(e);t.push(` ${(e+(n.args?` `+n.args:``)).padEnd(32)} ${n.summary.split(`
|
|
773
773
|
`)[0]}`)}}t.push(``,x.bold(`Quick start`),` lf setup vps --check ${x.dim(`# what is on vps, what setup would change`)}`,` lf setup vps ${x.dim("# docker + traefik on a server you reach with `ssh vps`")}`,` lf setup box --host 203.0.113.7 ${x.dim("# fresh box (root password once) → `ssh box` works")}`,` lf deploy ./dist -d example.com -s vps ${x.dim(`# publish a static site with HTTPS`)}`,` lf deploy ${x.dim(`# redeploy using ./.landfall.json`)}`,` lf deploy ./dist -y ${x.dim(`# no domain yet: serve at <name>.<ip>.sslip.io`)}`,` lf auth my-site --user anna ${x.dim(`# put a password in front of a site`)}`,` lf updates -s vps && lf update -s vps ${x.dim(`# pending OS/image updates, then apply them`)}`,` lf ls ${x.dim(`# what runs where`)}`,` lf history -s vps && lf undo -s vps ${x.dim(`# what landfall changed, and revert the last of it`)}`,``,x.dim(`Servers are ssh targets: any ~/.ssh/config alias works as --server.`)),console.log(t.join(`
|
|
774
774
|
`))}async function Va(e){if(!e.length||e[0]===`help`||e[0]===`-h`||e[0]===`--help`)return Ba(),0;if(e[0]===`-v`||e[0]===`--version`||e[0]===`version`)return console.log(Fa),0;let{cmd:t,rest:n}=za(e);if(!t)return console.error(`${x.red(`error:`)} unknown command "${e.join(` `)}"\n`),Ba(),2;let{values:r,positionals:i,rest:a}=se(t,n);if(r.help)return console.log(ce(t)),0;k(!!r.yes),$e.cmd=le(t,i,r);let o=await t.run(i,r,a);return typeof o==`number`?o:0}async function Ha(e){try{return await Va(e)}catch(e){return e instanceof E?console.error(`${x.red(`error:`)} ${e.message}`):console.error(e),1}}Ha(process.argv.slice(2)).then(e=>process.stdout.write(``,()=>process.exit(e)));export{fe as A,Ue as C,U as D,K as E,Ee as F,V as I,pe as L,de as M,R as N,W as O,B as P,x as R,lt as S,q as T,jt as _,Vi as a,Ot as b,mi as c,or as d,ur as f,mn as g,fn as h,Ea as i,H as j,G as k,oi as l,En as m,wa as n,Pi as o,Gn as p,Sa as r,Ii as s,Fa as t,sr as u,Ct as v,ke as w,Q as x,kt as y,oe as z};
|
package/dist/index.html
CHANGED
|
@@ -410,6 +410,8 @@ function openMenu(anchor, items) {
|
|
|
410
410
|
all[e.key === "Home" ? 0 : all.length - 1].focus();
|
|
411
411
|
}
|
|
412
412
|
};
|
|
413
|
+
// Keep focus where it is on mousedown: a browser that does not focus clicked buttons would blur the menu and close it before the click.
|
|
414
|
+
m.onmousedown = (e) => e.preventDefault();
|
|
413
415
|
m.addEventListener("focusout", (e) => {
|
|
414
416
|
if (!m.contains(e.relatedTarget)) closeMenu(false);
|
|
415
417
|
});
|
|
@@ -423,6 +425,8 @@ function openMenu(anchor, items) {
|
|
|
423
425
|
function closeMenu(restore = true) {
|
|
424
426
|
const m = $("#menu");
|
|
425
427
|
if (!m) return;
|
|
428
|
+
// Removing the focused item fires focusout, which calls closeMenu again: drop the id first so that call finds nothing.
|
|
429
|
+
m.removeAttribute("id");
|
|
426
430
|
m.remove();
|
|
427
431
|
menuAnchor?.setAttribute("aria-expanded", "false");
|
|
428
432
|
if (restore && menuAnchor?.isConnected) menuAnchor.focus({ preventScroll: true });
|
|
@@ -1685,6 +1689,9 @@ function settingsView(n) {
|
|
|
1685
1689
|
if (d) run(["traefik", "setup", ...sv(n), "--dashboard", d], reload);
|
|
1686
1690
|
}, "Apply");
|
|
1687
1691
|
const sites = S.overview[n]?.data?.sites ?? [];
|
|
1692
|
+
// Sites behind a Traefik the server already had (proxy.env): lf traefik setup refuses to touch it.
|
|
1693
|
+
const proxy = S.overview[n]?.data?.proxy;
|
|
1694
|
+
const foreign = proxy && proxy !== "traefik" ? proxy : "";
|
|
1688
1695
|
const external = (d) => S.overview[n]?.data?.others?.find((e) => e.domains.includes(d))?.name;
|
|
1689
1696
|
const servedBy = (d) => sites.find((s) => s.domains.includes(d))?.meta.name;
|
|
1690
1697
|
const serve = (d) => {
|
|
@@ -1721,8 +1728,8 @@ function settingsView(n) {
|
|
|
1721
1728
|
${x.suggest.length ? `<div class="note">${x.domains.length ? "Other domains" : "Domains"} your sites on this server already use: ${x.suggest.map((g) => `<b>${esc(g.domain)}</b> <span class="faint">(${g.hosts})</span> ${btn("Add", () => run(["server", "domain", n, "--add", g.domain], reload), "sm")}`).join(" ")}</div>` : ""}</div>
|
|
1722
1729
|
</div>
|
|
1723
1730
|
<div class="card">
|
|
1724
|
-
<h2>Traefik <span class="actions">${btn("Dashboard…", dashboard, "sm")}</span></h2>
|
|
1725
|
-
<div class="body">${rows([
|
|
1731
|
+
<h2>Traefik <span class="actions">${foreign ? "" : btn("Dashboard…", dashboard, "sm")}</span></h2>
|
|
1732
|
+
<div class="body">${foreign ? `<div class="note">Sites go through <b class="mono">${esc(foreign)}</b>, a Traefik landfall did not install and never changes: its dashboard is set up in its own config.</div>` : ""}${rows([
|
|
1726
1733
|
["image", esc(x.traefik.summary)],
|
|
1727
1734
|
["e-mail", esc(x.traefik.email ?? "none")],
|
|
1728
1735
|
["dashboard", x.traefik.dashboard ? `<a href="https://${esc(x.traefik.dashboard)}" target="_blank" rel="noopener">${esc(x.traefik.dashboard)}</a>` : "off"],
|