Skip to content

Commit c9e066c

Browse files
committed
Standalone favicon inlining, container hardening en CI rename
- Inline favicon als data-URI in standalone HTML via custom Vite plugin - Kopieer favicon naar boekhouding-frontend public/ voor /favicon.ico - Voeg security headers toe aan standalone invulhulpen nginx location - Hernoem workflow naar "Build Containers"
1 parent d7e284a commit c9e066c

4 files changed

Lines changed: 49 additions & 2 deletions

File tree

.github/workflows/build-containers.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build Experimental Containers
1+
name: Build Containers
22

33
on:
44
push:

apps/standalone-form/vite.config.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,51 @@
1+
import { readFileSync } from 'node:fs'
12
import { fileURLToPath, URL } from 'node:url'
23

3-
import { defineConfig } from 'vite'
4+
import { defineConfig, type Plugin } from 'vite'
45
import vue from '@vitejs/plugin-vue'
56
import vueDevTools from 'vite-plugin-vue-devtools'
67
import { viteSingleFile } from 'vite-plugin-singlefile'
78

9+
/** Inline favicon as data-URI in built HTML so the output is truly a single file. */
10+
function inlineFavicon(): Plugin {
11+
return {
12+
name: 'inline-favicon',
13+
enforce: 'post',
14+
generateBundle(_, bundle) {
15+
// Find the favicon asset and the HTML entry
16+
let faviconKey: string | undefined
17+
let faviconData: Uint8Array | undefined
18+
for (const [key, chunk] of Object.entries(bundle)) {
19+
if (key.endsWith('.ico') && chunk.type === 'asset') {
20+
faviconKey = key
21+
faviconData = chunk.source as Uint8Array
22+
break
23+
}
24+
}
25+
if (!faviconKey || !faviconData) return
26+
27+
const base64 = Buffer.from(faviconData).toString('base64')
28+
29+
// Replace favicon href in HTML and remove the loose asset
30+
for (const chunk of Object.values(bundle)) {
31+
if (chunk.type === 'asset' && chunk.fileName.endsWith('.html')) {
32+
chunk.source = (chunk.source as string).replace(
33+
new RegExp(`<link rel="icon" href="[^"]*${faviconKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}"[^>]*>`),
34+
`<link rel="icon" href="data:image/x-icon;base64,${base64}" />`,
35+
)
36+
}
37+
}
38+
delete bundle[faviconKey]
39+
},
40+
}
41+
}
842

943
// https://vite.dev/config/
1044
export default defineConfig(({ mode }) => ({
1145
plugins: [
1246
vue(),
1347
mode === 'development' && vueDevTools(),
48+
inlineFavicon(),
1449
viteSingleFile(),
1550
],
1651
base: '/',

containers/frontend/Containerfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ COPY apps/boekhouding-frontend/ apps/boekhouding-frontend/
2626
WORKDIR /app/apps/standalone-form
2727
RUN npx vite build
2828

29+
RUN mkdir -p /app/apps/boekhouding-frontend/public && \
30+
cp /app/apps/boekhouding-frontend/node_modules/@nl-rvo/assets/images/favicon/favicon.ico \
31+
/app/apps/boekhouding-frontend/public/favicon.ico
32+
2933
# Build frontend SPA
3034
WORKDIR /app/apps/boekhouding-frontend
3135
RUN npx vite build

containers/frontend/default.conf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,17 @@ server {
2828
}
2929

3030
# Standalone invulhulp (single-file SPA via viteSingleFile)
31+
# viteSingleFile inlinet alle JS/CSS → vereist 'unsafe-inline' voor script-src
3132
location /invulhulpen/ {
3233
alias /usr/share/nginx/html/invulhulpen/;
3334
try_files $uri /invulhulpen/index.html;
35+
36+
add_header X-Content-Type-Options "nosniff" always;
37+
add_header X-Frame-Options "SAMEORIGIN" always;
38+
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
39+
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
40+
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self';" always;
41+
add_header Strict-Transport-Security "max-age=31536000" always;
3442
}
3543

3644
# Hoofd-SPA met Vue Router fallback

0 commit comments

Comments
 (0)