feat(lasuite): add Projects (Planka Kanban) service

Deploy Planka-based project management at projects.DOMAIN_SUFFIX:
- ConfigMap with OIDC, S3, SMTP, La Gaufre widget config
- Deployment + Service (init container for DB migrations, Sails on 1337)
- OAuth2Client (client_secret_basic, redirect to /oidc-callback)
- VaultDynamicSecret for DATABASE_URL, VaultStaticSecret for SECRET_KEY
- Pingora route with websocket support (Socket.io)
- Image overrides in both local and production overlays
- TLS cert dnsNames updated for projects subdomain
- Integration service.json updated with Projects entry
- seaweedfs-s3-credentials rolloutRestartTargets includes projects
This commit is contained in:
2026-03-20 13:41:54 +00:00
parent b9d9ad72fe
commit bfe0280732
10 changed files with 300 additions and 8 deletions

View File

@@ -272,6 +272,11 @@ data:
prefix = "/__"
backend = "http://calendars-backend.lasuite.svc.cluster.local:80"
[[routes]]
host_prefix = "projects"
backend = "http://projects.lasuite.svc.cluster.local:80"
websocket = true
[[routes]]
host_prefix = "s3"
backend = "http://seaweedfs-filer.storage.svc.cluster.local:8333"

View File

@@ -21,24 +21,39 @@ data:
{
"services": [
{
"name": "Reuniões",
"url": "https://meet.DOMAIN_SUFFIX",
"logo": "https://integration.DOMAIN_SUFFIX/logos/visio.svg?v=2"
"name": "Calendar",
"url": "https://cal.DOMAIN_SUFFIX",
"logo": "https://integration.DOMAIN_SUFFIX/logos/calendar.svg?v=1"
},
{
"name": "Drive",
"url": "https://drive.DOMAIN_SUFFIX",
"logo": "https://integration.DOMAIN_SUFFIX/logos/drive.svg?v=1"
},
{
"name": "Mail",
"url": "https://mail.DOMAIN_SUFFIX",
"logo": "https://integration.DOMAIN_SUFFIX/logos/mail.svg?v=1"
},
{
"name": "Meet",
"url": "https://meet.DOMAIN_SUFFIX",
"logo": "https://integration.DOMAIN_SUFFIX/logos/visio.svg?v=2"
},
{
"name": "Projects",
"url": "https://projects.DOMAIN_SUFFIX",
"logo": "https://integration.DOMAIN_SUFFIX/logos/projects.svg?v=1"
},
{
"name": "Source Code",
"url": "https://src.DOMAIN_SUFFIX",
"logo": "https://integration.DOMAIN_SUFFIX/logos/docs.svg?v=1"
},
{
"name": "Account",
"url": "https://auth.DOMAIN_SUFFIX",
"logo": "https://integration.DOMAIN_SUFFIX/logos/account.svg?v=1"
},
{
"name": "Calendário",
"url": "https://cal.DOMAIN_SUFFIX",
"logo": "https://integration.DOMAIN_SUFFIX/logos/calendar.svg?v=1"
}
]
}

View File

@@ -51,6 +51,8 @@ resources:
- calendars-frontend-caddyfile.yaml
- calendars-frontend-deployment.yaml
- calendars-frontend-service.yaml
- projects-config.yaml
- projects-deployment.yaml
patches:
# Rewrite hardcoded production integration URL + inject theme CSS in people-frontend

View File

@@ -200,3 +200,23 @@ spec:
tokenEndpointAuthMethod: client_secret_post
secretName: oidc-calendars
skipConsent: true
---
# ── Projects (Kanban) ──────────────────────────────────────────────────────
apiVersion: hydra.ory.sh/v1alpha1
kind: OAuth2Client
metadata:
name: projects
namespace: lasuite
spec:
clientName: Projects
grantTypes:
- authorization_code
- refresh_token
responseTypes:
- code
scope: openid email profile
redirectUris:
- https://projects.DOMAIN_SUFFIX/oidc-callback
tokenEndpointAuthMethod: client_secret_basic
secretName: oidc-projects
skipConsent: true

View File

@@ -0,0 +1,40 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: projects-config
namespace: lasuite
data:
BASE_URL: "https://projects.DOMAIN_SUFFIX"
TRUST_PROXY: "1"
NODE_ENV: "production"
# OIDC — Hydra as the identity provider
OIDC_ISSUER: "https://auth.DOMAIN_SUFFIX/"
OIDC_SCOPES: "openid email profile"
OIDC_ENFORCED: "true"
OIDC_IGNORE_USERNAME: "true"
OIDC_IGNORE_ROLES: "true"
OIDC_ADMIN_ROLES: "*"
OIDC_FULLNAME_ATTRIBUTES: "given_name,family_name"
# S3 file storage via SeaweedFS
S3_ENDPOINT: "http://seaweedfs-filer.storage.svc.cluster.local:8333"
S3_BUCKET: "projects"
S3_REGION: "us-east-1"
S3_FORCE_PATH_STYLE: "true"
# SMTP via in-cluster Postfix relay
SMTP_HOST: "postfix.lasuite.svc.cluster.local"
SMTP_PORT: "25"
SMTP_SECURE: "false"
SMTP_FROM: "Projects <noreply@DOMAIN_SUFFIX>"
# La Gaufre waffle menu widget
REACT_APP_LAGAUFRE_WIDGET_API_URL: "https://integration.DOMAIN_SUFFIX/api/v2/services.json"
REACT_APP_LAGAUFRE_WIDGET_PATH: "https://integration.DOMAIN_SUFFIX/api/v2/"
# Default language for new OIDC users (browser detection fallback if unset)
DEFAULT_LANGUAGE: "en-US"
# Permissions
ALLOW_ALL_TO_CREATE_PROJECTS: "true"

View File

@@ -0,0 +1,121 @@
# Planka-based Kanban project management — single container (SPA bundled into Sails backend).
# Image: src.DOMAIN_SUFFIX/studio/projects:latest
# Built from projects/Dockerfile
#
# Secrets injected via env vars:
# - projects-db-url (VaultDynamicSecret): DATABASE_URL
# - projects-app-secrets (VaultStaticSecret): SECRET_KEY
# - oidc-projects (Hydra Maester): CLIENT_ID, CLIENT_SECRET
# - seaweedfs-s3-credentials (VaultStaticSecret): S3_ACCESS_KEY, S3_SECRET_KEY
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: projects
namespace: lasuite
spec:
replicas: 1
selector:
matchLabels:
app: projects
template:
metadata:
labels:
app: projects
spec:
initContainers:
- name: db-migrate
image: projects
command: ["node", "db/init.js"]
envFrom:
- configMapRef:
name: projects-config
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: projects-db-url
key: url
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: projects-app-secrets
key: SECRET_KEY
resources:
limits:
memory: 256Mi
requests:
memory: 128Mi
cpu: 50m
containers:
- name: projects
image: projects
command: ["node", "app.js", "--prod"]
ports:
- name: http
containerPort: 1337
envFrom:
- configMapRef:
name: projects-config
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: projects-db-url
key: url
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: projects-app-secrets
key: SECRET_KEY
- name: OIDC_CLIENT_ID
valueFrom:
secretKeyRef:
name: oidc-projects
key: CLIENT_ID
- name: OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: oidc-projects
key: CLIENT_SECRET
- name: S3_ACCESS_KEY
valueFrom:
secretKeyRef:
name: seaweedfs-s3-credentials
key: S3_ACCESS_KEY
- name: S3_SECRET_KEY
valueFrom:
secretKeyRef:
name: seaweedfs-s3-credentials
key: S3_SECRET_KEY
resources:
limits:
memory: 512Mi
requests:
memory: 256Mi
cpu: 50m
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 15
periodSeconds: 30
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 10
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: projects
namespace: lasuite
spec:
selector:
app: projects
ports:
- name: http
port: 80
targetPort: 1337

View File

@@ -49,6 +49,8 @@ spec:
name: messages-backend
- kind: Deployment
name: messages-worker
- kind: Deployment
name: projects
destination:
name: seaweedfs-s3-credentials
create: true
@@ -637,3 +639,79 @@ spec:
text: "{{ index .Secrets \"caldav-outbound-api-key\" }}"
CALDAV_INTERNAL_API_KEY:
text: "{{ index .Secrets \"caldav-internal-api-key\" }}"
---
# Projects DB credentials from OpenBao database secrets engine (static role, 24h rotation).
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultDynamicSecret
metadata:
name: projects-db-url
namespace: lasuite
spec:
vaultAuthRef: vso-auth
mount: database
path: static-creds/projects
allowStaticCreds: true
refreshAfter: 5m
rolloutRestartTargets:
- kind: Deployment
name: projects
destination:
name: projects-db-url
create: true
overwrite: true
transformation:
excludeRaw: true
templates:
url:
text: "postgresql://{{ index .Secrets \"username\" }}:{{ index .Secrets \"password\" }}@postgres-rw.data.svc.cluster.local:5432/projects_db"
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: projects-app-secrets
namespace: lasuite
spec:
vaultAuthRef: vso-auth
mount: secret
type: kv-v2
path: projects
refreshAfter: 30s
rolloutRestartTargets:
- kind: Deployment
name: projects
destination:
name: projects-app-secrets
create: true
overwrite: true
transformation:
excludeRaw: true
templates:
SECRET_KEY:
text: "{{ index .Secrets \"secret-key\" }}"
---
# Postfix DKIM signing key from OpenBao KV.
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: postfix-dkim
namespace: lasuite
spec:
vaultAuthRef: vso-auth
mount: secret
type: kv-v2
path: postfix-dkim
refreshAfter: 1h
rolloutRestartTargets:
- kind: Deployment
name: postfix
destination:
name: postfix-dkim
create: true
overwrite: true
transformation:
excludeRaw: true
templates:
private.key:
text: "{{ index .Secrets \"private-key\" }}"
selector:
text: "{{ index .Secrets \"selector\" }}"

View File

@@ -53,6 +53,11 @@ images:
newName: src.DOMAIN_SUFFIX/studio/meet-frontend
newTag: latest
# Projects (Kanban) — built and pushed by `sunbeam build projects`
- name: projects
newName: src.DOMAIN_SUFFIX/studio/projects
newTag: latest
# Calendars — built from source and pushed to Gitea registry.
- name: calendars-backend
newName: src.DOMAIN_SUFFIX/studio/calendars-backend

View File

@@ -71,3 +71,4 @@ spec:
- integration.DOMAIN_SUFFIX
- livekit.DOMAIN_SUFFIX
- cal.DOMAIN_SUFFIX
- projects.DOMAIN_SUFFIX

View File

@@ -75,6 +75,11 @@ images:
newName: src.DOMAIN_SUFFIX/studio/calendars-frontend
newTag: latest
# Projects (Kanban) — built and pushed by `sunbeam build projects`
- name: projects
newName: src.DOMAIN_SUFFIX/studio/projects
newTag: latest
# Tuwunel Matrix homeserver — built and pushed by `sunbeam build tuwunel`
- name: tuwunel
newName: src.DOMAIN_SUFFIX/studio/tuwunel