package integrations import ( "fmt" "os" "path/filepath" "strings" ) // OnlyOfficeFileBrowserHandler enables/disables OnlyOffice document editing in FileBrowser Quantum. type OnlyOfficeFileBrowserHandler struct{} func (h *OnlyOfficeFileBrowserHandler) Apply(ac *ApplyContext) error { jwtSecret := ac.ProviderEnv["JWT_SECRET"] if jwtSecret == "" { ac.Logger.Printf("[ERROR] [integrations] OnlyOffice-FileBrowser apply: JWT_SECRET not set") return fmt.Errorf("OnlyOffice JWT_SECRET nincs beállítva — telepítsd újra az alkalmazást") } subdomain := ac.ProviderEnv["SUBDOMAIN"] if subdomain == "" && ac.ProviderMeta != nil { subdomain = ac.ProviderMeta.Subdomain } if subdomain == "" { ac.Logger.Printf("[ERROR] [integrations] OnlyOffice-FileBrowser apply: subdomain unknown") return fmt.Errorf("OnlyOffice aldomain nem ismert") } configPath := filepath.Join(ac.StacksDir, "filebrowser", "config.yaml") officeURL := fmt.Sprintf("https://%s.%s", subdomain, ac.Domain) configData, err := os.ReadFile(configPath) if err != nil { ac.Logger.Printf("[ERROR] [integrations] OnlyOffice-FileBrowser apply: %v", err) return fmt.Errorf("FileBrowser config olvasási hiba: %w", err) } // Remove any existing integrations section, then append the new one configStr := removeIntegrationsSection(string(configData)) internalURL := "http://onlyoffice:80" integrationsBlock := fmt.Sprintf("integrations:\n office:\n url: %q\n internalUrl: %q\n secret: %q\n viewOnly: false\n", officeURL, internalURL, jwtSecret) configStr = strings.TrimRight(configStr, "\n") + "\n" + integrationsBlock // Atomic write tmpPath := configPath + ".tmp" if err := os.WriteFile(tmpPath, []byte(configStr), 0644); err != nil { ac.Logger.Printf("[ERROR] [integrations] OnlyOffice-FileBrowser apply: %v", err) return fmt.Errorf("config írási hiba: %w", err) } if err := os.Rename(tmpPath, configPath); err != nil { _ = os.Remove(tmpPath) ac.Logger.Printf("[ERROR] [integrations] OnlyOffice-FileBrowser apply: %v", err) return fmt.Errorf("config átnevezési hiba: %w", err) } ac.Logger.Printf("[INFO] [integrations] FileBrowser config updated with OnlyOffice integration") return ac.RestartStack("filebrowser") } func (h *OnlyOfficeFileBrowserHandler) Revoke(ac *ApplyContext) error { configPath := filepath.Join(ac.StacksDir, "filebrowser", "config.yaml") ac.Logger.Printf("[DEBUG] [integrations] OnlyOfficeFileBrowser.Revoke: configPath=%s", configPath) configData, err := os.ReadFile(configPath) if err != nil { if os.IsNotExist(err) { return nil } ac.Logger.Printf("[ERROR] [integrations] OnlyOffice-FileBrowser revoke: %v", err) return fmt.Errorf("config olvasási hiba: %w", err) } cleaned := removeIntegrationsSection(string(configData)) hadIntegrations := cleaned != string(configData) ac.Logger.Printf("[DEBUG] [integrations] OnlyOfficeFileBrowser.Revoke: integrationsSectionFound=%v", hadIntegrations) if !hadIntegrations { return nil // no integrations section, nothing to remove } tmpPath := configPath + ".tmp" if err := os.WriteFile(tmpPath, []byte(cleaned), 0644); err != nil { ac.Logger.Printf("[ERROR] [integrations] OnlyOffice-FileBrowser revoke: %v", err) return fmt.Errorf("config írási hiba: %w", err) } if err := os.Rename(tmpPath, configPath); err != nil { _ = os.Remove(tmpPath) ac.Logger.Printf("[ERROR] [integrations] OnlyOffice-FileBrowser revoke: %v", err) return fmt.Errorf("config átnevezési hiba: %w", err) } ac.Logger.Printf("[INFO] [integrations] FileBrowser config cleaned — OnlyOffice integration removed") return ac.RestartStack("filebrowser") } // removeIntegrationsSection strips the integrations: YAML block from a config string. // It removes from the line starting with "integrations:" to the next unindented key or EOF. func removeIntegrationsSection(config string) string { lines := strings.Split(config, "\n") var result []string inBlock := false for _, line := range lines { if strings.HasPrefix(line, "integrations:") { inBlock = true continue } if inBlock { trimmed := strings.TrimRight(line, " \t\r") if trimmed == "" { continue // skip blank lines within block } if line[0] != ' ' && line[0] != '\t' { // New top-level key — end of integrations block inBlock = false result = append(result, line) } // else: still inside indented block, skip continue } result = append(result, line) } return strings.Join(result, "\n") }