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
+6
View File
@@ -535,6 +535,12 @@ func deepCopyStack(s *Stack) Stack {
}
}
// Deep-copy Meta.Integrations
if s.Meta.Integrations != nil {
cp.Meta.Integrations = make([]IntegrationDef, len(s.Meta.Integrations))
copy(cp.Meta.Integrations, s.Meta.Integrations)
}
// Deep-copy Meta.HealthCheck pointer
if s.Meta.HealthCheck != nil {
hcCopy := *s.Meta.HealthCheck
+13
View File
@@ -21,6 +21,7 @@ type Metadata struct {
AppInfo AppInfo `yaml:"app_info" json:"app_info"`
OptionalConfig []OptionalConfigGroup `yaml:"optional_config" json:"optional_config"`
HealthCheck *HealthCheckConfig `yaml:"healthcheck,omitempty" json:"healthcheck,omitempty"`
Integrations []IntegrationDef `yaml:"integrations,omitempty" json:"integrations,omitempty"`
}
// AppInfo holds detailed app information for the info page.
@@ -78,6 +79,13 @@ type SelectOption struct {
Label string `yaml:"label" json:"label"`
}
// IntegrationDef defines a single integration this app can provide to a target app.
type IntegrationDef struct {
Target string `yaml:"target" json:"target"` // target app slug: "filebrowser", "nextcloud"
Label string `yaml:"label" json:"label"` // UI label (Hungarian)
Description string `yaml:"description" json:"description"` // UI description
}
// HealthCheckConfig defines controller-side health probe configuration.
// When configured, the controller periodically probes the app's container
// and overrides the stack state to "unhealthy" if the service is not responding.
@@ -210,3 +218,8 @@ func (m *Metadata) HasAppInfo() bool {
func (m *Metadata) HasOptionalConfig() bool {
return len(m.OptionalConfig) > 0
}
// HasIntegrations returns true if the metadata defines any integrations.
func (m *Metadata) HasIntegrations() bool {
return len(m.Integrations) > 0
}