Update fc-landing: public-safe page, no LAN refs, bare-metal RKE2 footer

This commit is contained in:
2026-03-11 00:38:50 +00:00
parent 848eb83f83
commit 5f30f85569

View File

@@ -1,248 +1,320 @@
# FlowerCore Landing Page # FlowerCore Landing Page
# Blue Jay Lab branded landing page # Blue Jay Lab branded landing page - PUBLIC facing
# ArgoCD managed - BlueJay Lab # ArgoCD managed - BlueJay Lab
--- ---
# fc-system namespace is shared; don't overwrite if it exists apiVersion: v1
apiVersion: v1 kind: Namespace
kind: Namespace metadata:
metadata: name: fc-system
name: fc-system labels:
labels: app.kubernetes.io/part-of: bluejay-infra
app.kubernetes.io/part-of: bluejay-infra ---
--- # Landing page HTML (public-safe - no internal LAN references)
# Landing page HTML apiVersion: v1
apiVersion: v1 kind: ConfigMap
kind: ConfigMap metadata:
metadata: name: fc-landing-html
name: fc-landing-html namespace: fc-system
namespace: fc-system data:
data: index.html: |
index.html: | <!DOCTYPE html>
<!DOCTYPE html> <html lang="en">
<html lang="en"> <head>
<head> <meta charset="utf-8">
<meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1"> <title>FlowerCore</title>
<title>FlowerCore - Blue Jay Lab</title> <style>
<style> * { margin: 0; padding: 0; box-sizing: border-box; }
* { margin: 0; padding: 0; box-sizing: border-box; } body {
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #0a1628 0%, #1a2744 50%, #0d1f3c 100%);
background: linear-gradient(135deg, #0a1628 0%, #1a2744 50%, #0d1f3c 100%); color: #e0e8f0;
color: #e0e8f0; min-height: 100vh;
min-height: 100vh; display: flex;
display: flex; flex-direction: column;
flex-direction: column; align-items: center;
align-items: center; justify-content: center;
justify-content: center; }
} .hero {
.hero { text-align: center;
text-align: center; padding: 3rem;
padding: 3rem; max-width: 800px;
max-width: 800px; }
} .logo {
.logo { font-size: 5rem;
font-size: 5rem; margin-bottom: 1.5rem;
margin-bottom: 1.5rem; filter: drop-shadow(0 0 20px rgba(74, 158, 255, 0.3));
filter: drop-shadow(0 0 20px rgba(74, 158, 255, 0.3)); }
} h1 {
h1 { font-size: 3rem;
font-size: 3rem; background: linear-gradient(135deg, #4a9eff, #7ab3ff);
background: linear-gradient(135deg, #4a9eff, #7ab3ff); -webkit-background-clip: text;
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
-webkit-text-fill-color: transparent; background-clip: text;
background-clip: text; margin-bottom: 0.5rem;
margin-bottom: 0.5rem; }
} .subtitle {
.subtitle { font-size: 1.3rem;
font-size: 1.3rem; color: #7ab3ff;
color: #7ab3ff; font-weight: 300;
font-weight: 300; margin-bottom: 1rem;
margin-bottom: 3rem; }
} .description {
.services { font-size: 1rem;
display: grid; color: #8aa8c4;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); line-height: 1.6;
gap: 1rem; margin-bottom: 3rem;
width: 100%; max-width: 600px;
max-width: 700px; }
padding: 0 1rem; .services {
} display: grid;
.service { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
background: rgba(74, 158, 255, 0.08); gap: 1rem;
border: 1px solid rgba(74, 158, 255, 0.2); width: 100%;
border-radius: 8px; max-width: 700px;
padding: 1.2rem; padding: 0 1rem;
text-decoration: none; }
color: inherit; .service {
transition: all 0.2s; background: rgba(74, 158, 255, 0.08);
} border: 1px solid rgba(74, 158, 255, 0.2);
.service:hover { border-radius: 8px;
background: rgba(74, 158, 255, 0.15); padding: 1.2rem;
border-color: rgba(74, 158, 255, 0.5); text-decoration: none;
transform: translateY(-2px); color: inherit;
} transition: all 0.2s;
.service h3 { color: #4a9eff; font-size: 0.95rem; margin-bottom: 0.3rem; } }
.service p { color: #8aa8c4; font-size: 0.8rem; } .service:hover {
.footer { background: rgba(74, 158, 255, 0.15);
margin-top: 3rem; border-color: rgba(74, 158, 255, 0.5);
color: #4a6580; transform: translateY(-2px);
font-size: 0.8rem; }
} .service h3 { color: #4a9eff; font-size: 0.95rem; margin-bottom: 0.3rem; }
</style> .service p { color: #8aa8c4; font-size: 0.8rem; }
</head> .status-bar {
<body> display: flex;
<div class="hero"> gap: 2rem;
<div class="logo">&#x1F33B;</div> margin-top: 2rem;
<h1>FlowerCore</h1> padding: 1rem 2rem;
<p class="subtitle">Blue Jay Lab</p> background: rgba(74, 158, 255, 0.05);
</div> border-radius: 8px;
<div class="services"> border: 1px solid rgba(74, 158, 255, 0.1);
<a class="service" href="https://gitea.iamworkin.lan"> }
<h3>Gitea</h3> .status-item { text-align: center; }
<p>Git repositories</p> .status-item .value { color: #4a9eff; font-size: 1.5rem; font-weight: 700; }
</a> .status-item .label { color: #6a8ca4; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 1px; }
<a class="service" href="https://argocd.iamworkin.lan"> .footer {
<h3>ArgoCD</h3> margin-top: 3rem;
<p>GitOps deployments</p> color: #4a6580;
</a> font-size: 0.8rem;
<a class="service" href="https://zabbix.iamworkin.lan"> }
<h3>Zabbix</h3> .footer a { color: #4a6580; text-decoration: none; }
<p>Monitoring</p> .footer a:hover { color: #7ab3ff; }
</a> </style>
<a class="service" href="https://guac.iamworkin.lan"> </head>
<h3>Guacamole</h3> <body>
<p>Remote desktop</p> <div class="hero">
</a> <div class="logo">&#x1F33B;</div>
<a class="service" href="https://element.iamworkin.lan"> <h1>FlowerCore</h1>
<h3>Element</h3> <p class="subtitle">Blue Jay Lab</p>
<p>Matrix chat</p> <p class="description">
</a> Multi-tenant service management platform built on .NET 10,
<a class="service" href="https://mail.iamworkin.lan"> Kubernetes, and GitOps. Digital signage, telephony IVR,
<h3>Mail</h3> MySQL/PHP hosting, and infrastructure automation.
<p>Snappymail webmail</p> </p>
</a> </div>
<a class="service" href="https://intranet.iamworkin.lan"> <div class="services">
<h3>Intranet</h3> <a class="service" href="https://gitea.flowercore.io">
<p>Lab portal</p> <h3>Source</h3>
</a> <p>Gitea repositories</p>
<a class="service" href="https://pki.iamworkin.lan"> </a>
<h3>PKI</h3> <a class="service" href="https://webmail.flowercore.io">
<p>Certificates</p> <h3>Mail</h3>
</a> <p>Webmail access</p>
</div> </a>
<p class="footer">FlowerCore &middot; RKE2 on Harvester &middot; ArgoCD managed</p> <a class="service" href="https://element.flowercore.io">
</body> <h3>Chat</h3>
</html> <p>Matrix messaging</p>
--- </a>
# nginx configuration <a class="service" href="https://github.com/FlowerCoreIO">
apiVersion: v1 <h3>GitHub</h3>
kind: ConfigMap <p>Open source</p>
metadata: </a>
name: fc-landing-nginx-conf </div>
namespace: fc-system <div class="status-bar">
data: <div class="status-item">
default.conf: | <div class="value">17</div>
server { <div class="label">Services</div>
listen 80; </div>
server_name _; <div class="status-item">
root /usr/share/nginx/html; <div class="value">13</div>
index index.html; <div class="label">VLANs</div>
</div>
location / { <div class="status-item">
try_files $uri $uri/ =404; <div class="value">12k+</div>
} <div class="label">Tests</div>
</div>
location /healthz { </div>
access_log off; <p class="footer">
return 200 "ok"; FlowerCore &middot; Bare-metal RKE2 &middot; ArgoCD managed
add_header Content-Type text/plain; &middot; <a href="mailto:admin@flowercore.io">Contact</a>
} </p>
} </body>
--- </html>
# Landing Page Deployment ---
apiVersion: apps/v1 # nginx configuration
kind: Deployment apiVersion: v1
metadata: kind: ConfigMap
name: fc-landing metadata:
namespace: fc-system name: fc-landing-nginx-conf
labels: namespace: fc-system
app: fc-landing data:
spec: default.conf: |
replicas: 1 server {
selector: listen 80;
matchLabels: server_name _;
app: fc-landing root /usr/share/nginx/html;
template: index index.html;
metadata:
labels: location / {
app: fc-landing try_files $uri $uri/ =404;
spec: }
containers:
- name: nginx location /healthz {
image: nginx:alpine access_log off;
ports: return 200 "ok";
- containerPort: 80 add_header Content-Type text/plain;
name: http }
volumeMounts: }
- name: nginx-conf ---
mountPath: /etc/nginx/conf.d/default.conf # Landing Page Deployment
subPath: default.conf apiVersion: apps/v1
- name: html kind: Deployment
mountPath: /usr/share/nginx/html metadata:
resources: name: fc-landing
requests: namespace: fc-system
memory: 16Mi labels:
cpu: 5m app: fc-landing
limits: spec:
memory: 64Mi replicas: 1
cpu: 50m selector:
livenessProbe: matchLabels:
httpGet: app: fc-landing
path: /healthz template:
port: 80 metadata:
initialDelaySeconds: 5 labels:
periodSeconds: 10 app: fc-landing
readinessProbe: spec:
httpGet: containers:
path: /healthz - name: nginx
port: 80 image: nginx:alpine
initialDelaySeconds: 3 ports:
periodSeconds: 5 - containerPort: 80
volumes: name: http
- name: nginx-conf volumeMounts:
configMap: - name: nginx-conf
name: fc-landing-nginx-conf mountPath: /etc/nginx/conf.d/default.conf
- name: html subPath: default.conf
configMap: - name: html
name: fc-landing-html mountPath: /usr/share/nginx/html
--- resources:
apiVersion: v1 requests:
kind: Service memory: 16Mi
metadata: cpu: 5m
name: fc-landing limits:
namespace: fc-system memory: 64Mi
spec: cpu: 50m
selector: livenessProbe:
app: fc-landing httpGet:
ports: path: /healthz
- port: 80 port: 80
targetPort: 80 initialDelaySeconds: 5
name: http periodSeconds: 10
--- readinessProbe:
# Traefik IngressRoute (internal only, no public cert needed) httpGet:
apiVersion: traefik.io/v1alpha1 path: /healthz
kind: IngressRoute port: 80
metadata: initialDelaySeconds: 3
name: fc-landing periodSeconds: 5
namespace: fc-system volumes:
spec: - name: nginx-conf
entryPoints: configMap:
- websecure name: fc-landing-nginx-conf
routes: - name: html
- match: Host(`flowercore.iamworkin.lan`) configMap:
kind: Rule name: fc-landing-html
services: ---
- name: fc-landing apiVersion: v1
port: 80 kind: Service
tls: {} metadata:
name: fc-landing
namespace: fc-system
spec:
selector:
app: fc-landing
ports:
- port: 80
targetPort: 80
name: http
---
# Internal IngressRoute (LAN access)
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: fc-landing
namespace: fc-system
spec:
entryPoints:
- websecure
routes:
- match: Host(`flowercore.iamworkin.lan`)
kind: Rule
services:
- name: fc-landing
port: 80
tls: {}
---
# Public IngressRoute (flowercore.io with Cloudflare origin cert)
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: fc-landing-public
namespace: fc-system
spec:
entryPoints:
- websecure
routes:
- match: Host(`flowercore.io`) || Host(`www.flowercore.io`)
kind: Rule
services:
- name: fc-landing
port: 80
tls:
secretName: cf-origin-flowercore-io
---
# HTTP to HTTPS redirect for public domain
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: fc-landing-public-http
namespace: fc-system
spec:
entryPoints:
- web
routes:
- match: Host(`flowercore.io`) || Host(`www.flowercore.io`)
kind: Rule
services:
- name: fc-landing
port: 80
middlewares:
- name: redirect-https
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: redirect-https
namespace: fc-system
spec:
redirectScheme:
scheme: https
permanent: true