Add production deployment path for Rent-Ruby - #1
Conversation
| app.post("/api/legal-library-usage", (req, res) => { | ||
| const documentId = Number(req.body?.document_id); | ||
| if (!Number.isInteger(documentId) || documentId < 1) { | ||
| return res.status(400).json({ error: "document_id is required" }); | ||
| } | ||
|
|
||
| const document = db.prepare("SELECT id FROM legal_library_2026 WHERE id = ?").get(documentId); | ||
| if (!document) { | ||
| return res.status(404).json({ error: "document not found" }); | ||
| } | ||
|
|
||
| const clientIp = getClientIp(req); | ||
| if (!canLogLegalLibraryUsage(clientIp)) { | ||
| return res.status(429).json({ error: "too many usage events" }); | ||
| } | ||
|
|
||
| db.prepare("INSERT INTO legal_library_usage (document_id, user_email) VALUES (?, ?)") | ||
| .run(documentId, "Anonymous"); | ||
| res.json({ status: "ok" }); | ||
| }); |
|
What the emergency would be just getting a get three pages up on the site you know rent ruby.com and what the homepage would look like it doesn't have to be you know like you don't have to double click into it and then the second page would be the detective portal interface and you don't bullet points on what they would have but you could forget the law library why would we want to give them a lawyer that would just become more can of worms delete that and yeah, just like you know lease update time tracking you know the ability to track sub leases that are legal. You'll be IP addresses and things of that nature because that's a real problem you can't kick anybody out in Oakland Oakland but the biggest thing we can do is prevent subs that are legal that are improved by ownership so yeah then if you get me like a really dialed in Renne roll with the 1986 Celtics as the Tenants and then the 1986 Lakers as the other 1210s of 24 units you know then I can go from there was explaining what you know what the purchase gonna be all that but just picture the best red roll available for landlord and that's what we're gonna have |
|
B. rent Ruby and get it and get him that I did where I said why are we using this one or something like that? There's also one that Brent Ruby.manus.space or something like that that actually doesn't look too bad but I've seen a couple versions that look decent so if it's not too hard, but it can come from get home then so be it but it doesn't have to be a Google studio site. I just like the Google studio site because the whole thing you know. |
There was a problem hiding this comment.
Pull request overview
This PR adds a production-friendly deployment path (Cloud Run + npm start) for the Rent-Ruby full-stack Express/Vite app, and introduces a static “owner showcase” mode for meeting-safe demos (Firebase Hosting) while also seeding/serving the missing Legal Library 2026 API.
Changes:
- Add Cloud Run Dockerfile + production
start/previewscripts, supportPORT+ configurable SQLiteDATABASE_PATH, and add a/healthzendpoint. - Add Legal Library 2026 schema + seed data + API routes, with basic usage logging + rate limiting.
- Add static showcase mode (
VITE_STATIC_SHOWCASE) with an owner snapshot component and tenant portal demo data; update branding/metadata/docs accordingly.
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
server.ts |
Adds configurable SQLite path, /healthz, Legal Library tables + seed + routes, and rate limiting logic. |
Dockerfile |
Adds Cloud Run-compatible container build and runtime command. |
package.json |
Adds production start, adjusts preview, and adds build:showcase. |
README.md |
Updates project docs for local/prod/Cloud Run + Firebase showcase steps. |
src/App.tsx |
Adds static showcase view gating and integrates showcase snapshot + demo Tenant Portal mode. |
src/components/TenantPortal.tsx |
Adds demoMode + initialTab props and seeds demo state to avoid Firebase/API calls. |
src/components/OwnerShowcaseSnapshot.tsx |
New owner-facing snapshot component for static showcase / homepage. |
src/components/NeighborhoodMosaic.tsx |
Switches neighborhood images to remote URLs for showcase reliability. |
index.html |
Updates title/meta/OG/Twitter metadata to Rent-Ruby branding. |
firebase.json / .firebaserc |
Adds Firebase Hosting config for static showcase deployment. |
.env.example / .dockerignore |
Documents env vars and improves container build hygiene. |
package-lock.json |
Locks dependency updates consistent with script/runtime changes. |
Comments suppressed due to low confidence (1)
src/components/TenantPortal.tsx:244
useEffectregisters anonAuthStateChangedlistener (and nestedonSnapshotlisteners) but never returns a cleanup function. Also, thereturn () => unsubCustom()inside the auth callback is ignored by Firebase, so Firestore listeners will leak on auth changes and component unmount (and now also wheneverdemoModetoggles).
// Auth and Firebase Sync
const unsubscribeAuth = onAuthStateChanged(auth, (user) => {
if (user) {
// Set unit based on email for trial focus
if (user.email === 'fsu9913@gmail.com') {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const adminTabs = isStaticShowcase | ||
| ? [ | ||
| { id: 'portfolio', label: 'Portfolio', icon: LayoutGrid }, | ||
| { id: 'rent-roll', label: 'Rent Roll', icon: FileText }, | ||
| ] | ||
| : [ | ||
| { id: 'portfolio', label: 'Portfolio', icon: LayoutGrid }, | ||
| { id: 'rent-roll', label: 'Rent Roll', icon: FileText }, | ||
| { id: 'maintenance', label: 'Ops', icon: Wrench }, | ||
| { id: 'ceo', label: 'CEO Brief', icon: Activity }, | ||
| { id: 'vendors', label: 'Vendors', icon: ShieldCheck } | ||
| ]; |
| {adminTabs.map((tab) => ( | ||
| <button | ||
| key={tab.id} | ||
| onClick={() => setAdminTab(tab.id as any)} |
| const isStaticShowcase = (import.meta as any).env?.VITE_STATIC_SHOWCASE === 'true'; | ||
| const [view, setView] = useState<AppView>(getInitialView); |
| async function startServer() { | ||
| const app = express(); | ||
| const PORT = 3000; | ||
| const PORT = Number(process.env.PORT) || 3000; | ||
|
|
||
| app.use(express.json()); | ||
| app.use(cookieParser()); |
| const canLogLegalLibraryUsage = (clientIp: string) => { | ||
| const now = Date.now(); | ||
| const current = legalLibraryUsageRequests.get(clientIp); | ||
|
|
||
| if (!current || current.resetAt <= now) { | ||
| legalLibraryUsageRequests.set(clientIp, { | ||
| count: 1, | ||
| resetAt: now + LEGAL_LIBRARY_USAGE_WINDOW_MS, | ||
| }); | ||
| return true; | ||
| } | ||
|
|
||
| if (current.count >= LEGAL_LIBRARY_USAGE_MAX_PER_WINDOW) { | ||
| return false; | ||
| } | ||
|
|
||
| current.count += 1; | ||
| return true; | ||
| }; |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1d727bf360
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return forwardedFor[0] || req.ip || "unknown"; | ||
| } | ||
|
|
||
| return forwardedFor?.split(",")[0]?.trim() || req.ip || "unknown"; |
There was a problem hiding this comment.
Derive rate-limit keys from trusted client addresses
When this public endpoint is behind Cloud Run/Express, callers can send their own X-Forwarded-For header; because this code uses the first header value directly as the rate-limit key, a client can change the header on each POST to /api/legal-library-usage and avoid the 30/min cap. Key the limiter from a trusted proxy-derived address instead, e.g. configure trust proxy and use req.ip, or sanitize the forwarded chain.
Useful? React with 👍 / 👎.
Summary
npm startpath for the full-stack Express/Vite app.PORT, support configurable SQLiteDATABASE_PATH, and add a/healthzendpoint.CANONICAL_DOMAINvariable.LayoutGridicon import that blocked TypeScript compilation.firebase.json+.firebaserctargeting the existing Firebase projectgen-lang-client-0013150741.npm run build:showcasepredeploy buildsVITE_STATIC_SHOWCASE=true.demoModewith seeded static data and no Firebase/API calls./owner-demo.htmlfor the Rent-Ruby owner meeting, now focused on Mailbox 105, hospital/transit cover map, Chase deposit rent-roll sync, offsite archives, owner safeguards, and contacthello@rent-ruby/415-900-8563./walk-hike-ride-oakland.htmlusing the dark Silverback-style template adapted for Rent-Ruby neighborhood storytelling, nurse/healthcare-worker audience copy, hospital/transit map overlay, and the same contact details.https://rent-ruby.manus.space:rentruby.manus.spacereturns 404, butrent-ruby.manus.spaceis live.Ruby at Mosswood,Oakland's Epicenter, and healthcare-worker location framing.hello@rent-rubyand415-900-8563in the app footer, Tenant Portal, and static fallback pages.Verification
npm run lint✅npm run build✅ (Vite reports a large bundle warning only)npm run build:showcase✅ (same large bundle warning only)npx vite preview --host 127.0.0.1 --port 4180✅/owner-demo.htmland/walk-hike-ride-oakland.htmlreturned 200 and include the Manus-derived hospital/transit copy ✅hello@rent-ruby/415-900-8563✅/owner-demo.htmlincludes hospital/transit map copy, Kaiser, MacArthur BART, Owner Safeguards, Timestamped Read Receipts, Signature Evidence, Sublease Protection, Offsite Archive, False-Claim Defense, and Chase deposit sync copy ✅/walk-hike-ride-oakland.htmlincludes hospital/transit time cards for Kaiser, Alta Bates/Summit, UCSF Children's Oakland, and MacArthur BART ✅PORT=4173 DATABASE_PATH=/tmp/rent-ruby-smoke.db npm start✅GET /healthzreturned 200 ✅GET /api/legal-library-2026returned 200 with 5 seeded documents ✅POST /api/legal-library-usagereturned 200 for a valid document ✅POST /api/legal-library-usagereturned 404 for an invalid document ✅Anonymous✅GET /returned 200 and included Rent-Ruby metadata ✅Deployment status / blocker
rent-ruby.comandwww.rent-ruby.comcurrently resolve through Cloudflare and serve a defaultMy Google AI Studio Apppage.rent-rubyl.comdoes not resolve in DNS.rentruby.comredirects tohttps://localrealtors.com/.firebase_logingenerated a browser login link and requires a human auth code;firebase_deploy --only hostingfails until login is completed.403 Authentication error, andgcloud/Docker are not installed in this agent environment, so I could not change live DNS/edge routing or run an actual Cloud Run deploy from here.Fastest meeting-ready live step
Authenticate Firebase, then run:
That will publish the static owner showcase and fallback pages to:
https://gen-lang-client-0013150741.web.apphttps://gen-lang-client-0013150741.firebaseapp.comhttps://gen-lang-client-0013150741.web.app/owner-demo.htmlhttps://gen-lang-client-0013150741.web.app/walk-hike-ride-oakland.htmlFor a temporary preview instead of replacing the live Hosting release:
The full interactive app still needs the Node-capable Cloud Run path and domain routing.
Slack Thread