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') }}
|
||||
restore-keys: ${{ runner.os }}-maven-
|
||||
|
||||
- name: Build Frontend
|
||||
run: |
|
||||
cd ui
|
||||
npm ci
|
||||
npm run build
|
||||
|
||||
- name: Build and Test (unit tests only)
|
||||
run: >-
|
||||
mvn clean verify -B
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
# 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
|
||||
WORKDIR /build
|
||||
COPY .mvn/ .mvn/
|
||||
COPY mvnw pom.xml ./
|
||||
RUN --mount=type=cache,target=/root/.m2/repository ./mvnw dependency:go-offline -B
|
||||
COPY src/ src/
|
||||
COPY --from=frontend /ui/dist/ src/main/resources/static/
|
||||
RUN --mount=type=cache,target=/root/.m2/repository ./mvnw package -DskipTests -B
|
||||
|
||||
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 |
|
||||
| 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
|
||||
|
||||
### Running Tests
|
||||
|
||||
@@ -79,6 +79,9 @@ services:
|
||||
- traefik.http.services.api.loadbalancer.server.port=8080
|
||||
- traefik.http.routers.forwardauth.rule=Path(`/auth/verify`)
|
||||
- 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:
|
||||
- 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