S Downshiftr Open the app

App Developer Docs

Setup & Running

The app is split into two processes: a Hono backend (port 3001) and a Vite dev server for the Vue frontend (port 5173). In development you run both; in production the backend serves the built frontend as static files.

Prerequisites

  • Node.js 20+
  • An OpenAI API key
  • (Optional) SMTP credentials for real magic-link emails — in development, links are printed to the terminal

First-time setup

# 1 — Install backend dependencies
cd backend
npm install

# 2 — Configure environment
cp .env.example .env
# Open .env and at minimum set:
#   OPENAI_API_KEY=sk-...
#   OPENAI_MODEL=gpt-5-mini   (or whatever current model you want)

# 3 — Install frontend dependencies
cd ../frontend
npm install

Running in development

From the repo root:

./dev.sh

This starts both processes. Frontend hot-reloads are proxied transparently through Vite to the backend on port 3001.

Or run them separately in two terminals:

# Terminal 1
cd backend && npm run dev

# Terminal 2
cd frontend && npm run dev

Open http://localhost:5173.

Real SMTP is not required locally. When NODE_ENV is not production, the backend logs the magic link URL directly to the terminal instead of sending an email:

[dev] Magic link for you@email.com :
 http://localhost:3001/api/auth/magic?token=abc123...

Click that URL in your terminal to complete sign-in.

Running the test suites

# Backend integration tests
cd backend && npm test

# Frontend component tests
cd frontend && npm test

Tests use an in-memory SQLite database and mock both email sending and OpenAI calls — no environment variables required.

Production build

cd frontend && npm run build       # outputs to frontend/dist/
cd ../backend && NODE_ENV=production npm start

The backend serves frontend/dist/ as static files and handles all /api/* routes. A single process on a single port.

Deployment targets

Production runs on Hostinger and uses the root sulcideploy.sh helper.

Fly.io is still supported from both the main local clone and the remotedev clone:

# Fly app from fly.toml
flyctl deploy

# Fly dev app from fly.dev.toml
flyctl deploy -c fly.dev.toml

The Fly Docker build is intentionally separate from the Hostinger install flow. Hostinger uses the root postinstall and committed build artifacts; Fly builds backend dependencies, the Vue frontend, and the Astro site inside Docker.

Environment variables

All variables live in backend/.env. See backend/.env.example for the full list.

VariableRequiredDefaultNotes
PORTno3001Backend listen port
DB_PATHno./sulci.dbSQLite file path. Use :memory: for tests.
OPENAI_API_KEYyesYour OpenAI secret key
OPENAI_MODELnogpt-5-miniModel sent to the completions API
APP_URLnohttp://localhost:3001Base URL for magic-link emails. Must be the public URL in prod (https://downshiftr.com) or links point at localhost.
COOKIE_SECUREnofalseSet to true in production (HTTPS only)
MAIL_HOST / MAIL_PORT / MAIL_USER / MAIL_PASSnoSMTP for real email. Read first; falls back to SMTP_HOST/SMTP_PORT/SMTP_USER/SMTP_PASS.
SMTP_FROM / SMTP_FROM_NAMEnoSender address and display name
VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEYnoWeb Push keys. Generate once: node -e "console.log(require('web-push').generateVAPIDKeys())". Blank = push disabled (subscribe/test/tick no-op).
VAPID_SUBJECTnomailto:admin@downshiftr.comContact URI sent to push services
CRON_SECRETnoShared secret for the notification cron. The cron calls GET /api/push/tick with header x-cron-key: $CRON_SECRET.

Prod env on Hostinger: values are injected at git-deploy time from .builds/config/.env (set via the hPanel Node env UI). Editing that file + restarting does not update the live process — push a deploy to apply env changes.