site

module
v0.0.85 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 6, 2026 License: MIT

README ΒΆ

Rocky Ads Web Site

Rocky Ads is classified ads for the Internet.

Newspaper classifieds

Remember classified ads? Easy, simple. Post an ad with your phone number and folks call you. Rocky Ads works the same way, except your phone number stays hidden.

β€ƒπŸš« No email
β€ƒπŸš« No Facebook friends
β€ƒπŸš« No posting fees
β€ƒπŸš« No credit cards

All you need is your phone number to get started.

Development

flowchart LR
  Browser -->|HTTP / HTMX| Site["Site<br/>Go / Fiber"]
  Browser -->|presigned PUT/GET| MinIO
  Browser -->|Turnstile| Cloudflare["Cloudflare<br/>Turnstile"]
  Site --> Postgres[(Postgres)]
  Site --> Redis[(Redis)]
  Site --> MinIO
  Site --> Ollama
  Site --> Twilio
  Site --> Cloudflare
  Site --> Grok["Grok API<br/>x.ai"]
  Site --> Geoapify
  Twilio -->|SMS webhooks| Site
Prerequisites
CSS (Tailwind)

The app uses Tailwind CSS v4 (listed in package.json; no need to install it separately). Install deps once, then build the stylesheet before or while running the server:

  1. npm install
  2. One-off build: npm run build-css-dev
    • Or watch mode (rebuild on changes): npm run build-css
    • Or production (minified): npm run build-css-prod

Input: input.css β†’ output: static/css/output.css.

Object storage (MinIO)

Ad images are stored in MinIO. The server requires MINIO_API_URL (and related credentials) to start.

  • MINIO_API_URL β€” S3 API endpoint used by the server (private-network host in production).
  • MINIO_PUBLIC_URL β€” browser-facing host used when minting presigned PUT/GET URLs (e.g. https://minio.rockyads.com or http://127.0.0.1:9000 locally). Defaults to MINIO_API_URL if unset.

Browsers upload WebP derivatives via short-lived presigned PUT URLs. Listing/detail pages embed reused long-lived presigned GET URLs so image bytes come from MinIO and remain browser-cacheable.

SMS (Twilio)

Phone verification (register / change-phone) uses Twilio Verify. Account recovery inbound SMS and unread-message alerts use Programmable Messaging. See doc/DOC_SMS_OTP_AND_PUMPING_DEFENSES.md.

Always required at server start:

  • PUBLIC_SITE_URL β€” public base URL for the site (non-localhost for real SMS). Used for Twilio webhooks (…/api/sms/webhook) and SMS deep links (e.g. …/auth/user/messages). Local: your ngrok (or similar) HTTPS URL.

Required at server start unless ALLOW_TEST_REGISTRATION=true:

  • TWILIO_ACCOUNT_SID β€” Twilio account SID (must start with AC).
  • TWILIO_AUTH_TOKEN β€” Twilio auth token.
  • TWILIO_FROM_NUMBER β€” Messaging sender in E.164 (e.g. +12025550123).
  • TWILIO_VERIFY_SERVICE_SID β€” Verify Service SID (must start with VA).
Turnstile (Cloudflare)

Bot gate before OTP start (register / change-phone). Required at server start unless ALLOW_TEST_REGISTRATION=true:

  • TURNSTILE_SITE_KEY β€” public site key (browser widget).
  • TURNSTILE_SECRET_KEY β€” secret key (server-side verification).
DB (Postgres)
  • DATABASE_URL β€” PostgreSQL DSN. Default: postgres://localhost:5432/rockyads?sslmode=disable. With docker-compose Postgres: postgres://postgres:postgres@localhost:5432/rockyads?sslmode=disable.
  • DB_ENCRYPTION_KEY β€” required. Base64-encoded 32-byte (256-bit) key for encrypting user name/phone.
  • DB_HASH_PEPPER β€” required. Base64-encoded 32-byte (256-bit) HMAC pepper for name_hash / phone_hash lookups. Must differ from DB_ENCRYPTION_KEY.
Redis
  • REDIS_URL β€” required. Redis URL for shared rate limits (registration/recovery/login IP + per-username login failures + per-phone OTP starts). Local: redis://localhost:6379 with docker compose up -d redis. Render: Key Value internal URL (e.g. redis://red-xxx:6379).

Inspect Redis:

# local compose (Redis container)
docker compose exec redis redis-cli

# jump server (rebuild image for redis-tools)
docker compose exec -it jump-server sh -c 'redis-cli -u "$REDIS_URL"'
# on Render SSH / shell (REDIS_URL from BASE):
redis-cli -u "$REDIS_URL"
LLM / geocoding APIs
  • GROK_API_KEY β€” xAI Grok API key (chat completions for rock opinions / AI features).
  • GEOAPIFY_API_KEY β€” Geoapify API key (forward geocoding for locations table cache misses).
  • OLLAMA_URL β€” Ollama HTTP endpoint for embeddings (nomic-embed-text). Default: http://localhost:11434.
  • FAL_API_KEY β€” fal.ai key for go run ./cmd/gen_images only (not required by the server).
JWT
  • JWT_SECRET β€” required. Secret for signing auth cookies; at least 32 characters.
Other settings
  • PORT β€” HTTP listen port. Default: 10000.
  • PORT_TEST β€” port used by integration tests. Default: 10001.
  • APP_NAME β€” display name. Default: Rocky Ads.
  • CONTACT_EMAIL β€” contact address shown in the UI. Default: contact@rockyads.com.
  • LOCAL_DEVELOPMENT β€” set to true so auth cookies work over plain HTTP (otherwise cookies are Secure-only).
  • ALLOW_TEST_REGISTRATION β€” set to true to skip SMS verification for +1555010xxxx phones and relax Twilio startup checks (dev/test only).
  • LOG_LEVEL β€” debug / info / … Default: info.
  • LOG_FORMAT β€” json or text. Default: json.
  • LOG_FILE β€” optional log file path; empty logs to stderr.
Local dev with docker-compose
docker compose up -d   # starts postgres, redis, minio, jump-server, ollama

Set in .env:

DATABASE_URL=postgres://postgres:postgres@localhost:5432/rockyads?sslmode=disable
REDIS_URL=redis://localhost:6379
DB_ENCRYPTION_KEY=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
DB_HASH_PEPPER=AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=
JWT_SECRET=local-dev-jwt-secret-key-min-32-chars
PUBLIC_SITE_URL=http://localhost:10000
LOCAL_DEVELOPMENT=true
ALLOW_TEST_REGISTRATION=true
MINIO_API_URL=http://127.0.0.1:9000
MINIO_PUBLIC_URL=http://127.0.0.1:9000
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=minioadmin
MINIO_BUCKET_NAME=rockyads
OLLAMA_URL=http://localhost:11434

REDIS_URL is required; start Redis via Compose before running the server.

Add Twilio (TWILIO_*), GROK_API_KEY, GEOAPIFY_API_KEY, and FAL_API_KEY when you need real SMS, Grok, location geocoding, or image generation. For real SMS locally, set PUBLIC_SITE_URL to an ngrok (or similar) HTTPS URL so Twilio can reach /api/sms/webhook.

Initialize the database via the jump-server admin TUI:

docker compose exec -it jump-server admin

Main menu: Database Β· Users Β· Embeddings. Under Database, choose Init database ([I]). User PII and promote/demote/delete live under Users (not the web admin dashboard).

Run the site (loads .env into the environment):

set -a && source .env && set +a
go run ./cmd/server

Open http://localhost:10000.

Directories ΒΆ

Path Synopsis
cmd
admin command
gen_images command
migrate_images command
quote_server command
server command
internal
ad
db
facet
Package facet defines the category-specific ad attributes (price, mileage, hours, year, condition, ...) in one typed registry.
Package facet defines the category-specific ad attributes (price, mileage, hours, year, condition, ...) in one typed registry.
kv
ui

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL