Support for searching plugins by name

This commit is contained in:
rockstardev 2024-10-09 06:47:11 -05:00
parent ae76cc1ca2
commit 2329c4a75f
5 changed files with 50 additions and 8 deletions

View File

@ -18,12 +18,13 @@ namespace BTCPayServer.Controllers
[HttpGet("server/plugins")] [HttpGet("server/plugins")]
public async Task<IActionResult> ListPlugins( public async Task<IActionResult> ListPlugins(
[FromServices] PluginService pluginService, [FromServices] PluginService pluginService,
[FromServices] BTCPayServerOptions btcPayServerOptions) [FromServices] BTCPayServerOptions btcPayServerOptions,
string search = null)
{ {
IEnumerable<PluginService.AvailablePlugin> availablePlugins; IEnumerable<PluginService.AvailablePlugin> availablePlugins;
try try
{ {
availablePlugins = await pluginService.GetRemotePlugins(); availablePlugins = await pluginService.GetRemotePlugins(search);
} }
catch (Exception) catch (Exception)
{ {

View File

@ -82,7 +82,7 @@ namespace BTCPayServer.HostedServices
var installedPlugins = var installedPlugins =
pluginService.LoadedPlugins.ToDictionary(plugin => plugin.Identifier, plugin => plugin.Version); pluginService.LoadedPlugins.ToDictionary(plugin => plugin.Identifier, plugin => plugin.Version);
var remotePlugins = await pluginService.GetRemotePlugins(); var remotePlugins = await pluginService.GetRemotePlugins(null);
//take the latest version of each plugin //take the latest version of each plugin
var remotePluginsList = remotePlugins var remotePluginsList = remotePlugins
.GroupBy(plugin => plugin.Identifier) .GroupBy(plugin => plugin.Identifier)

View File

@ -55,11 +55,13 @@ namespace BTCPayServer.Plugins
this.httpClient = httpClient; this.httpClient = httpClient;
} }
static JsonSerializerSettings serializerSettings = new() { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() }; static JsonSerializerSettings serializerSettings = new() { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() };
public async Task<PublishedVersion[]> GetPublishedVersions(string btcpayVersion, bool includePreRelease) public async Task<PublishedVersion[]> GetPublishedVersions(string btcpayVersion, bool includePreRelease, string searchPluginName = null)
{ {
var queryString = $"?includePreRelease={includePreRelease}"; var queryString = $"?includePreRelease={includePreRelease}";
if (btcpayVersion is not null) if (btcpayVersion is not null)
queryString += $"&btcpayVersion={btcpayVersion}&"; queryString += $"&btcpayVersion={btcpayVersion}";
if (searchPluginName is not null)
queryString += $"&searchPluginName={searchPluginName}";
var result = await httpClient.GetStringAsync($"api/v1/plugins{queryString}"); var result = await httpClient.GetStringAsync($"api/v1/plugins{queryString}");
return JsonConvert.DeserializeObject<PublishedVersion[]>(result, serializerSettings) ?? throw new InvalidOperationException(); return JsonConvert.DeserializeObject<PublishedVersion[]>(result, serializerSettings) ?? throw new InvalidOperationException();
} }

View File

@ -46,9 +46,10 @@ namespace BTCPayServer.Plugins
return pluginManifest.Version; return pluginManifest.Version;
} }
public async Task<AvailablePlugin[]> GetRemotePlugins() public async Task<AvailablePlugin[]> GetRemotePlugins(string searchPluginName)
{ {
var versions = await _pluginBuilderClient.GetPublishedVersions(null, _policiesSettings.PluginPreReleases); var versions = await _pluginBuilderClient.GetPublishedVersions(
null, _policiesSettings.PluginPreReleases, searchPluginName);
return versions.Select(v => return versions.Select(v =>
{ {
var p = v.ManifestInfo.ToObject<AvailablePlugin>(); var p = v.ManifestInfo.ToObject<AvailablePlugin>();

View File

@ -296,7 +296,45 @@
</div> </div>
} }
@if (availableAndNotInstalled.Any()) <form method="get" asp-action="ListPlugins" class="row mb-4" id="searchForm">
<div class="col-12 col-lg-6">
<div class="input-group">
<input type="text" name="search" class="form-control" placeholder="Search for plugins..."
value="@ViewContext.HttpContext.Request.Query["search"]" />
<button type="submit" class="btn btn-primary">
<vc:icon symbol="actions-search" />
</button>
</div>
</div>
</form>
<script>
// if we search using form, scroll to it
document.addEventListener("DOMContentLoaded", function() {
const searchForm = document.getElementById('searchForm');
// Set session storage flag on form submit
searchForm.addEventListener('submit', function() {
sessionStorage.setItem('formSubmitted', 'true');
});
// Check session storage to see if form was submitted, and scroll + clear it
if (sessionStorage.getItem('formSubmitted') === 'true') {
sessionStorage.removeItem('formSubmitted');
if (searchForm) {
searchForm.scrollIntoView();
}
}
});
</script>
@if (!availableAndNotInstalled.Any())
{
<div class="row mb-4">
<div class="col mb-4" style="margin-left:10px;">No plugins found</div>
</div>
}
else
{ {
<h3 class="mb-4">Available Plugins</h3> <h3 class="mb-4">Available Plugins</h3>
<div class="row mb-4"> <div class="row mb-4">