diff --git a/.dockerignore b/.dockerignore index 3729ff0..e260943 100644 --- a/.dockerignore +++ b/.dockerignore @@ -22,4 +22,6 @@ **/secrets.dev.yaml **/values.dev.yaml LICENSE -README.md \ No newline at end of file +README.mddocs +dev +.github diff --git a/.goreleaser-nightly.yml b/.goreleaser-nightly.yml index fb02411..464ce3d 100644 --- a/.goreleaser-nightly.yml +++ b/.goreleaser-nightly.yml @@ -68,7 +68,7 @@ dockers: - --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }} - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.licenses=AGPL-3.0 - dockerfile: Dockerfile + dockerfile: Dockerfile.goreleaser extra_files: - config.toml.sample - docker-entrypoint.sh @@ -89,7 +89,7 @@ dockers: - --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }} - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.licenses=AGPL-3.0 - dockerfile: Dockerfile + dockerfile: Dockerfile.goreleaser extra_files: - config.toml.sample - docker-entrypoint.sh @@ -111,7 +111,7 @@ dockers: - --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }} - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.licenses=AGPL-3.0 - dockerfile: Dockerfile + dockerfile: Dockerfile.goreleaser extra_files: - config.toml.sample - docker-entrypoint.sh @@ -133,7 +133,7 @@ dockers: - --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }} - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.licenses=AGPL-3.0 - dockerfile: Dockerfile + dockerfile: Dockerfile.goreleaser extra_files: - config.toml.sample - docker-entrypoint.sh diff --git a/.goreleaser.yml b/.goreleaser.yml index ca5a63d..289fd70 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -61,7 +61,7 @@ dockers: - --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }} - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.licenses=AGPL-3.0 - dockerfile: Dockerfile + dockerfile: Dockerfile.goreleaser extra_files: - config.toml.sample - docker-entrypoint.sh @@ -83,7 +83,7 @@ dockers: - --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }} - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.licenses=AGPL-3.0 - dockerfile: Dockerfile + dockerfile: Dockerfile.goreleaser extra_files: - config.toml.sample - docker-entrypoint.sh @@ -106,7 +106,7 @@ dockers: - --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }} - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.licenses=AGPL-3.0 - dockerfile: Dockerfile + dockerfile: Dockerfile.goreleaser extra_files: - config.toml.sample - docker-entrypoint.sh @@ -129,7 +129,7 @@ dockers: - --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }} - --label=org.opencontainers.image.revision={{ .FullCommit }} - --label=org.opencontainers.image.licenses=AGPL-3.0 - dockerfile: Dockerfile + dockerfile: Dockerfile.goreleaser extra_files: - config.toml.sample - docker-entrypoint.sh diff --git a/Dockerfile b/Dockerfile index 06339a2..1cb1007 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,50 @@ +# Self-contained build for EagleCast. Builds the email-builder and admin +# frontend, compiles the Go backend, and stuffs all static assets into a +# single binary. Used by PaaS platforms like Coolify that build from git. +# (Release builds via goreleaser use Dockerfile.goreleaser instead.) + +ARG APP_VERSION=v1.0.0 + +# ---- email-builder (React) ---- +FROM node:22-alpine AS email-builder +WORKDIR /build/email-builder +COPY frontend/email-builder/package.json frontend/email-builder/yarn.lock ./ +RUN yarn install --frozen-lockfile +COPY frontend/email-builder/ ./ +RUN yarn build + +# ---- admin frontend (Vue) ---- +FROM node:22-alpine AS frontend +ARG APP_VERSION +WORKDIR /build/frontend +COPY frontend/package.json frontend/yarn.lock ./ +RUN yarn install --frozen-lockfile +COPY frontend/ ./ +COPY --from=email-builder /build/email-builder/dist ./public/static/email-builder +ENV VUE_APP_VERSION=${APP_VERSION} +RUN yarn build + +# ---- backend (Go) ---- +FROM golang:1.26-alpine AS backend +ARG APP_VERSION +WORKDIR /build +COPY go.mod go.sum ./ +RUN go mod download +RUN go install github.com/knadh/stuffbin/...@latest +COPY . . +COPY --from=frontend /build/frontend/dist ./frontend/dist +RUN CGO_ENABLED=0 go build -o eaglecast \ + -ldflags="-s -w -X 'main.buildString=${APP_VERSION} (docker)' -X 'main.versionString=${APP_VERSION}'" \ + ./cmd +RUN /go/bin/stuffbin -a stuff -in eaglecast -out eaglecast \ + config.toml.sample \ + schema.sql queries:/queries permissions.json \ + static/public:/public \ + static/email-templates \ + frontend/dist:/admin \ + i18n:/i18n + +# ---- runtime ---- FROM alpine:latest # Install dependencies @@ -7,7 +54,7 @@ RUN apk --no-cache add ca-certificates tzdata shadow su-exec WORKDIR /eaglecast # Copy only the necessary files -COPY eaglecast . +COPY --from=backend /build/eaglecast . COPY config.toml.sample config.toml # Copy the entrypoint script diff --git a/Dockerfile.goreleaser b/Dockerfile.goreleaser new file mode 100644 index 0000000..06339a2 --- /dev/null +++ b/Dockerfile.goreleaser @@ -0,0 +1,26 @@ +FROM alpine:latest + +# Install dependencies +RUN apk --no-cache add ca-certificates tzdata shadow su-exec + +# Set the working directory +WORKDIR /eaglecast + +# Copy only the necessary files +COPY eaglecast . +COPY config.toml.sample config.toml + +# Copy the entrypoint script +COPY docker-entrypoint.sh /usr/local/bin/ + +# Make the entrypoint script executable +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +# Expose the application port +EXPOSE 9000 + +# Set the entrypoint +ENTRYPOINT ["docker-entrypoint.sh"] + +# Define the command to run the application +CMD ["./eaglecast"] diff --git a/docker-compose.coolify.yml b/docker-compose.coolify.yml new file mode 100644 index 0000000..3060a9d --- /dev/null +++ b/docker-compose.coolify.yml @@ -0,0 +1,68 @@ +# Coolify deployment for EagleCast. +# +# In Coolify: create a new resource -> Docker Compose -> point it at this +# repository and set "Docker Compose Location" to /docker-compose.coolify.yml. +# Coolify substitutes the $SERVICE_* magic variables below: it generates the +# database password automatically and assigns the public domain (FQDN) you +# configure to the eaglecast service on port 9000. TLS and the reverse proxy +# are handled by Coolify's own proxy, so no ports are published here. +# +# Optional: set EAGLECAST_ADMIN_USER / EAGLECAST_ADMIN_PASSWORD in the +# Coolify environment variables UI before the first deploy to auto-create the +# Super Admin. Otherwise, visit the app after deploy to create it. +# +# After the first login, set the root URL in Admin -> Settings -> General to +# your public https:// domain so links in e-mails point at the right host. + +services: + eaglecast: + build: + context: . + dockerfile: Dockerfile + restart: unless-stopped + environment: + - SERVICE_FQDN_EAGLECAST_9000 + - EAGLECAST_app__address=0.0.0.0:9000 + - EAGLECAST_db__host=db + - EAGLECAST_db__port=5432 + - EAGLECAST_db__user=eaglecast + - EAGLECAST_db__password=$SERVICE_PASSWORD_EAGLECASTDB + - EAGLECAST_db__database=eaglecast + - EAGLECAST_db__ssl_mode=disable + - EAGLECAST_db__max_open=25 + - EAGLECAST_db__max_idle=25 + - EAGLECAST_db__max_lifetime=300s + - TZ=Etc/UTC + - EAGLECAST_ADMIN_USER=${EAGLECAST_ADMIN_USER:-} + - EAGLECAST_ADMIN_PASSWORD=${EAGLECAST_ADMIN_PASSWORD:-} + command: [sh, -c, "./eaglecast --install --idempotent --yes --config '' && ./eaglecast --upgrade --yes --config '' && ./eaglecast --config ''"] + depends_on: + db: + condition: service_healthy + volumes: + - eaglecast-uploads:/eaglecast/uploads + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:9000/health"] + interval: 30s + timeout: 5s + retries: 5 + start_period: 30s + + db: + image: postgres:17-alpine + restart: unless-stopped + environment: + - POSTGRES_USER=eaglecast + - POSTGRES_PASSWORD=$SERVICE_PASSWORD_EAGLECASTDB + - POSTGRES_DB=eaglecast + volumes: + - eaglecast-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U eaglecast"] + interval: 10s + timeout: 5s + retries: 6 + +volumes: + eaglecast-data: + eaglecast-uploads: diff --git a/docs/docs/content/installation.md b/docs/docs/content/installation.md index 33ecff4..64871c5 100644 --- a/docs/docs/content/installation.md +++ b/docs/docs/content/installation.md @@ -72,6 +72,17 @@ max_lifetime = "300s" command: [sh, -c, "./eaglecast --install --idempotent --yes --config /eaglecast/config.toml && ./eaglecast --upgrade --yes --config /eaglecast/config.toml && ./eaglecast --config /eaglecast/config.toml"] ``` +### Coolify + +The repository ships a Coolify-ready compose file, [docker-compose.coolify.yml](https://source.offmarket.win/aleagle/EagleCast/src/branch/master/docker-compose.coolify.yml). + +1. In Coolify, add a new resource -> *Docker Compose* and point it at the EagleCast git repository. +2. Set *Docker Compose Location* to `/docker-compose.coolify.yml`. +3. Assign your public domain to the `eaglecast` service (port 9000). Coolify's proxy handles TLS. +4. Optionally set `EAGLECAST_ADMIN_USER` and `EAGLECAST_ADMIN_PASSWORD` in the environment variables UI before the first deploy to auto-create the Super Admin. +5. Deploy. The image is built from source (frontend + backend) by the multi-stage `Dockerfile`, and the database schema is installed/upgraded automatically on start. +6. After logging in, set the root URL in *Admin -> Settings -> General* to your public `https://` domain. + ----------- ## Nightly