Ramble
가치 있는 활동을 위한 마켓플레이스
MWP는 야생동물 사진작가 Matan Wild의 Lightroom 프리셋·포토샵 플러그인·편집 가이드를 즉시 다운로드와 안전 결제로 제공합니다
Lightroom presets, a Photoshop plugin and editing guides from wildlife photographer Matan Wild. Instant download, secure checkout.
커머스HTML · PythonGitHub @matansharon-a11y온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
MWP Presets — build and launch guide
The online shop for MWP Lightroom and Photoshop presets: a static site on GitHub Pages, Supabase for the database, sign-in, private file storage and server code, Lemon Squeezy for card payments, and Make.com feeding monday.com and email.
Nothing here goes live, emails a customer or changes a price without approval. Copy and design are approved first, and every money term (prices, refunds, discounts) is Matan's sign-off. See the checklist at the end.README 전문 보기 ↗The online shop for MWP Lightroom and Photoshop presets: a static site on GitHub Pages, Supabase for the database, sign-in, private file storage and server code, Lemon Squeezy for card payments, and Make.com feeding monday.com and email.
Nothing here goes live, emails a customer or changes a price without approval. Copy and design are approved first, and every money term (prices, refunds, discounts) is Matan's sign-off. See the checklist at the end.
Visitor ─► Website (GitHub Pages) ─► Checkout (Lemon Squeezy)
│ │ paid / refunded event
│ magic-link sign-in ▼
└──► My downloads ─► get-download ─► checkout-webhook ─► Database (Supabase)
│ │
▼ ▼
Private storage (5-minute links) Make.com ─► monday.com
└► MailerLite emails
Supabase is the system of record. If Make.com or monday.com is down, sales and downloads keep working; the sync catches up.
mwp-presets/
├── index.html Home: hero, featured packs, before/after, how it works, free sampler, FAQ
├── shop.html All packs, filter by program
├── product.html One pack (?p=<slug>): gallery, before/after slider, buy box, SEO data
├── account.html My downloads: magic-link sign-in and the customer's library
├── support.html Support form
├── thank-you.html Checkout redirects here
├── legal.html Licence, refunds, terms, privacy (DRAFT, needs approval)
├── 404.html GitHub Pages serves this for unknown URLs
├── robots.txt · sitemap.xml · .nojekyll · .gitignore
├── assets/
│ ├── css/tokens.css Design tokens: colours, type, spacing (edit brand values here only)
│ ├── css/styles.css All site components
│ ├── img/favicon.svg
│ └── js/
│ ├── config.js Supabase URL + anon key, site URL ← the only file you edit to connect
│ ├── lib/api.js Supabase client, catalogue queries, function calls, formatting
│ ├── lib/ui.js Header, nav, reveal, toasts, before/after slider, product cards, checkout
│ └── pages/*.js One script per page
├── admin/
│ ├── index.html · admin.css · admin.js Admin dashboard
└── supabase/
├── config.toml
├── migrations/ 001 schema · 002 security · 003 admin reporting
└── functions/
├── _shared/http.ts CORS, responses, service client, Make.com forwarding, HMAC
├── checkout-webhook/ Paid and refunded orders
├── get-download/ 5-minute signed download links
├── support/ Support requests
├── subscribe/ Free sampler sign-ups
└── .env.example
No build step, no framework: every file is served as it is. The HTML pages share a header and footer; when you change one, change it in every page (search and replace across the folder in VS Code).
Every page follows the same skeleton: skip link, fixed header, <main id="main">, footer, one page script loaded as a module.
| Page | Layout | Data it loads |
|---|---|---|
| Home | Full-bleed hero photo with gradient, numbered sections (01 Collections, 02 Before and after, 03 How it works, 04 Questions), photo band with the sampler form | Featured products; first before/after pair found |
| Shop | Page title, filter chips (only when packs differ in compatibility), 3-column card grid | All live products |
| Product | Two columns: gallery (before/after slider + thumbnails) and a sticky buy box; description below | One product with its media |
| My downloads | Sign-in panel, then the library list | The customer's entitlements |
| Support | Two-column form panel | None (writes a ticket) |
The visual language follows mw.photography: near-black canvas (#0A0908), warm off-white type, headlines that pair light words with a bold phrase ("Edits made in the field. Ready for yours."), small numbered eyebrows ("01 · Collections"), captions in the "Title · Place" pattern, and photographs given the full width.
Images on the home and 404 pages currently load from https://mw.photography/img/…. Before launch, copy those files into assets/img/ and change the paths, so the shop doesn't depend on the other site.
assets/css/tokens.css holds every design decision as a variable: colours, fonts, the type scale (fluid with clamp()), an 8-point spacing scale, radii and motion. styles.css only ever reads those variables.
tokens.css (for example --c-accent, --f-sans). I couldn't read mw.photography's stylesheet directly, so the font (Inter Tight) and the golden accent are close matches chosen from the site's look; swap them for the brand book v3 values..btn, --accent, --ghost, --sm), eyebrow, display and section headings, product card with hover image, before/after slider, buy box, checklist, spec list, FAQ, forms, panels, library rows, toasts..reveal fade up as they scroll into view; all motion switches off for visitors who set "reduce motion".<html lang="he" dir="rtl"> on a page and the layout, slider and arrows mirror; Hebrew text uses Heebo (add it to the Google Fonts link).config.js – connection settings. IS_CONFIGURED lets pages show a clear message instead of failing when keys are missing.lib/api.js – the only place that talks to Supabase. Products are read straight from the database (row level security only returns live ones); downloads, support and sign-ups go through Edge Functions. Everything inserted into the page passes through esc() to prevent script injection.lib/ui.js – the header turns solid on scroll; the mobile menu opens and closes with focus and Escape handled; the before/after slider works by mouse, touch and keyboard (a hidden range input carries it) and nudges once when first seen; checkout links get the buyer's email and the visit's utm_source added, which flows through to monday.com as the order source.pages/*.js – one per page, each small and readable.Run the three files in supabase/migrations/ in order in Supabase → SQL Editor (or supabase db push with the CLI).
| Table | Purpose |
|---|---|
products | Catalogue: name and bold half, tagline, description, price, currency, includes, compatibility, checkout link and variant ID, images, live/featured flags |
product_files | Where each paid .zip lives. Separate so visitors can never read the path |
product_media | Before/after pairs (shared pair_key) and gallery images |
customers | One per buyer email; linked to their login on first sign-in |
orders, order_items | Written only by the payment webhook |
entitlements | "This customer may download this product"; revoked on full refund |
downloads | Every download, with a salted IP hash (no raw IPs) |
support_tickets, subscribers, webhook_events | Support, email sign-ups, payment-event log for idempotency |
admins | Users allowed into /admin/ |
Security (002_security.sql): row level security on every table. Visitors read live products and their images; customers read only their own rows; admins (checked by is_admin()) manage everything; Edge Functions use the service role. Two storage buckets: presets (private, paid zips, admins upload) and product-media (public images).
Reporting (003_admin_reporting.sql): admin_stats() feeds the Overview tab in one call, keeping revenue separate per currency; admin_orders and admin_customers views feed the tables.
Customers and admins both sign in with a magic link (no passwords to leak or reset).
https://shop.mw.photography; Redirect URLs: https://shop.mw.photography/account.html, https://shop.mw.photography/admin/, and http://localhost:8000/** for local testing.How a customer gets access: they pay with an email → the webhook creates a customers row and an entitlement for that email → they sign in with the same email → on first download, the login is linked to their customer row.
Making an admin: sign in once at /admin/ (you'll see "isn't an admin"), then run in SQL Editor:
insert into public.admins (user_id, email)
select id, email from auth.users where email = '[email protected]';
Remove an admin with delete from public.admins where email = '…';.
All uploads happen in the admin dashboard, Products → Edit.
product-media/<slug>/<kind>-<id>.webp and cached for a year.IMAGE_TRANSFORMS: true in assets/js/config.js.presets/<slug>/v<version>/<file>.zip. A new version gets a new path; old files are kept so a download in progress never breaks./admin/ (not linked from the site, blocked in robots.txt, and protected by the database whatever the browser does).
| Tab | What you can do |
|---|---|
| Overview | Revenue per currency, paid and refunded orders, new customers, sampler sign-ups, downloads, open tickets; a chart of paid orders per day (7, 30 or 90 days); top packs |
| Products | Create and edit packs, upload images and the zip, set featured and live. A pack can't go live without a checkout link, variant ID and zip |
| Orders | Search by email or order number, export CSV |
| Customers | Lifetime value, last order, marketing consent, export CSV |
| Support | Open and all tickets, change status, reply by email. Refund requests are marked for Matan |
Refunds themselves are issued in the Lemon Squeezy dashboard; the webhook then removes access automatically and tells Make.com.
npm install -g supabase # or: brew install supabase/tap/supabase
supabase login
supabase link --project-ref YOUR-PROJECT-REF
supabase secrets set --env-file supabase/functions/.env # after filling it from .env.example
supabase functions deploy checkout-webhook
supabase functions deploy get-download
supabase functions deploy support
supabase functions deploy subscribe
config.toml turns off gateway JWT checks because each function authenticates its own callers: the webhook by HMAC signature, downloads by the customer's session, the two public forms by validation and rate limits.
| Secret | Value |
|---|---|
SITE_URL | https://shop.mw.photography |
ALLOWED_ORIGINS | the site URL, plus http://localhost:8000 while testing |
LS_WEBHOOK_SECRET_TEST, LS_WEBHOOK_SECRET_LIVE | set by the "Connect Lemon Squeezy" workflow itself (never typed): the webhook signing secrets |
MAKE_ORDER_WEBHOOK, MAKE_SUPPORT_WEBHOOK, MAKE_SUBSCRIBE_WEBHOOK | the three Make.com custom webhook URLs |
MAKE_SHARED_SECRET | random string; Make scenarios reject calls without it in the x-mwp-secret header |
IP_HASH_SALT | random string |
DOWNLOAD_DAILY_LIMIT | 5 |
Generate random strings with openssl rand -hex 32.
https://shop.mw.photography/thank-you.html.order_created, order_refunded) and sets its signing secret on the function.If the business turns out to be able to use Stripe directly, only checkout-webhook and the checkout link change; everything else stays.
Each scenario starts with Webhooks → Custom webhook, followed by a filter x-mwp-secret = your shared secret.
| Webhook | Payload type | Scenario |
|---|---|---|
MAKE_ORDER_WEBHOOK | order | Find or create the customer in monday.com Preset Customers → create the order in Preset Orders (Status Paid) → add to MailerLite "Buyers" → send the delivery email with downloads_url → set Status Delivered |
refund / partial_refund | Find the order by order_number → Status Refunded → remove from "Buyers" | |
MAKE_SUPPORT_WEBHOOK | support | Create the item in Preset Support (owner Amelia, or Matan for category = refund) → add owned as an update → auto-reply |
MAKE_SUBSCRIBE_WEBHOOK | subscribe | Add to the MailerLite "Free sampler" group (its automation sends the presets) → create or update the person in Preset Customers, Segment Free sampler |
Example order payload:
{
"type": "order",
"order_id": "6a1f…",
"order_number": "1042",
"email": "[email protected]",
"name": "Jane Doe",
"product": "Savanna Golden Hour",
"product_slug": "savanna-golden-hour",
"total": 29,
"currency": "USD",
"source": "instagram",
"repeat_customer": false,
"downloads_url": "https://shop.mw.photography/account.html?email=jane%40example.com",
"sent_at": "2026-10-10T08:15:00Z"
}
assets/js/config.js, and set SITE_URL./admin/, create the first product, upload its images and zip.cd mwp-presets
python3 -m http.server 8000 # then open http://localhost:8000
Set SITE_URL in config.js to http://localhost:8000 while testing so magic links return to your machine, and change it back before pushing.
4242 4242 4242 4242: an order, entitlement and webhook event appear in Supabase; the item appears in monday.com; the delivery email arrives/admin/ with a non-admin email: refusedmwp-presets and push this folder (GitHub Desktop: Add existing repository → Publish).main, folder / (root).CNAME record presets → <your-github-username>.github.io; then enter the domain in Settings → Pages → Custom domain.legal.html approved: licence, refunds (14-day draft), terms, privacytokens.css checked against brand book v3assets/img/SITE_URL correct in config.js and in Supabase secrets