尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Every flyer in Tapovan, on one map. A website where organisers pin their event flyers (₹50 for 4 weeks), and travellers filter what's on by date, vibe, price and area, see it on a map, and get alerts for the musicians, cafes and kinds of events they love.
Built for rishikesh.suhmantics.com.
| For visitors | For organisers | For you (admin) |
|---|---|---|
| Flyer wall, day-by-day agenda, and a flyer map: every flyer pinned where it's happening | Snap a flyer → details are read from the photo (optional AI); straighten a wall photo in one drag | Post flyers yourself and record cash in one tap |
| Filter: today / tomorrow / weekend / date, category, area, price, time of day | ₹50 = 4 weeks; recurring events show on every date | Approve organiser submissions once they've paid |
| Follow artists, venues, vibes → phone notifications or email | Private link to edit, renew, and see views, "interested", WhatsApp and directions taps | Visitors per day, most-viewed flyers, revenue |
| /today — the day's list, shareable to WhatsApp with a picture preview | Followers are notified the moment the flyer goes live | Fix map pins, feature/hide listings, handle reports |
| Ganga Aarti times and directions; add to calendar; directions | Listed in Google's event results | Upload the site's photos; CSV exports |
| Class timetables (Tapashala, Divine Circle…) by day, kept apart from one-off events | Post a whole weekly timetable as one listing | Starter pack: 12 real Tapovan flyers ready to publish |
| QR codes on flyers become tappable buttons (WhatsApp group, booking form, map pin) | QR codes are read automatically | Batch upload: snap a whole wall, post them one by one later |
| Near me (walking minutes), On now / in 25 min badges | A QR poster for their own wall | Story image of today's flyers for Instagram / WhatsApp status |
| My plan: save flyers (no account), reminder an hour before | Duplicate warnings; fix pins by pasting a Google Maps link |
The site runs on Vercel's free plan: Vercel runs the app, Turso (free) holds the database, and Vercel
Blob holds the flyer photos. Every push to main on GitHub deploys itself; your data lives in Turso and Blob, so
deploys never touch it. (Prefer one server of your own? See the folded section below.)
In Vercel: Add New → Project → import github.com/suhmantics-droid/rishikesh. Then, in the project:
BLOB_READ_WRITE_TOKEN for you.iad1), and copy its URL and a token.| Name | Value |
|---|---|
ADMIN_PASSWORD | your password for /admin — make it long |
SESSION_SECRET | any long random string (signs the admin login) |
TURSO_DATABASE_URL, TURSO_AUTH_TOKEN | from Turso |
CRON_SECRET | any long random string — Vercel's daily cron sends it to /api/cron |
CRON_GITHUB_REPO | suhmantics-droid/rishikesh — lets the 15-minute GitHub job run the site's jobs |
SITE_URL | https://rishikesh.suhmantics.com once the domain is connected (until then the Vercel address is used) |
Redeploy after changing variables (Deployments → ⋯ → Redeploy).
Vercel → project → Settings → Domains → add rishikesh.suhmantics.com. Vercel shows the DNS record to add
(a CNAME named rishikesh); add exactly that wherever suhmantics.com's DNS is managed. On Cloudflare, set it
to DNS only (grey cloud). HTTPS is set up automatically. Then set SITE_URL (above) and redeploy, so links in
emails, share previews and QR posters use the real address. For GitHub's 15-minute job, also set a repository
variable SITE_URL (GitHub → repo → Settings → Secrets and variables → Actions → Variables) if the site
isn't at rishikesh.suhmantics.com.
ADMIN_PASSWORD.The same code runs as one Docker container plus Caddy for HTTPS, with the database (SQLite) and photos on the server's disk — leave the Turso and Blob variables empty. The server installs itself from a script pasted into a web form and pulls new code from GitHub every 15 minutes.
The code lives at github.com/suhmantics-droid/rishikesh. If the repo is private, create a read-only token
for the server: GitHub → Settings → Developer settings → Fine-grained tokens → Generate → Repository access:
only rishikesh → Permissions: Contents: Read-only. Copy the token. (A public repo needs no token — the
code contains no passwords or keys.)
deploy/cloud-init.sh, then edit its top four lines: your admin password, and the
GitHub token if the repo is private.0.0.0.0/0, IP protocol TCP, Destination port range 80,443 → Add.The server now installs itself (about 10–15 minutes).
Wherever suhmantics.com's DNS is managed (your registrar's website, or Cloudflare), add a record:
| Type | Name | Value |
|---|---|---|
A | rishikesh | the server's public IP |
On Cloudflare, set it to DNS only (grey cloud). Within a few minutes https://rishikesh.suhmantics.com loads, with HTTPS set up automatically.
From a terminal, any Ubuntu server works (Hetzner, DigitalOcean, a ₹500/month VPS):
git clone https://github.com/suhmantics-droid/rishikesh.git && cd rishikesh
bash deploy/setup.sh # installs Docker, asks for an admin password, builds and starts
bash deploy/update.sh pulls and rebuilds by hand; bash deploy/backup.sh takes a backup now.
| You want to… | Do this |
|---|---|
| Post a flyer someone WhatsApped you | Sign in → Post a flyer → upload → check → Cash received (₹50) — publish now |
| Post a whole wall of flyers from a walk | Admin → 📸 Add photos → pick them all → later, Check & post each one from Photos to post |
| Fix a venue's map pin | In Google Maps tap the place → Share → Copy link → Admin → Venues & pins → paste → Use → Save |
| Post today's flyers to Instagram / WhatsApp status | Admin → Post today's story → Today (or Tomorrow / Weekend) and pick the app |
| Give a cafe its own poster | Admin → Venues & pins → 🖨 Print their QR poster (or /v/<venue>/poster) |
| Post one for free | Same, but Publish free |
| Approve an organiser who submitted online and paid you cash | Admin → To do → ✓ Received — publish |
| Fix a wrong date/price | Click the listing in Admin → Listings → Edit details |
| Feature a listing at the top | Admin → Listings → Feature |
| Take something down | Admin → Listings → Hide (or from a report in To do) |
| See what emails went out | Admin → Email log |
Organisers get a private manage link (by email, and on screen) to edit, see views and renew. If you posted it for them, open it from Admin → Listings and send them the link.
Automatic jobs, every 15 minutes: the morning "what's on today" email from 07:30 IST, a renewal reminder to
organisers 2 days before their listing ends, "starting soon" reminders for flyers people saved to their plan, and
clearing out full-size upload photos after 3 days (they're kept only for straightening). On Vercel they're run by
the GitHub job in .github/workflows/jobs.yml (plus Vercel's own daily cron as a backstop); on your own server, by
the app itself. GitHub pauses scheduled jobs in a repository with no commits for 60 days — if the morning emails
stop, re-enable Scheduled jobs under the repo's Actions tab. Admin → Send today's digests runs them by hand.
Updates: on Vercel, every push to main deploys in a couple of minutes, and a failed build leaves the previous
version running. (Your own server checks GitHub every 15 minutes and rebuilds.) Your data is never touched by
updates.
Backups: Admin has CSV downloads of listings, payments and subscribers. On your own server a full backup runs
nightly at 03:15 into deploy/backups/ (last 14 kept).
?when=tomorrow / ?when=weekend) is a 1080×1920 picture of the flyers with
times, venues and the site address. "Share as a story" on /today and in Admin sends it straight to a phone's
share sheet. One a day on Instagram and in WhatsApp status is the cheapest way to grow.Everything below is set in /admin → Settings (no server access needed). A value in the server's .env
file overrides the admin page.
Add a Claude API key (from https://console.anthropic.com). Uploading a flyer then fills in the title, dates (including "every Saturday"), times, venue, price, artists and contact details for the organiser to check. It uses Claude Opus 5.5 at low effort with an automatic fallback model; expect roughly ₹1–2 per flyer read. Results are cached per upload.
Add a Resend API key from https://resend.com (free tier: 3,000 emails/month) and verify suhmantics.com in Resend (it gives you DNS records to add). Until then, emails are only recorded in Admin → Email log, not sent.
Payment mode picks how organisers pay:
| Mode | How it works | Needs |
|---|---|---|
cash (now) | Organiser submits; you collect cash and tap Received in Admin | nothing |
upi | Organiser scans a QR for your UPI ID and enters the UTR; you approve in Admin. No fees. | UPI ID and payee name |
razorpay | UPI, cards, netbanking; goes live instantly, no manual step. ~2% fee. | Razorpay key ID and secret, plus a webhook to /api/webhooks/razorpay (events payment.captured, order.paid) and its secret |
Price and length are set in the server's .env: LISTING_PRICE_PAISE (5000 = ₹50) and LISTING_DAYS (28).
The map uses OpenStreetMap tiles, which is fine for a small site. If traffic grows, get a free key from MapTiler or
Stadia Maps and set NEXT_PUBLIC_MAP_TILES (rebuild needed). Directions always open in Google Maps.
npm install
npm run seed:demo # optional: 14 sample flyers at fictional venues (marked "Sample")
npm run dev # http://localhost:3000 — admin password in dev is "admin"
npm test # unit + listing-lifecycle tests
npm run seed:demo -- --clear removes the samples (or use the button in Admin).
@libsql/client: hosted by Turso when TURSO_DATABASE_URL is set, otherwise a file in
data/app.db. Flyers resized to WebP with sharp, stored in Vercel Blob when BLOB_READ_WRITE_TOKEN is set,
otherwise in data/uploads/ — src/lib/storage.tszxing-wasm; Web Push (web-push, VAPID keys generated on first use) for phone notificationsnext/og; type set in Fraunces (headings) and Inter@anthropic-ai/sdk, structured outputs) for reading flyers — src/lib/extract.tssrc/lib/email.tssrc/lib/payments.ts (cash / UPI QR / Razorpay)src/app/ pages: / (browse), /map (flyer map), /e/[slug] (event), /v/[slug] (venue) + /poster, /a/[slug] (artist),
/today (+ /today/story image), /plan, /venues, /artists, /post, /manage/[token], /alerts, /admin,
/about, /poster; api/ routes
src/components/ Browse (filters + views), MapView, FlyerCard, PostFlow, PayWidget, AlertsForm …
src/lib/ db (schema), data (queries), listings (incl. timetables), payments, alerts, extract,
qr (QR codes → links), warp (straightening), collage (map layout), starter, uploads (batch
queue, duplicates), maplink (Google Maps links → pins), push (incl. plan reminders), storage
(disk or Vercel Blob), cron, site
seed/starter/ the starter pack: flyers.json plus the straightened flyer images
deploy/ cloud-init.sh (browser setup), setup.sh, auto-update.sh, backup.sh, update.sh, Caddyfile
tests/ node:test suites (dates, utils, listing lifecycle, filters, payments)
Product settings (name, tagline, areas, categories, map centre) are in src/lib/site.ts.