Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  nodes-camera.ts

  Sprache: JAVA
 

Spracherkennung für: .ts vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

import * as fs from "node:fs/promises";
import * as path from "node:path";
import { fetchWithSsrFGuard } from "../infra/net/fetch-guard.js";
import { normalizeHostname } from "../infra/net/hostname.js";
import { resolveCliName } from "./cli-name.js";
import {
  asBoolean,
  asNumber,
  asRecord,
  asString,
  resolveTempPathParts,
} from "./nodes-media-utils.js";

const MAX_CAMERA_URL_DOWNLOAD_BYTES = 250 * 1024 * 1024;

export type CameraFacing = "front" | "back";

export type CameraSnapPayload = {
  format: string;
  base64?: string;
  url?: string;
  width: number;
  height: number;
};

export type CameraClipPayload = {
  format: string;
  base64?: string;
  url?: string;
  durationMs: number;
  hasAudio: boolean;
};

export function parseCameraSnapPayload(value: unknown): CameraSnapPayload {
  const obj = asRecord(value);
  const format = asString(obj.format);
  const base64 = asString(obj.base64);
  const url = asString(obj.url);
  const width = asNumber(obj.width);
  const height = asNumber(obj.height);
  if (!format || (!base64 && !url) || width === undefined || height === undefined) {
    throw new Error("invalid camera.snap payload");
  }
  return { format, ...(base64 ? { base64 } : {}), ...(url ? { url } : {}), width, height };
}

export function parseCameraClipPayload(value: unknown): CameraClipPayload {
  const obj = asRecord(value);
  const format = asString(obj.format);
  const base64 = asString(obj.base64);
  const url = asString(obj.url);
  const durationMs = asNumber(obj.durationMs);
  const hasAudio = asBoolean(obj.hasAudio);
  if (!format || (!base64 && !url) || durationMs === undefined || hasAudio === undefined) {
    throw new Error("invalid camera.clip payload");
  }
  return { format, ...(base64 ? { base64 } : {}), ...(url ? { url } : {}), durationMs, hasAudio };
}

export function cameraTempPath(opts: {
  kind: "snap" | "clip";
  facing?: CameraFacing;
  ext: string;
  tmpDir?: string;
  id?: string;
}) {
  const { tmpDir, id, ext } = resolveTempPathParts({
    tmpDir: opts.tmpDir,
    id: opts.id,
    ext: opts.ext,
  });
  const facingPart = opts.facing ? `-${opts.facing}` : "";
  const cliName = resolveCliName();
  return path.join(tmpDir, `${cliName}-camera-${opts.kind}${facingPart}-${id}${ext}`);
}

export async function writeUrlToFile(
  filePath: string,
  url: string,
  opts: { expectedHost: string },
) {
  const parsed = new URL(url);
  if (parsed.protocol !== "https:") {
    throw new Error(`writeUrlToFile: only https URLs are allowed, got ${parsed.protocol}`);
  }
  const expectedHost = normalizeHostname(opts.expectedHost);
  if (!expectedHost) {
    throw new Error("writeUrlToFile: expectedHost is required");
  }
  if (normalizeHostname(parsed.hostname) !== expectedHost) {
    throw new Error(
      `writeUrlToFile: url host ${parsed.hostname} must match node host ${opts.expectedHost}`,
    );
  }

  const policy = {
    allowPrivateNetwork: true,
    allowedHostnames: [expectedHost],
    hostnameAllowlist: [expectedHost],
  };

  let release: () => Promise<void> = async () => {};
  let bytes = 0;
  try {
    const guarded = await fetchWithSsrFGuard({
      url,
      auditContext: "writeUrlToFile",
      policy,
    });
    release = guarded.release;
    const finalUrl = new URL(guarded.finalUrl);
    if (finalUrl.protocol !== "https:") {
      throw new Error(`writeUrlToFile: redirect resolved to non-https URL ${guarded.finalUrl}`);
    }
    if (normalizeHostname(finalUrl.hostname) !== expectedHost) {
      throw new Error(
        `writeUrlToFile: redirect host ${finalUrl.hostname} must match node host ${opts.expectedHost}`,
      );
    }
    const res = guarded.response;
    if (!res.ok) {
      throw new Error(`failed to download ${url}: ${res.status} ${res.statusText}`);
    }

    const contentLengthRaw = res.headers.get("content-length");
    const contentLength = contentLengthRaw ? Number.parseInt(contentLengthRaw, 10) : undefined;
    if (
      typeof contentLength === "number" &&
      Number.isFinite(contentLength) &&
      contentLength > MAX_CAMERA_URL_DOWNLOAD_BYTES
    ) {
      throw new Error(
        `writeUrlToFile: content-length ${contentLength} exceeds max ${MAX_CAMERA_URL_DOWNLOAD_BYTES}`,
      );
    }

    const body = res.body;
    if (!body) {
      throw new Error(`failed to download ${url}: empty response body`);
    }

    const fileHandle = await fs.open(filePath, "w");
    let thrown: unknown;
    try {
      const reader = body.getReader();
      while (true) {
        const { done, value } = await reader.read();
        if (done) {
          break;
        }
        if (!value || value.byteLength === 0) {
          continue;
        }
        bytes += value.byteLength;
        if (bytes > MAX_CAMERA_URL_DOWNLOAD_BYTES) {
          throw new Error(
            `writeUrlToFile: downloaded ${bytes} bytes, exceeds max ${MAX_CAMERA_URL_DOWNLOAD_BYTES}`,
          );
        }
        await fileHandle.write(value);
      }
    } catch (err) {
      thrown = err;
    } finally {
      await fileHandle.close();
    }

    if (thrown) {
      await fs.unlink(filePath).catch(() => {});
      throw thrown;
    }
  } finally {
    await release();
  }

  return { path: filePath, bytes };
}

export async function writeBase64ToFile(filePath: string, base64: string) {
  const buf = Buffer.from(base64, "base64");
  await fs.writeFile(filePath, buf);
  return { path: filePath, bytes: buf.length };
}

export function requireNodeRemoteIp(remoteIp?: string): string {
  const normalized = remoteIp?.trim();
  if (!normalized) {
    throw new Error("camera URL payload requires node remoteIp");
  }
  return normalized;
}

export async function writeCameraPayloadToFile(params: {
  filePath: string;
  payload: { url?: string; base64?: string };
  expectedHost?: string;
  invalidPayloadMessage?: string;
}) {
  if (params.payload.url) {
    await writeUrlToFile(params.filePath, params.payload.url, {
      expectedHost: requireNodeRemoteIp(params.expectedHost),
    });
    return;
  }
  if (params.payload.base64) {
    await writeBase64ToFile(params.filePath, params.payload.base64);
    return;
  }
  throw new Error(params.invalidPayloadMessage ?? "invalid camera payload");
}

export async function writeCameraClipPayloadToFile(params: {
  payload: CameraClipPayload;
  facing: CameraFacing;
  tmpDir?: string;
  id?: string;
  expectedHost?: string;
}): Promise<string> {
  const filePath = cameraTempPath({
    kind: "clip",
    facing: params.facing,
    ext: params.payload.format,
    tmpDir: params.tmpDir,
    id: params.id,
  });
  await writeCameraPayloadToFile({
    filePath,
    payload: params.payload,
    expectedHost: params.expectedHost,
    invalidPayloadMessage: "invalid camera.clip payload",
  });
  return filePath;
}

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet am  2026-04-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge