diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..80a3d048 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,28 @@ +# Build context trimming for Dockerfile.build (self-contained fork build). +# Keep `assets/` (fork frontend submodule) — it is required by the frontend stage. + +.git +.github +.gitignore + +# Local runtime state / secrets — must never enter the image. +data/ +conf.ini +*.db +*.db-journal +*.sqlite +*.log + +# Regenerated inside the build (frontend stage produces a fresh assets.zip). +application/statics/assets.zip + +# Frontend artifacts rebuilt in-stage. +assets/node_modules/ +assets/build/ +front/ +node_modules/ + +# Editor / OS noise. +.idea/ +.vscode/ +.DS_Store diff --git a/.gitmodules b/.gitmodules index 1d5acb22..e4c11e09 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "assets"] path = assets - url = https://github.com/cloudreve/frontend.git + url = https://github.com/thotenn/cloudreve-front.git diff --git a/Dockerfile.build b/Dockerfile.build new file mode 100644 index 00000000..e1737a8a --- /dev/null +++ b/Dockerfile.build @@ -0,0 +1,103 @@ +# syntax=docker/dockerfile:1 +# +# Self-contained multi-stage build for the Cloudreve FORK (thotenn/cloudreve). +# +# Difference vs ./Dockerfile: +# - ./Dockerfile is the UPSTREAM/goreleaser one: it only PACKAGES an already-built binary +# (`COPY cloudreve ./cloudreve`). goreleaser builds the frontend + Go binary beforehand. +# - THIS file builds everything from source in one shot (frontend -> assets.zip -> Go binary +# -> runtime image), so a plain `docker build` / Coolify "build from git" produces a runnable +# fork image with NO external build steps. +# +# Docs: ../docs/cloudreve/context/setup/docker-solution/ (build) and setup/coolify-deploy.md (deploy). +# +# --------------------------------------------------------------------------------------------- +# Frontend sourcing: the fork UI must be present as ./assets (the `assets` submodule repointed to +# thotenn/cloudreve-front). Coolify clones submodules recursively. If you don't want to repoint the +# submodule, replace the `COPY assets/` below with a clone (see the FRONT_REF ARG note). +# --------------------------------------------------------------------------------------------- +# +# Version: CR_VERSION should equal application/constants/constants.go `BackendVersion` (currently +# 4.14.1). We deliberately do NOT inject BackendVersion via -ldflags: constants.go stays the single +# source of truth that fires DB migrations (bumped per ticket, e.g. APP-100). CR_VERSION only labels +# the embedded frontend so the backend's "static resource version" check matches (cosmetic warning). + +ARG CR_VERSION=4.14.1 + +######################## Stage 1 — frontend -> assets.zip ######################## +FROM node:20-slim AS frontend +ARG CR_VERSION +WORKDIR /build + +RUN apt-get update \ + && apt-get install -y --no-install-recommends zip git ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# The fork frontend, as the recursively-checked-out `assets` submodule. +# Alternative (no submodule change): comment the COPY and use, before `yarn`: +# ARG FRONT_REF=master +# RUN git clone --depth 1 -b "$FRONT_REF" https://github.com/thotenn/cloudreve-front assets +COPY assets/ ./assets/ + +# Pin Yarn Berry (fork uses 4.x; .yarnrc.yml has no packageManager/yarnPath → corepack would be ambiguous). +RUN corepack enable && corepack prepare yarn@4.12.0 --activate +WORKDIR /build/assets +ENV NODE_OPTIONS=--max-old-space-size=8192 +# Label the build version (Yarn Berry-safe; avoids `yarn version` plugin differences). +RUN node -e "const f='./package.json',p=require(f);p.version='${CR_VERSION}';require('fs').writeFileSync(f,JSON.stringify(p,null,2))" +RUN --mount=type=cache,target=/usr/local/share/.cache/yarn \ + yarn install --network-timeout 1000000 \ + && yarn run build + +WORKDIR /build +# Embed layout expected by //go:embed: fs.Sub(statics, "assets/build") -> archive holds assets/build/... +RUN zip -qr assets.zip assets/build + +######################## Stage 2 — Go binary (embeds assets.zip) ######################## +FROM golang:1.25-alpine AS backend +ARG CR_VERSION +RUN apk add --no-cache git +WORKDIR /src + +COPY go.mod go.sum ./ +RUN --mount=type=cache,target=/go/pkg/mod go mod download + +COPY . . +# Real embedded UI (overrides any stale local application/statics/assets.zip copied above). +COPY --from=frontend /build/assets.zip ./application/statics/assets.zip + +ENV CGO_ENABLED=0 GOOS=linux +# NOTE: no `-X ...BackendVersion=` on purpose (see header). Only LastCommit is labelled. +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build \ + go build -trimpath -buildvcs=false \ + -ldflags "-s -w -X 'github.com/cloudreve/Cloudreve/v4/application/constants.LastCommit=docker-${CR_VERSION}'" \ + -o /out/cloudreve . + +######################## Stage 3 — runtime ######################## +FROM alpine:latest AS runtime +WORKDIR /cloudreve + +# Same system deps as ./Dockerfile — ffmpeg + vips-tools are what the media post-processing +# (APP-101) needs; they ship in the image, no host ffmpeg required. +RUN apk update \ + && apk add --no-cache tzdata vips-tools ffmpeg libreoffice aria2 supervisor \ + font-noto font-noto-cjk libheif libraw-tools \ + && mkdir -p ./data/temp/aria2 \ + && chmod -R 766 ./data/temp/aria2 + +ENV CR_ENABLE_ARIA2=1 \ + CR_SETTING_DEFAULT_thumb_ffmpeg_enabled=1 \ + CR_SETTING_DEFAULT_thumb_vips_enabled=1 \ + CR_SETTING_DEFAULT_thumb_libreoffice_enabled=1 \ + CR_SETTING_DEFAULT_media_meta_ffprobe=1 \ + CR_SETTING_DEFAULT_thumb_libraw_enabled=1 + +COPY .build/aria2.supervisor.conf .build/entrypoint.sh ./ +COPY --from=backend /out/cloudreve ./cloudreve + +RUN chmod +x ./cloudreve ./entrypoint.sh + +EXPOSE 5212 443 6888 6888/udp +VOLUME ["/cloudreve/data"] +ENTRYPOINT ["sh", "./entrypoint.sh"] diff --git a/assets b/assets index 2e995f1d..3a57015c 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 2e995f1d4676d15b714dd82cb3aefe2e7697fcea +Subproject commit 3a57015c5b2ce05567b1026791b020bba6de8258 diff --git a/docker-compose.coolify.yml b/docker-compose.coolify.yml new file mode 100644 index 00000000..dd64c886 --- /dev/null +++ b/docker-compose.coolify.yml @@ -0,0 +1,67 @@ +services: + cloudreve: + build: + context: . + dockerfile: Dockerfile.build + args: + CR_VERSION: "4.14.1" + container_name: cloudreve-fork + depends_on: + postgresql: + condition: service_healthy + redis: + condition: service_healthy + restart: unless-stopped + ports: + - "127.0.0.1:8078:5212" + # aria2 BitTorrent — NOT reachable through the HTTP tunnel; publish directly only if needed: + # - "6888:6888" + # - "6888:6888/udp" + environment: + - CR_CONF_Database.Type=postgres + - CR_CONF_Database.Host=postgresql + - CR_CONF_Database.User=cloudreve + - CR_CONF_Database.Name=cloudreve + - CR_CONF_Database.Port=5432 + - CR_CONF_Redis.Server=redis:6379 + volumes: + - backend_data:/cloudreve/data + deploy: + resources: + limits: + cpus: "1.5" + memory: 1g + + postgresql: + image: postgres:17 + container_name: cloudreve-postgres + restart: unless-stopped + environment: + - POSTGRES_USER=cloudreve + - POSTGRES_DB=cloudreve + - POSTGRES_HOST_AUTH_METHOD=trust + volumes: + - database_postgres:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U cloudreve -d cloudreve"] + interval: 5s + timeout: 5s + retries: 12 + start_period: 10s + + redis: + image: redis:latest + container_name: cloudreve-redis + restart: unless-stopped + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 12 + +volumes: + backend_data: + database_postgres: + redis_data: