Dugout Value
투구·타격·예측 연구와 선수·계약·트레이드 가치를 비교하는 도구 제공
성능 향상을 원하는 모든 사람, 전투 선수와 몸매 관리자를 위한 비공개 미리보기
Private preview for anyone who wants to improve performance — combat athletes and people getting in shape. From Ricky / SVG MMA Academy.
스포츠·피트니스TypeScript · JavaScriptGitHub @rickysvg온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Private-preview web app from Ricky Maynez / SVG MMA Academy.
SVG Performance is not gym-members-only. It is for anybody who wants to improve performance — especially combat sports athletes, and also people just getting in shape. SVG members are welcome (gym verify / member pricing). You do not have to train at SVG to create an account.README 전문 보기 ↗Private-preview web app from Ricky Maynez / SVG MMA Academy.
SVG Performance is not gym-members-only. It is for anybody who wants to improve performance — especially combat sports athletes, and also people just getting in shape. SVG members are welcome (gym verify / member pricing). You do not have to train at SVG to create an account.
You can run this on a laptop. A new person can create an account, follow one labeled DEMO strength program, save a workout, tap Watch form for a YouTube technique video, log a meal estimate, open a DEMO lesson, talk to SVG Coach, and use a Home screen with a lime welcome, quick-action tiles, today’s workout, a daily quote, nutrition, and an SVG & CO shop block.
It does not charge live cards, talk to Gymdesk, fake an Apple Watch pairing, or claim Ricky types each AI reply.
COACHING_GUIDE.md + DEMO seeds. No API key = honest offline/DEMO answers.export.xml, or CSV from iPhone Health / Shortcuts. Rows are labeled apple_health or apple_watch_import. The web app cannot pair a Watch. Polar is optional (env keys). Manual avg/max is a backup. Analysis is not medical advice. Automatic Watch sync is Phase 2 (native iOS / HealthKit).Headings, lime highlighter kickers, primary pills, and big stats use Anton (font-display). Body copy stays Geist. Primary tabs (phone, sticky top, lime highlight on the active tab): Home · Train · Fuel · Learn · Coach. Shop, Book, Profile, and Log out sit on the bottom account bar with the official circular neon badge. First open plays Ricky’s splash video until the neon ring closes, with the original logo spark lined up on the neon star (motion still starts muted so phones animate; skipped on reduced motion and later visits). The + button is a quick add for workout, food, a body metric, or heart rate. Paid app plans are additional to gym dues.
No Postgres install is required on your laptop. Local setup uses a SQLite file. A phone-friendly hosted preview uses Postgres (Neon or Vercel Postgres) — steps below.
npm install
npm run setup
npm run dev
Then open http://localhost:3000.
npm run setup creates a local .env if needed, the database tables, the DEMO program, and DEMO lessons.
To make yourself an admin after you create an account:
npm run admin:promote -- [email protected]
To make a coach account (for reports):
npm run staff:promote -- [email protected] coach
Or set ADMIN_BOOTSTRAP_EMAIL to that email and run npm run db:seed again.
See .env.example. Names only — put real values in your private .env:
| Name | What it is for |
|---|---|
DATABASE_URL | Laptop: file:./dev.db. Hosted: a postgresql://… string from Neon or Vercel Postgres. Never a SQLite file on Vercel. |
AUTH_SECRET | Long random string used to hash session and reset tokens |
APP_URL | Public address (http://localhost:3000 locally) |
SMTP_HOST SMTP_PORT SMTP_USER SMTP_PASS SMTP_FROM | Optional mail. Empty in production turns off self-serve password reset (same message for every email, no link on screen). Local development can still show a one-time link. Reminders stay in-app on Home only until SMTP is set |
ADMIN_BOOTSTRAP_EMAIL | Optional. Seed promotes this existing account to admin |
ADMIN_EMAILS | Optional. Comma-separated emails that can open the form-check review queue, along with any account whose role is already admin |
OPENAI_API_KEY OPENAI_MODEL | Required for live SVG Coach. Empty means members get short built-in answers only. OPENAI_MODEL defaults to gpt-4o-mini |
COACH_LLM_TIMEOUT_MS | Optional. First-byte timeout for the live coach call, in milliseconds. Default 20000 |
STRIPE_SECRET_KEY | Optional. Stripe TEST secret only (sk_test_...) |
STRIPE_WEBHOOK_SECRET | Optional. Needed to verify webhooks |
STRIPE_PRICE_GYM | Optional. TEST price id for SVG Performance gym monthly ($19) |
STRIPE_PRICE_STANDALONE | Optional. TEST price id for SVG Performance nonmember monthly ($29) |
STRIPE_PRICE_PERFORMANCE_MONTHLY / _YEAR | Optional. TEST price ids for SVG Pro ($9.99/mo, $72.99/yr). Checkout stays off until billing is approved |
STRIPE_PRICE_COACHING_MONTHLY | Optional. TEST price id for VIP Coaching ($249/mo). The env name stays; the member-facing name is VIP Coaching |
If Polar keys are missing: Heart rate still works with Apple Health import, manual entry, and labeled DEMO samples. Connect Polar (TEST) stays off. We never show Apple Watch as connected.
The $249/mo plan is VIP Coaching (coaching / coaching_monthly). Athletes apply with five questions. Ricky approves or declines in /admin/vip, and only an approval unlocks checkout for that person. Public pages say "Limited spots". Caps (20 active, 3 new in a week, 4 in fight camp) stay on the admin desk. A full cap pauses intake and offers the waitlist a 72-hour hold when a seat opens.
Verified academy members pay $149 the first month, then $249. Pro subscribers who are not academy members can get a one-time credit of up to $30 for unused Pro time. Those two offers do not combine. The app does not create Stripe products, prices, or coupons. Set the ids below in the Stripe Dashboard (TEST only) and leave them empty for a no-op:
| Name | What it is for |
|---|---|
STRIPE_PRICE_COACHING_MONTHLY | TEST price id for VIP Coaching at $249/mo. Empty = checkout does not call Stripe and nobody is charged |
STRIPE_COUPON_VIP_ACADEMY_INTRO | Once coupon, $100 off, so the first invoice is $149 and renewal stays $249. Empty = academy members are quoted $149 in the app and Stripe is not given a coupon |
STRIPE_COUPON_VIP_PRO_CREDIT | Once coupon for the $30 cap. Applied only when the computed credit is exactly $30 |
STRIPE_COUPON_VIP_CREDIT_<cents> | Optional once coupon for an exact credit, such as STRIPE_COUPON_VIP_CREDIT_999 for $9.99 |
STRIPE_VIP_PRORATION | Set to 1 to prorate an existing Pro subscription when unused Pro value is already within $30. Without a stored subscription item id the app records the credit and does not call Stripe |
Replies to messages, form checks, and check-ins are promised within 48 hours.
VIP Coaching (coaching and coaching_* checkout rows, $249/mo) gets a weekly form at /checkin. Existing Fighter Development, Elite, VIP, and Platinum subscriptions keep that access. SVG Performance does not. Admins, including emails in ADMIN_EMAILS, review check-ins at /coach/checkins. An admin can turn the form on for one account from that page when testing.
Reminders:
/checkin. Vercel Hobby only allows a cron that runs once a day, so /api/cron/checkins is scheduled 0 0 * * * (daily, sometime in the 00:00 UTC hour). That daily run still sends the reminder and the next day's follow-up; the send can land later than the chosen hour. For the chosen hour, add the Vercel CRON_SECRET value as a repository secret named CRON_SECRET (GitHub → Settings → Secrets and variables → Actions). The workflow .github/workflows/checkin-reminders.yml then POSTs to https://svg-performance.vercel.app/api/cron/checkins every hour. Until that secret exists, the workflow skips and the daily Vercel cron is what sends reminders.SMTP_HOST and the rest). The in-app Home banner stays up until they submit, with or without push.Generate VAPID keys and put them in the environment (local .env and Vercel):
npx web-push generate-vapid-keys --json
Set VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, and VAPID_SUBJECT (mailto:[email protected]). Also set CRON_SECRET on Vercel, and add that same value as the GitHub Actions secret CRON_SECRET. Without OPENAI_API_KEY, submit still saves and drafts the summary and reply from the check-in numbers.
Form videos reuse the private form-check upload (BLOB_READ_WRITE_TOKEN on Vercel).
The browser cannot fully pair an Apple Watch. Members import a file:
export.xml.apple_health or apple_watch_import, never “Apple Watch connected.”Phase 2: a native iOS companion with HealthKit for automatic Watch sync. Not in this preview.
{APP_URL}/api/polar/callback (local: http://localhost:3000/api/polar/callback).POLAR_CLIENT_ID, POLAR_CLIENT_SECRET, and POLAR_REDIRECT_URI in .env. Restart npm run dev.Never put Polar secrets in git.
Never put a live sk_live_ key in this preview. Live keys are rejected.
Affirm, Klarna, and similar pay-over-time methods use those same TEST keys. Stripe does not require extra Affirm/Klarna secrets for Checkout. Turn the methods on in the Stripe Dashboard (TEST).
If Stripe keys are missing: checkout buttons stay off and nobody is marked paid. Training still works. Pricing still shows the Affirm/Klarna message as coming soon — we do not fake a successful buy.
If OpenAI is missing: SVG Coach still refuses unsafe asks and answers common questions from DEMO notes.
If SMTP is missing: reminders still appear on Home once per day after your preferred hour. No email is sent. Turn them off under Profile → Reminders.
Do this only with test keys. Do not turn on live billing.
Create a Stripe account and stay in Test mode (toggle in the Stripe dashboard).
Copy the secret key that starts with sk_test_ into STRIPE_SECRET_KEY.
In Stripe, create monthly recurring TEST prices for the plans you want to try. At minimum put ids in STRIPE_PRICE_GYM ($19) and STRIPE_PRICE_STANDALONE ($29). Pro is $9.99/mo (STRIPE_PRICE_PERFORMANCE_MONTHLY) or $72.99/yr (STRIPE_PRICE_PERFORMANCE_YEAR). Prepaid / 3-month / 6-month env names exist as inactive stubs. Gym-member prices still need an admin verify. Buy buttons stay Coming soon until live billing is approved.
On your laptop, install the Stripe CLI, then forward webhooks:
stripe listen --forward-to localhost:3000/api/stripe/webhook
Copy the CLI whsec_... value into STRIPE_WEBHOOK_SECRET. Restart npm run dev.
Log in, open Pricing, start TEST checkout. Use Stripe’s test card 4242 4242 4242 4242, any future date, any CVC. If Affirm/Klarna appear, that is Stripe TEST — still not live money. We never invent a successful Affirm/Klarna purchase when keys are missing.
The success page does not unlock tools. Access changes after the webhook (checkout.session.completed or invoice.paid), the same for card or BNPL. SVG does not store loan details.
Duplicate webhooks are ignored. Failed payment → past_due (tools lock if keys are on). Cancel → no access. If the period end date is in the past, access is treated as expired.
Gym $19 still needs an admin verify. The gym checkbox never grants a price by itself.
Without keys: Pricing shows “not configured” and Affirm/Klarna as coming soon. Nobody is faked as paid.
Cards and BNPL loans never touch this app’s database.
Do this only in Stripe Test mode. Do not enable live charges.
STRIPE_SECRET_KEY.payment_method_types, then Dashboard automatic_payment_methods, then card-only. Still TEST.npm test
Coverage includes:
/onboarding; completed users reach Home; answers persist and edit on Profileapple_health / apple_watch_import; zone math; RHR trend; DEMO samples labeled DEMOSee EVALS.md for the SVG Coach evaluation set.
npm install && npm run setup && npm run devUse this before inviting ~15–20 adults. Check a box only if you actually tried it.
/onboarding and cannot open Home until the survey is saved. Logout / login / password reset behave.If any of those fail, do not expand the pilot yet.
The app stores counts only (workout_logged, food_logged, lesson_completed, help_requested, reminder_shown). No food names, no chat text, no card numbers.
Suggested questions for a 4–8 week private pilot:
invoice.paid webhook, how many got another invoice.paid about 30 days later vs customer.subscription.deleted / past_due? Stripe TEST is still not live money.Do not use days-active copy as a public leaderboard.
This repo can run on Vercel + Neon (or Vercel Postgres). The files vercel.json and Dockerfile do not mean a public site exists until you deploy with your accounts. This README never invents a live URL.
SQLite (file:./dev.db) stays for the laptop only. Vercel’s disk is not a real database. Hosted deploys must use Postgres.
svg-performance-preview. Pick any region close to you. Create.postgresql://.-pooler in the host name (that one is simpler for the first deploy). If a later build asks for a pooled URL, you can switch.Vercel Postgres is also fine: in a Vercel project, Storage → Create Database → Postgres, then copy DATABASE_URL.
On your laptop, in a terminal:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
Copy the printed line. That is AUTH_SECRET. Not a Stripe key.
Open https://vercel.com and sign in (GitHub is easiest).
Add New… → Project and import rickysvg/Svg-performance (or your fork).
Framework should be Next.js. Leave the root folder as .
Do not deploy yet. Open Environment Variables and add these three names (Production + Preview):
| Name | Value |
|---|---|
AUTH_SECRET | The random string from step B |
DATABASE_URL | The postgresql://… string from Neon (not file:./dev.db) |
APP_URL | Leave a placeholder like https://example.vercel.app for the first deploy, then fix it in step D |
Leave every STRIPE_* name empty unless you are ready for Stripe TEST (sk_test_… only). Never sk_live_.
Click Deploy. Wait until Vercel says the build succeeded.
Open the URL Vercel shows you on your phone. That is your preview. Write it down yourself. This document still does not claim one.
The build runs node scripts/prisma-prepare.mjs --deploy, which generates the Postgres Prisma client and runs prisma db push (creates tables). It uses DATABASE_URL when that is a postgresql:// string, otherwise POSTGRES_URL_NON_POOLING, DATABASE_URL_UNPOOLED, DIRECT_URL, POSTGRES_PRISMA_URL, or POSTGRES_URL. Production fails on purpose if none of those is a postgres URL. A Preview deploy with none of them still generates the client and skips db push so the check can finish; that preview cannot read data until a Preview URL is set.
https://something.vercel.app).APP_URL → paste that real URL.The first deploy has empty tables (no DEMO program until you seed). Home still loads — it shows an empty training CTA instead of “This page hit a snag.” Seed when you want the DEMO strength days, DEMO martial-arts skill days, and lessons:
DATABASE_URL="postgresql://PASTE_THE_SAME_NEON_STRING" npm run db:seed
That writes demo-strength-base and demo-combat-skills plus DEMO lessons into Neon. It does not enable live billing.
Train / Home / Calendar use a Core weekday planner on top of those seeded days. Programming rotates every 3 calendar weeks (Block A → B → C → A) using the same Monday week index as the bike rotation. The weekday skeleton stays (bag + rotating lifts + Tue/Thu bike). Bag themes, technique drills, and lift menus change with the block. Lift days are longer S&C sessions (explosive work, a heavy compound, rotation, and a carry or brace). The prescription under each lift includes RIR (reps in reserve) so the athlete knows how hard to go, for example 4 sets × 6 @ 0-2 RIR, 120s rest between sets. Weighted lifts log reps + lbs. Loaded carries and the hand-weight shadow round log seconds + lbs (load_timed). Empty-hand shadow, planks, and other cardio stay timed. Bag / pads rounds stay timed rounds. Experience on the profile scales round length and rest (beginner ~2 min / 90s rest; advanced/pro ~3–5 min / 30–45s, with a longer bag on advanced Mon/Wed/Fri).
After this mesocycle / shadowbox / bag-theme / RIR lift change merges, re-seed production Neon with the same command and the Vercel DATABASE_URL. A code deploy does not insert the new block days or retag logMode. Reseed deletes and recreates the DEMO programs. Existing workout logs remain, but their demo-day link is cleared (programDayId set null) and is not restored automatically. Name heuristics still treat “plank” as a hold, and “weighted shadow” / “shadowbox with weights” as lbs + seconds, if a host is not re-seeded yet.
Then create your admin account on the hosted site and run (still on the laptop, same DATABASE_URL):
DATABASE_URL="postgresql://PASTE_THE_SAME_NEON_STRING" npm run admin:promote -- [email protected]
Same beginner walkthrough as the laptop Stripe section. Point a Dashboard TEST webhook or stripe listen --forward-to {your-real-APP_URL}/api/stripe/webhook. Affirm/Klarna still need Dashboard TEST payment methods. No live mode.
docker build -t svg-performance .
docker run --rm -p 3000:3000 --env-file .env svg-performance
DATABASE_URL=file:./dev.db and mount a volume over prisma/ and uploads/ if you want data to survive the container.DATABASE_URL to your postgresql://… string. On start the container runs the same prepare + db push path.There is no production URL in this document on purpose.
npm test: SQLite. npm run setup still uses file:./dev.db and the existing Prisma migrations.scripts/prisma-prepare.mjs copies prisma/schema.prisma → prisma/schema.postgres.prisma and runs db push. We do not replay SQLite migration SQL on Postgres (those files are SQLite-only)..env or a real connection string.content/coach-savage/ (folder name kept). Interview questions are not loaded into the model.src/prisma/ (SQLite schema + generated schema.postgres.prisma)scripts/prisma-prepare.mjscontent/coach-savage/DECISIONS.mdSTRIPE_PRICE_PERFORMANCE_ANNUAL_* |
| Optional. Inactive prepaid annual stubs ($72.99/yr) |
STRIPE_PRICE_CONDITIONING_GYM / _NON | Optional. Fighter Conditioning monthly $49 / $59 |
STRIPE_PRICE_CONDITIONING_CAMP_* / _ANNUAL_* | Optional. Inactive 12-Week Fight Camp / annual stubs |
STRIPE_PRICE_DEVELOPMENT_GYM / _NON | Optional. Fighter Development monthly $149 / $179 |
STRIPE_PRICE_DEVELOPMENT_3MO_* / _6MO_* | Optional. Inactive prepaid coaching stubs |
STRIPE_PRICE_ELITE_GYM / _NON | Optional. Elite Online monthly $299 / $349 (cap ~6) |
STRIPE_PRICE_ELITE_3MO_* / _6MO_* | Optional. Inactive prepaid Elite stubs |
STRIPE_PRICE_VIP | Optional. SVG VIP one-time $699 (cap 2; includes 1 month Fighter Development) |
STRIPE_PRICE_PLATINUM | Optional. SVG Platinum VIP one-time $1,199 (cap 1; includes 1 month Elite Online) |
PROGRESS_PHOTO_DIR | Optional. Local folder for progress photos (default uploads/progress-photos). Never commit those files |
TRAINING_CLIP_DIR | Optional. Local folder for private training clips (default uploads/training-clips) |
FOCUS_VIDEO_DIR | Optional. Local folder for uploaded weekly focus clips (default uploads/focus-videos) |
FORM_CHECK_DIR | Optional. Local folder for private form-check clips (default uploads/form-checks). Never commit those files |
BLOB_READ_WRITE_TOKEN | Optional locally. Set this on Vercel (Vercel Blob store → read/write token) so form-check videos persist and stay private. Without it, hosted uploads do not save and the form says so. Weekly check-in form videos use this same store |
VAPID_PUBLIC_KEY VAPID_PRIVATE_KEY VAPID_SUBJECT | Web Push for weekly check-in reminders. Generate with npx web-push generate-vapid-keys --json. VAPID_SUBJECT is a mailto: or https: contact URL. Empty keys: the in-app banner still shows, and email is the fallback when SMTP is set |
CRON_SECRET | Shared secret for the cron routes. Vercel sends Authorization: Bearer <CRON_SECRET>. Copy the same value into a GitHub Actions secret named CRON_SECRET so the hourly check-in workflow can call the route |
POLAR_CLIENT_ID POLAR_CLIENT_SECRET POLAR_REDIRECT_URI | Optional. Polar AccessLink. Empty = Connect Polar (TEST) / not configured |
S3_BUCKET S3_REGION S3_ACCESS_KEY_ID S3_SECRET_ACCESS_KEY S3_ENDPOINT | Names only for a later cloud disk. Not wired in this preview |