Skip to content

Commit

Permalink
Merge pull request #27 from btcpayserver/feat/plugin-visibility
Browse files Browse the repository at this point in the history
Plugin administration and visibility editing
  • Loading branch information
rockstardev authored Oct 3, 2024
2 parents 996dfa2 + 6ec9a35 commit 516985b
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 33 deletions.
128 changes: 105 additions & 23 deletions PluginBuilder/Controllers/AdminController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PluginBuilder.APIModels;
using PluginBuilder.DataModels;
using PluginBuilder.Services;
using PluginBuilder.ViewModels;
using PluginBuilder.ViewModels.Admin;
Expand All @@ -31,37 +32,118 @@ public AdminController(UserManager<IdentityUser> userManager, RoleManager<Identi
public async Task<IActionResult> ListPlugins()
{
await using var conn = await _connectionFactory.Open();
var rows = await conn.QueryAsync(
$"""
SELECT p.slug, v.ver, v.build_id, v.btcpay_min_ver, v.pre_release, v.updated_at, u."Email" as email
FROM plugins p
JOIN versions v ON p.slug = v.plugin_slug
JOIN users_plugins up ON v.plugin_slug = up.plugin_slug
JOIN "AspNetUsers" u ON up.user_id = u."Id"
WHERE v.ver = (SELECT MAX(ver) FROM versions WHERE plugin_slug = p.slug)
ORDER BY p.slug
""");
var rows = await conn.QueryAsync($"""
SELECT p.slug, p.visibility, v.ver, v.build_id, v.btcpay_min_ver, v.pre_release, v.updated_at, u."Email" as email
FROM plugins p
LEFT JOIN (
SELECT plugin_slug, MAX(ver) AS ver, build_id, btcpay_min_ver, pre_release, updated_at
FROM versions
GROUP BY plugin_slug, build_id, btcpay_min_ver, pre_release, updated_at
) v ON p.slug = v.plugin_slug
LEFT JOIN users_plugins up ON v.plugin_slug = up.plugin_slug
LEFT JOIN "AspNetUsers" u ON up.user_id = u."Id"
ORDER BY p.slug;
""");
var plugins = new List<AdminPluginViewModel>();
foreach (var row in rows)
{
var plugin = new AdminPluginViewModel
var plugin = new AdminPluginViewModel { ProjectSlug = row.slug, Visibility = row.visibility };

if (row.ver != null)
{
ProjectSlug = row.slug,
Version = string.Join('.', row.ver),
BuildId = row.build_id,
BtccpayMinVer = string.Join('.', row.btcpay_min_ver),
PreRelease = row.pre_release,
UpdatedAt = row.updated_at,
PublisherEmail = row.email
};
plugin.Version = string.Join('.', row.ver);
plugin.BuildId = row.build_id;
plugin.BtcPayMinVer = string.Join('.', row.btcpay_min_ver);
plugin.PreRelease = row.pre_release;
plugin.UpdatedAt = row.updated_at;
plugin.PublisherEmail = row.email;
}

plugins.Add(plugin);
}

return View(plugins);




}

// Plugin Edit
[HttpGet("plugins/edit/{slug}")]
public async Task<IActionResult> PluginEdit(string slug)
{
await using var conn = await _connectionFactory.Open();
var plugin = await conn.QueryFirstOrDefaultAsync<PluginViewModel>(
"SELECT * FROM plugins WHERE slug = @Slug", new { Slug = slug });
if (plugin == null)
{
return NotFound();
}

return View(plugin);
}

//
[HttpPost("plugins/edit/{slug}")]
public async Task<IActionResult> PluginEdit(string slug, PluginViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}

await using var conn = await _connectionFactory.Open();
var affectedRows = await conn.ExecuteAsync(
$"""
UPDATE plugins
SET settings = @settings::JSONB, visibility = @visibility::plugin_visibility_enum
WHERE slug = @slug
""",
new
{
settings = model.Settings,
visibility = model.Visibility.ToString().ToLowerInvariant(),
slug
});
if (affectedRows == 0)
{
return NotFound();
}

return RedirectToAction("ListPlugins");
}

// Plugin Delete
[HttpGet("plugins/delete/{slug}")]
public async Task<IActionResult> PluginDelete(string slug)
{
await using var conn = await _connectionFactory.Open();
var plugin = await conn.QueryFirstOrDefaultAsync<PluginViewModel>(
"SELECT * FROM plugins WHERE slug = @Slug", new { Slug = slug });
if (plugin == null)
{
return NotFound();
}

return View(plugin);
}

[HttpPost("plugins/delete/{slug}")]
public async Task<IActionResult> PluginDeleteConfirmed(string slug)
{
await using var conn = await _connectionFactory.Open();
var affectedRows = await conn.ExecuteAsync(
$"""
DELETE FROM builds WHERE plugin_slug = @Slug;
DELETE FROM builds_ids WHERE plugin_slug = @Slug;
DELETE FROM builds_logs WHERE plugin_slug = @Slug;
DELETE FROM users_plugins WHERE plugin_slug = @Slug;
DELETE FROM versions WHERE plugin_slug = @Slug;
DELETE FROM plugins WHERE slug = @Slug;
""", new { Slug = slug });
if (affectedRows == 0)
{
return NotFound();
}

return RedirectToAction("ListPlugins");
}

// list users
Expand Down
13 changes: 8 additions & 5 deletions PluginBuilder/Controllers/ApiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,14 @@ public async Task<IActionResult> Plugins(
await using var conn = await ConnectionFactory.Open();
// This query probably doesn't have right indexes
var rows = await conn.QueryAsync<(string plugin_slug, int[] ver, string settings, long id, string manifest_info, string build_info)>(
$"SELECT lv.plugin_slug, lv.ver, p.settings, b.id, b.manifest_info, b.build_info FROM {getVersions}(@btcpayVersion, @includePreRelease) lv " +
"JOIN builds b ON b.plugin_slug = lv.plugin_slug AND b.id = lv.build_id " +
"JOIN plugins p ON b.plugin_slug = p.slug " +
"WHERE b.manifest_info IS NOT NULL AND b.build_info IS NOT NULL " +
"ORDER BY manifest_info->>'Name'",
$"""
SELECT lv.plugin_slug, lv.ver, p.settings, b.id, b.manifest_info, b.build_info
FROM {getVersions}(@btcpayVersion, @includePreRelease) lv
JOIN builds b ON b.plugin_slug = lv.plugin_slug AND b.id = lv.build_id
JOIN plugins p ON b.plugin_slug = p.slug
WHERE b.manifest_info IS NOT NULL AND b.build_info IS NOT NULL AND (p.visibility = 'unlisted' OR p.visibility = 'listed')
ORDER BY manifest_info->>'Name'
""",
new
{
btcpayVersion = btcpayVersion?.VersionParts,
Expand Down
3 changes: 3 additions & 0 deletions PluginBuilder/Data/Scripts/10.PluginVisibility.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CREATE TYPE plugin_visibility_enum AS ENUM ('hidden', 'unlisted', 'listed');
ALTER TABLE public.plugins
ADD COLUMN visibility plugin_visibility_enum NOT NULL DEFAULT 'unlisted';
8 changes: 8 additions & 0 deletions PluginBuilder/DataModels/PluginVisibilityEnum.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace PluginBuilder.DataModels;

public enum PluginVisibilityEnum
{
Hidden = 0,
Unlisted = 1,
Listed = 2
}
4 changes: 4 additions & 0 deletions PluginBuilder/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Npgsql;
using PluginBuilder.Authentication;
using PluginBuilder.DataModels;
using PluginBuilder.HostedServices;
using PluginBuilder.Services;

Expand Down Expand Up @@ -101,6 +103,8 @@ public void AddServices(IConfiguration configuration, IServiceCollection service
{
b.UseNpgsql(configuration.GetRequired("POSTGRES"));
});
NpgsqlConnection.GlobalTypeMapper.MapEnum<PluginVisibilityEnum>("plugin_visibility_enum");

services.AddIdentity<IdentityUser, IdentityRole>(options =>
{
options.Password.RequireDigit = false;
Expand Down
9 changes: 6 additions & 3 deletions PluginBuilder/ViewModels/Admin/AdminPluginViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using PluginBuilder.DataModels;

namespace PluginBuilder.ViewModels.Admin;

public class AdminPluginViewModel
{
public string ProjectSlug { get; set; }
public string Version { get; set; }
public long BuildId { get; set; }
public string BtccpayMinVer { get; set; }
public string? Version { get; set; }
public long? BuildId { get; set; }
public string? BtcPayMinVer { get; set; }
public bool PreRelease { get; set; }
public DateTime UpdatedAt { get; set; }
public string PublisherEmail { get; set; }
public PluginVisibilityEnum Visibility { get; set; }
}
11 changes: 11 additions & 0 deletions PluginBuilder/ViewModels/Admin/PluginEditViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using PluginBuilder.DataModels;

namespace PluginBuilder.ViewModels.Admin;

public class PluginViewModel
{
public string Slug { get; set; }
public string Identifier { get; set; }
public string Settings { get; set; }
public PluginVisibilityEnum Visibility { get; set; }
}
11 changes: 9 additions & 2 deletions PluginBuilder/Views/Admin/ListPlugins.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ ViewData.SetActivePage(AdminNavPages.Plugins, "Plugins");
}

<div class="d-sm-flex align-items-center justify-content-between">
<h2 class="mb-0">
<h2 class="mb-4">
@ViewData["Title"]
</h2>
</div>
Expand All @@ -20,6 +20,8 @@ ViewData.SetActivePage(AdminNavPages.Plugins, "Plugins");
<th>Pre-Release</th>
<th>Updated At</th>
<th>Publisher Email</th>
<th>Visibility</th>
<th></th>
</tr>
</thead>
<tbody>
Expand All @@ -29,10 +31,15 @@ ViewData.SetActivePage(AdminNavPages.Plugins, "Plugins");
<td>@plugin.ProjectSlug</td>
<td>@plugin.Version</td>
<td>@plugin.BuildId</td>
<td>@plugin.BtccpayMinVer</td>
<td>@plugin.BtcPayMinVer</td>
<td>@plugin.PreRelease</td>
<td>@plugin.UpdatedAt</td>
<td>@plugin.PublisherEmail</td>
<td>@plugin.Visibility</td>
<td>
<a asp-controller="Admin" asp-action="PluginEdit" asp-route-slug="@plugin.ProjectSlug">Edit</a> |
<a asp-controller="Admin" asp-action="PluginDelete" asp-route-slug="@plugin.ProjectSlug">Delete</a>
</td>
</tr>
}
</tbody>
Expand Down
30 changes: 30 additions & 0 deletions PluginBuilder/Views/Admin/PluginDelete.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using PluginBuilder.DataModels
@model PluginViewModel
@{
Layout = "_Layout";
ViewData.SetActivePage(AdminNavPages.Plugins, "Are you sure you want to delete this Plugin?");
}

<div class="d-sm-flex align-items-center justify-content-between">
<h2 class="mb-4">
@ViewData["Title"]
</h2>
</div>

<form asp-action="Delete">
<div class="form-group">
<label asp-for="Slug" class="control-label"></label>
<input asp-for="Slug" class="form-control" readonly="readonly" />
<span asp-validation-for="Slug" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Identifier" class="control-label"></label>
<input asp-for="Identifier" class="form-control" readonly="readonly" />
<span asp-validation-for="Identifier" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" id="Delete" value="Delete" />
<a asp-action="ListPlugins" class="btn btn-link">Cancel</a>
</div>
</form>
40 changes: 40 additions & 0 deletions PluginBuilder/Views/Admin/PluginEdit.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using PluginBuilder.DataModels
@model PluginViewModel
@{
Layout = "_Layout";
ViewData.SetActivePage(AdminNavPages.Plugins, "Edit Plugin");
}

<div class="d-sm-flex align-items-center justify-content-between">
<h2 class="mb-0">
@ViewData["Title"]
</h2>
</div>

<form asp-action="Edit">
<div class="form-group">
<label asp-for="Slug" class="control-label"></label>
<input asp-for="Slug" class="form-control" readonly="readonly" />
<span asp-validation-for="Slug" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Identifier" class="control-label"></label>
<input asp-for="Identifier" class="form-control" readonly="readonly" />
<span asp-validation-for="Identifier" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Settings" class="control-label"></label>
<textarea asp-for="Settings" class="form-control" rows="5"></textarea>
<span asp-validation-for="Settings" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Visibility" class="control-label"></label>
<select asp-for="Visibility" class="form-control" asp-items="Html.GetEnumSelectList<PluginVisibilityEnum>()"></select>
<span asp-validation-for="Visibility" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" id="Save" value="Save" />
<a asp-action="ListPlugins" class="btn btn-link">Cancel</a>
</div>
</form>

0 comments on commit 516985b

Please sign in to comment.