403Webshell
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/app/api/analyze/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/vhosts/gracious-boyd.217-154-3-148.plesk.page/nextjs/app/api/analyze/route.legacy.ts
/* eslint-disable @typescript-eslint/no-unused-vars */
import { NextRequest, NextResponse } from 'next/server';
import sharp from 'sharp';
import Jimp from 'jimp';
import { maybeRectifyPerspective } from './rectify';
import { detectGridWithOcclusionCached as detectGridWithOcclusion } from './robust';

export const runtime = 'nodejs';

function toBuffer(ab: ArrayBuffer): Buffer {
  return Buffer.from(new Uint8Array(ab));
}

/** einfache Gleitmittelung */
function smooth1D(arr: Float32Array, win = 5): Float32Array {
  const out = new Float32Array(arr.length);
  const half = Math.max(1, Math.floor(win / 2));
  for (let i = 0; i < arr.length; i++) {
    let s = 0, c = 0;
    for (let k = -half; k <= half; k++) {
      const j = i + k;
      if (j >= 0 && j < arr.length) { s += arr[j]; c++; }
    }
    out[i] = c ? s / c : arr[i];
  }
  return out;
}

function autoPeriod(sig: Float32Array, minP: number, maxP: number): number | null {
  const n = sig.length;
  let mean = 0;
  for (let i = 0; i < n; i++) mean += sig[i];
  mean /= n;
  const x = new Float32Array(n);
  for (let i = 0; i < n; i++) x[i] = sig[i] - mean;

  let bestLag = -1;
  let bestVal = -Infinity;
  for (let lag = minP; lag <= Math.min(maxP, n - 2); lag++) {
    let s = 0;
    for (let i = 0; i + lag < n; i++) s += x[i] * x[i + lag];
    if (s > bestVal) { bestVal = s; bestLag = lag; }
  }
  return bestLag > 0 ? bestLag : null;
}

function bestPhase(sig: Float32Array, T: number): number {
  let best = 0;
  let bestVal = -Infinity;
  for (let phase = 0; phase < T; phase++) {
    let s = 0;
    for (let i = phase; i < sig.length; i += T) s += sig[i];
    if (s > bestVal) { bestVal = s; best = phase; }
  }
  return best;
}

/** Overlay mit sharp/RAW-Buffer (kein Jimp mehr hier) */
async function buildOverlaySharp(
  w: number,
  h: number,
  vlines: number[],
  hlines: number[],
  mask: Uint8Array | null
): Promise<string> {
  const px = Buffer.allocUnsafe(w * h * 4);
  // transparent füllen
  for (let i = 0; i < px.length; i += 4) {
    px[i + 0] = 0; // R
    px[i + 1] = 0; // G
    px[i + 2] = 0; // B
    px[i + 3] = 0; // A
  }

  const put = (x: number, y: number, r: number, g: number, b: number, a: number) => {
    if (x < 0 || y < 0 || x >= w || y >= h) return;
    const p = (y * w + x) * 4;
    px[p + 0] = r; px[p + 1] = g; px[p + 2] = b; px[p + 3] = a;
  };

  // Maske (magenta, halbtransparent)
  if (mask) {
    for (let y = 0; y < h; y++) {
      for (let x = 0; x < w; x++) {
        if (mask[y * w + x] > 0) {
          put(x, y, 255, 0, 255, 80);
        }
      }
    }
  }

  // vertikale Linien (rot, voll)
  for (const x of vlines) {
    for (let y = 0; y < h; y++) put(Math.round(x), y, 255, 0, 0, 255);
  }
  // horizontale Linien (grün, voll)
  for (const y of hlines) {
    for (let x = 0; x < w; x++) put(x, Math.round(y), 0, 255, 0, 255);
  }

  const png = await sharp(px, { raw: { width: w, height: h, channels: 4 } }).png().toBuffer();
  return `data:image/png;base64,${png.toString('base64')}`;
}

async function decodeWithSharpToGray(buf: Buffer, maxWidth = 1000) {
  const meta = await sharp(buf, { failOnError: false }).metadata();
  const srcW = meta.width ?? 0;
  const srcH = meta.height ?? 0;
  if (!(Number.isFinite(srcW) && Number.isFinite(srcH) && srcW > 0 && srcH > 0)) {
    throw new Error('unsupported_or_invalid_image');
  }

  const scale = Math.min(1, maxWidth / srcW);
  const W = Math.max(1, Math.round(srcW * scale));
  const H = Math.max(1, Math.round(srcH * scale));

  // Graustufen als RAW (1 Kanal bevorzugt)
  const { data, info } = await sharp(buf, { failOnError: false })
    .resize({ width: W, height: H, fit: 'inside' })
    .greyscale()
    .raw()
    .toBuffer({ resolveWithObject: true });

  let gray: Uint8Array;
  if (info.channels === 1) {
    gray = new Uint8Array(data);
  } else {
    const u8 = new Uint8Array(data);
    gray = new Uint8Array(info.width * info.height);
    // nur ersten Kanal verwenden (keine Mittelung über Alpha)
    for (let i = 0, p = 0; i < gray.length; i++, p += info.channels) {
      gray[i] = u8[p] ?? 0;
    }
  }
  return { gray, W: info.width, H: info.height, srcW, srcH, scale };
}

async function decodeMaskToMono(mbuf: Buffer, W: number, H: number): Promise<Uint8Array | null> {
  try {
    const { data, info } = await sharp(mbuf, { failOnError: false })
      .resize({ width: W, height: H, fit: 'fill' })
      .ensureAlpha()
      .raw()
      .toBuffer({ resolveWithObject: true });
    const u8 = new Uint8Array(data);
    const mono = new Uint8Array(W * H);
    for (let i = 0, p = 0; i < mono.length; i++, p += info.channels) {
      const r = u8[p] ?? 0, g = u8[p + 1] ?? 0, b = u8[p + 2] ?? 0, a = u8[p + 3] ?? 255;
      mono[i] = (a > 10 && (r + g + b) > 0) ? 1 : 0;
    }
    return mono;
  } catch {
    return null;
  }
}

export async function POST(req: NextRequest) {
  const url = new URL(req.url);
  const rectifyParam = url.searchParams.get("rectify");
  const rectify = rectifyParam !== "0"; // default: an
  try {
    const url = new URL(req.url);
    const wantDebug = url.searchParams.get('debug') != null;

    const form = await req.formData();
    const file = form.get('file') as unknown as File | null;
    const maskFile = (form.get('mask') as unknown as File | null) || null;

    if (!file) return NextResponse.json({ ok: false, error: 'No file' }, { status: 422 });
    const buf = toBuffer(await file.arrayBuffer());
    if (!buf || buf.length < 16) return NextResponse.json({ ok: false, error: 'Empty or too small image' }, { status: 422 });

    // Primär sharp
    let grayU8: Uint8Array, W: number, H: number, srcW: number, srcH: number, scale: number;
    try {
      const d = await decodeWithSharpToGray(buf, 1000);
      grayU8 = d.gray; W = d.W; H = d.H; srcW = d.srcW; srcH = d.srcH; scale = d.scale;
    } catch (e: any) {
      // Fallback Jimp
      try {
        const j = await Jimp.read(buf);
        srcW = j.getWidth(); srcH = j.getHeight();
        if (!(Number.isFinite(srcW) && Number.isFinite(srcH) && srcW > 0 && srcH > 0)) {
          return NextResponse.json({ ok: false, error: 'Decode error: image width/height invalid (w and h must be numbers).' }, { status: 422 });
        }
        const maxW = 1000;
        const s = Math.min(1, maxW / srcW);
        W = Math.max(1, Math.round(srcW * s));
        H = Math.max(1, Math.round(srcH * s));
        scale = s;
        const work = j.clone().resize(W, H, Jimp.RESIZE_BILINEAR).greyscale();
        const u8 = new Uint8Array(W * H);
        for (let y = 0; y < H; y++) {
          for (let x = 0; x < W; x++) {
            const { r } = Jimp.intToRGBA(work.getPixelColor(x, y));
            u8[y * W + x] = r;
          }
        }
        grayU8 = u8;
      } catch {
        return NextResponse.json({ ok: false, error: 'Unsupported image format or decode failure' }, { status: 415 });
      }
    }

    // Maske optional
    let maskArr: Uint8Array | null = null;
    if (maskFile) {
      const mbuf = toBuffer(await maskFile.arrayBuffer());
      if (mbuf && mbuf.length > 0) maskArr = await decodeMaskToMono(mbuf, W, H);
    }

    // Sobel
    const gx = new Float32Array(W * H);
    const gy = new Float32Array(W * H);
    const sobelX = [-1, 0, 1, -2, 0, 2, -1, 0, 1];
    const sobelY = [-1, -2, -1, 0, 0, 0, 1, 2, 1];
    for (let y = 1; y < H - 1; y++) {
      for (let x = 1; x < W - 1; x++) {
        let sxv = 0, syv = 0, k = 0;
        for (let j = -1; j <= 1; j++) {
          for (let i = -1; i <= 1; i++) {
            const v = grayU8[(y + j) * W + (x + i)];
            sxv += v * sobelX[k];
            syv += v * sobelY[k];
            k++;
          }
        }
        const idx = y * W + x;
        if (maskArr && maskArr[idx] === 1) { gx[idx] = 0; gy[idx] = 0; }
        else { gx[idx] = Math.abs(sxv); gy[idx] = Math.abs(syv); }
      }
    }

    // Projektionen
    const projX = new Float32Array(W);
    const projY = new Float32Array(H);
    for (let x = 0; x < W; x++) { let s = 0; for (let y = 0; y < H; y++) s += gx[y * W + x]; projX[x] = s; }
    for (let y = 0; y < H; y++) { let s = 0; for (let x = 0; x < W; x++) s += gy[y * W + x]; projY[y] = s; }

    const spx = smooth1D(projX, 7);
    const spy = smooth1D(projY, 7);

    const minPeriod = Math.max(8, Math.floor(Math.min(W, H) * 0.02));
    const Tx = autoPeriod(spx, minPeriod, Math.max(minPeriod + 1, Math.floor(W / 3)));
    const Ty = autoPeriod(spy, minPeriod, Math.max(minPeriod + 1, Math.floor(H / 3)));
    if (!Tx || !Ty) {
      return NextResponse.json({ ok: false, error: 'Could not detect periodic grid (Tx/Ty null).' }, { status: 422 });
    }

    const phaseX = bestPhase(spx, Tx);
    const phaseY = bestPhase(spy, Ty);

    const vlinesWork: number[] = [];
    for (let x = phaseX; x < W; x += Tx) vlinesWork.push(Math.round(x));
    const hlinesWork: number[] = [];
    for (let y = phaseY; y < H; y += Ty) hlinesWork.push(Math.round(y));

    const groutWork = Math.round(Math.max(1, Math.min(Tx, Ty) * 0.06));

    const invScale = 1 / scale;
    const vlines = vlinesWork.map((x) => Math.max(1, Math.round(x * invScale)));
    const hlines = hlinesWork.map((y) => Math.max(1, Math.round(y * invScale)));
    const tile_w_px = Math.round(Tx * invScale);
    const tile_h_px = Math.round(Ty * invScale);
    const grout_px = groutWork * invScale;

    let debug_overlay: string | null = null;
    if (wantDebug) debug_overlay = await buildOverlaySharp(W, H, vlinesWork, hlinesWork, maskArr);

    return NextResponse.json({
      ok: true,
      image: { width: srcW, height: srcH },
      tile_w_px,
      tile_h_px,
      grout_px,
      tile_w_mm: null,
      tile_h_mm: null,
      grout_mm: null,
      grid: { vlines, hlines },
      source: 'cv@sharp+projection+autocorr',
      debug_overlay,
      name: (file as any)?.name,
      type: (file as any)?.type,
    });
  } catch (e: any) {
    const msg = e?.message || String(e);
    if (process.env.DEBUG_ANALYZE === '1') {
      console.error('API /api/analyze fatal:', msg);
    }
    const code = /unsupported|decode|invalid image size|power of two/i.test(msg) ? 422 : (/unsupported|decode/i.test(msg) ? 415 : 500);
    return NextResponse.json({ ok: false, error: msg }, { status: code });
  }
}

Youez - 2016 - github.com/yon3zu
LinuXploit