NRI waitlist landing, lifelike cursor, signup notifications & deploy hardening#2
Merged
Merged
Conversation
…ploy hardening Pre-launch interest-capture flow for the OperatorOS NRI pivot, plus the deploy fixes needed to ship it on a VPS. Frontend - CustomCursor: bespoke lifelike pointer (trailing ring + magnetic snap, morph/press states, reduced-motion & touch fallback), mounted globally - Landing: CTAs route to a Textura waitlist modal (WaitlistProvider) with explicit submit states; demo-login hint + SEO/OG meta - Dashboard: admin-only "Export leads" button (authenticated blob download) Backend - Waitlist: public idempotent POST + admin GET + CSV export (formula-injection guarded); model, schema, route, migration 004 - Notifications: in-app bell (notification_service) + Celery email/Telegram delivery (HTML-escaped), dispatched off the request path via BackgroundTasks - Demo seed relocated to app/seed.py (runnable as `python -m app.seed`) Fixes / hardening - config: CORS_ORIGINS NoDecode (pydantic-settings 2.x boot crash) - requirements: pin bcrypt==4.0.1 (passlib 1.7.4 incompatibility) - prod compose: close Postgres/Redis ports, pass DOMAIN to Caddy - Makefile: `make deploy` (build + migrate + seed, idempotent) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Automates the full server-side deploy: Docker install, firewall, nip.io domain fallback (real HTTPS without buying a domain), .env generation with fresh secrets, then build + migrate + seed. User only needs to provision the box; everything after `git clone` is one command. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
test_redis_url_without_password asserted CELERY_* defaults but didn't set them, so they fell through to CI service-container env vars (localhost) and failed. Pass all three URLs explicitly. Fixes the failing backend pytest job.
Migration 001 hardcoded several non-native Enum columns as VARCHAR(11), but the ORM models size them to their longest value. The mismatch made valid values un-insertable (caught by a real seed run against Postgres): - clients.entity_type 11 -> 15 (private_limited / public_limited) - compliance_tasks.task_type 11 -> 17 (professional_tax) - compliance_tasks.status 11 -> 12 (under_review) Without this, the app cannot onboard a Pvt Ltd client or set a task to \"under review\" — a pre-existing defect in core functionality. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- seed: add 3 compliance tasks due within the next 7 days so the dashboard \"Upcoming Deadlines\" panel is populated instead of empty - fix dashboard \"Recent Activity\": the panel read activity.items, but the API returns typed buckets (recent_queries / recent_documents / recent_computations). Merge them into one time-sorted ActivityItem feed so seeded queries/computations actually render (frontend<->API shape mismatch) - vite: add preview-server /api proxy (changeOrigin:false) so the prod build can be run locally against the dockerized backend, mirroring prod nginx Host behaviour Verified live: both panels populate against the real seeded backend. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds 20 tests (suite 159 -> 179, all green): - test_waitlist: POST create, idempotency-on-email (no duplicate row), email normalisation, admin-only list/export auth, CSV export + formula-injection guard, and _csv_safe unit cases - test_notification_service: notify_staff fans out one notification per active staffer in its own session, swallows DB errors without raising (signup stays safe), enqueue_signup_delivery is broker-failure-safe, create_notification adds - test_migrations: DB-free guard that the Alembic graph has a single head and a linear chain including 004_waitlist and 005_fix_enum_lengths Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pre-launch interest-capture flow for the OperatorOS NRI pivot, plus the deploy fixes needed to ship it on a VPS. Goal: a polished, working demo that generates interest before building the full subscription machinery.
Frontend
data-cursor-label), 60fps via direct DOM writes, with reduced-motion & touch fallback. Mounted globally.WaitlistProvider) with explicit submit states (no silent failures); demo-login hint + SEO/OG meta.Backend
POST /api/waitlist, adminGET, andGET /export.csv; model, schema, route, Alembic migration004_waitlist.notification_service) + Celery email/Telegram delivery, dispatched off the request path viaBackgroundTasks(broker-failure-safe).app/seed.py(python -m app.seed) so it's baked into the backend image; India/NRI demo data fills every dashboard surface.Fixes / hardening
config.py:CORS_ORIGINSNoDecode— fixes pydantic-settings 2.x boot crash on a fresh build.requirements.txt: pinbcrypt==4.0.1— passlib 1.7.4 incompatibility that breaks all logins on a fresh build.DOMAINto Caddy (was issuing TLS for the wrong domain).Makefile:make deploy(build → migrate → seed, idempotent).🔒 Security (please review)
export.csvprefixes values starting with= + - @to neutralize Excel/Sheets formula execution.html.escape()d before sending withparse_mode=HTML(flagged by automated review, fixed in this branch).require_api_key_or_admin; the UI role gate is defense-in-depth only.Verification
004)tsc --noEmitclean,vite buildsucceedsmake deployDeploy
export DOMAIN=… && make deploy(set.envfrom.env.examplefirst). Demo login seeded:demo@operatoros.in/OperatorOS#2026.🤖 Generated with Claude Code