From 9013740b8361c04ce9edb1b2a4ac7fda4938aae4 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Mon, 6 Apr 2026 14:24:33 +0200 Subject: [PATCH] fix: mount custom sign-in UI over Logto experience dist CUSTOM_UI_PATH is a Logto Cloud feature, not available in OSS. The correct approach for self-hosted Logto is to volume-mount over /etc/logto/packages/experience/dist/. - Use init container (sign-in-ui) to copy dist to shared volume as root (fixes permission denied with cameleer user) - Logto mounts signinui volume at experience/dist path - Logto depends on sign-in-ui init container completion - Remove saas-entrypoint.sh approach (no longer needed) - Revert Dockerfile entrypoint to direct java -jar - Permit /favicon.svg in SecurityConfig for sign-in page logo Tested: full OIDC flow works end-to-end via Playwright. Co-Authored-By: Claude Opus 4.6 (1M context) --- Dockerfile | 4 +--- docker-compose.yml | 14 +++++++++++--- .../cameleer/saas/config/SecurityConfig.java | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index f86e06f..4f2cf7f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,8 +35,6 @@ WORKDIR /app RUN addgroup -S cameleer && adduser -S cameleer -G cameleer COPY --from=build /build/target/*.jar app.jar COPY --from=sign-in-frontend /ui/dist/ /app/sign-in-dist/ -COPY docker/saas-entrypoint.sh /app/entrypoint.sh -RUN chmod +x /app/entrypoint.sh USER cameleer EXPOSE 8080 -ENTRYPOINT ["/app/entrypoint.sh"] +ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/docker-compose.yml b/docker-compose.yml index 8cdc437..d94f807 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,15 +63,16 @@ services: depends_on: postgres: condition: service_healthy + sign-in-ui: + condition: service_completed_successfully entrypoint: ["sh", "-c", "npm run cli db seed -- --swe && npm start"] volumes: - - signinui:/etc/logto/custom-ui + - signinui:/etc/logto/packages/experience/dist environment: DB_URL: postgres://${POSTGRES_USER:-cameleer}:${POSTGRES_PASSWORD:-cameleer_dev}@postgres:5432/logto ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost} ADMIN_ENDPOINT: http://${PUBLIC_HOST:-localhost}:3002 TRUST_PROXY_HEADER: 1 - CUSTOM_UI_PATH: /etc/logto/custom-ui healthcheck: test: ["CMD-SHELL", "node -e \"require('http').get('http://localhost:3001/oidc/.well-known/openid-configuration', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))\""] interval: 5s @@ -121,6 +122,14 @@ services: networks: - cameleer + sign-in-ui: + image: ${CAMELEER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-saas}:${VERSION:-latest} + restart: "no" + user: root + entrypoint: ["sh", "-c", "cp -r /app/sign-in-dist/* /data/sign-in-ui/ && echo '[sign-in-ui] Copied custom UI to shared volume'"] + volumes: + - signinui:/data/sign-in-ui + cameleer-saas: image: ${CAMELEER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-saas}:${VERSION:-latest} restart: unless-stopped @@ -133,7 +142,6 @@ services: - /var/run/docker.sock:/var/run/docker.sock - jardata:/data/jars - bootstrapdata:/data/bootstrap:ro - - signinui:/data/sign-in-ui environment: SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/${POSTGRES_DB:-cameleer_saas} SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER:-cameleer} diff --git a/src/main/java/net/siegeln/cameleer/saas/config/SecurityConfig.java b/src/main/java/net/siegeln/cameleer/saas/config/SecurityConfig.java index a168fdb..7ca5e1f 100644 --- a/src/main/java/net/siegeln/cameleer/saas/config/SecurityConfig.java +++ b/src/main/java/net/siegeln/cameleer/saas/config/SecurityConfig.java @@ -40,7 +40,7 @@ public class SecurityConfig { .requestMatchers("/api/config").permitAll() .requestMatchers("/", "/index.html", "/login", "/callback", "/environments/**", "/license", "/admin/**").permitAll() - .requestMatchers("/_app/**", "/favicon.ico", "/logo.svg", "/logo-dark.svg").permitAll() + .requestMatchers("/_app/**", "/favicon.ico", "/favicon.svg", "/logo.svg", "/logo-dark.svg").permitAll() .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt ->