🌌 HOLOCRON ANALYTICS
"Where the Force of Data Meets the Wisdom of the Galaxy"
📑 Table of Contents
- 📜 Overview
- 📂 Project Structure
- 🏗️ Architecture and Performance Engineering
- 🛠️ Engineering Behind the Scenes (Deep Dive)
- 🗺️ Navigation and Features
- 🔗 Integration & Data Protocols (End-to-End)
- 📚 Technical Documentation (Deep Dive)
- 🛡️ Infrastructure & Security (DevSecOps)
- 🔌 API Reference (Endpoints)
- 🎨 Design System
- 📋 Environment Variables
- 💻 Detailed Tech Stack
- 🚀 How to Run
- 👨💻 Developer
📜 Overview
Holocron Analytics is an immersive and gamified platform built to explore the Star Wars universe. Far beyond a simple wiki, this project turns querying data from the public API (SWAPI) into a “Jedi Archivist” experience, using Artificial Intelligence (OpenAI GPT-4o) to enrich data, generate dynamic quizzes, and simulate interactions with Master Yoda.
Key Features
- 🔍 Digital Holocron: Advanced lookup for Characters, Films, Starships, Planets, Species, and Vehicles.
- 🤖 Master Yoda AI: An integrated LLM-powered chatbot that answers questions about the galaxy with the iconic Jedi Master’s personality, using cached data for ultra-fast responses.
- ⚔️ Gamification & Jedi Trials: A progression system where users climb ranks (Padawan -> Master) by completing challenges and quizzes.
- 📊 Visual Reports: Detailed charts on species distribution, vehicles per film, and galaxy statistics.
- 🚀 Extreme Performance: Advanced caching and background-loading strategies for instant navigation.
- 📱 Responsive Design: Fully adaptive UI for desktop, tablet, and mobile with high-end UX.
📂 Project Structure
Star-Wars-App/
├── backend/ # FastAPI API (Python)
│ ├── src/app/
│ │ ├── application/ # Application Layer (Use Cases)
│ │ │ └── services/ # Business services
│ │ │ ├── chat_service.py # AI chat engine
│ │ │ ├── gamification_service.py # XP and achievements system
│ │ │ ├── quiz_service.py # AI quiz generation
│ │ │ └── rag_search.py # Hybrid search engine (RAG)
│ │ ├── domain/ # Domain Layer (DDD)
│ │ │ ├── entities/ # Business entities
│ │ │ ├── enums/ # Enumerations (JediRank, etc.)
│ │ │ ├── exceptions/ # Custom exceptions
│ │ │ ├── repositories/ # Repository interfaces
│ │ │ └── schemas/ # Pydantic schemas (DTOs)
│ │ ├── infrastructure/ # Infrastructure Layer
│ │ │ ├── cache/ # In-memory cache system
│ │ │ ├── config/ # Settings/config
│ │ │ ├── db/ # SQLAlchemy models and connection
│ │ │ ├── external/ # External integrations
│ │ │ │ ├── openai/ # OpenAI client (GPT-4o)
│ │ │ │ └── swapi/ # SWAPI client
│ │ │ ├── security/ # JWT, auth handlers
│ │ │ └── etag_middleware.py # HTTP cache middleware
│ │ └── interfaces/ # Interface Layer
│ │ └── api/v1/
│ │ ├── routers/ # API endpoints
│ │ │ ├── auth.py # Google OAuth authentication
│ │ │ ├── characters.py # Characters CRUD
│ │ │ ├── chat.py # AI chat
│ │ │ ├── films.py # Films CRUD
│ │ │ ├── gamification.py # XP, Rank, Achievements
│ │ │ ├── health.py # Health check
│ │ │ ├── image_fallbacks.py # Image management
│ │ │ ├── metadata.py # Metadata for filters
│ │ │ ├── planets.py # Planets CRUD
│ │ │ ├── species.py # Species CRUD
│ │ │ ├── starships.py # Starships CRUD
│ │ │ └── vehicles.py # Vehicles CRUD
│ │ └── dependencies/ # Dependency injection
│ ├── alembic/ # DB migrations
│ │ └── versions/ # Migration scripts
│ ├── tests/ # Unit tests (Pytest)
│ ├── Dockerfile.cloud-run # Production Dockerfile
│ ├── Dockerfile.dev # Development Dockerfile
│ └── pyproject.toml # Python deps (Poetry)
│
├── frontend/ # React SPA (TypeScript)
│ ├── src/
│ │ ├── App/ # Root component and navigation
│ │ ├── features/ # Domain modules (Feature-based)
│ │ │ ├── auth/ # Google authentication
│ │ │ │ ├── components/ # UserMenu, LoginButton
│ │ │ │ ├── context/ # AuthProvider, AuthContext
│ │ │ │ ├── pages/ # LoginPage, AuthLoadingPage
│ │ │ │ └── services/ # authService (API calls)
│ │ │ ├── characters/ # Characters module
│ │ │ │ ├── components/ # CharacterCard, CharacterModal
│ │ │ │ ├── hooks/ # useCharacters, useCharacterDetails
│ │ │ │ ├── pages/ # CharactersPage
│ │ │ │ └── services/ # charactersService
│ │ │ ├── chat/ # AI Chat module
│ │ │ │ ├── components/ # YodaChatBubble, ChatModal
│ │ │ │ ├── context/ # ChatProvider
│ │ │ │ └── hooks/ # useChat, useChatHistory
│ │ │ ├── dashboard/ # Main dashboard
│ │ │ ├── films/ # Films module
│ │ │ ├── gamification/ # Gamification module
│ │ │ │ ├── components/ # QuizModal, AchievementCard
│ │ │ │ ├── hooks/ # useGamification, useQuiz
│ │ │ │ └── pages/ # GamificationPage
│ │ │ ├── planets/ # Planets module
│ │ │ ├── reports/ # Reports module
│ │ │ │ ├── components/ # Charts, LeaderboardTable
│ │ │ │ └── pages/ # ReportsPage
│ │ │ ├── species/ # Species module
│ │ │ ├── starships/ # Starships module
│ │ │ └── vehicles/ # Vehicles module
│ │ └── shared/ # Shared code
│ │ ├── components/ # Reusable components
│ │ │ ├── CustomSelect/ # Custom Star Wars select
│ │ │ ├── FilmFilter/ # Global film filter
│ │ │ ├── PageLayout/ # Base page layout
│ │ │ ├── Pagination/ # Responsive pagination
│ │ │ ├── ScrollToTop/ # Back-to-top button
│ │ │ └── StarfieldEvents/ # Animated background
│ │ ├── hooks/ # Shared hooks
│ │ │ ├── useFilmOptions.ts # Film options
│ │ │ ├── useMetadataOptions.ts # Metadata for filters
│ │ │ └── usePrefetchAllData.ts # 6-phase prefetch
│ │ ├── services/ # Shared services
│ │ │ ├── api.ts # Base HTTP client
│ │ │ └── metadata.service.ts # Metadata service
│ │ ├── stores/ # Zustand stores
│ │ └── styles/ # Global styles
│ │ └── global.css # CSS Variables, Starfield
│ ├── api/ # Vercel Serverless Functions
│ ├── index.html # HTML entry point
│ ├── vite.config.ts # Vite config
│ └── package.json # Node.js dependencies
│
├── regras-desenvolvimento-python-react/ # Standards documentation
│ ├── regras-backend.md # Python/FastAPI standards
│ ├── regras-frontend.md # React/TypeScript standards
│ └── regras-testes.md # Testing standards
│
├── docker-compose.dev.yml # Development compose
├── deploy-starwars.ps1 # GCP deploy script
└── README.md # This documentation
🏗️ Architecture and Performance Engineering
The solution was designed with an obsessive focus on performance and fluid UX, using an intelligent loading strategy and multi-level caching.
%%{title: "Performance and Cache Strategy"}%%
flowchart TD
Init["🚀 App Initialization"] --> FastLoad["🔥 Critical Load"]
FastLoad --> Render["🖥️ Immediate Render (Optimized LCP)"]
Render --> Background["⏳ Background Loading"]
Background --> FetchAll["📡 Massive Fetch from SWAPI"]
FetchAll --> Process["⚙️ Processing & Enrichment"]
Process --> Cache["💾 Local Cache (Memory)"]
subgraph UX_Fluidity ["Smooth Navigation"]
UserAction["🖱️ User Navigation"]
UserAction -- "Data already in Cache" --> InstantServe["⚡ Instant Display"]
UserAction -- "Chat/Quiz" --> ContextAware["🧠 Preloaded Context"]
end
Cache -.-> UX_Fluidity
🧠 Artificial Intelligence Pipeline
The AI integration is not just a “wrapper”. It actively fills data gaps and generates content.
%%{title: "AI Data Enrichment Pipeline"}%%
sequenceDiagram
participant C as Client
participant B as Backend
participant DB as Database
participant SW as SWAPI
participant AI as Vertex AI (Gemini)
C->>B: GET /characters/1 (Luke Skywalker)
B->>DB: Lookup Local Cache
alt Incomplete or Missing Data
B->>SW: Fetch Basic Data
SW-->>B: Raw JSON (SWAPI)
B->>AI: Prompt: "Generate RPG stats for Luke"
AI-->>B: Enriched JSON (Force: 85, Agility: 80...)
B->>DB: Save/Update Record
end
B-->>C: Return Full Character + Stats
🛠️ Engineering Behind the Scenes (Deep Dive)
What sets Holocron Analytics apart is the attention to “invisible” technical details that make the experience magical.
1. 🔍 Search Optimization (NLP & RAG)
The search system (rag_search.py) implements a Retrieval-Augmented Generation engine that understands native Portuguese.
- Hybrid Fuzzy Search: Uses
rapidfuzz(Levenshtein Distance) to correct typos ("Anakin Skywaler" -> Finds "Anakin Skywalker"). - PT-BR Stemming: A custom algorithm that removes suffixes (-inho, -mente, -ão) to understand search intent.
- Entity Detection: Smart heuristics distinguish when the user searches for a droid ("R2") vs. a generic term, avoiding false positives.
- Synonyms: Automatically maps terms like "sabre de luz" ↔ "lightsaber" or "robô" ↔ "droid".
2. 🧙 Persona Engineering (Prompting)
Master Yoda AI is not just a standard chatbot.
- Dynamic Context: The system injects official SWAPI data snippets into the model context to keep answers factual (Grounding), reducing hallucinations.
- Syntactic Inversion: The system prompt instructs the AI to mimic Yoda’s unique grammar (Object-Subject-Verb).
- Multiple Personas: The architecture supports persona switching, also enabling a "Darth Vader" persona that responds with hostility and forbidden emojis.
3. ✨ Cinematic Frontend
The UI was built for total immersion:
- Dynamic Starfield: The star background (
StarfieldEvents.tsx) is not a looping video. It’s a procedurally-generated particle system simulating “space traffic”, with meteors varying in speed, angle, and depth. - Accessibility: The system detects the OS
prefers-reduced-motionpreference and automatically disables heavy animations for motion-sensitive users.
🗺️ Navigation and Features
1. 🔍 Holocron (Data Exploration)
Dedicated pages for each Star Wars entity, with rich, interactive cards.
- Pages:
Characters,Films,Starships,Planets,Species,Vehicles. - Advanced Filters:
- Filter by Name, Gender, Climate (Planets), Class (Starships), and more.
- Fuzzy (approximate) search to find terms even with typos.
- Dynamic sorting and instant pagination (client-side pagination thanks to preloading).
2. 📊 Reports and Analytics
Located in the reports folder, this section provides visual insights through interactive charts.
- Species Distribution: Pie/bar charts showing the diversity of the galaxy.
- Starship Comparison: Scatter plots comparing speed vs. cost.
- Timeline: Visual timeline of films and events.
3. ⚔️ Full Gamification
The system keeps users engaged through a levels and rewards system.
- Jedi Trials: AI-generated quizzes that test knowledge.
- XP Bar: Earn experience by exploring the system, completing quizzes, and discovering “easter eggs”.
- Rankings: Start as Youngling, become Initiate, Padawan, Knight, and finally Jedi Master.
- Achievements: Unlockable badges (e.g., "Visited all planets", "Answered 10 questions in a row").
4. ⚡ Phased Loading Strategy (6-Phases Prefetch)
To ensure an “instant” experience, the frontend (usePrefetchAllData.ts) implements a 6-stage hydration pipeline that runs silently in the background:
- Phase 1 (Critical): Dashboard (LCP). Loads data for Home.
- Phase 2 (Navigation): First 3 pages of ALL listings (Characters, Starships, etc).
- Phase 3 (Rich Content): Full details of all films.
- Phase 4 (Big Data): Loads massive datasets (100+ items) to generate the Reports charts.
- Phase 5 (Social): User profile, achievements, leaderboard, and daily challenges.
- Phase 6 (Bulk Quiz): Downloads hundreds of potential questions so Jedi Trials works offline/without delay.
All of this happens without blocking the main thread, using setTimeout and React Query priority management.
5. 🛡️ Intelligent Cache Middleware
The backend does not rely only on the browser. We implemented a Global ETag Middleware (etag_middleware.py) that:
- Intercepts all JSON responses.
- Generates a SHA-256 hash of the content.
- Compares it with the request’s
If-None-Matchheader. - Returns
304 Not Modified(0 bytes body) if data hasn’t changed, saving bandwidth and client processing.
6. 🖼️ Image Lookup & Fallback Strategy
SWAPI does not provide images. ImageLookupService implements a layered resolution strategy:
- Databank Index: Tries to match the resource name against an image index extracted from the official Star Wars Databank.
- Legacy Fallback: If it fails, it looks up a local table (
image_fallbacks) manually mapped for obscure items. - Conservative Matching: Uses string normalization algorithms (
casefold + strip) to ensure "X-Wing" matches "x-wing fighter", but returnsNoneif there’s no absolute certainty (avoiding wrong images).
7. 📑 Abstract Pagination (SWAPI Slicing)
SWAPI enforces pagination of 10 items. The frontend needs grids of 12, 8, or 100 items.
The swapi_pagination.py module solves this with Virtual Slicing:
- Mathematically computes which SWAPI pages (e.g., page 3 and 4) contain the desired slice (e.g., items 25 to 36).
- Fetches only the required pages in parallel (
asyncio.gather). - Combines results and slices the exact array for the client.
- Result: The frontend can request
pageSize=100and the backend transparently orchestrates 10 parallel SWAPI calls.
8. 🎮 Gamification Engine (Jedi Trials)
The engagement system (gamification_service.py) is not just a points counter.
- Behavioral Achievements: Achievements like "Friend of Yoda" or "Vader’s Minion" are unlocked by analyzing interaction history with specific personas (
chat_stats_by_persona). - Live XP & Ranks: Level calculation is instant, using a progression curve based on cumulative XP (Youngling $\to$ Padawan $\to$ Knight $\to$ Master).
- Leaderboard Aggregation: Optimized SQL queries aggregate complex statistics in real time, computing accuracy (% correct) and best quiz sessions for the global leaderboard.
🔗 Integration & Data Protocols (End-to-End)
The SWAPI communication architecture was designed to be resilient and invisible to the end user. Below is the full request flow.
1. Client Protocol (SWAPIClient)
SWAPIClient (src/app/infrastructure/external/swapi/client.py) acts as an intelligent gateway:
- HTTP Keep-Alive: Uses
httpx.AsyncClientto keep persistent connections, reducing TCP/TLS handshake overhead across multiple requests. - Transparent Pagination:
_paginate_allabstracts the APInextcursor logic, automatically iterating until it consumes all data when needed (e.g., for reports). - URL Normalization: The client normalizes URLs and IDs automatically, ensuring
https://swapi.dev/api/people/1/andhttps://swapi.dev/api/people/1are treated as the same cache resource.
2. Data Flow (Sequence Diagram)
The diagram below illustrates the path of a simple request (e.g., "List Starships") and how the system chooses between Cache, External API, and Image Lookup.
sequenceDiagram
participant UI as Frontend (React Query)
participant API as FastAPI Backend
participant Cache as Memory Cache (TTL)
participant SWAPI as SWAPI.dev
participant DB as PostgreSQL (Images Setup)
UI->>API: GET /starships?page=1&pageSize=12
API->>Cache: GET swapi:starships:page:1
alt Cache Miss (First Access)
API->>SWAPI: GET /starships/?page=1 & page=2 (Parallel)
SWAPI-->>API: JSON Raw Data
API->>DB: Resolve Image URLs (Virtual Join)
DB-->>API: Starship URLs
API->>API: Normalize Data (snake_case, numbers)
API->>Cache: SET swapi:starships:page:1 (TTL 1h)
API-->>UI: Optimized JSON + Images
else Cache Hit
Cache-->>API: Ready Data
API-->>UI: Instant Response (<10ms)
end
3. Data Handling and Normalization
SWAPI returns “dirty” data by modern standards (strings for numbers, mixed snake_case).
StarWarsApp implements a Data Sanitization layer:
- Number Parsing:
parse_swapi_numberconverts complex strings like"unknown","n/a", or ranges"30-165"into safe numeric types (Optional[float]) to enable correct sorting and charts. - Date Standardization: All dates are converted to strict ISO-8601.
- Cross-Reference Resolving: Relationship URLs (e.g.,
pilots: [...]) are kept as references but prepared for lazy loading in the frontend.
4. 🔐 Secure Authentication Protocol (JWT + Cookie)
We implemented the Silent Refresh pattern for maximum security:
- Access Token: Short-lived (15min), stored in memory (JavaScript), used as a Bearer Token.
- Refresh Token: Long-lived (7 days), stored in an HttpOnly Cookie (Secure, SameSite), inaccessible via JS.
- Token Rotation: On each refresh, the previous token is invalidated and a new one is issued, preventing replay attacks.
sequenceDiagram
participant U as User
participant F as Frontend
participant B as Backend
participant D as Database
U->>F: Login with Google
F->>B: Send Google Credential
B->>D: Verify/Create User
B-->>F: Return Access Token + User Info
B-->>U: Set-Cookie: refresh_token (HttpOnly)
Note over F,B: Secure Navigation
F->>B: GET /api/v1/characters (Bearer Access)
B-->>F: 200 OK
Note over F,B: Access Token Expires
F->>B: GET /api/v1/characters
B-->>F: 401 Unauthorized
F->>B: POST /auth/refresh (Automatic Cookie)
B->>D: Validate Refresh Token
B-->>F: New Access Token
B-->>U: Set-Cookie: New refresh_token
F->>B: Retry Original Request
5. 🤖 Context-Aware Chat Engine (ChatService)
chat_service.py is the brain of the application, orchestrating over 2,000 lines of conditional logic and AI.
- Intent Routing: Before calling the LLM, the system analyzes intent with regex heuristics. Example: "Who is Luke?" queries the local DB/SWAPI directly, saving tokens and latency.
- RAG Pipeline:
- Extract Entities: Identifies "Luke", "Tatooine" in the sentence.
- Hybrid Search: Vector + keyword search in the knowledge base.
- Context Injection: Injects JSON snippets (e.g., force stats) into the system prompt.
- Persona Tuning: Adjusts the response to the "Yoda" style (syntactic inversion) or "Vader".
📚 Technical Documentation (Deep Dive)
For developers who want to understand internal details, we document all critical subsystems directly here:
🤖 AI & NLP Engineering (Click to expand)
🏗️ Intent Processing Pipeline
The system does not simply send the user’s message to the LLM. There is a strict pipeline to ensure factual accuracy and save tokens.
flowchart TD
UserInput["🗣️ User Message"] --> PreProcess["🔧 Pre-processing"]
PreProcess --> Routing{⚡ Intent Routing}
Routing -- "Regex/Fixed Pattern" --> DirectReply["📝 Direct Reply (No LLM)"]
Routing -- "Complex Question" --> RAG["🔍 RAG Search (Context Recovery)"]
RAG -- "Data Found" --> ContextInj["💉 Context Injection (JSON)"]
ContextInj --> PromptEng["🎭 Persona Engineering"]
PromptEng --> LLM["🤖 AI Invocation"]
LLM --> Response["💬 Final Answer"]
DirectReply --> Response
1. Critical Pre-processing
Before any search, we apply Star Wars-specific sanitization:
- Droid Name Protection: The algorithm protects patterns like "R2-D2", "C-3PO", "BB-8" so they are not broken during normalization.
- Problem: Normalizing "R2 D2" becomes
['r2', 'd2']. - Solution: Regex identifies
\br2[\s\-]?d2\band converts it to a single tokenR2-D2before tokenization.
- Problem: Normalizing "R2 D2" becomes
2. Intent Routing (Intent Routing)
_route_structured_intent tries to resolve the request without spending AI:
- Explicit Entity: If the user says "Who is Luke?", the system extracts "Luke", fetches from SWAPI, and responds with a fixed template.
- Category Questions: "Is R2-D2 a robot?". The system checks the character’s category ("robot") and answers as a boolean.
🔍 RAG Engine (Retrieval-Augmented Generation)
The rag_search.py file implements a hybrid search engine optimized for Portuguese.
Search Algorithm
We use a combination of techniques to find the correct entity (Character, Planet, Starship) even with typos.
| Technique | Library/Implementation | Goal |
|---|---|---|
| Fuzzy Matching | rapidfuzz (Levenshtein) |
Fix typos ("Anakin Skywaler" → "Skywalker") |
| PT-BR Stemming | Custom simplified RSLP |
Reduce words ("correndo" → "corr", "filmes" → "film") |
| Stopwords Removal | Custom list | Remove noise ("o", "a", "de", "para") but keep "Darth", "Lord" |
| Alias Mapping | Static dictionary | Map nicknames ("Padme", "Ani", "Imperador") to canonical names |
Context Injection Strategy
When an entity is found (e.g., "Luke Skywalker"), the system injects a JSON Snippet into the LLM System Prompt.
Example System Prompt:
SWAPI DATA (factual reference):
{
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"hair_color": "blond",
"skin_color": "fair",
"eye_color": "blue",
"birth_year": "19BBY",
"gender": "male"
}
Rules:
- For attributes present in the data, use the values above as the source of truth.
- Do not invent numbers if they are not here.
This removes hallucinations about “hard” data (height, weight, colors) while allowing the AI to improvise about personality and lore.
🎭 Persona Engineering
YodaAIService manages response personality. The System Prompt is swapped dynamically:
Master Yoda (persona="yoda")
-
Instruction: "Answer in Brazilian Portuguese in Yoda’s style, inverting sentence order when possible."
-
Permissions: Can use emojis (🌟, ⚔️).
-
Tone: Wise, calm, lightly humorous.
Darth Vader (persona="vader")
-
Instruction: "Cold, authoritative, intimidating voice. Use breathing onomatopoeia (pshhh... khhh)."
-
Restrictions: FORBIDDEN to use emojis.
-
Tone: Hostile, impatient, superior.
🎮 Gamification Mechanics (Click to expand)
📈 XP and Level System (Rankings)
User progression is based on Accumulated XP (Experience). Rank calculation is done in real time whenever total XP changes.
Rankings Table (JediRank)
| Required XP | Rank (Title) | Description |
|---|---|---|
| 0 - 99 | 🧒 Youngling | Beginner in the Force. |
| 100 - 299 | 🧘 Initiate | Starting training. |
| 300 - 699 | 🗡️ Padawan | Apprentice to a Master. |
| 700 - 1499 | ⚔️ Jedi Knight | Defender of peace in the galaxy. |
| 1500 - 2999 | 🧙♂️ Jedi Master | Great wisdom and power. |
| 3000 - 4999 | 🏛️ Council Member | Leadership of the Jedi Order. |
| 5000+ | 🌌 Grand Master | Peak Force connection (Yoda level). |
Source: app/domain/enums/jedi_rank.py
💰 XP Sources
How the user earns experience:
-
Quiz (Jedi Trials)
- +10 XP per correct answer.
- Formula:
xp = correct * 10 - There is no penalty for mistakes.
-
Daily Challenge
- +30 XP fixed upon completing the day’s goal.
- Example: "Send 3 messages to Yoda today".
-
Achievements
- Value varies by achievement (see table below).
- XP is granted only once (when unlocked).
🏆 Achievements and Triggers
Achievements are checked on each interaction (_apply_achievement_rules in gamification_service.py). Some are based on behavior and not only numbers.
| ID | Name | XP | Unlock Condition (Trigger) |
|---|---|---|---|
primeiro_contato |
First Contact | +50 | Perform the first interaction (Chat or Search). |
explorador |
Explorer | +100 | Perform 10 searches in the Holocron. |
amigo_yoda |
Friend of Yoda | +150 | Send 5 messages talking with the Yoda persona. |
lacaio_vader |
Vader’s Minion | +150 | Send 5 messages talking with the Darth Vader persona. |
Persona Detection Logic
The system counts messages separately for each persona in conversations:
# Rule pseudocode
if yoda_messages >= 5: unlock("amigo_yoda")
if vader_messages >= 5: unlock("lacaio_vader")
This encourages the user to try different interaction modes.
📊 Leaderboard (Global Ranking)
The Leaderboard (get_quiz_leaderboard) uses SQL aggregation to rank the best players.
Tie-break criteria:
- Highest score in a single Quiz session (
best_score). - (Implicit) Order of insertion in the database.
The system also computes Accuracy:
Accuracy = (Total Correct / Total Questions) * 100
🎲 Daily Challenge Generation
Currently, the daily challenge is generated deterministically based on the date:
- ID:
daily_chat_{YYYY-MM-DD} - Goal: Send 3 messages.
- Reset: The count resets at midnight (UTC).
🚀 Production Deploy Guide (Click to expand)
📦 Infrastructure Overview
The project uses a Serverless Containerized architecture on GCP:
- Compute: Google Cloud Run (Stateless Containers).
- Registry: Google Artifact Registry (Docker image storage).
- Secrets: Google Secret Manager (Secure credential management).
- Auth: Google IAM (Service Accounts for runtime).
🛠️ Prerequisites
Before running, you need the following installed:
- Google Cloud SDK (
gcloud): Authenticated (gcloud auth login). - Docker Desktop: Running locally (for image build).
- PowerShell: Terminal for running the script.
📜 Deploy Script (deploy-starwars.ps1)
The script automates 6 critical steps that would take hours manually.
Step 1: Authentication and Setup
Checks whether gcloud and docker are available and defines the target PROJECT_ID.
Step 2: Enable APIs
Enables the required services in the GCP project:
run.googleapis.com(Cloud Run)secretmanager.googleapis.com(Secrets)artifactregistry.googleapis.com(Docker Repo)iam.googleapis.com(Permissions)
Step 3: Runtime Service Account
Creates a dedicated identity for the backend runtime (star-wars-cloudrun-runtime).
- Why? To avoid using the “Default Compute” account which has excessive permissions.
- Permissions: Grants
roles/secretmanager.secretAccessorso the backend can read the DB password and API keys.
Step 4: Artifact Registry
- Checks whether the Docker repo (
cloud-run) exists in the region. - If it doesn’t, creates it automatically.
- Configures
docker-credential-gcrto allow push.
Step 5: Build & Push
- Runs
docker buildusingDockerfile.cloud-run.- Note: Uses a lightweight base image (
python:3.12-slim).
- Note: Uses a lightweight base image (
- Runs
docker pushto upload the image to Artifact Registry.
Step 6: Deploy to Cloud Run
Deploys the service with the following production settings:
- Memory: 512Mi (Optimized for Free Tier).
- CPU: 1 vCPU.
- Auto-scaling: 0 to 1 instance (avoid costs when idle, “scale to zero”).
- Env Vars: Injects non-sensitive variables (DB Host, CORS).
- Secrets: Mounts secrets (
holocron-db-password,holocron-jwt-secret-key) as environment variables.
🔑 Secrets Management
The script does NOT create secrets. You must create them manually once in the GCP Console or via CLI:
# Example: Creating the JWT secret
printf "my-super-secret-key" | gcloud secrets create holocron-jwt-secret-key --data-file=-
# Example: Creating the DB secret
printf "db-password" | gcloud secrets create holocron-db-password --data-file=-
Required Secrets
-
holocron-jwt-secret-key: To sign auth tokens. -
holocron-db-password: PostgreSQL password.
Optional Secrets
holocron-openai-api-key: IfAI_ENABLED=true.
🔄 Useful Commands
Quick Deploy (Default)
.\deploy-starwars.ps1
Deploy to Another Region
.\deploy-starwars.ps1 -Region "us-central1"
Error Logs
If the container fails to start, check logs in GCP:
gcloud run services logs read star-wars-backend
🎨 UX & Frontend Architecture (Click to expand)
📱 State Management Strategy
We use a hybrid strategy for state management:
-
Server State (React Query / TanStack):
- API data (Characters, Films, User Profile).
- Cache, retries, and automatic revalidation.
- Example:
useQuery(['characters', page], fetchCharacters).
-
Client/UI State (Zustand):
- Non-persistent global UI state.
- No need for complex Providers (Context API Hell).
- Example:
useImageEditModeStoreto control image editing modals.
🔐 Auth UX Pattern (Silent Refresh)
To avoid abruptly logging the user out or showing 401 errors on screen:
- Refresh Token: Stored in an
HttpOnlyCookie (inaccessible via JS). - Strategy: The
/auth/refreshendpoint returns 204 No Content (instead of 401) when there is no session.- This lets the frontend smoothly decide whether to show “Login” or “Profile” without throwing exceptions in the console.
⚡ Performance & Caching (Backend Side)
The backend implements ETagMiddleware (etag_middleware.py) to optimize data transfer and reduce latency:
- Hashing Algorithm: We intercept every JSON response and compute a SHA-256 hash of the body (
hexdigest). - Content Negotiation (RFC 7232):
- The server always sends
Cache-Control: private, max-age=0, must-revalidate. This forces the browser to always ask the server “did this change?” before using cache. - If the request
If-None-Matchheader matches the computed hash:- We return 304 Not Modified.
- CRITICAL: We remove the response body and the
Content-Lengthheader, reducing the payload from KB/MB to ~0 bytes.
- The server always sends
- Cache Security:
- We add
Vary: Origin, Authorizationto ensure one user never receives another user’s private cache (e.g., profile data). - The middleware reconstructs the response iterator (
response.body_iterator) to ensure the stream can be consumed both for hashing and by the final client.
- We add
📐 Quality Assurance & Data Architecture (Click to expand)
🧪 Testing Strategy (QA)
We maintain code quality with a test suite split into two layers:
-
Backend (Pytest):
- Focus on isolated Unit Tests for business rules (
GamificationService,ChatService). - Integration tests ensure API contracts (
schemas) are respected.
- Focus on isolated Unit Tests for business rules (
-
Frontend (Vitest):
- Fast unit tests for Hooks and Stores (
useImageEditModeStore). - Ensures UI logic (e.g., toggles, formatting) works without rendering the entire app.
- Fast unit tests for Hooks and Stores (
💾 Data Engineering
Data robustness is guaranteed by three pillars:
-
Strict Validation (Pydantic V2):
- API inputs and outputs are typed.
- App settings use custom validators to parse CSV/JSON from environment variables (
cors_allow_origins).
-
Database Migrations (Alembic):
- PostgreSQL schema versioning.
env.pywas customized to injectPYTHONPATHdynamically, enabling migrations even in complex container environments.
-
Logging & Observability:
- Centralized configuration (
alembic.ini,custom loggers) to trace silent production errors.
- Centralized configuration (
🛡️ Infrastructure & Security (DevSecOps)
Security is not an “add-on”, but part of the cloud-native infrastructure design.
1. 🔑 Secrets Management
We adopted a Zero Hardcoded Secrets strategy.
- Frontend (Vercel): Public keys (Google Client ID) and API URLs are injected at build time via Vercel environment variables. No private key ever touches the client bundle.
- Backend (Google Cloud Run): Critical secrets (DB Password, JWT Secret, OpenAI Key) are managed by GCP Secret Manager. They are mounted into the container as runtime environment variables, ensuring not even the Dockerfile can access them.
2. 🐳 Secure Containerization Pipeline
Dockerfile.cloud-run follows hardening best practices:
- Minimal Base Image: Based on
python:3.12-slimto drastically reduce the attack surface (fewer vulnerable binaries). - Stateless by Design: The container retains no data. Uploads and persistence are delegated to external services (Storage/PostgreSQL).
- Auto-Migrations: The entrypoint runs
alembic upgrade headon every deploy, ensuring the DB schema stays synchronized with application code (Infrastructure as Code).
3. 💾 Persistence and Resilience
Although the application heavily relies on cache, PostgreSQL is the source of truth for critical data:
- User Profiles: Sensitive data and gamification progress.
- Image Fallbacks: Our
image_fallbackstable acts as a failover system. If the external image CDN fails, the system automatically falls back to curated assets stored in the database.
graph TD
subgraph "Public Zone (Vercel)"
Frontend[React SPA]
end
subgraph "Private Zone (Google Cloud Platform)"
LB[Load Balancer HTTPS]
subgraph "Cloud Run (Auto-Scaling)"
API[FastAPI Container]
end
Secret[GCP Secret Manager]
DB[(PostgreSQL)]
Vertex[Vertex AI]
end
Frontend -- "Bearer JWT (TLS 1.3)" --> LB
LB --> API
API -- "Env Vars Injection" --> Secret
API -- "SQLAlchemy (Connection Pool)" --> DB
API -- "IAM Credentials" --> Vertex
style Secret fill:#f9f,stroke:#333
style DB fill:#ccf,stroke:#333
9. 🛡️ Compliance & Security Standards (RBAC & OWASP)
The project was audited following strict software security principles, aligned with the OWASP Top 10 and corporate compliance rules.
9.1 Access and Identity (RBAC & Invoker)
-
Cloud Run Invoker: The backend is not public. It runs in
authenticated-onlymode, accepting only requests with a valid Google-signed OIDC token (via Vercel Service Account). No one can access the API directly without going through the frontend. -
RBAC (Role-Based Access Control): Internally, administrative endpoints validate the JWT token. Only authenticated users (
require_authenticated_user_id) can persist data or generate complex reports.
9.2 Vulnerability Prevention (OWASP)
| Vulnerability | Implemented Mitigation Strategy |
|---|---|
| SQL Injection | Strict use of SQLAlchemy ORM with parameterized queries. No SQL string is manually concatenated. |
| XSS (Cross-Site Scripting) | React 19 with default auto-escaping. Sensitive cookies (refresh_token) marked HttpOnly to prevent JS theft. |
| Sensitive Data Exposure | Secrets managed by GCP Secret Manager. Logs sanitized to avoid leaking PII (Personal Identifiable Information). |
| Broken Access Control | Token verification middleware on all protected routes (Depends(require_authenticated_user_id)). |
9.3 Cloud Run Private Access Diagram
This graph shows how backend access is locked down.
%%{title: "Cloud Run Private Access & API Shielding"}%%
flowchart LR
subgraph External_World ["External World"]
User[User]
Hacker[Attacker]
end
subgraph Security_Perimeter ["Security Perimeter (Google Auth)"]
Vercel["Vercel Frontend"]
end
subgraph Private_Zone ["Private Zone (Secure Backend)"]
CloudRun["Cloud Run Backend"]
DB[("PostgreSQL")]
Secrets["Secret Manager"]
end
subgraph Shielded_APIs ["External APIs (Shielded)"]
SWAPI["SWAPI (Data)"]
AI["OpenAI / Vertex AI"]
end
%% User flow
User -- HTTPS --> Vercel
Hacker -. "Direct Access (403 Forbidden)" .-> CloudRun
%% Service-to-service auth
Vercel -- "OIDC ID Token (Auth)" --> CloudRun
%% Backend accessing resources
CloudRun -- "Internal Traffic" --> DB
CloudRun -- "Load API Keys" --> Secrets
%% Shielding: backend protects keys and rate-limits
CloudRun -- "Proxied Request" --> SWAPI
CloudRun -- "Secure Context" --> AI
%% Styling
style Vercel fill:#000,color:#fff
style CloudRun fill:#4285F4,color:#fff
style Hacker stroke:red,stroke-width:2px,stroke-dasharray: 5 5
style Secrets fill:#FFEB3B,color:#000
🔌 API Reference (Endpoints)
The REST API follows OpenAPI 3.0 standards. Interactive documentation is available at /docs (Swagger UI).
🔐 Authentication (/api/v1/auth)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /google |
Login via Google OAuth (Credential One-Tap) | ❌ |
| POST | /refresh |
Refresh Access Token using Refresh Cookie | Cookie |
| POST | /logout |
Invalidate session and clear cookies | ✅ |
| GET | /me |
Return authenticated user profile | ✅ |
👤 Characters (/api/v1/characters)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | / |
List characters with filters and pagination | ❌ |
| GET | /{id} |
Details for a specific character | ❌ |
Query Params: page, pageSize, search, gender, homeworld, species, film, sort
🎬 Films (/api/v1/films)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | / |
List all films in the saga | ❌ |
| GET | /{id} |
Details for a specific film | ❌ |
🚀 Starships (/api/v1/starships)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | / |
List starships with filters and pagination | ❌ |
| GET | /{id} |
Details for a specific starship | ❌ |
Query Params: page, pageSize, search, starship_class, manufacturer, film, sort
🌍 Planets (/api/v1/planets)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | / |
List planets with filters and pagination | ❌ |
| GET | /{id} |
Details for a specific planet | ❌ |
Query Params: page, pageSize, search, climate, terrain, film, sort
👽 Species (/api/v1/species)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | / |
List species with filters and pagination | ❌ |
| GET | /{id} |
Details for a specific species | ❌ |
Query Params: page, pageSize, search, classification, designation, language, film, sort
🚗 Vehicles (/api/v1/vehicles)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | / |
List vehicles with filters and pagination | ❌ |
| GET | /{id} |
Details for a specific vehicle | ❌ |
Query Params: page, pageSize, search, vehicle_class, manufacturer, film, sort
🤖 AI Chat (/api/v1/chat)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | / |
Send message and receive AI response | ✅ |
| GET | /history |
User conversation history | ✅ |
Body (POST):
{
"message": "Who is Luke Skywalker?",
"persona": "yoda" // or "vader"
}
🎮 Gamification (/api/v1/gamification)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /profile |
Gamification profile (XP, Rank, Stats) | ✅ |
| GET | /achievements |
List user achievements | ✅ |
| GET | /leaderboard |
Global users leaderboard | ❌ |
| GET | /daily-challenge |
Current daily challenge | ✅ |
| POST | /quiz/start |
Start a quiz session | ✅ |
| POST | /quiz/answer |
Answer a quiz question | ✅ |
| POST | /quiz/finish |
Finish quiz and receive XP | ✅ |
📊 Metadata (/api/v1/metadata)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /characters/homeworlds |
List unique homeworlds | ❌ |
| GET | /characters/species |
List unique species | ❌ |
| GET | /characters/genders |
List unique genders | ❌ |
| GET | /planets/climates |
List unique climates | ❌ |
| GET | /planets/terrains |
List unique terrains | ❌ |
| GET | /species/classifications |
List unique classifications | ❌ |
| GET | /species/designations |
List unique designations | ❌ |
| GET | /species/languages |
List unique languages | ❌ |
| GET | /starships/classes |
List unique starship classes | ❌ |
| GET | /starships/manufacturers |
List unique starship manufacturers | ❌ |
| GET | /vehicles/classes |
List unique vehicle classes | ❌ |
| GET | /vehicles/manufacturers |
List unique vehicle manufacturers | ❌ |
🖼️ Images (/api/v1/image-fallbacks)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | / |
List all image fallbacks | ❌ |
| POST | / |
Create or update an image fallback | ✅ |
| DELETE | /{id} |
Remove an image fallback | ✅ |
❤️ Health Check (/api/v1/health)
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | / |
API health status | ❌ |
| GET | /ready |
Readiness probe (for Kubernetes/Cloud Run) | ❌ |
🎨 Design System
The Holocron Analytics UI was designed to deliver an immersive Star Wars experience.
🎨 Color Palette
| CSS Token | Hex | Usage |
|---|---|---|
--color-primary |
#35e6ff |
Primary color (Neon Cyan) |
--color-secondary |
#ff35a0 |
Accent color (Magenta) |
--color-background |
#0a0a12 |
Main background (Space Black) |
--color-surface |
#1a1a2e |
Cards and elevated surfaces |
--color-text-primary |
#ffffff |
Primary text |
--color-text-secondary |
#8b8b9a |
Secondary text |
--color-success |
#00ff87 |
Success states |
--color-warning |
#ffcc00 |
Warning states |
--color-error |
#ff4444 |
Error states |
✨ Visual Effects
- Glassmorphism: Cards with
backdrop-filter: blur(12px)and translucent borders. - Neon Glow: Colored shadows (
box-shadow) for interactive elements. - Dynamic Starfield: Animated background with CSS/JS particles.
- Hover Transitions: Smooth effects with
transformandopacity(0.2s ease).
🧩 Reusable Components
| Component | Location | Description |
|---|---|---|
CustomSelect |
shared/components/CustomSelect |
Styled dropdown with Star Wars theme |
FilmFilter |
shared/components/FilmFilter |
Global film filter (7 films) |
PageLayout |
shared/components/PageLayout |
Base layout with header, content, commands |
Pagination |
shared/components/Pagination |
Responsive page navigation |
ScrollToTop |
shared/components/ScrollToTop |
Floating button to scroll back to top |
StarfieldEvents |
shared/components/StarfieldEvents |
Animated background with meteors |
DetailsModal |
shared/components/DetailsModal |
Details modal with glassmorphism |
FallbackEditableImage |
shared/components/... |
Image with fallback and inline editing |
🪝 Custom Hooks
| Hook | File | Description |
|---|---|---|
useFilmOptions |
shared/hooks/useFilmOptions.ts |
Formatted options for FilmFilter |
useMetadataOptions |
shared/hooks/useMetadataOptions.ts |
10+ variants for entity filters |
usePrefetchAllData |
shared/hooks/usePrefetchAllData.ts |
6-phase prefetch pipeline |
useImageFallback |
shared/hooks/useImageFallback.ts |
Image fallback management |
📋 Environment Variables
Backend (.env or GCP Secret Manager)
# 🔌 Database
DATABASE_URL=postgresql://user:password@host:5432/dbname
# 🔐 Authentication
JWT_SECRET_KEY=your-256-bit-secret-key
JWT_ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=15
# 🌐 Google OAuth
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
# 🤖 OpenAI
OPENAI_API_KEY=sk-...
AI_ENABLED=true
OPENAI_MODEL=gpt-4o
# 🔗 CORS
CORS_ALLOW_ORIGINS=http://localhost:5173,https://your-domain.vercel.app
# 📊 Environment
ENVIRONMENT=development # or production
LOG_LEVEL=INFO
Frontend (.env or Vercel Environment Variables)
# 🔗 API
VITE_API_URL=http://localhost:8000/api/v1
# 🌐 Google OAuth (Public Key)
VITE_GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
# ⚙️ Configuration
VITE_APP_TITLE=Holocron Analytics
💻 Detailed Tech Stack
Backend (Python 3.12)
| Category | Technology | Version | Usage |
|---|---|---|---|
| Framework | FastAPI | 0.109 | High-performance async REST API |
| ORM | SQLAlchemy | 2.0 | Object-relational mapping |
| Migrations | Alembic | 1.13 | DB schema versioning |
| Validation | Pydantic | 2.9 | Data validation and schemas |
| Database | PostgreSQL | 9.5+ | Primary relational database |
| Cache | cachetools (TTLCache) | 5.5 | In-memory cache with TTL |
| AI | OpenAI SDK | 1.66 | GPT-4o integration |
| NLP | rapidfuzz | 3.10 | Fuzzy matching (Levenshtein) |
| HTTP | httpx | 0.27 | Async HTTP client |
| Auth | python-jose | 3.3 | JWT encoding/decoding |
| Security | passlib + bcrypt | 1.7 | Password hashing |
Frontend (Node.js 20+)
| Category | Technology | Version | Usage |
|---|---|---|---|
| Framework | React | 19.0 | Declarative UI library |
| Language | TypeScript | 5.6 | Static typing |
| Bundler | Vite | 6.0 | Ultra-fast build tool |
| State | Zustand | 5.0 | Lightweight global state |
| Data | TanStack Query | 5.62 | Server data fetching and cache |
| Charts | Recharts | 3.7 | Responsive SVG charts |
| Styling | CSS Modules | - | Locally scoped styles |
| Routing | React Router | 7.1 | SPA navigation |
Infrastructure
| Category | Technology | Usage |
|---|---|---|
| Container | Docker + Docker Compose | Development and production |
| Backend | Google Cloud Run | Serverless backend hosting |
| Frontend | Vercel | Frontend hosting and CDN |
| Secrets | GCP Secret Manager | Secure credential management |
| Registry | Google Artifact Registry | Docker image repository |
🚀 How to Run
Prerequisites
- Docker & Docker Compose (Recommended)
Running with Docker
-
At the project root:
docker-compose -f docker-compose.dev.yml up -d --build -
Access:
- Frontend:
http://localhost:5173 - Docs:
http://localhost:8000/docs
- Frontend:
👨💻 Developer
Project delivered for the PowerOfData Technical Challenge.
"Do or do not. There is no try." — Master Yoda