Monix
How everything fits together
This guide matches the repo: Django runs the scan engine and persists scan results in PostgreSQL, serves authenticated APIs (Supabase JWT, optional Google Search Console OAuth, optional Cloudflare API token), and the Next.js app is where you sign in to manage sites, scans, and integrations.
Overview
Monix analyzes a public URL and produces category scores (security, SEO, performance) plus an overall score. Each run is stored as a Scan row (JSON payload, score, expiry) so you can reopen reports and list history. The product surface is the authenticated dashboard: you sign in with Supabase, add monitored sites (targets), run scans, and browse results. Optionally connect Google Search Console for search analytics and Cloudflare for edge HTTP metrics when hostnames match your zones.
Using the product
- Sign in — The browser authenticates with Supabase Auth. The Next.js app sends
Authorization: Bearer <JWT>to Django; the API verifies the JWT (JWKS) and maps the user to a Django account. Optional Sign in with Google for the app uses social-auth (/api/auth/login/google-oauth2/) and the shared callback/api/auth/google/callback/. Search Console OAuth uses the same client and typically the sameGOOGLE_REDIRECT_URI; Django routes webmasters scope to the GSC token handler (api_auth_google_callback_compat). - Overview — The dashboard home summarizes activity, average scores, Search Console rollups when connected, and Cloudflare edge totals when a monitored hostname matches a zone on your API token.
- Analytics — Search Console tables (per-target summaries, top queries, sync) plus Cloudflare edge charts when connected.
- Sites — Monitored URLs (targets). Add a site, run scans, and link Search Console properties when the URL matches a verified property. Cloudflare metrics appear automatically when the hostname matches a zone on your token—no per-site toggle.
- Integrations — Dashboard → Integrations for Google Search Console and Cloudflare connection status.
- Scan history — Lists completed scans; open a row to view the full report for that run.
- Reports — Each scan is persisted as a
Scanrow (see Reports & persistence). JSON is served fromGET /api/reports/<uuid>/(shareable until expiry). The app also surfaces reports under/dashboard/report/<id>. - Profile & settings — Account and preferences from the sidebar.
Google OAuth (Console setup)
If Google shows does not comply with Google's OAuth 2.0 policy or asks you to register the redirect URI, fix it in Google Cloud Console — not in Monix code. Use one OAuth 2.0 Client ID with type Web application.
- Open APIs & Services → Credentials, select your client (or create one).
- Under Authorized redirect URIs, click Add URI and paste exactly (local Django; includes trailing slash). Sign-in and Search Console share this path:
http://localhost:8000/api/auth/google/callback/If you use127.0.0.1everywhere, add that host instead and setDJANGO_PUBLIC_BASE_URLto match. - Set
GOOGLE_REDIRECT_URIto that same callback URL so GSC token exchange matches. You do not need a separate/api/gsc/callback/unless you configure it that way yourself. - Under OAuth consent screen: if publishing status is Testing, add your Google account under Test users. Save and wait a minute before retrying.
Google Search Console
Monix can read Search Analytics (read-only) and list your verified sites using Google's OAuth 2.0 flow. Refresh tokens are stored encrypted in PostgreSQL; the browser never holds long-lived secrets. This is separate from signing in with Google for Monix itself—GSC uses its own consent screen and scopes (webmasters.readonly, plus standard OpenID profile scopes).
What you see in the app
- Overview — Totals and charts built from cached per-target metrics when at least one target has synced data.
- Analytics — Full table of targets with property URLs, sync errors, summary numbers, and top queries.
- Matching — For each target URL, Django picks a Search Console property you have verified (URL-prefix or domain) that matches the target host. If nothing matches, the target records a clear sync message instead of failing the rest of the app.
When data syncs
- After you connect GSC, creating a new target triggers a sync attempt for that target (server-side).
- Use Sync Search Console on Analytics (or the equivalent API) to re-fetch metrics for every target—useful right after connecting or when you add properties in Google.
Django API routes
GET /api/gsc/connect/— Returns JSON with a Google authorization URL (signedstate).GET /api/auth/google/callback/— Recommended redirect URI for GSC OAuth (must matchGOOGLE_REDIRECT_URI). Withwebmastersscope, exchanges the code and stores tokens, then redirects toGSC_OAUTH_SUCCESS_URL/ error URL.GET /api/gsc/callback/— Alternate handler with the same logic if you register this path in Google Cloud instead.GET /api/gsc/status/— Whether the current user has stored credentials.GET /api/gsc/sites/— List verified sites (for debugging or future UI).POST /api/gsc/analytics/— On-demand analytics for a givensite_url(JSON body; optional date range).POST /api/gsc/sync-targets/— Re-sync all targets for the user.POST /api/gsc/disconnect/— Remove stored Search Console tokens for the current user.
Register the redirect URI in Google Cloud exactly as GOOGLE_REDIRECT_URI. The default in .env.example is http://localhost:8000/api/auth/google/callback/. Success and error browser redirects after OAuth use GSC_OAUTH_SUCCESS_URL and GSC_OAUTH_ERROR_URL (defaults point at the Next.js app with query flags).
Cloudflare
Users paste a Cloudflare API token in the app. Django verifies it, encrypts it with the same Fernet machinery as GSC tokens, and calls Cloudflare API v4 (REST for zones and token verify; GraphQL for zone HTTP request analytics). No Cloudflare secrets live in the browser.
Create a custom token with Zone → Zone → Read and Zone → Analytics → Read for the zones you need. The dashboard matches each monitored site hostname to a zone on that token and rolls up requests, cached bytes, threats, and country breakdowns on Overview, Sites, Analytics, and Issues.
Django API routes
GET /api/cloudflare/status/— Connection status and account summary.POST /api/cloudflare/connect/— JSON body{ "api_token": "…" }; verifies and stores the token.DELETE /api/cloudflare/disconnect/— Remove stored credentials.GET /api/cloudflare/zones/— List zones visible to the token.GET /api/cloudflare/analytics/— Query paramszone_id, optionaldays(default 7).
Architecture
Browser (Next.js)
│
└─► Django :8000 — Supabase JWT auth, scan engine, targets, scans,
reports API, GSC OAuth, Cloudflare token proxy
PostgreSQL — DATABASE_URL; stores User, Target, Scan, integration credentials.
Google APIs — Search Console via stored OAuth refresh tokens.
Cloudflare — api.cloudflare.com (zones, GraphQL HTTP analytics) via stored API token.When you trigger a scan from the authenticated app, Django validates the bearer token, associates the run with a target when provided, and runs the scan engine in-process. Results are written through reports.persistence.save_scan_result (see Reports & persistence below).
Reports & persistence
Scan outcomes are stored in the reports.Scan model: a unique report_id (UUID), the scanned URL, composite score, full results JSON from the engine, optional target FK, and expires_at / is_expired for shareable report lifetime (default TTL 30 days from persistence.save_scan_result).
GET /api/reports/<uuid>/ returns the JSON payload for non-expired scans (used for public sharing and the in-app report viewer). List endpoints under /api/scans/ return metadata for the signed-in user's targets.
Scan engine
The core/scan_engine/ package hosts scan orchestration, enrichment, and scoring (invoked from Django views). Tasks such as TLS, DNS, headers, redirects, cookies, geo, optional port checks, and—when configured—PageSpeed run and feed into category scores. The response model includes findings, recommendations, summaries, and raw fields the UI renders in tables and panels.
Django reports & auth
The core/ project owns models for targets, scans, and reports; exposes REST-style JSON under /api/; and includes the admin UI at /admin/. CORS is configured so the browser can call Django directly from the Next.js origin. Google Search Console and Cloudflare integration endpoints live here (/api/gsc/*, /api/cloudflare/*). Admin login can be rate-limited via django-axes using AXES_* in environment.
Next.js web app
The web/ app uses the App Router. API calls are centralized in src/lib/api.ts: Django base URL from NEXT_PUBLIC_DJANGO_URL or legacy NEXT_PUBLIC_API_URL (defaults to http://localhost:8000). Do not proxy browser calls through Next.js /api/* routes in a way that fights Django's trailing-slash behavior—the client calls Django directly.
What gets analyzed
Security
- —TLS / certificate validity
- —Security headers and cookie flags
- —DNS and host intelligence
- —Ports, redirects, tech fingerprint
- —Geo / IP context
SEO
- —Title, meta description, Open Graph
- —robots.txt and sitemap signals
- —Canonical and heading structure
- —Optional: GSC clicks, impressions, queries (OAuth)
- —Optional: Cloudflare edge requests & threats (API token)
Performance
- —Google PageSpeed when an API key is set
- —Core Web Vitals and lab metrics
- —Accessibility and best-practices scores
Configuration
See repository .env.example for the full list. Commonly:
- DATABASE_URL
- PostgreSQL URL for Django.
- DJANGO_SECRET_KEY
- Required for Django sessions and signing.
- PAGESPEED_API_KEY
- Optional; improves PageSpeed Insights rate limits.
- GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET
- OAuth client for Google login and Search Console; create in Google Cloud Console.
- DJANGO_PUBLIC_BASE_URL
- Canonical origin of Django (no trailing slash). With default path, sign-in redirect is …/api/auth/google/callback/. In DEBUG defaults to http://localhost:8000.
- GOOGLE_LOGIN_REDIRECT_PATH
- Optional path segment for sign-in only (default /api/auth/google/callback/). Must match Authorized redirect URIs.
- GOOGLE_REDIRECT_URI
- Full URL for GSC OAuth code exchange — use the same …/api/auth/google/callback/ as sign-in when sharing one Console URI.
- GSC_OAUTH_SUCCESS_URL / GSC_OAUTH_ERROR_URL
- Where the browser lands after GSC OAuth (defaults: Next.js Projects page with query flags).
- GOOGLE_REFRESH_TOKEN_FERNET_KEY
- Optional Fernet key for stored GSC refresh tokens and Cloudflare API tokens; if unset, a key is derived from DJANGO_SECRET_KEY.
Local development
From the repo root: create a Python venv, install requirements, copy .env.example to .env, run Django in core/ with migrations, and run the Next.js dev server in web/ with Bun. Use pytest from the repo root for backend tests; bun run build for the frontend. If your team uses setup.sh, follow that for the exact sequence.