2026-05-13 19:42:49 -07:00
2026-05-13 19:42:49 -07:00
2026-05-13 19:42:49 -07:00
2026-05-13 19:42:49 -07:00
2026-05-13 19:42:49 -07:00
2026-05-13 19:42:49 -07:00
2026-05-13 19:42:49 -07:00
2026-05-13 19:42:49 -07:00
2026-05-13 19:42:49 -07:00
2026-05-13 19:42:49 -07:00
2026-05-13 19:42:49 -07:00
2026-05-13 19:42:49 -07:00

Veola

Self-hosted Go web app that tracks items across e-commerce platforms (eBay, Amazon family, Yahoo Auctions JP, Mercari JP) via the Apify scraping API and pushes deal alerts to a self-hosted ntfy 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 for the full specification.

Requirements

  • Go 1.22+ (developed against 1.25)
  • An Apify account + API key
  • A reachable ntfy instance (self-hosted or ntfy.sh)

Build

go build -o veola-bin .

The binary is named veola-bin rather than veola because the module is also veolago build cannot write a binary with the same name as the module dir.

If you change any .templ files, regenerate first:

~/go/bin/templ generate

Configure

Copy the example and edit:

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:

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

./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

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.

Description
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.
Readme 573 KiB
Languages
Go 67%
templ 21.4%
CSS 7.3%
JavaScript 3%
Makefile 1.3%