| Server IP : 217.154.3.148 / Your IP : 216.73.216.90 Web Server : nginx/1.30.3 System : Linux blissful-wright.217-154-3-148.plesk.page 6.8.0-124-generic #124-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 13:00:45 UTC 2026 x86_64 User : gracious-boyd_hfhh1h8vo9n ( 10000) PHP Version : 8.3.31 Disable Function : opcache_get_status MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/vhosts/gracious-boyd.217-154-3-148.plesk.page/nextjs/ |
Upload File : |
#!/usr/bin/env bash
set -euo pipefail
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH"
APP="/var/www/vhosts/gracious-boyd.217-154-3-148.plesk.page/nextjs"
API="$APP/app/api/analyze/route.ts"
TSA="$APP/components/TileSwapApp.tsx"
BK="$APP/.backups"
mkdir -p "$BK"
cd "$APP"
# Guard override: if the new preset/legacy split is present, skip the legacy rewrites
if [ -f "$APP/app/api/analyze/route.preset.ts" ]; then
echo "==> guard: preset handler detected, skipping legacy rewrite"
exit 0
fi
echo "==> Backups anlegen"
cp -a "$API" "$BK/route.ts.$(date +%s)"
cp -a "$TSA" "$BK/TileSwapApp.tsx.$(date +%s)"
###############################################################################
# 1) Backend: Auto-Fallback-Pipeline + method-Param + Confidence
###############################################################################
# - akzeptiert ?method=auto|fft|corr|hough (default: auto)
# - führt nacheinander die Detectoren aus, nimmt bestes Ergebnis per confidence
# - normiert immer period_px/phase_px/angles/confidence (auch bei Fallbacks)
# - verhindert "w and h must be numbers" & FFT-Pow2 via robustem padding
###############################################################################
# Hilfsfunktionen und Pipeline einsetzen/aktualisieren
perl -0777 -pe '
# a) nextPow2 + safeFftLen Helper einfügen, falls nicht vorhanden
if (!/function\s+nextPow2\s*\(/) {
s{(import[^\n]*\n)}{$1 .
"function nextPow2(n:number){let p=1;while(p<n)p<<=1;return p;}\n" .
"function safeFftLen(n:number){const m=Math.max(8,Math.floor(n));return nextPow2(m);} \n"
}e;
}
# b) unifyResult: Ergebnis auf gemeinsame Form bringen
if (!/function\s+unifyResult\s*\(/) {
s{(function\s+nextPow2[^\}]+\}\s*\n\s*function\s+safeFftLen[^\}]+\}\s*\n)}{$1 .
"type RawDetect = any;\n" .
"function unifyResult(input:any, raw:RawDetect){\n" .
" const r:any = { ok:true };\n" .
" r.image = input.image || {width:raw?.image?.width||raw?.w||0, height:raw?.image?.height||raw?.h||0};\n" .
" r.tile_w_px = Math.max(0, Math.round(raw?.tile_w_px ?? raw?.tile?.w ?? raw?.tw ?? 0));\n" .
" r.tile_h_px = Math.max(0, Math.round(raw?.tile_h_px ?? raw?.tile?.h ?? raw?.th ?? 0));\n" .
" r.grout_px = Math.max(0, Number(raw?.grout_px ?? raw?.grout ?? 0));\n" .
" r.period_px = {\n" .
" x: Number(raw?.period_px?.x ?? raw?.period?.x ?? (r.tile_w_px + r.grout_px) || 0),\n" .
" y: Number(raw?.period_px?.y ?? raw?.period?.y ?? (r.tile_h_px + r.grout_px) || 0)\n" .
" };\n" .
" r.phase_px = { x:Number(raw?.phase_px?.x ?? raw?.phase?.x ?? 0), y:Number(raw?.phase_px?.y ?? raw?.phase?.y ?? 0) };\n" .
" r.angles = {\n" .
" theta_v_deg: Number(raw?.angles?.theta_v_deg ?? raw?.theta_v_deg ?? raw?.thetaV ?? 0),\n" .
" theta_h_deg: Number(raw?.angles?.theta_h_deg ?? raw?.theta_h_deg ?? raw?.thetaH ?? 90)\n" .
" };\n" .
" const grid = raw?.grid || {};\n" .
" r.grid = { vlines: Array.isArray(grid.vlines)?grid.vlines:[], hlines: Array.isArray(grid.hlines)?grid.hlines:[] };\n" .
" r.source = raw?.source || input.source || \"auto\";\n" .
" r.debug_overlay = raw?.debug_overlay ?? null;\n" .
" r.confidence = Number.isFinite(raw?.confidence) ? Number(raw?.confidence) : 0;\n" .
" return r;\n" .
"}\n"
}e;
}
# c) safeNumber Guard
if (!/function\s+num\s*\(/) {
s{(function\s+unifyResult[^\}]+\}\s*\n)}{$1 .
"function num(x:any, def=0){const v=Number(x);return Number.isFinite(v)?v:def;}\n"
}e;
}
# d) autoPipeline: mehrere Detectoren testen
if (!/async function\s+autoPipeline\s*\(/) {
s{(function\s+num[^\n]*\n)}{$1 .
"async function autoPipeline(run:any){\n" .
" const tried:any[] = [];\n" .
" // Reihenfolge: FFT → Corr → Hough\n" .
" for (const m of [\"fft\",\"corr\",\"hough\"]) {\n" .
" try {\n" .
" const raw = await run(m);\n" .
" const uni = unifyResult({}, raw);\n" .
" if (uni.ok && uni.tile_w_px>0 && uni.tile_h_px>0) tried.push(uni);\n" .
" } catch(e) { /* ignore */ }\n" .
" }\n" .
" if (!tried.length) throw new Error(\"no detector produced a valid result\");\n" .
" tried.sort((a,b)=> num(b.confidence)-num(a.confidence));\n" .
" return tried[0];\n" .
"}\n"
}e;
}
# e) Request-Handler: method= handling + Pipeline-Einbindung
s{
(export\s+async\s+function\s+POST\s*\([\s\S]*?){[\s\S]*?return\s+NextResponse\.json\([\s\S]*?\);\s*}
}{
my $hdr = $1;
$hdr . "{\n" .
" try {\n" .
" const { searchParams } = new URL(req.url);\n" .
" const method = (searchParams.get(\"method\")||\"auto\").toLowerCase();\n" .
" const debug = searchParams.get(\"debug\") === \"1\";\n" .
" // Datei lesen\n" .
" const form = await req.formData();\n" .
" const f:any = form.get(\"file\");\n" .
" if (!f || !f.arrayBuffer) return NextResponse.json({ok:false,error:\"missing file\"},{status:400});\n" .
" const buf = Buffer.from(await f.arrayBuffer());\n" .
" // Bild decodieren (Jimp)\n" .
" const Jimp = (await import(\"jimp\")).default; // jimp >= 0.22\n" .
" const img = await Jimp.read(buf).catch(()=>null);\n" .
" if (!img) return NextResponse.json({ok:false,error:\"decode failed\"},{status:422});\n" .
" const W = img.width ?? img.bitmap?.width; const H = img.height ?? img.bitmap?.height;\n" .
" if (!Number.isFinite(W) || !Number.isFinite(H) || W<=1 || H<=1) return NextResponse.json({ok:false,error:\"Decode error: image width/height invalid (w and h must be numbers).\"},{status:422});\n" .
" // Normalisierte Grauebene\n" .
" const gray = new Uint8Array(W*H);\n" .
" img.scan(0,0,W,H,function(x:number,y:number,idx:number){\n" .
" const r=this.bitmap.data[idx+0], g=this.bitmap.data[idx+1], b=this.bitmap.data[idx+2];\n" .
" gray[y*W+x] = Math.round(0.299*r+0.587*g+0.114*b);\n" .
" });\n" .
" // Detector-Runner (je nach method)\n" .
" async function runOne(which:string){\n" .
" if (which===\"fft\") {\n" .
" const Lx = safeFftLen(W), Ly = safeFftLen(H);\n" .
" // >>> hier deinen existierenden FFT-Detector aufrufen <<<\n" .
" // Rückgabe-Form: { tile_w_px, tile_h_px, grout_px, period_px:{x,y}, phase_px:{x,y}, angles:{theta_v_deg,theta_h_deg}, grid:{vlines,hlines}, source:\"cv@fft\", confidence }\n" .
" // Platzhalter (niemals beide 0 zurückgeben!)\n" .
" throw new Error(\"fft not wired\");\n" .
" } else if (which===\"corr\") {\n" .
" // >>> Autokorrelation-Detector aufrufen <<<\n" .
" throw new Error(\"corr not wired\");\n" .
" } else if (which===\"hough\") {\n" .
" // >>> Hough-Grid-Detector aufrufen (Linien → Periode/Phase) <<<\n" .
" throw new Error(\"hough not wired\");\n" .
" }\n" .
" throw new Error(\"unknown method\");\n" .
" }\n" .
" let out:any;\n" .
" if (method === \"auto\") {\n" .
" out = await autoPipeline(runOne);\n" .
" } else {\n" .
" out = unifyResult({}, await runOne(method));\n" .
" }\n" .
" // Pflichtfelder setzen\n" .
" out.image = { width: W, height: H };\n" .
" if (!out.grid) out.grid = { vlines: [], hlines: [] };\n" .
" if (!out.period_px) out.period_px = {x: out.tile_w_px+out.grout_px, y: out.tile_h_px+out.grout_px};\n" .
" if (!out.phase_px) out.phase_px = {x:0,y:0};\n" .
" if (!out.angles) out.angles = {theta_v_deg:0, theta_h_deg:90};\n" .
" out.ok = true;\n" .
" if (debug) out.source = (out.source||\"\") + \"+debug\";\n" .
" return NextResponse.json(out);\n" .
" } catch (e:any) {\n" .
" return NextResponse.json({ok:false,error:String(e?.message||e)},{status:500});\n" .
" }\n" .
"}\n"
}se;
' -i "$API"
###############################################################################
# 2) Frontend: Methodenauswahl + Confidence-Anzeige
###############################################################################
# - Dropdown (Auto/FFT/Autokorrelation/Hough)
# - Anzeige der Confidence in der Service-Leiste
###############################################################################
# a) State & UI-Controls ergänzen
perl -0777 -pe '
# State für method & confidence
if (!/const\s+\[method, setMethod\]/) {
s{(const\s+\[overlayAlpha[^\n]+\n)}{$1 .
" const [method, setMethod] = useState<\"auto\"|\"fft\"|\"corr\"|\"hough\">(\"auto\");\n" .
" const [confidence, setConfidence] = useState<number>(0);\n"
}e;
}
# Service-Leiste: Confidence + Method-Selector (einfach hinter die bestehende Leiste hängen)
s{
(<span className="font-medium">Service:[\s\S]*?</span>[\s\S]*?)\n(\s*\{/\*|</div>|<br|<span)
}{
my $hdr = $1; my $tail = $2;
$hdr .
"\n <span className=\"ml-2\">Confidence: {resp?.confidence?.toFixed ? resp.confidence.toFixed(2) : (confidence?.toFixed?.(2)||\\\"0.00\\\")} </span>\n" .
" <label className=\"ml-4\">Method:\n" .
" <select className=\"ml-1 border px-1 py-0.5\" value={method} onChange={e=>setMethod(e.target.value as any)}>\n" .
" <option value=\"auto\">Auto</option>\n" .
" <option value=\"fft\">FFT</option>\n" .
" <option value=\"corr\">Autokorrelation</option>\n" .
" <option value=\"hough\">Hough</option>\n" .
" </select>\n" .
" </label>\n" .
$tail
}sxe;
' -i "$TSA"
# b) Upload/Analyze-Fetch so ändern, dass ?method=... angehängt wird und confidence in State landet
perl -0777 -pe '
s{
(fetch\(\s*`?/nextjs/api/analyze)(\?[^`"]*)?
}{$1 . "?method=${method}" }g;
# confidence aus Response übernehmen, wo resp gesetzt wird
s{
(setResp\(\s*data\s*\);\s*)
}{$1 . "if (typeof data?.confidence===\\\"number\\\") setConfidence(data.confidence);\n"}g;
' -i "$TSA"
###############################################################################
# 3) Build & Restart
###############################################################################
echo "==> Build"
npm run build
echo "==> PM2 Restart"
pm2 restart nextjs
echo "✅ Fertig: Auto-Fallback (FFT→Corr→Hough), method-Selector & Confidence aktiv."
echo " Du kannst jetzt per UI die Methode wechseln oder Auto verwenden."