plugin_framework, test plugin manager, uninstall is not fully tested

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou 2023-05-10 23:57:46 +08:00
parent 4ee0fd9676
commit 095ac46255
12 changed files with 327 additions and 116 deletions

View File

@ -1466,15 +1466,22 @@ class _PluginState extends State<_Plugin> {
return ListView(
physics: DraggableNeverScrollableScrollPhysics(),
controller: scrollController,
children: model.plugins
.map((entry) => DesktopSettingsCard(plugin: entry))
.toList(),
children: model.plugins.map((entry) => pluginCard(entry)).toList(),
).marginOnly(bottom: _kListViewBottomMargin);
}),
),
);
}
Widget pluginCard(PluginInfo plugin) {
return ChangeNotifierProvider.value(
value: plugin,
child: Consumer<PluginInfo>(
builder: (context, model, child) => DesktopSettingsCard(plugin: model),
),
);
}
Widget accountAction() {
return Obx(() => _Button(
gFFI.userModel.userName.value.isEmpty ? 'Login' : 'Logout',

View File

@ -144,17 +144,20 @@ class PluginInfo with ChangeNotifier {
SourceInfo sourceInfo;
Meta meta;
String installedVersion; // It is empty if not installed.
DateTime installTime;
String failedMsg;
String invalidReason; // It is empty if valid.
PluginInfo({
required this.sourceInfo,
required this.meta,
required this.installedVersion,
required this.installTime,
required this.invalidReason,
this.failedMsg = '',
});
bool get installed => installedVersion.isNotEmpty;
bool get needUpdate => installed && installedVersion != meta.version;
void update(PluginInfo plugin) {
assert(plugin.meta.id == meta.id, 'Plugin id not match');
if (plugin.meta.id != meta.id) {
@ -164,10 +167,28 @@ class PluginInfo with ChangeNotifier {
sourceInfo = plugin.sourceInfo;
meta = plugin.meta;
installedVersion = plugin.installedVersion;
installTime = plugin.installTime;
invalidReason = plugin.invalidReason;
notifyListeners();
}
void setInstall(String msg) {
if (msg == "finished") {
msg = '';
}
failedMsg = msg;
if (msg.isEmpty) {
installedVersion = meta.version;
}
notifyListeners();
}
void setUninstall(String msg) {
failedMsg = msg;
if (msg.isEmpty) {
installedVersion = '';
}
notifyListeners();
}
}
class PluginManager with ChangeNotifier {
@ -194,11 +215,27 @@ class PluginManager with ChangeNotifier {
_handlePluginList(evt['plugin_list']);
} else if (evt['plugin_update'] != null) {
_handlePluginUpdate(evt['plugin_update']);
} else if (evt['plugin_install'] != null && evt['id'] != null) {
_handlePluginInstall(evt['id'], evt['plugin_install']);
} else if (evt['plugin_uninstall'] != null && evt['id'] != null) {
_handlePluginUninstall(evt['id'], evt['plugin_uninstall']);
} else {
debugPrint('Failed to handle manager event: $evt');
}
}
void _sortPlugins() {
plugins.sort((a, b) {
if (a.installed) {
return -1;
} else if (b.installed) {
return 1;
} else {
return 0;
}
});
}
void _handlePluginUpdate(Map<String, dynamic> evt) {
final plugin = _getPluginFromEvent(evt);
if (plugin == null) {
@ -207,6 +244,8 @@ class PluginManager with ChangeNotifier {
for (var i = 0; i < _plugins.length; i++) {
if (_plugins[i].meta.id == plugin.meta.id) {
_plugins[i].update(plugin);
_sortPlugins();
notifyListeners();
return;
}
}
@ -225,9 +264,32 @@ class PluginManager with ChangeNotifier {
} catch (e) {
debugPrint('Failed to decode $e, plugin list \'$pluginList\'');
}
_sortPlugins();
notifyListeners();
}
void _handlePluginInstall(String id, String msg) {
for (var i = 0; i < _plugins.length; i++) {
if (_plugins[i].meta.id == id) {
_plugins[i].setInstall(msg);
_sortPlugins();
notifyListeners();
return;
}
}
}
void _handlePluginUninstall(String id, String msg) {
for (var i = 0; i < _plugins.length; i++) {
if (_plugins[i].meta.id == id) {
_plugins[i].setUninstall(msg);
_sortPlugins();
notifyListeners();
return;
}
}
}
PluginInfo? _getPluginFromEvent(Map<String, dynamic> evt) {
final s = evt['source'];
assert(s != null, 'Source is null');
@ -273,19 +335,10 @@ class PluginManager with ChangeNotifier {
publishInfo:
PublishInfo(lastReleased: lastReleased, published: published),
);
late DateTime installTime;
try {
installTime =
DateTime.parse(evt['install_time'] ?? '1970-01-01T00+00:00');
} catch (e) {
installTime = DateTime.utc(1970);
}
return PluginInfo(
sourceInfo: source,
meta: meta,
installedVersion: evt['installed_version'],
installTime: installTime,
invalidReason: evt['invalid_reason'] ?? '',
);
}

View File

@ -28,10 +28,13 @@ class DesktopSettingsCard extends StatefulWidget {
class _DesktopSettingsCardState extends State<DesktopSettingsCard> {
PluginInfo get plugin => widget.plugin;
bool get installed => plugin.installedVersion.isNotEmpty;
bool get installed => plugin.installed;
bool isEnabled = false;
@override
Widget build(BuildContext context) {
isEnabled = bind.pluginIsEnabled(id: plugin.meta.id);
return Row(
children: [
Flexible(
@ -70,7 +73,7 @@ class _DesktopSettingsCardState extends State<DesktopSettingsCard> {
child: Row(
children: [
Text(
translate(widget.plugin.meta.name),
widget.plugin.meta.name,
textAlign: TextAlign.start,
style: const TextStyle(
fontSize: _kTitleFontSize,
@ -95,24 +98,25 @@ class _DesktopSettingsCardState extends State<DesktopSettingsCard> {
return Container(
child: ElevatedButton(
onPressed: onPressed,
child: Text(label),
child: Text(translate(label)),
),
);
}
Widget headerInstallEnable() {
final installButton = headerButton(installed ? 'uninstall' : 'install', () {
bind.pluginInstall(
id: plugin.meta.id,
b: !installed,
);
});
final installButton = headerButton(
installed ? 'Uninstall' : 'Install',
() {
bind.pluginInstall(
id: plugin.meta.id,
b: !installed,
);
},
);
if (installed) {
final needUpdate =
plugin.installedVersion.compareTo(plugin.meta.version) < 0;
final updateButton = needUpdate
? headerButton('update', () {
final updateButton = plugin.needUpdate
? headerButton('Update', () {
bind.pluginInstall(
id: plugin.meta.id,
b: !installed,
@ -120,10 +124,9 @@ class _DesktopSettingsCardState extends State<DesktopSettingsCard> {
})
: Container();
final isEnabled = bind.pluginIsEnabled(id: plugin.meta.id);
final enableButton = !installed
? Container()
: headerButton(isEnabled ? 'disable' : 'enable', () {
: headerButton(isEnabled ? 'Disable' : 'Enable', () {
if (isEnabled) {
clearPlugin(plugin.meta.id);
}
@ -175,7 +178,7 @@ class _DesktopSettingsCardState extends State<DesktopSettingsCard> {
}
Widget more() {
if (!installed) {
if (!(installed && isEnabled)) {
return Container();
}

View File

@ -1,6 +1,6 @@
use std::{
future::Future,
sync::{Arc, Mutex},
sync::{Arc, Mutex, RwLock},
};
#[derive(Debug, Eq, PartialEq)]
@ -61,6 +61,7 @@ lazy_static::lazy_static! {
lazy_static::lazy_static! {
static ref IS_SERVER: bool = std::env::args().nth(1) == Some("--server".to_owned());
static ref SERVER_RUNNING: Arc<RwLock<bool>> = Default::default();
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
@ -93,9 +94,14 @@ pub fn global_init() -> bool {
pub fn global_clean() {}
#[inline]
pub fn set_server_running(b: bool) {
*SERVER_RUNNING.write().unwrap() = b;
}
#[inline]
pub fn is_server() -> bool {
*IS_SERVER
*IS_SERVER || *SERVER_RUNNING.read().unwrap()
}
#[inline]

View File

@ -245,7 +245,7 @@ pub fn core_main() -> Option<Vec<String>> {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if args[0] == "--plugin-install" {
if args.len() == 3 {
crate::plugin::privileged_install_plugin(&args[1], &args[2]);
crate::plugin::install_plugin_with_url(&args[1], &args[2]);
}
return None;
} else if args[0] == "--plugin-uninstall" {

View File

@ -1580,12 +1580,11 @@ pub fn plugin_install(id: String, b: bool) {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
if b {
allow_err!(crate::plugin::user_install_plugin(&id));
if let Err(e) = crate::plugin::install_plugin(&id) {
log::error!("Failed to install plugin '{}': {}", id, e);
}
} else {
// to-do: uninstall plugin
// 1. unload 2. remove configs 3. remove config files
// allow_err!(super::unload_plugin(&id));
crate::plugin::uninstall_plugin(&id);
crate::plugin::uninstall_plugin(&id, true);
}
}
}

View File

@ -36,6 +36,16 @@ fn path_plugins(id: &str) -> PathBuf {
HbbConfig::path("plugins").join(id)
}
pub fn remove(id: &str) {
CONFIG_SHARED.lock().unwrap().remove(id);
CONFIG_PEERS.lock().unwrap().remove(id);
// allow_err is Ok here.
allow_err!(ManagerConfig::remove_plugin(id));
if let Err(e) = fs::remove_dir_all(path_plugins(id)) {
log::error!("Failed to remove plugin '{}' directory: {}", id, e);
}
}
impl Deref for SharedConfig {
type Target = HashMap<String, String>;
@ -207,6 +217,7 @@ impl PeerConfig {
#[derive(Debug, Serialize, Deserialize)]
pub struct PluginStatus {
pub enabled: bool,
pub uninstalled: bool,
}
const MANAGER_VERSION: &str = "0.1.0";
@ -269,7 +280,13 @@ impl ManagerConfig {
if let Some(status) = lock.plugins.get_mut(id) {
status.enabled = enabled;
} else {
lock.plugins.insert(id.to_owned(), PluginStatus { enabled });
lock.plugins.insert(
id.to_owned(),
PluginStatus {
enabled,
uninstalled: false,
},
);
}
hbb_common::config::store_path(Self::path(), &*lock)
}
@ -292,29 +309,50 @@ impl ManagerConfig {
#[inline]
pub fn add_plugin(id: &str) -> ResultType<()> {
let mut lock = CONFIG_MANAGER.lock().unwrap();
lock.plugins
.insert(id.to_owned(), PluginStatus { enabled: true });
lock.plugins.insert(
id.to_owned(),
PluginStatus {
enabled: true,
uninstalled: false,
},
);
hbb_common::config::store_path(Self::path(), &*lock)
}
#[inline]
pub fn remove_plugin(id: &str, uninstall: bool) -> ResultType<()> {
pub fn remove_plugin(id: &str) -> ResultType<()> {
let mut lock = CONFIG_MANAGER.lock().unwrap();
lock.plugins.remove(id);
hbb_common::config::store_path(Self::path(), &*lock)?;
if uninstall {
allow_err!(fs::remove_dir_all(path_plugins(id)));
}
Ok(())
hbb_common::config::store_path(Self::path(), &*lock)
}
pub fn remove_plugins(uninstall: bool) {
#[inline]
pub fn is_uninstalled(id: &str) -> bool {
CONFIG_MANAGER
.lock()
.unwrap()
.plugins
.get(id)
.map(|p| p.uninstalled)
.unwrap_or(false)
}
#[inline]
pub fn set_uninstall(id: &str, uninstall: bool) -> ResultType<()> {
let mut lock = CONFIG_MANAGER.lock().unwrap();
lock.plugins.clear();
allow_err!(hbb_common::config::store_path(Self::path(), &*lock));
if uninstall {
allow_err!(fs::remove_dir_all(HbbConfig::path("plugins")));
if let Some(status) = lock.plugins.get_mut(id) {
status.uninstalled = uninstall;
} else {
lock.plugins.insert(
id.to_owned(),
PluginStatus {
enabled: true,
uninstalled: uninstall,
},
);
}
hbb_common::config::store_path(Self::path(), &*lock)?;
Ok(())
}
}

View File

@ -24,6 +24,7 @@ pub enum Plugin {
Load(String),
Reload(String),
InstallStatus((String, InstallStatus)),
Uninstall(String),
}
#[tokio::main(flavor = "current_thread")]
@ -66,6 +67,11 @@ pub async fn reload_plugin(id: &str) -> ResultType<()> {
reload_plugin_async(id).await
}
#[tokio::main(flavor = "current_thread")]
pub async fn uninstall_plugin(id: &str) -> ResultType<()> {
uninstall_plugin_async(id).await
}
async fn get_config_async(id: &str, name: &str, ms_timeout: u64) -> ResultType<Option<String>> {
let mut c = connect(ms_timeout, "").await?;
c.send(&Data::Plugin(Plugin::Config(
@ -164,6 +170,13 @@ async fn reload_plugin_async(id: &str) -> ResultType<()> {
Ok(())
}
async fn uninstall_plugin_async(id: &str) -> ResultType<()> {
let mut c = connect(1000, "").await?;
c.send(&Data::Plugin(Plugin::Uninstall(id.to_owned())))
.await?;
Ok(())
}
pub async fn handle_plugin(plugin: Plugin, stream: &mut Connection) {
match plugin {
Plugin::Config(id, name, value) => match value {
@ -206,11 +219,15 @@ pub async fn handle_plugin(plugin: Plugin, stream: &mut Connection) {
}
},
Plugin::Load(id) => {
allow_err!(super::config::ManagerConfig::set_uninstall(&id, false));
allow_err!(super::load_plugin(&id));
}
Plugin::Reload(id) => {
allow_err!(super::reload_plugin(&id));
}
Plugin::Uninstall(id) => {
super::manager::uninstall_plugin(&id, false);
}
_ => {}
}
}

View File

@ -8,12 +8,14 @@ use serde_derive::{Deserialize, Serialize};
use serde_json;
use std::{
collections::HashMap,
fs,
sync::{Arc, Mutex},
};
const MSG_TO_UI_PLUGIN_MANAGER_LIST: &str = "plugin_list";
const MSG_TO_UI_PLUGIN_MANAGER_UPDATE: &str = "plugin_update";
const MSG_TO_UI_PLUGIN_MANAGER_INSTALL: &str = "plugin_install";
const MSG_TO_UI_PLUGIN_MANAGER_UNINSTALL: &str = "plugin_uninstall";
const IPC_PLUGIN_POSTFIX: &str = "_plugin";
@ -40,7 +42,6 @@ pub struct PluginInfo {
pub source: PluginSource,
pub meta: PluginMeta,
pub installed_version: String,
pub install_time: String,
pub invalid_reason: String,
}
@ -78,7 +79,6 @@ fn get_source_plugins() -> HashMap<String, PluginInfo> {
source: source.clone(),
meta: meta.clone(),
installed_version: "".to_string(),
install_time: "".to_string(),
invalid_reason: "".to_string(),
},
);
@ -110,9 +110,18 @@ fn send_plugin_list_event(plugins: &HashMap<String, PluginInfo>) {
pub fn load_plugin_list() {
let mut plugin_info_lock = PLUGIN_INFO.lock().unwrap();
let mut plugins = get_source_plugins();
for (id, info) in super::plugins::get_plugin_infos().read().unwrap().iter() {
// A big read lock is needed to prevent race conditions.
// Loading plugin list may be slow.
// Users may call uninstall plugin in the middle.
let plugin_infos = super::plugins::get_plugin_infos();
let plugin_infos_read_lock = plugin_infos.read().unwrap();
for (id, info) in plugin_infos_read_lock.iter() {
if info.uninstalled {
continue;
}
if let Some(p) = plugins.get_mut(id) {
p.install_time = info.install_time.clone();
p.installed_version = info.desc.meta().version.clone();
p.invalid_reason = "".to_string();
} else {
@ -126,7 +135,6 @@ pub fn load_plugin_list() {
},
meta: info.desc.meta().clone(),
installed_version: info.desc.meta().version.clone(),
install_time: info.install_time.clone(),
invalid_reason: "".to_string(),
},
);
@ -139,14 +147,34 @@ pub fn load_plugin_list() {
pub fn install_plugin(id: &str) -> ResultType<()> {
match PLUGIN_INFO.lock().unwrap().get(id) {
Some(plugin) => {
let _plugin_url = format!(
"{}/plugins/{}/{}_{}.zip",
plugin.source.url, plugin.meta.id, plugin.meta.id, plugin.meta.version
);
// to-do: Support args with space in quotes. 'arg 1' and "arg 2"
#[cfg(windows)]
let _res =
crate::platform::elevate(&format!("--plugin-install {} {}", id, _plugin_url))?;
{
let mut same_plugin_exists = false;
if let Some(version) = super::plugins::get_version(id) {
if version == plugin.meta.version {
same_plugin_exists = true;
}
}
// to-do: Support args with space in quotes. 'arg 1' and "arg 2"
let plugin_url = format!(
"{}/plugins/{}/{}_{}.zip",
plugin.source.url, plugin.meta.id, plugin.meta.id, plugin.meta.version
);
let args = if same_plugin_exists {
format!("--plugin-install {}", id)
} else {
format!("--plugin-install {} {}", id, plugin_url)
};
let allowed = crate::platform::elevate(&args)?;
if allowed && same_plugin_exists {
super::ipc::load_plugin(id)?;
super::plugins::load_plugin(id)?;
super::plugins::mark_uninstalled(id, false);
push_install_event(id, "finished");
}
}
Ok(())
}
None => {
@ -155,26 +183,93 @@ pub fn install_plugin(id: &str) -> ResultType<()> {
}
}
pub(super) fn remove_plugins() {}
// 1. Add to uninstall list.
// 2. Try remove.
// 2. Remove on the next start.
pub fn uninstall_plugin(id: &str) {
// to-do: add to uninstall list.
super::plugins::unload_plugin(id);
fn get_uninstalled_plugins() -> ResultType<Vec<String>> {
let plugins_dir = super::get_plugins_dir()?;
let mut plugins = Vec::new();
if plugins_dir.exists() {
for entry in std::fs::read_dir(plugins_dir)? {
match entry {
Ok(entry) => {
let plugin_dir = entry.path();
if plugin_dir.is_dir() {
if let Some(id) = plugin_dir.file_name().and_then(|n| n.to_str()) {
if super::config::ManagerConfig::is_uninstalled(id) {
plugins.push(id.to_string());
}
}
}
}
Err(e) => {
log::error!("Failed to read plugins dir entry, {}", e);
}
}
}
}
Ok(plugins)
}
fn push_install_event(id: &str, msg: &str) {
pub(super) fn remove_plugins() -> ResultType<()> {
for id in get_uninstalled_plugins()?.iter() {
super::config::remove(id as _);
if let Ok(dir) = super::get_plugin_dir(id as _) {
allow_err!(fs::remove_dir_all(dir));
}
}
Ok(())
}
pub fn uninstall_plugin(id: &str, called_by_ui: bool) {
if called_by_ui {
match crate::platform::elevate(&format!("--plugin-uninstall {}", id)) {
Ok(true) => {
if let Err(e) = super::ipc::uninstall_plugin(id) {
log::error!("Failed to uninstall plugin '{}': {}", id, e);
push_uninstall_event(id, "failed");
return;
}
super::plugins::unload_plugin(id);
super::plugins::mark_uninstalled(id, true);
super::config::remove(id);
push_uninstall_event(id, "");
}
Ok(false) => {
return;
}
Err(e) => {
log::error!("Failed to uninstall plugin '{}': {}", id, e);
push_uninstall_event(id, "failed");
return;
}
}
}
if is_server() {
super::plugins::unload_plugin(&id);
// allow_err is Ok here.
allow_err!(super::config::ManagerConfig::set_uninstall(&id, true));
}
}
fn push_event(id: &str, r#type: &str, msg: &str) {
let mut m = HashMap::new();
m.insert("name", MSG_TO_UI_TYPE_PLUGIN_MANAGER);
m.insert("id", id);
m.insert(MSG_TO_UI_PLUGIN_MANAGER_INSTALL, msg);
m.insert(r#type, msg);
if let Ok(event) = serde_json::to_string(&m) {
let _res = flutter::push_global_event(flutter::APP_TYPE_MAIN, event.clone());
}
}
#[inline]
fn push_uninstall_event(id: &str, msg: &str) {
push_event(id, MSG_TO_UI_PLUGIN_MANAGER_UNINSTALL, msg);
}
#[inline]
fn push_install_event(id: &str, msg: &str) {
push_event(id, MSG_TO_UI_PLUGIN_MANAGER_INSTALL, msg);
}
async fn handle_conn(mut stream: crate::ipc::Connection) {
loop {
tokio::select! {
@ -197,7 +292,7 @@ async fn handle_conn(mut stream: crate::ipc::Connection) {
InstallStatus::Finished => {
allow_err!(super::plugins::load_plugin(&id));
allow_err!(super::ipc::load_plugin_async(id).await);
load_plugin_list();
std::thread::spawn(load_plugin_list);
push_install_event(&id, "finished");
}
InstallStatus::FailedCreating => {
@ -329,7 +424,7 @@ pub(super) mod install {
Ok(())
}
pub fn install_plugin(id: &str, url: &str) {
pub fn install_plugin_with_url(id: &str, url: &str) {
let plugin_dir = match super::super::get_plugin_dir(id) {
Ok(d) => d,
Err(e) => {

View File

@ -1,4 +1,4 @@
use hbb_common::{allow_err, libc, log, ResultType};
use hbb_common::{libc, log, ResultType};
use std::{
env,
ffi::{c_char, c_int, c_void, CStr},
@ -19,12 +19,11 @@ mod plog;
mod plugins;
pub use manager::{
install::install_plugin as privileged_install_plugin, install_plugin as user_install_plugin,
load_plugin_list, uninstall_plugin,
install::install_plugin_with_url, install_plugin, load_plugin_list, uninstall_plugin,
};
pub use plugins::{
handle_client_event, handle_listen_event, handle_server_event, handle_ui_event, load_plugin,
reload_plugin, sync_ui, unload_plugin, unload_plugins,
reload_plugin, sync_ui, unload_plugin,
};
const MSG_TO_UI_TYPE_PLUGIN_EVENT: &str = "plugin_event";
@ -92,9 +91,13 @@ pub fn init() {
if !is_server() {
std::thread::spawn(move || manager::start_ipc());
} else {
manager::remove_plugins();
if let Err(e) = manager::remove_plugins() {
log::error!("Failed to remove plugins: {}", e);
}
}
if let Err(e) = plugins::load_plugins() {
log::error!("Failed to load plugins: {}", e);
}
allow_err!(plugins::load_plugins());
}
#[inline]

View File

@ -3,7 +3,7 @@ use super::{desc::Desc, errno::*, *};
use crate::common::is_server;
use crate::flutter;
use hbb_common::{
allow_err, bail,
bail,
dlopen::symbor::Library,
lazy_static, log,
message_proto::{Message, Misc, PluginFailure, PluginRequest},
@ -15,7 +15,6 @@ use std::{
ffi::{c_char, c_void},
path::PathBuf,
sync::{Arc, RwLock},
time::SystemTime,
};
const METHOD_HANDLE_UI: &[u8; 10] = b"handle_ui\0";
@ -29,7 +28,7 @@ lazy_static::lazy_static! {
pub(super) struct PluginInfo {
pub path: String,
pub install_time: String,
pub uninstalled: bool,
pub desc: Desc,
}
@ -307,7 +306,7 @@ fn load_plugin_dir(dir: &PathBuf) {
Err(e) => {
log::error!(
"Failed to read '{}' dir entry, {}",
dir.file_stem().and_then(|f| f.to_str()).unwrap_or(""),
dir.file_name().and_then(|f| f.to_str()).unwrap_or(""),
e
);
}
@ -316,20 +315,18 @@ fn load_plugin_dir(dir: &PathBuf) {
}
}
pub fn unload_plugins() {
log::info!("Plugins unloaded");
PLUGINS.write().unwrap().clear();
if change_manager() {
super::config::ManagerConfig::remove_plugins(false);
}
}
pub fn unload_plugin(id: &str) {
log::info!("Plugin {} unloaded", id);
PLUGINS.write().unwrap().remove(id);
if change_manager() {
allow_err!(super::config::ManagerConfig::remove_plugin(id, false));
}
}
pub(super) fn mark_uninstalled(id: &str, uninstalled: bool) {
log::info!("Plugin {} uninstall", id);
PLUGIN_INFO
.write()
.unwrap()
.get_mut(id)
.map(|info| info.uninstalled = uninstalled);
}
pub fn reload_plugin(id: &str) -> ResultType<()> {
@ -364,7 +361,7 @@ fn load_plugin_path(path: &str) -> ResultType<()> {
};
plugin.init(&init_data, path)?;
if change_manager() {
if is_server() {
super::config::ManagerConfig::add_plugin(&desc.meta().id)?;
}
@ -372,18 +369,11 @@ fn load_plugin_path(path: &str) -> ResultType<()> {
// Ui may be not ready now, so we need to update again once ui is ready.
reload_ui(&desc, None);
let install_time = PathBuf::from(path)
.metadata()
.and_then(|d| d.created())
.unwrap_or(SystemTime::UNIX_EPOCH);
let install_time = chrono::DateTime::<chrono::Local>::from(install_time)
.format("%Y-%m-%d %H:%M:%S")
.to_string();
// add plugins
let id = desc.meta().id.clone();
let plugin_info = PluginInfo {
path: path.to_string(),
install_time,
uninstalled: false,
desc,
};
PLUGIN_INFO.write().unwrap().insert(id.clone(), plugin_info);
@ -582,15 +572,6 @@ fn make_plugin_failure(id: &str, name: &str, msg: &str) -> Message {
msg_out
}
#[inline]
fn change_manager() -> bool {
#[cfg(debug_assertions)]
let change_manager = true;
#[cfg(not(debug_assertions))]
let change_manager = is_server();
change_manager
}
fn reload_ui(desc: &Desc, sync_to: Option<&str>) {
for (location, ui) in desc.location().ui.iter() {
if let Ok(ui) = serde_json::to_string(&ui) {
@ -641,3 +622,11 @@ pub(super) fn get_desc_conf(id: &str) -> Option<super::desc::Config> {
.get(id)
.map(|info| info.desc.config().clone())
}
pub(super) fn get_version(id: &str) -> Option<String> {
PLUGIN_INFO
.read()
.unwrap()
.get(id)
.map(|info| info.desc.meta().version.clone())
}

View File

@ -394,6 +394,7 @@ pub async fn start_server(is_server: bool) {
}
if is_server {
crate::common::set_server_running(true);
std::thread::spawn(move || {
if let Err(err) = crate::ipc::start("") {
log::error!("Failed to start ipc: {}", err);