尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Rental property listing
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗A rental-listing marketplace for Americans relocating to Germany — military and expat. It pairs the structure of a real-estate portal with the low-friction posting of a classifieds board, drawing on two supply sources:
Every self-listed submission is reviewed by an admin before it goes live, and listings come down the day they're rented. See CLAUDE.md for the full product brief; design-reference/milhaus-landing-mockup.html is the source of truth for visual direction (colours, type, layout).
Admin dashboard — the review queue, the live-listings table with one-click status changes, and the archive. Built to be run solo by a non-technical stakeholder.
Listings and photos above are demo data (node scripts/seed-demo-listings.mjs);
the photos are CC0.
MVP feature-complete against the build plan in CLAUDE.md (scaffold → design system → browse → listing detail → auth → post-a-listing → admin dashboard → SEO → monitoring). Not yet built: legal pages (Impressum, privacy, terms), a lister-facing "my listings" area, email notifications, custom SMTP for production. Not in scope: car listings, payments, housing-office bulk import.
| Framework | Next.js 16 (App Router, TypeScript, Turbopack) |
| Backend | Supabase — Postgres + Row Level Security, Auth (magic link), Storage |
| Styling | Tailwind CSS v4 (theme derived from the mockup's design tokens) |
| i18n | next-intl — English (default, no prefix) and German (/de) |
| Hosting | Vercel |
| Monitoring | Sentry (@sentry/nextjs) + Vercel Analytics |
supabase db pushnpm install
cp .env.example .env.local # fill in the values below
npm run dev # http://localhost:3000
All are documented inline in .env.example. The three you need to run the app at all:
| Variable | Where to find it |
|---|---|
NEXT_PUBLIC_SUPABASE_URL | Supabase dashboard → Project Settings → API |
NEXT_PUBLIC_SUPABASE_ANON_KEY | same page — safe to expose, RLS-constrained |
SUPABASE_SERVICE_ROLE_KEY | same page — server-only, bypasses RLS (used by the seed script and the dev sign-in route) |
Optional: NEXT_PUBLIC_SITE_URL, NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION,
NEXT_PUBLIC_SENTRY_DSN, and SENTRY_ORG / SENTRY_PROJECT /
SENTRY_AUTH_TOKEN (source-map upload only). Everything degrades gracefully
when they're unset. Never commit .env.local.
Full walkthrough in supabase/SETUP.md. In short:
20260821095414_init_schema.sql then the rest into the SQL Editor,
or supabase link + supabase db push). This creates the profiles and
listings tables, their RLS policies, and the listing-photos bucket.http://localhost:3000/** (plus the
deployed domain) to Redirect URLs.update public.profiles set role = 'admin' where contact_email = '[email protected]';
The shared Supabase mailer is capped at 2 emails/hour, and magic-link tokens get spent by email/browser link scanners. So for local work:
http://localhost:3000/auth/[email protected]
Signs you in with no email round-trip. It 404s unless NODE_ENV is
development and the request is on localhost — inert in production.
The real magic-link flow (/sign-in) also works: it delivers both a
click-to-confirm link and a 6-digit code, either of which completes sign-in.
node scripts/seed-demo-listings.mjs
Creates two demo owner accounts and a handful of listings with real CC0 photos, for showing the site before real inventory exists. Rerunnable; see the header comment in the script to remove the demo data.
| Command | |
|---|---|
npm run dev | Dev server (Turbopack) on :3000 |
npm run build | Production build |
npm run start | Serve the production build |
npm run lint | ESLint |
src/
app/
[locale]/ Public site — locale-scoped (/, /de/...)
page.tsx Landing + browse (grid, filters, stamp badges)
listings/[id]/ Listing detail
post/ Post-a-listing form (auth required)
sign-in/ Magic-link sign-in
for-landlords/ Marketing
how-verification-works/
admin/ Admin dashboard — English-only, outside [locale]
page.tsx Review queue + live/archived tables
listings/new/ Admin add-a-listing
users/ Role management
auth/ Auth route handlers (outside [locale])
confirm/ Magic-link interstitial (button → server action)
dev-signin/ Dev-only sign-in shortcut
sign-out/
sitemap.ts, robots.ts, icon.tsx, apple-icon.tsx
components/ UI (listing card, stamp badge, filter modal, forms, ...)
lib/
supabase/ Browser + server client factories
listings.ts, profiles.ts Typed server-side reads
types.ts Shared domain types (mirrors the DB)
bases.ts, amenities.ts, roles.ts Fixed vocab + the admin-role check
i18n/ next-intl routing/navigation
proxy.ts Middleware: locale routing + Supabase session refresh
messages/ en.json / de.json translation catalogues
supabase/ Migrations, templates, SETUP.md
design-reference/ The approved landing-page mockup
scripts/ Demo-data seed + its CC0 photos
Two tables (see supabase/migrations/20260821095414_init_schema.sql):
listings — type (rental, enum-extensible for a future car),
title/description, address/city/base/distance_to_base, price, beds,
baths, size, available_from, photos[], amenities[],
source (housing_office | self_listed),
status (draft → pending_review → active → rented → archived),
is_featured, is_promoted (reserved for a future paid tier), owner_id.profiles — one row per auth user; role is one of
owner · admin · housing_office_partner · landlord ·
individual_lister.Security lives in the database. RLS scopes every read (the public sees
only status = 'active'; owners see their own; admins see all), and DB
triggers enforce the invariants — a non-admin can't flip a listing to
active, and a non-admin can't escalate their own role. The Next.js layer
mostly shapes queries and trusts RLS to do the gatekeeping.
owner and admin are functionally equal today (isAdminRole in
src/lib/roles.ts is the single check); owner is the
stakeholder, admin is whoever operates the site.
English is unprefixed (/, /listings/…); German is /de/…. Locale
detection is off — a fresh page load always starts in English; switching
to German carries forward only through in-app navigation. The /admin tree
is English-only and sits outside the [locale] segment. Sitemap and metadata
emit hreflang alternates for both.
.env.local, with a real
NEXT_PUBLIC_SITE_URL).https://<domain>/** to
Redirect URLs.NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION.The build makes no filesystem or long-running-process assumptions — it's serverless-safe as-is.
// src/app/listings/page.tsx. Hard rule."use client" only where interactivity needs
it.Work happens on feature branches with a PR into main. Keep the file-path
header comment on every file, and prefer widening a CHECK constraint in a
new migration over reaching for free-text where a fixed vocab exists
(bases.ts, amenities.ts).