尚未检测到更新。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.