feat: add SQLite persistence and REST API for temporary whitelisting

- Migrate IP-based temporary whitelisting from file to SQLite storage
- Add REST API endpoints for managing temporary and permanent whitelists
- Create `.env.example` with required environment variables
- Document API endpoints in README.md and docs/api.md
- Add new dependency `modernc.org/sqlite` for SQLite support
- Update deployment and security documentation
This commit is contained in:
db123-test
2026-05-04 09:08:11 +03:30
parent a5c9c42533
commit 515de33837
18 changed files with 467 additions and 254 deletions

View File

@@ -79,6 +79,21 @@ A shell script would work but:
Go's standard library is sufficient for our needs.
## Why SQLite?
SQLite provides:
- Single file — no external dependencies.
- Persistence across restarts.
- ACID transactions.
- Simple WAL mode for concurrent reads.
For a handful of IPs (dozens at most), SQLite is:
- Simple to deploy.
- Fast (single-file, no network).
- No external dependencies.
## Why in-memory temporary whitelist?
For a handful of IPs (dozens at most), an in-memory map is:
@@ -94,33 +109,12 @@ If you need persistence across restarts, add a disk-backed store later.
Checking expiry on every request adds latency (two map lookups per request).
The cleanup goroutine is a one-time cost that keeps the hot path fast.
## Why a file watcher?
The permanent whitelist file is the source of truth for permanent whitelisted IPs.
We poll the file every 30 seconds (configurable) and reload the in-memory store
if the file has changed.
## Why poll instead of inotify?
Polling is simpler and works inside Docker containers where inotify
may not be available (no host filesystem access).
## Why not use a database?
For our use case (a handful of IPs), a database is overkill.
We chose an in-memory map + periodic cleanup.
## Why not use Redis?
For our use case, Redis is overkill.
We chose an in-memory map + periodic cleanup.
## Why not use SQLite?
For our use case, SQLite is overkill.
We chose an in-memory map + periodic cleanup.
## Why not use a key-value store?
For our use case, a key-value store is overkill.
We chose an in-memory map + periodic cleanup.