A full-stack GitHub PR analytics platform for engineering teams
Track pull requests, review velocity, team performance, and collaboration patterns β all in one beautiful dashboard deployed on Cloudflare's edge network.
π Features Β· ποΈ Architecture Β· π οΈ Tech Stack Β· β‘ Quick Start Β· βοΈ Deployment Β· π API
| Feature | Description |
|---|---|
| π PR Metrics Dashboard | Open/merged/closed counts, velocity trends, weekly activity charts |
| π Team Leaderboard | Ranked team members by merged PRs, reviews, comments, and code volume |
| π₯ Team Members Status | Real-time active/inactive status based on organization activity |
| π₯ Team Spotlights | Hot streaks, fastest reviewers, top commenters |
| β±οΈ Review Velocity | Time-to-first-review, time-to-merge, reviewer load analytics |
| π’ Bottleneck Detection | Slow PRs ranked by wait time with severity coloring |
| π§© PR Type Distribution | Feature / Fix / Refactor / Chore breakdown via title conventions |
| π Code Volume | Additions vs deletions per week with stacked bar charts |
| πΉ Google Meet Integration | Create and end meetings directly, track attendees and analytics |
| π Real-Time Webhooks | GitHub webhooks invalidate cache β new PRs appear immediately |
| π Bilingual (EN/AR) | Full localization with RTL support |
| π¨ Dark/Light Theme | Custom design system with smooth theme transitions |
| π± Responsive | Sidebar on desktop, bottom nav on mobile, adaptive layouts |
| β‘ Edge-Deployed | Backend on Cloudflare Workers (0ms cold start), frontend on Pages |
graph TD
User([Developer User])
GitHubWeb([GitHub Webhooks])
PubSub([Google Cloud Pub/Sub])
subgraph Edge ["Cloudflare Edge Network"]
SPA[Flutter Web SPA<br/>Cloudflare Pages]
API[Hono Router + DI<br/>Cloudflare Worker]
end
subgraph Store ["Cloudflare Serverless Persistence"]
KV[(Workers KV<br/>Caches & Auth)]
D1[(D1 SQLite<br/>Relational Log)]
Queue>Cloudflare Queue<br/>Async Job Queue]
end
subgraph External ["External APIs"]
GHAPI[GitHub REST API]
MeetAPI[Google Meet API]
end
User -->|Views Dashboards/HTTPS| SPA
SPA -->|JSON Queries| API
GitHubWeb -->|Sends Event Streams| API
PubSub -->|Pushes Workspace Events| API
API <--> KV
API <--> D1
API -->|Submit Background Job| Queue
Queue -->|Consume & Compute| API
API -->|App Auth / JWT| GHAPI
API -->|OAuth2 Tokens| MeetAPI
classDef External fill:#2D3748,color:#fff,stroke:#4A5568
classDef Edge fill:#C05621,color:#fff,stroke:#9C4221
classDef Store fill:#2B6CB0,color:#fff,stroke:#2C5282
class External External
class Edge Edge
class Store,KV,D1,Queue Store
System Purpose:
Sellio Metrics is a full-stack engineering operations platform designed to measure and visualize engineering team velocity. It tracks pull requests, reviewer engagement, code collaboration patterns, team activity, and meeting durations. The platform operates completely on the edge to provide real-time dashboards with extremely low latency.
Main Features:
- PR Tracking & Analytics: Real-time visibility into open/merged pull requests, code volume (additions/deletions), PR classifications (features, chores, bugs), and bottlenecks (stale PRs).
- Leaderboards & Gamification: Points-based system tracking merged PRs, code contributions, review activity, and attendance.
- Team Activity Status: Real-time visibility into active/inactive team members based on organization engagement.
- Meeting Lifecycles: Google Meet integration for creating, tracking duration (via explicit check-in/out), and automatically terminating meetings.
- Real-Time Data Sync: Webhook-driven invalidation and background recalculation of metrics.
Main Actors:
- Developers / Users: The engineering team using the Flutter web frontend to monitor performance.
- GitHub API: Source of truth for all repository, PR, and engagement metrics via authenticated App connections.
- Google Meet API & Workspace Events: Handles meeting creation, OAuth2 user consent, and pushes real-time event notifications via Google Cloud Pub/Sub.
- Infrastructure (Cloudflare Edge): Powers execution, caching, and serving.
Architecture Style:
Edge-Native Modular Monolith (Serverless) + Single Page Application (SPA)
Reasoning:
- Codebase Analysis Structure: The backend is packaged as a single deployable Cloudflare Worker (
backend/src/worker.ts), routing requests using Hono via distinct feature modules (/api/repos,/api/prs, etc.). Because it is deployed as a single runtime rather than distributed decoupled services, it functions as a monolith. However, internally it utilizes Awilix Dependency Injection and Clean Architecture to keep the service execution completely modular (resembling a Micro-Modular Monolith). - Edge Deployment First: The system delegates standard long-running service capabilities to serverless edge equivalents: D1 (relational data), KV (edge caching), and Cloudflare Queues (asynchronous message queuing).
Boundaries:
- Frontend β· API Layer: Secured HTTPS REST communication. The UI requests parsed JSON aggregates; does not understand where data originates.
- API Router β· Service Layer: The boundary enforced by the DI Container. Routes orchestrate input (Hono request parsing) and pass POJOs to strictly-typed domain services.
- Service Layer β· External Dependencies (DB, Cache, GitHub): Business logic interacts only with unified Repositories or Client abstractions (
GitHubClient,CacheService).
graph TB
subgraph HTTP ["HTTP & Router Boundary (worker.ts)"]
Controller[Hono Middleware & Route Endpoints]
end
subgraph Services ["Business Domain"]
RepServ(Repos Service)
PrServ(PRs Metrics Service)
LeadServ(Score Aggregation Subsystem)
HookServ(Webhook Ingest Orchestrator)
MeetServ(Meeting Lifecycle Tracker)
MeetEventsServ(Google PubSub Event Handler)
end
subgraph Repositories ["Data Storage Interfaces"]
RepoD1(SQLite Query Builders)
CacheSV(Namespaced Edge KV Store)
end
subgraph Infra ["Infrastructure Wrappers"]
GHClient[Rate-Limited GitHub App Client]
GoogAuth[Google Tokens & Endpoints]
WsClient[Workspace Events Client]
end
Controller -.-> RepServ & PrServ & LeadServ & HookServ & MeetServ & MeetEventsServ
RepServ & PrServ -.-> GHClient
RepServ & PrServ -.-> RepoD1 & CacheSV
HookServ -.-> RepoD1
HookServ -.-> |Invalidates| CacheSV
MeetEventsServ -.-> WsClient
MeetEventsServ -.-> |Push Decoded Payload| CacheSV
LeadServ -.-> RepoD1 & CacheSV
MeetServ -.-> GoogAuth
MeetServ -.-> RepoD1 & CacheSV
- Presentation Layer (Pages & Widgets): Displays the UI (e.g., Hux Design System charts, Leaderboards).
- Providers (State Management): Co-located reactive state handlers that orchestrate fetching data and updating the UI.
- Domain Layer (Entities & Services): Contains business object representations (
BottleneckEntity,PrEntity) and rules (NO UI dependencies). - Data Layer (Repositories & RemoteDataSources): Implements interfaces defined by the Domain to fetch data from the REST API.
- Hono Router: Entry point that applies CORS, parses requests, and delegates endpoints to specific feature modules.
- Service Layer (Feature Modules):
- Metrics & Repos Services: Uses Git client to fetch organization state and caches the results.
- ScoreAggregationService: Aggregates PR, code, and attendance data into leaderboard structures.
- Attendance & Meetings Service: Handles checking in/out of meetings via
ATTENDANCE_KVand Google APIs. - MeetEventsService: Subscribes Spaces to Workspace Events and ingests pushes via Google Cloud Pub/Sub (
WEBHOOK_QUEUE//api/meet-events/webhook) to track real-time participant joins, drops, and durations. - WebhookService: Ingests external GitHub triggers and delegates caching invalidation / point creation.
- Infrastructure Integrations (Octokit + D1):
- D1 Service: Abstracted query builder for SQLite interacting with
merged_prs,pr_comments, etc. - CachedGitHubClient: Wrapper around Octokit resolving App Auth (JWT to temporary access token), enforcing Rate Limit protections.
- D1 Service: Abstracted query builder for SQLite interacting with
Backend Endpoint Organization:
- System:
/api/health,/api/logs,/api/debug(telemetry and connectivity). - GitHub Sync:
/api/repos,/api/prs,/api/sync/github,/api/webhooks(system automation boundary). - User Activity:
/api/scores,/api/points,/api/members(Leaderboards & organization states). - Meetings/Attendance:
/api/meetings,/api/meet-events,/api/attendance(Temporal lifecycle management).
Services / Business Logic:
- Strict
Controller -> Service -> Mapper -> Resultpipeline. Mappers are explicitly isolated as pure functions without dependencies. - Score derivation relies entirely on SQL aggregations (
SUM(r.points) JOIN point_rules) instead of hardcoding points natively in themerged_prstables.
Background Workers (Cron & Queues):
- Cron: Bound in
worker.ts'sscheduled()export. ExecutesprecomputeSnapshots()continuously to ensure high data-heavy queries do not impact normal user navigation times. - Queue Consumers: Built-in Cloudflare Queues bound to
queue()inworker.tshandle asynchronous message batches, specifically mappingrecompute_scoresso immediate user operations aren't blocked.
Performance & Resiliency:
- Caching: Distributed KV edge caching across 4 namespaces (
CACHE,SCORES_KV,MEMBERS_KV,ATTENDANCE_KV). - Rate Limiting: Managed implicitly through aggressive caching (reducing upstream dependency) combined with
RateLimitGuardbuilt into theCachedGitHubClient. - Error Handling: Standardized error handler translating exceptions (
GitHubApiError,RateLimitError) into unified JSON constructs (message,statusCode). - Retries: The Queueing interface handles failures natively using standard Cloudflare retries (
msg.retry()).
sequenceDiagram
participant GitHub
participant Worker HTTP
participant Webhook Service
participant D1 Relational
participant Queue
participant Background CPU
participant Cache
GitHub->>Worker HTTP: POST /api/webhooks (PR Merged Content)
Worker HTTP->>Webhook Service: Forward JSON Body
Webhook Service->>D1 Relational: INSERT OR IGNORE merged_prs (Idempotent)
D1 Relational-->>Webhook Service: Inserted successfully
Webhook Service->>Queue: Submit Job: { type: 'recompute_scores' }
Webhook Service-->>Worker HTTP: Yield Control Flow
Worker HTTP-->>GitHub: HTTP 202 Accepted (Fast Return)
Note over Queue, Cache: Asynchronous Background Execution
Queue-->>Background CPU: Process Batch Job (Queue Consumer)
Background CPU->>D1 Relational: RUN SUM(points) Query Aggregation
D1 Relational-->>Background CPU: Raw Score Values
Background CPU->>Cache: Set Key `sellio:leaderboard:all` (TTL 6H)
flowchart LR
Origin[GitHub State Changes] --> |Webhooks| IngestLayer[Webhook Route]
IngestLayer --> |Normalize / Pure Mapping| DBStore[(D1 Relational Entities)]
Client[Flutter Frontend UI] --> |GET /api/scores| ScoreLayer[Score Calculation]
DBStore --> |SQL JOIN points_rules| ScoreLayer
ScoreLayer --> |Cache Read / Write| KVCache[(Workers KV Snapshots)]
KVCache --> |JSON Return Payload| Client
Client2[Metrics Dashboard UI] --> |GET /api/prs| PrLayer[PR Fetch Controller]
PrLayer --> |Fall through Cache| ExternalApi[GitHub REST API]
ExternalApi --> |JSON Data| PrLayer
PrLayer --> Client2
Data flows sync for user queries, and async for system updates.
-
Synchronous User Query (e.g., Load Dashboard):
- Frontend requests
/api/metrics/repo. - Worker queries
CACHEKV namespace. - Cache Hit: Return JSON immediately (< 20ms).
- Cache Miss: Request App token -> Fetch from GitHub API -> Transform via pure Mapping layer -> Write to KV with 1hr TTL -> Return to client.
- Frontend requests
-
Asynchronous Webhooks (Event-Driven Updates):
- Developer interacts with GitHub (merges a PR) or joins a Google Meet.
- GitHub Webhook POSTs to
/api/webhooks/ Google Cloud Pub/Sub POSTs to/api/meet-events/webhook. - Webhook worker writes to the D1 database (idempotent
INSERT OR IGNORE) or caches the Meet Event intoEVENTS_CACHE_KEYlimit 200 items. - Worker queues a background task to
WEBHOOK_QUEUEindicating caching should be invalidated. - The queue consumer picks up the event and triggers backend recomputation / cache dropping.
-
Chronological / Background Jobs:
- A Cron schedule hits
scheduled(ScheduledEvent)running naturally every 6 hours. - Worker precomputes massive queries (e.g., All Time Leaderboard Snapshot) and drops them in
SCORES_KV.
- A Cron schedule hits
Important Design Decisions:
- Execution over Storage: The system avoids computing rigid scores synchronously.
merged_prsis an immutable log. "Points" are deduced dynamically inSCORES_KVusingpoint_rules. Upgrading point calculations for past behavior only requires changing rule tables. - Cloudflare V8 Isolates: Relying on Cloudflare Workers guarantees sub-millisecond cold starts globally without warming layers, perfect for dashboard analytics.
- Dependency Rule: Enters strict boundary constraints utilizing
Awilixproxy mode scopes. Domain functions cannot import framework-level HTTP constructs, which means the domain logic is runtime-agnostic. - Temporal Decoupling: Instead of synchronous webhook processing, Webhooks issue state insertions to DB and hand invalidations off to Queues. Immediate GitHub API success is returned to avoid GitHub timeout dropping.
graph TD
node1["Developer Machine"]
subgraph GitHubPipelines ["CI/CD Execution Environment"]
act1["Actions: Flutter UI Builder"]
act2["Actions: TypeScript Transpiler"]
end
subgraph Prod ["Cloudflare Global Edge"]
subgraph DNS ["Anycast Routing Network"]
page["Cloudflare Pages<br/>(Statics)"]
work["Cloudflare Worker Runtime<br/>(Isolates)"]
end
d1["D1 (SQLite Distributed Hub)"]
kv["Workers KV Edge Nodes"]
end
node1 -->|Push Code| GitHubPipelines
act1 -->|Upload Artifacts| page
act2 -->|Deploy via Wrangler| work
page -->|Rest API Cross-Origin| work
work --> d1 & kv
External Integrations:
- GitHub App API: Core engine source. Used to query Repos, PRs, comments, additions, and review lifecycle stages. Relies on App private keys (JWT payload) mapped to installation tokens.
- Google Authenticator (OAuth2): Handles user consent required to generate calendar/meeting links.
- Google Meet API (REST v2) & Workspace Events: Used via authenticated endpoints to initialize video chat instances dynamically and administratively close spaces. Leverages Google Cloud Pub/Sub to push real-time participant/meeting lifecycle events.
Frontend State Management & UI:
- App Pattern: Relies on Provider mapped conceptually by feature boundary (e.g., Dashboard metrics are bound to a singular domain Provider component).
- API Communication pattern: Widgets consume DI-registered
IMetricsRepositoryimplementations, ensuring the UI remains completely isolated from the HTTP layer. Data deserialization occurs at the boundaries parsing cleanEntities. - UI: Designed symmetrically utilizing the
Hux Design System. Fully i18n standardized supporting both Left-to-Right English and Right-to-Left Arabic. Relies heavily on cached backend aggregates to immediately load without skeleton screen fatigue.
erDiagram
REPOS ||--o{ MERGED_PRS : contains
MEMBERS ||--o{ MERGED_PRS : authors
MERGED_PRS ||--o{ PR_COMMENTS : receives
MEMBERS ||--o{ PR_COMMENTS : comments
MEETING_SESSIONS ||--o{ MEETING_ATTENDANCE : hosts
MEMBERS ||--o{ MEETING_ATTENDANCE : attends
REPOS {
int id PK
string owner
string name
string pushed_at
}
MEMBERS {
string login PK
string display_name
string avatar_url
}
MERGED_PRS {
int id PK
int pr_number
string author FK
int repo_id FK
int additions
int deletions
datetime merged_at
}
PR_COMMENTS {
int id PK
int pr_id FK
string author FK
string comment_type
}
MEETING_ATTENDANCE {
string id PK
string developer_login FK
string session_id FK
int duration_minutes
}
POINT_RULES {
string event_type PK
float points
string description
}
The Cloudflare D1 (SQLite) instance forms an idempotent registry tracking historical engineering milestones.
repos(1) ββ (N)merged_prsmembers(1) ββ (N)merged_prs(Author)merged_prs(1) ββ (N)pr_commentsmeeting_sessions(1) ββ (N)meeting_attendancepoint_rules(Dictionary table mapping Event Name -> Points -> Description)
Data is strictly normalized. Bots are excluded upstream upon ingest, eliminating noise from is_bot queries. Additions, deletions, statuses, bodies, and creation times are maintained natively for analytics processing.
| Technology | Purpose |
|---|---|
| TypeScript | Type-safe API development |
| Cloudflare Workers | Serverless edge compute (0ms cold start, 100k+ req/day free) |
| Workers KV | Edge-distributed caching with TTL (100k reads/day free) |
| Cloudflare D1 | Serverless SQLite relational database accessed via the Repository Pattern |
| Hono | Ultrafast, lightweight web framework built for Edge runtimes |
| Awilix | Dependency injection (PROXY mode, scoped lifetimes) |
| Octokit | GitHub REST API client with App authentication |
| GitHub App Auth | JWT β Installation token (5,000 req/hr, auto-refresh) |
| Google Meet API | Native REST API via fetch (Cloudflare Worker compatible) |
| Google Auth Library | Managing OAuth2 user consent flow and handling permissions |
| Technology | Purpose |
|---|---|
| Flutter Web | Cross-platform UI framework |
| Provider | Reactive state management |
| Hux Design System | Custom component library (badges, buttons, charts, sidebar) |
| Lucide Icons | Modern icon set |
| AppLocalizations | Built-in i18n (English + Arabic with RTL) |
| Technology | Purpose |
|---|---|
| Cloudflare Pages | Static site hosting with global CDN |
| GitHub Actions | Separate CI/CD pipelines for frontend and backend |
| GitHub Webhooks | Real-time cache invalidation on PR events |
| Tool | Version | Link |
|---|---|---|
| Node.js | 22+ | nodejs.org |
| Flutter | 3.x | flutter.dev |
| Wrangler CLI | 4.x | npm i -g wrangler |
| GitHub App | β | Create one |
git clone https://github.com/Sellio-Squad/sellio_metrics.git
cd sellio_metrics
# Backend
cd backend && npm install
# Frontend
cd ../frontend && flutter pub getcd backend
cp .env.example .envEdit .env:
APP_ID=123456
INSTALLATION_ID=12345678
GITHUB_ORG=Sellio-Squad
# Paste your private key with literal \n for line breaks:
APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIB...\n-----END RSA PRIVATE KEY-----"
# Google Meet Credentials
GOOGLE_CLIENT_ID=your_oauth2_client_id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your_oauth2_client_secret
GOOGLE_REDIRECT_URI=https://sellio-metrics.abdoessam743.workers.dev/api/meetings/oauth2callbackπ‘ Private Key Tip (PowerShell):
(Get-Content backend\github-app.pem -Raw).Replace("`r`n", "\n").Replace("`n", "\n")Copy the output and paste as
APP_PRIVATE_KEYvalue.
Terminal 1 β Backend (starts at http://localhost:3001):
cd backend
npm run devTerminal 2 β Frontend (opens in Chrome):
cd frontend
flutter run -d chrome --dart-define=API_BASE_URL=http://localhost:3001# Health check
curl http://localhost:3001/api/health
# β {"status":"ok","org":"Sellio-Squad","timestamp":"..."}
# Repos list
curl http://localhost:3001/api/repos
# PR metrics
curl "http://localhost:3001/api/metrics/Sellio-Squad/sellio_mobile?state=all"Both frontend and backend deploy automatically via GitHub Actions when changes are pushed to main.
| Push changes to... | Frontend deploys? | Backend deploys? |
|---|---|---|
frontend/ |
β | β |
backend/ |
β | β |
| Both | β | β |
- dash.cloudflare.com/profile/api-tokens β Create Token
- Permissions:
Cloudflare Pages: Edit,Workers Scripts: Edit,Workers KV Storage: Edit
cd backend
npx wrangler kv namespace create CACHECopy the id into backend/wrangler.toml.
cd backend
npx wrangler secret put APP_ID
npx wrangler secret put INSTALLATION_ID
npx wrangler secret put APP_PRIVATE_KEY
npx wrangler secret put GOOGLE_CLIENT_ID
npx wrangler secret put GOOGLE_CLIENT_SECRETSecrets (Settings β Secrets β Actions):
| Name | Value |
|---|---|
CLOUDFLARE_API_TOKEN |
Your API token |
CLOUDFLARE_ACCOUNT_ID |
Your account ID |
Variables (Settings β Variables β Actions):
| Name | Value |
|---|---|
API_BASE_URL |
https://your-worker.workers.dev |
- Go to your GitHub App settings β Webhooks
- Webhook URL:
https://your-worker.workers.dev/api/webhooks/github - Content type:
application/json - Events: Select
Pull requests,Pull request reviews,Issue comments,Pull request review comments
When a PR is opened/merged/reviewed, the webhook invalidates the cached metrics, so the dashboard shows changes immediately.
git add -A
git commit -m "deploy: initial production deployment"
git push origin mainProduction: https://sellio-metrics.abdoessam743.workers.dev
Local: http://localhost:3001
Server health check.
{ "status": "ok", "org": "Sellio-Squad", "timestamp": "2026-02-28T15:21:48Z" }List all organization repositories.
{
"org": "Sellio-Squad",
"count": 6,
"repos": [{ "name": "sellio_mobile", "full_name": "Sellio-Squad/sellio_mobile", ... }]
}Fetch PR metrics for a repository.
| Param | Type | Default | Description |
|---|---|---|---|
state |
string |
all |
open, closed, all |
per_page |
number |
100 |
Results per page |
{
"owner": "Sellio-Squad",
"repo": "sellio_mobile",
"count": 127,
"metrics": [{ "prNumber": 42, "title": "feat: checkout flow", "state": "merged", ... }]
}Calculate leaderboard from PR data (sent in body).
Retrieves the target Google OAuth2 login link for Google Meet consent page.
Checks if the backend currently possesses a valid Google OAuth2 token.
Clears the current Google OAuth2 tokens from persistent storage.
Creates a brand new Google workspace, returning the direct link to jump into the call. Only works if authenticated.
Removes all users and terminates an ongoing meeting via the Google Meet infrastructure.
Returns activity status (Active/Inactive) for all organization members.
GitHub webhook endpoint β invalidates cached metrics on PR events.
sellio_metrics/
βββ .github/workflows/
β βββ deploy-frontend.yml β Flutter build β Cloudflare Pages
β βββ deploy-backend.yml β Type check β Cloudflare Workers
β
βββ backend/ β TypeScript API (Cloudflare Workers)
β βββ src/
β β βββ config/env.ts β Validated environment config
β β βββ core/ β DI container, errors, logger, types
β β βββ infra/
β β β βββ cache/ β CacheService (Workers KV)
β β β βββ github/ β GitHubClient, CachedGitHubClient, RateLimitGuard
β β βββ modules/
β β β βββ health/ β GET /api/health
β β β βββ repos/ β GET /api/repos
β β β βββ metrics/ β GET /api/metrics/:owner/:repo
β β β βββ leaderboard/ β POST /api/metrics/leaderboard
β β β βββ webhook/ β POST /api/webhooks/github
β β βββ worker.ts β Workers entry point (router + handlers)
β βββ wrangler.toml β Workers + KV config
β βββ package.json
β
βββ frontend/ β Flutter Web (Cloudflare Pages)
βββ lib/
βββ core/ β Constants, extensions, l10n, navigation, theme
βββ design_system/ β Hux component barrel exports
βββ domain/ β Entities, repositories (interfaces), services
βββ data/ β Models, datasources, repository implementations
βββ presentation/ β Pages, providers, widgets
| Variable | Required | Description |
|---|---|---|
APP_ID |
β | GitHub App ID |
APP_PRIVATE_KEY |
β | RSA private key (PKCS#8 PEM) |
INSTALLATION_ID |
β | GitHub App installation ID |
GOOGLE_CLIENT_ID |
β | Google OAuth2 Client ID |
GOOGLE_CLIENT_SECRET |
β | Google OAuth2 Client Secret |
| Variable | Default | Description |
|---|---|---|
GITHUB_ORG |
Sellio-Squad |
Organization name |
GOOGLE_REDIRECT_URI |
/api/meetings/oauth2callback |
URI Google uses after user consent |
LOG_LEVEL |
info |
trace, debug, info, warn, error |
NODE_ENV |
production |
Environment |
| Variable | Default | Description |
|---|---|---|
API_BASE_URL |
http://localhost:3001 |
Backend URL, passed via --dart-define |
| Decision | Rationale |
|---|---|
| Cloudflare Workers over traditional server | 0ms cold start, free tier (100k req/day), global edge deployment |
| Workers KV over Redis | Free tier, no server to manage, edge-distributed, built-in TTL |
| GitHub App over PAT | 5,000 req/hr, org-level access, auto-rotating tokens |
| Awilix DI | No decorators, PROXY mode, scoped lifetimes, easy testing |
| Provider over Bloc/Riverpod | Lower boilerplate for dashboard use-case, native Flutter integration |
| Separate CI/CD | Frontend and backend deploy independently β faster feedback loops |
Private key error: unsupported
Convert your RSA key to PKCS#8:
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private-key.pem -out private-key-pkcs8.pemThen set the PKCS#8 key as APP_PRIVATE_KEY.
New PRs don't appear
- Ensure the GitHub webhook is configured (see Deployment β Step 5)
- Or use the Refresh button in the dashboard to force re-fetch
- Check KV cache TTL β cached results expire automatically based on TTL
CORS errors in development
Make sure you pass --dart-define=API_BASE_URL=http://localhost:3001 when running Flutter.
The Worker includes Access-Control-Allow-Origin: * on all responses.
Flutter gen-l10n not generating
Ensure pubspec.yaml has flutter: generate: true and run from frontend/ directory.
MIT β see LICENSE for details.
Built with β€οΈ by the Sellio Squad
Measure what matters. Ship with confidence.