Skip to content

Commit 28ebbee

Browse files
authored
Merge pull request #2 from qdequele/qdequele/backend-rebuild-rails-82f5a8
Backend rebuild: Rails SaaS control plane + Rust crawl engine (SCR-85 / SCR-87)
2 parents 149cd12 + 86aeec3 commit 28ebbee

239 files changed

Lines changed: 22549 additions & 11142 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,54 @@ ENABLE_CORS=true
1717
# JWT secret for auth tokens
1818
JWT_SECRET=dev-jwt-secret-change-in-production
1919

20+
# Auth brute-force limit (attempts/min/IP), read by the Rails app's
21+
# Rack::Attack throttle. Default 5; raise for contract-test runs (contracts/
22+
# suite signs up many throwaway users). Leave unset in prod.
23+
# AUTH_RATE_LIMIT=1000
24+
2025
# PostgreSQL connection URL
2126
DATABASE_URL=postgres://scrapix:scrapix@localhost:5433/scrapix
2227

28+
# -----------------------------------------------------------------------------
29+
# SaaS Control Plane (Rails) — SCR-85 backend split
30+
# -----------------------------------------------------------------------------
31+
# Port for the Rails app (saas/)
32+
SAAS_PORT=8081
33+
34+
# Console proxy routing: path prefixes listed in SAAS_PREFIXES (comma-
35+
# separated) are proxied to the Rails app; everything else goes to the Rust
36+
# crawl engine. Since phase 9 the Rust API no longer serves any SaaS route,
37+
# so SAAS_API_URL + the full prefix list below are REQUIRED for the console
38+
# to work. Set inline on the console entry in Procfile.dev for native dev.
39+
# SAAS_EXCLUDE_PREFIXES keeps deeper paths on Rust when a route group is
40+
# split mid-migration (empty since phase 7).
41+
SAAS_API_URL=http://localhost:8081
42+
SAAS_PREFIXES=analytics,configs,engines,auth,account,webhooks,oauth,.well-known,mcp
43+
44+
# MCP tool generation (Rails /mcp): path to the frozen OpenAPI snapshot and
45+
# the base URL the MCP server uses to call back into the SaaS app itself.
46+
# Defaults work for native dev; docker-compose sets both explicitly.
47+
# MCP_OPENAPI_SPEC_PATH=contracts/openapi.json
48+
# MCP_SAAS_BASE_URL=http://localhost:8081
49+
50+
# Transactional email (Rails ActionMailer via Resend SMTP; SolidQueue drains
51+
# the shared scheduled_emails queue the Rust engine writes into). Without the
52+
# key, dev deliveries go to ActionMailer's :test backend.
53+
# RESEND_API_KEY=re_...
54+
# Some hosts (Scaleway) block egress on 465/587; Resend also listens on 2465.
55+
# RESEND_SMTP_PORT=465
56+
# CONSOLE_PUBLIC_URL=https://scrapix.meilisearch.com
57+
58+
# Social login (Rodauth omniauth in the Rails app). Register the callback
59+
# URLs /auth/google/callback and /auth/github/callback with the providers.
60+
# GOOGLE_CLIENT_ID=...
61+
# GOOGLE_CLIENT_SECRET=...
62+
# GITHUB_CLIENT_ID=...
63+
# GITHUB_CLIENT_SECRET=...
64+
65+
# Rust crawl engine URL, used by the Rails app to submit crawls (config trigger)
66+
# SCRAPIX_ENGINE_URL=http://localhost:8080
67+
2368
# -----------------------------------------------------------------------------
2469
# Kafka/Redpanda Configuration
2570
# -----------------------------------------------------------------------------
@@ -89,6 +134,8 @@ BATCH_TIMEOUT_SECS=5
89134
# Stripe Payments (Optional)
90135
# -----------------------------------------------------------------------------
91136
# Enables credit purchases via Stripe. Without these, topups are free (dev mode).
137+
# Purchases/webhooks are handled by the Rails app; the Rust engine reads only
138+
# STRIPE_SECRET_KEY (for auto-topup charges triggered by usage debits).
92139
# STRIPE_SECRET_KEY=sk_test_...
93140
# STRIPE_WEBHOOK_SECRET=whsec_...
94141

CLAUDE.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,9 @@ scrapix health
169169

170170
## Analytics API (Tinybird-style)
171171

172-
When ClickHouse is configured (`CLICKHOUSE_URL` environment variable), the API server automatically:
173-
1. **Persists crawl events to ClickHouse** - PageCrawled and PageFailed events are batched (100 events) and flushed every 5 seconds
174-
2. **Enables the analytics API** at `/analytics/v0/pipes/`
172+
When ClickHouse is configured (`CLICKHOUSE_URL` environment variable):
173+
1. The **Rust engine persists crawl events to ClickHouse** - PageCrawled and PageFailed events are batched (100 events) and flushed every 5 seconds
174+
2. The **Rails SaaS app serves the analytics API** at `/analytics/v0/pipes/` (port 8081, or via the console proxy)
175175

176176
This provides long-term analytics storage beyond the in-memory diagnostics.
177177

@@ -243,6 +243,26 @@ curl "http://localhost:8080/analytics/v0/pipes/kpis.json?hours=24"
243243

244244
## Architecture
245245

246+
### Two Backends (SCR-85 split)
247+
248+
The backend is deliberately split into two services sharing one Postgres:
249+
250+
- **Rails SaaS control plane (`saas/`, port 8081)** — auth + sessions, social
251+
login, account/team/invites, API keys, billing + Stripe, saved crawl
252+
configs + engines CRUD, analytics pipes, the OAuth 2.1 provider, and the
253+
MCP server at `/mcp`. Contract-tested by `contracts/`.
254+
- **Rust crawl engine (`bins/scrapix-api`, port 8080)** — /scrape, /map,
255+
/search, /crawl*, jobs, WebSockets, diagnostics, plus the background tasks
256+
that belong to the data plane: cron scheduler (fires saved configs), email
257+
scheduler (delivers the shared `scheduled_emails` queue via Resend),
258+
OAuth token cleanup, and Stripe auto-topup on usage debits. It validates
259+
credentials (API keys, Bearer tokens, session JWTs) but issues none.
260+
261+
The console proxy (`console/src/app/api/scrapix/[...path]/route.ts`) routes
262+
by path prefix via `SAAS_API_URL` + `SAAS_PREFIXES`; the frozen full-platform
263+
spec is `contracts/openapi.json`, the engine-only spec is
264+
`contracts/openapi.engine.json`.
265+
246266
### Workspace Structure
247267

248268
The project is organized as a Cargo workspace with two main directories:

Cargo.lock

Lines changed: 0 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Procfile.dev

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ api: set -a && source .env && set +a && cargo watch -w crates -w bins -x 'r
22
frontier: set -a && source .env && set +a && cargo watch -w crates -w bins -x 'run -p scrapix --bin scrapix -- frontier'
33
crawler: set -a && source .env && set +a && cargo watch -w crates -w bins -x 'run -p scrapix --bin scrapix -- crawler'
44
content: set -a && source .env && set +a && cargo watch -w crates -w bins -x 'run -p scrapix --bin scrapix -- content'
5-
console: cd console && npm install && npm run dev
5+
console: cd console && npm install && SAAS_API_URL=http://localhost:8081 SAAS_PREFIXES=analytics,configs,engines,auth,account,webhooks,oauth,.well-known,mcp npm run dev
6+
saas: set -a && source .env && set +a && cd saas && bin/rails db:prepare && SOLID_QUEUE_IN_PUMA=1 OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES bin/rails server

bins/scrapix-api/Cargo.toml

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -60,34 +60,19 @@ tracing-subscriber = { workspace = true }
6060
# Async traits
6161
async-trait = { workspace = true }
6262

63-
# Auth
63+
# Credential validation (API keys, OAuth Bearer tokens, session JWTs)
6464
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "uuid", "chrono", "tls-rustls"] }
6565
sha2 = "0.10"
6666
hex = "0.4"
67-
argon2 = "0.5"
68-
jsonwebtoken = "9"
6967
axum-extra = { version = "0.10", features = ["cookie"] }
70-
rand = { workspace = true }
71-
72-
# OAuth
73-
base64 = { workspace = true }
74-
75-
# Rate limiting
76-
redis = { workspace = true }
77-
dashmap = { workspace = true }
78-
79-
# URL encoding for OAuth redirects
80-
urlencoding = "2.1"
81-
82-
# MCP HTTP transport
83-
rmcp = { version = "1.2", features = ["server", "transport-streamable-http-server"] }
84-
rmcp-openapi = "0.25"
8568

8669
# Cron scheduling
8770
croner = "3"
8871

8972
# Stripe payments
90-
async-stripe = { version = "0.38", default-features = false, features = ["runtime-tokio-hyper", "checkout", "billing", "connect", "webhook-events"] }
73+
# "billing" alone doesn't compile in async-stripe 0.38 (its quote resource
74+
# references checkout types), so checkout stays enabled alongside it.
75+
async-stripe = { version = "0.38", default-features = false, features = ["runtime-tokio-hyper", "checkout", "billing"] }
9176
hmac = "0.12"
9277

9378
# OpenAPI

0 commit comments

Comments
 (0)