feat: add Docker configuration and environment setup for backend and frontend
This commit is contained in:
44
.env.example
Normal file
44
.env.example
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Copy this file to .env and fill in the values before running `docker compose up`.
|
||||||
|
|
||||||
|
# ── Ports (optional, defaults shown) ─────────────────────────────────────────
|
||||||
|
BACKEND_PORT=3000
|
||||||
|
FRONTEND_PORT=3001
|
||||||
|
|
||||||
|
# ── Auth ──────────────────────────────────────────────────────────────────────
|
||||||
|
# Generate: openssl rand -hex 32
|
||||||
|
JWT_SECRET=change-me-generate-with-openssl-rand-hex-32
|
||||||
|
|
||||||
|
# Generate: openssl rand -base64 64
|
||||||
|
COOKIE_SECRET=change-me-must-be-at-least-64-characters-long-for-production!!
|
||||||
|
|
||||||
|
JWT_EXPIRY_HOURS=24
|
||||||
|
|
||||||
|
# Set to true when serving over HTTPS
|
||||||
|
SECURE_COOKIE=false
|
||||||
|
PRODUCTION=false
|
||||||
|
|
||||||
|
# ── CORS ──────────────────────────────────────────────────────────────────────
|
||||||
|
# Origin(s) from which the browser will hit the backend, comma-separated.
|
||||||
|
# Must match what users type in their browser for the frontend.
|
||||||
|
# Example (local): http://localhost:3001
|
||||||
|
# Example (remote): https://tv.example.com
|
||||||
|
CORS_ALLOWED_ORIGINS=http://localhost:3001
|
||||||
|
|
||||||
|
# ── Frontend / API URL ────────────────────────────────────────────────────────
|
||||||
|
# Public URL of the BACKEND, as seen from the user's browser.
|
||||||
|
# This is baked into the Next.js client bundle at build time.
|
||||||
|
# Example (local): http://localhost:3000/api/v1
|
||||||
|
# Example (remote): https://api.example.com/api/v1
|
||||||
|
NEXT_PUBLIC_API_URL=http://localhost:3000/api/v1
|
||||||
|
|
||||||
|
# ── Jellyfin ──────────────────────────────────────────────────────────────────
|
||||||
|
JELLYFIN_BASE_URL=http://jellyfin:8096
|
||||||
|
JELLYFIN_API_KEY=your-jellyfin-api-key-here
|
||||||
|
JELLYFIN_USER_ID=your-jellyfin-user-id-here
|
||||||
|
|
||||||
|
# ── Database pool (optional) ──────────────────────────────────────────────────
|
||||||
|
DB_MAX_CONNECTIONS=5
|
||||||
|
DB_MIN_CONNECTIONS=1
|
||||||
|
|
||||||
|
# ── PostgreSQL (optional, uncomment db service in compose.yml first) ──────────
|
||||||
|
# POSTGRES_PASSWORD=change-me
|
||||||
73
compose.yml
Normal file
73
compose.yml
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
services:
|
||||||
|
|
||||||
|
# ── Backend (Rust / Axum) ──────────────────────────────────────────────────
|
||||||
|
backend:
|
||||||
|
build: ./k-tv-backend
|
||||||
|
ports:
|
||||||
|
- "${BACKEND_PORT:-3000}:3000"
|
||||||
|
environment:
|
||||||
|
- HOST=0.0.0.0
|
||||||
|
- PORT=3000
|
||||||
|
- DATABASE_URL=sqlite:///app/data/k-tv.db?mode=rwc
|
||||||
|
# Allow requests from the browser (the user-facing frontend URL)
|
||||||
|
- CORS_ALLOWED_ORIGINS=${CORS_ALLOWED_ORIGINS}
|
||||||
|
# Auth — generate with: openssl rand -hex 32
|
||||||
|
- JWT_SECRET=${JWT_SECRET}
|
||||||
|
# Cookie secret — generate with: openssl rand -base64 64
|
||||||
|
- COOKIE_SECRET=${COOKIE_SECRET}
|
||||||
|
- JWT_EXPIRY_HOURS=${JWT_EXPIRY_HOURS:-24}
|
||||||
|
- SECURE_COOKIE=${SECURE_COOKIE:-false}
|
||||||
|
- PRODUCTION=${PRODUCTION:-false}
|
||||||
|
- DB_MAX_CONNECTIONS=${DB_MAX_CONNECTIONS:-5}
|
||||||
|
- DB_MIN_CONNECTIONS=${DB_MIN_CONNECTIONS:-1}
|
||||||
|
# Jellyfin — all three required for schedule generation
|
||||||
|
- JELLYFIN_BASE_URL=${JELLYFIN_BASE_URL}
|
||||||
|
- JELLYFIN_API_KEY=${JELLYFIN_API_KEY}
|
||||||
|
- JELLYFIN_USER_ID=${JELLYFIN_USER_ID}
|
||||||
|
volumes:
|
||||||
|
- backend_data:/app/data
|
||||||
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "curl -f http://localhost:3000/api/v1/config || exit 1"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
# ── Frontend (Next.js) ────────────────────────────────────────────────────
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./k-tv-frontend
|
||||||
|
args:
|
||||||
|
# Browser-visible backend URL — must be reachable from the user's browser.
|
||||||
|
# If running on a server: http://your-server-ip:3000/api/v1
|
||||||
|
# Baked into the client bundle at build time; rebuild after changing.
|
||||||
|
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:3000/api/v1}
|
||||||
|
ports:
|
||||||
|
- "${FRONTEND_PORT:-3001}:3001"
|
||||||
|
environment:
|
||||||
|
# Server-side API URL — uses Docker's internal network, never exposed.
|
||||||
|
# Next.js API routes (e.g. /api/stream/[channelId]) use this.
|
||||||
|
API_URL: http://backend:3000/api/v1
|
||||||
|
depends_on:
|
||||||
|
backend:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
backend_data:
|
||||||
|
|
||||||
|
# ── Optional: PostgreSQL ───────────────────────────────────────────────────
|
||||||
|
# Uncomment the db service and set DATABASE_URL in backend's environment:
|
||||||
|
# DATABASE_URL: postgres://ktv:${POSTGRES_PASSWORD}@db:5432/ktv
|
||||||
|
#
|
||||||
|
# db:
|
||||||
|
# image: postgres:16-alpine
|
||||||
|
# environment:
|
||||||
|
# POSTGRES_USER: ktv
|
||||||
|
# POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
# POSTGRES_DB: ktv
|
||||||
|
# volumes:
|
||||||
|
# - db_data:/var/lib/postgresql/data
|
||||||
|
# restart: unless-stopped
|
||||||
|
#
|
||||||
|
# db_data:
|
||||||
4
k-tv-frontend/.dockerignore
Normal file
4
k-tv-frontend/.dockerignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.next
|
||||||
|
node_modules
|
||||||
|
.env*
|
||||||
|
*.md
|
||||||
46
k-tv-frontend/Dockerfile
Normal file
46
k-tv-frontend/Dockerfile
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# ── Stage 1: Install dependencies ────────────────────────────────────────────
|
||||||
|
FROM oven/bun:1 AS deps
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json bun.lock ./
|
||||||
|
RUN bun install --frozen-lockfile
|
||||||
|
|
||||||
|
# ── Stage 2: Build ────────────────────────────────────────────────────────────
|
||||||
|
FROM oven/bun:1 AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# NEXT_PUBLIC_* vars are baked into the client bundle at build time.
|
||||||
|
# Pass the public backend URL via --build-arg (see compose.yml).
|
||||||
|
ARG NEXT_PUBLIC_API_URL=http://localhost:3000/api/v1
|
||||||
|
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
|
RUN bun run build
|
||||||
|
|
||||||
|
# ── Stage 3: Production runner ────────────────────────────────────────────────
|
||||||
|
FROM node:22-alpine AS runner
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs \
|
||||||
|
&& adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
# standalone output + static assets
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
EXPOSE 3001
|
||||||
|
ENV PORT=3001
|
||||||
|
ENV HOSTNAME=0.0.0.0
|
||||||
|
|
||||||
|
CMD ["node", "server.js"]
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
/* config options here */
|
output: "standalone",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|||||||
Reference in New Issue
Block a user