Symfony backend and React frontend for a CQRLOG-style web UI. The backend preserves and normalizes some legacy field names used by the original desktop application. A few of those names are more than 20 years old and are somewhat obsolete today, but they are kept for compatibility with the existing data model. Most of the code was created with help from OpenAI Codex and Claude.
- PHP 8.2+ / Symfony 7
- React + TypeScript + Vite
- MariaDB
- Docker Compose for local development and production packaging
- Docker with Compose
- Node.js 22+ and npm
Start the Symfony app and MariaDB:
make devThis starts:
- app on
http://localhost:4000 - MariaDB on
localhost:23306
The development database is initialized from schema.sql.
In a second terminal:
cd frontend
npm install
npm run devThen open the URL printed by Vite, usually:
http://localhost:5173
The Vite dev server proxies /api/* to the Symfony app running on port 4000.
Radio sync expects a JSON API compatible with ok2cqr/pico-radio-gateway.
make dev-down
make dev-logs
make testmake dev-downstops the local Docker stackmake dev-logstails development logsmake testresets the local test database and runs backend PHPUnit tests
Production uses one Docker image for the Symfony app and built frontend. The container expects an existing MariaDB database with the required CQRLOG schema.
The production container:
- builds the frontend from
/frontend - copies the built assets into
/public - serves frontend and API from the same origin
- Docker with Compose
- reachable MariaDB instance with the required schema already imported
- valid production values for
APP_SECRET, database access, and app URL - outbound HTTPS access to
www.hamqth.comfor DXCC lookups
Create the production env file:
cp .env.prod.example .env.prodFill in at least:
APP_SECRETAPP_PORTDEFAULT_URILOGIN_USERNAMELOGIN_PASSWORDDB_HOSTDB_PORTDB_NAMEDB_USERDB_PASSWORD
APP_PORT is the host port published by Docker for the app container, for example 8080.
DEFAULT_URI should be the public base URL of the app, for example https://log.example.com. Symfony uses it as the default host and scheme when generating absolute URLs outside a normal HTTP request.
The DB_* values should point to the same MariaDB database used by the desktop CQRLOG application.
Optional:
SESSION_IDLE_TIMEOUT_SECONDS- session idle timeout in seconds (0 = no timeout)FRONTEND_RADIO_SYNC_DEFAULT_URLFRONTEND_RADIO_SYNC_DEFAULT_POLL_INTERVAL_SECONDS
Radio sync expects a JSON API compatible with ok2cqr/pico-radio-gateway.
make prodThis is equivalent to:
docker compose --env-file .env.prod -f docker-compose.prod.yml up -d --buildUseful production commands:
make prod
make prod-logs
make prod-down
make prod-configCheck the container:
docker compose --env-file .env.prod -f docker-compose.prod.yml psCheck the health endpoint:
curl -fsS http://127.0.0.1:8080/api/healthIf you run the app behind Apache or another reverse proxy, publish your public hostname there and proxy traffic to 127.0.0.1:8080.
Example:
<VirtualHost *:80>
ServerName log.yourdomain.com
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>.env.prodis ignored by Git and should stay local to the server.- The app does not create the production schema for you.
- Back up the MariaDB database, not only the container image.
- If HamQTH is unreachable,
/api/dxccwill return502.