RequestHub is a public request intake system. Users submit bug reports, configuration changes, or feature requests through a public web form; the backend checks the request against existing tickets for likely duplicates and, if it's not a duplicate, files it as a Jira issue.
- A user fills out the public form (type, title, description, email, school) and answers a simple captcha.
- The API pulls recent tickets of the same type and asks an LLM (via the OpenAI API) whether the new request duplicates one of them.
- If a duplicate is found above the configured confidence threshold, the API returns
409 Conflictwith the matching ticket's key instead of creating a new one. - Otherwise, the API creates a Jira issue (mapped to Bug/Task/Story depending on the request type) and stores the ticket and submission in Postgres.
backend/ FastAPI service (SQLAlchemy models, Alembic migrations, Jira & duplicate
detection integrations, tests)
frontend/ React + Vite public request form
docker-compose.yml Local Postgres instance
- Backend: FastAPI, SQLAlchemy, Alembic, Postgres, httpx
- Frontend: React 19, TypeScript, Vite, Axios
- Integrations: Jira REST API (issue creation), OpenAI API (duplicate detection)
docker compose up -dThis starts Postgres on localhost:5433 (user/password/db: requesthub).
cd backend
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtCreate a .env file in backend/ with:
DATABASE_URL=postgresql://requesthub:requesthub@localhost:5433/requesthub
JIRA_BASE_URL=https://your-domain.atlassian.net
JIRA_EMAIL=you@example.com
JIRA_API_TOKEN=your-jira-api-token
JIRA_PROJECT_KEY=REQ
OPENAI_API_KEY=your-openai-api-key
OPENAI_MODEL=gpt-4o-mini
DUPLICATE_CONFIDENCE_THRESHOLD=0.7
ADMIN_USERNAME=admin
ADMIN_PASSWORD_HASH=your-argon2-password-hash
JWT_SECRET_KEY=a-long-random-secret
# Only needed in production, to allow the deployed frontend to call this API.
FRONTEND_URL=https://your-app.vercel.appRun migrations and start the API:
alembic upgrade head
uvicorn app.main:app --reloadThe API is available at http://localhost:8000 (health check at /api/health).
cd frontend
npm install
npm run devThe app is available at http://localhost:5173 by default.
The simplest setup for a demo/test deployment: Railway for the API + Postgres, Vercel for the frontend.
- Create a new Railway project from this GitHub repo, root directory
backend/. - Add a Postgres service to the same project — Railway injects
DATABASE_URLautomatically. - Set the rest of the environment variables from the list above (
JIRA_*,OPENAI_*,ADMIN_*,JWT_SECRET_KEY) in the service's Variables tab. - Railway uses the repo's
Procfile, which runs migrations before starting the server on every deploy:web: alembic upgrade head && uvicorn app.main:app --host 0.0.0.0 --port $PORT - Once deployed, note the public URL Railway gives you (e.g.
https://requesthub-api.up.railway.app) — the frontend needs it. - Set
FRONTEND_URLto the Vercel URL from the next step, so CORS allows it.
- Import the repo into Vercel, root directory
frontend/. It auto-detects the Vite build. - Set the environment variable
VITE_API_URLto the Railway backend URL from above. - The repo's
vercel.jsonadds the SPA rewrite so client-side routes (like/admin/login) work on refresh.
cd backend
pytestCreates a new request. Returns the created ticket (201), or a 409 with the matching
ticket_key and confidence if it looks like a duplicate of an existing ticket.
Basic health check.
Authenticates with username/password and returns a JWT access_token for the
admin endpoints below.
Requires a bearer token. Basic auth check / welcome payload.
Requires a bearer token. Paginated, filterable list of tickets, with each ticket's
submission count. Query params: page, limit, request_type, school, title,
sort (newest (default), oldest, or requests).
Requires a bearer token. Full detail for a single ticket, including its Jira URL and
the list of requesters (email, school, submission date) who filed it. Returns 404
if the ticket doesn't exist.