Skip to content

Add production deployment path for Rent-Ruby - #1

Merged
fsu9913-gif merged 4 commits into
mainfrom
cursor/make-rent-ruby-live-1bd0
Jun 20, 2026
Merged

Add production deployment path for Rent-Ruby#1
fsu9913-gif merged 4 commits into
mainfrom
cursor/make-rent-ruby-live-1bd0

Conversation

@fsu9913-gif

@fsu9913-gif fsu9913-gif commented Jun 20, 2026

Copy link
Copy Markdown

Summary

  • Add a Cloud Run-compatible Dockerfile and production npm start path for the full-stack Express/Vite app.
  • Honor platform PORT, support configurable SQLite DATABASE_PATH, and add a /healthz endpoint.
  • Seed and serve the missing Legal Library 2026 API routes used by the admin UI.
  • Harden Legal Library usage logging: validate document IDs, rate-limit per client IP, and avoid trusting client-supplied email for audit identity.
  • Update launch metadata/docs from legacy SILVERBACKAI copy to Rent-Ruby and document custom-domain deployment steps with an explicit CANONICAL_DOMAIN variable.
  • Fix the missing LayoutGrid icon import that blocked TypeScript compilation.
  • Add an emergency Firebase Hosting owner-showcase mode for tomorrow's meeting:
    • firebase.json + .firebaserc targeting the existing Firebase project gen-lang-client-0013150741.
    • npm run build:showcase predeploy builds VITE_STATIC_SHOWCASE=true.
    • Homepage now includes the product tour, smart-building intelligence, and a polished owner rent-roll snapshot.
    • Tenant Portal supports demoMode with seeded static data and no Firebase/API calls.
    • Tenant demo now opens Mailbox 105 by default and highlights Rent, Maint., Notice to Enter, Lease Updates, and Construction as the 94609 legal-ready portal items.
    • Owner/Admin static mode limits tabs to safe Portfolio + Rent Roll snapshot.
    • Neighborhood mosaic now uses working remote image URLs.
  • Add standalone fallback pages that work without backend/Firebase login:
    • /owner-demo.html for the Rent-Ruby owner meeting, now focused on Mailbox 105, hospital/transit cover map, Chase deposit rent-roll sync, offsite archives, owner safeguards, and contact hello@rent-ruby / 415-900-8563.
    • /walk-hike-ride-oakland.html using 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.
  • Fold in the working Manus page positioning from https://rent-ruby.manus.space:
    • rentruby.manus.space returns 404, but rent-ruby.manus.space is live.
    • Added Ruby at Mosswood, Oakland's Epicenter, and healthcare-worker location framing.
    • Aligned cover stats: MacArthur BART 4 min walk, Kaiser 5 min drive, Alta Bates 8 min drive, UCSF Benioff Children's 10 min drive, Highland 12 min drive.
  • Expand owner-protection messaging:
    • Automatic legal notices for Notice to Enter, lease updates, and construction notifications.
    • Timestamped read receipts: sent, opened, viewed, acknowledged.
    • Tenant signature evidence: unit, timestamp, IP/domain, device, and location approximation.
    • Sublease/guest-policy acknowledgments and false-claim defense timeline.
    • Offsite archive for notices, signatures, payment evidence, and maintenance records.
    • Rent roll updates when Chase deposits download, with payment matching and exception flags.
  • Update visible public contact info to hello@rent-ruby and 415-900-8563 in 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)
  • Static preview with npx vite preview --host 127.0.0.1 --port 4180
  • Static routes /owner-demo.html and /walk-hike-ride-oakland.html returned 200 and include the Manus-derived hospital/transit copy ✅
  • Compiled React bundle includes hello@rent-ruby / 415-900-8563
  • /owner-demo.html includes 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.html includes hospital/transit time cards for Kaiser, Alta Bates/Summit, UCSF Children's Oakland, and MacArthur BART ✅
  • Production smoke server with PORT=4173 DATABASE_PATH=/tmp/rent-ruby-smoke.db npm start
  • GET /healthz returned 200 ✅
  • GET /api/legal-library-2026 returned 200 with 5 seeded documents ✅
  • POST /api/legal-library-usage returned 200 for a valid document ✅
  • POST /api/legal-library-usage returned 404 for an invalid document ✅
  • DB check confirmed forged client email was stored as Anonymous
  • GET / returned 200 and included Rent-Ruby metadata ✅

Deployment status / blocker

  • rent-ruby.com and www.rent-ruby.com currently resolve through Cloudflare and serve a default My Google AI Studio App page.
  • rent-rubyl.com does not resolve in DNS.
  • rentruby.com redirects to https://localrealtors.com/.
  • Firebase MCP sees the project/config but has no authenticated Firebase user. firebase_login generated a browser login link and requires a human auth code; firebase_deploy --only hosting fails until login is completed.
  • Cloudflare MCP returned 403 Authentication error, and gcloud/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:

npx -y firebase-tools@latest login
npx -y firebase-tools@latest use gen-lang-client-0013150741
npx -y firebase-tools@latest deploy --only hosting

That will publish the static owner showcase and fallback pages to:

  • https://gen-lang-client-0013150741.web.app
  • https://gen-lang-client-0013150741.firebaseapp.com
  • https://gen-lang-client-0013150741.web.app/owner-demo.html
  • https://gen-lang-client-0013150741.web.app/walk-hike-ride-oakland.html

For a temporary preview instead of replacing the live Hosting release:

npx -y firebase-tools@latest hosting:channel:deploy owner-showcase --expires 7d

The full interactive app still needs the Node-capable Cloud Run path and domain routing.

Slack Thread

Open in Web Open in Cursor 

Comment thread server.ts
Comment on lines +823 to +826
app.get("/api/legal-library-2026", (req, res) => {
const documents = db.prepare("SELECT * FROM legal_library_2026 ORDER BY is_mandatory DESC, category, title").all();
res.json(documents);
});
Comment thread server.ts Fixed
Comment thread server.ts
Comment on lines +861 to +880
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" });
});
@fsu9913-gif

Copy link
Copy Markdown
Author

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

@fsu9913-gif

Copy link
Copy Markdown
Author

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.

@fsu9913-gif
fsu9913-gif marked this pull request as ready for review June 20, 2026 17:07
Copilot AI review requested due to automatic review settings June 20, 2026 17:07
@fsu9913-gif
fsu9913-gif merged commit 9b79992 into main Jun 20, 2026
2 of 3 checks passed
@fsu9913-gif
fsu9913-gif deleted the cursor/make-rent-ruby-live-1bd0 branch June 20, 2026 17:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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/preview scripts, support PORT + configurable SQLite DATABASE_PATH, and add a /healthz endpoint.
  • 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

  • useEffect registers an onAuthStateChanged listener (and nested onSnapshot listeners) but never returns a cleanup function. Also, the return () => unsubCustom() inside the auth callback is ignored by Firebase, so Firestore listeners will leak on auth changes and component unmount (and now also whenever demoMode toggles).
    // 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.

Comment thread src/App.tsx
Comment on lines +114 to +125
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 }
];
Comment thread src/App.tsx
{adminTabs.map((tab) => (
<button
key={tab.id}
onClick={() => setAdminTab(tab.id as any)}
Comment thread src/App.tsx
Comment on lines +107 to +108
const isStaticShowcase = (import.meta as any).env?.VITE_STATIC_SHOWCASE === 'true';
const [view, setView] = useState<AppView>(getInitialView);
Comment thread server.ts
Comment on lines 565 to 570
async function startServer() {
const app = express();
const PORT = 3000;
const PORT = Number(process.env.PORT) || 3000;

app.use(express.json());
app.use(cookieParser());
Comment thread server.ts
Comment thread server.ts
Comment on lines +33 to +51
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;
};

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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".

Comment thread server.ts
return forwardedFor[0] || req.ip || "unknown";
}

return forwardedFor?.split(",")[0]?.trim() || req.ip || "unknown";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

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.

4 participants