feat: add SPA controller, Traefik route, CI frontend build, and HOWTO update
- SpaController catch-all forwards non-API routes to index.html - Traefik SPA route at priority=1 catches all unmatched paths - CI pipeline builds frontend before Maven - Dockerfile adds multi-stage frontend build - HOWTO.md documents frontend development workflow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,12 @@ jobs:
|
|||||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||||
restore-keys: ${{ runner.os }}-maven-
|
restore-keys: ${{ runner.os }}-maven-
|
||||||
|
|
||||||
|
- name: Build Frontend
|
||||||
|
run: |
|
||||||
|
cd ui
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
|
||||||
- name: Build and Test (unit tests only)
|
- name: Build and Test (unit tests only)
|
||||||
run: >-
|
run: >-
|
||||||
mvn clean verify -B
|
mvn clean verify -B
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM node:22-alpine AS frontend
|
||||||
|
WORKDIR /ui
|
||||||
|
COPY ui/package.json ui/package-lock.json ui/.npmrc ./
|
||||||
|
RUN npm ci
|
||||||
|
COPY ui/ .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
FROM eclipse-temurin:21-jdk-alpine AS build
|
FROM eclipse-temurin:21-jdk-alpine AS build
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
COPY .mvn/ .mvn/
|
COPY .mvn/ .mvn/
|
||||||
COPY mvnw pom.xml ./
|
COPY mvnw pom.xml ./
|
||||||
RUN --mount=type=cache,target=/root/.m2/repository ./mvnw dependency:go-offline -B
|
RUN --mount=type=cache,target=/root/.m2/repository ./mvnw dependency:go-offline -B
|
||||||
COPY src/ src/
|
COPY src/ src/
|
||||||
|
COPY --from=frontend /ui/dist/ src/main/resources/static/
|
||||||
RUN --mount=type=cache,target=/root/.m2/repository ./mvnw package -DskipTests -B
|
RUN --mount=type=cache,target=/root/.m2/repository ./mvnw package -DskipTests -B
|
||||||
|
|
||||||
FROM eclipse-temurin:21-jre-alpine
|
FROM eclipse-temurin:21-jre-alpine
|
||||||
|
|||||||
46
HOWTO.md
46
HOWTO.md
@@ -302,6 +302,52 @@ Query params: `since`, `until` (ISO timestamps), `limit` (default 500), `stream`
|
|||||||
| HIGH | Unlimited | 50 | 90 days | + Debugger, Replay |
|
| HIGH | Unlimited | 50 | 90 days | + Debugger, Replay |
|
||||||
| BUSINESS | Unlimited | Unlimited | 365 days | All features |
|
| BUSINESS | Unlimited | Unlimited | 365 days | All features |
|
||||||
|
|
||||||
|
## Frontend Development
|
||||||
|
|
||||||
|
The SaaS management UI is a React SPA in the `ui/` directory.
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ui
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dev Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ui
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
The Vite dev server starts on http://localhost:5173 and proxies `/api` to `http://localhost:8080` (the Spring Boot backend). Run the backend in another terminal with `mvn spring-boot:run` or via Docker Compose.
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
| Variable | Purpose | Default |
|
||||||
|
|----------|---------|---------|
|
||||||
|
| `VITE_LOGTO_ENDPOINT` | Logto OIDC endpoint | `http://localhost:3001` |
|
||||||
|
| `VITE_LOGTO_CLIENT_ID` | Logto application client ID | (empty) |
|
||||||
|
|
||||||
|
Create a `ui/.env.local` file for local overrides:
|
||||||
|
```bash
|
||||||
|
VITE_LOGTO_ENDPOINT=http://localhost:3001
|
||||||
|
VITE_LOGTO_CLIENT_ID=your-client-id
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ui
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Output goes to `src/main/resources/static/` (configured in `vite.config.ts`). The subsequent `mvn package` bundles the SPA into the JAR. In Docker builds, the Dockerfile handles this automatically via a multi-stage build.
|
||||||
|
|
||||||
|
### SPA Routing
|
||||||
|
|
||||||
|
Spring Boot serves `index.html` for all non-API routes via `SpaController.java`. React Router handles client-side routing. The SPA lives at `/`, while the observability dashboard (cameleer3-server) is at `/dashboard`.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
### Running Tests
|
### Running Tests
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ services:
|
|||||||
- traefik.http.services.api.loadbalancer.server.port=8080
|
- traefik.http.services.api.loadbalancer.server.port=8080
|
||||||
- traefik.http.routers.forwardauth.rule=Path(`/auth/verify`)
|
- traefik.http.routers.forwardauth.rule=Path(`/auth/verify`)
|
||||||
- traefik.http.services.forwardauth.loadbalancer.server.port=8080
|
- traefik.http.services.forwardauth.loadbalancer.server.port=8080
|
||||||
|
- traefik.http.routers.spa.rule=PathPrefix(`/`)
|
||||||
|
- traefik.http.routers.spa.priority=1
|
||||||
|
- traefik.http.services.spa.loadbalancer.server.port=8080
|
||||||
networks:
|
networks:
|
||||||
- cameleer
|
- cameleer
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package net.siegeln.cameleer.saas.config;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
public class SpaController {
|
||||||
|
|
||||||
|
@GetMapping(value = {"/", "/login", "/callback", "/environments/**", "/license"})
|
||||||
|
public String spa() {
|
||||||
|
return "forward:/index.html";
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user