Extract AppAPI auth and OCS preferences from CheckDNS.

Publish gonexapp v1 with Credentials, OCSClient, and parameterized
AppAPIPreferences so ExApps share Nextcloud ExApp plumbing per ADR 0013.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Konrad Neitzel
2026-08-26 19:14:22 +02:00
co-authored by Cursor
commit 7921694782
9 changed files with 501 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
package gonexapp
import (
"encoding/json"
"fmt"
"net/http"
"strings"
)
// AppAPIPreferences reads and writes one string ExApp preference for the requesting user.
type AppAPIPreferences struct {
Cred Credentials
AppID string
Key string
Client *http.Client
OCS OCSClient
}
// NewAppAPIPreferences returns a preference store for appID and key using cred for auth.
func NewAppAPIPreferences(cred Credentials, appID, key string) AppAPIPreferences {
return AppAPIPreferences{
Cred: cred,
AppID: appID,
Key: key,
OCS: OCSClient{Cred: cred},
}
}
func (p AppAPIPreferences) ocsClient() OCSClient {
c := p.OCS
if c.Cred.BaseURL == "" {
c.Cred = p.Cred
}
if c.Client == nil {
c.Client = p.Client
}
return c
}
// Get loads the configured preference key for the requesting user.
func (p AppAPIPreferences) Get() (string, error) {
ocs := p.ocsClient()
body, _ := json.Marshal(map[string]any{"configKeys": []string{p.Key}})
raw, err := ocs.Call(http.MethodPost, "apps/app_api/api/v1/ex-app/preference/get-values", body)
if err != nil {
return "", err
}
return decodePreferenceValue(raw, p.Key)
}
// Set stores value for the configured preference key.
func (p AppAPIPreferences) Set(value string) error {
ocs := p.ocsClient()
body, _ := json.Marshal(map[string]any{
"configKey": p.Key,
"configValue": value,
"sensitive": 0,
})
_, err := ocs.Call(http.MethodPost, "apps/app_api/api/v1/ex-app/preference", body)
return err
}
func decodePreferenceValue(raw []byte, key string) (string, error) {
var parsed struct {
OCS struct {
Data []struct {
ConfigKey string `json:"configkey"`
ConfigValue string `json:"configvalue"`
} `json:"data"`
} `json:"ocs"`
}
if err := json.Unmarshal(raw, &parsed); err != nil {
var items []struct {
ConfigKey string `json:"configkey"`
ConfigValue string `json:"configvalue"`
}
if err2 := json.Unmarshal(raw, &items); err2 != nil {
return "", fmt.Errorf("preferences decode: %w", err)
}
for _, it := range items {
if it.ConfigKey == key {
return strings.TrimSpace(it.ConfigValue), nil
}
}
return "", nil
}
for _, it := range parsed.OCS.Data {
if it.ConfigKey == key {
return strings.TrimSpace(it.ConfigValue), nil
}
}
return "", nil
}