Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ dist/
.DS_Store
coverage/
*.log
downloads/
hlsDownloads/
server/logs/
*.log
53 changes: 53 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
services:
# ── Redis ────────────────────────────────────────────────
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5

# ── API Server ───────────────────────────────────────────
api:
build:
context: ./server
dockerfile: Dockerfile
target: api
ports:
- "${PORT:-8080}:${PORT:-8080}"
env_file:
- ./server/.env
environment:
- IOREDIS_URL=redis://redis:6379
depends_on:
redis:
condition: service_healthy
restart: unless-stopped

# ── Workers (transcode + HLS) ───────────────────────────
worker:
build:
context: ./server
dockerfile: Dockerfile
target: worker
env_file:
- ./server/.env
environment:
- IOREDIS_URL=redis://redis:6379
depends_on:
redis:
condition: service_healthy
restart: unless-stopped
# Workers may need more memory for FFmpeg transcoding
deploy:
resources:
limits:
memory: 2G

volumes:
redis_data:
12 changes: 12 additions & 0 deletions server/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
node_modules
dist
downloads
hlsDownloads
logs
.env
.env.*
!.env.example
.DS_Store
*.log
.git
.Dockerfile
Comment on lines +1 to +12
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if there's actually a .Dockerfile file or if it should be Dockerfile
fd -H '\.?Dockerfile' server/

Repository: vector17002/video-transcoding

Length of output: 90


Remove or correct the .Dockerfile entry on line 12.

The .dockerignore file excludes a file named .Dockerfile (with leading dot), but the actual Dockerfile in the repository is named Dockerfile (without the dot). This entry is either a typo or unnecessary. If it was meant to exclude the Dockerfile from the build context, change it to Dockerfile; otherwise, remove it entirely.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/.dockerignore` around lines 1 - 12, The .dockerignore currently
contains an entry for ".Dockerfile" which appears to be a typo; update the
.dockerignore by either removing the ".Dockerfile" entry entirely or replacing
it with "Dockerfile" if you intend to exclude the actual Dockerfile, ensuring
the change targets the ".Dockerfile" line in the diff so only the intended file
is excluded from the build context.

49 changes: 49 additions & 0 deletions server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# ── Stage 1: Install deps & build ──────────────────────
FROM node:22-alpine AS builder

WORKDIR /app

# Install dependencies first (layer cache)
COPY package.json package-lock.json ./
RUN npm ci

# Copy source and build TypeScript
COPY tsconfig.json ./
COPY src/ ./src/
RUN npm i typescript
RUN npm run build


# ── Stage 2: Production deps only ─────────────────────
FROM node:22-alpine AS prod-deps

WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci --omit=dev && npm cache clean --force


# ── Stage 3a: API server (lightweight — no FFmpeg) ─────
FROM node:22-alpine AS api

WORKDIR /app

COPY --from=prod-deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package.json ./

CMD ["node", "dist/index.js"]

# ── Stage 3b: Worker (with FFmpeg for transcoding) ─────
FROM node:22-alpine AS worker

# Only workers need FFmpeg
RUN apk add --no-cache ffmpeg

WORKDIR /app

COPY --from=prod-deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package.json ./

CMD ["node", "dist/worker.js"]
Comment on lines +31 to +54
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Run the final images as a non-root user.

Both runtime stages execute Node/FFmpeg as root today, which unnecessarily widens the blast radius if either service is compromised.

Harden the runtime stages
 FROM node:22-alpine AS api

 WORKDIR /app

-COPY --from=prod-deps /app/node_modules ./node_modules
-COPY --from=builder /app/dist ./dist
-COPY package.json ./
+COPY --chown=node:node --from=prod-deps /app/node_modules ./node_modules
+COPY --chown=node:node --from=builder /app/dist ./dist
+COPY --chown=node:node package.json ./
+USER node
 FROM node:22-alpine AS worker

 # Only workers need FFmpeg
 RUN apk add --no-cache ffmpeg

 WORKDIR /app

-COPY --from=prod-deps /app/node_modules ./node_modules
-COPY --from=builder /app/dist ./dist
-COPY package.json ./
+COPY --chown=node:node --from=prod-deps /app/node_modules ./node_modules
+COPY --chown=node:node --from=builder /app/dist ./dist
+COPY --chown=node:node package.json ./
+USER node
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
FROM node:22-alpine AS api
WORKDIR /app
COPY --from=prod-deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package.json ./
CMD ["node", "dist/index.js"]
# ── Stage 3b: Worker (with FFmpeg for transcoding) ─────
FROM node:22-alpine AS worker
# Only workers need FFmpeg
RUN apk add --no-cache ffmpeg
WORKDIR /app
COPY --from=prod-deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package.json ./
CMD ["node", "dist/worker.js"]
FROM node:22-alpine AS api
WORKDIR /app
COPY --chown=node:node --from=prod-deps /app/node_modules ./node_modules
COPY --chown=node:node --from=builder /app/dist ./dist
COPY --chown=node:node package.json ./
USER node
CMD ["node", "dist/index.js"]
# ── Stage 3b: Worker (with FFmpeg for transcoding) ─────
FROM node:22-alpine AS worker
# Only workers need FFmpeg
RUN apk add --no-cache ffmpeg
WORKDIR /app
COPY --chown=node:node --from=prod-deps /app/node_modules ./node_modules
COPY --chown=node:node --from=builder /app/dist ./dist
COPY --chown=node:node package.json ./
USER node
CMD ["node", "dist/worker.js"]
🧰 Tools
🪛 Checkov (3.2.510)

[low] 1-54: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)


[low] 1-54: Ensure that a user for the container has been created

(CKV_DOCKER_3)

🪛 Hadolint (2.14.0)

[warning] 46-46: Pin versions in apk add. Instead of apk add <package> use apk add <package>=<version>

(DL3018)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/Dockerfile` around lines 31 - 54, The runtime stages ("api" and
"worker") run Node/FFmpeg as root; create a non-root user and group (e.g.,
adduser/appuser), chown the /app workspace and node_modules/dist to that user,
and switch to that user with USER before the CMD in both the api and worker
stages; ensure the FFmpeg installation step (apk add ffmpeg) and any files
created earlier remain accessible to the non-root account so that
"dist/index.js" and "dist/worker.js" can be executed without root privileges.

83 changes: 75 additions & 8 deletions server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading