Skip to content

翻译进行中

在中文翻译完成之前,本页面会自动镜像英文原版。内容暂为英文,但导航和界面已完成翻译。

Architecture

A reference for the moving parts. Useful before you self-host, before you contribute, and before you debug anything operational.

Production service shape

Veytics runs three backend roles from one versioned application image:

text
veytics.com -------> OSINT / Core API -----> markets, aircraft, vessels, users
       |
       +-----------> News Aggregator ------> sources, canonical stories, AI copy

admin.veytics.com -> Admin Console
                       +-> Core admin API
                       +-> Aggregator admin API
                       +-> News Publisher --> Telegram, Threads, X, Meta, Bluesky

News Aggregator -> transactional outbox -> Redis Stream -> consumers
All services ----> PostgreSQL (shared during the first migration phase)

docs.veytics.com remains an independent static VitePress application. The combined backend shown below remains available as ATLAS_SERVICE_ROLE=all for local development and rollback.

Legacy all-in-one compatibility shape

                        ┌─────────────────┐
                        │   User browser  │
                        │  React + Cesium │
                        └────────┬────────┘
                                 │  HTTPS (REST, ~30s polling)

        ┌────────────────────────────────────────────┐
        │   FastAPI backend (uvicorn)                │
        │   ┌──────────────┐                          │
        │   │ HTTP routes  │                          │
        │   └──────┬───────┘                          │
        │          │                                  │
        │   ┌──────▼──────────────────────────┐       │
        │   │ Services layer (httpx clients)  │       │
        │   └──┬──────┬──────┬───────┬──────┬─┘       │
        └──────┼──────┼──────┼───────┼──────┼─────────┘
               │      │      │       │      │
               ▼      ▼      ▼       ▼      ▼
           EODHD  Finnhub  OpenSky  AISstream  Stripe

           ┌─────────────┐   ┌──────────┐
           │ PostgreSQL  │   │  Redis   │
           └─────────────┘   └──────────┘

Components

Frontend — React + Vite (frontend/)

  • Stack: React, Vite, plain JavaScript (no TypeScript), Tailwind, React Router.
  • Entry: frontend/src/App.jsx.
  • Workspace root: OSINTDashboard.jsx — orchestrates all panels.
  • Routing: Product and marketing routes only. Administration is not bundled into this application; nginx redirects the former /admin path to https://admin.veytics.com/.
  • State: Local component state + a small set of context providers; no global Redux/MobX. Server state is fetched per panel via small hooks (e.g., useMarketData.js).
  • i18n: 10 locales (en, de, es, fr, pl, pt, ru, uk, vi, zh) in frontend/src/i18n/. All keys mirrored across all locales.
  • 3D globe: Cesium, accessed via the public VITE_CESIUM_ION_TOKEN.
  • Build output: Static SPA — deployable behind any CDN.

Admin console — React + Vite (apps/admin/)

  • Independent origin: all operator tools live at admin.veytics.com.
  • Project surfaces: News Publisher, News Aggregator, Veytics Terminal, and Platform Runtime are selected independently, leaving room for future microservice consoles.
  • Terminal tools: user and subscription management, plans, audit events, runtime/API/AI usage, social and translation health, email campaigns, trial invites, AI chat controls and strategies, feedback, feature flags, Telegram, source management, social review queues, and reel tooling.
  • API boundary: the browser reaches core, aggregator, and publisher through /service/core, /service/news, and /service/publishing; provider secrets never enter the client bundle.

Backend services — FastAPI (backend/)

  • Stack: Python 3.11+, FastAPI, SQLAlchemy, Alembic, httpx (async), Poetry.
  • Entry: backend/main.py, selected with ATLAS_SERVICE_ROLE=core|aggregator|publisher. all is the compatibility role.
  • Routes: Modular under backend/routes/ — one file per surface (auth, market, news, alerts, ai_chat, vessels, aircraft, …).
  • Services: backend/services/ — one file per upstream provider, all httpx.AsyncClient-based with explicit timeouts and asyncio.Semaphore for rate limits.
  • Auth: JWT access tokens (short-lived) + refresh tokens (HTTP-only cookie). Note: the Settings two-factor/TOTP toggle is a UI placeholder only — 2FA is not yet implemented server-side.
  • News delivery: canonical story writes and service events commit together; a durable outbox dispatches events to a replayable Redis Stream.
  • Publishing: social credentials exist only in the publisher process. The editorial queue supports drafts, ready items, schedules, retries, and delivery history.
  • Live updates: The browser gets fresh quotes and alerts by polling REST endpoints (~30s); alert conditions are evaluated server-side on a schedule. There is no browser WebSocket — backend/routes/websocket.py is a no-op stub.

Database — PostgreSQL

  • Schemas: users, watchlists, alerts, screens, graphs, dashboards, portfolios, subscriptions.
  • Migrations: Alembic — alembic upgrade head after every backend update.
  • Recommended version: 14+. Earlier versions miss features like generated columns used in some indexes.

Cache — Redis

  • What it caches:
    • Short-TTL upstream responses (quotes, OHLC, fundamentals) so the backend doesn't hammer Finnhub / EODHD on every request.
    • Rate-limit counters per user and per upstream provider.
    • WebSocket session state.
  • Recommended version: 6+.
  • Veytics does not run without Redis — the rate-limit middleware refuses to start if REDIS_URL doesn't connect.

Documentation — VitePress (apps/docs/)

  • Independent app: static deploy, separate from frontend and backend.
  • Glossary auto-gen: generate-glossary-docs.mjs reads frontend/src/components/onboarding/glossary.js and writes a single auto-managed page.
  • Screenshot capture: capture-screenshots.mjs drives Playwright against the running frontend.

Request lifecycle

For a typical "give me AAPL quote" request:

  1. FrontenduseMarketData('AAPL') fires.
  2. HTTPGET /api/market/quote?symbol=AAPL with the access-token cookie.
  3. Backend routebackend/routes/market.py authenticates, rate-limits via Redis, then calls the service layer.
  4. Servicefinnhub.py checks Redis cache; on miss, calls Finnhub with httpx.AsyncClient, populates cache, returns.
  5. Response — JSON to the frontend.
  6. Render — Market panel displays the quote with a realtime/delayed badge based on the response timestamp.

For a live stream, the WebSocket channel replaces step 2–5 with a long-lived subscription, and the service layer publishes ticks as they arrive.

Secrets boundary

A non-negotiable rule: the frontend never has a backend-provider key. The split:

VariableSidePublic?
EODHD_API_KEYBackendNo
OPENSKY_PASSWORDBackendNo
AISSTREAM_API_KEYBackendNo
STRIPE_SECRET_KEYBackendNo
SECRET_KEY (JWT)BackendNo
DATABASE_URLBackendNo
VITE_API_URLFrontendYes (bundled)
VITE_ADMIN_URLFrontendYes (bundled)
VITE_CESIUM_ION_TOKENFrontendYes (bundled)

Anything with a VITE_* prefix is bundled into the JS payload and is therefore world-readable. Use Cesium Ion tokens scoped to globe rendering, never an admin token.

Scaling considerations

  • Backend is stateless beyond Redis sessions — horizontal scale by adding uvicorn replicas behind a load balancer with sticky WebSocket sessions.
  • PostgreSQL is the common bottleneck before the upstream providers are. Read replicas help for reporting workloads.
  • Redis is small and fast — a single instance handles many users. Replicate for HA.
  • Upstream providers rate-limit per Veytics-account (one shared upstream account for all Veytics users on a hosted instance). Self-hosted instances control their own quota.

Where to read more

  • Self-hosting — deployment shapes.
  • Data providers — what each upstream is for.
  • API keys — how to obtain each credential.
  • Stripe billing — billing webhook flow.
  • Repo: source code is the most authoritative reference. Read the route file for the surface you care about; route files are deliberately short.

Released under the project license. Public sources only — no proprietary or restricted data.