From 22552aec99c17c48e9df2ddddde8fe590cbb032a Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Fri, 28 Nov 2025 02:50:25 +0000 Subject: [PATCH] Initial commit. --- .github/workflows/autoupdate.yaml | 47 ++ .github/workflows/release.yml | 83 +++ .gitignore | 2 + .goreleaser.yaml | 62 ++ Formula/lolcathost.rb | 57 ++ Makefile | 120 ++++ README.md | 332 +++++++++++ cmd/lolcathost/main.go | 307 ++++++++++ docs/index.html | 670 +++++++++++++++++++++ docs/lolcathost.png | Bin 0 -> 427786 bytes go.mod | 38 ++ go.sum | 64 ++ internal/client/client.go | 427 ++++++++++++++ internal/client/client_test.go | 516 ++++++++++++++++ internal/config/config.go | 541 +++++++++++++++++ internal/config/config_test.go | 267 +++++++++ internal/config/validation.go | 211 +++++++ internal/config/validation_test.go | 436 ++++++++++++++ internal/daemon/daemon.go | 133 +++++ internal/daemon/dns.go | 142 +++++ internal/daemon/dns_test.go | 108 ++++ internal/daemon/hosts.go | 319 ++++++++++ internal/daemon/hosts_test.go | 422 ++++++++++++++ internal/daemon/peercred_darwin.go | 57 ++ internal/daemon/peercred_linux.go | 37 ++ internal/daemon/security.go | 196 +++++++ internal/daemon/security_test.go | 206 +++++++ internal/daemon/server.go | 803 +++++++++++++++++++++++++ internal/installer/installer.go | 474 +++++++++++++++ internal/protocol/protocol.go | 226 ++++++++ internal/protocol/protocol_test.go | 227 ++++++++ internal/tui/app.go | 904 +++++++++++++++++++++++++++++ internal/tui/form.go | 336 +++++++++++ internal/tui/groups.go | 232 ++++++++ internal/tui/list.go | 429 ++++++++++++++ internal/tui/list_test.go | 409 +++++++++++++ internal/tui/presets.go | 356 ++++++++++++ internal/tui/styles.go | 150 +++++ internal/version/checker.go | 159 +++++ internal/version/checker_test.go | 99 ++++ semver.yaml | 22 + 41 files changed, 10626 insertions(+) create mode 100644 .github/workflows/autoupdate.yaml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .goreleaser.yaml create mode 100644 Formula/lolcathost.rb create mode 100644 Makefile create mode 100644 README.md create mode 100644 cmd/lolcathost/main.go create mode 100644 docs/index.html create mode 100644 docs/lolcathost.png create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/client/client.go create mode 100644 internal/client/client_test.go create mode 100644 internal/config/config.go create mode 100644 internal/config/config_test.go create mode 100644 internal/config/validation.go create mode 100644 internal/config/validation_test.go create mode 100644 internal/daemon/daemon.go create mode 100644 internal/daemon/dns.go create mode 100644 internal/daemon/dns_test.go create mode 100644 internal/daemon/hosts.go create mode 100644 internal/daemon/hosts_test.go create mode 100644 internal/daemon/peercred_darwin.go create mode 100644 internal/daemon/peercred_linux.go create mode 100644 internal/daemon/security.go create mode 100644 internal/daemon/security_test.go create mode 100644 internal/daemon/server.go create mode 100644 internal/installer/installer.go create mode 100644 internal/protocol/protocol.go create mode 100644 internal/protocol/protocol_test.go create mode 100644 internal/tui/app.go create mode 100644 internal/tui/form.go create mode 100644 internal/tui/groups.go create mode 100644 internal/tui/list.go create mode 100644 internal/tui/list_test.go create mode 100644 internal/tui/presets.go create mode 100644 internal/tui/styles.go create mode 100644 internal/version/checker.go create mode 100644 internal/version/checker_test.go create mode 100644 semver.yaml diff --git a/.github/workflows/autoupdate.yaml b/.github/workflows/autoupdate.yaml new file mode 100644 index 0000000..0937faa --- /dev/null +++ b/.github/workflows/autoupdate.yaml @@ -0,0 +1,47 @@ +name: AutoUpdate + +on: + schedule: + - cron: "0 3 * * *" + workflow_dispatch: + +jobs: + prepare: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.24" + cache: true + + - name: Install dependencies + run: go get ./... + + test: + needs: prepare + runs-on: ubuntu-latest + container: golang:1 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install dependencies + run: apt-get update && apt-get install -y ca-certificates make + + - name: Tidy and update modules + run: | + go mod tidy + go get -u -v ./... + + - name: Run tests + run: CI_RUN=${CI} go test -v ./... + + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "Update go.mod and go.sum" + file_pattern: "go.mod go.sum" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0b1a6ea --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,83 @@ +name: Release + +on: + push: + branches: + - main + paths: + - "**.go" + - "go.mod" + - "go.sum" + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.24" + + - name: Run tests + run: go test -race -v ./... + + version: + needs: test + runs-on: ubuntu-latest + outputs: + version: ${{ steps.semver.outputs.version }} + version_tag: ${{ steps.semver.outputs.version_tag }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Calculate version + id: semver + uses: lukaszraczylo/semver-generator@v1 + with: + config_file: semver.yaml + repository_local: true + + - name: Print version + run: | + echo "Version: ${{ steps.semver.outputs.version }}" + echo "Version tag: ${{ steps.semver.outputs.version_tag }}" + + release: + needs: version + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.24" + + - name: Create and push tag + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag -a ${{ needs.version.outputs.version_tag }} -m "Release ${{ needs.version.outputs.version }}" + git push origin ${{ needs.version.outputs.version_tag }} + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: "~> v2" + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f3c7e2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +CLAUDE.md +build diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..953a22a --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,62 @@ +version: 2 + +before: + hooks: + - go mod tidy + +builds: + - id: lolcathost + main: ./cmd/lolcathost + binary: lolcathost + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + goarch: + - amd64 + - arm64 + ldflags: + - -s -w + - -X main.appVersion={{.Version}} + +archives: + - id: lolcathost + format: tar.gz + name_template: "lolcathost-{{ .Version }}-{{ .Os }}-{{ .Arch }}" + files: + - LICENSE + - README.md + +checksum: + name_template: "lolcathost-{{ .Version }}-checksums.txt" + algorithm: sha256 + +changelog: + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" + - "^Merge" + - "^WIP" + +release: + github: + owner: lukaszraczylo + name: lolcathost + name_template: "Release {{.Version}}" + draft: false + prerelease: auto + +brews: + - repository: + owner: lukaszraczylo + name: brew-taps + token: "{{ .Env.HOMEBREW_TAP_TOKEN }}" + directory: Formula + homepage: https://github.com/lukaszraczylo/lolcathost + description: "Dynamic host management tool for macOS and Linux with TUI" + license: MIT + test: | + system "#{bin}/lolcathost", "--version" diff --git a/Formula/lolcathost.rb b/Formula/lolcathost.rb new file mode 100644 index 0000000..e23b1be --- /dev/null +++ b/Formula/lolcathost.rb @@ -0,0 +1,57 @@ +class Lolcathost < Formula + desc "Dynamic host management tool for macOS and Linux with TUI" + homepage "https://github.com/lukaszraczylo/lolcathost" + license "MIT" + + version "0.1.0" + + on_macos do + on_arm do + url "https://github.com/lukaszraczylo/lolcathost/releases/download/v#{version}/lolcathost-#{version}-darwin-arm64.tar.gz" + sha256 "PLACEHOLDER_SHA256_DARWIN_ARM64" + end + + on_intel do + url "https://github.com/lukaszraczylo/lolcathost/releases/download/v#{version}/lolcathost-#{version}-darwin-amd64.tar.gz" + sha256 "PLACEHOLDER_SHA256_DARWIN_AMD64" + end + end + + on_linux do + on_arm do + url "https://github.com/lukaszraczylo/lolcathost/releases/download/v#{version}/lolcathost-#{version}-linux-arm64.tar.gz" + sha256 "PLACEHOLDER_SHA256_LINUX_ARM64" + end + + on_intel do + url "https://github.com/lukaszraczylo/lolcathost/releases/download/v#{version}/lolcathost-#{version}-linux-amd64.tar.gz" + sha256 "PLACEHOLDER_SHA256_LINUX_AMD64" + end + end + + def install + bin.install "lolcathost" + end + + def caveats + <<~EOS + lolcathost requires root access for the daemon to modify /etc/hosts. + + After installation: + 1. Run: sudo lolcathost --install + This will install the LaunchDaemon (macOS) or systemd service (Linux) + + 2. Create a config file at ~/.config/lolcathost/config.yaml + + 3. Run: lolcathost + This launches the TUI for managing host entries + + For more information: + https://github.com/lukaszraczylo/lolcathost + EOS + end + + test do + assert_match version.to_s, shell_output("#{bin}/lolcathost --version") + end +end diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3e48e96 --- /dev/null +++ b/Makefile @@ -0,0 +1,120 @@ +.PHONY: build test lint vet staticcheck clean install uninstall fmt + +# Build variables +BINARY_NAME=lolcathost +VERSION?=1.0.0 +BUILD_DIR=./build +LDFLAGS=-ldflags "-s -w -X main.appVersion=$(VERSION)" + +# Go commands +GOCMD=go +GOBUILD=$(GOCMD) build +GOTEST=$(GOCMD) test +GOVET=$(GOCMD) vet +GOFMT=$(GOCMD) fmt +GOMOD=$(GOCMD) mod + +# Default target +all: lint test build + +# Build the binary +build: + $(GOBUILD) $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME) ./cmd/lolcathost + +# Build for all platforms +build-all: build-darwin-arm64 build-darwin-amd64 build-linux-arm64 build-linux-amd64 + +build-darwin-arm64: + GOOS=darwin GOARCH=arm64 $(GOBUILD) $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-darwin-arm64 ./cmd/lolcathost + +build-darwin-amd64: + GOOS=darwin GOARCH=amd64 $(GOBUILD) $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-darwin-amd64 ./cmd/lolcathost + +build-linux-arm64: + GOOS=linux GOARCH=arm64 $(GOBUILD) $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-arm64 ./cmd/lolcathost + +build-linux-amd64: + GOOS=linux GOARCH=amd64 $(GOBUILD) $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64 ./cmd/lolcathost + +# Run tests +test: + $(GOTEST) -v ./... + +# Run tests with coverage +test-coverage: + $(GOTEST) -v -coverprofile=coverage.out ./... + $(GOCMD) tool cover -html=coverage.out -o coverage.html + +# Run single test +test-run: + $(GOTEST) -v -run $(TEST) ./... + +# Run benchmarks +bench: + $(GOTEST) -bench=. -benchmem ./... + +# Linting +lint: vet staticcheck + +vet: + $(GOVET) ./... + +staticcheck: + @command -v staticcheck >/dev/null 2>&1 || { echo "Installing staticcheck..."; go install honnef.co/go/tools/cmd/staticcheck@latest; } + staticcheck ./... + +# Format code +fmt: + $(GOFMT) ./... + +# Tidy dependencies +tidy: + $(GOMOD) tidy + +# Clean build artifacts +clean: + rm -rf $(BUILD_DIR) + rm -f coverage.out coverage.html + +# Install locally (for development) +install: build + sudo cp $(BUILD_DIR)/$(BINARY_NAME) /usr/local/bin/ + @echo "Installed to /usr/local/bin/$(BINARY_NAME)" + @echo "Run 'sudo lolcathost --install' to set up the daemon" + +# Uninstall +uninstall: + sudo rm -f /usr/local/bin/$(BINARY_NAME) + @echo "Removed /usr/local/bin/$(BINARY_NAME)" + @echo "Note: Run 'sudo lolcathost --uninstall' first to remove the daemon" + +# Development helpers +dev: fmt lint test build + +# Run the TUI (requires daemon to be installed) +run: build + $(BUILD_DIR)/$(BINARY_NAME) + +# Run as daemon (requires sudo) +run-daemon: build + sudo $(BUILD_DIR)/$(BINARY_NAME) --daemon + +# Show help +help: + @echo "Available targets:" + @echo " all - Lint, test, and build" + @echo " build - Build the binary" + @echo " build-all - Build for all platforms" + @echo " test - Run tests" + @echo " test-coverage - Run tests with coverage report" + @echo " test-run - Run specific test (use TEST=TestName)" + @echo " bench - Run benchmarks" + @echo " lint - Run linters (vet + staticcheck)" + @echo " fmt - Format code" + @echo " tidy - Tidy go.mod" + @echo " clean - Clean build artifacts" + @echo " install - Install binary to /usr/local/bin" + @echo " uninstall - Remove binary from /usr/local/bin" + @echo " dev - Format, lint, test, and build" + @echo " run - Run the TUI" + @echo " run-daemon - Run as daemon (requires sudo)" diff --git a/README.md b/README.md new file mode 100644 index 0000000..b7b02c4 --- /dev/null +++ b/README.md @@ -0,0 +1,332 @@ +

+ lolcathost logo +

+ +

+ lolcathost +

+ +

+ Release + License + Go Report Card +

+ +

+ Dynamic hosts file manager with interactive terminal UI +

+ +lolcathost manages your `/etc/hosts` file with an interactive terminal interface. It provides real-time management, automatic backups, group organization, presets, and a secure daemon-based architecture. + +## Features + +- **Interactive TUI** - Terminal interface with keyboard navigation +- **Live management** - Add, edit, and delete host entries without restarting +- **Groups** - Organize hosts into logical groups +- **Presets** - Save and apply preset configurations with a single command +- **Auto-backup** - Automatic backups before every change with rollback support +- **Secure daemon** - Privileged daemon handles file access via Unix socket IPC +- **Domain blocking** - Configurable blocklist to prevent dangerous entries +- **Cross-platform** - Works on macOS (LaunchDaemon) and Linux (systemd) +- **CLI & TUI** - Both command-line and interactive modes for flexibility +- **Auto-update check** - Notifies you when a new version is available + +## Comparison with Other Tools + +| Feature | lolcathost | [HostsMan](https://hostsfileman.github.io/) | [Gas Mask](https://github.com/2ndalpha/gasmask) | Manual editing | +|---------|------------|---------------------------------------------|------------------------------------------------|----------------| +| **Platform** | macOS/Linux | Windows | macOS only | All | +| **Interface** | Terminal TUI | Desktop GUI | Desktop GUI | Text editor | +| **Daemon architecture** | Yes (secure) | No | No | N/A | +| **Real-time sync** | Yes | No | Manual | Manual | +| **Groups** | Yes | Yes | Yes | Manual | +| **Presets** | Yes | Yes | Yes | No | +| **Auto-backup** | 10 rolling | Manual | Manual | No | +| **Rollback** | Yes | No | No | No | +| **CLI automation** | Yes | Limited | No | Yes | +| **Rate limiting** | Yes | No | No | N/A | +| **Domain blocking** | Yes | No | No | No | +| **Auto-update check** | Yes | No | No | N/A | + +## Installation + +### Homebrew (macOS/Linux) + +```bash +brew install lukaszraczylo/brew-taps/lolcathost +``` + +After Homebrew installation, run: + +```bash +sudo lolcathost --install +``` + +### Quick Install + +```bash +curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/lolcathost/main/install.sh | bash +``` + +### Manual Download + +Download binaries from the [releases page](https://github.com/lukaszraczylo/lolcathost/releases). + +### Build from Source + +```bash +git clone https://github.com/lukaszraczylo/lolcathost.git +cd lolcathost +make build +sudo ./build/lolcathost --install +``` + +### Post-Installation + +The installer will: +- Install the binary to `/usr/local/bin/lolcathost` +- Create a LaunchDaemon (macOS) or systemd service (Linux) +- Start the daemon automatically +- Create the default config at `~/.config/lolcathost/config.yaml` + +## Quick Start + +After installation, open a **new terminal** and run: + +```bash +lolcathost +``` + +### Keyboard Controls + +| Key | Action | +|-----|--------| +| `↑↓` / `j/k` | Navigate entries | +| `Space` / `Enter` | Toggle entry enabled/disabled | +| `n` | Add new host entry | +| `e` | Edit selected entry | +| `d` | Delete selected entry | +| `p` | Open preset picker | +| `g` | Open group manager | +| `/` | Search | +| `r` | Refresh list | +| `?` | Show help | +| `q` | Quit | + +## Configuration + +### Config File Location + +Default: `~/.config/lolcathost/config.yaml` + +### Example Configuration + +```yaml +# Groups for organizing host entries +groups: + - name: development + hosts: + - domain: myapp.local + ip: 127.0.0.1 + enabled: true + - domain: api.myapp.local + ip: 127.0.0.1 + enabled: true + + - name: staging + hosts: + - domain: staging.example.com + ip: 192.168.1.100 + enabled: false + +# Presets for quick configuration switching +presets: + - name: work + enable: + - myapp-local + - api-myapp-local + disable: + - staging-example-com + + - name: testing + enable: + - staging-example-com + disable: + - myapp-local + +# Domain blocklist (prevent adding these domains) +blocklist: + - google.com + - facebook.com + - github.com +``` + +### Host Entry Fields + +| Field | Required | Description | +|-------|----------|-------------| +| `domain` | Yes | The hostname (e.g., myapp.local) | +| `ip` | Yes | IP address to resolve to | +| `enabled` | No | Whether entry is active (default: false) | + +Note: Aliases are auto-generated from domain names (e.g., `myapp.local` becomes `myapp-local`). + +## CLI Commands + +```bash +lolcathost # Launch TUI +lolcathost list # List all entries +lolcathost on # Enable entry +lolcathost off # Disable entry +lolcathost preset # Apply preset +lolcathost status # Show daemon status +``` + +### Version & Updates + +```bash +lolcathost --version # Show current version +lolcathost --update # Check for updates +``` + +### Installation Commands + +```bash +sudo lolcathost --install # Install daemon +sudo lolcathost --uninstall # Uninstall daemon +``` + +## Status Indicators + +| Indicator | Description | +|-----------|-------------| +| `● Active` | Entry is enabled and in /etc/hosts | +| `○ Disabled` | Entry is disabled | +| `◐ Pending` | Operation in progress | +| `✗ Error` | Operation failed | + +## Architecture + +lolcathost uses a daemon-based architecture for security: + +``` +┌─────────────────┐ ┌─────────────────────┐ +│ lolcathost │ JSON │ Daemon │ +│ CLI / TUI │◄───────►│ (runs as root) │ +│ (runs as user) │ Unix │ │ +└─────────────────┘ Socket └──────────┬──────────┘ + │ + ┌────────▼────────┐ + │ /etc/hosts │ + └─────────────────┘ +``` + +**Daemon** (runs as root): +- Handles `/etc/hosts` modifications +- Creates automatic backups (10 rolling) +- Validates inputs (domain, IP) +- Rate limiting protection (100 req/min per PID) +- Flushes DNS cache automatically + +**Client** (CLI/TUI, runs as user): +- Connects via Unix socket +- JSON protocol for commands +- No sudo required for operations +- Real-time status updates + +Socket: `/var/run/lolcathost.sock` +Backups: `/var/backups/lolcathost/` + +## Troubleshooting + +### "daemon not running (socket not found)" + +The daemon isn't running. Install or reinstall: + +```bash +sudo lolcathost --uninstall +sudo lolcathost --install +``` + +Then open a **new terminal** for group membership to take effect. + +### Check Daemon Status + +```bash +# macOS +sudo launchctl list | grep lolcathost + +# Linux +sudo systemctl status lolcathost +``` + +### View Daemon Logs + +```bash +# macOS/Linux +cat /var/log/lolcathost/daemon.log +cat /var/log/lolcathost/daemon.err +``` + +### DNS Cache Not Flushing + +lolcathost automatically flushes the DNS cache after changes: + +- **macOS**: Uses `dscacheutil -flushcache` and `killall -HUP mDNSResponder` +- **Linux**: Uses `systemd-resolve --flush-caches` or `nscd -i hosts` + +If changes don't take effect, manually flush: + +```bash +# macOS +sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder + +# Linux (systemd) +sudo systemd-resolve --flush-caches +``` + +## Development + +### Prerequisites + +- Go 1.24+ +- macOS or Linux + +### Build + +```bash +make build # Build binary +make test # Run tests +make test-coverage # Tests with coverage +make lint # Run linters +make dev # Format, lint, test, build +``` + +### Project Structure + +``` +cmd/lolcathost/ - Entry point, CLI commands +internal/ + protocol/ - JSON message types (Unix socket) + config/ - YAML config parsing, hot-reload + daemon/ - Socket server, /etc/hosts management + client/ - Socket client library + installer/ - --install/--uninstall logic + tui/ - Bubble Tea TUI + version/ - Update checker +``` + +## License + +MIT License - see [LICENSE](LICENSE). + +## Acknowledgments + +- [Bubble Tea](https://github.com/charmbracelet/bubbletea) - Terminal UI framework +- [Lipgloss](https://github.com/charmbracelet/lipgloss) - Terminal styling + +## Links + +- [Website](https://lukaszraczylo.github.io/lolcathost) +- [Issues](https://github.com/lukaszraczylo/lolcathost/issues) +- [Releases](https://github.com/lukaszraczylo/lolcathost/releases) diff --git a/cmd/lolcathost/main.go b/cmd/lolcathost/main.go new file mode 100644 index 0000000..89a71e3 --- /dev/null +++ b/cmd/lolcathost/main.go @@ -0,0 +1,307 @@ +// Package main provides the entry point for the lolcathost application. +package main + +import ( + "context" + "flag" + "fmt" + "os" + "text/tabwriter" + "time" + + "github.com/lukaszraczylo/lolcathost/internal/client" + "github.com/lukaszraczylo/lolcathost/internal/config" + "github.com/lukaszraczylo/lolcathost/internal/daemon" + "github.com/lukaszraczylo/lolcathost/internal/installer" + "github.com/lukaszraczylo/lolcathost/internal/protocol" + "github.com/lukaszraczylo/lolcathost/internal/tui" + "github.com/lukaszraczylo/lolcathost/internal/version" +) + +// version is set at compile time via ldflags +var appVersion = "dev" + +const ( + githubOwner = "lukaszraczylo" + githubRepo = "lolcathost" +) + +func main() { + // Flags + daemonMode := flag.Bool("daemon", false, "Run as daemon (called by LaunchDaemon/systemd)") + installFlag := flag.Bool("install", false, "Install the daemon service (requires sudo)") + uninstallFlag := flag.Bool("uninstall", false, "Uninstall the daemon service (requires sudo)") + versionFlag := flag.Bool("version", false, "Show version") + updateFlag := flag.Bool("update", false, "Check for updates") + configPath := flag.String("config", config.DefaultConfigPath(), "Path to config file") + + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "lolcathost - Dynamic Host Management\n\n") + fmt.Fprintf(os.Stderr, "Usage:\n") + fmt.Fprintf(os.Stderr, " lolcathost Launch TUI\n") + fmt.Fprintf(os.Stderr, " lolcathost list List all entries\n") + fmt.Fprintf(os.Stderr, " lolcathost on Enable entry\n") + fmt.Fprintf(os.Stderr, " lolcathost off Disable entry\n") + fmt.Fprintf(os.Stderr, " lolcathost preset Apply preset\n") + fmt.Fprintf(os.Stderr, " lolcathost status Show daemon status\n") + fmt.Fprintf(os.Stderr, "\n") + fmt.Fprintf(os.Stderr, "Installation:\n") + fmt.Fprintf(os.Stderr, " sudo lolcathost --install Install daemon\n") + fmt.Fprintf(os.Stderr, " sudo lolcathost --uninstall Uninstall daemon\n") + fmt.Fprintf(os.Stderr, "\n") + fmt.Fprintf(os.Stderr, "Options:\n") + flag.PrintDefaults() + } + + flag.Parse() + + // Version + if *versionFlag { + fmt.Printf("lolcathost version %s\n", appVersion) + os.Exit(0) + } + + // Update check + if *updateFlag { + checkForUpdates() + os.Exit(0) + } + + // Install/Uninstall + if *installFlag { + runInstall() + return + } + + if *uninstallFlag { + runUninstall() + return + } + + // Daemon mode + if *daemonMode { + runDaemon(*configPath) + return + } + + // Parse subcommand + args := flag.Args() + if len(args) == 0 { + // No subcommand - launch TUI + runTUI(*configPath) + return + } + + // Handle subcommands + switch args[0] { + case "list": + runList() + case "on": + if len(args) < 2 { + fmt.Fprintln(os.Stderr, "Usage: lolcathost on ") + os.Exit(1) + } + runOn(args[1]) + case "off": + if len(args) < 2 { + fmt.Fprintln(os.Stderr, "Usage: lolcathost off ") + os.Exit(1) + } + runOff(args[1]) + case "preset": + if len(args) < 2 { + fmt.Fprintln(os.Stderr, "Usage: lolcathost preset ") + os.Exit(1) + } + runPreset(args[1]) + case "status": + runStatus() + default: + fmt.Fprintf(os.Stderr, "Unknown command: %s\n", args[0]) + flag.Usage() + os.Exit(1) + } +} + +func runInstall() { + inst, err := installer.New() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + if err := inst.Install(); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } +} + +func runUninstall() { + inst, err := installer.New() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + if err := inst.Uninstall(); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } +} + +func runDaemon(configPath string) { + daemon.Version = appVersion + d, err := daemon.New(configPath) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to create daemon: %v\n", err) + os.Exit(1) + } + + if err := d.Run(); err != nil { + fmt.Fprintf(os.Stderr, "Daemon error: %v\n", err) + os.Exit(1) + } +} + +func runTUI(configPath string) { + // Check installation + if err := installer.CheckInstallation(); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + fmt.Fprintln(os.Stderr, "\nTo install, run: sudo lolcathost --install") + os.Exit(1) + } + + if err := tui.RunWithVersion(protocol.SocketPath, configPath, appVersion, githubOwner, githubRepo); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } +} + +func runList() { + c := connectClient() + defer c.Close() + + entries, err := c.List() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + if len(entries) == 0 { + fmt.Println("No entries configured.") + return + } + + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + fmt.Fprintln(w, "STATUS\tDOMAIN\tIP\tALIAS\tGROUP") + fmt.Fprintln(w, "------\t------\t--\t-----\t-----") + + for _, e := range entries { + status := "○" + if e.Enabled { + status = "●" + } + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", status, e.Domain, e.IP, e.Alias, e.Group) + } + + w.Flush() +} + +func runOn(alias string) { + c := connectClient() + defer c.Close() + + data, err := c.Enable(alias) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + fmt.Printf("✓ Enabled: %s → %s\n", alias, data.Domain) +} + +func runOff(alias string) { + c := connectClient() + defer c.Close() + + data, err := c.Disable(alias) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + fmt.Printf("✓ Disabled: %s → %s\n", alias, data.Domain) +} + +func runPreset(name string) { + c := connectClient() + defer c.Close() + + if err := c.ApplyPreset(name); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + fmt.Printf("✓ Applied preset: %s\n", name) +} + +func runStatus() { + c := connectClient() + defer c.Close() + + status, err := c.Status() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + fmt.Printf("Status: %s\n", greenIf("running", status.Running)) + fmt.Printf("Version: %s\n", status.Version) + fmt.Printf("Uptime: %d seconds\n", status.Uptime) + fmt.Printf("Active entries: %d\n", status.ActiveCount) + fmt.Printf("Total requests: %d\n", status.RequestCount) +} + +func connectClient() *client.Client { + // Check installation first + if err := installer.CheckInstallation(); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + fmt.Fprintln(os.Stderr, "\nTo install, run: sudo lolcathost --install") + os.Exit(1) + } + + c := client.New(protocol.SocketPath) + if err := c.Connect(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to connect to daemon: %v\n", err) + os.Exit(1) + } + + return c +} + +func greenIf(s string, condition bool) string { + if condition { + return "\033[32m" + s + "\033[0m" + } + return "\033[31mnot " + s + "\033[0m" +} + +func checkForUpdates() { + fmt.Printf("lolcathost version %s\n", appVersion) + fmt.Println("Checking for updates...") + + checker := version.NewChecker(githubOwner, githubRepo, appVersion) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + update := checker.CheckForUpdate(ctx) + if update == nil { + fmt.Println("You are running the latest version.") + return + } + + fmt.Printf("\n\033[32mUpdate available: v%s\033[0m\n", update.LatestVersion) + fmt.Printf("Download: %s\n", update.ReleaseURL) + fmt.Println("\nTo update, download the latest release from the URL above") + fmt.Println("or use your package manager (e.g., 'brew upgrade lolcathost').") +} diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..a1d2a64 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,670 @@ + + + + + + lolcathost - Dynamic Hosts File Manager + + + + + + + + + + + + + + + +
+
+
+
+
+ +
+
+
+ lolcathost logo +
+
+
lolcathost
+
+

+ Dynamic Hosts File
Manager +

+

+ Terminal interface for managing your /etc/hosts file with automatic backups, groups, presets, and a secure daemon architecture. +

+ +
+ Version + License + Go Report +
+
+
+
+ + +
+
+
+

Features

+

Everything you need for managing local host entries

+
+
+
+
+
+ +
+
+

Interactive TUI

+

Beautiful terminal interface with real-time updates and keyboard navigation

+
+
+
+
+
+
+ +
+
+

Live Management

+

Add, edit, delete, and toggle host entries without restarting

+
+
+
+
+
+
+ +
+
+

Groups

+

Organize hosts into groups for better management

+
+
+
+
+
+
+ +
+
+

Presets

+

Save and apply preset configurations with a single command

+
+
+
+
+
+
+ +
+
+

Auto-Backup

+

Automatic backups before every change with rollback support

+
+
+
+
+
+
+ +
+
+

Secure Daemon

+

Privileged daemon handles file access via Unix socket IPC

+
+
+
+
+
+
+ +
+
+

Domain Blocking

+

Configurable domain blocklist to prevent dangerous entries

+
+
+
+
+
+
+ +
+
+

Cross-Platform

+

Works on macOS (LaunchDaemon) and Linux (systemd)

+
+
+
+
+
+
+ +
+
+

CLI & TUI

+

Both command-line and interactive modes for flexibility

+
+
+
+
+
+
+ + +
+
+
+

Installation

+

Get started in under a minute

+
+
+
+

+ + Quick Install +

+
curl -fsSL https://raw.githubusercontent.com/lukaszraczylo/lolcathost/main/install.sh | bash
+
+
+

+ + Build from Source +

+
git clone https://github.com/lukaszraczylo/lolcathost.git
+cd lolcathost
+make build
+sudo ./build/lolcathost --install
+
+
+

+ + Post-Installation +

+

The installer will:

+
    +
  • Install the binary to /usr/local/bin/lolcathost
  • +
  • Create a LaunchDaemon (macOS) or systemd service (Linux)
  • +
  • Start the daemon automatically
  • +
  • Create the default config at ~/.config/lolcathost/config.yaml
  • +
+
+
+
+
+ + +
+
+
+

Usage

+

Simple and intuitive interface

+
+
+
+

Interactive Mode (TUI)

+
lolcathost
+

Keyboard Controls

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
KeyAction
up/down or j/kNavigate entries
Space or EnterToggle entry enabled/disabled
nAdd new host entry
eEdit selected entry
dDelete selected entry
pOpen preset picker
/Search
rRefresh list
?Show help
qQuit
+
+
+
+

Status Indicators

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
IndicatorDescription
● ActiveEntry is enabled and in /etc/hosts
○ DisabledEntry is disabled
◔ PendingOperation in progress
✗ ErrorOperation failed
+
+
+
+
+
+ + +
+
+
+

Configuration

+

Flexible YAML-based configuration

+
+
+
+

Config File Location

+

Default: ~/.config/lolcathost/config.yaml

+
+
+

Example Configuration

+
# Groups for organizing host entries
+groups:
+  - name: development
+    hosts:
+      - domain: myapp.local
+        ip: 127.0.0.1
+        enabled: true
+      - domain: api.myapp.local
+        ip: 127.0.0.1
+        enabled: true
+
+  - name: staging
+    hosts:
+      - domain: staging.example.com
+        ip: 192.168.1.100
+        enabled: false
+
+# Presets for quick configuration switching
+presets:
+  - name: work
+    enable:
+      - myapp.local
+      - api.myapp.local
+    disable:
+      - staging.example.com
+
+  - name: testing
+    enable:
+      - staging.example.com
+    disable:
+      - myapp.local
+
+# Domain blocklist (prevent adding these domains)
+blocklist:
+  - google.com
+  - facebook.com
+  - github.com
+
+
+

Host Entry Fields

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldRequiredDescription
domainYesThe hostname (e.g., myapp.local)
ipYesIP address to resolve to
enabledNoWhether entry is active (default: false)
+
+
+
+
+
+ + +
+
+
+

CLI Commands

+

Scriptable command-line interface

+
+
+
+
+ lolcathost list +
+

List all host entries

+
+
+
+ lolcathost enable <alias> +
+

Enable a host entry by its alias

+
+
+
+ lolcathost disable <alias> +
+

Disable a host entry by its alias

+
+
+
+ lolcathost add -d <domain> -i <ip> -g <group> +
+

Add a new host entry

+
+
+
+ lolcathost delete <alias> +
+

Delete a host entry by its alias

+
+
+
+ lolcathost preset <name> +
+

Apply a named preset

+
+
+
+ lolcathost rollback <backup> +
+

Restore from a backup

+
+
+
+ lolcathost status +
+

Show daemon status

+
+
+
+ sudo lolcathost --install +
+

Install the daemon service

+
+
+
+ sudo lolcathost --uninstall +
+

Uninstall the daemon service

+
+
+
+
+ + +
+
+
+

Architecture

+

Secure daemon-based design

+
+
+
+
+
+

+ + Daemon +

+
    +
  • • Runs as root (LaunchDaemon/systemd)
  • +
  • • Handles /etc/hosts modifications
  • +
  • • Creates automatic backups
  • +
  • • Validates inputs (domain, IP)
  • +
  • • Rate limiting protection
  • +
+
+
+

+ + Client (CLI/TUI) +

+
    +
  • • Runs as regular user
  • +
  • • Connects via Unix socket
  • +
  • • JSON protocol for commands
  • +
  • • No sudo required for operations
  • +
  • • Real-time status updates
  • +
+
+
+
+

+ + Socket: /var/run/lolcathost.sock +

+
+
+
+
+
+ + + + + + + diff --git a/docs/lolcathost.png b/docs/lolcathost.png new file mode 100644 index 0000000000000000000000000000000000000000..a0f58d02147c3cab92be81ca604d7b50ece53c83 GIT binary patch literal 427786 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⩔3nZGx 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

RRx;DP$UJ+0V9g!T}Jz%q=siHn_$yyM#AffR$4 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%0A@%Nh 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 $;T6jBeaKy*kj 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

aLE%!t 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%0AyXDP;uCFyu@J1el}c$CX`#(B_*L*J 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

?A4I~FEAgU@X-9addzA-`ubqz3 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 literal 0 HcmV?d00001 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7fa90a3 --- /dev/null +++ b/go.mod @@ -0,0 +1,38 @@ +module github.com/lukaszraczylo/lolcathost + +go 1.24.2 + +require ( + github.com/charmbracelet/bubbles v0.21.0 + github.com/charmbracelet/bubbletea v1.3.10 + github.com/charmbracelet/lipgloss v1.1.0 + github.com/fsnotify/fsnotify v1.9.0 + github.com/stretchr/testify v1.11.1 + golang.org/x/sys v0.38.0 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/atotto/clipboard v0.1.4 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/charmbracelet/colorprofile v0.3.3 // indirect + github.com/charmbracelet/x/ansi v0.11.2 // indirect + github.com/charmbracelet/x/cellbuf v0.0.14 // indirect + github.com/charmbracelet/x/term v0.2.2 // indirect + github.com/clipperhouse/displaywidth v0.6.0 // indirect + github.com/clipperhouse/stringish v0.1.1 // indirect + github.com/clipperhouse/uax29/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/lucasb-eyer/go-colorful v1.3.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.19 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.16.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + golang.org/x/text v0.31.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..8a36fb8 --- /dev/null +++ b/go.sum @@ -0,0 +1,64 @@ +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= +github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= +github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= +github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= +github.com/charmbracelet/colorprofile v0.3.3 h1:DjJzJtLP6/NZ8p7Cgjno0CKGr7wwRJGxWUwh2IyhfAI= +github.com/charmbracelet/colorprofile v0.3.3/go.mod h1:nB1FugsAbzq284eJcjfah2nhdSLppN2NqvfotkfRYP4= +github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= +github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/x/ansi v0.11.2 h1:XAG3FSjiVtFvgEgGrNBkCNNYrsucAt8c6bfxHyROLLs= +github.com/charmbracelet/x/ansi v0.11.2/go.mod h1:9tY2bzX5SiJCU0iWyskjBeI2BRQfvPqI+J760Mjf+Rg= +github.com/charmbracelet/x/cellbuf v0.0.14 h1:iUEMryGyFTelKW3THW4+FfPgi4fkmKnnaLOXuc+/Kj4= +github.com/charmbracelet/x/cellbuf v0.0.14/go.mod h1:P447lJl49ywBbil/KjCk2HexGh4tEY9LH0/1QrZZ9rA= +github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk= +github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI= +github.com/clipperhouse/displaywidth v0.6.0 h1:k32vueaksef9WIKCNcoqRNyKbyvkvkysNYnAWz2fN4s= +github.com/clipperhouse/displaywidth v0.6.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o= +github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= +github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= +github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= +github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= +github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= +github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/client/client.go b/internal/client/client.go new file mode 100644 index 0000000..4db93bd --- /dev/null +++ b/internal/client/client.go @@ -0,0 +1,427 @@ +// Package client provides a client library for communicating with the lolcathost daemon. +package client + +import ( + "bufio" + "encoding/json" + "fmt" + "net" + "sync" + "time" + + "github.com/lukaszraczylo/lolcathost/internal/protocol" +) + +// Client is a client for the lolcathost daemon. +type Client struct { + socketPath string + conn net.Conn + reader *bufio.Reader + timeout time.Duration + mu sync.Mutex +} + +// New creates a new client. +func New(socketPath string) *Client { + return &Client{ + socketPath: socketPath, + timeout: 5 * time.Second, + } +} + +// NewWithTimeout creates a new client with a custom timeout. +func NewWithTimeout(socketPath string, timeout time.Duration) *Client { + return &Client{ + socketPath: socketPath, + timeout: timeout, + } +} + +// Connect establishes a connection to the daemon. +func (c *Client) Connect() error { + c.mu.Lock() + defer c.mu.Unlock() + + // Close existing connection if any + if c.conn != nil { + c.conn.Close() + c.conn = nil + c.reader = nil + } + + conn, err := net.DialTimeout("unix", c.socketPath, c.timeout) + if err != nil { + return fmt.Errorf("failed to connect to daemon: %w", err) + } + + c.conn = conn + c.reader = bufio.NewReader(conn) + return nil +} + +// Close closes the connection. +func (c *Client) Close() error { + c.mu.Lock() + defer c.mu.Unlock() + + if c.conn != nil { + err := c.conn.Close() + c.conn = nil + c.reader = nil + return err + } + return nil +} + +// send sends a request and receives a response. +func (c *Client) send(req *protocol.Request) (*protocol.Response, error) { + c.mu.Lock() + defer c.mu.Unlock() + + if c.conn == nil { + return nil, fmt.Errorf("not connected") + } + + // Set deadline + c.conn.SetDeadline(time.Now().Add(c.timeout)) + + // Send request + data, err := json.Marshal(req) + if err != nil { + return nil, fmt.Errorf("failed to marshal request: %w", err) + } + data = append(data, '\n') + + if _, err := c.conn.Write(data); err != nil { + return nil, fmt.Errorf("failed to send request: %w", err) + } + + // Read response + line, err := c.reader.ReadBytes('\n') + if err != nil { + return nil, fmt.Errorf("failed to read response: %w", err) + } + + var resp protocol.Response + if err := json.Unmarshal(line, &resp); err != nil { + return nil, fmt.Errorf("failed to parse response: %w", err) + } + + return &resp, nil +} + +// Ping checks if the daemon is responsive. +func (c *Client) Ping() error { + req, _ := protocol.NewRequest(protocol.RequestPing, nil) + resp, err := c.send(req) + if err != nil { + return err + } + if !resp.IsOK() { + return fmt.Errorf("ping failed: %s", resp.Message) + } + return nil +} + +// Status returns the daemon's status. +func (c *Client) Status() (*protocol.StatusData, error) { + req, _ := protocol.NewRequest(protocol.RequestStatus, nil) + resp, err := c.send(req) + if err != nil { + return nil, err + } + if !resp.IsOK() { + return nil, fmt.Errorf("status failed: %s", resp.Message) + } + + var data protocol.StatusData + if err := resp.ParseData(&data); err != nil { + return nil, err + } + return &data, nil +} + +// List returns all host entries. +func (c *Client) List() ([]protocol.HostEntry, error) { + req, _ := protocol.NewRequest(protocol.RequestList, nil) + resp, err := c.send(req) + if err != nil { + return nil, err + } + if !resp.IsOK() { + return nil, fmt.Errorf("list failed: %s", resp.Message) + } + + var data protocol.ListData + if err := resp.ParseData(&data); err != nil { + return nil, err + } + return data.Entries, nil +} + +// Set enables or disables a host entry by alias. +func (c *Client) Set(alias string, enabled bool, force bool) (*protocol.SetData, error) { + req, _ := protocol.NewRequest(protocol.RequestSet, protocol.SetPayload{ + Alias: alias, + Enabled: enabled, + Force: force, + }) + + resp, err := c.send(req) + if err != nil { + return nil, err + } + if !resp.IsOK() { + return nil, fmt.Errorf("%s: %s", resp.Code, resp.Message) + } + + var data protocol.SetData + if err := resp.ParseData(&data); err != nil { + return nil, err + } + return &data, nil +} + +// Enable enables a host entry by alias. +func (c *Client) Enable(alias string) (*protocol.SetData, error) { + return c.Set(alias, true, false) +} + +// Disable disables a host entry by alias. +func (c *Client) Disable(alias string) (*protocol.SetData, error) { + return c.Set(alias, false, false) +} + +// Add adds a new host entry. +func (c *Client) Add(domain, ip, alias, group string, enabled bool) (*protocol.SetData, error) { + req, _ := protocol.NewRequest(protocol.RequestAdd, protocol.AddPayload{ + Domain: domain, + IP: ip, + Alias: alias, + Group: group, + Enabled: enabled, + }) + + resp, err := c.send(req) + if err != nil { + return nil, err + } + if !resp.IsOK() { + return nil, fmt.Errorf("%s: %s", resp.Code, resp.Message) + } + + var data protocol.SetData + if err := resp.ParseData(&data); err != nil { + return nil, err + } + return &data, nil +} + +// Delete removes a host entry by alias. +func (c *Client) Delete(alias string) error { + req, _ := protocol.NewRequest(protocol.RequestDelete, protocol.DeletePayload{ + Alias: alias, + }) + + resp, err := c.send(req) + if err != nil { + return err + } + if !resp.IsOK() { + return fmt.Errorf("%s: %s", resp.Code, resp.Message) + } + return nil +} + +// AddGroup adds a new group. +func (c *Client) AddGroup(name string) error { + req, _ := protocol.NewRequest(protocol.RequestAddGroup, protocol.GroupPayload{ + Name: name, + }) + + resp, err := c.send(req) + if err != nil { + return err + } + if !resp.IsOK() { + return fmt.Errorf("%s: %s", resp.Code, resp.Message) + } + return nil +} + +// DeleteGroup removes a group and all its hosts. +func (c *Client) DeleteGroup(name string) error { + req, _ := protocol.NewRequest(protocol.RequestDeleteGroup, protocol.GroupPayload{ + Name: name, + }) + + resp, err := c.send(req) + if err != nil { + return err + } + if !resp.IsOK() { + return fmt.Errorf("%s: %s", resp.Code, resp.Message) + } + return nil +} + +// ListGroups returns all group names. +func (c *Client) ListGroups() ([]string, error) { + req, _ := protocol.NewRequest(protocol.RequestListGroups, nil) + resp, err := c.send(req) + if err != nil { + return nil, err + } + if !resp.IsOK() { + return nil, fmt.Errorf("%s: %s", resp.Code, resp.Message) + } + + var data protocol.GroupsData + if err := resp.ParseData(&data); err != nil { + return nil, err + } + return data.Groups, nil +} + +// Sync synchronizes the config to the hosts file. +func (c *Client) Sync() error { + req, _ := protocol.NewRequest(protocol.RequestSync, nil) + resp, err := c.send(req) + if err != nil { + return err + } + if !resp.IsOK() { + return fmt.Errorf("sync failed: %s", resp.Message) + } + return nil +} + +// ApplyPreset applies a named preset. +func (c *Client) ApplyPreset(name string) error { + req, _ := protocol.NewRequest(protocol.RequestPreset, protocol.PresetPayload{ + Name: name, + }) + + resp, err := c.send(req) + if err != nil { + return err + } + if !resp.IsOK() { + return fmt.Errorf("preset failed: %s", resp.Message) + } + return nil +} + +// Rollback restores a backup by name. +func (c *Client) Rollback(backupName string) error { + req, _ := protocol.NewRequest(protocol.RequestRollback, protocol.RollbackPayload{ + BackupName: backupName, + }) + + resp, err := c.send(req) + if err != nil { + return err + } + if !resp.IsOK() { + return fmt.Errorf("rollback failed: %s", resp.Message) + } + return nil +} + +// ListBackups returns available backups. +func (c *Client) ListBackups() ([]protocol.BackupInfo, error) { + req, _ := protocol.NewRequest(protocol.RequestBackups, nil) + resp, err := c.send(req) + if err != nil { + return nil, err + } + if !resp.IsOK() { + return nil, fmt.Errorf("backups failed: %s", resp.Message) + } + + var data protocol.BackupsData + if err := resp.ParseData(&data); err != nil { + return nil, err + } + return data.Backups, nil +} + +// RenameGroup renames a group. +func (c *Client) RenameGroup(oldName, newName string) error { + req, _ := protocol.NewRequest(protocol.RequestRenameGroup, protocol.RenameGroupPayload{ + OldName: oldName, + NewName: newName, + }) + + resp, err := c.send(req) + if err != nil { + return err + } + if !resp.IsOK() { + return fmt.Errorf("%s: %s", resp.Code, resp.Message) + } + return nil +} + +// AddPreset adds a new preset. +func (c *Client) AddPreset(name string, enable, disable []string) error { + req, _ := protocol.NewRequest(protocol.RequestAddPreset, protocol.AddPresetPayload{ + Name: name, + Enable: enable, + Disable: disable, + }) + + resp, err := c.send(req) + if err != nil { + return err + } + if !resp.IsOK() { + return fmt.Errorf("%s: %s", resp.Code, resp.Message) + } + return nil +} + +// DeletePreset removes a preset by name. +func (c *Client) DeletePreset(name string) error { + req, _ := protocol.NewRequest(protocol.RequestDeletePreset, protocol.PresetPayload{ + Name: name, + }) + + resp, err := c.send(req) + if err != nil { + return err + } + if !resp.IsOK() { + return fmt.Errorf("%s: %s", resp.Code, resp.Message) + } + return nil +} + +// ListPresets returns all presets. +func (c *Client) ListPresets() ([]protocol.PresetInfo, error) { + req, _ := protocol.NewRequest(protocol.RequestListPresets, nil) + resp, err := c.send(req) + if err != nil { + return nil, err + } + if !resp.IsOK() { + return nil, fmt.Errorf("%s: %s", resp.Code, resp.Message) + } + + var data protocol.PresetsData + if err := resp.ParseData(&data); err != nil { + return nil, err + } + return data.Presets, nil +} + +// IsConnected checks if the daemon is reachable. +func IsConnected(socketPath string) bool { + client := New(socketPath) + if err := client.Connect(); err != nil { + return false + } + defer client.Close() + + return client.Ping() == nil +} diff --git a/internal/client/client_test.go b/internal/client/client_test.go new file mode 100644 index 0000000..ac9fd15 --- /dev/null +++ b/internal/client/client_test.go @@ -0,0 +1,516 @@ +package client + +import ( + "bufio" + "encoding/json" + "net" + "os" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/lukaszraczylo/lolcathost/internal/protocol" +) + +// mockServer creates a mock Unix socket server for testing +type mockServer struct { + listener net.Listener + path string + handler func(req *protocol.Request) *protocol.Response +} + +func newMockServer(t *testing.T) *mockServer { + // Use /tmp directly to avoid long paths (Unix socket paths have ~104 char limit on macOS) + tmpDir, err := os.MkdirTemp("/tmp", "lolcat") + require.NoError(t, err) + t.Cleanup(func() { os.RemoveAll(tmpDir) }) + + socketPath := filepath.Join(tmpDir, "s.sock") + + listener, err := net.Listen("unix", socketPath) + require.NoError(t, err) + + ms := &mockServer{ + listener: listener, + path: socketPath, + } + + go ms.serve() + + return ms +} + +func (ms *mockServer) serve() { + for { + conn, err := ms.listener.Accept() + if err != nil { + return + } + go ms.handleConn(conn) + } +} + +func (ms *mockServer) handleConn(conn net.Conn) { + defer conn.Close() + + reader := bufio.NewReader(conn) + for { + line, err := reader.ReadBytes('\n') + if err != nil { + return + } + + var req protocol.Request + if err := json.Unmarshal(line, &req); err != nil { + continue + } + + var resp *protocol.Response + if ms.handler != nil { + resp = ms.handler(&req) + } else { + resp, _ = protocol.NewOKResponse(nil) + } + + data, _ := json.Marshal(resp) + conn.Write(append(data, '\n')) + } +} + +func (ms *mockServer) close() { + ms.listener.Close() + os.Remove(ms.path) +} + +func TestClient_Connect(t *testing.T) { + t.Run("success", func(t *testing.T) { + server := newMockServer(t) + defer server.close() + + client := New(server.path) + err := client.Connect() + require.NoError(t, err) + defer client.Close() + + assert.NotNil(t, client.conn) + assert.NotNil(t, client.reader) + }) + + t.Run("failure - socket not found", func(t *testing.T) { + client := New("/nonexistent/socket.sock") + err := client.Connect() + assert.Error(t, err) + }) +} + +func TestClient_Ping(t *testing.T) { + server := newMockServer(t) + defer server.close() + + server.handler = func(req *protocol.Request) *protocol.Response { + if req.Type == protocol.RequestPing { + resp, _ := protocol.NewOKResponse(map[string]string{"pong": "ok"}) + return resp + } + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "unexpected request") + } + + client := New(server.path) + err := client.Connect() + require.NoError(t, err) + defer client.Close() + + err = client.Ping() + assert.NoError(t, err) +} + +func TestClient_Status(t *testing.T) { + server := newMockServer(t) + defer server.close() + + server.handler = func(req *protocol.Request) *protocol.Response { + if req.Type == protocol.RequestStatus { + resp, _ := protocol.NewOKResponse(protocol.StatusData{ + Running: true, + Version: "1.0.0", + Uptime: 3600, + ActiveCount: 5, + RequestCount: 100, + }) + return resp + } + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "unexpected") + } + + client := New(server.path) + err := client.Connect() + require.NoError(t, err) + defer client.Close() + + status, err := client.Status() + require.NoError(t, err) + + assert.True(t, status.Running) + assert.Equal(t, "1.0.0", status.Version) + assert.Equal(t, int64(3600), status.Uptime) + assert.Equal(t, 5, status.ActiveCount) + assert.Equal(t, int64(100), status.RequestCount) +} + +func TestClient_List(t *testing.T) { + server := newMockServer(t) + defer server.close() + + server.handler = func(req *protocol.Request) *protocol.Response { + if req.Type == protocol.RequestList { + resp, _ := protocol.NewOKResponse(protocol.ListData{ + Entries: []protocol.HostEntry{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "a", Enabled: true, Group: "dev"}, + {Domain: "b.com", IP: "127.0.0.1", Alias: "b", Enabled: false, Group: "dev"}, + }, + }) + return resp + } + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "unexpected") + } + + client := New(server.path) + err := client.Connect() + require.NoError(t, err) + defer client.Close() + + entries, err := client.List() + require.NoError(t, err) + + assert.Len(t, entries, 2) + assert.Equal(t, "a.com", entries[0].Domain) + assert.True(t, entries[0].Enabled) + assert.Equal(t, "b.com", entries[1].Domain) + assert.False(t, entries[1].Enabled) +} + +func TestClient_Set(t *testing.T) { + server := newMockServer(t) + defer server.close() + + server.handler = func(req *protocol.Request) *protocol.Response { + if req.Type == protocol.RequestSet { + var payload protocol.SetPayload + req.ParsePayload(&payload) + + resp, _ := protocol.NewOKResponse(protocol.SetData{ + Domain: "example.com", + Applied: true, + }) + return resp + } + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "unexpected") + } + + client := New(server.path) + err := client.Connect() + require.NoError(t, err) + defer client.Close() + + data, err := client.Set("test", true, false) + require.NoError(t, err) + + assert.Equal(t, "example.com", data.Domain) + assert.True(t, data.Applied) +} + +func TestClient_Enable(t *testing.T) { + server := newMockServer(t) + defer server.close() + + server.handler = func(req *protocol.Request) *protocol.Response { + if req.Type == protocol.RequestSet { + var payload protocol.SetPayload + req.ParsePayload(&payload) + assert.True(t, payload.Enabled) + + resp, _ := protocol.NewOKResponse(protocol.SetData{Domain: "test.com", Applied: true}) + return resp + } + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "unexpected") + } + + client := New(server.path) + err := client.Connect() + require.NoError(t, err) + defer client.Close() + + _, err = client.Enable("test") + assert.NoError(t, err) +} + +func TestClient_Disable(t *testing.T) { + server := newMockServer(t) + defer server.close() + + server.handler = func(req *protocol.Request) *protocol.Response { + if req.Type == protocol.RequestSet { + var payload protocol.SetPayload + req.ParsePayload(&payload) + assert.False(t, payload.Enabled) + + resp, _ := protocol.NewOKResponse(protocol.SetData{Domain: "test.com", Applied: true}) + return resp + } + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "unexpected") + } + + client := New(server.path) + err := client.Connect() + require.NoError(t, err) + defer client.Close() + + _, err = client.Disable("test") + assert.NoError(t, err) +} + +func TestClient_Sync(t *testing.T) { + server := newMockServer(t) + defer server.close() + + server.handler = func(req *protocol.Request) *protocol.Response { + if req.Type == protocol.RequestSync { + resp, _ := protocol.NewOKResponse(map[string]bool{"synced": true}) + return resp + } + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "unexpected") + } + + client := New(server.path) + err := client.Connect() + require.NoError(t, err) + defer client.Close() + + err = client.Sync() + assert.NoError(t, err) +} + +func TestClient_ApplyPreset(t *testing.T) { + server := newMockServer(t) + defer server.close() + + server.handler = func(req *protocol.Request) *protocol.Response { + if req.Type == protocol.RequestPreset { + var payload protocol.PresetPayload + req.ParsePayload(&payload) + assert.Equal(t, "local", payload.Name) + + resp, _ := protocol.NewOKResponse(map[string]string{"preset": "local"}) + return resp + } + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "unexpected") + } + + client := New(server.path) + err := client.Connect() + require.NoError(t, err) + defer client.Close() + + err = client.ApplyPreset("local") + assert.NoError(t, err) +} + +func TestClient_Rollback(t *testing.T) { + server := newMockServer(t) + defer server.close() + + server.handler = func(req *protocol.Request) *protocol.Response { + if req.Type == protocol.RequestRollback { + var payload protocol.RollbackPayload + req.ParsePayload(&payload) + assert.Equal(t, "hosts.backup.bak", payload.BackupName) + + resp, _ := protocol.NewOKResponse(map[string]string{"restored": payload.BackupName}) + return resp + } + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "unexpected") + } + + client := New(server.path) + err := client.Connect() + require.NoError(t, err) + defer client.Close() + + err = client.Rollback("hosts.backup.bak") + assert.NoError(t, err) +} + +func TestClient_ListBackups(t *testing.T) { + server := newMockServer(t) + defer server.close() + + server.handler = func(req *protocol.Request) *protocol.Response { + if req.Type == protocol.RequestBackups { + resp, _ := protocol.NewOKResponse(protocol.BackupsData{ + Backups: []protocol.BackupInfo{ + {Name: "hosts.20231201.bak", Timestamp: 1701432000, Size: 1024}, + {Name: "hosts.20231130.bak", Timestamp: 1701345600, Size: 1000}, + }, + }) + return resp + } + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "unexpected") + } + + client := New(server.path) + err := client.Connect() + require.NoError(t, err) + defer client.Close() + + backups, err := client.ListBackups() + require.NoError(t, err) + + assert.Len(t, backups, 2) + assert.Equal(t, "hosts.20231201.bak", backups[0].Name) +} + +func TestClient_ErrorResponse(t *testing.T) { + server := newMockServer(t) + defer server.close() + + server.handler = func(req *protocol.Request) *protocol.Response { + return protocol.NewErrorResponse(protocol.ErrCodeBlockedDomain, "domain is blocked") + } + + client := New(server.path) + err := client.Connect() + require.NoError(t, err) + defer client.Close() + + _, err = client.Set("test", true, false) + assert.Error(t, err) + assert.Contains(t, err.Error(), "domain is blocked") +} + +func TestClient_NotConnected(t *testing.T) { + client := New("/nonexistent/socket.sock") + + _, err := client.Status() + assert.Error(t, err) + assert.Contains(t, err.Error(), "not connected") +} + +func TestClient_Timeout(t *testing.T) { + client := NewWithTimeout("/nonexistent.sock", 100*time.Millisecond) + assert.Equal(t, 100*time.Millisecond, client.timeout) +} + +func TestIsConnected(t *testing.T) { + t.Run("connected", func(t *testing.T) { + server := newMockServer(t) + defer server.close() + + server.handler = func(req *protocol.Request) *protocol.Response { + resp, _ := protocol.NewOKResponse(nil) + return resp + } + + connected := IsConnected(server.path) + assert.True(t, connected) + }) + + t.Run("not connected", func(t *testing.T) { + connected := IsConnected("/nonexistent/socket.sock") + assert.False(t, connected) + }) +} + +// Matrix test for request types +func TestClient_RequestTypes_Matrix(t *testing.T) { + types := []struct { + name string + reqType protocol.RequestType + call func(*Client) error + }{ + {"ping", protocol.RequestPing, func(c *Client) error { return c.Ping() }}, + {"status", protocol.RequestStatus, func(c *Client) error { _, err := c.Status(); return err }}, + {"list", protocol.RequestList, func(c *Client) error { _, err := c.List(); return err }}, + {"sync", protocol.RequestSync, func(c *Client) error { return c.Sync() }}, + {"preset", protocol.RequestPreset, func(c *Client) error { return c.ApplyPreset("test") }}, + {"backups", protocol.RequestBackups, func(c *Client) error { _, err := c.ListBackups(); return err }}, + } + + for _, tt := range types { + t.Run(tt.name, func(t *testing.T) { + server := newMockServer(t) + defer server.close() + + receivedType := protocol.RequestType("") + server.handler = func(req *protocol.Request) *protocol.Response { + receivedType = req.Type + + switch req.Type { + case protocol.RequestStatus: + resp, _ := protocol.NewOKResponse(protocol.StatusData{}) + return resp + case protocol.RequestList: + resp, _ := protocol.NewOKResponse(protocol.ListData{}) + return resp + case protocol.RequestBackups: + resp, _ := protocol.NewOKResponse(protocol.BackupsData{}) + return resp + default: + resp, _ := protocol.NewOKResponse(nil) + return resp + } + } + + client := New(server.path) + err := client.Connect() + require.NoError(t, err) + defer client.Close() + + _ = tt.call(client) + assert.Equal(t, tt.reqType, receivedType) + }) + } +} + +func BenchmarkClient_Ping(b *testing.B) { + tmpDir := b.TempDir() + socketPath := filepath.Join(tmpDir, "bench.sock") + + listener, err := net.Listen("unix", socketPath) + require.NoError(b, err) + defer listener.Close() + + go func() { + for { + conn, err := listener.Accept() + if err != nil { + return + } + go func(c net.Conn) { + defer c.Close() + reader := bufio.NewReader(c) + for { + _, err := reader.ReadBytes('\n') + if err != nil { + return + } + resp, _ := protocol.NewOKResponse(nil) + data, _ := json.Marshal(resp) + c.Write(append(data, '\n')) + } + }(conn) + } + }() + + client := New(socketPath) + err = client.Connect() + require.NoError(b, err) + defer client.Close() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = client.Ping() + } +} diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..8fecbb8 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,541 @@ +// Package config handles YAML configuration parsing and hot-reload. +package config + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "sync" + + "github.com/fsnotify/fsnotify" + "gopkg.in/yaml.v3" +) + +// SystemConfigDir is the system-wide config directory for the daemon. +const SystemConfigDir = "/etc/lolcathost" + +// SystemConfigPath is the system-wide config file path for the daemon. +const SystemConfigPath = "/etc/lolcathost/config.yaml" + +// DefaultConfigDir returns the default config directory path for users. +func DefaultConfigDir() string { + home, err := os.UserHomeDir() + if err != nil { + return "" + } + return filepath.Join(home, ".config", "lolcathost") +} + +// DefaultConfigPath returns the default config file path for users. +func DefaultConfigPath() string { + return filepath.Join(DefaultConfigDir(), "config.yaml") +} + +// FlushMethod defines DNS cache flush methods. +type FlushMethod string + +const ( + FlushMethodAuto FlushMethod = "auto" + FlushMethodDscacheutil FlushMethod = "dscacheutil" + FlushMethodKillall FlushMethod = "killall" + FlushMethodBoth FlushMethod = "both" +) + +// Settings holds global configuration settings. +type Settings struct { + AutoApply bool `yaml:"autoApply"` + FlushMethod FlushMethod `yaml:"flushMethod"` +} + +// Host represents a single host entry in configuration. +type Host struct { + Domain string `yaml:"domain"` + IP string `yaml:"ip"` + Alias string `yaml:"alias"` + Enabled bool `yaml:"enabled"` +} + +// Group represents a group of host entries. +type Group struct { + Name string `yaml:"name"` + Hosts []Host `yaml:"hosts"` +} + +// Preset defines a named preset that enables/disables specific aliases. +type Preset struct { + Name string `yaml:"name"` + Enable []string `yaml:"enable,omitempty"` + Disable []string `yaml:"disable,omitempty"` +} + +// Config represents the complete configuration. +type Config struct { + Settings Settings `yaml:"settings"` + Groups []Group `yaml:"groups"` + Presets []Preset `yaml:"presets"` +} + +// Manager handles configuration loading and watching. +type Manager struct { + path string + config *Config + mu sync.RWMutex + watcher *fsnotify.Watcher + onChange func(*Config) + stopCh chan struct{} +} + +// NewManager creates a new config manager. +func NewManager(path string) *Manager { + return &Manager{ + path: path, + stopCh: make(chan struct{}), + } +} + +// Load reads and parses the configuration file. +func (m *Manager) Load() error { + data, err := os.ReadFile(m.path) + if err != nil { + return fmt.Errorf("failed to read config file: %w", err) + } + + var cfg Config + if err := yaml.Unmarshal(data, &cfg); err != nil { + return fmt.Errorf("failed to parse config file: %w", err) + } + + if err := ValidateConfig(&cfg); err != nil { + return fmt.Errorf("invalid config: %w", err) + } + + m.mu.Lock() + m.config = &cfg + m.mu.Unlock() + + return nil +} + +// Get returns the current configuration. +func (m *Manager) Get() *Config { + m.mu.RLock() + defer m.mu.RUnlock() + return m.config +} + +// Watch starts watching the config file for changes. +func (m *Manager) Watch(onChange func(*Config)) error { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return fmt.Errorf("failed to create watcher: %w", err) + } + + m.watcher = watcher + m.onChange = onChange + + go m.watchLoop() + + if err := watcher.Add(m.path); err != nil { + return fmt.Errorf("failed to watch config file: %w", err) + } + + return nil +} + +func (m *Manager) watchLoop() { + for { + select { + case event, ok := <-m.watcher.Events: + if !ok { + return + } + if event.Has(fsnotify.Write) || event.Has(fsnotify.Create) { + if err := m.Load(); err == nil && m.onChange != nil { + m.onChange(m.Get()) + } + } + case <-m.watcher.Errors: + // Ignore watcher errors + case <-m.stopCh: + return + } + } +} + +// Stop stops watching the config file. +func (m *Manager) Stop() { + close(m.stopCh) + if m.watcher != nil { + m.watcher.Close() + } +} + +// GetAllHosts returns all hosts from all groups. +func (c *Config) GetAllHosts() []Host { + var hosts []Host + for _, g := range c.Groups { + hosts = append(hosts, g.Hosts...) + } + return hosts +} + +// FindHostByAlias finds a host by its alias. +func (c *Config) FindHostByAlias(alias string) (*Host, *Group) { + for i := range c.Groups { + for j := range c.Groups[i].Hosts { + if c.Groups[i].Hosts[j].Alias == alias { + return &c.Groups[i].Hosts[j], &c.Groups[i] + } + } + } + return nil, nil +} + +// FindPreset finds a preset by name. +func (c *Config) FindPreset(name string) *Preset { + for i := range c.Presets { + if c.Presets[i].Name == name { + return &c.Presets[i] + } + } + return nil +} + +// SetHostEnabled sets the enabled state of a host by alias. +func (c *Config) SetHostEnabled(alias string, enabled bool) bool { + for i := range c.Groups { + for j := range c.Groups[i].Hosts { + if c.Groups[i].Hosts[j].Alias == alias { + c.Groups[i].Hosts[j].Enabled = enabled + return true + } + } + } + return false +} + +// GenerateAlias creates a unique alias from a domain name. +func (c *Config) GenerateAlias(domain string) string { + // Convert domain to alias format: example.com -> example-com + alias := strings.ReplaceAll(domain, ".", "-") + alias = strings.ReplaceAll(alias, "_", "-") + alias = strings.ToLower(alias) + + // Check if alias exists, if so append a number + baseAlias := alias + counter := 1 + for { + if existing, _ := c.FindHostByAlias(alias); existing == nil { + break + } + counter++ + alias = fmt.Sprintf("%s-%d", baseAlias, counter) + } + + return alias +} + +// AddHost adds a new host to the configuration. +func (c *Config) AddHost(domain, ip, alias, groupName string, enabled bool) error { + // Auto-generate alias if empty + if alias == "" { + alias = c.GenerateAlias(domain) + } else { + // Check for duplicate alias + if existing, _ := c.FindHostByAlias(alias); existing != nil { + return fmt.Errorf("alias already exists: %s", alias) + } + } + + host := Host{ + Domain: domain, + IP: ip, + Alias: alias, + Enabled: enabled, + } + + // Find or create group + for i := range c.Groups { + if c.Groups[i].Name == groupName { + c.Groups[i].Hosts = append(c.Groups[i].Hosts, host) + return nil + } + } + + // Create new group + c.Groups = append(c.Groups, Group{ + Name: groupName, + Hosts: []Host{host}, + }) + return nil +} + +// AddGroup adds a new empty group. +func (c *Config) AddGroup(name string) error { + // Check if group already exists + for _, g := range c.Groups { + if g.Name == name { + return fmt.Errorf("group already exists: %s", name) + } + } + + c.Groups = append(c.Groups, Group{ + Name: name, + Hosts: []Host{}, + }) + return nil +} + +// DeleteGroup removes a group and all its hosts. +func (c *Config) DeleteGroup(name string) error { + for i, g := range c.Groups { + if g.Name == name { + c.Groups = append(c.Groups[:i], c.Groups[i+1:]...) + return nil + } + } + return fmt.Errorf("group not found: %s", name) +} + +// RenameGroup renames an existing group. +func (c *Config) RenameGroup(oldName, newName string) error { + // Check if new name already exists + for _, g := range c.Groups { + if g.Name == newName { + return fmt.Errorf("group already exists: %s", newName) + } + } + + for i := range c.Groups { + if c.Groups[i].Name == oldName { + c.Groups[i].Name = newName + return nil + } + } + return fmt.Errorf("group not found: %s", oldName) +} + +// GetGroups returns all group names. +func (c *Config) GetGroups() []string { + names := make([]string, len(c.Groups)) + for i, g := range c.Groups { + names[i] = g.Name + } + return names +} + +// DeleteHost removes a host by alias. +func (c *Config) DeleteHost(alias string) bool { + for i := range c.Groups { + for j := range c.Groups[i].Hosts { + if c.Groups[i].Hosts[j].Alias == alias { + c.Groups[i].Hosts = append(c.Groups[i].Hosts[:j], c.Groups[i].Hosts[j+1:]...) + return true + } + } + } + return false +} + +// UpdateHost updates an existing host by alias. +func (c *Config) UpdateHost(oldAlias, domain, ip, newAlias, groupName string) error { + // Find the host + var foundGroup int = -1 + var foundHost int = -1 + for i := range c.Groups { + for j := range c.Groups[i].Hosts { + if c.Groups[i].Hosts[j].Alias == oldAlias { + foundGroup = i + foundHost = j + break + } + } + if foundHost >= 0 { + break + } + } + + if foundHost < 0 { + return fmt.Errorf("alias not found: %s", oldAlias) + } + + // Check for duplicate alias if alias is changing + if oldAlias != newAlias { + if existing, _ := c.FindHostByAlias(newAlias); existing != nil { + return fmt.Errorf("alias already exists: %s", newAlias) + } + } + + // Get current enabled state + enabled := c.Groups[foundGroup].Hosts[foundHost].Enabled + + // If group is changing, move to new group + if c.Groups[foundGroup].Name != groupName { + // Remove from old group + c.Groups[foundGroup].Hosts = append(c.Groups[foundGroup].Hosts[:foundHost], c.Groups[foundGroup].Hosts[foundHost+1:]...) + + // Add to new group + host := Host{ + Domain: domain, + IP: ip, + Alias: newAlias, + Enabled: enabled, + } + + // Find or create target group + found := false + for i := range c.Groups { + if c.Groups[i].Name == groupName { + c.Groups[i].Hosts = append(c.Groups[i].Hosts, host) + found = true + break + } + } + if !found { + c.Groups = append(c.Groups, Group{ + Name: groupName, + Hosts: []Host{host}, + }) + } + } else { + // Update in place + c.Groups[foundGroup].Hosts[foundHost].Domain = domain + c.Groups[foundGroup].Hosts[foundHost].IP = ip + c.Groups[foundGroup].Hosts[foundHost].Alias = newAlias + } + + return nil +} + +// ApplyPreset applies a preset to the configuration. +func (c *Config) ApplyPreset(name string) error { + preset := c.FindPreset(name) + if preset == nil { + return fmt.Errorf("preset not found: %s", name) + } + + for _, alias := range preset.Enable { + c.SetHostEnabled(alias, true) + } + for _, alias := range preset.Disable { + c.SetHostEnabled(alias, false) + } + return nil +} + +// AddPreset adds a new preset. +func (c *Config) AddPreset(name string, enable, disable []string) error { + // Check if preset already exists + for _, p := range c.Presets { + if p.Name == name { + return fmt.Errorf("preset already exists: %s", name) + } + } + + c.Presets = append(c.Presets, Preset{ + Name: name, + Enable: enable, + Disable: disable, + }) + return nil +} + +// DeletePreset removes a preset by name. +func (c *Config) DeletePreset(name string) error { + for i, p := range c.Presets { + if p.Name == name { + c.Presets = append(c.Presets[:i], c.Presets[i+1:]...) + return nil + } + } + return fmt.Errorf("preset not found: %s", name) +} + +// GetPresets returns all presets. +func (c *Config) GetPresets() []Preset { + return c.Presets +} + +// EnsureDefaultGroup ensures at least one group exists, creating "default" if needed. +func (c *Config) EnsureDefaultGroup() { + if len(c.Groups) == 0 { + c.Groups = append(c.Groups, Group{ + Name: "default", + Hosts: []Host{}, + }) + } +} + +// Save writes the configuration to the file. +func (m *Manager) Save() error { + m.mu.RLock() + cfg := m.config + m.mu.RUnlock() + + if cfg == nil { + return fmt.Errorf("no config loaded") + } + + data, err := yaml.Marshal(cfg) + if err != nil { + return fmt.Errorf("failed to marshal config: %w", err) + } + + if err := os.WriteFile(m.path, data, 0644); err != nil { + return fmt.Errorf("failed to write config: %w", err) + } + + return nil +} + +// CreateDefault creates a default configuration file. +func CreateDefault(path string) error { + dir := filepath.Dir(path) + if err := os.MkdirAll(dir, 0755); err != nil { + return fmt.Errorf("failed to create config directory: %w", err) + } + + cfg := &Config{ + Settings: Settings{ + AutoApply: true, + FlushMethod: FlushMethodAuto, + }, + Groups: []Group{ + { + Name: "development", + Hosts: []Host{ + { + Domain: "example.local", + IP: "127.0.0.1", + Alias: "example-local", + Enabled: false, + }, + }, + }, + }, + Presets: []Preset{ + { + Name: "local", + Enable: []string{"example-local"}, + Disable: []string{}, + }, + { + Name: "clear", + Enable: []string{}, + Disable: []string{"example-local"}, + }, + }, + } + + data, err := yaml.Marshal(cfg) + if err != nil { + return fmt.Errorf("failed to marshal default config: %w", err) + } + + if err := os.WriteFile(path, data, 0644); err != nil { + return fmt.Errorf("failed to write default config: %w", err) + } + + return nil +} diff --git a/internal/config/config_test.go b/internal/config/config_test.go new file mode 100644 index 0000000..ab58dfe --- /dev/null +++ b/internal/config/config_test.go @@ -0,0 +1,267 @@ +package config + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestConfig_GetAllHosts(t *testing.T) { + cfg := &Config{ + Groups: []Group{ + { + Name: "dev", + Hosts: []Host{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "a", Enabled: true}, + {Domain: "b.com", IP: "127.0.0.1", Alias: "b", Enabled: false}, + }, + }, + { + Name: "staging", + Hosts: []Host{ + {Domain: "c.com", IP: "192.168.1.1", Alias: "c", Enabled: true}, + }, + }, + }, + } + + hosts := cfg.GetAllHosts() + assert.Len(t, hosts, 3) + assert.Equal(t, "a.com", hosts[0].Domain) + assert.Equal(t, "b.com", hosts[1].Domain) + assert.Equal(t, "c.com", hosts[2].Domain) +} + +func TestConfig_FindHostByAlias(t *testing.T) { + cfg := &Config{ + Groups: []Group{ + { + Name: "dev", + Hosts: []Host{ + {Domain: "example.com", IP: "127.0.0.1", Alias: "example", Enabled: true}, + }, + }, + }, + } + + t.Run("found", func(t *testing.T) { + host, group := cfg.FindHostByAlias("example") + require.NotNil(t, host) + require.NotNil(t, group) + assert.Equal(t, "example.com", host.Domain) + assert.Equal(t, "dev", group.Name) + }) + + t.Run("not found", func(t *testing.T) { + host, group := cfg.FindHostByAlias("nonexistent") + assert.Nil(t, host) + assert.Nil(t, group) + }) +} + +func TestConfig_FindPreset(t *testing.T) { + cfg := &Config{ + Presets: []Preset{ + {Name: "local", Enable: []string{"a"}, Disable: []string{"b"}}, + {Name: "staging", Enable: []string{"b"}, Disable: []string{"a"}}, + }, + } + + t.Run("found", func(t *testing.T) { + preset := cfg.FindPreset("local") + require.NotNil(t, preset) + assert.Equal(t, "local", preset.Name) + assert.Equal(t, []string{"a"}, preset.Enable) + }) + + t.Run("not found", func(t *testing.T) { + preset := cfg.FindPreset("nonexistent") + assert.Nil(t, preset) + }) +} + +func TestConfig_SetHostEnabled(t *testing.T) { + cfg := &Config{ + Groups: []Group{ + { + Name: "dev", + Hosts: []Host{ + {Domain: "example.com", IP: "127.0.0.1", Alias: "example", Enabled: false}, + }, + }, + }, + } + + t.Run("enable existing", func(t *testing.T) { + result := cfg.SetHostEnabled("example", true) + assert.True(t, result) + assert.True(t, cfg.Groups[0].Hosts[0].Enabled) + }) + + t.Run("disable existing", func(t *testing.T) { + result := cfg.SetHostEnabled("example", false) + assert.True(t, result) + assert.False(t, cfg.Groups[0].Hosts[0].Enabled) + }) + + t.Run("nonexistent alias", func(t *testing.T) { + result := cfg.SetHostEnabled("nonexistent", true) + assert.False(t, result) + }) +} + +func TestConfig_ApplyPreset(t *testing.T) { + cfg := &Config{ + Groups: []Group{ + { + Name: "dev", + Hosts: []Host{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "a", Enabled: false}, + {Domain: "b.com", IP: "127.0.0.1", Alias: "b", Enabled: true}, + }, + }, + }, + Presets: []Preset{ + {Name: "swap", Enable: []string{"a"}, Disable: []string{"b"}}, + }, + } + + t.Run("valid preset", func(t *testing.T) { + err := cfg.ApplyPreset("swap") + require.NoError(t, err) + assert.True(t, cfg.Groups[0].Hosts[0].Enabled) + assert.False(t, cfg.Groups[0].Hosts[1].Enabled) + }) + + t.Run("nonexistent preset", func(t *testing.T) { + err := cfg.ApplyPreset("nonexistent") + assert.Error(t, err) + }) +} + +func TestManager_LoadAndGet(t *testing.T) { + // Create temp config file + tmpDir := t.TempDir() + configPath := filepath.Join(tmpDir, "config.yaml") + + configContent := ` +settings: + autoApply: true + flushMethod: auto +groups: + - name: development + hosts: + - domain: example.com + ip: 127.0.0.1 + alias: example-local + enabled: true +presets: + - name: local + enable: [example-local] + disable: [] +` + err := os.WriteFile(configPath, []byte(configContent), 0644) + require.NoError(t, err) + + manager := NewManager(configPath) + err = manager.Load() + require.NoError(t, err) + + cfg := manager.Get() + require.NotNil(t, cfg) + + assert.True(t, cfg.Settings.AutoApply) + assert.Equal(t, FlushMethodAuto, cfg.Settings.FlushMethod) + assert.Len(t, cfg.Groups, 1) + assert.Equal(t, "development", cfg.Groups[0].Name) + assert.Len(t, cfg.Groups[0].Hosts, 1) + assert.Equal(t, "example.com", cfg.Groups[0].Hosts[0].Domain) +} + +func TestManager_Save(t *testing.T) { + tmpDir := t.TempDir() + configPath := filepath.Join(tmpDir, "config.yaml") + + // Create initial config + err := CreateDefault(configPath) + require.NoError(t, err) + + // Load and modify + manager := NewManager(configPath) + err = manager.Load() + require.NoError(t, err) + + cfg := manager.Get() + cfg.Groups[0].Hosts[0].Enabled = true + + // Save + err = manager.Save() + require.NoError(t, err) + + // Reload and verify + manager2 := NewManager(configPath) + err = manager2.Load() + require.NoError(t, err) + + cfg2 := manager2.Get() + assert.True(t, cfg2.Groups[0].Hosts[0].Enabled) +} + +func TestCreateDefault(t *testing.T) { + tmpDir := t.TempDir() + configPath := filepath.Join(tmpDir, "subdir", "config.yaml") + + err := CreateDefault(configPath) + require.NoError(t, err) + + // Verify file exists + _, err = os.Stat(configPath) + require.NoError(t, err) + + // Verify content is valid + manager := NewManager(configPath) + err = manager.Load() + require.NoError(t, err) + + cfg := manager.Get() + require.NotNil(t, cfg) + assert.True(t, cfg.Settings.AutoApply) + assert.Len(t, cfg.Groups, 1) + assert.Len(t, cfg.Presets, 2) +} + +func TestManager_Load_InvalidYAML(t *testing.T) { + tmpDir := t.TempDir() + configPath := filepath.Join(tmpDir, "config.yaml") + + err := os.WriteFile(configPath, []byte("invalid: yaml: content:"), 0644) + require.NoError(t, err) + + manager := NewManager(configPath) + err = manager.Load() + assert.Error(t, err) +} + +func TestManager_Load_FileNotFound(t *testing.T) { + manager := NewManager("/nonexistent/path/config.yaml") + err := manager.Load() + assert.Error(t, err) +} + +func TestFlushMethod(t *testing.T) { + methods := []FlushMethod{ + FlushMethodAuto, + FlushMethodDscacheutil, + FlushMethodKillall, + FlushMethodBoth, + } + + for _, m := range methods { + t.Run(string(m), func(t *testing.T) { + assert.NotEmpty(t, string(m)) + }) + } +} diff --git a/internal/config/validation.go b/internal/config/validation.go new file mode 100644 index 0000000..4a1059e --- /dev/null +++ b/internal/config/validation.go @@ -0,0 +1,211 @@ +// Package config provides validation functions for configuration. +package config + +import ( + "fmt" + "net" + "regexp" + "strings" +) + +// domainRegex validates domain names. +var domainRegex = regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$|^localhost$`) + +// aliasRegex validates alias names. +var aliasRegex = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}$`) + +// blockedDomains contains domains that cannot be modified. +var blockedDomains = map[string]bool{ + "apple.com": true, + "icloud.com": true, + "icloud-content.com": true, + "apple-dns.cn": true, + "apple-dns.net": true, + "mzstatic.com": true, + "itunes.apple.com": true, + "updates.apple.com": true, +} + +// ValidationError represents a configuration validation error. +type ValidationError struct { + Field string + Message string +} + +func (e *ValidationError) Error() string { + return fmt.Sprintf("%s: %s", e.Field, e.Message) +} + +// ValidateConfig validates the entire configuration. +func ValidateConfig(cfg *Config) error { + if cfg == nil { + return &ValidationError{Field: "config", Message: "config is nil"} + } + + if err := validateSettings(&cfg.Settings); err != nil { + return err + } + + // Track aliases for uniqueness + aliases := make(map[string]bool) + + for i, g := range cfg.Groups { + if err := validateGroup(&g, i, aliases); err != nil { + return err + } + } + + for i, p := range cfg.Presets { + if err := validatePreset(&p, i, aliases); err != nil { + return err + } + } + + return nil +} + +func validateSettings(s *Settings) error { + switch s.FlushMethod { + case FlushMethodAuto, FlushMethodDscacheutil, FlushMethodKillall, FlushMethodBoth, "": + // Valid + default: + return &ValidationError{ + Field: "settings.flushMethod", + Message: fmt.Sprintf("invalid flush method: %s", s.FlushMethod), + } + } + return nil +} + +func validateGroup(g *Group, index int, aliases map[string]bool) error { + if strings.TrimSpace(g.Name) == "" { + return &ValidationError{ + Field: fmt.Sprintf("groups[%d].name", index), + Message: "group name is required", + } + } + + for i, h := range g.Hosts { + if err := validateHost(&h, index, i, aliases); err != nil { + return err + } + } + + return nil +} + +func validateHost(h *Host, groupIndex, hostIndex int, aliases map[string]bool) error { + fieldPrefix := fmt.Sprintf("groups[%d].hosts[%d]", groupIndex, hostIndex) + + // Validate domain + if !ValidateDomain(h.Domain) { + return &ValidationError{ + Field: fieldPrefix + ".domain", + Message: fmt.Sprintf("invalid domain: %s", h.Domain), + } + } + + // Check blocked domains + if IsBlockedDomain(h.Domain) { + return &ValidationError{ + Field: fieldPrefix + ".domain", + Message: fmt.Sprintf("domain is blocked: %s", h.Domain), + } + } + + // Validate IP + if !ValidateIP(h.IP) { + return &ValidationError{ + Field: fieldPrefix + ".ip", + Message: fmt.Sprintf("invalid IP address: %s", h.IP), + } + } + + // Validate alias + if !ValidateAlias(h.Alias) { + return &ValidationError{ + Field: fieldPrefix + ".alias", + Message: fmt.Sprintf("invalid alias: %s", h.Alias), + } + } + + // Check alias uniqueness + if aliases[h.Alias] { + return &ValidationError{ + Field: fieldPrefix + ".alias", + Message: fmt.Sprintf("duplicate alias: %s", h.Alias), + } + } + aliases[h.Alias] = true + + return nil +} + +func validatePreset(p *Preset, index int, aliases map[string]bool) error { + fieldPrefix := fmt.Sprintf("presets[%d]", index) + + if strings.TrimSpace(p.Name) == "" { + return &ValidationError{ + Field: fieldPrefix + ".name", + Message: "preset name is required", + } + } + + // Note: We don't validate preset aliases strictly anymore. + // Unknown aliases in presets will simply be skipped when applying the preset. + // This allows presets to survive when hosts are removed from the config. + + return nil +} + +// ValidateDomain checks if a domain name is valid. +func ValidateDomain(domain string) bool { + if domain == "" { + return false + } + return domainRegex.MatchString(domain) +} + +// ValidateIP checks if an IP address is valid (IPv4 or IPv6). +func ValidateIP(ip string) bool { + if ip == "" { + return false + } + return net.ParseIP(ip) != nil +} + +// ValidateAlias checks if an alias is valid. +func ValidateAlias(alias string) bool { + if alias == "" { + return false + } + return aliasRegex.MatchString(alias) +} + +// IsBlockedDomain checks if a domain is in the blocklist. +func IsBlockedDomain(domain string) bool { + domain = strings.ToLower(domain) + + // Check exact match + if blockedDomains[domain] { + return true + } + + // Check if it's a subdomain of a blocked domain + for blocked := range blockedDomains { + if strings.HasSuffix(domain, "."+blocked) { + return true + } + } + + return false +} + +// GetBlockedDomains returns a copy of the blocked domains list. +func GetBlockedDomains() []string { + domains := make([]string, 0, len(blockedDomains)) + for d := range blockedDomains { + domains = append(domains, d) + } + return domains +} diff --git a/internal/config/validation_test.go b/internal/config/validation_test.go new file mode 100644 index 0000000..36830e6 --- /dev/null +++ b/internal/config/validation_test.go @@ -0,0 +1,436 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestValidateDomain(t *testing.T) { + tests := []struct { + domain string + valid bool + }{ + {"example.com", true}, + {"sub.example.com", true}, + {"my-app.example.com", true}, + {"localhost", true}, + {"a.b.c.d.example.com", true}, + {"example123.com", true}, + + {"", false}, + {"-example.com", false}, + {"example-.com", false}, + {"example.c", false}, // TLD too short + {"example", false}, // No TLD + {".example.com", false}, + {"example..com", false}, + } + + for _, tt := range tests { + t.Run(tt.domain, func(t *testing.T) { + result := ValidateDomain(tt.domain) + assert.Equal(t, tt.valid, result, "domain: %s", tt.domain) + }) + } +} + +func TestValidateIP(t *testing.T) { + tests := []struct { + ip string + valid bool + }{ + // Valid IPv4 + {"127.0.0.1", true}, + {"192.168.1.1", true}, + {"0.0.0.0", true}, + {"255.255.255.255", true}, + + // Valid IPv6 + {"::1", true}, + {"2001:db8::1", true}, + {"fe80::1", true}, + {"::ffff:192.168.1.1", true}, + + // Invalid + {"", false}, + {"256.0.0.1", false}, + {"192.168.1", false}, + {"not-an-ip", false}, + {"192.168.1.1.1", false}, + } + + for _, tt := range tests { + t.Run(tt.ip, func(t *testing.T) { + result := ValidateIP(tt.ip) + assert.Equal(t, tt.valid, result, "ip: %s", tt.ip) + }) + } +} + +func TestValidateAlias(t *testing.T) { + tests := []struct { + alias string + valid bool + }{ + {"my-alias", true}, + {"myalias", true}, + {"my_alias", true}, + {"alias123", true}, + {"a", true}, + {"a-b_c-d", true}, + + {"", false}, + {"-startswithdash", false}, + {"_startswithunderscore", false}, + {"has spaces", false}, + {"has.dot", false}, + } + + for _, tt := range tests { + t.Run(tt.alias, func(t *testing.T) { + result := ValidateAlias(tt.alias) + assert.Equal(t, tt.valid, result, "alias: %s", tt.alias) + }) + } +} + +func TestIsBlockedDomain(t *testing.T) { + tests := []struct { + domain string + blocked bool + }{ + // Blocked domains + {"apple.com", true}, + {"icloud.com", true}, + {"sub.apple.com", true}, + {"deep.sub.icloud.com", true}, + {"APPLE.COM", true}, // Case insensitive + + // Allowed domains + {"example.com", false}, + {"myapp.com", false}, + {"applestore.com", false}, // Not a subdomain + {"notapple.com", false}, + } + + for _, tt := range tests { + t.Run(tt.domain, func(t *testing.T) { + result := IsBlockedDomain(tt.domain) + assert.Equal(t, tt.blocked, result, "domain: %s", tt.domain) + }) + } +} + +func TestGetBlockedDomains(t *testing.T) { + domains := GetBlockedDomains() + assert.NotEmpty(t, domains) + assert.Contains(t, domains, "apple.com") + assert.Contains(t, domains, "icloud.com") +} + +func TestValidateConfig(t *testing.T) { + t.Run("valid config", func(t *testing.T) { + cfg := &Config{ + Settings: Settings{ + AutoApply: true, + FlushMethod: FlushMethodAuto, + }, + Groups: []Group{ + { + Name: "development", + Hosts: []Host{ + {Domain: "example.com", IP: "127.0.0.1", Alias: "example", Enabled: true}, + }, + }, + }, + Presets: []Preset{ + {Name: "local", Enable: []string{"example"}, Disable: []string{}}, + }, + } + + err := ValidateConfig(cfg) + assert.NoError(t, err) + }) + + t.Run("nil config", func(t *testing.T) { + err := ValidateConfig(nil) + assert.Error(t, err) + }) + + t.Run("invalid flush method", func(t *testing.T) { + cfg := &Config{ + Settings: Settings{FlushMethod: "invalid"}, + } + err := ValidateConfig(cfg) + assert.Error(t, err) + }) + + t.Run("empty group name", func(t *testing.T) { + cfg := &Config{ + Groups: []Group{{Name: "", Hosts: []Host{}}}, + } + err := ValidateConfig(cfg) + assert.Error(t, err) + }) + + t.Run("invalid domain", func(t *testing.T) { + cfg := &Config{ + Groups: []Group{ + { + Name: "dev", + Hosts: []Host{ + {Domain: "invalid", IP: "127.0.0.1", Alias: "test", Enabled: true}, + }, + }, + }, + } + err := ValidateConfig(cfg) + assert.Error(t, err) + }) + + t.Run("blocked domain", func(t *testing.T) { + cfg := &Config{ + Groups: []Group{ + { + Name: "dev", + Hosts: []Host{ + {Domain: "apple.com", IP: "127.0.0.1", Alias: "test", Enabled: true}, + }, + }, + }, + } + err := ValidateConfig(cfg) + assert.Error(t, err) + }) + + t.Run("invalid IP", func(t *testing.T) { + cfg := &Config{ + Groups: []Group{ + { + Name: "dev", + Hosts: []Host{ + {Domain: "example.com", IP: "invalid", Alias: "test", Enabled: true}, + }, + }, + }, + } + err := ValidateConfig(cfg) + assert.Error(t, err) + }) + + t.Run("invalid alias", func(t *testing.T) { + cfg := &Config{ + Groups: []Group{ + { + Name: "dev", + Hosts: []Host{ + {Domain: "example.com", IP: "127.0.0.1", Alias: "-invalid", Enabled: true}, + }, + }, + }, + } + err := ValidateConfig(cfg) + assert.Error(t, err) + }) + + t.Run("duplicate alias", func(t *testing.T) { + cfg := &Config{ + Groups: []Group{ + { + Name: "dev", + Hosts: []Host{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "same", Enabled: true}, + {Domain: "b.com", IP: "127.0.0.1", Alias: "same", Enabled: true}, + }, + }, + }, + } + err := ValidateConfig(cfg) + assert.Error(t, err) + }) + + t.Run("empty preset name", func(t *testing.T) { + cfg := &Config{ + Groups: []Group{ + { + Name: "dev", + Hosts: []Host{ + {Domain: "example.com", IP: "127.0.0.1", Alias: "test", Enabled: true}, + }, + }, + }, + Presets: []Preset{ + {Name: "", Enable: []string{}}, + }, + } + err := ValidateConfig(cfg) + assert.Error(t, err) + }) + + t.Run("preset with unknown alias is allowed", func(t *testing.T) { + // Unknown aliases in presets are now allowed (they're simply skipped when applied) + // This allows presets to survive when hosts are removed from the config + cfg := &Config{ + Groups: []Group{ + { + Name: "dev", + Hosts: []Host{ + {Domain: "example.com", IP: "127.0.0.1", Alias: "test", Enabled: true}, + }, + }, + }, + Presets: []Preset{ + {Name: "local", Enable: []string{"unknown"}}, + }, + } + err := ValidateConfig(cfg) + assert.NoError(t, err) + }) +} + +func TestValidationError(t *testing.T) { + err := &ValidationError{Field: "test.field", Message: "test message"} + assert.Equal(t, "test.field: test message", err.Error()) +} + +func TestValidateSettings(t *testing.T) { + tests := []struct { + name string + method FlushMethod + wantErr bool + }{ + {"auto", FlushMethodAuto, false}, + {"dscacheutil", FlushMethodDscacheutil, false}, + {"killall", FlushMethodKillall, false}, + {"both", FlushMethodBoth, false}, + {"empty", "", false}, + {"invalid", "invalid", true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + settings := &Settings{FlushMethod: tt.method} + err := validateSettings(settings) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +// Matrix testing for domain validation +func TestValidateDomain_Matrix(t *testing.T) { + prefixes := []string{"", "sub.", "a.b."} + domains := []string{"example", "my-app", "test123"} + tlds := []string{".com", ".io", ".co.uk", ".dev"} + + for _, prefix := range prefixes { + for _, domain := range domains { + for _, tld := range tlds { + fullDomain := prefix + domain + tld + t.Run(fullDomain, func(t *testing.T) { + result := ValidateDomain(fullDomain) + assert.True(t, result, "expected %s to be valid", fullDomain) + }) + } + } + } +} + +// Matrix testing for IP validation +func TestValidateIP_Matrix(t *testing.T) { + octets := []string{"0", "127", "192", "255"} + + for _, o1 := range octets { + for _, o2 := range octets { + for _, o3 := range octets { + for _, o4 := range octets { + ip := o1 + "." + o2 + "." + o3 + "." + o4 + t.Run(ip, func(t *testing.T) { + result := ValidateIP(ip) + assert.True(t, result, "expected %s to be valid", ip) + }) + } + } + } + } +} + +// Benchmark tests +func BenchmarkValidateDomain(b *testing.B) { + domains := []string{ + "example.com", + "sub.example.com", + "very.long.subdomain.chain.example.com", + } + + for _, domain := range domains { + b.Run(domain, func(b *testing.B) { + for i := 0; i < b.N; i++ { + ValidateDomain(domain) + } + }) + } +} + +func BenchmarkValidateIP(b *testing.B) { + ips := []string{ + "127.0.0.1", + "192.168.1.1", + "::1", + "2001:db8::1", + } + + for _, ip := range ips { + b.Run(ip, func(b *testing.B) { + for i := 0; i < b.N; i++ { + ValidateIP(ip) + } + }) + } +} + +func BenchmarkIsBlockedDomain(b *testing.B) { + domains := []string{ + "example.com", // not blocked + "apple.com", // blocked + "sub.icloud.com", // blocked subdomain + } + + for _, domain := range domains { + b.Run(domain, func(b *testing.B) { + for i := 0; i < b.N; i++ { + IsBlockedDomain(domain) + } + }) + } +} + +func BenchmarkValidateConfig(b *testing.B) { + cfg := &Config{ + Settings: Settings{AutoApply: true, FlushMethod: FlushMethodAuto}, + Groups: []Group{ + { + Name: "development", + Hosts: []Host{ + {Domain: "a.example.com", IP: "127.0.0.1", Alias: "a", Enabled: true}, + {Domain: "b.example.com", IP: "127.0.0.1", Alias: "b", Enabled: true}, + {Domain: "c.example.com", IP: "127.0.0.1", Alias: "c", Enabled: false}, + }, + }, + }, + Presets: []Preset{ + {Name: "local", Enable: []string{"a", "b"}, Disable: []string{"c"}}, + }, + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := ValidateConfig(cfg) + require.NoError(b, err) + } +} diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go new file mode 100644 index 0000000..7fa32a9 --- /dev/null +++ b/internal/daemon/daemon.go @@ -0,0 +1,133 @@ +// Package daemon provides the main daemon loop and lifecycle management. +package daemon + +import ( + "fmt" + "os" + "os/signal" + "syscall" + "time" + + "github.com/lukaszraczylo/lolcathost/internal/config" + "github.com/lukaszraczylo/lolcathost/internal/protocol" +) + +// Daemon represents the lolcathost daemon. +type Daemon struct { + server *Server + config *config.Manager + stopCh chan struct{} + cleanupCh chan struct{} +} + +// New creates a new daemon instance. +func New(configPath string) (*Daemon, error) { + cfgManager := config.NewManager(configPath) + + // Try to load config, create default if it doesn't exist + if err := cfgManager.Load(); err != nil { + if os.IsNotExist(err) { + if err := config.CreateDefault(configPath); err != nil { + return nil, fmt.Errorf("failed to create default config: %w", err) + } + if err := cfgManager.Load(); err != nil { + return nil, fmt.Errorf("failed to load default config: %w", err) + } + } else { + return nil, fmt.Errorf("failed to load config: %w", err) + } + } + + // Ensure at least one group exists + cfg := cfgManager.Get() + if cfg != nil { + cfg.EnsureDefaultGroup() + // Save if we added a default group + if len(cfg.Groups) == 1 && cfg.Groups[0].Name == "default" && len(cfg.Groups[0].Hosts) == 0 { + cfgManager.Save() + } + } + + server := NewServer(protocol.SocketPath, cfgManager) + + return &Daemon{ + server: server, + config: cfgManager, + stopCh: make(chan struct{}), + cleanupCh: make(chan struct{}), + }, nil +} + +// Run starts the daemon and blocks until stopped. +func (d *Daemon) Run() error { + // Verify we're running as root + if os.Geteuid() != 0 { + return fmt.Errorf("daemon must run as root") + } + + // Start the server + if err := d.server.Start(); err != nil { + return fmt.Errorf("failed to start server: %w", err) + } + + // Watch config for changes + if err := d.config.Watch(d.onConfigChange); err != nil { + fmt.Fprintf(os.Stderr, "warning: failed to watch config: %v\n", err) + } + + // Start cleanup goroutine + go d.cleanupLoop() + + // Wait for shutdown signal + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) + + select { + case <-sigCh: + fmt.Println("Received shutdown signal") + case <-d.stopCh: + fmt.Println("Shutdown requested") + } + + return d.shutdown() +} + +// Stop signals the daemon to stop. +func (d *Daemon) Stop() { + close(d.stopCh) +} + +func (d *Daemon) shutdown() error { + close(d.cleanupCh) + d.config.Stop() + + if err := d.server.Stop(); err != nil { + return fmt.Errorf("failed to stop server: %w", err) + } + + return nil +} + +func (d *Daemon) onConfigChange(cfg *config.Config) { + fmt.Println("Config changed, syncing hosts file...") + // The server will use the updated config on next request + // We could trigger a sync here if autoApply is enabled + if cfg != nil && cfg.Settings.AutoApply { + // Sync hosts file with new config + // This is handled by the server internally + } +} + +func (d *Daemon) cleanupLoop() { + ticker := time.NewTicker(5 * time.Minute) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + d.server.rateLimiter.Cleanup() + case <-d.cleanupCh: + return + } + } +} diff --git a/internal/daemon/dns.go b/internal/daemon/dns.go new file mode 100644 index 0000000..0397576 --- /dev/null +++ b/internal/daemon/dns.go @@ -0,0 +1,142 @@ +// Package daemon provides DNS cache flushing functionality. +package daemon + +import ( + "fmt" + "os/exec" + "runtime" +) + +// DNSFlusher handles DNS cache flushing. +type DNSFlusher struct { + method FlushMethod +} + +// FlushMethod defines the DNS flush method to use. +type FlushMethod string + +const ( + FlushMethodAuto FlushMethod = "auto" + FlushMethodDscacheutil FlushMethod = "dscacheutil" + FlushMethodKillall FlushMethod = "killall" + FlushMethodBoth FlushMethod = "both" + FlushMethodSystemd FlushMethod = "systemd" + FlushMethodNscd FlushMethod = "nscd" +) + +// NewDNSFlusher creates a new DNS flusher. +func NewDNSFlusher(method FlushMethod) *DNSFlusher { + return &DNSFlusher{method: method} +} + +// Flush flushes the DNS cache using the configured method. +func (f *DNSFlusher) Flush() error { + method := f.method + if method == FlushMethodAuto || method == "" { + method = f.detectMethod() + } + + switch runtime.GOOS { + case "darwin": + return f.flushDarwin(method) + case "linux": + return f.flushLinux(method) + default: + return fmt.Errorf("unsupported operating system: %s", runtime.GOOS) + } +} + +func (f *DNSFlusher) detectMethod() FlushMethod { + switch runtime.GOOS { + case "darwin": + return FlushMethodBoth + case "linux": + // Check for systemd-resolve first + if _, err := exec.LookPath("systemd-resolve"); err == nil { + return FlushMethodSystemd + } + if _, err := exec.LookPath("resolvectl"); err == nil { + return FlushMethodSystemd + } + // Fall back to nscd + if _, err := exec.LookPath("nscd"); err == nil { + return FlushMethodNscd + } + return FlushMethodAuto + default: + return FlushMethodAuto + } +} + +func (f *DNSFlusher) flushDarwin(method FlushMethod) error { + var errs []error + + switch method { + case FlushMethodDscacheutil: + if err := runCommand("dscacheutil", "-flushcache"); err != nil { + return fmt.Errorf("dscacheutil failed: %w", err) + } + case FlushMethodKillall: + if err := runCommand("killall", "-HUP", "mDNSResponder"); err != nil { + return fmt.Errorf("killall mDNSResponder failed: %w", err) + } + case FlushMethodBoth: + if err := runCommand("dscacheutil", "-flushcache"); err != nil { + errs = append(errs, fmt.Errorf("dscacheutil failed: %w", err)) + } + if err := runCommand("killall", "-HUP", "mDNSResponder"); err != nil { + errs = append(errs, fmt.Errorf("killall mDNSResponder failed: %w", err)) + } + if len(errs) == 2 { + return fmt.Errorf("all DNS flush methods failed: %v, %v", errs[0], errs[1]) + } + default: + // Auto - try both + _ = runCommand("dscacheutil", "-flushcache") + _ = runCommand("killall", "-HUP", "mDNSResponder") + } + + return nil +} + +func (f *DNSFlusher) flushLinux(method FlushMethod) error { + switch method { + case FlushMethodSystemd: + // Try resolvectl first (newer), then systemd-resolve (older) + if err := runCommand("resolvectl", "flush-caches"); err != nil { + if err := runCommand("systemd-resolve", "--flush-caches"); err != nil { + return fmt.Errorf("systemd DNS flush failed: %w", err) + } + } + case FlushMethodNscd: + // Try to restart nscd + if err := runCommand("nscd", "-i", "hosts"); err != nil { + // Try service restart as fallback + if err := runCommand("service", "nscd", "restart"); err != nil { + return fmt.Errorf("nscd flush failed: %w", err) + } + } + default: + // Auto - try all methods + // Try systemd first + if err := runCommand("resolvectl", "flush-caches"); err == nil { + return nil + } + if err := runCommand("systemd-resolve", "--flush-caches"); err == nil { + return nil + } + // Try nscd + if err := runCommand("nscd", "-i", "hosts"); err == nil { + return nil + } + // On many Linux systems, no explicit flush is needed as /etc/hosts is read directly + // So we return nil here + } + + return nil +} + +func runCommand(name string, args ...string) error { + cmd := exec.Command(name, args...) + return cmd.Run() +} diff --git a/internal/daemon/dns_test.go b/internal/daemon/dns_test.go new file mode 100644 index 0000000..2d647f4 --- /dev/null +++ b/internal/daemon/dns_test.go @@ -0,0 +1,108 @@ +package daemon + +import ( + "runtime" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewDNSFlusher(t *testing.T) { + tests := []FlushMethod{ + FlushMethodAuto, + FlushMethodDscacheutil, + FlushMethodKillall, + FlushMethodBoth, + FlushMethodSystemd, + FlushMethodNscd, + } + + for _, method := range tests { + t.Run(string(method), func(t *testing.T) { + flusher := NewDNSFlusher(method) + assert.NotNil(t, flusher) + assert.Equal(t, method, flusher.method) + }) + } +} + +func TestDNSFlusher_DetectMethod(t *testing.T) { + flusher := NewDNSFlusher(FlushMethodAuto) + + method := flusher.detectMethod() + + switch runtime.GOOS { + case "darwin": + assert.Equal(t, FlushMethodBoth, method) + case "linux": + // Could be systemd, nscd, or auto depending on system + assert.Contains(t, []FlushMethod{FlushMethodSystemd, FlushMethodNscd, FlushMethodAuto}, method) + } +} + +func TestFlushMethod_String(t *testing.T) { + methods := map[FlushMethod]string{ + FlushMethodAuto: "auto", + FlushMethodDscacheutil: "dscacheutil", + FlushMethodKillall: "killall", + FlushMethodBoth: "both", + FlushMethodSystemd: "systemd", + FlushMethodNscd: "nscd", + } + + for method, expected := range methods { + t.Run(expected, func(t *testing.T) { + assert.Equal(t, expected, string(method)) + }) + } +} + +// Note: Actually testing DNS flush requires root and modifies system state, +// so we skip those tests in unit tests. They would be integration tests. + +func TestDNSFlusher_Flush_UnsupportedOS(t *testing.T) { + // This test only makes sense if we're not on darwin or linux + if runtime.GOOS == "darwin" || runtime.GOOS == "linux" { + t.Skip("Test only applicable on unsupported OS") + } + + flusher := NewDNSFlusher(FlushMethodAuto) + err := flusher.Flush() + assert.Error(t, err) + assert.Contains(t, err.Error(), "unsupported operating system") +} + +// Matrix test for flush methods +func TestFlushMethod_Matrix(t *testing.T) { + methods := []FlushMethod{ + FlushMethodAuto, + FlushMethodDscacheutil, + FlushMethodKillall, + FlushMethodBoth, + FlushMethodSystemd, + FlushMethodNscd, + } + + platforms := []string{"darwin", "linux"} + + for _, method := range methods { + for _, platform := range platforms { + t.Run(string(method)+"_"+platform, func(t *testing.T) { + flusher := NewDNSFlusher(method) + assert.NotNil(t, flusher) + + // Just verify no panic when checking method + _ = flusher.method + }) + } + } +} + +func BenchmarkDNSFlusher_DetectMethod(b *testing.B) { + flusher := NewDNSFlusher(FlushMethodAuto) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = flusher.detectMethod() + } +} diff --git a/internal/daemon/hosts.go b/internal/daemon/hosts.go new file mode 100644 index 0000000..c43fe28 --- /dev/null +++ b/internal/daemon/hosts.go @@ -0,0 +1,319 @@ +// Package daemon implements the privileged daemon that manages /etc/hosts. +package daemon + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + "regexp" + "sort" + "strings" + "time" +) + +const ( + // HostsPath is the path to the system hosts file. + HostsPath = "/etc/hosts" + // BackupDir is the directory for hosts file backups. + BackupDir = "/var/backups/lolcathost" + // MaxBackups is the maximum number of backups to keep. + MaxBackups = 10 + + // Markers for the managed section. + markerStart = "# ========== LOLCATHOST MANAGED - DO NOT EDIT ==========" + markerEnd = "# ========== END LOLCATHOST ==========" +) + +// HostEntry represents a single entry in the hosts file. +type HostEntry struct { + IP string + Domain string + Alias string + Enabled bool +} + +// HostsManager handles reading and writing the hosts file. +type HostsManager struct { + hostsPath string + backupDir string +} + +// NewHostsManager creates a new hosts manager. +func NewHostsManager() *HostsManager { + return &HostsManager{ + hostsPath: HostsPath, + backupDir: BackupDir, + } +} + +// NewHostsManagerWithPaths creates a hosts manager with custom paths (for testing). +func NewHostsManagerWithPaths(hostsPath, backupDir string) *HostsManager { + return &HostsManager{ + hostsPath: hostsPath, + backupDir: backupDir, + } +} + +// ReadManagedEntries reads the lolcathost-managed entries from the hosts file. +func (m *HostsManager) ReadManagedEntries() ([]HostEntry, error) { + file, err := os.Open(m.hostsPath) + if err != nil { + return nil, fmt.Errorf("failed to open hosts file: %w", err) + } + defer file.Close() + + var entries []HostEntry + inManagedSection := false + scanner := bufio.NewScanner(file) + entryRegex := regexp.MustCompile(`^(\S+)\s+(\S+)\s+#\s*lolcathost:(\S+)$`) + + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + + if line == markerStart { + inManagedSection = true + continue + } + if line == markerEnd { + inManagedSection = false + continue + } + + if inManagedSection && !strings.HasPrefix(line, "#") && line != "" { + matches := entryRegex.FindStringSubmatch(line) + if len(matches) == 4 { + entries = append(entries, HostEntry{ + IP: matches[1], + Domain: matches[2], + Alias: matches[3], + Enabled: true, + }) + } + } + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("failed to read hosts file: %w", err) + } + + return entries, nil +} + +// WriteManagedEntries writes the managed entries to the hosts file. +func (m *HostsManager) WriteManagedEntries(entries []HostEntry) error { + // Create backup first + if err := m.CreateBackup(); err != nil { + return fmt.Errorf("failed to create backup: %w", err) + } + + // Read existing content + content, err := os.ReadFile(m.hostsPath) + if err != nil { + return fmt.Errorf("failed to read hosts file: %w", err) + } + + // Remove existing managed section + newContent := m.removeManagedSection(string(content)) + + // Build new managed section + managedSection := m.buildManagedSection(entries) + + // Append managed section + newContent = strings.TrimRight(newContent, "\n") + "\n\n" + managedSection + + // Write atomically + if err := m.writeAtomic(newContent); err != nil { + return fmt.Errorf("failed to write hosts file: %w", err) + } + + return nil +} + +func (m *HostsManager) removeManagedSection(content string) string { + lines := strings.Split(content, "\n") + var result []string + inManagedSection := false + + for _, line := range lines { + trimmed := strings.TrimSpace(line) + if trimmed == markerStart { + inManagedSection = true + continue + } + if trimmed == markerEnd { + inManagedSection = false + continue + } + if !inManagedSection { + result = append(result, line) + } + } + + // Remove trailing empty lines + for len(result) > 0 && strings.TrimSpace(result[len(result)-1]) == "" { + result = result[:len(result)-1] + } + + return strings.Join(result, "\n") +} + +func (m *HostsManager) buildManagedSection(entries []HostEntry) string { + var sb strings.Builder + sb.WriteString(markerStart) + sb.WriteString("\n") + + for _, entry := range entries { + if entry.Enabled { + sb.WriteString(fmt.Sprintf("%s\t%s\t# lolcathost:%s\n", entry.IP, entry.Domain, entry.Alias)) + } + } + + sb.WriteString(markerEnd) + sb.WriteString("\n") + + return sb.String() +} + +func (m *HostsManager) writeAtomic(content string) error { + // Write to temp file first + tmpFile := m.hostsPath + ".tmp" + if err := os.WriteFile(tmpFile, []byte(content), 0644); err != nil { + return err + } + + // Rename atomically + if err := os.Rename(tmpFile, m.hostsPath); err != nil { + os.Remove(tmpFile) + return err + } + + return nil +} + +// CreateBackup creates a backup of the current hosts file. +func (m *HostsManager) CreateBackup() error { + if err := os.MkdirAll(m.backupDir, 0755); err != nil { + return fmt.Errorf("failed to create backup directory: %w", err) + } + + content, err := os.ReadFile(m.hostsPath) + if err != nil { + return fmt.Errorf("failed to read hosts file: %w", err) + } + + timestamp := time.Now().Format("20060102-150405") + backupPath := filepath.Join(m.backupDir, fmt.Sprintf("hosts.%s.bak", timestamp)) + + if err := os.WriteFile(backupPath, content, 0644); err != nil { + return fmt.Errorf("failed to write backup: %w", err) + } + + // Cleanup old backups + if err := m.cleanupBackups(); err != nil { + // Log but don't fail + fmt.Fprintf(os.Stderr, "warning: failed to cleanup backups: %v\n", err) + } + + return nil +} + +func (m *HostsManager) cleanupBackups() error { + entries, err := os.ReadDir(m.backupDir) + if err != nil { + return err + } + + var backups []os.DirEntry + for _, entry := range entries { + if !entry.IsDir() && strings.HasPrefix(entry.Name(), "hosts.") && strings.HasSuffix(entry.Name(), ".bak") { + backups = append(backups, entry) + } + } + + if len(backups) <= MaxBackups { + return nil + } + + // Sort by name (timestamp) descending + sort.Slice(backups, func(i, j int) bool { + return backups[i].Name() > backups[j].Name() + }) + + // Remove oldest backups + for i := MaxBackups; i < len(backups); i++ { + path := filepath.Join(m.backupDir, backups[i].Name()) + os.Remove(path) + } + + return nil +} + +// ListBackups returns a list of available backups. +func (m *HostsManager) ListBackups() ([]BackupInfo, error) { + entries, err := os.ReadDir(m.backupDir) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, err + } + + var backups []BackupInfo + for _, entry := range entries { + if entry.IsDir() || !strings.HasPrefix(entry.Name(), "hosts.") || !strings.HasSuffix(entry.Name(), ".bak") { + continue + } + + info, err := entry.Info() + if err != nil { + continue + } + + backups = append(backups, BackupInfo{ + Name: entry.Name(), + Timestamp: info.ModTime().Unix(), + Size: info.Size(), + }) + } + + // Sort by timestamp descending + sort.Slice(backups, func(i, j int) bool { + return backups[i].Timestamp > backups[j].Timestamp + }) + + return backups, nil +} + +// BackupInfo holds information about a backup file. +type BackupInfo struct { + Name string + Timestamp int64 + Size int64 +} + +// RestoreBackup restores a backup by name. +func (m *HostsManager) RestoreBackup(name string) error { + backupPath := filepath.Join(m.backupDir, name) + + // Validate backup name to prevent path traversal + if filepath.Base(name) != name || !strings.HasPrefix(name, "hosts.") || !strings.HasSuffix(name, ".bak") { + return fmt.Errorf("invalid backup name") + } + + content, err := os.ReadFile(backupPath) + if err != nil { + return fmt.Errorf("failed to read backup: %w", err) + } + + // Create a backup of current state before restoring + if err := m.CreateBackup(); err != nil { + return fmt.Errorf("failed to create backup before restore: %w", err) + } + + if err := m.writeAtomic(string(content)); err != nil { + return fmt.Errorf("failed to restore backup: %w", err) + } + + return nil +} diff --git a/internal/daemon/hosts_test.go b/internal/daemon/hosts_test.go new file mode 100644 index 0000000..97b57ea --- /dev/null +++ b/internal/daemon/hosts_test.go @@ -0,0 +1,422 @@ +package daemon + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestHostsManager_ReadManagedEntries(t *testing.T) { + tmpDir := t.TempDir() + hostsPath := filepath.Join(tmpDir, "hosts") + + hostsContent := `127.0.0.1 localhost +255.255.255.255 broadcasthost +::1 localhost + +# ========== LOLCATHOST MANAGED - DO NOT EDIT ========== +127.0.0.1 example.com # lolcathost:example-local +192.168.1.1 api.example.com # lolcathost:api-local +# ========== END LOLCATHOST ========== +` + err := os.WriteFile(hostsPath, []byte(hostsContent), 0644) + require.NoError(t, err) + + manager := NewHostsManagerWithPaths(hostsPath, filepath.Join(tmpDir, "backups")) + entries, err := manager.ReadManagedEntries() + require.NoError(t, err) + + assert.Len(t, entries, 2) + assert.Equal(t, "127.0.0.1", entries[0].IP) + assert.Equal(t, "example.com", entries[0].Domain) + assert.Equal(t, "example-local", entries[0].Alias) + assert.Equal(t, "192.168.1.1", entries[1].IP) + assert.Equal(t, "api.example.com", entries[1].Domain) + assert.Equal(t, "api-local", entries[1].Alias) +} + +func TestHostsManager_ReadManagedEntries_NoSection(t *testing.T) { + tmpDir := t.TempDir() + hostsPath := filepath.Join(tmpDir, "hosts") + + hostsContent := `127.0.0.1 localhost +255.255.255.255 broadcasthost +` + err := os.WriteFile(hostsPath, []byte(hostsContent), 0644) + require.NoError(t, err) + + manager := NewHostsManagerWithPaths(hostsPath, filepath.Join(tmpDir, "backups")) + entries, err := manager.ReadManagedEntries() + require.NoError(t, err) + + assert.Empty(t, entries) +} + +func TestHostsManager_WriteManagedEntries(t *testing.T) { + tmpDir := t.TempDir() + hostsPath := filepath.Join(tmpDir, "hosts") + backupDir := filepath.Join(tmpDir, "backups") + + // Create initial hosts file + initialContent := `127.0.0.1 localhost +255.255.255.255 broadcasthost +` + err := os.WriteFile(hostsPath, []byte(initialContent), 0644) + require.NoError(t, err) + + manager := NewHostsManagerWithPaths(hostsPath, backupDir) + + entries := []HostEntry{ + {IP: "127.0.0.1", Domain: "myapp.com", Alias: "myapp-local", Enabled: true}, + {IP: "127.0.0.1", Domain: "api.myapp.com", Alias: "api-local", Enabled: true}, + {IP: "192.168.1.1", Domain: "staging.myapp.com", Alias: "staging", Enabled: false}, + } + + err = manager.WriteManagedEntries(entries) + require.NoError(t, err) + + // Read back + content, err := os.ReadFile(hostsPath) + require.NoError(t, err) + + contentStr := string(content) + assert.Contains(t, contentStr, "127.0.0.1\tlocalhost") + assert.Contains(t, contentStr, "# ========== LOLCATHOST MANAGED - DO NOT EDIT ==========") + assert.Contains(t, contentStr, "127.0.0.1\tmyapp.com\t# lolcathost:myapp-local") + assert.Contains(t, contentStr, "127.0.0.1\tapi.myapp.com\t# lolcathost:api-local") + assert.NotContains(t, contentStr, "staging.myapp.com") // disabled + assert.Contains(t, contentStr, "# ========== END LOLCATHOST ==========") +} + +func TestHostsManager_WriteManagedEntries_UpdatesExisting(t *testing.T) { + tmpDir := t.TempDir() + hostsPath := filepath.Join(tmpDir, "hosts") + backupDir := filepath.Join(tmpDir, "backups") + + // Create hosts file with existing managed section + initialContent := `127.0.0.1 localhost + +# ========== LOLCATHOST MANAGED - DO NOT EDIT ========== +127.0.0.1 old.com # lolcathost:old +# ========== END LOLCATHOST ========== +` + err := os.WriteFile(hostsPath, []byte(initialContent), 0644) + require.NoError(t, err) + + manager := NewHostsManagerWithPaths(hostsPath, backupDir) + + entries := []HostEntry{ + {IP: "127.0.0.1", Domain: "new.com", Alias: "new", Enabled: true}, + } + + err = manager.WriteManagedEntries(entries) + require.NoError(t, err) + + content, err := os.ReadFile(hostsPath) + require.NoError(t, err) + + contentStr := string(content) + assert.Contains(t, contentStr, "127.0.0.1\tlocalhost") + assert.Contains(t, contentStr, "new.com") + assert.NotContains(t, contentStr, "old.com") +} + +func TestHostsManager_CreateBackup(t *testing.T) { + tmpDir := t.TempDir() + hostsPath := filepath.Join(tmpDir, "hosts") + backupDir := filepath.Join(tmpDir, "backups") + + hostsContent := "127.0.0.1\tlocalhost\n" + err := os.WriteFile(hostsPath, []byte(hostsContent), 0644) + require.NoError(t, err) + + manager := NewHostsManagerWithPaths(hostsPath, backupDir) + + err = manager.CreateBackup() + require.NoError(t, err) + + // Verify backup exists + entries, err := os.ReadDir(backupDir) + require.NoError(t, err) + assert.Len(t, entries, 1) + assert.True(t, strings.HasPrefix(entries[0].Name(), "hosts.")) + assert.True(t, strings.HasSuffix(entries[0].Name(), ".bak")) + + // Verify backup content + backupContent, err := os.ReadFile(filepath.Join(backupDir, entries[0].Name())) + require.NoError(t, err) + assert.Equal(t, hostsContent, string(backupContent)) +} + +func TestHostsManager_ListBackups(t *testing.T) { + tmpDir := t.TempDir() + hostsPath := filepath.Join(tmpDir, "hosts") + backupDir := filepath.Join(tmpDir, "backups") + + // Create hosts file + err := os.WriteFile(hostsPath, []byte("localhost"), 0644) + require.NoError(t, err) + + // Manually create backup files with different timestamps + err = os.MkdirAll(backupDir, 0755) + require.NoError(t, err) + + backupNames := []string{ + "hosts.20231201-120000.bak", + "hosts.20231201-120001.bak", + "hosts.20231201-120002.bak", + } + for _, name := range backupNames { + err = os.WriteFile(filepath.Join(backupDir, name), []byte("backup"), 0644) + require.NoError(t, err) + } + + manager := NewHostsManagerWithPaths(hostsPath, backupDir) + + backups, err := manager.ListBackups() + require.NoError(t, err) + assert.Len(t, backups, 3) +} + +func TestHostsManager_ListBackups_NoBackupDir(t *testing.T) { + tmpDir := t.TempDir() + hostsPath := filepath.Join(tmpDir, "hosts") + backupDir := filepath.Join(tmpDir, "nonexistent") + + manager := NewHostsManagerWithPaths(hostsPath, backupDir) + + backups, err := manager.ListBackups() + require.NoError(t, err) + assert.Empty(t, backups) +} + +func TestHostsManager_RestoreBackup(t *testing.T) { + tmpDir := t.TempDir() + hostsPath := filepath.Join(tmpDir, "hosts") + backupDir := filepath.Join(tmpDir, "backups") + + // Create initial hosts file + initialContent := "initial content" + err := os.WriteFile(hostsPath, []byte(initialContent), 0644) + require.NoError(t, err) + + manager := NewHostsManagerWithPaths(hostsPath, backupDir) + + // Create backup + err = manager.CreateBackup() + require.NoError(t, err) + + // Modify hosts file + err = os.WriteFile(hostsPath, []byte("modified content"), 0644) + require.NoError(t, err) + + // Get backup name + backups, err := manager.ListBackups() + require.NoError(t, err) + require.Len(t, backups, 1) + + // Restore + err = manager.RestoreBackup(backups[0].Name) + require.NoError(t, err) + + // Verify content restored + content, err := os.ReadFile(hostsPath) + require.NoError(t, err) + assert.Equal(t, initialContent, string(content)) +} + +func TestHostsManager_RestoreBackup_InvalidName(t *testing.T) { + tmpDir := t.TempDir() + manager := NewHostsManagerWithPaths( + filepath.Join(tmpDir, "hosts"), + filepath.Join(tmpDir, "backups"), + ) + + tests := []string{ + "../../../etc/passwd", + "hosts.bak", // Missing timestamp + "notahosts.backup", // Wrong format + "", + } + + for _, name := range tests { + t.Run(name, func(t *testing.T) { + err := manager.RestoreBackup(name) + assert.Error(t, err) + }) + } +} + +func TestHostsManager_CleanupBackups(t *testing.T) { + tmpDir := t.TempDir() + hostsPath := filepath.Join(tmpDir, "hosts") + backupDir := filepath.Join(tmpDir, "backups") + + err := os.WriteFile(hostsPath, []byte("localhost"), 0644) + require.NoError(t, err) + + manager := NewHostsManagerWithPaths(hostsPath, backupDir) + + // Create more than MaxBackups + for i := 0; i < MaxBackups+5; i++ { + err = manager.CreateBackup() + require.NoError(t, err) + } + + // Verify only MaxBackups remain + backups, err := manager.ListBackups() + require.NoError(t, err) + assert.LessOrEqual(t, len(backups), MaxBackups) +} + +func TestHostsManager_RemoveManagedSection(t *testing.T) { + manager := &HostsManager{} + + tests := []struct { + name string + input string + expected string + }{ + { + name: "with managed section", + input: `127.0.0.1 localhost + +# ========== LOLCATHOST MANAGED - DO NOT EDIT ========== +127.0.0.1 example.com # lolcathost:test +# ========== END LOLCATHOST ========== +`, + expected: "127.0.0.1\tlocalhost", + }, + { + name: "without managed section", + input: "127.0.0.1\tlocalhost\n", + expected: "127.0.0.1\tlocalhost", + }, + { + name: "multiple managed sections", + input: `127.0.0.1 localhost +# ========== LOLCATHOST MANAGED - DO NOT EDIT ========== +entry1 +# ========== END LOLCATHOST ========== +more content +# ========== LOLCATHOST MANAGED - DO NOT EDIT ========== +entry2 +# ========== END LOLCATHOST ========== +`, + expected: "127.0.0.1\tlocalhost\nmore content", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := manager.removeManagedSection(tt.input) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestHostsManager_BuildManagedSection(t *testing.T) { + manager := &HostsManager{} + + entries := []HostEntry{ + {IP: "127.0.0.1", Domain: "a.com", Alias: "a", Enabled: true}, + {IP: "192.168.1.1", Domain: "b.com", Alias: "b", Enabled: true}, + {IP: "10.0.0.1", Domain: "c.com", Alias: "c", Enabled: false}, + } + + result := manager.buildManagedSection(entries) + + assert.Contains(t, result, "# ========== LOLCATHOST MANAGED - DO NOT EDIT ==========") + assert.Contains(t, result, "127.0.0.1\ta.com\t# lolcathost:a") + assert.Contains(t, result, "192.168.1.1\tb.com\t# lolcathost:b") + assert.NotContains(t, result, "c.com") // disabled + assert.Contains(t, result, "# ========== END LOLCATHOST ==========") +} + +// Matrix tests for hosts file parsing +func TestHostsManager_ReadManagedEntries_Matrix(t *testing.T) { + ips := []string{"127.0.0.1", "192.168.1.1", "::1"} + domains := []string{"example.com", "sub.example.com", "my-app.test"} + aliases := []string{"test", "my-alias", "app-1"} + + for _, ip := range ips { + for _, domain := range domains { + for _, alias := range aliases { + t.Run(ip+"/"+domain+"/"+alias, func(t *testing.T) { + tmpDir := t.TempDir() + hostsPath := filepath.Join(tmpDir, "hosts") + + content := "# ========== LOLCATHOST MANAGED - DO NOT EDIT ==========\n" + content += ip + "\t" + domain + "\t# lolcathost:" + alias + "\n" + content += "# ========== END LOLCATHOST ==========\n" + + err := os.WriteFile(hostsPath, []byte(content), 0644) + require.NoError(t, err) + + manager := NewHostsManagerWithPaths(hostsPath, filepath.Join(tmpDir, "backups")) + entries, err := manager.ReadManagedEntries() + require.NoError(t, err) + require.Len(t, entries, 1) + + assert.Equal(t, ip, entries[0].IP) + assert.Equal(t, domain, entries[0].Domain) + assert.Equal(t, alias, entries[0].Alias) + }) + } + } + } +} + +func BenchmarkHostsManager_ReadManagedEntries(b *testing.B) { + tmpDir := b.TempDir() + hostsPath := filepath.Join(tmpDir, "hosts") + + // Create a hosts file with many entries + var content strings.Builder + content.WriteString("127.0.0.1\tlocalhost\n") + content.WriteString("# ========== LOLCATHOST MANAGED - DO NOT EDIT ==========\n") + for i := 0; i < 100; i++ { + content.WriteString("127.0.0.1\texample" + string(rune('a'+i%26)) + ".com\t# lolcathost:alias" + string(rune('a'+i%26)) + "\n") + } + content.WriteString("# ========== END LOLCATHOST ==========\n") + + err := os.WriteFile(hostsPath, []byte(content.String()), 0644) + require.NoError(b, err) + + manager := NewHostsManagerWithPaths(hostsPath, filepath.Join(tmpDir, "backups")) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = manager.ReadManagedEntries() + } +} + +func BenchmarkHostsManager_WriteManagedEntries(b *testing.B) { + tmpDir := b.TempDir() + hostsPath := filepath.Join(tmpDir, "hosts") + backupDir := filepath.Join(tmpDir, "backups") + + err := os.WriteFile(hostsPath, []byte("127.0.0.1\tlocalhost\n"), 0644) + require.NoError(b, err) + + manager := NewHostsManagerWithPaths(hostsPath, backupDir) + + entries := make([]HostEntry, 50) + for i := range entries { + entries[i] = HostEntry{ + IP: "127.0.0.1", + Domain: "example" + string(rune('a'+i%26)) + ".com", + Alias: "alias" + string(rune('a'+i%26)), + Enabled: i%2 == 0, + } + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = manager.WriteManagedEntries(entries) + } +} diff --git a/internal/daemon/peercred_darwin.go b/internal/daemon/peercred_darwin.go new file mode 100644 index 0000000..c9100c7 --- /dev/null +++ b/internal/daemon/peercred_darwin.go @@ -0,0 +1,57 @@ +//go:build darwin + +package daemon + +import ( + "net" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +// getPeerCredentials extracts peer credentials from a Unix socket connection on macOS. +// Note: macOS Xucred doesn't include PID, so we use LOCAL_PEERPID separately. +func (s *Server) getPeerCredentials(conn net.Conn) *PeerCredentials { + unixConn, ok := conn.(*net.UnixConn) + if !ok { + return nil + } + + rawConn, err := unixConn.SyscallConn() + if err != nil { + return nil + } + + var creds *PeerCredentials + rawConn.Control(func(fd uintptr) { + xucred, err := unix.GetsockoptXucred(int(fd), unix.SOL_LOCAL, unix.LOCAL_PEERCRED) + if err != nil { + return + } + + // Get PID separately using LOCAL_PEERPID + var pid int32 + pidLen := uint32(unsafe.Sizeof(pid)) + _, _, errno := syscall.Syscall6( + syscall.SYS_GETSOCKOPT, + fd, + unix.SOL_LOCAL, + 0x002, // LOCAL_PEERPID + uintptr(unsafe.Pointer(&pid)), + uintptr(unsafe.Pointer(&pidLen)), + 0, + ) + if errno != 0 { + pid = 0 + } + + creds = &PeerCredentials{ + UID: xucred.Uid, + GID: xucred.Groups[0], + PID: pid, + } + }) + + return creds +} diff --git a/internal/daemon/peercred_linux.go b/internal/daemon/peercred_linux.go new file mode 100644 index 0000000..ad9f21f --- /dev/null +++ b/internal/daemon/peercred_linux.go @@ -0,0 +1,37 @@ +//go:build linux + +package daemon + +import ( + "net" + + "golang.org/x/sys/unix" +) + +// getPeerCredentials extracts peer credentials from a Unix socket connection on Linux. +func (s *Server) getPeerCredentials(conn net.Conn) *PeerCredentials { + unixConn, ok := conn.(*net.UnixConn) + if !ok { + return nil + } + + rawConn, err := unixConn.SyscallConn() + if err != nil { + return nil + } + + var creds *PeerCredentials + rawConn.Control(func(fd uintptr) { + ucred, err := unix.GetsockoptUcred(int(fd), unix.SOL_SOCKET, unix.SO_PEERCRED) + if err != nil { + return + } + creds = &PeerCredentials{ + UID: ucred.Uid, + GID: ucred.Gid, + PID: ucred.Pid, + } + }) + + return creds +} diff --git a/internal/daemon/security.go b/internal/daemon/security.go new file mode 100644 index 0000000..7659dad --- /dev/null +++ b/internal/daemon/security.go @@ -0,0 +1,196 @@ +// Package daemon provides security functions including rate limiting and audit logging. +package daemon + +import ( + "encoding/json" + "fmt" + "os" + "os/user" + "sync" + "time" +) + +const ( + // AuditLogPath is the path to the audit log file. + AuditLogPath = "/var/log/lolcathost/audit.log" + // RateLimit is the maximum requests per minute per PID. + RateLimit = 100 + // RateLimitWindow is the time window for rate limiting. + RateLimitWindow = time.Minute +) + +// RateLimiter implements per-PID rate limiting. +type RateLimiter struct { + mu sync.Mutex + requests map[int32][]time.Time + limit int + window time.Duration +} + +// NewRateLimiter creates a new rate limiter. +func NewRateLimiter(limit int, window time.Duration) *RateLimiter { + return &RateLimiter{ + requests: make(map[int32][]time.Time), + limit: limit, + window: window, + } +} + +// Allow checks if a request from the given PID should be allowed. +func (r *RateLimiter) Allow(pid int32) bool { + r.mu.Lock() + defer r.mu.Unlock() + + now := time.Now() + cutoff := now.Add(-r.window) + + // Get existing requests for this PID + reqs := r.requests[pid] + + // Filter out old requests + var validReqs []time.Time + for _, t := range reqs { + if t.After(cutoff) { + validReqs = append(validReqs, t) + } + } + + // Check if under limit + if len(validReqs) >= r.limit { + r.requests[pid] = validReqs + return false + } + + // Add new request + validReqs = append(validReqs, now) + r.requests[pid] = validReqs + + return true +} + +// Cleanup removes old entries from the rate limiter. +func (r *RateLimiter) Cleanup() { + r.mu.Lock() + defer r.mu.Unlock() + + now := time.Now() + cutoff := now.Add(-r.window) + + for pid, reqs := range r.requests { + var validReqs []time.Time + for _, t := range reqs { + if t.After(cutoff) { + validReqs = append(validReqs, t) + } + } + if len(validReqs) == 0 { + delete(r.requests, pid) + } else { + r.requests[pid] = validReqs + } + } +} + +// AuditLogger handles audit logging. +type AuditLogger struct { + mu sync.Mutex + file *os.File + path string + encoder *json.Encoder +} + +// AuditEntry represents a single audit log entry. +type AuditEntry struct { + Timestamp string `json:"timestamp"` + UID uint32 `json:"uid"` + PID int32 `json:"pid"` + Action string `json:"action"` + Details any `json:"details,omitempty"` + Success bool `json:"success"` + Error string `json:"error,omitempty"` +} + +// NewAuditLogger creates a new audit logger. +func NewAuditLogger(path string) (*AuditLogger, error) { + // Ensure directory exists + dir := path[:len(path)-len("/audit.log")] + if err := os.MkdirAll(dir, 0755); err != nil { + return nil, fmt.Errorf("failed to create log directory: %w", err) + } + + file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + return nil, fmt.Errorf("failed to open audit log: %w", err) + } + + return &AuditLogger{ + file: file, + path: path, + encoder: json.NewEncoder(file), + }, nil +} + +// Log writes an audit entry. +func (a *AuditLogger) Log(uid uint32, pid int32, action string, details any, success bool, errMsg string) { + a.mu.Lock() + defer a.mu.Unlock() + + entry := AuditEntry{ + Timestamp: time.Now().UTC().Format(time.RFC3339), + UID: uid, + PID: pid, + Action: action, + Details: details, + Success: success, + Error: errMsg, + } + + // Ignore encoding errors - audit logging should not fail the operation + _ = a.encoder.Encode(entry) +} + +// Close closes the audit logger. +func (a *AuditLogger) Close() error { + a.mu.Lock() + defer a.mu.Unlock() + + if a.file != nil { + err := a.file.Close() + a.file = nil // Prevent double close + return err + } + return nil +} + +// PeerCredentials holds the credentials of a connected peer. +type PeerCredentials struct { + UID uint32 + GID uint32 + PID int32 +} + +// isUserInGroup checks if a user (by UID) is a member of a group (by GID). +// This checks supplementary groups, not just the primary GID. +func isUserInGroup(uid uint32, targetGID uint32) bool { + // Look up user by UID + u, err := user.LookupId(fmt.Sprintf("%d", uid)) + if err != nil { + return false + } + + // Get user's group IDs + groupIDs, err := u.GroupIds() + if err != nil { + return false + } + + // Check if target GID is in the list + targetGIDStr := fmt.Sprintf("%d", targetGID) + for _, gid := range groupIDs { + if gid == targetGIDStr { + return true + } + } + + return false +} diff --git a/internal/daemon/security_test.go b/internal/daemon/security_test.go new file mode 100644 index 0000000..4bf1ca4 --- /dev/null +++ b/internal/daemon/security_test.go @@ -0,0 +1,206 @@ +package daemon + +import ( + "os" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRateLimiter_Allow(t *testing.T) { + t.Run("under limit", func(t *testing.T) { + rl := NewRateLimiter(5, time.Minute) + + for i := 0; i < 5; i++ { + assert.True(t, rl.Allow(123), "request %d should be allowed", i) + } + }) + + t.Run("over limit", func(t *testing.T) { + rl := NewRateLimiter(3, time.Minute) + + for i := 0; i < 3; i++ { + assert.True(t, rl.Allow(123)) + } + + // 4th request should be blocked + assert.False(t, rl.Allow(123)) + }) + + t.Run("different PIDs", func(t *testing.T) { + rl := NewRateLimiter(2, time.Minute) + + // PID 1 + assert.True(t, rl.Allow(1)) + assert.True(t, rl.Allow(1)) + assert.False(t, rl.Allow(1)) + + // PID 2 should have its own limit + assert.True(t, rl.Allow(2)) + assert.True(t, rl.Allow(2)) + assert.False(t, rl.Allow(2)) + }) + + t.Run("window expiration", func(t *testing.T) { + rl := NewRateLimiter(2, 10*time.Millisecond) + + assert.True(t, rl.Allow(123)) + assert.True(t, rl.Allow(123)) + assert.False(t, rl.Allow(123)) + + // Wait for window to expire + time.Sleep(15 * time.Millisecond) + + // Should be allowed again + assert.True(t, rl.Allow(123)) + }) +} + +func TestRateLimiter_Cleanup(t *testing.T) { + rl := NewRateLimiter(10, 10*time.Millisecond) + + // Add requests from multiple PIDs + for pid := int32(1); pid <= 5; pid++ { + rl.Allow(pid) + } + + assert.Len(t, rl.requests, 5) + + // Wait for expiration + time.Sleep(15 * time.Millisecond) + + // Cleanup + rl.Cleanup() + + assert.Empty(t, rl.requests) +} + +func TestAuditLogger_Log(t *testing.T) { + tmpDir := t.TempDir() + logPath := filepath.Join(tmpDir, "audit.log") + + logger, err := NewAuditLogger(logPath) + require.NoError(t, err) + defer logger.Close() + + logger.Log(1000, 12345, "set", map[string]string{"alias": "test"}, true, "") + logger.Log(1000, 12345, "sync", nil, false, "sync failed") + + // Read log file + content, err := os.ReadFile(logPath) + require.NoError(t, err) + + contentStr := string(content) + assert.Contains(t, contentStr, `"action":"set"`) + assert.Contains(t, contentStr, `"uid":1000`) + assert.Contains(t, contentStr, `"pid":12345`) + assert.Contains(t, contentStr, `"success":true`) + assert.Contains(t, contentStr, `"action":"sync"`) + assert.Contains(t, contentStr, `"success":false`) + assert.Contains(t, contentStr, `"error":"sync failed"`) +} + +func TestAuditLogger_CreatesDirectory(t *testing.T) { + tmpDir := t.TempDir() + logPath := filepath.Join(tmpDir, "subdir", "audit.log") + + logger, err := NewAuditLogger(logPath) + require.NoError(t, err) + defer logger.Close() + + // Verify directory was created + _, err = os.Stat(filepath.Dir(logPath)) + assert.NoError(t, err) +} + +func TestAuditLogger_Close(t *testing.T) { + tmpDir := t.TempDir() + logPath := filepath.Join(tmpDir, "audit.log") + + logger, err := NewAuditLogger(logPath) + require.NoError(t, err) + + err = logger.Close() + assert.NoError(t, err) + + // Closing again should not error + err = logger.Close() + assert.NoError(t, err) +} + +func TestPeerCredentials(t *testing.T) { + creds := &PeerCredentials{ + UID: 501, + GID: 20, + PID: 12345, + } + + assert.Equal(t, uint32(501), creds.UID) + assert.Equal(t, uint32(20), creds.GID) + assert.Equal(t, int32(12345), creds.PID) +} + +// Matrix test for rate limiting +func TestRateLimiter_Matrix(t *testing.T) { + limits := []int{1, 5, 10, 100} + windows := []time.Duration{10 * time.Millisecond, 100 * time.Millisecond, time.Second} + + for _, limit := range limits { + for _, window := range windows { + t.Run( + "limit="+string(rune('0'+limit))+"_window="+window.String(), + func(t *testing.T) { + rl := NewRateLimiter(limit, window) + + // Should allow exactly 'limit' requests + for i := 0; i < limit; i++ { + assert.True(t, rl.Allow(1)) + } + + // Next should be blocked + assert.False(t, rl.Allow(1)) + }, + ) + } + } +} + +func BenchmarkRateLimiter_Allow(b *testing.B) { + rl := NewRateLimiter(RateLimit, RateLimitWindow) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + rl.Allow(int32(i % 100)) + } +} + +func BenchmarkRateLimiter_Cleanup(b *testing.B) { + rl := NewRateLimiter(RateLimit, RateLimitWindow) + + // Pre-populate with requests + for i := 0; i < 1000; i++ { + rl.Allow(int32(i)) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + rl.Cleanup() + } +} + +func BenchmarkAuditLogger_Log(b *testing.B) { + tmpDir := b.TempDir() + logPath := filepath.Join(tmpDir, "audit.log") + + logger, err := NewAuditLogger(logPath) + require.NoError(b, err) + defer logger.Close() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + logger.Log(1000, 12345, "set", map[string]string{"alias": "test"}, true, "") + } +} diff --git a/internal/daemon/server.go b/internal/daemon/server.go new file mode 100644 index 0000000..f1aa6cd --- /dev/null +++ b/internal/daemon/server.go @@ -0,0 +1,803 @@ +// Package daemon provides the Unix socket server for the daemon. +package daemon + +import ( + "bufio" + "encoding/json" + "fmt" + "net" + "os" + "sync" + "time" + + "github.com/lukaszraczylo/lolcathost/internal/config" + "github.com/lukaszraczylo/lolcathost/internal/protocol" +) + +// Version is set by the main package at startup +var Version = "dev" + +// Server is the daemon's Unix socket server. +type Server struct { + socketPath string + listener net.Listener + config *config.Manager + hosts *HostsManager + flusher *DNSFlusher + rateLimiter *RateLimiter + auditLogger *AuditLogger + mu sync.RWMutex + running bool + stopCh chan struct{} + requestCount int64 + startTime int64 +} + +// NewServer creates a new daemon server. +func NewServer(socketPath string, cfgManager *config.Manager) *Server { + return &Server{ + socketPath: socketPath, + config: cfgManager, + hosts: NewHostsManager(), + flusher: NewDNSFlusher(FlushMethodAuto), + rateLimiter: NewRateLimiter(RateLimit, RateLimitWindow), + stopCh: make(chan struct{}), + } +} + +// Start starts the server. +func (s *Server) Start() error { + // Remove existing socket + os.Remove(s.socketPath) + + listener, err := net.Listen("unix", s.socketPath) + if err != nil { + return fmt.Errorf("failed to listen on socket: %w", err) + } + + // Set socket permissions: 0660 root:lolcathost + if err := os.Chmod(s.socketPath, 0660); err != nil { + listener.Close() + return fmt.Errorf("failed to set socket permissions: %w", err) + } + + // Set socket group to lolcathost (GID 850) + if err := os.Chown(s.socketPath, 0, 850); err != nil { + listener.Close() + return fmt.Errorf("failed to set socket ownership: %w", err) + } + + s.listener = listener + s.running = true + s.startTime = currentTimeUnix() + + // Try to create audit logger, but don't fail if it doesn't work + if logger, err := NewAuditLogger(AuditLogPath); err == nil { + s.auditLogger = logger + } + + go s.acceptLoop() + + return nil +} + +func currentTimeUnix() int64 { + return time.Now().Unix() +} + +// Stop stops the server. +func (s *Server) Stop() error { + s.mu.Lock() + s.running = false + s.mu.Unlock() + + close(s.stopCh) + + if s.listener != nil { + s.listener.Close() + } + + os.Remove(s.socketPath) + + if s.auditLogger != nil { + s.auditLogger.Close() + } + + return nil +} + +func (s *Server) acceptLoop() { + for { + conn, err := s.listener.Accept() + if err != nil { + select { + case <-s.stopCh: + return + default: + continue + } + } + + go s.handleConnection(conn) + } +} + +// LolcathostGID is the group ID for the lolcathost group. +const LolcathostGID = 850 + +func (s *Server) handleConnection(conn net.Conn) { + defer conn.Close() + + // Get peer credentials + creds := s.getPeerCredentials(conn) + + // Authorization check: verify peer is authorized + if !s.isAuthorized(creds) { + s.writeResponse(conn, protocol.NewErrorResponse(protocol.ErrCodeUnauthorized, "unauthorized: user not in lolcathost group")) + if s.auditLogger != nil { + var uid uint32 + var pid int32 + if creds != nil { + uid = creds.UID + pid = creds.PID + } + s.auditLogger.Log(uid, pid, "connect", nil, false, "unauthorized access attempt") + } + return + } + + reader := bufio.NewReader(conn) + for { + line, err := reader.ReadBytes('\n') + if err != nil { + return + } + + var req protocol.Request + if err := json.Unmarshal(line, &req); err != nil { + s.writeResponse(conn, protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "invalid JSON")) + continue + } + + // Rate limiting + if creds != nil && !s.rateLimiter.Allow(creds.PID) { + s.writeResponse(conn, protocol.NewErrorResponse(protocol.ErrCodeRateLimited, "rate limit exceeded")) + continue + } + + s.mu.Lock() + s.requestCount++ + s.mu.Unlock() + + resp := s.handleRequest(&req, creds) + s.writeResponse(conn, resp) + } +} + +// isAuthorized checks if the peer is authorized to access the daemon. +// Authorized users are: root (UID 0) or members of the lolcathost group (GID 850). +func (s *Server) isAuthorized(creds *PeerCredentials) bool { + if creds == nil { + // Can't verify credentials - deny by default + return false + } + + // Root is always authorized + if creds.UID == 0 { + return true + } + + // Check if user's primary GID is lolcathost + if creds.GID == LolcathostGID { + return true + } + + // Check supplementary groups (user might be in lolcathost as secondary group) + // This requires looking up the user's groups from the system + return isUserInGroup(creds.UID, LolcathostGID) +} + +func (s *Server) writeResponse(conn net.Conn, resp *protocol.Response) { + data, _ := json.Marshal(resp) + data = append(data, '\n') + conn.Write(data) +} + +func (s *Server) handleRequest(req *protocol.Request, creds *PeerCredentials) *protocol.Response { + var uid uint32 + var pid int32 + if creds != nil { + uid = creds.UID + pid = creds.PID + } + + switch req.Type { + case protocol.RequestPing: + return s.handlePing() + + case protocol.RequestStatus: + return s.handleStatus() + + case protocol.RequestList: + return s.handleList() + + case protocol.RequestSet: + resp := s.handleSet(req) + if s.auditLogger != nil { + var payload protocol.SetPayload + _ = req.ParsePayload(&payload) + s.auditLogger.Log(uid, pid, "set", payload, resp.IsOK(), resp.Message) + } + return resp + + case protocol.RequestSync: + resp := s.handleSync() + if s.auditLogger != nil { + s.auditLogger.Log(uid, pid, "sync", nil, resp.IsOK(), resp.Message) + } + return resp + + case protocol.RequestPreset: + resp := s.handlePreset(req) + if s.auditLogger != nil { + var payload protocol.PresetPayload + _ = req.ParsePayload(&payload) + s.auditLogger.Log(uid, pid, "preset", payload, resp.IsOK(), resp.Message) + } + return resp + + case protocol.RequestRollback: + resp := s.handleRollback(req) + if s.auditLogger != nil { + var payload protocol.RollbackPayload + _ = req.ParsePayload(&payload) + s.auditLogger.Log(uid, pid, "rollback", payload, resp.IsOK(), resp.Message) + } + return resp + + case protocol.RequestBackups: + return s.handleBackups() + + case protocol.RequestAdd: + resp := s.handleAdd(req) + if s.auditLogger != nil { + var payload protocol.AddPayload + _ = req.ParsePayload(&payload) + s.auditLogger.Log(uid, pid, "add", payload, resp.IsOK(), resp.Message) + } + return resp + + case protocol.RequestDelete: + resp := s.handleDelete(req) + if s.auditLogger != nil { + var payload protocol.DeletePayload + _ = req.ParsePayload(&payload) + s.auditLogger.Log(uid, pid, "delete", payload, resp.IsOK(), resp.Message) + } + return resp + + case protocol.RequestAddGroup: + resp := s.handleAddGroup(req) + if s.auditLogger != nil { + var payload protocol.GroupPayload + _ = req.ParsePayload(&payload) + s.auditLogger.Log(uid, pid, "add_group", payload, resp.IsOK(), resp.Message) + } + return resp + + case protocol.RequestDeleteGroup: + resp := s.handleDeleteGroup(req) + if s.auditLogger != nil { + var payload protocol.GroupPayload + _ = req.ParsePayload(&payload) + s.auditLogger.Log(uid, pid, "delete_group", payload, resp.IsOK(), resp.Message) + } + return resp + + case protocol.RequestListGroups: + return s.handleListGroups() + + case protocol.RequestRenameGroup: + resp := s.handleRenameGroup(req) + if s.auditLogger != nil { + var payload protocol.RenameGroupPayload + _ = req.ParsePayload(&payload) + s.auditLogger.Log(uid, pid, "rename_group", payload, resp.IsOK(), resp.Message) + } + return resp + + case protocol.RequestAddPreset: + resp := s.handleAddPreset(req) + if s.auditLogger != nil { + var payload protocol.AddPresetPayload + _ = req.ParsePayload(&payload) + s.auditLogger.Log(uid, pid, "add_preset", payload, resp.IsOK(), resp.Message) + } + return resp + + case protocol.RequestDeletePreset: + resp := s.handleDeletePreset(req) + if s.auditLogger != nil { + var payload protocol.PresetPayload + _ = req.ParsePayload(&payload) + s.auditLogger.Log(uid, pid, "delete_preset", payload, resp.IsOK(), resp.Message) + } + return resp + + case protocol.RequestListPresets: + return s.handleListPresets() + + default: + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, fmt.Sprintf("unknown request type: %s", req.Type)) + } +} + +func (s *Server) handlePing() *protocol.Response { + resp, _ := protocol.NewOKResponse(map[string]string{"pong": "ok"}) + return resp +} + +func (s *Server) handleStatus() *protocol.Response { + s.mu.RLock() + reqCount := s.requestCount + startTime := s.startTime + s.mu.RUnlock() + + cfg := s.config.Get() + var activeCount int + if cfg != nil { + for _, h := range cfg.GetAllHosts() { + if h.Enabled { + activeCount++ + } + } + } + + data := protocol.StatusData{ + Running: true, + Version: Version, + Uptime: nowUnix() - startTime, + ActiveCount: activeCount, + RequestCount: reqCount, + } + + resp, _ := protocol.NewOKResponse(data) + return resp +} + +func nowUnix() int64 { + return time.Now().Unix() +} + +func (s *Server) handleList() *protocol.Response { + cfg := s.config.Get() + if cfg == nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, "no configuration loaded") + } + + var entries []protocol.HostEntry + for _, g := range cfg.Groups { + for _, h := range g.Hosts { + entries = append(entries, protocol.HostEntry{ + Domain: h.Domain, + IP: h.IP, + Alias: h.Alias, + Enabled: h.Enabled, + Group: g.Name, + }) + } + } + + resp, _ := protocol.NewOKResponse(protocol.ListData{Entries: entries}) + return resp +} + +func (s *Server) handleSet(req *protocol.Request) *protocol.Response { + var payload protocol.SetPayload + if err := req.ParsePayload(&payload); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "invalid payload") + } + + cfg := s.config.Get() + if cfg == nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, "no configuration loaded") + } + + host, _ := cfg.FindHostByAlias(payload.Alias) + if host == nil { + return protocol.NewErrorResponse(protocol.ErrCodeNotFound, fmt.Sprintf("alias not found: %s", payload.Alias)) + } + + // Check for conflicts if enabling + if payload.Enabled && !payload.Force { + for _, g := range cfg.Groups { + for _, h := range g.Hosts { + if h.Alias != payload.Alias && h.Domain == host.Domain && h.Enabled { + return protocol.NewErrorResponse(protocol.ErrCodeConflict, + fmt.Sprintf("domain %s already mapped by alias %s (use force to override)", host.Domain, h.Alias)) + } + } + } + } + + // Update config + cfg.SetHostEnabled(payload.Alias, payload.Enabled) + + // Save config + if err := s.config.Save(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to save config: %v", err)) + } + + // Sync to hosts file + if err := s.syncHostsFile(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to sync hosts: %v", err)) + } + + resp, _ := protocol.NewOKResponse(protocol.SetData{ + Domain: host.Domain, + Applied: true, + }) + return resp +} + +func (s *Server) handleSync() *protocol.Response { + if err := s.syncHostsFile(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to sync: %v", err)) + } + + resp, _ := protocol.NewOKResponse(map[string]bool{"synced": true}) + return resp +} + +func (s *Server) handlePreset(req *protocol.Request) *protocol.Response { + var payload protocol.PresetPayload + if err := req.ParsePayload(&payload); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "invalid payload") + } + + cfg := s.config.Get() + if cfg == nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, "no configuration loaded") + } + + if err := cfg.ApplyPreset(payload.Name); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeNotFound, err.Error()) + } + + // Save config + if err := s.config.Save(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to save config: %v", err)) + } + + // Sync to hosts file + if err := s.syncHostsFile(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to sync hosts: %v", err)) + } + + resp, _ := protocol.NewOKResponse(map[string]string{"preset": payload.Name, "applied": "true"}) + return resp +} + +func (s *Server) handleRollback(req *protocol.Request) *protocol.Response { + var payload protocol.RollbackPayload + if err := req.ParsePayload(&payload); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "invalid payload") + } + + if err := s.hosts.RestoreBackup(payload.BackupName); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to restore backup: %v", err)) + } + + // Flush DNS after restore + s.flusher.Flush() + + resp, _ := protocol.NewOKResponse(map[string]string{"restored": payload.BackupName}) + return resp +} + +func (s *Server) handleBackups() *protocol.Response { + backups, err := s.hosts.ListBackups() + if err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to list backups: %v", err)) + } + + var infos []protocol.BackupInfo + for _, b := range backups { + infos = append(infos, protocol.BackupInfo{ + Name: b.Name, + Timestamp: b.Timestamp, + Size: b.Size, + }) + } + + resp, _ := protocol.NewOKResponse(protocol.BackupsData{Backups: infos}) + return resp +} + +func (s *Server) handleAdd(req *protocol.Request) *protocol.Response { + var payload protocol.AddPayload + if err := req.ParsePayload(&payload); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "invalid payload") + } + + // Validate domain + if payload.Domain == "" { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidDomain, "domain is required") + } + + // Validate IP + if payload.IP == "" { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidIP, "IP address is required") + } + + // Validate group + if payload.Group == "" { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "group is required") + } + + // Check blocked domains + if config.IsBlockedDomain(payload.Domain) { + return protocol.NewErrorResponse(protocol.ErrCodeBlockedDomain, fmt.Sprintf("domain %s is blocked", payload.Domain)) + } + + cfg := s.config.Get() + if cfg == nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, "no configuration loaded") + } + + // Add to config (alias will be auto-generated if empty) + if err := cfg.AddHost(payload.Domain, payload.IP, payload.Alias, payload.Group, payload.Enabled); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeConflict, err.Error()) + } + + // Save config + if err := s.config.Save(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to save config: %v", err)) + } + + // Sync to hosts file + if err := s.syncHostsFile(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to sync hosts: %v", err)) + } + + resp, _ := protocol.NewOKResponse(protocol.SetData{ + Domain: payload.Domain, + Applied: true, + }) + return resp +} + +func (s *Server) handleDelete(req *protocol.Request) *protocol.Response { + var payload protocol.DeletePayload + if err := req.ParsePayload(&payload); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "invalid payload") + } + + if payload.Alias == "" { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "alias is required") + } + + cfg := s.config.Get() + if cfg == nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, "no configuration loaded") + } + + // Delete from config + if !cfg.DeleteHost(payload.Alias) { + return protocol.NewErrorResponse(protocol.ErrCodeNotFound, fmt.Sprintf("alias not found: %s", payload.Alias)) + } + + // Save config + if err := s.config.Save(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to save config: %v", err)) + } + + // Sync to hosts file + if err := s.syncHostsFile(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to sync hosts: %v", err)) + } + + resp, _ := protocol.NewOKResponse(map[string]string{"deleted": payload.Alias}) + return resp +} + +func (s *Server) handleAddGroup(req *protocol.Request) *protocol.Response { + var payload protocol.GroupPayload + if err := req.ParsePayload(&payload); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "invalid payload") + } + + if payload.Name == "" { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "group name is required") + } + + cfg := s.config.Get() + if cfg == nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, "no configuration loaded") + } + + if err := cfg.AddGroup(payload.Name); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeConflict, err.Error()) + } + + // Save config + if err := s.config.Save(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to save config: %v", err)) + } + + resp, _ := protocol.NewOKResponse(map[string]string{"added": payload.Name}) + return resp +} + +func (s *Server) handleDeleteGroup(req *protocol.Request) *protocol.Response { + var payload protocol.GroupPayload + if err := req.ParsePayload(&payload); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "invalid payload") + } + + if payload.Name == "" { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "group name is required") + } + + cfg := s.config.Get() + if cfg == nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, "no configuration loaded") + } + + if err := cfg.DeleteGroup(payload.Name); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeNotFound, err.Error()) + } + + // Save config + if err := s.config.Save(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to save config: %v", err)) + } + + // Sync to hosts file + if err := s.syncHostsFile(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to sync hosts: %v", err)) + } + + resp, _ := protocol.NewOKResponse(map[string]string{"deleted": payload.Name}) + return resp +} + +func (s *Server) handleListGroups() *protocol.Response { + cfg := s.config.Get() + if cfg == nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, "no configuration loaded") + } + + resp, _ := protocol.NewOKResponse(protocol.GroupsData{Groups: cfg.GetGroups()}) + return resp +} + +func (s *Server) handleRenameGroup(req *protocol.Request) *protocol.Response { + var payload protocol.RenameGroupPayload + if err := req.ParsePayload(&payload); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "invalid payload") + } + + if payload.OldName == "" || payload.NewName == "" { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "old_name and new_name are required") + } + + cfg := s.config.Get() + if cfg == nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, "no configuration loaded") + } + + if err := cfg.RenameGroup(payload.OldName, payload.NewName); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeNotFound, err.Error()) + } + + // Save config + if err := s.config.Save(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to save config: %v", err)) + } + + resp, _ := protocol.NewOKResponse(map[string]string{"renamed": payload.NewName}) + return resp +} + +func (s *Server) handleAddPreset(req *protocol.Request) *protocol.Response { + var payload protocol.AddPresetPayload + if err := req.ParsePayload(&payload); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "invalid payload") + } + + if payload.Name == "" { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "preset name is required") + } + + cfg := s.config.Get() + if cfg == nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, "no configuration loaded") + } + + if err := cfg.AddPreset(payload.Name, payload.Enable, payload.Disable); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeConflict, err.Error()) + } + + // Save config + if err := s.config.Save(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to save config: %v", err)) + } + + resp, _ := protocol.NewOKResponse(map[string]string{"added": payload.Name}) + return resp +} + +func (s *Server) handleDeletePreset(req *protocol.Request) *protocol.Response { + var payload protocol.PresetPayload + if err := req.ParsePayload(&payload); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "invalid payload") + } + + if payload.Name == "" { + return protocol.NewErrorResponse(protocol.ErrCodeInvalidRequest, "preset name is required") + } + + cfg := s.config.Get() + if cfg == nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, "no configuration loaded") + } + + if err := cfg.DeletePreset(payload.Name); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeNotFound, err.Error()) + } + + // Save config + if err := s.config.Save(); err != nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, fmt.Sprintf("failed to save config: %v", err)) + } + + resp, _ := protocol.NewOKResponse(map[string]string{"deleted": payload.Name}) + return resp +} + +func (s *Server) handleListPresets() *protocol.Response { + cfg := s.config.Get() + if cfg == nil { + return protocol.NewErrorResponse(protocol.ErrCodeInternalError, "no configuration loaded") + } + + presets := cfg.GetPresets() + infos := make([]protocol.PresetInfo, len(presets)) + for i, p := range presets { + infos[i] = protocol.PresetInfo{ + Name: p.Name, + Enable: p.Enable, + Disable: p.Disable, + } + } + + resp, _ := protocol.NewOKResponse(protocol.PresetsData{Presets: infos}) + return resp +} + +func (s *Server) syncHostsFile() error { + cfg := s.config.Get() + if cfg == nil { + return fmt.Errorf("no configuration loaded") + } + + var entries []HostEntry + for _, g := range cfg.Groups { + for _, h := range g.Hosts { + entries = append(entries, HostEntry{ + IP: h.IP, + Domain: h.Domain, + Alias: h.Alias, + Enabled: h.Enabled, + }) + } + } + + if err := s.hosts.WriteManagedEntries(entries); err != nil { + return err + } + + // Flush DNS cache + return s.flusher.Flush() +} diff --git a/internal/installer/installer.go b/internal/installer/installer.go new file mode 100644 index 0000000..9f44c87 --- /dev/null +++ b/internal/installer/installer.go @@ -0,0 +1,474 @@ +// Package installer handles installation and uninstallation of the lolcathost daemon. +package installer + +import ( + "fmt" + "os" + "os/exec" + "os/user" + "path/filepath" + "runtime" + "strconv" + "strings" + + "github.com/lukaszraczylo/lolcathost/internal/config" +) + +const ( + // GroupName is the name of the lolcathost group. + GroupName = "lolcathost" + // GroupGID is the GID for the lolcathost group (macOS). + GroupGID = 850 + + // Paths + LogDir = "/var/log/lolcathost" + BackupDir = "/var/backups/lolcathost" + SocketPath = "/var/run/lolcathost.sock" + LaunchDaemonDir = "/Library/LaunchDaemons" + SystemdDir = "/etc/systemd/system" +) + +// LaunchDaemonPlist is the macOS LaunchDaemon plist template. +const LaunchDaemonPlist = ` + + + + Label + com.lolcathost.daemon + ProgramArguments + + %s + --daemon + --config + /etc/lolcathost/config.yaml + + RunAtLoad + + KeepAlive + + StandardOutPath + /var/log/lolcathost/daemon.log + StandardErrorPath + /var/log/lolcathost/daemon.err + + +` + +// SystemdUnit is the Linux systemd unit template. +const SystemdUnit = `[Unit] +Description=lolcathost - Dynamic Host Management Daemon +After=network.target + +[Service] +Type=simple +ExecStart=%s --daemon --config /etc/lolcathost/config.yaml +Restart=always +RestartSec=5 +User=root +Group=root + +[Install] +WantedBy=multi-user.target +` + +// Installer handles installation and uninstallation. +type Installer struct { + binaryPath string + verbose bool +} + +// New creates a new installer. +func New() (*Installer, error) { + binaryPath, err := os.Executable() + if err != nil { + return nil, fmt.Errorf("failed to get executable path: %w", err) + } + + // Resolve symlinks + binaryPath, err = filepath.EvalSymlinks(binaryPath) + if err != nil { + return nil, fmt.Errorf("failed to resolve executable path: %w", err) + } + + return &Installer{ + binaryPath: binaryPath, + verbose: true, + }, nil +} + +// Install performs the full installation. +func (i *Installer) Install() error { + if os.Geteuid() != 0 { + return fmt.Errorf("--install requires sudo") + } + + i.log("Installing lolcathost...") + + // Create group + if err := i.createGroup(); err != nil { + return fmt.Errorf("failed to create group: %w", err) + } + + // Add current user to group + if err := i.addCurrentUserToGroup(); err != nil { + return fmt.Errorf("failed to add user to group: %w", err) + } + + // Create directories + if err := i.createDirectories(); err != nil { + return fmt.Errorf("failed to create directories: %w", err) + } + + // Create system config for daemon + if err := i.createSystemConfig(); err != nil { + return fmt.Errorf("failed to create system config: %w", err) + } + + // Install service + if runtime.GOOS == "darwin" { + if err := i.installLaunchDaemon(); err != nil { + return fmt.Errorf("failed to install LaunchDaemon: %w", err) + } + } else if runtime.GOOS == "linux" { + if err := i.installSystemdService(); err != nil { + return fmt.Errorf("failed to install systemd service: %w", err) + } + } + + // Create default config for the invoking user + if err := i.createDefaultConfig(); err != nil { + i.log("Warning: failed to create default config: %v", err) + } + + i.log("") + i.log("✓ Installed successfully!") + i.log("") + i.log("Next steps:") + i.log(" 1. Open a NEW terminal (for group membership to take effect)") + i.log(" 2. Run 'lolcathost' to start the TUI") + i.log("") + + return nil +} + +// Uninstall removes the installation. +func (i *Installer) Uninstall() error { + if os.Geteuid() != 0 { + return fmt.Errorf("--uninstall requires sudo") + } + + i.log("Uninstalling lolcathost...") + + // Stop and remove service + if runtime.GOOS == "darwin" { + i.uninstallLaunchDaemon() + } else if runtime.GOOS == "linux" { + i.uninstallSystemdService() + } + + // Remove socket + os.Remove(SocketPath) + + // Note: We don't remove the group, logs, or backups + // The user may want to keep these + + i.log("") + i.log("✓ Uninstalled successfully!") + i.log("") + i.log("Note: Log files, backups, and the group were preserved.") + i.log("To fully remove, manually delete:") + i.log(" - /var/log/lolcathost/") + i.log(" - /var/backups/lolcathost/") + i.log(" - ~/.config/lolcathost/") + if runtime.GOOS == "darwin" { + i.log(" - Remove group: sudo dscl . -delete /Groups/%s", GroupName) + } else { + i.log(" - Remove group: sudo groupdel %s", GroupName) + } + i.log("") + + return nil +} + +func (i *Installer) log(format string, args ...any) { + if i.verbose { + fmt.Printf(format+"\n", args...) + } +} + +func (i *Installer) createGroup() error { + switch runtime.GOOS { + case "darwin": + return i.createGroupDarwin() + case "linux": + return i.createGroupLinux() + default: + return fmt.Errorf("unsupported OS: %s", runtime.GOOS) + } +} + +func (i *Installer) createGroupDarwin() error { + // Check if group exists + if _, err := exec.Command("dscl", ".", "-read", "/Groups/"+GroupName).Output(); err == nil { + i.log(" Group '%s' already exists", GroupName) + return nil + } + + i.log(" Creating group '%s' (GID %d)...", GroupName, GroupGID) + + // Create group + cmds := [][]string{ + {"dscl", ".", "-create", "/Groups/" + GroupName}, + {"dscl", ".", "-create", "/Groups/" + GroupName, "PrimaryGroupID", strconv.Itoa(GroupGID)}, + {"dscl", ".", "-create", "/Groups/" + GroupName, "RealName", "lolcathost users"}, + } + + for _, args := range cmds { + if err := exec.Command(args[0], args[1:]...).Run(); err != nil { + return fmt.Errorf("command %v failed: %w", args, err) + } + } + + return nil +} + +func (i *Installer) createGroupLinux() error { + // Check if group exists + if _, err := exec.Command("getent", "group", GroupName).Output(); err == nil { + i.log(" Group '%s' already exists", GroupName) + return nil + } + + i.log(" Creating group '%s'...", GroupName) + + if err := exec.Command("groupadd", "-r", GroupName).Run(); err != nil { + return fmt.Errorf("groupadd failed: %w", err) + } + + return nil +} + +func (i *Installer) addCurrentUserToGroup() error { + // Get the real user (not root) + username := os.Getenv("SUDO_USER") + if username == "" { + // Fall back to current user + u, err := user.Current() + if err != nil { + return fmt.Errorf("failed to get current user: %w", err) + } + username = u.Username + } + + if username == "root" { + i.log(" Skipping adding root to group") + return nil + } + + switch runtime.GOOS { + case "darwin": + return i.addUserToGroupDarwin(username) + case "linux": + return i.addUserToGroupLinux(username) + default: + return fmt.Errorf("unsupported OS: %s", runtime.GOOS) + } +} + +func (i *Installer) addUserToGroupDarwin(username string) error { + // Check if user is already in group + output, err := exec.Command("dscl", ".", "-read", "/Groups/"+GroupName, "GroupMembership").Output() + if err == nil && strings.Contains(string(output), username) { + i.log(" User '%s' already in group '%s'", username, GroupName) + return nil + } + + i.log(" Adding user '%s' to group '%s'...", username, GroupName) + + if err := exec.Command("dscl", ".", "-append", "/Groups/"+GroupName, "GroupMembership", username).Run(); err != nil { + return fmt.Errorf("failed to add user to group: %w", err) + } + + return nil +} + +func (i *Installer) addUserToGroupLinux(username string) error { + // Check if user is already in group + output, err := exec.Command("id", "-nG", username).Output() + if err == nil && strings.Contains(string(output), GroupName) { + i.log(" User '%s' already in group '%s'", username, GroupName) + return nil + } + + i.log(" Adding user '%s' to group '%s'...", username, GroupName) + + if err := exec.Command("usermod", "-aG", GroupName, username).Run(); err != nil { + return fmt.Errorf("failed to add user to group: %w", err) + } + + return nil +} + +func (i *Installer) createDirectories() error { + dirs := []string{LogDir, BackupDir, config.SystemConfigDir} + + for _, dir := range dirs { + i.log(" Creating directory '%s'...", dir) + if err := os.MkdirAll(dir, 0755); err != nil { + return fmt.Errorf("failed to create %s: %w", dir, err) + } + } + + return nil +} + +func (i *Installer) createSystemConfig() error { + // Check if system config already exists + if _, err := os.Stat(config.SystemConfigPath); err == nil { + i.log(" System config already exists at %s", config.SystemConfigPath) + return nil + } + + i.log(" Creating system config at %s...", config.SystemConfigPath) + return config.CreateDefault(config.SystemConfigPath) +} + +func (i *Installer) installLaunchDaemon() error { + plistPath := filepath.Join(LaunchDaemonDir, "com.lolcathost.daemon.plist") + plistContent := fmt.Sprintf(LaunchDaemonPlist, i.binaryPath) + + i.log(" Writing LaunchDaemon plist...") + if err := os.WriteFile(plistPath, []byte(plistContent), 0644); err != nil { + return fmt.Errorf("failed to write plist: %w", err) + } + + // Unload if already loaded + exec.Command("launchctl", "bootout", "system/com.lolcathost.daemon").Run() + + // Bootstrap the daemon + i.log(" Starting daemon...") + if err := exec.Command("launchctl", "bootstrap", "system", plistPath).Run(); err != nil { + return fmt.Errorf("failed to bootstrap daemon: %w", err) + } + + return nil +} + +func (i *Installer) uninstallLaunchDaemon() { + plistPath := filepath.Join(LaunchDaemonDir, "com.lolcathost.daemon.plist") + + i.log(" Stopping daemon...") + exec.Command("launchctl", "bootout", "system/com.lolcathost.daemon").Run() + + i.log(" Removing LaunchDaemon plist...") + os.Remove(plistPath) +} + +func (i *Installer) installSystemdService() error { + unitPath := filepath.Join(SystemdDir, "lolcathost.service") + unitContent := fmt.Sprintf(SystemdUnit, i.binaryPath) + + i.log(" Writing systemd unit...") + if err := os.WriteFile(unitPath, []byte(unitContent), 0644); err != nil { + return fmt.Errorf("failed to write unit file: %w", err) + } + + // Reload systemd + i.log(" Reloading systemd...") + if err := exec.Command("systemctl", "daemon-reload").Run(); err != nil { + return fmt.Errorf("failed to reload systemd: %w", err) + } + + // Enable and start the service + i.log(" Enabling and starting service...") + if err := exec.Command("systemctl", "enable", "--now", "lolcathost.service").Run(); err != nil { + return fmt.Errorf("failed to enable service: %w", err) + } + + return nil +} + +func (i *Installer) uninstallSystemdService() { + i.log(" Stopping and disabling service...") + exec.Command("systemctl", "disable", "--now", "lolcathost.service").Run() + + i.log(" Removing systemd unit...") + os.Remove(filepath.Join(SystemdDir, "lolcathost.service")) + + exec.Command("systemctl", "daemon-reload").Run() +} + +func (i *Installer) createDefaultConfig() error { + // Get the real user's home directory + username := os.Getenv("SUDO_USER") + if username == "" { + return nil // Can't determine user + } + + u, err := user.Lookup(username) + if err != nil { + return fmt.Errorf("failed to lookup user: %w", err) + } + + configPath := filepath.Join(u.HomeDir, ".config", "lolcathost", "config.yaml") + + // Check if config already exists + if _, err := os.Stat(configPath); err == nil { + i.log(" Config already exists at %s", configPath) + return nil + } + + i.log(" Creating default config at %s...", configPath) + + if err := config.CreateDefault(configPath); err != nil { + return err + } + + // Change ownership to the real user + uid, _ := strconv.Atoi(u.Uid) + gid, _ := strconv.Atoi(u.Gid) + + configDir := filepath.Dir(configPath) + os.Chown(configDir, uid, gid) + os.Chown(filepath.Dir(configDir), uid, gid) + os.Chown(configPath, uid, gid) + + return nil +} + +// CheckInstallation checks if the daemon is properly installed. +func CheckInstallation() error { + // Check if socket exists + if _, err := os.Stat(SocketPath); os.IsNotExist(err) { + return fmt.Errorf("daemon not running (socket not found)") + } + + // Check if user is in group + u, err := user.Current() + if err != nil { + return fmt.Errorf("failed to get current user: %w", err) + } + + groups, err := u.GroupIds() + if err != nil { + return fmt.Errorf("failed to get user groups: %w", err) + } + + inGroup := false + for _, gid := range groups { + g, err := user.LookupGroupId(gid) + if err != nil { + continue + } + if g.Name == GroupName { + inGroup = true + break + } + } + + if !inGroup { + return fmt.Errorf("user '%s' is not in group '%s'. Run 'sudo lolcathost --install' and open a new terminal", u.Username, GroupName) + } + + return nil +} diff --git a/internal/protocol/protocol.go b/internal/protocol/protocol.go new file mode 100644 index 0000000..00b540a --- /dev/null +++ b/internal/protocol/protocol.go @@ -0,0 +1,226 @@ +// Package protocol defines shared message types for client-daemon communication. +package protocol + +import ( + "encoding/json" + "fmt" +) + +// SocketPath is the Unix socket path for daemon communication. +const SocketPath = "/var/run/lolcathost.sock" + +// RequestType defines the type of request. +type RequestType string + +const ( + RequestPing RequestType = "ping" + RequestStatus RequestType = "status" + RequestList RequestType = "list" + RequestSet RequestType = "set" + RequestAdd RequestType = "add" + RequestDelete RequestType = "delete" + RequestSync RequestType = "sync" + RequestPreset RequestType = "preset" + RequestRollback RequestType = "rollback" + RequestBackups RequestType = "backups" + RequestAddGroup RequestType = "add_group" + RequestDeleteGroup RequestType = "delete_group" + RequestRenameGroup RequestType = "rename_group" + RequestListGroups RequestType = "list_groups" + RequestAddPreset RequestType = "add_preset" + RequestDeletePreset RequestType = "delete_preset" + RequestListPresets RequestType = "list_presets" +) + +// ErrorCode defines standard error codes. +type ErrorCode string + +const ( + ErrCodeInvalidRequest ErrorCode = "INVALID_REQUEST" + ErrCodeInvalidDomain ErrorCode = "INVALID_DOMAIN" + ErrCodeInvalidIP ErrorCode = "INVALID_IP" + ErrCodeBlockedDomain ErrorCode = "BLOCKED_DOMAIN" + ErrCodeRateLimited ErrorCode = "RATE_LIMITED" + ErrCodeUnauthorized ErrorCode = "UNAUTHORIZED" + ErrCodeNotFound ErrorCode = "NOT_FOUND" + ErrCodeConflict ErrorCode = "CONFLICT" + ErrCodeInternalError ErrorCode = "INTERNAL_ERROR" + ErrCodePermissionError ErrorCode = "PERMISSION_ERROR" +) + +// Request represents a client request to the daemon. +type Request struct { + Type RequestType `json:"type"` + Payload json.RawMessage `json:"payload,omitempty"` +} + +// SetPayload is the payload for set requests. +type SetPayload struct { + Alias string `json:"alias"` + Enabled bool `json:"enabled"` + Force bool `json:"force,omitempty"` +} + +// PresetPayload is the payload for preset requests. +type PresetPayload struct { + Name string `json:"name"` +} + +// RollbackPayload is the payload for rollback requests. +type RollbackPayload struct { + BackupName string `json:"backup_name"` +} + +// AddPayload is the payload for add requests. +type AddPayload struct { + Domain string `json:"domain"` + IP string `json:"ip"` + Alias string `json:"alias"` + Group string `json:"group"` + Enabled bool `json:"enabled"` +} + +// DeletePayload is the payload for delete requests. +type DeletePayload struct { + Alias string `json:"alias"` +} + +// GroupPayload is the payload for group add/delete requests. +type GroupPayload struct { + Name string `json:"name"` +} + +// RenameGroupPayload is the payload for rename_group requests. +type RenameGroupPayload struct { + OldName string `json:"old_name"` + NewName string `json:"new_name"` +} + +// GroupsData is the data for list_groups responses. +type GroupsData struct { + Groups []string `json:"groups"` +} + +// AddPresetPayload is the payload for add_preset requests. +type AddPresetPayload struct { + Name string `json:"name"` + Enable []string `json:"enable"` + Disable []string `json:"disable"` +} + +// PresetInfo represents a preset with its configuration. +type PresetInfo struct { + Name string `json:"name"` + Enable []string `json:"enable"` + Disable []string `json:"disable"` +} + +// PresetsData is the data for list_presets responses. +type PresetsData struct { + Presets []PresetInfo `json:"presets"` +} + +// Response represents a daemon response. +type Response struct { + Status string `json:"status"` + Data json.RawMessage `json:"data,omitempty"` + Message string `json:"message,omitempty"` + Code ErrorCode `json:"code,omitempty"` +} + +// StatusData is the data for status responses. +type StatusData struct { + Running bool `json:"running"` + Version string `json:"version"` + Uptime int64 `json:"uptime_seconds"` + ActiveCount int `json:"active_count"` + RequestCount int64 `json:"request_count"` +} + +// HostEntry represents a single host entry. +type HostEntry struct { + Domain string `json:"domain"` + IP string `json:"ip"` + Alias string `json:"alias"` + Enabled bool `json:"enabled"` + Group string `json:"group"` +} + +// ListData is the data for list responses. +type ListData struct { + Entries []HostEntry `json:"entries"` +} + +// SetData is the data for set responses. +type SetData struct { + Domain string `json:"domain"` + Applied bool `json:"applied"` +} + +// BackupsData is the data for backups responses. +type BackupsData struct { + Backups []BackupInfo `json:"backups"` +} + +// BackupInfo represents a backup file. +type BackupInfo struct { + Name string `json:"name"` + Timestamp int64 `json:"timestamp"` + Size int64 `json:"size"` +} + +// NewRequest creates a new request with the given type and payload. +func NewRequest(reqType RequestType, payload interface{}) (*Request, error) { + req := &Request{Type: reqType} + if payload != nil { + data, err := json.Marshal(payload) + if err != nil { + return nil, fmt.Errorf("failed to marshal payload: %w", err) + } + req.Payload = data + } + return req, nil +} + +// NewOKResponse creates a success response with optional data. +func NewOKResponse(data interface{}) (*Response, error) { + resp := &Response{Status: "ok"} + if data != nil { + dataBytes, err := json.Marshal(data) + if err != nil { + return nil, fmt.Errorf("failed to marshal data: %w", err) + } + resp.Data = dataBytes + } + return resp, nil +} + +// NewErrorResponse creates an error response. +func NewErrorResponse(code ErrorCode, message string) *Response { + return &Response{ + Status: "error", + Code: code, + Message: message, + } +} + +// ParsePayload unmarshals the request payload into the given target. +func (r *Request) ParsePayload(target interface{}) error { + if r.Payload == nil { + return fmt.Errorf("no payload in request") + } + return json.Unmarshal(r.Payload, target) +} + +// ParseData unmarshals the response data into the given target. +func (r *Response) ParseData(target interface{}) error { + if r.Data == nil { + return fmt.Errorf("no data in response") + } + return json.Unmarshal(r.Data, target) +} + +// IsOK returns true if the response indicates success. +func (r *Response) IsOK() bool { + return r.Status == "ok" +} diff --git a/internal/protocol/protocol_test.go b/internal/protocol/protocol_test.go new file mode 100644 index 0000000..fec06d0 --- /dev/null +++ b/internal/protocol/protocol_test.go @@ -0,0 +1,227 @@ +package protocol + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewRequest(t *testing.T) { + tests := []struct { + name string + reqType RequestType + payload interface{} + wantErr bool + }{ + { + name: "ping request without payload", + reqType: RequestPing, + payload: nil, + wantErr: false, + }, + { + name: "set request with payload", + reqType: RequestSet, + payload: SetPayload{Alias: "test", Enabled: true}, + wantErr: false, + }, + { + name: "preset request with payload", + reqType: RequestPreset, + payload: PresetPayload{Name: "local"}, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req, err := NewRequest(tt.reqType, tt.payload) + if tt.wantErr { + assert.Error(t, err) + return + } + require.NoError(t, err) + assert.Equal(t, tt.reqType, req.Type) + if tt.payload != nil { + assert.NotNil(t, req.Payload) + } + }) + } +} + +func TestRequest_ParsePayload(t *testing.T) { + t.Run("valid payload", func(t *testing.T) { + payload := SetPayload{Alias: "test-alias", Enabled: true, Force: false} + req, err := NewRequest(RequestSet, payload) + require.NoError(t, err) + + var parsed SetPayload + err = req.ParsePayload(&parsed) + require.NoError(t, err) + assert.Equal(t, "test-alias", parsed.Alias) + assert.True(t, parsed.Enabled) + assert.False(t, parsed.Force) + }) + + t.Run("nil payload", func(t *testing.T) { + req := &Request{Type: RequestPing} + var parsed SetPayload + err := req.ParsePayload(&parsed) + assert.Error(t, err) + }) +} + +func TestNewOKResponse(t *testing.T) { + t.Run("with data", func(t *testing.T) { + data := StatusData{ + Running: true, + Version: "1.0.0", + Uptime: 3600, + ActiveCount: 5, + RequestCount: 100, + } + + resp, err := NewOKResponse(data) + require.NoError(t, err) + assert.Equal(t, "ok", resp.Status) + assert.NotNil(t, resp.Data) + assert.True(t, resp.IsOK()) + }) + + t.Run("without data", func(t *testing.T) { + resp, err := NewOKResponse(nil) + require.NoError(t, err) + assert.Equal(t, "ok", resp.Status) + assert.Nil(t, resp.Data) + }) +} + +func TestNewErrorResponse(t *testing.T) { + resp := NewErrorResponse(ErrCodeBlockedDomain, "domain is blocked") + + assert.Equal(t, "error", resp.Status) + assert.Equal(t, ErrCodeBlockedDomain, resp.Code) + assert.Equal(t, "domain is blocked", resp.Message) + assert.False(t, resp.IsOK()) +} + +func TestResponse_ParseData(t *testing.T) { + t.Run("valid data", func(t *testing.T) { + data := ListData{ + Entries: []HostEntry{ + {Domain: "example.com", IP: "127.0.0.1", Alias: "example", Enabled: true, Group: "dev"}, + }, + } + resp, err := NewOKResponse(data) + require.NoError(t, err) + + var parsed ListData + err = resp.ParseData(&parsed) + require.NoError(t, err) + assert.Len(t, parsed.Entries, 1) + assert.Equal(t, "example.com", parsed.Entries[0].Domain) + }) + + t.Run("nil data", func(t *testing.T) { + resp := &Response{Status: "ok"} + var parsed ListData + err := resp.ParseData(&parsed) + assert.Error(t, err) + }) +} + +func TestRequestTypes(t *testing.T) { + types := []RequestType{ + RequestPing, + RequestStatus, + RequestList, + RequestSet, + RequestSync, + RequestPreset, + RequestRollback, + RequestBackups, + } + + for _, rt := range types { + t.Run(string(rt), func(t *testing.T) { + req, err := NewRequest(rt, nil) + require.NoError(t, err) + assert.Equal(t, rt, req.Type) + + // Verify JSON marshaling works + data, err := json.Marshal(req) + require.NoError(t, err) + assert.Contains(t, string(data), string(rt)) + }) + } +} + +func TestErrorCodes(t *testing.T) { + codes := []ErrorCode{ + ErrCodeInvalidRequest, + ErrCodeInvalidDomain, + ErrCodeInvalidIP, + ErrCodeBlockedDomain, + ErrCodeRateLimited, + ErrCodeNotFound, + ErrCodeConflict, + ErrCodeInternalError, + ErrCodePermissionError, + } + + for _, code := range codes { + t.Run(string(code), func(t *testing.T) { + resp := NewErrorResponse(code, "test error") + assert.Equal(t, code, resp.Code) + + // Verify JSON marshaling works + data, err := json.Marshal(resp) + require.NoError(t, err) + assert.Contains(t, string(data), string(code)) + }) + } +} + +func TestHostEntry(t *testing.T) { + entry := HostEntry{ + Domain: "example.com", + IP: "127.0.0.1", + Alias: "example-local", + Enabled: true, + Group: "development", + } + + data, err := json.Marshal(entry) + require.NoError(t, err) + + var parsed HostEntry + err = json.Unmarshal(data, &parsed) + require.NoError(t, err) + + assert.Equal(t, entry.Domain, parsed.Domain) + assert.Equal(t, entry.IP, parsed.IP) + assert.Equal(t, entry.Alias, parsed.Alias) + assert.Equal(t, entry.Enabled, parsed.Enabled) + assert.Equal(t, entry.Group, parsed.Group) +} + +func TestBackupInfo(t *testing.T) { + info := BackupInfo{ + Name: "hosts.20231201-120000.bak", + Timestamp: 1701432000, + Size: 1024, + } + + data, err := json.Marshal(info) + require.NoError(t, err) + + var parsed BackupInfo + err = json.Unmarshal(data, &parsed) + require.NoError(t, err) + + assert.Equal(t, info.Name, parsed.Name) + assert.Equal(t, info.Timestamp, parsed.Timestamp) + assert.Equal(t, info.Size, parsed.Size) +} diff --git a/internal/tui/app.go b/internal/tui/app.go new file mode 100644 index 0000000..3e9375f --- /dev/null +++ b/internal/tui/app.go @@ -0,0 +1,904 @@ +// Package tui provides the main Bubble Tea application. +package tui + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/charmbracelet/bubbles/textinput" + tea "github.com/charmbracelet/bubbletea" + + "github.com/lukaszraczylo/lolcathost/internal/client" + "github.com/lukaszraczylo/lolcathost/internal/config" + "github.com/lukaszraczylo/lolcathost/internal/protocol" + "github.com/lukaszraczylo/lolcathost/internal/version" +) + +// ViewMode represents the current view mode. +type ViewMode int + +const ( + ViewList ViewMode = iota + ViewForm + ViewPresets + ViewGroups + ViewHelp + ViewSearch +) + +// Model is the main Bubble Tea model. +type Model struct { + // Client + client *client.Client + connected bool + + // Config + configPath string + config *config.Manager + + // Views + mode ViewMode + list *ListView + form *Form + presetPicker *PresetPicker + groupPicker *GroupPicker + searchInput textinput.Model + + // State + width int + height int + message string + messageStyle string // "error" or "success" + messageTime time.Time + searchTerm string + allGroups []string // All groups including empty ones + + // Update notification + updateAvailable bool + updateVersion string + updateURL string + + // Version info for update checking + version string + githubOwner string + githubRepo string +} + +// Message types +type ( + connectMsg struct{ err error } + refreshMsg struct { + entries []protocol.HostEntry + err error + } + toggleMsg struct { + alias string + err error + } + presetMsg struct { + name string + err error + } + addMsg struct { + domain string + err error + } + deleteMsg struct { + alias string + err error + } + addPresetMsg struct { + name string + err error + } + deletePresetMsg struct { + name string + err error + } + refreshPresetsMsg struct { + presets []protocol.PresetInfo + err error + } + addGroupMsg struct { + name string + err error + } + renameGroupMsg struct { + name string + err error + } + deleteGroupMsg struct { + name string + err error + } + refreshGroupsMsg struct { + groups []string + err error + } + clearMsgMsg struct{} + tickMsg struct{} + updateMsg struct { + version string + url string + } +) + +// NewModel creates a new TUI model. +func NewModel(socketPath, configPath string) *Model { + searchInput := textinput.New() + searchInput.Placeholder = "Search..." + searchInput.CharLimit = 100 + searchInput.Width = 50 + + return &Model{ + client: client.New(socketPath), + configPath: configPath, + config: config.NewManager(configPath), + list: NewListView(), + form: NewForm(), + presetPicker: NewPresetPicker(), + groupPicker: NewGroupPicker(), + searchInput: searchInput, + mode: ViewList, + } +} + +// Init initializes the model. +func (m *Model) Init() tea.Cmd { + return tea.Batch( + m.connect(), + tea.SetWindowTitle("lolcathost"), + m.tick(), + m.checkForUpdate(), + ) +} + +func (m *Model) connect() tea.Cmd { + return func() tea.Msg { + if err := m.client.Connect(); err != nil { + return connectMsg{err: err} + } + return connectMsg{err: nil} + } +} + +func (m *Model) refresh() tea.Cmd { + return func() tea.Msg { + entries, err := m.client.List() + if err != nil { + return refreshMsg{entries: nil, err: err} + } + return refreshMsg{entries: entries, err: nil} + } +} + +func (m *Model) toggle(alias string, enabled bool) tea.Cmd { + return func() tea.Msg { + _, err := m.client.Set(alias, enabled, false) + return toggleMsg{alias: alias, err: err} + } +} + +func (m *Model) applyPreset(name string) tea.Cmd { + return func() tea.Msg { + err := m.client.ApplyPreset(name) + return presetMsg{name: name, err: err} + } +} + +func (m *Model) addHost(domain, ip, alias, group string) tea.Cmd { + return func() tea.Msg { + _, err := m.client.Add(domain, ip, alias, group, false) + return addMsg{domain: domain, err: err} + } +} + +func (m *Model) deleteHost(alias string) tea.Cmd { + return func() tea.Msg { + err := m.client.Delete(alias) + return deleteMsg{alias: alias, err: err} + } +} + +func (m *Model) addPreset(name string, enable, disable []string) tea.Cmd { + return func() tea.Msg { + err := m.client.AddPreset(name, enable, disable) + return addPresetMsg{name: name, err: err} + } +} + +func (m *Model) deletePreset(name string) tea.Cmd { + return func() tea.Msg { + err := m.client.DeletePreset(name) + return deletePresetMsg{name: name, err: err} + } +} + +func (m *Model) refreshPresets() tea.Cmd { + return func() tea.Msg { + presets, err := m.client.ListPresets() + return refreshPresetsMsg{presets: presets, err: err} + } +} + +func (m *Model) addGroup(name string) tea.Cmd { + return func() tea.Msg { + err := m.client.AddGroup(name) + return addGroupMsg{name: name, err: err} + } +} + +func (m *Model) renameGroup(oldName, newName string) tea.Cmd { + return func() tea.Msg { + err := m.client.RenameGroup(oldName, newName) + return renameGroupMsg{name: newName, err: err} + } +} + +func (m *Model) deleteGroup(name string) tea.Cmd { + return func() tea.Msg { + err := m.client.DeleteGroup(name) + return deleteGroupMsg{name: name, err: err} + } +} + +func (m *Model) refreshGroups() tea.Cmd { + return func() tea.Msg { + groups, err := m.client.ListGroups() + return refreshGroupsMsg{groups: groups, err: err} + } +} + +func (m *Model) tick() tea.Cmd { + return tea.Tick(time.Second*3, func(t time.Time) tea.Msg { + return tickMsg{} + }) +} + +func (m *Model) clearMsg() tea.Cmd { + return tea.Tick(time.Second*3, func(t time.Time) tea.Msg { + return clearMsgMsg{} + }) +} + +func (m *Model) checkForUpdate() tea.Cmd { + if m.githubOwner == "" || m.githubRepo == "" { + return nil + } + return func() tea.Msg { + checker := version.NewChecker(m.githubOwner, m.githubRepo, m.version) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + if update := checker.CheckForUpdate(ctx); update != nil { + return updateMsg{version: update.LatestVersion, url: update.ReleaseURL} + } + return nil + } +} + +// Update handles messages. +func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var cmds []tea.Cmd + + switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.width = msg.Width + m.height = msg.Height + m.list.SetSize(msg.Width, msg.Height-10) + m.form.SetSize(msg.Width, msg.Height) + m.presetPicker.SetSize(msg.Width, msg.Height) + m.groupPicker.SetSize(msg.Width, msg.Height) + // Set search input width + searchWidth := msg.Width - 20 + if searchWidth > 60 { + searchWidth = 60 + } + m.searchInput.Width = searchWidth + + case tea.KeyMsg: + cmd := m.handleKey(msg) + if cmd != nil { + cmds = append(cmds, cmd) + } + + case connectMsg: + if msg.err != nil { + m.connected = false + m.setError(fmt.Sprintf("Failed to connect: %v", msg.err)) + } else { + m.connected = true + cmds = append(cmds, m.refresh()) + cmds = append(cmds, m.refreshPresets()) + cmds = append(cmds, m.refreshGroups()) + m.loadConfig() + } + + case refreshMsg: + if msg.err != nil { + m.setError(fmt.Sprintf("Refresh failed: %v", msg.err)) + // Mark as disconnected to trigger reconnect + m.connected = false + m.client.Close() + } else if msg.entries != nil { + m.list.SetItems(msg.entries) + } + + case toggleMsg: + if msg.err != nil { + m.list.SetError(msg.alias, true) + m.setError(fmt.Sprintf("Toggle failed: %v", msg.err)) + } else { + m.list.SetPending(msg.alias, false) + cmds = append(cmds, m.refresh()) + m.setSuccess("Entry toggled") + } + + case presetMsg: + if msg.err != nil { + m.setError(fmt.Sprintf("Preset failed: %v", msg.err)) + } else { + cmds = append(cmds, m.refresh()) + m.setSuccess(fmt.Sprintf("Applied preset: %s", msg.name)) + } + m.mode = ViewList + + case addMsg: + if msg.err != nil { + m.setError(fmt.Sprintf("Add failed: %v", msg.err)) + } else { + cmds = append(cmds, m.refresh()) + m.setSuccess(fmt.Sprintf("Added host: %s", msg.domain)) + } + m.mode = ViewList + + case deleteMsg: + if msg.err != nil { + m.setError(fmt.Sprintf("Delete failed: %v", msg.err)) + } else { + cmds = append(cmds, m.refresh()) + m.setSuccess(fmt.Sprintf("Deleted: %s", msg.alias)) + } + + case addPresetMsg: + if msg.err != nil { + m.setError(fmt.Sprintf("Add preset failed: %v", msg.err)) + } else { + cmds = append(cmds, m.refreshPresets()) + m.setSuccess(fmt.Sprintf("Added preset: %s", msg.name)) + } + m.presetPicker.CancelForm() + + case deletePresetMsg: + if msg.err != nil { + m.setError(fmt.Sprintf("Delete preset failed: %v", msg.err)) + } else { + cmds = append(cmds, m.refreshPresets()) + m.setSuccess(fmt.Sprintf("Deleted preset: %s", msg.name)) + } + m.presetPicker.CancelForm() + + case refreshPresetsMsg: + if msg.err == nil && msg.presets != nil { + m.presetPicker.SetPresetsWithInfo(msg.presets) + } + + case addGroupMsg: + if msg.err != nil { + m.setError(fmt.Sprintf("Add group failed: %v", msg.err)) + } else { + cmds = append(cmds, m.refreshGroups()) + cmds = append(cmds, m.refresh()) // Refresh list to show new group + m.setSuccess(fmt.Sprintf("Added group: %s", msg.name)) + } + m.groupPicker.CancelForm() + + case renameGroupMsg: + if msg.err != nil { + m.setError(fmt.Sprintf("Rename group failed: %v", msg.err)) + } else { + cmds = append(cmds, m.refreshGroups()) + cmds = append(cmds, m.refresh()) + m.setSuccess(fmt.Sprintf("Renamed group to: %s", msg.name)) + } + m.groupPicker.CancelForm() + + case deleteGroupMsg: + if msg.err != nil { + m.setError(fmt.Sprintf("Delete group failed: %v", msg.err)) + } else { + cmds = append(cmds, m.refreshGroups()) + cmds = append(cmds, m.refresh()) + m.setSuccess(fmt.Sprintf("Deleted group: %s", msg.name)) + } + m.groupPicker.CancelForm() + + case refreshGroupsMsg: + if msg.err == nil && msg.groups != nil { + m.allGroups = msg.groups + m.groupPicker.SetGroups(msg.groups) + } + + case clearMsgMsg: + if time.Since(m.messageTime) >= time.Second*3 { + m.message = "" + } + + case tickMsg: + // Reconnect if disconnected + if !m.connected { + cmds = append(cmds, m.connect()) + } + cmds = append(cmds, m.tick()) + + case updateMsg: + if msg.version != "" { + m.updateAvailable = true + m.updateVersion = msg.version + m.updateURL = msg.url + } + } + + return m, tea.Batch(cmds...) +} + +func (m *Model) handleKey(msg tea.KeyMsg) tea.Cmd { + // Global keys + switch msg.String() { + case "ctrl+c": + return tea.Quit + } + + // Mode-specific keys + switch m.mode { + case ViewList: + return m.handleListKey(msg) + case ViewForm: + return m.handleFormKey(msg) + case ViewPresets: + return m.handlePresetKey(msg) + case ViewGroups: + return m.handleGroupKey(msg) + case ViewHelp: + return m.handleHelpKey(msg) + case ViewSearch: + return m.handleSearchKey(msg) + } + + return nil +} + +func (m *Model) handleListKey(msg tea.KeyMsg) tea.Cmd { + switch msg.String() { + case "q": + return tea.Quit + case "esc": + // Clear search if active + if m.searchTerm != "" { + m.searchTerm = "" + m.searchInput.Reset() + } + case "up", "k": + m.list.MoveUp() + case "down", "j": + m.list.MoveDown() + case " ", "enter": + return m.toggleSelected() + case "n": + m.mode = ViewForm + m.form.SetGroups(m.allGroups) + m.form.Init() + case "e": + if item := m.list.Selected(); item != nil { + m.mode = ViewForm + m.form.SetGroups(m.allGroups) + m.form.InitEdit(item.Entry.Domain, item.Entry.IP, item.Entry.Alias, item.Entry.Group) + } + case "d": + if item := m.list.Selected(); item != nil { + return m.deleteHost(item.Entry.Alias) + } + case "p": + m.mode = ViewPresets + case "g": + m.mode = ViewGroups + return m.refreshGroups() + case "/": + m.mode = ViewSearch + m.searchInput.Focus() + case "?": + m.mode = ViewHelp + case "r": + return m.refresh() + } + return nil +} + +func (m *Model) handleFormKey(msg tea.KeyMsg) tea.Cmd { + switch msg.String() { + case "esc": + m.mode = ViewList + return nil + case "enter": + if errMsg := m.form.Validate(); errMsg != "" { + m.setError(errMsg) + return m.clearMsg() + } + domain, ip, group := m.form.Values() + if m.form.IsEdit() { + // For edit, delete old and add new (simple approach) + oldAlias := m.form.EditAlias() + return tea.Sequence( + func() tea.Msg { + m.client.Delete(oldAlias) + return nil + }, + m.addHost(domain, ip, "", group), // Empty alias = auto-generate + ) + } + return m.addHost(domain, ip, "", group) // Empty alias = auto-generate + } + + return m.form.Update(msg) +} + +func (m *Model) handlePresetKey(msg tea.KeyMsg) tea.Cmd { + // Handle based on preset picker mode + switch m.presetPicker.Mode() { + case PresetModeSelect: + return m.handlePresetSelectKey(msg) + case PresetModeAdd, PresetModeEdit: + return m.handlePresetFormKey(msg) + case PresetModeConfirmDelete: + return m.handlePresetDeleteKey(msg) + } + return nil +} + +func (m *Model) handlePresetSelectKey(msg tea.KeyMsg) tea.Cmd { + switch msg.String() { + case "esc", "q": + m.mode = ViewList + case "up", "k": + m.presetPicker.MoveUp() + case "down", "j": + m.presetPicker.MoveDown() + case "enter": + if preset := m.presetPicker.Selected(); preset != "" { + return m.applyPreset(preset) + } + case "n": + m.presetPicker.InitAdd() + case "e": + m.presetPicker.InitEdit() + case "d": + m.presetPicker.InitDelete() + } + return nil +} + +func (m *Model) handlePresetFormKey(msg tea.KeyMsg) tea.Cmd { + switch msg.String() { + case "esc": + m.presetPicker.CancelForm() + return nil + case "enter": + if errMsg := m.presetPicker.ValidateForm(); errMsg != "" { + m.setError(errMsg) + return m.clearMsg() + } + name, enable, disable := m.presetPicker.FormValues() + if m.presetPicker.IsEdit() { + // For edit, delete old and add new + oldName := m.presetPicker.EditName() + return tea.Sequence( + func() tea.Msg { + m.client.DeletePreset(oldName) + return nil + }, + m.addPreset(name, enable, disable), + ) + } + return m.addPreset(name, enable, disable) + } + return m.presetPicker.Update(msg) +} + +func (m *Model) handlePresetDeleteKey(msg tea.KeyMsg) tea.Cmd { + switch msg.String() { + case "y", "Y": + if preset := m.presetPicker.Selected(); preset != "" { + return m.deletePreset(preset) + } + m.presetPicker.CancelForm() + case "n", "N", "esc": + m.presetPicker.CancelForm() + } + return nil +} + +func (m *Model) handleGroupKey(msg tea.KeyMsg) tea.Cmd { + // Handle based on group picker mode + switch m.groupPicker.Mode() { + case GroupModeSelect: + return m.handleGroupSelectKey(msg) + case GroupModeAdd, GroupModeRename: + return m.handleGroupFormKey(msg) + case GroupModeConfirmDelete: + return m.handleGroupDeleteKey(msg) + } + return nil +} + +func (m *Model) handleGroupSelectKey(msg tea.KeyMsg) tea.Cmd { + switch msg.String() { + case "esc", "q": + m.mode = ViewList + case "up", "k": + m.groupPicker.MoveUp() + case "down", "j": + m.groupPicker.MoveDown() + case "n": + m.groupPicker.InitAdd() + case "r": + m.groupPicker.InitRename() + case "d": + m.groupPicker.InitDelete() + } + return nil +} + +func (m *Model) handleGroupFormKey(msg tea.KeyMsg) tea.Cmd { + switch msg.String() { + case "esc": + m.groupPicker.CancelForm() + return nil + case "enter": + if errMsg := m.groupPicker.ValidateForm(); errMsg != "" { + m.setError(errMsg) + return m.clearMsg() + } + name := m.groupPicker.FormValue() + if m.groupPicker.IsRename() { + oldName := m.groupPicker.EditName() + return m.renameGroup(oldName, name) + } + return m.addGroup(name) + } + return m.groupPicker.Update(msg) +} + +func (m *Model) handleGroupDeleteKey(msg tea.KeyMsg) tea.Cmd { + switch msg.String() { + case "y", "Y": + if group := m.groupPicker.Selected(); group != "" { + return m.deleteGroup(group) + } + m.groupPicker.CancelForm() + case "n", "N", "esc": + m.groupPicker.CancelForm() + } + return nil +} + +func (m *Model) handleHelpKey(msg tea.KeyMsg) tea.Cmd { + switch msg.String() { + case "esc", "q", "?": + m.mode = ViewList + } + return nil +} + +func (m *Model) handleSearchKey(msg tea.KeyMsg) tea.Cmd { + switch msg.String() { + case "esc": + m.mode = ViewList + m.searchTerm = "" + m.searchInput.Reset() + return nil + case "enter": + m.searchTerm = m.searchInput.Value() + m.mode = ViewList + return nil + } + + var cmd tea.Cmd + m.searchInput, cmd = m.searchInput.Update(msg) + return cmd +} + +func (m *Model) toggleSelected() tea.Cmd { + item := m.list.Selected() + if item == nil { + return nil + } + + m.list.SetPending(item.Entry.Alias, true) + return m.toggle(item.Entry.Alias, !item.Entry.Enabled) +} + +func (m *Model) loadConfig() { + if err := m.config.Load(); err != nil { + return + } + + cfg := m.config.Get() + if cfg == nil { + return + } + + var presetNames []string + for _, p := range cfg.Presets { + presetNames = append(presetNames, p.Name) + } + m.presetPicker.SetPresets(presetNames) +} + +func (m *Model) setError(msg string) { + m.message = msg + m.messageStyle = "error" + m.messageTime = time.Now() +} + +func (m *Model) setSuccess(msg string) { + m.message = msg + m.messageStyle = "success" + m.messageTime = time.Now() +} + +// View renders the UI. +func (m *Model) View() string { + var sb strings.Builder + + // Title with version + title := titleStyle.Render("lolcathost - Host Management") + sb.WriteString(title) + + // Update notification + if m.updateAvailable { + sb.WriteString(" ") + sb.WriteString(updateStyle.Render(fmt.Sprintf("Update available: v%s", m.updateVersion))) + } + + sb.WriteString("\n\n") + + // Main content based on mode + switch m.mode { + case ViewList: + sb.WriteString(m.list.ViewFiltered(m.searchTerm)) + case ViewForm: + sb.WriteString(m.form.View()) + case ViewPresets: + sb.WriteString(m.presetPicker.View()) + case ViewGroups: + sb.WriteString(m.groupPicker.View()) + case ViewHelp: + sb.WriteString(m.helpView()) + case ViewSearch: + sb.WriteString(m.searchView()) + } + + // Message + if m.message != "" { + sb.WriteString("\n") + if m.messageStyle == "error" { + sb.WriteString(errorMsgStyle.Render(m.message)) + } else { + sb.WriteString(successMsgStyle.Render(m.message)) + } + } + + // Calculate remaining space for footer positioning + currentContent := sb.String() + currentLines := strings.Count(currentContent, "\n") + 1 + + // Fill space to push footer to bottom (reserve 3 lines for footer) + footerHeight := 3 + remainingLines := m.height - currentLines - footerHeight + if remainingLines > 0 { + sb.WriteString(strings.Repeat("\n", remainingLines)) + } + + // Footer (help bar + status bar) + if m.mode == ViewList { + sb.WriteString("\n") + sb.WriteString(m.helpBar()) + } + sb.WriteString("\n") + sb.WriteString(m.statusBar()) + + return sb.String() +} + +func (m *Model) helpBar() string { + return helpBarStyle.Render(fmt.Sprintf("%s/%s: Navigate %s: Toggle %s: New %s: Edit %s: Delete %s: Presets %s: Groups %s: Search %s: Help %s: Quit", + helpKeyStyle.Render("↑↓"), + helpKeyStyle.Render("jk"), + helpKeyStyle.Render("Space"), + helpKeyStyle.Render("n"), + helpKeyStyle.Render("e"), + helpKeyStyle.Render("d"), + helpKeyStyle.Render("p"), + helpKeyStyle.Render("g"), + helpKeyStyle.Render("/"), + helpKeyStyle.Render("?"), + helpKeyStyle.Render("q"))) +} + +func (m *Model) statusBar() string { + var status string + if m.connected { + status = connectedStyle.String() + } else { + status = disconnectedStyle.String() + } + + active := fmt.Sprintf("%d active", m.list.ActiveCount()) + total := fmt.Sprintf("%d total", m.list.Len()) + + return statusBarStyle.Render(fmt.Sprintf("%s | %s | %s", status, active, total)) +} + +func (m *Model) helpView() string { + var sb strings.Builder + + sb.WriteString(titleStyle.Render("Help")) + sb.WriteString("\n\n") + + help := []struct{ key, desc string }{ + {"↑/↓ or j/k", "Navigate up/down"}, + {"Space/Enter", "Toggle entry on/off"}, + {"n", "Add new entry"}, + {"e", "Edit selected entry"}, + {"d", "Delete selected entry"}, + {"p", "Open preset manager"}, + {"g", "Open group manager"}, + {"/", "Search"}, + {"r", "Refresh list"}, + {"?", "Toggle this help"}, + {"q", "Quit"}, + } + + for _, h := range help { + sb.WriteString(fmt.Sprintf(" %s %s\n", + helpKeyStyle.Width(15).Render(h.key), + helpDescStyle.Render(h.desc))) + } + + sb.WriteString("\n") + sb.WriteString(helpDescStyle.Render("Press ? or Esc to close")) + + return dialogStyle.Render(sb.String()) +} + +func (m *Model) searchView() string { + var sb strings.Builder + + sb.WriteString(titleStyle.Render("Search")) + sb.WriteString("\n\n") + + sb.WriteString(inputFocusStyle.Render(m.searchInput.View())) + sb.WriteString("\n\n") + sb.WriteString(helpDescStyle.Render("Enter to search • Esc to cancel")) + + return dialogStyle.Render(sb.String()) +} + +// Run starts the TUI application. +func Run(socketPath, configPath string) error { + return RunWithVersion(socketPath, configPath, "dev", "", "") +} + +// RunWithVersion starts the TUI application with version info for update checking. +func RunWithVersion(socketPath, configPath, version, githubOwner, githubRepo string) error { + m := NewModel(socketPath, configPath) + m.version = version + m.githubOwner = githubOwner + m.githubRepo = githubRepo + p := tea.NewProgram(m, tea.WithAltScreen()) + + _, err := p.Run() + return err +} diff --git a/internal/tui/form.go b/internal/tui/form.go new file mode 100644 index 0000000..aa7cc69 --- /dev/null +++ b/internal/tui/form.go @@ -0,0 +1,336 @@ +// Package tui provides the form component for adding/editing entries. +package tui + +import ( + "fmt" + "strings" + + "github.com/charmbracelet/bubbles/textinput" + tea "github.com/charmbracelet/bubbletea" +) + +// FormMode represents the form mode. +type FormMode int + +const ( + FormModeAdd FormMode = iota + FormModeEdit +) + +// FormField represents a form field index. +type FormField int + +const ( + FieldDomain FormField = iota + FieldIP + FieldGroup + FieldCount +) + +// Form handles the add/edit entry form. +type Form struct { + mode FormMode + fields []textinput.Model + focus FormField + width int + height int + editAlias string // Original alias when editing + + // Group dropdown + groups []string + groupCursor int + groupFocused bool +} + +// NewForm creates a new form. +func NewForm() *Form { + fields := make([]textinput.Model, FieldCount) + + // Domain field + fields[FieldDomain] = textinput.New() + fields[FieldDomain].Placeholder = "example.com" + fields[FieldDomain].CharLimit = 253 + + // IP field + fields[FieldIP] = textinput.New() + fields[FieldIP].Placeholder = "127.0.0.1" + fields[FieldIP].CharLimit = 45 // IPv6 max + + // Group field (not used as text input, but kept for compatibility) + fields[FieldGroup] = textinput.New() + fields[FieldGroup].Placeholder = "development" + fields[FieldGroup].CharLimit = 63 + + return &Form{ + fields: fields, + focus: FieldDomain, + groups: []string{"default"}, + } +} + +// SetGroups sets the available groups for the dropdown. +func (f *Form) SetGroups(groups []string) { + if len(groups) == 0 { + f.groups = []string{"default"} + } else { + f.groups = groups + } + // Reset cursor if out of bounds + if f.groupCursor >= len(f.groups) { + f.groupCursor = 0 + } +} + +// Init initializes the form for adding a new entry. +func (f *Form) Init() { + f.mode = FormModeAdd + f.editAlias = "" + + for i := range f.fields { + f.fields[i].Reset() + } + + f.fields[FieldIP].SetValue("127.0.0.1") + f.groupCursor = 0 + f.groupFocused = false + f.focus = FieldDomain + f.fields[FieldDomain].Focus() +} + +// InitEdit initializes the form for editing an existing entry. +func (f *Form) InitEdit(domain, ip, alias, group string) { + f.mode = FormModeEdit + f.editAlias = alias + + f.fields[FieldDomain].SetValue(domain) + f.fields[FieldIP].SetValue(ip) + + // Find the group in the list + f.groupCursor = 0 + for i, g := range f.groups { + if g == group { + f.groupCursor = i + break + } + } + + f.groupFocused = false + f.focus = FieldDomain + f.fields[FieldDomain].Focus() +} + +// SetSize sets the form dimensions. +func (f *Form) SetSize(width, height int) { + f.width = width + f.height = height + + inputWidth := min(50, width-10) + for i := range f.fields { + f.fields[i].Width = inputWidth + } +} + +// Update handles input events. +func (f *Form) Update(msg tea.Msg) tea.Cmd { + switch msg := msg.(type) { + case tea.KeyMsg: + // Handle group dropdown navigation + if f.focus == FieldGroup { + switch msg.String() { + case "tab": + f.nextField() + return nil + case "shift+tab": + f.prevField() + return nil + case "up", "k": + if f.groupCursor > 0 { + f.groupCursor-- + } + return nil + case "down", "j": + if f.groupCursor < len(f.groups)-1 { + f.groupCursor++ + } + return nil + case "left": + if f.groupCursor > 0 { + f.groupCursor-- + } + return nil + case "right": + if f.groupCursor < len(f.groups)-1 { + f.groupCursor++ + } + return nil + } + return nil + } + + // Handle text input fields + switch msg.String() { + case "tab", "down": + f.nextField() + return nil + case "shift+tab", "up": + f.prevField() + return nil + } + } + + // Update the focused text field (only for Domain and IP) + if f.focus != FieldGroup { + var cmd tea.Cmd + f.fields[f.focus], cmd = f.fields[f.focus].Update(msg) + return cmd + } + + return nil +} + +func (f *Form) nextField() { + if f.focus != FieldGroup { + f.fields[f.focus].Blur() + } + f.focus = (f.focus + 1) % FieldCount + if f.focus != FieldGroup { + f.fields[f.focus].Focus() + } +} + +func (f *Form) prevField() { + if f.focus != FieldGroup { + f.fields[f.focus].Blur() + } + f.focus = (f.focus - 1 + FieldCount) % FieldCount + if f.focus != FieldGroup { + f.fields[f.focus].Focus() + } +} + +// Values returns the form values (domain, ip, group). +func (f *Form) Values() (domain, ip, group string) { + group = "" + if f.groupCursor < len(f.groups) { + group = f.groups[f.groupCursor] + } + return strings.TrimSpace(f.fields[FieldDomain].Value()), + strings.TrimSpace(f.fields[FieldIP].Value()), + group +} + +// EditAlias returns the original alias when editing. +func (f *Form) EditAlias() string { + return f.editAlias +} + +// IsEdit returns true if in edit mode. +func (f *Form) IsEdit() bool { + return f.mode == FormModeEdit +} + +// Validate validates the form values. +func (f *Form) Validate() string { + domain, ip, group := f.Values() + + if domain == "" { + return "Domain is required" + } + if ip == "" { + return "IP address is required" + } + if group == "" { + return "Group is required" + } + + return "" +} + +// View renders the form. +func (f *Form) View() string { + var sb strings.Builder + + title := "Add New Entry" + if f.mode == FormModeEdit { + title = "Edit Entry" + } + + sb.WriteString(titleStyle.Render(title)) + sb.WriteString("\n\n") + + // Domain field + sb.WriteString(inputLabelStyle.Render("Domain:")) + sb.WriteString("\n") + style := inputStyle + if f.focus == FieldDomain { + style = inputFocusStyle + } + sb.WriteString(style.Render(f.fields[FieldDomain].View())) + sb.WriteString("\n\n") + + // IP field + sb.WriteString(inputLabelStyle.Render("IP Address:")) + sb.WriteString("\n") + style = inputStyle + if f.focus == FieldIP { + style = inputFocusStyle + } + sb.WriteString(style.Render(f.fields[FieldIP].View())) + sb.WriteString("\n\n") + + // Group dropdown + sb.WriteString(inputLabelStyle.Render("Group:")) + sb.WriteString("\n") + sb.WriteString(f.renderGroupDropdown()) + sb.WriteString("\n\n") + + sb.WriteString("\n") + sb.WriteString(helpDescStyle.Render("Tab/↓ next • Shift+Tab/↑ prev • ←→ select group • Enter save • Esc cancel")) + + return dialogStyle.Render(sb.String()) +} + +func (f *Form) renderGroupDropdown() string { + isFocused := f.focus == FieldGroup + + // Get current group name + currentGroup := "default" + if f.groupCursor < len(f.groups) { + currentGroup = f.groups[f.groupCursor] + } + + // Build the selector content: ◀ group_name ▶ + var content string + if isFocused { + // Show arrows when focused + leftArrow := "◀" + rightArrow := "▶" + if f.groupCursor == 0 { + leftArrow = " " // dim or hide left arrow at start + } + if f.groupCursor >= len(f.groups)-1 { + rightArrow = " " // dim or hide right arrow at end + } + content = leftArrow + " " + currentGroup + " " + rightArrow + } else { + content = " " + currentGroup + " " + } + + // Show position indicator if multiple groups + if len(f.groups) > 1 { + content += fmt.Sprintf(" (%d/%d)", f.groupCursor+1, len(f.groups)) + } + + // Apply border style + if isFocused { + return inputFocusStyle.Render(content) + } + return inputStyle.Render(content) +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/internal/tui/groups.go b/internal/tui/groups.go new file mode 100644 index 0000000..55463e9 --- /dev/null +++ b/internal/tui/groups.go @@ -0,0 +1,232 @@ +// Package tui provides the group management component. +package tui + +import ( + "strings" + + "github.com/charmbracelet/bubbles/textinput" + tea "github.com/charmbracelet/bubbletea" +) + +// GroupMode represents the group view mode. +type GroupMode int + +const ( + GroupModeSelect GroupMode = iota + GroupModeAdd + GroupModeRename + GroupModeConfirmDelete +) + +// GroupPicker handles the group selection and management UI. +type GroupPicker struct { + groups []string + cursor int + width int + height int + mode GroupMode + input textinput.Model + editName string // Original name when renaming +} + +// NewGroupPicker creates a new group picker. +func NewGroupPicker() *GroupPicker { + input := textinput.New() + input.Placeholder = "group-name" + input.CharLimit = 63 + + return &GroupPicker{ + input: input, + mode: GroupModeSelect, + } +} + +// SetGroups updates the available groups. +func (g *GroupPicker) SetGroups(groups []string) { + g.groups = groups + if g.cursor >= len(groups) { + g.cursor = max(0, len(groups)-1) + } +} + +// SetSize sets the picker dimensions. +func (g *GroupPicker) SetSize(width, height int) { + g.width = width + g.height = height + g.input.Width = min(50, width-10) +} + +// MoveUp moves the cursor up. +func (g *GroupPicker) MoveUp() { + if g.cursor > 0 { + g.cursor-- + } +} + +// MoveDown moves the cursor down. +func (g *GroupPicker) MoveDown() { + if g.cursor < len(g.groups)-1 { + g.cursor++ + } +} + +// Selected returns the currently selected group. +func (g *GroupPicker) Selected() string { + if g.cursor >= 0 && g.cursor < len(g.groups) { + return g.groups[g.cursor] + } + return "" +} + +// Len returns the number of groups. +func (g *GroupPicker) Len() int { + return len(g.groups) +} + +// Mode returns the current mode. +func (g *GroupPicker) Mode() GroupMode { + return g.mode +} + +// InitAdd initializes the form for adding a new group. +func (g *GroupPicker) InitAdd() { + g.mode = GroupModeAdd + g.editName = "" + g.input.Reset() + g.input.Focus() +} + +// InitRename initializes the form for renaming an existing group. +func (g *GroupPicker) InitRename() { + selected := g.Selected() + if selected == "" { + return + } + + g.mode = GroupModeRename + g.editName = selected + g.input.SetValue(selected) + g.input.Focus() +} + +// InitDelete starts delete confirmation. +func (g *GroupPicker) InitDelete() { + if g.Selected() == "" { + return + } + g.mode = GroupModeConfirmDelete +} + +// CancelForm cancels the current form operation. +func (g *GroupPicker) CancelForm() { + g.mode = GroupModeSelect + g.editName = "" + g.input.Reset() + g.input.Blur() +} + +// Update handles input events for form mode. +func (g *GroupPicker) Update(msg tea.KeyMsg) tea.Cmd { + var cmd tea.Cmd + g.input, cmd = g.input.Update(msg) + return cmd +} + +// FormValue returns the form input value. +func (g *GroupPicker) FormValue() string { + return strings.TrimSpace(g.input.Value()) +} + +// EditName returns the original name when renaming. +func (g *GroupPicker) EditName() string { + return g.editName +} + +// IsRename returns true if in rename mode. +func (g *GroupPicker) IsRename() bool { + return g.mode == GroupModeRename +} + +// ValidateForm validates the form value. +func (g *GroupPicker) ValidateForm() string { + value := g.FormValue() + if value == "" { + return "Group name is required" + } + return "" +} + +// View renders the group picker. +func (g *GroupPicker) View() string { + switch g.mode { + case GroupModeAdd, GroupModeRename: + return g.formView() + case GroupModeConfirmDelete: + return g.deleteView() + default: + return g.selectView() + } +} + +func (g *GroupPicker) selectView() string { + var sb strings.Builder + + sb.WriteString(titleStyle.Render("Groups")) + sb.WriteString("\n\n") + + if len(g.groups) == 0 { + sb.WriteString(helpDescStyle.Render("No groups configured.")) + sb.WriteString("\n\n") + sb.WriteString(helpDescStyle.Render("Press 'n' to create one")) + } else { + for i, group := range g.groups { + if i == g.cursor { + sb.WriteString(presetSelectedStyle.Render("▸ " + group)) + } else { + sb.WriteString(presetItemStyle.Render(" " + group)) + } + sb.WriteString("\n") + } + } + + sb.WriteString("\n\n") + sb.WriteString(helpDescStyle.Render("↑↓ navigate • n new • r rename • d delete • Esc back")) + + return dialogStyle.Render(sb.String()) +} + +func (g *GroupPicker) formView() string { + var sb strings.Builder + + title := "Add New Group" + if g.mode == GroupModeRename { + title = "Rename Group" + } + + sb.WriteString(titleStyle.Render(title)) + sb.WriteString("\n\n") + + sb.WriteString(inputLabelStyle.Render("Name:")) + sb.WriteString("\n") + sb.WriteString(inputFocusStyle.Render(g.input.View())) + sb.WriteString("\n\n") + sb.WriteString(helpDescStyle.Render("Enter save • Esc cancel")) + + return dialogStyle.Render(sb.String()) +} + +func (g *GroupPicker) deleteView() string { + var sb strings.Builder + + groupName := g.Selected() + + sb.WriteString(titleStyle.Render("Delete Group")) + sb.WriteString("\n\n") + sb.WriteString(errorMsgStyle.Render("Are you sure you want to delete group '" + groupName + "'?")) + sb.WriteString("\n") + sb.WriteString(helpDescStyle.Render("This will remove all hosts in this group!")) + sb.WriteString("\n\n") + sb.WriteString(helpDescStyle.Render("y confirm • n/Esc cancel")) + + return dialogStyle.Render(sb.String()) +} diff --git a/internal/tui/list.go b/internal/tui/list.go new file mode 100644 index 0000000..72a0188 --- /dev/null +++ b/internal/tui/list.go @@ -0,0 +1,429 @@ +// Package tui provides the list view component. +package tui + +import ( + "fmt" + "strings" + + "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/lipgloss/table" + "github.com/lukaszraczylo/lolcathost/internal/protocol" +) + +// EntryItem represents a displayable host entry. +type EntryItem struct { + Entry protocol.HostEntry + Pending bool + HasError bool +} + +// ListView handles the list of host entries. +type ListView struct { + items []EntryItem + groups map[string][]int // group name -> indices in items + groupOrder []string // ordered group names + cursor int + width int + height int +} + +// NewListView creates a new list view. +func NewListView() *ListView { + return &ListView{ + groups: make(map[string][]int), + } +} + +// SetItems updates the list items. +func (l *ListView) SetItems(entries []protocol.HostEntry) { + l.items = make([]EntryItem, len(entries)) + l.groups = make(map[string][]int) + l.groupOrder = nil + + groupSeen := make(map[string]bool) + + for i, e := range entries { + l.items[i] = EntryItem{Entry: e} + + if !groupSeen[e.Group] { + groupSeen[e.Group] = true + l.groupOrder = append(l.groupOrder, e.Group) + } + + l.groups[e.Group] = append(l.groups[e.Group], i) + } + + // Reset cursor if out of bounds + if l.cursor >= len(l.items) { + l.cursor = max(0, len(l.items)-1) + } +} + +// SetSize sets the view dimensions. +func (l *ListView) SetSize(width, height int) { + l.width = width + l.height = height +} + +// MoveUp moves the cursor up. +func (l *ListView) MoveUp() { + if l.cursor > 0 { + l.cursor-- + } +} + +// MoveDown moves the cursor down. +func (l *ListView) MoveDown() { + if l.cursor < len(l.items)-1 { + l.cursor++ + } +} + +// Selected returns the currently selected item. +func (l *ListView) Selected() *EntryItem { + if l.cursor >= 0 && l.cursor < len(l.items) { + return &l.items[l.cursor] + } + return nil +} + +// SelectedAlias returns the alias of the selected item. +func (l *ListView) SelectedAlias() string { + if item := l.Selected(); item != nil { + return item.Entry.Alias + } + return "" +} + +// SetPending marks an item as pending. +func (l *ListView) SetPending(alias string, pending bool) { + for i := range l.items { + if l.items[i].Entry.Alias == alias { + l.items[i].Pending = pending + break + } + } +} + +// SetError marks an item as having an error. +func (l *ListView) SetError(alias string, hasError bool) { + for i := range l.items { + if l.items[i].Entry.Alias == alias { + l.items[i].HasError = hasError + break + } + } +} + +// UpdateEntry updates an entry's enabled state. +func (l *ListView) UpdateEntry(alias string, enabled bool) { + for i := range l.items { + if l.items[i].Entry.Alias == alias { + l.items[i].Entry.Enabled = enabled + l.items[i].Pending = false + l.items[i].HasError = false + break + } + } +} + +// Len returns the number of items. +func (l *ListView) Len() int { + return len(l.items) +} + +// ActiveCount returns the number of enabled entries. +func (l *ListView) ActiveCount() int { + count := 0 + for _, item := range l.items { + if item.Entry.Enabled { + count++ + } + } + return count +} + +// FindByAlias finds an item by alias. +func (l *ListView) FindByAlias(alias string) *EntryItem { + for i := range l.items { + if l.items[i].Entry.Alias == alias { + return &l.items[i] + } + } + return nil +} + +// Filter filters items by search term. +func (l *ListView) Filter(term string) []EntryItem { + if term == "" { + return l.items + } + + term = strings.ToLower(term) + var filtered []EntryItem + for _, item := range l.items { + if strings.Contains(strings.ToLower(item.Entry.Domain), term) || + strings.Contains(strings.ToLower(item.Entry.Alias), term) || + strings.Contains(strings.ToLower(item.Entry.IP), term) || + strings.Contains(strings.ToLower(item.Entry.Group), term) { + filtered = append(filtered, item) + } + } + return filtered +} + +// ViewFiltered renders the list filtered by search term. +func (l *ListView) ViewFiltered(searchTerm string) string { + if searchTerm == "" { + return l.View() + } + + filtered := l.Filter(searchTerm) + if len(filtered) == 0 { + emptyStyle := lipgloss.NewStyle().Foreground(colorMuted) + return "\n" + emptyStyle.Render(fmt.Sprintf(" No results for '%s'. Press Esc to clear search.", searchTerm)) + "\n" + } + + var sb strings.Builder + + // Show search indicator + searchIndicator := lipgloss.NewStyle(). + Foreground(colorWarning). + Bold(true). + Render(fmt.Sprintf(" Search: %s (%d results)", searchTerm, len(filtered))) + sb.WriteString(searchIndicator) + sb.WriteString("\n") + + // Group header style - bright colors for dark terminals + groupHeaderStyle := lipgloss.NewStyle(). + Bold(true). + Foreground(colorGroupHeader). + Background(lipgloss.Color("238")). + Padding(0, 1). + MarginTop(1) + + // Organize filtered items by group + groupItems := make(map[string][]EntryItem) + var groupOrder []string + groupSeen := make(map[string]bool) + + for _, item := range filtered { + group := item.Entry.Group + if !groupSeen[group] { + groupSeen[group] = true + groupOrder = append(groupOrder, group) + } + groupItems[group] = append(groupItems[group], item) + } + + for _, groupName := range groupOrder { + items := groupItems[groupName] + if len(items) == 0 { + continue + } + + // Group header + headerText := fmt.Sprintf(" %s (%d)", strings.ToUpper(groupName), len(items)) + sb.WriteString(groupHeaderStyle.Render(headerText)) + sb.WriteString("\n") + + // Build rows for this group's table + var rows [][]string + for _, item := range items { + status := l.getStatusString(item) + rows = append(rows, []string{ + truncate(item.Entry.Domain, 30), + truncate(item.Entry.IP, 15), + status, + }) + } + + // Create table for this group + t := table.New(). + Border(lipgloss.HiddenBorder()). + Headers("DOMAIN", "IP ADDRESS", "STATUS"). + Rows(rows...). + StyleFunc(func(row, col int) lipgloss.Style { + // Header row + if row == table.HeaderRow { + return lipgloss.NewStyle(). + Bold(true). + Foreground(colorHeader). + Padding(0, 1) + } + + baseStyle := lipgloss.NewStyle().Padding(0, 1) + + if row >= 0 && row < len(items) { + item := items[row] + + // Disabled rows are muted + if !item.Entry.Enabled && !item.Pending && !item.HasError { + return baseStyle.Foreground(colorMuted) + } + + // Status column gets colored based on status + if col == 2 { // STATUS column + if item.HasError { + return baseStyle.Foreground(colorError) + } + if item.Pending { + return baseStyle.Foreground(colorWarning) + } + if item.Entry.Enabled { + return baseStyle.Foreground(colorSuccess) + } + } + } + + return baseStyle + }) + + sb.WriteString(t.Render()) + sb.WriteString("\n") + } + + return sb.String() +} + +// GroupCount returns the number of groups. +func (l *ListView) GroupCount() int { + return len(l.groupOrder) +} + +// GetGroups returns all group names. +func (l *ListView) GetGroups() []string { + return l.groupOrder +} + +// View renders the list with groups as headers. +func (l *ListView) View() string { + if len(l.items) == 0 { + emptyStyle := lipgloss.NewStyle().Foreground(colorMuted) + return "\n" + emptyStyle.Render(" No host entries configured. Press 'n' to add a new entry.") + "\n" + } + + var sb strings.Builder + + // Group header style - bright colors for dark terminals + groupHeaderStyle := lipgloss.NewStyle(). + Bold(true). + Foreground(colorGroupHeader). + Background(lipgloss.Color("238")). + Padding(0, 1). + MarginTop(1) + + for _, groupName := range l.groupOrder { + indices := l.groups[groupName] + if len(indices) == 0 { + continue + } + + // Group header + headerText := fmt.Sprintf(" %s (%d)", strings.ToUpper(groupName), len(indices)) + sb.WriteString(groupHeaderStyle.Render(headerText)) + sb.WriteString("\n") + + // Build rows for this group's table + var rows [][]string + // Store actual item indices for cursor matching + itemIndices := make([]int, len(indices)) + copy(itemIndices, indices) + + for _, idx := range indices { + item := l.items[idx] + status := l.getStatusString(item) + rows = append(rows, []string{ + truncate(item.Entry.Domain, 30), + truncate(item.Entry.IP, 15), + status, + }) + } + + // Create table for this group + t := table.New(). + Border(lipgloss.HiddenBorder()). + Headers("DOMAIN", "IP ADDRESS", "STATUS"). + Rows(rows...). + StyleFunc(func(row, col int) lipgloss.Style { + // Header row + if row == table.HeaderRow { + return lipgloss.NewStyle(). + Bold(true). + Foreground(colorHeader). + Padding(0, 1) + } + + baseStyle := lipgloss.NewStyle().Padding(0, 1) + + // Check if this row is selected + if row >= 0 && row < len(itemIndices) { + actualItemIdx := itemIndices[row] + isSelected := actualItemIdx == l.cursor + item := l.items[actualItemIdx] + + // Selected row gets background highlight + if isSelected { + return baseStyle. + Background(colorSelectedBg). + Foreground(colorSelectedFg) + } + + // Disabled rows are muted + if !item.Entry.Enabled && !item.Pending && !item.HasError { + return baseStyle.Foreground(colorMuted) + } + + // Status column gets colored based on status + if col == 2 { // STATUS column + if item.HasError { + return baseStyle.Foreground(colorError) + } + if item.Pending { + return baseStyle.Foreground(colorWarning) + } + if item.Entry.Enabled { + return baseStyle.Foreground(colorSuccess) + } + } + } + + return baseStyle + }) + + sb.WriteString(t.Render()) + sb.WriteString("\n") + } + + return sb.String() +} + +func (l *ListView) getStatusString(item EntryItem) string { + if item.HasError { + return "✗ Error" + } + if item.Pending { + return "◐ Pending" + } + if item.Entry.Enabled { + return "● Active" + } + return "○ Disabled" +} + +func truncate(s string, maxLen int) string { + if len(s) <= maxLen { + return s + } + if maxLen <= 3 { + return s[:maxLen] + } + return s[:maxLen-3] + "..." +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/internal/tui/list_test.go b/internal/tui/list_test.go new file mode 100644 index 0000000..d0577de --- /dev/null +++ b/internal/tui/list_test.go @@ -0,0 +1,409 @@ +package tui + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/lukaszraczylo/lolcathost/internal/protocol" +) + +func TestListView_SetItems(t *testing.T) { + lv := NewListView() + + entries := []protocol.HostEntry{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "a", Enabled: true, Group: "dev"}, + {Domain: "b.com", IP: "127.0.0.1", Alias: "b", Enabled: false, Group: "dev"}, + {Domain: "c.com", IP: "192.168.1.1", Alias: "c", Enabled: true, Group: "staging"}, + } + + lv.SetItems(entries) + + assert.Equal(t, 3, lv.Len()) + assert.Len(t, lv.groups, 2) + assert.Contains(t, lv.groupOrder, "dev") + assert.Contains(t, lv.groupOrder, "staging") +} + +func TestListView_Navigation(t *testing.T) { + lv := NewListView() + entries := []protocol.HostEntry{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "a", Enabled: true, Group: "dev"}, + {Domain: "b.com", IP: "127.0.0.1", Alias: "b", Enabled: false, Group: "dev"}, + {Domain: "c.com", IP: "192.168.1.1", Alias: "c", Enabled: true, Group: "staging"}, + } + lv.SetItems(entries) + + // Initial position + assert.Equal(t, 0, lv.cursor) + + // Move down + lv.MoveDown() + assert.Equal(t, 1, lv.cursor) + + lv.MoveDown() + assert.Equal(t, 2, lv.cursor) + + // Can't move past end + lv.MoveDown() + assert.Equal(t, 2, lv.cursor) + + // Move up + lv.MoveUp() + assert.Equal(t, 1, lv.cursor) + + lv.MoveUp() + assert.Equal(t, 0, lv.cursor) + + // Can't move before start + lv.MoveUp() + assert.Equal(t, 0, lv.cursor) +} + +func TestListView_Selected(t *testing.T) { + lv := NewListView() + + t.Run("empty list", func(t *testing.T) { + item := lv.Selected() + assert.Nil(t, item) + }) + + t.Run("with items", func(t *testing.T) { + entries := []protocol.HostEntry{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "a", Enabled: true, Group: "dev"}, + {Domain: "b.com", IP: "127.0.0.1", Alias: "b", Enabled: false, Group: "dev"}, + } + lv.SetItems(entries) + + item := lv.Selected() + require.NotNil(t, item) + assert.Equal(t, "a.com", item.Entry.Domain) + + lv.MoveDown() + item = lv.Selected() + require.NotNil(t, item) + assert.Equal(t, "b.com", item.Entry.Domain) + }) +} + +func TestListView_SelectedAlias(t *testing.T) { + lv := NewListView() + + t.Run("empty list", func(t *testing.T) { + alias := lv.SelectedAlias() + assert.Empty(t, alias) + }) + + t.Run("with items", func(t *testing.T) { + entries := []protocol.HostEntry{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "my-alias", Enabled: true, Group: "dev"}, + } + lv.SetItems(entries) + + alias := lv.SelectedAlias() + assert.Equal(t, "my-alias", alias) + }) +} + +func TestListView_SetPending(t *testing.T) { + lv := NewListView() + entries := []protocol.HostEntry{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "a", Enabled: true, Group: "dev"}, + } + lv.SetItems(entries) + + assert.False(t, lv.items[0].Pending) + + lv.SetPending("a", true) + assert.True(t, lv.items[0].Pending) + + lv.SetPending("a", false) + assert.False(t, lv.items[0].Pending) + + // Non-existent alias should not panic + lv.SetPending("nonexistent", true) +} + +func TestListView_SetError(t *testing.T) { + lv := NewListView() + entries := []protocol.HostEntry{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "a", Enabled: true, Group: "dev"}, + } + lv.SetItems(entries) + + assert.False(t, lv.items[0].HasError) + + lv.SetError("a", true) + assert.True(t, lv.items[0].HasError) + + lv.SetError("a", false) + assert.False(t, lv.items[0].HasError) +} + +func TestListView_UpdateEntry(t *testing.T) { + lv := NewListView() + entries := []protocol.HostEntry{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "a", Enabled: false, Group: "dev"}, + } + lv.SetItems(entries) + lv.items[0].Pending = true + lv.items[0].HasError = true + + lv.UpdateEntry("a", true) + + assert.True(t, lv.items[0].Entry.Enabled) + assert.False(t, lv.items[0].Pending) + assert.False(t, lv.items[0].HasError) +} + +func TestListView_ActiveCount(t *testing.T) { + lv := NewListView() + entries := []protocol.HostEntry{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "a", Enabled: true, Group: "dev"}, + {Domain: "b.com", IP: "127.0.0.1", Alias: "b", Enabled: false, Group: "dev"}, + {Domain: "c.com", IP: "192.168.1.1", Alias: "c", Enabled: true, Group: "staging"}, + } + lv.SetItems(entries) + + assert.Equal(t, 2, lv.ActiveCount()) +} + +func TestListView_FindByAlias(t *testing.T) { + lv := NewListView() + entries := []protocol.HostEntry{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "a", Enabled: true, Group: "dev"}, + {Domain: "b.com", IP: "127.0.0.1", Alias: "b", Enabled: false, Group: "dev"}, + } + lv.SetItems(entries) + + t.Run("found", func(t *testing.T) { + item := lv.FindByAlias("b") + require.NotNil(t, item) + assert.Equal(t, "b.com", item.Entry.Domain) + }) + + t.Run("not found", func(t *testing.T) { + item := lv.FindByAlias("nonexistent") + assert.Nil(t, item) + }) +} + +func TestListView_Filter(t *testing.T) { + lv := NewListView() + entries := []protocol.HostEntry{ + {Domain: "myapp.com", IP: "127.0.0.1", Alias: "myapp", Enabled: true, Group: "dev"}, + {Domain: "api.myapp.com", IP: "127.0.0.1", Alias: "api", Enabled: false, Group: "dev"}, + {Domain: "other.com", IP: "192.168.1.1", Alias: "other", Enabled: true, Group: "staging"}, + } + lv.SetItems(entries) + + t.Run("empty term", func(t *testing.T) { + filtered := lv.Filter("") + assert.Len(t, filtered, 3) + }) + + t.Run("by domain", func(t *testing.T) { + filtered := lv.Filter("myapp") + assert.Len(t, filtered, 2) + }) + + t.Run("by alias", func(t *testing.T) { + filtered := lv.Filter("api") + assert.Len(t, filtered, 1) + assert.Equal(t, "api.myapp.com", filtered[0].Entry.Domain) + }) + + t.Run("by IP", func(t *testing.T) { + filtered := lv.Filter("192.168") + assert.Len(t, filtered, 1) + assert.Equal(t, "other.com", filtered[0].Entry.Domain) + }) + + t.Run("case insensitive", func(t *testing.T) { + filtered := lv.Filter("MYAPP") + assert.Len(t, filtered, 2) + }) + + t.Run("no match", func(t *testing.T) { + filtered := lv.Filter("nonexistent") + assert.Empty(t, filtered) + }) +} + +func TestListView_View(t *testing.T) { + t.Run("empty list", func(t *testing.T) { + lv := NewListView() + view := lv.View() + assert.Contains(t, view, "No host entries") + }) + + t.Run("with items", func(t *testing.T) { + lv := NewListView() + entries := []protocol.HostEntry{ + {Domain: "example.com", IP: "127.0.0.1", Alias: "example", Enabled: true, Group: "dev"}, + } + lv.SetItems(entries) + + view := lv.View() + // Group header is shown as section title (uppercase) + assert.Contains(t, view, "DEV") + // Table headers + assert.Contains(t, view, "DOMAIN") + assert.Contains(t, view, "IP ADDRESS") + assert.Contains(t, view, "STATUS") + // Data is in the view + assert.Contains(t, view, "example.com") + assert.Contains(t, view, "127.0.0.1") + assert.Contains(t, view, "Active") + }) +} + +func TestListView_SetSize(t *testing.T) { + lv := NewListView() + lv.SetSize(80, 24) + + assert.Equal(t, 80, lv.width) + assert.Equal(t, 24, lv.height) +} + +func TestListView_CursorBounds(t *testing.T) { + lv := NewListView() + + // Set items + entries := []protocol.HostEntry{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "a", Enabled: true, Group: "dev"}, + {Domain: "b.com", IP: "127.0.0.1", Alias: "b", Enabled: true, Group: "dev"}, + } + lv.SetItems(entries) + lv.cursor = 1 + + // Set fewer items - cursor should be adjusted + entries = []protocol.HostEntry{ + {Domain: "a.com", IP: "127.0.0.1", Alias: "a", Enabled: true, Group: "dev"}, + } + lv.SetItems(entries) + + assert.Equal(t, 0, lv.cursor) +} + +func TestTruncate(t *testing.T) { + tests := []struct { + input string + maxLen int + expected string + }{ + {"short", 10, "short"}, + {"exactly10!", 10, "exactly10!"}, + {"this is too long", 10, "this is..."}, + {"", 5, ""}, + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + result := truncate(tt.input, tt.maxLen) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestMax(t *testing.T) { + assert.Equal(t, 5, max(3, 5)) + assert.Equal(t, 5, max(5, 3)) + assert.Equal(t, 5, max(5, 5)) + assert.Equal(t, 0, max(0, -1)) +} + +// Matrix test for navigation +func TestListView_Navigation_Matrix(t *testing.T) { + sizes := []int{1, 5, 10, 100} + + for _, size := range sizes { + t.Run("size="+string(rune('0'+size)), func(t *testing.T) { + lv := NewListView() + + entries := make([]protocol.HostEntry, size) + for i := range entries { + entries[i] = protocol.HostEntry{ + Domain: "domain" + string(rune('a'+i%26)) + ".com", + IP: "127.0.0.1", + Alias: "alias" + string(rune('a'+i%26)), + Enabled: true, + Group: "dev", + } + } + lv.SetItems(entries) + + // Move to end + for i := 0; i < size*2; i++ { + lv.MoveDown() + } + assert.Equal(t, size-1, lv.cursor) + + // Move to start + for i := 0; i < size*2; i++ { + lv.MoveUp() + } + assert.Equal(t, 0, lv.cursor) + }) + } +} + +func BenchmarkListView_SetItems(b *testing.B) { + entries := make([]protocol.HostEntry, 100) + for i := range entries { + entries[i] = protocol.HostEntry{ + Domain: "domain.com", + IP: "127.0.0.1", + Alias: "alias", + Enabled: true, + Group: "dev", + } + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + lv := NewListView() + lv.SetItems(entries) + } +} + +func BenchmarkListView_Filter(b *testing.B) { + lv := NewListView() + entries := make([]protocol.HostEntry, 100) + for i := range entries { + entries[i] = protocol.HostEntry{ + Domain: "domain" + string(rune('a'+i%26)) + ".com", + IP: "127.0.0.1", + Alias: "alias" + string(rune('a'+i%26)), + Enabled: true, + Group: "dev", + } + } + lv.SetItems(entries) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = lv.Filter("domain") + } +} + +func BenchmarkListView_View(b *testing.B) { + lv := NewListView() + entries := make([]protocol.HostEntry, 50) + for i := range entries { + entries[i] = protocol.HostEntry{ + Domain: "domain.com", + IP: "127.0.0.1", + Alias: "alias", + Enabled: i%2 == 0, + Group: "group" + string(rune('a'+i%5)), + } + } + lv.SetItems(entries) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = lv.View() + } +} diff --git a/internal/tui/presets.go b/internal/tui/presets.go new file mode 100644 index 0000000..c79df6f --- /dev/null +++ b/internal/tui/presets.go @@ -0,0 +1,356 @@ +// Package tui provides the preset picker component. +package tui + +import ( + "strings" + + "github.com/charmbracelet/bubbles/textinput" + tea "github.com/charmbracelet/bubbletea" + "github.com/lukaszraczylo/lolcathost/internal/protocol" +) + +// PresetMode represents the preset view mode. +type PresetMode int + +const ( + PresetModeSelect PresetMode = iota + PresetModeAdd + PresetModeEdit + PresetModeConfirmDelete +) + +// PresetFormField represents a form field index. +type PresetFormField int + +const ( + PresetFieldName PresetFormField = iota + PresetFieldEnable + PresetFieldDisable + PresetFieldCount +) + +// PresetPicker handles the preset selection and management UI. +type PresetPicker struct { + presets []protocol.PresetInfo + cursor int + width int + height int + mode PresetMode + fields []textinput.Model + focus PresetFormField + editName string // Original name when editing +} + +// NewPresetPicker creates a new preset picker. +func NewPresetPicker() *PresetPicker { + fields := make([]textinput.Model, PresetFieldCount) + + // Name field + fields[PresetFieldName] = textinput.New() + fields[PresetFieldName].Placeholder = "preset-name" + fields[PresetFieldName].CharLimit = 63 + + // Enable field + fields[PresetFieldEnable] = textinput.New() + fields[PresetFieldEnable].Placeholder = "alias1,alias2,alias3" + fields[PresetFieldEnable].CharLimit = 500 + + // Disable field + fields[PresetFieldDisable] = textinput.New() + fields[PresetFieldDisable].Placeholder = "alias1,alias2,alias3" + fields[PresetFieldDisable].CharLimit = 500 + + return &PresetPicker{ + fields: fields, + mode: PresetModeSelect, + } +} + +// SetPresets updates the available presets (legacy method for compatibility). +func (p *PresetPicker) SetPresets(presets []string) { + p.presets = make([]protocol.PresetInfo, len(presets)) + for i, name := range presets { + p.presets[i] = protocol.PresetInfo{Name: name} + } + if p.cursor >= len(presets) { + p.cursor = max(0, len(presets)-1) + } +} + +// SetPresetsWithInfo updates the available presets with full info. +func (p *PresetPicker) SetPresetsWithInfo(presets []protocol.PresetInfo) { + p.presets = presets + if p.cursor >= len(presets) { + p.cursor = max(0, len(presets)-1) + } +} + +// SetSize sets the picker dimensions. +func (p *PresetPicker) SetSize(width, height int) { + p.width = width + p.height = height + + inputWidth := min(60, width-10) + for i := range p.fields { + p.fields[i].Width = inputWidth + } +} + +// MoveUp moves the cursor up. +func (p *PresetPicker) MoveUp() { + if p.cursor > 0 { + p.cursor-- + } +} + +// MoveDown moves the cursor down. +func (p *PresetPicker) MoveDown() { + if p.cursor < len(p.presets)-1 { + p.cursor++ + } +} + +// Selected returns the currently selected preset name. +func (p *PresetPicker) Selected() string { + if p.cursor >= 0 && p.cursor < len(p.presets) { + return p.presets[p.cursor].Name + } + return "" +} + +// SelectedInfo returns the currently selected preset info. +func (p *PresetPicker) SelectedInfo() *protocol.PresetInfo { + if p.cursor >= 0 && p.cursor < len(p.presets) { + return &p.presets[p.cursor] + } + return nil +} + +// Len returns the number of presets. +func (p *PresetPicker) Len() int { + return len(p.presets) +} + +// Mode returns the current mode. +func (p *PresetPicker) Mode() PresetMode { + return p.mode +} + +// SetMode sets the mode. +func (p *PresetPicker) SetMode(mode PresetMode) { + p.mode = mode +} + +// InitAdd initializes the form for adding a new preset. +func (p *PresetPicker) InitAdd() { + p.mode = PresetModeAdd + p.editName = "" + for i := range p.fields { + p.fields[i].Reset() + } + p.focus = PresetFieldName + p.fields[PresetFieldName].Focus() +} + +// InitEdit initializes the form for editing an existing preset. +func (p *PresetPicker) InitEdit() { + preset := p.SelectedInfo() + if preset == nil { + return + } + + p.mode = PresetModeEdit + p.editName = preset.Name + + p.fields[PresetFieldName].SetValue(preset.Name) + p.fields[PresetFieldEnable].SetValue(strings.Join(preset.Enable, ",")) + p.fields[PresetFieldDisable].SetValue(strings.Join(preset.Disable, ",")) + + p.focus = PresetFieldName + p.fields[PresetFieldName].Focus() +} + +// InitDelete starts delete confirmation. +func (p *PresetPicker) InitDelete() { + if p.SelectedInfo() == nil { + return + } + p.mode = PresetModeConfirmDelete +} + +// CancelForm cancels the current form operation. +func (p *PresetPicker) CancelForm() { + p.mode = PresetModeSelect + p.editName = "" + for i := range p.fields { + p.fields[i].Reset() + p.fields[i].Blur() + } +} + +// Update handles input events for form mode. +func (p *PresetPicker) Update(msg tea.KeyMsg) tea.Cmd { + switch msg.String() { + case "tab", "down": + p.nextField() + return nil + case "shift+tab", "up": + p.prevField() + return nil + } + + // Update the focused field + var cmd tea.Cmd + p.fields[p.focus], cmd = p.fields[p.focus].Update(msg) + return cmd +} + +func (p *PresetPicker) nextField() { + p.fields[p.focus].Blur() + p.focus = (p.focus + 1) % PresetFieldCount + p.fields[p.focus].Focus() +} + +func (p *PresetPicker) prevField() { + p.fields[p.focus].Blur() + p.focus = (p.focus - 1 + PresetFieldCount) % PresetFieldCount + p.fields[p.focus].Focus() +} + +// FormValues returns the form values (name, enable list, disable list). +func (p *PresetPicker) FormValues() (name string, enable, disable []string) { + name = strings.TrimSpace(p.fields[PresetFieldName].Value()) + + enableStr := strings.TrimSpace(p.fields[PresetFieldEnable].Value()) + if enableStr != "" { + for _, s := range strings.Split(enableStr, ",") { + if trimmed := strings.TrimSpace(s); trimmed != "" { + enable = append(enable, trimmed) + } + } + } + + disableStr := strings.TrimSpace(p.fields[PresetFieldDisable].Value()) + if disableStr != "" { + for _, s := range strings.Split(disableStr, ",") { + if trimmed := strings.TrimSpace(s); trimmed != "" { + disable = append(disable, trimmed) + } + } + } + + return name, enable, disable +} + +// EditName returns the original name when editing. +func (p *PresetPicker) EditName() string { + return p.editName +} + +// IsEdit returns true if in edit mode. +func (p *PresetPicker) IsEdit() bool { + return p.mode == PresetModeEdit +} + +// ValidateForm validates the form values. +func (p *PresetPicker) ValidateForm() string { + name, enable, disable := p.FormValues() + + if name == "" { + return "Preset name is required" + } + if len(enable) == 0 && len(disable) == 0 { + return "At least one alias to enable or disable is required" + } + + return "" +} + +// View renders the preset picker. +func (p *PresetPicker) View() string { + switch p.mode { + case PresetModeAdd, PresetModeEdit: + return p.formView() + case PresetModeConfirmDelete: + return p.deleteView() + default: + return p.selectView() + } +} + +func (p *PresetPicker) selectView() string { + var sb strings.Builder + + sb.WriteString(titleStyle.Render("Presets")) + sb.WriteString("\n\n") + + if len(p.presets) == 0 { + sb.WriteString(helpDescStyle.Render("No presets configured.")) + sb.WriteString("\n\n") + sb.WriteString(helpDescStyle.Render("Press 'n' to create one")) + } else { + for i, preset := range p.presets { + if i == p.cursor { + sb.WriteString(presetSelectedStyle.Render("▸ " + preset.Name)) + } else { + sb.WriteString(presetItemStyle.Render(" " + preset.Name)) + } + sb.WriteString("\n") + } + } + + sb.WriteString("\n\n") + sb.WriteString(helpDescStyle.Render("↑↓ navigate • Enter apply • n new • e edit • d delete • Esc cancel")) + + return dialogStyle.Render(sb.String()) +} + +func (p *PresetPicker) formView() string { + var sb strings.Builder + + title := "Add New Preset" + if p.mode == PresetModeEdit { + title = "Edit Preset" + } + + sb.WriteString(titleStyle.Render(title)) + sb.WriteString("\n\n") + + labels := []string{"Name:", "Enable aliases (comma-separated):", "Disable aliases (comma-separated):"} + + for i, label := range labels { + sb.WriteString(inputLabelStyle.Render(label)) + sb.WriteString("\n") + + style := inputStyle + if PresetFormField(i) == p.focus { + style = inputFocusStyle + } + + sb.WriteString(style.Render(p.fields[i].View())) + sb.WriteString("\n\n") + } + + sb.WriteString("\n") + sb.WriteString(helpDescStyle.Render("Tab/↓ next • Shift+Tab/↑ prev • Enter save • Esc cancel")) + + return dialogStyle.Render(sb.String()) +} + +func (p *PresetPicker) deleteView() string { + var sb strings.Builder + + preset := p.SelectedInfo() + presetName := "" + if preset != nil { + presetName = preset.Name + } + + sb.WriteString(titleStyle.Render("Delete Preset")) + sb.WriteString("\n\n") + sb.WriteString(errorMsgStyle.Render("Are you sure you want to delete preset '" + presetName + "'?")) + sb.WriteString("\n\n") + sb.WriteString(helpDescStyle.Render("y confirm • n/Esc cancel")) + + return dialogStyle.Render(sb.String()) +} diff --git a/internal/tui/styles.go b/internal/tui/styles.go new file mode 100644 index 0000000..ffa2bb6 --- /dev/null +++ b/internal/tui/styles.go @@ -0,0 +1,150 @@ +// Package tui provides the terminal user interface. +package tui + +import ( + "github.com/charmbracelet/lipgloss" +) + +// Colors - matching kportal style, optimized for dark terminals +var ( + colorPrimary = lipgloss.Color("205") // Pink/Magenta + colorSuccess = lipgloss.Color("42") // Green + colorWarning = lipgloss.Color("220") // Yellow + colorError = lipgloss.Color("196") // Red + colorMuted = lipgloss.Color("245") // Gray (brighter for dark terminals) + colorAccent = lipgloss.Color("141") // Light purple (brighter for dark terminals) + colorHeader = lipgloss.Color("220") // Yellow for headers + colorSelectedBg = lipgloss.Color("236") // Gray background for selection + colorSelectedFg = lipgloss.Color("255") // White foreground for selection + colorGroupHeader = lipgloss.Color("213") // Light pink for group headers +) + +// Title and header styles +var ( + titleStyle = lipgloss.NewStyle(). + Bold(true). + Foreground(colorHeader). + Padding(0, 1) +) + +// Status indicators +var ( + enabledStyle = lipgloss.NewStyle(). + Foreground(colorSuccess). + Bold(true) + + disabledStyle = lipgloss.NewStyle(). + Foreground(colorMuted) + + pendingStyle = lipgloss.NewStyle(). + Foreground(colorWarning) + + errorIndicatorStyle = lipgloss.NewStyle(). + Foreground(colorError) +) + +// Status bar and help +var ( + statusBarStyle = lipgloss.NewStyle(). + Foreground(colorMuted) + + connectedStyle = lipgloss.NewStyle(). + Foreground(colorSuccess). + SetString("Connected") + + disconnectedStyle = lipgloss.NewStyle(). + Foreground(colorError). + SetString("Disconnected") + + helpBarStyle = lipgloss.NewStyle(). + Foreground(colorMuted) + + helpKeyStyle = lipgloss.NewStyle(). + Foreground(colorHeader). + Bold(true) + + helpDescStyle = lipgloss.NewStyle(). + Foreground(colorMuted) +) + +// Message styles +var ( + errorMsgStyle = lipgloss.NewStyle(). + Foreground(colorError). + Bold(true). + MarginTop(1) + + successMsgStyle = lipgloss.NewStyle(). + Foreground(colorSuccess). + MarginTop(1) + + updateStyle = lipgloss.NewStyle(). + Foreground(colorSuccess). + Bold(true) +) + +// Form styles +var ( + inputLabelStyle = lipgloss.NewStyle(). + Foreground(colorPrimary). + Bold(true) + + inputStyle = lipgloss.NewStyle(). + Border(lipgloss.RoundedBorder()). + BorderForeground(colorMuted). + Padding(0, 1) + + inputFocusStyle = lipgloss.NewStyle(). + Border(lipgloss.RoundedBorder()). + BorderForeground(colorPrimary). + Padding(0, 1) +) + +// Dialog/modal styles +var ( + dialogStyle = lipgloss.NewStyle(). + Border(lipgloss.RoundedBorder()). + BorderForeground(colorAccent). + Padding(1, 2) + + presetItemStyle = lipgloss.NewStyle(). + Padding(0, 1) + + presetSelectedStyle = lipgloss.NewStyle(). + Background(colorSelectedBg). + Foreground(colorSelectedFg). + Padding(0, 1) +) + +// Indicator returns the appropriate status indicator string. +func Indicator(enabled bool, pending bool, hasError bool) string { + if hasError { + return errorIndicatorStyle.Render("✗") + } + if pending { + return pendingStyle.Render("◐") + } + if enabled { + return enabledStyle.Render("●") + } + return disabledStyle.Render("○") +} + +// StatusText returns the status text with appropriate styling +func StatusText(enabled bool, pending bool, hasError bool) string { + if hasError { + return errorIndicatorStyle.Render("✗ Error") + } + if pending { + return pendingStyle.Render("◐ Pending") + } + if enabled { + return enabledStyle.Render("● Active") + } + return disabledStyle.Render("○ Disabled") +} + +// HelpItem formats a help item. +func HelpItem(key, desc string) string { + return helpKeyStyle.Render(key) + " " + helpDescStyle.Render(desc) +} diff --git a/internal/version/checker.go b/internal/version/checker.go new file mode 100644 index 0000000..24519bf --- /dev/null +++ b/internal/version/checker.go @@ -0,0 +1,159 @@ +// Package version provides version checking against GitHub releases. +package version + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strings" + "time" +) + +const ( + // githubReleasesURL is the GitHub API endpoint for latest release + githubReleasesURL = "https://api.github.com/repos/%s/%s/releases/latest" + // requestTimeout is the timeout for HTTP requests + requestTimeout = 5 * time.Second +) + +// ReleaseInfo contains information about a GitHub release +type ReleaseInfo struct { + TagName string `json:"tag_name"` + HTMLURL string `json:"html_url"` + Name string `json:"name"` +} + +// UpdateInfo contains information about an available update +type UpdateInfo struct { + CurrentVersion string + LatestVersion string + ReleaseURL string + ReleaseName string +} + +// Checker checks for new versions on GitHub +type Checker struct { + owner string + repo string + current string + client *http.Client +} + +// NewChecker creates a new version checker +func NewChecker(owner, repo, currentVersion string) *Checker { + return &Checker{ + owner: owner, + repo: repo, + current: normalizeVersion(currentVersion), + client: &http.Client{ + Timeout: requestTimeout, + }, + } +} + +// CheckForUpdate checks if a newer version is available. +// Returns nil if current version is up to date or if check fails. +// This is designed to fail silently - network errors should not impact the user. +func (c *Checker) CheckForUpdate(ctx context.Context) *UpdateInfo { + release, err := c.fetchLatestRelease(ctx) + if err != nil { + return nil + } + + latestVersion := normalizeVersion(release.TagName) + if isNewerVersion(latestVersion, c.current) { + return &UpdateInfo{ + CurrentVersion: c.current, + LatestVersion: latestVersion, + ReleaseURL: release.HTMLURL, + ReleaseName: release.Name, + } + } + + return nil +} + +// fetchLatestRelease fetches the latest release info from GitHub API +func (c *Checker) fetchLatestRelease(ctx context.Context) (*ReleaseInfo, error) { + url := fmt.Sprintf(githubReleasesURL, c.owner, c.repo) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, err + } + + req.Header.Set("Accept", "application/vnd.github.v3+json") + req.Header.Set("User-Agent", "lolcathost-version-checker") + + resp, err := c.client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("GitHub API returned status %d", resp.StatusCode) + } + + var release ReleaseInfo + if err := json.NewDecoder(resp.Body).Decode(&release); err != nil { + return nil, err + } + + return &release, nil +} + +// normalizeVersion removes 'v' or 'V' prefix and trims whitespace +func normalizeVersion(v string) string { + v = strings.TrimSpace(v) + v = strings.TrimPrefix(v, "v") + v = strings.TrimPrefix(v, "V") + return v +} + +// isNewerVersion compares two semver-like versions. +// Returns true if latest is newer than current. +func isNewerVersion(latest, current string) bool { + latestParts := parseVersion(latest) + currentParts := parseVersion(current) + + // Compare each part + for i := 0; i < len(latestParts) && i < len(currentParts); i++ { + if latestParts[i] > currentParts[i] { + return true + } + if latestParts[i] < currentParts[i] { + return false + } + } + + // If all compared parts are equal, longer version is newer + // e.g., 1.0.1 > 1.0 + return len(latestParts) > len(currentParts) +} + +// parseVersion splits a version string into numeric parts +func parseVersion(v string) []int { + // Remove any suffix like -beta, -rc1, etc. + if idx := strings.IndexAny(v, "-+"); idx != -1 { + v = v[:idx] + } + + parts := strings.Split(v, ".") + result := make([]int, 0, len(parts)) + + for _, p := range parts { + var num int + fmt.Sscanf(p, "%d", &num) + result = append(result, num) + } + + return result +} + +// FormatUpdateMessage formats a user-friendly update notification +func (u *UpdateInfo) FormatUpdateMessage() string { + return fmt.Sprintf("New version available: %s (current: %s) - %s", + u.LatestVersion, u.CurrentVersion, u.ReleaseURL) +} diff --git a/internal/version/checker_test.go b/internal/version/checker_test.go new file mode 100644 index 0000000..a7ba712 --- /dev/null +++ b/internal/version/checker_test.go @@ -0,0 +1,99 @@ +package version + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNormalizeVersion(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {"v1.0.0", "1.0.0"}, + {"1.0.0", "1.0.0"}, + {" v2.1.3 ", "2.1.3"}, + {"V1.0.0", "1.0.0"}, + {"v0.1.0", "0.1.0"}, + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + result := normalizeVersion(tt.input) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestParseVersion(t *testing.T) { + tests := []struct { + input string + expected []int + }{ + {"1.0.0", []int{1, 0, 0}}, + {"2.1.3", []int{2, 1, 3}}, + {"1.0", []int{1, 0}}, + {"10.20.30", []int{10, 20, 30}}, + {"1.0.0-beta", []int{1, 0, 0}}, + {"1.0.0-rc1", []int{1, 0, 0}}, + {"1.0.0+build123", []int{1, 0, 0}}, + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + result := parseVersion(tt.input) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestIsNewerVersion(t *testing.T) { + tests := []struct { + name string + latest string + current string + expected bool + }{ + {"major version bump", "2.0.0", "1.0.0", true}, + {"minor version bump", "1.1.0", "1.0.0", true}, + {"patch version bump", "1.0.1", "1.0.0", true}, + {"same version", "1.0.0", "1.0.0", false}, + {"current is newer major", "1.0.0", "2.0.0", false}, + {"current is newer minor", "1.0.0", "1.1.0", false}, + {"current is newer patch", "1.0.0", "1.0.1", false}, + {"longer version is newer", "1.0.1", "1.0", true}, + {"shorter version is older", "1.0", "1.0.1", false}, + {"double digit versions", "10.0.0", "9.0.0", true}, + {"with prerelease suffix", "1.1.0", "1.0.0-beta", true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := isNewerVersion(tt.latest, tt.current) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestUpdateInfo_FormatUpdateMessage(t *testing.T) { + info := &UpdateInfo{ + CurrentVersion: "1.0.0", + LatestVersion: "1.1.0", + ReleaseURL: "https://github.com/lukaszraczylo/lolcathost/releases/tag/v1.1.0", + } + + msg := info.FormatUpdateMessage() + assert.Contains(t, msg, "1.0.0") + assert.Contains(t, msg, "1.1.0") + assert.Contains(t, msg, "https://github.com") +} + +func TestNewChecker(t *testing.T) { + checker := NewChecker("lukaszraczylo", "lolcathost", "v1.0.0") + + assert.Equal(t, "lukaszraczylo", checker.owner) + assert.Equal(t, "lolcathost", checker.repo) + assert.Equal(t, "1.0.0", checker.current) // Should be normalized + assert.NotNil(t, checker.client) +} diff --git a/semver.yaml b/semver.yaml new file mode 100644 index 0000000..95d3a1d --- /dev/null +++ b/semver.yaml @@ -0,0 +1,22 @@ +version: 1 + +force: + major: 0 + minor: 1 + patch: 0 + +blacklist: + - "Merge branch" + - "Merge pull request" + - "WIP" + +wording: + minor: + - "feat" + - "feature" + major: + - "breaking" + - "major" + - "BREAKING CHANGE" + release: + - "release candidate"