feat: Removed comments
Server Deploy / deploy (push) Failing after 1m13s

This commit is contained in:
2026-07-24 09:56:54 +01:00
parent 1a6cf03c03
commit e798365be2
27 changed files with 1557 additions and 1714 deletions
+6 -10
View File
@@ -1,5 +1,5 @@
/* ==========================================================================
Vantage marketing site design tokens
Vantage marketing site design tokens
Palette is anchored on the logo navy (#0B2A58). The accent IS the brand
navy, lifting to a readable blue on dark grounds; status colours (up/down/
pending) are semantic and deliberately never reused as the accent.
@@ -346,7 +346,10 @@ code {
border-radius: 4px;
border: 1px solid transparent;
cursor: pointer;
transition: transform 0.1s ease, filter 0.15s ease, border-color 0.15s ease;
transition:
transform 0.1s ease,
filter 0.15s ease,
border-color 0.15s ease;
}
.btn:active {
@@ -439,14 +442,7 @@ code {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(
100deg,
#071628 0%,
#071628 26%,
rgba(7, 22, 40, 0.86) 42%,
rgba(7, 22, 40, 0.35) 62%,
rgba(7, 22, 40, 0.1) 100%
);
background: linear-gradient(100deg, #071628 0%, #071628 26%, rgba(7, 22, 40, 0.86) 42%, rgba(7, 22, 40, 0.35) 62%, rgba(7, 22, 40, 0.1) 100%);
z-index: 0;
}
+3 -3
View File
@@ -9,8 +9,8 @@ export default function OverviewPage() {
<span className="tag">Self-hosted fleet control plane</span>
<h1>Your servers, under one pane of glass you actually own.</h1>
<p className="lede">
Vantage holds SSH keys, runs scripts, watches services, stores secrets and opens consoles across every machine you manage. One agent per server, outbound connections only,
all state in your own database.
Vantage holds SSH keys, runs scripts, watches services, stores secrets and opens consoles across every machine you manage. One agent per server, outbound connections only, all
state in your own database.
</p>
<div className="hero__acts">
<Link className="btn btn--solid" href="/start">
@@ -32,7 +32,7 @@ export default function OverviewPage() {
<div className="split" style={{ marginTop: "2rem" }}>
<p className="prose">
Most small fleets end up with keys in a spreadsheet, scripts in someone&apos;s home directory, uptime checks in a separate service, secrets in a chat thread, and no record of
who ran what. None of those systems know about each other, so every question who can reach this box, what ran on it last, is it even up gets answered by hand.
who ran what. None of those systems know about each other, so every question who can reach this box, what ran on it last, is it even up gets answered by hand.
</p>
<div className="specs specs--flush">
<div className="spec">
+139 -132
View File
@@ -4,163 +4,170 @@ import { useEffect, useState } from "react";
/*
* The hero's signature element: a fleet panel that plays one honest cycle of
* what the product actually does a workflow runs three steps, a TLS monitor
* fails and opens an incident, a key revocation lands then rests. It is a
* what the product actually does a workflow runs three steps, a TLS monitor
* fails and opens an incident, a key revocation lands then rests. It is a
* dramatisation, not live data, so nothing here talks to an API.
*/
type LogLine = { time: string; body: React.ReactNode };
type Beat = {
at: number;
line: LogLine;
effect?: "incident" | "runDone" | "revoked";
at: number;
line: LogLine;
effect?: "incident" | "runDone" | "revoked";
};
const BEATS: Beat[] = [
{ at: 600, line: { time: "14:22:02", body: "running · step 1/3 · pull image" } },
{ at: 1500, line: { time: "14:22:04", body: "running · step 2/3 · migrate database" } },
{
at: 2600,
line: { time: "14:22:07", body: <><span className="ok">ok</span> · migrate database · exit 0</> },
},
{
at: 3400,
line: { time: "14:22:08", body: "running · step 3/3 · restart service" },
effect: "incident",
},
{
at: 4300,
line: { time: "14:22:10", body: <><span className="er">monitor</span> · edge-gw-02 tls · connection refused</> },
},
{
at: 5200,
line: { time: "14:22:11", body: <><span className="ok">ok</span> · restart service · exit 0</> },
effect: "runDone",
},
{
at: 6000,
line: { time: "14:22:12", body: <>run finished · <span className="ok">success</span> · 3 steps · 1 server</> },
effect: "revoked",
},
{ at: 600, line: { time: "14:22:02", body: "running · step 1/3 · pull image" } },
{ at: 1500, line: { time: "14:22:04", body: "running · step 2/3 · migrate database" } },
{
at: 2600,
line: {
time: "14:22:07",
body: (
<>
<span className="ok">ok</span> · migrate database · exit 0
</>
),
},
},
{
at: 3400,
line: { time: "14:22:08", body: "running · step 3/3 · restart service" },
effect: "incident",
},
{
at: 4300,
line: {
time: "14:22:10",
body: (
<>
<span className="er">monitor</span> · edge-gw-02 tls · connection refused
</>
),
},
},
{
at: 5200,
line: {
time: "14:22:11",
body: (
<>
<span className="ok">ok</span> · restart service · exit 0
</>
),
},
effect: "runDone",
},
{
at: 6000,
line: {
time: "14:22:12",
body: (
<>
run finished · <span className="ok">success</span> · 3 steps · 1 server
</>
),
},
effect: "revoked",
},
];
const FIRST_LINE: LogLine = { time: "14:22:01", body: "queued · deploy-app · 1 server" };
const MAX_LINES = 7;
export function InstrumentPanel() {
const [lines, setLines] = useState<LogLine[]>([FIRST_LINE]);
const [incident, setIncident] = useState(false);
const [runActive, setRunActive] = useState(true);
const [revoked, setRevoked] = useState(false);
const [resting, setResting] = useState(false);
const [lines, setLines] = useState<LogLine[]>([FIRST_LINE]);
const [incident, setIncident] = useState(false);
const [runActive, setRunActive] = useState(true);
const [revoked, setRevoked] = useState(false);
const [resting, setResting] = useState(false);
useEffect(() => {
const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
useEffect(() => {
const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const apply = (effect: Beat["effect"]) => {
if (effect === "incident") setIncident(true);
if (effect === "runDone") setRunActive(false);
if (effect === "revoked") setRevoked(true);
};
const apply = (effect: Beat["effect"]) => {
if (effect === "incident") setIncident(true);
if (effect === "runDone") setRunActive(false);
if (effect === "revoked") setRevoked(true);
};
if (reduced) {
setLines([FIRST_LINE, ...BEATS.map((b) => b.line)].slice(-MAX_LINES));
BEATS.forEach((b) => apply(b.effect));
setResting(true);
return;
}
if (reduced) {
setLines([FIRST_LINE, ...BEATS.map((b) => b.line)].slice(-MAX_LINES));
BEATS.forEach((b) => apply(b.effect));
setResting(true);
return;
}
const timers = BEATS.map((beat) =>
window.setTimeout(() => {
setLines((prev) => [...prev, beat.line].slice(-MAX_LINES));
apply(beat.effect);
}, beat.at)
);
timers.push(window.setTimeout(() => setResting(true), 6600));
const timers = BEATS.map((beat) =>
window.setTimeout(() => {
setLines((prev) => [...prev, beat.line].slice(-MAX_LINES));
apply(beat.effect);
}, beat.at),
);
timers.push(window.setTimeout(() => setResting(true), 6600));
return () => timers.forEach(window.clearTimeout);
}, []);
return () => timers.forEach(window.clearTimeout);
}, []);
return (
<div className="instrument">
<div className="rail">
<div className="instrument__bar">
<span>
<b>northgate</b> · fleet
</span>
<span>12 servers</span>
<span>{incident ? "10 up" : "11 up"}</span>
<span>{incident ? "2 down" : "1 down"}</span>
<span>3 monitors</span>
<span>{runActive ? "1 run active" : "no runs active"}</span>
<span className="instrument__clock">14:22:12 UTC</span>
</div>
<div className="panes">
<section className="pane" aria-label="Fleet status">
<h2 className="pane__h">
Fleet <span>{revoked ? "key revoked · 1 server updated" : "agents polling"}</span>
</h2>
<Row host="proxmox-node-1" sub="4 keys · 12% cpu" state="up" label="Active" />
<Row host="db-primary" sub="3 keys · 61% cpu" state="up" label="Active" />
<Row
host="edge-gw-02"
sub={incident ? "2 keys · tls refused" : "2 keys · tls 41d"}
state={incident ? "down" : "up"}
label={incident ? "Incident" : "Active"}
/>
<Row host="win-build-01" sub="agent 1.4.1 · update ready" state="pend" label="Pending" />
<Row
host="app-worker-03"
sub={revoked ? "4 keys · 1 revoked" : "5 keys · idle"}
state="up"
label="Active"
/>
</section>
<section className="pane" aria-label="Workflow run">
<h2 className="pane__h">
Run <span>run_8f31c2</span>
</h2>
<div className="stream" aria-live="polite">
{lines.map((line, i) => (
<div key={`${line.time}-${i}`}>
<span className="t">{line.time}</span> {line.body}
return (
<div className="instrument">
<div className="rail">
<div className="instrument__bar">
<span>
<b>northgate</b> · fleet
</span>
<span>12 servers</span>
<span>{incident ? "10 up" : "11 up"}</span>
<span>{incident ? "2 down" : "1 down"}</span>
<span>3 monitors</span>
<span>{runActive ? "1 run active" : "no runs active"}</span>
<span className="instrument__clock">14:22:12 UTC</span>
</div>
))}
{resting && (
<div>
<span className="caret">_</span>
<div className="panes">
<section className="pane" aria-label="Fleet status">
<h2 className="pane__h">
Fleet <span>{revoked ? "key revoked · 1 server updated" : "agents polling"}</span>
</h2>
<Row host="proxmox-node-1" sub="4 keys · 12% cpu" state="up" label="Active" />
<Row host="db-primary" sub="3 keys · 61% cpu" state="up" label="Active" />
<Row host="edge-gw-02" sub={incident ? "2 keys · tls refused" : "2 keys · tls 41d"} state={incident ? "down" : "up"} label={incident ? "Incident" : "Active"} />
<Row host="win-build-01" sub="agent 1.4.1 · update ready" state="pend" label="Pending" />
<Row host="app-worker-03" sub={revoked ? "4 keys · 1 revoked" : "5 keys · idle"} state="up" label="Active" />
</section>
<section className="pane" aria-label="Workflow run">
<h2 className="pane__h">
Run <span>run_8f31c2</span>
</h2>
<div className="stream" aria-live="polite">
{lines.map((line, i) => (
<div key={`${line.time}-${i}`}>
<span className="t">{line.time}</span> {line.body}
</div>
))}
{resting && (
<div>
<span className="caret">_</span>
</div>
)}
</div>
</section>
</div>
)}
</div>
</section>
</div>
</div>
</div>
);
);
}
function Row({
host,
sub,
state,
label,
}: {
host: string;
sub: string;
state: "up" | "down" | "pend";
label: string;
}) {
return (
<div className="frow">
<span className={state === "up" ? "dot" : `dot dot--${state}`} />
<span className="frow__host">{host}</span>
<span className="frow__sub">{sub}</span>
<span className={`chip chip--${state}`}>{label}</span>
</div>
);
function Row({ host, sub, state, label }: { host: string; sub: string; state: "up" | "down" | "pend"; label: string }) {
return (
<div className="frow">
<span className={state === "up" ? "dot" : `dot dot--${state}`} />
<span className="frow__host">{host}</span>
<span className="frow__sub">{sub}</span>
<span className={`chip chip--${state}`}>{label}</span>
</div>
);
}
+2 -2
View File
@@ -41,7 +41,7 @@ export function OrgForm() {
We sent a confirmation link. Open it and <b>{slug || "your organisation"}</b> is created with you as its owner. The link works once and expires in 24 hours.
</p>
<p style={{ color: "var(--ink-3)", marginTop: "0.75rem", fontSize: "0.88rem" }}>
Nothing exists until you confirm if the email does not arrive, start again or contact support@hostxtra.co.uk.
Nothing exists until you confirm if the email does not arrive, start again or contact support@hostxtra.co.uk.
</p>
</div>
);
@@ -80,7 +80,7 @@ export function OrgForm() {
<div className="field">
<label htmlFor="o-pass">Password</label>
<input id="o-pass" name="password" type="password" autoComplete="new-password" minLength={MIN_PASSWORD} required aria-describedby="o-pass-err" />
<small>At least {MIN_PASSWORD} characters. Use a manager you are about to manage SSH keys with it.</small>
<small>At least {MIN_PASSWORD} characters. Use a manager you are about to manage SSH keys with it.</small>
{fieldError("password") && (
<small id="o-pass-err" className="field__err">
{fieldError("password")}
+4 -4
View File
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/
/
import "./.next/dev/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
+2 -2
View File
@@ -1,7 +1,7 @@
import type { NextConfig } from "next";
// Standalone output, matching web/: the build emits a self-contained server
// bundle that runs under Node in the runtime image.
const nextConfig: NextConfig = {
output: "standalone",
};