Keitaro Remote Control frontend service
  • Python 50.8%
  • HTML 43.7%
  • JavaScript 4.2%
  • CSS 1%
  • Dockerfile 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
ilavir 70fb7a327b feat(health): improve ping error formatting and display
- Extract ping error formatting into dedicated helper function for consistency
- Handle upstream Keitaro error responses with fallback chain (response.error > response > payload.error)
- Prefix HTTP status codes to error messages when available
- Update flash message to show "OK" for successful pings instead of generic status
- Enhance error display in health table with conditional rendering based on response type
- Support both dict and string response payloads in template error rendering
- Improve error message clarity and user experience in health check workflows
2026-09-22 11:24:54 +03:00
app feat(health): improve ping error formatting and display 2026-09-22 11:24:54 +03:00
logs feat(logging): add comprehensive logging and observability 2026-05-11 13:27:41 +03:00
.dockerignore Initial frontend: Flask app with Bootstrap 5.3.8, instances CRUD, keys management, health dashboard 2026-05-11 13:15:10 +03:00
.env.example feat(logs): add instance logs viewer with tabbed interface and clean action 2026-08-13 13:09:05 +03:00
.gitignore feat(logs): add instance logs viewer with tabbed interface and clean action 2026-08-13 13:09:05 +03:00
config.py feat(logs): add instance logs viewer with tabbed interface and clean action 2026-08-13 13:09:05 +03:00
docker-compose.yml chore(docker): make app port configurable and expose container port 2026-05-28 15:57:52 +03:00
Dockerfile feat(downloads): implement configurable timeout and streaming enhancements 2026-08-11 17:12:27 +03:00
pyproject.toml feat(reports): expand reports module with campaigns, offers, and affiliate networks 2026-05-29 12:36:16 +03:00
pyrightconfig.json feat(campaigns): add tabbed campaign details modal with flows section 2026-07-01 13:26:33 +03:00
README.md feat(logs): add instance logs viewer with tabbed interface and clean action 2026-08-13 13:09:05 +03:00
run.py Initial frontend: Flask app with Bootstrap 5.3.8, instances CRUD, keys management, health dashboard 2026-05-11 13:15:10 +03:00
uv.lock style(ui): refactor layout to sidebar navigation with improved styling 2026-05-29 15:02:52 +03:00

Keitaro Remote Control — Frontend

Flask web UI for managing Keitaro tracker instances via the KRC backend API. Internal admin tool with server-rendered pages, Bootstrap 5.3.8, no build step.

Tech Stack

Layer Technology
Framework Flask 3.1.1
Templates Jinja2 + Bootstrap 5.3.8 (CDN)
HTTP Client httpx 0.28.1
Auth PyJWT 2.12.1 (long-lived token, nginx basic auth)
Package Manager uv
Production Server Gunicorn 25.3.0

Architecture

Browser → nginx (basic auth) → Flask Frontend :8350 → FastAPI Backend :8300 → MariaDB / Keitaro

The frontend generates a long-lived JWT at startup (superadmin role) and uses it for all backend API calls. Access control is handled by nginx basic auth — no login form.

Layout

  • Top navbar: global navigation (Instances, Reports, Health)
  • Left sidebar: appears on instance-scoped pages with instance name and sub-navigation
  • Full-width content: tables use the full viewport width for better readability

Modules

Page Path Description
Instances /instances CRUD for Keitaro tracker instances
Instance Detail /instances/<id> Instance overview with edit/delete
API Keys /instances/<id>/keys Manage API keys per instance
Campaigns /instances/<id>/campaigns Campaign list with filter, pagination, detail modal (tabbed: Info, Parameters, Postbacks, Flows)
Offers /instances/<id>/offers Offer list with filter, pagination, detail modal, download/upload
Landings /instances/<id>/landings Landing pages list with pagination, group enrichment, download/upload
Traffic Sources /instances/<id>/traffic-sources Traffic sources list
Affiliate Networks /instances/<id>/affiliate-networks Affiliate networks list
Users /instances/<id>/users Users list with detail modal
Domains /instances/<id>/domains Domains list
Reports (per-instance) /instances/<id>/reports/<type> Campaigns, offers, campaign-groups, affiliate-networks, landings, or traffic-sources report for the selected instance
Reports (global) /reports/ and /reports/<type> Same report types aggregated across every active instance into one table, with a per-row "Instance" column
Conversions /instances/<id>/conversions Conversions log with filters and pagination
Logs /instances/<id>/logs Instance logs viewer with tabs per log type, search, pagination, and clean action
Health /health Service status + ping all/single instances

Features

Campaigns & Offers

  • Server-side filter by ID (comma-separated, e.g. ?campaign_id=12,34)
  • Pagination (configurable page size via PAGE_SIZE env var)
  • Click any row to open a tabbed detail modal (Info, Parameters, Postbacks, Flows)
  • Flows tab: lazily loaded via AJAX, shows flow cards with landings, offers (linked), filters, triggers
  • Offers in flows: enriched with country, affiliate network, full local path (instance URL + offer path), and campaign domain; each offer has a "Copy to clipboard" button producing a tab-separated line for Excel paste, plus a last-7-days statistics table (clicks, unique clicks, cost, conversions, leads, rejected, sales, CR, approve, profit confirmed, ROI confirmed) from the reports API
  • Offer and landing names resolved from backend (cached)
  • Campaign IDs in reports link directly to the campaigns list (pre-filtered)
  • Domain and Traffic Source names resolved from their respective APIs
  • Campaign link generated from domain + alias (http/https based on domain SSL status)
  • Download button for local offers/landings (streams zip via backend from Keitaro)
  • Upload button for offers/landings (zip file, validated, base64-encoded to backend)

Landings

  • Paginated list with group name enrichment
  • Download landing archive (streamed zip from backend)
  • Upload landing archive (zip validation + size limit)

Reports

Reports have one shared implementation used by both scopes:

  • Per-instance (/instances/<id>/reports/<type>) — scoped to a selected instance, in the instance sidebar. Behavior unchanged from before: same report types, filters, defaults, links, formatting, and client-side header sorting.
  • Global (/reports/ and /reports/<type>) — top-level navbar entry. Queries every active instance sequentially (one request per instance) and combines successful rows into a single full-width table with a leading Instance column. Each Instance cell links to that instance's own per-instance report of the same type, carrying over the current interval/date range. Rows are initially sorted by unique clicks descending; clicking any column header (including Instance) re-sorts client-side exactly like per-instance reports. If one or more instances fail, a warning names each failed instance and the report still displays rows from the successful ones; if all fail, an error is shown with no rows.

Shared across both scopes:

  • Report types: campaigns, offers, campaign groups, affiliate networks, landings, traffic sources — same tabs, same order
  • Date interval selector (today, yesterday, last 7 days, this month, last month, this year, last year, custom)
  • Custom range: selecting "Custom" reveals from/to date pickers for an explicit date range
  • Campaign report: optional filter by campaign/campaign-group IDs; Offers/Landings reports: optional filter by their IDs
  • Clickable dimension IDs linking to entity list/report pages, resolved against the row's source instance
  • Backend row limit per request is configurable independently per scope (see Configuration)

Conversions Log

  • Date interval selector (same choices as reports, including custom range)
  • Filter by campaign IDs (comma-separated)
  • Filter by conversion status
  • Server-side pagination with total count
  • Columns: sub_id, campaign, campaign_id, campaign_group, offer, offer_id, landing_id, landing, ts, postback_datetime, status, country_code

Health

  • Service health check endpoint
  • Ping all instances at once
  • Ping individual instance from any page (via referrer redirect)

Local Development

Requires uv and the backend running on port 8300.

# Start backend first (from ../backend/)
cd ../backend
uv sync
uv run fastapi dev app/main.py --host 0.0.0.0 --port 8300

# Then start frontend
cd ../frontend
cp .env.example .env
# Edit .env — set JWT_SECRET_KEY to match backend's JWT_SECRET_KEY

uv sync
uv run python run.py

Frontend runs on http://localhost:8350.

Configuration

All config via environment variables (.env file supported).

Variable Required Default Description
JWT_SECRET_KEY Yes changeme Shared JWT secret (must match backend)
SECRET_KEY No dev-secret Flask session secret
BACKEND_URL No http://localhost:8300 Backend API base URL
APP_PORT No 8350 Port to listen on
PAGE_SIZE No 1000 Items per page for paginated lists
UPLOAD_MAX_SIZE_MB No 50 Max upload file size in MB (offers/landings)
UPLOAD_TIMEOUT_SECONDS No 120 Timeout for upload requests to backend
REPORT_ROW_LIMIT_PER_INSTANCE No 25 Backend row limit per report request in per-instance scope
REPORT_ROW_LIMIT_GLOBAL No 25 Backend row limit per report request (applied per instance) in global scope
LOGS_PAGE_SIZE No 100 Number of log rows per page
LOGS_CLEAN_ENABLED No true Enable/disable log clean buttons (false/0/no to disable)

Docker

# Standalone
docker build -t krc-frontend .
docker run -p 8350:8350 -e JWT_SECRET_KEY=your-secret -e BACKEND_URL=http://host:8300 krc-frontend

# With backend (docker-compose)
docker compose up -d

Project Structure

frontend/
├── app/
│   ├── __init__.py              # App factory + JWT generation
│   ├── dashboard/               # / → redirect to instances
│   ├── health/                  # Health dashboard
│   ├── instances/               # Instance CRUD
│   ├── keys/                    # API key management
│   ├── campaigns/               # Campaign list (paginated, filterable, detail modal)
│   ├── offers/                  # Offer list (paginated, filterable, detail modal, download/upload)
│   ├── landings/                # Landing pages (paginated, detail modal, download/upload)
│   ├── traffic_sources/         # Traffic sources list
│   ├── affiliate_networks/     # Affiliate networks list
│   ├── users/                   # Users list (detail modal)
│   ├── domains/                 # Domains list
│   ├── reports/                 # Report views: one shared implementation for per-instance and global scope
│   │   ├── definitions.py       # Immutable ReportDefinition per report type
│   │   └── orchestrator.py      # render_report(scope, report_type, instance_id=None)
│   ├── conversions/             # Conversions log (paginated, filterable)
│   ├── logs/                    # Instance logs viewer (tabbed, searchable, clean action)
│   ├── services/
│   │   └── api_client.py       # Backend HTTP client
│   ├── intervals.py            # Shared interval choices, get_interval() and build_range() helpers
│   ├── static/
│   │   ├── css/style.css       # Custom styles (sidebar layout)
│   │   └── js/interval-filter.js  # Toggles custom date fields for the interval selector
│   └── templates/
│       ├── base.html           # Layout (navbar, sidebar, Bootstrap 5.3.8)
│       ├── _custom_date_inputs.html  # Shared from/to date inputs for "Custom" interval
│       ├── campaigns/
│       ├── conversions/
│       ├── logs/
│       ├── domains/
│       ├── health/
│       ├── instances/
│       ├── keys/
│       ├── landings/
│       ├── offers/
│       ├── reports/
│       │   ├── report.html
│       │   ├── _tabs.html
│       │   ├── _filters.html
│       │   └── _table.html
│       ├── traffic_sources/
│       └── affiliate_networks/
├── config.py                    # Config class (env vars)
├── run.py                       # Dev entry point
├── pyproject.toml
├── uv.lock
├── Dockerfile
└── docker-compose.yml