塑造个人品牌,发现细分市场,构建可持续盈利的产品
개인 브랜드 구축·세분화 시장 발굴·지속 가능한 수익 제품 만들기를 공유
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Core pages:
/admin — sign in, upload ad videos, assign each one to an auto, watch the fleet on a live map with each tablet's status, address, and what's currently playing/player — what runs on each tablet. Registers itself with an auto number, downloads its ads once, and plays them fully offline from then on.The authenticated admin area also includes driver management:
/admin/drivers — search registered drivers, see who's due for their monthly payment, and register a new one from a modal right here (no separate page)/admin/drivers/[id] — a driver's profile: their linked tablet's live status, documents, and payment cycle with a "mark as paid" action/admin/drivers/[id]/edit — update a driver's details or replace individual documents/admin/history — ads that aren't live right now (paused, not started yet, or finished), kept off the main dashboard so it stays focused on what's actually running/admin/autos — every tablet in the fleet in one place: add a new one, set or edit its SIM number, see its live status/battery/last seen, turn its screen off or back on, or delete it/admin/reports — pick an auto and a date to see its GPS route for that day, drawn on a mapPlus a small Android app in android/ — a kiosk wrapper around
/player (no watermark, screen always on, auto-launches after reboot). See
android/README.md for install instructions and where
to download the built APK.
setup.sql, grant the trusted admin role to every approved pre-existing admin or Team access account. Replace the example email with one account's exact email, run this block in SQL Editor, then repeat the same exact-email block separately for each other approved pre-existing account. The block aborts unless each run updates exactly one account; never remove the email filter, broaden it, or automatically promote every user.
do $$
declare
promoted_count integer;
begin
update auth.users
set raw_app_meta_data = coalesce(raw_app_meta_data, '{}'::jsonb)
|| jsonb_build_object('role', 'admin')
where lower(email) = lower('[email protected]');
get diagnostics promoted_count = row_count;
if promoted_count <> 1 then
raise exception 'Expected exactly one admin account, updated %', promoted_count;
end if;
end $$;
setup.sql → Run. Creates the autos, ads, and drivers tables, the ads and private driver-documents storage buckets, the security rules, and seeds one test auto (AUTO-01).@smato.local, digits only. 9876543210 → [email protected].@smato.local address can never receive, so sign-in fails forever with "Invalid login credentials". If you already created a user without ticking it, fix it by running this once in the SQL Editor (swap in the real email):
update auth.users set email_confirmed_at = now() where email = '[email protected]';
npm install
cp .env.local.example .env.local
Open .env.local and paste in your three values:
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhb...
SUPABASE_SERVICE_ROLE_KEY=eyJhb...
Then:
npm run dev
Open http://localhost:3000 — you should see the two links.
The anon key is safe in the browser. The rules in setup.sql mean it can only read ads, check a device in, and post a GPS fix. Only a permanent signed-in account whose trusted app_metadata.role is admin can open the admin shell or manage protected data and storage. The service role key is different — it bypasses all of that, so it only ever lives in this env var, read server-side by app/api/admin/create-admin, never sent to the browser.
npm i -g vercel
vercel
Or push to GitHub and import the repo at vercel.com.
Then add the environment variables in Vercel: Project → Settings → Environment Variables. Add NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, and SUPABASE_SERVICE_ROLE_KEY, then redeploy. Without them the app builds but can't reach your data (and Team access won't be able to add new logins).
You'll get a URL like https://your-app.vercel.app.
https://your-app.vercel.app/adminhttps://your-app.vercel.app/admin/drivers (Register a driver opens as a modal from here)https://your-app.vercel.app/admin/historyhttps://your-app.vercel.app/playerBefore the first upgrade to role-protected admin policies, run the exact-email, single-account migration in Supabase step 2 separately for every approved pre-existing admin or Team access account, changing the exact email for each run. Do not auto-promote all existing users. Then run the current setup.sql again in the connected project's SQL Editor. It is idempotent: it adds the driver table, private document bucket, and access policies without recreating existing fleet or advertisement data. Each promoted account must sign out and back in afterward to refresh its JWT role claim; until that account's migration and refresh are complete, it is intentionally denied admin access.
https://your-app.vercel.app/player in Chrome on the tablet while it has internet. This registers the service worker — after this the page loads with no connection.AUTO-01). Saved on that tablet permanently — you won't see the setup screen again.https://your-app.vercel.app/playerAndroid settings that matter more than the app:
To check a screen in the field: tap the top-right corner five times. Shows network, last sync, ads downloaded, what's playing, and the last GPS fix.
To turn a screen off remotely: each card in the Fleet view has a Turn screen off button. It blacks out that tablet, dims the window's own brightness to minimum, and pauses playback instantly if it's online, and the moment it's online again if it wasn't — then tap to turn on brings it straight back. Inside the Android app this is deliberately a dim, not a true OS-level sleep: an earlier version called DevicePolicyManager.lockNow() for the real thing, but once a screen is actually asleep, Android can suspend the tablet's connection to the admin panel right along with it — so "turn back on" sometimes never arrived. Staying dimmed instead of truly sleeping keeps that connection alive the whole time, trading a little extra power saving for control from the admin panel that actually works every time. Still visually identical to off.
In /admin, under Add an ad:
AUTO-01), or All autos. Pick + New auto… to type a number that hasn't checked in yet — it'll be assigned to that video and appear in the Fleet list right away.Ads loop all day, back to back, in the order they were added — there's no manual play-order field to set; each new ad just joins the back of the queue. There's no time-of-day scheduling either — if it's active, it's in the rotation. The ads list shows each one's position (e.g. "plays 2 of 3 here") so it's clear what order a given tablet is actually playing in.
The tablet picks it up within seconds if it's online — submitting, pausing, resuming, or deleting an ad broadcasts a message straight to every connected tablet, which triggers an immediate sync. No polling delay, and no Supabase dashboard setting to configure — it uses Realtime Broadcast, which works out of the box with the anon key (unlike the "Replication" toggle used for database change tracking, which this app doesn't rely on).
Open /admin/drivers after signing in to search the driver directory, or click Register a driver to fill out the form in a modal right there — no separate page, the list refreshes the moment it closes. Registration requires a name, mobile number, auto number plate, Driving Licence number, Aadhaar number, and three images: a driver photo, Driving Licence image, and Aadhaar image. Images must be JPEG, PNG, or WebP and under 5 MB.
Each auto can have one registered driver only — the auto number plate doubles as that auto's identifier in the fleet system, so a driver's profile page (/admin/drivers/[id]) shows their linked tablet's live status directly (online/offline, last seen, GPS). Mobile numbers, Driving Licence numbers, and Aadhaar numbers are also unique, so the directory cannot accidentally create a duplicate identity or auto assignment.
Payment cycle — every driver is paid ₹1,000 on a rolling 30-day cycle starting from their onboarding date (or their last payment, once they have one). A driver's profile shows whether they're due, and a Mark this month as paid button resets the clock. The Drivers list and their profile both show a due/paid badge; the Drivers page header totals how many drivers are currently due.
Driver documents are private. The app stores only private storage paths and creates short-lived signed URLs for authenticated admins when an image must be shown. Full Aadhaar and Driving Licence numbers show everywhere in the admin console — the directory, the profile, the edit form — since every viewer is already an authenticated admin. Keep real identifiers and signed storage URLs out of documentation, issue reports, and screenshots regardless.
A driver's profile also has a Delete button — removes the record and their uploaded documents for good, with a confirmation first.
blob: URL, which is a known weak spot on some older Android WebView builds), never streamed./admin), plus a once-an-hour safety-net check in case that connection ever silently drops.The Fleet map and addresses use OpenStreetMap (via Leaflet) and Nominatim — both free, no API key. That's fine at the scale of ~20 autos; if you ever outgrow it, that's the piece to swap for Google Maps.
Data used is roughly the size of the videos, once. A 30-second 1080p ad is about 30 MB, so ten ads a month is around 300 MB per tablet.
/player, then turn WiFi off completely and reload. If it still plays, caching is working.app/
page.js landing page with both links
admin/page.js control panel: sign in, fleet + map, upload form, ads list, team access
api/admin/create-admin/route.js server-side (service role) admin creation
player/page.js tablet player: setup screen, offline sync, GPS, playback, debug HUD
layout.js fonts and shell
globals.css all styling
lib/
supabase.js client + storage URL helper + realtime broadcast
adCache.js downloaded ad storage (Cache Storage API, served by sw.js)
time.js date/campaign logic
geocode.js lat/lng → address (OpenStreetMap Nominatim)
components/
FleetStrip.js per-auto status cards (online, now playing, address)
FleetMap.js live map of every auto's last GPS fix (Leaflet)
public/
sw.js service worker
manifest.json
setup.sql run once in Supabase
/adminanon public key (sometimes labeled publishable), and the service_role key (labeled secret). The service role key powers the in-app "Team access" panel — keep it out of anything public; it never goes in a NEXT_PUBLIC_ variable.