feat: initial La Gaufre v2 integration service
Multi-stage Docker image that: - Builds lagaufre.js v2 widget from suitenumerique/integration source (context must be sunbeam/ root; see sunbeam build integration) - Serves the widget, official La Suite SVG logos, custom logos for drive/mail/people, and a v1-compat gaufre.js wrapper via nginx gaufre.js reveals the ui-kit GaufreButton (adds lasuite--gaufre-loaded to <html>), loads the v2 widget, and wires button clicks via event delegation to survive React hydration replacing the initial DOM element. services.json is the only runtime-variable file; it is mounted from the integration-config ConfigMap which contains the deployed service list with DOMAIN_SUFFIX substituted at apply time.
This commit is contained in:
16
.dockerignore
Normal file
16
.dockerignore
Normal file
@@ -0,0 +1,16 @@
|
||||
# Allowlist: only send the files needed for the integration-service image build.
|
||||
*
|
||||
|
||||
# Parent directories must be explicitly included for allowlist patterns to work.
|
||||
!integration/
|
||||
!integration/packages/
|
||||
!integration/packages/widgets/
|
||||
!integration/packages/widgets/**
|
||||
!integration/packages/integration/
|
||||
!integration/packages/integration/public/
|
||||
!integration/packages/integration/public/logos/
|
||||
!integration/packages/integration/public/logos/*.svg
|
||||
|
||||
# Our service-specific files
|
||||
!integration-service/
|
||||
!integration-service/**
|
||||
27
Dockerfile
Normal file
27
Dockerfile
Normal file
@@ -0,0 +1,27 @@
|
||||
# ── Stage 1: build lagaufre.js from suitenumerique/integration source ──────────
|
||||
# Build context must be the sunbeam/ root so we can reach both
|
||||
# integration/packages/widgets/ and integration-service/.
|
||||
FROM node:22-alpine AS widget-build
|
||||
WORKDIR /src
|
||||
COPY integration/packages/widgets/ .
|
||||
RUN npm ci && npm run build
|
||||
|
||||
# ── Stage 2: nginx serving all static assets ────────────────────────────────────
|
||||
FROM nginx:alpine
|
||||
|
||||
# Official La Suite service logos from the integration package source tree.
|
||||
COPY integration/packages/integration/public/logos/ /usr/share/nginx/html/logos/
|
||||
|
||||
# Custom logos for O Estúdio services not in the official La Suite logo set
|
||||
# (drive, mail, people — same two-tone #000091/#e1000f style as upstream).
|
||||
COPY integration-service/logos/ /usr/share/nginx/html/logos/
|
||||
|
||||
# Built lagaufre.js v2 widget (11 kB, Shadow DOM, ARIA-compliant popup).
|
||||
COPY --from=widget-build /src/dist/lagaufre.js /usr/share/nginx/html/widget/lagaufre.js
|
||||
|
||||
# v1-compat gaufre.js — thin wrapper loaded by people-frontend via sub_filter.
|
||||
# Derives its origin from the script URL at runtime; no DOMAIN_SUFFIX baked in.
|
||||
COPY integration-service/gaufre.js /usr/share/nginx/html/gaufre.js
|
||||
|
||||
# Nginx config — only services.json is mounted at runtime (from ConfigMap).
|
||||
COPY integration-service/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
54
gaufre.js
Normal file
54
gaufre.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* O Estúdio — La Gaufre v1 compatibility wrapper.
|
||||
*
|
||||
* Loaded by people-frontend via the URL rewritten by nginx sub_filter.
|
||||
* Responsibilities:
|
||||
* 1. Add "lasuite--gaufre-loaded" to <html> so the GaufreButton becomes visible.
|
||||
* 2. Dynamically load the lagaufre.js v2 widget from the same origin.
|
||||
* 3. Wire button clicks via event delegation — survives React hydration
|
||||
* replacing the initial SSR'd DOM element.
|
||||
*
|
||||
* No DOMAIN_SUFFIX baked in — the integration origin is derived from this
|
||||
* script's own src URL at runtime, so the same image works in every environment.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
// Reveal the GaufreButton immediately (synchronously, before anything else).
|
||||
// @gouvfr-lasuite/ui-kit hides .lasuite-gaufre-btn until this class is present.
|
||||
document.documentElement.classList.add('lasuite--gaufre-loaded');
|
||||
|
||||
// Derive the integration service origin from this script's URL.
|
||||
var origin = (function () {
|
||||
var s = document.querySelector('#lasuite-gaufre-script');
|
||||
return (s && s.src) ? new URL(s.src).origin : window.location.origin;
|
||||
})();
|
||||
|
||||
var widgetReady = false;
|
||||
|
||||
// Load the lagaufre v2 widget. We do NOT pass buttonElement to avoid
|
||||
// holding a stale reference after React hydration replaces DOM nodes.
|
||||
// Button clicks are handled via event delegation below instead.
|
||||
var script = document.createElement('script');
|
||||
script.src = origin + '/api/v2/lagaufre.js';
|
||||
script.onload = function () {
|
||||
window._lasuite_widget = window._lasuite_widget || [];
|
||||
window._lasuite_widget.push(['lagaufre', 'init', {
|
||||
api: origin + '/api/v2/services.json',
|
||||
label: 'O Estúdio',
|
||||
closeLabel: 'Fechar',
|
||||
newWindowLabelSuffix: ' · nova janela',
|
||||
}]);
|
||||
widgetReady = true;
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
|
||||
// Event delegation — listens on document (bubbling) so it works regardless
|
||||
// of which element React hydration puts in the DOM after page load.
|
||||
document.addEventListener('click', function (e) {
|
||||
if (!widgetReady) return;
|
||||
if (!e.target.closest('.js-lasuite-gaufre-btn')) return;
|
||||
window._lasuite_widget = window._lasuite_widget || [];
|
||||
window._lasuite_widget.push(['lagaufre', 'toggle']);
|
||||
});
|
||||
})();
|
||||
6
logos/drive.svg
Normal file
6
logos/drive.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Folder tab -->
|
||||
<path d="M4 20v-4a3 3 0 013-3h9.17l3.41 3.41A2 2 0 0021 17h20a3 3 0 013 3v2H4z" fill="#e1000f"/>
|
||||
<!-- Folder body -->
|
||||
<path d="M4 22h40v16a4 4 0 01-4 4H8a4 4 0 01-4-4V22z" fill="#000091"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 320 B |
6
logos/mail.svg
Normal file
6
logos/mail.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Envelope body -->
|
||||
<path d="M4 14h40v22a2 2 0 01-2 2H6a2 2 0 01-2-2V14z" fill="#000091"/>
|
||||
<!-- Envelope fold (chevron) -->
|
||||
<path d="M4 14l20 15 20-15H4z" fill="#e1000f"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 286 B |
10
logos/people.svg
Normal file
10
logos/people.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Person 2 head (behind) -->
|
||||
<circle cx="30" cy="13" r="7" fill="#e1000f"/>
|
||||
<!-- Person 2 body (behind) -->
|
||||
<path d="M18 38c0-6.627 5.373-12 12-12s12 5.373 12 12H18z" fill="#e1000f"/>
|
||||
<!-- Person 1 head (front) -->
|
||||
<circle cx="18" cy="13" r="7" fill="#000091"/>
|
||||
<!-- Person 1 body (front) -->
|
||||
<path d="M6 38c0-6.627 5.373-12 12-12s12 5.373 12 12H6z" fill="#000091"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 489 B |
39
nginx.conf
Normal file
39
nginx.conf
Normal file
@@ -0,0 +1,39 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
# v2 widget — served from image, short cache OK (changes only on redeploy)
|
||||
location = /api/v2/lagaufre.js {
|
||||
alias /usr/share/nginx/html/widget/lagaufre.js;
|
||||
add_header Content-Type "application/javascript; charset=utf-8";
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
add_header Cache-Control "public, max-age=3600";
|
||||
}
|
||||
|
||||
# v2 services — mounted from ConfigMap (contains expanded DOMAIN_SUFFIX URLs)
|
||||
location = /api/v2/services.json {
|
||||
alias /etc/integration/services.json;
|
||||
add_header Content-Type "application/json; charset=utf-8";
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
add_header Cache-Control "no-cache";
|
||||
}
|
||||
|
||||
# SVG logos — served from image, long cache (change only on redeploy)
|
||||
location ~ ^/logos/[a-zA-Z0-9_-]+\.svg$ {
|
||||
root /usr/share/nginx/html;
|
||||
add_header Content-Type "image/svg+xml";
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
add_header Cache-Control "public, max-age=86400";
|
||||
}
|
||||
|
||||
# v1 gaufre.js — thin wrapper served from image (no DOMAIN_SUFFIX needed)
|
||||
location = /api/v1/gaufre.js {
|
||||
alias /usr/share/nginx/html/gaufre.js;
|
||||
add_header Content-Type "application/javascript; charset=utf-8";
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
}
|
||||
|
||||
location / {
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user