mirror of
https://gitlab.w-hs.de/an14051/backerup-website.git
synced 2026-07-27 17:35:29 +00:00
feat: update links to Gitea and enhance TerminalTyper for docker-compose support
This commit is contained in:
@@ -6,24 +6,59 @@ import { LINKS } from "@/lib/links";
|
|||||||
interface Line {
|
interface Line {
|
||||||
type: "comment" | "cmd" | "cmd-cont" | "output" | "success" | "error";
|
type: "comment" | "cmd" | "cmd-cont" | "output" | "success" | "error";
|
||||||
text: string;
|
text: string;
|
||||||
|
copyable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildLines(): Line[] {
|
function buildDockerRunLines(): Line[] {
|
||||||
return [
|
return [
|
||||||
{ type: "comment", text: "# Pull the latest Backerup image" },
|
{ type: "comment", text: "# Quick start: Run Backerup directly" },
|
||||||
{ type: "cmd", text: `docker pull ${LINKS.dockerImage}` },
|
{ type: "cmd", text: "docker run -d --name backerup \\", copyable: true },
|
||||||
{ type: "output", text: `latest: Pulling from ${LINKS.dockerImage.split(":")[0]}` },
|
{ type: "cmd-cont", text: ` -p ${LINKS.uiPort}:80 \\`, copyable: true },
|
||||||
{ type: "output", text: "Digest: sha256:a3f8b1c2d9e4f7g6h5..." },
|
{ type: "cmd-cont", text: " -v /var/run/docker.sock:/var/run/docker.sock \\", copyable: true },
|
||||||
{ type: "success", text: "✓ Status: Image is up to date" },
|
{ type: "cmd-cont", text: " -v ${BACKUP_DIR:-/backups}:/backups \\", copyable: true },
|
||||||
{ type: "comment", text: "# Start Backerup with Docker socket access" },
|
{ type: "cmd-cont", text: " -v backerup-config:/app/config \\", copyable: true },
|
||||||
{ type: "cmd", text: "docker run -d --name backerup \\" },
|
{ type: "cmd-cont", text: " -e BACKUP_DIR=/backups \\", copyable: true },
|
||||||
{ type: "cmd-cont", text: ` -p ${LINKS.uiPort}:${LINKS.uiPort} \\` },
|
{ type: "cmd-cont", text: " -e BACKUP_CONFIG_DIR=/app/config \\", copyable: true },
|
||||||
{ type: "cmd-cont", text: " -v /var/run/docker.sock:/var/run/docker.sock \\" },
|
{ type: "cmd-cont", text: ` -e HOST_BACKUP_DIR=/backups \\`, copyable: true },
|
||||||
{ type: "cmd-cont", text: ` ${LINKS.dockerImage}` },
|
{ type: "cmd-cont", text: ` ${LINKS.dockerImage}`, copyable: true },
|
||||||
{ type: "success", text: "✓ Container started · a7f82c3d1b09" },
|
{ type: "output", text: "a7f82c3d1b09" },
|
||||||
|
{ type: "success", text: "✓ Container started" },
|
||||||
|
{ type: "success", text: "✓ Ready at http://localhost:8080" },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildDockerComposeLines(): Line[] {
|
||||||
|
return [
|
||||||
|
{ type: "comment", text: "# Using docker-compose for easier management" },
|
||||||
|
{ type: "cmd", text: "cat > docker-compose.yml << 'EOF'", copyable: false },
|
||||||
|
{ type: "output", text: "services:" },
|
||||||
|
{ type: "output", text: " backerup:" },
|
||||||
|
{ type: "output", text: ` image: ${LINKS.dockerImage}` },
|
||||||
|
{ type: "output", text: " ports:" },
|
||||||
|
{ type: "output", text: ` - \"${LINKS.uiPort}:80\"` },
|
||||||
|
{ type: "output", text: " volumes:" },
|
||||||
|
{ type: "output", text: " - /var/run/docker.sock:/var/run/docker.sock" },
|
||||||
|
{ type: "output", text: " - ${BACKUP_DIR:-/backups}:/backups" },
|
||||||
|
{ type: "output", text: " - backerup-config:/app/config" },
|
||||||
|
{ type: "output", text: " environment:" },
|
||||||
|
{ type: "output", text: " - BACKUP_DIR=/backups" },
|
||||||
|
{ type: "output", text: " - BACKUP_CONFIG_DIR=/app/config" },
|
||||||
|
{ type: "output", text: " - HOST_BACKUP_DIR=${BACKUP_DIR:-/backups}" },
|
||||||
|
{ type: "output", text: " restart: unless-stopped" },
|
||||||
|
{ type: "output", text: "volumes:" },
|
||||||
|
{ type: "output", text: " backerup-config:" },
|
||||||
|
{ type: "output", text: "EOF" },
|
||||||
|
{ type: "success", text: "✓ docker-compose.yml created" },
|
||||||
|
{ type: "cmd", text: "docker compose up -d", copyable: false },
|
||||||
|
{ type: "output", text: "[+] Building 2.3s (12/12) FINISHED" },
|
||||||
|
{ type: "success", text: "✓ Ready at http://localhost:8080" },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildLines(mode: "docker-run" | "docker-compose" = "docker-run"): Line[] {
|
||||||
|
return mode === "docker-run" ? buildDockerRunLines() : buildDockerComposeLines();
|
||||||
|
}
|
||||||
|
|
||||||
function lineSpeed(line: Line) {
|
function lineSpeed(line: Line) {
|
||||||
if (line.type === "output" || line.type === "success" || line.type === "error") return 12;
|
if (line.type === "output" || line.type === "success" || line.type === "error") return 12;
|
||||||
if (line.type === "comment") return 22;
|
if (line.type === "comment") return 22;
|
||||||
@@ -47,16 +82,44 @@ function lineColor(line: Line) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function linePrompt(line: Line) {
|
function linePrompt(line: Line): string {
|
||||||
if (line.type === "cmd") return "$ ";
|
if (line.type === "cmd") return "$ ";
|
||||||
if (line.type === "cmd-cont") return " ";
|
if (line.type === "cmd-cont") return " ";
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TerminalTyper() {
|
export default function TerminalTyper({ mode = "docker-run" }: { mode?: "docker-run" | "docker-compose" } = {}) {
|
||||||
const LINES = buildLines();
|
const LINES = buildLines(mode);
|
||||||
const [doneLines, setDoneLines] = useState<number[]>([]);
|
const [doneLines, setDoneLines] = useState<number[]>([]);
|
||||||
const [current, setCurrent] = useState<{ idx: number; chars: number } | null>({ idx: 0, chars: 0 });
|
const [current, setCurrent] = useState<{ idx: number; chars: number } | null>({ idx: 0, chars: 0 });
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
|
const copyToClipboard = () => {
|
||||||
|
let textToCopy = "";
|
||||||
|
|
||||||
|
if (mode === "docker-compose") {
|
||||||
|
// For docker-compose, copy the YAML content only
|
||||||
|
const startIdx = LINES.findIndex(line => line.type === "output" && line.text === "services:");
|
||||||
|
const endIdx = LINES.findIndex((line, i) => i > startIdx && line.type === "output" && line.text === "EOF");
|
||||||
|
|
||||||
|
if (startIdx !== -1 && endIdx !== -1) {
|
||||||
|
// Copy the YAML content
|
||||||
|
textToCopy = LINES.slice(startIdx, endIdx)
|
||||||
|
.map(line => line.text)
|
||||||
|
.join("\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For docker-run, copy all cmd and cmd-cont lines
|
||||||
|
textToCopy = LINES.filter(line => line.type === "cmd" || line.type === "cmd-cont")
|
||||||
|
.map(line => line.text.trim())
|
||||||
|
.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.clipboard.writeText(textToCopy).then(() => {
|
||||||
|
setCopied(true);
|
||||||
|
setTimeout(() => setCopied(false), 2000);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!current) return;
|
if (!current) return;
|
||||||
@@ -86,6 +149,16 @@ export default function TerminalTyper() {
|
|||||||
}, [current]);
|
}, [current]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<button
|
||||||
|
onClick={copyToClipboard}
|
||||||
|
className="flex items-center gap-2 px-3 py-1.5 rounded-lg text-xs font-medium text-slate-300 bg-slate-800/50 hover:bg-slate-700/50 transition-colors border border-slate-700/50"
|
||||||
|
>
|
||||||
|
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||||
|
</svg>
|
||||||
|
{copied ? "Copied!" : "Copy command"}
|
||||||
|
</button>
|
||||||
<div className="font-mono text-xs sm:text-sm leading-[1.85] select-none">
|
<div className="font-mono text-xs sm:text-sm leading-[1.85] select-none">
|
||||||
{doneLines.map((i) => {
|
{doneLines.map((i) => {
|
||||||
const line = LINES[i];
|
const line = LINES[i];
|
||||||
@@ -111,5 +184,6 @@ export default function TerminalTyper() {
|
|||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,13 +96,13 @@ export default async function RootLayout({
|
|||||||
Share
|
Share
|
||||||
</Link>
|
</Link>
|
||||||
<a
|
<a
|
||||||
href={LINKS.github}
|
href={LINKS.gitea}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="nav-link px-3 py-2 rounded-lg text-slate-400 hover:text-white hover:bg-white/5 transition-all flex items-center gap-1.5"
|
className="nav-link px-3 py-2 rounded-lg text-slate-400 hover:text-white hover:bg-white/5 transition-all flex items-center gap-1.5"
|
||||||
>
|
>
|
||||||
<GitHubIcon />
|
<GitHubIcon />
|
||||||
<span className="hidden sm:inline">GitHub</span>
|
<span className="hidden sm:inline">Gitea</span>
|
||||||
</a>
|
</a>
|
||||||
{session?.role === "admin" && (
|
{session?.role === "admin" && (
|
||||||
<Link
|
<Link
|
||||||
@@ -162,13 +162,13 @@ export default async function RootLayout({
|
|||||||
</p>
|
</p>
|
||||||
<div className="mt-6 flex gap-3">
|
<div className="mt-6 flex gap-3">
|
||||||
<a
|
<a
|
||||||
href={LINKS.github}
|
href={LINKS.gitea}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex items-center gap-2 px-4 py-2 rounded-lg border border-white/10 bg-white/3 text-xs text-slate-400 hover:text-white hover:border-white/20 transition-all"
|
className="flex items-center gap-2 px-4 py-2 rounded-lg border border-white/10 bg-white/3 text-xs text-slate-400 hover:text-white hover:border-white/20 transition-all"
|
||||||
>
|
>
|
||||||
<GitHubIcon />
|
<GitHubIcon />
|
||||||
GitHub
|
Gitea
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -180,7 +180,7 @@ export default async function RootLayout({
|
|||||||
<li><Link href="/browse" className="hover:text-cyan-400 transition-colors">Browse configs</Link></li>
|
<li><Link href="/browse" className="hover:text-cyan-400 transition-colors">Browse configs</Link></li>
|
||||||
<li><Link href="/share" className="hover:text-cyan-400 transition-colors">Share a backup</Link></li>
|
<li><Link href="/share" className="hover:text-cyan-400 transition-colors">Share a backup</Link></li>
|
||||||
<li><a href={LINKS.docs} className="hover:text-cyan-400 transition-colors">Documentation</a></li>
|
<li><a href={LINKS.docs} className="hover:text-cyan-400 transition-colors">Documentation</a></li>
|
||||||
<li><a href={LINKS.githubReleases} className="hover:text-cyan-400 transition-colors">Changelog</a></li>
|
<li><a href={LINKS.giteaReleases} className="hover:text-cyan-400 transition-colors">Changelog</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -188,10 +188,10 @@ export default async function RootLayout({
|
|||||||
<div>
|
<div>
|
||||||
<h3 className="text-xs font-semibold uppercase tracking-widest text-slate-500 mb-4">Community</h3>
|
<h3 className="text-xs font-semibold uppercase tracking-widest text-slate-500 mb-4">Community</h3>
|
||||||
<ul className="space-y-3 text-sm text-slate-500">
|
<ul className="space-y-3 text-sm text-slate-500">
|
||||||
<li><a href={LINKS.github} className="hover:text-cyan-400 transition-colors">GitHub</a></li>
|
<li><a href={LINKS.gitea} className="hover:text-cyan-400 transition-colors">Gitea</a></li>
|
||||||
<li><a href={LINKS.githubIssues} className="hover:text-cyan-400 transition-colors">Issues</a></li>
|
<li><a href={LINKS.giteaIssues} className="hover:text-cyan-400 transition-colors">Issues</a></li>
|
||||||
<li><a href={LINKS.githubDiscussions} className="hover:text-cyan-400 transition-colors">Discussions</a></li>
|
<li><a href={LINKS.giteaDiscussions} className="hover:text-cyan-400 transition-colors">Discussions</a></li>
|
||||||
<li><a href={LINKS.githubContributing} className="hover:text-cyan-400 transition-colors">Contributing</a></li>
|
<li><a href={LINKS.giteaContributing} className="hover:text-cyan-400 transition-colors">Contributing</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+11
-24
@@ -263,13 +263,13 @@ export default function LandingPage() {
|
|||||||
</svg>
|
</svg>
|
||||||
</Link>
|
</Link>
|
||||||
<a
|
<a
|
||||||
href={LINKS.github}
|
href={LINKS.gitea}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex items-center gap-2 rounded-xl border border-white/8 bg-white/3 px-7 py-3.5 text-sm font-semibold text-slate-300 transition-all hover:bg-white/8 hover:text-white hover:scale-105"
|
className="flex items-center gap-2 rounded-xl border border-white/8 bg-white/3 px-7 py-3.5 text-sm font-semibold text-slate-300 transition-all hover:bg-white/8 hover:text-white hover:scale-105"
|
||||||
>
|
>
|
||||||
<GitHubIcon />
|
<GitHubIcon />
|
||||||
GitHub
|
Gitea
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -512,10 +512,10 @@ export default function LandingPage() {
|
|||||||
Quick start
|
Quick start
|
||||||
</span>
|
</span>
|
||||||
<h2 className="text-4xl font-bold tracking-tight text-white mb-4">
|
<h2 className="text-4xl font-bold tracking-tight text-white mb-4">
|
||||||
One command to start
|
Setup with docker-compose
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-slate-400 mb-10">
|
<p className="text-slate-400 mb-10">
|
||||||
Backerup runs as a Docker container. Give it access to your Docker socket and you're done.
|
The easiest way to get Backerup running is with docker-compose for declarative, reproducible deployments.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="rounded-2xl border border-white/6 glass overflow-hidden text-left">
|
<div className="rounded-2xl border border-white/6 glass overflow-hidden text-left">
|
||||||
@@ -523,24 +523,11 @@ export default function LandingPage() {
|
|||||||
<span className="h-2.5 w-2.5 rounded-full bg-red-500/70" />
|
<span className="h-2.5 w-2.5 rounded-full bg-red-500/70" />
|
||||||
<span className="h-2.5 w-2.5 rounded-full bg-yellow-500/70" />
|
<span className="h-2.5 w-2.5 rounded-full bg-yellow-500/70" />
|
||||||
<span className="h-2.5 w-2.5 rounded-full bg-emerald-500/70" />
|
<span className="h-2.5 w-2.5 rounded-full bg-emerald-500/70" />
|
||||||
<span className="ml-3 text-xs font-mono text-slate-500">terminal</span>
|
<span className="ml-3 text-xs font-mono text-slate-500">docker-compose setup</span>
|
||||||
|
</div>
|
||||||
|
<div className="p-6">
|
||||||
|
<TerminalTyper mode="docker-compose" />
|
||||||
</div>
|
</div>
|
||||||
<pre className="overflow-x-auto p-6 text-sm font-mono leading-7 text-slate-300">
|
|
||||||
<span className="text-slate-600 select-none"># Pull & run — replace /path/to/backups with your backup destination</span>{"\n"}
|
|
||||||
<span className="text-cyan-500 select-none">$ </span>
|
|
||||||
<span>{"docker run -d \\"}</span>{"\n"}
|
|
||||||
<span className="text-slate-500 select-none">{" "}</span>
|
|
||||||
<span>{"--name backerup \\"}</span>{"\n"}
|
|
||||||
<span className="text-slate-500 select-none">{" "}</span>
|
|
||||||
<span>{`-p ${LINKS.uiPort}:${LINKS.uiPort} \\`}</span>{"\n"}
|
|
||||||
<span className="text-slate-500 select-none">{" "}</span>
|
|
||||||
<span>{"-v /var/run/docker.sock:/var/run/docker.sock \\"}</span>{"\n"}
|
|
||||||
<span className="text-slate-500 select-none">{" "}</span>
|
|
||||||
<span>{"-v /path/to/backups:/data \\"}</span>{"\n"}
|
|
||||||
<span className="text-slate-500 select-none">{" "}</span>
|
|
||||||
<span className="text-slate-200">{LINKS.dockerImage}</span>{"\n"}
|
|
||||||
<span className="text-emerald-400">{`✓ Open http://localhost:${LINKS.uiPort} to get started`}</span>
|
|
||||||
</pre>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</AnimateIn>
|
</AnimateIn>
|
||||||
@@ -571,13 +558,13 @@ export default function LandingPage() {
|
|||||||
</p>
|
</p>
|
||||||
<div className="mt-10 flex flex-wrap justify-center gap-4">
|
<div className="mt-10 flex flex-wrap justify-center gap-4">
|
||||||
<a
|
<a
|
||||||
href={LINKS.github}
|
href={LINKS.gitea}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex items-center gap-2 rounded-xl border border-white/10 bg-white/5 px-7 py-3.5 text-sm font-semibold text-white transition-all hover:bg-white/10 hover:scale-105"
|
className="flex items-center gap-2 px-4 py-2 rounded-lg border border-white/10 bg-white/3 text-xs text-slate-400 hover:text-white hover:border-white/20 transition-all"
|
||||||
>
|
>
|
||||||
<GitHubIcon />
|
<GitHubIcon />
|
||||||
Star on GitHub
|
Star on Gitea
|
||||||
</a>
|
</a>
|
||||||
<Link
|
<Link
|
||||||
href="/share"
|
href="/share"
|
||||||
|
|||||||
+10
-10
@@ -5,25 +5,25 @@
|
|||||||
* ─────────────────────────────────────────────────────────────
|
* ─────────────────────────────────────────────────────────────
|
||||||
*/
|
*/
|
||||||
export const LINKS = {
|
export const LINKS = {
|
||||||
// ── GitHub ────────────────────────────────────────────────
|
// ── Gitea ─────────────────────────────────────────────────
|
||||||
/** Repository root — used in the nav, footer, and call-to-action buttons */
|
/** Repository root — used in the nav, footer, and call-to-action buttons */
|
||||||
github: "https://github.com/evan-buss/backerup",
|
gitea: "https://gitea.doomlabs.de/KptltD00M/backerup",
|
||||||
/** Releases / changelog page */
|
/** Releases / changelog page */
|
||||||
githubReleases: "https://github.com/evan-buss/backerup/releases",
|
giteaReleases: "https://gitea.doomlabs.de/KptltD00M/backerup/releases",
|
||||||
/** Issue tracker */
|
/** Issue tracker */
|
||||||
githubIssues: "https://github.com/evan-buss/backerup/issues",
|
giteaIssues: "https://gitea.doomlabs.de/KptltD00M/backerup/issues",
|
||||||
/** GitHub Discussions forum */
|
/** Gitea Discussions forum */
|
||||||
githubDiscussions: "https://github.com/evan-buss/backerup/discussions",
|
giteaDiscussions: "https://gitea.doomlabs.de/KptltD00M/backerup/discussions",
|
||||||
/** CONTRIBUTING.md guide */
|
/** CONTRIBUTING.md guide */
|
||||||
githubContributing: "https://github.com/evan-buss/backerup/blob/main/CONTRIBUTING.md",
|
giteaContributing: "https://gitea.doomlabs.de/KptltD00M/backerup/src/branch/main/CONTRIBUTING.md",
|
||||||
|
|
||||||
// ── Documentation ─────────────────────────────────────────
|
// ── Documentation ─────────────────────────────────────────
|
||||||
/** Primary documentation URL (separate from GitHub if hosted elsewhere) */
|
/** Primary documentation URL (separate from Gitea if hosted elsewhere) */
|
||||||
docs: "https://github.com/evan-buss/backerup",
|
docs: "https://gitea.doomlabs.de/KptltD00M/backerup",
|
||||||
|
|
||||||
// ── Docker ────────────────────────────────────────────────
|
// ── Docker ────────────────────────────────────────────────
|
||||||
/** Full Docker image reference shown in install snippets */
|
/** Full Docker image reference shown in install snippets */
|
||||||
dockerImage: "ghcr.io/evan-buss/backerup:latest",
|
dockerImage: "gitea.doomlabs.de/kptltd00m/backerup:latest",
|
||||||
/** Default UI port the container exposes */
|
/** Default UI port the container exposes */
|
||||||
uiPort: "8080",
|
uiPort: "8080",
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
Reference in New Issue
Block a user