尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
Tulip — AI Safety & Journey Planner for Delhi NCR
A safety-first journey planner for female commuters across Delhi, Noida, Gurugram, Ghaziabad, and Faridabad. Built on Next.js (App Router) + Supabase.檢視完整 README ↗A safety-first journey planner for female commuters across Delhi, Noida, Gurugram, Ghaziabad, and Faridabad. Built on Next.js (App Router) + Supabase.
Working with zero extra setup (beyond your Supabase project):
httpOnly cookie, valid for 1 week — after that, the user has to log in again. User records live in Supabase Postgres (public.users), accessed only via the service-role key; the browser never talks to Supabase directly for auth. Login always returns the same generic error whether the identifier doesn't exist or the password is wrong, by design — it stops the endpoint being used to check who has an account heresrc/components/AuthModal.tsx via RequireAuthGate) instead of a blanket login wall on the whole page. The header shows a "Sign In" pill for guests, or a rounded avatar with a dropdown (email, manage contacts, sign out) once authenticatedtel:, delete)sms: links and WhatsApp (wa.me) share buttons per contact, plus copy/open-in-MapswatchPosition; if the connection drops, pings are cached in localStorage and flushed automatically on the browser's online event, with an "Offline Mode — Route Cached Locally" banner while disconnected. A real shareable link (/track/[id]) lets a trusted contact — who has no Tulip account — watch that location update live on a map, polling a deliberately unauthenticated endpoint (/api/sos/[id]/public) where the alert's random id is itself the access token; it exposes only status/name/location, never account details, and stops updating once you stop trackingsrc/lib/riskTier.ts)src/components/SafetyMapContainer.tsx), and nearby-amenity dots along the route within 1 km — washrooms, hospitals, police stations, restaurants/cafes, and pharmacies/malls, each its own color/icon, sized larger the closer they are to the route, with a popup on click showing name/type/distance (live Overpass data) — plus a "recenter to my GPS" control. Restaurants/cafes were split into their own category (separate from pharmacy/mall) after finding that bundling them into one heavier query made restaurants disappear whenever that query happened to time out/bot page), backed by Google's Gemini API directly (/api/chat). Stateless by design — the client sends the recent conversation history with every request, and nothing is persisted server-side afterward, on this app's end or Google'ssrc/lib/sunTimes.ts) drives "after sunset" safety weighting, instead of a fixed 19:00–06:00 window that was off by over an hour depending on the season. During the day, live foot-traffic density is weighted more heavily than street lighting; after dark, that flipswatchPosition to a single low-accuracy fix every 5 minutes, and the Journey page swaps its map/search UI for a lightweight offline view built entirely from an IndexedDB cache (src/lib/offlineDb.ts) written the last time a route was searched online — turn-by-turn text directions (real OSRM steps), nearby hospitals/police/metro stations (live Overpass data, fetched once per search), tel: buttons for trusted contacts and emergency numbers, and a Check In Now button that opens the device's native SMS app (sms: URI) pre-filled with the cached location — no fetch/XHR involved, so it works purely over the cellular voice/SMS network with zero data. A service worker (public/sw.js) caches static build assets cache-first for faster reloads; API calls always go to the network live rather than serving stale cached data.Wired up but inert until you add credentials:
| Feature | Needs |
|---|---|
| Automatic SMS delivery for "Share My Location" (instead of the manual SMS/WhatsApp fallback) | TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_FROM_NUMBER |
| Ally chat replies | GEMINI_API_KEY (without it, /api/chat returns a clear "not configured" error rather than crashing) |
| Forgot-password reset emails | GMAIL_USER, GMAIL_APP_PASSWORD (a 16-character Google App Password, not the account's login password — requires 2-Step Verification enabled on that Google account). Without them, /api/auth/forgot-password still responds normally, but no email actually goes out — check server logs |
Not implemented — Google Earth Engine night-light scoring. Real Earth Engine access requires a Google Cloud service account approved for Earth Engine, which is a manual multi-day process on Google's side and can't be wired up in this session. The safety score currently uses OSM street-light + shop/amenity density as a documented stand-in (see src/lib/scoring.ts). Swap in Earth Engine later without changing the API shape.
Not implemented — live traffic congestion. The free OSRM routing server used for real distance/duration doesn't expose live congestion data, so the rush-hour score is a time-of-day heuristic (computeHeuristicRushScore in src/lib/scoring.ts), not a live delay ratio. A paid traffic API (Google Directions with traffic_model, or Mapbox) would replace this with real congestion data.
Rate limits to know about: Photon and the public OSRM demo server are shared, rate-limited community services meant for light/demo use, not production traffic. Fine for development and low-volume use; a production deployment should move to a paid or self-hosted instance of each (or a Google Maps Platform key, which requires a billing account but has a large free monthly credit) to avoid being rate-limited or blocked.
Not implemented — real Delhi Metro/DTC per-station routing. Delhi Metro/DTC don't publish a public real-time GTFS routing feed, so Metro/bus legs are distance-based time estimates (see the comment in src/app/api/routes/plan/route.ts), not real station-by-station itineraries.
Mock data — the safety map's heatmap/footfall markers. SafetyMapContainer seeds its heatmap and corridor markers from a small hardcoded list of well-known Delhi NCR locations, not a live feed — the route scoring itself (safety/rush scores shown on route cards) uses the real, live OSM signals described above. Swapping the map's mock points for a live source is a drop-in change in that one file.
Deliberately not using Mapbox. An earlier version of this map used Mapbox GL JS, but Mapbox's signup asked for a payment card, so the map was rebuilt on Leaflet + free OpenStreetMap tiles instead (with a CSS filter faking the dark look, after CARTO's free anonymous dark tiles also started requiring a key) — no key, no card, no billing account for anyone running this project.
Low Power / Offline Mode was adapted from a Mapbox/TomTom/Google Earth Engine-based spec to this app's actual stack (Leaflet, no TomTom, no Earth Engine, free OSM services) — anything in that spec tied to those specific services doesn't apply here. The nearby-help-points lookup uses the free Overpass API and can occasionally time out (504) under load on their shared public server, same as the other Overpass-based signals in this app; the offline view just shows no help points that time, rather than fabricating any.
4-tier risk badges and route amenity dots were also adapted from a Google Maps/Mapbox/Places/Earth-Engine-based spec — the multi-source "safety evaluation" described there uses services this app doesn't have; the 4-tier classification instead buckets the same live OSM street-light/foot-traffic score already computed for every route. While building the amenity dots, hitting the free Overpass API with 3 amenity-type queries in parallel reliably broke the third one — their public instance enforces a small concurrent-request limit per client. Fixed by querying sequentially (src/lib/helpPoints.ts); worth remembering for any future Overpass call added to this app.
Not implemented — full multi-stop route planner. A separate spec asked for reorderable intermediate stops (start → stop 1 → stop 2 → destination) with a single chained route. That's a genuinely large addition (multi-waypoint OSRM routing, a stop-reordering UI, rewriting the safety/fare scoring to work leg-by-leg) that wasn't built in this pass — flagged rather than silently skipped. The existing single origin→destination planner, autocomplete, and amenity-dot map all work today.
Not implemented — real Google/Mapbox route alternatives (multiple road variants per mode). OSRM's public server does support an alternatives=true param for a few different driving-route options, but per-mode alternative routing (e.g. a second Metro interchange option) isn't wired up — each mode currently returns one representative route.
/ — home: a welcome hub with quick links to Journey, Safety Tools, Contacts, and Ally/journey — the Journey search hero, safety map, and route results (redirects to /auth if signed out)/safety — Safety Tools: SOS quick-dial, Share My Location, Live Journey Tracking/contacts — trusted contacts and location-reminder alarms/bot — full-page Ally (also available as a floating widget on every page)/auth — sign up / log innpm install.env.example to .env.local and fill in what you have. JWT_SECRET and the Supabase values are required; generate a secret with node -e "console.log(require('crypto').randomBytes(48).toString('base64'))". Everything else is optional and each feature degrades gracefully without its key.supabase/schema.sql, supabase/schema_tulip.sql, supabase/schema_users.sql, supabase/schema_migrate_to_custom_auth.sql, supabase/schema_phone_login.sql.npm run devSignup and login are handled entirely by this app, not Supabase Auth — public.users stores email, a bcrypt password_hash, full_name, and phone; every other table's user_id foreign key points at it. Row Level Security on these tables has no policies (nothing but the service-role key can touch them), so authorization is enforced in each API route by explicitly filtering on the session's user id — see src/lib/auth.ts (bcrypt hashing, JWT sign/verify, cookie helpers) and src/lib/api.ts (requireUser(), used at the top of every protected route).
phone is normalized to +91XXXXXXXXXX at signup (not stored as free text) so it can double as a login identifier alongside email — schema_phone_login.sql adds a partial unique index on it (NULLs excluded) since two accounts sharing a phone value would otherwise make a phone-login lookup ambiguous. Accounts created before this change may have an unnormalized or missing phone and won't be able to log in by phone until it's re-entered through a normalized flow.
Contact phone numbers rely on Supabase's infrastructure-level encryption at rest plus server-only access (service-role key, never exposed to the browser) rather than an additional application-level encryption layer — a deliberate choice given this project's scale, not an oversight.
Auth: /api/auth/signup, /api/auth/login, /api/auth/logout, /api/auth/me (guest vs signed-in check for client components)
Safety: /api/contacts, /api/contacts/:id, /api/location/share, /api/reminders, /api/reminders/:id, /api/sos*, /api/incidents*
Journey: /api/geocode, /api/directions, /api/routes/plan, /api/route-amenities
Chat: /api/chat
/api/health