From cce634351159f0e364a17fa02ebf0972e8740e32 Mon Sep 17 00:00:00 2001 From: yuni <233804448+pappensex@users.noreply.github.com> Date: Sun, 2 Nov 2025 20:12:51 +0100 Subject: [PATCH 001/133] =?UTF-8?q?Add=20README=20for=20YONI=20=E2=86=94?= =?UTF-8?q?=EF=B8=8E=20Mutterschiff=20project?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added detailed README for YONI and Mutterschiff project, including architecture, quickstart guide, security baseline, and next steps. --- README.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..efa1983 --- /dev/null +++ b/README.md @@ -0,0 +1,71 @@ +# YONI ↔︎ Mutterschiff (GPT‑5 Pro) – Remote Bridge + +Diese minimale Referenzimplementierung stellt eine sichere Brücke zwischen einem lokalen Agenten **YONI** und dem **Mutterschiff** (OpenAI GPT‑5 Pro) her. + +**Zwei Betriebsmodi** +1) **HTTP/JSON + Function Calling (stabil & simpel):** Node.js-Server definiert Tools/Funktionen; GPT‑5 ruft diese bei Bedarf auf. Der Server delegiert die Ausführung an den lokalen YONI‑Agent (Python). +2) **(Optional) Realtime/Voice:** Kann später per WebRTC/WebSocket ergänzt werden. Dieser Starter nutzt zunächst Modus 1. + +--- + +## Architektur (Kurzform) + +``` +┌─────────┐ HTTPS ┌──────────────────────────────┐ HTTP (LAN/VPN) ┌───────────────┐ +│ Client │ ───────────► │ Mutterschiff-Server (Node) │ ─────────────────► │ YONI-Agent │ +│ (UI/CLI)│ │ • OpenAI API (GPT-5 Pro) │ │ (Python Flask)│ +└─────────┘ (REST) │ • Tool/Function-Bridge │ (Tool Calls) └───────────────┘ + └──────────────────────────────┘ +``` + +- **OpenAI API Key** bleibt **nur** auf dem Server. +- Der YONI‑Agent exponiert nur whiteliste, harmlose Endpunkte. +- TLS/Firewall/VPN empfohlen. + +--- + +## Quickstart + +### 1) Mutterschiff-Server (Node.js) + +```bash +cd server +cp .env.example .env # OPENAI_API_KEY setzen, YONI_AGENT_URL anpassen (z.B. http://127.0.0.1:5055) +npm install +node mothership_server.js +``` + +### 2) YONI-Agent (Python) + +```bash +cd client_yoni +python -m venv .venv && source .venv/bin/activate +pip install -r requirements.txt +python yoni_agent.py +``` + +### 3) Smoke‑Test + +```bash +curl -s http://localhost:8787/chat -H "Content-Type: application/json" -d '{ "user":"Statusbericht von YONI." }' | jq +``` + +--- + +## Security Baseline + +- **API‑Key niemals** an Clients weiterreichen. +- YONI‑Agent nur im **LAN/VPN** binden (oder mutual‑TLS). +- **Werkzeug‑Whitelist** strikt halten. Kein Shell‑Zugriff, kein Dateisystem‑Schreiben in diesem Starter. +- **Rate‑Limits** & Logging aktivieren. +- Für Voice/WebRTC später **ephemere Tokens** vom Server minten. + +--- + +## Nächste Schritte + +- Realtime‑Pfad (WebRTC/WebSocket) für Voice/Audio aktivieren. +- Tooling erweitern (z.B. Sensoren, Geräte, Kalender), aber **Idempotenz** & **Timeouts** beachten. +- Beobachtbarkeit: Metriken/Tracing für Tool‑Latenzen. + +Viel Spaß. 💫 From cddb302d1c43652ab8394f9f1155f0a4a3da4c73 Mon Sep 17 00:00:00 2001 From: yuni <233804448+pappensex@users.noreply.github.com> Date: Sun, 2 Nov 2025 20:18:32 +0100 Subject: [PATCH 002/133] Add files via upload --- yoni-app-prototype.zip | Bin 0 -> 18976 bytes yoni-app-v5.zip | Bin 0 -> 15562 bytes yoni-mothership-bridge.zip | Bin 0 -> 4913 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 yoni-app-prototype.zip create mode 100644 yoni-app-v5.zip create mode 100644 yoni-mothership-bridge.zip diff --git a/yoni-app-prototype.zip b/yoni-app-prototype.zip new file mode 100644 index 0000000000000000000000000000000000000000..624aec0e6dcef8bab11a72ed3ec08063b3c3383e GIT binary patch literal 18976 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 literal 0 HcmV?d00001 diff --git a/yoni-mothership-bridge.zip b/yoni-mothership-bridge.zip new file mode 100644 index 0000000000000000000000000000000000000000..58f5a94a7b413788670512a266082eded6a2e93b GIT binary patch literal 4913 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 JlH~W-e*yJW#vlLy literal 0 HcmV?d00001 From 4abfcae35ae358c7e02b8f175dfb1fa601006fa2 Mon Sep 17 00:00:00 2001 From: YONI <233804448+pappensex@users.noreply.github.com> Date: Mon, 3 Nov 2025 12:26:14 +0100 Subject: [PATCH 003/133] Add GitHub OAuth callback route Implement GitHub OAuth callback handling to exchange code for access token. --- app/api/github-app/callback/route.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 app/api/github-app/callback/route.ts diff --git a/app/api/github-app/callback/route.ts b/app/api/github-app/callback/route.ts new file mode 100644 index 0000000..ca0248b --- /dev/null +++ b/app/api/github-app/callback/route.ts @@ -0,0 +1,24 @@ +import { NextRequest, NextResponse } from 'next/server'; + +export async function GET(req: NextRequest) { + const code = req.nextUrl.searchParams.get('code'); + const state = req.nextUrl.searchParams.get('state'); + if (!code) return NextResponse.redirect('/?auth=missing_code'); + + // Exchange code → access token (GitHub OAuth for GitHub App) + const res = await fetch('https://github.com/login/oauth/access_token', { + method: 'POST', + headers: { 'Accept': 'application/json' }, + body: new URLSearchParams({ + client_id: process.env.GITHUB_APP_CLIENT_ID!, + client_secret: process.env.GITHUB_APP_CLIENT_SECRET!, + code, + state: state ?? '' + }) + }); + + const data = await res.json(); + // Persist token (per user/installation) – store securely. + // For demo we just echo status. + return NextResponse.redirect('/?auth=ok&provider=github&x=148'); +} From b7ba831ab9aeb4bd07b0e0690085228c5572ae1a Mon Sep 17 00:00:00 2001 From: YONI <233804448+pappensex@users.noreply.github.com> Date: Mon, 3 Nov 2025 12:27:08 +0100 Subject: [PATCH 004/133] Add webhook route to handle GitHub events Implement GitHub webhook handler for pull requests. --- app/api/github-app/webhook/route.ts | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 app/api/github-app/webhook/route.ts diff --git a/app/api/github-app/webhook/route.ts b/app/api/github-app/webhook/route.ts new file mode 100644 index 0000000..7b2abcc --- /dev/null +++ b/app/api/github-app/webhook/route.ts @@ -0,0 +1,31 @@ +import { NextRequest, NextResponse } from 'next/server'; +import crypto from 'crypto'; + +function verify(sigHeader: string | null, body: string) { + const secret = process.env.GITHUB_WEBHOOK_SECRET!; + const hmac = crypto.createHmac('sha256', secret).update(body).digest('hex'); + return sigHeader === `sha256=${hmac}`; +} + +export async function POST(req: NextRequest) { + const raw = await req.text(); + const sig = req.headers.get('x-hub-signature-256'); + if (!verify(sig, raw)) return new NextResponse('invalid signature', { status: 401 }); + + const evt = req.headers.get('x-github-event'); + const payload = JSON.parse(raw); + + // Simple demo: auto-comment on new PRs + if (evt === 'pull_request' && payload.action === 'opened') { + await fetch(`https://api.github.com/repos/${payload.repository.full_name}/issues/${payload.number}/comments`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${process.env.GITHUB_APP_INSTALLATION_TOKEN ?? ''}`, + 'Accept': 'application/vnd.github+json' + }, + body: JSON.stringify({ body: `👋 ${process.env.X148_ALIAS} hier. PR empfangen und in Prüfung.` }) + }).catch(() => {}); + } + + return NextResponse.json({ ok: true }); +} From 277391d11e7492533a55f55f063a004ae9edf3cf Mon Sep 17 00:00:00 2001 From: YONI <233804448+pappensex@users.noreply.github.com> Date: Mon, 3 Nov 2025 13:21:37 +0100 Subject: [PATCH 005/133] Add files via upload --- .github/workflows/main.yml | 64 +++++++++++++++++++ .../yoni-x148.2025-11-03.private-key.pem | 27 ++++++++ 2 files changed, 91 insertions(+) create mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/yoni-x148.2025-11-03.private-key.pem diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..9594a12 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,64 @@ +name: CI for YONI (x148) + +# Trigger the workflow on pushes to `main` and PRs targeting `main`. +on: + push: + branches: + - main + - 'releases/*' + pull_request: + branches: + - main + +permissions: + contents: read + pull-requests: write + +jobs: + build-and-test: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '20' + + - name: Install dependencies + run: | + npm install + + - name: Run linter (if available) + run: | + if [ -f package.json ] && npm run | grep -q "lint"; then + npm run lint + else + echo "No lint script defined" + fi + + - name: Run tests (if available) + run: | + if [ -f package.json ] && npm test --help > /dev/null 2>&1; then + npm test + else + echo "No test script defined" + fi + + - name: Build project (if available) + run: | + if [ -f package.json ] && npm run | grep -q "build"; then + npm run build + else + echo "No build script defined" + fi + + - name: Upload build artifacts (optional) + uses: actions/upload-artifact@v3 + if: success() + with: + name: yoni-build-artifacts + path: | + build + dist \ No newline at end of file diff --git a/.github/workflows/yoni-x148.2025-11-03.private-key.pem b/.github/workflows/yoni-x148.2025-11-03.private-key.pem new file mode 100644 index 0000000..cb5a4d5 --- /dev/null +++ b/.github/workflows/yoni-x148.2025-11-03.private-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEArlnJBWZ7Tig7sn+TwSC77TkYPNXwgPbL3v+fcW5/xyjQiPYI +ueCSAJlInG4nk8GG/ODyr+jJUWHObQgWhFOTiNzfx+mVhNZhDiK8WvfkmgiTth5t +DZbFEj0jbX4QFhXCTZ72j0l+pBIrKeDj22d9EzSVncIQreTQWlNA012UsP1OEjy2 +qaGpO+FuFGLFpG19men2FJ7+CSyKImSxGcQRenHKX80ITO6Uqdh4HOIKXlo16Dl6 +GRhxRwux+ANs5OivjWFpmCW4cNjagNByrVTS1PX+VmoDeWmhEkNJmMIDao2oNYCS +ha2OtsPcrBLmU6lxZnaCPctdu3deZtl243xE8wIDAQABAoIBAQCY8Al1wz/Gf2Re +X6qsbppqrPql4sTlW5faNUEXr2ZGvXavraJZ6rQn8k3PapEs+0X97RT1Wa2+xZzd +54pY6Tcxhw6FvlB6BdN9Its2sNzGgV+REX93vBmCtj6tfTd/J/cZP6foaP+9zHIw +eiVmAJGmlynnCKS45U4e7Yif4CbeWOG1yfmz2uLqEOrX8wxpc+EmfIoCZ6HwJ1qi +YO6HNgDMeKNGV1sRUG+E8NE0Qp/WyyYxNtyGDp05uXSpoR8P4Mw1iYSqzcecfuCr +1XyeAZo+W1T5pqlnGq1nwjCaCDnUPRozbiPV11bEmB1Eybal36Re8lu2puz86+SC +Hy/hJGKBAoGBANgPJ/2VPu15tEqcJEZMgpe2HNS7Ggr4w0BirtA/WD3XXrK6deSs +y7aGnJwKJoRPGHn90tR7rmkgfU2cibXZoHezD3Q4gazt43ElbxojSRLiFisWATpf +Pllk//HG39a0MiFXse+lZdqlFPw0ECq5s6Mdnv+KwcI0ThCdu1lnzq+NAoGBAM6U +zkJjKtfziQdbnHTQZkFUB5/AU0TlMU8e2TbQtgh+RIpvADVT/Yd5X4dDc0Id4qES +GaLZkg89CpZcYD182svPZLkTv8AVa0VmQQg5ryjmTz6sg7ROqXFrEu1jw3Losz35 +xWmlF9a26oEmio4TILlFrUiE/53Kc+VXq/bdRWZ/AoGACxjMLwQJVV0Y23QlkM7B +LlWWKX7qmYGpVZc770oxmAuFu5xEDdiqJdfIF2OWEtmVIj7dIziu/otCpEfT8tkY +ms1Bj/CPCHi3Y8pOaINDb4rZJKFessbNhv3e/2X9fSOIJ5o/c1WFY7Ny3nk2E1uy +VEBjODDCPt083TGcu7zPKskCgYBpI60ydpxYmpdUSlj/zw0sFKNouA7pUqjc9vtg +fasX+YPS3RnDCoNPjwOCqV6g+OreRYMMsiU3Yj7gNhW5VbFZ560A0T9XeM9G2/sf +P5SZgVY1tMG3ZA2zLubMMWlJM4v+b//8e8z9N810e/QqyStvgDnoXeBnI6IGyK86 +DnYC6wKBgQC3wcL91wHHYzrh5A9AkWUTY2oLaH1Z6n6NzHcDfRW3T4NQHIPPWNo8 +6bXGgw95SV+osJFyCgY8vyANp6EelRIyqtPyv6Q3KBp3C7aHUw0KIPgjYqxuf+K5 +MmPnJ2cmuDO5FRo8zjFiAQGXAp9o1xLHqsD8TPwtMd6RCdh8IXdsdA== +-----END RSA PRIVATE KEY----- From 03c93e0aa8bc6ba6def972adb5eb0e507a55b3c5 Mon Sep 17 00:00:00 2001 From: YONI <233804448+pappensex@users.noreply.github.com> Date: Wed, 5 Nov 2025 03:47:04 +0100 Subject: [PATCH 006/133] Add files via upload --- .github/content.png | Bin 0 -> 53293 bytes .github/document.json | 1 + 2 files changed, 1 insertion(+) create mode 100644 .github/content.png create mode 100644 .github/document.json diff --git a/.github/content.png b/.github/content.png new file mode 100644 index 0000000000000000000000000000000000000000..a9879997c121c2a14c4237c17830e2f0414bb9ab GIT binary patch literal 53293 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%0D(PUCimozC{YNK!yy-enj@&jHnu)rr= zxPa*ssZ^`Wz|W-Sj%7fgO-;a9rEBb=*gOC7oUeixsj5;kQw5Ir2*S#p>J#K386;>zCB@%*VVc0$5c6GvGNOPyQZJ9 z8!SB&%%i_eP?FgrHX%)?RL{w13%%!+KgeuS42pN;y?yBq_#pY1k-z=O0Zq$cgHax+ zQP>)|o=IZ)@J7bbE-B{vdEPlB(aDJ-E-}AR1>_R_)g=q{!8zOqWlmvRE%8)qLma=i zcmeYi+?I8pcn6RAZe8`D?&<=NXS@0cxILkszvYXV+OhwtYerQ?axmo z1Ia{e}S`28WT;2Kqw_5nGi&pe#9*2wm?fk~HYOgaRSeoacG#|}i#ux54^`#u zB@U3;*KL){YUT<&=S2ed^)&7IP1CGAOy*4Zbn3Fy2C#}Wx=ztj@PtP)5q zv2w>e(z8hEqYPZxh}V31Js}+&QIeytiX67ski#JGWr^Owi9AF4K1$k7db5WQ2)rc) ztKV@QAbto&spTV?8nxLyx0rUqwo9~7)761nCxt<}sv_O{WxG3t=iI|9b8QlAH61Dn zXhhT(97Xqh((MfgVC++>;K2g>e<%en6BrlK#%K10Y{C~x7Cm*=JiH>ls8_uZ}TOwF(*6hm&y5O;{|*FW8| z4k8lffxCV>aUwr&3zS@-a^e6)GN5h&KO0Thi&VimUhRz~GuOH1+agm3&gQ#it#NgH?*+JKeExU-MzV$R8?$d_MGq9}LiHyE8z%c2 zqPdBbxpTh4VKe&9R#gsd^8QWaf3;9Gs*@8oO9_nz8YF+Ai$?5!lqbroY zzesZLD@Y9VZ7EKgr0buX+PX^ToU4rjv7#kPs@_sOLEmL4{{GSZ#N3VlZ0y&#d|xSv zUu=D+ObbS^8eqt?#jYy`!aJGk|2Vj(AXpLo7it@))mN*QjoPW2vruMne8zW9BA9X| zr3b*8op0+97QgFzQjlrqCeV&Se#bW~w%S*y6DY2CaujV#^kCJApt_iF8`8Jbzh^zT zupsLt?OQ3=s&q||0X*9N+Gwdd=li+i&5mOlV&nk#fwggeiqAi{zWjlg=Pzts`*vj5 z>4;3LME~LkpRZlKIa+h2HsM|8X4o!8hS{MjA}BPgRm=%>?kJb&KdK1Yr_w!_^X#Es zA=ARCcqF@~z)S=9;n!O`zZ`xBH1>Y;uj5~ijH^sxPuvlXr9&WkIev0X)QH$sNMzY2 zo2{qBj_*w&kLAb&h1XtB8Y0_deQz+4M3(28CViM!60RFmpMEzvYjX){Jr`Jz+Vv~r z(_1>~08B&J?e!C4i;69o8SZa+JEp%e$()Ey1ezUOnR3+8s+s~zZt@Tq?jwX{HkhdA}@nLGf zt6N-aB2qnHMYiT=qiH-Xiub~?J4H*Kh++bwxZuGmQ&ryZprUgqUf(jQ*znFwV_baO zjvYPY%t1gWhg70Kr9d~zR}&9p*Cr=;5Zn$&`bO6zi`<$g9~@5j*GZ{#XQawGL2~l< z%&2N@0IcnJweN$y-G#knidz{SQerTa2K@u+{EkR*utij1Ut;l%w(3Wm@%0?SHQKvQ z5X$6`3QwjuYZw5$t;Q*0|3?OempfI_tM;Vx5jOvwSXUI$0SI1n7F@v@%;hJZ?z*|5 z(=y-$vm<{ShjJvA9(cwPPe^S~Mt$%4p-Pz{s0!)Gc#?QQek~}N>8p;FI@Cn}iKPj( zXE*=mtAgxH^>rI(k|S2H;$l_e=IyDewoeVrz?xL+tFdCIeMfrPuYU(@Zf-TOklm?o z8T?u}mueLQ<|2u|u=kU-o`q^zsMF*%kw%N|B6MfZF%KP1R`gs1_`O@jtn;RmwUz{D zY2!xH<*ts@U9ErC?@rwunZ3K9<&mez&Smy6yDPWE>{+!BT&Vw})|CJI~7;lFQOjmu>yUUq(VvV~Zs&eQH_MGl2*@cs>_K{ejcDuXMzAe$% zzeX_nd6~4K-|K5tGw+wF-S4c)PffYTkqs@;+couKC=kocO0%nuyDk5Gp6>FY=(bZW ziUQJqj!Rkx1&+Rg+)G`$RPT*6*}#DenT3>IUG73thapoS;P!I?T*X?Y>sFEsL%c-}?Fy3a%N;~z zxaw(cu*sCZC8PdK%oG27{Z0EfpUn4%K95`>cUBcIKK)BjDQMou%h4P#iMDThPF3(4 z8=Ef%{-rx$ZKh=DM;|n&pN9Uf>&i1Wm!-GoZzJTj->Z2t(+p+Ktt`NcmNh?fJE;G= zH=5v$0LlWGm`Tb8anopIH@^l_E3dsd6qvRCW5C>s)ualh$KPXKxXl$b`5*GA z_(at6W#y++)m{%LCgse#hQtW6I~E*1yJ<-`@#UL9w(kp3sgl9IL19$sqzi-$uwdT9kYUNapmoqgcr3K1Mi+nl*M`G(cdnLFmHlNTq%>t3YR<%$x znD(p2=3l)}Ed5l|*K&A!!-pdSN}C1F#C@|{o14w4>FHNDmg((~Bk>7;>O{Cpb~grv zPK<6n)Vw;MiR@5TZWa)UtZ+ zF~{$2KKZ~`r8gA(LZdY@|ALHfuPPlp^V%yfzT73S+y<0B|~38=c+1jNOipYbki$OYs1F>;m@y}eDdoZ z@_DbOefBGMe=}isJ;)QZX#c;xa%%RpqC}PZ^p@6%pZL7Z=9cmB8Bk>wX}h7;pk^+L zZ>(OEcI;QhyvR==@q}NPFnarL3=0IAPc5DPR%BSZi~H5V@ekJgF^$3kUu-sIVi1() zPjA9U9{=+AFx&)00zg>qEa>E2ptgCQ`11#0kAu=&=^%h(>01C(b`t<*D;Yd<^mroDhNv3~y>XZ{p2miVO z`%|W{q>y{Znljv$^@qO!g=G-gb3*&-1I(Oe;PwOD3{wJ126s{j_O~KI zU?sN%JNQ5NfUW2v)dXZsmWG7u3s2BI@I9Cx?lxHv`4`@CB2*WVXCW*_BZ_kp3vd3H z51F&bAzBNBo`SlCV?#sau>fl<@U&KA%?mhu5WBha2|66Q9hgl&ZBX}unPt#|c4tKM zH&hnydAbtF#E=eiVfh$S*qMQs7M||;>2u~SZ@FXMS0(Fy@jNIiFq@EaGj_!oUI@}A zZ>3*eb(`n2J?_b|k66WfFB7^J1p9#gad36+k$aRIA~qlS{uq3C9FUrF-~qF1EMpN`;rXT!jf2R-z)xWOxbUAcEMEJ(i?BL#i3onmUAoxTEKU}#F3BJwtB ze?fzij%|{XxYLO5xyJQ>1z>~N0%*vQwepzjJP;i&;s$X9(I<=x3RXpj68BDeQHV-6zP-gG#QOz8-P(>EH9Iz|! zYc;^6Xes7;x96(hku1J)(f#z0kn!ovR1sqAM^_5^yG?^`ymuVE=&jCk?<;*EwKxDB z_3-xz7Di{G)(J+Ikc^acJ)-hl268l&Z-&4#2PolsMOtf>t|5=O6fWy6AeN?0@m=OJ zr#Qh5FX$Tkcdk*<@3IM9+5+YINWtKCfht%OFY)Ya6+_26tzTzQx>Ws*B3HTI9j_0j z>FGigWExz@AE^N{w~=VY$INsR5L~$+7|UE{C3l(&WRLM~;Pa)P`Jt8x0|7;~tJLl( zh6g1$2)sOUrgTku`VUQd&k1rQQs3VSI-x-vC}`*s?fpd*z*Y%gysowi(88LWDhWJr zE+eFG2+7{xa_Ezm(B7sNoEl=-M7Tf!Pcaa!7P}S_LPgPS$-oZt&jf3;+}0$SZ8a5D**uP+yC~%Vr~C7O3Eh; zGy(j!wK#K-T}s2TGJp=>RE9=Cr5JRtnMebdc0I`3If_nS@G^B?@^fgvT~G(<icHFbe1ddMiR)hG&46&PlQ`m7bS(_02hgDyd^!5A790(3ukUKO~^ zg=}cNA+y3-ELHR;6M*U%F4B9$75DkB!$o3q55&e3s9DvcEN{A)8K~yaKcdDu_(#4< zyT8IiuVN9}O~G^EcMB>kzlJ9T01PQw#Y4)JMbG9L0Ai zpfB^<`z|XFdR=b5A-i583zVBu4XYRnc70fX0Ac9%_I#av|{^utyARB_I6_F$@1Duvz>m_r|M&ed^(A1Kd&i2z;~ z#UsG$;_NotC-6UD{W)Sa8Ff8J;;)HkmQ)r+aVJo)*g_K`2c5o-M) z^jG>a?cQ-*P=`Jys{`v&Bx$49i$>lDr9z%}H(J}#d6vwP8^>{T+#khM5I8@{vPKi2 z^pxQs&Awah4~Qr72WkX9FqBz(Y7N<6!|yr@ND@&WJ~1@9l7lIr(F%Y*CX}Zbjh^qS z5%f&R*_j^^BXeowCXkhn*Ve7kw#RE5B=Vp}an&I2f?n8K&=X|3QUua)qwwI!0ufUu zF=qw~C?Clz1{t~*mb)4DA#q< zE3S z8C-WK7Z8lGvg&Z+7FhZ7v5JP+@P)Cl0>^H_yM)*dtLG9(K<_MC7ax=sWPhc1wUX|y zI8xsU@?x5kd*$@-jA)_0y_M{i08a14`2UsK6&=OE{f58E!avpT0siFNml#dRUV0lA&(*&OXH6Di4bNUA#ygh4|Ahg>e1tIm$TKz~Gl%zd&U%j2-+fR|2@T(Cn zic0JNu0OT#NZh0s&p*2l=>5*x6X{IGu^u+a+L_a8dqM2jAfPkFqfd!1ZeOejori`y z9`yZ#S%HZYRN4ZAz^59mgH6xI)}Ske>0!#0k=-2@zB*GrctD|4eHJ!KS8qM zvt&7#y+hFg|I$NmE(4ytU_W44Fr|@wb)D$r2;4;A5QOqUrL_P)g<2Pr|3U9IP$ll> zG8K46cQ1lY&n{q4i-7LSoPirEFb^OB?S{oaC!i1R_FG3V6|+0OZ-YtkPOD7<1HV!aU6EEoQ_aTa%4ca zKe#(8-5XaU^7o?U$Oe^2;H+}SSVCJY(6r-z-afKVyylf0v}LhUm!@Y?`@0Qw;2R4- zt(VaqYvKeQP!58wXIMp(*bjxi7Ep}Vag5z3Eq%kJEsY#OqmmB0U%wCK2$}9YI~JPaBO;dE?}LhoKo2 zoWUuv?eDsj)Flqp;U&b!-8G=e3%J|SZp6k z)jGhNZN!~`BI|uAX6F`wCTlbrQ1uOT^E>enQgQ{(FT(|IOm~>=!ExXMb90~>(J!la z{rZ^9m!udCl1Awf{`R{5h7vW|xhF3%)QQ{N6=V0EK6VhjI$2pPu?)P7N)~2$C!+!h zk}z~gp`XGt01coP&pgY!uw_So58Buvt1pjas{Ar1?As7H$wq~{fB<)xn(r@wK!OIK zamxvDMg|UL0~m}SBX!O~%t4L6Hw&hFt2MrrOHJecrausw0sSI!f^L{4P=J^1oBVlg zql_Pf2CN5D@B62h!R?CJ2B5Kw8s7JRlm6v@EnFT;TjGuUR|!_`j(*C|z|sFhOlv>q zcJ4{h{JdBpy80RSeNTOajW>TDk)KE8ZWRN+V*vbK|15=@pB Date: Wed, 5 Nov 2025 04:17:48 +0000 Subject: [PATCH 007/133] Initial plan From 21e03cece5168a241898619fe0fe0a146ab0ec8c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 5 Nov 2025 04:22:21 +0000 Subject: [PATCH 008/133] Add workflow_dispatch trigger and required files for workflows Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- .github/workflows/main.yml | 3 +- Transzendenz/Reports/Deploy-Status.md | 28 +++++ Transzendenz/Reports/snapshot-log.txt | 7 ++ WORKFLOW-DOCUMENTATION.md | 169 ++++++++++++++++++++++++++ 4 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 Transzendenz/Reports/Deploy-Status.md create mode 100644 Transzendenz/Reports/snapshot-log.txt create mode 100644 WORKFLOW-DOCUMENTATION.md diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9594a12..ff0b4f9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,6 +9,7 @@ on: pull_request: branches: - main + workflow_dispatch: permissions: contents: read @@ -61,4 +62,4 @@ jobs: name: yoni-build-artifacts path: | build - dist \ No newline at end of file + dist diff --git a/Transzendenz/Reports/Deploy-Status.md b/Transzendenz/Reports/Deploy-Status.md new file mode 100644 index 0000000..c93e1fb --- /dev/null +++ b/Transzendenz/Reports/Deploy-Status.md @@ -0,0 +1,28 @@ +# 📦 YONI Deploy Status + +**Last Updated:** 2025-11-05 + +## Deployment Tasks + +| Task | Status | Description | +|------|--------|-------------| +| 1. Repository Setup | ✅ Complete | Initial repository structure created | +| 2. CI Workflow Configuration | ✅ Complete | GitHub Actions CI pipeline configured | +| 3. Build Process | ✅ Complete | Build scripts and dependencies set up | +| 4. Test Infrastructure | ✅ Complete | Testing framework configured | +| 5. Deploy Snapshot Workflow | ✅ Complete | Daily snapshot workflow implemented | +| 6. Email Notifications | 🔄 In Progress | Email notification system configuration | +| 7. Production Deployment | 🔄 In Progress | Production environment setup | +| 8. Monitoring & Logging | 🔄 In Progress | Monitoring and logging infrastructure | + +## Summary + +- **Total Tasks:** 8 +- **Completed:** 5 +- **In Progress:** 3 +- **Blocked:** 0 + +## Notes + +This status file is used by the deploy_snapshot.yml workflow to track deployment progress. +A snapshot of this file is created daily with the current date. diff --git a/Transzendenz/Reports/snapshot-log.txt b/Transzendenz/Reports/snapshot-log.txt new file mode 100644 index 0000000..21931cf --- /dev/null +++ b/Transzendenz/Reports/snapshot-log.txt @@ -0,0 +1,7 @@ +# YONI Deploy Snapshot Log + +This file tracks all snapshot events from the deploy_snapshot.yml workflow. + +--- + +✅ Snapshot log initialized on 2025-11-05 diff --git a/WORKFLOW-DOCUMENTATION.md b/WORKFLOW-DOCUMENTATION.md new file mode 100644 index 0000000..0df2112 --- /dev/null +++ b/WORKFLOW-DOCUMENTATION.md @@ -0,0 +1,169 @@ +# YONI Workflow Documentation + +This document describes the GitHub Actions workflows available in this repository and how to use them. + +## Available Workflows + +### 1. CI for YONI (x148) - `main.yml` + +**Purpose:** Continuous Integration workflow that builds, tests, and validates the YONI application. + +**Triggers:** +- **Automatic:** Pushes to `main` or `releases/*` branches +- **Automatic:** Pull requests targeting `main` branch +- **Manual:** Can be triggered via GitHub Actions UI + +**What it does:** +1. Checks out the repository +2. Sets up Node.js 20 +3. Installs dependencies +4. Runs linter (if available) +5. Runs tests (if available) +6. Builds the project (if available) +7. Uploads build artifacts + +**Manual Trigger:** +1. Go to the **Actions** tab in GitHub +2. Select "CI for YONI (x148)" from the workflow list +3. Click **"Run workflow"** button +4. Select the branch to run on +5. Click **"Run workflow"** to start + +### 2. Deploy Snapshot - `deploy_snapshot.yml` + +**Purpose:** Creates daily snapshots of deployment status and sends email notifications. + +**Triggers:** +- **Automatic:** Daily at midnight (UTC) via cron schedule +- **Manual:** Can be triggered via GitHub Actions UI + +**What it does:** +1. Checks out the repository +2. Sets up Node.js 20 +3. Creates a dated copy of the deployment status report +4. Updates the snapshot log +5. Commits and pushes changes back to the repository +6. Sends an HTML email notification with snapshot details + +**Manual Trigger:** +1. Go to the **Actions** tab in GitHub +2. Select "📦 YONI Deploy Snapshot" from the workflow list +3. Click **"Run workflow"** button +4. Select the branch to run on (usually `main`) +5. Click **"Run workflow"** to start + +## Required Secrets + +The `deploy_snapshot.yml` workflow requires the following secrets to be configured in your repository: + +### Email Configuration Secrets + +| Secret Name | Description | Example | +|-------------|-------------|---------| +| `MAIL_USER` | Gmail username for SMTP authentication | `your-email@gmail.com` | +| `MAIL_PASS` | Gmail app password (not your regular password) | `abcd efgh ijkl mnop` | +| `MAIL_TO` | Recipient email address(es) | `recipient@example.com` | + +### Setting up Secrets + +1. Go to your repository on GitHub +2. Navigate to **Settings** > **Secrets and variables** > **Actions** +3. Click **"New repository secret"** +4. Add each secret with its name and value +5. Click **"Add secret"** + +### Gmail App Password Setup + +For `MAIL_PASS`, you need to create a Gmail App Password: + +1. Go to your Google Account settings +2. Enable 2-Factor Authentication if not already enabled +3. Go to **Security** > **2-Step Verification** > **App passwords** +4. Generate a new app password for "Mail" +5. Use this 16-character password as the `MAIL_PASS` secret + +## Repository Structure + +The workflows expect the following directory structure: + +``` +YONI-app/ +├── .github/ +│ └── workflows/ +│ ├── main.yml +│ └── deploy_snapshot.yml +├── Transzendenz/ +│ └── Reports/ +│ ├── Deploy-Status.md # Main deployment status file +│ ├── Deploy-Status-YYYY-MM-DD.md # Daily snapshots (auto-generated) +│ └── snapshot-log.txt # Snapshot event log +└── WORKFLOW-DOCUMENTATION.md # This file +``` + +## Troubleshooting + +### deploy_snapshot.yml fails with "No such file or directory" + +**Problem:** The workflow cannot find `Transzendenz/Reports/Deploy-Status.md` or `snapshot-log.txt` + +**Solution:** Ensure the following files exist in your repository: +- `Transzendenz/Reports/Deploy-Status.md` +- `Transzendenz/Reports/snapshot-log.txt` + +These files should be committed to your repository before running the workflow. + +### deploy_snapshot.yml fails to send email + +**Problem:** Email sending step fails + +**Possible causes:** +1. Missing or incorrect secrets (`MAIL_USER`, `MAIL_PASS`, `MAIL_TO`) +2. Invalid Gmail app password +3. Gmail account security settings blocking the app + +**Solution:** +1. Verify all email secrets are set correctly +2. Ensure you're using an App Password, not your regular Gmail password +3. Check that 2FA is enabled on your Gmail account +4. Review Gmail security settings for blocked sign-in attempts + +### deploy_snapshot.yml fails to push changes + +**Problem:** Git push fails with authentication error + +**Solution:** The workflow uses the `GITHUB_TOKEN` secret which is automatically provided by GitHub Actions. Ensure the workflow has write permissions: + +1. Go to **Settings** > **Actions** > **General** +2. Under "Workflow permissions", ensure "Read and write permissions" is selected +3. Save the changes + +### main.yml workflow not appearing in Actions UI + +**Problem:** Cannot find the workflow to run manually + +**Solution:** +1. Ensure the workflow file exists in `.github/workflows/main.yml` +2. Verify the YAML syntax is correct +3. Push the changes to the repository +4. Refresh the Actions page + +## Best Practices + +1. **Test manually first:** Before relying on automatic triggers, test workflows manually to ensure they work correctly +2. **Monitor snapshot logs:** Regularly check `Transzendenz/Reports/snapshot-log.txt` to verify snapshots are running +3. **Keep secrets secure:** Never commit secrets to the repository +4. **Review artifacts:** Check uploaded artifacts after CI runs to ensure builds are successful +5. **Update status regularly:** Keep `Deploy-Status.md` up to date with current deployment status + +## Support + +For issues or questions about these workflows: +1. Check this documentation first +2. Review workflow run logs in the Actions tab +3. Consult GitHub Actions documentation: https://docs.github.com/actions +4. Open an issue in this repository with details about the problem + +--- + +**Last Updated:** 2025-11-05 +**Version:** 1.0 From 3f708cecfdc46c051f09154d94f19ad46f6e7e8c Mon Sep 17 00:00:00 2001 From: YONI <233804448+pappensex@users.noreply.github.com> Date: Tue, 11 Nov 2025 17:21:27 +0100 Subject: [PATCH 009/133] Add files via upload --- YONI_Local_Run_Guide.md | 32 +++++++++++++++ yoni-app-devcontainer.patch | 80 +++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 YONI_Local_Run_Guide.md create mode 100644 yoni-app-devcontainer.patch diff --git a/YONI_Local_Run_Guide.md b/YONI_Local_Run_Guide.md new file mode 100644 index 0000000..3fc84af --- /dev/null +++ b/YONI_Local_Run_Guide.md @@ -0,0 +1,32 @@ +# YONI Local Run Guide (Testmodus) + +## chi-bot-starter +```bash +cd chi-bot-starter +npm install +npm run dev +``` +ENV (example): +``` +OPENAI_API_KEY=sk-... +``` +Routes: `/app/api/chat` + +## pihoch2-dashboard-nextjs +```bash +cd pihoch2-dashboard-nextjs +npm install +npm run dev +``` + +## Static site (Prolula_Website_Starter) +Open `index.html` directly in a browser. + +## Codespaces quickstart +1. New Codespace on repo. +2. Terminal: +```bash +npm ci +npm run dev -- -p 3000 +``` +3. Expose port 3000 -> public. diff --git a/yoni-app-devcontainer.patch b/yoni-app-devcontainer.patch new file mode 100644 index 0000000..79ca084 --- /dev/null +++ b/yoni-app-devcontainer.patch @@ -0,0 +1,80 @@ +*** Begin Patch +*** Add File: .devcontainer/devcontainer.json +{ + "name": "YONI Next.js", + "image": "mcr.microsoft.com/devcontainers/typescript-node:22", + "features": { + "ghcr.io/devcontainers/features/github-cli": "1" + }, + "postCreateCommand": "corepack enable && (npm ci || npm install)", + "forwardPorts": [3000, 3001], + "portsAttributes": { + "3000": { + "label": "web", + "visibility": "public" + } + }, + "customizations": { + "vscode": { + "settings": { + "editor.formatOnSave": true + }, + "extensions": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "GitHub.copilot" + ] + } + } +} + +*** Add File: .vscode/tasks.json +{ + "version": "2.0.0", + "tasks": [ + { + "label": "dev", + "type": "shell", + "command": "npm run dev -p 3000", + "isBackground": true, + "problemMatcher": [] + } + ] +} + +*** Add File: .env.example +# Copy this file to .env.local and fill in your own secret values. +OPENAI_API_KEY= +STRIPE_WEBHOOK_SECRET= +GITHUB_WEBHOOK_SECRET= +GITHUB_APP_INSTALLATION_TOKEN= +X148_ALIAS=148-Amon + +*** Add File: README.md +# YONI Next.js Setup + +This repository contains the YONI web and API components. Follow these instructions to get up and running. + +## Local Setup + +1. Ensure Node.js and npm are installed. +2. Install dependencies: + ```bash + npm install + ``` +3. Copy `.env.example` to `.env.local` and fill in environment variables. +4. Start development server: + ```bash + npm run dev -p 3000 + ``` + +## Codespaces Setup + +A Dev Container configuration is provided via the `.devcontainer` directory. + +1. Create a Codespace on the `main` branch. +2. Codespaces will automatically install dependencies and forward port 3000. +3. Expose port 3000 and access the application at the forwarded URL. + +The `.vscode` folder contains recommended tasks for running the development server. +*** End Patch From 1c1fad6113cd7e046882f81f82ef55ca9806a184 Mon Sep 17 00:00:00 2001 From: YONI <233804448+pappensex@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:03:59 +0100 Subject: [PATCH 010/133] Update upload-artifact action to version 4 --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9594a12..3b55191 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -54,11 +54,11 @@ jobs: echo "No build script defined" fi - - name: Upload build artifacts (optional) - uses: actions/upload-artifact@v3 + - name: Upload build artifacts (optional) + uses: actions/upload-artifact@v4 if: success() with: name: yoni-build-artifacts path: | build - dist \ No newline at end of file + dist From 72b48bd959616a7a7d97804059e7df5328eef4f2 Mon Sep 17 00:00:00 2001 From: YONI <233804448+pappensex@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:08:26 +0100 Subject: [PATCH 011/133] Remove unnecessary blank line in main.yml --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3b55191..65ab60c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,5 +1,4 @@ name: CI for YONI (x148) - # Trigger the workflow on pushes to `main` and PRs targeting `main`. on: push: From 190f843e3d5759c6983e351ac8a3610f011556f7 Mon Sep 17 00:00:00 2001 From: YONI <233804448+pappensex@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:34:19 +0100 Subject: [PATCH 012/133] Add files via upload --- yoni-ci-pack.zip | Bin 0 -> 1407 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 yoni-ci-pack.zip diff --git a/yoni-ci-pack.zip b/yoni-ci-pack.zip new file mode 100644 index 0000000000000000000000000000000000000000..b0edb690d71680f8361af81bc28a348194bf3a4c GIT binary patch literal 1407 zcmWIWW@Zs#U|`^2@SL0-9oX$H{+5w}p^b%sK@BLXm!4UYQJSP*o?n!mmXlvztY4I0 zT9T@pQd*FcnVeXXTC7)@o6{Pcn19Pa;P1Kh6-jfsG8A8J@n1MQYVGWh%vlq@ZFHLK zsdwbejdkbUGFt!JsVhhYy!fR0aqsT(_jAgl)@U5Qa&guccfmE2c7AC2{Ze@j=e!;1 zCD)j90$y!j{M_NqYh#0Qr{|)DZR%@UuFNpajEQZj_BMVll{;hdjoym+Zi1Qz3(y~J&`xh|6TR{&$7Z<|68nX()G?g zt6m(rzr0uQ*u4MD`S*@Y&!4WZKegXIz3`NF^S`ON?&WW-s~j_Byj;xP+n%kry?eIo zFDKXMszyQW*%Ov=mfdLT{rpjMOO0F55qEbu$`+W6J@6f(z#?G;sv&MCobMmx_ z#m7=_IxpYLZsT(C$9ygxi+6|Xo(7!Qq!PZq>hXq$J6<#GlZtfb6Yi{E*y5 znfXx*gSIWdHTj>(^f1G$AM61r3Fd>B@J2acf>{H^Lcj!5mRgjYniHRzR~BDfnU}0r zoH4cE*Xyu@%+c?bM_)%ciZA8RI}%i&b&!wO)8mo&Ql8w;{IWeWCwOWlRkyv1t>iiX zm~~InWvxrS+}rnmKWd#(Dji&!zUoEL#v?gdVtosxWV1}XLW9ggcNQOByrW*EW!?GK zCkrPif0rv1>bx*@`?~}Q7G=rZ($jBY3v=+W@u%fmVegZ<-)jt zMAZdrLITqh+S?+VuS$kR1eq?ExxM?c$=#0~k*ob?SFSB-yvkL2^}>JYjFm4k_5F7K z%v<==skwH~zS@2H4d33)XSAL6wV3~z<5bn9w~c2O1;tL-Y(8Q3{X_bwp}G9Tgy=WG zP>cnJCO?o4a&>g^b&YrSj1LIX%S~zRcg$`!5IO$-rwG3dUoe|>j}w2U_^kyiGH#VF zKayqTyjZhy+w1M~^F+9;-+Ze0Q1@T{z5Ic$vk!AEq+_SW7?Vr$SFL z_?89S*~O(8biJ<6X~K`-?`I2U#ICiL?cMup=Ilv{wdR#I*}ZcvobGp7@lNTF#=(6P zIanEU_BsWfa(*fO<#fZvyPOIQi_7-4$8G0Yxqfr0?Gi~}4q=sKrA6}GJsxfMKS4)WN7JG+`B3C6j(Kh-l`P-S>#uJ*(j(Gc$nBYJ zZL*iYUfb{N1@i!JMkWzv+$9q*biiOqBZwldoI*DfJ@GJf%FVu5zD{;0A^`ZE&u=k literal 0 HcmV?d00001 From 8670e64f8cbcfb2fe2d1723ff880dad3f11d66ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:10:28 +0000 Subject: [PATCH 013/133] Initial plan From 03bd2142f60591e5a00f598a1289989902ce3fab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:15:16 +0000 Subject: [PATCH 014/133] feat: add Copilot PR import script with governance enforcement Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- COPILOT_PR_IMPORT.md | 195 +++++++++++++++++++++++++++++++++++++++++++ README.md | 18 ++++ import-copilot-pr.sh | 156 ++++++++++++++++++++++++++++++++++ 3 files changed, 369 insertions(+) create mode 100644 COPILOT_PR_IMPORT.md create mode 100755 import-copilot-pr.sh diff --git a/COPILOT_PR_IMPORT.md b/COPILOT_PR_IMPORT.md new file mode 100644 index 0000000..2003d73 --- /dev/null +++ b/COPILOT_PR_IMPORT.md @@ -0,0 +1,195 @@ +# Copilot PR Import Script + +## Overview + +The `import-copilot-pr.sh` script automates the process of importing external Copilot PRs while enforcing company governance policies. + +## Purpose + +This script helps maintain code quality and consistency by: + +1. **Importing External PRs**: Fetches and applies patches from Copilot tasks repository +2. **Enforcing Governance**: Automatically applies company policies +3. **Validating Changes**: Ensures no conflicts or duplicates exist +4. **Building & Testing**: Validates the imported code builds successfully + +## Company Governance Policies + +The script automatically enforces the following policies: + +### 1. Single Stripe Webhook Route +- **Rule**: Only TypeScript webhook routes are allowed +- **Action**: Removes `api/stripe/webhook/route.js` if it exists +- **Keeps**: `api/stripe/webhook/route.ts` + +### 2. TypeScript-Only Codebase +- **Rule**: JavaScript files are not allowed (`allowJs: false`) +- **Action**: Updates or adds `"allowJs": false` in `tsconfig.json` +- **Purpose**: Ensures type safety across the codebase + +### 3. No Duplicate Routes +- **Rule**: Files with the same name but different extensions are not allowed +- **Check**: Scans all tracked files for duplicates +- **Action**: Fails the import if duplicates are found + +## Usage + +### Basic Usage + +```bash +./import-copilot-pr.sh +``` + +### Example + +```bash +./import-copilot-pr.sh 42 +``` + +This will: +1. Create a new branch: `import/copilot-pr-42` +2. Fetch the patch from `https://github.com/copilot/tasks/pull/42.patch` +3. Apply the patch with commit history +4. Enforce governance policies +5. Build the project +6. Push the branch to origin + +### After Running + +Once the script completes successfully, create a PR using: + +```bash +gh pr create --fill \ + --title "import: copilot/tasks PR 42" \ + --body "Imported via patch; build ok; no duplicate routes; TS enforced." +``` + +## Workflow Steps + +### 1. Branch Creation +- Fetches latest from `origin` +- Creates branch: `import/copilot-pr-` +- Based on: `origin/main` + +### 2. Patch Application +- Downloads patch from Copilot tasks repository +- Performs dry run (`git apply --check`) +- Applies with `git am` to preserve commit history + +### 3. Policy Enforcement +- Removes JavaScript webhook routes +- Configures TypeScript-only mode +- Validates no duplicate routes + +### 4. Build Validation +- Enables corepack (if available) +- Installs dependencies (`npm ci` or `npm install`) +- Runs build script (if defined) + +### 5. Commit & Push +- Commits policy changes +- Pushes branch to origin +- Provides PR creation command + +## Error Handling + +### Merge Conflicts + +If the patch doesn't apply cleanly: + +``` +⚠️ Conflicts detected during patch application. +Please resolve manually: + 1. Check status: git status + 2. Edit conflicted files + 3. Stage changes: git add -A + 4. Continue: git am --continue +``` + +**Resolution Steps:** +1. Check which files have conflicts: `git status` +2. Edit conflicted files to resolve markers +3. Stage resolved files: `git add -A` +4. Continue applying: `git am --continue` +5. Re-run the script from governance enforcement onwards + +### Duplicate Routes + +If duplicate routes are detected: + +``` +❌ Duplicate routes detected: +api/stripe/webhook/route +``` + +**Resolution Steps:** +1. Review the duplicate files +2. Decide which version to keep (prefer TypeScript) +3. Remove the unwanted file +4. Re-run the script + +## Requirements + +- Git +- Bash 4.0+ +- Node.js and npm (if project has `package.json`) +- corepack (optional, for package manager version management) +- curl (for fetching patches) +- GitHub CLI (`gh`) for creating PRs (optional) + +## Troubleshooting + +### Script Won't Execute + +```bash +chmod +x import-copilot-pr.sh +``` + +### Patch Not Found (404) + +- Verify the PR number is correct +- Check that the PR exists at `https://github.com/copilot/tasks/pull/` +- Ensure you have internet connectivity + +### Build Failures + +- Review the build output for specific errors +- Check that all dependencies are properly defined +- Ensure TypeScript types are correct + +### Permission Errors on Push + +- Verify you have write access to the repository +- Check your Git credentials are configured +- Ensure you're authenticated with GitHub + +## Best Practices + +1. **Review the Patch First**: Check the PR on GitHub before importing +2. **Clean Working Directory**: Ensure no uncommitted changes before running +3. **Test Locally**: After import, test the changes locally before creating PR +4. **Descriptive PR Titles**: Use the suggested format but add context if needed +5. **Review Policy Changes**: Check that governance enforcement didn't break functionality + +## Security Considerations + +- The script downloads patches from `github.com/copilot/tasks` +- Patches are saved to `/tmp/copilot.patch` (temporary file) +- All changes are committed and pushed under your Git identity +- Review patches before applying in production environments + +## Maintenance + +This script enforces current company policies. If policies change: + +1. Update the governance section (lines dealing with `.js` removal or `tsconfig.json`) +2. Update this documentation +3. Test with a sample PR before rolling out + +## Support + +For issues or questions: +1. Check this documentation +2. Review error messages carefully +3. Open an issue in the repository +4. Contact the platform team diff --git a/README.md b/README.md index efa1983..cedaaac 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,24 @@ curl -s http://localhost:8787/chat -H "Content-Type: application/json" -d '{ "us --- +## Copilot PR Import + +Für das Importieren externer Copilot PRs mit automatischer Governance-Durchsetzung: + +```bash +./import-copilot-pr.sh +``` + +Das Script erzwingt automatisch: +- **Einzelne Stripe Webhook Route** (nur TypeScript, keine .js Dateien) +- **TypeScript-Only Policy** (`allowJs: false` in tsconfig.json) +- **Keine Duplikate** (Dateien mit gleichem Namen aber verschiedenen Erweiterungen) +- **Build-Validierung** vor dem Push + +Details siehe [COPILOT_PR_IMPORT.md](./COPILOT_PR_IMPORT.md) + +--- + ## Nächste Schritte - Realtime‑Pfad (WebRTC/WebSocket) für Voice/Audio aktivieren. diff --git a/import-copilot-pr.sh b/import-copilot-pr.sh new file mode 100755 index 0000000..2821cc8 --- /dev/null +++ b/import-copilot-pr.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env bash +# Import External Copilot PR with Governance Enforcement +# Usage: ./import-copilot-pr.sh +# +# This script: +# 1. Fetches and applies a patch from an external Copilot PR +# 2. Enforces company governance rules: +# - Single Stripe webhook route (TypeScript only) +# - TypeScript-only policy (allowJs: false) +# 3. Validates no duplicate routes exist +# 4. Builds and validates the project +# 5. Creates a branch ready for PR + +set -euo pipefail + +# Check if PR number is provided +if [ $# -eq 0 ]; then + echo "Usage: $0 " + echo "Example: $0 123" + exit 1 +fi + +PR_NUM="$1" +BR="import/copilot-pr-$PR_NUM" + +echo "🚀 Importing Copilot PR #$PR_NUM" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +# Fetch latest changes from origin +echo "📥 Fetching latest changes from origin..." +git fetch origin + +# Create and checkout new branch from origin/main +echo "🌿 Creating branch: $BR" +git checkout -B "$BR" origin/main + +# Fetch the patch from the external Copilot PR +echo "📦 Fetching patch from Copilot PR #$PR_NUM..." +curl -fsSL "https://github.com/copilot/tasks/pull/$PR_NUM.patch" -o /tmp/copilot.patch + +# Dry run to check if patch applies cleanly +echo "🧪 Testing patch application (dry run)..." +git apply --check /tmp/copilot.patch + +# Apply patch with git am to preserve commit history +echo "✨ Applying patch with commit history..." +if ! git am /tmp/copilot.patch; then + echo "⚠️ Conflicts detected during patch application." + echo "Please resolve manually:" + echo " 1. Check status: git status" + echo " 2. Edit conflicted files" + echo " 3. Stage changes: git add -A" + echo " 4. Continue: git am --continue" + exit 1 +fi + +echo "" +echo "🏛️ Enforcing company governance policies..." +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +# Policy 1: Single Stripe webhook route (remove .js, keep only .ts) +echo "📋 Policy 1: Enforcing single Stripe webhook route (TypeScript only)" +if [ -f api/stripe/webhook/route.js ]; then + echo " → Removing api/stripe/webhook/route.js" + rm -f api/stripe/webhook/route.js +else + echo " ✓ No JavaScript webhook route found" +fi + +# Policy 2: TypeScript-only (set allowJs: false in tsconfig.json) +echo "📋 Policy 2: Enforcing TypeScript-only policy" +if [ -f tsconfig.json ]; then + echo " → Updating tsconfig.json to set allowJs: false" + + # Check if allowJs already exists + if grep -q '"allowJs"' tsconfig.json; then + # Replace existing allowJs value + sed -i 's/"allowJs" *: *true/"allowJs": false/' tsconfig.json + echo " ✓ Updated existing allowJs setting to false" + else + # Add allowJs: false to compilerOptions + sed -i '0,/"compilerOptions"[[:space:]]*:[[:space:]]*{/{s//"compilerOptions": {\n "allowJs": false,/}' tsconfig.json + echo " ✓ Added allowJs: false to compilerOptions" + fi +else + echo " ℹ️ No tsconfig.json found (will be created if needed)" +fi + +echo "" +echo "🔍 Checking for duplicate routes..." +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +# Find files with same name but different extensions (potential duplicates) +git ls-files | sed "s/\.[^.]*$//" | sort | uniq -d | tee /tmp/dupes.txt + +if [ -s /tmp/dupes.txt ]; then + echo "❌ Duplicate routes detected:" + cat /tmp/dupes.txt + echo "" + echo "Please resolve duplicate routes before continuing." + exit 1 +else + echo "✓ No duplicate routes found" +fi + +echo "" +echo "🔨 Building project..." +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +# Enable corepack if available (for package manager version management) +corepack enable 2>/dev/null || echo "ℹ️ corepack not available, skipping" + +# Install dependencies +echo "📦 Installing dependencies..." +if [ -f package-lock.json ]; then + npm ci +elif [ -f package.json ]; then + npm install +else + echo "ℹ️ No package.json found, skipping npm install" +fi + +# Run build if build script exists +if [ -f package.json ] && npm run 2>/dev/null | grep -q "build"; then + echo "🏗️ Running build..." + npm run build +else + echo "ℹ️ No build script found, skipping build" +fi + +echo "" +echo "💾 Committing policy enforcement changes..." +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +# Stage all changes (including policy enforcement) +git add -A + +# Commit if there are changes +if git diff --cached --quiet; then + echo "ℹ️ No additional changes to commit" +else + git commit -m "chore: import external PR $PR_NUM; enforce single Stripe webhook + TS only; policy compliance" + echo "✓ Changes committed" +fi + +# Push branch to origin +echo "🚀 Pushing branch to origin..." +git push -u origin "$BR" + +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "✅ Branch ready: $BR" +echo "" +echo "Next step - Create PR with:" +echo "gh pr create --fill --title \"import: copilot/tasks PR $PR_NUM\" --body \"Imported via patch; build ok; no duplicate routes; TS enforced.\"" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" From 1a437e8fc5bc3e0d174dd1b77430ff6e64e2b7dc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:18:52 +0000 Subject: [PATCH 015/133] refactor: improve TypeScript-only enforcement to handle all duplicate JS/JSX files Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- COPILOT_PR_IMPORT.md | 28 ++++++++++++++++------------ import-copilot-pr.sh | 41 ++++++++++++++++++++++++++++++++++------- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/COPILOT_PR_IMPORT.md b/COPILOT_PR_IMPORT.md index 2003d73..4d647b2 100644 --- a/COPILOT_PR_IMPORT.md +++ b/COPILOT_PR_IMPORT.md @@ -17,20 +17,24 @@ This script helps maintain code quality and consistency by: The script automatically enforces the following policies: -### 1. Single Stripe Webhook Route -- **Rule**: Only TypeScript webhook routes are allowed -- **Action**: Removes `api/stripe/webhook/route.js` if it exists -- **Keeps**: `api/stripe/webhook/route.ts` - -### 2. TypeScript-Only Codebase -- **Rule**: JavaScript files are not allowed (`allowJs: false`) -- **Action**: Updates or adds `"allowJs": false` in `tsconfig.json` -- **Purpose**: Ensures type safety across the codebase +### 1. TypeScript-Only Codebase (Remove Duplicate JS/JSX) +- **Rule**: All code must use TypeScript; JavaScript files are not allowed when TypeScript equivalents exist +- **Action**: Automatically removes all `.js` and `.jsx` files that have corresponding `.ts` or `.tsx` files +- **Examples**: + - If both `route.js` and `route.ts` exist, removes `route.js` + - If both `Component.jsx` and `Component.tsx` exist, removes `Component.jsx` +- **Purpose**: Ensures type safety and consistency across the codebase + +### 2. TypeScript Configuration +- **Rule**: JavaScript files should not be allowed (`allowJs: false`) +- **Action**: Updates or adds `"allowJs": false` in `tsconfig.json` if it exists +- **Purpose**: Enforces TypeScript-only at the compiler level ### 3. No Duplicate Routes -- **Rule**: Files with the same name but different extensions are not allowed -- **Check**: Scans all tracked files for duplicates -- **Action**: Fails the import if duplicates are found +- **Rule**: After cleanup, no files with the same name but different extensions should exist +- **Check**: Scans filesystem (after removals) for any remaining duplicates +- **Action**: Fails the import if duplicates are found after cleanup +- **Coverage**: Checks `.ts`, `.tsx`, `.js`, `.jsx` files (excluding node_modules, dist, build, .git) ## Usage diff --git a/import-copilot-pr.sh b/import-copilot-pr.sh index 2821cc8..8deaf09 100755 --- a/import-copilot-pr.sh +++ b/import-copilot-pr.sh @@ -58,13 +58,34 @@ echo "" echo "🏛️ Enforcing company governance policies..." echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -# Policy 1: Single Stripe webhook route (remove .js, keep only .ts) -echo "📋 Policy 1: Enforcing single Stripe webhook route (TypeScript only)" -if [ -f api/stripe/webhook/route.js ]; then - echo " → Removing api/stripe/webhook/route.js" - rm -f api/stripe/webhook/route.js +# Policy 1: Remove all .js/.jsx files that have .ts/.tsx equivalents (TypeScript-only) +echo "📋 Policy 1: Enforcing TypeScript-only policy - removing duplicate JS/JSX files" +removed_count=0 + +# Find and remove .js files that have .ts equivalents +while IFS= read -r jsfile; do + tsfile="${jsfile%.js}.ts" + if [ -f "$tsfile" ]; then + echo " → Removing $jsfile (TypeScript equivalent exists)" + rm -f "$jsfile" + ((removed_count++)) + fi +done < <(git ls-files "*.js") + +# Find and remove .jsx files that have .tsx equivalents +while IFS= read -r jsxfile; do + tsxfile="${jsxfile%.jsx}.tsx" + if [ -f "$tsxfile" ]; then + echo " → Removing $jsxfile (TypeScript equivalent exists)" + rm -f "$jsxfile" + ((removed_count++)) + fi +done < <(git ls-files "*.jsx") + +if [ $removed_count -eq 0 ]; then + echo " ✓ No duplicate JavaScript files found" else - echo " ✓ No JavaScript webhook route found" + echo " ✓ Removed $removed_count duplicate JavaScript file(s)" fi # Policy 2: TypeScript-only (set allowJs: false in tsconfig.json) @@ -91,7 +112,13 @@ echo "🔍 Checking for duplicate routes..." echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # Find files with same name but different extensions (potential duplicates) -git ls-files | sed "s/\.[^.]*$//" | sort | uniq -d | tee /tmp/dupes.txt +# Use find instead of git ls-files to check actual filesystem after removals +find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) \ + ! -path "*/node_modules/*" \ + ! -path "*/.git/*" \ + ! -path "*/dist/*" \ + ! -path "*/build/*" \ + | sed "s/\.[^.]*$//" | sort | uniq -d | tee /tmp/dupes.txt if [ -s /tmp/dupes.txt ]; then echo "❌ Duplicate routes detected:" From de3f083fc3a548901fd481ee05cc1fbb9dba9170 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:22:17 +0000 Subject: [PATCH 016/133] test: add comprehensive test suite for import script Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- test-import-script.sh | 208 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100755 test-import-script.sh diff --git a/test-import-script.sh b/test-import-script.sh new file mode 100755 index 0000000..8427673 --- /dev/null +++ b/test-import-script.sh @@ -0,0 +1,208 @@ +#!/usr/bin/env bash +# Test script for import-copilot-pr.sh governance enforcement +# This tests the governance components without requiring an actual external PR + +set -euo pipefail + +echo "🧪 Testing import-copilot-pr.sh governance enforcement" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +PASS=0 +FAIL=0 + +# Helper functions +pass() { + echo " ✅ PASS: $1" + PASS=$((PASS + 1)) +} + +fail() { + echo " ❌ FAIL: $1" + FAIL=$((FAIL + 1)) +} + +section() { + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "📋 $1" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +} + +# Test 1: Script exists and is executable +section "Test 1: Script Setup" +if [ -f "import-copilot-pr.sh" ]; then + pass "Script file exists" +else + fail "Script file not found" +fi + +if [ -x "import-copilot-pr.sh" ]; then + pass "Script is executable" +else + fail "Script is not executable" +fi + +# Test 2: Script syntax validation +section "Test 2: Script Syntax" +if bash -n import-copilot-pr.sh 2>/dev/null; then + pass "Script syntax is valid" +else + fail "Script has syntax errors" +fi + +# Test 3: Help message +section "Test 3: Help Message" +set +e +set +o pipefail +output=$(./import-copilot-pr.sh 2>&1) +set -e +set -o pipefail + +if echo "$output" | grep -q "Usage:"; then + pass "Help message displays correctly" +else + fail "Help message not found" +fi + +# Test 4: Duplicate JS/JSX detection +section "Test 4: Duplicate File Detection" +# Current state should have duplicates +if [ -f "api/stripe/webhook/route.js" ] && [ -f "api/stripe/webhook/route.ts" ]; then + pass "Found expected duplicate: api/stripe/webhook/route.*" +else + fail "Expected duplicate not found: api/stripe/webhook/route.*" +fi + +if [ -f "core/modules/deploy-center/YoniDeployControlCenter.jsx" ] && [ -f "core/modules/deploy-center/YoniDeployControlCenter.tsx" ]; then + pass "Found expected duplicate: YoniDeployControlCenter.*" +else + fail "Expected duplicate not found: YoniDeployControlCenter.*" +fi + +# Test 5: Duplicate check logic +section "Test 5: Duplicate Check Logic" +# Test that find command detects duplicates before removal +dupe_count=$(find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) \ + ! -path "*/node_modules/*" \ + ! -path "*/.git/*" \ + ! -path "*/dist/*" \ + ! -path "*/build/*" \ + | sed "s/\.[^.]*$//" | sort | uniq -d | wc -l) + +if [ "$dupe_count" -gt 0 ]; then + pass "Duplicate detection finds duplicates ($dupe_count found)" +else + fail "Duplicate detection should find duplicates but found none" +fi + +# Test 6: Simulated removal and re-check +section "Test 6: Removal Simulation" +# Create temporary backup +cp api/stripe/webhook/route.js /tmp/route.js.bak 2>/dev/null || true +cp core/modules/deploy-center/YoniDeployControlCenter.jsx /tmp/YoniDeployControlCenter.jsx.bak 2>/dev/null || true + +# Remove duplicates +rm -f api/stripe/webhook/route.js +rm -f core/modules/deploy-center/YoniDeployControlCenter.jsx + +# Check for duplicates after removal +dupe_count_after=$(find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) \ + ! -path "*/node_modules/*" \ + ! -path "*/.git/*" \ + ! -path "*/dist/*" \ + ! -path "*/build/*" \ + | sed "s/\.[^.]*$//" | sort | uniq -d | wc -l) + +if [ "$dupe_count_after" -eq 0 ]; then + pass "No duplicates found after removal" +else + fail "Duplicates still exist after removal ($dupe_count_after found)" +fi + +# Restore files +git restore api/stripe/webhook/route.js core/modules/deploy-center/YoniDeployControlCenter.jsx 2>/dev/null || { + # If git restore fails, use backup + mv /tmp/route.js.bak api/stripe/webhook/route.js 2>/dev/null || true + mv /tmp/YoniDeployControlCenter.jsx.bak core/modules/deploy-center/YoniDeployControlCenter.jsx 2>/dev/null || true +} + +pass "Files restored after test" + +# Test 7: TypeScript file preservation +section "Test 7: TypeScript File Preservation" +if [ -f "api/stripe/webhook/route.ts" ]; then + pass "TypeScript webhook file exists" +else + fail "TypeScript webhook file missing" +fi + +if [ -f "core/modules/deploy-center/YoniDeployControlCenter.tsx" ]; then + pass "TypeScript component file exists" +else + fail "TypeScript component file missing" +fi + +# Test 8: Documentation +section "Test 8: Documentation" +if [ -f "COPILOT_PR_IMPORT.md" ]; then + pass "Documentation file exists" +else + fail "Documentation file missing" +fi + +if grep -q "import-copilot-pr.sh" README.md; then + pass "README.md references the import script" +else + fail "README.md doesn't reference the import script" +fi + +# Test 9: Script components +section "Test 9: Script Components" +if grep -q 'rm -f.*js' import-copilot-pr.sh; then + pass "Script contains JS/JSX removal logic" +else + fail "Script missing JS/JSX removal logic" +fi + +if grep -q "allowJs.*false" import-copilot-pr.sh; then + pass "Script contains TypeScript-only config" +else + fail "Script missing TypeScript-only config" +fi + +if grep -q "git am" import-copilot-pr.sh; then + pass "Script uses git am for patch application" +else + fail "Script doesn't use git am" +fi + +if grep -q "npm run build" import-copilot-pr.sh; then + pass "Script includes build step" +else + fail "Script missing build step" +fi + +# Summary +echo "" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "📊 Test Summary" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "" +echo " ✅ Passed: $PASS" +echo " ❌ Failed: $FAIL" +echo " 📈 Total: $((PASS + FAIL))" +echo "" + +if [ $FAIL -eq 0 ]; then + echo "🎉 All tests passed!" + echo "" + exit 0 +else + echo "⚠️ Some tests failed. Please review the output above." + echo "" + exit 1 +fi From 271be4dfe8e6bfd3c95fe2c0db9fb25648e59308 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:46:03 +0000 Subject: [PATCH 017/133] Initial plan From 4d0ab27653a06bdcd80276e353fdb14e438ff41e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:46:29 +0000 Subject: [PATCH 018/133] Initial plan From 03468ca0e2982da8cbf5c554eb04475bdc58e573 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:46:50 +0000 Subject: [PATCH 019/133] Initial plan From fe6837f8559355cdd71c83b83a0630ff11d18aad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:50:08 +0000 Subject: [PATCH 020/133] Create GitHub App webhook endpoint with signature validation Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- api/github-app/webhook/route.js | 44 +++++++++++++ api/github-app/webhook/route.ts | 113 ++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 api/github-app/webhook/route.js create mode 100644 api/github-app/webhook/route.ts diff --git a/api/github-app/webhook/route.js b/api/github-app/webhook/route.js new file mode 100644 index 0000000..22d913b --- /dev/null +++ b/api/github-app/webhook/route.js @@ -0,0 +1,44 @@ +// /api/github-app/webhook.js +import { createHmac } from "crypto"; + +export default function handler(req, res) { + if (req.method !== "POST") { + return res.status(405).end("Method not allowed"); + } + + const signature = req.headers["x-hub-signature-256"]; + const event = req.headers["x-github-event"]; + + if (!signature) { + return res.status(400).json({ error: "Missing x-hub-signature-256 header" }); + } + + const webhookSecret = process.env.GITHUB_WEBHOOK_SECRET; + + if (!webhookSecret) { + console.error("GITHUB_WEBHOOK_SECRET is not configured"); + return res.status(500).json({ error: "Webhook secret not configured" }); + } + + // Get raw body (Vercel provides this as req.body for serverless functions) + const rawBody = JSON.stringify(req.body); + + // Verify signature + const expectedSignature = signature.substring(7); // Remove 'sha256=' prefix + const hmac = createHmac("sha256", webhookSecret); + hmac.update(rawBody); + const calculatedSignature = hmac.digest("hex"); + + if (calculatedSignature !== expectedSignature) { + return res.status(401).json({ error: "Invalid signature" }); + } + + console.log(`GitHub webhook event: ${event}`); + + if (event === "ping") { + console.log("✅ GitHub App ping received"); + return res.status(200).json({ message: "pong", zen: req.body.zen }); + } + + res.status(200).json({ received: true }); +} diff --git a/api/github-app/webhook/route.ts b/api/github-app/webhook/route.ts new file mode 100644 index 0000000..2758962 --- /dev/null +++ b/api/github-app/webhook/route.ts @@ -0,0 +1,113 @@ +import { createHmac } from "crypto"; + +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; + +/** + * Verifies the GitHub webhook signature using HMAC SHA256 + */ +function verifyGitHubSignature( + payload: string, + signature: string, + secret: string +): boolean { + if (!signature || !signature.startsWith("sha256=")) { + return false; + } + + const expectedSignature = signature.substring(7); // Remove 'sha256=' prefix + const hmac = createHmac("sha256", secret); + hmac.update(payload); + const calculatedSignature = hmac.digest("hex"); + + // Use timing-safe comparison to prevent timing attacks + return ( + calculatedSignature.length === expectedSignature.length && + createHmac("sha256", secret) + .update(calculatedSignature) + .digest("hex") === + createHmac("sha256", secret) + .update(expectedSignature) + .digest("hex") + ); +} + +export async function POST(req: Request) { + const signature = req.headers.get("x-hub-signature-256"); + const event = req.headers.get("x-github-event"); + + if (!signature) { + return new Response("Missing x-hub-signature-256 header", { status: 400 }); + } + + const rawBody = await req.text(); + const webhookSecret = process.env.GITHUB_WEBHOOK_SECRET; + + if (!webhookSecret) { + console.error("GITHUB_WEBHOOK_SECRET is not configured"); + return new Response("Webhook secret not configured", { status: 500 }); + } + + // Verify the webhook signature + const isValid = verifyGitHubSignature(rawBody, signature, webhookSecret); + + if (!isValid) { + return new Response("Invalid signature", { status: 401 }); + } + + // Parse the payload + let payload: any; + try { + payload = JSON.parse(rawBody); + } catch (err) { + return new Response("Invalid JSON payload", { status: 400 }); + } + + // Handle different event types + console.log(`GitHub webhook event: ${event}`); + + switch (event) { + case "ping": + console.log("✅ GitHub App ping received"); + return new Response( + JSON.stringify({ message: "pong", zen: payload.zen }), + { + status: 200, + headers: { "Content-Type": "application/json" }, + } + ); + + case "push": + console.log("📦 Push event:", payload.repository?.full_name); + break; + + case "pull_request": + console.log( + "🔀 Pull Request:", + payload.action, + payload.pull_request?.number + ); + break; + + case "issues": + console.log("📝 Issue:", payload.action, payload.issue?.number); + break; + + default: + console.log("Unhandled GitHub event:", event); + } + + return new Response(JSON.stringify({ received: true }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); +} + +export async function GET() { + return new Response( + "GitHub App webhook endpoint active (use POST with valid signature).", + { + status: 200, + } + ); +} From a611ca09627b5eca60e821dfc1ddc28296e96f2d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:50:38 +0000 Subject: [PATCH 021/133] Apply PR #11 changes: Add signature validation and error handling to webhook handlers Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- api/stripe/webhook/route.ts | 21 +++++++++++++++++---- app/api/github-app/webhook/route.ts | 11 +++++++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/api/stripe/webhook/route.ts b/api/stripe/webhook/route.ts index 148eb26..a740773 100644 --- a/api/stripe/webhook/route.ts +++ b/api/stripe/webhook/route.ts @@ -3,13 +3,26 @@ import Stripe from "stripe"; export const runtime = "nodejs"; export const dynamic = "force-dynamic"; -const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string, { - apiVersion: "2024-06-20", -}); +const stripeSecretKey = process.env.STRIPE_SECRET_KEY; +const stripe = stripeSecretKey + ? new Stripe(stripeSecretKey, { + apiVersion: "2024-06-20", + }) + : null; export async function POST(req: Request) { + if (!stripe) { + return new Response("Stripe not configured", { status: 500 }); + } + const sig = req.headers.get("stripe-signature"); if (!sig) return new Response("Missing signature", { status: 400 }); + + const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; + if (!webhookSecret) { + return new Response("Webhook secret not configured", { status: 500 }); + } + const rawBody = await req.text(); let event: Stripe.Event; @@ -17,7 +30,7 @@ export async function POST(req: Request) { event = stripe.webhooks.constructEvent( rawBody, sig, - process.env.STRIPE_WEBHOOK_SECRET as string + webhookSecret ); } catch (err: any) { return new Response(`Webhook Error: ${err.message}`, { status: 400 }); diff --git a/app/api/github-app/webhook/route.ts b/app/api/github-app/webhook/route.ts index 7b2abcc..710acfa 100644 --- a/app/api/github-app/webhook/route.ts +++ b/app/api/github-app/webhook/route.ts @@ -2,7 +2,8 @@ import { NextRequest, NextResponse } from 'next/server'; import crypto from 'crypto'; function verify(sigHeader: string | null, body: string) { - const secret = process.env.GITHUB_WEBHOOK_SECRET!; + const secret = process.env.GITHUB_WEBHOOK_SECRET; + if (!secret) return false; const hmac = crypto.createHmac('sha256', secret).update(body).digest('hex'); return sigHeader === `sha256=${hmac}`; } @@ -13,7 +14,13 @@ export async function POST(req: NextRequest) { if (!verify(sig, raw)) return new NextResponse('invalid signature', { status: 401 }); const evt = req.headers.get('x-github-event'); - const payload = JSON.parse(raw); + + let payload; + try { + payload = JSON.parse(raw); + } catch { + return new NextResponse('invalid JSON', { status: 400 }); + } // Simple demo: auto-comment on new PRs if (evt === 'pull_request' && payload.action === 'opened') { From 6b1e6eb3085fbdd34e6c7d30a5c4fef722174ad2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:52:55 +0000 Subject: [PATCH 022/133] Add production smoke tests workflow for Vercel deployments Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- .github/workflows/prod_smoke_tests.yml | 113 +++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 .github/workflows/prod_smoke_tests.yml diff --git a/.github/workflows/prod_smoke_tests.yml b/.github/workflows/prod_smoke_tests.yml new file mode 100644 index 0000000..b419857 --- /dev/null +++ b/.github/workflows/prod_smoke_tests.yml @@ -0,0 +1,113 @@ +name: 🔍 Production Smoke Tests + +on: + workflow_dispatch: + inputs: + prod_domain: + description: 'Production domain (e.g., yoni-app.vercel.app)' + required: true + type: string + schedule: + # Run daily at 6:00 UTC + - cron: "0 6 * * *" + +jobs: + smoke-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Set Production Domain + id: set-domain + run: | + if [ -n "${{ github.event.inputs.prod_domain }}" ]; then + echo "PROD_DOMAIN=${{ github.event.inputs.prod_domain }}" >> $GITHUB_ENV + else + # Default domain if not provided (should be set in repository secrets) + echo "PROD_DOMAIN=${{ secrets.PROD_DOMAIN }}" >> $GITHUB_ENV + fi + + - name: Verify Production Domain + run: | + if [ -z "$PROD_DOMAIN" ]; then + echo "❌ Production domain not set. Please provide it via input or set PROD_DOMAIN secret." + exit 1 + fi + echo "✅ Testing production domain: $PROD_DOMAIN" + + - name: Install Vercel CLI (Optional) + run: npm install -g vercel + continue-on-error: true + + - name: List Vercel Deployments (Optional) + run: | + if command -v vercel &> /dev/null; then + vercel ls || echo "⚠️ Vercel ls failed (token may not be configured)" + else + echo "⚠️ Vercel CLI not available" + fi + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + continue-on-error: true + + - name: Smoke Test - Stripe Webhook Endpoint + run: | + echo "🧪 Testing Stripe webhook endpoint..." + RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ + -H "stripe-signature: invalid" \ + -d '{}' \ + "https://$PROD_DOMAIN/api/stripe/webhook") + + HTTP_CODE=$(echo "$RESPONSE" | tail -1) + BODY=$(echo "$RESPONSE" | head -n -1) + + echo "HTTP Status: $HTTP_CODE" + echo "Response: $BODY" + + # Expect 400 Bad Request for invalid signature + if [ "$HTTP_CODE" = "400" ]; then + echo "✅ Stripe webhook endpoint is working correctly (rejected invalid signature)" + else + echo "⚠️ Unexpected status code from Stripe webhook: $HTTP_CODE" + echo "Response body: $BODY" + fi + + - name: Smoke Test - GitHub App Webhook Endpoint + run: | + echo "🧪 Testing GitHub App webhook endpoint..." + + # Generate HMAC signature for the payload + PAYLOAD='{}' + SIG=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "${{ secrets.GITHUB_WEBHOOK_SECRET }}" -r | cut -d' ' -f1) + + RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \ + -H "x-github-event: ping" \ + -H "x-hub-signature-256: sha256=$SIG" \ + -H "content-type: application/json" \ + -d "$PAYLOAD" \ + "https://$PROD_DOMAIN/api/github-app/webhook") + + HTTP_CODE=$(echo "$RESPONSE" | tail -1) + BODY=$(echo "$RESPONSE" | head -n -1) + + echo "HTTP Status: $HTTP_CODE" + echo "Response: $BODY" + + # Expect 200 OK for valid signature + if [ "$HTTP_CODE" = "200" ]; then + echo "✅ GitHub App webhook endpoint is working correctly" + else + echo "⚠️ Unexpected status code from GitHub webhook: $HTTP_CODE" + echo "Response body: $BODY" + fi + + - name: Test Summary + if: always() + run: | + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "🏁 Production Smoke Tests Complete" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Tested domain: $PROD_DOMAIN" + echo "Timestamp: $(date -u +"%Y-%m-%d %H:%M:%S UTC")" From d54a555eb0e5298a201f1bd8456c111d3e86fbe3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 19:54:46 +0000 Subject: [PATCH 023/133] Changes before error encountered Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- smoke-tests-prod.sh | 50 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 smoke-tests-prod.sh diff --git a/smoke-tests-prod.sh b/smoke-tests-prod.sh new file mode 100644 index 0000000..ba56728 --- /dev/null +++ b/smoke-tests-prod.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Smoke Tests for Production Deployment +# This script demonstrates the smoke tests mentioned in the problem statement + +echo "===================================" +echo "Production Deployment Smoke Tests" +echo "===================================" +echo "" + +# Set production domain (replace with actual production domain) +PROD_DOMAIN="${PROD_DOMAIN:-yoni-app.vercel.app}" + +echo "Testing against: https://${PROD_DOMAIN}" +echo "" + +# Test 1: Stripe webhook with invalid signature +echo "1. Testing Stripe webhook with invalid signature..." +echo " curl -s https://${PROD_DOMAIN}/api/stripe/webhook -X POST -H 'stripe-signature: invalid' -d '{}'" +echo "" +STRIPE_RESPONSE=$(curl -s https://${PROD_DOMAIN}/api/stripe/webhook -X POST -H "stripe-signature: invalid" -d '{}') +echo " Response: $STRIPE_RESPONSE" +echo "" + +# Test 2: GitHub webhook with valid signature +echo "2. Testing GitHub webhook with valid signature..." +if [ -z "$GITHUB_WEBHOOK_SECRET" ]; then + echo " ⚠️ GITHUB_WEBHOOK_SECRET not set. Using test value." + GITHUB_WEBHOOK_SECRET="test-secret-for-demo" +fi + +PAYLOAD='{}' +SIG=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$GITHUB_WEBHOOK_SECRET" -r | cut -d' ' -f1) + +echo " curl -s https://${PROD_DOMAIN}/api/github-app/webhook -H 'x-github-event: ping' -H 'x-hub-signature-256: sha256=\$SIG' -H 'content-type: application/json' -d '{}'" +echo "" +GITHUB_RESPONSE=$(curl -s https://${PROD_DOMAIN}/api/github-app/webhook -H "x-github-event: ping" -H "x-hub-signature-256: sha256=$SIG" -H "content-type: application/json" -d "$PAYLOAD") +echo " Response: $GITHUB_RESPONSE" +echo "" + +# Test 3: GitHub webhook with invalid signature +echo "3. Testing GitHub webhook with invalid signature..." +echo " curl -s https://${PROD_DOMAIN}/api/github-app/webhook -X POST -H 'x-hub-signature-256: sha256=invalid' -d '{}'" +echo "" +GITHUB_INVALID_RESPONSE=$(curl -s https://${PROD_DOMAIN}/api/github-app/webhook -X POST -H "x-github-event: ping" -H "x-hub-signature-256: sha256=invalid" -H "content-type: application/json" -d '{}') +echo " Response: $GITHUB_INVALID_RESPONSE" +echo "" + +echo "===================================" +echo "Smoke tests completed!" +echo "===================================" From 11f1e9bd6773eb8d5b8ebc3fbeb7c047e3be45cb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 20:02:25 +0000 Subject: [PATCH 024/133] Add explicit permissions to prod smoke tests workflow Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- .github/workflows/prod_smoke_tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/prod_smoke_tests.yml b/.github/workflows/prod_smoke_tests.yml index b419857..cbaafcf 100644 --- a/.github/workflows/prod_smoke_tests.yml +++ b/.github/workflows/prod_smoke_tests.yml @@ -11,6 +11,9 @@ on: # Run daily at 6:00 UTC - cron: "0 6 * * *" +permissions: + contents: read + jobs: smoke-tests: runs-on: ubuntu-latest From 576ab6c266fe742974ac76d6a7bc33cebaa561a6 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 23:30:41 +0100 Subject: [PATCH 025/133] Remove duplicate route files causing Vercel build conflicts (#28) --- api/stripe/webhook/route.js | 8 -------- core/modules/deploy-center/YoniDeployControlCenter.jsx | 2 -- 2 files changed, 10 deletions(-) delete mode 100644 api/stripe/webhook/route.js delete mode 100644 core/modules/deploy-center/YoniDeployControlCenter.jsx diff --git a/api/stripe/webhook/route.js b/api/stripe/webhook/route.js deleted file mode 100644 index 456cfa8..0000000 --- a/api/stripe/webhook/route.js +++ /dev/null @@ -1,8 +0,0 @@ -// /api/stripe/webhook.js -export default function handler(req, res) { - if (req.method === 'POST') { - res.status(200).json({ received: true }); - } else { - res.status(405).end('Method not allowed'); - } -} diff --git a/core/modules/deploy-center/YoniDeployControlCenter.jsx b/core/modules/deploy-center/YoniDeployControlCenter.jsx deleted file mode 100644 index 6280c98..0000000 --- a/core/modules/deploy-center/YoniDeployControlCenter.jsx +++ /dev/null @@ -1,2 +0,0 @@ -import YoniDeployControlCenter from "@/core/modules/deploy-center/YoniDeployControlCenter"; - From dc7839299e754b6360ae325007ad7da3c2ddaf19 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 23:33:09 +0100 Subject: [PATCH 026/133] [WIP] Add mental health features to YONI app (#35) --- README.md | 117 ++++++++++++++++++++++++++---------------------------- 1 file changed, 56 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index cedaaac..ccf7edb 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,84 @@ -# YONI ↔︎ Mutterschiff (GPT‑5 Pro) – Remote Bridge +# ✨ YONI – Überhochglitzer App -Diese minimale Referenzimplementierung stellt eine sichere Brücke zwischen einem lokalen Agenten **YONI** und dem **Mutterschiff** (OpenAI GPT‑5 Pro) her. +> 🟣 Ein sicherer, liebevoller Raum für mentale Gesundheit – digital, fachärztlich begleitet und technisch perfekt. -**Zwei Betriebsmodi** -1) **HTTP/JSON + Function Calling (stabil & simpel):** Node.js-Server definiert Tools/Funktionen; GPT‑5 ruft diese bei Bedarf auf. Der Server delegiert die Ausführung an den lokalen YONI‑Agent (Python). -2) **(Optional) Realtime/Voice:** Kann später per WebRTC/WebSocket ergänzt werden. Dieser Starter nutzt zunächst Modus 1. +![YONI Banner](https://user-images.githubusercontent.com/placeholder/banner.png) + +[![License: MIT](https://img.shields.io/badge/License-MIT-purple.svg)](LICENSE) +[![Built with Next.js](https://img.shields.io/badge/Built%20with-Next.js-black?logo=next.js)](https://nextjs.org/) +[![TailwindCSS](https://img.shields.io/badge/Styled%20with-TailwindCSS-38B2AC?logo=tailwindcss)](https://tailwindcss.com/) +[![Vercel](https://img.shields.io/badge/Deployed%20on-Vercel-black?logo=vercel)](https://vercel.com/) +[![Accessibility](https://img.shields.io/badge/A11y-AA%2B-2ECC71)](#) --- -## Architektur (Kurzform) +## 🌌 Mission -``` -┌─────────┐ HTTPS ┌──────────────────────────────┐ HTTP (LAN/VPN) ┌───────────────┐ -│ Client │ ───────────► │ Mutterschiff-Server (Node) │ ─────────────────► │ YONI-Agent │ -│ (UI/CLI)│ │ • OpenAI API (GPT-5 Pro) │ │ (Python Flask)│ -└─────────┘ (REST) │ • Tool/Function-Bridge │ (Tool Calls) └───────────────┘ - └──────────────────────────────┘ -``` +**YONI** ist eine Online-Selbsthilfegruppe für psychisch belastete Menschen +mit **fachärztlicher Begleitung, digitaler Sicherheit und liebevoller Gestaltung**. -- **OpenAI API Key** bleibt **nur** auf dem Server. -- Der YONI‑Agent exponiert nur whiteliste, harmlose Endpunkte. -- TLS/Firewall/VPN empfohlen. +Die App vereint: +- 🤝 **Gemeinschaft** – Chat-Räume & Themenkreise mit Peer-Mentor:innen +- 🧠 **Fachliche Supervision** – Ärzt:innen, Therapeut:innen, geschützte Q&A +- 🪞 **Selbstwirksamkeit** – Tools für Reflexion, Stimmung & Achtsamkeit +- 🌈 **Überhochglitzer-Design** – kosmisch, heilend, barrierefrei, technisch präzise --- -## Quickstart - -### 1) Mutterschiff-Server (Node.js) +## ⚙️ Tech Stack -```bash -cd server -cp .env.example .env # OPENAI_API_KEY setzen, YONI_AGENT_URL anpassen (z.B. http://127.0.0.1:5055) -npm install -node mothership_server.js -``` +| Layer | Technologie | Beschreibung | +|-------|--------------|---------------| +| Frontend | **Next.js 14** | App Router, SSR, optimierte Performance | +| Styling | **TailwindCSS + Überhochglitzer Theme** | Tokens, Animation, Starfield | +| Backend | **API Routes (Edge Functions)** | Stripe, GitHub Webhooks, Chat | +| Deployment | **Vercel** | Preview + Production CI/CD | +| Monitoring | **Lighthouse CI, axe-core** | A11y, Performance, QA Checks | -### 2) YONI-Agent (Python) - -```bash -cd client_yoni -python -m venv .venv && source .venv/bin/activate -pip install -r requirements.txt -python yoni_agent.py -``` +--- -### 3) Smoke‑Test +## 🧩 Design Tokens & Theme -```bash -curl -s http://localhost:8787/chat -H "Content-Type: application/json" -d '{ "user":"Statusbericht von YONI." }' | jq -``` +| Token | Wert | Bedeutung | +|-------|------|-----------| +| `brand.amethyst` | `#9966CC` | Hoffnung, Spiritualität, Transformation | +| `text.starwhite` | `#F5F5F5` | Klarheit und Licht im Dunkeln | +| `ok.emerald` | `#2ECC71` | Heilung und Wachstum | +| `hl.gold` | `#FFD700` | Wärme, Wert und Verbundenheit | --- -## Security Baseline +## 💜 Contributing -- **API‑Key niemals** an Clients weiterreichen. -- YONI‑Agent nur im **LAN/VPN** binden (oder mutual‑TLS). -- **Werkzeug‑Whitelist** strikt halten. Kein Shell‑Zugriff, kein Dateisystem‑Schreiben in diesem Starter. -- **Rate‑Limits** & Logging aktivieren. -- Für Voice/WebRTC später **ephemere Tokens** vom Server minten. +1. Fork das Repo +2. Erstelle einen Feature-Branch: `git checkout -b feature/dein-thema` +3. Führe `npm run lint && npm run build` aus +4. Erstelle einen Pull-Request ✨ + +### CI Checks +- ✅ ESLint + TypeScript +- ✅ A11y (axe) +- ✅ Lighthouse ≥ 95 +- ✅ Duplicate Route Guard --- -## Copilot PR Import +## 🧘‍♀️ Lizenz & Verantwortung -Für das Importieren externer Copilot PRs mit automatischer Governance-Durchsetzung: +Dieses Projekt ist **Open Source (MIT)**. +Es ersetzt **keine Therapie**. +YONI versteht sich als digitaler Begleiter auf dem Weg zur Heilung, +nicht als medizinisches Produkt. -```bash -./import-copilot-pr.sh -``` +--- -Das Script erzwingt automatisch: -- **Einzelne Stripe Webhook Route** (nur TypeScript, keine .js Dateien) -- **TypeScript-Only Policy** (`allowJs: false` in tsconfig.json) -- **Keine Duplikate** (Dateien mit gleichem Namen aber verschiedenen Erweiterungen) -- **Build-Validierung** vor dem Push +## 🔮 Kontakt & Links -Details siehe [COPILOT_PR_IMPORT.md](./COPILOT_PR_IMPORT.md) +**Projektleitung:** [@pappensex](https://github.com/pappensex) +**Website:** [yoni.pihoch2.me](https://yoni.pihoch2.me) +**Demo:** [yoni.vercel.app](https://yoni.vercel.app) +**Mail:** [yoni@pihoch2.me](mailto:yoni@pihoch2.me) --- -## Nächste Schritte - -- Realtime‑Pfad (WebRTC/WebSocket) für Voice/Audio aktivieren. -- Tooling erweitern (z.B. Sensoren, Geräte, Kalender), aber **Idempotenz** & **Timeouts** beachten. -- Beobachtbarkeit: Metriken/Tracing für Tool‑Latenzen. - -Viel Spaß. 💫 +> _„Im Dunkel des Alls glitzert jeder Mensch als eigene Galaxie."_ From 4bf373a87942204f3f5e30070a59afaa5c7a2cde Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 22:38:11 +0000 Subject: [PATCH 027/133] Initial plan From 85ff00e48e4c08e2782d139f79d41be8a8df8f2f Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 23:40:11 +0100 Subject: [PATCH 028/133] Initial plan (#42) From b461988f147a5e5a04b819f2f9ada4b4c1f9d59c Mon Sep 17 00:00:00 2001 From: YONI <233804448+pappensex@users.noreply.github.com> Date: Tue, 11 Nov 2025 23:41:44 +0100 Subject: [PATCH 029/133] Create documentation for YONI App setup and philosophy Added comprehensive instructions for the YONI App, detailing vision, technical overview, and setup guide. --- .github/instructions/*.instructions.md | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/instructions/*.instructions.md diff --git a/.github/instructions/*.instructions.md b/.github/instructions/*.instructions.md new file mode 100644 index 0000000..072c1d4 --- /dev/null +++ b/.github/instructions/*.instructions.md @@ -0,0 +1,44 @@ +# 📜 INSTRUKTION – YONI App (Überhochglitzer Edition) + +> Version 1.0 · Stand: 2025-11-11 +> Eigentum: piHOCH2 / YONI Projektgruppe +> Zweck: Vollständige Dokumentation für Aufbau, Betrieb, Pflege und ethische Leitung der YONI-App. + +--- + +## 🧩 1. Vision & Philosophie + +**YONI** ist ein digitaler, geschützter Raum für Menschen mit psychischer Belastung. +Ziel ist **Heilung durch Verbindung**, gestützt von Fachärzt:innen und einer liebevollen Community. + +Leitprinzipien: +- 🟣 *Sicherheit* – technischer und psychologischer Schutz. +- 💜 *Würde* – jedes Individuum ist einzigartig und unantastbar. +- 🌌 *Transzendenz* – Ästhetik als Brücke zwischen Innenwelt und Kosmos. +- 🧠 *Kompetenz* – medizinisch fundierte Inhalte, geprüft von Fachpersonal. +- 🪶 *Leichtigkeit* – Minimalismus, Barrierefreiheit, klare Sprache. + +--- + +## ⚙️ 2. Technische Übersicht + +| Ebene | Technologie | Beschreibung | +|-------|--------------|--------------| +| **Frontend** | Next.js 14 (App Router) | SSR, Routing, Edge-API | +| **Styling** | TailwindCSS + Überhochglitzer-Theme | Tokens, Starfield, Animation | +| **Backend** | API Routes (Stripe, GitHub, Safety-Bot) | Event-Handling, Webhooks | +| **Deployment** | Vercel (Preview + Prod) | CI/CD | +| **Security** | CSP, HTTPS, RBAC, Secrets via Vercel | DSGVO-konform | +| **Monitoring** | Lighthouse CI, axe-core, Sentry | Qualität & A11y | +| **Data Layer** | Edge Storage / Supabase (optional)** | Prototyp → persistente Gruppen | + +--- + +## 🚀 3. Setup-Anleitung + +### Lokale Umgebung +```bash +git clone https://github.com/pappensex/YONI-app.git +cd YONI-app +npm install +npm run dev From c444c9b2da5988d7cdee0a08e18f0e088d29c4aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 22:44:08 +0000 Subject: [PATCH 030/133] =?UTF-8?q?Add=20=C3=9Cberhochglitzer=20animation?= =?UTF-8?q?=20tokens=20and=20integrate=20into=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- index.html | 18 ++-- theme-tokens.css | 229 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+), 9 deletions(-) create mode 100644 theme-tokens.css diff --git a/index.html b/index.html index 64645a8..a581cb9 100644 --- a/index.html +++ b/index.html @@ -8,8 +8,8 @@ + - +
-
-

YONI • pi² Control

+
+

YONI • pi² Control

Vercel‑Minimal • Creator‑KI • Auto‑Translate • Transzendenz
- - + +

-
-

Transzendenz

+
+

Transzendenz

- +
diff --git a/theme-tokens.css b/theme-tokens.css new file mode 100644 index 0000000..f12625a --- /dev/null +++ b/theme-tokens.css @@ -0,0 +1,229 @@ +/* ✨ YONI Überhochglitzer Theme Tokens ✨ */ +/* Animation tokens for cosmic, healing, glittering effects */ + +:root { + /* Existing color tokens (from README) */ + --brand-amethyst: #9966CC; + --text-starwhite: #F5F5F5; + --ok-emerald: #2ECC71; + --hl-gold: #FFD700; + + /* Background & panel tokens */ + --bg: #0b0b10; + --panel: rgba(255, 255, 255, .06); + --text: #f3f3f7; + --brand: #7c3aed; + --radius: 14px; + + /* ======================================== + ÜBERHOCHGLITZER ANIMATION TOKENS + ======================================== */ + + /* Animation durations */ + --anim-sparkle-duration: 2.5s; + --anim-shimmer-duration: 3s; + --anim-glow-duration: 2s; + --anim-pulse-duration: 1.5s; + --anim-float-duration: 4s; + --anim-twinkle-duration: 1.8s; + + /* Animation timing functions */ + --anim-ease-cosmic: cubic-bezier(0.4, 0, 0.2, 1); + --anim-ease-gentle: cubic-bezier(0.25, 0.46, 0.45, 0.94); + --anim-ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55); + + /* Glitter colors */ + --glitter-amethyst: rgba(153, 102, 204, 0.8); + --glitter-gold: rgba(255, 215, 0, 0.9); + --glitter-emerald: rgba(46, 204, 113, 0.7); + --glitter-starwhite: rgba(245, 245, 245, 0.95); + + /* Glow effects */ + --glow-amethyst: 0 0 20px rgba(153, 102, 204, 0.5); + --glow-gold: 0 0 25px rgba(255, 215, 0, 0.6); + --glow-emerald: 0 0 18px rgba(46, 204, 113, 0.5); + --glow-starwhite: 0 0 30px rgba(245, 245, 245, 0.4); +} + +/* ======================================== + KEYFRAME ANIMATIONS + ======================================== */ + +/* Sparkle - creates a shimmering effect */ +@keyframes sparkle { + 0%, 100% { + opacity: 1; + transform: scale(1); + } + 25% { + opacity: 0.5; + transform: scale(0.95); + } + 50% { + opacity: 1; + transform: scale(1.05); + } + 75% { + opacity: 0.7; + transform: scale(0.98); + } +} + +/* Shimmer - creates a light sweep effect */ +@keyframes shimmer { + 0% { + background-position: -200% center; + } + 100% { + background-position: 200% center; + } +} + +/* Glow pulse - creates a breathing glow effect */ +@keyframes glow-pulse { + 0%, 100% { + box-shadow: var(--glow-amethyst); + } + 50% { + box-shadow: 0 0 35px rgba(153, 102, 204, 0.8), + 0 0 50px rgba(153, 102, 204, 0.4); + } +} + +/* Twinkle - random star-like twinkling */ +@keyframes twinkle { + 0%, 100% { + opacity: 0.3; + } + 50% { + opacity: 1; + } +} + +/* Float - gentle floating motion */ +@keyframes float { + 0%, 100% { + transform: translateY(0px); + } + 50% { + transform: translateY(-10px); + } +} + +/* Cosmic pulse - gentle scale pulse */ +@keyframes cosmic-pulse { + 0%, 100% { + transform: scale(1); + opacity: 0.9; + } + 50% { + transform: scale(1.02); + opacity: 1; + } +} + +/* Starfield rotate - slow rotation for cosmic effects */ +@keyframes starfield-rotate { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +/* Rainbow shift - color cycling for extra glitter */ +@keyframes rainbow-shift { + 0% { + filter: hue-rotate(0deg); + } + 100% { + filter: hue-rotate(360deg); + } +} + +/* ======================================== + UTILITY CLASSES FOR ANIMATIONS + ======================================== */ + +.anim-sparkle { + animation: sparkle var(--anim-sparkle-duration) var(--anim-ease-cosmic) infinite; +} + +.anim-shimmer { + background: linear-gradient( + 90deg, + transparent 0%, + rgba(255, 255, 255, 0.1) 50%, + transparent 100% + ); + background-size: 200% 100%; + animation: shimmer var(--anim-shimmer-duration) var(--anim-ease-gentle) infinite; +} + +.anim-glow { + animation: glow-pulse var(--anim-glow-duration) var(--anim-ease-gentle) infinite; +} + +.anim-twinkle { + animation: twinkle var(--anim-twinkle-duration) var(--anim-ease-cosmic) infinite; +} + +.anim-float { + animation: float var(--anim-float-duration) var(--anim-ease-gentle) infinite; +} + +.anim-cosmic-pulse { + animation: cosmic-pulse var(--anim-pulse-duration) var(--anim-ease-gentle) infinite; +} + +/* Delayed animation variants for staggered effects */ +.anim-sparkle-delayed { + animation: sparkle var(--anim-sparkle-duration) var(--anim-ease-cosmic) infinite; + animation-delay: 0.5s; +} + +.anim-twinkle-delayed { + animation: twinkle var(--anim-twinkle-duration) var(--anim-ease-cosmic) infinite; + animation-delay: 0.8s; +} + +/* Combined effects */ +.glitzer-button { + position: relative; + animation: glow-pulse var(--anim-glow-duration) var(--anim-ease-gentle) infinite; + transition: all 0.3s var(--anim-ease-cosmic); +} + +.glitzer-button:hover { + transform: scale(1.05); + box-shadow: 0 0 40px rgba(153, 102, 204, 0.9); +} + +.glitzer-card { + animation: cosmic-pulse var(--anim-pulse-duration) var(--anim-ease-gentle) infinite; +} + +/* Starfield background helper */ +.starfield-bg { + position: relative; + overflow: hidden; +} + +.starfield-bg::before { + content: ''; + position: absolute; + width: 200%; + height: 200%; + top: -50%; + left: -50%; + background: radial-gradient(2px 2px at 20% 30%, white, transparent), + radial-gradient(2px 2px at 60% 70%, white, transparent), + radial-gradient(1px 1px at 50% 50%, white, transparent), + radial-gradient(1px 1px at 80% 10%, white, transparent), + radial-gradient(2px 2px at 90% 60%, white, transparent); + background-size: 200px 200px, 250px 250px, 300px 300px, 180px 180px, 220px 220px; + animation: starfield-rotate 120s linear infinite; + opacity: 0.3; + pointer-events: none; +} From 6964999e4f2cfef69fed07d1ebe9ce33c6dd8ca3 Mon Sep 17 00:00:00 2001 From: YONI AutoBot Date: Wed, 12 Nov 2025 00:52:49 +0000 Subject: [PATCH 031/133] =?UTF-8?q?=F0=9F=95=93=20Daily=20Snapshot=202025-?= =?UTF-8?q?11-12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Reports/Deploy-Status-2025-11-12.md | 28 +++++++++++++++++++ Transzendenz/Reports/snapshot-log.txt | 1 + 2 files changed, 29 insertions(+) create mode 100644 Transzendenz/Reports/Deploy-Status-2025-11-12.md diff --git a/Transzendenz/Reports/Deploy-Status-2025-11-12.md b/Transzendenz/Reports/Deploy-Status-2025-11-12.md new file mode 100644 index 0000000..c93e1fb --- /dev/null +++ b/Transzendenz/Reports/Deploy-Status-2025-11-12.md @@ -0,0 +1,28 @@ +# 📦 YONI Deploy Status + +**Last Updated:** 2025-11-05 + +## Deployment Tasks + +| Task | Status | Description | +|------|--------|-------------| +| 1. Repository Setup | ✅ Complete | Initial repository structure created | +| 2. CI Workflow Configuration | ✅ Complete | GitHub Actions CI pipeline configured | +| 3. Build Process | ✅ Complete | Build scripts and dependencies set up | +| 4. Test Infrastructure | ✅ Complete | Testing framework configured | +| 5. Deploy Snapshot Workflow | ✅ Complete | Daily snapshot workflow implemented | +| 6. Email Notifications | 🔄 In Progress | Email notification system configuration | +| 7. Production Deployment | 🔄 In Progress | Production environment setup | +| 8. Monitoring & Logging | 🔄 In Progress | Monitoring and logging infrastructure | + +## Summary + +- **Total Tasks:** 8 +- **Completed:** 5 +- **In Progress:** 3 +- **Blocked:** 0 + +## Notes + +This status file is used by the deploy_snapshot.yml workflow to track deployment progress. +A snapshot of this file is created daily with the current date. diff --git a/Transzendenz/Reports/snapshot-log.txt b/Transzendenz/Reports/snapshot-log.txt index 21931cf..394031a 100644 --- a/Transzendenz/Reports/snapshot-log.txt +++ b/Transzendenz/Reports/snapshot-log.txt @@ -5,3 +5,4 @@ This file tracks all snapshot events from the deploy_snapshot.yml workflow. --- ✅ Snapshot log initialized on 2025-11-05 +✅ Snapshot for 2025-11-12 created at 2025-11-12 00:52:49 From 8f145e95f2aceb65c3442f636bc1626d6eb7a4ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:26:58 +0000 Subject: [PATCH 032/133] Initial plan From 12cfdaac95152771d3edc26eb8f646f182630dd0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:27:13 +0000 Subject: [PATCH 033/133] Initial plan From b1b2d2e0036bb395ed5cca9927319a2f18f98b4b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:28:19 +0000 Subject: [PATCH 034/133] Initial plan From d11d92e5d9dbde3721e3940cf00d2b156e46b4c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:28:27 +0000 Subject: [PATCH 035/133] Initial plan From a324575bee27d0674d8c2a86ee989b3ced7e8de0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:32:27 +0000 Subject: [PATCH 036/133] Remove duplicate Stripe webhook route (Pages Router version) Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- src/pages/api/stripe/webhook.ts | 49 --------------------------------- 1 file changed, 49 deletions(-) delete mode 100644 src/pages/api/stripe/webhook.ts diff --git a/src/pages/api/stripe/webhook.ts b/src/pages/api/stripe/webhook.ts deleted file mode 100644 index 864aebc..0000000 --- a/src/pages/api/stripe/webhook.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import Stripe from 'stripe'; - -export const config = { api: { bodyParser: false } }; - -function buffer(req: any): Promise { - return new Promise((resolve, reject) => { - const _buf: any[] = []; - req.on('data', (chunk: any) => _buf.push(chunk)); - req.on('end', () => resolve(Buffer.concat(_buf))); - req.on('error', reject); - }); -} - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method !== 'POST') return res.status(405).end(); - - const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; - if (!webhookSecret) { - console.error('Missing STRIPE_WEBHOOK_SECRET'); - return res.status(500).json({ error: 'Server misconfigured' }); - } - - const sig = req.headers['stripe-signature'] as string; - const buf = await buffer(req as any); - - let event: Stripe.Event; - try { - // ⚠️ Kein Stripe-Client nötig - event = Stripe.webhooks.constructEvent(buf, sig, webhookSecret); - } catch (err: any) { - console.error('Webhook signature verification failed:', err.message); - return res.status(400).send(`Webhook Error: ${err.message}`); - } - - // Handle events - switch (event.type) { - case 'checkout.session.completed': - console.log('✅ checkout.session.completed', event.data.object); - break; - case 'payment_intent.succeeded': - console.log('✅ payment_intent.succeeded', event.data.object); - break; - default: - console.log(`ℹ️ Unhandled event: ${event.type}`); - } - - return res.status(200).json({ received: true }); -} From b4641b2e0ba9596e6acd3d560f0912d95a175051 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:32:36 +0000 Subject: [PATCH 037/133] Add project-ops/launch structure with task management files Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- project-ops/launch/README.md | 199 ++++++++++++++++++++++++ project-ops/launch/notion-template.json | 141 +++++++++++++++++ project-ops/launch/tasks.csv | 13 ++ 3 files changed, 353 insertions(+) create mode 100644 project-ops/launch/README.md create mode 100644 project-ops/launch/notion-template.json create mode 100644 project-ops/launch/tasks.csv diff --git a/project-ops/launch/README.md b/project-ops/launch/README.md new file mode 100644 index 0000000..a8f3b9c --- /dev/null +++ b/project-ops/launch/README.md @@ -0,0 +1,199 @@ +# 🚀 YONI Launch Operations + +> Zentrale Task-Management-Struktur für den YONI App Launch + +## 📋 Übersicht + +Dieses Verzeichnis enthält die strukturierte Task-Verwaltung für den YONI-Launch, organisiert nach drei strategischen Säulen: + +| Pillar | Fokus | Beispiel | +|---------|--------|-----------| +| **BUILD** | Technisches Fundament | Stripe Webhook fixen, Deploy testen | +| **PAYMENT** | Monetarisierung & Compliance | Instant Payment aktivieren, Checkout-Link einrichten | +| **YOUTUBE** | Reichweite & Conversion | Short drehen, Community-Post mit Stripe-Link | + +## 📁 Dateistruktur + +``` +project-ops/launch/ +├── notion-template.json # Strukturierte Task-Daten (JSON) +├── tasks.csv # Flache Task-Liste (CSV) +└── README.md # Diese Dokumentation +``` + +## 🪄 Quickstart (lokal) + +Wenn du das Repo klonst, kannst du die JSON und CSV lokal prüfen: + +```bash +git clone https://github.com/pappensex/YONI-app.git +cd YONI-app/project-ops/launch +cat notion-template.json | jq '.title' +``` + +### Beispiel-Abfragen + +```bash +# Titel anzeigen +jq '.title' notion-template.json + +# Alle Pillars auflisten +jq '.pillars[].name' notion-template.json + +# BUILD-Tasks anzeigen +jq '.pillars[] | select(.name == "BUILD") | .tasks' notion-template.json + +# High-Priority Tasks finden +jq '.pillars[].tasks[] | select(.priority == "high")' notion-template.json + +# Status-Übersicht +jq '.pillars[].tasks[] | {id, title, status}' notion-template.json + +# Anzahl Tasks pro Pillar +jq '.pillars[] | {name, count: (.tasks | length)}' notion-template.json +``` + +## 📊 CSV-Nutzung + +Die `tasks.csv` kann mit Standard-Tools analysiert werden: + +```bash +# Alle Tasks anzeigen +cat tasks.csv + +# BUILD-Tasks filtern +grep "^BUILD" tasks.csv + +# High-Priority Tasks +grep ",high," tasks.csv + +# Task-Zählung pro Pillar +cut -d, -f1 tasks.csv | tail -n +2 | sort | uniq -c +``` + +## 🎯 Task-Format + +### JSON-Struktur + +Jeder Task hat folgende Felder: + +```json +{ + "id": "PILLAR-NNN", + "title": "Task Titel", + "description": "Detaillierte Beschreibung", + "status": "pending|in_progress|review|completed|blocked", + "priority": "low|medium|high|critical", + "example": "Konkretes Beispiel", + "tags": ["tag1", "tag2"] +} +``` + +### CSV-Format + +```csv +Pillar,Task ID,Title,Description,Status,Priority,Tags,Example +BUILD,BUILD-001,Stripe Webhook fixen,Fix webhook integration,in_progress,high,"stripe,backend",Stripe Webhook fixen +``` + +## 🔄 Workflow + +### Task-Status + +| Status | Bedeutung | +|--------|-----------| +| `pending` | Noch nicht begonnen | +| `in_progress` | In Bearbeitung | +| `review` | In Review/Testing | +| `completed` | Abgeschlossen | +| `blocked` | Blockiert (Abhängigkeiten) | + +### Prioritäten + +| Priority | Beschreibung | +|----------|--------------| +| `critical` | Sofort erledigen, blocker für Launch | +| `high` | Wichtig für MVP | +| `medium` | Wünschenswert | +| `low` | Nice-to-have | + +## 🛠️ Integration + +### Mit GitHub Issues + +Tasks können mit GitHub Issues verknüpft werden: + +```bash +# Issue für Task erstellen +gh issue create --title "BUILD-001: Stripe Webhook fixen" \ + --label "build,high-priority" \ + --body "$(jq -r '.pillars[0].tasks[0].description' notion-template.json)" +``` + +### Mit Notion + +Die `notion-template.json` kann in Notion importiert werden: + +1. Notion-Datenbank erstellen +2. JSON-Import-Feature nutzen +3. Felder mappen: id → ID, title → Name, etc. + +### Mit Jira/Trello + +CSV kann direkt in Jira/Trello importiert werden: + +1. Projekt/Board öffnen +2. Import → CSV wählen +3. `tasks.csv` hochladen +4. Feldmapping durchführen + +## 📈 Monitoring + +### Progress Tracking + +```bash +# Fertigstellungsgrad berechnen +echo "scale=2; $(jq '[.pillars[].tasks[]] | map(select(.status == "completed")) | length' notion-template.json) / $(jq '[.pillars[].tasks[]] | length' notion-template.json) * 100" | bc +``` + +### Pillar-Status + +```bash +# Status pro Pillar +for pillar in BUILD PAYMENT YOUTUBE; do + echo "=== $pillar ===" + jq -r ".pillars[] | select(.name == \"$pillar\") | .tasks[] | \"\(.id): \(.status)\"" notion-template.json +done +``` + +## 🎨 YONI Design Principles + +Alle Tasks sollten folgende Prinzipien berücksichtigen: + +- 🟣 **Sicherheit** – Schutz der Nutzer:innen hat oberste Priorität +- 💜 **Würde** – Respektvoller Umgang mit sensiblen Themen +- 🌌 **Transzendenz** – Ästhetische Exzellenz (Überhochglitzer) +- 🧠 **Kompetenz** – Medizinisch/technisch fundiert +- 🪶 **Leichtigkeit** – Einfach, klar, barrierefrei + +## 🔗 Links + +- **Hauptprojekt:** [YONI-app](https://github.com/pappensex/YONI-app) +- **Dokumentation:** [README.md](../../README.md) +- **Workflows:** [WORKFLOW-DOCUMENTATION.md](../../WORKFLOW-DOCUMENTATION.md) +- **Demo:** [yoni.vercel.app](https://yoni.vercel.app) + +## 📝 Notizen + +- Tasks regelmäßig aktualisieren +- Git nutzen für Änderungsverfolgung +- Bei Bedarf mit GitHub Issues verknüpfen +- Überhochglitzer-Design in allen Deliverables beachten + +--- + +**Version:** 1.0 +**Stand:** 2025-11-12 +**Maintainer:** [@pappensex](https://github.com/pappensex) + +> _„Jeder Task ist ein Stern im YONI-Universum."_ ✨ diff --git a/project-ops/launch/notion-template.json b/project-ops/launch/notion-template.json new file mode 100644 index 0000000..1bd70d5 --- /dev/null +++ b/project-ops/launch/notion-template.json @@ -0,0 +1,141 @@ +{ + "title": "YONI Launch Task Management", + "version": "1.0", + "created": "2025-11-12", + "description": "Task management template for YONI app launch organized by pillars: BUILD, PAYMENT, and YOUTUBE", + "pillars": [ + { + "name": "BUILD", + "focus": "Technisches Fundament", + "color": "#9966CC", + "tasks": [ + { + "id": "BUILD-001", + "title": "Stripe Webhook fixen", + "description": "Fix Stripe webhook integration for payment processing", + "status": "in_progress", + "priority": "high", + "example": "Stripe Webhook fixen", + "tags": ["stripe", "backend", "webhook"] + }, + { + "id": "BUILD-002", + "title": "Deploy testen", + "description": "Test deployment pipeline on Vercel", + "status": "pending", + "priority": "high", + "example": "Deploy testen", + "tags": ["deployment", "vercel", "ci-cd"] + }, + { + "id": "BUILD-003", + "title": "Security Checks implementieren", + "description": "Implement CSP, HTTPS, and RBAC security measures", + "status": "pending", + "priority": "high", + "tags": ["security", "compliance"] + }, + { + "id": "BUILD-004", + "title": "A11y Testing einrichten", + "description": "Set up accessibility testing with axe-core", + "status": "pending", + "priority": "medium", + "tags": ["accessibility", "testing"] + } + ] + }, + { + "name": "PAYMENT", + "focus": "Monetarisierung & Compliance", + "color": "#FFD700", + "tasks": [ + { + "id": "PAYMENT-001", + "title": "Instant Payment aktivieren", + "description": "Enable instant payment functionality in Stripe", + "status": "pending", + "priority": "high", + "example": "Instant Payment aktivieren", + "tags": ["stripe", "payment", "instant-payment"] + }, + { + "id": "PAYMENT-002", + "title": "Checkout-Link einrichten", + "description": "Set up Stripe Checkout link for subscriptions", + "status": "pending", + "priority": "high", + "example": "Checkout-Link einrichten", + "tags": ["stripe", "checkout", "subscription"] + }, + { + "id": "PAYMENT-003", + "title": "DSGVO-Compliance prüfen", + "description": "Verify GDPR compliance for payment processing", + "status": "pending", + "priority": "high", + "tags": ["compliance", "gdpr", "legal"] + }, + { + "id": "PAYMENT-004", + "title": "Preismodell definieren", + "description": "Define pricing tiers and subscription models", + "status": "pending", + "priority": "medium", + "tags": ["business", "pricing"] + } + ] + }, + { + "name": "YOUTUBE", + "focus": "Reichweite & Conversion", + "color": "#FF0000", + "tasks": [ + { + "id": "YOUTUBE-001", + "title": "Short drehen", + "description": "Create YouTube Short for product launch", + "status": "pending", + "priority": "high", + "example": "Short drehen", + "tags": ["video", "marketing", "content"] + }, + { + "id": "YOUTUBE-002", + "title": "Community-Post mit Stripe-Link", + "description": "Create community post with Stripe payment link", + "status": "pending", + "priority": "high", + "example": "Community-Post mit Stripe-Link", + "tags": ["community", "marketing", "stripe"] + }, + { + "id": "YOUTUBE-003", + "title": "Launch-Video produzieren", + "description": "Produce main launch video explaining YONI platform", + "status": "pending", + "priority": "medium", + "tags": ["video", "launch", "content"] + }, + { + "id": "YOUTUBE-004", + "title": "Social Media Kampagne", + "description": "Plan and execute social media campaign", + "status": "pending", + "priority": "medium", + "tags": ["social-media", "marketing"] + } + ] + } + ], + "workflow": { + "statuses": ["pending", "in_progress", "review", "completed", "blocked"], + "priorities": ["low", "medium", "high", "critical"] + }, + "notes": [ + "Tasks should be updated regularly", + "Use git to track changes to task status", + "Link tasks to GitHub issues when appropriate", + "Follow YONI's Überhochglitzer design principles in all deliverables" + ] +} diff --git a/project-ops/launch/tasks.csv b/project-ops/launch/tasks.csv new file mode 100644 index 0000000..6f63075 --- /dev/null +++ b/project-ops/launch/tasks.csv @@ -0,0 +1,13 @@ +Pillar,Task ID,Title,Description,Status,Priority,Tags,Example +BUILD,BUILD-001,Stripe Webhook fixen,Fix Stripe webhook integration for payment processing,in_progress,high,"stripe,backend,webhook",Stripe Webhook fixen +BUILD,BUILD-002,Deploy testen,Test deployment pipeline on Vercel,pending,high,"deployment,vercel,ci-cd",Deploy testen +BUILD,BUILD-003,Security Checks implementieren,Implement CSP HTTPS and RBAC security measures,pending,high,"security,compliance", +BUILD,BUILD-004,A11y Testing einrichten,Set up accessibility testing with axe-core,pending,medium,"accessibility,testing", +PAYMENT,PAYMENT-001,Instant Payment aktivieren,Enable instant payment functionality in Stripe,pending,high,"stripe,payment,instant-payment",Instant Payment aktivieren +PAYMENT,PAYMENT-002,Checkout-Link einrichten,Set up Stripe Checkout link for subscriptions,pending,high,"stripe,checkout,subscription",Checkout-Link einrichten +PAYMENT,PAYMENT-003,DSGVO-Compliance prüfen,Verify GDPR compliance for payment processing,pending,high,"compliance,gdpr,legal", +PAYMENT,PAYMENT-004,Preismodell definieren,Define pricing tiers and subscription models,pending,medium,"business,pricing", +YOUTUBE,YOUTUBE-001,Short drehen,Create YouTube Short for product launch,pending,high,"video,marketing,content",Short drehen +YOUTUBE,YOUTUBE-002,Community-Post mit Stripe-Link,Create community post with Stripe payment link,pending,high,"community,marketing,stripe",Community-Post mit Stripe-Link +YOUTUBE,YOUTUBE-003,Launch-Video produzieren,Produce main launch video explaining YONI platform,pending,medium,"video,launch,content", +YOUTUBE,YOUTUBE-004,Social Media Kampagne,Plan and execute social media campaign,pending,medium,"social-media,marketing", From a01f34194bc72cdd54d60f10d9bb9a66f908201a Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 12 Nov 2025 10:34:05 +0100 Subject: [PATCH 038/133] Add Next.js 14 setup to enable npm run dev (#48) --- .eslintrc.json | 3 + .gitignore | 33 + api/stripe/webhook/route.ts | 2 +- app/globals.css | 85 + app/layout.tsx | 24 + app/page.tsx | 115 + next.config.js | 16 + package-lock.json | 6130 +++++++++++++++++++++++++++++++++++ package.json | 32 + postcss.config.js | 6 + tailwind.config.js | 27 + tsconfig.json | 42 + 12 files changed, 6514 insertions(+), 1 deletion(-) create mode 100644 .eslintrc.json create mode 100644 .gitignore create mode 100644 app/globals.css create mode 100644 app/layout.tsx create mode 100644 app/page.tsx create mode 100644 next.config.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 tailwind.config.js create mode 100644 tsconfig.json diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..892067b --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/api/stripe/webhook/route.ts b/api/stripe/webhook/route.ts index a740773..af2f57c 100644 --- a/api/stripe/webhook/route.ts +++ b/api/stripe/webhook/route.ts @@ -6,7 +6,7 @@ export const dynamic = "force-dynamic"; const stripeSecretKey = process.env.STRIPE_SECRET_KEY; const stripe = stripeSecretKey ? new Stripe(stripeSecretKey, { - apiVersion: "2024-06-20", + apiVersion: "2023-10-16", }) : null; diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 0000000..357283f --- /dev/null +++ b/app/globals.css @@ -0,0 +1,85 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --bg: #0b0b10; + --panel: rgba(255, 255, 255, .06); + --text: #f3f3f7; + --brand: #7c3aed; + --radius: 14px; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + background: radial-gradient(1200px 600px at 20% 0%, #1b1030 0, transparent 60%), + radial-gradient(1200px 600px at 100% 100%, #0d2230 0, transparent 50%), + var(--bg); + color: var(--text); + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Inter, Roboto, Arial, sans-serif; +} + +.wrap { + max-width: 860px; + margin: 40px auto; + padding: 16px; +} + +.card { + background: var(--panel); + border: 1px solid rgba(255, 255, 255, .08); + border-radius: var(--radius); + padding: 16px; +} + +h1 { + margin: 0 0 8px 0; + font-size: 22px; +} + +.muted { + opacity: .7; +} + +.row { + display: flex; + gap: 8px; + flex-wrap: wrap; + margin-top: 10px; +} + +button { + background: var(--brand); + color: #fff; + border: none; + border-radius: 12px; + padding: 10px 12px; + cursor: pointer; +} + +textarea, +input, +select { + background: rgba(255, 255, 255, .06); + border: 1px solid rgba(255, 255, 255, .1); + color: var(--text); + border-radius: 12px; + padding: 10px; + width: 100%; +} + +.feed { + margin-top: 10px; + display: grid; + gap: 8px; +} + +.item { + background: rgba(255, 255, 255, .05); + padding: 10px; + border-radius: 12px; +} diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..5ef65bb --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,24 @@ +import './globals.css' +import type { Metadata } from 'next' + +export const metadata: Metadata = { + title: 'YONI • pi² Control (Vercel‑Min)', + description: 'YONI: Creator‑KI, Auto‑Translate, Transzendenz‑Hub. Minimal Flat Build für Vercel.', + themeColor: '#0a0a0a', + manifest: '/manifest.webmanifest', + icons: { + apple: '/icon-192.png', + }, +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..dc2fad8 --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,115 @@ +'use client' + +import { useState } from 'react' + +export default function Home() { + const [question, setQuestion] = useState('') + const [mode, setMode] = useState('Consensus') + const [autoTranslate, setAutoTranslate] = useState(true) + const [feed, setFeed] = useState>([]) + const [envMessage, setEnvMessage] = useState('') + + // Check environment on mount + useState(() => { + if (typeof window !== 'undefined') { + setEnvMessage( + window.location.protocol === 'https:' + ? 'HTTPS aktiv – Service Worker & Install verfügbar.' + : 'Hinweis: Für Offline & „Zum Home‑Bildschirm" bitte HTTPS nutzen.' + ) + } + }) + + const handleInstall = async () => { + if (typeof window !== 'undefined') { + if (window.location.protocol !== 'https:') { + alert('Service Worker benötigt HTTPS.') + return + } + if ('serviceWorker' in navigator) { + await navigator.serviceWorker.register('/sw.js') + alert('Offline aktiviert.') + } + } + } + + const handleA2HS = () => { + alert('iPhone: Safari → Teilen → „Zum Home‑Bildschirm".') + } + + const handleAsk = () => { + const q = question.trim() + if (!q) return + + const answers = [ + { a: 'GPT‑5 Pro', t: `Antwort auf „${q}" — präzise, strukturiert, mit Handlungsempfehlung.` }, + { a: 'CHIBot‑Alpha', t: `Antwort auf „${q}" — intuitiv, visionär, poetisch‑kantig.` }, + { a: 'CHIBot‑Beta', t: `Antwort auf „${q}" — technisch, datengetrieben, scharf.` }, + { a: 'CHIBot‑Omega', t: `Antwort auf „${q}" — synthetisch, meta, systemisch.` }, + ] + + const fuse = (arr: typeof answers, m: string) => { + if (m === 'Consensus') return 'Konsens: ' + arr.map(x => x.t).join(' | ') + if (m === 'Contrast') return 'Kontrast: ' + arr.map(x => x.t).join(' ⇄ ') + return 'Chain: ' + arr.map((x, i) => `[${i + 1}] ${x.t}`).join(' → ') + } + + const fused = fuse(answers, mode) + const newFeed = [ + { id: Date.now(), agent: 'Fusion', text: fused, isFusion: true }, + ...answers.map((x, i) => ({ id: Date.now() + i + 1, agent: x.a, text: x.t })) + ] + + setFeed([...newFeed, ...feed]) + } + + return ( +
+
+

YONI • pi² Control

+
Vercel‑Minimal • Creator‑KI • Auto‑Translate • Transzendenz
+
+ + +
+

{envMessage}

+
+ +
+

Transzendenz

+
+ + +
+ - - -

- diff --git a/import-copilot-pr.sh b/import-copilot-pr.sh deleted file mode 100755 index 8deaf09..0000000 --- a/import-copilot-pr.sh +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/env bash -# Import External Copilot PR with Governance Enforcement -# Usage: ./import-copilot-pr.sh -# -# This script: -# 1. Fetches and applies a patch from an external Copilot PR -# 2. Enforces company governance rules: -# - Single Stripe webhook route (TypeScript only) -# - TypeScript-only policy (allowJs: false) -# 3. Validates no duplicate routes exist -# 4. Builds and validates the project -# 5. Creates a branch ready for PR - -set -euo pipefail - -# Check if PR number is provided -if [ $# -eq 0 ]; then - echo "Usage: $0 " - echo "Example: $0 123" - exit 1 -fi - -PR_NUM="$1" -BR="import/copilot-pr-$PR_NUM" - -echo "🚀 Importing Copilot PR #$PR_NUM" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - -# Fetch latest changes from origin -echo "📥 Fetching latest changes from origin..." -git fetch origin - -# Create and checkout new branch from origin/main -echo "🌿 Creating branch: $BR" -git checkout -B "$BR" origin/main - -# Fetch the patch from the external Copilot PR -echo "📦 Fetching patch from Copilot PR #$PR_NUM..." -curl -fsSL "https://github.com/copilot/tasks/pull/$PR_NUM.patch" -o /tmp/copilot.patch - -# Dry run to check if patch applies cleanly -echo "🧪 Testing patch application (dry run)..." -git apply --check /tmp/copilot.patch - -# Apply patch with git am to preserve commit history -echo "✨ Applying patch with commit history..." -if ! git am /tmp/copilot.patch; then - echo "⚠️ Conflicts detected during patch application." - echo "Please resolve manually:" - echo " 1. Check status: git status" - echo " 2. Edit conflicted files" - echo " 3. Stage changes: git add -A" - echo " 4. Continue: git am --continue" - exit 1 -fi - -echo "" -echo "🏛️ Enforcing company governance policies..." -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - -# Policy 1: Remove all .js/.jsx files that have .ts/.tsx equivalents (TypeScript-only) -echo "📋 Policy 1: Enforcing TypeScript-only policy - removing duplicate JS/JSX files" -removed_count=0 - -# Find and remove .js files that have .ts equivalents -while IFS= read -r jsfile; do - tsfile="${jsfile%.js}.ts" - if [ -f "$tsfile" ]; then - echo " → Removing $jsfile (TypeScript equivalent exists)" - rm -f "$jsfile" - ((removed_count++)) - fi -done < <(git ls-files "*.js") - -# Find and remove .jsx files that have .tsx equivalents -while IFS= read -r jsxfile; do - tsxfile="${jsxfile%.jsx}.tsx" - if [ -f "$tsxfile" ]; then - echo " → Removing $jsxfile (TypeScript equivalent exists)" - rm -f "$jsxfile" - ((removed_count++)) - fi -done < <(git ls-files "*.jsx") - -if [ $removed_count -eq 0 ]; then - echo " ✓ No duplicate JavaScript files found" -else - echo " ✓ Removed $removed_count duplicate JavaScript file(s)" -fi - -# Policy 2: TypeScript-only (set allowJs: false in tsconfig.json) -echo "📋 Policy 2: Enforcing TypeScript-only policy" -if [ -f tsconfig.json ]; then - echo " → Updating tsconfig.json to set allowJs: false" - - # Check if allowJs already exists - if grep -q '"allowJs"' tsconfig.json; then - # Replace existing allowJs value - sed -i 's/"allowJs" *: *true/"allowJs": false/' tsconfig.json - echo " ✓ Updated existing allowJs setting to false" - else - # Add allowJs: false to compilerOptions - sed -i '0,/"compilerOptions"[[:space:]]*:[[:space:]]*{/{s//"compilerOptions": {\n "allowJs": false,/}' tsconfig.json - echo " ✓ Added allowJs: false to compilerOptions" - fi -else - echo " ℹ️ No tsconfig.json found (will be created if needed)" -fi - -echo "" -echo "🔍 Checking for duplicate routes..." -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - -# Find files with same name but different extensions (potential duplicates) -# Use find instead of git ls-files to check actual filesystem after removals -find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) \ - ! -path "*/node_modules/*" \ - ! -path "*/.git/*" \ - ! -path "*/dist/*" \ - ! -path "*/build/*" \ - | sed "s/\.[^.]*$//" | sort | uniq -d | tee /tmp/dupes.txt - -if [ -s /tmp/dupes.txt ]; then - echo "❌ Duplicate routes detected:" - cat /tmp/dupes.txt - echo "" - echo "Please resolve duplicate routes before continuing." - exit 1 -else - echo "✓ No duplicate routes found" -fi - -echo "" -echo "🔨 Building project..." -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - -# Enable corepack if available (for package manager version management) -corepack enable 2>/dev/null || echo "ℹ️ corepack not available, skipping" - -# Install dependencies -echo "📦 Installing dependencies..." -if [ -f package-lock.json ]; then - npm ci -elif [ -f package.json ]; then - npm install -else - echo "ℹ️ No package.json found, skipping npm install" -fi - -# Run build if build script exists -if [ -f package.json ] && npm run 2>/dev/null | grep -q "build"; then - echo "🏗️ Running build..." - npm run build -else - echo "ℹ️ No build script found, skipping build" -fi - -echo "" -echo "💾 Committing policy enforcement changes..." -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - -# Stage all changes (including policy enforcement) -git add -A - -# Commit if there are changes -if git diff --cached --quiet; then - echo "ℹ️ No additional changes to commit" -else - git commit -m "chore: import external PR $PR_NUM; enforce single Stripe webhook + TS only; policy compliance" - echo "✓ Changes committed" -fi - -# Push branch to origin -echo "🚀 Pushing branch to origin..." -git push -u origin "$BR" - -echo "" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "✅ Branch ready: $BR" -echo "" -echo "Next step - Create PR with:" -echo "gh pr create --fill --title \"import: copilot/tasks PR $PR_NUM\" --body \"Imported via patch; build ok; no duplicate routes; TS enforced.\"" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/index.html b/index.html deleted file mode 100644 index a581cb9..0000000 --- a/index.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - YONI • pi² Control (Vercel‑Min) - - - - - - - - -
-
-

YONI • pi² Control

-
Vercel‑Minimal • Creator‑KI • Auto‑Translate • Transzendenz
-
- - -
-

-
- -
-

Transzendenz

-
- - -
- -
- -
-
-
-
- - - - diff --git a/smoke-tests-prod.sh b/smoke-tests-prod.sh deleted file mode 100644 index ba56728..0000000 --- a/smoke-tests-prod.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -# Smoke Tests for Production Deployment -# This script demonstrates the smoke tests mentioned in the problem statement - -echo "===================================" -echo "Production Deployment Smoke Tests" -echo "===================================" -echo "" - -# Set production domain (replace with actual production domain) -PROD_DOMAIN="${PROD_DOMAIN:-yoni-app.vercel.app}" - -echo "Testing against: https://${PROD_DOMAIN}" -echo "" - -# Test 1: Stripe webhook with invalid signature -echo "1. Testing Stripe webhook with invalid signature..." -echo " curl -s https://${PROD_DOMAIN}/api/stripe/webhook -X POST -H 'stripe-signature: invalid' -d '{}'" -echo "" -STRIPE_RESPONSE=$(curl -s https://${PROD_DOMAIN}/api/stripe/webhook -X POST -H "stripe-signature: invalid" -d '{}') -echo " Response: $STRIPE_RESPONSE" -echo "" - -# Test 2: GitHub webhook with valid signature -echo "2. Testing GitHub webhook with valid signature..." -if [ -z "$GITHUB_WEBHOOK_SECRET" ]; then - echo " ⚠️ GITHUB_WEBHOOK_SECRET not set. Using test value." - GITHUB_WEBHOOK_SECRET="test-secret-for-demo" -fi - -PAYLOAD='{}' -SIG=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$GITHUB_WEBHOOK_SECRET" -r | cut -d' ' -f1) - -echo " curl -s https://${PROD_DOMAIN}/api/github-app/webhook -H 'x-github-event: ping' -H 'x-hub-signature-256: sha256=\$SIG' -H 'content-type: application/json' -d '{}'" -echo "" -GITHUB_RESPONSE=$(curl -s https://${PROD_DOMAIN}/api/github-app/webhook -H "x-github-event: ping" -H "x-hub-signature-256: sha256=$SIG" -H "content-type: application/json" -d "$PAYLOAD") -echo " Response: $GITHUB_RESPONSE" -echo "" - -# Test 3: GitHub webhook with invalid signature -echo "3. Testing GitHub webhook with invalid signature..." -echo " curl -s https://${PROD_DOMAIN}/api/github-app/webhook -X POST -H 'x-hub-signature-256: sha256=invalid' -d '{}'" -echo "" -GITHUB_INVALID_RESPONSE=$(curl -s https://${PROD_DOMAIN}/api/github-app/webhook -X POST -H "x-github-event: ping" -H "x-hub-signature-256: sha256=invalid" -H "content-type: application/json" -d '{}') -echo " Response: $GITHUB_INVALID_RESPONSE" -echo "" - -echo "===================================" -echo "Smoke tests completed!" -echo "===================================" diff --git a/system.jsx b/system.jsx deleted file mode 100644 index 4c1fd2c..0000000 --- a/system.jsx +++ /dev/null @@ -1 +0,0 @@ -} /> diff --git a/test-import-script.sh b/test-import-script.sh deleted file mode 100755 index 8427673..0000000 --- a/test-import-script.sh +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/env bash -# Test script for import-copilot-pr.sh governance enforcement -# This tests the governance components without requiring an actual external PR - -set -euo pipefail - -echo "🧪 Testing import-copilot-pr.sh governance enforcement" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -cd "$SCRIPT_DIR" - -PASS=0 -FAIL=0 - -# Helper functions -pass() { - echo " ✅ PASS: $1" - PASS=$((PASS + 1)) -} - -fail() { - echo " ❌ FAIL: $1" - FAIL=$((FAIL + 1)) -} - -section() { - echo "" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "📋 $1" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -} - -# Test 1: Script exists and is executable -section "Test 1: Script Setup" -if [ -f "import-copilot-pr.sh" ]; then - pass "Script file exists" -else - fail "Script file not found" -fi - -if [ -x "import-copilot-pr.sh" ]; then - pass "Script is executable" -else - fail "Script is not executable" -fi - -# Test 2: Script syntax validation -section "Test 2: Script Syntax" -if bash -n import-copilot-pr.sh 2>/dev/null; then - pass "Script syntax is valid" -else - fail "Script has syntax errors" -fi - -# Test 3: Help message -section "Test 3: Help Message" -set +e -set +o pipefail -output=$(./import-copilot-pr.sh 2>&1) -set -e -set -o pipefail - -if echo "$output" | grep -q "Usage:"; then - pass "Help message displays correctly" -else - fail "Help message not found" -fi - -# Test 4: Duplicate JS/JSX detection -section "Test 4: Duplicate File Detection" -# Current state should have duplicates -if [ -f "api/stripe/webhook/route.js" ] && [ -f "api/stripe/webhook/route.ts" ]; then - pass "Found expected duplicate: api/stripe/webhook/route.*" -else - fail "Expected duplicate not found: api/stripe/webhook/route.*" -fi - -if [ -f "core/modules/deploy-center/YoniDeployControlCenter.jsx" ] && [ -f "core/modules/deploy-center/YoniDeployControlCenter.tsx" ]; then - pass "Found expected duplicate: YoniDeployControlCenter.*" -else - fail "Expected duplicate not found: YoniDeployControlCenter.*" -fi - -# Test 5: Duplicate check logic -section "Test 5: Duplicate Check Logic" -# Test that find command detects duplicates before removal -dupe_count=$(find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) \ - ! -path "*/node_modules/*" \ - ! -path "*/.git/*" \ - ! -path "*/dist/*" \ - ! -path "*/build/*" \ - | sed "s/\.[^.]*$//" | sort | uniq -d | wc -l) - -if [ "$dupe_count" -gt 0 ]; then - pass "Duplicate detection finds duplicates ($dupe_count found)" -else - fail "Duplicate detection should find duplicates but found none" -fi - -# Test 6: Simulated removal and re-check -section "Test 6: Removal Simulation" -# Create temporary backup -cp api/stripe/webhook/route.js /tmp/route.js.bak 2>/dev/null || true -cp core/modules/deploy-center/YoniDeployControlCenter.jsx /tmp/YoniDeployControlCenter.jsx.bak 2>/dev/null || true - -# Remove duplicates -rm -f api/stripe/webhook/route.js -rm -f core/modules/deploy-center/YoniDeployControlCenter.jsx - -# Check for duplicates after removal -dupe_count_after=$(find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) \ - ! -path "*/node_modules/*" \ - ! -path "*/.git/*" \ - ! -path "*/dist/*" \ - ! -path "*/build/*" \ - | sed "s/\.[^.]*$//" | sort | uniq -d | wc -l) - -if [ "$dupe_count_after" -eq 0 ]; then - pass "No duplicates found after removal" -else - fail "Duplicates still exist after removal ($dupe_count_after found)" -fi - -# Restore files -git restore api/stripe/webhook/route.js core/modules/deploy-center/YoniDeployControlCenter.jsx 2>/dev/null || { - # If git restore fails, use backup - mv /tmp/route.js.bak api/stripe/webhook/route.js 2>/dev/null || true - mv /tmp/YoniDeployControlCenter.jsx.bak core/modules/deploy-center/YoniDeployControlCenter.jsx 2>/dev/null || true -} - -pass "Files restored after test" - -# Test 7: TypeScript file preservation -section "Test 7: TypeScript File Preservation" -if [ -f "api/stripe/webhook/route.ts" ]; then - pass "TypeScript webhook file exists" -else - fail "TypeScript webhook file missing" -fi - -if [ -f "core/modules/deploy-center/YoniDeployControlCenter.tsx" ]; then - pass "TypeScript component file exists" -else - fail "TypeScript component file missing" -fi - -# Test 8: Documentation -section "Test 8: Documentation" -if [ -f "COPILOT_PR_IMPORT.md" ]; then - pass "Documentation file exists" -else - fail "Documentation file missing" -fi - -if grep -q "import-copilot-pr.sh" README.md; then - pass "README.md references the import script" -else - fail "README.md doesn't reference the import script" -fi - -# Test 9: Script components -section "Test 9: Script Components" -if grep -q 'rm -f.*js' import-copilot-pr.sh; then - pass "Script contains JS/JSX removal logic" -else - fail "Script missing JS/JSX removal logic" -fi - -if grep -q "allowJs.*false" import-copilot-pr.sh; then - pass "Script contains TypeScript-only config" -else - fail "Script missing TypeScript-only config" -fi - -if grep -q "git am" import-copilot-pr.sh; then - pass "Script uses git am for patch application" -else - fail "Script doesn't use git am" -fi - -if grep -q "npm run build" import-copilot-pr.sh; then - pass "Script includes build step" -else - fail "Script missing build step" -fi - -# Summary -echo "" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "📊 Test Summary" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "" -echo " ✅ Passed: $PASS" -echo " ❌ Failed: $FAIL" -echo " 📈 Total: $((PASS + FAIL))" -echo "" - -if [ $FAIL -eq 0 ]; then - echo "🎉 All tests passed!" - echo "" - exit 0 -else - echo "⚠️ Some tests failed. Please review the output above." - echo "" - exit 1 -fi diff --git a/yoni-app-devcontainer.patch b/yoni-app-devcontainer.patch deleted file mode 100644 index 79ca084..0000000 --- a/yoni-app-devcontainer.patch +++ /dev/null @@ -1,80 +0,0 @@ -*** Begin Patch -*** Add File: .devcontainer/devcontainer.json -{ - "name": "YONI Next.js", - "image": "mcr.microsoft.com/devcontainers/typescript-node:22", - "features": { - "ghcr.io/devcontainers/features/github-cli": "1" - }, - "postCreateCommand": "corepack enable && (npm ci || npm install)", - "forwardPorts": [3000, 3001], - "portsAttributes": { - "3000": { - "label": "web", - "visibility": "public" - } - }, - "customizations": { - "vscode": { - "settings": { - "editor.formatOnSave": true - }, - "extensions": [ - "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode", - "GitHub.copilot" - ] - } - } -} - -*** Add File: .vscode/tasks.json -{ - "version": "2.0.0", - "tasks": [ - { - "label": "dev", - "type": "shell", - "command": "npm run dev -p 3000", - "isBackground": true, - "problemMatcher": [] - } - ] -} - -*** Add File: .env.example -# Copy this file to .env.local and fill in your own secret values. -OPENAI_API_KEY= -STRIPE_WEBHOOK_SECRET= -GITHUB_WEBHOOK_SECRET= -GITHUB_APP_INSTALLATION_TOKEN= -X148_ALIAS=148-Amon - -*** Add File: README.md -# YONI Next.js Setup - -This repository contains the YONI web and API components. Follow these instructions to get up and running. - -## Local Setup - -1. Ensure Node.js and npm are installed. -2. Install dependencies: - ```bash - npm install - ``` -3. Copy `.env.example` to `.env.local` and fill in environment variables. -4. Start development server: - ```bash - npm run dev -p 3000 - ``` - -## Codespaces Setup - -A Dev Container configuration is provided via the `.devcontainer` directory. - -1. Create a Codespace on the `main` branch. -2. Codespaces will automatically install dependencies and forward port 3000. -3. Expose port 3000 and access the application at the forwarded URL. - -The `.vscode` folder contains recommended tasks for running the development server. -*** End Patch diff --git a/yoni-app-prototype.zip b/yoni-app-prototype.zip deleted file mode 100644 index 624aec0e6dcef8bab11a72ed3ec08063b3c3383e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18976 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 diff --git a/yoni-ci-pack.zip b/yoni-ci-pack.zip deleted file mode 100644 index b0edb690d71680f8361af81bc28a348194bf3a4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1407 zcmWIWW@Zs#U|`^2@SL0-9oX$H{+5w}p^b%sK@BLXm!4UYQJSP*o?n!mmXlvztY4I0 zT9T@pQd*FcnVeXXTC7)@o6{Pcn19Pa;P1Kh6-jfsG8A8J@n1MQYVGWh%vlq@ZFHLK zsdwbejdkbUGFt!JsVhhYy!fR0aqsT(_jAgl)@U5Qa&guccfmE2c7AC2{Ze@j=e!;1 zCD)j90$y!j{M_NqYh#0Qr{|)DZR%@UuFNpajEQZj_BMVll{;hdjoym+Zi1Qz3(y~J&`xh|6TR{&$7Z<|68nX()G?g zt6m(rzr0uQ*u4MD`S*@Y&!4WZKegXIz3`NF^S`ON?&WW-s~j_Byj;xP+n%kry?eIo zFDKXMszyQW*%Ov=mfdLT{rpjMOO0F55qEbu$`+W6J@6f(z#?G;sv&MCobMmx_ z#m7=_IxpYLZsT(C$9ygxi+6|Xo(7!Qq!PZq>hXq$J6<#GlZtfb6Yi{E*y5 znfXx*gSIWdHTj>(^f1G$AM61r3Fd>B@J2acf>{H^Lcj!5mRgjYniHRzR~BDfnU}0r zoH4cE*Xyu@%+c?bM_)%ciZA8RI}%i&b&!wO)8mo&Ql8w;{IWeWCwOWlRkyv1t>iiX zm~~InWvxrS+}rnmKWd#(Dji&!zUoEL#v?gdVtosxWV1}XLW9ggcNQOByrW*EW!?GK zCkrPif0rv1>bx*@`?~}Q7G=rZ($jBY3v=+W@u%fmVegZ<-)jt zMAZdrLITqh+S?+VuS$kR1eq?ExxM?c$=#0~k*ob?SFSB-yvkL2^}>JYjFm4k_5F7K z%v<==skwH~zS@2H4d33)XSAL6wV3~z<5bn9w~c2O1;tL-Y(8Q3{X_bwp}G9Tgy=WG zP>cnJCO?o4a&>g^b&YrSj1LIX%S~zRcg$`!5IO$-rwG3dUoe|>j}w2U_^kyiGH#VF zKayqTyjZhy+w1M~^F+9;-+Ze0Q1@T{z5Ic$vk!AEq+_SW7?Vr$SFL z_?89S*~O(8biJ<6X~K`-?`I2U#ICiL?cMup=Ilv{wdR#I*}ZcvobGp7@lNTF#=(6P zIanEU_BsWfa(*fO<#fZvyPOIQi_7-4$8G0Yxqfr0?Gi~}4q=sKrA6}GJsxfMKS4)WN7JG+`B3C6j(Kh-l`P-S>#uJ*(j(Gc$nBYJ zZL*iYUfb{N1@i!JMkWzv+$9q*biiOqBZwldoI*DfJ@GJf%FVu5zD{;0A^`ZE&u=k diff --git a/yoni-mothership-bridge.zip b/yoni-mothership-bridge.zip deleted file mode 100644 index 58f5a94a7b413788670512a266082eded6a2e93b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4913 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 JlH~W-e*yJW#vlLy From 15dbac7dc1fb8f7da9bdaf4323e6a90d6d7d07e8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 13 Nov 2025 02:22:04 +0000 Subject: [PATCH 100/133] Initial plan From 84942ee05cce6600364fb280e8a9c49b31a3caea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 13 Nov 2025 02:58:27 +0000 Subject: [PATCH 101/133] Initial plan From 3a0204451f9bc57eb55b25dce78a76ecfee1e8dd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 13 Nov 2025 03:04:47 +0000 Subject: [PATCH 102/133] Migrate Stripe webhook to app/api and upgrade to API version 2024-06-20 Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- app/api/checkout/route.ts | 4 +--- {api => app/api}/stripe/webhook/route.ts | 25 ++++++------------------ package-lock.json | 8 ++++---- package.json | 2 +- 4 files changed, 12 insertions(+), 27 deletions(-) rename {api => app/api}/stripe/webhook/route.ts (86%) diff --git a/app/api/checkout/route.ts b/app/api/checkout/route.ts index cea9cc6..5516b1a 100644 --- a/app/api/checkout/route.ts +++ b/app/api/checkout/route.ts @@ -4,9 +4,7 @@ import Stripe from 'stripe'; export const runtime = 'nodejs'; const stripeSecretKey = process.env.STRIPE_SECRET_KEY!; -const stripe = new Stripe(stripeSecretKey, { - apiVersion: "2023-10-16", -}); +const stripe = new Stripe(stripeSecretKey, { apiVersion: '2024-06-20' }); export async function POST(req: NextRequest) { try { diff --git a/api/stripe/webhook/route.ts b/app/api/stripe/webhook/route.ts similarity index 86% rename from api/stripe/webhook/route.ts rename to app/api/stripe/webhook/route.ts index eac731a..fc1a710 100644 --- a/api/stripe/webhook/route.ts +++ b/app/api/stripe/webhook/route.ts @@ -1,47 +1,37 @@ import { NextRequest, NextResponse } from 'next/server'; import Stripe from 'stripe'; -export const runtime = 'nodejs'; +export const runtime = 'nodejs'; export const dynamic = 'force-dynamic'; const stripeSecretKey = process.env.STRIPE_SECRET_KEY!; -const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!; +const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!; -const stripe = new Stripe(stripeSecretKey, { - apiVersion: "2023-10-16", -}); +const stripe = new Stripe(stripeSecretKey, { apiVersion: '2024-06-20' }); -// RAW-BODY auslesen async function getRawBody(req: NextRequest): Promise { const chunks: Uint8Array[] = []; const reader = req.body?.getReader(); - if (!reader) return Buffer.from(''); - while (true) { const { done, value } = await reader.read(); if (done) break; if (value) chunks.push(value); } - return Buffer.concat(chunks.map((c) => Buffer.from(c))); } export async function POST(req: NextRequest) { let event: Stripe.Event; - try { const sig = req.headers.get('stripe-signature'); if (!sig) return new NextResponse('Missing Stripe-Signature header', { status: 400 }); - const rawBody = await getRawBody(req); - event = stripe.webhooks.constructEvent(rawBody, sig, webhookSecret); } catch (err: any) { console.error('❌ Webhook verification failed:', err); return new NextResponse(`Webhook Error: ${err.message}`, { status: 400 }); } - try { switch (event.type) { case 'checkout.session.completed': { @@ -49,19 +39,16 @@ export async function POST(req: NextRequest) { console.log('✅ Checkout completed:', session.id); break; } - case 'payment_intent.succeeded': { const intent = event.data.object as Stripe.PaymentIntent; console.log('💰 Payment succeeded:', intent.id); break; } - - default: + default: { console.log(`ℹ️ Unhandled event: ${event.type}`); + } } - return new NextResponse('Webhook received', { status: 200 }); - } catch (error) { console.error('❌ Error handling webhook:', error); return new NextResponse('Internal Server Error', { status: 500 }); @@ -69,5 +56,5 @@ export async function POST(req: NextRequest) { } export async function GET() { - return new NextResponse("Webhook endpoint active", { status: 200 }); + return new NextResponse('Webhook endpoint active', { status: 200 }); } diff --git a/package-lock.json b/package-lock.json index 744049c..59cc2d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "openai": "^6.8.1", "react": "^18.3.1", "react-dom": "^18.3.1", - "stripe": "^14.25.0" + "stripe": "^16.12.0" }, "devDependencies": { "@types/node": "^20.17.6", @@ -5529,9 +5529,9 @@ } }, "node_modules/stripe": { - "version": "14.25.0", - "resolved": "https://registry.npmjs.org/stripe/-/stripe-14.25.0.tgz", - "integrity": "sha512-wQS3GNMofCXwH8TSje8E1SE8zr6ODiGtHQgPtO95p9Mb4FhKC9jvXR2NUTpZ9ZINlckJcFidCmaTFV4P6vsb9g==", + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-16.12.0.tgz", + "integrity": "sha512-H7eFVLDxeTNNSn4JTRfL2//LzCbDrMSZ+2q1c7CanVWgK2qIW5TwS+0V7N9KcKZZNpYh/uCqK0PyZh/2UsaAtQ==", "license": "MIT", "dependencies": { "@types/node": ">=8.1.0", diff --git a/package.json b/package.json index ff29e0f..fb28453 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "openai": "^6.8.1", "react": "^18.3.1", "react-dom": "^18.3.1", - "stripe": "^14.25.0" + "stripe": "^16.12.0" }, "devDependencies": { "@types/node": "^20.17.6", From 0d99d296411a213f8072ca06aa09ce8d2be33597 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 13 Nov 2025 03:05:49 +0000 Subject: [PATCH 103/133] Initial plan From 07db91eba7fe663a52418c68a69b9032969396f9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 13 Nov 2025 03:10:24 +0000 Subject: [PATCH 104/133] Add Vercel domain configuration script and documentation Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- DEPLOYMENT.md | 104 ++++++++++++++++++++++++++------- README.md | 9 ++- package.json | 4 +- scripts/README.md | 113 +++++++++++++++++++++++++++++++++++ scripts/setup-domains.sh | 123 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 329 insertions(+), 24 deletions(-) create mode 100644 scripts/README.md create mode 100755 scripts/setup-domains.sh diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index c157d0e..5cd6d35 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -159,50 +159,110 @@ Nach dem Deployment ist die App automatisch verfügbar unter: - `https://yoni-app.vercel.app` (automatisch generiert) - `https://yoni-app-pihoch2.vercel.app` (mit Organization-Name) -### Custom Domain hinzufügen +### Custom Domains für pihoch2.me -#### 1. Vercel-Subdomain registrieren +YONI nutzt folgende Custom Domains: +- `pihoch2.me` - Hauptdomain +- `www.pihoch2.me` - WWW-Subdomain +- `app.pihoch2.me` - App-Subdomain +- `api.pihoch2.me` - API-Subdomain + +#### Methode 1: Automatisiertes Setup-Script (Empfohlen) + +**Voraussetzungen:** +- Vercel CLI installiert: `npm i -g vercel` +- Bei Vercel angemeldet: `vercel login` + +**Domains hinzufügen:** ```bash -vercel domains add yoni-app-pihoch2.vercel.app +# Alle Domains auf einmal hinzufügen +./scripts/setup-domains.sh + +# Oder erst einen Dry-Run machen +./scripts/setup-domains.sh --dry-run ``` -#### 2. Eigene Domain konfigurieren (yoni.pihoch2.me) +Das Script fügt automatisch alle konfigurierten Domains hinzu: +- `pihoch2.me` +- `www.pihoch2.me` +- `app.pihoch2.me` +- `api.pihoch2.me` + +#### Methode 2: Manuelle Konfiguration via CLI -**Schritt 1: Domain zu Vercel hinzufügen** +**Domains einzeln hinzufügen:** ```bash -vercel domains add yoni.pihoch2.me +vercel domains add pihoch2.me +vercel domains add www.pihoch2.me +vercel domains add app.pihoch2.me +vercel domains add api.pihoch2.me ``` -**Schritt 2: DNS konfigurieren** +#### Methode 3: Vercel Dashboard + +1. Gehe zu [vercel.com/dashboard](https://vercel.com/dashboard) +2. Wähle dein Projekt +3. Settings → Domains +4. Klicke "Add Domain" +5. Gib die Domain ein (z.B. `pihoch2.me`) +6. Wiederhole für alle weiteren Domains -Vercel gibt dir die benötigten DNS-Records. Gehe zu deinem DNS-Provider (z.B. Cloudflare, namecheap, etc.) und füge hinzu: +### DNS-Konfiguration +Nach dem Hinzufügen der Domains zu Vercel, konfiguriere die DNS-Records bei deinem DNS-Provider: + +#### Root Domain (pihoch2.me) + +``` +Type: A +Name: @ +Value: 76.76.21.21 +``` + +**Alternative (CNAME flattening):** ``` Type: CNAME -Name: yoni +Name: @ Value: cname.vercel-dns.com ``` -**Schritt 3: Warten auf DNS-Propagation** +#### Subdomains -- DNS-Änderungen können bis zu 48 Stunden dauern -- Typischerweise: 5-30 Minuten -- Überprüfen: `nslookup yoni.pihoch2.me` +``` +Type: CNAME +Name: www +Value: cname.vercel-dns.com -**Schritt 4: SSL-Zertifikat** +Type: CNAME +Name: app +Value: cname.vercel-dns.com -Vercel generiert automatisch ein kostenloses SSL-Zertifikat via Let's Encrypt. +Type: CNAME +Name: api +Value: cname.vercel-dns.com +``` -#### Alternative: Domain via Vercel Dashboard +### DNS-Propagation überprüfen -1. Gehe zu [vercel.com/dashboard](https://vercel.com/dashboard) -2. Wähle dein Projekt -3. Settings → Domains -4. Klicke "Add Domain" -5. Gib `yoni.pihoch2.me` ein -6. Folge den DNS-Anweisungen +```bash +# Root Domain +nslookup pihoch2.me + +# Subdomains +nslookup www.pihoch2.me +nslookup app.pihoch2.me +nslookup api.pihoch2.me +``` + +**Zeitrahmen:** +- DNS-Änderungen können bis zu 48 Stunden dauern +- Typischerweise: 5-30 Minuten + +### SSL-Zertifikate + +Vercel generiert automatisch kostenlose SSL-Zertifikate via Let's Encrypt für alle konfigurierten Domains. --- diff --git a/README.md b/README.md index 36cb657..4668163 100644 --- a/README.md +++ b/README.md @@ -71,9 +71,16 @@ Siehe [YONI_Local_Run_Guide.md](YONI_Local_Run_Guide.md) für Details. Siehe **[DEPLOYMENT.md](DEPLOYMENT.md)** für vollständige Deployment-Anleitung: - 🤖 Automatisches Deployment via GitHub Actions - 💻 Manuelles Deployment via Vercel CLI -- 🌐 Domain-Konfiguration +- 🌐 Domain-Konfiguration (pihoch2.me, www, app, api) - 🔒 Umgebungsvariablen +**Quick Domain Setup:** +```bash +# Domain-Setup-Script ausführen +npm run domains:setup:dry-run # Vorschau +npm run domains:setup # Domains hinzufügen +``` + --- ## 💜 Contributing diff --git a/package.json b/package.json index ff29e0f..96589b2 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,9 @@ "lint": "next lint", "fix:content": "node project-ops/launch/fix-content.js", "validate:notion": "node project-ops/launch/validate-notion-template.js", - "test:notion": "node project-ops/launch/test-validate.js" + "test:notion": "node project-ops/launch/test-validate.js", + "domains:setup": "bash scripts/setup-domains.sh", + "domains:setup:dry-run": "bash scripts/setup-domains.sh --dry-run" }, "dependencies": { "next": "^14.2.15", diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..2b7e8f9 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,113 @@ +# Scripts Directory + +This directory contains utility scripts for the YONI app. + +## Available Scripts + +### Domain Management + +#### `setup-domains.sh` + +Automatically configures custom domains for the YONI app on Vercel. + +**Configured Domains:** +- `pihoch2.me` - Root domain +- `www.pihoch2.me` - WWW subdomain +- `app.pihoch2.me` - App subdomain +- `api.pihoch2.me` - API subdomain + +**Prerequisites:** +- Vercel CLI installed: `npm i -g vercel` +- Authenticated with Vercel: `vercel login` + +**Usage:** + +```bash +# Dry run (shows what would be done without making changes) +./scripts/setup-domains.sh --dry-run + +# Or via npm +npm run domains:setup:dry-run + +# Add domains to Vercel +./scripts/setup-domains.sh + +# Or via npm +npm run domains:setup +``` + +**Help:** + +```bash +./scripts/setup-domains.sh --help +``` + +**After running the script:** + +1. Configure DNS records with your DNS provider +2. Point each domain to: `cname.vercel-dns.com` +3. Wait for DNS propagation (5-30 minutes) +4. Verify with: `nslookup ` + +See `DEPLOYMENT.md` for detailed DNS configuration instructions. + +--- + +### Notion Validation + +#### `validate-notion.js` + +Validates Notion template configuration. + +**Usage:** + +```bash +node scripts/validate-notion.js + +# Or via npm +npm run validate:notion +``` + +--- + +## Adding New Scripts + +When adding new scripts to this directory: + +1. Make scripts executable: `chmod +x scripts/your-script.sh` +2. Add appropriate documentation to this README +3. Consider adding npm script shortcuts in `package.json` +4. Follow existing naming conventions +5. Include help/usage information in the script + +--- + +## Troubleshooting + +### "Vercel CLI is not installed" + +Install the Vercel CLI globally: + +```bash +npm i -g vercel +``` + +### "Not logged in to Vercel" + +Login to Vercel: + +```bash +vercel login +``` + +### Script Permission Denied + +Make the script executable: + +```bash +chmod +x scripts/setup-domains.sh +``` + +--- + +> _„Im Dunkel des Alls glitzert jeder Mensch als eigene Galaxie."_ ✨ diff --git a/scripts/setup-domains.sh b/scripts/setup-domains.sh new file mode 100755 index 0000000..f67a0cd --- /dev/null +++ b/scripts/setup-domains.sh @@ -0,0 +1,123 @@ +#!/bin/bash +# YONI App - Vercel Domain Setup Script +# This script adds custom domains to the Vercel project +# Usage: ./scripts/setup-domains.sh [--dry-run] + +set -e + +# Color codes for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Domains to add +DOMAINS=( + "pihoch2.me" + "www.pihoch2.me" + "app.pihoch2.me" + "api.pihoch2.me" +) + +DRY_RUN=false + +# Parse arguments +for arg in "$@"; do + case $arg in + --dry-run) + DRY_RUN=true + shift + ;; + --help) + echo "Usage: $0 [--dry-run]" + echo "" + echo "Options:" + echo " --dry-run Show what would be done without making changes" + echo " --help Show this help message" + exit 0 + ;; + esac +done + +echo -e "${BLUE}╔═══════════════════════════════════════════════════╗${NC}" +echo -e "${BLUE}║ YONI App - Vercel Domain Setup Script ║${NC}" +echo -e "${BLUE}╚═══════════════════════════════════════════════════╝${NC}" +echo "" + +# Check if vercel CLI is installed +if ! command -v vercel &> /dev/null; then + echo -e "${RED}❌ Error: Vercel CLI is not installed${NC}" + echo -e "${YELLOW}Please install it with: npm i -g vercel${NC}" + exit 1 +fi + +# Check if user is logged in to Vercel +if ! vercel whoami &> /dev/null; then + echo -e "${RED}❌ Error: Not logged in to Vercel${NC}" + echo -e "${YELLOW}Please login with: vercel login${NC}" + exit 1 +fi + +echo -e "${GREEN}✓ Vercel CLI is installed and authenticated${NC}" +echo "" + +if [ "$DRY_RUN" = true ]; then + echo -e "${YELLOW}🔍 DRY RUN MODE - No changes will be made${NC}" + echo "" +fi + +echo -e "${BLUE}Domains to be added:${NC}" +for domain in "${DOMAINS[@]}"; do + echo -e " • $domain" +done +echo "" + +if [ "$DRY_RUN" = true ]; then + echo -e "${YELLOW}Would execute the following commands:${NC}" + for domain in "${DOMAINS[@]}"; do + echo -e "${YELLOW} vercel domains add $domain${NC}" + done + echo "" + echo -e "${GREEN}✓ Dry run complete${NC}" + exit 0 +fi + +# Confirm before proceeding +read -p "Do you want to proceed? (y/N): " -n 1 -r +echo "" +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo -e "${YELLOW}Aborted by user${NC}" + exit 0 +fi + +echo "" +echo -e "${BLUE}Adding domains to Vercel...${NC}" +echo "" + +# Add each domain +for domain in "${DOMAINS[@]}"; do + echo -e "${BLUE}Adding domain: $domain${NC}" + + if vercel domains add "$domain"; then + echo -e "${GREEN}✓ Successfully added: $domain${NC}" + else + # Domain might already exist, check status + echo -e "${YELLOW}⚠ Could not add $domain (might already exist)${NC}" + fi + echo "" +done + +echo -e "${GREEN}╔═══════════════════════════════════════════════════╗${NC}" +echo -e "${GREEN}║ Setup Complete! ║${NC}" +echo -e "${GREEN}╚═══════════════════════════════════════════════════╝${NC}" +echo "" +echo -e "${BLUE}Next steps:${NC}" +echo -e "1. Configure DNS records with your DNS provider" +echo -e "2. Point each domain to: ${YELLOW}cname.vercel-dns.com${NC}" +echo -e "3. Wait for DNS propagation (5-30 minutes)" +echo -e "4. Verify with: ${YELLOW}nslookup ${NC}" +echo "" +echo -e "${BLUE}For detailed DNS configuration, see:${NC}" +echo -e " ${YELLOW}DEPLOYMENT.md - Domain Configuration section${NC}" +echo "" From 9316c5bd56788c3cab3b5c9792da3240c0261844 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 13 Nov 2025 03:12:50 +0000 Subject: [PATCH 105/133] Initial plan From 7a3f887522a80fddf1142dd99fdc44913d7743a6 Mon Sep 17 00:00:00 2001 From: YONI AutoBot Date: Fri, 14 Nov 2025 00:52:37 +0000 Subject: [PATCH 106/133] =?UTF-8?q?=F0=9F=95=93=20Daily=20Snapshot=202025-?= =?UTF-8?q?11-14?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Reports/Deploy-Status-2025-11-14.md | 28 +++++++++++++++++++ Transzendenz/Reports/snapshot-log.txt | 1 + 2 files changed, 29 insertions(+) create mode 100644 Transzendenz/Reports/Deploy-Status-2025-11-14.md diff --git a/Transzendenz/Reports/Deploy-Status-2025-11-14.md b/Transzendenz/Reports/Deploy-Status-2025-11-14.md new file mode 100644 index 0000000..c93e1fb --- /dev/null +++ b/Transzendenz/Reports/Deploy-Status-2025-11-14.md @@ -0,0 +1,28 @@ +# 📦 YONI Deploy Status + +**Last Updated:** 2025-11-05 + +## Deployment Tasks + +| Task | Status | Description | +|------|--------|-------------| +| 1. Repository Setup | ✅ Complete | Initial repository structure created | +| 2. CI Workflow Configuration | ✅ Complete | GitHub Actions CI pipeline configured | +| 3. Build Process | ✅ Complete | Build scripts and dependencies set up | +| 4. Test Infrastructure | ✅ Complete | Testing framework configured | +| 5. Deploy Snapshot Workflow | ✅ Complete | Daily snapshot workflow implemented | +| 6. Email Notifications | 🔄 In Progress | Email notification system configuration | +| 7. Production Deployment | 🔄 In Progress | Production environment setup | +| 8. Monitoring & Logging | 🔄 In Progress | Monitoring and logging infrastructure | + +## Summary + +- **Total Tasks:** 8 +- **Completed:** 5 +- **In Progress:** 3 +- **Blocked:** 0 + +## Notes + +This status file is used by the deploy_snapshot.yml workflow to track deployment progress. +A snapshot of this file is created daily with the current date. diff --git a/Transzendenz/Reports/snapshot-log.txt b/Transzendenz/Reports/snapshot-log.txt index 4f8e974..6da5aa5 100644 --- a/Transzendenz/Reports/snapshot-log.txt +++ b/Transzendenz/Reports/snapshot-log.txt @@ -7,3 +7,4 @@ This file tracks all snapshot events from the deploy_snapshot.yml workflow. ✅ Snapshot log initialized on 2025-11-05 ✅ Snapshot for 2025-11-12 created at 2025-11-12 00:52:49 ✅ Snapshot for 2025-11-13 created at 2025-11-13 00:52:36 +✅ Snapshot for 2025-11-14 created at 2025-11-14 00:52:37 From 72409082340c10bc28c5651c94cd88dd8d2f1157 Mon Sep 17 00:00:00 2001 From: YONI AutoBot Date: Sat, 15 Nov 2025 00:51:33 +0000 Subject: [PATCH 107/133] =?UTF-8?q?=F0=9F=95=93=20Daily=20Snapshot=202025-?= =?UTF-8?q?11-15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Reports/Deploy-Status-2025-11-15.md | 28 +++++++++++++++++++ Transzendenz/Reports/snapshot-log.txt | 1 + 2 files changed, 29 insertions(+) create mode 100644 Transzendenz/Reports/Deploy-Status-2025-11-15.md diff --git a/Transzendenz/Reports/Deploy-Status-2025-11-15.md b/Transzendenz/Reports/Deploy-Status-2025-11-15.md new file mode 100644 index 0000000..c93e1fb --- /dev/null +++ b/Transzendenz/Reports/Deploy-Status-2025-11-15.md @@ -0,0 +1,28 @@ +# 📦 YONI Deploy Status + +**Last Updated:** 2025-11-05 + +## Deployment Tasks + +| Task | Status | Description | +|------|--------|-------------| +| 1. Repository Setup | ✅ Complete | Initial repository structure created | +| 2. CI Workflow Configuration | ✅ Complete | GitHub Actions CI pipeline configured | +| 3. Build Process | ✅ Complete | Build scripts and dependencies set up | +| 4. Test Infrastructure | ✅ Complete | Testing framework configured | +| 5. Deploy Snapshot Workflow | ✅ Complete | Daily snapshot workflow implemented | +| 6. Email Notifications | 🔄 In Progress | Email notification system configuration | +| 7. Production Deployment | 🔄 In Progress | Production environment setup | +| 8. Monitoring & Logging | 🔄 In Progress | Monitoring and logging infrastructure | + +## Summary + +- **Total Tasks:** 8 +- **Completed:** 5 +- **In Progress:** 3 +- **Blocked:** 0 + +## Notes + +This status file is used by the deploy_snapshot.yml workflow to track deployment progress. +A snapshot of this file is created daily with the current date. diff --git a/Transzendenz/Reports/snapshot-log.txt b/Transzendenz/Reports/snapshot-log.txt index 6da5aa5..a960224 100644 --- a/Transzendenz/Reports/snapshot-log.txt +++ b/Transzendenz/Reports/snapshot-log.txt @@ -8,3 +8,4 @@ This file tracks all snapshot events from the deploy_snapshot.yml workflow. ✅ Snapshot for 2025-11-12 created at 2025-11-12 00:52:49 ✅ Snapshot for 2025-11-13 created at 2025-11-13 00:52:36 ✅ Snapshot for 2025-11-14 created at 2025-11-14 00:52:37 +✅ Snapshot for 2025-11-15 created at 2025-11-15 00:51:33 From 3b04a88b0bec5e137124882241cc5d031a9f5f08 Mon Sep 17 00:00:00 2001 From: YONI AutoBot Date: Sun, 16 Nov 2025 00:56:17 +0000 Subject: [PATCH 108/133] =?UTF-8?q?=F0=9F=95=93=20Daily=20Snapshot=202025-?= =?UTF-8?q?11-16?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Reports/Deploy-Status-2025-11-16.md | 28 +++++++++++++++++++ Transzendenz/Reports/snapshot-log.txt | 1 + 2 files changed, 29 insertions(+) create mode 100644 Transzendenz/Reports/Deploy-Status-2025-11-16.md diff --git a/Transzendenz/Reports/Deploy-Status-2025-11-16.md b/Transzendenz/Reports/Deploy-Status-2025-11-16.md new file mode 100644 index 0000000..c93e1fb --- /dev/null +++ b/Transzendenz/Reports/Deploy-Status-2025-11-16.md @@ -0,0 +1,28 @@ +# 📦 YONI Deploy Status + +**Last Updated:** 2025-11-05 + +## Deployment Tasks + +| Task | Status | Description | +|------|--------|-------------| +| 1. Repository Setup | ✅ Complete | Initial repository structure created | +| 2. CI Workflow Configuration | ✅ Complete | GitHub Actions CI pipeline configured | +| 3. Build Process | ✅ Complete | Build scripts and dependencies set up | +| 4. Test Infrastructure | ✅ Complete | Testing framework configured | +| 5. Deploy Snapshot Workflow | ✅ Complete | Daily snapshot workflow implemented | +| 6. Email Notifications | 🔄 In Progress | Email notification system configuration | +| 7. Production Deployment | 🔄 In Progress | Production environment setup | +| 8. Monitoring & Logging | 🔄 In Progress | Monitoring and logging infrastructure | + +## Summary + +- **Total Tasks:** 8 +- **Completed:** 5 +- **In Progress:** 3 +- **Blocked:** 0 + +## Notes + +This status file is used by the deploy_snapshot.yml workflow to track deployment progress. +A snapshot of this file is created daily with the current date. diff --git a/Transzendenz/Reports/snapshot-log.txt b/Transzendenz/Reports/snapshot-log.txt index a960224..96d995e 100644 --- a/Transzendenz/Reports/snapshot-log.txt +++ b/Transzendenz/Reports/snapshot-log.txt @@ -9,3 +9,4 @@ This file tracks all snapshot events from the deploy_snapshot.yml workflow. ✅ Snapshot for 2025-11-13 created at 2025-11-13 00:52:36 ✅ Snapshot for 2025-11-14 created at 2025-11-14 00:52:37 ✅ Snapshot for 2025-11-15 created at 2025-11-15 00:51:33 +✅ Snapshot for 2025-11-16 created at 2025-11-16 00:56:17 From 374abed9cb1476f5e0e60aab8a868265b4a2b8f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 16 Nov 2025 17:56:06 +0000 Subject: [PATCH 109/133] Initial plan From 6f7fcee05f26206fffef215af0924ee9edf8634e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 16 Nov 2025 18:03:35 +0000 Subject: [PATCH 110/133] feat: Add static-first + ISR configuration with revalidate endpoint Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- .env.example | 5 ++ STATIC_ISR_CONFIG.md | 112 ++++++++++++++++++++++++++++++++++++ app/api/revalidate/route.ts | 63 ++++++++++++++++++++ app/layout.tsx | 3 + vercel.json | 8 ++- 5 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 STATIC_ISR_CONFIG.md create mode 100644 app/api/revalidate/route.ts diff --git a/.env.example b/.env.example index d0f9128..124da37 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,8 @@ # OpenAI API Configuration # Get your API key from: https://platform.openai.com/api-keys OPENAI_API_KEY=your-openai-api-key-here + +# Revalidation Secret +# Used to secure the /api/revalidate endpoint +# Generate a random string for production +REVALIDATE_SECRET=your-secret-key-here diff --git a/STATIC_ISR_CONFIG.md b/STATIC_ISR_CONFIG.md new file mode 100644 index 0000000..4ea3db2 --- /dev/null +++ b/STATIC_ISR_CONFIG.md @@ -0,0 +1,112 @@ +# Static-First + ISR Configuration + +This document explains the static-first and ISR (Incremental Static Regeneration) configuration applied to the YONI app. + +## Changes Made + +### 1. vercel.json Configuration + +Updated `vercel.json` to include static export configuration: + +```json +{ + "version": 2, + "build": { + "env": {} + }, + "output": "export", + "routes": [] +} +``` + +**Note**: The `"output": "export"` setting in `vercel.json` is kept as requested, but **NOT** applied in `next.config.js` because it's incompatible with API routes. The app uses several API routes (`/api/chat`, `/api/checkout`, `/api/github-app/callback`, `/api/revalidate`) which require server-side functionality. + +### 2. ISR Configuration (Layout) + +Added ISR with 10-minute revalidation to the root layout (`app/layout.tsx`): + +```typescript +export const revalidate = 600 // 10 minutes +``` + +This enables Incremental Static Regeneration for all pages, allowing them to be regenerated every 10 minutes. + +### 3. On-Demand Revalidation API + +Created `/app/api/revalidate/route.ts` - a webhook endpoint for on-demand revalidation triggered by CMS or Stripe events. + +**Usage**: +```bash +POST /api/revalidate?secret=YOUR_SECRET +Content-Type: application/json + +{ + "path": "/some-path" +} +``` + +or + +```bash +POST /api/revalidate?secret=YOUR_SECRET +Content-Type: application/json + +{ + "tag": "some-tag" +} +``` + +**Security**: Requires `REVALIDATE_SECRET` environment variable to be set. + +## Configuration Rationale + +### Why NOT Full Static Export? + +While the problem statement requested `output: "export"`, this configuration is **incompatible** with: + +1. **API Routes**: The app has 4 API endpoints that require server-side execution +2. **ISR**: The `revalidate` configuration requires server-side regeneration +3. **On-Demand Revalidation**: The `/api/revalidate` endpoint needs server functionality + +### Current Architecture: Hybrid (Static-First + Server) + +The implemented solution uses: + +- **Static pre-rendering** for pages (○ symbol in build output) +- **ISR** with 10-minute revalidation for content updates +- **Server-side API routes** (ƒ symbol in build output) for: + - `/api/chat` - ChatGPT integration + - `/api/checkout` - Stripe checkout + - `/api/github-app/callback` - GitHub OAuth + - `/api/revalidate` - On-demand revalidation + +This is the recommended approach for Next.js App Router applications that need both static optimization and dynamic functionality. + +## Environment Variables Required + +To use the revalidation endpoint, add to your `.env` or Vercel environment variables: + +``` +REVALIDATE_SECRET=your-secret-key-here +``` + +## Build Output + +``` +Route (app) Size First Load JS +┌ ○ / 7.04 kB 94.3 kB +├ ○ /_not-found 873 B 88.2 kB +├ ƒ /api/chat 0 B 0 B +├ ƒ /api/checkout 0 B 0 B +├ ƒ /api/github-app/callback 0 B 0 B +└ ƒ /api/revalidate 0 B 0 B + +○ (Static) prerendered as static content +ƒ (Dynamic) server-rendered on demand +``` + +## Additional Notes + +- The `cache: 'no-store'` audit was performed - no instances found in the codebase +- Images are already configured with `unoptimized: true` for static compatibility +- The SvelteKit configuration mentioned in the problem statement does not apply to this Next.js project diff --git a/app/api/revalidate/route.ts b/app/api/revalidate/route.ts new file mode 100644 index 0000000..8275f1c --- /dev/null +++ b/app/api/revalidate/route.ts @@ -0,0 +1,63 @@ +import { NextRequest, NextResponse } from 'next/server' +import { revalidatePath, revalidateTag } from 'next/cache' + +/** + * On-demand revalidation endpoint for CMS/Stripe events + * + * Usage: + * POST /api/revalidate?secret=YOUR_SECRET + * Body: { path: '/some-path' } or { tag: 'some-tag' } + * + * Requires REVALIDATE_SECRET environment variable + */ +export async function POST(request: NextRequest) { + try { + // Verify secret token for security + const secret = request.nextUrl.searchParams.get('secret') + + if (secret !== process.env.REVALIDATE_SECRET) { + return NextResponse.json( + { error: 'Invalid secret' }, + { status: 401 } + ) + } + + const body = await request.json() + + // Revalidate by path + if (body.path) { + revalidatePath(body.path) + return NextResponse.json({ + revalidated: true, + type: 'path', + target: body.path, + now: Date.now() + }) + } + + // Revalidate by tag + if (body.tag) { + revalidateTag(body.tag) + return NextResponse.json({ + revalidated: true, + type: 'tag', + target: body.tag, + now: Date.now() + }) + } + + return NextResponse.json( + { error: 'Missing path or tag parameter' }, + { status: 400 } + ) + } catch (error: any) { + console.error('Revalidation error:', error) + return NextResponse.json( + { + error: 'Failed to revalidate', + details: error.message + }, + { status: 500 } + ) + } +} diff --git a/app/layout.tsx b/app/layout.tsx index deb9aaa..29b9000 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,9 @@ import './globals.css' import type { Metadata } from 'next' +// Enable ISR with 10-minute revalidation +export const revalidate = 600 + export const metadata: Metadata = { title: 'YONI • pi² Control (18+)', description: 'YONI: Creator‑KI, Auto‑Translate, Transzendenz‑Hub. Minimal Flat Build für Vercel. Für Nutzer:innen ab 18 Jahren.', diff --git a/vercel.json b/vercel.json index 9fb15c4..89cc597 100644 --- a/vercel.json +++ b/vercel.json @@ -1,4 +1,8 @@ { - "$schema": "https://openapi.vercel.sh/vercel.json", - "routes": [{ "src": "^/contact$", "dest": "/contact.html" }] + "version": 2, + "build": { + "env": {} + }, + "output": "export", + "routes": [] } From dfc0f19ff9e0e9cdb6df12df1f43ba5bd379ca76 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 16 Nov 2025 18:06:44 +0000 Subject: [PATCH 111/133] docs: Add implementation summary with visual guide Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- IMPLEMENTATION_SUMMARY.md | 182 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 IMPLEMENTATION_SUMMARY.md diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md new file mode 100644 index 0000000..e485f48 --- /dev/null +++ b/IMPLEMENTATION_SUMMARY.md @@ -0,0 +1,182 @@ +# 🎯 Implementation Summary: Static-First + ISR Setup + +## ✅ What Was Implemented + +### 1. 📋 vercel.json Configuration +```json +{ + "version": 2, + "build": { + "env": {} + }, + "output": "export", + "routes": [] +} +``` +**Status:** ✅ Complete +**Note:** Configuration added as requested. However, `output: 'export'` was not added to `next.config.js` to maintain compatibility with API routes. + +--- + +### 2. 🔄 ISR (Incremental Static Regeneration) +**File:** `app/layout.tsx` + +```typescript +// Enable ISR with 10-minute revalidation +export const revalidate = 600 +``` + +**Status:** ✅ Complete +**Effect:** Pages are statically generated and automatically revalidated every 10 minutes + +--- + +### 3. 🔗 On-Demand Revalidation Webhook +**File:** `app/api/revalidate/route.ts` + +```typescript +POST /api/revalidate?secret=YOUR_SECRET +Body: { "path": "/" } or { "tag": "posts" } +``` + +**Status:** ✅ Complete +**Features:** +- Path-based revalidation: `{ "path": "/some-path" }` +- Tag-based revalidation: `{ "tag": "some-tag" }` +- Secured with `REVALIDATE_SECRET` environment variable + +**Use Cases:** +- Triggered by CMS content updates +- Triggered by Stripe payment events +- Manual cache invalidation + +--- + +### 4. ⚙️ Environment Variables +**File:** `.env.example` + +```env +REVALIDATE_SECRET=your-secret-key-here +``` + +**Status:** ✅ Complete +**Purpose:** Secures the revalidation endpoint + +--- + +### 5. 🔍 Cache Audit +**Task:** Remove `cache: 'no-store'` where not essential + +**Status:** ✅ Complete +**Result:** No instances of `cache: 'no-store'` found in the codebase + +--- + +## 📊 Build Results + +### Page Generation + +| Route | Type | Size | Description | +|-------|------|------|-------------| +| `/` | ○ Static | 7.04 kB | Main page (pre-rendered) | +| `/_not-found` | ○ Static | 873 B | 404 page | +| `/api/chat` | ƒ Dynamic | 0 B | ChatGPT API | +| `/api/checkout` | ƒ Dynamic | 0 B | Stripe checkout | +| `/api/github-app/callback` | ƒ Dynamic | 0 B | GitHub OAuth | +| `/api/revalidate` | ƒ Dynamic | 0 B | Revalidation webhook | + +**Legend:** +- ○ = Static (pre-rendered at build time) +- ƒ = Dynamic (server-rendered on demand) + +--- + +## 🏗️ Architecture + +### Hybrid Approach (Static-First + Server) + +``` +┌─────────────────────────────────────────┐ +│ Static Pages (ISR) │ +│ - Pre-rendered at build time │ +│ - Cached for 10 minutes │ +│ - Revalidated in background │ +└─────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────┐ +│ API Routes (Dynamic) │ +│ - ChatGPT integration │ +│ - Stripe checkout │ +│ - GitHub OAuth callback │ +│ - On-demand revalidation │ +└─────────────────────────────────────────┘ +``` + +--- + +## 🔒 Security Status + +### CodeQL Analysis: ✅ PASSED +- **JavaScript/TypeScript:** 0 alerts +- **No vulnerabilities detected** + +### Security Features +- ✅ Revalidation endpoint secured with secret token +- ✅ Proper input validation in API routes +- ✅ No sensitive data exposure + +--- + +## 📝 Next Steps + +### To Deploy to Vercel: + +1. **Set Environment Variable:** + ```bash + vercel env add REVALIDATE_SECRET production + ``` + +2. **Deploy:** + ```bash + vercel --prod + ``` + +### To Trigger Revalidation: + +**From CMS Webhook:** +```bash +curl -X POST "https://your-app.vercel.app/api/revalidate?secret=YOUR_SECRET" \ + -H "Content-Type: application/json" \ + -d '{"path":"/"}' +``` + +**From Stripe Webhook:** +```bash +curl -X POST "https://your-app.vercel.app/api/revalidate?secret=YOUR_SECRET" \ + -H "Content-Type: application/json" \ + -d '{"tag":"pricing"}' +``` + +--- + +## 📚 Documentation + +- **Main Docs:** `STATIC_ISR_CONFIG.md` - Comprehensive configuration guide +- **Environment:** `.env.example` - Required environment variables +- **This File:** `IMPLEMENTATION_SUMMARY.md` - Quick reference + +--- + +## ✨ Benefits Achieved + +1. ⚡ **Performance:** Static pages load instantly +2. 🔄 **Freshness:** ISR ensures content updates every 10 minutes +3. 🎯 **Flexibility:** On-demand revalidation for immediate updates +4. 🛡️ **Security:** Secured webhook endpoint +5. 💰 **Cost:** Reduced server load with static generation + +--- + +**Implementation Date:** 2025-11-16 +**Status:** ✅ Complete and Production-Ready From d68f6a0761ba463c1e41ef0217be47a3603d5e1a Mon Sep 17 00:00:00 2001 From: YONI AutoBot Date: Mon, 17 Nov 2025 00:53:58 +0000 Subject: [PATCH 112/133] =?UTF-8?q?=F0=9F=95=93=20Daily=20Snapshot=202025-?= =?UTF-8?q?11-17?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Reports/Deploy-Status-2025-11-17.md | 28 +++++++++++++++++++ Transzendenz/Reports/snapshot-log.txt | 1 + 2 files changed, 29 insertions(+) create mode 100644 Transzendenz/Reports/Deploy-Status-2025-11-17.md diff --git a/Transzendenz/Reports/Deploy-Status-2025-11-17.md b/Transzendenz/Reports/Deploy-Status-2025-11-17.md new file mode 100644 index 0000000..c93e1fb --- /dev/null +++ b/Transzendenz/Reports/Deploy-Status-2025-11-17.md @@ -0,0 +1,28 @@ +# 📦 YONI Deploy Status + +**Last Updated:** 2025-11-05 + +## Deployment Tasks + +| Task | Status | Description | +|------|--------|-------------| +| 1. Repository Setup | ✅ Complete | Initial repository structure created | +| 2. CI Workflow Configuration | ✅ Complete | GitHub Actions CI pipeline configured | +| 3. Build Process | ✅ Complete | Build scripts and dependencies set up | +| 4. Test Infrastructure | ✅ Complete | Testing framework configured | +| 5. Deploy Snapshot Workflow | ✅ Complete | Daily snapshot workflow implemented | +| 6. Email Notifications | 🔄 In Progress | Email notification system configuration | +| 7. Production Deployment | 🔄 In Progress | Production environment setup | +| 8. Monitoring & Logging | 🔄 In Progress | Monitoring and logging infrastructure | + +## Summary + +- **Total Tasks:** 8 +- **Completed:** 5 +- **In Progress:** 3 +- **Blocked:** 0 + +## Notes + +This status file is used by the deploy_snapshot.yml workflow to track deployment progress. +A snapshot of this file is created daily with the current date. diff --git a/Transzendenz/Reports/snapshot-log.txt b/Transzendenz/Reports/snapshot-log.txt index 96d995e..b228792 100644 --- a/Transzendenz/Reports/snapshot-log.txt +++ b/Transzendenz/Reports/snapshot-log.txt @@ -10,3 +10,4 @@ This file tracks all snapshot events from the deploy_snapshot.yml workflow. ✅ Snapshot for 2025-11-14 created at 2025-11-14 00:52:37 ✅ Snapshot for 2025-11-15 created at 2025-11-15 00:51:33 ✅ Snapshot for 2025-11-16 created at 2025-11-16 00:56:17 +✅ Snapshot for 2025-11-17 created at 2025-11-17 00:53:58 From 9ca4d67670978634e1aedeefc4e5e7785e385f98 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 17 Nov 2025 20:28:42 +0000 Subject: [PATCH 113/133] Initial plan From b3b8641969835a69f88c5111511209645d431c71 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 17 Nov 2025 20:33:25 +0000 Subject: [PATCH 114/133] Add PWA support with manifest, service worker, and meta tags Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- app/layout.tsx | 32 +++++++++++++++++++++++++++----- public/icons/icon-192.png | Bin 0 -> 2026 bytes public/icons/icon-512.png | Bin 0 -> 5102 bytes public/manifest.json | 23 +++++++++++++++++++++++ public/service-worker.js | 26 ++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 public/icons/icon-192.png create mode 100644 public/icons/icon-512.png create mode 100644 public/manifest.json create mode 100644 public/service-worker.js diff --git a/app/layout.tsx b/app/layout.tsx index 29b9000..3d0fdf5 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,16 +1,25 @@ import './globals.css' -import type { Metadata } from 'next' +import type { Metadata, Viewport } from 'next' +import Script from 'next/script' // Enable ISR with 10-minute revalidation export const revalidate = 600 +export const viewport: Viewport = { + themeColor: '#8b5cf6', +} + export const metadata: Metadata = { title: 'YONI • pi² Control (18+)', description: 'YONI: Creator‑KI, Auto‑Translate, Transzendenz‑Hub. Minimal Flat Build für Vercel. Für Nutzer:innen ab 18 Jahren.', - themeColor: '#0a0a0a', - manifest: '/manifest.webmanifest', + manifest: '/manifest.json', icons: { - apple: '/icon-192.png', + apple: '/icons/icon-192.png', + }, + appleWebApp: { + capable: true, + statusBarStyle: 'black', + title: 'YONI', }, other: { 'age-rating': '18+', @@ -24,7 +33,20 @@ export default function RootLayout({ }) { return ( - {children} + + {children} + + ) } diff --git a/public/icons/icon-192.png b/public/icons/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..fe1a63ba1f7726f35e40ea9db78406095319e95f GIT binary patch literal 2026 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 bWXq`RMoe$mq0I@fCILUM{hsVSVQ2peFn+*7 literal 0 HcmV?d00001 diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..be7b374 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "YONI", + "short_name": "YONI", + "description": "YONI – Transzendenz-App im PIHOCH7 Modus.", + "start_url": "/", + "scope": "/", + "display": "standalone", + "orientation": "portrait", + "background_color": "#0b021f", + "theme_color": "#8b5cf6", + "icons": [ + { + "src": "/icons/icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/icons/icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/public/service-worker.js b/public/service-worker.js new file mode 100644 index 0000000..fb4933d --- /dev/null +++ b/public/service-worker.js @@ -0,0 +1,26 @@ +const CACHE_NAME = "yoni-cache-v1"; +const URLS_TO_CACHE = ["/", "/index.html", "/manifest.json"]; + +self.addEventListener("install", (event) => { + event.waitUntil( + caches.open(CACHE_NAME).then((cache) => cache.addAll(URLS_TO_CACHE)) + ); +}); + +self.addEventListener("activate", (event) => { + event.waitUntil( + caches.keys().then((keys) => + Promise.all( + keys + .filter((key) => key !== CACHE_NAME) + .map((key) => caches.delete(key)) + ) + ) + ); +}); + +self.addEventListener("fetch", (event) => { + event.respondWith( + caches.match(event.request).then((response) => response || fetch(event.request)) + ); +}); From 4d723a8ec509a747f9f2f7e53941d82d90a15390 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 17 Nov 2025 20:34:14 +0000 Subject: [PATCH 115/133] Remove old PWA files from root directory Co-authored-by: pappensex <233804448+pappensex@users.noreply.github.com> --- icon-192.png | Bin 2026 -> 0 bytes icon-512.png | Bin 5102 -> 0 bytes manifest.webmanifest | 13 ------------- sw.js | 17 ----------------- 4 files changed, 30 deletions(-) delete mode 100644 icon-192.png delete mode 100644 icon-512.png delete mode 100644 manifest.webmanifest delete mode 100644 sw.js diff --git a/icon-192.png b/icon-192.png deleted file mode 100644 index fe1a63ba1f7726f35e40ea9db78406095319e95f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2026 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 bWXq`RMoe$mq0I@fCILUM{hsVSVQ2peFn+*7 diff --git a/manifest.webmanifest b/manifest.webmanifest deleted file mode 100644 index 0fd1acd..0000000 --- a/manifest.webmanifest +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "YONI • pi² Control (18+)", - "short_name": "YONI", - "description": "YONI: Creator-KI, Auto-Translate, Transzendenz-Hub für Nutzer:innen ab 18 Jahren", - "display": "standalone", - "start_url": "/", - "background_color": "#0b0b10", - "theme_color": "#0b0b10", - "icons": [ - {"src": "/icon-192.png", "sizes":"192x192", "type":"image/png"}, - {"src": "/icon-512.png", "sizes":"512x512", "type":"image/png"} - ] -} \ No newline at end of file diff --git a/sw.js b/sw.js deleted file mode 100644 index 7709f35..0000000 --- a/sw.js +++ /dev/null @@ -1,17 +0,0 @@ -self.addEventListener('install', e=> self.skipWaiting()); -self.addEventListener('activate', e=> self.clients.claim()); -self.addEventListener('fetch', e=>{ - if(e.request.method!=='GET') return; - e.respondWith((async()=>{ - const cache = await caches.open('yoni-min'); - const cached = await cache.match(e.request); - if(cached) return cached; - try{ - const res = await fetch(e.request); - if(res && res.status===200){ cache.put(e.request, res.clone()); } - return res; - }catch(_){ - return cached || new Response('',{status:200}); - } - })()); -}); \ No newline at end of file From b1b4183fcb8aeee82fac281a78adbd6af7267d23 Mon Sep 17 00:00:00 2001 From: YONI Date: Mon, 17 Nov 2025 21:47:10 +0100 Subject: [PATCH 116/133] Add custom agent template markdown file Create a template for a custom agent configuration. --- .github/agents/my-agent.agent.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/agents/my-agent.agent.md diff --git a/.github/agents/my-agent.agent.md b/.github/agents/my-agent.agent.md new file mode 100644 index 0000000..39a8426 --- /dev/null +++ b/.github/agents/my-agent.agent.md @@ -0,0 +1,13 @@ +--- +# Fill in the fields below to create a basic custom agent for your repository. +# The Copilot CLI can be used for local testing: https://gh.io/customagents/cli +# To make this agent available, merge this file into the default repository branch. +# For format details, see: https://gh.io/customagents/config + +name: +description: +--- + +# My Agent + +Describe what your agent does here... From 961678400d45f710212292a7d01bceb96b70ab18 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 17 Nov 2025 21:01:37 +0000 Subject: [PATCH 117/133] Initial plan From a6ab7a0018946edd46b9de10e99f43b2fc9206fe Mon Sep 17 00:00:00 2001 From: YONI AutoBot Date: Tue, 18 Nov 2025 00:52:11 +0000 Subject: [PATCH 118/133] =?UTF-8?q?=F0=9F=95=93=20Daily=20Snapshot=202025-?= =?UTF-8?q?11-18?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Reports/Deploy-Status-2025-11-18.md | 28 +++++++++++++++++++ Transzendenz/Reports/snapshot-log.txt | 1 + 2 files changed, 29 insertions(+) create mode 100644 Transzendenz/Reports/Deploy-Status-2025-11-18.md diff --git a/Transzendenz/Reports/Deploy-Status-2025-11-18.md b/Transzendenz/Reports/Deploy-Status-2025-11-18.md new file mode 100644 index 0000000..c93e1fb --- /dev/null +++ b/Transzendenz/Reports/Deploy-Status-2025-11-18.md @@ -0,0 +1,28 @@ +# 📦 YONI Deploy Status + +**Last Updated:** 2025-11-05 + +## Deployment Tasks + +| Task | Status | Description | +|------|--------|-------------| +| 1. Repository Setup | ✅ Complete | Initial repository structure created | +| 2. CI Workflow Configuration | ✅ Complete | GitHub Actions CI pipeline configured | +| 3. Build Process | ✅ Complete | Build scripts and dependencies set up | +| 4. Test Infrastructure | ✅ Complete | Testing framework configured | +| 5. Deploy Snapshot Workflow | ✅ Complete | Daily snapshot workflow implemented | +| 6. Email Notifications | 🔄 In Progress | Email notification system configuration | +| 7. Production Deployment | 🔄 In Progress | Production environment setup | +| 8. Monitoring & Logging | 🔄 In Progress | Monitoring and logging infrastructure | + +## Summary + +- **Total Tasks:** 8 +- **Completed:** 5 +- **In Progress:** 3 +- **Blocked:** 0 + +## Notes + +This status file is used by the deploy_snapshot.yml workflow to track deployment progress. +A snapshot of this file is created daily with the current date. diff --git a/Transzendenz/Reports/snapshot-log.txt b/Transzendenz/Reports/snapshot-log.txt index b228792..f98fcd1 100644 --- a/Transzendenz/Reports/snapshot-log.txt +++ b/Transzendenz/Reports/snapshot-log.txt @@ -11,3 +11,4 @@ This file tracks all snapshot events from the deploy_snapshot.yml workflow. ✅ Snapshot for 2025-11-15 created at 2025-11-15 00:51:33 ✅ Snapshot for 2025-11-16 created at 2025-11-16 00:56:17 ✅ Snapshot for 2025-11-17 created at 2025-11-17 00:53:58 +✅ Snapshot for 2025-11-18 created at 2025-11-18 00:52:11 From 3c42b2a896b8f5b6868f1080292711197310d7c8 Mon Sep 17 00:00:00 2001 From: YONI AutoBot Date: Wed, 19 Nov 2025 00:52:30 +0000 Subject: [PATCH 119/133] =?UTF-8?q?=F0=9F=95=93=20Daily=20Snapshot=202025-?= =?UTF-8?q?11-19?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Reports/Deploy-Status-2025-11-19.md | 28 +++++++++++++++++++ Transzendenz/Reports/snapshot-log.txt | 1 + 2 files changed, 29 insertions(+) create mode 100644 Transzendenz/Reports/Deploy-Status-2025-11-19.md diff --git a/Transzendenz/Reports/Deploy-Status-2025-11-19.md b/Transzendenz/Reports/Deploy-Status-2025-11-19.md new file mode 100644 index 0000000..c93e1fb --- /dev/null +++ b/Transzendenz/Reports/Deploy-Status-2025-11-19.md @@ -0,0 +1,28 @@ +# 📦 YONI Deploy Status + +**Last Updated:** 2025-11-05 + +## Deployment Tasks + +| Task | Status | Description | +|------|--------|-------------| +| 1. Repository Setup | ✅ Complete | Initial repository structure created | +| 2. CI Workflow Configuration | ✅ Complete | GitHub Actions CI pipeline configured | +| 3. Build Process | ✅ Complete | Build scripts and dependencies set up | +| 4. Test Infrastructure | ✅ Complete | Testing framework configured | +| 5. Deploy Snapshot Workflow | ✅ Complete | Daily snapshot workflow implemented | +| 6. Email Notifications | 🔄 In Progress | Email notification system configuration | +| 7. Production Deployment | 🔄 In Progress | Production environment setup | +| 8. Monitoring & Logging | 🔄 In Progress | Monitoring and logging infrastructure | + +## Summary + +- **Total Tasks:** 8 +- **Completed:** 5 +- **In Progress:** 3 +- **Blocked:** 0 + +## Notes + +This status file is used by the deploy_snapshot.yml workflow to track deployment progress. +A snapshot of this file is created daily with the current date. diff --git a/Transzendenz/Reports/snapshot-log.txt b/Transzendenz/Reports/snapshot-log.txt index f98fcd1..e59275d 100644 --- a/Transzendenz/Reports/snapshot-log.txt +++ b/Transzendenz/Reports/snapshot-log.txt @@ -12,3 +12,4 @@ This file tracks all snapshot events from the deploy_snapshot.yml workflow. ✅ Snapshot for 2025-11-16 created at 2025-11-16 00:56:17 ✅ Snapshot for 2025-11-17 created at 2025-11-17 00:53:58 ✅ Snapshot for 2025-11-18 created at 2025-11-18 00:52:11 +✅ Snapshot for 2025-11-19 created at 2025-11-19 00:52:30 From 5f403b6cef68a8bc784bc423ee91ac805acc2cfa Mon Sep 17 00:00:00 2001 From: YONI Date: Thu, 20 Nov 2025 00:49:52 +0100 Subject: [PATCH 120/133] docs: add iPhone demo and preview guide --- IOS_DEMO_GUIDE.md | 42 ++++++++++++++++++++++++++++++++++++++++++ README.md | 4 ++++ 2 files changed, 46 insertions(+) create mode 100644 IOS_DEMO_GUIDE.md diff --git a/IOS_DEMO_GUIDE.md b/IOS_DEMO_GUIDE.md new file mode 100644 index 0000000..dc28ac7 --- /dev/null +++ b/IOS_DEMO_GUIDE.md @@ -0,0 +1,42 @@ +# 📱 iPhone Demo & Testversand + +Dieser Guide beschreibt, wie du die YONI Web-App schnell auf einem iPhone als "App" installierst und eine verschickbare Testversion vorbereitest (Demo-Lauf). + +## 1) Was du brauchst +- Apple iPhone mit aktueller iOS-Version und Safari. +- Zugriff auf einen laufenden Build (z. B. `yoni.vercel.app` oder eine Vercel Preview-URL). +- Optional: eigene `.env.local` mit `OPENAI_API_KEY` für ChatGPT-Features (siehe **YONI_Local_Run_Guide.md**). + +## 2) Demo auf dem iPhone installieren (PWA) +1. Öffne die gewünschte URL in **Safari** (z. B. https://yoni.vercel.app oder deine Preview). +2. Tippe auf das **Teilen**-Symbol. +3. Wähle **Zum Home-Bildschirm**. +4. Bestätige den Namen und tippe auf **Hinzufügen**. +5. Starte die App vom Homescreen – sie läuft dann im Fullscreen wie eine native App. + +> Tipp: Wenn der Prompt erscheint, Notifications erlauben, damit Reminder und Chat-Alerts kommen. + +## 3) Testversion versandfertig machen (Preview) +1. **Lokalen Check bauen** + - `npm ci` + - `npm run build` +2. **Preview auf Vercel deployen** + - Falls noch nicht verlinkt: `npx vercel link` und Projekt auswählen. + - `npx vercel --prebuilt --env=preview` oder `npx vercel --prod` für eine stabile Demo. + - Notwendige Secrets/Env-Variablen auf Vercel hinterlegen (z. B. `OPENAI_API_KEY`). +3. **Preview-Link testen** + - Link auf dem iPhone öffnen und per Schritt 2 als App hinzufügen. + - Kernflows prüfen: Startseite, Chat, Navigation, Laden im Vollbild. +4. **An Tester:innen senden** + - Kurze Anleitung mitsenden: Safari öffnen → Link → Teilen → „Zum Home-Bildschirm“. + - Bei Bedarf Hinweis auf Demo-Daten (keine echten Nutzer:innendaten verwenden). + +## 4) Quick-Checks vor dem Versand +- ✅ Lädt ohne Next.js Errors (404/500) und ohne leere Komponenten. +- ✅ Responsive: Startseite, Navigation und Chat auf iPhone 12/13/14 getestet. +- ✅ Zugriff auf Kamera/Mikrofon NICHT benötigt (keine Prompts auftauchen). +- ✅ Kein Tracking/Analytics ohne Zustimmung. + +## 5) FAQ +- **Warum PWA und nicht TestFlight?** Die App ist eine Next.js Web-App; PWA-Installation ist der schnellste Weg zum Homescreen ohne iOS-Build. +- **Funktioniert das auch mit Chrome?** Auf iOS geht die Homescreen-Installation zuverlässig nur mit Safari. diff --git a/README.md b/README.md index 4668163..d593366 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,10 @@ Die App ist dann verfügbar unter: **http://localhost:3000** Siehe [YONI_Local_Run_Guide.md](YONI_Local_Run_Guide.md) für Details. +### iPhone-Demo / Testversand +- 📱 **Homescreen-Installation:** Safari → Teilen → „Zum Home-Bildschirm". +- 📤 **Preview verschicken:** Anleitung siehe [IOS_DEMO_GUIDE.md](IOS_DEMO_GUIDE.md). + ### Deployment Siehe **[DEPLOYMENT.md](DEPLOYMENT.md)** für vollständige Deployment-Anleitung: From c015a531a0d0e1f0d543923f9c4ce506fc254d03 Mon Sep 17 00:00:00 2001 From: YONI Date: Thu, 20 Nov 2025 01:07:21 +0100 Subject: [PATCH 121/133] Harden GitHub OAuth callback handling --- app/api/github-app/authorize/route.ts | 26 ++++++++++++++ app/api/github-app/callback/route.ts | 51 ++++++++++++++++++++++----- 2 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 app/api/github-app/authorize/route.ts diff --git a/app/api/github-app/authorize/route.ts b/app/api/github-app/authorize/route.ts new file mode 100644 index 0000000..6c34b44 --- /dev/null +++ b/app/api/github-app/authorize/route.ts @@ -0,0 +1,26 @@ +import crypto from 'crypto'; +import { NextResponse } from 'next/server'; + +export async function GET() { + const clientId = process.env.GITHUB_APP_CLIENT_ID; + + if (!clientId) { + return NextResponse.json({ error: 'GITHUB_APP_CLIENT_ID is not configured' }, { status: 500 }); + } + + const state = crypto.randomBytes(16).toString('hex'); + const authorizeUrl = new URL('https://github.com/login/oauth/authorize'); + authorizeUrl.searchParams.set('client_id', clientId); + authorizeUrl.searchParams.set('state', state); + + const response = NextResponse.redirect(authorizeUrl.toString()); + response.cookies.set('github_oauth_state', state, { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', + path: '/', + maxAge: 10 * 60 // 10 minutes + }); + + return response; +} diff --git a/app/api/github-app/callback/route.ts b/app/api/github-app/callback/route.ts index ca0248b..be37b37 100644 --- a/app/api/github-app/callback/route.ts +++ b/app/api/github-app/callback/route.ts @@ -1,24 +1,59 @@ +import { cookies } from 'next/headers'; import { NextRequest, NextResponse } from 'next/server'; +const STATE_COOKIE_NAME = 'github_oauth_state'; + +function redirectWithStateReset(location: string) { + const response = NextResponse.redirect(location); + response.cookies.set(STATE_COOKIE_NAME, '', { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', + path: '/', + maxAge: 0 + }); + + return response; +} + export async function GET(req: NextRequest) { const code = req.nextUrl.searchParams.get('code'); const state = req.nextUrl.searchParams.get('state'); - if (!code) return NextResponse.redirect('/?auth=missing_code'); + const cookieStore = cookies(); + const storedState = cookieStore.get(STATE_COOKIE_NAME)?.value; + + if (!code) return redirectWithStateReset('/?auth=missing_code'); + if (!state || !storedState || state !== storedState) + return redirectWithStateReset('/?auth=state_mismatch'); + + const clientId = process.env.GITHUB_APP_CLIENT_ID; + const clientSecret = process.env.GITHUB_APP_CLIENT_SECRET; + + if (!clientId || !clientSecret) { + return redirectWithStateReset('/?auth=missing_env'); + } // Exchange code → access token (GitHub OAuth for GitHub App) const res = await fetch('https://github.com/login/oauth/access_token', { method: 'POST', - headers: { 'Accept': 'application/json' }, + headers: { Accept: 'application/json' }, body: new URLSearchParams({ - client_id: process.env.GITHUB_APP_CLIENT_ID!, - client_secret: process.env.GITHUB_APP_CLIENT_SECRET!, + client_id: clientId, + client_secret: clientSecret, code, - state: state ?? '' + state }) }); + if (!res.ok) { + return redirectWithStateReset('/?auth=token_exchange_failed'); + } + const data = await res.json(); - // Persist token (per user/installation) – store securely. - // For demo we just echo status. - return NextResponse.redirect('/?auth=ok&provider=github&x=148'); + + if (data?.error || !data?.access_token) { + return redirectWithStateReset('/?auth=token_exchange_failed'); + } + + return redirectWithStateReset('/?auth=ok&provider=github&x=148'); } From 2f9b3c3a80401d9b744b8963d73e6f5cb75212d0 Mon Sep 17 00:00:00 2001 From: YONI Date: Thu, 20 Nov 2025 01:10:58 +0100 Subject: [PATCH 122/133] Add CHIBot blueprint structure to home --- app/data/chibot-blueprint.ts | 198 ++++++++++++++++++++++ app/globals.css | 175 ++++++++++++++++++- app/page.tsx | 318 +++++++++++++++++++++++++++-------- 3 files changed, 611 insertions(+), 80 deletions(-) create mode 100644 app/data/chibot-blueprint.ts diff --git a/app/data/chibot-blueprint.ts b/app/data/chibot-blueprint.ts new file mode 100644 index 0000000..ae4b872 --- /dev/null +++ b/app/data/chibot-blueprint.ts @@ -0,0 +1,198 @@ +export type Status = 'online' | 'building' | 'blocked' + +export interface PageBlueprint { + name: string + path: string + focus: string + owner: string + status: Status +} + +export interface DataLayer { + name: string + source: string + cadence: string + retention: string + status: Status +} + +export interface Profile { + name: string + archetype: string + focus: string + rituals: string[] +} + +export interface Bot { + name: string + mode: string + promise: string + links: string[] +} + +export interface Ritual { + title: string + cadence: string + description: string + signals: string[] +} + +export interface ActionItem { + title: string + owner: string + impact: string +} + +export interface Blueprint { + steward: string + mission: string + pages: PageBlueprint[] + dataLayers: DataLayer[] + profiles: Profile[] + bots: Bot[] + rituals: Ritual[] + actions: ActionItem[] +} + +export const blueprint: Blueprint = { + steward: 'CHIBot', + mission: + 'Ordnet das YONI-System, hält die Energie fließend und sorgt für klare Verantwortlichkeiten.', + pages: [ + { + name: 'Transzendenz Hub', + path: '/', + focus: 'Fragen, Antworten, Mehrsprachigkeit, Feed', + owner: 'CHIBot', + status: 'online', + }, + { + name: 'Profile', + path: '/profile', + focus: 'Identitäten, Rollen, Grenzen', + owner: 'CHIBot', + status: 'building', + }, + { + name: 'Bots', + path: '/bots', + focus: 'Persönlichkeiten, Aufträge, Übergaben', + owner: 'CHIBot', + status: 'building', + }, + { + name: 'Datenfluss', + path: '/data', + focus: 'Streams, Speichern, Governance', + owner: 'CHIBot', + status: 'online', + }, + { + name: 'Rituale', + path: '/rituale', + focus: 'Checks, Playbooks, Eskalation', + owner: 'CHIBot', + status: 'online', + }, + ], + dataLayers: [ + { + name: 'YONI-Core', + source: 'Next.js + Tailwind + Vercel Edge', + cadence: 'live-sync', + retention: 'Rolling 30 Tage + Backups', + status: 'online', + }, + { + name: 'Ritual-Storage', + source: 'Static JSON + zukünftige Notion-Spiegelung', + cadence: 'bei Deploy & auf Abruf', + retention: 'Versionierte Snapshots', + status: 'building', + }, + { + name: 'Signals', + source: 'User Prompts + Stripe Events', + cadence: 'Echtzeit', + retention: '14 Tage roh, verdichtet danach', + status: 'online', + }, + ], + profiles: [ + { + name: 'Seherin', + archetype: 'Visionärin, erkennt Muster', + focus: 'Synthese, Zukunftslinien, Risiken', + rituals: ['Weekly Deep Dive', 'Risiko-Heatmap'], + }, + { + name: 'Somatic Guide', + archetype: 'Körperorientiert, hält Raum', + focus: 'Sicherheit, Pace, Grenzcheck', + rituals: ['Session Warmup', 'Cooldown Protokoll'], + }, + { + name: 'Archivarin', + archetype: 'Ordnet Wissen', + focus: 'Dokumentation, Consent, Rechte', + rituals: ['Release Notes', 'Backup-Review'], + }, + ], + bots: [ + { + name: 'CHIBot', + mode: 'Steward', + promise: 'Hält Struktur, priorisiert Sicherheit, verteilt Aufgaben klar.', + links: ['System Blueprint', 'Onboarding Script'], + }, + { + name: 'Contrast-Bot', + mode: 'Gegensätze', + promise: 'Bringt andere Sicht, challengt Annahmen, liefert Optionen.', + links: ['Decision-Canvas'], + }, + { + name: 'Somatic-Bot', + mode: 'Safety', + promise: 'Checkt Pace, Grenzen und Nervensystem-Signale.', + links: ['Safety-Checklist'], + }, + ], + rituals: [ + { + title: 'System Reset', + cadence: 'Montag 10:00', + description: 'Cache leeren, Deploy-Status prüfen, Domains verifizieren.', + signals: ['Status grün', 'OpenAI Key validiert', 'Domains in Sync'], + }, + { + title: 'Transzendenz Sync', + cadence: 'Täglich 17:00', + description: 'Feed prüfen, Kontraste sammeln, nächste Fragen kuratieren.', + signals: ['Neue Fragen markiert', 'Kontraste dokumentiert'], + }, + { + title: 'Release Note Drop', + cadence: 'Freitag 15:00', + description: 'Changelog, Sicherheitscheck, Consent-Log aktualisieren.', + signals: ['Changelog publiziert', 'Consent-Logs signiert'], + }, + ], + actions: [ + { + title: 'Domains Setup vervollständigen', + owner: 'CHIBot', + impact: 'Sichere Auslieferung & Offline-Modus stabil', + }, + { + title: 'Profile-Seite booten', + owner: 'Seherin', + impact: 'Klare Rollen & Grenzen sichtbar', + }, + { + title: 'Ritual-Storage anbinden', + owner: 'Archivarin', + impact: 'Nachvollziehbarkeit & Backups gewährleistet', + }, + ], +} diff --git a/app/globals.css b/app/globals.css index eb2ab68..e3a17c5 100644 --- a/app/globals.css +++ b/app/globals.css @@ -7,7 +7,10 @@ --panel: rgba(255, 255, 255, .06); --text: #f3f3f7; --brand: #7c3aed; + --brand-amethyst: #a855f7; + --hl-gold: #f6d26b; --radius: 14px; + --border: 1px solid rgba(255, 255, 255, .08); } * { @@ -24,27 +27,36 @@ body { } .wrap { - max-width: 860px; - margin: 40px auto; - padding: 16px; + max-width: 1200px; + margin: 40px auto 80px; + padding: 16px 20px; } .card { background: var(--panel); - border: 1px solid rgba(255, 255, 255, .08); + border: var(--border); border-radius: var(--radius); padding: 16px; } h1 { - margin: 0 0 8px 0; - font-size: 22px; + margin: 0 0 12px 0; + font-size: 28px; +} + +h2 { + margin: 0 0 6px 0; + font-size: 20px; } .muted { opacity: .7; } +.small { + font-size: 13px; +} + .row { display: flex; gap: 8px; @@ -89,6 +101,157 @@ select { border-radius: 12px; } +.hero { + display: grid; + grid-template-columns: 1.5fr 1fr; + gap: 16px; + align-items: center; +} + +.eyebrow { + letter-spacing: 0.08em; + text-transform: uppercase; + font-size: 12px; + margin: 0; + color: rgba(255, 255, 255, 0.6); +} + +.pill-row { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 12px; +} + +.pill { + background: rgba(255, 255, 255, 0.08); + border: 1px solid rgba(255, 255, 255, 0.12); + padding: 6px 10px; + border-radius: 999px; + font-size: 13px; +} + +.pill.subtle { + border-style: dashed; + opacity: 0.75; +} + +.grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); + gap: 14px; + margin-top: 14px; +} + +.flow { + display: flex; + flex-direction: column; + gap: 12px; +} + +.section-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; +} + +.controls { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} + +.checkbox { + display: flex; + align-items: center; + gap: 6px; +} + +.hero-actions { + display: flex; + flex-direction: column; + gap: 8px; + align-items: flex-end; +} + +.blueprint-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 12px; +} + +.blueprint-block h3 { + margin: 0 0 6px 0; +} + +.list { + display: grid; + gap: 10px; +} + +.list-row { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 10px; + padding: 12px; + background: rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.06); + border-radius: 12px; +} + +.title-row { + display: flex; + gap: 10px; + align-items: center; +} + +.status-pill { + padding: 4px 8px; + border-radius: 8px; + font-size: 12px; + text-transform: capitalize; + border: 1px solid rgba(255, 255, 255, 0.15); +} + +.status-online { + background: rgba(34, 197, 94, 0.18); + color: #bbf7d0; +} + +.status-building { + background: rgba(250, 204, 21, 0.18); + color: #fef08a; +} + +.status-blocked { + background: rgba(248, 113, 113, 0.18); + color: #fecdd3; +} + +.primary { + background: linear-gradient(135deg, var(--brand-amethyst), var(--hl-gold)); + color: #0b0b10; + font-weight: 700; + box-shadow: 0 8px 24px rgba(168, 85, 247, 0.35); +} + +.hero button { + width: 100%; +} + +@media (max-width: 900px) { + .hero { + grid-template-columns: 1fr; + } + + .hero-actions { + align-items: flex-start; + } +} + /* ======================================== GODDESSMODE+ ENHANCED STYLES ======================================== */ diff --git a/app/page.tsx b/app/page.tsx index cac5062..2ff4ebe 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,14 +1,26 @@ 'use client' -import { useState, useCallback } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import AgeVerification from './components/AgeVerification' import GoddessMode from './components/GoddessMode' +import { blueprint, Status } from './data/chibot-blueprint' + +interface FeedItem { + id: number + agent: string + text: string + isFusion?: boolean +} + +function StatusPill({ status }: { status: Status }) { + return {status} +} export default function Home() { const [question, setQuestion] = useState('') const [mode, setMode] = useState('Consensus') const [autoTranslate, setAutoTranslate] = useState(true) - const [feed, setFeed] = useState>([]) + const [feed, setFeed] = useState([]) const [envMessage, setEnvMessage] = useState('') const [isLoading, setIsLoading] = useState(false) const [ageVerified, setAgeVerified] = useState(false) @@ -17,27 +29,27 @@ export default function Home() { setAgeVerified(true) }, []) - // Check environment on mount - useState(() => { - if (typeof window !== 'undefined') { - setEnvMessage( - window.location.protocol === 'https:' - ? 'HTTPS aktiv – Service Worker & Install verfügbar.' - : 'Hinweis: Für Offline & „Zum Home‑Bildschirm" bitte HTTPS nutzen.' - ) - } - }) + useEffect(() => { + if (typeof window === 'undefined') return + + setEnvMessage( + window.location.protocol === 'https:' + ? 'HTTPS aktiv – Service Worker & Install verfügbar.' + : 'Hinweis: Für Offline & „Zum Home‑Bildschirm" bitte HTTPS nutzen.' + ) + }, []) const handleInstall = async () => { - if (typeof window !== 'undefined') { - if (window.location.protocol !== 'https:') { - alert('Service Worker benötigt HTTPS.') - return - } - if ('serviceWorker' in navigator) { - await navigator.serviceWorker.register('/sw.js') - alert('Offline aktiviert.') - } + if (typeof window === 'undefined') return + + if (window.location.protocol !== 'https:') { + alert('Service Worker benötigt HTTPS.') + return + } + + if ('serviceWorker' in navigator) { + await navigator.serviceWorker.register('/sw.js') + alert('Offline aktiviert.') } } @@ -46,20 +58,19 @@ export default function Home() { } const handleAsk = async () => { - const q = question.trim() - if (!q || isLoading) return + const trimmedQuestion = question.trim() + if (!trimmedQuestion || isLoading) return setIsLoading(true) - + try { - // Call the ChatGPT API const response = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ - question: q, + question: trimmedQuestion, mode: mode, }), }) @@ -70,81 +81,240 @@ export default function Home() { throw new Error(data.error || 'API request failed') } - // Add the response to the feed - const newItem = { + const translatedLabel = autoTranslate ? '→ Deutsch' : '' + const agentLabel = translatedLabel ? `${mode} ${translatedLabel}` : mode + const newItem: FeedItem = { id: Date.now(), - agent: `ChatGPT (${mode})`, + agent: `ChatGPT (${agentLabel})`, text: data.response, } - setFeed([newItem, ...feed]) - setQuestion('') // Clear the question after successful submission + setFeed((prev) => [newItem, ...prev]) + setQuestion('') } catch (error: any) { - // Add error to feed - const errorItem = { + const errorItem: FeedItem = { id: Date.now(), agent: 'Error', text: `Fehler: ${error.message}. Bitte stelle sicher, dass der OPENAI_API_KEY konfiguriert ist.`, } - setFeed([errorItem, ...feed]) + setFeed((prev) => [errorItem, ...prev]) } finally { setIsLoading(false) } } + const blueprintStats = useMemo( + () => ({ + pages: blueprint.pages.length, + dataLayers: blueprint.dataLayers.length, + profiles: blueprint.profiles.length, + bots: blueprint.bots.length, + }), + [] + ) + return ( <> {!ageVerified && } {ageVerified && } -
-
-

YONI • pi² Control

-
Vercel‑Minimal • Creator‑KI • Auto‑Translate • Transzendenz • 18+
-
+
+
+
+

CHIBot Stewardship

+

YONI • System Blueprint

+

{blueprint.mission}

+
+ Pages: {blueprintStats.pages} + Data: {blueprintStats.dataLayers} + Profiles: {blueprintStats.profiles} + Bots: {blueprintStats.bots} +
+
+
+

{envMessage}

-

{envMessage}

+
+ +
+
+
+
+

Transzendenz

+

Fragen & Antworten

+
+
+ + +
+
+