step 5 (continued): prune stale root files, drop frontend split
Delete Dockerfile.frontend and docker-entrypoint-fe.sh (single-image decision per ROADMAP). Remove gutted /chat, /notes, /projects routes from nginx.conf. Fix Chat Switchboard branding in entrypoint and .env.example. Update DB names to switchboard_core, remove dead Extraction config. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,65 +0,0 @@
|
|||||||
# ==========================================
|
|
||||||
# Switchboard Core - Frontend Dockerfile
|
|
||||||
# ==========================================
|
|
||||||
# Static SPA served by nginx. In k8s, the
|
|
||||||
# Ingress routes /api/ and /ws to the backend
|
|
||||||
# service directly.
|
|
||||||
#
|
|
||||||
# Supports path-based deployment via BASE_PATH
|
|
||||||
# env var (e.g. /dev, /test, or empty for root).
|
|
||||||
# The entrypoint generates the nginx config and
|
|
||||||
# injects BASE_PATH into index.html at startup.
|
|
||||||
#
|
|
||||||
# v0.22.6+: Set BACKEND_URL to proxy page routes
|
|
||||||
# (/, /login, /admin, /editor, /notes, /settings)
|
|
||||||
# to the Go backend for server-rendered templates.
|
|
||||||
# When unset, page routes fall through to SPA.
|
|
||||||
#
|
|
||||||
# Build context: repo root
|
|
||||||
# ==========================================
|
|
||||||
|
|
||||||
# Stage 1: Download vendor libs (vendor/ is gitignored)
|
|
||||||
FROM node:20-alpine AS vendor
|
|
||||||
WORKDIR /tmp
|
|
||||||
RUN npm pack marked@16.3.0 dompurify@3.2.4 mermaid@11.4.1 katex@0.16.11 2>/dev/null && \
|
|
||||||
mkdir -p /vendor/mermaid /vendor/katex/fonts && \
|
|
||||||
tar xzf marked-*.tgz -C /tmp && cp /tmp/package/lib/marked.umd.js /vendor/marked.min.js && \
|
|
||||||
rm -rf /tmp/package && \
|
|
||||||
tar xzf dompurify-*.tgz -C /tmp && cp /tmp/package/dist/purify.min.js /vendor/purify.min.js && \
|
|
||||||
rm -rf /tmp/package && \
|
|
||||||
tar xzf mermaid-*.tgz -C /tmp && cp /tmp/package/dist/mermaid.min.js /vendor/mermaid/mermaid.min.js && \
|
|
||||||
rm -rf /tmp/package && \
|
|
||||||
tar xzf katex-*.tgz -C /tmp && \
|
|
||||||
cp /tmp/package/dist/katex.min.js /vendor/katex/katex.min.js && \
|
|
||||||
cp /tmp/package/dist/katex.min.css /vendor/katex/katex.min.css && \
|
|
||||||
cp -r /tmp/package/dist/fonts/* /vendor/katex/fonts/ && \
|
|
||||||
rm -rf /tmp/package /tmp/*.tgz
|
|
||||||
|
|
||||||
# Stage 2: CM6 editor bundle (esbuild → single IIFE)
|
|
||||||
FROM node:20-alpine AS cm6-build
|
|
||||||
WORKDIR /build
|
|
||||||
COPY src/editor/ /build/src/editor/
|
|
||||||
COPY scripts/build-editor.sh /build/scripts/build-editor.sh
|
|
||||||
RUN cd /build/src/editor && npm ci --loglevel=warn
|
|
||||||
RUN sh /build/scripts/build-editor.sh /build/dist
|
|
||||||
|
|
||||||
# Stage 3: nginx + entrypoint
|
|
||||||
FROM nginx:1-alpine
|
|
||||||
|
|
||||||
# Remove default config — entrypoint generates it
|
|
||||||
RUN rm -f /etc/nginx/conf.d/default.conf
|
|
||||||
|
|
||||||
COPY src/ /usr/share/nginx/html/
|
|
||||||
COPY --from=vendor /vendor/ /usr/share/nginx/html/vendor/
|
|
||||||
COPY --from=cm6-build /build/dist/ /usr/share/nginx/html/vendor/codemirror/
|
|
||||||
COPY VERSION /VERSION
|
|
||||||
COPY docker-entrypoint-fe.sh /docker-entrypoint-fe.sh
|
|
||||||
RUN chmod +x /docker-entrypoint-fe.sh
|
|
||||||
|
|
||||||
EXPOSE 80
|
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
||||||
CMD wget --no-verbose --tries=1 --spider http://localhost/healthz 2>/dev/null || \
|
|
||||||
wget --no-verbose --tries=1 --spider http://localhost/ || exit 1
|
|
||||||
|
|
||||||
ENTRYPOINT ["/docker-entrypoint-fe.sh"]
|
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
# Data persists in ./data/ between restarts.
|
# Data persists in ./data/ between restarts.
|
||||||
# To reset: rm -rf ./data/
|
# To reset: rm -rf ./data/
|
||||||
#
|
#
|
||||||
# For Postgres / multi-replica / k8s deployment see k8s/ and server/Dockerfile + Dockerfile.frontend.
|
# For Postgres / multi-replica / k8s deployment see k8s/ and Dockerfile.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
switchboard:
|
switchboard:
|
||||||
|
|||||||
@@ -1,190 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# ============================================
|
|
||||||
# Chat Switchboard - Frontend Entrypoint
|
|
||||||
# ============================================
|
|
||||||
# nginx serves static assets and proxies page
|
|
||||||
# routes to the Go backend for template rendering.
|
|
||||||
# API/WS routes are handled by Ingress directly.
|
|
||||||
#
|
|
||||||
# Environment (required):
|
|
||||||
# BACKEND_URL - Backend service URL
|
|
||||||
# (e.g. "http://switchboard-be:8080")
|
|
||||||
#
|
|
||||||
# Environment (optional):
|
|
||||||
# BASE_PATH - URL prefix (e.g. "/dev", "" for root)
|
|
||||||
# ENVIRONMENT - Environment name for logging
|
|
||||||
# ============================================
|
|
||||||
set -e
|
|
||||||
|
|
||||||
BASE_PATH="${BASE_PATH:-}"
|
|
||||||
BACKEND_URL="${BACKEND_URL:?BACKEND_URL is required (e.g. http://switchboard-be:8080)}"
|
|
||||||
ENVIRONMENT="${ENVIRONMENT:-production}"
|
|
||||||
|
|
||||||
# Read version from VERSION file (baked in at build time)
|
|
||||||
APP_VERSION="dev"
|
|
||||||
if [ -f /VERSION ]; then
|
|
||||||
APP_VERSION=$(cat /VERSION | tr -d '[:space:]')
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Compute build hash from JS file contents (unique per deploy)
|
|
||||||
BUILD_HASH=$(find /usr/share/nginx/html/js -name '*.js' -exec md5sum {} + 2>/dev/null | sort | md5sum | cut -c1-8)
|
|
||||||
if [ -z "${BUILD_HASH}" ]; then
|
|
||||||
BUILD_HASH=$(date +%s | md5sum | cut -c1-8)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Inject into index.html (redirect page) ──
|
|
||||||
sed -i \
|
|
||||||
-e "s|%%BASE_PATH%%|${BASE_PATH}|g" \
|
|
||||||
-e "s|%%APP_VERSION%%|${APP_VERSION}|g" \
|
|
||||||
/usr/share/nginx/html/index.html
|
|
||||||
|
|
||||||
# Inject version and build hash into service worker
|
|
||||||
sed -i \
|
|
||||||
-e "s|%%APP_VERSION%%|${APP_VERSION}|g" \
|
|
||||||
-e "s|%%BUILD_HASH%%|${BUILD_HASH}|g" \
|
|
||||||
/usr/share/nginx/html/sw.js
|
|
||||||
|
|
||||||
echo "✅ Frontend: BASE_PATH=${BASE_PATH:-/} VERSION=${APP_VERSION} BUILD=${BUILD_HASH} BACKEND=${BACKEND_URL}"
|
|
||||||
|
|
||||||
# ── Page route proxy blocks ─────────────────
|
|
||||||
page_proxy_block() {
|
|
||||||
local path="$1"
|
|
||||||
cat <<PROXY
|
|
||||||
location ${path} {
|
|
||||||
proxy_pass ${BACKEND_URL};
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Host \$host;
|
|
||||||
proxy_set_header X-Real-IP \$remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
||||||
}
|
|
||||||
PROXY
|
|
||||||
}
|
|
||||||
|
|
||||||
PAGE_ROUTES=$(cat <<ROUTES
|
|
||||||
# ── Page routes → backend ────────────────
|
|
||||||
$(page_proxy_block "= ${BASE_PATH}/")
|
|
||||||
$(page_proxy_block "= ${BASE_PATH}/login")
|
|
||||||
location ~ ^${BASE_PATH}/chat/[^/]+\$ {
|
|
||||||
proxy_pass ${BACKEND_URL};
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Host \$host;
|
|
||||||
proxy_set_header X-Real-IP \$remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
||||||
}
|
|
||||||
$(page_proxy_block "${BASE_PATH}/admin")
|
|
||||||
$(page_proxy_block "${BASE_PATH}/editor")
|
|
||||||
$(page_proxy_block "${BASE_PATH}/notes")
|
|
||||||
$(page_proxy_block "${BASE_PATH}/projects")
|
|
||||||
$(page_proxy_block "${BASE_PATH}/settings")
|
|
||||||
$(page_proxy_block "${BASE_PATH}/w")
|
|
||||||
# v0.27.0: Extension surface page routes
|
|
||||||
$(page_proxy_block "${BASE_PATH}/s")
|
|
||||||
# v0.27.0: Extension surface static assets (proxied to backend; on-disk at /data/packages/)
|
|
||||||
$(page_proxy_block "${BASE_PATH}/surfaces")
|
|
||||||
ROUTES
|
|
||||||
)
|
|
||||||
|
|
||||||
# ── Generate nginx config ───────────────────
|
|
||||||
if [ -z "${BASE_PATH}" ]; then
|
|
||||||
cat > /etc/nginx/conf.d/default.conf <<NGINX
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name _;
|
|
||||||
root /usr/share/nginx/html;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
gzip on;
|
|
||||||
gzip_vary on;
|
|
||||||
gzip_min_length 1024;
|
|
||||||
gzip_proxied expired no-cache no-store private auth;
|
|
||||||
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/javascript;
|
|
||||||
|
|
||||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2)$ {
|
|
||||||
expires 1y;
|
|
||||||
add_header Cache-Control "public, immutable";
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /sw.js {
|
|
||||||
expires off;
|
|
||||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
|
||||||
}
|
|
||||||
|
|
||||||
location /branding/ {
|
|
||||||
alias /branding/;
|
|
||||||
expires 1h;
|
|
||||||
add_header Cache-Control "public";
|
|
||||||
try_files \$uri =404;
|
|
||||||
}
|
|
||||||
|
|
||||||
${PAGE_ROUTES}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files \$uri \$uri/ /index.html;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /healthz {
|
|
||||||
return 200 'ok';
|
|
||||||
add_header Content-Type text/plain;
|
|
||||||
}
|
|
||||||
|
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
||||||
add_header X-Content-Type-Options "nosniff" always;
|
|
||||||
add_header X-XSS-Protection "1; mode=block" always;
|
|
||||||
}
|
|
||||||
NGINX
|
|
||||||
else
|
|
||||||
cat > /etc/nginx/conf.d/default.conf <<NGINX
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name _;
|
|
||||||
|
|
||||||
gzip on;
|
|
||||||
gzip_vary on;
|
|
||||||
gzip_min_length 1024;
|
|
||||||
gzip_proxied expired no-cache no-store private auth;
|
|
||||||
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/javascript;
|
|
||||||
|
|
||||||
${PAGE_ROUTES}
|
|
||||||
|
|
||||||
location ${BASE_PATH}/ {
|
|
||||||
alias /usr/share/nginx/html/;
|
|
||||||
try_files \$uri \$uri/ ${BASE_PATH}/index.html;
|
|
||||||
|
|
||||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2)\$ {
|
|
||||||
expires 1y;
|
|
||||||
add_header Cache-Control "public, immutable";
|
|
||||||
}
|
|
||||||
|
|
||||||
location = ${BASE_PATH}/sw.js {
|
|
||||||
alias /usr/share/nginx/html/sw.js;
|
|
||||||
expires off;
|
|
||||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
location ${BASE_PATH}/branding/ {
|
|
||||||
alias /branding/;
|
|
||||||
expires 1h;
|
|
||||||
add_header Cache-Control "public";
|
|
||||||
try_files \$uri =404;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = ${BASE_PATH} {
|
|
||||||
return 301 ${BASE_PATH}/;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /healthz {
|
|
||||||
return 200 'ok';
|
|
||||||
add_header Content-Type text/plain;
|
|
||||||
}
|
|
||||||
|
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
||||||
add_header X-Content-Type-Options "nosniff" always;
|
|
||||||
add_header X-XSS-Protection "1; mode=block" always;
|
|
||||||
}
|
|
||||||
NGINX
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec nginx -g 'daemon off;'
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# ============================================
|
# ============================================
|
||||||
# Chat Switchboard - Backend Launcher
|
# Switchboard Core - Backend Launcher
|
||||||
# ============================================
|
# ============================================
|
||||||
# Runs as an nginx entrypoint.d hook.
|
# Runs as an nginx entrypoint.d hook.
|
||||||
# Starts the Go backend in the background
|
# Starts the Go backend in the background
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "🔀 Starting Chat Switchboard backend..."
|
echo "🔀 Starting Switchboard Core backend..."
|
||||||
|
|
||||||
# Launch Go backend in background (from /app so migrations are found)
|
# Launch Go backend in background (from /app so migrations are found)
|
||||||
cd /app
|
cd /app
|
||||||
|
|||||||
10
nginx.conf
10
nginx.conf
@@ -68,19 +68,9 @@ server {
|
|||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ ^/chat/[^/]+$ {
|
|
||||||
proxy_pass $backend;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /admin { proxy_pass $backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
|
location /admin { proxy_pass $backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
|
||||||
location /team-admin { proxy_pass $backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
|
location /team-admin { proxy_pass $backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
|
||||||
location /notes { proxy_pass $backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
|
|
||||||
location /settings { proxy_pass $backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
|
location /settings { proxy_pass $backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
|
||||||
location /projects { proxy_pass $backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
|
|
||||||
location /w/ { proxy_pass $backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
|
location /w/ { proxy_pass $backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
|
||||||
|
|
||||||
# v0.27.0: Extension surface page routes → backend (Go templates)
|
# v0.27.0: Extension surface page routes → backend (Go templates)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Chat Switchboard v0.12 - Server Environment Variables
|
# Switchboard Core - Server Environment Variables
|
||||||
# Copy this file to .env and fill in the values
|
# Copy this file to .env and fill in the values
|
||||||
|
|
||||||
# ── Server ───────────────────────────────────
|
# ── Server ───────────────────────────────────
|
||||||
@@ -9,15 +9,17 @@ BASE_PATH= # e.g. /chat for path-based routing
|
|||||||
# ── Database (PostgreSQL) ────────────────────
|
# ── Database (PostgreSQL) ────────────────────
|
||||||
DB_HOST=localhost
|
DB_HOST=localhost
|
||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
DB_USER=chat_switchboard
|
DB_USER=switchboard_core
|
||||||
DB_PASSWORD=your-secure-password-here
|
DB_PASSWORD=your-secure-password-here
|
||||||
DB_NAME=chat_switchboard
|
DB_NAME=switchboard_core
|
||||||
DB_SSL_MODE=disable
|
DB_SSL_MODE=disable
|
||||||
DB_MAX_CONNS=25
|
DB_MAX_CONNS=25
|
||||||
|
|
||||||
# ── Auth ─────────────────────────────────────
|
# ── Auth ─────────────────────────────────────
|
||||||
JWT_SECRET=your-super-secret-jwt-key-change-in-production
|
JWT_SECRET=your-super-secret-jwt-key-change-in-production
|
||||||
JWT_EXPIRATION=24h
|
# JWT durations (hardcoded in handlers/auth.go — not configurable via env):
|
||||||
|
# Access token: 15 minutes
|
||||||
|
# Refresh token: 7 days
|
||||||
JWT_ISSUER=switchboard-core
|
JWT_ISSUER=switchboard-core
|
||||||
|
|
||||||
# ── Bootstrap Admin ──────────────────────────
|
# ── Bootstrap Admin ──────────────────────────
|
||||||
@@ -61,7 +63,3 @@ STORAGE_PATH=/data/storage
|
|||||||
# S3_REGION=us-east-1
|
# S3_REGION=us-east-1
|
||||||
# S3_PREFIX= # optional key prefix for shared buckets
|
# S3_PREFIX= # optional key prefix for shared buckets
|
||||||
# S3_FORCE_PATH_STYLE=true # required for MinIO, Ceph RGW
|
# S3_FORCE_PATH_STYLE=true # required for MinIO, Ceph RGW
|
||||||
|
|
||||||
# ── Extraction ───────────────────────────────
|
|
||||||
# EXTRACTION_MODE=inline # "inline" or "sidecar"
|
|
||||||
# EXTRACTION_CONCURRENCY=3
|
|
||||||
|
|||||||
Reference in New Issue
Block a user