103 lines
3.3 KiB
Markdown
103 lines
3.3 KiB
Markdown
# 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.
|