QAIra is a QA management workspace with a Fastify backend, a React frontend, and PostgreSQL for persistence.
This repo is now PostgreSQL-first. SQLite is no longer part of the runtime, which avoids the read-only database file issues that were happening with mounted .db files.
- Backend: Fastify on Node.js
- Frontend: React + Vite
- Database: PostgreSQL 16
- Containers: Docker Compose
backend/api: Fastify APIbackend/db/schema.sql: PostgreSQL schema loaded on first database bootbackend/db/seed.sql: sample data loaded on first database bootbackend/docker-compose.yml: backend + PostgreSQL onlydocker-compose.full.yml: full stack with PostgreSQL, API, and frontenddocker-compose.platform.yml: full stack plus HAProxy, Prometheus, Grafana, Loki, Promtail, and OpenTelemetry Collectorfrontend: React apptestengine: standalone Playwright worker planeopenapi.yaml: API contract
Start everything from the repo root:
./qaira.sh up --stack fullOpen the apps:
- Frontend:
http://localhost:8080 - API:
http://localhost:3000 - PostgreSQL:
localhost:5432
On the first boot, the Postgres container creates the qaira database and automatically runs:
backend/db/schema.sqlbackend/db/seed.sql
Use qaira.sh as the single root entrypoint for service relation config, image publishing, and compose deployment:
./qaira.sh config --stack full
./qaira.sh push
./qaira.sh deploy --stack platform
./qaira.sh deploy --stack testengine --yes./qaira.sh config writes the resolved service contract to .qaira/<stack>.env. Override any endpoint or image with environment variables or --config <file>:
QAIRA_PUBLIC_URL=https://qaira.example.com \
QAIRA_API_BASE_URL=https://qaira.example.com/api \
./qaira.sh deploy --stack platformThe deploy command prints the target stack, compose file, frontend URL, API URLs, and Test Engine URL before it changes containers. Use --yes only from automation.
For AWS EC2 hosts, prefer the scripts in deploymentscripts:
deploymentscripts/aws-app-deploy.sh
QAIRA_API_BASE_URL=https://qaira.qualipal.in/api deploymentscripts/aws-testengine-deploy.sh
deploymentscripts/aws-status.sh
deploymentscripts/aws-logs.sh --stack appRun the backend and PostgreSQL without the frontend:
cd backend
docker pull postgres:16-alpine
docker compose up --buildYou can also use:
cd backend
./start.shThe API will be available at http://localhost:3000.
From the repo root, target services through the same operator script:
./qaira.sh up --stack full postgres api
./qaira.sh up --stack full frontend
./qaira.sh up --stack testengine testengineFor the stronger Docker edge and observability layer:
docker compose -f docker-compose.platform.yml up -dThat stack adds:
- HAProxy on
http://localhost - Prometheus on
http://localhost:9090 - Grafana on
http://localhost:3001 - Loki on
http://localhost:3100 - HAProxy stats on
http://localhost:8404/stats
If port 80 is already used on the host, publish HAProxy on another host port:
QAIRA_HTTP_PORT=8081 docker compose -f docker-compose.platform.yml up -dMore detail is in PLATFORM_STACK.md.
If you want to run Postgres yourself instead of using Compose, these are the explicit steps.
- Pull the image:
docker pull postgres:16-alpine- Start PostgreSQL:
docker run --name qaira-postgres \
-e POSTGRES_DB=qaira \
-e POSTGRES_USER=qaira \
-e POSTGRES_PASSWORD=qaira \
-p 5432:5432 \
-d postgres:16-alpine- Load the schema:
PGPASSWORD=qaira psql -h localhost -U qaira -d qaira -f backend/db/schema.sql- Load the sample data:
PGPASSWORD=qaira psql -h localhost -U qaira -d qaira -f backend/db/seed.sql- Run the API locally:
cd backend/api
npm install
DATABASE_URL=postgresql://qaira:qaira@localhost:5432/qaira npm start- Run the frontend locally in another terminal:
cd frontend
npm install
VITE_API_BASE_URL=http://localhost:3000 npm run devThe seed file creates these users:
admin@testiny.ai/admin123member@testiny.ai/member123
DATABASE_URLExample:postgresql://qaira:qaira@postgres:5432/qairaSESSION_SECRETCORS_ORIGINLOG_LEVEL
POSTGRES_DBPOSTGRES_USERPOSTGRES_PASSWORD
QAIRA_API_BASE_URLfor the container build/runtime config Example:http://localhost:3000fordocker-compose.full.yml, or/apiwhen the frontend and API share the same public host behind HAProxy or a load balancerVITE_API_BASE_URLfor local Vite developmentQAIRA_BACKEND_IMAGEto override the backend Docker Hub imageQAIRA_FRONTEND_IMAGEto override the frontend Docker Hub image
- The official
postgres:16-alpineimage creates the database defined byPOSTGRES_DB. - The mounted SQL files in
backend/dbare executed only when the Postgres data directory is empty. - Sample data is inserted from
backend/db/seed.sql, so the backend and frontend have working starter content immediately.
If you want a fresh database and fresh seed data:
From the repo root:
docker compose -f docker-compose.full.yml down -v
docker compose -f docker-compose.full.yml pull
docker compose -f docker-compose.full.yml up -dFrom the backend folder:
docker compose down -v
docker compose up --builddocker-compose.full.ymlnow uses Docker Hub images by default:- Backend:
jayarajumetta/qaira-backend:latest - Frontend:
jayarajumetta/qaira-frontend:latest - The frontend is configured to talk to
http://localhost:3000by default in local Docker workflows. - The schema in
backend/db/schema.sqlis ordered for PostgreSQL foreign key creation, andseed.sqluses PostgreSQL boolean values.