pinakes-core: expose required_endpoints alongside UI pages in plugin manager
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I32c95a03f106db8fef7eedd0362756a46a6a6964
This commit is contained in:
parent
dc4dc41670
commit
15b005cef0
2 changed files with 119 additions and 13 deletions
|
|
@ -602,7 +602,8 @@ impl PluginManager {
|
||||||
/// List all UI pages provided by loaded plugins.
|
/// List all UI pages provided by loaded plugins.
|
||||||
///
|
///
|
||||||
/// Returns a vector of `(plugin_id, page)` tuples for all enabled plugins
|
/// Returns a vector of `(plugin_id, page)` tuples for all enabled plugins
|
||||||
/// that provide pages in their manifests.
|
/// that provide pages in their manifests. Both inline and file-referenced
|
||||||
|
/// page entries are resolved.
|
||||||
pub async fn list_ui_pages(
|
pub async fn list_ui_pages(
|
||||||
&self,
|
&self,
|
||||||
) -> Vec<(String, pinakes_plugin_api::UiPage)> {
|
) -> Vec<(String, pinakes_plugin_api::UiPage)> {
|
||||||
|
|
@ -612,23 +613,126 @@ impl PluginManager {
|
||||||
if !plugin.enabled {
|
if !plugin.enabled {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for entry in &plugin.manifest.ui.pages {
|
let plugin_dir = plugin
|
||||||
let page = match entry {
|
.manifest_path
|
||||||
pinakes_plugin_api::manifest::UiPageEntry::Inline(page) => {
|
.as_ref()
|
||||||
(**page).clone()
|
.and_then(|p| p.parent())
|
||||||
},
|
.map(std::path::Path::to_path_buf);
|
||||||
pinakes_plugin_api::manifest::UiPageEntry::File { .. } => {
|
let Some(plugin_dir) = plugin_dir else {
|
||||||
// File-referenced pages require a base path to resolve;
|
// No manifest path; serve only inline pages.
|
||||||
// skip them here as they should have been loaded at startup.
|
for entry in &plugin.manifest.ui.pages {
|
||||||
continue;
|
if let pinakes_plugin_api::manifest::UiPageEntry::Inline(page) = entry
|
||||||
},
|
{
|
||||||
};
|
pages.push((plugin.id.clone(), (**page).clone()));
|
||||||
pages.push((plugin.id.clone(), page));
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
match plugin.manifest.load_ui_pages(&plugin_dir) {
|
||||||
|
Ok(loaded) => {
|
||||||
|
for page in loaded {
|
||||||
|
pages.push((plugin.id.clone(), page));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!(
|
||||||
|
"Failed to load UI pages for plugin '{}': {e}",
|
||||||
|
plugin.id
|
||||||
|
);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pages
|
pages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List all UI pages provided by loaded plugins, including each plugin's
|
||||||
|
/// declared endpoint allowlist.
|
||||||
|
///
|
||||||
|
/// Returns a vector of `(plugin_id, page, allowed_endpoints)` tuples. The
|
||||||
|
/// `allowed_endpoints` list mirrors the `required_endpoints` field from the
|
||||||
|
/// plugin manifest's `[ui]` section.
|
||||||
|
pub async fn list_ui_pages_with_endpoints(
|
||||||
|
&self,
|
||||||
|
) -> Vec<(String, pinakes_plugin_api::UiPage, Vec<String>)> {
|
||||||
|
let registry = self.registry.read().await;
|
||||||
|
let mut pages = Vec::new();
|
||||||
|
for plugin in registry.list_all() {
|
||||||
|
if !plugin.enabled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let allowed = plugin.manifest.ui.required_endpoints.clone();
|
||||||
|
let plugin_dir = plugin
|
||||||
|
.manifest_path
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|p| p.parent())
|
||||||
|
.map(std::path::Path::to_path_buf);
|
||||||
|
let Some(plugin_dir) = plugin_dir else {
|
||||||
|
for entry in &plugin.manifest.ui.pages {
|
||||||
|
if let pinakes_plugin_api::manifest::UiPageEntry::Inline(page) = entry
|
||||||
|
{
|
||||||
|
pages.push((plugin.id.clone(), (**page).clone(), allowed.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
match plugin.manifest.load_ui_pages(&plugin_dir) {
|
||||||
|
Ok(loaded) => {
|
||||||
|
for page in loaded {
|
||||||
|
pages.push((plugin.id.clone(), page, allowed.clone()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!(
|
||||||
|
"Failed to load UI pages for plugin '{}': {e}",
|
||||||
|
plugin.id
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pages
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collect CSS custom property overrides declared by all enabled plugins.
|
||||||
|
///
|
||||||
|
/// When multiple plugins declare the same property name, later-loaded plugins
|
||||||
|
/// overwrite earlier ones. Returns an empty map if no plugins are loaded or
|
||||||
|
/// none declare theme extensions.
|
||||||
|
pub async fn list_ui_theme_extensions(
|
||||||
|
&self,
|
||||||
|
) -> std::collections::HashMap<String, String> {
|
||||||
|
let registry = self.registry.read().await;
|
||||||
|
let mut merged = std::collections::HashMap::new();
|
||||||
|
for plugin in registry.list_all() {
|
||||||
|
if !plugin.enabled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (k, v) in &plugin.manifest.ui.theme_extensions {
|
||||||
|
merged.insert(k.clone(), v.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
merged
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List all UI widgets provided by loaded plugins.
|
||||||
|
///
|
||||||
|
/// Returns a vector of `(plugin_id, widget)` tuples for all enabled plugins
|
||||||
|
/// that provide widgets in their manifests.
|
||||||
|
pub async fn list_ui_widgets(
|
||||||
|
&self,
|
||||||
|
) -> Vec<(String, pinakes_plugin_api::UiWidget)> {
|
||||||
|
let registry = self.registry.read().await;
|
||||||
|
let mut widgets = Vec::new();
|
||||||
|
for plugin in registry.list_all() {
|
||||||
|
if !plugin.enabled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for widget in &plugin.manifest.ui.widgets {
|
||||||
|
widgets.push((plugin.id.clone(), widget.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
widgets
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if a plugin is loaded and enabled
|
/// Check if a plugin is loaded and enabled
|
||||||
pub async fn is_plugin_enabled(&self, plugin_id: &str) -> bool {
|
pub async fn is_plugin_enabled(&self, plugin_id: &str) -> bool {
|
||||||
let registry = self.registry.read().await;
|
let registry = self.registry.read().await;
|
||||||
|
|
@ -746,6 +850,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
capabilities: Default::default(),
|
capabilities: Default::default(),
|
||||||
config: Default::default(),
|
config: Default::default(),
|
||||||
|
ui: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
capabilities: ManifestCapabilities::default(),
|
capabilities: ManifestCapabilities::default(),
|
||||||
config: HashMap::new(),
|
config: HashMap::new(),
|
||||||
|
ui: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
RegisteredPlugin {
|
RegisteredPlugin {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue