Merge branch 'main' into feature/162_fuzzy_search
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
carla 2025-11-12 12:00:58 +01:00
commit 56516d78b6
8 changed files with 203 additions and 21 deletions

View file

@ -100,6 +100,53 @@ volumes:
host:
path: /tmp/drone_cache
---
kind: pipeline
type: docker
name: build-and-publish
trigger:
branch:
- main
event:
- push
- tag
steps:
- name: build-and-publish-container
image: plugins/docker
settings:
registry: git.local-it.org
repo: git.local-it.org/local-it/mitgliederverwaltung
username:
from_secret: DRONE_REGISTRY_USERNAME
password:
from_secret: DRONE_REGISTRY_TOKEN
auto_tag: true
auto_tag_suffix: ${DRONE_COMMIT_SHA:0:8}
when:
event:
- tag
- name: build-and-publish-container-branch
image: plugins/docker
settings:
registry: git.local-it.org
repo: git.local-it.org/local-it/mitgliederverwaltung
username:
from_secret: DRONE_REGISTRY_USERNAME
password:
from_secret: DRONE_REGISTRY_TOKEN
tags:
- latest
- ${DRONE_COMMIT_SHA:0:8}
when:
event:
- push
depends_on:
- check
---
kind: pipeline
type: docker

View file

@ -1 +1,16 @@
OIDC_CLIENT_SECRET=
# Production Environment Variables for docker-compose.prod.yml
# Copy this file to .env and fill in the actual values
# Required: Phoenix secrets (generate with: mix phx.gen.secret)
SECRET_KEY_BASE=changeme-run-mix-phx.gen.secret
TOKEN_SIGNING_SECRET=changeme-run-mix-phx.gen.secret
# Required: Hostname for URL generation
PHX_HOST=localhost
# Optional: OIDC Configuration
# These have defaults in docker-compose.prod.yml, only override if needed
# OIDC_CLIENT_ID=mv
# OIDC_BASE_URL=http://localhost:8080/auth/v1
# OIDC_REDIRECT_URI=http://localhost:4001/auth/user/rauthy/callback
# OIDC_CLIENT_SECRET=your-rauthy-client-secret

View file

@ -14,7 +14,7 @@
ARG BUILDER_IMAGE="hexpm/elixir:1.18.3-erlang-27.3-debian-bullseye-20250317-slim"
ARG RUNNER_IMAGE="debian:bullseye-20250317-slim"
FROM ${BUILDER_IMAGE} as builder
FROM ${BUILDER_IMAGE} AS builder
# install build dependencies
RUN apt-get update -y && apt-get install -y build-essential git \
@ -70,9 +70,9 @@ RUN apt-get update -y && \
# Set the locale
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US:en
ENV LC_ALL=en_US.UTF-8
WORKDIR "/app"
RUN chown nobody /app

View file

@ -183,12 +183,70 @@ Useful `just` commands:
- `just reset-database` — reset local DB
- `just regen-migrations <name>` — regenerate migrations
## 📦 Deployment
## 📦 Production Deployment
A production release image is built via the provided `Dockerfile`.
Entrypoint: `/app/bin/server`.
### Local Production Testing
For testing the production Docker build locally:
1. **Generate secrets:**
```bash
mix phx.gen.secret # for SECRET_KEY_BASE
mix phx.gen.secret # for TOKEN_SIGNING_SECRET
```
2. **Create `.env` file:**
```bash
# Copy template and edit
cp .env.example .env
nano .env
# Required variables:
SECRET_KEY_BASE=<your-generated-secret>
TOKEN_SIGNING_SECRET=<your-generated-secret>
PHX_HOST=localhost
# Optional (have defaults in docker-compose.prod.yml):
# OIDC_CLIENT_ID=mv
# OIDC_BASE_URL=http://localhost:8080/auth/v1
# OIDC_REDIRECT_URI=http://localhost:4001/auth/user/rauthy/callback
# OIDC_CLIENT_SECRET=<from-rauthy-client>
```
3. **Start development environment** (for Rauthy):
```bash
docker compose up -d
```
4. **Start production environment:**
```bash
docker compose -f docker-compose.prod.yml up
```
5. **Run database migrations:**
```bash
docker compose -f docker-compose.prod.yml exec app /app/bin/mv eval "Mv.Release.migrate"
```
6. **Access the production app:**
- Production App: http://localhost:4001
- Uses same Rauthy instance as dev (localhost:8080)
**Note:** The local production setup uses `network_mode: host` to share localhost with the development Rauthy instance. For real production deployment, configure an external OIDC provider and remove `network_mode: host`.
### Real Production Deployment
For actual production deployment:
1. **Use an external OIDC provider** (not the local Rauthy)
2. **Update `docker-compose.prod.yml`:**
- Remove `network_mode: host`
- Set `OIDC_BASE_URL` to your production OIDC provider
- Configure proper Docker networks
3. **Set up SSL/TLS** (e.g., via reverse proxy like Nginx/Traefik)
4. **Use secure secrets management** (environment variables, Docker secrets, vault)
5. **Configure database backups**
More detailed deployment docs are planned.
## 🤝 Contributing

View file

@ -16,5 +16,16 @@ config :swoosh, local: false
# Do not print debug messages in production
config :logger, level: :info
# AshAuthentication production configuration
# These must be set at compile-time (not in runtime.exs) because
# Application.compile_env!/3 is used in lib/accounts/user.ex
config :mv, :session_identifier, :jti
config :mv, :require_token_presence_for_authentication, true
# Token signing secret - using a placeholder that MUST be overridden
# at runtime via environment variable in config/runtime.exs
config :mv, :token_signing_secret, "REPLACE_ME_AT_RUNTIME"
# Runtime production configuration, including reading
# of environment variables, is done on config/runtime.exs.

View file

@ -53,12 +53,24 @@ if config_env() == :prod do
config :mv, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
config :mv, :rauthy, redirect_uri: "http://localhost:4000/auth/user/rauthy/callback"
# Rauthy OIDC configuration
config :mv, :rauthy,
client_id: System.get_env("OIDC_CLIENT_ID") || "mv",
base_url: System.get_env("OIDC_BASE_URL") || "http://localhost:8080/auth/v1",
client_secret: System.get_env("OIDC_CLIENT_SECRET"),
redirect_uri:
System.get_env("OIDC_REDIRECT_URI") || "http://#{host}:#{port}/auth/user/rauthy/callback"
# AshAuthentication production configuration
config :mv, :session_identifier, :jti
# Token signing secret from environment variable
# This overrides the placeholder value set in prod.exs
token_signing_secret =
System.get_env("TOKEN_SIGNING_SECRET") ||
raise """
environment variable TOKEN_SIGNING_SECRET is missing.
You can generate one by calling: mix phx.gen.secret
"""
config :mv, :require_token_presence_for_authentication, true
config :mv, :token_signing_secret, token_signing_secret
config :mv, MvWeb.Endpoint,
url: [host: host, port: 443, scheme: "https"],
@ -70,7 +82,13 @@ if config_env() == :prod do
ip: {0, 0, 0, 0, 0, 0, 0, 0},
port: port
],
secret_key_base: secret_key_base
secret_key_base: secret_key_base,
# Allow connections from localhost and 127.0.0.1
check_origin: [
"//#{host}",
"//localhost:#{port}",
"//127.0.0.1:#{port}"
]
# ## SSL Support
#

38
docker-compose.prod.yml Normal file
View file

@ -0,0 +1,38 @@
services:
app:
image: git.local-it.org/local-it/mitgliederverwaltung:latest
container_name: mv-prod-app
# Use host network for local testing to access localhost:8080 (Rauthy)
# In real production, remove this and use external OIDC provider
network_mode: host
environment:
DATABASE_URL: "ecto://postgres:postgres@localhost:5001/mv_prod"
SECRET_KEY_BASE: "${SECRET_KEY_BASE}"
TOKEN_SIGNING_SECRET: "${TOKEN_SIGNING_SECRET}"
PHX_HOST: "${PHX_HOST}"
PORT: "4001"
PHX_SERVER: "true"
# Rauthy OIDC config - uses localhost because of host network mode
OIDC_CLIENT_ID: "mv"
OIDC_BASE_URL: "http://localhost:8080/auth/v1"
OIDC_CLIENT_SECRET: "${OIDC_CLIENT_SECRET:-}"
OIDC_REDIRECT_URI: "http://localhost:4001/auth/user/rauthy/callback"
depends_on:
- db-prod
restart: unless-stopped
db-prod:
image: postgres:16-alpine
container_name: mv-prod-db
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: mv_prod
volumes:
- postgres_data_prod:/var/lib/postgresql/data
ports:
- "5001:5432"
restart: unless-stopped
volumes:
postgres_data_prod:

View file

@ -1,7 +1,6 @@
networks:
local:
rauthy-dev:
driver: bridge
services:
db:
@ -39,12 +38,8 @@ services:
- LISTEN_SCHEME=http
- PUB_URL=localhost:8080
- BOOTSTRAP_ADMIN_PASSWORD_PLAIN=RauthyTest12345
#- HIQLITE=false
#- PG_HOST=db
#- PG_PORT=5432
#- PG_USER=postgres
#- PG_PASSWORD=postgres
#- PG_DB_NAME=mv_dev
# Disable strict IP validation to allow access from multiple Docker networks
- SESSION_VALIDATE_IP=false
ports:
- "8080:8080"
depends_on: