Implemented a basic plugin system

This commit is contained in:
Marcel Baumgartner
2023-07-22 23:44:45 +02:00
parent 512a989609
commit 21bea974a9
15 changed files with 412 additions and 309 deletions

View File

@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Components;
namespace Moonlight.App.Helpers;
public class ComponentHelper
{
public static RenderFragment FromType(Type type) => builder =>
{
builder.OpenComponent(0, type);
builder.CloseComponent();
};
}

View File

@@ -0,0 +1,15 @@
using Moonlight.App.Plugin.UI;
using Moonlight.App.Plugin.UI.Servers;
using Moonlight.App.Plugin.UI.Webspaces;
namespace Moonlight.App.Plugin;
public abstract class MoonlightPlugin
{
public string Name { get; set; } = "N/A";
public string Author { get; set; } = "N/A";
public string Version { get; set; } = "N/A";
public Func<ServerPageContext, Task>? OnBuildServerPage { get; set; }
public Func<WebspacePageContext, Task>? OnBuildWebspacePage { get; set; }
}

View File

@@ -0,0 +1,11 @@
using Moonlight.App.Database.Entities;
namespace Moonlight.App.Plugin.UI.Servers;
public class ServerPageContext
{
public List<ServerTab> Tabs { get; set; } = new();
public List<ServerSetting> Settings { get; set; } = new();
public Server Server { get; set; }
public User User { get; set; }
}

View File

@@ -0,0 +1,9 @@
using Microsoft.AspNetCore.Components;
namespace Moonlight.App.Plugin.UI.Servers;
public class ServerSetting
{
public string Name { get; set; }
public RenderFragment Component { get; set; }
}

View File

@@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Components;
namespace Moonlight.App.Plugin.UI.Servers;
public class ServerTab
{
public string Name { get; set; }
public string Route { get; set; }
public string Icon { get; set; }
public RenderFragment Component { get; set; }
}

View File

@@ -0,0 +1,10 @@
using Moonlight.App.Database.Entities;
namespace Moonlight.App.Plugin.UI.Webspaces;
public class WebspacePageContext
{
public List<WebspaceTab> Tabs { get; set; } = new();
public User User { get; set; }
public WebSpace WebSpace { get; set; }
}

View File

@@ -0,0 +1,10 @@
using Microsoft.AspNetCore.Components;
namespace Moonlight.App.Plugin.UI.Webspaces;
public class WebspaceTab
{
public string Name { get; set; } = "N/A";
public string Route { get; set; } = "/";
public RenderFragment Component { get; set; }
}

View File

@@ -0,0 +1,69 @@
using System.Reflection;
using Moonlight.App.Database.Entities;
using Moonlight.App.Helpers;
using Moonlight.App.Plugin;
using Moonlight.App.Plugin.UI;
using Moonlight.App.Plugin.UI.Servers;
using Moonlight.App.Plugin.UI.Webspaces;
namespace Moonlight.App.Services;
public class PluginService
{
public List<MoonlightPlugin> Plugins { get; set; }
public PluginService()
{
LoadPlugins();
}
private void LoadPlugins()
{
Plugins = new();
var pluginType = typeof(MoonlightPlugin);
foreach (var pluginFile in Directory.EnumerateFiles(
PathBuilder.Dir(Directory.GetCurrentDirectory(), "storage", "plugins"))
.Where(x => x.EndsWith(".dll")))
{
var assembly = Assembly.LoadFile(pluginFile);
foreach (var type in assembly.GetTypes())
{
if (type.IsSubclassOf(pluginType))
{
var plugin = (Activator.CreateInstance(type) as MoonlightPlugin)!;
Logger.Info($"Loaded plugin '{plugin.Name}' ({plugin.Version}) by {plugin.Author}");
Plugins.Add(plugin);
}
}
}
Logger.Info($"Loaded {Plugins.Count} plugins");
}
public async Task<ServerPageContext> BuildServerPage(ServerPageContext context)
{
foreach (var plugin in Plugins)
{
if (plugin.OnBuildServerPage != null)
await plugin.OnBuildServerPage.Invoke(context);
}
return context;
}
public async Task<WebspacePageContext> BuildWebspacePage(WebspacePageContext context)
{
foreach (var plugin in Plugins)
{
if (plugin.OnBuildWebspacePage != null)
await plugin.OnBuildWebspacePage.Invoke(context);
}
return context;
}
}

View File

@@ -90,6 +90,7 @@
<Folder Include="App\ApiClients\CloudPanel\Resources\" />
<Folder Include="App\Http\Middleware" />
<Folder Include="storage\backups\" />
<Folder Include="storage\plugins\" />
<Folder Include="storage\resources\public\background\" />
</ItemGroup>

View File

@@ -239,6 +239,7 @@ namespace Moonlight
builder.Services.AddSingleton<MalwareScanService>();
builder.Services.AddSingleton<TelemetryService>();
builder.Services.AddSingleton<TempMailService>();
builder.Services.AddSingleton<PluginService>();
// Other
builder.Services.AddSingleton<MoonlightService>();
@@ -289,6 +290,7 @@ namespace Moonlight
_ = app.Services.GetRequiredService<MalwareScanService>();
_ = app.Services.GetRequiredService<TelemetryService>();
_ = app.Services.GetRequiredService<TempMailService>();
_ = app.Services.GetRequiredService<PluginService>();
_ = app.Services.GetRequiredService<MoonlightService>();

View File

@@ -1,4 +1,5 @@
@using Moonlight.App.Database.Entities
@using Moonlight.App.Plugin.UI.Webspaces
@using Moonlight.App.Services
@using Moonlight.App.Services.Interop
@@ -34,26 +35,14 @@
<div class="card mb-xl-10 mb-5">
<div class="card-body pt-0 pb-0">
<ul class="nav nav-stretch nav-line-tabs nav-line-tabs-2x border-transparent fs-5 fw-bold">
@foreach (var tab in Context.Tabs)
{
<li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 0 ? "active" : "")" href="/webspace/@(WebSpace.Id)">
<TL>Dashboard</TL>
</a>
</li>
<li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/webspace/@(WebSpace.Id)/files">
<TL>Files</TL>
</a>
</li>
<li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 2 ? "active" : "")" href="/webspace/@(WebSpace.Id)/sftp">
<TL>Sftp</TL>
</a>
</li>
<li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 3 ? "active" : "")" href="/webspace/@(WebSpace.Id)/databases">
<TL>Databases</TL>
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Route == tab.Route ? "active" : "")" href="/webspace/@(WebSpace.Id + tab.Route)">
<TL>@(tab.Name)</TL>
</a>
</li>
}
</ul>
</div>
</div>
@@ -61,11 +50,14 @@
@code
{
[Parameter]
public int Index { get; set; }
public string Route { get; set; }
[Parameter]
public WebSpace WebSpace { get; set; }
[CascadingParameter]
public WebspacePageContext Context { get; set; }
private async Task Delete()
{
if (await AlertService.ConfirmMath())

View File

@@ -4,12 +4,16 @@
@using Microsoft.EntityFrameworkCore
@using Moonlight.App.Database.Entities
@using Moonlight.App.Events
@using Moonlight.App.Helpers
@using Moonlight.App.Helpers.Wings
@using Moonlight.App.Helpers.Wings.Enums
@using Moonlight.App.Plugin.UI
@using Moonlight.App.Plugin.UI.Servers
@using Moonlight.App.Repositories
@using Moonlight.App.Services
@using Moonlight.App.Services.Sessions
@using Moonlight.Shared.Components.Xterm
@using Moonlight.Shared.Views.Server.Settings
@using Newtonsoft.Json
@inject ImageRepository ImageRepository
@@ -21,10 +25,11 @@
@inject DynamicBackgroundService DynamicBackgroundService
@inject SmartTranslateService SmartTranslateService
@inject IdentityService IdentityService
@inject PluginService PluginService
@implements IDisposable
<LazyLoader Load="LoadData">
<LazyLoader Load="Load">
@if (CurrentServer == null)
{
<NotFoundAlert/>
@@ -33,7 +38,7 @@
{
if (NodeOnline)
{
if (Console.ConsoleState == ConsoleState.Connected)
if (Console != null && Console.ConsoleState == ConsoleState.Connected)
{
if (Console.ServerState == ServerState.Installing || CurrentServer.Installing)
{
@@ -75,47 +80,36 @@
<CascadingValue Value="Console">
<CascadingValue Value="CurrentServer">
<CascadingValue Value="Tags">
<CascadingValue Value="Context">
<SmartRouter Route="@Route">
<Route Path="/">
<ServerNavigation Index="0">
<ServerConsole/>
</ServerNavigation>
</Route>
<Route Path="/files">
<ServerNavigation Index="1">
<ServerFiles/>
</ServerNavigation>
</Route>
<Route Path="/backups">
<ServerNavigation Index="2">
<ServerBackups/>
</ServerNavigation>
</Route>
<Route Path="/network">
<ServerNavigation Index="3">
<ServerNetwork/>
</ServerNavigation>
</Route>
<Route Path="/addons">
<ServerNavigation Index="4">
<ServerAddons/>
</ServerNavigation>
</Route>
<Route Path="/settings">
<ServerNavigation Index="5">
<ServerSettings/>
@foreach (var tab in Context.Tabs)
{
<Route Path="@(tab.Route)">
<ServerNavigation Route="@(tab.Route)">
@(tab.Component)
</ServerNavigation>
</Route>
}
</SmartRouter>
</CascadingValue>
</CascadingValue>
</CascadingValue>
</CascadingValue>
}
}
else
{
<div class="alert alert-info">
<div class="d-flex justify-content-center flex-center">
<div class="card">
<div class="card-body text-center">
<h1 class="card-title">
<TL>Connecting</TL>
</h1>
<p class="card-text fs-4">
<TL>Connecting to the servers console to stream logs and the current resource usage</TL>
</p>
</div>
</div>
</div>
}
}
@@ -144,21 +138,19 @@
[Parameter]
public string ServerUuid { get; set; }
[Parameter]
public string? Route { get; set; }
private WingsConsole? Console;
private Server? CurrentServer;
private Node Node;
private bool NodeOnline = false;
private Image Image;
private NodeAllocation NodeAllocation;
private string[] Tags;
private Terminal? InstallConsole;
private ServerPageContext Context;
protected override void OnInitialized()
{
Console = new();
@@ -171,68 +163,61 @@
Console.OnMessage += async (_, s) =>
{
if (Console.ServerState == ServerState.Installing)
{
if (InstallConsole != null)
if (Console.ServerState == ServerState.Installing && InstallConsole != null)
{
if (s.IsInternal)
await InstallConsole.WriteLine("\x1b[38;5;16;48;5;135m\x1b[39m\x1b[1m Moonlight \x1b[0m " + s.Content + "\x1b[0m");
else
await InstallConsole.WriteLine(s.Content);
}
}
};
}
private async Task LoadData(LazyLoader lazyLoader)
private async Task Load(LazyLoader lazyLoader)
{
await lazyLoader.SetText("Requesting server data");
try
{
var uuid = Guid.Parse(ServerUuid);
if (!Guid.TryParse(ServerUuid, out var uuid))
return;
CurrentServer = ServerRepository
.Get()
.Include(x => x.Allocations)
.Include(x => x.Image)
.Include("Image.Variables")
.Include(x => x.Node)
.Include(x => x.Variables)
.Include(x => x.MainAllocation)
.Include(x => x.Owner)
.First(x => x.Uuid == uuid);
}
catch (Exception)
{
// ignored
}
if (CurrentServer != null)
{
if (CurrentServer.Owner.Id != IdentityService.User.Id && !IdentityService.User.Admin)
{
CurrentServer = null;
return;
}
if (CurrentServer != null)
{
if (string.IsNullOrEmpty(CurrentServer.Image.BackgroundImageUrl))
await DynamicBackgroundService.Reset();
else
await DynamicBackgroundService.Change(CurrentServer.Image.BackgroundImageUrl);
await lazyLoader.SetText("Checking node online status");
NodeOnline = await ServerService.IsHostUp(CurrentServer);
if (NodeOnline)
{
await lazyLoader.SetText("Checking server variables");
if (!NodeOnline)
return;
var image = ImageRepository
.Get()
.Include(x => x.Variables)
.First(x => x.Id == CurrentServer.Image.Id);
await lazyLoader.SetText("Checking server variables");
// Live variable migration
foreach (var variable in image.Variables)
foreach (var variable in CurrentServer.Image.Variables)
{
if (!CurrentServer.Variables.Any(x => x.Key == variable.Key))
if (CurrentServer.Variables.All(x => x.Key != variable.Key))
{
CurrentServer.Variables.Add(new ServerVariable()
{
@@ -246,15 +231,85 @@
// Tags
await lazyLoader.SetText("Requesting tags");
await lazyLoader.SetText("Reading tags");
Tags = JsonConvert.DeserializeObject<string[]>(image.TagsJson) ?? Array.Empty<string>();
Image = image;
Tags = JsonConvert.DeserializeObject<string[]>(CurrentServer.Image.TagsJson) ?? Array.Empty<string>();
// Build server pages and settings
Context = new ServerPageContext()
{
Server = CurrentServer,
User = IdentityService.User
};
Context.Tabs.Add(new()
{
Name = "Console",
Route = "/",
Icon = "terminal",
Component = ComponentHelper.FromType(typeof(ServerConsole))
});
Context.Tabs.Add(new()
{
Name = "Files",
Route = "/files",
Icon = "folder",
Component = ComponentHelper.FromType(typeof(ServerFiles))
});
Context.Tabs.Add(new()
{
Name = "Backups",
Route = "/backups",
Icon = "box",
Component = ComponentHelper.FromType(typeof(ServerBackups))
});
Context.Tabs.Add(new()
{
Name = "Network",
Route = "/network",
Icon = "wifi",
Component = ComponentHelper.FromType(typeof(ServerNetwork))
});
Context.Tabs.Add(new()
{
Name = "Settings",
Route = "/settings",
Icon = "cog",
Component = ComponentHelper.FromType(typeof(ServerSettings))
});
// Add default settings
Context.Settings.Add(new()
{
Name = "Rename",
Component = ComponentHelper.FromType(typeof(ServerRenameSetting))
});
Context.Settings.Add(new()
{
Name = "Reset",
Component = ComponentHelper.FromType(typeof(ServerResetSetting))
});
Context.Settings.Add(new()
{
Name = "Delete",
Component = ComponentHelper.FromType(typeof(ServerDeleteSetting))
});
Context = await PluginService.BuildServerPage(Context);
await lazyLoader.SetText("Connecting to console");
await ReconnectConsole();
// Register event system
await Event.On<Server>($"server.{CurrentServer.Uuid}.installComplete", this, server =>
{
NavigationManager.NavigateTo(NavigationManager.Uri, true);
@@ -268,12 +323,6 @@
return Task.CompletedTask;
});
if (string.IsNullOrEmpty(Image.BackgroundImageUrl))
await DynamicBackgroundService.Reset();
else
await DynamicBackgroundService.Change(Image.BackgroundImageUrl);
}
}
}

View File

@@ -3,6 +3,8 @@
@using Moonlight.App.Helpers
@using Moonlight.App.Helpers.Wings
@using Moonlight.App.Helpers.Wings.Enums
@using Moonlight.App.Plugin.UI
@using Moonlight.App.Plugin.UI.Servers
@using Moonlight.App.Services.Sessions
@inject SmartTranslateService TranslationService
@@ -122,66 +124,19 @@
<div class="mt-5 row">
<div class="d-flex flex-column flex-md-row card card-body p-5">
<ul class="nav nav-tabs nav-pills flex-row border-0 flex-md-column fs-6 pe-5 mb-5">
@foreach (var tab in Context.Tabs)
{
<li class="nav-item w-100 me-0 mb-md-2">
<a href="/server/@(CurrentServer.Uuid)/" class="nav-link w-100 btn btn-flex @(Index == 0 ? "active" : "") btn-active-light-primary">
<i class="bx bx-terminal bx-sm me-2"></i>
<a href="/server/@(CurrentServer.Uuid + tab.Route)" class="nav-link w-100 btn btn-flex @(Route == tab.Route ? "active" : "") btn-active-light-primary">
<i class="bx bx-@(tab.Icon) bx-sm me-2"></i>
<span class="d-flex flex-column align-items-start">
<span class="fs-5">
<TL>Console</TL>
</span>
</span>
</a>
</li>
<li class="nav-item w-100 me-0 mb-md-2">
<a href="/server/@(CurrentServer.Uuid)/files" class="nav-link w-100 btn btn-flex @(Index == 1 ? "active" : "") btn-active-light-primary">
<i class="bx bx-folder bx-sm me-2"></i>
<span class="d-flex flex-column align-items-start">
<span class="fs-5">
<TL>Files</TL>
</span>
</span>
</a>
</li>
<li class="nav-item w-100 me-0 mb-md-2">
<a href="/server/@(CurrentServer.Uuid)/backups" class="nav-link w-100 btn btn-flex @(Index == 2 ? "active" : "") btn-active-light-primary">
<i class="bx bx-box bx-sm me-2"></i>
<span class="d-flex flex-column align-items-start">
<span class="fs-5">
<TL>Backups</TL>
</span>
</span>
</a>
</li>
<li class="nav-item w-100 me-0 mb-md-2">
<a href="/server/@(CurrentServer.Uuid)/network" class="nav-link w-100 btn btn-flex @(Index == 3 ? "active" : "") btn-active-light-primary">
<i class="bx bx-wifi bx-sm me-2"></i>
<span class="d-flex flex-column align-items-start">
<span class="fs-5">
<TL>Network</TL>
</span>
</span>
</a>
</li>
<li class="nav-item w-100 me-0 mb-md-2">
<a href="/server/@(CurrentServer.Uuid)/addons" class="nav-link w-100 btn btn-flex @(Index == 4 ? "active" : "") btn-active-light-primary">
<i class="bx bx-plug bx-sm me-2"></i>
<span class="d-flex flex-column align-items-start">
<span class="fs-5">
<TL>Addons</TL>
</span>
</span>
</a>
</li>
<li class="nav-item w-100 me-0 mb-md-2">
<a href="/server/@(CurrentServer.Uuid)/settings" class="nav-link w-100 btn btn-flex @(Index == 5 ? "active" : "") btn-active-light-primary">
<i class="bx bx-cog bx-sm me-2"></i>
<span class="d-flex flex-column align-items-start">
<span class="fs-5">
<TL>Settings</TL>
<TL>@(tab.Name)</TL>
</span>
</span>
</a>
</li>
}
</ul>
<div class="tab-content w-100">
<div class="tab-pane fade show active">
@@ -198,16 +153,17 @@
[CascadingParameter]
public Server CurrentServer { get; set; }
[CascadingParameter]
public WingsConsole Console { get; set; }
[CascadingParameter]
public ServerPageContext Context { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public int Index { get; set; } = 0;
public string Route { get; set; } = "/";
//TODO: NodeIpService which loads and caches raw ips for nodes (maybe)

View File

@@ -1,91 +1,29 @@
@using Moonlight.App.Database.Entities
@using Moonlight.Shared.Views.Server.Settings
@using Microsoft.AspNetCore.Components.Rendering
@using Moonlight.App.Plugin.UI.Servers
<LazyLoader Load="Load">
<div class="row">
@foreach (var setting in Settings)
<div class="row">
@foreach (var setting in Context.Settings)
{
<div class="col-12 col-md-6 p-3">
<div class="accordion" id="serverSetting@(setting.GetHashCode())">
<div class="accordion-item">
<h2 class="accordion-header" id="serverSetting-header@(setting.GetHashCode())">
<button class="accordion-button fs-4 fw-semibold collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#serverSetting-body@(setting.GetHashCode())" aria-expanded="false" aria-controls="serverSetting-body@(setting.GetHashCode())">
<TL>@(setting.Key)</TL>
<TL>@(setting.Name)</TL>
</button>
</h2>
<div id="serverSetting-body@(setting.GetHashCode())" class="accordion-collapse collapse" aria-labelledby="serverSetting-header@(setting.GetHashCode())" data-bs-parent="#serverSetting">
<div class="accordion-body">
@(GetComponent(setting.Value))
@(setting.Component)
</div>
</div>
</div>
</div>
</div>
}
</div>
</LazyLoader>
</div>
@code
{
[CascadingParameter]
public Server CurrentServer { get; set; }
[CascadingParameter]
public string[] Tags { get; set; }
private Dictionary<string, Type> Settings = new();
private Task Load(LazyLoader lazyLoader)
{
if (Tags.Contains("paperversion"))
Settings.Add("Paper version", typeof(PaperVersionSetting));
if (Tags.Contains("forgeversion"))
Settings.Add("Forge version", typeof(ForgeVersionSetting));
if (Tags.Contains("fabricversion"))
Settings.Add("Fabric version", typeof(FabricVersionSetting));
if (Tags.Contains("join2start"))
Settings.Add("Join2Start", typeof(Join2StartSetting));
if (Tags.Contains("javascriptversion"))
Settings.Add("Javascript version", typeof(JavascriptVersionSetting));
if (Tags.Contains("javascriptfile"))
Settings.Add("Javascript file", typeof(JavascriptFileSetting));
if (Tags.Contains("pythonversion"))
Settings.Add("Python version", typeof(PythonVersionSetting));
if (Tags.Contains("javaversion"))
Settings.Add("Java version", typeof(JavaRuntimeVersionSetting));
if (Tags.Contains("dotnetversion"))
Settings.Add("Dotnet version", typeof(DotnetVersionSetting));
if (Tags.Contains("pythonfile"))
Settings.Add("Python file", typeof(PythonFileSetting));
if (Tags.Contains("javafile"))
Settings.Add("Jar file", typeof(JavaFileSetting));
if (Tags.Contains("dotnetfile"))
Settings.Add("Dll file", typeof(DotnetFileSetting));
Settings.Add("Rename", typeof(ServerRenameSetting));
Settings.Add("Reset", typeof(ServerResetSetting));
Settings.Add("Delete", typeof(ServerDeleteSetting));
return Task.CompletedTask;
}
private RenderFragment GetComponent(Type type) => builder =>
{
builder.OpenComponent(0, type);
builder.CloseComponent();
};
public ServerPageContext Context { get; set; }
}

View File

@@ -4,10 +4,13 @@
@using Moonlight.App.Services
@using Moonlight.Shared.Components.WebsiteControl
@using Microsoft.EntityFrameworkCore
@using Moonlight.App.Helpers
@using Moonlight.App.Plugin.UI.Webspaces
@using Moonlight.App.Services.Sessions
@inject Repository<WebSpace> WebSpaceRepository
@inject WebSpaceService WebSpaceService
@inject PluginService PluginService
@inject IdentityService IdentityService
<LazyLoader Load="Load">
@@ -32,43 +35,17 @@
if (HostOnline)
{
<CascadingValue Value="CurrentWebspace">
@{
var index = 0;
switch (Route)
<CascadingValue Value="Context">
<SmartRouter Route="@(Route)">
@foreach (var tab in Context.Tabs)
{
case "files":
index = 1;
break;
case "sftp":
index = 2;
break;
case "databases":
index = 3;
break;
default:
index = 0;
break;
}
<WebSpaceNavigation Index="index" WebSpace="CurrentWebspace" />
@switch (Route)
{
case "files":
<WebSpaceFiles />
break;
case "sftp":
<WebSpaceSftp />
break;
case "databases":
<WebSpaceDatabases />
break;
default:
<WebSpaceDashboard />
break;
}
<Route Path="@(tab.Route)">
<WebSpaceNavigation Route="@(tab.Route)" WebSpace="CurrentWebspace"/>
@(tab.Component)
</Route>
}
</SmartRouter>
</CascadingValue>
</CascadingValue>
}
else
@@ -101,6 +78,8 @@
private WebSpace? CurrentWebspace;
private bool HostOnline = false;
private WebspacePageContext Context;
private async Task Load(LazyLoader lazyLoader)
{
CurrentWebspace = WebSpaceRepository
@@ -112,14 +91,53 @@
if (CurrentWebspace != null)
{
if (CurrentWebspace.Owner.Id != IdentityService.User.Id && !IdentityService.User.Admin)
{
CurrentWebspace = null;
return;
}
if (CurrentWebspace != null)
{
await lazyLoader.SetText("Checking host system online status");
HostOnline = await WebSpaceService.IsHostUp(CurrentWebspace);
if (!HostOnline)
return;
Context = new WebspacePageContext()
{
WebSpace = CurrentWebspace,
User = IdentityService.User
};
Context.Tabs.Add(new()
{
Route = "/",
Name = "Dashboard",
Component = ComponentHelper.FromType(typeof(WebSpaceDashboard))
});
Context.Tabs.Add(new()
{
Route = "/files",
Name = "Files",
Component = ComponentHelper.FromType(typeof(WebSpaceFiles))
});
Context.Tabs.Add(new()
{
Route = "/sftp",
Name = "SFTP",
Component = ComponentHelper.FromType(typeof(WebSpaceSftp))
});
Context.Tabs.Add(new()
{
Route = "/databases",
Name = "Databases",
Component = ComponentHelper.FromType(typeof(WebSpaceDatabases))
});
Context = await PluginService.BuildWebspacePage(Context);
}
}
}