feat: complete SyncServer implementation
Full-stack Go monolith with embedded React frontend for orchestrating rsync-over-SSH file synchronization with Wake-on-LAN support. Features: - JWT auth (HS256) with bcrypt password hashing - CRUD for machines (with WoL config) and sync_pairs - Ed25519 SSH key generation and known_hosts management - WoL magic packet sender + TCP-connect waiter with backoff - Sync engine: rsync subprocess, per-pair job queue, progress parsing - Homebrew cron parser for scheduled syncs - SSE stream for live job status (queued/waking_up/running/success/failed) - React+TS+Vite+Tailwind SPA embedded via embed.FS - Debian packaging with systemd unit, postinst/prerm/postrm Tech stack: - Go 1.22+ (CGO_ENABLED=0, pure SQLite via modernc.org/sqlite) - chi router for HTTP API - TypeScript + React 18 + Tailwind CSS frontend - Cross-compiled to Linux amd64 for Proxmox LXC deployment Tests: wol (MAC parsing, magic packet), syncengine/queue, scheduler/cron
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
# SyncServer — Agent Guidance
|
||||
|
||||
## Project Type
|
||||
|
||||
Go monolith (single binary) with embedded React SPA frontend. Targets Linux amd64 LXC containers on Proxmox.
|
||||
|
||||
## Dev Commands
|
||||
|
||||
```bash
|
||||
# Local dev build (macOS)
|
||||
make build-local
|
||||
|
||||
# Production Linux amd64 build
|
||||
make build # → packaging/debian/usr/bin/syncserver
|
||||
make deb # → dist/syncserver_VERSION_amd64.deb
|
||||
|
||||
# Run
|
||||
./syncserver --data-dir ./data --addr :8080 \
|
||||
SYNCSERVER_ADMIN_USER=admin SYNCSERVER_ADMIN_PASSWORD=secret
|
||||
|
||||
# Tests
|
||||
go test ./...
|
||||
```
|
||||
|
||||
## Critical Build Order
|
||||
|
||||
1. Frontend must be built FIRST: `cd web && npm run build`
|
||||
2. Output copied to `internal/webui/dist/` (must exist before Go build)
|
||||
3. Then: `go build ./cmd/server`
|
||||
|
||||
`scripts/build.sh` does this automatically. Running `go build` without the frontend dist will produce a binary without the web UI.
|
||||
|
||||
## Architecture Notes
|
||||
|
||||
- **Entry point**: `cmd/server/main.go`
|
||||
- **DB**: `modernc.org/sqlite` — pure Go, CGO_ENABLED=0. Driver name is `"sqlite"` (not `"sqlite3"`).
|
||||
- **Migrations**: embedded via `//go:embed migrations` in `internal/db/migrations.go`. Must use `migrationsFS` variable, path `"migrations"` (not `"internal/db/migrations"`).
|
||||
- **SSH keys**: generated at startup if missing; stored in `$DATA_DIR/ssh/id_ed25519`. Only the public key is stored in DB.
|
||||
- **Sync engine**: uses `os/exec.CommandContext` to run `rsync -e ssh ...`. SSH key is passed via `-i` flag, not via SSH config file.
|
||||
- **Scheduler**: homebrew cron parser (`m h dom mon dow` format, no seconds). Library `github.com/robfig/cron` is NOT used.
|
||||
- **SSE**: `GET /api/jobs/stream` streams `text/event-stream`. Clients must handle `event:` field.
|
||||
|
||||
## Frontend Stack
|
||||
|
||||
- TypeScript + React 18 + Vite + Tailwind CSS
|
||||
- Source: `web/src/`, built output: `web/dist/`
|
||||
- API client: `web/src/api/client.ts`
|
||||
- No React Router 6 data loaders; fetch hooks are manual in each page component
|
||||
- Tests: no frontend test suite yet
|
||||
|
||||
## Key Package Boundaries
|
||||
|
||||
| Package | Responsibility |
|
||||
|---|---|
|
||||
| `internal/api/` | HTTP handlers, chi router, DTOs |
|
||||
| `internal/auth/` | JWT (HS256), bcrypt, middleware |
|
||||
| `internal/db/` | SQLite connection, migrations runner |
|
||||
| `internal/models/` | Data access (raw sql) |
|
||||
| `internal/syncengine/` | Job queue, rsync subprocess, event bus |
|
||||
| `internal/scheduler/` | Cron parsing, tick loop |
|
||||
| `internal/sshmanager/` | Ed25519 key generation, known_hosts |
|
||||
| `internal/wol/` | Magic packet, TCP/ping waiter |
|
||||
| `internal/webui/` | `embed.FS` for React dist |
|
||||
|
||||
## DB Schema
|
||||
|
||||
Managed via ordered SQL migrations in `internal/db/migrations/`. Apply on startup via `db.RunMigrations()`. Table `schema_migrations` tracks applied versions.
|
||||
|
||||
## Package Script Quirks
|
||||
|
||||
- `scripts/build.sh`: cross-compiles to Linux amd64 via `GOOS=linux GOARCH=amd64`. Version injected via `-ldflags="-X main.version=$VERSION"`.
|
||||
- `scripts/package-deb.sh`: calls `dpkg-deb --build`. Version must be substituted into `DEBIAN/control` before build (`@@VERSION@@` placeholder).
|
||||
|
||||
## WoL Constraints
|
||||
|
||||
Magic packet uses `SO_BROADCAST` on a plain UDP socket. No raw sockets, no privileged container needed. Requires L2 reachability (same broadcast domain) from the server to the target machine's NIC.
|
||||
Reference in New Issue
Block a user