The shell paints the instance wallpaper by default, and ExApps pick menu icons from NextcloudIcons instead of raw paths. Co-authored-by: Cursor <cursoragent@cursor.com>
6.6 KiB
go-nc-exapp
Shared Go library for Nextcloud ExApp Services: AppAPI authentication, OCS JSON calls, per-user ExApp preferences, Notifications, Users and Groups, an optional Required Groups Access Gate, and an optional App navigation shell.
Import: gitea.neitzel.de/konrad/go-nc-exapp (package gonexapp).
Scope
Included
- Credentials — Nextcloud base URL, AppAPI secret, and requesting user identity
- AuthHeaders — outbound AppAPI authorization for OCS and other Nextcloud calls
- WithUser — credentials scoped to a specific requesting user
- UserFromRequest — extract the requesting user from inbound AppAPI-proxied requests
- OCSClient — authenticated OCS calls that always append
format=json - AppAPIPreferences — parameterized get/set of a string ExApp preference (caller supplies app id and key)
- AppAPINotifications —
Send(Recipient = Credentials user) andSendTo(explicit Recipient); Subject required; Message, Link, and rich-object params optional. AppAPI’s notification OCS is limited (no actions, no custom icon) - Groups — Users and Groups reads:
UserGroups,GroupMembers,ListGroups(no search/paging). Directory calls (GroupMembers/ListGroups) run as the Credentials user and need an admin or subadmin - Access Gate — optional Required Groups enforcement (
Wrap+Check), English denied HTML for browsers (200 +frame-ancestors 'self'), positive membership cache; default skip for lifecycle paths and/js/top-menu scripts; env helpers forREQUIRED_GROUPS/REQUIRED_GROUPS_CACHE_SECONDS - Top Menu visibility —
TopMenuAdminRequiredhelper for deploy envTOP_MENU_ADMIN_REQUIRED(0/1for AppAPI top-menu OCS) - App navigation — optional Files-style shell (
AppNavigation.Handler). The ExApp supplies the tree, the page for each item, and an optional header. The selected item is theitemquery parameter (override withSelectKey). Other query parameters, including a Visit folder, are left in place. By default the shell loads the Nextcloud theme stylesheets, paints--image-background, and copies the surrounding page’sdata-theme-*markers; setDisableThemeto skip the stylesheets. An item may setIconto a same-origin image URL.NextcloudIconsnames the core SVGs Nextcloud already serves (NextcloudIcons.Folder,NextcloudIcons.Password, and the rest). Not mounting the handler keeps the ExApp’s own page. Lifecycle routes, HaRP startup, and Top Menu registration stay in the ExApp.
Excluded
- ExApp lifecycle HTTP routes (
/heartbeat,/enabled, …) — the Gate skips these by default but does not implement them - HaRP listen /
serve()and unix-socket bootstrap - Top-menu, script, and iframe UI registration
- WebDAV and file storage (see go-nc-files)
- Visit folder resolution (see go-nc-files)
- Fan-out Notifications (
SendToGroup/SendToAdmins) - A Library default privileged / admin user for directory OCS (callers who need that use
WithUserthemselves) - CheckDNS (or any ExApp) wiring for Notifications or Groups — products opt in separately
Usage
cred := gonexapp.Credentials{
BaseURL: "https://nextcloud.example",
AppID: "myexapp",
AppVersion: "0.1.0",
AAVersion: "1.0.0",
AppSecret: os.Getenv("APP_SECRET"),
UserID: "alice",
}
prefs := gonexapp.NewAppAPIPreferences(cred, "myexapp", "savedDefault")
value, err := prefs.Get()
err = gonexapp.NewAppAPINotifications(cred).Send(gonexapp.Notification{Subject: "Job finished"})
members, err := gonexapp.NewGroups(cred).GroupMembers("CheckDNS")
groupsEnv, groupsSet := os.LookupEnv("REQUIRED_GROUPS")
groups := gonexapp.ResolveRequiredGroups(groupsEnv, groupsSet, nil)
ttl := gonexapp.ParseCacheSeconds(os.Getenv("REQUIRED_GROUPS_CACHE_SECONDS"), gonexapp.DefaultCacheSeconds)
handler := gonexapp.AccessGate{Cred: cred, Groups: groups, CacheTTL: ttl}.Wrap(inner)
Each ExApp chooses its own preference keys; this library does not hardcode product-specific names.
Declare REQUIRED_GROUPS and REQUIRED_GROUPS_CACHE_SECONDS in the ExApp info.xml so Deploy options can set them.
The Gate skips /heartbeat, /enabled, /init, and any path under /js/ (AppAPI top-menu bootstrap). Serve the registered top-menu script under /js/… so a non-member still loads it and can show Denied UI in the Nextcloud shell. API routes stay gated.
Denied HTML is 200 with Content-Security-Policy: … frame-ancestors 'self'. Without that header AppAPI’s proxy defaults to frame-ancestors 'none' and a denied iframe stays blank. Non-HTML denials remain 403.
Top Menu visibility (TOP_MENU_ADMIN_REQUIRED)
Declare in info.xml under <environment-variables>. At enable time the ExApp reads the env and passes "0" or "1" to AppAPI’s top-menu OCS adminRequired. Only 0 and 1 are valid; anything else falls back to DefaultTopMenuAdminRequired (true → admins only).
adminRequired := gonexapp.TopMenuAdminRequired(
os.Getenv(gonexapp.EnvTopMenuAdminRequired),
gonexapp.DefaultTopMenuAdminRequired,
)
// use adminRequired in POST …/ui/top-menu when registering the menu entry
Applying a change: AppAPI registers the top menu when the ExApp receives PUT /enabled?enabled=1. Changing the deploy env alone does not update the menu entry.
- Set the new value in Deploy options (UI) or
occ app_api:app:register … --env TOP_MENU_ADMIN_REQUIRED=…/ update deploy config. - Recreate or restart the ExApp container so the new env is present.
- Re-run lifecycle: disable then enable the ExApp (UI or
occ app_api:app:disable/app_api:app:enable), orocc app_api:app:update … -eafter an image/info update.
Route access_level in info.xml is separate and only changes when AppAPI re-reads info.xml on register/update — not via this env.
Runnable package examples: go test -run Example.
Domain language
See CONTEXT.md for AppAPI credentials, Requesting user, Recipient, ExApp preference, Notification, OCS, Required Groups, Users and Groups, Access Gate, App navigation, and Top Menu visibility terminology.
Testing
Unit tests use httptest fake OCS servers. No live Nextcloud is required for Library CI.
Related
- go-nc-files — WebDAV, Working Folder, Saved Default, Visit resolution
- Workspace ADR 0013 — extraction from CheckDNS
- Workspace ADR
docs/adr/go-nc-exapp/0001-required-groups-access-gate.md— Access Gate decisions - Workspace ADR
docs/adr/go-nc-exapp/0002-users-and-groups-as-requesting-user.md— directory OCS as Requesting user - Workspace ADR
docs/adr/go-nc-exapp/0003-app-navigation-shell.md— App navigation shell