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 same GOOGLE_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 Scan row (see Reports & persistence). JSON is served from GET /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.

  1. Open APIs & Services → Credentials, select your client (or create one).
  2. 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 use 127.0.0.1 everywhere, add that host instead and set DJANGO_PUBLIC_BASE_URL to match.
  3. Set GOOGLE_REDIRECT_URI to 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.
  4. 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 (signed state).
  • GET /api/auth/google/callback/ — Recommended redirect URI for GSC OAuth (must match GOOGLE_REDIRECT_URI). With webmasters scope, exchanges the code and stores tokens, then redirects to GSC_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 given site_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 params zone_id, optional days (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.