diff --git a/base/lasuite/integration-deployment.yaml b/base/lasuite/integration-deployment.yaml new file mode 100644 index 0000000..23807b4 --- /dev/null +++ b/base/lasuite/integration-deployment.yaml @@ -0,0 +1,328 @@ +# La Gaufre integration service — O Estúdio app launcher. +# Serves a custom gaufre.js widget and services.json for our app list. +# Apps set GAUFREJS_URL=https://integration.DOMAIN_SUFFIX/api/v1/gaufre.js +# which loads this widget; clicking it fetches /api/v1/services.json. + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: integration-config + namespace: lasuite +data: + services.json: | + [ + { + "id": "documentos", + "name": "Documentos", + "url": "https://docs.DOMAIN_SUFFIX" + }, + { + "id": "ficheiros", + "name": "Ficheiros", + "url": "https://drive.DOMAIN_SUFFIX" + }, + { + "id": "reunioes", + "name": "Reuniões", + "url": "https://meet.DOMAIN_SUFFIX" + }, + { + "id": "conversas", + "name": "Conversas", + "url": "https://chat.DOMAIN_SUFFIX" + }, + { + "id": "correio", + "name": "Correio", + "url": "https://mail.DOMAIN_SUFFIX" + }, + { + "id": "pessoas", + "name": "Pessoas", + "url": "https://people.DOMAIN_SUFFIX" + }, + { + "id": "descobrir", + "name": "Descobrir", + "url": "https://find.DOMAIN_SUFFIX" + }, + { + "id": "administrar", + "name": "Administrar", + "url": "https://admin.DOMAIN_SUFFIX" + } + ] + + gaufre.js: | + /** + * O Estúdio — La Gaufre widget (self-hosted). + * Drop-in replacement for the official integration.lasuite.numerique.gouv.fr widget. + * Fetches services.json from its own origin and renders a DSFR-style popup. + */ + (function () { + 'use strict'; + + const origin = (function () { + const s = document.currentScript; + if (s && s.src) { + const u = new URL(s.src); + return u.origin; + } + return window.location.origin; + })(); + + const SERVICES_URL = origin + '/api/v1/services.json'; + const SUITE_NAME = 'O Estúdio'; + + const STYLE = ` + #estudio-gaufre-overlay { + display: none; + position: fixed; + inset: 0; + z-index: 9999; + } + #estudio-gaufre-overlay.open { + display: block; + } + #estudio-gaufre-backdrop { + position: absolute; + inset: 0; + } + #estudio-gaufre-panel { + position: absolute; + top: 56px; + left: 16px; + background: #fff; + border: 1px solid #ddd; + border-radius: 8px; + box-shadow: 0 4px 24px rgba(0,0,0,.15); + padding: 16px; + width: 320px; + z-index: 1; + } + #estudio-gaufre-panel h2 { + font-size: 14px; + font-weight: 700; + color: #666; + margin: 0 0 12px; + text-transform: uppercase; + letter-spacing: .05em; + } + #estudio-gaufre-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; + } + .estudio-gaufre-app { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + padding: 12px 8px; + border-radius: 6px; + text-decoration: none; + color: #333; + font-size: 12px; + font-weight: 500; + transition: background .15s; + } + .estudio-gaufre-app:hover { + background: #f5f5f5; + } + .estudio-gaufre-icon { + width: 40px; + height: 40px; + border-radius: 8px; + background: #e8eaf6; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + } + `; + + const ICONS = { + documentos: '📄', + ficheiros: '📁', + reunioes: '🎥', + conversas: '💬', + correio: '✉️', + pessoas: '👥', + descobrir: '🔍', + administrar: '⚙️', + }; + + function injectStyles() { + const el = document.createElement('style'); + el.textContent = STYLE; + document.head.appendChild(el); + } + + function buildPanel(services) { + const overlay = document.createElement('div'); + overlay.id = 'estudio-gaufre-overlay'; + + const backdrop = document.createElement('div'); + backdrop.id = 'estudio-gaufre-backdrop'; + backdrop.addEventListener('click', close); + + const panel = document.createElement('div'); + panel.id = 'estudio-gaufre-panel'; + + const title = document.createElement('h2'); + title.textContent = SUITE_NAME; + panel.appendChild(title); + + const grid = document.createElement('div'); + grid.id = 'estudio-gaufre-grid'; + + services.forEach(function (svc) { + const a = document.createElement('a'); + a.className = 'estudio-gaufre-app'; + a.href = svc.url; + + const icon = document.createElement('div'); + icon.className = 'estudio-gaufre-icon'; + icon.textContent = ICONS[svc.id] || '🔲'; + + const label = document.createElement('span'); + label.textContent = svc.name; + + a.appendChild(icon); + a.appendChild(label); + grid.appendChild(a); + }); + + panel.appendChild(grid); + overlay.appendChild(backdrop); + overlay.appendChild(panel); + document.body.appendChild(overlay); + return overlay; + } + + let overlay = null; + let services = null; + + function open() { + if (!services) { + fetch(SERVICES_URL) + .then(function (r) { return r.json(); }) + .then(function (data) { + services = data; + overlay = buildPanel(services); + overlay.classList.add('open'); + }); + } else { + if (!overlay) overlay = buildPanel(services); + overlay.classList.add('open'); + } + } + + function close() { + if (overlay) overlay.classList.remove('open'); + } + + function init() { + injectStyles(); + document.querySelectorAll('.js-lasuite-gaufre-btn').forEach(function (btn) { + btn.addEventListener('click', function (e) { + e.stopPropagation(); + overlay && overlay.classList.contains('open') ? close() : open(); + }); + }); + document.addEventListener('keydown', function (e) { + if (e.key === 'Escape') close(); + }); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } + })(); + + nginx.conf: | + server { + listen 80; + server_name _; + + location = /api/v1/services.json { + alias /etc/integration/services.json; + add_header Content-Type "application/json; charset=utf-8"; + add_header Access-Control-Allow-Origin "*"; + } + + location = /api/v1/gaufre.js { + alias /etc/integration/gaufre.js; + add_header Content-Type "application/javascript; charset=utf-8"; + add_header Access-Control-Allow-Origin "*"; + } + + location / { + return 404; + } + } +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: integration + namespace: lasuite +spec: + replicas: 1 + selector: + matchLabels: + app: integration + template: + metadata: + labels: + app: integration + spec: + containers: + - name: integration + image: nginx:alpine + ports: + - name: http + containerPort: 80 + volumeMounts: + - name: config + mountPath: /etc/integration + - name: nginx-conf + mountPath: /etc/nginx/conf.d/default.conf + subPath: nginx.conf + resources: + limits: + memory: 32Mi + requests: + memory: 16Mi + cpu: 5m + volumes: + - name: config + configMap: + name: integration-config + items: + - key: services.json + path: services.json + - key: gaufre.js + path: gaufre.js + - name: nginx-conf + configMap: + name: integration-config + items: + - key: nginx.conf + path: nginx.conf +--- +apiVersion: v1 +kind: Service +metadata: + name: integration + namespace: lasuite +spec: + selector: + app: integration + ports: + - name: http + port: 80 + targetPort: 80