Secure REST API for the Insighta demographic intelligence platform.
insighta-backend/
├── app/
│ ├── routers/ # HTTP route handlers only
│ ├── services/ # Business logic (profiles, github, external APIs)
│ ├── core/ # Shared utilities (tokens, users, countries)
│ ├── middleware/ # Logging, rate limiting
│ ├── dependencies/ # FastAPI dependency injection
│ └── models.py # SQLAlchemy ORM models
├── scripts/ # Admin promotion script
└── tests/
Three tables: profiles, users, refresh_tokens.
- User clicks "Continue with GitHub"
- Backend redirects to
GET /auth/github→ GitHub OAuth page - GitHub redirects to
GET /auth/github/callback - Backend exchanges code, creates/updates user, issues token pair
- Backend sets HTTP-only cookies (
access_token,refresh_token) - Redirects to
http://localhost:3000/auth/callback
- CLI generates
state,code_verifier,code_challenge - CLI starts local HTTP server on
localhost:9876 - CLI calls
GET /auth/github?state=...&code_challenge=...&code_verifier=...&redirect_uri=http://localhost:9876/callback - Backend stores
code_verifierkeyed bystate - After GitHub auth, backend detects
cli_redirect_uri, redirects to CLI server with tokens in query params - CLI reads tokens, stores in
~/.insighta/credentials.json
| Token | Type | Expiry | Storage |
|---|---|---|---|
| Access token | JWT (HS256) | 3 minutes | Browser cookie / CLI file |
| Refresh token | Opaque (SHA-256 hashed in DB) | 5 minutes | Browser cookie / CLI file |
- Refresh tokens are single-use — revoked immediately on use, new pair issued
- Access tokens carry
sub(user ID),role,username - Expired access token → auto-refresh attempted → re-login if refresh also expired
Two roles: admin and analyst (default).
| Action | Admin | Analyst |
|---|---|---|
| List profiles | ✓ | ✓ |
| Search profiles | ✓ | ✓ |
| Get profile | ✓ | ✓ |
| Export CSV | ✓ | ✓ |
| Create profile | ✓ | ✗ |
| Delete profile | ✓ | ✗ |
| Promote users | ✓ | ✗ |
Enforced via FastAPI dependency injection:
CurrentUser— any authenticated userAdminUser— admin role required
Disabled accounts (is_active = false) receive 403 on all requests.
Rule-based only. No LLMs.
Gender: male, males, man, men, boy, boys, female, females, woman, women, girl, girls
Age groups: child, children, kid, kids, teenager, teenagers, teen, teens, adult, adults, senior, seniors, elderly, old
Age ranges:
young/youth→ min_age=16, max_age=24above N/over N/older than N→ min_age=Nbelow N/under N/younger than N→ max_age=Nbetween N and M→ min_age=N, max_age=M
Countries: Full country names matched against a dictionary (longest match wins to avoid "niger" matching inside "nigeria")
| Query | Filters |
|---|---|
young males from nigeria |
gender=male, min_age=16, max_age=24, country_id=NG |
adult females above 30 |
gender=female, age_group=adult, min_age=30 |
people from guinea-bissau |
country_id=GW |
seniors under 80 |
age_group=senior, max_age=80 |
- No stemming — "male" matches but "masculine" does not
- Country matching is name-based only, not ISO code
- Queries with no recognizable tokens return
400 Unable to interpret query - No negation support ("not from nigeria")
- No OR logic ("males or females")
All /api/* endpoints require:
Authorization: Bearer <access_token>X-API-Version: 1
| Method | Path | Auth |
|---|---|---|
| GET | /auth/github |
None |
| GET | /auth/github/callback |
None |
| POST | /auth/refresh |
None |
| POST | /auth/logout |
None |
| GET | /auth/me |
Any |
| Method | Path | Role |
|---|---|---|
| GET | /api/profiles |
Any |
| POST | /api/profiles |
Admin |
| GET | /api/profiles/search |
Any |
| GET | /api/profiles/export |
Any |
| GET | /api/profiles/{id} |
Any |
| DELETE | /api/profiles/{id} |
Admin |
| Method | Path | Role |
|---|---|---|
| PATCH | /api/admin/users/{id}/promote |
Admin |
| Scope | Limit |
|---|---|
/auth/* |
10 req/min |
| All other endpoints | 60 req/min per user |
git clone https://github.com/muizzyranking/insighta-backend
cd insighta-backend
uv sync
cp .env.example .env # fill in values
uv run python -m app.seed
uv run fastapi dev app/app.pySECRET_KEY=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GITHUB_REDIRECT_URI=http://localhost:8000/auth/github/callback
DATABASE_URL=sqlite+aiosqlite:///./insighta.db
APP_ENV=developmentuv run python scripts/make_admin.py your-github-usernameuv run pytest tests/ -v