feat: custom Logto image + auto-redirect to sign-in
- Add docker/logto.Dockerfile: builds custom Logto image with sign-in UI baked into /etc/logto/packages/experience/dist/ - Remove sign-in-ui init container, signinui volume, CUSTOM_UI_PATH (CUSTOM_UI_PATH is Logto Cloud only, not available in OSS) - Remove sign-in build stage from SaaS Dockerfile (now in logto.Dockerfile) - Remove docker/saas-entrypoint.sh (no longer needed) - LoginPage auto-redirects to Logto OIDC on mount instead of showing "Sign in with Logto" button — seamless sign-in experience Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
10
Dockerfile
10
Dockerfile
@@ -9,15 +9,6 @@ RUN echo "//gitea.siegeln.net/api/packages/cameleer/npm/:_authToken=${REGISTRY_T
|
|||||||
COPY ui/ .
|
COPY ui/ .
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# Sign-in UI: custom Logto sign-in experience
|
|
||||||
FROM --platform=$BUILDPLATFORM node:22-alpine AS sign-in-frontend
|
|
||||||
ARG REGISTRY_TOKEN
|
|
||||||
WORKDIR /ui
|
|
||||||
COPY ui/sign-in/package.json ui/sign-in/package-lock.json ui/sign-in/.npmrc ./
|
|
||||||
RUN echo "//gitea.siegeln.net/api/packages/cameleer/npm/:_authToken=${REGISTRY_TOKEN}" >> .npmrc && npm ci
|
|
||||||
COPY ui/sign-in/ .
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
# Maven build: runs natively on build host (no QEMU emulation)
|
# Maven build: runs natively on build host (no QEMU emulation)
|
||||||
FROM --platform=$BUILDPLATFORM eclipse-temurin:21-jdk-alpine AS build
|
FROM --platform=$BUILDPLATFORM eclipse-temurin:21-jdk-alpine AS build
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
@@ -34,7 +25,6 @@ FROM eclipse-temurin:21-jre-alpine
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN addgroup -S cameleer && adduser -S cameleer -G cameleer
|
RUN addgroup -S cameleer && adduser -S cameleer -G cameleer
|
||||||
COPY --from=build /build/target/*.jar app.jar
|
COPY --from=build /build/target/*.jar app.jar
|
||||||
COPY --from=sign-in-frontend /ui/dist/ /app/sign-in-dist/
|
|
||||||
USER cameleer
|
USER cameleer
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
ENTRYPOINT ["java", "-jar", "app.jar"]
|
ENTRYPOINT ["java", "-jar", "app.jar"]
|
||||||
|
|||||||
@@ -58,16 +58,15 @@ services:
|
|||||||
- cameleer
|
- cameleer
|
||||||
|
|
||||||
logto:
|
logto:
|
||||||
image: ghcr.io/logto-io/logto:latest
|
image: ${LOGTO_IMAGE:-gitea.siegeln.net/cameleer/cameleer-logto}:${VERSION:-latest}
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/logto.Dockerfile
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
sign-in-ui:
|
|
||||||
condition: service_completed_successfully
|
|
||||||
entrypoint: ["sh", "-c", "npm run cli db seed -- --swe && npm start"]
|
entrypoint: ["sh", "-c", "npm run cli db seed -- --swe && npm start"]
|
||||||
volumes:
|
|
||||||
- signinui:/etc/logto/packages/experience/dist
|
|
||||||
environment:
|
environment:
|
||||||
DB_URL: postgres://${POSTGRES_USER:-cameleer}:${POSTGRES_PASSWORD:-cameleer_dev}@postgres:5432/logto
|
DB_URL: postgres://${POSTGRES_USER:-cameleer}:${POSTGRES_PASSWORD:-cameleer_dev}@postgres:5432/logto
|
||||||
ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}
|
ENDPOINT: ${PUBLIC_PROTOCOL:-https}://${PUBLIC_HOST:-localhost}
|
||||||
@@ -122,14 +121,6 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- cameleer
|
- 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:
|
cameleer-saas:
|
||||||
image: ${CAMELEER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-saas}:${VERSION:-latest}
|
image: ${CAMELEER_IMAGE:-gitea.siegeln.net/cameleer/cameleer-saas}:${VERSION:-latest}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@@ -250,4 +241,3 @@ volumes:
|
|||||||
certs:
|
certs:
|
||||||
jardata:
|
jardata:
|
||||||
bootstrapdata:
|
bootstrapdata:
|
||||||
signinui:
|
|
||||||
|
|||||||
14
docker/logto.Dockerfile
Normal file
14
docker/logto.Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# Build custom sign-in UI
|
||||||
|
FROM --platform=$BUILDPLATFORM node:22-alpine AS sign-in
|
||||||
|
ARG REGISTRY_TOKEN
|
||||||
|
WORKDIR /ui
|
||||||
|
COPY ui/sign-in/package.json ui/sign-in/package-lock.json ui/sign-in/.npmrc ./
|
||||||
|
RUN echo "//gitea.siegeln.net/api/packages/cameleer/npm/:_authToken=${REGISTRY_TOKEN}" >> .npmrc && npm ci
|
||||||
|
COPY ui/sign-in/ .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Custom Logto with baked-in sign-in UI
|
||||||
|
FROM ghcr.io/logto-io/logto:latest
|
||||||
|
COPY --from=sign-in /ui/dist/ /etc/logto/packages/experience/dist/
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# Copy sign-in UI dist to shared volume for Logto's CUSTOM_UI_PATH
|
|
||||||
if [ -d /app/sign-in-dist ] && [ -d /data/sign-in-ui ]; then
|
|
||||||
cp -r /app/sign-in-dist/* /data/sign-in-ui/
|
|
||||||
echo "[saas] Copied sign-in UI to shared volume"
|
|
||||||
fi
|
|
||||||
exec java -jar /app/app.jar "$@"
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { useLogto } from '@logto/react';
|
import { useLogto } from '@logto/react';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
import { Button, Spinner } from '@cameleer/design-system';
|
import { Spinner } from '@cameleer/design-system';
|
||||||
|
|
||||||
export function LoginPage() {
|
export function LoginPage() {
|
||||||
const { signIn, isAuthenticated, isLoading } = useLogto();
|
const { signIn, isAuthenticated, isLoading } = useLogto();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const redirected = useRef(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
@@ -13,27 +14,16 @@ export function LoginPage() {
|
|||||||
}
|
}
|
||||||
}, [isAuthenticated, navigate]);
|
}, [isAuthenticated, navigate]);
|
||||||
|
|
||||||
if (isLoading) {
|
useEffect(() => {
|
||||||
return (
|
if (!isLoading && !isAuthenticated && !redirected.current) {
|
||||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh' }}>
|
redirected.current = true;
|
||||||
<Spinner />
|
signIn(`${window.location.origin}/platform/callback`);
|
||||||
</div>
|
}
|
||||||
);
|
}, [isLoading, isAuthenticated, signIn]);
|
||||||
}
|
|
||||||
|
|
||||||
const handleLogin = () => {
|
|
||||||
signIn(`${window.location.origin}/platform/callback`);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh' }}>
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh' }}>
|
||||||
<div style={{ textAlign: 'center' }}>
|
<Spinner />
|
||||||
<h1>Cameleer SaaS</h1>
|
|
||||||
<p style={{ marginBottom: '2rem', color: 'var(--color-text-secondary)' }}>
|
|
||||||
Managed Apache Camel Runtime
|
|
||||||
</p>
|
|
||||||
<Button onClick={handleLogin}>Sign in with Logto</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user