Initial commit

This commit is contained in:
2026-05-13 19:42:49 -07:00
commit cfa01bd4ef
54 changed files with 11718 additions and 0 deletions

102
README.md Normal file
View File

@@ -0,0 +1,102 @@
# Veola
Self-hosted Go web app that tracks items across e-commerce platforms (eBay, Amazon family, Yahoo Auctions JP, Mercari JP) via the [Apify](https://apify.com) scraping API and pushes deal alerts to a self-hosted [ntfy](https://ntfy.sh) instance.
Track. Watch. Notice.
## Features
- Watch arbitrary items across multiple marketplaces with per-item search queries, target prices, and poll intervals
- Active-listing, sold-listing, and price-comparison actors per item
- Price-history chart and best-price badge once enough history accumulates
- Deal alerts pushed to ntfy when current price falls at or below target
- Single-binary deploy, SQLite storage, no CGO
See [`veola-spec.md`](veola-spec.md) for the full specification.
## Requirements
- Go 1.22+ (developed against 1.25)
- An [Apify](https://apify.com) account + API key
- A reachable [ntfy](https://ntfy.sh) instance (self-hosted or ntfy.sh)
## Build
```sh
go build -o veola-bin .
```
The binary is named `veola-bin` rather than `veola` because the module is also `veola``go build` cannot write a binary with the same name as the module dir.
If you change any `.templ` files, regenerate first:
```sh
~/go/bin/templ generate
```
## Configure
Copy the example and edit:
```sh
cp config.toml.example config.toml
```
Both `session_secret` and `encryption_key` must be at least 32 bytes and different from each other. Generate with:
```sh
openssl rand -hex 32
```
`encryption_key` encrypts secrets at rest in SQLite (Apify keys, ntfy settings). Rotating it invalidates stored secrets — re-enter them through `/settings` after rotation.
## Run
```sh
./veola-bin -config config.toml
```
First-run flow:
1. Visit `http://localhost:8080/`. With no users, you are redirected to `/setup`.
2. Create the admin account.
3. Log in at `/login`.
4. Add items at `/items/new`. Optionally fill in your Apify key and ntfy URL via `/settings` if you didn't put them in `config.toml`.
The scheduler starts with the server and polls each active item on its configured interval. The bottom-of-hour global poll runs every `scheduler.global_poll_interval_minutes`.
## Layout
```
main.go entry point: config, db open, scheduler, http server
internal/
config/ TOML config loading and validation
crypto/ AES-GCM encryption for secrets at rest
db/ SQLite schema, migrations, store
models/ domain types
apify/ Apify API client
ntfy/ ntfy push client
auth/ session + CSRF
scheduler/ poll loop, alert/dedup/badge logic
handlers/ HTTP handlers
templates/ templ components
static/ CSS, vendored htmx
```
## Test
```sh
go test ./...
```
Unit tests cover crypto round-trip, db round-trip and dedup, and scheduler alert/badge logic. No handler-level tests yet.
## Operate
- The SQLite file lives at `server.db_path` (default `./veola.db`). Back this up — it holds your watched items, history, encrypted secrets, and user accounts.
- The process responds to `SIGINT` / `SIGTERM` with a graceful HTTP shutdown (30s timeout) followed by scheduler stop.
- Logs go to stdout as structured `log/slog` records.
## Aesthetic
Sega Genesis blue. Not dark mode, not light mode — blue mode. See the visual design section of `veola-spec.md` for the palette.