Spacehub v2 — Feature & Implementation Plan
Greenfield TypeScript rewrite of Spacehub23 (XAF Blazor + XPO + SQL Server). Stack: Turborepo + Next.js 15 + Hono + Drizzle + Postgres 16 + Flutter mobile. Strategy: strangler fig per domain, aggressive simplification.
GET /v2/health returns
{"status":"ok","db":"up"}.
How to read this plan
Each domain page follows the same shape: Scope (what's in / out) → Library picks (with rationale) → Data model (Drizzle sketch) → API surface (Hono routes) → Build steps (numbered, copy-pasteable) → Open questions (need your call before building).
Read the pages in the order shown in the sidebar. Architecture first, then Phases, then domain pages.
Feature map
Auth & sessions
Email/password + JWT for mobile, httpOnly cookie for web. SMS OTP for tenants. Refresh-token rotation.
Phase 1Identity & ACL
User / Profile / PropertyOwner hierarchy. Postgres RLS replaces XAF custom operators.
Phase 1Property & rooms
Property → Floor → Room hierarchy. Floor plans, pricing, owner assignment.
Phase 1Contracts
Single Contract entity with payment schedule (jsonb where possible). Explicit transition functions.
Phase 2Billing & invoices
Unified Invoice (kind: rent/utility/sale/discount). BigInt Money. Kills decimal-equality bug.
Phase 3Payments
QPay + SocialPay clients. Webhook handlers with signature verification.
Phase 4Bank reconciliation
Khan / Golomt / TDB statement import. Indexed fuzzy match — fixes O(N·M) leak.
Phase 4eBarimt 3.0
Sale → outbox → BullMQ worker → eBarimt REST. Replaces fire-and-forget post-save hook.
Phase 4SMS & push
131344 SMS gateway client, Firebase Admin (FCM v1), per-device token registry.
Phase 5Reports
React-PDF or Puppeteer for invoice/statement PDFs. Replaces XtraReport DB blobs.
Phase 5Chatbot
Gemini 2.5 streaming via Vercel AI SDK. Port 10 function-calling tools. PO whitelist → config.
Phase 5File storage
S3-compatible (R2 / MinIO). Presigned uploads. sharp thumbnails.
Background jobs
BullMQ + Redis. Replaces external HTTP cron triggers. Daily contract-expiry SMS, bill generation, outbox drain.
PlatformOpenAPI & Flutter
Zod → OpenAPI (auto). Flutter regen via swagger_parser. Single API for web + mobile.
Observability
Pino + Sentry + OpenTelemetry. Request-id correlation. Drizzle query traces.
PlatformDevOps & hosting
Hetzner + Coolify (recommended). GitHub Actions + Turborepo remote cache. Docker multi-stage.
PlatformHeadline simplifications vs Spacehub23
| v1 (XAF) | v2 (TS) | Win |
|---|---|---|
| 321 BO files, composite keys, 2-tier status enums | ~70 tables, UUID PKs, single status enum + transition fn | Kills KNOWN_ISSUES #3 desync |
| 52 fat XAF ViewControllers (popup choreography) | Hono route handlers + Next.js server components | ~70% LOC drop in controller layer |
| XAF PermissionPolicy + custom operators (implicit) | Postgres RLS policies (DB-enforced, explicit) | ACL bugs structurally impossible |
| Decimal == / <= payment comparison | BigInt Money + compare() | Kills KNOWN_ISSUES #2 (0.25₮ underpaid) |
| Fire-and-forget post-save hooks | Outbox table + BullMQ worker | Retryable, observable, no race |
| External HTTP cron (cron-job.org) | BullMQ repeatable jobs in-process | No external dep, idempotent by ID |
XtraReport blobs in DB (ReportDataV2) | React-PDF templates in repo (code-reviewed) | Templates versioned, diffable |
| 2 enum systems per status entity | 1 enum + explicit transitionTo() | Kills desync class of bugs |
| 85 enum files (many high-cardinality) | ~25 enums + reference tables for Bank/Region/etc. | Add new bank without code change |
| 3 .NET services (Blazor, ApiV2, Automation) | 3 TS apps (web, api, workers) — same model + lang | Shared types, single deploy story |
Open architectural questions (you decide on return)
Each is summarized on the linked page; pick before building the feature slice.
- Auth lib: better-auth vs Auth.js vs roll-our-own with
jose+argon2. - RLS bridging:
SET LOCALin transaction-per-request vs JWT-claim pattern (Supabase style). - Money type: keep custom 50-LOC BigInt impl vs adopt
dinero.jsv2. - Job queue: BullMQ (Redis) vs
pg-boss(Postgres-only — drops a moving part). - PDF lib: React-PDF (declarative) vs Puppeteer (HTML fidelity).
- File storage: Cloudflare R2 (cheap egress) vs self-host MinIO on the existing VPS.
- Hosting: Vercel (web) + Hetzner+Coolify (api) vs all-in-one on Hetzner.
- Migration: dual-write only vs Debezium CDC SQL Server ↔ Postgres.