feat: app-to-app integration framework + OnlyOffice handlers

Generic integration system for connecting deployed apps via toggle UI.
First handlers: OnlyOffice→FileBrowser (config.yaml patch) and
OnlyOffice→Nextcloud (occ CLI). Lifecycle hooks auto-suspend on
stop and re-apply on start.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 20:06:20 +01:00
parent d3b53d9877
commit 0a5840a255
15 changed files with 992 additions and 1 deletions
@@ -179,6 +179,67 @@ async function saveOptionalConfig(stackName) {
</script>
{{end}}
{{if .HasIntegrations}}
<div class="app-optional-config">
<h3>Integrációk</h3>
<p class="config-group-desc">
Más telepített alkalmazásokkal való összekapcsolás. Az integráció automatikusan felfüggesztődik, ha bármelyik alkalmazás leáll, és újraaktiválódik indításkor.
</p>
{{range .Integrations}}
<div class="config-field" style="display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.75rem 0;border-bottom:1px solid var(--border)">
<div style="flex:1">
<strong>{{.Label}}</strong>
<p class="config-field-help" style="margin:0">{{.Description}}</p>
{{if not .TargetDeployed}}
<span class="badge badge-muted" style="margin-top:.25rem;display:inline-block">Nincs telepítve</span>
{{else if not .TargetRunning}}
<span class="badge badge-orphaned" style="margin-top:.25rem;display:inline-block">Célalkalmazás leállítva</span>
{{else if eq .Status "error"}}
<span class="badge badge-orphaned" style="margin-top:.25rem;display:inline-block">Hiba</span>
{{else if .Enabled}}
<span class="badge badge-ok" style="margin-top:.25rem;display:inline-block">Aktív</span>
{{end}}
</div>
<label class="toggle">
<input type="checkbox"
{{if .Enabled}}checked{{end}}
{{if not .TargetDeployed}}disabled title="A célalkalmazás nincs telepítve"{{end}}
{{if and .TargetDeployed (not .TargetRunning)}}disabled title="A célalkalmazás nem fut"{{end}}
onchange="toggleIntegration('{{$.Stack.Name}}', '{{.Target}}', this.checked, this)">
<span class="toggle-label"></span>
</label>
</div>
{{end}}
</div>
<script>
async function toggleIntegration(provider, target, enable, checkbox) {
checkbox.disabled = true;
try {
var resp = await fetch('/api/integrations/' + provider + '/' + target, {
method: 'POST',
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
body: JSON.stringify({enable: enable})
});
var data = await resp.json();
if (!data.ok) {
checkbox.checked = !enable;
alert(data.error || 'Hiba történt');
} else {
// Reload to update status badges
setTimeout(function(){ location.reload(); }, 500);
return;
}
} catch(err) {
checkbox.checked = !enable;
alert('Hálózati hiba');
}
checkbox.disabled = false;
}
</script>
{{end}}
{{if .GeoGlobalEnabled}}
<div class="app-optional-config">
<h3>Földrajzi korlátozás</h3>