diff --git a/.npmrc.docker b/.npmrc.docker index 1bc9e12..f45c489 100644 --- a/.npmrc.docker +++ b/.npmrc.docker @@ -1 +1 @@ -@bytelyst:registry=http://gitea.bytelyst.com:3300/api/packages/bytelyst/npm/ \ No newline at end of file +@bytelyst:registry=http://localhost:3300/api/packages/bytelyst/npm/ \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index be77a32..7e2b49d 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -4,12 +4,10 @@ WORKDIR /app/backend RUN npm install -g pnpm@10.6.5 COPY .npmrc.docker ./.npmrc +COPY .docker-deps/ ../.docker-deps/ COPY backend/package.json ./package.json -RUN --mount=type=secret,id=gitea_npm_token \ - TOKEN=$(cat /run/secrets/gitea_npm_token) && \ - echo "//gitea.bytelyst.com:3300/:_authToken=$TOKEN" >> .npmrc && \ - pnpm install --ignore-scripts --lockfile=false +RUN pnpm install --ignore-scripts --lockfile=false COPY backend/tsconfig.json ./tsconfig.json COPY backend/src/ ./src/ diff --git a/docker-compose.yml b/docker-compose.yml index 3ca7d90..9a44223 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,37 +1,38 @@ services: backend: + container_name: notelett-backend build: context: . dockerfile: backend/Dockerfile ports: - "4016:4016" environment: - - NODE_ENV=${NODE_ENV:-development} - - PORT=4016 - - HOST=0.0.0.0 - - PRODUCT_ID=notelett - - SERVICE_NAME=notelett-backend - - JWT_SECRET=${JWT_SECRET:-dev-secret-change-me-at-least-32-characters} - - COSMOS_ENDPOINT=${COSMOS_ENDPOINT:-} - - COSMOS_KEY=${COSMOS_KEY:-} - - COSMOS_DATABASE=${COSMOS_DATABASE:-bytelyst} - - DB_PROVIDER=${DB_PROVIDER:-memory} - - CORS_ORIGIN=${CORS_ORIGIN:-http://localhost:3000} - - PLATFORM_SERVICE_URL=${PLATFORM_SERVICE_URL:-http://localhost:4003} - - EXTRACTION_SERVICE_URL=${EXTRACTION_SERVICE_URL:-http://localhost:4005} - - MCP_SERVER_URL=${MCP_SERVER_URL:-http://localhost:4007} - - TELEMETRY_ENABLED=${TELEMETRY_ENABLED:-false} - - FEATURE_FLAGS_ENABLED=${FEATURE_FLAGS_ENABLED:-false} - - FIELD_ENCRYPT_ENABLED=${FIELD_ENCRYPT_ENABLED:-false} - - FIELD_ENCRYPT_KEY_PROVIDER=${FIELD_ENCRYPT_KEY_PROVIDER:-memory} - - LLM_PROVIDER=${LLM_PROVIDER:-mock} - - OPENAI_API_KEY=${OPENAI_API_KEY:-} - - OPENAI_BASE_URL=${OPENAI_BASE_URL:-} - - AZURE_OPENAI_ENDPOINT=${AZURE_OPENAI_ENDPOINT:-} - - AZURE_OPENAI_API_KEY=${AZURE_OPENAI_API_KEY:-} - - LLM_DEFAULT_MODEL=${LLM_DEFAULT_MODEL:-gpt-4o-mini} - - LLM_VISION_MODEL=${LLM_VISION_MODEL:-gpt-4o} - - LLM_EMBEDDING_MODEL=${LLM_EMBEDDING_MODEL:-text-embedding-3-small} + NODE_ENV: development + PORT: 4016 + HOST: 0.0.0.0 + PRODUCT_ID: notelett + SERVICE_NAME: notelett-backend + JWT_SECRET: ${JWT_SECRET:-dev-secret-change-me-at-least-32-characters} + COSMOS_ENDPOINT: ${COSMOS_ENDPOINT:-} + COSMOS_KEY: ${COSMOS_KEY:-} + COSMOS_DATABASE: ${COSMOS_DATABASE:-bytelyst} + DB_PROVIDER: ${DB_PROVIDER:-memory} + CORS_ORIGIN: ${CORS_ORIGIN:-http://localhost:3000} + PLATFORM_SERVICE_URL: ${PLATFORM_SERVICE_URL:-http://localhost:4003} + EXTRACTION_SERVICE_URL: ${EXTRACTION_SERVICE_URL:-http://localhost:4005} + MCP_SERVER_URL: ${MCP_SERVER_URL:-http://localhost:4007} + TELEMETRY_ENABLED: ${TELEMETRY_ENABLED:-false} + FEATURE_FLAGS_ENABLED: ${FEATURE_FLAGS_ENABLED:-false} + FIELD_ENCRYPT_ENABLED: ${FIELD_ENCRYPT_ENABLED:-false} + FIELD_ENCRYPT_KEY_PROVIDER: ${FIELD_ENCRYPT_KEY_PROVIDER:-memory} + LLM_PROVIDER: ${LLM_PROVIDER:-mock} + OPENAI_API_KEY: ${OPENAI_API_KEY:-} + OPENAI_BASE_URL: ${OPENAI_BASE_URL:-} + AZURE_OPENAI_ENDPOINT: ${AZURE_OPENAI_ENDPOINT:-} + AZURE_OPENAI_API_KEY: ${AZURE_OPENAI_API_KEY:-} + LLM_DEFAULT_MODEL: ${LLM_DEFAULT_MODEL:-gpt-4o-mini} + LLM_VISION_MODEL: ${LLM_VISION_MODEL:-gpt-4o} + LLM_EMBEDDING_MODEL: ${LLM_EMBEDDING_MODEL:-text-embedding-3-small} restart: unless-stopped healthcheck: test: ["CMD-SHELL", "node -e \"fetch('http://localhost:4016/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""] @@ -40,24 +41,25 @@ services: retries: 3 web: + container_name: notelett-web build: context: . dockerfile: web/Dockerfile args: - NEXT_PUBLIC_NOTES_API_URL: ${NEXT_PUBLIC_NOTES_API_URL:-http://localhost:4016/api} + NEXT_PUBLIC_NOTES_API_URL: https://api.bytelyst.com/notelett NEXT_PUBLIC_PLATFORM_SERVICE_URL: ${NEXT_PUBLIC_PLATFORM_SERVICE_URL:-http://localhost:4003/api} ports: - "3000:3045" environment: - - NODE_ENV=production - - NEXT_PUBLIC_PRODUCT_NAME=NoteLett - - NEXT_PUBLIC_PRODUCT_ID=notelett - - NEXT_PUBLIC_NOTES_API_URL=${NEXT_PUBLIC_NOTES_API_URL:-http://localhost:4016/api} - - NEXT_PUBLIC_PLATFORM_SERVICE_URL=${NEXT_PUBLIC_PLATFORM_SERVICE_URL:-http://localhost:4003/api} - - NEXT_PUBLIC_EXTRACTION_SERVICE_URL=${EXTRACTION_SERVICE_URL:-http://localhost:4005} - - NEXT_PUBLIC_MCP_SERVER_URL=${MCP_SERVER_URL:-http://localhost:4007}/api - - NEXT_PUBLIC_DIAGNOSTICS_URL=${DIAGNOSTICS_URL:-http://localhost:3000} - - NEXT_PUBLIC_TELEMETRY_TRANSPORT=fetch + NODE_ENV: production + NEXT_PUBLIC_PRODUCT_NAME: NoteLett + NEXT_PUBLIC_PRODUCT_ID: notelett + NEXT_PUBLIC_NOTES_API_URL: https://api.bytelyst.com/notelett + NEXT_PUBLIC_PLATFORM_SERVICE_URL: ${NEXT_PUBLIC_PLATFORM_SERVICE_URL:-http://localhost:4003/api} + NEXT_PUBLIC_EXTRACTION_SERVICE_URL: ${EXTRACTION_SERVICE_URL:-http://localhost:4005} + NEXT_PUBLIC_MCP_SERVER_URL: ${MCP_SERVER_URL:-http://localhost:4007}/api + NEXT_PUBLIC_DIAGNOSTICS_URL: ${DIAGNOSTICS_URL:-http://localhost:3000} + NEXT_PUBLIC_TELEMETRY_TRANSPORT: fetch depends_on: backend: condition: service_healthy diff --git a/scripts/docker-prep.sh b/scripts/docker-prep.sh new file mode 100755 index 0000000..b508a37 --- /dev/null +++ b/scripts/docker-prep.sh @@ -0,0 +1,146 @@ +#!/usr/bin/env bash +# Pack @bytelyst/* tarballs from the sibling common-plat repo for +# self-contained Docker builds that don't need the Gitea npm registry. +# +# Usage: +# ./scripts/docker-prep.sh # pack tarballs + rewrite package.json +# ./scripts/docker-prep.sh --restore # undo rewrite + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +COMMON_PLAT="${COMMON_PLAT:-${REPO_DIR}/../learning_ai/learning_ai_common_plat}" +if [[ ! -d "$COMMON_PLAT" && -d "${REPO_DIR}/../learning_ai_common_plat" ]]; then + COMMON_PLAT="${REPO_DIR}/../learning_ai_common_plat" +fi + +TARBALL_DIR="${REPO_DIR}/.docker-deps" + +# ── Restore mode ─────────────────────────────────────────────────── +if [[ "${1:-}" == "--restore" ]]; then + echo "Restoring original package.json files..." + for bak in $(find "$REPO_DIR" -name "package.json.bak" -not -path "*/node_modules/*"); do + mv "$bak" "${bak%.bak}" + echo " Restored ${bak%.bak}" + done + rm -rf "$TARBALL_DIR" + echo "Done." + exit 0 +fi + +# ── Pack mode ────────────────────────────────────────────────────── +if [[ ! -d "$COMMON_PLAT" ]]; then + echo "Common platform checkout not found: $COMMON_PLAT" >&2 + echo "Set COMMON_PLAT=/path/to/learning_ai_common_plat or place it at ../learning_ai/learning_ai_common_plat." >&2 + exit 1 +fi + +echo "=== docker-prep: packing @bytelyst/* tarballs ===" + +rm -rf "$TARBALL_DIR" +mkdir -p "$TARBALL_DIR" + +# Build all packages first (--filter limits to packages/, skips services/) +echo "Building @bytelyst/* packages..." +(cd "$COMMON_PLAT" && pnpm -r --filter './packages/*' build) + +# Pack each package and build a mapping of name → tarball filename +# (uses a temp file instead of associative array for bash 3.2 compat) +TARBALL_MAP_FILE=$(mktemp) +trap 'rm -f "$TARBALL_MAP_FILE"' EXIT + +for pkg_dir in "$COMMON_PLAT"/packages/*/; do + pkg_name=$(node -p "require('${pkg_dir}package.json').name" 2>/dev/null || true) + if [[ -z "$pkg_name" ]]; then continue; fi + + echo " Packing $pkg_name..." + tarball=$(cd "$pkg_dir" && pnpm pack --pack-destination "$TARBALL_DIR" 2>/dev/null | tail -1) + filename=$(basename "$tarball") + echo "${pkg_name}=${filename}" >> "$TARBALL_MAP_FILE" + echo " -> $filename" +done + +# ── Rewrite package.json files ───────────────────────────────────── +echo "" +echo "Rewriting package.json @bytelyst/* refs to .docker-deps/ tarballs..." + +rewrite_package_json() { + local pkg_file="$1" + local rel_prefix="$2" # relative path from package.json dir to repo root + + if [[ ! -f "$pkg_file" ]]; then return; fi + + # Backup + cp "$pkg_file" "${pkg_file}.bak" + + local tmp="${pkg_file}.tmp" + cp "$pkg_file" "$tmp" + + while IFS='=' read -r pkg_name tarball; do + [[ -z "$pkg_name" ]] && continue + node -e " + const fs = require('fs'); + const file = process.argv[1]; + const pkgName = process.argv[2]; + const replacement = process.argv[3]; + const p = JSON.parse(fs.readFileSync(file, 'utf8')); + for (const section of ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']) { + if (p[section] && Object.prototype.hasOwnProperty.call(p[section], pkgName)) { + p[section][pkgName] = replacement; + } + } + fs.writeFileSync(file, JSON.stringify(p, null, 2) + '\n'); + " "$tmp" "$pkg_name" "file:${rel_prefix}.docker-deps/${tarball}" + done < "$TARBALL_MAP_FILE" + + mv "$tmp" "$pkg_file" + echo " Rewrote $pkg_file" +} + +# Backend package.json +rewrite_package_json "${REPO_DIR}/backend/package.json" "../" + +# Web package.json +rewrite_package_json "${REPO_DIR}/web/package.json" "../" + +# ── Inject pnpm.overrides for transitive @bytelyst/* deps ───────── +# Tarball packages may depend on other @bytelyst/* packages (e.g. +# @bytelyst/fastify-core → @bytelyst/errors). Without overrides, pnpm +# tries to fetch them from the npm registry which fails. +inject_overrides() { + local pkg_file="$1" + local rel_prefix="$2" + + if [[ ! -f "$pkg_file" ]]; then return; fi + + local overrides="" + while IFS='=' read -r pkg_name tarball; do + [[ -z "$pkg_name" ]] && continue + if [[ -n "$overrides" ]]; then overrides="$overrides, "; fi + overrides="$overrides\"${pkg_name}\": \"file:${rel_prefix}.docker-deps/${tarball}\"" + done < "$TARBALL_MAP_FILE" + + if [[ -n "$overrides" ]]; then + node -e " + const fs = require('fs'); + const p = JSON.parse(fs.readFileSync('${pkg_file}', 'utf8')); + p.pnpm = p.pnpm || {}; + p.pnpm.overrides = { ...(p.pnpm.overrides || {}), ...JSON.parse('{${overrides}}') }; + fs.writeFileSync('${pkg_file}', JSON.stringify(p, null, 2) + '\n'); + " + echo " Injected pnpm.overrides into $pkg_file" + fi +} + +inject_overrides "${REPO_DIR}/backend/package.json" "../" +inject_overrides "${REPO_DIR}/web/package.json" "../" + +echo "" +echo "Done. Tarballs in $TARBALL_DIR" +echo "" +echo "To build Docker images:" +echo " docker compose build" +echo "" +echo "To restore after build:" +echo " ./scripts/docker-prep.sh --restore" diff --git a/web/Dockerfile b/web/Dockerfile index cde9cb2..54f5288 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -10,11 +10,9 @@ RUN npm config set strict-ssl false \ && npm install -g pnpm@10.6.5 COPY .npmrc.docker ./.npmrc -COPY .docker-deps/ /app/.docker-deps/ +COPY .docker-deps/ ../.docker-deps/ COPY web/package.json ./package.json -RUN --mount=type=secret,id=gitea_npm_token \ - export GITEA_NPM_TOKEN="$(cat /run/secrets/gitea_npm_token 2>/dev/null || echo '')" && \ - pnpm install --ignore-scripts --lockfile=false +RUN pnpm install --ignore-scripts --lockfile=false COPY web/next.config.ts ./next.config.ts COPY web/tsconfig.json ./tsconfig.json