mirror of
https://github.com/Tencent/WeKnora.git
synced 2026-10-15 10:58:21 +08:00
Plugins can add pages to the app. Each is an HTML entry under ui/ in the
package:
| Point | Shown as | Default minRole |
| ------------------ | ----------------------------- | --------------- |
| `pages` | toolbox tabs | viewer |
| `settingsSections` | a section under Plugins in settings | admin |
| `kbTabs` | a tab on each knowledge base (gets knowledgeBaseId) | viewer |
Declarative plugins may have pages too.
Isolation:
- A page runs in `<iframe sandbox="allow-scripts allow-forms allow-popups
allow-popups-to-escape-sandbox">` without allow-same-origin, so it has an
opaque origin and cannot read the app's storage.
- Its files come from /api/v1/plugin-ui/assets/{id}/{version}/ui/...
(served without login, only files under ui/ of the loaded version).
- They carry a CSP with connect-src 'none', so the page cannot reach the
network.
Bridge:
- The page talks to the app only over postMessage: `api.request`,
`ui.toast`, `ui.confirm`, `ui.navigate`, `ui.resize`, `ui.close`.
- The app answers only its frame's window, rate-limited, and validates
paths and sizes.
- It sends init (plugin, mount, locale, role, theme tokens, context) and
theme/locale changes.
- api.request goes to POST /api/v1/plugins/:id/ui-request. The app checks
the workspace has the plugin on and the caller meets the page's minRole
(same rules as the route guards), then calls the plugin's new
/v1/ui/request endpoint with the caller's role.
SDKs:
- Go: `p.UI` and `UIJSON`.
- Python: `@plugin.ui` and `UIResponse`.
- Both list "ui" in their manifest; conformance checks the endpoint; the
OpenAPI spec documents it.
- packages/plugin-ui is the page side: a dependency-free ES module with
types that connects, applies the theme as --wk-* CSS variables and keeps
the frame sized.
Also:
- Fix the Python SDK reading Go timestamps with fewer than 6 fraction
digits on Python < 3.11. Key-value entries tripped on it.
- examples/plugins/links: a Python plugin with all three kinds of page,
storing links in the Host API key-value store.
32 lines
1.2 KiB
Go
32 lines
1.2 KiB
Go
package pluginapi
|
|
|
|
import "encoding/json"
|
|
|
|
// UIRequestPath answers requests from a plugin's pages. Pages run in a
|
|
// sandboxed iframe that can reach nothing itself; what they send through
|
|
// the WeKnora bridge (api.request) arrives here, with the caller's context
|
|
// and configuration in the envelope.
|
|
const UIRequestPath = "/v1/ui/request"
|
|
|
|
// UIRequest is one request a plugin page made.
|
|
type UIRequest struct {
|
|
// Mount is the page it came from, as "<point>/<id>" ("pages/links").
|
|
Mount string `json:"mount"`
|
|
// Method and Path are the page's own routing: WeKnora does not
|
|
// interpret them. Path starts with "/".
|
|
Method string `json:"method"`
|
|
Path string `json:"path"`
|
|
// Body is the JSON the page sent, if any.
|
|
Body json.RawMessage `json:"body,omitempty"`
|
|
// Role is the caller's workspace role: viewer, contributor, admin or
|
|
// owner. WeKnora already refused callers below the mount's minRole.
|
|
Role string `json:"role,omitempty"`
|
|
}
|
|
|
|
// UIResponse is what the page receives.
|
|
type UIResponse struct {
|
|
// Status is an HTTP-style status for the page (200 when zero).
|
|
Status int `json:"status,omitempty"`
|
|
Body json.RawMessage `json:"body,omitempty"`
|
|
}
|