diff --git a/frontend/app/components/TerminalTyper.tsx b/frontend/app/components/TerminalTyper.tsx index 1c32e95..7919775 100644 --- a/frontend/app/components/TerminalTyper.tsx +++ b/frontend/app/components/TerminalTyper.tsx @@ -6,24 +6,59 @@ import { LINKS } from "@/lib/links"; interface Line { type: "comment" | "cmd" | "cmd-cont" | "output" | "success" | "error"; text: string; + copyable?: boolean; } -function buildLines(): Line[] { +function buildDockerRunLines(): Line[] { return [ - { type: "comment", text: "# Pull the latest Backerup image" }, - { type: "cmd", text: `docker pull ${LINKS.dockerImage}` }, - { type: "output", text: `latest: Pulling from ${LINKS.dockerImage.split(":")[0]}` }, - { type: "output", text: "Digest: sha256:a3f8b1c2d9e4f7g6h5..." }, - { type: "success", text: "✓ Status: Image is up to date" }, - { type: "comment", text: "# Start Backerup with Docker socket access" }, - { type: "cmd", text: "docker run -d --name backerup \\" }, - { type: "cmd-cont", text: ` -p ${LINKS.uiPort}:${LINKS.uiPort} \\` }, - { type: "cmd-cont", text: " -v /var/run/docker.sock:/var/run/docker.sock \\" }, - { type: "cmd-cont", text: ` ${LINKS.dockerImage}` }, - { type: "success", text: "✓ Container started · a7f82c3d1b09" }, + { type: "comment", text: "# Quick start: Run Backerup directly" }, + { type: "cmd", text: "docker run -d --name backerup \\", copyable: true }, + { type: "cmd-cont", text: ` -p ${LINKS.uiPort}:80 \\`, copyable: true }, + { type: "cmd-cont", text: " -v /var/run/docker.sock:/var/run/docker.sock \\", copyable: true }, + { type: "cmd-cont", text: " -v ${BACKUP_DIR:-/backups}:/backups \\", copyable: true }, + { type: "cmd-cont", text: " -v backerup-config:/app/config \\", copyable: true }, + { type: "cmd-cont", text: " -e BACKUP_DIR=/backups \\", copyable: true }, + { type: "cmd-cont", text: " -e BACKUP_CONFIG_DIR=/app/config \\", copyable: true }, + { type: "cmd-cont", text: ` -e HOST_BACKUP_DIR=/backups \\`, copyable: true }, + { type: "cmd-cont", text: ` ${LINKS.dockerImage}`, copyable: true }, + { 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) { if (line.type === "output" || line.type === "success" || line.type === "error") return 12; 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-cont") return " "; return ""; } -export default function TerminalTyper() { - const LINES = buildLines(); +export default function TerminalTyper({ mode = "docker-run" }: { mode?: "docker-run" | "docker-compose" } = {}) { + const LINES = buildLines(mode); const [doneLines, setDoneLines] = useState([]); 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(() => { if (!current) return; @@ -86,30 +149,41 @@ export default function TerminalTyper() { }, [current]); return ( -
- {doneLines.map((i) => { - const line = LINES[i]; - const prompt = linePrompt(line); - return ( -
- {prompt && {prompt}} - {line.text} -
- ); - })} +
+ +
+ {doneLines.map((i) => { + const line = LINES[i]; + const prompt = linePrompt(line); + return ( +
+ {prompt && {prompt}} + {line.text} +
+ ); + })} - {current && current.idx < LINES.length && (() => { - const line = LINES[current.idx]; - const prompt = linePrompt(line); - const partial = line.text.slice(0, current.chars); - return ( -
- {prompt && {prompt}} - {partial} - -
- ); - })()} + {current && current.idx < LINES.length && (() => { + const line = LINES[current.idx]; + const prompt = linePrompt(line); + const partial = line.text.slice(0, current.chars); + return ( +
+ {prompt && {prompt}} + {partial} + +
+ ); + })()} +
); } diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index 708aef7..0ff6519 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -96,13 +96,13 @@ export default async function RootLayout({ Share - GitHub + Gitea {session?.role === "admin" && (
- GitHub + Gitea
@@ -180,7 +180,7 @@ export default async function RootLayout({
  • Browse configs
  • Share a backup
  • Documentation
  • -
  • Changelog
  • +
  • Changelog
  • @@ -188,10 +188,10 @@ export default async function RootLayout({

    Community

    diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 3114486..445d0f4 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -263,13 +263,13 @@ export default function LandingPage() { - GitHub + Gitea @@ -512,10 +512,10 @@ export default function LandingPage() { Quick start

    - One command to start + Setup with docker-compose

    - 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.

    @@ -523,24 +523,11 @@ export default function LandingPage() { - terminal + docker-compose setup +
    +
    +
    -
    -                # Pull & run — replace /path/to/backups with your backup destination{"\n"}
    -                $ 
    -                {"docker run -d \\"}{"\n"}
    -                {"    "}
    -                {"--name backerup \\"}{"\n"}
    -                {"    "}
    -                {`-p ${LINKS.uiPort}:${LINKS.uiPort} \\`}{"\n"}
    -                {"    "}
    -                {"-v /var/run/docker.sock:/var/run/docker.sock \\"}{"\n"}
    -                {"    "}
    -                {"-v /path/to/backups:/data \\"}{"\n"}
    -                {"    "}
    -                {LINKS.dockerImage}{"\n"}
    -                {`✓  Open http://localhost:${LINKS.uiPort} to get started`}
    -              
    @@ -571,13 +558,13 @@ export default function LandingPage() {

    - Star on GitHub + Star on Gitea