diff --git a/alfa-client/.dockerignore b/alfa-client/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..e3021fb5e56465e9a1fd27422455f83fff5be3b2 --- /dev/null +++ b/alfa-client/.dockerignore @@ -0,0 +1,31 @@ +# Files die während eines docker build nicht in den Build Context geladen werden sollen. +.DS_Store +.angular +.dockerignore +.editorconfig +.eslintignore +.eslintrc.json +.git +.gitattributes +.gitignore +.nx +.settings +.prettierignore +.prettierrc +.scannerwork +.vscode +Dockerfile.nx-build-base +README.md +coverage +data +dist +jest.config.ts +jest.preset.js +node_modules +pom.xml +proxy.conf.mjs +proxy.dev.conf.json +sonar-project.properties +test-report.xml +tmp +tools diff --git a/alfa-client/Dockerfile.nx-build-base b/alfa-client/Dockerfile.nx-build-base new file mode 100644 index 0000000000000000000000000000000000000000..fdf916bf04f10cf733c654375661572d19ff5a11 --- /dev/null +++ b/alfa-client/Dockerfile.nx-build-base @@ -0,0 +1,9 @@ +FROM node:18 as builder + +ARG NODE_ENV +ARG BUILD_FLAG + +WORKDIR /app/builder +COPY . . + +RUN npm install --registry=https://nexus.ozg-sh.de/repository/npm-proxy diff --git a/alfa-client/README.md b/alfa-client/README.md index 6cc5caff942cf0618625190a651c10a86b41fd51..688707e0cf97fd2e6588db614a1e12573e2dc834 100644 --- a/alfa-client/README.md +++ b/alfa-client/README.md @@ -24,3 +24,21 @@ Hinweise: * Parameter testResultsProcessor, collectCoverage, coverageReporters führen zu _Unknown option_ warnings während jeden Tests, wenn sie in der jeweiligen _jest.config.ts_ definiert sind. Werden sie dagegen als CLI Parameter beim Start der Tests übergeben, wird keine Warnung produziert. * Es können mehrere Tests parallel laufen mittel `nx run-many --target=test --parallel 8`. Tests sollten dann nicht zusätzlich mit Jest's _maxWorkers_ Parameter parallelisiert werden. + +## Docker build + +Base Image: Wird manuell von Entwicklern gebaut, nach Änderungen an package.json. + +``` +bin/nx-build-base.sh +``` + +Anwendungsimage - wird in der Regel vom Jenkins gebaut. +- APPNAME: Name der App im Verzeichnis apps/ +- Versionierung steuert Jenkins +- build-arg CONFIGURATION steuert production build + +``` +docker buildx build --push --platform linux/amd64 --tag docker.ozg-sh.de/APPNAME:x.x.x --build-arg CONFIGURATION=production -f apps/APPNAME/Dockerfile . +``` + diff --git a/alfa-client/apps/admin/Dockerfile b/alfa-client/apps/admin/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..b5aaa960a3151ca0e2ea9de7337a7d0a1572412d --- /dev/null +++ b/alfa-client/apps/admin/Dockerfile @@ -0,0 +1,24 @@ +# Benutzt das vorher zu bauende Docker image "nx-build-base:x.y.z" +# Siehe ../Dockerfile.nx-build-base +FROM docker.ozg-sh.de/nx-build-base:1.0.0 AS builder + +ARG NODE_ENV +ARG CONFIGURATION + +# Turn off Nx Daemon +ENV CI=true + +WORKDIR /app/builder +COPY . . + +RUN echo "Building configuration: $CONFIGURATION..." + +RUN npx nx build admin --outputHashing=all --configuration ${CONFIGURATION:-development} \ + && ./node_modules/.bin/gzipper compress ./dist --verbose --exclude jpg,jpeg,png,ico,woff,woff2 + +FROM nginx:stable-alpine + +WORKDIR /usr/share/nginx/html + +COPY --from=builder /app/builder/dist/apps/admin ./ +COPY --from=builder /app/builder/apps/admin/nginx.conf /etc/nginx/nginx.conf diff --git a/alfa-client/apps/admin/nginx.conf b/alfa-client/apps/admin/nginx.conf new file mode 100644 index 0000000000000000000000000000000000000000..690983250aa4dafff17f7105da8aec82d60333e0 --- /dev/null +++ b/alfa-client/apps/admin/nginx.conf @@ -0,0 +1,66 @@ +# Verarbeitungsreihenfolge von location rules: +# -------------------------------------------------------------------------------------------------------------------------------------------- +# Search-Order Modifier Description Match-Type Stops-search-on-match +# -------------------------------------------------------------------------------------------------------------------------------------------- +# 1st = The URI must match the specified pattern exactly Simple-string Yes +# 2nd ^~ The URI must begin with the specified pattern Simple-string Yes +# 3rd (None) The URI must begin with the specified pattern Simple-string No +# 4th ~ The URI must be a case-sensitive match to the specified Rx Perl-Compatible-Rx Yes (first match) +# 4th ~* The URI must be a case-insensitive match to the specified Rx Perl-Compatible-Rx Yes (first match) +# N/A @ Defines a named location block. Simple-string Yes +# -------------------------------------------------------------------------------------------------------------------------------------------- +# +# Regex Matches werden bevorzugt verwendet. +# Mehr: https://stackoverflow.com/a/59846239/1546181 + +worker_processes 1; + +events { + worker_connections 1024; +} + +http { + server_tokens off; + access_log off; + error_log stderr crit; + + server { + listen 80; + server_name localhost; + + root /usr/share/nginx/html; + index index.html; + include /etc/nginx/mime.types; + + gzip on; + gzip_min_length 1000; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; + + # Add security related headers. + # TODO Fuehrt teilweise zu Content Security Policy Fehler (CSP). Cache deaktivieren beim Testen! + # see https://dri.es/headers?url=https://meine-domain.xy + #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + #add_header X-Content-Type-Options "nosniff" always; + #add_header Referrer-Policy "strict-origin-when-cross-origin" always; + #add_header X-Frame-Options "SAMEORIGIN" always; + # Tricky for Angular, see https://github.com/angular/angular-cli/issues/3430#issuecomment-415063027 + # Seit Angular 12 ist 'unsafe-inline' für script-src nötig, weil index.html:13 irgendwas geladen wird + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/manifest-src + #add_header Content-Security-Policy "default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self'; connect-src 'self'; manifest-src 'self'; font-src 'self'" always; + + location ^~ /api { + proxy_pass http://admin:8080/api; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + # Enable Path Routing (default for Angular) + location / { + try_files $uri$args $uri$args/ /index.html; + } + } +} diff --git a/alfa-client/apps/alfa/Dockerfile b/alfa-client/apps/alfa/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..8e7af2b647be7ff0f41eba6d876aecf542df6896 --- /dev/null +++ b/alfa-client/apps/alfa/Dockerfile @@ -0,0 +1,24 @@ +# Benutzt das vorher zu bauende Docker image "nx-build-base:x.y.z" +# Siehe ../Dockerfile.nx-build-base +FROM docker.ozg-sh.de/nx-build-base:1.0.0 AS builder + +ARG NODE_ENV +ARG CONFIGURATION + +# Turn off Nx Daemon +ENV CI=true + +WORKDIR /app/builder +COPY . . + +RUN echo "Building configuration: $CONFIGURATION..." + +RUN npx nx build alfa --outputHashing=all --configuration ${CONFIGURATION:-development} \ + && ./node_modules/.bin/gzipper compress ./dist --verbose --exclude jpg,jpeg,png,ico,woff,woff2 + +FROM nginx:stable-alpine + +WORKDIR /usr/share/nginx/html + +COPY --from=builder /app/builder/dist/apps/alfa ./ +COPY --from=builder /app/builder/apps/alfa/nginx.conf /etc/nginx/nginx.conf diff --git a/alfa-client/apps/alfa/nginx.conf b/alfa-client/apps/alfa/nginx.conf new file mode 100644 index 0000000000000000000000000000000000000000..44c76f4fe8b366bcbda480bed2c3a1aed60602c8 --- /dev/null +++ b/alfa-client/apps/alfa/nginx.conf @@ -0,0 +1,66 @@ +# Verarbeitungsreihenfolge von location rules: +# -------------------------------------------------------------------------------------------------------------------------------------------- +# Search-Order Modifier Description Match-Type Stops-search-on-match +# -------------------------------------------------------------------------------------------------------------------------------------------- +# 1st = The URI must match the specified pattern exactly Simple-string Yes +# 2nd ^~ The URI must begin with the specified pattern Simple-string Yes +# 3rd (None) The URI must begin with the specified pattern Simple-string No +# 4th ~ The URI must be a case-sensitive match to the specified Rx Perl-Compatible-Rx Yes (first match) +# 4th ~* The URI must be a case-insensitive match to the specified Rx Perl-Compatible-Rx Yes (first match) +# N/A @ Defines a named location block. Simple-string Yes +# -------------------------------------------------------------------------------------------------------------------------------------------- +# +# Regex Matches werden bevorzugt verwendet. +# Mehr: https://stackoverflow.com/a/59846239/1546181 + +worker_processes 1; + +events { + worker_connections 1024; +} + +http { + server_tokens off; + access_log off; + error_log stderr crit; + + server { + listen 80; + server_name localhost; + + root /usr/share/nginx/html; + index index.html; + include /etc/nginx/mime.types; + + gzip on; + gzip_min_length 1000; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; + + # Add security related headers. + # TODO Fuehrt teilweise zu Content Security Policy Fehler (CSP). Cache deaktivieren beim Testen! + # see https://dri.es/headers?url=https://meine-domain.xy + #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + #add_header X-Content-Type-Options "nosniff" always; + #add_header Referrer-Policy "strict-origin-when-cross-origin" always; + #add_header X-Frame-Options "SAMEORIGIN" always; + # Tricky for Angular, see https://github.com/angular/angular-cli/issues/3430#issuecomment-415063027 + # Seit Angular 12 ist 'unsafe-inline' für script-src nötig, weil index.html:13 irgendwas geladen wird + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/manifest-src + #add_header Content-Security-Policy "default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self'; connect-src 'self'; manifest-src 'self'; font-src 'self'" always; + + location ^~ /api { + proxy_pass http://alfa:8080/api; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + # Enable Path Routing (default for Angular) + location / { + try_files $uri$args $uri$args/ /index.html; + } + } +} diff --git a/alfa-client/bin/nx-build-base.sh b/alfa-client/bin/nx-build-base.sh new file mode 100755 index 0000000000000000000000000000000000000000..c72b4c397f62eb135ef800c04e351ef42f2636bf --- /dev/null +++ b/alfa-client/bin/nx-build-base.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# +# Benötigt vorheriges `docker login docker.ozg-sh.de -u developer -p ******` + +# TODO Image-Version aus der package.json extrahieren +VERSION="1.0.0" +TAG="docker.ozg-sh.de/nx-build-base:${VERSION}" + +echo "Building ${TAG}..." +docker buildx build --push --platform linux/amd64 --tag ${TAG} -f Dockerfile.nx-build-base . diff --git a/alfa-client/package-lock.json b/alfa-client/package-lock.json index 3f14f9b084cda2f11cd4385202af28a8503b4bc0..a4aaf8a1125f5d06a2582e2844c4eca54b79de67 100644 --- a/alfa-client/package-lock.json +++ b/alfa-client/package-lock.json @@ -88,6 +88,7 @@ "eslint": "8.46.0", "eslint-config-prettier": "9.0.0", "eslint-plugin-cypress": "2.15.1", + "gzipper": "^7.2.0", "jasmine-marbles": "~0.9.2", "jest": "^29.4.3", "jest-environment-jsdom": "^29.4.3", @@ -5677,6 +5678,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@gfx/zopfli": { + "version": "1.0.15", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/@gfx/zopfli/-/zopfli-1.0.15.tgz", + "integrity": "sha512-7mBgpi7UD82fsff5ThQKet0uBTl4BYerQuc+/qA1ELTwWEiIedRTcD3JgiUu9wwZ2kytW8JOb165rSdAt8PfcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -21484,6 +21498,13 @@ "node": ">=12" } }, + "node_modules/duplex-maker": { + "version": "1.0.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/duplex-maker/-/duplex-maker-1.0.0.tgz", + "integrity": "sha512-KoHuzggxg7f+vvjqOHfXxaQYI1POzBm+ah0eec7YDssZmbt6QFBI8d1nl5GQwAgR2f+VQCPvyvZtmWWqWuFtlA==", + "dev": true, + "license": "MIT" + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/duplexer/-/duplexer-0.1.2.tgz", @@ -24066,6 +24087,36 @@ "dev": true, "license": "MIT" }, + "node_modules/gzipper": { + "version": "7.2.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/gzipper/-/gzipper-7.2.0.tgz", + "integrity": "sha512-qwYQr7GWBXIm9Cdzud+tyM/s9N+QFzGDZoF9YR8RYJbDKOYowzjMDPEinFtm78EQeeYMC/FJW2FXY0bHkyUgsA==", + "dev": true, + "license": "GPL-3.0", + "dependencies": { + "@gfx/zopfli": "^1.0.15", + "commander": "^7.2.0", + "deep-equal": "^2.0.5", + "simple-zstd": "^1.4.0", + "uuid": "^8.3.2" + }, + "bin": { + "gzipper": "bin/index.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gzipper/node_modules/commander": { + "version": "7.2.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/handle-thing/-/handle-thing-2.0.1.tgz", @@ -25796,6 +25847,13 @@ "node": ">=8" } }, + "node_modules/is-zst": { + "version": "1.0.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/is-zst/-/is-zst-1.0.0.tgz", + "integrity": "sha512-ZA5lvshKAl8z30dX7saXLpVhpsq3d2EHK9uf7qtUjnOtdw4XBpAoWb2RvZ5kyoaebdoidnGI0g2hn9Z7ObPbww==", + "dev": true, + "license": "MIT" + }, "node_modules/isarray": { "version": "2.0.5", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/isarray/-/isarray-2.0.5.tgz", @@ -34345,6 +34403,18 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/process-streams": { + "version": "1.0.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/process-streams/-/process-streams-1.0.1.tgz", + "integrity": "sha512-Z+FHhxiBhiQ4t/xTY3Bo2SxZG/CehflyckFsQirAXFRf/BfVnDePzpo58eq9JI4XfFu1RnX5C5EAE6V4sce1+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplex-maker": "^1.0.0", + "quotemeta": "0.0.0", + "tempfile": "^1.1.0" + } + }, "node_modules/progress": { "version": "2.0.3", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/progress/-/progress-2.0.3.tgz", @@ -34710,6 +34780,13 @@ ], "license": "MIT" }, + "node_modules/quotemeta": { + "version": "0.0.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/quotemeta/-/quotemeta-0.0.0.tgz", + "integrity": "sha512-1XGObUh7RN5b58vKuAsrlfqT+Rc4vmw8N4pP9gFCq1GFlTdV0Ex/D2Ro1Drvrqj++HPi3ig0Np17XPslELeMRA==", + "dev": true, + "license": "MIT" + }, "node_modules/ramda": { "version": "0.29.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/ramda/-/ramda-0.29.0.tgz", @@ -36310,6 +36387,29 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/simple-zstd": { + "version": "1.4.2", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/simple-zstd/-/simple-zstd-1.4.2.tgz", + "integrity": "sha512-kGYEvT33M5XfyQvvW4wxl3eKcWbdbCc1V7OZzuElnaXft0qbVzoIIXHXiCm3JCUki+MZKKmvjl8p2VGLJc5Y/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-zst": "^1.0.0", + "peek-stream": "^1.1.3", + "process-streams": "^1.0.1", + "through2": "^4.0.2" + } + }, + "node_modules/simple-zstd/node_modules/through2": { + "version": "4.0.2", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/sisteransi/-/sisteransi-1.0.5.tgz", @@ -37897,6 +37997,28 @@ "rimraf": "bin.js" } }, + "node_modules/tempfile": { + "version": "1.1.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/tempfile/-/tempfile-1.1.1.tgz", + "integrity": "sha512-NjT12fW6pSEKz1eVcADgaKfeM+XZ4+zSaqVz46XH7+CiEwcelnwtGWRRjF1p+xyW2PVgKKKS2UUw1LzRelntxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "^1.0.0", + "uuid": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tempfile/node_modules/uuid": { + "version": "2.0.3", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha512-FULf7fayPdpASncVy4DLh3xydlXEJJpvIELjYjNeQWYUZ9pclcpvCZSr2gkmN2FrrGcI7G/cJsIEwk5/8vfXpg==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "license": "MIT" + }, "node_modules/tempy": { "version": "1.0.1", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/tempy/-/tempy-1.0.1.tgz", diff --git a/alfa-client/package.json b/alfa-client/package.json index 19d7727d9d58ed622d516e4b1c3568490f7df9cf..b1dbd1d07e3adc8b1db21e5a5c31f73ab182ef76 100644 --- a/alfa-client/package.json +++ b/alfa-client/package.json @@ -121,6 +121,7 @@ "eslint": "8.46.0", "eslint-config-prettier": "9.0.0", "eslint-plugin-cypress": "2.15.1", + "gzipper": "^7.2.0", "jasmine-marbles": "~0.9.2", "jest": "^29.4.3", "jest-environment-jsdom": "^29.4.3",