docker
This commit is contained in:
9
.dockerignore
Normal file
9
.dockerignore
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.angular
|
||||||
|
.git
|
||||||
|
.env
|
||||||
|
coverage
|
||||||
|
npm-debug.log*
|
||||||
|
apps/api/dist
|
||||||
|
apps/web/dist
|
||||||
@@ -3,6 +3,7 @@ NODE_ENV=development
|
|||||||
API_PORT=3000
|
API_PORT=3000
|
||||||
WEB_PORT=4200
|
WEB_PORT=4200
|
||||||
PUBLIC_WEB_URL=http://localhost:4200
|
PUBLIC_WEB_URL=http://localhost:4200
|
||||||
|
API_BASE_URL=http://localhost:3000
|
||||||
|
|
||||||
DATABASE_URL=mysql://ldap_portal:change-me@mysql.example.com:3306/ldap_portal
|
DATABASE_URL=mysql://ldap_portal:change-me@mysql.example.com:3306/ldap_portal
|
||||||
DATABASE_SSL=false
|
DATABASE_SSL=false
|
||||||
|
|||||||
34
Dockerfile
Normal file
34
Dockerfile
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
FROM node:22-alpine AS deps
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
COPY apps/api/package.json apps/api/package.json
|
||||||
|
COPY apps/web/package.json apps/web/package.json
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
FROM deps AS build
|
||||||
|
COPY tsconfig.base.json ./
|
||||||
|
COPY eslint.config.mjs ./
|
||||||
|
COPY apps/api apps/api
|
||||||
|
COPY apps/web apps/web
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM node:22-alpine AS runtime
|
||||||
|
WORKDIR /app
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV API_PORT=3000
|
||||||
|
ENV WEB_PORT=8080
|
||||||
|
ENV API_BASE_URL=http://localhost:3000
|
||||||
|
|
||||||
|
RUN apk add --no-cache nginx
|
||||||
|
|
||||||
|
COPY --from=deps /app/node_modules node_modules
|
||||||
|
COPY --from=build /app/apps/api/dist api
|
||||||
|
COPY --from=build /app/apps/web/dist/web/browser /usr/share/nginx/html
|
||||||
|
COPY apps/api/package.json package.json
|
||||||
|
COPY docker/single-container.nginx.conf /etc/nginx/http.d/default.conf
|
||||||
|
COPY docker/start-single-container.sh /usr/local/bin/start-ldap-portal
|
||||||
|
RUN chmod +x /usr/local/bin/start-ldap-portal \
|
||||||
|
&& mkdir -p /run/nginx /var/log/nginx
|
||||||
|
|
||||||
|
EXPOSE 3000 8080
|
||||||
|
CMD ["start-ldap-portal"]
|
||||||
27
README.md
27
README.md
@@ -35,6 +35,33 @@ docker compose up --build
|
|||||||
Passe vor dem Start mindestens `DATABASE_URL`, `JWT_SECRET`, `TOKEN_SECRET`, `LLDAP_*` und `SMTP_*` an.
|
Passe vor dem Start mindestens `DATABASE_URL`, `JWT_SECRET`, `TOKEN_SECRET`, `LLDAP_*` und `SMTP_*` an.
|
||||||
Fuer OIDC muessen zusaetzlich `OIDC_ISSUER`, `OIDC_COOKIE_SECRET`, `OIDC_ADMIN_GROUP` und `OIDC_ADMIN_GROUP_UUID` gesetzt werden.
|
Fuer OIDC muessen zusaetzlich `OIDC_ISSUER`, `OIDC_COOKIE_SECRET`, `OIDC_ADMIN_GROUP` und `OIDC_ADMIN_GROUP_UUID` gesetzt werden.
|
||||||
|
|
||||||
|
## Single Container Image
|
||||||
|
|
||||||
|
Das Root-`Dockerfile` baut API und Angular in ein einzelnes Image. Der Container startet:
|
||||||
|
|
||||||
|
- NestJS API / IdP auf Port `3000`
|
||||||
|
- Nginx Web-UI auf Port `8080`
|
||||||
|
|
||||||
|
Build und Push:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t registry.example.com/ldap-portal/idp:latest .
|
||||||
|
docker push registry.example.com/ldap-portal/idp:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Start:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d --name ldap-portal-idp \
|
||||||
|
--env-file .env \
|
||||||
|
-e API_BASE_URL=https://id.example.com \
|
||||||
|
-p 3000:3000 \
|
||||||
|
-p 8080:8080 \
|
||||||
|
registry.example.com/ldap-portal/idp:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
`API_BASE_URL` wird beim Containerstart in `/config.js` geschrieben und vom Angular-Frontend gelesen. Setze es auf die aus Browser-Sicht erreichbare API-/IdP-URL.
|
||||||
|
|
||||||
## Externe Dienste
|
## Externe Dienste
|
||||||
|
|
||||||
Die Anwendung bringt keine Datenbank und keinen LLDAP-Server mehr per Compose mit. Erwartet werden:
|
Die Anwendung bringt keine Datenbank und keinen LLDAP-Server mehr per Compose mit. Erwartet werden:
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
"browser": "src/main.ts",
|
"browser": "src/main.ts",
|
||||||
"polyfills": ["zone.js"],
|
"polyfills": ["zone.js"],
|
||||||
"tsConfig": "tsconfig.app.json",
|
"tsConfig": "tsconfig.app.json",
|
||||||
"assets": ["src/favicon.ico"],
|
"assets": ["src/favicon.ico", "src/config.js"],
|
||||||
"styles": ["src/styles.css"]
|
"styles": ["src/styles.css"]
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
|
|||||||
3
apps/web/src/config.js
Normal file
3
apps/web/src/config.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
window.__LDAP_PORTAL_CONFIG__ = {
|
||||||
|
apiBaseUrl: 'http://localhost:3000'
|
||||||
|
};
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
<title>LDAP Portal</title>
|
<title>LDAP Portal</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<script src="/config.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
|
|||||||
@@ -19,6 +19,14 @@ import { VerifyEmailComponent } from './app/pages/verify-email.component';
|
|||||||
import { API_BASE_URL } from './app/shared/api-base-url';
|
import { API_BASE_URL } from './app/shared/api-base-url';
|
||||||
import { AuthService } from './app/shared/auth.service';
|
import { AuthService } from './app/shared/auth.service';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
__LDAP_PORTAL_CONFIG__?: {
|
||||||
|
apiBaseUrl?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const authInterceptor: HttpInterceptorFn = (request, next) => {
|
const authInterceptor: HttpInterceptorFn = (request, next) => {
|
||||||
const token = localStorage.getItem('accessToken');
|
const token = localStorage.getItem('accessToken');
|
||||||
if (!token) {
|
if (!token) {
|
||||||
@@ -58,6 +66,6 @@ bootstrapApplication(AppComponent, {
|
|||||||
provideRouter(routes),
|
provideRouter(routes),
|
||||||
provideHttpClient(withInterceptors([authInterceptor])),
|
provideHttpClient(withInterceptors([authInterceptor])),
|
||||||
AuthService,
|
AuthService,
|
||||||
{ provide: API_BASE_URL, useValue: 'http://localhost:3000' },
|
{ provide: API_BASE_URL, useValue: window.__LDAP_PORTAL_CONFIG__?.apiBaseUrl ?? 'http://localhost:3000' },
|
||||||
],
|
],
|
||||||
}).catch((error) => console.error(error));
|
}).catch((error) => console.error(error));
|
||||||
|
|||||||
@@ -1,20 +1,12 @@
|
|||||||
services:
|
services:
|
||||||
api:
|
app:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: apps/api/Dockerfile
|
dockerfile: Dockerfile
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
ports:
|
|
||||||
- "3000:3000"
|
|
||||||
|
|
||||||
web:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: apps/web/Dockerfile
|
|
||||||
environment:
|
environment:
|
||||||
API_BASE_URL: http://localhost:3000
|
API_BASE_URL: http://localhost:3000
|
||||||
ports:
|
ports:
|
||||||
- "4200:80"
|
- "3000:3000"
|
||||||
depends_on:
|
- "8080:8080"
|
||||||
- api
|
|
||||||
|
|||||||
15
docker/single-container.nginx.conf
Normal file
15
docker/single-container.nginx.conf
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
server_name _;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location = /config.js {
|
||||||
|
add_header Cache-Control "no-store";
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
docker/start-single-container.sh
Normal file
24
docker/start-single-container.sh
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
cat >/usr/share/nginx/html/config.js <<EOF
|
||||||
|
window.__LDAP_PORTAL_CONFIG__ = {
|
||||||
|
apiBaseUrl: "${API_BASE_URL:-http://localhost:3000}"
|
||||||
|
};
|
||||||
|
EOF
|
||||||
|
|
||||||
|
node /app/api/main.js &
|
||||||
|
api_pid="$!"
|
||||||
|
|
||||||
|
nginx -g "daemon off;" &
|
||||||
|
nginx_pid="$!"
|
||||||
|
|
||||||
|
term() {
|
||||||
|
kill "$api_pid" "$nginx_pid" 2>/dev/null || true
|
||||||
|
wait "$api_pid" "$nginx_pid" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
trap term INT TERM
|
||||||
|
|
||||||
|
wait -n "$api_pid" "$nginx_pid"
|
||||||
|
term
|
||||||
Reference in New Issue
Block a user