A crew-scheduling prototype for military flight operations. The app is now a React + FastAPI + SQLite stack designed to run on one VPS.
Less admin. More flying.
- React 19 + TypeScript 5.9 + Vite 7 + Tailwind CSS 4
- FastAPI + Pydantic, served by Uvicorn
- SQLite via Python's stdlib
sqlite3 vite-plugin-singlefile, sonpm run buildoutputs one self-containeddist/index.html
npm install
python3 -m pip install -r requirements.txt
npm run devLocal development runs FastAPI at 127.0.0.1:8000 and Vite at 127.0.0.1:5173. Vite proxies /api to FastAPI.
Useful commands:
npm run dev # FastAPI + Vite dev servers
npm run build # production React build -> dist/index.html
npm run preview # FastAPI serves built React + API
npx tsc --noEmit # TypeScript check
python3 -m compileall backend scriptsFastAPI owns persistence. React reads and writes through same-origin /api/* endpoints:
| Endpoint | Purpose |
|---|---|
GET /api/health |
Backend health check |
GET /api/state |
Load full scheduler state |
PUT /api/state |
Persist a full scheduler state replacement |
POST /api/reset |
Re-seed SQLite from backend/seed.json |
Local SQLite defaults to data/simplyfly.sqlite. Production should set:
SIMPLYFLY_DB_PATH=/var/lib/simplyfly/simplyfly.sqliteThe backend enables SQLite foreign keys, WAL mode, and a busy timeout. Run one Uvicorn worker for this single-VPS deployment.
- Install Node.js, Python 3.10+, and a reverse proxy such as Nginx or Caddy.
- Clone or copy the app to a release directory.
- Install dependencies and build React:
npm ci
python3 -m pip install -r requirements.txt
npm run build- Create the persistent database directory:
sudo mkdir -p /var/lib/simplyfly
sudo chown simplyfly:simplyfly /var/lib/simplyfly- Run the app with one Uvicorn process:
SIMPLYFLY_DB_PATH=/var/lib/simplyfly/simplyfly.sqlite \
python3 -m uvicorn backend.main:app --host 127.0.0.1 --port 8000- Point Nginx or Caddy at
127.0.0.1:8000and terminate HTTPS there.
Example systemd service:
[Unit]
Description=SimplyFly Flight Scheduler
After=network.target
[Service]
User=simplyfly
Group=simplyfly
WorkingDirectory=/opt/simplyfly
Environment=SIMPLYFLY_DB_PATH=/var/lib/simplyfly/simplyfly.sqlite
ExecStart=/usr/bin/python3 -m uvicorn backend.main:app --host 127.0.0.1 --port 8000
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.targetExample Nginx server block:
server {
listen 80;
server_name schedule.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Keep /var/lib/simplyfly outside deploy wipes. A simple backup can use SQLite's online backup command:
sqlite3 /var/lib/simplyfly/simplyfly.sqlite ".backup '/var/backups/simplyfly-$(date +%F).sqlite'"backend/
main.py FastAPI app, SQLite schema, seed/reset, static serving
seed.json Initial scheduler state
src/
App.tsx Root component, API loading/saving, auth state
store.ts API persistence helpers + uid()
types.ts Domain types + day-label utilities
components/ Login, flyer dashboard, admin scheduler, shared UI
scripts/
dev.py Starts FastAPI and Vite together
- Tailwind v4 uses CSS-first configuration — edit
@themeinsrc/index.css, notailwind.config.* @/path alias maps tosrc/(configured in bothtsconfig.jsonandvite.config.ts)vite-plugin-singlefileinlines all assets into the build output- No test framework or linter is configured; use the checks above before deploying
- The frontend no longer uses
localStorage; existing browser-local data is not migrated. src/mockData.tsremains ignored and is not required.- SQLite is suitable here because the deployment target is one VPS with one app process.