From 10ab464a43fb3fb04422436a78e5656c677566ce Mon Sep 17 00:00:00 2001 From: dimgigov Date: Wed, 6 May 2026 11:39:18 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20Phase=206=20=E2=80=94=20Docker=20+=20do?= =?UTF-8?q?cker-compose=20+=20backup/restore=20CLI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dockerfile: multi-stage build (nim:alpine -> alpine), env-configurable - docker-compose.yml: single node with volume + healthcheck - backup.nim: backup/restore tar.gz snapshots, list/cleanup - CLI: baradadb backup|restore|list --data-dir=DIR - All 216 tests pass --- Dockerfile | 18 +++++++ docker-compose.yml | 23 ++++++++ src/barabadb/core/backup.nim | 102 +++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 src/barabadb/core/backup.nim diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1d628d6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM nim:2.2-alpine AS builder +WORKDIR /build +COPY baradadb.nimble . +COPY src/ src/ +RUN nimble install -d -y || true +RUN nim c -d:release --opt:speed -o:baradadb src/baradadb.nim + +FROM alpine:latest +WORKDIR /app +COPY --from=builder /build/baradadb . +RUN mkdir -p /data +ENV BARADB_PORT=9000 +ENV BARADB_DATA_DIR=/data +ENV BARADB_HTTP_PORT=8080 +ENV BARADB_WS_PORT=8081 +EXPOSE 9000 8080 8081 +VOLUME ["/data"] +CMD ["./baradadb"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9eb901b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +version: "3.9" +services: + baradb: + build: . + ports: + - "9000:9000" + - "8080:8080" + - "8081:8081" + volumes: + - baradb_data:/data + environment: + - BARADB_PORT=9000 + - BARADB_DATA_DIR=/data + - BARADB_HTTP_PORT=8080 + - BARADB_WS_PORT=8081 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + interval: 10s + timeout: 5s + retries: 3 + +volumes: + baradb_data: diff --git a/src/barabadb/core/backup.nim b/src/barabadb/core/backup.nim new file mode 100644 index 0000000..49aa95b --- /dev/null +++ b/src/barabadb/core/backup.nim @@ -0,0 +1,102 @@ +## BaraDB Backup & Restore — tar.gz snapshots +import std/os +import std/osproc +import std/strutils +import std/times + +type + Backup* = object + path*: string + timestamp*: int64 + size*: int64 + +proc backupDataDir*(dataDir: string, output: string): bool = + ## Create a tar.gz backup of the data directory + if not dirExists(dataDir): + echo "Data directory not found: ", dataDir + return false + + let parent = parentDir(dataDir) + let name = lastPathPart(dataDir) + let cmd = "tar -czf " & quoteShell(output) & " -C " & quoteShell(parent) & " " & quoteShell(name) + let exitCode = execCmd(cmd) + return exitCode == 0 + +proc restoreDataDir*(input: string, dataDir: string): bool = + ## Restore from a tar.gz backup + if not fileExists(input): + echo "Backup file not found: ", input + return false + + if dirExists(dataDir): + removeDir(dataDir) + createDir(dataDir) + + let cmd = "tar -xzf " & quoteShell(input) & " -C " & quoteShell(dataDir) + let exitCode = execCmd(cmd) + return exitCode == 0 + +proc listBackups*(dataDir: string): seq[Backup] = + result = @[] + for kind, path in walkDir(parentDir(dataDir)): + let (_, name, ext) = splitFile(path) + if ext == ".gz": + var backup = Backup(path: path) + backup.size = getFileSize(path) + result.add(backup) + +proc cleanupOldBackups*(dataDir: string, keepLast: int = 5) = + var backups = listBackups(dataDir) + if backups.len <= keepLast: + return + for i in 0..<(backups.len - keepLast): + removeFile(backups[i].path) + +when isMainModule: + import std/os + import std/parseopt + + var + command = "" + dataDir = "data/server" + target = "" + + for kind, key, val in getopt(): + case kind + of cmdArgument: + if command == "": + command = key + elif target == "": + target = key + of cmdLongOption, cmdShortOption: + case key + of "data-dir", "d": dataDir = val + of "output", "o": target = val + of "input", "i": target = val + else: discard + of cmdEnd: discard + + case command + of "backup": + let outputFile = if target.len > 0: target else: "backup_" & $getTime().toUnix() & ".tar.gz" + let ok = backupDataDir(dataDir, outputFile) + if ok: + echo "Backup created: ", outputFile + else: + quit("Backup failed", 1) + of "restore": + if target.len == 0: + quit("Usage: backup restore --input=", 1) + let ok = restoreDataDir(target, dataDir) + if ok: + echo "Restored from: ", target + else: + quit("Restore failed", 1) + of "list": + let backups = listBackups(dataDir) + echo "Backups:" + for b in backups: + echo " ", b.path, " (", b.size, " bytes)" + else: + echo "Usage: backup [--data-dir=DIR] [--output/-i=FILE]" + quit(1)