Compare commits

...

50 Commits

Author SHA1 Message Date
Clemens Hering
a0d1e10c78 Added pipeline image scan
All checks were successful
Build and Deploy Container / build_and_deploy (push) Successful in 35s
Scan Image for CVEs / scan_image (push) Successful in 20s
2025-11-15 08:31:55 +01:00
Clemens Hering
88892ac425 Added pipeline image scan
Some checks failed
Build and Deploy Container / build_and_deploy (push) Successful in 35s
Scan Image for CVEs / scan_image (push) Failing after 16s
2025-11-15 08:28:09 +01:00
Clemens Hering
b7d091c84a Added pipeline image scan
Some checks failed
Build and Deploy Container / build_and_deploy (push) Successful in 36s
Scan Image for CVEs / build_and_deploy (push) Failing after 15s
2025-11-15 08:24:31 +01:00
Clemens Hering
93ba03f619 Added pipeline image scan
Some checks failed
Build and Deploy Container / build_and_deploy (push) Successful in 36s
Scan Image for CVEs / build_and_deploy (push) Failing after 15s
2025-11-15 08:22:15 +01:00
Clemens Hering
92d7e04154 Added package-lock.json
All checks were successful
Build and Deploy Container / build_and_deploy (push) Successful in 50s
2025-11-14 17:40:16 +01:00
Clemens Hering
51834e732e Removed package-lock,json
All checks were successful
Build and Deploy Container / build_and_deploy (push) Successful in 1m24s
2025-11-14 17:36:14 +01:00
Clemens Hering
3498a23ee1 Fixed enum. Chanded DevOps to DevSecOps
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 1s
2025-11-14 17:34:21 +01:00
Clemens Hering
c5b66c4298 back to alpine image
All checks were successful
Build and Deploy Container / build_and_deploy (push) Successful in 1m23s
2025-11-11 06:33:37 +01:00
Clemens Hering
f3984f8b57 fixup
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 1m28s
2025-11-11 06:29:14 +01:00
Clemens Hering
d0292ac192 base is now wolfios
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 23s
2025-11-11 06:27:09 +01:00
Clemens Hering
ef15dc1dfe traefik labels now consistant 2025-11-11 06:24:04 +01:00
Clemens Hering
105600ed9b fixed dubilacted line in labels
All checks were successful
Build and Deploy Container / build_and_deploy (push) Successful in 40s
2025-11-11 06:07:09 +01:00
Clemens Hering
d0234fc270 Breaking changes
All checks were successful
Build and Deploy Container / build_and_deploy (push) Successful in 41s
2025-11-11 06:02:48 +01:00
Clemens Hering
0d156b9107 Breaking changes
All checks were successful
Build and Deploy Container / build_and_deploy (push) Successful in 40s
2025-11-10 21:13:16 +01:00
Clemens Hering
9fd3df83e7 Breaking changes
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 33s
2025-11-10 21:10:31 +01:00
Clemens Hering
e9a52787ce added deployment check
All checks were successful
Build and Deploy Container / build_and_deploy (push) Successful in 40s
2025-11-10 19:25:29 +01:00
Clemens Hering
c48eaf6b49 added deployment check 2025-11-10 19:24:34 +01:00
Clemens Hering
2ee6580519 added deployment check
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 1m20s
2025-11-10 19:21:04 +01:00
Clemens Hering
85a4d3cb59 added deployment check
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 40s
2025-11-10 19:08:30 +01:00
Clemens Hering
10d0fd0c98 added deployment check
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 40s
2025-11-10 19:06:44 +01:00
Clemens Hering
14dadbbbae added deployment check
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 22s
2025-11-10 18:51:40 +01:00
Clemens Hering
cdd8b400d9 added deployment check
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 39s
2025-11-10 18:41:04 +01:00
Clemens Hering
aeb50fe3be added deployment check
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 39s
2025-11-10 18:38:04 +01:00
Clemens Hering
832e7e4056 added deployment check
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 40s
2025-11-10 18:36:14 +01:00
Clemens Hering
8ff2a0513f deploy test
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 40s
2025-11-10 08:02:15 +01:00
Clemens Hering
c677c8533c deploy test
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 1m19s
2025-11-10 07:59:01 +01:00
Clemens Hering
b2a4b0ff3b deploy test
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 28s
2025-11-10 07:53:15 +01:00
Clemens Hering
c04d55c02f added deployment check
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 28s
2025-11-10 07:51:00 +01:00
Clemens Hering
3314e31514 removed network requirement in quadlet 2025-11-10 07:47:58 +01:00
Clemens Hering
3f0f5a35c6 fixed quadlet and path
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 28s
2025-11-10 07:43:20 +01:00
Clemens Hering
3c3d4eda48 disable astro telemetry 2025-11-10 07:39:14 +01:00
Clemens Hering
27237bd8c6 disable astro telemetry 2025-11-10 07:36:17 +01:00
Clemens Hering
70cd6a879c test local clone
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 29s
2025-11-10 07:18:49 +01:00
Clemens Hering
ae6d0e28a3 test local clone
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 1m9s
2025-11-09 17:56:30 +01:00
Clemens Hering
e6d6d7999c test local clone
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 21s
2025-11-09 17:50:08 +01:00
Clemens Hering
22a90824ff test local clone
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 20s
2025-11-09 17:47:13 +01:00
Clemens Hering
f62c0f21d8 test local clone
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 21s
2025-11-09 17:41:40 +01:00
Clemens Hering
38b486ab2c test local clone
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 19s
2025-11-09 16:23:36 +01:00
8336d9c19b Update .gitea/workflows/deploy.yaml
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 47s
2025-11-09 16:13:05 +01:00
Clemens Hering
c9a470e044 test local clone
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 8s
2025-11-09 16:00:44 +01:00
Clemens Hering
4b1ae663ec test local clone
Some checks failed
Build and Deploy Container / build_and_deploy (push) Has been cancelled
2025-11-09 15:53:26 +01:00
Clemens Hering
e09e0018b2 test local clone
Some checks failed
Build and Deploy Container / build_and_deploy (push) Has been cancelled
2025-11-09 15:42:23 +01:00
Clemens Hering
8bd5ed3e05 test local clone
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 3s
2025-11-09 14:43:22 +01:00
Clemens Hering
73e364dd11 test local clone
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 1s
2025-11-09 09:11:53 +01:00
Clemens Hering
31555d8386 test local clone
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 4s
2025-11-09 08:38:52 +01:00
Clemens Hering
bf48b02fb5 test local clone
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 0s
2025-11-09 08:32:56 +01:00
Clemens Hering
fc2393b5c3 test local clone
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 0s
2025-11-09 08:30:04 +01:00
Clemens Hering
b4b668dd41 fixed runner label
Some checks failed
Build and Deploy Container / build_and_deploy (push) Failing after 8s
2025-11-09 07:54:39 +01:00
Clemens Hering
a6da3566d0 Chaned fixed values to variables
Some checks failed
Build and Deploy Container / build_and_deploy (push) Has been cancelled
2025-11-08 15:08:54 +01:00
Clemens Hering
56cb4555f8 optimisation for cell phones
Some checks failed
Build and Deploy Container / build_and_deploy (push) Has been cancelled
2025-11-08 12:32:59 +01:00
20 changed files with 6761 additions and 137 deletions

View File

@@ -6,28 +6,102 @@ on:
- main
- develop
env: # global: unkritische, strukturgebende Variablen
TARGET_HOST: host.containers.internal
TARGET_USER: traefik
APP_DIR: /home/traefik/valtrix-website
CONTAINER_NAME: valtrix-website
QUADLET_FILE: ./deploy/valtrix-website.container
jobs:
build_and_deploy:
runs-on: self-hosted
runs-on: ubuntu-latest
env: # Job-spezifisch: Secrets und sensible Werte
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }}
steps:
- name: Pre-clean Git global config (avoid https→ssh rewrite)
shell: bash
run: |
set -euo pipefail
echo "Cleaning up global git config"
git config --global --unset-all core.sshCommand || true
for key in $(git config --global --get-regexp '^url\\..*\\.insteadof$' 2>/dev/null | awk '{print $1}'); do
if echo "$key" | grep -qi 'gitea\\.smb-corp\\.de'; then
git config --global --unset-all "$key" || true
fi
done
- name: Setup SSH for git/scp
shell: bash
run: |
install -m 700 -d ~/.ssh
printf "%s\n" "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
printf "%s\n" "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts || true
chmod 644 ~/.ssh/known_hosts
# Ensure host keys exist
(ssh-keygen -F "$TARGET_HOST" >/dev/null || ssh-keyscan -H "$TARGET_HOST" >> ~/.ssh/known_hosts) || true
(ssh-keygen -F gitea.smb-corp.de >/dev/null || ssh-keyscan -H gitea.smb-corp.de >> ~/.ssh/known_hosts) || true
- name: Checkout Repository
uses: actions/checkout@v4
- name: Copy code to target host
- name: Copy repository to target host (atomic replace)
shell: bash
run: |
rsync -avz --delete ./ user@zielserver:/home/user/app/
set -euo pipefail
TMP_DIR="$APP_DIR.tmp.$(date +%s)"
ssh -i ~/.ssh/id_ed25519 $TARGET_USER@$TARGET_HOST "mkdir -p '$TMP_DIR'"
scp -r -i ~/.ssh/id_ed25519 ./* $TARGET_USER@$TARGET_HOST:$TMP_DIR/
ssh -i ~/.ssh/id_ed25519 $TARGET_USER@$TARGET_HOST "
set -euo pipefail;
if [ -d '$APP_DIR' ]; then rm -rf '$APP_DIR'; fi;
mv '$TMP_DIR' '$APP_DIR'
"
- name: Build container on target host
shell: bash
run: |
ssh user@zielserver '
cd /home/user/app
podman build -t myapp:latest .
'
ssh -i ~/.ssh/id_ed25519 $TARGET_USER@$TARGET_HOST "
set -euo pipefail
export APP_DIR='$APP_DIR' CONTAINER_NAME='$CONTAINER_NAME'
cd \"\$APP_DIR\"
echo 'Building container: '\$CONTAINER_NAME 'in' \$APP_DIR
podman build -t \$CONTAINER_NAME:latest .
"
- name: Replace Quadlet file
- name: Backup existing Quadlet file
shell: bash
run: |
scp ./systemd/myapp.container user@zielserver:~/.config/containers/systemd/
ssh user@zielserver '
ssh -i ~/.ssh/id_ed25519 $TARGET_USER@$TARGET_HOST "
set -euo pipefail
export CONTAINER_NAME='$CONTAINER_NAME'
QFILE=~/.config/containers/systemd/\$CONTAINER_NAME.container
test -f \"\$QFILE\" && cp \"\$QFILE\" \"\$QFILE.bak\" || true
"
- name: Replace Quadlet file and restart service
shell: bash
run: |
scp -i ~/.ssh/id_ed25519 "$QUADLET_FILE" $TARGET_USER@$TARGET_HOST:~/.config/containers/systemd/
ssh -i ~/.ssh/id_ed25519 $TARGET_USER@$TARGET_HOST "
set -euo pipefail
export CONTAINER_NAME='$CONTAINER_NAME'
systemctl --user daemon-reload
systemctl --user restart myapp.service
'
systemctl --user restart \$CONTAINER_NAME.service
echo 'Service restarted: '\$CONTAINER_NAME
"
- name: Verify deployment
shell: bash
run: |
ssh -i ~/.ssh/id_ed25519 $TARGET_USER@$TARGET_HOST "
set -euo pipefail
export CONTAINER_NAME='$CONTAINER_NAME'
echo 'Running containers:'
podman ps --filter \"name=\$CONTAINER_NAME\" --format \"table {{.Names}}\t{{.Image}}\t{{.Status}}\"
echo '--- Last 20 log lines ---'
podman logs \$CONTAINER_NAME --tail 20 || echo 'No logs available'
"

View File

@@ -0,0 +1,41 @@
name: Scan Image for CVEs
on:
push:
branches:
- main
- develop
env: # global: unkritische, strukturgebende Variablen
TARGET_HOST: host.containers.internal
TARGET_USER: traefik
CONTAINER_NAME: localhost/valtrix-website
jobs:
scan_image:
runs-on: ubuntu-latest
env: # Job-spezifisch: Secrets und sensible Werte
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }}
steps:
- name: Setup SSH for git/scp
shell: bash
run: |
install -m 700 -d ~/.ssh
printf "%s\n" "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
printf "%s\n" "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts || true
chmod 644 ~/.ssh/known_hosts
# Ensure host keys exist
(ssh-keygen -F "$TARGET_HOST" >/dev/null || ssh-keyscan -H "$TARGET_HOST" >> ~/.ssh/known_hosts) || true
(ssh-keygen -F gitea.smb-corp.de >/dev/null || ssh-keyscan -H gitea.smb-corp.de >> ~/.ssh/known_hosts) || true
- name: Scan container image with Trivy
shell: bash
run: |
ssh -i ~/.ssh/id_ed25519 $TARGET_USER@$TARGET_HOST "
set -euo pipefail
export CONTAINER_NAME='$CONTAINER_NAME'
trivy image localhost/valtrix-website:latest
"

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
node_modules
package-lock.json
dist
.env
.DS_Store

View File

@@ -17,6 +17,7 @@ ENV NODE_ENV=production
ENV PORT=3000
ENV WEB_ROOT=/app/dist
ENV TZ=Europe/Berlin
ENV ASTRO_TELEMETRY_DISABLED=1
COPY --from=builder /app/dist /app/dist
COPY server.mjs /app/server.mjs
# Drop root: use the pre-created node user

36
Containerfile.notworking Normal file
View File

@@ -0,0 +1,36 @@
# syntax=docker/dockerfile:1.7
###########
# BUILD STAGE
###########
FROM cgr.dev/chainguard/node:latest-dev AS build
WORKDIR /app
# Copy dependency manifests
COPY package*.json ./
# Install all deps (inkl. dev)
RUN --mount=type=cache,target=/root/.npm npm ci
# Copy app source and build
COPY . .
RUN npm run build
###########
# RUNTIME STAGE
###########
FROM cgr.dev/chainguard/node:latest
ENV NODE_ENV=production
WORKDIR /app
# Copy only whats needed to run
COPY --from=build /app/package*.json ./
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/server.mjs ./server.mjs
# Chainguard runs as nonroot by default (user `nonroot`)
USER node
#EXPOSE 3000
CMD ["node", "./server.mjs"]

View File

@@ -1,33 +1,30 @@
[Unit]
Description=Valtrix Website
Requires=edge.network
After=edge.network
[Container]
Image=localhost/valtrixweb
ContainerName=valtrixwebsite
Image=localhost/valtrix-website
ContainerName=valtrix-website
Network=edge
AutoUpdate=registry
Environment=TZ=Europe/Berlin
#Traefik Labels
Label=traefik.enable=true
Label=traefik.http.routers.wtw.rule=Host(`www.valtrix.systems`)
Label=traefik.http.services.wtw.loadbalancer.server.port=3000
Label=traefik.http.routers.wtw.entrypoints=websecure
Label=traefik.http.routers.wtw.tls=true
Label=traefik.http.routers.wtw.tls.certresolver=le
Label="traefik.enable=true"
Label="traefik.http.routers.wtw.rule=Host(`www.valtrix.systems`)"
Label="traefik.http.services.wtw.loadbalancer.server.port=3000"
Label="traefik.http.routers.wtw.entrypoints=websecure"
Label="traefik.http.routers.wtw.tls=true"
Label="traefik.http.routers.wtw.tls.certresolver=le"
Label=traefik.http.routers.wtw-http.rule=Host(`www.valtrix.systems`)
Label=traefik.http.routers.wtw-http.entrypoints=web
Label=traefik.http.routers.wtw-http.middlewares=wtw-redirect
Label=traefik.http.middlewares.wtw-redirect.redirectscheme.scheme=https
Label=traefik.http.middlewares.wtw-redirect.redirectscheme.permanent=true
Label=traefik.http.routers.wtw.middlewares=secure-headers@file
Label="traefik.http.routers.wtw-http.rule=Host(`www.valtrix.systems`)"
Label="traefik.http.routers.wtw-http.entrypoints=web"
Label="traefik.http.routers.wtw-http.middlewares=wtw-redirect"
Label="traefik.http.middlewares.wtw-redirect.redirectscheme.scheme=https"
Label="traefik.http.middlewares.wtw-redirect.redirectscheme.permanent=true"
Label="traefik.http.routers.wtw.middlewares=secure-headers@file"
Label="traefik.http.middlewares.wtw-sec.headers.customResponseHeaders.Content-Security-Policy=default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; script-src-elem 'self' 'unsafe-inline'; connect-src 'self' wss: https:; font-src 'self' data:; worker-src 'self' blob:;"
Label=traefik.http.routers.wtw.middlewares=wtw-sec@docker
Label=traefik.http.routers.wtw.middlewares=wtw-sec@docker
Label="traefik.http.routers.wtw.middlewares=wtw-sec@docker"
Label="traefik.http.routers.wtw.middlewares=auth"
Label="traefik.http.middlewares.auth.basicauth.users=smb:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"

6320
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,13 +4,15 @@ const { title = "Valtrix Struktur schafft Wert", description = "Cloud, Secur
// Language & routing helpers
const pathname = Astro.url.pathname;
const isEn = pathname.startsWith('/en');
const isDe = pathname.startsWith('/de');
const lang = isEn ? 'en' : 'de';
const prefix = isEn ? '/en' : '';
const stripEn = (p) => p.replace(/^\/en(?=\/|$)/, '');
const altHref = isEn ? (stripEn(pathname) || '/') : ('/en' + (pathname === '/' ? '' : pathname));
// Default to /de for links so navigation always points to language-prefixed routes
const prefix = isEn ? '/en' : '/de';
const stripLang = (p) => p.replace(/^\/(?:en|de)(?=\/|$)/, '');
const altHref = isEn ? ('/de' + (stripLang(pathname) || '/')) : ('/en' + (stripLang(pathname) || '/'));
// Canonical/alternate URLs (use provided path when present for og/link tags)
const canonicalPath = path || pathname;
const deHref = stripEn(canonicalPath) || '/';
const deHref = canonicalPath.startsWith('/de') ? canonicalPath : ('/de' + (canonicalPath === '/' ? '' : canonicalPath));
const enHref = canonicalPath.startsWith('/en') ? canonicalPath : ('/en' + (canonicalPath === '/' ? '' : canonicalPath));
// Nav labels per language
const labels = isEn ? {
@@ -50,22 +52,39 @@ const labels = isEn ? {
<link rel="alternate" hreflang="en" href={`https://valtrix.systems${enHref}`} />
</head>
<body class="bg-white transition-colors duration-300">
<header class="max-w-7xl mx-auto px-6 py-6">
<header class="max-w-7xl mx-auto px-6 py-4">
<nav class="flex items-center justify-between">
<a href={`${prefix || '/'}`} class="flex items-center gap-3 font-semibold">
<a href={`${prefix || '/'}`} class="flex items-center gap-3 font-semibold min-w-0">
<img src="/logo-valtrix.png" alt="Valtrix" class="h-9 w-9 rounded-md shadow-sm"/>
<span class="tracking-wide text-lg">VALTRIX</span>
</a>
<div class="flex items-center gap-6">
<div class="hidden md:flex items-center gap-6">
<a href={`${prefix}/leistungen`} class="text-textMuted hover:text-white">{labels.services}</a>
<a href={`${prefix}/sicherheit`} class="text-textMuted hover:text-white">{labels.security}</a>
<a href={`${prefix}/cases`} class="text-textMuted hover:text-white">{labels.cases}</a>
<a href={`${prefix}/ueber-uns`} class="text-textMuted hover:text-white">{labels.about}</a>
<a href={`${prefix}/kontakt`} class="px-4 py-2 rounded-brand bg-primary text-white hover:opacity-90">{labels.contact}</a>
<a href={altHref} class="px-3 py-2 border rounded-brand hover:bg-graybg" aria-label="Language switch">{labels.langShort}</a>
<button id="themeToggle" aria-label="Theme" class="px-3 py-2 border rounded-brand hover:bg-graybg">Light</button>
<button data-theme-toggle aria-label="Theme" class="px-3 py-2 border rounded-brand hover:bg-graybg">Light</button>
</div>
<button id="navToggle" class="md:hidden px-3 py-2 border rounded-brand hover:bg-graybg" aria-controls="mobileMenu" aria-expanded="false" aria-label="Navigation">
Menu
</button>
</nav>
<!-- Mobile menu -->
<div id="mobileMenu" class="md:hidden hidden mt-3 border rounded-brand p-4 bg-[color:var(--color-bg,#0b0e14)]">
<div class="grid gap-3">
<a href={`${prefix}/leistungen`} class="block text-white">{labels.services}</a>
<a href={`${prefix}/sicherheit`} class="block text-white">{labels.security}</a>
<a href={`${prefix}/cases`} class="block text-white">{labels.cases}</a>
<a href={`${prefix}/ueber-uns`} class="block text-white">{labels.about}</a>
<a href={`${prefix}/kontakt`} class="block px-4 py-2 text-center rounded-brand bg-primary text-white hover:opacity-90">{labels.contact}</a>
<div class="flex items-center gap-3 pt-2 border-t mt-2">
<a href={altHref} class="px-3 py-2 border rounded-brand hover:bg-graybg" aria-label="Language switch">{labels.langShort}</a>
<button data-theme-toggle aria-label="Theme" class="px-3 py-2 border rounded-brand hover:bg-graybg">Light</button>
</div>
</div>
</div>
</header>
<main class="max-w-7xl mx-auto px-6">
<slot />
@@ -77,14 +96,36 @@ const labels = isEn ? {
© {new Date().getFullYear()} Valtrix · <a href={`${prefix}/impressum`} class="underline">{labels.imprint}</a> · <a href={`${prefix}/datenschutz`} class="underline">{labels.privacy}</a>
</footer>
<script>
const btn = document.getElementById('themeToggle');
// Theme toggle for all buttons
const html = document.documentElement;
const set = (m)=>{ html.setAttribute('data-theme', m); btn.textContent = m==='dark' ? 'Light' : 'Dark'; };
set(localStorage.getItem('theme')||'dark');
btn?.addEventListener('click', ()=>{
const themeBtns = Array.from(document.querySelectorAll('[data-theme-toggle]'));
const setTheme = (m)=>{
html.setAttribute('data-theme', m);
themeBtns.forEach(b=> b.textContent = m==='dark' ? 'Light' : 'Dark');
};
setTheme(localStorage.getItem('theme')||'dark');
themeBtns.forEach(btn=> btn.addEventListener('click', ()=>{
const next = html.getAttribute('data-theme')==='dark' ? 'light' : 'dark';
set(next); localStorage.setItem('theme', next);
setTheme(next); localStorage.setItem('theme', next);
}));
// Mobile nav toggle
const navToggle = document.getElementById('navToggle');
const mobileMenu = document.getElementById('mobileMenu');
const closeMenu = () => {
mobileMenu?.classList.add('hidden');
navToggle?.setAttribute('aria-expanded','false');
};
navToggle?.addEventListener('click', ()=>{
const isOpen = !mobileMenu.classList.contains('hidden');
if (isOpen) { closeMenu(); }
else {
mobileMenu.classList.remove('hidden');
navToggle.setAttribute('aria-expanded','true');
}
});
// Close menu on link click
mobileMenu?.querySelectorAll('a').forEach(a=> a.addEventListener('click', closeMenu));
</script>
</body>
</html>

View File

@@ -1,15 +1,15 @@
---
import Base from "../layouts/Base.astro";
import Base from "../../layouts/Base.astro";
const items = [
{ slug: "cloud-migration", title: "Sichere Cloud-Migration (AWS/Azure)", summary: "Landing Zone, Zero-Trust, Pipeline & Observability in 6 Wochen." },
{ slug: "k8s-hardening", title: "Kubernetes Hardening & GitOps", summary: "CIS Benchmarks, OPA Policies-as-Code, ArgoCD Rollouts." }
];
---
<Base path="/cases" title="Referenzen Valtrix">
<Base path="/de/cases" title="Referenzen Valtrix">
<h1 class="text-3xl font-bold mt-8">Referenzen</h1>
<div class="grid md:grid-cols-2 gap-6 mt-6">
{items.map(i => (
<a href={`/cases/${i.slug}`} class="p-6 border rounded-brand card hover:shadow-sm transition-shadow block">
<a href={`/de/cases/${i.slug}`} class="p-6 border rounded-brand card hover:shadow-sm transition-shadow block">
<h2 class="font-semibold text-lg">{i.title}</h2>
<p class="mt-2 text-textMuted">{i.summary}</p>
<span class="mt-3 inline-block text-primary underline">Case ansehen</span>

View File

@@ -0,0 +1,36 @@
---
import Base from "../../../layouts/Base.astro";
---
<Base path="/de/cases/cloud-migration" title="Case Study Sichere Cloud-Migration">
<h1 class="text-3xl font-bold mt-8">Case Study: Sichere Cloud-Migration</h1>
<p class="mt-4 text-textMuted">Enterprise-Migration von On-Prem nach Cloud mit Fokus auf Sicherheit & Compliance.</p>
<section class="mt-8 grid md:grid-cols-3 gap-6">
<div class="p-6 border rounded-brand card">
<h3 class="font-semibold">Ziel</h3>
<p class="mt-2 text-textMuted">Sichere AWS/Azure Landing Zone, segmentiertes Netzwerk, zentralisiertes IAM.</p>
</div>
<div class="p-6 border rounded-brand card">
<h3 class="font-semibold">Vorgehen</h3>
<p class="mt-2 text-textMuted">Guardrails, IaC (Terraform/OpenTofu), GitOps, Observability, Cost Controls.</p>
</div>
<div class="p-6 border rounded-brand card">
<h3 class="font-semibold">Ergebnis</h3>
<p class="mt-2 text-textMuted">Schnelleres Deployment, Auditfähigkeit, reduzierte Risiken & Kosten.</p>
</div>
</section>
<section class="mt-10">
<h2 class="text-2xl font-bold mb-3">Kennzahlen</h2>
<ul class="list-disc pl-6 text-textMuted">
<li>6 Wochen bis produktionsreifer Basis</li>
<li>~40% weniger manuelle Deployments durch CI/CD</li>
<li>100% Infrastruktur als Code & Policies-as-Code</li>
</ul>
</section>
<div class="mt-12 rounded-brand p-6 bg-gradient-to-r from-accent to-primary text-white flex items-center justify-between">
<div class="text-lg font-semibold tracking-wide">Ähnliches Projekt geplant?</div>
<a href="/de/kontakt" class="px-5 py-3 rounded-brand bg-white text-primary font-medium">Jetzt anfragen</a>
</div>
</Base>

View File

@@ -0,0 +1,48 @@
---
import Base from "../../../layouts/Base.astro";
---
<Base path="/de/cases/k8s-hardening" title="Case Study Kubernetes Hardening & GitOps">
<h1 class="text-3xl font-bold mt-8">Case Study: Kubernetes Hardening & GitOps</h1>
<p class="mt-4 text-textMuted">Produktionsreifes K8sSetup mit Härtung nach CIS, durchgängigen PoliciesasCode und GitOpsDeployments.</p>
<section class="mt-8 grid md:grid-cols-3 gap-6">
<div class="p-6 border rounded-brand card">
<h3 class="font-semibold">Ziel</h3>
<p class="mt-2 text-textMuted">Sicheres ClusterBaseline: identitätszentrierte Zugriffe, NetzwerkSegmentierung, saubere Supply Chain.</p>
</div>
<div class="p-6 border rounded-brand card">
<h3 class="font-semibold">Vorgehen</h3>
<p class="mt-2 text-textMuted">CIS Benchmarks, OPA/Gatekeeper oder Kyverno Policies, Pod Security Standards, signierte Artefakte & GitOps (Argo CD).</p>
</div>
<div class="p-6 border rounded-brand card">
<h3 class="font-semibold">Ergebnis</h3>
<p class="mt-2 text-textMuted">Reproduzierbare Deployments, Policykonforme Releases, Auditfähigkeit und reduzierte Angriffsfläche.</p>
</div>
</section>
<section class="mt-10">
<h2 class="text-2xl font-bold mb-3">Kennzahlen</h2>
<ul class="list-disc pl-6 text-textMuted">
<li>100% deklarative Deployments (GitOps, Pullbasiert)</li>
<li>~80% der CISKontrollen automatisiert überprüfbar</li>
<li>PolicyBreaks verhindern riskante Manifeste vor dem Rollout</li>
<li>Signierte ContainerImages & SBOM für Kernservices</li>
</ul>
</section>
<section class="mt-10 grid md:grid-cols-2 gap-6">
<div class="p-6 border rounded-brand card">
<h3 class="font-semibold">GitOps Setup</h3>
<p class="mt-2 text-textMuted">Mehrstufige Environments (dev/stage/prod) mit Branch/TagStrategie, KustomizeOverlays und PRbasierten Changes.</p>
</div>
<div class="p-6 border rounded-brand card">
<h3 class="font-semibold">Cluster Hardening</h3>
<p class="mt-2 text-textMuted">RBAC LeastPrivilege, NetworkPolicies defaultdeny, Pod Security, ImageScanning, SecretsManagement.</p>
</div>
</section>
<div class="mt-12 rounded-brand p-6 bg-gradient-to-r from-accent to-primary text-white flex items-center justify-between">
<div class="text-lg font-semibold tracking-wide">Ähnliches Projekt geplant?</div>
<a href="/de/kontakt" class="px-5 py-3 rounded-brand bg-white text-primary font-medium">Jetzt anfragen</a>
</div>
</Base>

View File

@@ -1,15 +1,15 @@
---
import Base from "../layouts/Base.astro";
import Base from "../../layouts/Base.astro";
const updated = new Date().toLocaleDateString('de-DE', { year: 'numeric', month: 'long', day: '2-digit' });
---
<Base path="/datenschutz" title="Datenschutz Valtrix">
<Base path="/de/datenschutz" title="Datenschutz Valtrix">
<h1 class="text-2xl font-bold mt-8">Datenschutzhinweise</h1>
<p class="mt-4 text-textMuted">Diese Hinweise informieren darüber, wie wir personenbezogene Daten beim Besuch dieser Website verarbeiten. Die Website ist als statische Seite umgesetzt; es werden keine Tracking oder MarketingCookies eingesetzt und keine externen Ressourcen (z.B. Google Fonts, Analytics, Formdienste) geladen.</p>
<section class="mt-8 grid gap-2">
<h2 class="text-xl font-semibold">1. Verantwortlicher</h2>
<p>Verantwortlicher im Sinne der DSGVO ist: <strong>Valtrix</strong>. Anschrift siehe <a class="underline text-primary" href="/impressum">Impressum</a>. EMail: <a class="underline text-primary" href="mailto:kontakt@valtrix.systems">kontakt@valtrix.systems</a></p>
<p>Verantwortlicher im Sinne der DSGVO ist: <strong>Valtrix</strong>. Anschrift siehe <a class="underline text-primary" href="/de/impressum">Impressum</a>. EMail: <a class="underline text-primary" href="mailto:kontakt@valtrix.systems">kontakt@valtrix.systems</a></p>
</section>
<section class="mt-6 grid gap-2">

View File

@@ -1,7 +1,7 @@
---
import Base from "../layouts/Base.astro";
import Base from "../../layouts/Base.astro";
---
<Base path="/impressum" title="Impressum Valtrix">
<Base path="/de/impressum" title="Impressum Valtrix">
<h1 class="text-2xl font-bold mt-8">Impressum</h1>
<p><strong>VALTRIX</strong> GmbH i.G.</p>
<p>Am Wassergraben 4, 10179 Berlin</p>

82
src/pages/de/index.astro Normal file
View File

@@ -0,0 +1,82 @@
---
import Base from "../../layouts/Base.astro";
const benefits = [
{ title: "Zero-Trust & Härtung", text: "Security-by-Design, CIS/BSI-Richtlinien, automatisierte Policies." },
{ title: "Cloud Foundations", text: "Landing Zones, Identity, Observability sauber & skalierbar." },
{ title: "Automatisierung", text: "IaC, CI/CD, GitOps, wiederholbar & auditfähig." }
];
const approach = [
{ title: 'Assess', text: 'Risiko- und Reifegradanalyse: schneller Quick-Scan, klare Prioritäten.' },
{ title: 'Design', text: 'Zielarchitektur & Guardrails: sichere, skalierbare Blueprint-Entwürfe.' },
{ title: 'Build', text: 'IaC, Pipelines & Policies-as-Code: reproduzierbar und überprüfbar.' },
{ title: 'Run', text: 'Monitoring, Audits & Kostentransparenz: nachhaltig betreibbar.' }
];
---
<Base path="/de">
<section class="py-16 grid lg:grid-cols-2 gap-12 items-center">
<div class="fade-in">
<h1 class="text-4xl md:text-5xl font-bold leading-tight">
Security-first Cloud Consulting.
</h1>
<p class="mt-4 text-lg text-textMuted">
Wir verbinden <strong>Struktur</strong> und <strong>Wert</strong>: sichere Cloud-Architekturen, Automatisierung und Compliance pragmatisch und messbar.
</p>
<div class="mt-8 flex gap-4">
<a href="/de/leistungen" class="px-5 py-3 rounded-brand bg-primary text-white hover:opacity-90">Leistungen</a>
<a href="/de/kontakt" class="px-5 py-3 rounded-brand border border-primary text-primary hover:bg-graybg">Kontakt</a>
</div>
<div class="mt-8 grid grid-cols-3 gap-4 text-sm text-textMuted">
<div class="p-3 border rounded-brand card">DSGVO / EU-Hosting</div>
<div class="p-3 border rounded-brand card">BSI/ISO-orientiert</div>
<div class="p-3 border rounded-brand card">Auditfähig</div>
</div>
</div>
</section>
<section class="py-10 grid md:grid-cols-3 gap-6">
{benefits.map(b => (
<div class="p-6 border rounded-brand hover:shadow-sm transition-shadow card">
<h3 class="font-semibold text-lg">{b.title}</h3>
<p class="mt-2 text-textMuted">{b.text}</p>
</div>
))}
</section>
<section class="py-12">
<h2 class="text-2xl font-bold mb-6">Leistungen</h2>
<div class="grid md:grid-cols-3 gap-6">
<div class="p-6 border rounded-brand card">
<h3 class="font-semibold text-lg">Cloud & DevSecOps</h3>
<p class="mt-2 text-textMuted">Plan, Build & Run effizient, sicher, skalierbar.</p>
<a href="/de/leistungen" class="mt-3 inline-block text-primary underline">Mehr erfahren</a>
</div>
<div class="p-6 border rounded-brand card">
<h3 class="font-semibold text-lg">Security & Compliance</h3>
<p class="mt-2 text-textMuted">Zero-Trust, IAM, Hardening, Audits, BSI/ISOorientierte Prozesse.</p>
</div>
<div class="p-6 border rounded-brand card">
<h3 class="font-semibold text-lg">AI & Automation</h3>
<p class="mt-2 text-textMuted">RAG/Agenten, Automatisierung von Betriebsprozessen, sichere Integration.</p>
</div>
</div>
</section>
<section class="py-14">
<h2 class="text-2xl font-bold mb-6">Unser Ansatz</h2>
<div class="grid md:grid-cols-4 gap-6">
{approach.map((a, i) => (
<div class="p-6 border rounded-brand card">
<div class="text-xl font-semibold">{i+1}. {a.title}</div>
<p class="mt-2 text-textMuted">{a.text}</p>
</div>
))}
</div>
</section>
<section class="py-12">
<div class="rounded-brand p-8 bg-gradient-to-r from-accent to-primary text-white flex flex-col md:flex-row items-center justify-between gap-6">
<div class="text-xl font-semibold tracking-wide">Sichere Cloud pragmatisch umgesetzt.</div>
<a href="/de/kontakt" class="px-6 py-3 rounded-brand bg-white text-primary font-medium">Projekt anfragen</a>
</div>
</section>
</Base>

View File

@@ -1,7 +1,7 @@
---
import Base from "../layouts/Base.astro";
import Base from "../../layouts/Base.astro";
---
<Base path="/kontakt" title="Kontakt Valtrix">
<Base path="/de/kontakt" title="Kontakt Valtrix">
<h1 class="text-3xl font-bold mt-8">Kontakt</h1>
<form id="contactForm" class="mt-6 grid gap-4 max-w-xl">
<input class="border rounded-brand p-3" name="name" placeholder="Ihr Name" required>

View File

@@ -1,7 +1,7 @@
---
import Base from "../layouts/Base.astro";
import Base from "../../layouts/Base.astro";
---
<Base path="/leistungen" title="Leistungen Valtrix">
<Base path="/de/leistungen" title="Leistungen Valtrix">
<h1 class="text-3xl font-bold font-bold mt-8">Leistungen</h1>
<div class="grid md:grid-cols-3 gap-6 mt-6">
<div class="p-6 border rounded-brand card">

View File

@@ -1,7 +1,7 @@
---
import Base from "../layouts/Base.astro";
import Base from "../../layouts/Base.astro";
---
<Base path="/sicherheit" title="Sicherheit & Compliance Valtrix">
<Base path="/de/sicherheit" title="Sicherheit & Compliance Valtrix">
<h1 class="text-3xl font-bold mt-8">Sicherheit & Compliance</h1>
<ul class="list-disc pl-6 mt-4 text-textMuted">
<li>Datensouverän: EU-Hosting, DSGVO-konform</li>

View File

@@ -1,7 +1,7 @@
---
import Base from "../layouts/Base.astro";
import Base from "../../layouts/Base.astro";
---
<Base path="/ueber-uns" title="Über uns Valtrix">
<Base path="/de/ueber-uns" title="Über uns Valtrix">
<h1 class="text-3xl font-bold mt-8">Über uns</h1>
<p class="mt-4 text-textMuted max-w-2xl">Wir sind ein Team aus Cloud-, Security- und AI-Engineers. Wir bauen robuste Systeme pragmatisch und messbar.</p>
</Base>

View File

@@ -3,10 +3,10 @@ import Base from "../../layouts/Base.astro";
const benefits = [
{ title: "Zero-Trust & Hardening", text: "Security-by-Design, CIS/BSI guidelines, automated policies." },
{ title: "Cloud Foundations", text: "Landing zones, identity, observability clean & scalable." },
{ title: "Automation", text: "IaC/CI/CD, GitOps, reproducible & auditable." }
{ title: "Automation", text: "IaC, CI/CD, GitOps, reproducible & auditable." }
];
const features = [
{ title: "Cloud & DevOps", text: "Plan, Build & Run efficient, secure, scalable." },
{ title: "Cloud & SecOps", text: "Plan, Build & Run efficient, secure, scalable." },
{ title: "Security & Compliance", text: "BSI/ISO-aligned, Zero-Trust, audits, policies-as-code." },
{ title: "AI & Automation", text: "RAG, agents & process automation with measurable outcome." }
];
@@ -56,12 +56,24 @@ const features = [
<section class="py-14">
<h2 class="text-2xl font-bold mb-6">Our approach</h2>
<ol class="grid md:grid-cols-4 gap-6 list-decimal pl-5">
<li class="p-5 border rounded-brand card"><strong>Assess:</strong> Risk & maturity → quick scan.</li>
<li class="p-5 border rounded-brand card"><strong>Design:</strong> Target architecture & guardrails.</li>
<li class="p-5 border rounded-brand card"><strong>Build:</strong> IaC, pipelines, policies-as-code.</li>
<li class="p-5 border rounded-brand card"><strong>Run:</strong> Monitoring, audits, cost transparency.</li>
</ol>
<div class="grid md:grid-cols-4 gap-6">
<div class="p-6 border rounded-brand card">
<div class="text-xl font-semibold">1. Assess</div>
<p class="mt-2 text-textMuted">Risk & maturity analysis: quick-scan, clear priorities.</p>
</div>
<div class="p-6 border rounded-brand card">
<div class="text-xl font-semibold">2. Design</div>
<p class="mt-2 text-textMuted">Target architecture & guardrails: secure, scalable blueprints.</p>
</div>
<div class="p-6 border rounded-brand card">
<div class="text-xl font-semibold">3. Build</div>
<p class="mt-2 text-textMuted">IaC, pipelines & policies-as-code: reproducible & testable.</p>
</div>
<div class="p-6 border rounded-brand card">
<div class="text-xl font-semibold">4. Run</div>
<p class="mt-2 text-textMuted">Monitoring, audits & cost transparency for sustainable operations.</p>
</div>
</div>
</section>
<section class="py-12">

View File

@@ -1,73 +1,10 @@
---
import Base from "../layouts/Base.astro";
const benefits = [
{ title: "Zero-Trust & Hardening", text: "Security-by-Design, CIS/BSI-Guidelines, automatisierte Policies." },
{ title: "Cloud Foundations", text: "Landing Zones, Identity, Observability sauber & skalierbar." },
{ title: "Automatisierung", text: "IaC/CI/CD, GitOps, wiederholbar & auditfähig." }
];
const features = [
{ title: "Cloud & DevOps", text: "Plan, Build & Run effizient, sicher, skalierbar." },
{ title: "Security & Compliance", text: "BSI/ISO-aligned, Zero-Trust, Audits, Policies-as-Code." },
{ title: "AI & Automation", text: "RAG, Agenten & Process Automation mit messbarem Outcome." }
];
---
<Base path="/">
<section class="py-16 grid lg:grid-cols-2 gap-12 items-center">
<div class="fade-in">
<h1 class="text-4xl md:text-5xl font-bold leading-tight">
Security-first Cloud Consulting.
</h1>
<p class="mt-4 text-lg text-textMuted">
Wir verbinden <strong>Struktur</strong> und <strong>Wert</strong>: sichere Cloud-Architekturen, Automatisierung und Compliance pragmatisch und messbar.
</p>
<div class="mt-8 flex gap-4">
<a href="/leistungen" class="px-5 py-3 rounded-brand bg-primary text-white hover:opacity-90">Leistungen</a>
<a href="/kontakt" class="px-5 py-3 rounded-brand border border-primary text-primary hover:bg-graybg">Kontakt</a>
</div>
<div class="mt-8 grid grid-cols-3 gap-4 text-sm text-textMuted">
<div class="p-3 border rounded-brand card">DSGVO / EU-Hosting</div>
<div class="p-3 border rounded-brand card">BSI/ISO-orientiert</div>
<div class="p-3 border rounded-brand card">Auditfähig</div>
</div>
</div>
</section>
<section class="py-10 grid md:grid-cols-3 gap-6">
{benefits.map(b => (
<div class="p-6 border rounded-brand hover:shadow-sm transition-shadow card">
<h3 class="font-semibold text-lg">{b.title}</h3>
<p class="mt-2 text-textMuted">{b.text}</p>
</div>
))}
</section>
<section class="py-12">
<h2 class="text-2xl font-bold mb-6">Leistungen</h2>
<div class="grid md:grid-cols-3 gap-6">
{features.map(f => (
<div class="p-6 border rounded-brand hover:-translate-y-0.5 transition card">
<h3 class="font-semibold text-lg">{f.title}</h3>
<p class="mt-2 text-textMuted">{f.text}</p>
<a href="/leistungen" class="mt-3 inline-block text-primary underline">Mehr erfahren</a>
</div>
))}
</div>
</section>
<section class="py-14">
<h2 class="text-2xl font-bold mb-6">Unser Ansatz</h2>
<ol class="grid md:grid-cols-4 gap-6 list-decimal pl-5">
<li class="p-5 border rounded-brand card"><strong>Assess:</strong> Risiko & Reifegrad → Quick-Scan.</li>
<li class="p-5 border rounded-brand card"><strong>Design:</strong> Zielarchitektur & Guardrails.</li>
<li class="p-5 border rounded-brand card"><strong>Build:</strong> IaC, Pipelines, Policies-as-Code.</li>
<li class="p-5 border rounded-brand card"><strong>Run:</strong> Monitoring, Audits, Kostentransparenz.</li>
</ol>
</section>
<section class="py-12">
<div class="rounded-brand p-8 bg-gradient-to-r from-accent to-primary text-white flex flex-col md:flex-row items-center justify-between gap-6">
<div class="text-xl font-semibold tracking-wide">Sichere Cloud pragmatisch umgesetzt.</div>
<a href="/kontakt" class="px-6 py-3 rounded-brand bg-white text-primary font-medium">Projekt anfragen</a>
</div>
</section>
<meta http-equiv="refresh" content="0;url=/de/" />
<main class="max-w-7xl mx-auto px-6 py-12">
<h1 class="text-2xl font-bold">Weiterleitung</h1>
<p class="mt-4">Die Seite wurde verschoben nach <a href="/de/" class="underline">/de/</a>. Sollte die automatische Weiterleitung fehlschlagen, klicke den Link.</p>
</main>
</Base>