Ctrl K
ComunidadGuías…

src/domain/local-n8n-sidecar.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { validateLocalDockerHost } from "../infrastructure/local-process.js";
import { validateSiwcAuthBinding, validateSiwcRegistrationId, validateSiwcRuntimeId } from "./safety.js";
import { validateDockerName } from "./validation.js";
import { validateInstallId, validateSha256Verifier } from "./local-endpoints.js";

export const LOCAL_N8N_SIDECAR_TARGET = "n8n-openai-oauth";
export const LOCAL_N8N_SIDECAR_HOSTNAME = "n8n-openai-oauth";
export const LOCAL_N8N_SIDECAR_PORT = 10_531;
export const LOCAL_N8N_SIDECAR_ENDPOINT =
  "http://n8n-openai-oauth:10531/v1";

const DOCKER_OBJECT_ID_PATTERN = /^[a-f0-9]{64}$/u;
const AUTH_GENERATION_MAX_BYTES = 128;

export function validateDockerObjectId(value, label = "Docker object") {
  if (typeof value !== "string" || !DOCKER_OBJECT_ID_PATTERN.test(value)) {
    throw new TypeError(`${label} identity is invalid.`);
  }
  return value;
}


export function createLocalN8nSidecarPlan({
  dockerHost,
  n8nContainerId,
  n8nContainerName,
  dockerNetworkId,
  networkName,
  authBinding,
}) {
  return {
    kind: "n8n-sidecar",
    target: LOCAL_N8N_SIDECAR_TARGET,
    label: "Self-hosted n8n bridge",
    endpoint: LOCAL_N8N_SIDECAR_ENDPOINT,
    baseUrl: LOCAL_N8N_SIDECAR_ENDPOINT,
    protocol: "openai-v1",
    upstreamAuth: "chatgpt-oauth",
    dockerHost: validateLocalDockerHost(dockerHost),
    n8nContainerId: validateDockerObjectId(
      n8nContainerId,
      "n8n container",
    ),
    n8nContainerName: validateDockerName(n8nContainerName),
    dockerNetworkId: validateDockerObjectId(
      dockerNetworkId,
      "Docker network",
    ),
    networkName: validateDockerName(networkName),
    authBinding: validateSiwcAuthBinding(authBinding),
    managedPath: "~/.relmio/local/n8n-openai-oauth",
    hostPublication: "none",
    unofficial: false,
  };
}

export function normalizeLocalN8nSidecarPlan(value) {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    throw new TypeError("The local n8n sidecar plan is invalid.");
  }
  const normalized = createLocalN8nSidecarPlan({
    dockerHost: value.dockerHost,
    n8nContainerId: value.n8nContainerId,
    n8nContainerName: value.n8nContainerName,
    dockerNetworkId: value.dockerNetworkId,
    networkName: value.networkName,
    authBinding: value.authBinding,
  });
  for (const [name, expected] of Object.entries(normalized)) {
    if (name === "authBinding") {
      for (const [field, bound] of Object.entries(expected)) {
        if (value.authBinding?.[field] !== bound) {
          throw new TypeError("The local n8n sidecar plan is invalid.");
        }
      }
    } else if (value[name] !== expected) {
      throw new TypeError("The local n8n sidecar plan is invalid.");
    }
  }
  return normalized;
}

export function createLocalN8nSidecarDockerfile({ installId }) {
  const safeInstallId = validateInstallId(installId);
  return `FROM node:24-bookworm-slim

ARG RELMIO_INSTALL_ID=${safeInstallId}
LABEL io.relmio.managed="true" \\
      io.relmio.target="${LOCAL_N8N_SIDECAR_TARGET}" \\
      io.relmio.install="${safeInstallId}"

WORKDIR /app
COPY --chown=node:node package.json package-lock.json ./
RUN npm ci --omit=dev --ignore-scripts && npm cache clean --force
COPY --chown=node:node services/ ./services/
COPY --chown=node:node gateway/ ./gateway/
COPY --chown=node:node infrastructure/ ./infrastructure/
USER node
ENTRYPOINT ["node", "/app/gateway/openai-oauth-sidecar.mjs"]
`;
}

export function createLocalN8nSidecarDockerignore() {
  return "**\n!Dockerfile\n!package.json\n!package-lock.json\n!services/\n!services/siwc-session.mjs\n!services/siwc-handoff.mjs\n!services/local-integration-lifecycle-lock.js\n!services/codex-images.mjs\n!services/model-discovery.mjs\n!gateway/\n!gateway/openai-oauth-sidecar.mjs\n!infrastructure/\n!infrastructure/local-process.js\n!infrastructure/process-identity.js\n";
}

export function createLocalN8nSidecarComposeFile({
  installId,
  networkName,
  registrationId,
  tokenSha256,
}) {
  const safeInstallId = validateInstallId(installId);
  const safeNetworkName = validateDockerName(networkName);
  const safeRegistrationId = validateSiwcRegistrationId(registrationId);
  const safeRuntimeId = validateSiwcRuntimeId(safeInstallId);
  const safeVerifier = validateSha256Verifier(tokenSha256);
  const image = `relmio-n8n-openai-oauth-${safeInstallId}:local`;

  return `services:
  openai-oauth:
    image: ${image}
    build:
      context: .
      dockerfile: Dockerfile
      args:
        RELMIO_INSTALL_ID: "${safeInstallId}"
      labels:
        io.relmio.managed: "true"
        io.relmio.target: "${LOCAL_N8N_SIDECAR_TARGET}"
        io.relmio.install: "${safeInstallId}"
    restart: unless-stopped
    init: true
    environment:
      N8N_OPENAI_OAUTH_HOME: /home/node/.relmio-siwc
      RELMIO_REGISTRATION_ID: "${safeRegistrationId}"
      RELMIO_RUNTIME_ID: "${safeRuntimeId}"
      RELMIO_GATEWAY_TOKEN_SHA256: "${safeVerifier}"
    volumes:
      - siwc-store:/home/node/.relmio-siwc
    expose:
      - "10531"
    networks:
      n8n-shared:
        aliases:
          - ${LOCAL_N8N_SIDECAR_HOSTNAME}
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    read_only: true
    tmpfs:
      - /tmp:size=16m,mode=1777
      - /home/node/.local:uid=1000,gid=1000,mode=0700
    pids_limit: 128
    mem_limit: 512m
    cpus: 1.0
    healthcheck:
      test:
        - CMD
        - node
        - -e
        - 'fetch("http://127.0.0.1:10531/health").then((response) => process.exit(response.ok ? 0 : 1)).catch(() => process.exit(1))'
      interval: 10s
      timeout: 5s
      retries: 6
      start_period: 20s
    labels:
      io.relmio.managed: "true"
      io.relmio.target: "${LOCAL_N8N_SIDECAR_TARGET}"
      io.relmio.install: "${safeInstallId}"

  credential-seed:
    image: ${image}
    pull_policy: never
    profiles:
      - relmio-credential-seed
    restart: "no"
    network_mode: none
    user: "0:0"
    entrypoint:
      - /bin/sh
      - -c
    command:
      - |
        set -eu
        owner="$$(stat -c '%u:%g:%a' /run/relmio-auth)"
        if [ "$$owner" = "0:0:755" ] || [ "$$owner" = "0:0:700" ]; then
          chmod 0700 /run/relmio-auth
          chown 1000:1000 /run/relmio-auth
        elif [ "$$owner" != "1000:1000:700" ]; then
          exit 1
        fi
        [ "$$(stat -c '%u:%g:%a' /run/relmio-auth)" = "1000:1000:700" ]
    volumes:
      - siwc-store:/run/relmio-auth
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
    read_only: true
    pids_limit: 32
    mem_limit: 64m
    cpus: 0.25
    logging:
      driver: "none"
    labels:
      io.relmio.managed: "true"
      io.relmio.target: "${LOCAL_N8N_SIDECAR_TARGET}"
      io.relmio.install: "${safeInstallId}"

networks:
  n8n-shared:
    external: true
    name: ${safeNetworkName}

volumes:
  siwc-store:
    labels:
      io.relmio.managed: "true"
      io.relmio.target: "${LOCAL_N8N_SIDECAR_TARGET}"
      io.relmio.install: "${safeInstallId}"
`;
}