Skip to content

Self host staging deploy#702

Open
feruzm wants to merge 5 commits intodevelopfrom
dp
Open

Self host staging deploy#702
feruzm wants to merge 5 commits intodevelopfrom
dp

Conversation

@feruzm
Copy link
Member

@feruzm feruzm commented Mar 14, 2026

Summary by CodeRabbit

  • New Features

    • Managed Hosting signup flow (username, configure, payment with Keychain/manual, success) and a hosting page
    • Custom domain support with CNAME setup instructions
  • Documentation

    • Deployment guide updated with pricing, setup steps, and payment memo format
  • Backend

    • Audit logging for account, subscription, upgrade and domain lifecycle events
    • CORS origin handling made configurable
  • Tests

    • New unit tests covering utilities, permlinks, RSS URL, and API memo/types parsing
  • Chores

    • CI/CD workflow for automated builds, image publish and deploys added

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an AuditService and integrates non-blocking audit logs across hosting API routes and payment processing; introduces a HostingSignup React flow with Hive Keychain payment and route; adds Vitest configs and multiple unit tests; switches docker-compose to image-based deploys and adds a multi-stage GitHub Actions workflow and deployment docs updates.

Changes

Cohort / File(s) Summary
CI/CD & Deployment
/.github/workflows/self-hosted.yml, apps/self-hosted/hosting/docker-compose.yml, apps/self-hosted/DEPLOYMENT.md
New multi-job GitHub Actions workflow (tests → build-blog/build-api → deploy-staging/deploy); docker-compose changed to use prebuilt images; DEPLOYMENT.md updated from "planned" to concrete instructions.
Testing config & packages
apps/self-hosted/vitest.config.ts, apps/self-hosted/hosting/api/vitest.config.ts, apps/self-hosted/package.json, apps/self-hosted/hosting/api/package.json
Added Vitest configs and test scripts; vitest added to deps/devDeps and test script entries introduced.
Audit logging service
apps/self-hosted/hosting/api/src/services/audit-service.ts
New AuditService and AuditEntry interface with fire-and-forget inserts into audit_log and parseClientIp helper; errors are logged but not thrown.
API audit integration
apps/self-hosted/hosting/api/src/routes/auth.ts, .../routes/domains.ts, .../routes/tenants.ts, .../payment-listener.ts
Integrated AuditService + client IP parsing into auth (/verify), domain add/verify/remove, tenant create/subscribe/upgrade/config/delete, and payment listener; logs emitted non-blocking with relevant metadata.
Frontend: Hosting signup & route
apps/self-hosted/src/features/hosting/components/hosting-signup.tsx, apps/self-hosted/src/routes/hosting.tsx, apps/self-hosted/src/routeTree.gen.ts
New HostingSignup component (username → configure → payment → success) with API integration, polling activation, Hive Keychain flow; new /hosting route and route-tree entries added.
Unit tests: utilities & types
apps/self-hosted/src/features/.../permlink.test.ts, apps/self-hosted/src/features/.../utils.test.ts, apps/self-hosted/src/utils/rss-feed-url.test.ts, apps/self-hosted/hosting/api/src/types.test.ts
Numerous new tests covering permlink generation, deepClone/update utilities, RSS feed URL behavior, and comprehensive memo/Tenant mapping tests.
Type/import adjustments & services
apps/self-hosted/hosting/api/src/services/domain-service.ts, .../tenant-service.ts, apps/self-hosted/hosting/api/src/services/tenant-service.ts
Adjusted import paths for shared types (../../types → ../types) and minor re-exports; no public signature changes.
Runtime/input tweaks
apps/self-hosted/src/utils/rss-feed-url.ts, apps/self-hosted/hosting/api/src/types.ts, apps/self-hosted/hosting/api/src/index.ts
RSS util trims and rejects whitespace-only IDs; parseMemo explicitly validates months > 0; CORS origins made dynamic using BASE_DOMAIN env var.
New tests & helpers
apps/self-hosted/src/features/hosting/components/*, apps/self-hosted/src/utils/*, apps/self-hosted/src/features/*
Added client-side tests and helpers (e.g., utils tests) supporting new frontend features.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/Browser
    participant HSC as HostingSignup (UI)
    participant API as Hosting API
    participant HK as Hive Keychain

    User->>HSC: Enter username
    HSC->>API: GET /v1/tenants/{username}/status
    API-->>HSC: availability / status
    User->>HSC: Submit config
    HSC->>API: POST /v1/tenants (username, config)
    API-->>HSC: Created + PaymentInstructions
    User->>HSC: Click "Pay with Keychain"
    HSC->>HK: Initiate transfer (to, amount, memo)
    HK-->>HSC: Transfer success
    HSC->>API: Poll GET /v1/tenants/{username}/status
    API-->>HSC: subscriptionStatus = "active"
    HSC->>User: Show success (onSuccess)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 A tiny hop, a logging beat,

Signup blossoms, payments meet,
Tests and images sail away,
CI hums through night and day,
Self-hosted burrow wakes—hooray! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Self host staging deploy' is vague and does not clearly convey the primary changes in this comprehensive pull request, which includes CI/CD pipeline setup, audit logging, hosting signup feature, and test infrastructure. Use a more descriptive title that captures the main objective, such as 'Add CI/CD pipeline, audit logging, and hosting signup feature' or 'Implement self-hosted deployment infrastructure and signup flow'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dp
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
apps/self-hosted/src/features/hosting/components/hosting-signup.tsx (1)

225-226: Move domain and price text out of hardcoded defaults.

Line 226 and Line 434-Line 435 hardcode blogs.ecency.com, 1 HBD/month, and 3 HBD/month, but the backend already makes BASE_DOMAIN, MONTHLY_PRICE_HBD, and PRO_UPGRADE_PRICE_HBD environment-driven in apps/self-hosted/hosting/api/src/routes/tenants.ts. That will drift on staging/self-hosted deployments.

Also applies to: 433-436

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/self-hosted/src/features/hosting/components/hosting-signup.tsx` around
lines 225 - 226, The UI hardcodes the domain and price strings; update the
HostingSignup component to stop using literal "blogs.ecency.com", "1 HBD/month",
and "3 HBD/month" and instead consume the environment-driven values the backend
exposes (BASE_DOMAIN, MONTHLY_PRICE_HBD, PRO_UPGRADE_PRICE_HBD). Locate the JSX
that renders the blog URL (the line showing "Your blog will be at: {username ||
'username'}.blogs.ecency.com") and the price lines (the monthly and pro upgrade
price text in the same component), and replace those string literals with values
provided by a prop or by fetching the tenant/settings endpoint used by
tenants.ts (or by importing a shared config if available) so the frontend
displays domain and prices from the backend-driven config rather than hardcoded
defaults.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/self-hosted/hosting/api/src/routes/tenants.ts`:
- Around line 108-114: Audit entries currently pass
c.req.header('x-forwarded-for') directly into parseClientIp (seen in the
AuditService.log calls), which allows client-controlled spoofing; update those
AuditService.log calls (at the spots using parseClientIp and
c.req.header('x-forwarded-for')) to derive the client IP from a trusted proxy
boundary instead of the raw header—for example, use the framework/request object
method that returns the remote peer IP (e.g., c.req.ip or
connection.remoteAddress) or a central trusted-proxy extractor configured once
and referenced here, then pass that trusted value into parseClientIp before
logging.

In `@apps/self-hosted/src/features/hosting/components/hosting-signup.tsx`:
- Around line 85-92: The check that only blocks active tenants is incorrect —
treat any existing tenant as unavailable. In the block that inspects the tenant
status (where you currently reference data.exists and data.subscriptionStatus),
change the logic to reject when data.exists is true (e.g., if (data.exists) {
setError('This username is already taken'); return; }) so unpaid/expired tenants
are blocked up front instead of letting setConfig/setStep advance to the
configure step and failing later on create.

---

Nitpick comments:
In `@apps/self-hosted/src/features/hosting/components/hosting-signup.tsx`:
- Around line 225-226: The UI hardcodes the domain and price strings; update the
HostingSignup component to stop using literal "blogs.ecency.com", "1 HBD/month",
and "3 HBD/month" and instead consume the environment-driven values the backend
exposes (BASE_DOMAIN, MONTHLY_PRICE_HBD, PRO_UPGRADE_PRICE_HBD). Locate the JSX
that renders the blog URL (the line showing "Your blog will be at: {username ||
'username'}.blogs.ecency.com") and the price lines (the monthly and pro upgrade
price text in the same component), and replace those string literals with values
provided by a prop or by fetching the tenant/settings endpoint used by
tenants.ts (or by importing a shared config if available) so the frontend
displays domain and prices from the backend-driven config rather than hardcoded
defaults.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b8291141-7b17-4943-93a1-d8ae15523c18

📥 Commits

Reviewing files that changed from the base of the PR and between 30a9080 and 0c2dcea.

📒 Files selected for processing (3)
  • .github/workflows/self-hosted.yml
  • apps/self-hosted/hosting/api/src/routes/tenants.ts
  • apps/self-hosted/src/features/hosting/components/hosting-signup.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/self-hosted.yml

Comment on lines +108 to +114
void AuditService.log({
tenantId: tenant.id,
eventType: 'tenant.created',
eventData: { username: body.username },
ipAddress: parseClientIp(c.req.header('x-forwarded-for')),
userAgent: c.req.header('user-agent'),
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't trust raw X-Forwarded-For for audit IPs.

Line 112 records c.req.header('x-forwarded-for') directly. Unless your ingress strips and rebuilds that header, it's client-controlled, so these new audit entries can be spoofed. The same pattern repeats at Line 237, Line 361, Line 402, and Line 464; please derive the IP at a trusted proxy boundary instead of trusting the raw header here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/self-hosted/hosting/api/src/routes/tenants.ts` around lines 108 - 114,
Audit entries currently pass c.req.header('x-forwarded-for') directly into
parseClientIp (seen in the AuditService.log calls), which allows
client-controlled spoofing; update those AuditService.log calls (at the spots
using parseClientIp and c.req.header('x-forwarded-for')) to derive the client IP
from a trusted proxy boundary instead of the raw header—for example, use the
framework/request object method that returns the remote peer IP (e.g., c.req.ip
or connection.remoteAddress) or a central trusted-proxy extractor configured
once and referenced here, then pass that trusted value into parseClientIp before
logging.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/self-hosted/hosting/docker-compose.yml (1)

77-77: Use bash parameter expansion to fail fast when TAG is missing.

The deployment workflow already exports TAG before running docker compose up -d, so the :-latest fallback only masks configuration errors in ad-hoc or miswired runs. Instead, use ${TAG:?TAG must be set} to fail immediately if TAG is unset or empty, surfacing wiring bugs rather than silently pulling the mutable latest image.

♻️ Suggested change
-    image: ecency/self-hosted:${TAG:-latest}
+    image: ecency/self-hosted:${TAG:?TAG must be set}

-    image: ecency/hosting-api:${TAG:-latest}
+    image: ecency/hosting-api:${TAG:?TAG must be set}

-    image: ecency/hosting-api:${TAG:-latest}
+    image: ecency/hosting-api:${TAG:?TAG must be set}

Also applies to: 125-125, 161-161

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/self-hosted/hosting/docker-compose.yml` at line 77, The compose file
currently uses the fallback expansion "image: ecency/self-hosted:${TAG:-latest}"
which hides missing TAG values; update this to use the failing expansion
"${TAG:?TAG must be set}" so docker compose will fail fast when TAG is unset or
empty (replace each occurrence of "image: ecency/self-hosted:${TAG:-latest}"
with "image: ecency/self-hosted:${TAG:?TAG must be set}" and apply the same
change for the other occurrences in the file).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/self-hosted/hosting/docker-compose.yml`:
- Line 77: The compose file currently uses the fallback expansion "image:
ecency/self-hosted:${TAG:-latest}" which hides missing TAG values; update this
to use the failing expansion "${TAG:?TAG must be set}" so docker compose will
fail fast when TAG is unset or empty (replace each occurrence of "image:
ecency/self-hosted:${TAG:-latest}" with "image: ecency/self-hosted:${TAG:?TAG
must be set}" and apply the same change for the other occurrences in the file).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b4190c89-e99b-45ac-8e16-05115e3b7e91

📥 Commits

Reviewing files that changed from the base of the PR and between 0c2dcea and 31bbb28.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • apps/self-hosted/hosting/api/src/index.ts
  • apps/self-hosted/hosting/api/src/services/audit-service.ts
  • apps/self-hosted/hosting/docker-compose.yml
  • apps/self-hosted/src/features/hosting/components/hosting-signup.tsx
  • apps/self-hosted/src/routeTree.gen.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant