140 lines
5.1 KiB
Markdown
140 lines
5.1 KiB
Markdown
# SyncServer — Agent Guidance
|
|
|
|
## Project Type
|
|
|
|
Go monolith (single binary) with embedded React SPA frontend. Targets Linux amd64 LXC containers on Proxmox.
|
|
|
|
## Dev Commands
|
|
|
|
```bash
|
|
# Clean build and package (Linux amd64)
|
|
make clean && VERSION=X.Y.Z make package
|
|
|
|
# Build only (no package), Linux amd64
|
|
make build-all
|
|
|
|
# Run
|
|
./syncserver --data-dir ./data --addr :8080 \
|
|
SYNCSERVER_ADMIN_USER=admin SYNCSERVER_ADMIN_PASSWORD=secret
|
|
|
|
# Tests
|
|
go test ./...
|
|
|
|
# Deploy (full build + bump + push + LXC deploy + verify)
|
|
./scripts/deploy.sh
|
|
```
|
|
|
|
## Version Bumping
|
|
|
|
Before each push that contains user-facing changes, run the version bump script:
|
|
|
|
```bash
|
|
./scripts/bump-version.sh
|
|
```
|
|
|
|
This reads the current version from `cmd/server/main.go`, increments the patch number (semver), and updates both `cmd/server/main.go` and `Makefile`. Then commit and push:
|
|
|
|
```bash
|
|
git add cmd/server/main.go Makefile
|
|
git commit -m 'Bump version to X.Y.Z'
|
|
git push
|
|
```
|
|
|
|
The `DEBIAN/control` uses `@@VERSION@@` placeholder and is substituted at `make package` time — no manual edit needed.
|
|
|
|
To verify the LXC is running the expected version:
|
|
|
|
```bash
|
|
ssh root@10.5.1.30 '/usr/bin/syncserver --version'
|
|
ssh root@10.5.1.30 'journalctl -u syncserver -n 3 --no-pager'
|
|
```
|
|
|
|
|
|
## Deploy Flow
|
|
|
|
The `make deploy` target (or `./scripts/deploy.sh`) automates the full deployment pipeline:
|
|
|
|
1. **Pre-flight** — checks git clean, on main branch, LXC reachable, disk space
|
|
2. **Version bump** — runs `bump-version.sh`, commits and pushes automatically
|
|
3. **Build** — `make clean && make package` (Linux amd64 .deb)
|
|
4. **Backup** — copies current `/usr/bin/syncserver` to `/var/backups/syncserver.previous.<version>`
|
|
5. **Deploy** — scp .deb to LXC, run `dpkg -i`
|
|
6. **Verify** — service active, version matches, Cache-Control headers correct, HTTP 200
|
|
|
|
### Deploy Options
|
|
|
|
```bash
|
|
./scripts/deploy.sh # Full deploy
|
|
./scripts/deploy.sh --dry-run # Simulate without making changes
|
|
SKIP_BUILD=1 ./scripts/deploy.sh # Use existing .deb
|
|
SKIP_VERIFY=1 ./scripts/deploy.sh # Skip post-deploy checks
|
|
LXC_HOST=root@10.5.1.30 ./scripts/deploy.sh # Custom target
|
|
```
|
|
|
|
### Rollback
|
|
|
|
If verification fails after deploy, the script automatically rolls back to the backup binary. Manual rollback:
|
|
|
|
```bash
|
|
ssh root@10.5.1.30 "cp /var/backups/syncserver.previous.<version> /usr/bin/syncserver && systemctl restart syncserver"
|
|
```
|
|
|
|
### LXC Host
|
|
|
|
- **Host**: `root@10.5.1.30`
|
|
- **Remote dir**: `~/move-data-nas`
|
|
- **Service**: `syncserver` (systemd)
|
|
|
|
## 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.
|