Skip to content

Architecture Overview

Daniel Truong edited this page Jun 4, 2026 · 21 revisions

Architecture Overview

This document describes the Eagle system architecture, component interactions, and request flow.

System Components

flowchart TB
    subgraph Users[" "]
        direction LR
        User["👤 Public User"]
        Staff["👔 EAO Staff"]
    end
    
    subgraph OpenShift["OpenShift Cluster"]
        Route["🌐 OpenShift Route<br/>projects.eao.gov.bc.ca"]
        
        subgraph Proxy["Reverse Proxy"]
            RProxy["eao-nginx"]
        end
        
        subgraph Apps["Application Layer"]
            direction LR
            Public["eagle-public<br/>Angular SPA"]
            Admin["eagle-admin<br/>Angular SPA"]
            API["eagle-api<br/>Node.js"]
        end
        
        subgraph Analytics["Analytics"]
            direction LR
            Collector["penguin-analytics<br/>Event Collector"]
            Metabase["Metabase<br/>Dashboards"]
        end
        
        subgraph Data["Data Layer"]
            direction LR
            MongoDB[("MongoDB")]
            TimescaleDB[("TimescaleDB")]
            Typesense[("Typesense")]
        end
        
        subgraph Search["Search Sync"]
            direction TB
            ChangeStream["Change Stream<br/>(real-time)"]
            Reindex["Reindex CronJob<br/>(nightly 2 AM)"]
            ContentExtract["Content Extract CronJob<br/>(every 2h)"]
        end

        subgraph DocIntel["Document Intelligence"]
            Docling["eagle-demi<br/>docling-serve CPU"]
        end
    end
    
    subgraph External["External Services"]
        Keycloak["🔐 BC Gov Keycloak<br/>loginproxy.gov.bc.ca"]
        NRS[("NRS Object Store<br/>nrs.objectstore.gov.bc.ca")]
        Engage["💬 EPIC Engage<br/>epic-engage-web-*.apps.gold.devops.gov.bc.ca"]
    end

    User --> Route
    Staff --> Route
    Route --> RProxy
    
    RProxy -->|"/"|Public
    RProxy -->|"/admin/"|Admin
    RProxy -->|"/api/"|API
    RProxy -->|"/analytics"|Collector
    
    Public & Admin -.->|config|API
    Public & Admin -.->|events|Collector
    
    Staff -.->|OIDC login|Keycloak
    Admin & API -.->|token validation|Keycloak
    
    Public -.->|"engage banner"|Engage
    
    API --> MongoDB
    ChangeStream -->|watch epic collection| MongoDB
    ChangeStream -->|upsert| Typesense
    Reindex -->|full rebuild| Typesense
    ContentExtract -->|download PDFs| NRS
    ContentExtract -->|insert DocumentChunks| MongoDB
    Public -.->|search| Typesense
    Collector --> TimescaleDB
    API -->|POST /v1/convert/file| Docling
    Docling -->|extracted text| API
    Metabase --> TimescaleDB
Loading

Component Details

eao-nginx (Reverse Proxy)

The reverse proxy (rproxy service) routes all incoming requests from projects.eao.gov.bc.ca to appropriate backend services based on URL path:

Path Service Description
/ eagle-public Public-facing website
/admin/ eagle-admin Staff administration portal
/api/ eagle-api REST API endpoints
/analytics penguin-analytics Event tracking API
/engage-api EPIC Engage API (proxied) Engage banner data for eagle-public

Repository: bcgov/eao-nginx

Key Configuration (conf.d/server.conf.tmpl):

  • Environment variables for upstream service URLs
  • Response caching via proxy_cache globalcache
  • HTTP header passthrough for authentication tokens

eagle-public

  • Technology: Angular 21 with nginx
  • Node Version: Node.js 24.x
  • Purpose: Public-facing interface for viewing environmental assessments
  • Config Pattern: Fetches runtime configuration from /api/public/config
  • Features: Project search, document viewing, comment submission, Engage banner
  • Engage Integration: eagle-public integrates with EPIC Engage in two ways:
    1. Engage Banner (EngageBannerComponent): When a project has an open comment period with isMet=true and a metURL, a banner is displayed at the top of the project page. Fetches engagement metadata from EPIC Engage via the /engage-api proxy.
    2. Commenting Tab (CommentingTabComponent): All public comment periods (both native eagle PCPs and Engage-managed PCPs) appear in a single unified list. PCPs with isMet=true render with Engage metadata (name, status, dates from EngageEngagement) fetched in parallel via forkJoin. Clicking an Engage PCP card opens the engagement in a new tab. PCPs sharing the same metURL are deduplicated to one card.
    • The Engage API URL is configured per environment in the rproxy ConfigMap (ENGAGE_API_URL). See Configuration Management for values.

eagle-admin

  • Technology: Angular 21 with nginx
  • Node Version: Node.js 22.x
  • Purpose: Staff portal for managing projects and documents
  • Config Pattern: Fetches runtime configuration from /api/config
  • Features: Project management, document uploads, user administration

eagle-api

  • Technology: Node.js with Express
  • Node Version: Node.js 22.x
  • Purpose: REST API backend for all data operations
  • Database: MongoDB for document storage
  • Key Endpoints:
    • GET /api/public/config - Public app configuration
    • GET /api/config - Admin app configuration
    • GET /api/project - Project data
    • GET /api/document - Document management
  • Search: Typesense powers full-text search for projects, documents, and PDF content. The sync service (Change Stream listener + CronJobs) lives in the standalone eagle-typesense repo. eagle-api only contains the search proxy (typesenseProxy.js) that forwards browser queries to Typesense with server-side role filtering.
  • Content Search: Document Content Search — separate CronJob in eagle-typesense extracts text from PDFs every 2h, stores as DocumentChunk records in MongoDB, synced to Typesense automatically.
  • Document Intelligence: On document upload, eagle-api calls eagle-demi (POST /v1/convert/file) to extract text from PDFs and DOCX files using docling-serve. Cluster-internal only — no external exposure.

eagle-demi

  • Technology: docling-serve CPU image (no custom code)
  • Image: ghcr.io/docling-project/docling-serve-cpu:v1.21.0
  • Purpose: ML-powered PDF/DOCX text extraction for EPIC documents
  • Access: ClusterIP only — reachable as http://eagle-demi:5001 from eagle-api pods
  • Auth: X-Api-Key header from eagle-demi-api-key OpenShift Secret
  • NetworkPolicy: Only role: api-eagle-epic pods (eagle-api) may connect
  • Status: Deployed to dev. June 2026.
  • Repository: bcgov/eagle-demi

penguin-analytics

  • Technology: Node.js Express
  • Purpose: Event collection and storage for usage analytics
  • Database: TimescaleDB (time-series PostgreSQL)
  • Key Endpoints:
    • POST /analytics - Single event ingestion
    • POST /analytics/batch - Batch event ingestion (up to 100)
    • GET /health - Health check
  • Dashboards: Metabase instance for visualization

Keycloak (BC Gov CSS)

External authentication service provided by BC Government Common Services:

Environment URL
Dev/Test https://dev.loginproxy.gov.bc.ca/auth
Prod https://loginproxy.gov.bc.ca/auth
  • Realm: eao-epic
  • Client ID (Admin): eagle-admin-console
  • Client ID (API): eagle-api-console
  • Protocol: OpenID Connect (OIDC)

Request Flow

Public User Accessing a Project

sequenceDiagram
    participant User
    participant Route as OpenShift Route
    participant RProxy as eao-nginx
    participant Public as eagle-public
    participant API as eagle-api
    participant DB as MongoDB
    
    User->>Route: GET projects.eao.gov.bc.ca/p/project-123
    Route->>RProxy: Forward (TLS terminated)
    RProxy->>Public: GET /p/project-123
    Public->>User: Return Angular SPA
    
    Note over User,Public: Browser loads Angular app
    
    User->>Route: GET /api/public/config
    Route->>RProxy: Forward
    RProxy->>API: GET /api/public/config
    API->>User: Return app configuration
    
    User->>Route: GET /api/public/project/project-123
    Route->>RProxy: Forward
    RProxy->>API: GET /api/public/project/project-123
    API->>DB: Query project
    DB->>API: Project data
    API->>User: Return project JSON
Loading

Configuration Architecture

Both Angular applications use a runtime configuration pattern to avoid rebuilding containers for configuration changes:

flowchart LR
    subgraph Build["Build Time"]
        Code[Source Code] --> Build1[yarn build]
        Build1 --> Artifact[Static Files]
        Artifact --> Image[Container Image]
    end
    
    subgraph Runtime["Runtime"]
        Image --> Container[Running Container]
        Container --> Nginx[nginx serves SPA]
        Nginx --> Browser[Browser loads app]
        Browser --> Config[Fetch /api/config]
        Config --> API[eagle-api]
        API --> App[App configured]
    end
Loading

Configuration Flow

  1. Dockerfile sets configEndpoint = true in env.js
  2. Browser loads Angular application
  3. App fetches configuration from API endpoint
  4. API returns environment-specific configuration (URLs, feature flags, etc.)
  5. App applies configuration and renders

Namespaces

Namespace Purpose Contains
6cdc9e-tools Shared tooling Image registry, build artifacts
6cdc9e-dev Development All services (dev tag)
6cdc9e-test Testing/UAT All services (test tag)
6cdc9e-prod Production All services (prod tag)

Image Tagging Strategy

Tag Purpose Mutable
dev Latest development build Yes
ci-latest Latest CI-verified build Yes
test Currently deployed to test Yes
prod Currently deployed to prod Yes
v1.2.3 Specific release version No
abc1234 Git commit SHA No

Security Considerations

  • TLS Termination: Handled at OpenShift Route level
  • Authentication: Keycloak/IDIR for admin users
  • API Security: JWT tokens for authenticated endpoints
  • Container Scanning: Trivy scans for CRITICAL/HIGH vulnerabilities
  • Network Policies: Namespace isolation (if configured)

Related Documentation

Clone this wiki locally