Easy-to-manage touch screens
무료 오픈소스 터치스크린으로 USB 설정 가이드, 드래그‑드롭 편집기, 로컬 제어를 제공하며 Home Assistant용으로 제작되었습니다
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기ZapiszPrzepis — archive-first PWA for saving recipes shared from social media
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗An archive-first PWA for saving recipes shared from social media. Every URL arrives via the system Share gesture and is transformed into a permanent, Polish-language copy of the recipe — independent of the original source.
Stack: Next.js 16 (App Router) · TypeScript · Tailwind v4 · Supabase (auth + Postgres + storage) · Inngest (async jobs) · PWA (offline + Web Share Target) · Cloudflare Workers
Live app: https://zapiszprzepis.pl
Registration is gated by an invite code. The code is a runtime secret (INVITE_CODE),
never committed — open an issue
if you would like one to try the app.
Every shared link goes through the same pipeline — fetch the source, extract the recipe with an LLM into a Polish-language copy, archive the image — and only the fetch step differs per source.
| Source | How the recipe text is obtained | Limits |
|---|---|---|
Facebook Reels and videos — facebook.com/reel/…, …/videos/…, app share links facebook.com/share/r/…, fb.watch | The post caption, read from Facebook's embedded-video plugin (plugins/video.php?show_text=true) — no Facebook session needed, and the part hidden behind "See more" is included. The cover frame becomes the recipe image and is archived right away, because Facebook's image links expire within days. | A recipe that exists only in the video, with no caption, cannot be extracted. Text and photo posts are not supported — Facebook's post plugin refuses them. |
YouTube — watch links, youtu.be, Shorts | The video description, scraped with Firecrawl. | The player is embedded on the recipe page. |
| Blogs and recipe sites — any other URL | Firecrawl main-content scrape, retried on the full page for classic blog templates; Blogspot posts come straight from the Blogger JSON feed. | An embedded YouTube player on the post is detected and embedded on the recipe page. |
A share that cannot be turned into a recipe lands in the header bell with the reason, and can be retried from there.
Requirements: Node ≥ 20.6, pnpm (Corepack-managed — version pinned in package.json#packageManager), Supabase account, Cloudflare account.
Clone and install:
git clone https://github.com/spokospace/zapiszprzepis.git
cd zapiszprzepis
corepack enable # one-time — activates the pinned pnpm version
pnpm install
Create a Supabase project at https://supabase.com/dashboard:
zapiszprzepis, Region: Central EU (Frankfurt), Plan: Freehttps://<ref>.supabase.co)anon public JWT)Configure redirect URLs in Supabase Authentication → URL Configuration:
https://zapiszprzepis.plhttp://localhost:3000/auth/callbackhttps://zapiszprzepis.pl/auth/callbackLocal .env.local:
cp .env.local.example .env.local
# fill in NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY
Link Supabase CLI and push migrations:
pnpm exec supabase login
pnpm exec supabase link --project-ref <ref>
pnpm exec supabase db push --linked
Recipe extraction (scraping + LLM) runs asynchronously via Inngest, outside the Cloudflare Worker request path (NFR p95 ≤ 3 min). Locally:
Start the Inngest Dev Server (separate terminal):
npx inngest-cli@latest dev
# → listens on http://localhost:8288
Start Next.js dev:
pnpm dev
Local dashboard: http://localhost:8288 — "Runs" tab shows execution history
Local secrets (.env.local):
INNGEST_EVENT_KEY=... # Inngest Dashboard → Event Keys
INNGEST_SIGNING_KEY=... # Inngest Dashboard → Signing Keys
Deployment:
pnpm run deploy # deploys the Worker to Cloudflare (Inngest connects via /api/inngest)
# `run` is required: bare `pnpm deploy` is pnpm's own workspace-deploy command, not this script
Two cron triggers run in the deployed Worker, declared in wrangler.jsonc and
dispatched by custom-worker.ts.
| Cron (UTC) | Job | Why |
|---|---|---|
30 3 * * * | Dump recipes + recipe_shares to R2 as recipes/<YYYY-MM-DD>.json | The free Supabase plan includes no backups |
0 4 * * * | SELECT one row through PostgREST | The free plan pauses a project after a week without database activity |
One-time setup — the backup silently logs backup: skipped until both exist:
npx wrangler r2 bucket create zapiszprzepis-backups
npx wrangler secret put SUPABASE_SERVICE_ROLE_KEY # paste the key when prompted
The dump needs the service role because RLS would otherwise hand it an empty array. It covers table rows only — archived images live in Supabase Storage and are not copied.
Restore is manual: fetch the object, then feed the arrays back through PostgREST or the SQL editor.
npx wrangler r2 object get zapiszprzepis-backups/recipes/2026-09-05.json --file backup.json
Nothing prunes old objects. At a few hundred kB a day that stays far inside R2's free tier for years, but it does grow without bound.
Installable on Android (Chrome/Edge), Windows, and macOS. Offline-capable with Web Share Target API.
Installation:
Web Share Target (system Share gesture):
text/plain, text/uri-list/share → processes URL → recipe appears in listService Worker & Offline (public/sw.js, tests in src/lib/sw.test.ts):
/_next/static/*, content-hashed): cache-first/api/*, and every non-GET request (Server Actions, Web Share Target): network only, never cached/), then an „Offline” response/sw.js?v=<build>: the new worker gets fresh caches and waits until every app window is closed, so open tabs are never reloadedpnpm dev # http://localhost:3000
pnpm build # production build
pnpm lint # ESLint
pnpm test # Vitest unit tests
pnpm check:auth # smoke test: ping Supabase auth/v1/health
src/middleware.ts — checks the session on every request with supabase.auth.getClaims() (the JWT is verified locally; an expiring session is refreshed); redirects unauthenticated users to /login.src/lib/supabase/{server,client,proxy}.ts — three Supabase client helpers with getAll/setAll cookie adapter.src/app/login/ — Server Component + Server Action (signInWithEmail — magic-link OTP).src/app/rejestracja/ — invite-code gated registration (/signup redirects here).src/app/auth/callback/ — Route Handler (exchangeCodeForSession with error mapping).supabase/migrations/ — baseline: pg_trgm + public.current_user_id() + RLS policies.Full PRD, roadmap, and implementation plans live in context/:
context/foundation/prd.md — Product Requirements Documentcontext/foundation/roadmap.md — feature map F-01 … S-09context/foundation/tech-stack.md — stack rationalecontext/foundation/test-plan.md — risk-based quality contractcontext/changes/<change-id>/ — per-change folder with plan and progress