diff --git a/.gitignore b/.gitignore index b183cd2..9425640 100644 --- a/.gitignore +++ b/.gitignore @@ -301,7 +301,7 @@ node_modules/ *.dsw *.dsp -# Visual Studio 6 technical files +# Visual Studio 6 technical files *.ncb *.aps @@ -395,44 +395,15 @@ FodyWeavers.xsd *.msp # JetBrains Rider -*.sln.iml +**/.idea/** -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf +# Style builds +**/style.min.css +**/package-lock.json +**/bun.lock -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Moonlight -storage/ -.idea/**/.idea -MoonlightServers.min.css -core.min.css - -# Build script for nuget packages -finalPackages/ -nupkgs/ - -# Local daemon tests -**/data/volumes/** - -# Local plugin build -tmp/ -style.min.css \ No newline at end of file +# Secrets +**/.env +**/appsettings.json +**/appsettings.Development.json +**/storage diff --git a/Hosts/MoonlightServers.Api.Host/Api.props b/Hosts/MoonlightServers.Api.Host/Api.props new file mode 100644 index 0000000..f531296 --- /dev/null +++ b/Hosts/MoonlightServers.Api.Host/Api.props @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Hosts/MoonlightServers.Api.Host/MoonlightServers.Api.Host.csproj b/Hosts/MoonlightServers.Api.Host/MoonlightServers.Api.Host.csproj new file mode 100644 index 0000000..dafcba5 --- /dev/null +++ b/Hosts/MoonlightServers.Api.Host/MoonlightServers.Api.Host.csproj @@ -0,0 +1,29 @@ + + + + net10.0 + enable + enable + + + + + + + + all + runtime; build; native; analyzers; buildtransitive + + + + + + + + + + + + + + diff --git a/Hosts/MoonlightServers.Api.Host/Program.cs b/Hosts/MoonlightServers.Api.Host/Program.cs new file mode 100644 index 0000000..6a74836 --- /dev/null +++ b/Hosts/MoonlightServers.Api.Host/Program.cs @@ -0,0 +1,9 @@ +using Moonlight.Api; +using SimplePlugin.Generated; + +var plugins = PluginRegistry + .Modules + .OfType() + .ToArray(); + +await StartupHandler.RunAsync(args, plugins); \ No newline at end of file diff --git a/Hosts/MoonlightServers.Api.Host/Properties/launchSettings.json b/Hosts/MoonlightServers.Api.Host/Properties/launchSettings.json new file mode 100644 index 0000000..f2c4d7a --- /dev/null +++ b/Hosts/MoonlightServers.Api.Host/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5031", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7240;http://localhost:5031", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Hosts/MoonlightServers.Api.Host/appsettings.Example.json b/Hosts/MoonlightServers.Api.Host/appsettings.Example.json new file mode 100644 index 0000000..6a31ede --- /dev/null +++ b/Hosts/MoonlightServers.Api.Host/appsettings.Example.json @@ -0,0 +1,23 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "Moonlight": { + "Database": { + "Host": "your-db.host", + "Username": "change_me", + "Password": "change_me", + "Database": "change_me" + }, + "Oidc": { + "Authority": "http://localhost:8092", + "RequireHttpsMetadata": false, + "ClientId": "clientId", + "ClientSecret": "clientSecret" + } + } +} diff --git a/Hosts/MoonlightServers.Frontend.Host/Frontend.props b/Hosts/MoonlightServers.Frontend.Host/Frontend.props new file mode 100644 index 0000000..384e727 --- /dev/null +++ b/Hosts/MoonlightServers.Frontend.Host/Frontend.props @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Hosts/MoonlightServers.Frontend.Host/MoonlightServers.Frontend.Host.csproj b/Hosts/MoonlightServers.Frontend.Host/MoonlightServers.Frontend.Host.csproj new file mode 100644 index 0000000..9bc74c0 --- /dev/null +++ b/Hosts/MoonlightServers.Frontend.Host/MoonlightServers.Frontend.Host.csproj @@ -0,0 +1,22 @@ + + + + net10.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/Hosts/MoonlightServers.Frontend.Host/Program.cs b/Hosts/MoonlightServers.Frontend.Host/Program.cs new file mode 100644 index 0000000..eff3e47 --- /dev/null +++ b/Hosts/MoonlightServers.Frontend.Host/Program.cs @@ -0,0 +1,9 @@ +using Moonlight.Frontend; +using SimplePlugin.Generated; + +var plugins = PluginRegistry + .Modules + .OfType() + .ToArray(); + +await StartupHandler.RunAsync(args, plugins); \ No newline at end of file diff --git a/Hosts/MoonlightServers.Frontend.Host/Styles/extract-classes.mjs b/Hosts/MoonlightServers.Frontend.Host/Styles/extract-classes.mjs new file mode 100644 index 0000000..7350766 --- /dev/null +++ b/Hosts/MoonlightServers.Frontend.Host/Styles/extract-classes.mjs @@ -0,0 +1,25 @@ +import fs from 'fs'; +import selectorParser from 'postcss-selector-parser'; + +export default function extractTailwindClasses(opts = {}) { + const classSet = new Set(); + + return { + postcssPlugin: 'extract-tailwind-classes', + Rule(rule) { + selectorParser(selectors => { + selectors.walkClasses(node => { + classSet.add(node.value); + }); + }).processSync(rule.selector); + }, + OnceExit() { + const classArray = Array.from(classSet).sort(); + fs.mkdirSync('../../../Servers.Frontend/Styles', { recursive: true }); + fs.writeFileSync('../../../Servers.Frontend/Styles/Servers.Frontend.map', classArray.join('\n')); + console.log(`Extracted classes ${classArray.length}`); + } + }; +} + +extractTailwindClasses.postcss = true; \ No newline at end of file diff --git a/Hosts/MoonlightServers.Frontend.Host/Styles/package.json b/Hosts/MoonlightServers.Frontend.Host/Styles/package.json new file mode 100644 index 0000000..f1efab1 --- /dev/null +++ b/Hosts/MoonlightServers.Frontend.Host/Styles/package.json @@ -0,0 +1,19 @@ +{ + "scripts": { + "dev": "npx postcss styles.css -o ../wwwroot/style.min.css --watch", + "dev-build": "npx postcss styles.css -o ../wwwroot/style.min.css", + "build": "cross-env EXTRACT_CLASSES=true npx postcss styles.css -o ../wwwroot/style.min.css" + }, + "dependencies": { + "@tailwindcss/postcss": "^4.1.18", + "cssnano": "^7.1.2", + "postcss": "^8.5.6", + "postcss-cli": "^11.0.1", + "postcss-selector-parser": "^7.1.1", + "tailwindcss": "^4.1.18", + "tw-animate-css": "^1.4.0" + }, + "devDependencies": { + "cross-env": "^10.1.0" + } +} \ No newline at end of file diff --git a/Hosts/MoonlightServers.Frontend.Host/Styles/postcss.config.mjs b/Hosts/MoonlightServers.Frontend.Host/Styles/postcss.config.mjs new file mode 100644 index 0000000..3bc7e1b --- /dev/null +++ b/Hosts/MoonlightServers.Frontend.Host/Styles/postcss.config.mjs @@ -0,0 +1,18 @@ +import tailwindcss from '@tailwindcss/postcss'; +import cssnano from 'cssnano'; +import extractTailwindClasses from './extract-classes.mjs'; + +const plugins = [ + tailwindcss, + cssnano({ + preset: 'default' + }) +]; + +if (process.env.EXTRACT_CLASSES === "true") { + plugins.push(extractTailwindClasses()); +} + +export default { + plugins +}; \ No newline at end of file diff --git a/Hosts/MoonlightServers.Frontend.Host/Styles/styles.css b/Hosts/MoonlightServers.Frontend.Host/Styles/styles.css new file mode 100644 index 0000000..2074f76 --- /dev/null +++ b/Hosts/MoonlightServers.Frontend.Host/Styles/styles.css @@ -0,0 +1,31 @@ +@import "tailwindcss"; +@import "tw-animate-css"; + +@import "../bin/ShadcnBlazor/scrollbar.css"; +@import "../bin/ShadcnBlazor/default-theme.css"; +@import "./theme.css"; + +@source "../bin/Moonlight.Frontend/*.map"; + +@source "../../../Moonlight.Api/**/*.razor"; +@source "../../../Moonlight.Api/**/*.cs"; +@source "../../../Moonlight.Api/**/*.html"; + +@source "../../../Moonlight.Frontend/**/*.razor"; +@source "../../../Moonlight.Frontend/**/*.cs"; +@source "../../../Moonlight.Frontend/**/*.html"; + +@custom-variant dark (&:is(.dark *)); + +#blazor-error-ui { + display: none; +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/Hosts/MoonlightServers.Frontend.Host/Styles/theme.css b/Hosts/MoonlightServers.Frontend.Host/Styles/theme.css new file mode 100644 index 0000000..510373c --- /dev/null +++ b/Hosts/MoonlightServers.Frontend.Host/Styles/theme.css @@ -0,0 +1,132 @@ +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.129 0.042 264.695); + --card: oklch(1 0 0); + --card-foreground: oklch(0.129 0.042 264.695); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.129 0.042 264.695); + --primary: oklch(0.208 0.042 265.755); + --primary-foreground: oklch(0.984 0.003 247.858); + --secondary: oklch(0.968 0.007 247.896); + --secondary-foreground: oklch(0.208 0.042 265.755); + --muted: oklch(0.968 0.007 247.896); + --muted-foreground: oklch(0.554 0.046 257.417); + --accent: oklch(0.968 0.007 247.896); + --accent-foreground: oklch(0.208 0.042 265.755); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.929 0.013 255.508); + --input: oklch(0.929 0.013 255.508); + --ring: oklch(0.704 0.04 256.788); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + + --sidebar: var(--background); + --sidebar-foreground: var(--foreground); + --sidebar-primary: var(--primary); + --sidebar-primary-foreground: var(--primary-foreground); + --sidebar-accent: var(--accent); + --sidebar-accent-foreground: var(--accent-foreground); + --sidebar-border: var(--border); + --sidebar-ring: var(--ring); + + --font-sans: Inter, sans-serif; + --font-serif: Georgia, serif; +} + +.dark { + /* Deep blue-slate background with purple undertones */ + --background: oklch(0.16 0.028 260); + --foreground: oklch(0.98 0.008 260); + + /* Cards with slightly lighter blue-slate */ + --card: oklch(0.21 0.032 260); + --card-foreground: oklch(0.98 0.008 260); + + /* Popovers with medium depth */ + --popover: oklch(0.24 0.035 260); + --popover-foreground: oklch(0.98 0.008 260); + + /* Vibrant blue-purple primary */ + --primary: oklch(0.62 0.18 270); + --primary-foreground: oklch(0.99 0.005 260); + + /* Secondary with blue-slate tone */ + --secondary: oklch(0.27 0.038 260); + --secondary-foreground: oklch(0.98 0.008 260); + + /* Muted elements */ + --muted: oklch(0.25 0.035 260); + --muted-foreground: oklch(0.66 0.025 260); + + /* Accent with purple-blue blend */ + --accent: oklch(0.36 0.065 268); + --accent-foreground: oklch(0.98 0.008 260); + + /* Destructive red with good contrast */ + --destructive: oklch(0.62 0.22 25); + --destructive-foreground: oklch(0.99 0.005 260); + + /* Subtle borders and inputs */ + --border: oklch(0.32 0.025 260); + --input: oklch(0.30 0.030 260); + --ring: oklch(0.62 0.18 270); + + /* Chart colors with blue-purple harmony */ + --chart-1: oklch(0.58 0.18 270); + --chart-2: oklch(0.62 0.16 245); + --chart-3: oklch(0.68 0.15 290); + --chart-4: oklch(0.60 0.20 260); + --chart-5: oklch(0.65 0.14 280); + + /* Sidebar with slightly different depth */ + --sidebar: oklch(0.18 0.030 260); + --sidebar-foreground: oklch(0.97 0.008 260); + --sidebar-primary: oklch(0.60 0.17 270); + --sidebar-primary-foreground: oklch(0.99 0.005 260); + --sidebar-accent: oklch(0.26 0.038 260); + --sidebar-accent-foreground: oklch(0.98 0.008 260); + --sidebar-border: oklch(0.30 0.025 260); + --sidebar-ring: oklch(0.58 0.17 270); +} + +@theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} \ No newline at end of file diff --git a/Hosts/MoonlightServers.Frontend.Host/wwwroot/index.html b/Hosts/MoonlightServers.Frontend.Host/wwwroot/index.html new file mode 100644 index 0000000..dfb9733 --- /dev/null +++ b/Hosts/MoonlightServers.Frontend.Host/wwwroot/index.html @@ -0,0 +1,108 @@ + + + + + + + Moonlight + + + + + + + + + + +
+
+ +
+
+
+ + + + + +
+
+
+ Loading application +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ An unhandled error has occurred. + Reload + 🗙 +
+ + + + + + + + diff --git a/MoonlightServers.Api/Http/Controllers/FormController.cs b/MoonlightServers.Api/Http/Controllers/FormController.cs new file mode 100644 index 0000000..03c765c --- /dev/null +++ b/MoonlightServers.Api/Http/Controllers/FormController.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using MoonlightServers.Shared.Http.Requests; +using MoonlightServers.Shared.Http.Responses; + +namespace MoonlightServers.Api.Http.Controllers; + +[Authorize] +[ApiController] +[Route("api/form")] +public class FormController : Controller +{ + [HttpPost] + public async Task> PostAsync([FromBody] FormSubmitDto dto) + { + return new FormResultDto() + { + Result = dto.TextField.Replace(" ", string.Empty) + }; + } +} \ No newline at end of file diff --git a/MoonlightServers.Api/MoonlightServers.Api.csproj b/MoonlightServers.Api/MoonlightServers.Api.csproj new file mode 100644 index 0000000..578ffbd --- /dev/null +++ b/MoonlightServers.Api/MoonlightServers.Api.csproj @@ -0,0 +1,36 @@ + + + + net10.0 + enable + enable + + + + + + + + + + content;contentfiles + + + + + + + + + + + + + + + + + + + + diff --git a/MoonlightServers.Api/Startup.cs b/MoonlightServers.Api/Startup.cs new file mode 100644 index 0000000..f3b2532 --- /dev/null +++ b/MoonlightServers.Api/Startup.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Moonlight.Api; +using SimplePlugin.Abstractions; +using SerializationContext = MoonlightServers.Shared.SerializationContext; + +namespace MoonlightServers.Api; + +[PluginModule] +public class Startup : MoonlightPlugin +{ + public override void PreBuild(WebApplicationBuilder builder) + { + builder.Services.AddControllers() + .AddApplicationPart(typeof(Startup).Assembly) + .AddJsonOptions(options => + { + options.JsonSerializerOptions.TypeInfoResolverChain.Add(SerializationContext.Default); + }); + } +} \ No newline at end of file diff --git a/MoonlightServers.ApiServer.Runtime/DevPluginLoader.cs b/MoonlightServers.ApiServer.Runtime/DevPluginLoader.cs deleted file mode 100644 index 61a7b17..0000000 --- a/MoonlightServers.ApiServer.Runtime/DevPluginLoader.cs +++ /dev/null @@ -1,10 +0,0 @@ -using MoonCore.PluginFramework; -using Moonlight.ApiServer.Plugins; - -namespace MoonlightServers.ApiServer.Runtime; - -[PluginLoader] -public partial class DevPluginLoader : IPluginStartup -{ - -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer.Runtime/MoonlightServers.ApiServer.Runtime.csproj b/MoonlightServers.ApiServer.Runtime/MoonlightServers.ApiServer.Runtime.csproj deleted file mode 100644 index aeea9e2..0000000 --- a/MoonlightServers.ApiServer.Runtime/MoonlightServers.ApiServer.Runtime.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - net9.0 - enable - enable - True - - - - - - - - - - - - - - - - Never - - - - - - - - diff --git a/MoonlightServers.ApiServer.Runtime/Program.cs b/MoonlightServers.ApiServer.Runtime/Program.cs deleted file mode 100644 index 7de0a11..0000000 --- a/MoonlightServers.ApiServer.Runtime/Program.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Moonlight.ApiServer.Configuration; -using Moonlight.ApiServer.Startup; -using MoonlightServers.ApiServer.Runtime; - -var pluginLoader = new DevPluginLoader(); -pluginLoader.Initialize(); - -var builder = WebApplication.CreateBuilder(args); - -builder.AddMoonlight(pluginLoader.Instances); - -var app = builder.Build(); - -app.UseMoonlight(pluginLoader.Instances); - -// Add frontend -var configuration = AppConfiguration.CreateEmpty(); -builder.Configuration.Bind(configuration); - -// Handle setup of wasm app hosting in the runtime -// so the Moonlight.ApiServer doesn't need the wasm package -if (configuration.Frontend.EnableHosting) -{ - if (app.Environment.IsDevelopment()) - app.UseWebAssemblyDebugging(); - - app.UseBlazorFrameworkFiles(); - app.UseStaticFiles(); -} - -app.MapMoonlight(pluginLoader.Instances); - - -await app.RunAsync(); \ No newline at end of file diff --git a/MoonlightServers.ApiServer.Runtime/Properties/launchSettings.json b/MoonlightServers.ApiServer.Runtime/Properties/launchSettings.json deleted file mode 100644 index 41ffc3d..0000000 --- a/MoonlightServers.ApiServer.Runtime/Properties/launchSettings.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", - "profiles": { - "Dev Server": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": false, - "launchUrl": "swagger", - "applicationUrl": "http://localhost:5269", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "MOONLIGHT_PUBLICURL": "http://localhost:5269", - "HTTP_PROXY": "", - "HTTPS_PROXY": "" - } - } - } -} diff --git a/MoonlightServers.ApiServer/Database/Entities/Allocation.cs b/MoonlightServers.ApiServer/Database/Entities/Allocation.cs deleted file mode 100644 index 7ec1b38..0000000 --- a/MoonlightServers.ApiServer/Database/Entities/Allocation.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace MoonlightServers.ApiServer.Database.Entities; - -public class Allocation -{ - public int Id { get; set; } - - // Relations - public Node Node { get; set; } - public Server? Server { get; set; } - - public string IpAddress { get; set; } - public int Port { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Database/Entities/Node.cs b/MoonlightServers.ApiServer/Database/Entities/Node.cs deleted file mode 100644 index c53f578..0000000 --- a/MoonlightServers.ApiServer/Database/Entities/Node.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace MoonlightServers.ApiServer.Database.Entities; - -public class Node -{ - public int Id { get; set; } - - // Relations - public List Servers { get; set; } = new(); - public List Allocations { get; set; } = new(); - - // Meta - public string Name { get; set; } - - // Connection details - public string Fqdn { get; set; } - public string Token { get; set; } - public string TokenId { get; set; } - public int HttpPort { get; set; } - public int FtpPort { get; set; } - public bool UseSsl { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Database/Entities/Server.cs b/MoonlightServers.ApiServer/Database/Entities/Server.cs deleted file mode 100644 index f7a6b34..0000000 --- a/MoonlightServers.ApiServer/Database/Entities/Server.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace MoonlightServers.ApiServer.Database.Entities; - -public class Server -{ - public int Id { get; set; } - - // Relations - public Star Star { get; set; } - public Node Node { get; set; } - public List Allocations { get; set; } = new(); - public List Variables { get; set; } = new(); - public List Backups { get; set; } = new(); - public List Shares { get; set; } = new(); - - // Meta - public string Name { get; set; } - public int OwnerId { get; set; } - - // Star specific stuff - public string? StartupOverride { get; set; } - public int DockerImageIndex { get; set; } - - // Resources and limits - public int Cpu { get; set; } - public int Memory { get; set; } - public int Disk { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Database/Entities/ServerBackup.cs b/MoonlightServers.ApiServer/Database/Entities/ServerBackup.cs deleted file mode 100644 index 361d152..0000000 --- a/MoonlightServers.ApiServer/Database/Entities/ServerBackup.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace MoonlightServers.ApiServer.Database.Entities; - -public class ServerBackup -{ - public int Id { get; set; } - - public DateTime CreatedAt { get; set; } - public DateTime CompletedAt { get; set; } - - public long Size { get; set; } - public bool Successful { get; set; } - public bool Completed { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Database/Entities/ServerShare.cs b/MoonlightServers.ApiServer/Database/Entities/ServerShare.cs deleted file mode 100644 index 02636cf..0000000 --- a/MoonlightServers.ApiServer/Database/Entities/ServerShare.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.ComponentModel.DataAnnotations.Schema; -using MoonlightServers.ApiServer.Models; - -namespace MoonlightServers.ApiServer.Database.Entities; - -public class ServerShare -{ - public int Id { get; set; } - - public int UserId { get; set; } - public Server Server { get; set; } - - public ServerShareContent Content { get; set; } = new(); - - [Column(TypeName="timestamp with time zone")] - public DateTime CreatedAt { get; set; } - - [Column(TypeName="timestamp with time zone")] - public DateTime UpdatedAt { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Database/Entities/ServerVariable.cs b/MoonlightServers.ApiServer/Database/Entities/ServerVariable.cs deleted file mode 100644 index 96cf5f9..0000000 --- a/MoonlightServers.ApiServer/Database/Entities/ServerVariable.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace MoonlightServers.ApiServer.Database.Entities; - -public class ServerVariable -{ - public int Id { get; set; } - - // Relations - public Server Server { get; set; } - - public string Key { get; set; } - public string Value { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Database/Entities/Star.cs b/MoonlightServers.ApiServer/Database/Entities/Star.cs deleted file mode 100644 index 927d2ed..0000000 --- a/MoonlightServers.ApiServer/Database/Entities/Star.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace MoonlightServers.ApiServer.Database.Entities; - -public class Star -{ - public int Id { get; set; } - - // References - public List Variables { get; set; } = new(); - public List DockerImages { get; set; } = new(); - - // Meta - public string Name { get; set; } - public string Version { get; set; } - public string Author { get; set; } - public string? UpdateUrl { get; set; } - public string? DonateUrl { get; set; } - - // Start and stop - public string StartupCommand { get; set; } - public string StopCommand { get; set; } - public string OnlineDetection { get; set; } - - // Install - public string InstallShell { get; set; } - public string InstallDockerImage { get; set; } - public string InstallScript { get; set; } - - // Misc - public int RequiredAllocations { get; set; } - public bool AllowDockerImageChange { get; set; } - public int DefaultDockerImage { get; set; } - public string ParseConfiguration { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Database/Entities/StarDockerImage.cs b/MoonlightServers.ApiServer/Database/Entities/StarDockerImage.cs deleted file mode 100644 index 6d8bc2e..0000000 --- a/MoonlightServers.ApiServer/Database/Entities/StarDockerImage.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MoonlightServers.ApiServer.Database.Entities; - -public class StarDockerImage -{ - public int Id { get; set; } - public Star Star { get; set; } - - public string DisplayName { get; set; } - public string Identifier { get; set; } - public bool AutoPulling { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Database/Entities/StarVariable.cs b/MoonlightServers.ApiServer/Database/Entities/StarVariable.cs deleted file mode 100644 index 70552ef..0000000 --- a/MoonlightServers.ApiServer/Database/Entities/StarVariable.cs +++ /dev/null @@ -1,21 +0,0 @@ -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.ApiServer.Database.Entities; - -public class StarVariable -{ - public int Id { get; set; } - public Star Star { get; set; } - - public string Name { get; set; } - public string Description { get; set; } - - public string Key { get; set; } - public string DefaultValue { get; set; } - - public bool AllowViewing { get; set; } - public bool AllowEditing { get; set; } - - public StarVariableType Type { get; set; } - public string? Filter { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Database/Migrations/20250922091731_RecreatedModelsInNewSchema.Designer.cs b/MoonlightServers.ApiServer/Database/Migrations/20250922091731_RecreatedModelsInNewSchema.Designer.cs deleted file mode 100644 index 7ecbcb4..0000000 --- a/MoonlightServers.ApiServer/Database/Migrations/20250922091731_RecreatedModelsInNewSchema.Designer.cs +++ /dev/null @@ -1,529 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using MoonlightServers.ApiServer.Database; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace MoonlightServers.ApiServer.Database.Migrations -{ - [DbContext(typeof(ServersDataContext))] - [Migration("20250922091731_RecreatedModelsInNewSchema")] - partial class RecreatedModelsInNewSchema - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasDefaultSchema("servers") - .HasAnnotation("ProductVersion", "9.0.9") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("IpAddress") - .IsRequired() - .HasColumnType("text"); - - b.Property("NodeId") - .HasColumnType("integer"); - - b.Property("Port") - .HasColumnType("integer"); - - b.Property("ServerId") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("NodeId"); - - b.HasIndex("ServerId"); - - b.ToTable("Allocations", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Fqdn") - .IsRequired() - .HasColumnType("text"); - - b.Property("FtpPort") - .HasColumnType("integer"); - - b.Property("HttpPort") - .HasColumnType("integer"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("Token") - .IsRequired() - .HasColumnType("text"); - - b.Property("TokenId") - .IsRequired() - .HasColumnType("text"); - - b.Property("UseSsl") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.ToTable("Nodes", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Cpu") - .HasColumnType("integer"); - - b.Property("Disk") - .HasColumnType("integer"); - - b.Property("DockerImageIndex") - .HasColumnType("integer"); - - b.Property("Memory") - .HasColumnType("integer"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("NodeId") - .HasColumnType("integer"); - - b.Property("OwnerId") - .HasColumnType("integer"); - - b.Property("StarId") - .HasColumnType("integer"); - - b.Property("StartupOverride") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("NodeId"); - - b.HasIndex("StarId"); - - b.ToTable("Servers", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerBackup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Completed") - .HasColumnType("boolean"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("ServerId") - .HasColumnType("integer"); - - b.Property("Size") - .HasColumnType("bigint"); - - b.Property("Successful") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("ServerId"); - - b.ToTable("ServerBackups", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerShare", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("ServerId") - .HasColumnType("integer"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("UserId") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ServerId"); - - b.ToTable("ServerShares", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Key") - .IsRequired() - .HasColumnType("text"); - - b.Property("ServerId") - .HasColumnType("integer"); - - b.Property("Value") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ServerId"); - - b.ToTable("ServerVariables", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AllowDockerImageChange") - .HasColumnType("boolean"); - - b.Property("Author") - .IsRequired() - .HasColumnType("text"); - - b.Property("DefaultDockerImage") - .HasColumnType("integer"); - - b.Property("DonateUrl") - .HasColumnType("text"); - - b.Property("InstallDockerImage") - .IsRequired() - .HasColumnType("text"); - - b.Property("InstallScript") - .IsRequired() - .HasColumnType("text"); - - b.Property("InstallShell") - .IsRequired() - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("OnlineDetection") - .IsRequired() - .HasColumnType("text"); - - b.Property("ParseConfiguration") - .IsRequired() - .HasColumnType("text"); - - b.Property("RequiredAllocations") - .HasColumnType("integer"); - - b.Property("StartupCommand") - .IsRequired() - .HasColumnType("text"); - - b.Property("StopCommand") - .IsRequired() - .HasColumnType("text"); - - b.Property("UpdateUrl") - .HasColumnType("text"); - - b.Property("Version") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Stars", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AutoPulling") - .HasColumnType("boolean"); - - b.Property("DisplayName") - .IsRequired() - .HasColumnType("text"); - - b.Property("Identifier") - .IsRequired() - .HasColumnType("text"); - - b.Property("StarId") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("StarId"); - - b.ToTable("StarDockerImages", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AllowEditing") - .HasColumnType("boolean"); - - b.Property("AllowViewing") - .HasColumnType("boolean"); - - b.Property("DefaultValue") - .IsRequired() - .HasColumnType("text"); - - b.Property("Description") - .IsRequired() - .HasColumnType("text"); - - b.Property("Filter") - .HasColumnType("text"); - - b.Property("Key") - .IsRequired() - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("StarId") - .HasColumnType("integer"); - - b.Property("Type") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("StarId"); - - b.ToTable("StarVariables", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b => - { - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Node", "Node") - .WithMany("Allocations") - .HasForeignKey("NodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server") - .WithMany("Allocations") - .HasForeignKey("ServerId"); - - b.Navigation("Node"); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b => - { - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Node", "Node") - .WithMany("Servers") - .HasForeignKey("NodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", "Star") - .WithMany() - .HasForeignKey("StarId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Node"); - - b.Navigation("Star"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerBackup", b => - { - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", null) - .WithMany("Backups") - .HasForeignKey("ServerId"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerShare", b => - { - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server") - .WithMany("Shares") - .HasForeignKey("ServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.OwnsOne("MoonlightServers.ApiServer.Models.ServerShareContent", "Content", b1 => - { - b1.Property("ServerShareId") - .HasColumnType("integer"); - - b1.HasKey("ServerShareId"); - - b1.ToTable("ServerShares", "servers"); - - b1.ToJson("Content"); - - b1.WithOwner() - .HasForeignKey("ServerShareId"); - - b1.OwnsMany("MoonlightServers.ApiServer.Models.ServerShareContent+SharePermission", "Permissions", b2 => - { - b2.Property("ServerShareContentServerShareId") - .HasColumnType("integer"); - - b2.Property("__synthesizedOrdinal") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - b2.Property("Identifier") - .IsRequired() - .HasColumnType("text"); - - b2.Property("Level") - .HasColumnType("integer"); - - b2.HasKey("ServerShareContentServerShareId", "__synthesizedOrdinal"); - - b2.ToTable("ServerShares", "servers"); - - b2.WithOwner() - .HasForeignKey("ServerShareContentServerShareId"); - }); - - b1.Navigation("Permissions"); - }); - - b.Navigation("Content") - .IsRequired(); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b => - { - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server") - .WithMany("Variables") - .HasForeignKey("ServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b => - { - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", "Star") - .WithMany("DockerImages") - .HasForeignKey("StarId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Star"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b => - { - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", "Star") - .WithMany("Variables") - .HasForeignKey("StarId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Star"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b => - { - b.Navigation("Allocations"); - - b.Navigation("Servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b => - { - b.Navigation("Allocations"); - - b.Navigation("Backups"); - - b.Navigation("Shares"); - - b.Navigation("Variables"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b => - { - b.Navigation("DockerImages"); - - b.Navigation("Variables"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/MoonlightServers.ApiServer/Database/Migrations/20250922091731_RecreatedModelsInNewSchema.cs b/MoonlightServers.ApiServer/Database/Migrations/20250922091731_RecreatedModelsInNewSchema.cs deleted file mode 100644 index facb456..0000000 --- a/MoonlightServers.ApiServer/Database/Migrations/20250922091731_RecreatedModelsInNewSchema.cs +++ /dev/null @@ -1,353 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace MoonlightServers.ApiServer.Database.Migrations -{ - /// - public partial class RecreatedModelsInNewSchema : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.EnsureSchema( - name: "servers"); - - migrationBuilder.CreateTable( - name: "Nodes", - schema: "servers", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Fqdn = table.Column(type: "text", nullable: false), - Token = table.Column(type: "text", nullable: false), - TokenId = table.Column(type: "text", nullable: false), - HttpPort = table.Column(type: "integer", nullable: false), - FtpPort = table.Column(type: "integer", nullable: false), - UseSsl = table.Column(type: "boolean", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Nodes", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Stars", - schema: "servers", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Version = table.Column(type: "text", nullable: false), - Author = table.Column(type: "text", nullable: false), - UpdateUrl = table.Column(type: "text", nullable: true), - DonateUrl = table.Column(type: "text", nullable: true), - StartupCommand = table.Column(type: "text", nullable: false), - StopCommand = table.Column(type: "text", nullable: false), - OnlineDetection = table.Column(type: "text", nullable: false), - InstallShell = table.Column(type: "text", nullable: false), - InstallDockerImage = table.Column(type: "text", nullable: false), - InstallScript = table.Column(type: "text", nullable: false), - RequiredAllocations = table.Column(type: "integer", nullable: false), - AllowDockerImageChange = table.Column(type: "boolean", nullable: false), - DefaultDockerImage = table.Column(type: "integer", nullable: false), - ParseConfiguration = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Stars", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Servers", - schema: "servers", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - StarId = table.Column(type: "integer", nullable: false), - NodeId = table.Column(type: "integer", nullable: false), - Name = table.Column(type: "text", nullable: false), - OwnerId = table.Column(type: "integer", nullable: false), - StartupOverride = table.Column(type: "text", nullable: true), - DockerImageIndex = table.Column(type: "integer", nullable: false), - Cpu = table.Column(type: "integer", nullable: false), - Memory = table.Column(type: "integer", nullable: false), - Disk = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Servers", x => x.Id); - table.ForeignKey( - name: "FK_Servers_Nodes_NodeId", - column: x => x.NodeId, - principalSchema: "servers", - principalTable: "Nodes", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_Servers_Stars_StarId", - column: x => x.StarId, - principalSchema: "servers", - principalTable: "Stars", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "StarDockerImages", - schema: "servers", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - StarId = table.Column(type: "integer", nullable: false), - DisplayName = table.Column(type: "text", nullable: false), - Identifier = table.Column(type: "text", nullable: false), - AutoPulling = table.Column(type: "boolean", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_StarDockerImages", x => x.Id); - table.ForeignKey( - name: "FK_StarDockerImages_Stars_StarId", - column: x => x.StarId, - principalSchema: "servers", - principalTable: "Stars", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "StarVariables", - schema: "servers", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - StarId = table.Column(type: "integer", nullable: false), - Name = table.Column(type: "text", nullable: false), - Description = table.Column(type: "text", nullable: false), - Key = table.Column(type: "text", nullable: false), - DefaultValue = table.Column(type: "text", nullable: false), - AllowViewing = table.Column(type: "boolean", nullable: false), - AllowEditing = table.Column(type: "boolean", nullable: false), - Type = table.Column(type: "integer", nullable: false), - Filter = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_StarVariables", x => x.Id); - table.ForeignKey( - name: "FK_StarVariables_Stars_StarId", - column: x => x.StarId, - principalSchema: "servers", - principalTable: "Stars", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "Allocations", - schema: "servers", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - NodeId = table.Column(type: "integer", nullable: false), - ServerId = table.Column(type: "integer", nullable: true), - IpAddress = table.Column(type: "text", nullable: false), - Port = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Allocations", x => x.Id); - table.ForeignKey( - name: "FK_Allocations_Nodes_NodeId", - column: x => x.NodeId, - principalSchema: "servers", - principalTable: "Nodes", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_Allocations_Servers_ServerId", - column: x => x.ServerId, - principalSchema: "servers", - principalTable: "Servers", - principalColumn: "Id"); - }); - - migrationBuilder.CreateTable( - name: "ServerBackups", - schema: "servers", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - CompletedAt = table.Column(type: "timestamp with time zone", nullable: false), - Size = table.Column(type: "bigint", nullable: false), - Successful = table.Column(type: "boolean", nullable: false), - Completed = table.Column(type: "boolean", nullable: false), - ServerId = table.Column(type: "integer", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_ServerBackups", x => x.Id); - table.ForeignKey( - name: "FK_ServerBackups_Servers_ServerId", - column: x => x.ServerId, - principalSchema: "servers", - principalTable: "Servers", - principalColumn: "Id"); - }); - - migrationBuilder.CreateTable( - name: "ServerShares", - schema: "servers", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - UserId = table.Column(type: "integer", nullable: false), - ServerId = table.Column(type: "integer", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), - Content = table.Column(type: "jsonb", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ServerShares", x => x.Id); - table.ForeignKey( - name: "FK_ServerShares_Servers_ServerId", - column: x => x.ServerId, - principalSchema: "servers", - principalTable: "Servers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "ServerVariables", - schema: "servers", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - ServerId = table.Column(type: "integer", nullable: false), - Key = table.Column(type: "text", nullable: false), - Value = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ServerVariables", x => x.Id); - table.ForeignKey( - name: "FK_ServerVariables_Servers_ServerId", - column: x => x.ServerId, - principalSchema: "servers", - principalTable: "Servers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_Allocations_NodeId", - schema: "servers", - table: "Allocations", - column: "NodeId"); - - migrationBuilder.CreateIndex( - name: "IX_Allocations_ServerId", - schema: "servers", - table: "Allocations", - column: "ServerId"); - - migrationBuilder.CreateIndex( - name: "IX_ServerBackups_ServerId", - schema: "servers", - table: "ServerBackups", - column: "ServerId"); - - migrationBuilder.CreateIndex( - name: "IX_Servers_NodeId", - schema: "servers", - table: "Servers", - column: "NodeId"); - - migrationBuilder.CreateIndex( - name: "IX_Servers_StarId", - schema: "servers", - table: "Servers", - column: "StarId"); - - migrationBuilder.CreateIndex( - name: "IX_ServerShares_ServerId", - schema: "servers", - table: "ServerShares", - column: "ServerId"); - - migrationBuilder.CreateIndex( - name: "IX_ServerVariables_ServerId", - schema: "servers", - table: "ServerVariables", - column: "ServerId"); - - migrationBuilder.CreateIndex( - name: "IX_StarDockerImages_StarId", - schema: "servers", - table: "StarDockerImages", - column: "StarId"); - - migrationBuilder.CreateIndex( - name: "IX_StarVariables_StarId", - schema: "servers", - table: "StarVariables", - column: "StarId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Allocations", - schema: "servers"); - - migrationBuilder.DropTable( - name: "ServerBackups", - schema: "servers"); - - migrationBuilder.DropTable( - name: "ServerShares", - schema: "servers"); - - migrationBuilder.DropTable( - name: "ServerVariables", - schema: "servers"); - - migrationBuilder.DropTable( - name: "StarDockerImages", - schema: "servers"); - - migrationBuilder.DropTable( - name: "StarVariables", - schema: "servers"); - - migrationBuilder.DropTable( - name: "Servers", - schema: "servers"); - - migrationBuilder.DropTable( - name: "Nodes", - schema: "servers"); - - migrationBuilder.DropTable( - name: "Stars", - schema: "servers"); - } - } -} diff --git a/MoonlightServers.ApiServer/Database/Migrations/ServersDataContextModelSnapshot.cs b/MoonlightServers.ApiServer/Database/Migrations/ServersDataContextModelSnapshot.cs deleted file mode 100644 index da45a02..0000000 --- a/MoonlightServers.ApiServer/Database/Migrations/ServersDataContextModelSnapshot.cs +++ /dev/null @@ -1,526 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using MoonlightServers.ApiServer.Database; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace MoonlightServers.ApiServer.Database.Migrations -{ - [DbContext(typeof(ServersDataContext))] - partial class ServersDataContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasDefaultSchema("servers") - .HasAnnotation("ProductVersion", "9.0.9") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("IpAddress") - .IsRequired() - .HasColumnType("text"); - - b.Property("NodeId") - .HasColumnType("integer"); - - b.Property("Port") - .HasColumnType("integer"); - - b.Property("ServerId") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("NodeId"); - - b.HasIndex("ServerId"); - - b.ToTable("Allocations", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Fqdn") - .IsRequired() - .HasColumnType("text"); - - b.Property("FtpPort") - .HasColumnType("integer"); - - b.Property("HttpPort") - .HasColumnType("integer"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("Token") - .IsRequired() - .HasColumnType("text"); - - b.Property("TokenId") - .IsRequired() - .HasColumnType("text"); - - b.Property("UseSsl") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.ToTable("Nodes", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Cpu") - .HasColumnType("integer"); - - b.Property("Disk") - .HasColumnType("integer"); - - b.Property("DockerImageIndex") - .HasColumnType("integer"); - - b.Property("Memory") - .HasColumnType("integer"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("NodeId") - .HasColumnType("integer"); - - b.Property("OwnerId") - .HasColumnType("integer"); - - b.Property("StarId") - .HasColumnType("integer"); - - b.Property("StartupOverride") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("NodeId"); - - b.HasIndex("StarId"); - - b.ToTable("Servers", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerBackup", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Completed") - .HasColumnType("boolean"); - - b.Property("CompletedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("ServerId") - .HasColumnType("integer"); - - b.Property("Size") - .HasColumnType("bigint"); - - b.Property("Successful") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("ServerId"); - - b.ToTable("ServerBackups", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerShare", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("ServerId") - .HasColumnType("integer"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("UserId") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("ServerId"); - - b.ToTable("ServerShares", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Key") - .IsRequired() - .HasColumnType("text"); - - b.Property("ServerId") - .HasColumnType("integer"); - - b.Property("Value") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ServerId"); - - b.ToTable("ServerVariables", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AllowDockerImageChange") - .HasColumnType("boolean"); - - b.Property("Author") - .IsRequired() - .HasColumnType("text"); - - b.Property("DefaultDockerImage") - .HasColumnType("integer"); - - b.Property("DonateUrl") - .HasColumnType("text"); - - b.Property("InstallDockerImage") - .IsRequired() - .HasColumnType("text"); - - b.Property("InstallScript") - .IsRequired() - .HasColumnType("text"); - - b.Property("InstallShell") - .IsRequired() - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("OnlineDetection") - .IsRequired() - .HasColumnType("text"); - - b.Property("ParseConfiguration") - .IsRequired() - .HasColumnType("text"); - - b.Property("RequiredAllocations") - .HasColumnType("integer"); - - b.Property("StartupCommand") - .IsRequired() - .HasColumnType("text"); - - b.Property("StopCommand") - .IsRequired() - .HasColumnType("text"); - - b.Property("UpdateUrl") - .HasColumnType("text"); - - b.Property("Version") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Stars", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AutoPulling") - .HasColumnType("boolean"); - - b.Property("DisplayName") - .IsRequired() - .HasColumnType("text"); - - b.Property("Identifier") - .IsRequired() - .HasColumnType("text"); - - b.Property("StarId") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("StarId"); - - b.ToTable("StarDockerImages", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AllowEditing") - .HasColumnType("boolean"); - - b.Property("AllowViewing") - .HasColumnType("boolean"); - - b.Property("DefaultValue") - .IsRequired() - .HasColumnType("text"); - - b.Property("Description") - .IsRequired() - .HasColumnType("text"); - - b.Property("Filter") - .HasColumnType("text"); - - b.Property("Key") - .IsRequired() - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("StarId") - .HasColumnType("integer"); - - b.Property("Type") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("StarId"); - - b.ToTable("StarVariables", "servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b => - { - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Node", "Node") - .WithMany("Allocations") - .HasForeignKey("NodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server") - .WithMany("Allocations") - .HasForeignKey("ServerId"); - - b.Navigation("Node"); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b => - { - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Node", "Node") - .WithMany("Servers") - .HasForeignKey("NodeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", "Star") - .WithMany() - .HasForeignKey("StarId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Node"); - - b.Navigation("Star"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerBackup", b => - { - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", null) - .WithMany("Backups") - .HasForeignKey("ServerId"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerShare", b => - { - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server") - .WithMany("Shares") - .HasForeignKey("ServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.OwnsOne("MoonlightServers.ApiServer.Models.ServerShareContent", "Content", b1 => - { - b1.Property("ServerShareId") - .HasColumnType("integer"); - - b1.HasKey("ServerShareId"); - - b1.ToTable("ServerShares", "servers"); - - b1.ToJson("Content"); - - b1.WithOwner() - .HasForeignKey("ServerShareId"); - - b1.OwnsMany("MoonlightServers.ApiServer.Models.ServerShareContent+SharePermission", "Permissions", b2 => - { - b2.Property("ServerShareContentServerShareId") - .HasColumnType("integer"); - - b2.Property("__synthesizedOrdinal") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - b2.Property("Identifier") - .IsRequired() - .HasColumnType("text"); - - b2.Property("Level") - .HasColumnType("integer"); - - b2.HasKey("ServerShareContentServerShareId", "__synthesizedOrdinal"); - - b2.ToTable("ServerShares", "servers"); - - b2.WithOwner() - .HasForeignKey("ServerShareContentServerShareId"); - }); - - b1.Navigation("Permissions"); - }); - - b.Navigation("Content") - .IsRequired(); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b => - { - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server") - .WithMany("Variables") - .HasForeignKey("ServerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Server"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b => - { - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", "Star") - .WithMany("DockerImages") - .HasForeignKey("StarId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Star"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b => - { - b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", "Star") - .WithMany("Variables") - .HasForeignKey("StarId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Star"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b => - { - b.Navigation("Allocations"); - - b.Navigation("Servers"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b => - { - b.Navigation("Allocations"); - - b.Navigation("Backups"); - - b.Navigation("Shares"); - - b.Navigation("Variables"); - }); - - modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b => - { - b.Navigation("DockerImages"); - - b.Navigation("Variables"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/MoonlightServers.ApiServer/Database/ServersDataContext.cs b/MoonlightServers.ApiServer/Database/ServersDataContext.cs deleted file mode 100644 index 696e9e1..0000000 --- a/MoonlightServers.ApiServer/Database/ServersDataContext.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Moonlight.ApiServer.Configuration; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Models; - -namespace MoonlightServers.ApiServer.Database; - -public class ServersDataContext : DbContext -{ - public DbSet Allocations { get; set; } - public DbSet Nodes { get; set; } - public DbSet Servers { get; set; } - public DbSet ServerBackups { get; set; } - public DbSet ServerShares { get; set; } - public DbSet ServerVariables { get; set; } - public DbSet Stars { get; set; } - public DbSet StarDockerImages { get; set; } - public DbSet StarVariables { get; set; } - - private readonly AppConfiguration Configuration; - private readonly string Schema = "servers"; - - public ServersDataContext(AppConfiguration configuration) - { - Configuration = configuration; - } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - if(optionsBuilder.IsConfigured) - return; - - var database = Configuration.Database; - - var connectionString = $"Host={database.Host};" + - $"Port={database.Port};" + - $"Database={database.Database};" + - $"Username={database.Username};" + - $"Password={database.Password}"; - - optionsBuilder.UseNpgsql(connectionString, builder => - { - builder.MigrationsHistoryTable("MigrationsHistory", Schema); - }); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Model.SetDefaultSchema(Schema); - - base.OnModelCreating(modelBuilder); - - #region Shares - - modelBuilder.Ignore(); - modelBuilder.Ignore(); - - modelBuilder.Entity(builder => - { - builder.OwnsOne(x => x.Content, navigationBuilder => - { - navigationBuilder.ToJson(); - - navigationBuilder.OwnsMany(x => x.Permissions); - }); - }); - - #endregion - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Extensions/ServerStateExtensions.cs b/MoonlightServers.ApiServer/Extensions/ServerStateExtensions.cs deleted file mode 100644 index ea2b732..0000000 --- a/MoonlightServers.ApiServer/Extensions/ServerStateExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using ServerState = MoonlightServers.Shared.Enums.ServerState; - -namespace MoonlightServers.ApiServer.Extensions; - -public static class ServerStateExtensions -{ - public static ServerState ToServerPowerState(this DaemonShared.Enums.ServerState state) - { - return state switch - { - DaemonShared.Enums.ServerState.Installing => ServerState.Installing, - DaemonShared.Enums.ServerState.Stopping => ServerState.Stopping, - DaemonShared.Enums.ServerState.Online => ServerState.Online, - DaemonShared.Enums.ServerState.Starting => ServerState.Starting, - DaemonShared.Enums.ServerState.Offline => ServerState.Offline, - _ => ServerState.Offline - }; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Helpers/NodeAuthOptions.cs b/MoonlightServers.ApiServer/Helpers/NodeAuthOptions.cs deleted file mode 100644 index 696063c..0000000 --- a/MoonlightServers.ApiServer/Helpers/NodeAuthOptions.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Microsoft.AspNetCore.Authentication; - -namespace MoonlightServers.ApiServer.Helpers; - -public class NodeAuthOptions : AuthenticationSchemeOptions -{ - -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Helpers/NodeAuthScheme.cs b/MoonlightServers.ApiServer/Helpers/NodeAuthScheme.cs deleted file mode 100644 index 7f32baf..0000000 --- a/MoonlightServers.ApiServer/Helpers/NodeAuthScheme.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Security.Claims; -using System.Text.Encodings.Web; -using Microsoft.AspNetCore.Authentication; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using MoonCore.Extended.Abstractions; -using MoonlightServers.ApiServer.Database.Entities; - -namespace MoonlightServers.ApiServer.Helpers; - -public class NodeAuthScheme : AuthenticationHandler -{ - public NodeAuthScheme(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, - ISystemClock clock) : base(options, logger, encoder, clock) - { - } - - public NodeAuthScheme(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) : base( - options, logger, encoder) - { - } - - protected override async Task HandleAuthenticateAsync() - { - if (!Request.Headers.ContainsKey("Authorization")) - return AuthenticateResult.NoResult(); - - var authHeaderValue = Request.Headers["Authorization"].FirstOrDefault(); - - if (string.IsNullOrEmpty(authHeaderValue)) - return AuthenticateResult.NoResult(); - - if (!authHeaderValue.Contains("Bearer ")) - return AuthenticateResult.NoResult(); - - var tokenParts = authHeaderValue - .Replace("Bearer ", "") - .Trim() - .Split('.'); - - if (tokenParts.Length != 2) - return AuthenticateResult.NoResult(); - - var tokenId = tokenParts[0]; - var token = tokenParts[1]; - - if (tokenId.Length != 6) - return AuthenticateResult.NoResult(); - - var nodeRepo = Context.RequestServices.GetRequiredService>(); - - var node = await nodeRepo - .Get() - .FirstOrDefaultAsync(x => x.TokenId == tokenId); - - if (node == null) - return AuthenticateResult.NoResult(); - - if (node.Token != token) - return AuthenticateResult.NoResult(); - - return AuthenticateResult.Success( - new AuthenticationTicket( - new ClaimsPrincipal( - new ClaimsIdentity( - [ - new Claim("nodeId", node.Id.ToString()) - ], - "nodeAuthentication" - ) - ), - "nodeAuthentication" - ) - ); - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/NodeAllocationsController.cs b/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/NodeAllocationsController.cs deleted file mode 100644 index 800b4b6..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/NodeAllocationsController.cs +++ /dev/null @@ -1,237 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using MoonCore.Common; -using MoonCore.Extended.Abstractions; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Mappers; -using MoonlightServers.Shared.Http.Requests.Admin.NodeAllocations; -using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations; - -namespace MoonlightServers.ApiServer.Http.Controllers.Admin.Nodes; - -[ApiController] -[Route("api/admin/servers/nodes/{nodeId:int}/allocations")] -public class NodeAllocationsController : Controller -{ - private readonly DatabaseRepository NodeRepository; - private readonly DatabaseRepository AllocationRepository; - - public NodeAllocationsController( - DatabaseRepository nodeRepository, - DatabaseRepository allocationRepository - ) - { - NodeRepository = nodeRepository; - AllocationRepository = allocationRepository; - } - - [HttpGet] - [Authorize(Policy = "permissions:admin.servers.nodes.get")] - public async Task>> GetAsync( - [FromRoute] int nodeId, - [FromQuery] int startIndex, - [FromQuery] int count - ) - { - if (count > 100) - return Problem("Only 100 items can be fetched at a time", statusCode: 400); - - var totalCount = await AllocationRepository - .Get() - .CountAsync(x => x.Node.Id == nodeId); - - var allocations = await AllocationRepository - .Get() - .OrderBy(x => x.Id) - .Skip(startIndex) - .Take(count) - .Where(x => x.Node.Id == nodeId) - .AsNoTracking() - .ProjectToAdminResponse() - .ToArrayAsync(); - - return new CountedData() - { - Items = allocations, - TotalCount = totalCount - }; - } - - [HttpGet("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.nodes.get")] - public async Task> GetSingleAsync([FromRoute] int nodeId, [FromRoute] int id) - { - var allocation = await AllocationRepository - .Get() - .Where(x => x.Node.Id == nodeId) - .AsNoTracking() - .ProjectToAdminResponse() - .FirstOrDefaultAsync(x => x.Id == id); - - if (allocation == null) - return Problem("No allocation with that id found", statusCode: 400); - - return allocation; - } - - [HttpPost] - [Authorize(Policy = "permissions:admin.servers.nodes.create")] - public async Task> CreateAsync( - [FromRoute] int nodeId, - [FromBody] CreateNodeAllocationRequest request - ) - { - var node = await NodeRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == nodeId); - - if (node == null) - return Problem("No node with that id found", statusCode: 404); - - var allocation = AllocationMapper.ToAllocation(request); - - var finalAllocation = await AllocationRepository.AddAsync(allocation); - - return AllocationMapper.ToNodeAllocation(finalAllocation); - } - - [HttpPatch("{id:int}")] - public async Task> UpdateAsync( - [FromRoute] int nodeId, - [FromRoute] int id, - [FromBody] UpdateNodeAllocationRequest request - ) - { - var allocation = await AllocationRepository - .Get() - .Where(x => x.Node.Id == nodeId) - .FirstOrDefaultAsync(x => x.Id == id); - - if (allocation == null) - return Problem("No allocation with that id found", statusCode: 404); - - AllocationMapper.Merge(request, allocation); - await AllocationRepository.UpdateAsync(allocation); - - return AllocationMapper.ToNodeAllocation(allocation); - } - - [HttpDelete("{id:int}")] - public async Task DeleteAsync([FromRoute] int nodeId, [FromRoute] int id) - { - var allocation = await AllocationRepository - .Get() - .Where(x => x.Node.Id == nodeId) - .FirstOrDefaultAsync(x => x.Id == id); - - if (allocation == null) - return Problem("No allocation with that id found", statusCode: 404); - - await AllocationRepository.RemoveAsync(allocation); - return NoContent(); - } - - [HttpPost("range")] - public async Task CreateRangeAsync( - [FromRoute] int nodeId, - [FromBody] CreateNodeAllocationRangeRequest request - ) - { - if (request.Start > request.End) - return Problem("Invalid start and end specified", statusCode: 400); - - if (request.End - request.Start == 0) - return Problem("Empty range specified", statusCode: 400); - - var node = await NodeRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == nodeId); - - if (node == null) - return Problem("No node with that id found", statusCode: 404); - - var existingAllocations = await AllocationRepository - .Get() - .Where(x => x.Port >= request.Start && x.Port <= request.End && - x.IpAddress == request.IpAddress) - .AsNoTracking() - .ToArrayAsync(); - - var ports = new List(); - - for (var i = request.Start; i < request.End; i++) - { - // Skip existing allocations - if (existingAllocations.Any(x => x.Port == i)) - continue; - - ports.Add(i); - } - - var allocations = ports - .Select(port => new Allocation() - { - IpAddress = request.IpAddress, - Port = port, - Node = node - }) - .ToArray(); - - await AllocationRepository.RunTransactionAsync(async set => { await set.AddRangeAsync(allocations); }); - return NoContent(); - } - - [HttpDelete("all")] - public async Task DeleteAllAsync([FromRoute] int nodeId) - { - var allocations = AllocationRepository - .Get() - .Where(x => x.Node.Id == nodeId) - .ToArray(); - - await AllocationRepository.RunTransactionAsync(set => { set.RemoveRange(allocations); }); - return NoContent(); - } - - [HttpGet("free")] - [Authorize(Policy = "permissions:admin.servers.nodes.get")] - public async Task>> GetFreeAsync( - [FromRoute] int nodeId, - [FromQuery] int startIndex, - [FromQuery] int count, - [FromQuery] int serverId = -1 - ) - { - if (count > 100) - return Problem("Only 100 items can be fetched at a time", statusCode: 400); - - var node = NodeRepository - .Get() - .FirstOrDefault(x => x.Id == nodeId); - - if (node == null) - return Problem("A node with this id could not be found", statusCode: 404); - - var freeAllocationsQuery = AllocationRepository - .Get() - .OrderBy(x => x.Id) - .Where(x => x.Node.Id == node.Id) - .Where(x => x.Server == null || x.Server.Id == serverId); - - var totalCount = await freeAllocationsQuery.CountAsync(); - - var allocations = await freeAllocationsQuery - .Skip(startIndex) - .Take(count) - .AsNoTracking() - .ProjectToAdminResponse() - .ToArrayAsync(); - - return new CountedData() - { - Items = allocations, - TotalCount = totalCount - }; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/NodeStatusController.cs b/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/NodeStatusController.cs deleted file mode 100644 index 2a1130b..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/NodeStatusController.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Diagnostics; -using Microsoft.AspNetCore.Mvc; -using MoonCore.Extended.Abstractions; -using Microsoft.AspNetCore.Authorization; -using Microsoft.EntityFrameworkCore; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Services; -using MoonlightServers.Shared.Http.Responses.Admin.Nodes.Sys; - -namespace MoonlightServers.ApiServer.Http.Controllers.Admin.Nodes; - -[ApiController] -[Route("api/admin/servers/nodes")] -public class NodeStatusController : Controller -{ - private readonly DatabaseRepository NodeRepository; - private readonly NodeService NodeService; - - public NodeStatusController(DatabaseRepository nodeRepository, NodeService nodeService) - { - NodeRepository = nodeRepository; - NodeService = nodeService; - } - - [HttpGet("{nodeId:int}/system/status")] - [Authorize(Policy = "permissions:admin.servers.nodes.status")] - public async Task> GetStatusAsync([FromRoute] int nodeId) - { - var node = await GetNodeAsync(nodeId); - - if (node.Value == null) - return node.Result ?? Problem("Unable to retrieve node"); - - NodeSystemStatusResponse response; - - var sw = new Stopwatch(); - sw.Start(); - - try - { - var statusResponse = await NodeService.GetSystemStatusAsync(node.Value); - - sw.Stop(); - - response = new() - { - Version = statusResponse.Version, - RoundtripError = statusResponse.TripError, - RoundtripSuccess = statusResponse.TripSuccess, - RoundtripTime = statusResponse.TripTime + sw.Elapsed, - RoundtripRemoteFailure = !statusResponse.TripSuccess // When the remote trip failed, it's the remotes fault - }; - } - catch (Exception e) - { - sw.Stop(); - - response = new() - { - Version = "Unknown", - RoundtripError = e.Message, - RoundtripSuccess = false, - RoundtripTime = sw.Elapsed, - RoundtripRemoteFailure = false - }; - } - - return response; - } - - private async Task> GetNodeAsync(int nodeId) - { - var result = await NodeRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == nodeId); - - if (result == null) - return Problem("A node with this id could not be found", statusCode: 404); - - return result; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/NodesController.cs b/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/NodesController.cs deleted file mode 100644 index 5b2b5e2..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/NodesController.cs +++ /dev/null @@ -1,114 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using MoonCore.Extended.Abstractions; -using Microsoft.AspNetCore.Authorization; -using MoonCore.Common; -using MoonCore.Helpers; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Mappers; -using MoonlightServers.Shared.Http.Requests.Admin.Nodes; -using MoonlightServers.Shared.Http.Responses.Admin.Nodes; - -namespace MoonlightServers.ApiServer.Http.Controllers.Admin.Nodes; - -[ApiController] -[Route("api/admin/servers/nodes")] -public class NodesController : Controller -{ - private readonly DatabaseRepository NodeRepository; - - public NodesController(DatabaseRepository nodeRepository) - { - NodeRepository = nodeRepository; - } - - [HttpGet] - [Authorize(Policy = "permissions:admin.servers.nodes.get")] - public async Task>> GetAsync( - [FromQuery] int startIndex, - [FromQuery] int count - ) - { - if (count > 100) - return Problem("Only 100 items can be fetched at a time", statusCode: 400); - - var totalCount = await NodeRepository.Get().CountAsync(); - - var items = await NodeRepository - .Get() - .OrderBy(x => x.Id) - .Skip(startIndex) - .Take(count) - .AsNoTracking() - .ProjectToAdminResponse() - .ToArrayAsync(); - - return new CountedData() - { - Items = items, - TotalCount = totalCount - }; - } - - [HttpGet("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.nodes.get")] - public async Task> GetSingleAsync([FromRoute] int id) - { - var node = await NodeRepository - .Get() - .AsNoTracking() - .ProjectToAdminResponse() - .FirstOrDefaultAsync(x => x.Id == id); - - if (node == null) - return Problem("No node with this id found", statusCode: 404); - - return node; - } - - [HttpPost] - [Authorize(Policy = "permissions:admin.servers.nodes.create")] - public async Task> CreateAsync([FromBody] CreateNodeRequest request) - { - var node = NodeMapper.ToNode(request); - - node.TokenId = Formatter.GenerateString(6); - node.Token = Formatter.GenerateString(32); - - var finalNode = await NodeRepository.AddAsync(node); - - return NodeMapper.ToAdminNodeResponse(finalNode); - } - - [HttpPatch("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.nodes.update")] - public async Task> UpdateAsync([FromRoute] int id, [FromBody] UpdateNodeRequest request) - { - var node = await NodeRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == id); - - if (node == null) - return Problem("No node with this id found", statusCode: 404); - - NodeMapper.Merge(request, node); - await NodeRepository.UpdateAsync(node); - - return NodeMapper.ToAdminNodeResponse(node); - } - - [HttpDelete("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.nodes.delete")] - public async Task DeleteAsync([FromRoute] int id) - { - var node = await NodeRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == id); - - if (node == null) - return Problem("No node with this id found", statusCode: 404); - - await NodeRepository.RemoveAsync(node); - return Ok(); - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/StatisticsController.cs b/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/StatisticsController.cs deleted file mode 100644 index 78f2467..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/StatisticsController.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using MoonCore.Extended.Abstractions; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Services; -using MoonlightServers.Shared.Http.Responses.Admin.Nodes.Statistics; - -namespace MoonlightServers.ApiServer.Http.Controllers.Admin.Nodes; - -[ApiController] -[Route("api/admin/servers/nodes/{nodeId:int}/statistics")] -[Authorize(Policy = "permissions:admin.servers.nodes.statistics")] -public class StatisticsController : Controller -{ - private readonly NodeService NodeService; - private readonly DatabaseRepository NodeRepository; - - public StatisticsController(NodeService nodeService, DatabaseRepository nodeRepository) - { - NodeService = nodeService; - NodeRepository = nodeRepository; - } - - [HttpGet] - [SuppressMessage("ReSharper.DPA", "DPA0011: High execution time of MVC action", MessageId = "time: 1142ms", - Justification = "The daemon has an artificial delay of one second to calculate accurate cpu usage values")] - public async Task> GetAsync([FromRoute] int nodeId) - { - var node = await GetNodeAsync(nodeId); - - if (node.Value == null) - return node.Result ?? Problem("Unable to retrieve node"); - - var statistics = await NodeService.GetStatisticsAsync(node.Value); - - return new StatisticsResponse() - { - Cpu = new() - { - Model = statistics.Cpu.Model, - Usage = statistics.Cpu.Usage, - UsagePerCore = statistics.Cpu.UsagePerCore - }, - Memory = new() - { - Available = statistics.Memory.Available, - Total = statistics.Memory.Total, - Cached = statistics.Memory.Cached, - Free = statistics.Memory.Free, - SwapFree = statistics.Memory.SwapFree, - SwapTotal = statistics.Memory.SwapTotal - }, - Disks = statistics.Disks.Select(x => new StatisticsResponse.DiskData() - { - Device = x.Device, - DiskFree = x.DiskFree, - DiskTotal = x.DiskTotal, - InodesFree = x.InodesFree, - InodesTotal = x.InodesTotal, - MountPath = x.MountPath - }).ToArray() - }; - } - - [HttpGet("docker")] - public async Task> GetDockerAsync([FromRoute] int nodeId) - { - var node = await GetNodeAsync(nodeId); - - if (node.Value == null) - return node.Result ?? Problem("Unable to retrieve node"); - - var statistics = await NodeService.GetDockerStatisticsAsync(node.Value); - - return new DockerStatisticsResponse() - { - BuildCacheReclaimable = statistics.BuildCacheReclaimable, - BuildCacheUsed = statistics.BuildCacheUsed, - ContainersReclaimable = statistics.ContainersReclaimable, - ContainersUsed = statistics.ContainersUsed, - ImagesReclaimable = statistics.ImagesReclaimable, - ImagesUsed = statistics.ImagesUsed, - Version = statistics.Version - }; - } - - private async Task> GetNodeAsync(int nodeId) - { - var result = await NodeRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == nodeId); - - if (result == null) - return Problem("A node with this id could not be found", statusCode: 404); - - return result; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Admin/Servers/ServerVariablesController.cs b/MoonlightServers.ApiServer/Http/Controllers/Admin/Servers/ServerVariablesController.cs deleted file mode 100644 index 4d7515e..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Admin/Servers/ServerVariablesController.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Microsoft.AspNetCore.Authorization; -using MoonCore.Common; -using MoonCore.Extended.Abstractions; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Mappers; -using MoonlightServers.Shared.Http.Responses.Admin.ServerVariables; - -namespace MoonlightServers.ApiServer.Http.Controllers.Admin.Servers; - -[ApiController] -[Route("api/admin/servers")] -public class ServerVariablesController : Controller -{ - private readonly DatabaseRepository VariableRepository; - private readonly DatabaseRepository ServerRepository; - - public ServerVariablesController( - DatabaseRepository variableRepository, - DatabaseRepository serverRepository - ) - { - VariableRepository = variableRepository; - ServerRepository = serverRepository; - } - - [HttpGet("{serverId:int}/variables")] - [Authorize(Policy = "permissions:admin.servers.read")] - public async Task>> GetAsync( - [FromRoute] int serverId, - [FromQuery] int startIndex, - [FromQuery] int count - ) - { - if (count > 100) - return Problem("Only 100 items can be fetched at a time", statusCode: 400); - - var serverExists = await ServerRepository - .Get() - .AnyAsync(x => x.Id == serverId); - - if (!serverExists) - return Problem("No server with this id found", statusCode: 404); - - var query = VariableRepository - .Get() - .Where(x => x.Server.Id == serverId); - - var totalCount = await query.CountAsync(); - - var variables = await query - .OrderBy(x => x.Id) - .Skip(startIndex) - .Take(count) - .AsNoTracking() - .ProjectToAdminResponse() - .ToArrayAsync(); - - return new CountedData() - { - Items = variables, - TotalCount = totalCount - }; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Admin/Servers/ServersController.cs b/MoonlightServers.ApiServer/Http/Controllers/Admin/Servers/ServersController.cs deleted file mode 100644 index 6d5031d..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Admin/Servers/ServersController.cs +++ /dev/null @@ -1,330 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Microsoft.AspNetCore.Authorization; -using Microsoft.Extensions.Logging; -using MoonCore.Common; -using MoonCore.Extended.Abstractions; -using Moonlight.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Mappers; -using MoonlightServers.ApiServer.Services; -using MoonlightServers.Shared.Http.Requests.Admin.Servers; -using MoonlightServers.Shared.Http.Responses.Admin.Servers; - -namespace MoonlightServers.ApiServer.Http.Controllers.Admin.Servers; - -[ApiController] -[Route("api/admin/servers")] -public class ServersController : Controller -{ - private readonly DatabaseRepository StarRepository; - private readonly DatabaseRepository NodeRepository; - private readonly DatabaseRepository AllocationRepository; - private readonly DatabaseRepository VariableRepository; - private readonly DatabaseRepository ServerRepository; - private readonly DatabaseRepository UserRepository; - private readonly ILogger Logger; - private readonly ServerService ServerService; - - public ServersController( - DatabaseRepository starRepository, - DatabaseRepository nodeRepository, - DatabaseRepository allocationRepository, - DatabaseRepository variableRepository, - DatabaseRepository serverRepository, - DatabaseRepository userRepository, - ILogger logger, - ServerService serverService - ) - { - StarRepository = starRepository; - NodeRepository = nodeRepository; - AllocationRepository = allocationRepository; - VariableRepository = variableRepository; - ServerRepository = serverRepository; - UserRepository = userRepository; - ServerService = serverService; - Logger = logger; - } - - [HttpGet] - [Authorize(Policy = "permissions:admin.servers.read")] - public async Task>> GetAsync( - [FromQuery] int startIndex, - [FromQuery] int count - ) - { - if (count > 100) - return Problem("Only 100 items can be fetched at a time", statusCode: 400); - - var totalCount = await ServerRepository.Get().CountAsync(); - - var servers = await ServerRepository - .Get() - .Include(x => x.Node) - .Include(x => x.Allocations) - .Include(x => x.Variables) - .Include(x => x.Star) - .OrderBy(x => x.Id) - .Skip(startIndex) - .Take(count) - .AsNoTracking() - .ProjectToAdminResponse() - .ToArrayAsync(); - - return new CountedData() - { - Items = servers, - TotalCount = totalCount - }; - } - - [HttpGet("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.read")] - public async Task> GetSingleAsync([FromRoute] int id) - { - var server = await ServerRepository - .Get() - .Include(x => x.Node) - .Include(x => x.Allocations) - .Include(x => x.Variables) - .Include(x => x.Star) - .AsNoTracking() - .Where(x => x.Id == id) - .ProjectToAdminResponse() - .FirstOrDefaultAsync(); - - if (server == null) - return Problem("No server with that id found", statusCode: 404); - - return server; - } - - [HttpPost] - [Authorize(Policy = "permissions:admin.servers.write")] - public async Task> CreateAsync([FromBody] CreateServerRequest request) - { - // Check if owner user exist - if (UserRepository.Get().All(x => x.Id != request.OwnerId)) - return Problem("No user with this id found", statusCode: 400); - - // Check if the star exists - var star = await StarRepository - .Get() - .Include(x => x.Variables) - .Include(x => x.DockerImages) - .FirstOrDefaultAsync(x => x.Id == request.StarId); - - if (star == null) - return Problem("No star with this id found", statusCode: 400); - - var node = await NodeRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == request.NodeId); - - if (node == null) - return Problem("No node with this id found", statusCode: 400); - - var allocations = new List(); - - // Fetch specified allocations from the request - foreach (var allocationId in request.AllocationIds) - { - var allocation = await AllocationRepository - .Get() - .Where(x => x.Server == null) - .Where(x => x.Node.Id == node.Id) - .FirstOrDefaultAsync(x => x.Id == allocationId); - - if (allocation == null) - continue; - - allocations.Add(allocation); - } - - // Check if the specified allocations are enough for the star - if (allocations.Count < star.RequiredAllocations) - { - var amountRequiredToSatisfy = star.RequiredAllocations - allocations.Count; - - var freeAllocations = await AllocationRepository - .Get() - .Where(x => x.Server == null) - .Where(x => x.Node.Id == node.Id) - .Take(amountRequiredToSatisfy) - .ToArrayAsync(); - - allocations.AddRange(freeAllocations); - - if (allocations.Count < star.RequiredAllocations) - { - return Problem( - $"Unable to find enough free allocations. Found: {allocations.Count}, Required: {star.RequiredAllocations}", - statusCode: 400 - ); - } - } - - var server = ServerMapper.ToServer(request); - - // Set allocations - server.Allocations = allocations; - - // Variables - foreach (var variable in star.Variables) - { - var requestVar = request.Variables.FirstOrDefault(x => x.Key == variable.Key); - - var serverVar = new ServerVariable() - { - Key = variable.Key, - Value = requestVar != null - ? requestVar.Value - : variable.DefaultValue - }; - - server.Variables.Add(serverVar); - } - - // Set relations - server.Node = node; - server.Star = star; - - var finalServer = await ServerRepository.AddAsync(server); - - try - { - await ServerService.SyncAsync(finalServer); - } - catch (Exception e) - { - Logger.LogError("Unable to sync server to node the server is assigned to: {e}", e); - - // We are deleting the server from the database after the creation has failed - // to ensure we won't have a bugged server in the database which doesn't exist on the node - await ServerRepository.RemoveAsync(finalServer); - - throw; - } - - return ServerMapper.ToAdminServerResponse(finalServer); - } - - [HttpPatch("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.write")] - public async Task> UpdateAsync( - [FromRoute] int id, - [FromBody] UpdateServerRequest request - ) - { - //TODO: Handle shrinking virtual disk - - var server = await ServerRepository - .Get() - .Include(x => x.Node) - .Include(x => x.Allocations) - .Include(x => x.Variables) - .Include(x => x.Star) - .FirstOrDefaultAsync(x => x.Id == id); - - if (server == null) - return Problem("No server with that id found", statusCode: 404); - - ServerMapper.Merge(request, server); - - var allocations = new List(); - - // Fetch specified allocations from the request - foreach (var allocationId in request.AllocationIds) - { - var allocation = await AllocationRepository - .Get() - .Where(x => x.Server == null || x.Server.Id == server.Id) - .Where(x => x.Node.Id == server.Node.Id) - .FirstOrDefaultAsync(x => x.Id == allocationId); - - // ^ This loads the allocations specified in the request. - // Valid allocations are either free ones or ones which are already allocated to this server - - if (allocation == null) - continue; - - allocations.Add(allocation); - } - - // Check if the specified allocations are enough for the star - if (allocations.Count < server.Star.RequiredAllocations) - { - return Problem( - $"You need to specify at least {server.Star.RequiredAllocations} allocation(s)", - statusCode: 400 - ); - } - - // Set allocations - server.Allocations = allocations; - - // Process variables - foreach (var variable in request.Variables) - { - // Search server variable associated to the variable in the request - var serverVar = server.Variables - .FirstOrDefault(x => x.Key == variable.Key); - - if (serverVar == null) - continue; - - // Update value - serverVar.Value = variable.Value; - } - - await ServerRepository.UpdateAsync(server); - - // Notify the node about the changes - await ServerService.SyncAsync(server); - - return ServerMapper.ToAdminServerResponse(server); - } - - [HttpDelete("{id:int}")] - public async Task DeleteAsync([FromRoute] int id, [FromQuery] bool force = false) - { - var server = await ServerRepository - .Get() - .Include(x => x.Node) - .Include(x => x.Star) - .Include(x => x.Variables) - .Include(x => x.Backups) - .Include(x => x.Allocations) - .FirstOrDefaultAsync(x => x.Id == id); - - if (server == null) - return Problem("No server with that id found", statusCode: 404); - - server.Variables.Clear(); - server.Backups.Clear(); - server.Allocations.Clear(); - - try - { - // If the sync fails on the node and we aren't forcing the deletion, - // we don't want to delete it from the database yet - await ServerService.SyncDeleteAsync(server); - } - catch (Exception e) - { - if (force) - { - Logger.LogWarning( - "An error occured while syncing deletion of a server to the node. Continuing anyways. Error: {e}", - e - ); - } - else - throw; - } - - await ServerRepository.RemoveAsync(server); - return NoContent(); - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarDockerImagesController.cs b/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarDockerImagesController.cs deleted file mode 100644 index 272dca3..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarDockerImagesController.cs +++ /dev/null @@ -1,159 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using MoonCore.Extended.Abstractions; -using Microsoft.AspNetCore.Authorization; -using MoonCore.Common; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Mappers; -using MoonlightServers.Shared.Http.Requests.Admin.StarDockerImages; -using MoonlightServers.Shared.Http.Responses.Admin.StarDockerImages; - -namespace MoonlightServers.ApiServer.Http.Controllers.Admin.Stars; - -[ApiController] -[Route("api/admin/servers/stars/{starId:int}/dockerImages")] -public class StarDockerImagesController : Controller -{ - private readonly DatabaseRepository StarRepository; - private readonly DatabaseRepository DockerImageRepository; - - public StarDockerImagesController( - DatabaseRepository starRepository, - DatabaseRepository dockerImageRepository - ) - { - StarRepository = starRepository; - DockerImageRepository = dockerImageRepository; - } - - [HttpGet] - [Authorize(Policy = "permissions:admin.servers.stars.get")] - public async Task>> GetAsync( - [FromRoute] int starId, - [FromQuery] int startIndex, - [FromQuery] int count - ) - { - var starExists = StarRepository - .Get() - .Any(x => x.Id == starId); - - if (!starExists) - return Problem("No star with this id found", statusCode: 404); - - var query = DockerImageRepository - .Get() - .Where(x => x.Star.Id == starId); - - var totalCount = await query.CountAsync(); - - var dockerImages = await query - .OrderBy(x => x.Id) - .Skip(startIndex) - .Take(count) - .AsNoTracking() - .ProjectToAdminResponse() - .ToArrayAsync(); - - return new CountedData() - { - Items = dockerImages, - TotalCount = totalCount - }; - } - - [HttpGet("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.stars.read")] - public async Task> GetSingleAsync([FromRoute] int starId, [FromRoute] int id) - { - var starExists = StarRepository - .Get() - .Any(x => x.Id == starId); - - if (!starExists) - return Problem("No star with this id found", statusCode: 404); - - var dockerImage = await DockerImageRepository - .Get() - .Where(x => x.Id == id && x.Star.Id == starId) - .ProjectToAdminResponse() - .FirstOrDefaultAsync(); - - if (dockerImage == null) - return Problem("No star docker image with this id found", statusCode: 404); - - return dockerImage; - } - - [HttpPost] - [Authorize(Policy = "permissions:admin.servers.stars.write")] - public async Task> CreateAsync( - [FromRoute] int starId, - [FromBody] CreateStarDockerImageRequest request - ) - { - var star = await StarRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == starId); - - if (star == null) - return Problem("No star with this id found", statusCode: 404); - - var dockerImage = DockerImageMapper.ToDockerImage(request); - dockerImage.Star = star; - - var finalDockerImage = await DockerImageRepository.AddAsync(dockerImage); - - return DockerImageMapper.ToAdminResponse(finalDockerImage); - } - - [HttpPatch("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.stars.write")] - public async Task> UpdateAsync( - [FromRoute] int starId, - [FromRoute] int id, - [FromBody] UpdateStarDockerImageRequest request - ) - { - var starExists = StarRepository - .Get() - .Any(x => x.Id == starId); - - if (!starExists) - return Problem("No star with this id found", statusCode: 404); - - var dockerImage = await DockerImageRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == id && x.Star.Id == starId); - - if (dockerImage == null) - return Problem("No star docker image with this id found", statusCode: 404); - - DockerImageMapper.Merge(request, dockerImage); - await DockerImageRepository.UpdateAsync(dockerImage); - - return DockerImageMapper.ToAdminResponse(dockerImage); - } - - [HttpDelete("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.stars.write")] - public async Task DeleteAsync([FromRoute] int starId, [FromRoute] int id) - { - var starExists = StarRepository - .Get() - .Any(x => x.Id == starId); - - if (!starExists) - return Problem("No star with this id found", statusCode: 404); - - var dockerImage = await DockerImageRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == id && x.Star.Id == starId); - - if (dockerImage == null) - return Problem("No star docker image with this id found", statusCode: 404); - - await DockerImageRepository.RemoveAsync(dockerImage); - return NoContent(); - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarImportExportController.cs b/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarImportExportController.cs deleted file mode 100644 index 53102f5..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarImportExportController.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Text; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Authorization; -using MoonCore.Exceptions; -using MoonlightServers.ApiServer.Mappers; -using MoonlightServers.ApiServer.Services; -using MoonlightServers.Shared.Http.Responses.Admin.Stars; - -namespace MoonlightServers.ApiServer.Http.Controllers.Admin.Stars; - -[ApiController] -[Route("api/admin/servers/stars")] -public class StarImportExportController : Controller -{ - private readonly StarImportExportService ImportExportService; - - public StarImportExportController(StarImportExportService importExportService) - { - ImportExportService = importExportService; - } - - [HttpGet("{starId:int}/export")] - [Authorize(Policy = "permissions:admin.servers.stars.get")] - public async Task ExportAsync([FromRoute] int starId) - { - var exportedStar = await ImportExportService.ExportAsync(starId); - return Content(exportedStar, "application/json"); - } - - [HttpPost("import")] - [Authorize(Policy = "permissions:admin.servers.stars.create")] - public async Task ImportAsync() - { - if (Request.Form.Files.Count == 0) - throw new HttpApiException("No file to import provided", 400); - - if (Request.Form.Files.Count > 1) - throw new HttpApiException("Only one file to import allowed", 400); - - var file = Request.Form.Files[0]; - await using var stream = file.OpenReadStream(); - using var sr = new StreamReader(stream, Encoding.UTF8); - var content = await sr.ReadToEndAsync(); - - var star = await ImportExportService.ImportAsync(content); - - return StarMapper.ToAdminResponse(star); - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarVariablesController.cs b/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarVariablesController.cs deleted file mode 100644 index 42874b8..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarVariablesController.cs +++ /dev/null @@ -1,160 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Microsoft.AspNetCore.Authorization; -using MoonCore.Common; -using MoonCore.Exceptions; -using MoonCore.Extended.Abstractions; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Mappers; -using MoonlightServers.Shared.Http.Requests.Admin.StarVariables; -using MoonlightServers.Shared.Http.Responses.Admin.StarVariables; - -namespace MoonlightServers.ApiServer.Http.Controllers.Admin.Stars; - -[ApiController] -[Route("api/admin/servers/stars/{starId:int}/variables")] -public class StarVariablesController : Controller -{ - private readonly DatabaseRepository StarRepository; - private readonly DatabaseRepository VariableRepository; - - public StarVariablesController( - DatabaseRepository starRepository, - DatabaseRepository variableRepository) - { - StarRepository = starRepository; - VariableRepository = variableRepository; - } - - [HttpGet] - [Authorize(Policy = "permissions:admin.servers.stars.get")] - public async Task>> GetAsync( - [FromRoute] int starId, - [FromQuery] int startIndex, - [FromQuery] int count - ) - { - if (count > 100) - return Problem("Only 100 items can be fetched at a time", statusCode: 400); - - var starExists = StarRepository - .Get() - .Any(x => x.Id == starId); - - if (!starExists) - return Problem("No star with this id found", statusCode: 404); - - var query = VariableRepository - .Get() - .Where(x => x.Star.Id == starId); - - var totalCount = await query.CountAsync(); - - var variables = await query - .OrderBy(x => x.Id) - .Skip(startIndex) - .Take(count) - .AsNoTracking() - .ProjectToAdminResponse() - .ToArrayAsync(); - - return new CountedData() - { - Items = variables, - TotalCount = totalCount - }; - } - - [HttpGet("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.stars.get")] - public async Task GetSingleAsync( - [FromRoute] int starId, - [FromRoute] int id - ) - { - var starExists = StarRepository - .Get() - .Any(x => x.Id == starId); - - if (!starExists) - throw new HttpApiException("No star with this id found", 404); - - var starVariable = await VariableRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == id && x.Star.Id == starId); - - if (starVariable == null) - throw new HttpApiException("No variable with this id found", 404); - - return StarVariableMapper.ToAdminResponse(starVariable); - } - - [HttpPost("")] - [Authorize(Policy = "permissions:admin.servers.stars.create")] - public async Task CreateAsync([FromRoute] int starId, - [FromBody] CreateStarVariableRequest request) - { - var star = StarRepository - .Get() - .FirstOrDefault(x => x.Id == starId); - - if (star == null) - throw new HttpApiException("No star with this id found", 404); - - var starVariable = StarVariableMapper.ToStarVariable(request); - starVariable.Star = star; - - await VariableRepository.AddAsync(starVariable); - - return StarVariableMapper.ToAdminResponse(starVariable); - } - - [HttpPatch("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.stars.update")] - public async Task UpdateAsync( - [FromRoute] int starId, - [FromRoute] int id, - [FromBody] UpdateStarVariableRequest request - ) - { - var starExists = StarRepository - .Get() - .Any(x => x.Id == starId); - - if (!starExists) - throw new HttpApiException("No star with this id found", 404); - - var starVariable = await VariableRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == id && x.Star.Id == starId); - - if (starVariable == null) - throw new HttpApiException("No variable with this id found", 404); - - StarVariableMapper.Merge(request, starVariable); - await VariableRepository.UpdateAsync(starVariable); - - return StarVariableMapper.ToAdminResponse(starVariable); - } - - [HttpDelete("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.stars.delete")] - public async Task DeleteAsync([FromRoute] int starId, [FromRoute] int id) - { - var starExists = StarRepository - .Get() - .Any(x => x.Id == starId); - - if (!starExists) - throw new HttpApiException("No star with this id found", 404); - - var starVariable = await VariableRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == id && x.Star.Id == starId); - - if (starVariable == null) - throw new HttpApiException("No variable with this id found", 404); - - await VariableRepository.RemoveAsync(starVariable); - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarsController.cs b/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarsController.cs deleted file mode 100644 index e4e5779..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarsController.cs +++ /dev/null @@ -1,128 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Microsoft.AspNetCore.Authorization; -using MoonCore.Common; -using MoonCore.Extended.Abstractions; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Mappers; -using MoonlightServers.Shared.Http.Requests.Admin.Stars; -using MoonlightServers.Shared.Http.Responses.Admin.Stars; - -namespace MoonlightServers.ApiServer.Http.Controllers.Admin.Stars; - -[ApiController] -[Route("api/admin/servers/stars")] -public class StarsController : Controller -{ - private readonly DatabaseRepository StarRepository; - - public StarsController(DatabaseRepository starRepository) - { - StarRepository = starRepository; - } - - [HttpGet] - [Authorize(Policy = "permissions:admin.servers.stars.read")] - public async Task>> GetAsync( - [FromQuery] int startIndex, - [FromQuery] int count - ) - { - if (count > 100) - return Problem("Only 100 items can be fetched at a time", statusCode: 400); - - var totalCount = await StarRepository.Get().CountAsync(); - - var stars = await StarRepository - .Get() - .OrderBy(x => x.Id) - .Skip(startIndex) - .Take(count) - .AsNoTracking() - .ProjectToAdminResponse() - .ToArrayAsync(); - - return new CountedData() - { - Items = stars, - TotalCount = totalCount - }; - } - - [HttpGet("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.stars.read")] - public async Task> GetSingleAsync([FromRoute] int id) - { - var star = await StarRepository - .Get() - .AsNoTracking() - .ProjectToAdminResponse() - .FirstOrDefaultAsync(x => x.Id == id); - - if (star == null) - return Problem("No star with that id found", statusCode: 404); - - return star; - } - - [HttpPost] - [Authorize(Policy = "permissions:admin.servers.stars.create")] - public async Task> CreateAsync([FromBody] CreateStarRequest request) - { - var star = StarMapper.ToStar(request); - - // Default values - star.DonateUrl = null; - star.UpdateUrl = null; - star.Version = "1.0.0"; - star.StartupCommand = "echo Starting up :)"; - star.StopCommand = "^C"; - star.OnlineDetection = "Online text"; - star.InstallShell = "/bin/bash"; - star.InstallDockerImage = "debian:latest"; - star.InstallScript = "echo Installing..."; - star.RequiredAllocations = 1; - star.AllowDockerImageChange = false; - star.DefaultDockerImage = -1; - star.ParseConfiguration = "[]"; - - var finalStar = await StarRepository.AddAsync(star); - - return StarMapper.ToAdminResponse(finalStar); - } - - [HttpPatch("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.stars.update")] - public async Task> UpdateAsync( - [FromRoute] int id, - [FromBody] UpdateStarRequest request - ) - { - var star = await StarRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == id); - - if (star == null) - return Problem("No star with that id found", statusCode: 404); - - StarMapper.Merge(request, star); - await StarRepository.UpdateAsync(star); - - return StarMapper.ToAdminResponse(star); - } - - [HttpDelete("{id:int}")] - [Authorize(Policy = "permissions:admin.servers.stars.delete")] - public async Task DeleteAsync([FromRoute] int id) - { - var star = await StarRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == id); - - if (star == null) - return Problem("No star with that id found", statusCode: 404); - - await StarRepository.RemoveAsync(star); - return NoContent(); - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Client/FilesController.cs b/MoonlightServers.ApiServer/Http/Controllers/Client/FilesController.cs deleted file mode 100644 index 7211ac5..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Client/FilesController.cs +++ /dev/null @@ -1,227 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using MoonCore.Extended.Abstractions; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Services; -using MoonlightServers.DaemonShared.Enums; -using MoonlightServers.Shared.Constants; -using MoonlightServers.Shared.Enums; -using MoonlightServers.Shared.Http.Requests.Client.Servers.Files; -using MoonlightServers.Shared.Http.Responses.Client.Servers.Files; - -namespace MoonlightServers.ApiServer.Http.Controllers.Client; - -[Authorize] -[ApiController] -[Route("api/client/servers/{serverId:int}/files")] -public class FilesController : Controller -{ - private readonly DatabaseRepository ServerRepository; - private readonly ServerFileSystemService ServerFileSystemService; - private readonly NodeService NodeService; - private readonly ServerAuthorizeService AuthorizeService; - - public FilesController( - DatabaseRepository serverRepository, - ServerFileSystemService serverFileSystemService, - NodeService nodeService, - ServerAuthorizeService authorizeService - ) - { - ServerRepository = serverRepository; - ServerFileSystemService = serverFileSystemService; - NodeService = nodeService; - AuthorizeService = authorizeService; - } - - [HttpGet("list")] - public async Task> ListAsync([FromRoute] int serverId, [FromQuery] string path) - { - var server = await GetServerByIdAsync(serverId, ServerPermissionLevel.Read); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - var entries = await ServerFileSystemService.ListAsync(server.Value, path); - - return entries.Select(x => new ServerFilesEntryResponse() - { - Name = x.Name, - Size = x.Size, - IsFolder = x.IsFolder, - CreatedAt = x.CreatedAt, - UpdatedAt = x.UpdatedAt - }).ToArray(); - } - - [HttpPost("move")] - public async Task MoveAsync([FromRoute] int serverId, [FromQuery] string oldPath, [FromQuery] string newPath) - { - var server = await GetServerByIdAsync(serverId, ServerPermissionLevel.ReadWrite); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - await ServerFileSystemService.MoveAsync(server.Value, oldPath, newPath); - return NoContent(); - } - - [HttpDelete("delete")] - public async Task DeleteAsync([FromRoute] int serverId, [FromQuery] string path) - { - var server = await GetServerByIdAsync(serverId, ServerPermissionLevel.ReadWrite); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - await ServerFileSystemService.DeleteAsync(server.Value, path); - return NoContent(); - } - - [HttpPost("mkdir")] - public async Task MkdirAsync([FromRoute] int serverId, [FromQuery] string path) - { - var server = await GetServerByIdAsync(serverId, ServerPermissionLevel.ReadWrite); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - await ServerFileSystemService.MkdirAsync(server.Value, path); - return NoContent(); - } - - [HttpPost("touch")] - public async Task TouchAsync([FromRoute] int serverId, [FromQuery] string path) - { - var server = await GetServerByIdAsync(serverId, ServerPermissionLevel.ReadWrite); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - await ServerFileSystemService.MkdirAsync(server.Value, path); - return NoContent(); - } - - [HttpGet("upload")] - public async Task> UploadAsync([FromRoute] int serverId) - { - var serverResult = await GetServerByIdAsync(serverId, ServerPermissionLevel.ReadWrite); - - if (serverResult.Value == null) - return serverResult.Result ?? Problem("Unable to retrieve server"); - - var server = serverResult.Value; - - var accessToken = NodeService.CreateAccessToken( - server.Node, - parameters => - { - parameters.Add("type", "upload"); - parameters.Add("serverId", server.Id); - }, - TimeSpan.FromMinutes(1) - ); - - var url = ""; - - url += server.Node.UseSsl ? "https://" : "http://"; - url += $"{server.Node.Fqdn}:{server.Node.HttpPort}/"; - url += $"api/servers/upload?access_token={accessToken}"; - - return new ServerFilesUploadResponse() - { - UploadUrl = url - }; - } - - [HttpGet("download")] - public async Task> DownloadAsync([FromRoute] int serverId, [FromQuery] string path) - { - var serverResult = await GetServerByIdAsync(serverId, ServerPermissionLevel.Read); - - if (serverResult.Value == null) - return serverResult.Result ?? Problem("Unable to retrieve server"); - - var server = serverResult.Value; - - var accessToken = NodeService.CreateAccessToken( - server.Node, - parameters => - { - parameters.Add("type", "download"); - parameters.Add("path", path); - parameters.Add("serverId", server.Id); - }, - TimeSpan.FromMinutes(1) - ); - - var url = ""; - - url += server.Node.UseSsl ? "https://" : "http://"; - url += $"{server.Node.Fqdn}:{server.Node.HttpPort}/"; - url += $"api/servers/download?access_token={accessToken}"; - - return new ServerFilesDownloadResponse() - { - DownloadUrl = url - }; - } - - [HttpPost("compress")] - public async Task CompressAsync([FromRoute] int serverId, [FromBody] ServerFilesCompressRequest request) - { - var server = await GetServerByIdAsync(serverId, ServerPermissionLevel.ReadWrite); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - if (!Enum.TryParse(request.Type, true, out CompressType type)) - return Problem("Invalid compress type provided", statusCode: 400); - - await ServerFileSystemService.CompressAsync(server.Value, type, request.Items, request.Destination); - return Ok(); - } - - [HttpPost("decompress")] - public async Task DecompressAsync([FromRoute] int serverId, [FromBody] ServerFilesDecompressRequest request) - { - var server = await GetServerByIdAsync(serverId, ServerPermissionLevel.ReadWrite); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - if (!Enum.TryParse(request.Type, true, out CompressType type)) - return Problem("Invalid decompress type provided", statusCode: 400); - - await ServerFileSystemService.DecompressAsync(server.Value, type, request.Path, request.Destination); - return NoContent(); - } - - private async Task> GetServerByIdAsync(int serverId, ServerPermissionLevel level) - { - var server = await ServerRepository - .Get() - .Include(x => x.Node) - .FirstOrDefaultAsync(x => x.Id == serverId); - - if (server == null) - return Problem("No server with this id found", statusCode: 404); - - var authorizeResult = await AuthorizeService.AuthorizeAsync( - User, server, - ServerPermissionConstants.Files, - level - ); - - if (!authorizeResult.Succeeded) - { - return Problem( - authorizeResult.Message ?? "No permission for the requested resource", - statusCode: 403 - ); - } - - return server; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Client/PowerController.cs b/MoonlightServers.ApiServer/Http/Controllers/Client/PowerController.cs deleted file mode 100644 index 9bf9ecb..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Client/PowerController.cs +++ /dev/null @@ -1,97 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using MoonCore.Extended.Abstractions; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Services; -using MoonlightServers.Shared.Constants; -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.ApiServer.Http.Controllers.Client; - -[ApiController] -[Authorize] -[Route("api/client/servers/{serverId:int}")] -public class PowerController : Controller -{ - private readonly DatabaseRepository ServerRepository; - private readonly ServerService ServerService; - private readonly ServerAuthorizeService AuthorizeService; - - public PowerController( - DatabaseRepository serverRepository, - ServerService serverService, - ServerAuthorizeService authorizeService - ) - { - ServerRepository = serverRepository; - ServerService = serverService; - AuthorizeService = authorizeService; - } - - [HttpPost("start")] - [Authorize] - public async Task StartAsync([FromRoute] int serverId) - { - var server = await GetServerByIdAsync(serverId); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - await ServerService.StartAsync(server.Value); - return NoContent(); - } - - [HttpPost("stop")] - [Authorize] - public async Task StopAsync([FromRoute] int serverId) - { - var server = await GetServerByIdAsync(serverId); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - await ServerService.StopAsync(server.Value); - return NoContent(); - } - - [HttpPost("kill")] - [Authorize] - public async Task KillAsync([FromRoute] int serverId) - { - var server = await GetServerByIdAsync(serverId); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - await ServerService.KillAsync(server.Value); - return NoContent(); - } - - private async Task> GetServerByIdAsync(int serverId) - { - var server = await ServerRepository - .Get() - .Include(x => x.Node) - .FirstOrDefaultAsync(x => x.Id == serverId); - - if (server == null) - return Problem("No server with this id found", statusCode: 404); - - var authorizeResult = await AuthorizeService.AuthorizeAsync( - User, server, - ServerPermissionConstants.Power, - ServerPermissionLevel.ReadWrite - ); - - if (!authorizeResult.Succeeded) - { - return Problem( - authorizeResult.Message ?? "No permission for the requested resource", - statusCode: 403 - ); - } - - return server; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Client/ServersController.cs b/MoonlightServers.ApiServer/Http/Controllers/Client/ServersController.cs deleted file mode 100644 index 2950bbc..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Client/ServersController.cs +++ /dev/null @@ -1,380 +0,0 @@ -using System.Security.Claims; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using MoonCore.Common; -using MoonCore.Extended.Abstractions; -using Moonlight.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Extensions; -using MoonlightServers.ApiServer.Mappers; -using MoonlightServers.ApiServer.Services; -using MoonlightServers.Shared.Constants; -using MoonlightServers.Shared.Enums; -using MoonlightServers.Shared.Http.Requests.Client.Servers; -using MoonlightServers.Shared.Http.Responses.Client.Servers; -using MoonlightServers.Shared.Http.Responses.Client.Servers.Allocations; - -namespace MoonlightServers.ApiServer.Http.Controllers.Client; - -[Authorize] -[ApiController] -[Route("api/client/servers")] -public class ServersController : Controller -{ - private readonly ServerService ServerService; - private readonly DatabaseRepository ServerRepository; - private readonly DatabaseRepository ShareRepository; - private readonly DatabaseRepository UserRepository; - private readonly NodeService NodeService; - private readonly ServerAuthorizeService AuthorizeService; - - public ServersController( - DatabaseRepository serverRepository, - NodeService nodeService, - ServerService serverService, - ServerAuthorizeService authorizeService, - DatabaseRepository shareRepository, - DatabaseRepository userRepository - ) - { - ServerRepository = serverRepository; - NodeService = nodeService; - ServerService = serverService; - AuthorizeService = authorizeService; - ShareRepository = shareRepository; - UserRepository = userRepository; - } - - [HttpGet] - public async Task>> GetAllAsync( - [FromQuery] int startIndex, - [FromQuery] int count - ) - { - if (count > 100) - return Problem("Only 100 items can be fetched at a time", statusCode: 400); - - var userIdClaim = User.FindFirstValue("UserId"); - - if (string.IsNullOrEmpty(userIdClaim)) - return Problem("Only users are able to use this endpoint", statusCode: 400); - - var userId = int.Parse(userIdClaim); - - var query = ServerRepository - .Get() - .Include(x => x.Allocations) - .Include(x => x.Star) - .Include(x => x.Node) - .Where(x => x.OwnerId == userId); - - var totalCount = await query.CountAsync(); - - var items = await query - .OrderBy(x => x.Id) - .Skip(startIndex) - .Take(count) - .AsNoTracking() - .ToArrayAsync(); - - var mappedItems = items.Select(x => new ServerDetailResponse() - { - Id = x.Id, - Name = x.Name, - NodeName = x.Node.Name, - StarName = x.Star.Name, - Cpu = x.Cpu, - Memory = x.Memory, - Disk = x.Disk, - Allocations = x.Allocations.Select(y => new AllocationDetailResponse() - { - Id = y.Id, - Port = y.Port, - IpAddress = y.IpAddress - }).ToArray() - }).ToArray(); - - return new CountedData() - { - Items = mappedItems, - TotalCount = totalCount - }; - } - - [HttpGet("shared")] - public async Task>> GetAllSharedAsync( - [FromQuery] int startIndex, - [FromQuery] int count - ) - { - if (count > 100) - return Problem("Only 100 items can be fetched at a time", statusCode: 400); - - var userIdClaim = User.FindFirstValue("UserId"); - - if (string.IsNullOrEmpty(userIdClaim)) - return Problem("Only users are able to use this endpoint", statusCode: 400); - - var userId = int.Parse(userIdClaim); - - var query = ShareRepository - .Get() - .Include(x => x.Server) - .ThenInclude(x => x.Node) - .Include(x => x.Server) - .ThenInclude(x => x.Star) - .Include(x => x.Server) - .ThenInclude(x => x.Allocations) - .Where(x => x.UserId == userId); - - var totalCount = await query.CountAsync(); - - var items = await query - .OrderBy(x => x.Id) - .Skip(startIndex) - .Take(count) - .ToArrayAsync(); - - var ownerIds = items - .Select(x => x.Server.OwnerId) - .Distinct() - .ToArray(); - - var owners = await UserRepository - .Get() - .Where(x => ownerIds.Contains(x.Id)) - .ToArrayAsync(); - - var mappedItems = items.Select(x => new ServerDetailResponse() - { - Id = x.Server.Id, - Name = x.Server.Name, - NodeName = x.Server.Node.Name, - StarName = x.Server.Star.Name, - Cpu = x.Server.Cpu, - Memory = x.Server.Memory, - Disk = x.Server.Disk, - Allocations = x.Server.Allocations.Select(y => new AllocationDetailResponse() - { - Id = y.Id, - Port = y.Port, - IpAddress = y.IpAddress - }).ToArray(), - Share = new() - { - SharedBy = owners.First(y => y.Id == x.Server.OwnerId).Username, - Permissions = ShareMapper.MapToPermissionLevels(x.Content) - } - }).ToArray(); - - return new CountedData() - { - Items = mappedItems, - TotalCount = totalCount - }; - } - - [HttpGet("{serverId:int}")] - public async Task> GetAsync([FromRoute] int serverId) - { - var server = await ServerRepository - .Get() - .Include(x => x.Allocations) - .Include(x => x.Star) - .Include(x => x.Node) - .FirstOrDefaultAsync(x => x.Id == serverId); - - if (server == null) - return Problem("No server with this id found", statusCode: 404); - - var authorizationResult = await AuthorizeService.AuthorizeAsync( - User, - server, - String.Empty, - ServerPermissionLevel.None - ); - - if (!authorizationResult.Succeeded) - { - return Problem( - authorizationResult.Message ?? "No server with this id found", - statusCode: 404 - ); - } - - // Create mapped response - var response = new ServerDetailResponse() - { - Id = server.Id, - Name = server.Name, - NodeName = server.Node.Name, - StarName = server.Star.Name, - Cpu = server.Cpu, - Memory = server.Memory, - Disk = server.Disk, - Allocations = server.Allocations.Select(y => new AllocationDetailResponse() - { - Id = y.Id, - Port = y.Port, - IpAddress = y.IpAddress - }).ToArray() - }; - - // Handle requests on shared servers - if (authorizationResult.Share != null) - { - var owner = await UserRepository - .Get() - .FirstAsync(x => x.Id == server.OwnerId); - - response.Share = new() - { - SharedBy = owner.Username, - Permissions = ShareMapper.MapToPermissionLevels(authorizationResult.Share.Content) - }; - } - - return response; - } - - [HttpGet("{serverId:int}/status")] - public async Task> GetStatusAsync([FromRoute] int serverId) - { - var server = await GetServerByIdAsync( - serverId, - ServerPermissionConstants.Console, - ServerPermissionLevel.None - ); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - var status = await ServerService.GetStatusAsync(server.Value); - - return new ServerStatusResponse() - { - State = status.State.ToServerPowerState() - }; - } - - [HttpGet("{serverId:int}/ws")] - public async Task> GetWebSocketAsync([FromRoute] int serverId) - { - var serverResult = await GetServerByIdAsync( - serverId, - ServerPermissionConstants.Console, - ServerPermissionLevel.Read - ); - - if (serverResult.Value == null) - return serverResult.Result ?? Problem("Unable to retrieve server"); - - var server = serverResult.Value; - - // TODO: Handle transparent node proxy - - var accessToken = NodeService.CreateAccessToken(server.Node, parameters => - { - parameters.Add("type", "websocket"); - parameters.Add("serverId", server.Id); - }, TimeSpan.FromMinutes(15)); // TODO: Configurable - - var url = ""; - - url += server.Node.UseSsl ? "https://" : "http://"; - url += $"{server.Node.Fqdn}:{server.Node.HttpPort}/api/servers/ws"; - - return new ServerWebSocketResponse() - { - Target = url, - AccessToken = accessToken - }; - } - - [HttpGet("{serverId:int}/logs")] - public async Task> GetLogsAsync([FromRoute] int serverId) - { - var server = await GetServerByIdAsync( - serverId, - ServerPermissionConstants.Console, - ServerPermissionLevel.Read - ); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - var logs = await ServerService.GetLogsAsync(server.Value); - - return new ServerLogsResponse() - { - Messages = logs.Messages - }; - } - - [HttpGet("{serverId:int}/stats")] - public async Task> GetStatsAsync([FromRoute] int serverId) - { - var server = await GetServerByIdAsync( - serverId, - ServerPermissionConstants.Console, - ServerPermissionLevel.Read - ); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - var stats = await ServerService.GetStatsAsync(server.Value); - - return new ServerStatsResponse() - { - CpuUsage = stats.CpuUsage, - MemoryUsage = stats.MemoryUsage, - NetworkRead = stats.NetworkRead, - NetworkWrite = stats.NetworkWrite, - IoRead = stats.IoRead, - IoWrite = stats.IoWrite - }; - } - - [HttpPost("{serverId:int}/command")] - public async Task CommandAsync([FromRoute] int serverId, [FromBody] ServerCommandRequest request) - { - var server = await GetServerByIdAsync( - serverId, - ServerPermissionConstants.Console, - ServerPermissionLevel.ReadWrite - ); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - await ServerService.RunCommandAsync(server.Value, request.Command); - - return NoContent(); - } - - private async Task> GetServerByIdAsync(int serverId, string permissionId, - ServerPermissionLevel level) - { - var server = await ServerRepository - .Get() - .Include(x => x.Node) - .FirstOrDefaultAsync(x => x.Id == serverId); - - if (server == null) - return Problem("No server with this id found", statusCode: 404); - - var authorizeResult = await AuthorizeService.AuthorizeAsync(User, server, permissionId, level); - - if (!authorizeResult.Succeeded) - { - return Problem( - authorizeResult.Message ?? "No permission for the requested resource", - statusCode: 403 - ); - } - - return server; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Client/SettingsController.cs b/MoonlightServers.ApiServer/Http/Controllers/Client/SettingsController.cs deleted file mode 100644 index 141327b..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Client/SettingsController.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using MoonCore.Extended.Abstractions; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Services; -using MoonlightServers.Shared.Constants; -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.ApiServer.Http.Controllers.Client; - -[Authorize] -[ApiController] -[Route("api/client/servers")] -public class SettingsController : Controller -{ - private readonly ServerService ServerService; - private readonly DatabaseRepository ServerRepository; - private readonly ServerAuthorizeService AuthorizeService; - - public SettingsController( - ServerService serverService, - DatabaseRepository serverRepository, - ServerAuthorizeService authorizeService - ) - { - ServerService = serverService; - ServerRepository = serverRepository; - AuthorizeService = authorizeService; - } - - [HttpPost("{serverId:int}/install")] - [Authorize] - public async Task InstallAsync([FromRoute] int serverId) - { - var server = await GetServerByIdAsync(serverId); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - await ServerService.InstallAsync(server.Value); - return NoContent(); - } - - private async Task> GetServerByIdAsync(int serverId) - { - var server = await ServerRepository - .Get() - .Include(x => x.Node) - .FirstOrDefaultAsync(x => x.Id == serverId); - - if (server == null) - return Problem("No server with this id found", statusCode: 404); - - var authorizeResult = await AuthorizeService.AuthorizeAsync( - User, server, - ServerPermissionConstants.Settings, - ServerPermissionLevel.ReadWrite - ); - - if (!authorizeResult.Succeeded) - { - return Problem( - authorizeResult.Message ?? "No permission for the requested resource", - statusCode: 403 - ); - } - - return server; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Client/SharesController.cs b/MoonlightServers.ApiServer/Http/Controllers/Client/SharesController.cs deleted file mode 100644 index 426b038..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Client/SharesController.cs +++ /dev/null @@ -1,251 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using MoonCore.Common; -using MoonCore.Extended.Abstractions; -using Moonlight.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Mappers; -using MoonlightServers.ApiServer.Services; -using MoonlightServers.Shared.Constants; -using MoonlightServers.Shared.Enums; -using MoonlightServers.Shared.Http.Requests.Client.Servers.Shares; -using MoonlightServers.Shared.Http.Responses.Client.Servers.Shares; - -namespace MoonlightServers.ApiServer.Http.Controllers.Client; - -[Authorize] -[ApiController] -[Route("api/client/servers/{serverId:int}/shares")] -public class SharesController : Controller -{ - private readonly DatabaseRepository ServerRepository; - private readonly DatabaseRepository ShareRepository; - private readonly DatabaseRepository UserRepository; - private readonly ServerAuthorizeService AuthorizeService; - - public SharesController( - DatabaseRepository serverRepository, - DatabaseRepository shareRepository, - DatabaseRepository userRepository, - ServerAuthorizeService authorizeService - ) - { - ServerRepository = serverRepository; - ShareRepository = shareRepository; - UserRepository = userRepository; - AuthorizeService = authorizeService; - } - - [HttpGet] - public async Task>> GetAllAsync( - [FromRoute] int serverId, - [FromQuery] int startIndex, - [FromQuery] int count - ) - { - if (count > 100) - return Problem("Only 100 items can be fetched at a time", statusCode: 400); - - var server = await GetServerByIdAsync(serverId); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - var query = ShareRepository - .Get() - .Where(x => x.Server.Id == server.Value.Id); - - var totalCount = await query.CountAsync(); - - var items = await query - .OrderBy(x => x.Id) - .Skip(startIndex) - .Take(count) - .ToArrayAsync(); - - var userIds = items - .Select(x => x.UserId) - .Distinct() - .ToArray(); - - var users = await UserRepository - .Get() - .Where(x => userIds.Contains(x.Id)) - .ToArrayAsync(); - - var mappedItems = items.Select(x => new ServerShareResponse() - { - Id = x.Id, - Username = users.First(y => y.Id == x.UserId).Username, - Permissions = ShareMapper.MapToPermissionLevels(x.Content) - }).ToArray(); - - return new CountedData() - { - Items = mappedItems, - TotalCount = totalCount - }; - } - - [HttpGet("{id:int}")] - public async Task> GetAsync( - [FromRoute] int serverId, - [FromRoute] int id - ) - { - var server = await GetServerByIdAsync(serverId); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - var share = await ShareRepository - .Get() - .FirstOrDefaultAsync(x => x.Server.Id == server.Value.Id && x.Id == id); - - if (share == null) - return Problem("A share with that id cannot be found", statusCode: 404); - - var user = await UserRepository - .Get() - .FirstAsync(x => x.Id == share.UserId); - - var mappedItem = new ServerShareResponse() - { - Id = share.Id, - Username = user.Username, - Permissions = ShareMapper.MapToPermissionLevels(share.Content) - }; - - return mappedItem; - } - - [HttpPost] - public async Task> CreateAsync( - [FromRoute] int serverId, - [FromBody] CreateShareRequest request - ) - { - var server = await GetServerByIdAsync(serverId); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - var user = await UserRepository - .Get() - .FirstOrDefaultAsync(x => x.Username == request.Username); - - if (user == null) - return Problem("A user with that username could not be found", statusCode: 400); - - var share = new ServerShare() - { - Server = server.Value, - Content = ShareMapper.MapToServerShareContent(request.Permissions), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow, - UserId = user.Id - }; - - var finalShare = await ShareRepository.AddAsync(share); - - var mappedItem = new ServerShareResponse() - { - Id = finalShare.Id, - Username = user.Username, - Permissions = ShareMapper.MapToPermissionLevels(finalShare.Content) - }; - - return mappedItem; - } - - [HttpPatch("{id:int}")] - public async Task> UpdateAsync( - [FromRoute] int serverId, - [FromRoute] int id, - [FromBody] UpdateShareRequest request - ) - { - var server = await GetServerByIdAsync(serverId); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - var share = await ShareRepository - .Get() - .FirstOrDefaultAsync(x => x.Server.Id == server.Value.Id && x.Id == id); - - if (share == null) - return Problem("A share with that id cannot be found", statusCode: 404); - - share.Content = ShareMapper.MapToServerShareContent(request.Permissions); - - share.UpdatedAt = DateTime.UtcNow; - - await ShareRepository.UpdateAsync(share); - - var user = await UserRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == share.UserId); - - if (user == null) - return Problem("A user with that id could not be found", statusCode: 400); - - var mappedItem = new ServerShareResponse() - { - Id = share.Id, - Username = user.Username, - Permissions = ShareMapper.MapToPermissionLevels(share.Content) - }; - - return mappedItem; - } - - [HttpDelete("{id:int}")] - public async Task DeleteAsync( - [FromRoute] int serverId, - [FromRoute] int id - ) - { - var server = await GetServerByIdAsync(serverId); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - var share = await ShareRepository - .Get() - .FirstOrDefaultAsync(x => x.Server.Id == server.Value.Id && x.Id == id); - - if (share == null) - return Problem("A share with that id cannot be found", statusCode: 404); - - await ShareRepository.RemoveAsync(share); - return NoContent(); - } - - private async Task> GetServerByIdAsync(int serverId) - { - var server = await ServerRepository - .Get() - .FirstOrDefaultAsync(x => x.Id == serverId); - - if (server == null) - return Problem("No server with this id found", statusCode: 404); - - var authorizeResult = await AuthorizeService.AuthorizeAsync( - User, server, - ServerPermissionConstants.Shares, - ServerPermissionLevel.ReadWrite - ); - - if (!authorizeResult.Succeeded) - { - return Problem( - authorizeResult.Message ?? "No permission for the requested resource", - statusCode: 403 - ); - } - - return server; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Client/VariablesController.cs b/MoonlightServers.ApiServer/Http/Controllers/Client/VariablesController.cs deleted file mode 100644 index 096d48b..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Client/VariablesController.cs +++ /dev/null @@ -1,205 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using MoonCore.Common; -using MoonCore.Exceptions; -using MoonCore.Extended.Abstractions; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Services; -using MoonlightServers.Shared.Constants; -using MoonlightServers.Shared.Enums; -using MoonlightServers.Shared.Http.Requests.Client.Servers.Variables; -using MoonlightServers.Shared.Http.Responses.Client.Servers.Variables; - -namespace MoonlightServers.ApiServer.Http.Controllers.Client; - -[Authorize] -[ApiController] -[Route("api/client/servers/{serverId:int}/variables")] -public class VariablesController : Controller -{ - private readonly DatabaseRepository ServerRepository; - private readonly DatabaseRepository ServerVariableRepository; - private readonly DatabaseRepository StarVariableRepository; - private readonly ServerAuthorizeService AuthorizeService; - - public VariablesController( - DatabaseRepository serverRepository, - ServerAuthorizeService authorizeService, - DatabaseRepository serverVariableRepository, - DatabaseRepository starVariableRepository - ) - { - ServerRepository = serverRepository; - AuthorizeService = authorizeService; - ServerVariableRepository = serverVariableRepository; - StarVariableRepository = starVariableRepository; - } - - [HttpGet] - public async Task>> GetAsync( - [FromRoute] int serverId, - [FromQuery] int startIndex, - [FromQuery] int count - ) - { - if (count > 100) - return Problem("Only 100 items can be fetched at a time", statusCode: 400); - - var server = await GetServerByIdAsync(serverId, ServerPermissionLevel.Read); - - if (server.Value == null) - return server.Result ?? Problem("Unable to retrieve server"); - - var query = StarVariableRepository - .Get() - .Where(x => x.Star.Id == server.Value.Star.Id); - - var totalCount = await query.CountAsync(); - - var starVariables = await query - .OrderBy(x => x.Id) - .Skip(startIndex) - .Take(count) - .ToArrayAsync(); - - var starVariableKeys = starVariables - .Select(x => x.Key) - .ToArray(); - - var serverVariables = await ServerVariableRepository - .Get() - .Where(x => x.Server.Id == server.Value.Id && starVariableKeys.Contains(x.Key)) - .ToArrayAsync(); - - var responses = starVariables.Select(starVariable => - { - var serverVariable = serverVariables.First(x => x.Key == starVariable.Key); - - return new ServerVariableDetailResponse() - { - Key = starVariable.Key, - Value = serverVariable.Value, - Type = starVariable.Type, - Name = starVariable.Name, - Description = starVariable.Description, - Filter = starVariable.Filter - }; - }).ToArray(); - - return new CountedData() - { - Items = responses, - TotalCount = totalCount - }; - } - - [HttpPut] - public async Task> UpdateSingleAsync( - [FromRoute] int serverId, - [FromBody] UpdateServerVariableRequest request - ) - { - // TODO: Handle filter - - var serverResult = await GetServerByIdAsync(serverId, ServerPermissionLevel.ReadWrite); - - if (serverResult.Value == null) - return serverResult.Result ?? Problem("Unable to retrieve server"); - - var server = serverResult.Value; - - var serverVariable = server.Variables.FirstOrDefault(x => x.Key == request.Key); - var starVariable = server.Star.Variables.FirstOrDefault(x => x.Key == request.Key); - - if (serverVariable == null || starVariable == null) - throw new HttpApiException($"No variable with the key found: {request.Key}", 400); - - serverVariable.Value = request.Value; - await ServerRepository.UpdateAsync(server); - - return new ServerVariableDetailResponse() - { - Key = starVariable.Key, - Value = serverVariable.Value, - Type = starVariable.Type, - Name = starVariable.Name, - Description = starVariable.Description, - Filter = starVariable.Filter - }; - } - - [HttpPatch] - public async Task> UpdateAsync( - [FromRoute] int serverId, - [FromBody] UpdateServerVariableRangeRequest request - ) - { - var serverResult = await GetServerByIdAsync(serverId, ServerPermissionLevel.ReadWrite); - - if (serverResult.Value == null) - return serverResult.Result ?? Problem("Unable to retrieve server"); - - var server = serverResult.Value; - - foreach (var variable in request.Variables) - { - // TODO: Handle filter - - var serverVariable = server.Variables.FirstOrDefault(x => x.Key == variable.Key); - var starVariable = server.Star.Variables.FirstOrDefault(x => x.Key == variable.Key); - - if (serverVariable == null || starVariable == null) - throw new HttpApiException($"No variable with the key found: {variable.Key}", 400); - - serverVariable.Value = variable.Value; - } - - await ServerRepository.UpdateAsync(server); - - return request.Variables.Select(requestVariable => - { - var serverVariable = server.Variables.First(x => x.Key == requestVariable.Key); - var starVariable = server.Star.Variables.First(x => x.Key == requestVariable.Key); - - return new ServerVariableDetailResponse() - { - Key = starVariable.Key, - Value = serverVariable.Value, - Type = starVariable.Type, - Name = starVariable.Name, - Description = starVariable.Description, - Filter = starVariable.Filter - }; - }).ToArray(); - } - - private async Task> GetServerByIdAsync(int serverId, ServerPermissionLevel level) - { - var server = await ServerRepository - .Get() - .Include(x => x.Star) - .ThenInclude(x => x.Variables) - .Include(x => x.Variables) - .FirstOrDefaultAsync(x => x.Id == serverId); - - if (server == null) - return Problem("No server with this id found", statusCode: 404); - - var authorizeResult = await AuthorizeService.AuthorizeAsync( - User, server, - ServerPermissionConstants.Variables, - level - ); - - if (!authorizeResult.Succeeded) - { - return Problem( - authorizeResult.Message ?? "No permission for the requested resource", - statusCode: 403 - ); - } - - return server; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Remote/Nodes/NodeTripController.cs b/MoonlightServers.ApiServer/Http/Controllers/Remote/Nodes/NodeTripController.cs deleted file mode 100644 index 543951e..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Remote/Nodes/NodeTripController.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace MoonlightServers.ApiServer.Http.Controllers.Remote.Nodes; - -[ApiController] -[Route("api/remote/server/node")] -[Authorize(AuthenticationSchemes = "nodeAuthentication")] -public class NodeTripController : Controller -{ - [HttpGet("trip")] - public Task GetAsync() => Task.CompletedTask; -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Remote/ServersController.cs b/MoonlightServers.ApiServer/Http/Controllers/Remote/ServersController.cs deleted file mode 100644 index f47ef28..0000000 --- a/MoonlightServers.ApiServer/Http/Controllers/Remote/ServersController.cs +++ /dev/null @@ -1,196 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using MoonCore.Common; -using MoonCore.Exceptions; -using MoonCore.Extended.Abstractions; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.DaemonShared.PanelSide.Http.Responses; - -namespace MoonlightServers.ApiServer.Http.Controllers.Remote; - -[ApiController] -[Route("api/remote/servers")] -[Authorize(AuthenticationSchemes = "nodeAuthentication")] -public class ServersController : Controller -{ - private readonly DatabaseRepository ServerRepository; - private readonly DatabaseRepository NodeRepository; - private readonly ILogger Logger; - - public ServersController( - DatabaseRepository serverRepository, - DatabaseRepository nodeRepository, - ILogger logger - ) - { - ServerRepository = serverRepository; - NodeRepository = nodeRepository; - Logger = logger; - } - - [HttpGet] - public async Task>> GetAsync( - [FromQuery] int startIndex, - [FromQuery] int count - ) - { - if (count > 100) - return Problem("Only 100 items can be fetched at a time", statusCode: 400); - - // Load the node via the id - var nodeId = int.Parse(User.Claims.First(x => x.Type == "nodeId").Value); - - var node = await NodeRepository - .Get() - .FirstAsync(x => x.Id == nodeId); - - var total = await ServerRepository - .Get() - .Where(x => x.Node.Id == node.Id) - .CountAsync(); - - var servers = await ServerRepository - .Get() - .Where(x => x.Node.Id == node.Id) - .Include(x => x.Star) - .ThenInclude(x => x.DockerImages) - .Include(x => x.Variables) - .Include(x => x.Allocations) - .Skip(startIndex) - .Take(count) - .ToArrayAsync(); - - var serverData = new List(); - - foreach (var server in servers) - { - var convertedData = ConvertToServerData(server); - - if (convertedData == null) - continue; - - serverData.Add(convertedData); - } - - return new CountedData() - { - Items = serverData.ToArray(), - TotalCount = total - }; - } - - [HttpGet("{id:int}")] - public async Task GetAsync([FromRoute] int id) - { - // Load the node via the id - var nodeId = int.Parse(User.Claims.First(x => x.Type == "nodeId").Value); - - var node = await NodeRepository - .Get() - .FirstAsync(x => x.Id == nodeId); - - // Load the server with the star data attached. We filter by the node to ensure the node can only access - // servers linked to it - var server = await ServerRepository - .Get() - .Where(x => x.Node.Id == node.Id) - .Include(x => x.Star) - .ThenInclude(x => x.DockerImages) - .Include(x => x.Variables) - .Include(x => x.Allocations) - .FirstOrDefaultAsync(x => x.Id == id); - - if (server == null) - throw new HttpApiException("No server with this id found", 404); - - var convertedData = ConvertToServerData(server); - - if (convertedData == null) - throw new HttpApiException("An error occured while creating the server data model", 500); - - return convertedData; - } - - [HttpGet("{id:int}/install")] - public async Task GetInstallAsync([FromRoute] int id) - { - // Load the node via the id - var nodeId = int.Parse(User.Claims.First(x => x.Type == "nodeId").Value); - - var node = await NodeRepository - .Get() - .FirstAsync(x => x.Id == nodeId); - - // Load the server with the star data attached. We filter by the node to ensure the node can only access - // servers linked to it - var server = await ServerRepository - .Get() - .Where(x => x.Node.Id == node.Id) - .Include(x => x.Star) - .FirstOrDefaultAsync(x => x.Id == id); - - if (server == null) - throw new HttpApiException("No server with this id found", 404); - - return new ServerInstallDataResponse() - { - Script = server.Star.InstallScript, - DockerImage = server.Star.InstallDockerImage, - Shell = server.Star.InstallShell - }; - } - - private ServerDataResponse? ConvertToServerData(Server server) - { - // Find the docker image to use for this server - StarDockerImage? dockerImage = null; - - // Handle server set image if specified - if (server.DockerImageIndex != -1) - { - dockerImage = server.Star.DockerImages - .Skip(server.DockerImageIndex) - .FirstOrDefault(); - } - - // Handle star default image if set - if (dockerImage == null && server.Star.DefaultDockerImage != -1) - { - dockerImage = server.Star.DockerImages - .Skip(server.Star.DefaultDockerImage) - .FirstOrDefault(); - } - - if (dockerImage == null) - dockerImage = server.Star.DockerImages.LastOrDefault(); - - if (dockerImage == null) - { - Logger.LogWarning("Unable to map server data for server {id}: No docker image available", server.Id); - return null; - } - - // Convert model - return new ServerDataResponse() - { - Id = server.Id, - StartupCommand = server.StartupOverride ?? server.Star.StartupCommand, - Allocations = server.Allocations.Select(x => new AllocationDataResponse() - { - IpAddress = x.IpAddress, - Port = x.Port - }).ToArray(), - Variables = server.Variables.ToDictionary(x => x.Key, x => x.Value), - Cpu = server.Cpu, - Disk = server.Disk, - Memory = server.Memory, - OnlineDetection = server.Star.OnlineDetection, - DockerImage = dockerImage.Identifier, - PullDockerImage = dockerImage.AutoPulling, - ParseConiguration = server.Star.ParseConfiguration, - StopCommand = server.Star.StopCommand, - }; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Implementations/ServerAuthFilters/AdminAuthFilter.cs b/MoonlightServers.ApiServer/Implementations/ServerAuthFilters/AdminAuthFilter.cs deleted file mode 100644 index c6afeee..0000000 --- a/MoonlightServers.ApiServer/Implementations/ServerAuthFilters/AdminAuthFilter.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Security.Claims; -using Microsoft.AspNetCore.Authorization; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Interfaces; -using MoonlightServers.ApiServer.Models; -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.ApiServer.Implementations.ServerAuthFilters; - -public class AdminAuthFilter : IServerAuthorizationFilter -{ - private readonly IAuthorizationService AuthorizationService; - - public int Priority => 0; - - public AdminAuthFilter(IAuthorizationService authorizationService) - { - AuthorizationService = authorizationService; - } - - public async Task ProcessAsync( - ClaimsPrincipal user, - Server server, - string permissionId, - ServerPermissionLevel requiredLevel - ) - { - var authResult = await AuthorizationService.AuthorizeAsync( - user, - "permissions:admin.servers.manage" - ); - - return authResult.Succeeded ? ServerAuthorizationResult.Success() : null; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Implementations/ServerAuthFilters/OwnerAuthFilter.cs b/MoonlightServers.ApiServer/Implementations/ServerAuthFilters/OwnerAuthFilter.cs deleted file mode 100644 index 83ff9c7..0000000 --- a/MoonlightServers.ApiServer/Implementations/ServerAuthFilters/OwnerAuthFilter.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Security.Claims; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Interfaces; -using MoonlightServers.ApiServer.Models; -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.ApiServer.Implementations.ServerAuthFilters; - -public class OwnerAuthFilter : IServerAuthorizationFilter -{ - public int Priority => 0; - - public Task ProcessAsync( - ClaimsPrincipal user, - Server server, - string permissionId, - ServerPermissionLevel requiredLevel - ) - { - var userIdValue = user.FindFirstValue("UserId"); - - if (string.IsNullOrEmpty(userIdValue)) // This is the case for api keys - return Task.FromResult(null); - - var userId = int.Parse(userIdValue); - - if (server.OwnerId != userId) - return Task.FromResult(null); - - return Task.FromResult( - ServerAuthorizationResult.Success() - ); - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Implementations/ServerAuthFilters/ShareAuthFilter.cs b/MoonlightServers.ApiServer/Implementations/ServerAuthFilters/ShareAuthFilter.cs deleted file mode 100644 index 690d67b..0000000 --- a/MoonlightServers.ApiServer/Implementations/ServerAuthFilters/ShareAuthFilter.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Security.Claims; -using Microsoft.EntityFrameworkCore; -using MoonCore.Extended.Abstractions; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Interfaces; -using MoonlightServers.ApiServer.Models; -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.ApiServer.Implementations.ServerAuthFilters; - -public class ShareAuthFilter : IServerAuthorizationFilter -{ - private readonly DatabaseRepository ShareRepository; - - public ShareAuthFilter(DatabaseRepository shareRepository) - { - ShareRepository = shareRepository; - } - - public int Priority => 0; - - public async Task ProcessAsync( - ClaimsPrincipal user, - Server server, - string permissionId, - ServerPermissionLevel requiredLevel - ) - { - var userIdValue = user.FindFirstValue("userId"); - - if (string.IsNullOrEmpty(userIdValue)) - return null; - - var userId = int.Parse(userIdValue); - - var share = await ShareRepository - .Get() - .FirstOrDefaultAsync(x => x.Server.Id == server.Id && x.UserId == userId); - - if (share == null) - return null; - - if (string.IsNullOrEmpty(permissionId) || requiredLevel == ServerPermissionLevel.None) - return ServerAuthorizationResult.Success(share); - - var possiblePermShare = share.Content.Permissions.FirstOrDefault(x => x.Identifier == permissionId); - - if (possiblePermShare == null) - return null; - - if (possiblePermShare.Level >= requiredLevel) - return ServerAuthorizationResult.Success(share); - - return null; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Interfaces/IServerAuthorizationFilter.cs b/MoonlightServers.ApiServer/Interfaces/IServerAuthorizationFilter.cs deleted file mode 100644 index fad756e..0000000 --- a/MoonlightServers.ApiServer/Interfaces/IServerAuthorizationFilter.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Security.Claims; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Models; -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.ApiServer.Interfaces; - -public interface IServerAuthorizationFilter -{ - // Return null => skip to next filter / handler - // Return any value, instant complete - - public int Priority { get; } - - public Task ProcessAsync( - ClaimsPrincipal user, - Server server, - string permissionId, - ServerPermissionLevel requiredLevel - ); -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Mappers/AllocationMapper.cs b/MoonlightServers.ApiServer/Mappers/AllocationMapper.cs deleted file mode 100644 index f4a3790..0000000 --- a/MoonlightServers.ApiServer/Mappers/AllocationMapper.cs +++ /dev/null @@ -1,18 +0,0 @@ -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.Shared.Http.Requests.Admin.NodeAllocations; -using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations; -using Riok.Mapperly.Abstractions; - -namespace MoonlightServers.ApiServer.Mappers; - -[Mapper(AllowNullPropertyAssignment = false)] -public static partial class AllocationMapper -{ - public static partial NodeAllocationResponse ToNodeAllocation(Allocation allocation); - public static partial Allocation ToAllocation(CreateNodeAllocationRequest request); - public static partial void Merge(UpdateNodeAllocationRequest request, Allocation allocation); - - // EF Projections - - public static partial IQueryable ProjectToAdminResponse(this IQueryable allocations); -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Mappers/DockerImageMapper.cs b/MoonlightServers.ApiServer/Mappers/DockerImageMapper.cs deleted file mode 100644 index 1e5b806..0000000 --- a/MoonlightServers.ApiServer/Mappers/DockerImageMapper.cs +++ /dev/null @@ -1,18 +0,0 @@ -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.Shared.Http.Requests.Admin.StarDockerImages; -using MoonlightServers.Shared.Http.Responses.Admin.StarDockerImages; -using Riok.Mapperly.Abstractions; - -namespace MoonlightServers.ApiServer.Mappers; - -[Mapper(AllowNullPropertyAssignment = false)] -public static partial class DockerImageMapper -{ - public static partial StarDockerImageResponse ToAdminResponse(StarDockerImage dockerImage); - public static partial StarDockerImage ToDockerImage(CreateStarDockerImageRequest request); - public static partial void Merge(UpdateStarDockerImageRequest request, StarDockerImage variable); - - // EF Migrations - - public static partial IQueryable ProjectToAdminResponse(this IQueryable dockerImages); -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Mappers/NodeMapper.cs b/MoonlightServers.ApiServer/Mappers/NodeMapper.cs deleted file mode 100644 index f7a7e76..0000000 --- a/MoonlightServers.ApiServer/Mappers/NodeMapper.cs +++ /dev/null @@ -1,18 +0,0 @@ -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.Shared.Http.Requests.Admin.Nodes; -using MoonlightServers.Shared.Http.Responses.Admin.Nodes; -using Riok.Mapperly.Abstractions; - -namespace MoonlightServers.ApiServer.Mappers; - -[Mapper(AllowNullPropertyAssignment = false)] -public static partial class NodeMapper -{ - public static partial NodeResponse ToAdminNodeResponse(Node node); - public static partial Node ToNode(CreateNodeRequest request); - public static partial void Merge(UpdateNodeRequest request, Node node); - - // EF Projections - - public static partial IQueryable ProjectToAdminResponse(this IQueryable nodes); -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Mappers/ServerMapper.cs b/MoonlightServers.ApiServer/Mappers/ServerMapper.cs deleted file mode 100644 index a92df9e..0000000 --- a/MoonlightServers.ApiServer/Mappers/ServerMapper.cs +++ /dev/null @@ -1,30 +0,0 @@ -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.Shared.Http.Requests.Admin.Servers; -using MoonlightServers.Shared.Http.Responses.Admin.Servers; -using Riok.Mapperly.Abstractions; - -namespace MoonlightServers.ApiServer.Mappers; - -[Mapper(AllowNullPropertyAssignment = false)] -public static partial class ServerMapper -{ - [UserMapping(Default = true)] - public static ServerResponse ToAdminServerResponse(Server server) - { - var response = ToAdminServerResponse_Internal(server); - - response.AllocationIds = server.Allocations.Select(x => x.Id).ToArray(); - - return response; - } - - private static partial ServerResponse ToAdminServerResponse_Internal(Server server); - - [MapperIgnoreSource(nameof(CreateServerRequest.Variables))] - public static partial Server ToServer(CreateServerRequest request); - public static partial void Merge(UpdateServerRequest request, Server server); - - // EF Projections - - public static partial IQueryable ProjectToAdminResponse(this IQueryable servers); -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Mappers/ServerVariableMapper.cs b/MoonlightServers.ApiServer/Mappers/ServerVariableMapper.cs deleted file mode 100644 index 4f2fa64..0000000 --- a/MoonlightServers.ApiServer/Mappers/ServerVariableMapper.cs +++ /dev/null @@ -1,15 +0,0 @@ -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.Shared.Http.Responses.Admin.ServerVariables; -using Riok.Mapperly.Abstractions; - -namespace MoonlightServers.ApiServer.Mappers; - -[Mapper(AllowNullPropertyAssignment = false)] -public static partial class ServerVariableMapper -{ - public static partial ServerVariableResponse ToAdminResponse(ServerVariable serverVariable); - - // EF Projections - - public static partial IQueryable ProjectToAdminResponse(this IQueryable variables); -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Mappers/ShareMapper.cs b/MoonlightServers.ApiServer/Mappers/ShareMapper.cs deleted file mode 100644 index 2bcd4bb..0000000 --- a/MoonlightServers.ApiServer/Mappers/ShareMapper.cs +++ /dev/null @@ -1,27 +0,0 @@ -using MoonlightServers.ApiServer.Models; -using MoonlightServers.Shared.Enums; -using Riok.Mapperly.Abstractions; - -namespace MoonlightServers.ApiServer.Mappers; - -[Mapper] -public static partial class ShareMapper -{ - public static ServerShareContent MapToServerShareContent(Dictionary permissionLevels) - { - return new ServerShareContent() - { - Permissions = permissionLevels.Select(x => new ServerShareContent.SharePermission() - { - Identifier = x.Key, - Level = x.Value - }).ToList() - }; - } - - public static Dictionary MapToPermissionLevels( - ServerShareContent content) - { - return content.Permissions.ToDictionary(x => x.Identifier, x => x.Level); - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Mappers/StarMapper.cs b/MoonlightServers.ApiServer/Mappers/StarMapper.cs deleted file mode 100644 index bf7dd9a..0000000 --- a/MoonlightServers.ApiServer/Mappers/StarMapper.cs +++ /dev/null @@ -1,18 +0,0 @@ -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.Shared.Http.Requests.Admin.Stars; -using MoonlightServers.Shared.Http.Responses.Admin.Stars; -using Riok.Mapperly.Abstractions; - -namespace MoonlightServers.ApiServer.Mappers; - -[Mapper(AllowNullPropertyAssignment = false)] -public static partial class StarMapper -{ - public static partial StarResponse ToAdminResponse(Star star); - public static partial Star ToStar(CreateStarRequest request); - public static partial void Merge(UpdateStarRequest request, Star star); - - // EF Projections - - public static partial IQueryable ProjectToAdminResponse(this IQueryable stars); -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Mappers/StarVariableMapper.cs b/MoonlightServers.ApiServer/Mappers/StarVariableMapper.cs deleted file mode 100644 index 3a5fbca..0000000 --- a/MoonlightServers.ApiServer/Mappers/StarVariableMapper.cs +++ /dev/null @@ -1,18 +0,0 @@ -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.Shared.Http.Requests.Admin.StarVariables; -using MoonlightServers.Shared.Http.Responses.Admin.StarVariables; -using Riok.Mapperly.Abstractions; - -namespace MoonlightServers.ApiServer.Mappers; - -[Mapper(AllowNullPropertyAssignment = false)] -public static partial class StarVariableMapper -{ - public static partial StarVariableResponse ToAdminResponse(StarVariable variable); - public static partial StarVariable ToStarVariable(CreateStarVariableRequest request); - public static partial void Merge(UpdateStarVariableRequest request, StarVariable variable); - - // EF Projections - - public static partial IQueryable ProjectToAdminResponse(this IQueryable variables); -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Models/ServerAuthorizationResult.cs b/MoonlightServers.ApiServer/Models/ServerAuthorizationResult.cs deleted file mode 100644 index 5cab9ab..0000000 --- a/MoonlightServers.ApiServer/Models/ServerAuthorizationResult.cs +++ /dev/null @@ -1,28 +0,0 @@ -using MoonlightServers.ApiServer.Database.Entities; - -namespace MoonlightServers.ApiServer.Models; - -public record ServerAuthorizationResult -{ - public bool Succeeded { get; set; } - public ServerShare? Share { get; set; } - public string? Message { get; set; } - - public static ServerAuthorizationResult Success(ServerShare? share = null) - { - return new() - { - Succeeded = true, - Share = share - }; - } - - public static ServerAuthorizationResult Failed(string? message = null) - { - return new() - { - Succeeded = false, - Message = message - }; - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Models/ServerShareContent.cs b/MoonlightServers.ApiServer/Models/ServerShareContent.cs deleted file mode 100644 index e79035c..0000000 --- a/MoonlightServers.ApiServer/Models/ServerShareContent.cs +++ /dev/null @@ -1,14 +0,0 @@ -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.ApiServer.Models; - -public record ServerShareContent -{ - public List Permissions { get; set; } = new(); - - public record SharePermission - { - public string Identifier { get; set; } - public ServerPermissionLevel Level { get; set; } - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Models/Stars/LegacyImageImportModel.cs b/MoonlightServers.ApiServer/Models/Stars/LegacyImageImportModel.cs deleted file mode 100644 index 5119879..0000000 --- a/MoonlightServers.ApiServer/Models/Stars/LegacyImageImportModel.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace MoonlightServers.ApiServer.Models.Stars; - -public class LegacyImageImportModel -{ - public string Name { get; set; } = ""; - - public string Author { get; set; } = ""; - public string? UpdateUrl { get; set; } - public string? DonateUrl { get; set; } - - public string StartupCommand { get; set; } = ""; - public string OnlineDetection { get; set; } = ""; - public string StopCommand { get; set; } = ""; - - public string InstallShell { get; set; } = ""; - public string InstallDockerImage { get; set; } = ""; - public string InstallScript { get; set; } = ""; - - public string ParseConfiguration { get; set; } = "[]"; - public int AllocationsNeeded { get; set; } = 1; - - public List Variables { get; set; } = new(); - - public int DefaultDockerImage { get; set; } = 0; - public bool AllowDockerImageChange { get; set; } = false; - public List DockerImages { get; set; } = new(); - - public class ImageVariable - { - public string Key { get; set; } = ""; - public string DefaultValue { get; set; } = ""; - - public string DisplayName { get; set; } = ""; - public string Description { get; set; } = ""; - - public bool AllowView { get; set; } = false; - public bool AllowEdit { get; set; } = false; - - public string? Filter { get; set; } - } - - public class ImageDockerImage // Weird name xd - { - public string DisplayName { get; set; } = ""; - public string Name { get; set; } = ""; - public bool AutoPull { get; set; } - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Models/Stars/LegacyImageParseConfigModel.cs b/MoonlightServers.ApiServer/Models/Stars/LegacyImageParseConfigModel.cs deleted file mode 100644 index 52a02ef..0000000 --- a/MoonlightServers.ApiServer/Models/Stars/LegacyImageParseConfigModel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace MoonlightServers.ApiServer.Models.Stars; - -public class LegacyImageParseConfigModel -{ - public string Type { get; set; } = ""; - public string File { get; set; } = ""; - public Dictionary Configuration { get; set; } = new(); -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Models/Stars/StarExportModel.cs b/MoonlightServers.ApiServer/Models/Stars/StarExportModel.cs deleted file mode 100644 index 6a50f01..0000000 --- a/MoonlightServers.ApiServer/Models/Stars/StarExportModel.cs +++ /dev/null @@ -1,55 +0,0 @@ -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.ApiServer.Models.Stars; - -public class StarExportModel -{ - // Meta - public string Name { get; set; } - public string Author { get; set; } - public string Version { get; set; } - public string? DonateUrl { get; set; } - public string? UpdateUrl { get; set; } - - // Start and stop - public string StartupCommand { get; set; } - public string StopCommand { get; set; } - public string OnlineDetection { get; set; } - - // Install - public string InstallShell { get; set; } - public string InstallDockerImage { get; set; } - public string InstallScript { get; set; } - - // Misc - public int RequiredAllocations { get; set; } - public bool AllowDockerImageChange { get; set; } - public int DefaultDockerImage { get; set; } - public string ParseConfiguration { get; set; } - - // Relations - public StarVariableExportModel[] Variables { get; set; } - public StarDockerImageExportModel[] DockerImages { get; set; } - - public class StarDockerImageExportModel - { - public string DisplayName { get; set; } - public string Identifier { get; set; } - public bool AutoPulling { get; set; } - } - - public class StarVariableExportModel - { - public string Name { get; set; } - public string Description { get; set; } - - public string Key { get; set; } - public string DefaultValue { get; set; } - - public bool AllowViewing { get; set; } - public bool AllowEditing { get; set; } - - public StarVariableType Type { get; set; } - public string? Filter { get; set; } - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/MoonlightServers.ApiServer.csproj b/MoonlightServers.ApiServer/MoonlightServers.ApiServer.csproj deleted file mode 100644 index cce6425..0000000 --- a/MoonlightServers.ApiServer/MoonlightServers.ApiServer.csproj +++ /dev/null @@ -1,39 +0,0 @@ - - - - net9.0 - enable - enable - Linux - - - - MoonlightServers.ApiServer - MoonlightServers.ApiServer - 2.1.0 - true - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MoonlightServers.ApiServer/PluginStartup.cs b/MoonlightServers.ApiServer/PluginStartup.cs deleted file mode 100644 index 98741f2..0000000 --- a/MoonlightServers.ApiServer/PluginStartup.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using MoonCore.Extensions; -using Moonlight.ApiServer.Configuration; -using Moonlight.ApiServer.Models; -using Moonlight.ApiServer.Plugins; -using MoonlightServers.ApiServer.Database; -using MoonlightServers.ApiServer.Helpers; -using MoonlightServers.ApiServer.Implementations.ServerAuthFilters; -using MoonlightServers.ApiServer.Interfaces; - -namespace MoonlightServers.ApiServer; - -public class PluginStartup : IPluginStartup -{ - public void AddPlugin(WebApplicationBuilder builder) - { - // Scan the current plugin assembly for di services - builder.Services.AutoAddServices(); - - builder.Services.AddDbContext(); - - // Configure authentication for the remote endpoints - builder.Services - .AddAuthentication() - .AddScheme("nodeAuthentication", null); - - var configuration = AppConfiguration.CreateEmpty(); - builder.Configuration.Bind(configuration); - - if (configuration.Frontend.EnableHosting) - { - builder.Services.AddSingleton(new FrontendConfigurationOption() - { - Scripts = - [ - "/_content/MoonlightServers.Frontend/js/XtermBlazor.min.js", - "/_content/MoonlightServers.Frontend/js/addon-fit.js", - "/_content/MoonlightServers.Frontend/js/moonlightServers.js" - ], - Styles = ["/_content/MoonlightServers.Frontend/css/XtermBlazor.min.css"] - }); - } - - // Add server auth filters - builder.Services.AddSingleton(); - builder.Services.AddScoped(); - builder.Services.AddScoped(); - } - - public void UsePlugin(WebApplication app) - { - } - - public void MapPlugin(WebApplication app) - { - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Services/NodeService.cs b/MoonlightServers.ApiServer/Services/NodeService.cs deleted file mode 100644 index 168eff9..0000000 --- a/MoonlightServers.ApiServer/Services/NodeService.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System.IdentityModel.Tokens.Jwt; -using System.Text; -using Microsoft.IdentityModel.Tokens; -using MoonCore.Attributes; -using MoonCore.Helpers; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Statistics; -using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Sys; - -namespace MoonlightServers.ApiServer.Services; - -[Singleton] -public class NodeService -{ - public string CreateAccessToken(Node node, Action> parameters, TimeSpan duration) - { - var claims = new Dictionary(); - parameters.Invoke(claims); - - var jwtSecurityTokenHandler = new JwtSecurityTokenHandler(); - - var securityTokenDescriptor = new SecurityTokenDescriptor() - { - Expires = DateTime.UtcNow.Add(duration), - NotBefore = DateTime.UtcNow.AddSeconds(-1), - Claims = claims, - IssuedAt = DateTime.UtcNow, - SigningCredentials = new SigningCredentials( - new SymmetricSecurityKey(Encoding.UTF8.GetBytes( - node.Token - )), - SecurityAlgorithms.HmacSha256 - ), - Audience = node.TokenId - }; - - var securityToken = jwtSecurityTokenHandler.CreateJwtSecurityToken(securityTokenDescriptor); - - return jwtSecurityTokenHandler.WriteToken(securityToken); - } - - public async Task GetSystemStatusAsync(Node node) - { - using var apiClient = CreateApiClient(node); - return await apiClient.GetJson("api/system/status"); - } - - #region Statistics - - public async Task GetStatisticsAsync(Node node) - { - using var apiClient = CreateApiClient(node); - return await apiClient.GetJson("api/statistics"); - } - - public async Task GetDockerStatisticsAsync(Node node) - { - using var apiClient = CreateApiClient(node); - return await apiClient.GetJson("api/statistics/docker"); - } - - #endregion - - #region Helpers - - public HttpApiClient CreateApiClient(Node node) - { - var url = ""; - - url += node.UseSsl ? "https://" : "http://"; - url += $"{node.Fqdn}:{node.HttpPort}/"; - - var httpClient = new HttpClient() - { - BaseAddress = new Uri(url) - }; - - httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {node.Token}"); - - return new HttpApiClient(httpClient); - } - - #endregion -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Services/ServerAuthorizeService.cs b/MoonlightServers.ApiServer/Services/ServerAuthorizeService.cs deleted file mode 100644 index d25f93c..0000000 --- a/MoonlightServers.ApiServer/Services/ServerAuthorizeService.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Security.Claims; -using MoonCore.Attributes; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Interfaces; -using MoonlightServers.ApiServer.Models; -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.ApiServer.Services; - -[Scoped] -public class ServerAuthorizeService -{ - private readonly IServerAuthorizationFilter[] AuthorizationFilters; - - public ServerAuthorizeService( - IEnumerable authorizationFilters - ) - { - AuthorizationFilters = authorizationFilters.ToArray(); - } - - public async Task AuthorizeAsync( - ClaimsPrincipal user, - Server server, - string permissionIdentifier, - ServerPermissionLevel permissionLevel - ) - { - foreach (var authorizationFilter in AuthorizationFilters) - { - var result = await authorizationFilter.ProcessAsync( - user, - server, - permissionIdentifier, - permissionLevel - ); - - if (result != null) - return result; - } - - return ServerAuthorizationResult.Failed(); - } -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Services/ServerFileSystemService.cs b/MoonlightServers.ApiServer/Services/ServerFileSystemService.cs deleted file mode 100644 index b571e3a..0000000 --- a/MoonlightServers.ApiServer/Services/ServerFileSystemService.cs +++ /dev/null @@ -1,122 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using MoonCore.Attributes; -using MoonCore.Extended.Abstractions; -using MoonCore.Helpers; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.DaemonShared.DaemonSide.Http.Requests; -using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Servers; -using MoonlightServers.DaemonShared.Enums; - -namespace MoonlightServers.ApiServer.Services; - -[Scoped] -public class ServerFileSystemService -{ - private readonly NodeService NodeService; - private readonly DatabaseRepository ServerRepository; - - public ServerFileSystemService( - NodeService nodeService, - DatabaseRepository serverRepository - ) - { - NodeService = nodeService; - ServerRepository = serverRepository; - } - - public async Task ListAsync(Server server, string path) - { - using var apiClient = await GetApiClientAsync(server); - - return await apiClient.GetJson( - $"api/servers/{server.Id}/files/list?path={path}" - ); - } - - public async Task MoveAsync(Server server, string oldPath, string newPath) - { - using var apiClient = await GetApiClientAsync(server); - - await apiClient.Post( - $"api/servers/{server.Id}/files/move?oldPath={oldPath}&newPath={newPath}" - ); - } - - public async Task DeleteAsync(Server server, string path) - { - using var apiClient = await GetApiClientAsync(server); - - await apiClient.Delete( - $"api/servers/{server.Id}/files/delete?path={path}" - ); - } - - public async Task MkdirAsync(Server server, string path) - { - using var apiClient = await GetApiClientAsync(server); - - await apiClient.Post( - $"api/servers/{server.Id}/files/mkdir?path={path}" - ); - } - - public async Task TouchAsync(Server server, string path) - { - using var apiClient = await GetApiClientAsync(server); - - await apiClient.Post( - $"api/servers/{server.Id}/files/touch?path={path}" - ); - } - - public async Task CompressAsync(Server server, CompressType type, string[] items, string destination) - { - using var apiClient = await GetApiClientAsync(server); - - await apiClient.Post( - $"api/servers/{server.Id}/files/compress", - new ServerFilesCompressRequest() - { - Type = type, - Items = items, - Destination = destination - } - ); - } - - public async Task DecompressAsync(Server server, CompressType type, string path, string destination) - { - using var apiClient = await GetApiClientAsync(server); - - await apiClient.Post( - $"api/servers/{server.Id}/files/decompress", - new ServerFilesDecompressRequest() - { - Type = type, - Path = path, - Destination = destination - } - ); - } - - #region Helpers - - private async Task GetApiClientAsync(Server server) - { - var serverWithNode = server; - - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - // It can be null when its not included when loading via ef !!! - if (server.Node == null) - { - serverWithNode = await ServerRepository - .Get() - .Include(x => x.Node) - .FirstAsync(x => x.Id == server.Id); - } - - return NodeService.CreateApiClient(serverWithNode.Node); - } - - #endregion -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Services/ServerService.cs b/MoonlightServers.ApiServer/Services/ServerService.cs deleted file mode 100644 index f1c3b1f..0000000 --- a/MoonlightServers.ApiServer/Services/ServerService.cs +++ /dev/null @@ -1,194 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using MoonCore.Attributes; -using MoonCore.Exceptions; -using MoonCore.Extended.Abstractions; -using MoonCore.Helpers; -using Moonlight.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.DaemonShared.DaemonSide.Http.Requests; -using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Servers; - -namespace MoonlightServers.ApiServer.Services; - -[Scoped] -public class ServerService -{ - private readonly NodeService NodeService; - private readonly DatabaseRepository ServerRepository; - - public ServerService(NodeService nodeService, DatabaseRepository serverRepository) - { - NodeService = nodeService; - ServerRepository = serverRepository; - } - - #region Power Actions - - public async Task StartAsync(Server server) - { - try - { - using var apiClient = await GetApiClientAsync(server); - await apiClient.Post($"api/servers/{server.Id}/start"); - } - catch (HttpRequestException e) - { - throw new HttpApiException("Unable to access the node the server is running on", 502); - } - } - - public async Task StopAsync(Server server) - { - try - { - using var apiClient = await GetApiClientAsync(server); - await apiClient.Post($"api/servers/{server.Id}/stop"); - } - catch (HttpRequestException e) - { - throw new HttpApiException("Unable to access the node the server is running on", 502); - } - } - - public async Task KillAsync(Server server) - { - try - { - using var apiClient = await GetApiClientAsync(server); - await apiClient.Post($"api/servers/{server.Id}/kill"); - } - catch (HttpRequestException e) - { - throw new HttpApiException("Unable to access the node the server is running on", 502); - } - } - - #endregion - - public async Task InstallAsync(Server server) - { - try - { - using var apiClient = await GetApiClientAsync(server); - await apiClient.Post($"api/servers/{server.Id}/install"); - } - catch (HttpRequestException e) - { - throw new HttpApiException("Unable to access the node the server is running on", 502); - } - } - - public async Task SyncAsync(Server server) - { - try - { - using var apiClient = await GetApiClientAsync(server); - await apiClient.Post($"api/servers/{server.Id}/sync"); - } - catch (HttpRequestException e) - { - throw new HttpApiException("Unable to access the node the server is running on", 502); - } - } - - public async Task SyncDeleteAsync(Server server) - { - try - { - using var apiClient = await GetApiClientAsync(server); - await apiClient.Delete($"api/servers/{server.Id}"); - } - catch (HttpRequestException e) - { - throw new HttpApiException("Unable to access the node the server is running on", 502); - } - } - - public async Task GetStatusAsync(Server server) - { - try - { - using var apiClient = await GetApiClientAsync(server); - return await apiClient.GetJson($"api/servers/{server.Id}/status"); - } - catch (HttpRequestException e) - { - throw new HttpApiException("Unable to access the node the server is running on", 502); - } - } - - public async Task GetLogsAsync(Server server) - { - try - { - using var apiClient = await GetApiClientAsync(server); - return await apiClient.GetJson($"api/servers/{server.Id}/logs"); - } - catch (HttpRequestException e) - { - throw new HttpApiException("Unable to access the node the server is running on", 502); - } - } - - public async Task GetStatsAsync(Server server) - { - try - { - using var apiClient = await GetApiClientAsync(server); - return await apiClient.GetJson($"api/servers/{server.Id}/stats"); - } - catch (HttpRequestException) - { - throw new HttpApiException("Unable to access the node the server is running on", 502); - } - } - - public async Task RunCommandAsync(Server server, string command) - { - try - { - using var apiClient = await GetApiClientAsync(server); - - await apiClient.Post( - $"api/servers/{server.Id}/command", - new ServerCommandRequest() - { - Command = command - } - ); - } - catch (HttpRequestException) - { - throw new HttpApiException("Unable to access the node the server is running on", 502); - } - } - - #region Helpers - - public bool IsAllowedToAccess(User user, Server server) - { - if (server.OwnerId == user.Id) - return true; - - return PermissionHelper.HasPermission(user.Permissions, "admin.servers.get"); - } - - private async Task GetApiClientAsync(Server server) - { - var serverWithNode = server; - - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - // It can be null when its not included when loading via ef !!! - if (server.Node == null) - { - serverWithNode = await ServerRepository - .Get() - .Include(x => x.Node) - .FirstAsync(x => x.Id == server.Id); - } - - return NodeService.CreateApiClient(serverWithNode.Node); - } - - #endregion -} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Services/StarImportExportService.cs b/MoonlightServers.ApiServer/Services/StarImportExportService.cs deleted file mode 100644 index 13a0327..0000000 --- a/MoonlightServers.ApiServer/Services/StarImportExportService.cs +++ /dev/null @@ -1,410 +0,0 @@ -using System.Text.Json; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using MoonCore.Attributes; -using MoonCore.Exceptions; -using MoonCore.Extended.Abstractions; -using MoonlightServers.ApiServer.Database.Entities; -using MoonlightServers.ApiServer.Models.Stars; -using MoonlightServers.Shared.Enums; -using MoonlightServers.Shared.Models; - -namespace MoonlightServers.ApiServer.Services; - -[Scoped] -public class StarImportExportService -{ - private readonly DatabaseRepository StarRepository; - private readonly ILogger Logger; - - public StarImportExportService(DatabaseRepository starRepository, ILogger logger) - { - StarRepository = starRepository; - Logger = logger; - } - - public async Task ExportAsync(int id) - { - var star = StarRepository - .Get() - .Include(x => x.DockerImages) - .Include(x => x.Variables) - .FirstOrDefault(x => x.Id == id); - - if (star == null) - throw new HttpApiException("No star with this id found", 404); - - var exportModel = new StarExportModel() - { - Name = star.Name, - Author = star.Author, - Version = star.Version, - DonateUrl = star.DonateUrl, - UpdateUrl = star.UpdateUrl, - InstallScript = star.InstallScript, - InstallShell = star.InstallShell, - InstallDockerImage = star.InstallDockerImage, - OnlineDetection = star.OnlineDetection, - StopCommand = star.StopCommand, - StartupCommand = star.StartupCommand, - ParseConfiguration = star.ParseConfiguration, - RequiredAllocations = star.RequiredAllocations, - AllowDockerImageChange = star.AllowDockerImageChange, - DefaultDockerImage = star.DefaultDockerImage, - Variables = star.Variables.Select(x => new StarExportModel.StarVariableExportModel() - { - Name = x.Name, - Type = x.Type, - Description = x.Description, - Filter = x.Filter, - Key = x.Key, - AllowEditing = x.AllowEditing, - AllowViewing = x.AllowViewing, - DefaultValue = x.DefaultValue - }).ToArray(), - DockerImages = star.DockerImages.Select(x => new StarExportModel.StarDockerImageExportModel() - { - Identifier = x.Identifier, - AutoPulling = x.AutoPulling, - DisplayName = x.DisplayName - }).ToArray() - }; - - var json = JsonSerializer.Serialize(exportModel, new JsonSerializerOptions() - { - WriteIndented = true - }); - - return json; - } - - public async Task ImportAsync(string json) - { - // Determine which importer to use based on simple patterns - if (json.Contains("RequiredAllocations")) - return await ImportStarAsync(json); - else if (json.Contains("AllocationsNeeded")) - return await ImportImageAsync(json); - else if (json.Contains("_comment")) - return await ImportEggAsync(json); - else - throw new HttpApiException("Unable to determine the format of the imported star/image/egg", 400); - } - - public async Task ImportStarAsync(string json) - { - try - { - var model = JsonSerializer.Deserialize(json, new JsonSerializerOptions() - { - PropertyNameCaseInsensitive = true - }); - - ArgumentNullException.ThrowIfNull(model); - - var star = new Star() - { - Name = model.Name, - Author = model.Author, - Version = model.Version, - DonateUrl = model.DonateUrl, - UpdateUrl = model.UpdateUrl, - InstallScript = model.InstallScript, - InstallShell = model.InstallShell, - InstallDockerImage = model.InstallDockerImage, - OnlineDetection = model.OnlineDetection, - StopCommand = model.StopCommand, - StartupCommand = model.StartupCommand, - ParseConfiguration = model.ParseConfiguration, - RequiredAllocations = model.RequiredAllocations, - AllowDockerImageChange = model.AllowDockerImageChange, - DefaultDockerImage = model.DefaultDockerImage, - Variables = model.Variables.Select(x => new StarVariable() - { - DefaultValue = x.DefaultValue, - Description = x.Description, - Filter = x.Filter, - Key = x.Key, - AllowEditing = x.AllowEditing, - AllowViewing = x.AllowViewing, - Type = x.Type, - Name = x.Name - }).ToList(), - DockerImages = model.DockerImages.Select(x => new StarDockerImage() - { - DisplayName = x.DisplayName, - AutoPulling = x.AutoPulling, - Identifier = x.Identifier - }).ToList() - }; - - var finalStar = await StarRepository.AddAsync(star); - - return finalStar; - } - catch (Exception e) - { - Logger.LogError("An unhandled error occured while importing star: {e}", e); - throw new HttpApiException("An unhandled error occured while importing star", 400); - } - } - - public async Task ImportImageAsync(string json) - { - try - { - var model = JsonSerializer.Deserialize(json, new JsonSerializerOptions() - { - PropertyNameCaseInsensitive = true - }); - - ArgumentNullException.ThrowIfNull(model); - - var star = new Star() - { - Name = model.Name, - Author = model.Author, - Version = "Imported from v2.0", - DonateUrl = model.DonateUrl, - UpdateUrl = model.UpdateUrl, - InstallScript = model.InstallScript, - InstallShell = model.InstallShell, - InstallDockerImage = model.InstallDockerImage, - OnlineDetection = model.OnlineDetection, - StopCommand = model.StopCommand, - StartupCommand = model.StartupCommand, - RequiredAllocations = model.AllocationsNeeded, - AllowDockerImageChange = model.AllowDockerImageChange, - DefaultDockerImage = model.DefaultDockerImage, - Variables = model.Variables.Select(x => new StarVariable() - { - DefaultValue = x.DefaultValue, - Description = x.Description, - Filter = x.Filter, - Key = x.Key, - AllowEditing = x.AllowEdit, - AllowViewing = x.AllowView, - Type = StarVariableType.Text, - Name = x.DisplayName - }).ToList(), - DockerImages = model.DockerImages.Select(x => new StarDockerImage() - { - DisplayName = x.DisplayName, - AutoPulling = x.AutoPull, - Identifier = x.Name - }).ToList() - }; - - #region Convert parse configurations - - var oldParseConfig = JsonSerializer.Deserialize(model.ParseConfiguration, - new JsonSerializerOptions() - { - PropertyNameCaseInsensitive = true - } - ); - - ArgumentNullException.ThrowIfNull(oldParseConfig); - - var newParseConfig = new List(); - - // Remap values - foreach (var config in oldParseConfig) - { - var parseConfiguration = new ParseConfiguration() - { - File = config.File, - Parser = Enum.TryParse(config.Type, true, out FileParsers parserType) - ? parserType - : FileParsers.File - }; - - foreach (var option in config.Configuration) - { - parseConfiguration.Entries.Add(new ParseConfiguration.ParseConfigurationEntry() - { - Key = option.Key, - Value = option.Value - }); - } - - newParseConfig.Add(parseConfiguration); - } - - star.ParseConfiguration = JsonSerializer.Serialize(newParseConfig); - - #endregion - - var finalStar = await StarRepository.AddAsync(star); - - return finalStar; - } - catch (Exception e) - { - Logger.LogError("An unhandled error occured while importing image: {e}", e); - throw new HttpApiException("An unhandled error occured while importing image", 400); - } - } - - public async Task ImportEggAsync(string json) - { - // Create result - var star = new Star(); - - // Prepare json - var fixedJson = json; - fixedJson = fixedJson.Replace("\\/", "/"); - var jsonDocument = JsonDocument.Parse(fixedJson); - var egg = jsonDocument.RootElement; - - // Let's go :O - - // Basic meta - star.Name = egg.GetProperty("name").GetString() ?? "Parse error"; - star.Author = egg.GetProperty("author").GetString() ?? "Parse error"; - - // Start & Stop and Status - var configSection = egg.GetProperty("config"); - - star.StartupCommand = egg.GetProperty("startup").GetString() ?? "Parse error"; - star.StopCommand = configSection.GetProperty("stop").GetString() ?? "Parse error"; - - var startupSectionJson = configSection.GetProperty("startup").GetString() ?? "{}"; - var startupSectionDocument = JsonDocument.Parse(startupSectionJson); - var startupSection = startupSectionDocument.RootElement; - - var doneProperty = startupSection.GetProperty("done"); - - if (doneProperty.ValueKind == JsonValueKind.Array) - star.OnlineDetection = doneProperty.Deserialize()?.First() ?? "Parse error"; - else - star.OnlineDetection = doneProperty.GetString() ?? "Parse error"; - - // Installation - var installationSection = egg.GetProperty("scripts").GetProperty("installation"); - - var shell = installationSection.GetProperty("entrypoint").GetString() ?? "bash"; - star.InstallShell = shell.StartsWith("/") ? shell : $"/bin/{shell}"; - - star.InstallDockerImage = installationSection.GetProperty("container").GetString() ?? "Parse error"; - star.InstallScript = installationSection.GetProperty("script").GetString() ?? "Parse error"; - - // Variables - var variables = egg.GetProperty("variables"); - - foreach (var variable in variables.EnumerateArray()) - { - var starVariable = new StarVariable() - { - Name = variable.GetProperty("name").GetString() ?? "Parse error", - Description = variable.GetProperty("description").GetString() ?? "Parse error", - Key = variable.GetProperty("env_variable").GetString() ?? "Parse error", - DefaultValue = variable.GetProperty("default_value").GetString() ?? "Parse error", - Type = StarVariableType.Text - }; - - // Check if the provided value is an int or a boolean as both are apparently valid - if (variable.GetProperty("user_editable").ValueKind == JsonValueKind.Number) - { - starVariable.AllowEditing = variable.GetProperty("user_editable").GetInt32() == 1; - starVariable.AllowViewing = variable.GetProperty("user_viewable").GetInt32() == 1; - } - else - { - starVariable.AllowEditing = variable.GetProperty("user_editable").GetBoolean(); - starVariable.AllowViewing = variable.GetProperty("user_viewable").GetBoolean(); - } - - star.Variables.Add(starVariable); - } - - // Docker images - if (egg.TryGetProperty("image", out var imageProperty)) // Variant 1 - { - star.DockerImages.Add(new StarDockerImage() - { - Identifier = imageProperty.GetString() ?? "Parse error", - DisplayName = imageProperty.GetString() ?? "Parse error", - AutoPulling = true - }); - } - else if (egg.TryGetProperty("images", out var imagesProperty)) // Variant 2 - { - foreach (var di in imagesProperty.EnumerateObject()) - { - star.DockerImages.Add(new StarDockerImage() - { - DisplayName = di.Name, - Identifier = di.Value.GetString() ?? "Parse error", - AutoPulling = true - }); - } - } - else if (egg.TryGetProperty("docker_images", out var dockerImages)) // Variant 3 - { - foreach (var di in dockerImages.EnumerateObject()) - { - star.DockerImages.Add(new StarDockerImage() - { - DisplayName = di.Name, - Identifier = di.Value.GetString() ?? "Parse error", - AutoPulling = true - }); - } - } - - // Parse configuration - var parseConfigurationJson = configSection.GetProperty("files").GetString() ?? "{}"; - var parseConfigurationDocument = JsonDocument.Parse(parseConfigurationJson); - var parseConfiguration = parseConfigurationDocument.RootElement; - - var resultPcs = new List(); - - foreach (var pConfig in parseConfiguration.EnumerateObject()) - { - var pc = new ParseConfiguration() - { - File = pConfig.Name - }; - - var parser = pConfig.Value.GetProperty("parser").GetString() ?? "Parse error"; - pc.Parser = Enum.TryParse(parser, true, out FileParsers fileParser) ? fileParser : FileParsers.File; - - foreach (var pConfigFind in pConfig.Value.GetProperty("find").EnumerateObject()) - { - var entry = new ParseConfiguration.ParseConfigurationEntry() - { - Key = pConfigFind.Name, - Value = pConfigFind.Value.GetString() ?? "Parse error" - }; - - // Fix up special variables - entry.Value = entry.Value.Replace("server.allocations.default.port", "SERVER_PORT"); - entry.Value = entry.Value.Replace("server.build.default.port", "SERVER_PORT"); - - pc.Entries.Add(entry); - } - - resultPcs.Add(pc); - } - - star.ParseConfiguration = JsonSerializer.Serialize(resultPcs); - - // Post parse fixes - - // - Stop command signal - // Some weird eggs use ^^C in as a stop command, so we need to handle this as well - // because moonlight handles power signals correctly, wings does/did not - star.StopCommand = star.StopCommand.Replace("^^C", "^C"); - - // - Set moonlight native values - star.RequiredAllocations = 1; - star.Version = "Imported egg"; - star.AllowDockerImageChange = true; - - // Finally save it to the db - var finalStar = await StarRepository.AddAsync(star); - - return finalStar; - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Configuration/AppConfiguration.cs b/MoonlightServers.Daemon/Configuration/AppConfiguration.cs deleted file mode 100644 index 5851246..0000000 --- a/MoonlightServers.Daemon/Configuration/AppConfiguration.cs +++ /dev/null @@ -1,72 +0,0 @@ -namespace MoonlightServers.Daemon.Configuration; - -public class AppConfiguration -{ - public DockerData Docker { get; set; } = new(); - public StorageData Storage { get; set; } = new(); - public SecurityData Security { get; set; } = new(); - public RemoteData Remote { get; set; } = new(); - public FilesData Files { get; set; } = new(); - public ServerData Server { get; set; } = new(); - public KestrelData Kestrel { get; set; } = new(); - - public class KestrelData - { - public int RequestBodySizeLimit { get; set; } = 25; - } - - public class RemoteData - { - public string Url { get; set; } - } - - public class DockerData - { - public string Uri { get; set; } = "unix:///var/run/docker.sock"; - public DockerCredentialData[] Credentials { get; set; } = []; - - public class DockerCredentialData - { - public string Domain { get; set; } - public string Username { get; set; } - public string Password { get; set; } - public string Email { get; set; } - } - } - - public class SecurityData - { - public string Token { get; set; } - public string TokenId { get; set; } - } - - public class StorageData - { - public string Volumes { get; set; } = Path.Combine("storage", "volumes"); - public string VirtualDisks { get; set; } = Path.Combine("storage", "virtualDisks"); - public string Backups { get; set; } = Path.Combine("storage", "backups"); - public string Install { get; set; } =Path.Combine("storage", "install"); - - public VirtualDiskData VirtualDiskOptions { get; set; } = new(); - } - - public record VirtualDiskData - { - public string FileSystemType { get; set; } = "ext4"; - public string E2FsckParameters { get; set; } = "-pf"; - } - - public class FilesData - { - public int UploadSizeLimit { get; set; } = 1024 * 2; - public int UploadChunkSize { get; set; } = 20; - } - - public class ServerData - { - public int WaitBeforeKillSeconds { get; set; } = 30; - public int TmpFsSize { get; set; } = 100; - public float MemoryOverheadMultiplier { get; set; } = 0.05f; - public int ConsoleMessageCacheLimit { get; set; } = 250; - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Enums/ServerState.cs b/MoonlightServers.Daemon/Enums/ServerState.cs deleted file mode 100644 index b2093b5..0000000 --- a/MoonlightServers.Daemon/Enums/ServerState.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace MoonlightServers.Daemon.Enums; - -public enum ServerState -{ - Offline = 0, - Starting = 1, - Online = 2, - Stopping = 3, - Installing = 4 -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Enums/ServerTrigger.cs b/MoonlightServers.Daemon/Enums/ServerTrigger.cs deleted file mode 100644 index ddc83f2..0000000 --- a/MoonlightServers.Daemon/Enums/ServerTrigger.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace MoonlightServers.Daemon.Enums; - -public enum ServerTrigger -{ - Start = 0, - Stop = 1, - Restart = 2, - Kill = 3, - Reinstall = 4, - NotifyOnline = 5, - NotifyRuntimeContainerDied = 6, - NotifyInstallationContainerDied = 7, - NotifyInternalError = 8 -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Extensions/ServerConfigurationExtensions.cs b/MoonlightServers.Daemon/Extensions/ServerConfigurationExtensions.cs deleted file mode 100644 index 53d2627..0000000 --- a/MoonlightServers.Daemon/Extensions/ServerConfigurationExtensions.cs +++ /dev/null @@ -1,359 +0,0 @@ -using Docker.DotNet.Models; -using Mono.Unix.Native; -using MoonCore.Helpers; -using MoonlightServers.Daemon.Configuration; -using MoonlightServers.Daemon.Models.Cache; -using MoonlightServers.DaemonShared.PanelSide.Http.Responses; - -namespace MoonlightServers.Daemon.Extensions; - -public static class ServerConfigurationExtensions -{ - public static ServerConfiguration ToServerConfiguration(this ServerDataResponse response) - { - return new ServerConfiguration() - { - Id = response.Id, - StartupCommand = response.StartupCommand, - Allocations = response.Allocations.Select(y => new ServerConfiguration.AllocationConfiguration() - { - IpAddress = y.IpAddress, - Port = y.Port - }).ToArray(), - Variables = response.Variables, - OnlineDetection = response.OnlineDetection, - DockerImage = response.DockerImage, - Cpu = response.Cpu, - Disk = response.Disk, - Memory = response.Memory, - StopCommand = response.StopCommand - }; - } - - public static CreateContainerParameters ToRuntimeCreateParameters( - this ServerConfiguration configuration, - AppConfiguration appConfiguration, - string hostPath, - string containerName - ) - { - var parameters = configuration.ToSharedCreateParameters(appConfiguration); - - #region Security - - parameters.HostConfig.CapDrop = new List() - { - "setpcap", "mknod", "audit_write", "net_raw", "dac_override", - "fowner", "fsetid", "net_bind_service", "sys_chroot", "setfcap" - }; - - parameters.HostConfig.ReadonlyRootfs = true; - parameters.HostConfig.SecurityOpt = new List() - { - "no-new-privileges" - }; - - #endregion - - #region Name - - parameters.Name = containerName; - parameters.Hostname = containerName; - - #endregion - - #region Docker Image - - parameters.Image = configuration.DockerImage; - - #endregion - - #region Environment - - parameters.Env = configuration.ToEnvironmentVariables() - .Select(x => $"{x.Key}={x.Value}") - .ToList(); - - #endregion - - #region Working Dir - - parameters.WorkingDir = "/home/container"; - - #endregion - - #region User - - var userId = Syscall.getuid(); // TODO: Extract to external service? - - if (userId == 0) - userId = 998; - - parameters.User = $"{userId}:{userId}"; - - /* - if (userId == 0) - { - // We are running as root, so we need to run the container as another user and chown the files when we make changes - parameters.User = $"998:998"; - } - else - { - // We are not running as root, so we start the container as the same user, - // as we are not able to chown the container content to a different user - parameters.User = $"{userId}:{userId}"; - }*/ - - #endregion - - #region Mounts - - parameters.HostConfig.Mounts = new List(); - - parameters.HostConfig.Mounts.Add(new() - { - Source = hostPath, - Target = "/home/container", - ReadOnly = false, - Type = "bind" - }); - - #endregion - - #region Port Bindings - - if (true) // TODO: Add network toggle - { - parameters.ExposedPorts = new Dictionary(); - parameters.HostConfig.PortBindings = new Dictionary>(); - - foreach (var allocation in configuration.Allocations) - { - parameters.ExposedPorts.Add($"{allocation.Port}/tcp", new()); - parameters.ExposedPorts.Add($"{allocation.Port}/udp", new()); - - parameters.HostConfig.PortBindings.Add($"{allocation.Port}/tcp", new List - { - new() - { - HostPort = allocation.Port.ToString(), - HostIP = allocation.IpAddress - } - }); - - parameters.HostConfig.PortBindings.Add($"{allocation.Port}/udp", new List - { - new() - { - HostPort = allocation.Port.ToString(), - HostIP = allocation.IpAddress - } - }); - } - } - - #endregion - - return parameters; - } - - public static CreateContainerParameters ToInstallationCreateParameters( - this ServerConfiguration configuration, - AppConfiguration appConfiguration, - string runtimeHostPath, - string installationHostPath, - string containerName, - string installDockerImage, - string installShell - ) - { - var parameters = configuration.ToSharedCreateParameters(appConfiguration); - - // - Name - parameters.Name = containerName; - parameters.Hostname = containerName; - - // - Image - parameters.Image = installDockerImage; - - // - Env - parameters.Env = configuration - .ToEnvironmentVariables() - .Select(x => $"{x.Key}={x.Value}") - .ToList(); - - // -- Working directory - parameters.WorkingDir = "/mnt/server"; - - // - User - // Note: Some images might not work if we set a user here - - var userId = Syscall.getuid(); - - // If we are root, we are able to change owner permissions after the installation - // so we run the installation as root, otherwise we need to run it as our current user, - // so we are able to access the files created by the installer - if (userId == 0) - parameters.User = "0:0"; - else - parameters.User = $"{userId}:{userId}"; - - // -- Mounts - parameters.HostConfig.Mounts = new List(); - - parameters.HostConfig.Mounts.Add(new() - { - Source = runtimeHostPath, - Target = "/mnt/server", - ReadOnly = false, - Type = "bind" - }); - - parameters.HostConfig.Mounts.Add(new() - { - Source = installationHostPath, - Target = "/mnt/install", - ReadOnly = false, - Type = "bind" - }); - - parameters.Cmd = [installShell, "/mnt/install/install.sh"]; - - return parameters; - } - - private static CreateContainerParameters ToSharedCreateParameters(this ServerConfiguration configuration, - AppConfiguration appConfiguration) - { - var parameters = new CreateContainerParameters() - { - HostConfig = new() - }; - - #region Input, output & error streams and tty - - parameters.Tty = true; - parameters.AttachStderr = true; - parameters.AttachStdin = true; - parameters.AttachStdout = true; - parameters.OpenStdin = true; - - #endregion - - #region CPU - - parameters.HostConfig.CPUQuota = configuration.Cpu * 1000; - parameters.HostConfig.CPUPeriod = 100000; - parameters.HostConfig.CPUShares = 1024; - - #endregion - - #region Memory & Swap - - var memoryLimit = configuration.Memory; - - // The overhead multiplier gives the container a little bit more memory to prevent crashes - var memoryOverhead = memoryLimit + (memoryLimit * appConfiguration.Server.MemoryOverheadMultiplier); - - long swapLimit = -1; - - /* - - // If swap is enabled globally and not disabled on this server, set swap - if (!configuration.Limits.DisableSwap && config.Server.EnableSwap) - swapLimit = (long)(memoryOverhead + memoryOverhead * config.Server.SwapMultiplier); - co - */ - - // Finalize limits by converting and updating the host config - parameters.HostConfig.Memory = ByteConverter.FromMegaBytes((long)memoryOverhead, 1000).Bytes; - parameters.HostConfig.MemoryReservation = ByteConverter.FromMegaBytes(memoryLimit, 1000).Bytes; - parameters.HostConfig.MemorySwap = - swapLimit == -1 ? swapLimit : ByteConverter.FromMegaBytes(swapLimit, 1000).Bytes; - - #endregion - - #region Misc Limits - - // -- Other limits - parameters.HostConfig.BlkioWeight = 100; - //container.HostConfig.PidsLimit = configuration.Limits.PidsLimit; - parameters.HostConfig.OomKillDisable = true; //!configuration.Limits.EnableOomKill; - - #endregion - - #region DNS - - // TODO: Read hosts dns settings? - - parameters.HostConfig.DNS = /*config.Docker.DnsServers.Any() ? config.Docker.DnsServers :*/ new List() - { - "1.1.1.1", - "9.9.9.9" - }; - - #endregion - - #region Tmpfs - - parameters.HostConfig.Tmpfs = new Dictionary() - { - { "/tmp", $"rw,exec,nosuid,size={appConfiguration.Server.TmpFsSize}M" } - }; - - #endregion - - #region Logging - - parameters.HostConfig.LogConfig = new() - { - Type = "json-file", // We need to use this provider, as the GetLogs endpoint needs it - Config = new Dictionary() - }; - - #endregion - - #region Labels - - parameters.Labels = new Dictionary(); - - parameters.Labels.Add("Software", "Moonlight-Panel"); - parameters.Labels.Add("ServerId", configuration.Id.ToString()); - - #endregion - - return parameters; - } - - public static Dictionary ToEnvironmentVariables(this ServerConfiguration configuration) - { - var result = new Dictionary - { - //TODO: Add timezone, add server ip - { "STARTUP", configuration.StartupCommand }, - { "SERVER_MEMORY", configuration.Memory.ToString() } - }; - - if (configuration.Allocations.Length > 0) - { - var mainAllocation = configuration.Allocations.First(); - - result.Add("SERVER_IP", mainAllocation.IpAddress); - result.Add("SERVER_PORT", mainAllocation.Port.ToString()); - } - - // Handle allocation variables - var i = 1; - foreach (var allocation in configuration.Allocations) - { - result.Add($"ML_PORT_{i}", allocation.Port.ToString()); - i++; - } - - // Copy variables as env vars - foreach (var variable in configuration.Variables) - result.Add(variable.Key, variable.Value); - - return result; - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Extensions/StateConfigurationExtensions.cs b/MoonlightServers.Daemon/Extensions/StateConfigurationExtensions.cs deleted file mode 100644 index 2ec7759..0000000 --- a/MoonlightServers.Daemon/Extensions/StateConfigurationExtensions.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Stateless; - -namespace MoonlightServers.Daemon.Extensions; - -public static class StateConfigurationExtensions -{ - public static StateMachine.StateConfiguration OnExitFrom( - this StateMachine.StateConfiguration configuration, TTrigger trigger, Action entryAction - ) - { - configuration.OnExit(transition => - { - if(!transition.Trigger!.Equals(trigger)) - return; - - entryAction.Invoke(); - }); - - return configuration; - } - - public static StateMachine.StateConfiguration OnExitFrom( - this StateMachine.StateConfiguration configuration, TTrigger trigger, Action.Transition> entryAction - ) - { - configuration.OnExit(transition => - { - if(!transition.Trigger!.Equals(trigger)) - return; - - entryAction.Invoke(transition); - }); - - return configuration; - } - - public static StateMachine.StateConfiguration OnExitFromAsync( - this StateMachine.StateConfiguration configuration, TTrigger trigger, Func entryAction - ) - { - configuration.OnExitAsync(transition => - { - if(!transition.Trigger!.Equals(trigger)) - return Task.CompletedTask; - - return entryAction.Invoke(); - }); - - return configuration; - } - - public static StateMachine.StateConfiguration OnExitFromAsync( - this StateMachine.StateConfiguration configuration, TTrigger trigger, Func.Transition, Task> entryAction - ) - { - configuration.OnExitAsync(transition => - { - if(!transition.Trigger!.Equals(trigger)) - return Task.CompletedTask; - - return entryAction.Invoke(transition); - }); - - return configuration; - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Helpers/AppConsoleFormatter.cs b/MoonlightServers.Daemon/Helpers/AppConsoleFormatter.cs new file mode 100644 index 0000000..8fdac5b --- /dev/null +++ b/MoonlightServers.Daemon/Helpers/AppConsoleFormatter.cs @@ -0,0 +1,76 @@ +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Logging.Console; + +namespace MoonlightServers.Daemon.Helpers; + +public class AppConsoleFormatter : ConsoleFormatter +{ + private const string TimestampColor = "\e[38;2;148;148;148m"; + private const string CategoryColor = "\e[38;2;198;198;198m"; + private const string MessageColor = "\e[38;2;255;255;255m"; + private const string Bold = "\e[1m"; + + // Pre-computed ANSI color codes for each log level + private const string CriticalColor = "\e[38;2;255;0;0m"; + private const string ErrorColor = "\e[38;2;255;0;0m"; + private const string WarningColor = "\e[38;2;215;215;0m"; + private const string InfoColor = "\e[38;2;135;215;255m"; + private const string DebugColor = "\e[38;2;198;198;198m"; + private const string TraceColor = "\e[38;2;68;68;68m"; + + public AppConsoleFormatter() : base(nameof(AppConsoleFormatter)) + { + } + + public override void Write( + in LogEntry logEntry, + IExternalScopeProvider? scopeProvider, + TextWriter textWriter) + { + var message = logEntry.Formatter(logEntry.State, logEntry.Exception); + + // Timestamp + textWriter.Write(TimestampColor); + textWriter.Write(DateTime.Now.ToString("dd.MM.yy HH:mm:ss")); + textWriter.Write(' '); + + // Log level with color and bold + var (levelText, levelColor) = GetLevelInfo(logEntry.LogLevel); + textWriter.Write(levelColor); + textWriter.Write(Bold); + textWriter.Write(levelText); + textWriter.Write(' '); + + // Category + textWriter.Write(CategoryColor); + textWriter.Write(logEntry.Category); + + // Message + textWriter.Write(MessageColor); + textWriter.Write(": "); + textWriter.Write(message); + + // Exception + if (logEntry.Exception != null) + { + textWriter.Write(MessageColor); + textWriter.WriteLine(logEntry.Exception.ToString()); + } + else + textWriter.WriteLine(); + } + + private static (string text, string color) GetLevelInfo(LogLevel logLevel) + { + return logLevel switch + { + LogLevel.Critical => ("CRIT", CriticalColor), + LogLevel.Error => ("ERRO", ErrorColor), + LogLevel.Warning => ("WARN", WarningColor), + LogLevel.Information => ("INFO", InfoColor), + LogLevel.Debug => ("DEBG", DebugColor), + LogLevel.Trace => ("TRCE", TraceColor), + _ => ("NONE", "") + }; + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Helpers/CompositeServiceProvider.cs b/MoonlightServers.Daemon/Helpers/CompositeServiceProvider.cs deleted file mode 100644 index ab5316a..0000000 --- a/MoonlightServers.Daemon/Helpers/CompositeServiceProvider.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace MoonlightServers.Daemon.Helpers; - -public class CompositeServiceProvider : IServiceProvider -{ - private readonly List ServiceProviders; - - public CompositeServiceProvider(params IServiceProvider[] serviceProviders) - { - ServiceProviders = new List(serviceProviders); - } - - public object? GetService(Type serviceType) - { - foreach (var provider in ServiceProviders) - { - try - { - var service = provider.GetService(serviceType); - - if (service != null) - return service; - } - catch (InvalidOperationException) - { - // Ignored - } - } - - return null; - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Helpers/HostSystemHelper.cs b/MoonlightServers.Daemon/Helpers/HostSystemHelper.cs deleted file mode 100644 index 42e0dc9..0000000 --- a/MoonlightServers.Daemon/Helpers/HostSystemHelper.cs +++ /dev/null @@ -1,263 +0,0 @@ -using System.Runtime.InteropServices; -using Mono.Unix.Native; -using MoonCore.Attributes; -using MoonCore.Helpers; -using MoonlightServers.Daemon.Models; - -namespace MoonlightServers.Daemon.Helpers; - -[Singleton] -public class HostSystemHelper -{ - private readonly ILogger Logger; - - public HostSystemHelper(ILogger logger) - { - Logger = logger; - } - - public string GetOsName() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // Windows platform detected - var osVersion = Environment.OSVersion.Version; - return $"Windows {osVersion.Major}.{osVersion.Minor}.{osVersion.Build}"; - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - var releaseRaw = File - .ReadAllLines("/etc/os-release") - .FirstOrDefault(x => x.StartsWith("PRETTY_NAME=")); - - if (string.IsNullOrEmpty(releaseRaw)) - return "Linux (unknown release)"; - - var release = releaseRaw - .Replace("PRETTY_NAME=", "") - .Replace("\"", ""); - - if (string.IsNullOrEmpty(release)) - return "Linux (unknown release)"; - - return release; - } - - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - // macOS platform detected - var osVersion = Environment.OSVersion.Version; - return $"Shitty macOS {osVersion.Major}.{osVersion.Minor}.{osVersion.Build}"; - } - - // Unknown platform - return "Unknown"; - } - - #region CPU Usage - - public async Task GetCpuUsageAsync() - { - var result = new CpuUsageDetails(); - var perCoreUsages = new List(); - - // Initial read - var (cpuLastStats, cpuLastSums) = await ReadAllCpuStatsAsync(); - - await Task.Delay(1000); - - // Second read - var (cpuNowStats, cpuNowSums) = await ReadAllCpuStatsAsync(); - - for (var i = 0; i < cpuNowStats.Length; i++) - { - var cpuDelta = cpuNowSums[i] - cpuLastSums[i]; - var cpuIdle = cpuNowStats[i][3] - cpuLastStats[i][3]; - var cpuUsed = cpuDelta - cpuIdle; - - var usage = 100.0 * cpuUsed / cpuDelta; - - if (i == 0) - result.OverallUsage = usage; - else - perCoreUsages.Add(usage); - } - - result.PerCoreUsage = perCoreUsages.ToArray(); - - // Get model name - var cpuInfoLines = await File.ReadAllLinesAsync("/proc/cpuinfo"); - var modelLine = cpuInfoLines.FirstOrDefault(x => x.StartsWith("model name")); - result.Model = modelLine?.Split(":")[1].Trim() ?? "N/A"; - - return result; - } - - private async Task<(long[][] cpuStatsList, long[] cpuSums)> ReadAllCpuStatsAsync() - { - var lines = await File.ReadAllLinesAsync("/proc/stat"); - - lines = lines.Where(line => line.StartsWith("cpu")) - .TakeWhile(line => line.StartsWith("cpu")) // Ensures only CPU lines are read - .ToArray(); - - var statsList = new List(); - var sumList = new List(); - - foreach (var line in lines) - { - var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries) - .Skip(1) // Skip the "cpu" label - .ToArray(); - - var cpuTimes = parts - .Select(long.Parse) - .ToArray(); - - var sum = cpuTimes.Sum(); - - statsList.Add(cpuTimes); - sumList.Add(sum); - } - - return (statsList.ToArray(), sumList.ToArray()); - } - - #endregion - - #region Memory - - public async Task ClearCachedMemoryAsync() - { - await File.WriteAllTextAsync("/proc/sys/vm/drop_caches", "3"); - } - - public async Task GetMemoryUsageAsync() - { - var details = new MemoryUsageDetails(); - - var lines = await File.ReadAllLinesAsync("/proc/meminfo"); - - foreach (var line in lines) - { - // We want to ignore all non kilobyte values - if (!line.Contains("kB")) - continue; - - // Split the line up so we can extract the id and the value - // to map it to the model field - var parts = line.Split(":"); - - var id = parts[0]; - var value = parts[1] - .Replace("kB", "") - .Trim(); - - if (!long.TryParse(value, out var longValue)) - continue; - - var bytes = ByteConverter.FromKiloBytes(longValue).Bytes; - - switch (id) - { - case "MemTotal": - details.Total = bytes; - break; - - case "MemFree": - details.Free = bytes; - break; - - case "MemAvailable": - details.Available = bytes; - break; - - case "Cached": - details.Cached = bytes; - break; - - case "SwapTotal": - details.SwapTotal = bytes; - break; - - case "SwapFree": - details.SwapFree = bytes; - break; - } - } - - return details; - } - - #endregion - - #region Disks - - public async Task GetDiskUsagesAsync() - { - var details = new List(); - - // First we need to check which mounts actually exist - var diskDevices = new Dictionary(); - string[] ignoredMounts = ["/boot/efi", "/boot"]; - - var mountLines = await File.ReadAllLinesAsync("/proc/mounts"); - - foreach (var mountLine in mountLines) - { - var parts = mountLine.Split(" "); - - var device = parts[0]; - var mountedAt = parts[1]; - - // We only want to handle mounted physical devices - if (!device.StartsWith("/dev/")) - continue; - - // Ignore certain mounts which we dont want to show - if (ignoredMounts.Contains(mountedAt)) - continue; - - diskDevices.Add(device, mountedAt); - } - - foreach (var diskMount in diskDevices) - { - var device = diskMount.Key; - var mount = diskMount.Value; - - var statusCode = Syscall.statvfs(mount, out var statvfs); - - if (statusCode != 0) - { - var error = Stdlib.GetLastError(); - - Logger.LogError( - "An error occured while checking disk stats for mount {mount}: {error}", - mount, - error - ); - - continue; - } - - // Source: https://man7.org/linux/man-pages/man3/statvfs.3.html - var detail = new DiskUsageDetails() - { - Device = device, - MountPath = mount, - DiskTotal = statvfs.f_blocks * statvfs.f_frsize, - DiskFree = statvfs.f_bfree * statvfs.f_frsize, - InodesTotal = statvfs.f_files, - InodesFree = statvfs.f_ffree - }; - - details.Add(detail); - } - - return details.ToArray(); - } - - #endregion -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Helpers/ServerFileSystem.cs b/MoonlightServers.Daemon/Helpers/ServerFileSystem.cs deleted file mode 100644 index bf7f05a..0000000 --- a/MoonlightServers.Daemon/Helpers/ServerFileSystem.cs +++ /dev/null @@ -1,354 +0,0 @@ -using System.Text; -using ICSharpCode.SharpZipLib.GZip; -using ICSharpCode.SharpZipLib.Tar; -using ICSharpCode.SharpZipLib.Zip; -using Mono.Unix.Native; -using MoonCore.Unix.SecureFs; -using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Servers; -using MoonlightServers.DaemonShared.Enums; - -namespace MoonlightServers.Daemon.Helpers; - -public class ServerFileSystem -{ - private readonly SecureFileSystem FileSystem; - - public ServerFileSystem(SecureFileSystem fileSystem) - { - FileSystem = fileSystem; - } - - public Task ListAsync(string inputPath) - { - var path = Normalize(inputPath); - var entries = FileSystem.ReadDir(path); - - IEnumerable entryQuery = entries; - - // Filter all lost+found directories on the root of the file system - // to hide the folder shown by virtual disk volumes - if (string.IsNullOrEmpty(inputPath) || inputPath == "/") - entryQuery = entryQuery.Where(x => x.Name != "lost+found"); - - var result = entryQuery - .Select(x => new ServerFileSystemResponse() - { - Name = x.Name, - IsFolder = x.IsDirectory, - Size = x.Size, - UpdatedAt = x.LastChanged, - CreatedAt = x.CreatedAt - }) - .ToArray(); - - return Task.FromResult(result); - } - - public Task MoveAsync(string inputOldPath, string inputNewPath) - { - var oldPath = Normalize(inputOldPath); - var newPath = Normalize(inputNewPath); - - FileSystem.Rename(oldPath, newPath); - - return Task.CompletedTask; - } - - public Task DeleteAsync(string inputPath) - { - var path = Normalize(inputPath); - - FileSystem.RemoveAll(path); - - return Task.CompletedTask; - } - - public Task MkdirAsync(string inputPath) - { - var path = Normalize(inputPath); - - FileSystem.MkdirAll(path, FilePermissions.ACCESSPERMS); - - return Task.CompletedTask; - } - - public Task TouchAsync(string inputPath) - { - var path = Normalize(inputPath); - - var parentDirectory = Path.GetDirectoryName(path); - - if (!string.IsNullOrEmpty(parentDirectory) && parentDirectory != "/") - FileSystem.MkdirAll(parentDirectory, FilePermissions.ACCESSPERMS); - - FileSystem.OpenFileWrite( - path, - _ => { }, - OpenFlags.O_CREAT - ); // We use these custom flags to ensure we aren't overwriting the file - - return Task.CompletedTask; - } - - public Task CreateChunkAsync(string inputPath, long totalSize, long positionToSkip, Stream chunkStream) - { - var path = Normalize(inputPath); - - var parentDirectory = Path.GetDirectoryName(path); - - if (!string.IsNullOrEmpty(parentDirectory) && parentDirectory != "/") - FileSystem.MkdirAll(parentDirectory, FilePermissions.ACCESSPERMS); - - FileSystem.OpenFileWrite(path, fileStream => - { - if (fileStream.Length != totalSize) - fileStream.SetLength(totalSize); - - fileStream.Position = positionToSkip; - - chunkStream.CopyTo(fileStream); - fileStream.Flush(); - }, OpenFlags.O_CREAT | OpenFlags.O_RDWR); // We use these custom flags to ensure we aren't overwriting the file - - return Task.CompletedTask; - } - - public Task CreateAsync(string inputPath, Stream dataStream) - { - var path = Normalize(inputPath); - - var parentDirectory = Path.GetDirectoryName(path); - - if (!string.IsNullOrEmpty(parentDirectory) && parentDirectory != "/") - FileSystem.MkdirAll(parentDirectory, FilePermissions.ACCESSPERMS); - - FileSystem.OpenFileWrite(path, stream => - { - stream.Position = 0; - dataStream.CopyTo(stream); - - stream.Flush(); - }); - - return Task.CompletedTask; - } - - public Task ReadAsync(string inputPath, Func onHandle) - { - var path = Normalize(inputPath); - - FileSystem.OpenFileRead(path, stream => - { - // No try catch here because the safe fs abstraction already handles every error occuring in the handle - onHandle.Invoke(stream).Wait(); - }); - - return Task.CompletedTask; - } - - #region Compression - - public Task CompressAsync(string[] itemsInput, string destinationInput, CompressType type) - { - var destination = Normalize(destinationInput); - var items = itemsInput.Select(Normalize); - - if (type == CompressType.Zip) - { - FileSystem.OpenFileWrite(destination, stream => - { - using var zipStream = new ZipOutputStream(stream); - - foreach (var item in items) - AddItemToZip(item, zipStream); - - zipStream.Flush(); - stream.Flush(); - - zipStream.Close(); - }); - } - else if (type == CompressType.TarGz) - { - FileSystem.OpenFileWrite(destination, stream => - { - using var gzStream = new GZipOutputStream(stream); - using var tarStream = new TarOutputStream(gzStream, Encoding.UTF8); - - foreach (var item in items) - AddItemToTar(item, tarStream); - - tarStream.Flush(); - gzStream.Flush(); - stream.Flush(); - - tarStream.Close(); - gzStream.Close(); - }); - } - - return Task.CompletedTask; - } - - public Task DecompressAsync(string pathInput, string destinationInput, CompressType type) - { - var path = Normalize(pathInput); - var destination = Normalize(destinationInput); - - if (type == CompressType.Zip) - { - FileSystem.OpenFileRead(path, fileStream => - { - var zipInputStream = new ZipInputStream(fileStream); - - ExtractZip(zipInputStream, destination); - }); - } - else if (type == CompressType.TarGz) - { - FileSystem.OpenFileRead(path, fileStream => - { - var gzInputStream = new GZipInputStream(fileStream); - var zipInputStream = new TarInputStream(gzInputStream, Encoding.UTF8); - - ExtractTar(zipInputStream, destination); - }); - } - - return Task.CompletedTask; - } - - private void AddItemToZip(string path, ZipOutputStream outputStream) - { - var item = FileSystem.Stat(path); - - if (item.IsDirectory) - { - var contents = FileSystem.ReadDir(path); - - foreach (var content in contents) - { - AddItemToZip( - Path.Combine(path, content.Name), - outputStream - ); - } - } - else - { - var entry = new ZipEntry(path) - { - Size = item.Size, - DateTime = item.LastChanged - }; - - outputStream.PutNextEntry(entry); - - FileSystem.OpenFileRead(path, stream => { stream.CopyTo(outputStream); }); - - outputStream.CloseEntry(); - } - } - - private void AddItemToTar(string path, TarOutputStream outputStream) - { - var item = FileSystem.Stat(path); - - if (item.IsDirectory) - { - var contents = FileSystem.ReadDir(path); - - foreach (var content in contents) - { - AddItemToTar( - Path.Combine(path, content.Name), - outputStream - ); - } - } - else - { - var entry = TarEntry.CreateTarEntry(path); - - entry.Name = path; - entry.Size = item.Size; - entry.ModTime = item.LastChanged; - - outputStream.PutNextEntry(entry); - - FileSystem.OpenFileRead(path, stream => { stream.CopyTo(outputStream); }); - - outputStream.CloseEntry(); - } - } - - private void ExtractZip(ZipInputStream inputStream, string destination) - { - while (true) - { - var entry = inputStream.GetNextEntry(); - - if (entry == null) - break; - - if (entry.IsDirectory) - continue; - - var fileDestination = Path.Combine(destination, entry.Name); - - var parentDirectory = Path.GetDirectoryName(fileDestination); - - if (!string.IsNullOrEmpty(parentDirectory) && parentDirectory != "/") - FileSystem.MkdirAll(parentDirectory, FilePermissions.ACCESSPERMS); - - FileSystem.OpenFileWrite(fileDestination, stream => - { - stream.Position = 0; - - inputStream.CopyTo(stream); - - stream.Flush(); - }); // This will override the file if it exists - } - } - - private void ExtractTar(TarInputStream inputStream, string destination) - { - while (true) - { - var entry = inputStream.GetNextEntry(); - - if (entry == null) - break; - - if (entry.IsDirectory) - continue; - - var fileDestination = Path.Combine(destination, entry.Name); - - var parentDirectory = Path.GetDirectoryName(fileDestination); - - if (!string.IsNullOrEmpty(parentDirectory) && parentDirectory != "/") - FileSystem.MkdirAll(parentDirectory, FilePermissions.ACCESSPERMS); - - FileSystem.OpenFileWrite(fileDestination, stream => - { - stream.Position = 0; - - inputStream.CopyTo(stream); - - stream.Flush(); - }); // This will override the file if it exists - } - } - - #endregion - - private string Normalize(string path) - { - return path - .Replace("//", "/") - .Replace("..", "") - .TrimStart('/'); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Helpers/TokenAuthOptions.cs b/MoonlightServers.Daemon/Helpers/TokenAuthOptions.cs deleted file mode 100644 index d853d95..0000000 --- a/MoonlightServers.Daemon/Helpers/TokenAuthOptions.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Microsoft.AspNetCore.Authentication; - -namespace MoonlightServers.Daemon.Helpers; - -public class TokenAuthOptions : AuthenticationSchemeOptions -{ - public string Token { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Helpers/TokenAuthScheme.cs b/MoonlightServers.Daemon/Helpers/TokenAuthScheme.cs deleted file mode 100644 index 0c08b7a..0000000 --- a/MoonlightServers.Daemon/Helpers/TokenAuthScheme.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Security.Claims; -using System.Text.Encodings.Web; -using Microsoft.AspNetCore.Authentication; -using Microsoft.Extensions.Options; - -namespace MoonlightServers.Daemon.Helpers; - -public class TokenAuthScheme : AuthenticationHandler -{ - public TokenAuthScheme(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, - ISystemClock clock) : base(options, logger, encoder, clock) - { - } - - public TokenAuthScheme(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) : base( - options, logger, encoder) - { - } - - protected override Task HandleAuthenticateAsync() - { - if (!Request.Headers.ContainsKey("Authorization")) - return Task.FromResult(AuthenticateResult.NoResult()); - - var authHeaderValue = Request.Headers["Authorization"].FirstOrDefault(); - - if (string.IsNullOrEmpty(authHeaderValue)) - return Task.FromResult(AuthenticateResult.NoResult()); - - if (!authHeaderValue.Contains("Bearer ")) - return Task.FromResult(AuthenticateResult.NoResult()); - - var providedToken = authHeaderValue - .Replace("Bearer ", "") - .Trim(); - - if (providedToken != Options.Token) - return Task.FromResult(AuthenticateResult.NoResult()); - - return Task.FromResult(AuthenticateResult.Success( - new AuthenticationTicket( - new ClaimsPrincipal( - new ClaimsIdentity("token") - ), - "token" - ) - )); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Helpers/UnsafeDockerClient.cs b/MoonlightServers.Daemon/Helpers/UnsafeDockerClient.cs deleted file mode 100644 index 9a529fc..0000000 --- a/MoonlightServers.Daemon/Helpers/UnsafeDockerClient.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Net.Sockets; -using System.Text.Json; -using MoonCore.Attributes; -using MoonCore.Helpers; -using MoonlightServers.Daemon.Configuration; -using MoonlightServers.Daemon.Models.UnsafeDocker; - -namespace MoonlightServers.Daemon.Helpers; - -[Singleton] -public class UnsafeDockerClient -{ - private readonly AppConfiguration Configuration; - - public UnsafeDockerClient(AppConfiguration configuration) - { - Configuration = configuration; - } - - public Task CreateHttpClientAsync() - { - var client = new HttpClient(new SocketsHttpHandler() - { - ConnectCallback = async (context, token) => - { - var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP); - var endpoint = new UnixDomainSocketEndPoint( - Formatter.ReplaceStart(Configuration.Docker.Uri, "unix://", "") - ); - await socket.ConnectAsync(endpoint, token); - return new NetworkStream(socket, ownsSocket: true); - } - }); - - return Task.FromResult(client); - } - - public async Task GetDataUsageAsync() - { - using var client = await CreateHttpClientAsync(); - var responseJson = await client.GetStringAsync("http://some.random.domain/v1.47/system/df"); - var response = JsonSerializer.Deserialize(responseJson)!; - - return response; - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Http/Controllers/Servers/PowerController.cs b/MoonlightServers.Daemon/Http/Controllers/Servers/PowerController.cs deleted file mode 100644 index f2b8c0b..0000000 --- a/MoonlightServers.Daemon/Http/Controllers/Servers/PowerController.cs +++ /dev/null @@ -1,77 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using MoonlightServers.Daemon.ServerSystem.Enums; -using MoonlightServers.Daemon.Services; - -namespace MoonlightServers.Daemon.Http.Controllers.Servers; - -[ApiController] -[Route("api/servers/{id:int}")] -public class PowerController : Controller -{ - private readonly ServerService ServerService; - - public PowerController(ServerService serverService) - { - ServerService = serverService; - } - - [HttpPost("start")] - public async Task StartAsync([FromRoute] int id) - { - var server = ServerService.GetById(id); - - if (server == null) - return Problem("No server with this id found", statusCode: 404); - - if (!server.StateMachine.CanFire(ServerTrigger.Start)) - return Problem("Cannot fire start trigger in this state"); - - await server.StateMachine.FireAsync(ServerTrigger.Start); - return NoContent(); - } - - [HttpPost("stop")] - public async Task StopAsync([FromRoute] int id) - { - var server = ServerService.GetById(id); - - if (server == null) - return Problem("No server with this id found", statusCode: 404); - - if (!server.StateMachine.CanFire(ServerTrigger.Stop)) - return Problem("Cannot fire stop trigger in this state"); - - await server.StateMachine.FireAsync(ServerTrigger.Stop); - return NoContent(); - } - - [HttpPost("kill")] - public async Task KillAsync([FromRoute] int id) - { - var server = ServerService.GetById(id); - - if (server == null) - return Problem("No server with this id found", statusCode: 404); - - if (!server.StateMachine.CanFire(ServerTrigger.Kill)) - return Problem("Cannot fire kill trigger in this state"); - - await server.StateMachine.FireAsync(ServerTrigger.Kill); - return NoContent(); - } - - [HttpPost("install")] - public async Task InstallAsync([FromRoute] int id) - { - var server = ServerService.GetById(id); - - if (server == null) - return Problem("No server with this id found", statusCode: 404); - - if (!server.StateMachine.CanFire(ServerTrigger.Install)) - return Problem("Cannot fire install trigger in this state"); - - await server.StateMachine.FireAsync(ServerTrigger.Install); - return NoContent(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Http/Controllers/Servers/ServersController.cs b/MoonlightServers.Daemon/Http/Controllers/Servers/ServersController.cs deleted file mode 100644 index 1c09495..0000000 --- a/MoonlightServers.Daemon/Http/Controllers/Servers/ServersController.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using MoonlightServers.Daemon.Mappers; -using MoonlightServers.Daemon.Services; -using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Servers; -using MoonlightServers.DaemonShared.Enums; - -namespace MoonlightServers.Daemon.Http.Controllers.Servers; - -[ApiController] -[Route("api/servers/{id:int}")] -public class ServersController : Controller -{ - private readonly ServerService ServerService; - private readonly ServerConfigurationMapper ConfigurationMapper; - - public ServersController(ServerService serverService, ServerConfigurationMapper configurationMapper) - { - ServerService = serverService; - ConfigurationMapper = configurationMapper; - } - - [HttpPost("sync")] - public async Task SyncAsync([FromRoute] int id) - { - await ServerService.InitializeByIdAsync(id); - return NoContent(); - } - - [HttpGet("status")] - public async Task> StatusAsync([FromRoute] int id) - { - var server = ServerService.GetById(id); - - if (server == null) - return Problem("No server with this id found", statusCode: 404); - - return new ServerStatusResponse() - { - State = (ServerState)server.StateMachine.State - }; - } - - [HttpGet("logs")] - public async Task> LogsAsync([FromRoute] int id) - { - var server = ServerService.GetById(id); - - if (server == null) - return Problem("No server with this id found", statusCode: 404); - - var messages = await server.Console.GetCacheAsync(); - - return new ServerLogsResponse() - { - Messages = messages.ToArray() - }; - } - - [HttpGet("stats")] - public async Task GetStatsAsync([FromRoute] int id) - { - return new ServerStatsResponse() - { - - }; - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Http/Controllers/Statistics/StatisticsController.cs b/MoonlightServers.Daemon/Http/Controllers/Statistics/StatisticsController.cs deleted file mode 100644 index ec032ac..0000000 --- a/MoonlightServers.Daemon/Http/Controllers/Statistics/StatisticsController.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using MoonlightServers.Daemon.Helpers; -using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Statistics; - -namespace MoonlightServers.Daemon.Http.Controllers.Statistics; - -[Authorize] -[ApiController] -[Route("api/statistics")] -public class StatisticsController : Controller -{ - private readonly HostSystemHelper HostSystemHelper; - - public StatisticsController(HostSystemHelper hostSystemHelper) - { - HostSystemHelper = hostSystemHelper; - } - - [HttpGet] - public async Task GetAsync() - { - var response = new StatisticsResponse(); - - var cpuUsage = await HostSystemHelper.GetCpuUsageAsync(); - - response.Cpu.Model = cpuUsage.Model; - response.Cpu.Usage = cpuUsage.OverallUsage; - response.Cpu.UsagePerCore = cpuUsage.PerCoreUsage; - - var memoryUsage = await HostSystemHelper.GetMemoryUsageAsync(); - - response.Memory.Available = memoryUsage.Available; - response.Memory.Cached = memoryUsage.Cached; - response.Memory.Free = memoryUsage.Free; - response.Memory.Total = memoryUsage.Total; - response.Memory.SwapTotal = memoryUsage.SwapTotal; - response.Memory.SwapFree = memoryUsage.SwapFree; - - var diskDetails = await HostSystemHelper.GetDiskUsagesAsync(); - - response.Disks = diskDetails.Select(x => new StatisticsResponse.DiskData() - { - Device = x.Device, - MountPath = x.MountPath, - DiskFree = x.DiskFree, - DiskTotal = x.DiskTotal, - InodesFree = x.InodesFree, - InodesTotal = x.InodesTotal - }).ToArray(); - - return response; - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Http/Controllers/Statistics/StatisticsDockerController.cs b/MoonlightServers.Daemon/Http/Controllers/Statistics/StatisticsDockerController.cs deleted file mode 100644 index 38ce37e..0000000 --- a/MoonlightServers.Daemon/Http/Controllers/Statistics/StatisticsDockerController.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using MoonlightServers.Daemon.Services; -using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Statistics; - -namespace MoonlightServers.Daemon.Http.Controllers.Statistics; - -// This controller hosts endpoints for the statistics for the docker environment - -[Authorize] -[ApiController] -[Route("api/statistics/docker")] -public class StatisticsDockerController : Controller -{ - private readonly DockerInfoService DockerInfoService; - - public StatisticsDockerController(DockerInfoService dockerInfoService) - { - DockerInfoService = dockerInfoService; - } - - [HttpGet] - public async Task GetAsync() - { - var usage = await DockerInfoService.GetDataUsageAsync(); - - return new StatisticsDockerResponse - { - Version = await DockerInfoService.GetDockerVersionAsync(), - ContainersReclaimable = usage.Containers.Reclaimable, - ContainersUsed = usage.Containers.Used, - BuildCacheReclaimable = usage.BuildCache.Reclaimable, - BuildCacheUsed = usage.BuildCache.Used, - ImagesUsed = usage.Images.Used, - ImagesReclaimable = usage.Images.Reclaimable - }; - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Http/Controllers/Sys/SystemStatusController.cs b/MoonlightServers.Daemon/Http/Controllers/Sys/SystemStatusController.cs deleted file mode 100644 index a046e0d..0000000 --- a/MoonlightServers.Daemon/Http/Controllers/Sys/SystemStatusController.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Diagnostics; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using MoonlightServers.Daemon.Services; -using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Sys; - -namespace MoonlightServers.Daemon.Http.Controllers.Sys; - -[Authorize] -[ApiController] -[Route("api/system/status")] -public class SystemStatusController : Controller -{ - private readonly RemoteService RemoteService; - - public SystemStatusController(RemoteService remoteService) - { - RemoteService = remoteService; - } - - public async Task GetAsync() - { - SystemStatusResponse response; - - var sw = new Stopwatch(); - sw.Start(); - - try - { - await RemoteService.GetStatusAsync(); - - sw.Stop(); - - response = new() - { - TripSuccess = true, - TripTime = sw.Elapsed, - Version = "2.1.0" // TODO: Set global - }; - } - catch (Exception e) - { - sw.Stop(); - - response = new() - { - TripError = e.Message, - TripTime = sw.Elapsed, - TripSuccess = false, - Version = "2.1.0" // TODO: Set global - }; - } - - return response; - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Http/Hubs/ServerWebSocketHub.cs b/MoonlightServers.Daemon/Http/Hubs/ServerWebSocketHub.cs deleted file mode 100644 index 82394b9..0000000 --- a/MoonlightServers.Daemon/Http/Hubs/ServerWebSocketHub.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.SignalR; - -namespace MoonlightServers.Daemon.Http.Hubs; - -[Authorize(AuthenticationSchemes = "accessToken", Policy = "serverWebsocket")] -public class ServerWebSocketHub : Hub -{ - private readonly ILogger Logger; - - public ServerWebSocketHub(ILogger logger) - { - Logger = logger; - } - - public override async Task OnConnectedAsync() - { - // The policies validated already the type and the token so we can assume we are authenticated - // and just start adding ourselves into the desired group - - var serverId = Context.User!.Claims.First(x => x.Type == "serverId").Value; - - await Groups.AddToGroupAsync( - Context.ConnectionId, - serverId - ); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Mappers/ServerConfigurationMapper.cs b/MoonlightServers.Daemon/Mappers/ServerConfigurationMapper.cs deleted file mode 100644 index 92750eb..0000000 --- a/MoonlightServers.Daemon/Mappers/ServerConfigurationMapper.cs +++ /dev/null @@ -1,363 +0,0 @@ -using Docker.DotNet.Models; -using Mono.Unix.Native; -using MoonCore.Helpers; -using MoonlightServers.Daemon.Configuration; -using MoonlightServers.Daemon.Models.Cache; -using MoonlightServers.DaemonShared.PanelSide.Http.Responses; - -namespace MoonlightServers.Daemon.Mappers; - -public class ServerConfigurationMapper -{ - private readonly AppConfiguration AppConfiguration; - - public ServerConfigurationMapper(AppConfiguration appConfiguration) - { - AppConfiguration = appConfiguration; - } - - public ServerConfiguration FromServerDataResponse(ServerDataResponse response) - { - return new ServerConfiguration() - { - Id = response.Id, - StartupCommand = response.StartupCommand, - Allocations = response.Allocations.Select(y => new ServerConfiguration.AllocationConfiguration() - { - IpAddress = y.IpAddress, - Port = y.Port - }).ToArray(), - Variables = response.Variables, - OnlineDetection = response.OnlineDetection, - DockerImage = response.DockerImage, - Cpu = response.Cpu, - Disk = response.Disk, - Memory = response.Memory, - StopCommand = response.StopCommand, - }; - } - - public CreateContainerParameters ToRuntimeParameters( - ServerConfiguration serverConfiguration, - string hostPath, - string containerName - ) - { - var parameters = ToSharedParameters(serverConfiguration); - - #region Security - - parameters.HostConfig.CapDrop = new List() - { - "setpcap", "mknod", "audit_write", "net_raw", "dac_override", - "fowner", "fsetid", "net_bind_service", "sys_chroot", "setfcap" - }; - - parameters.HostConfig.ReadonlyRootfs = true; - parameters.HostConfig.SecurityOpt = new List() - { - "no-new-privileges" - }; - - #endregion - - #region Name - - parameters.Name = containerName; - parameters.Hostname = containerName; - - #endregion - - #region Docker Image - - parameters.Image = serverConfiguration.DockerImage; - - #endregion - - #region Working Dir - - parameters.WorkingDir = "/home/container"; - - #endregion - - #region User - - // TODO: Extract this to an external service with config options and return a userspace user id and a install user id - // in order to know which permissions are required in order to run the container with the correct permissions - - var userId = Syscall.getuid(); - - if (userId == 0) - userId = 998; - - parameters.User = $"{userId}:{userId}"; - - /* - if (userId == 0) - { - // We are running as root, so we need to run the container as another user and chown the files when we make changes - parameters.User = $"998:998"; - } - else - { - // We are not running as root, so we start the container as the same user, - // as we are not able to chown the container content to a different user - parameters.User = $"{userId}:{userId}"; - }*/ - - #endregion - - #region Mounts - - parameters.HostConfig.Mounts = new List(); - - parameters.HostConfig.Mounts.Add(new() - { - Source = hostPath, - Target = "/home/container", - ReadOnly = false, - Type = "bind" - }); - - #endregion - - #region Port Bindings - - if (true) // TODO: Add network toggle - { - parameters.ExposedPorts = new Dictionary(); - parameters.HostConfig.PortBindings = new Dictionary>(); - - foreach (var allocation in serverConfiguration.Allocations) - { - parameters.ExposedPorts.Add($"{allocation.Port}/tcp", new()); - parameters.ExposedPorts.Add($"{allocation.Port}/udp", new()); - - parameters.HostConfig.PortBindings.Add($"{allocation.Port}/tcp", new List - { - new() - { - HostPort = allocation.Port.ToString(), - HostIP = allocation.IpAddress - } - }); - - parameters.HostConfig.PortBindings.Add($"{allocation.Port}/udp", new List - { - new() - { - HostPort = allocation.Port.ToString(), - HostIP = allocation.IpAddress - } - }); - } - } - - #endregion - - // TODO: Implement a way to directly startup a server without using the entrypoint.sh and parsing the startup command here - // in the daemon instead of letting it the entrypoint do. iirc pelican wants to do that as well so we need to do that - // sooner or later in order to stay compatible to pelican - // Possible flag name: LegacyEntrypointMode - - return parameters; - } - - public CreateContainerParameters ToInstallParameters( - ServerConfiguration serverConfiguration, - ServerInstallDataResponse installData, - string runtimeHostPath, - string installationHostPath, - string containerName - ) - { - var parameters = ToSharedParameters(serverConfiguration); - - // - Name - parameters.Name = containerName; - parameters.Hostname = containerName; - - // - Image - parameters.Image = installData.DockerImage; - - // -- Working directory - parameters.WorkingDir = "/mnt/server"; - - // - User - // Note: Some images might not work if we set a user here - - var userId = Syscall.getuid(); - - // If we are root, we are able to change owner permissions after the installation - // so we run the installation as root, otherwise we need to run it as our current user, - // so we are able to access the files created by the installer - if (userId == 0) - parameters.User = "0:0"; - else - parameters.User = $"{userId}:{userId}"; - - // -- Mounts - parameters.HostConfig.Mounts = new List(); - - parameters.HostConfig.Mounts.Add(new() - { - Source = runtimeHostPath, - Target = "/mnt/server", - ReadOnly = false, - Type = "bind" - }); - - parameters.HostConfig.Mounts.Add(new() - { - Source = installationHostPath, - Target = "/mnt/install", - ReadOnly = false, - Type = "bind" - }); - - parameters.Cmd = [installData.Shell, "/mnt/install/install.sh"]; - - return parameters; - } - - public CreateContainerParameters ToSharedParameters(ServerConfiguration serverConfiguration) - { - var parameters = new CreateContainerParameters() - { - HostConfig = new() - }; - - #region Input, output & error streams and tty - - parameters.Tty = true; - parameters.AttachStderr = true; - parameters.AttachStdin = true; - parameters.AttachStdout = true; - parameters.OpenStdin = true; - - #endregion - - #region CPU - - parameters.HostConfig.CPUQuota = serverConfiguration.Cpu * 1000; - parameters.HostConfig.CPUPeriod = 100000; - parameters.HostConfig.CPUShares = 1024; - - #endregion - - #region Memory & Swap - - var memoryLimit = serverConfiguration.Memory; - - // The overhead multiplier gives the container a little bit more memory to prevent crashes - var memoryOverhead = memoryLimit + (memoryLimit * AppConfiguration.Server.MemoryOverheadMultiplier); - - long swapLimit = -1; - - /* - - // If swap is enabled globally and not disabled on this server, set swap - if (!configuration.Limits.DisableSwap && config.Server.EnableSwap) - swapLimit = (long)(memoryOverhead + memoryOverhead * config.Server.SwapMultiplier); - co - */ - - // Finalize limits by converting and updating the host config - parameters.HostConfig.Memory = ByteConverter.FromMegaBytes((long)memoryOverhead, 1000).Bytes; - parameters.HostConfig.MemoryReservation = ByteConverter.FromMegaBytes(memoryLimit, 1000).Bytes; - parameters.HostConfig.MemorySwap = - swapLimit == -1 ? swapLimit : ByteConverter.FromMegaBytes(swapLimit, 1000).Bytes; - - #endregion - - #region Misc Limits - - // -- Other limits - parameters.HostConfig.BlkioWeight = 100; - //container.HostConfig.PidsLimit = configuration.Limits.PidsLimit; - parameters.HostConfig.OomKillDisable = true; //!configuration.Limits.EnableOomKill; - - #endregion - - #region DNS - - // TODO: Read hosts dns settings? - - parameters.HostConfig.DNS = /*config.Docker.DnsServers.Any() ? config.Docker.DnsServers :*/ new List() - { - "1.1.1.1", - "9.9.9.9" - }; - - #endregion - - #region Tmpfs - - parameters.HostConfig.Tmpfs = new Dictionary() - { - { "/tmp", $"rw,exec,nosuid,size={AppConfiguration.Server.TmpFsSize}M" } - }; - - #endregion - - #region Logging - - parameters.HostConfig.LogConfig = new() - { - Type = "json-file", // We need to use this provider, as the GetLogs endpoint needs it - Config = new Dictionary() - }; - - #endregion - - #region Labels - - parameters.Labels = new Dictionary(); - - parameters.Labels.Add("Software", "Moonlight-Panel"); - parameters.Labels.Add("ServerId", serverConfiguration.Id.ToString()); - - #endregion - - #region Environment - - parameters.Env = CreateEnvironmentVariables(serverConfiguration); - - #endregion - - return parameters; - } - - private List CreateEnvironmentVariables(ServerConfiguration serverConfiguration) - { - var result = new Dictionary - { - //TODO: Add timezone, add server ip - { "STARTUP", serverConfiguration.StartupCommand }, - { "SERVER_MEMORY", serverConfiguration.Memory.ToString() } - }; - - if (serverConfiguration.Allocations.Length > 0) - { - for (var i = 0; i < serverConfiguration.Allocations.Length; i++) - { - var allocation = serverConfiguration.Allocations[i]; - - result.Add($"ML_PORT_{i}", allocation.Port.ToString()); - - if (i == 0) // TODO: Implement a way to set the default/main allocation - { - result.Add("SERVER_IP", allocation.IpAddress); - result.Add("SERVER_PORT", allocation.Port.ToString()); - } - } - } - - // Copy variables as env vars - foreach (var variable in serverConfiguration.Variables) - result.Add(variable.Key, variable.Value); - - // Convert to the format of the docker library - return result.Select(variable => $"{variable.Key}={variable.Value}").ToList(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Models/Cache/ServerConfiguration.cs b/MoonlightServers.Daemon/Models/Cache/ServerConfiguration.cs deleted file mode 100644 index 5111fac..0000000 --- a/MoonlightServers.Daemon/Models/Cache/ServerConfiguration.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace MoonlightServers.Daemon.Models.Cache; - -public class ServerConfiguration -{ - public int Id { get; set; } - - // Limits - public int Cpu { get; set; } - public int Memory { get; set; } - public int Disk { get; set; } - - // Start, Stop & Status - public string StartupCommand { get; set; } - public string StopCommand { get; set; } - public string OnlineDetection { get; set; } - - // Container - public string DockerImage { get; set; } - public AllocationConfiguration[] Allocations { get; set; } - - public Dictionary Variables { get; set; } - - public struct AllocationConfiguration - { - public string IpAddress { get; set; } - public int Port { get; set; } - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Models/CpuUsageDetails.cs b/MoonlightServers.Daemon/Models/CpuUsageDetails.cs deleted file mode 100644 index 3d8a5c6..0000000 --- a/MoonlightServers.Daemon/Models/CpuUsageDetails.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace MoonlightServers.Daemon.Models; - -public class CpuUsageDetails -{ - public string Model { get; set; } - public double OverallUsage { get; set; } - public double[] PerCoreUsage { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Models/DiskUsageDetails.cs b/MoonlightServers.Daemon/Models/DiskUsageDetails.cs deleted file mode 100644 index 60ff9b8..0000000 --- a/MoonlightServers.Daemon/Models/DiskUsageDetails.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MoonlightServers.Daemon.Models; - -public class DiskUsageDetails -{ - public string Device { get; set; } - public string MountPath { get; set; } - public ulong DiskTotal { get; set; } - public ulong DiskFree { get; set; } - public ulong InodesTotal { get; set; } - public ulong InodesFree { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Models/InstallConfiguration.cs b/MoonlightServers.Daemon/Models/InstallConfiguration.cs new file mode 100644 index 0000000..98ef82f --- /dev/null +++ b/MoonlightServers.Daemon/Models/InstallConfiguration.cs @@ -0,0 +1,3 @@ +namespace MoonlightServers.Daemon.Models; + +public record InstallConfiguration(string Shell, string DockerImage, string Script); \ No newline at end of file diff --git a/MoonlightServers.Daemon/Models/MemoryUsageDetails.cs b/MoonlightServers.Daemon/Models/MemoryUsageDetails.cs deleted file mode 100644 index 39a2d1d..0000000 --- a/MoonlightServers.Daemon/Models/MemoryUsageDetails.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MoonlightServers.Daemon.Models; - -public class MemoryUsageDetails -{ - public long Total { get; set; } - public long Available { get; set; } - public long Free { get; set; } - public long Cached { get; set; } - public long SwapTotal { get; set; } - public long SwapFree { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Models/RuntimeConfiguration.cs b/MoonlightServers.Daemon/Models/RuntimeConfiguration.cs new file mode 100644 index 0000000..ae87a38 --- /dev/null +++ b/MoonlightServers.Daemon/Models/RuntimeConfiguration.cs @@ -0,0 +1,47 @@ +namespace MoonlightServers.Daemon.Models; + +public record RuntimeConfiguration( + RuntimeLimitsConfig Limits, + RuntimeStorageConfig Storage, + RuntimeTemplateConfig Template, + RuntimeNetworkConfig Network, + RuntimeEnvironmentConfig Environment +); + +public record RuntimeLimitsConfig( + int? CpuPercent, + int? Threads, + int? MemoryMb, + int? SwapMb +); + +public record RuntimeStorageConfig( + string Provider, + Dictionary Options, + int LimitMb +); + +public record RuntimeTemplateConfig( + string DockerImage, + string StartupCommand, + string StopCommand, + string[] OnlineTexts +); + +public record RuntimeNetworkConfig( + string[] Networks, + string? FriendlyName, + string? OutgoingIpAddress, + RuntimePortConfig? MainPort, + RuntimePortConfig[] Ports +); + +public record RuntimePortConfig( + string IpAddress, + int Port +); + +public record RuntimeEnvironmentConfig( + Dictionary Labels, + Dictionary Variables +); \ No newline at end of file diff --git a/MoonlightServers.Daemon/Models/ServerConsole.cs b/MoonlightServers.Daemon/Models/ServerConsole.cs deleted file mode 100644 index 08d517c..0000000 --- a/MoonlightServers.Daemon/Models/ServerConsole.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace MoonlightServers.Daemon.Models; - -public class ServerConsole -{ - public event Func? OnOutput; - public event Func? OnInput; - - public string[] Messages => GetMessages(); - private readonly Queue MessageCache = new(); - private int MaxMessagesInCache; - - public ServerConsole(int maxMessagesInCache) - { - MaxMessagesInCache = maxMessagesInCache; - } - - public async Task WriteToOutputAsync(string content) - { - lock (MessageCache) - { - MessageCache.Enqueue(content); - - if (MessageCache.Count > MaxMessagesInCache) - MessageCache.Dequeue(); - } - - if (OnOutput != null) - { - await OnOutput - .Invoke(content) - .ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing); - } - } - - public async Task WriteToInputAsync(string content) - { - if (OnInput != null) - await OnInput.Invoke(content); - } - - private string[] GetMessages() - { - lock (MessageCache) - return MessageCache.ToArray(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Models/UnsafeDocker/DataUsageResponse.cs b/MoonlightServers.Daemon/Models/UnsafeDocker/DataUsageResponse.cs deleted file mode 100644 index 332a1c4..0000000 --- a/MoonlightServers.Daemon/Models/UnsafeDocker/DataUsageResponse.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Text.Json.Serialization; - -namespace MoonlightServers.Daemon.Models.UnsafeDocker; - -public class DataUsageResponse -{ - [JsonPropertyName("BuildCache")] - public BuildCacheData[] BuildCache { get; set; } - - [JsonPropertyName("LayersSize")] - public long LayersSize { get; set; } - - [JsonPropertyName("Images")] - public ImageData[] Images { get; set; } - - [JsonPropertyName("Containers")] - public ContainerData[] Containers { get; set; } - - [JsonPropertyName("Volumes")] - public VolumeData[] Volumes { get; set; } - - public class BuildCacheData - { - [JsonPropertyName("Size")] - public long Size { get; set; } - - [JsonPropertyName("InUse")] - public bool InUse { get; set; } - } - - public class ContainerData - { - [JsonPropertyName("Id")] - public string Id { get; set; } - - [JsonPropertyName("SizeRw")] - public long SizeRw { get; set; } - - [JsonPropertyName("SizeRootFs")] - public long SizeRootFs { get; set; } - } - - public class ImageData - { - [JsonPropertyName("Containers")] - public long Containers { get; set; } - - [JsonPropertyName("Id")] - public string Id { get; set; } - - [JsonPropertyName("SharedSize")] - public long SharedSize { get; set; } - - [JsonPropertyName("Size")] - public long Size { get; set; } - } - - public class VolumeData - { - [JsonPropertyName("Name")] - public string Name { get; set; } - - [JsonPropertyName("UsageData")] - public VolumeUsageData UsageData { get; set; } - } - - public class VolumeUsageData - { - [JsonPropertyName("RefCount")] - public long RefCount { get; set; } - - [JsonPropertyName("Size")] - public long Size { get; set; } - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Models/UnsafeDocker/UsageData.cs b/MoonlightServers.Daemon/Models/UnsafeDocker/UsageData.cs deleted file mode 100644 index d427ff1..0000000 --- a/MoonlightServers.Daemon/Models/UnsafeDocker/UsageData.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MoonlightServers.Daemon.Models.UnsafeDocker; - -public class UsageData -{ - public long Used { get; set; } - public long Reclaimable { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Models/UnsafeDocker/UsageDataReport.cs b/MoonlightServers.Daemon/Models/UnsafeDocker/UsageDataReport.cs deleted file mode 100644 index d8677c2..0000000 --- a/MoonlightServers.Daemon/Models/UnsafeDocker/UsageDataReport.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace MoonlightServers.Daemon.Models.UnsafeDocker; - -public class UsageDataReport -{ - public UsageData Containers { get; set; } - public UsageData Images { get; set; } - public UsageData BuildCache { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/MoonlightServers.Daemon.csproj b/MoonlightServers.Daemon/MoonlightServers.Daemon.csproj index d25877a..c55d216 100644 --- a/MoonlightServers.Daemon/MoonlightServers.Daemon.csproj +++ b/MoonlightServers.Daemon/MoonlightServers.Daemon.csproj @@ -1,69 +1,28 @@ - net9.0 + net10.0 enable enable - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - <_ContentIncludedByDefault Remove="storage\volumes\2\banned-ips.json" /> - <_ContentIncludedByDefault Remove="storage\volumes\2\banned-players.json" /> - <_ContentIncludedByDefault Remove="storage\volumes\2\ops.json" /> - <_ContentIncludedByDefault Remove="storage\volumes\2\plugins\spark\config.json" /> - <_ContentIncludedByDefault Remove="storage\volumes\2\usercache.json" /> - <_ContentIncludedByDefault Remove="storage\volumes\2\version_history.json" /> - <_ContentIncludedByDefault Remove="storage\volumes\2\whitelist.json" /> - <_ContentIncludedByDefault Remove="volumes\3\banned-ips.json" /> - <_ContentIncludedByDefault Remove="volumes\3\banned-players.json" /> - <_ContentIncludedByDefault Remove="volumes\3\ops.json" /> - <_ContentIncludedByDefault Remove="volumes\3\plugins\spark\config.json" /> - <_ContentIncludedByDefault Remove="volumes\3\usercache.json" /> - <_ContentIncludedByDefault Remove="volumes\3\version_history.json" /> - <_ContentIncludedByDefault Remove="volumes\3\whitelist.json" /> - <_ContentIncludedByDefault Remove="storage\volumes\69\plugins\spark\config.json" /> - <_ContentIncludedByDefault Remove="storage\volumes\6\banned-ips.json" /> - <_ContentIncludedByDefault Remove="storage\volumes\6\banned-players.json" /> - <_ContentIncludedByDefault Remove="storage\volumes\6\ops.json" /> - <_ContentIncludedByDefault Remove="storage\volumes\6\plugins\spark\config.json" /> - <_ContentIncludedByDefault Remove="storage\volumes\6\usercache.json" /> - <_ContentIncludedByDefault Remove="storage\volumes\6\version_history.json" /> - <_ContentIncludedByDefault Remove="storage\volumes\6\whitelist.json" /> + + Server.cs + + + Server.cs + + + Server.cs + + + Server.cs + diff --git a/MoonlightServers.Daemon/Program.cs b/MoonlightServers.Daemon/Program.cs index c6732cf..3267770 100644 --- a/MoonlightServers.Daemon/Program.cs +++ b/MoonlightServers.Daemon/Program.cs @@ -1,5 +1,59 @@ -using MoonlightServers.Daemon; +using Microsoft.Extensions.Logging.Console; +using MoonlightServers.Daemon.Helpers; +using MoonlightServers.Daemon.ServerSystem; +using MoonlightServers.Daemon.ServerSystem.Implementations.Docker; +using MoonlightServers.Daemon.ServerSystem.Implementations.Local; +using MoonlightServers.Daemon.Services; -var startup = new Startup(); +var builder = WebApplication.CreateBuilder(args); -await startup.RunAsync(args); \ No newline at end of file +// Configure logging +builder.Logging.ClearProviders(); +builder.Logging.AddConsole(options => { options.FormatterName = nameof(AppConsoleFormatter); }); +builder.Logging.AddConsoleFormatter(); + +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddDockerServices(); +builder.Services.AddLocalServices(); + +builder.Services.AddControllers(); + +var app = builder.Build(); + +app.UseAuthorization(); + +app.MapControllers(); + +Task.Run(async () => +{ + Console.ReadLine(); + + try + { + var factory = app.Services.GetRequiredService(); + var server = await factory.CreateAsync("a0e3ddb4-2c72-4f4c-bc49-35650a4bc5c0"); + + await server.InitializeAsync(); + + Console.WriteLine($"Server: {server.State}"); + + Console.ReadLine(); + + if (server.State == ServerState.Offline) + await server.StartAsync(); + else + await server.StopAsync(); + + Console.ReadLine(); + + await server.DisposeAsync(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } +}); + +app.Run(); \ No newline at end of file diff --git a/MoonlightServers.Daemon/Properties/launchSettings.json b/MoonlightServers.Daemon/Properties/launchSettings.json index c0fa8c6..8e6ab21 100644 --- a/MoonlightServers.Daemon/Properties/launchSettings.json +++ b/MoonlightServers.Daemon/Properties/launchSettings.json @@ -1,16 +1,13 @@ { - "$schema": "http://json.schemastore.org/launchsettings.json", + "$schema": "https://json.schemastore.org/launchsettings.json", "profiles": { "http": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": false, - "launchUrl": "swagger", - "applicationUrl": "http://localhost:5275", + "applicationUrl": "http://localhost:5086", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "HTTPS_PROXY": "", - "HTTP_PROXY": "" + "ASPNETCORE_ENVIRONMENT": "Development" } } } diff --git a/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallConsole.cs b/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallConsole.cs new file mode 100644 index 0000000..4275aa8 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallConsole.cs @@ -0,0 +1,13 @@ +namespace MoonlightServers.Daemon.ServerSystem.Abstractions; + +public interface IInstallConsole +{ + public event Func? OnOutput; + + public Task AttachAsync(); + + public Task WriteInputAsync(string value); + + public Task ClearCacheAsync(); + public Task GetCacheAsync(); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallEnvironment.cs b/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallEnvironment.cs new file mode 100644 index 0000000..494ebee --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallEnvironment.cs @@ -0,0 +1,14 @@ +namespace MoonlightServers.Daemon.ServerSystem.Abstractions; + +public interface IInstallEnvironment : IAsyncDisposable +{ + public IInstallStatistics Statistics { get; } + public IInstallConsole Console { get; } + + public event Func? OnExited; + + public Task IsRunningAsync(); + + public Task StartAsync(); + public Task KillAsync(); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallEnvironmentService.cs b/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallEnvironmentService.cs new file mode 100644 index 0000000..1751ee7 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallEnvironmentService.cs @@ -0,0 +1,18 @@ +using MoonlightServers.Daemon.Models; + +namespace MoonlightServers.Daemon.ServerSystem.Abstractions; + +public interface IInstallEnvironmentService +{ + public Task FindAsync(string id); + + public Task CreateAsync( + string id, + RuntimeConfiguration runtimeConfiguration, + InstallConfiguration installConfiguration, + IInstallStorage installStorage, + IRuntimeStorage runtimeStorage + ); + + public Task DeleteAsync(IInstallEnvironment environment); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallStatistics.cs b/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallStatistics.cs new file mode 100644 index 0000000..efc52f4 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallStatistics.cs @@ -0,0 +1,11 @@ +namespace MoonlightServers.Daemon.ServerSystem.Abstractions; + +public interface IInstallStatistics +{ + public event Func? OnStatisticsReceived; + + public Task AttachAsync(); + + public Task ClearCacheAsync(); + public Task GetCacheAsync(); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallStorage.cs b/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallStorage.cs new file mode 100644 index 0000000..7fb07ac --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallStorage.cs @@ -0,0 +1,6 @@ +namespace MoonlightServers.Daemon.ServerSystem.Abstractions; + +public interface IInstallStorage +{ + public Task GetHostPathAsync(); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallStorageService.cs b/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallStorageService.cs new file mode 100644 index 0000000..7ac9588 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Abstractions/IInstallStorageService.cs @@ -0,0 +1,10 @@ +using MoonlightServers.Daemon.Models; + +namespace MoonlightServers.Daemon.ServerSystem.Abstractions; + +public interface IInstallStorageService +{ + public Task FindAsync(string id); + public Task CreateAsync(string id, RuntimeConfiguration runtimeConfiguration, InstallConfiguration installConfiguration); + public Task DeleteAsync(IInstallStorage installStorage); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeConsole.cs b/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeConsole.cs new file mode 100644 index 0000000..b1d6d1f --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeConsole.cs @@ -0,0 +1,13 @@ +namespace MoonlightServers.Daemon.ServerSystem.Abstractions; + +public interface IRuntimeConsole +{ + public event Func? OnOutput; + + public Task AttachAsync(); + + public Task WriteInputAsync(string value); + + public Task ClearCacheAsync(); + public Task GetCacheAsync(); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeEnvironment.cs b/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeEnvironment.cs new file mode 100644 index 0000000..714d5d6 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeEnvironment.cs @@ -0,0 +1,14 @@ +namespace MoonlightServers.Daemon.ServerSystem.Abstractions; + +public interface IRuntimeEnvironment : IAsyncDisposable +{ + public IRuntimeStatistics Statistics { get; } + public IRuntimeConsole Console { get; } + + public event Func? OnExited; + + public Task IsRunningAsync(); + + public Task StartAsync(); + public Task KillAsync(); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeEnvironmentService.cs b/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeEnvironmentService.cs new file mode 100644 index 0000000..226f5c9 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeEnvironmentService.cs @@ -0,0 +1,11 @@ +using MoonlightServers.Daemon.Models; + +namespace MoonlightServers.Daemon.ServerSystem.Abstractions; + +public interface IRuntimeEnvironmentService +{ + public Task FindAsync(string id); + public Task CreateAsync(string id, RuntimeConfiguration configuration, IRuntimeStorage runtimeStorage); + public Task UpdateAsync(IRuntimeEnvironment environment, RuntimeConfiguration configuration); + public Task DeleteAsync(IRuntimeEnvironment environment); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeStatistics.cs b/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeStatistics.cs new file mode 100644 index 0000000..ab65549 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeStatistics.cs @@ -0,0 +1,11 @@ +namespace MoonlightServers.Daemon.ServerSystem.Abstractions; + +public interface IRuntimeStatistics +{ + public event Func? OnStatisticsReceived; + + public Task AttachAsync(); + + public Task ClearCacheAsync(); + public Task GetCacheAsync(); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeStorage.cs b/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeStorage.cs new file mode 100644 index 0000000..d532125 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeStorage.cs @@ -0,0 +1,6 @@ +namespace MoonlightServers.Daemon.ServerSystem.Abstractions; + +public interface IRuntimeStorage +{ + public Task GetHostPathAsync(); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeStorageService.cs b/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeStorageService.cs new file mode 100644 index 0000000..919b948 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Abstractions/IRuntimeStorageService.cs @@ -0,0 +1,11 @@ +using MoonlightServers.Daemon.Models; + +namespace MoonlightServers.Daemon.ServerSystem.Abstractions; + +public interface IRuntimeStorageService +{ + public Task FindAsync(string id); + public Task CreateAsync(string id, RuntimeConfiguration configuration); + public Task UpdateAsync(IRuntimeStorage runtimeStorage, RuntimeConfiguration configuration); + public Task DeleteAsync(IRuntimeStorage runtimeStorage); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Docker/DockerConsole.cs b/MoonlightServers.Daemon/ServerSystem/Docker/DockerConsole.cs deleted file mode 100644 index 2888361..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Docker/DockerConsole.cs +++ /dev/null @@ -1,221 +0,0 @@ -using System.Text; -using Docker.DotNet; -using MoonCore.Events; -using MoonCore.Helpers; -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; - -namespace MoonlightServers.Daemon.ServerSystem.Docker; - -public class DockerConsole : IConsole -{ - private readonly EventSource StdOutEventSource = new(); - private readonly ConcurrentList StdOutCache = new(); - private readonly DockerClient DockerClient; - private readonly ServerContext Context; - private readonly ILogger Logger; - - private MultiplexedStream? CurrentStream; - private CancellationTokenSource Cts = new(); - - public DockerConsole(DockerClient dockerClient, ServerContext context) - { - DockerClient = dockerClient; - Context = context; - Logger = Context.Logger; - } - - public Task InitializeAsync() - => Task.CompletedTask; - - public async Task WriteStdInAsync(string content) - { - if (CurrentStream == null) - { - Logger.LogWarning("Unable to write to stdin as no stream is connected"); - return; - } - - var contextBuffer = Encoding.UTF8.GetBytes(content); - - await CurrentStream.WriteAsync(contextBuffer, 0, contextBuffer.Length, Cts.Token); - } - - public async Task WriteStdOutAsync(string content) - { - // Add output cache - if (StdOutCache.Count > 250) // TODO: Config - StdOutCache.RemoveRange(0, 100); - - StdOutCache.Add(content); - - // Fire event - await StdOutEventSource.InvokeAsync(content); - } - - public async Task AttachRuntimeAsync() - { - var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id); - - await AttachToContainerAsync(containerName); - } - - public async Task AttachInstallationAsync() - { - var containerName = string.Format(DockerConstants.InstallationNameTemplate, Context.Configuration.Id); - - await AttachToContainerAsync(containerName); - } - - private async Task AttachToContainerAsync(string containerName) - { - var cts = new CancellationTokenSource(); - - // Cancels previous active read task if it exists - if (!Cts.IsCancellationRequested) - await Cts.CancelAsync(); - - // Update the current cancellation token - Cts = cts; - - // Start reading task - Task.Run(async () => - { - // This loop is here to reconnect to the stream when connection is lost. - // This can occur when docker restarts for example - - while (!cts.IsCancellationRequested) - { - MultiplexedStream? innerStream = null; - - try - { - Logger.LogTrace("Attaching"); - - innerStream = await DockerClient.Containers.AttachContainerAsync( - containerName, - true, - new() - { - Stderr = true, - Stdin = true, - Stdout = true, - Stream = true - }, - cts.Token - ); - - CurrentStream = innerStream; - - var buffer = new byte[1024]; - - try - { - // Read while server tasks are not canceled - while (!cts.Token.IsCancellationRequested) - { - var readResult = await innerStream.ReadOutputAsync( - buffer, - 0, - buffer.Length, - cts.Token - ); - - if (readResult.EOF) - await cts.CancelAsync(); - - var decodedText = Encoding.UTF8.GetString(buffer, 0, readResult.Count); - - await WriteStdOutAsync(decodedText); - } - - Logger.LogTrace("Read loop exited"); - } - catch (TaskCanceledException) - { - // Ignored - } - catch (OperationCanceledException) - { - // Ignored - } - catch (Exception e) - { - Logger.LogWarning(e, "An unhandled error occured while reading from container stream"); - } - } - catch (TaskCanceledException) - { - // ignored - } - catch (DockerContainerNotFoundException) - { - // Container got removed. Stop the reconnect loop - - Logger.LogDebug("Container '{name}' got removed. Stopping reconnect stream for console", containerName); - await cts.CancelAsync(); - } - catch (Exception e) - { - Logger.LogError(e, "An error occured while attaching to container"); - } - - innerStream?.Dispose(); - } - - Logger.LogDebug("Disconnected from container stream"); - }); - } - - public async Task FetchRuntimeAsync() - { - var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id); - - await FetchFromContainerAsync(containerName); - } - - public async Task FetchInstallationAsync() - { - var containerName = string.Format(DockerConstants.InstallationNameTemplate, Context.Configuration.Id); - - await FetchFromContainerAsync(containerName); - } - - private async Task FetchFromContainerAsync(string containerName) - { - var logStream = await DockerClient.Containers.GetContainerLogsAsync(containerName, true, new() - { - Follow = false, - ShowStderr = true, - ShowStdout = true - }); - - var combinedOutput = await logStream.ReadOutputToEndAsync(Cts.Token); - var contentToAdd = combinedOutput.stdout + combinedOutput.stderr; - - await WriteStdOutAsync(contentToAdd); - } - - public Task ClearCacheAsync() - { - StdOutCache.Clear(); - return Task.CompletedTask; - } - - public Task> GetCacheAsync() - { - return Task.FromResult>(StdOutCache); - } - - public async Task SubscribeStdOutAsync(Func callback) - => await StdOutEventSource.SubscribeAsync(callback); - - public async ValueTask DisposeAsync() - { - if (!Cts.IsCancellationRequested) - await Cts.CancelAsync(); - - if (CurrentStream != null) - CurrentStream.Dispose(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Docker/DockerConstants.cs b/MoonlightServers.Daemon/ServerSystem/Docker/DockerConstants.cs deleted file mode 100644 index 8a82844..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Docker/DockerConstants.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MoonlightServers.Daemon.ServerSystem.Docker; - -public static class DockerConstants -{ - public const string RuntimeNameTemplate = "moonlight-runtime-{0}"; - public const string InstallationNameTemplate = "moonlight-installation-{0}"; -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Docker/DockerInstallation.cs b/MoonlightServers.Daemon/ServerSystem/Docker/DockerInstallation.cs deleted file mode 100644 index 2a9b89d..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Docker/DockerInstallation.cs +++ /dev/null @@ -1,184 +0,0 @@ -using Docker.DotNet; -using Docker.DotNet.Models; -using MoonCore.Events; -using MoonlightServers.Daemon.Mappers; -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; -using MoonlightServers.Daemon.Services; -using MoonlightServers.DaemonShared.PanelSide.Http.Responses; - -namespace MoonlightServers.Daemon.ServerSystem.Docker; - -public class DockerInstallation : IInstallation -{ - private readonly DockerEventService DockerEventService; - private readonly ServerConfigurationMapper Mapper; - private readonly DockerImageService ImageService; - private readonly ServerContext ServerContext; - private readonly DockerClient DockerClient; - private IReporter Reporter => ServerContext.Server.Reporter; - - private readonly EventSource ExitEventSource = new(); - - private IAsyncDisposable ContainerEventSubscription; - private string ContainerId; - - public DockerInstallation( - DockerClient dockerClient, - ServerContext serverContext, - ServerConfigurationMapper mapper, - DockerImageService imageService, - DockerEventService dockerEventService - ) - { - DockerClient = dockerClient; - ServerContext = serverContext; - Mapper = mapper; - ImageService = imageService; - DockerEventService = dockerEventService; - } - - public async Task InitializeAsync() - { - ContainerEventSubscription = await DockerEventService.SubscribeContainerAsync(OnContainerEvent); - } - - private async ValueTask OnContainerEvent(Message message) - { - // Only handle events for our own container - if (message.ID != ContainerId) - return; - - // Only handle die events - if (message.Action != "die") - return; - - int exitCode; - - if (message.Actor.Attributes.TryGetValue("exitCode", out var exitCodeStr)) - { - if (!int.TryParse(exitCodeStr, out exitCode)) - exitCode = 0; - } - else - exitCode = 0; - - - await ExitEventSource.InvokeAsync(exitCode); - } - - public async Task CheckExistsAsync() - { - try - { - var containerName = string.Format(DockerConstants.InstallationNameTemplate, ServerContext.Configuration.Id); - - await DockerClient.Containers.InspectContainerAsync( - containerName - ); - - return true; - } - catch (DockerContainerNotFoundException) - { - return false; - } - } - - public async Task CreateAsync( - string runtimePath, - string hostPath, - ServerInstallDataResponse data - ) - { - var containerName = string.Format(DockerConstants.InstallationNameTemplate, ServerContext.Configuration.Id); - - var parameters = Mapper.ToInstallParameters( - ServerContext.Configuration, - data, - runtimePath, - hostPath, - containerName - ); - - // Docker image - await Reporter.StatusAsync("Downloading docker image"); - - await ImageService.DownloadAsync(data.DockerImage, async status => { await Reporter.StatusAsync(status); }); - - await Reporter.StatusAsync("Downloaded docker image"); - - // Write install script to install fs - - await File.WriteAllTextAsync( - Path.Combine(hostPath, "install.sh"), - data.Script - ); - - // - - var response = await DockerClient.Containers.CreateContainerAsync(parameters); - ContainerId = response.ID; - - await Reporter.StatusAsync("Created container"); - } - - public async Task StartAsync() - { - var containerName = string.Format(DockerConstants.InstallationNameTemplate, ServerContext.Configuration.Id); - - await DockerClient.Containers.StartContainerAsync(containerName, new()); - } - - public async Task KillAsync() - { - var containerName = string.Format(DockerConstants.InstallationNameTemplate, ServerContext.Configuration.Id); - - await DockerClient.Containers.KillContainerAsync(containerName, new()); - } - - public async Task DestroyAsync() - { - var containerName = string.Format(DockerConstants.InstallationNameTemplate, ServerContext.Configuration.Id); - - try - { - var container = await DockerClient.Containers.InspectContainerAsync(containerName); - - if (container.State.Running) - await DockerClient.Containers.KillContainerAsync(containerName, new()); - - await DockerClient.Containers.RemoveContainerAsync(containerName, new() - { - Force = true - }); - } - catch (DockerContainerNotFoundException) - { - // Ignored - } - } - - public async Task SubscribeExitedAsync(Func callback) - => await ExitEventSource.SubscribeAsync(callback); - - public async Task RestoreAsync() - { - try - { - var containerName = string.Format(DockerConstants.InstallationNameTemplate, ServerContext.Configuration.Id); - - var container = await DockerClient.Containers.InspectContainerAsync(containerName); - ContainerId = container.ID; - } - catch (DockerContainerNotFoundException) - { - // Ignore - } - } - - public async ValueTask DisposeAsync() - { - await ContainerEventSubscription.DisposeAsync(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Docker/DockerRestorer.cs b/MoonlightServers.Daemon/ServerSystem/Docker/DockerRestorer.cs deleted file mode 100644 index 8d22edf..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Docker/DockerRestorer.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Docker.DotNet; -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; - -namespace MoonlightServers.Daemon.ServerSystem.Docker; - -public class DockerRestorer : IRestorer -{ - private readonly DockerClient DockerClient; - private readonly ServerContext Context; - - public DockerRestorer(DockerClient dockerClient, ServerContext context) - { - DockerClient = dockerClient; - Context = context; - } - - public Task InitializeAsync() - => Task.CompletedTask; - - public async Task HandleRuntimeAsync() - { - var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id); - - try - { - var container = await DockerClient.Containers.InspectContainerAsync( - containerName - ); - - return container.State.Running; - } - catch (DockerContainerNotFoundException) - { - return false; - } - } - - public async Task HandleInstallationAsync() - { - var containerName = string.Format(DockerConstants.InstallationNameTemplate, Context.Configuration.Id); - - try - { - var container = await DockerClient.Containers.InspectContainerAsync( - containerName - ); - - return container.State.Running; - } - catch (DockerContainerNotFoundException) - { - return false; - } - } - - public ValueTask DisposeAsync() - => ValueTask.CompletedTask; -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Docker/DockerRuntime.cs b/MoonlightServers.Daemon/ServerSystem/Docker/DockerRuntime.cs deleted file mode 100644 index aabc565..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Docker/DockerRuntime.cs +++ /dev/null @@ -1,177 +0,0 @@ -using Docker.DotNet; -using Docker.DotNet.Models; -using MoonCore.Events; -using MoonlightServers.Daemon.Mappers; -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; -using MoonlightServers.Daemon.Services; - -namespace MoonlightServers.Daemon.ServerSystem.Docker; - -public class DockerRuntime : IRuntime -{ - private readonly DockerClient DockerClient; - private readonly ServerContext Context; - private readonly ServerConfigurationMapper Mapper; - private readonly DockerEventService DockerEventService; - private readonly DockerImageService ImageService; - private readonly EventSource ExitEventSource = new(); - - private IReporter Reporter => Context.Server.Reporter; - private IAsyncDisposable ContainerEventSubscription; - private string ContainerId; - - public DockerRuntime( - DockerClient dockerClient, - ServerContext context, - ServerConfigurationMapper mapper, - DockerEventService dockerEventService, - DockerImageService imageService - ) - { - DockerClient = dockerClient; - Context = context; - Mapper = mapper; - DockerEventService = dockerEventService; - ImageService = imageService; - } - - public async Task InitializeAsync() - { - ContainerEventSubscription = await DockerEventService.SubscribeContainerAsync(OnContainerEvent); - } - - private async ValueTask OnContainerEvent(Message message) - { - // Only handle events for our own container - if (message.ID != ContainerId) - return; - - // Only handle die events - if (message.Action != "die") - return; - - int exitCode; - - if (message.Actor.Attributes.TryGetValue("exitCode", out var exitCodeStr)) - { - if (!int.TryParse(exitCodeStr, out exitCode)) - exitCode = 0; - } - else - exitCode = 0; - - - await ExitEventSource.InvokeAsync(exitCode); - } - - public async Task CheckExistsAsync() - { - try - { - var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id); - - await DockerClient.Containers.InspectContainerAsync( - containerName - ); - - return true; - } - catch (DockerContainerNotFoundException) - { - return false; - } - } - - public async Task CreateAsync(string path) - { - var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id); - - var parameters = Mapper.ToRuntimeParameters( - Context.Configuration, - path, - containerName - ); - - // Docker image - await Reporter.StatusAsync("Downloading docker image"); - - await ImageService.DownloadAsync( - Context.Configuration.DockerImage, - async status => { await Reporter.StatusAsync(status); } - ); - - await Reporter.StatusAsync("Downloaded docker image"); - - // - - var response = await DockerClient.Containers.CreateContainerAsync(parameters); - ContainerId = response.ID; - - await Reporter.StatusAsync("Created container"); - } - - public async Task StartAsync() - { - var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id); - - await DockerClient.Containers.StartContainerAsync(containerName, new()); - } - - public Task UpdateAsync() - { - return Task.CompletedTask; - } - - public async Task KillAsync() - { - var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id); - - await DockerClient.Containers.KillContainerAsync(containerName, new()); - } - - public async Task DestroyAsync() - { - var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id); - - try - { - var container = await DockerClient.Containers.InspectContainerAsync(containerName); - - if (container.State.Running) - await DockerClient.Containers.KillContainerAsync(containerName, new()); - - await DockerClient.Containers.RemoveContainerAsync(containerName, new() - { - Force = true - }); - } - catch (DockerContainerNotFoundException) - { - // Ignored - } - } - - public async Task SubscribeExitedAsync(Func callback) - => await ExitEventSource.SubscribeAsync(callback); - - public async Task RestoreAsync() - { - try - { - var containerName = string.Format(DockerConstants.RuntimeNameTemplate, Context.Configuration.Id); - - var container = await DockerClient.Containers.InspectContainerAsync(containerName); - ContainerId = container.ID; - } - catch (DockerContainerNotFoundException) - { - // Ignore - } - } - - public async ValueTask DisposeAsync() - { - await ContainerEventSubscription.DisposeAsync(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Docker/DockerStatistics.cs b/MoonlightServers.Daemon/ServerSystem/Docker/DockerStatistics.cs deleted file mode 100644 index a38f9ff..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Docker/DockerStatistics.cs +++ /dev/null @@ -1,25 +0,0 @@ -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; - -namespace MoonlightServers.Daemon.ServerSystem.Docker; - -public class DockerStatistics : IStatistics -{ - public Task InitializeAsync() - => Task.CompletedTask; - - public Task AttachRuntimeAsync() - => Task.CompletedTask; - - public Task AttachInstallationAsync() - => Task.CompletedTask; - - public Task ClearCacheAsync() - => Task.CompletedTask; - - public Task> GetCacheAsync() - => Task.FromResult>([]); - - public ValueTask DisposeAsync() - => ValueTask.CompletedTask; -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Enums/ServerState.cs b/MoonlightServers.Daemon/ServerSystem/Enums/ServerState.cs deleted file mode 100644 index 39603aa..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Enums/ServerState.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MoonlightServers.Daemon.ServerSystem.Enums; - -public enum ServerState -{ - Offline = 0, - Starting = 1, - Online = 2, - Stopping = 3, - Installing = 4, - Locked = 5 -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Enums/ServerTrigger.cs b/MoonlightServers.Daemon/ServerSystem/Enums/ServerTrigger.cs deleted file mode 100644 index 1338e47..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Enums/ServerTrigger.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace MoonlightServers.Daemon.ServerSystem.Enums; - -public enum ServerTrigger -{ - Start = 0, - Stop = 1, - Kill = 2, - DetectOnline = 3, - Install = 4, - Fail = 5, - Exited = 6 -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/FileSystems/RawInstallationFs.cs b/MoonlightServers.Daemon/ServerSystem/FileSystems/RawInstallationFs.cs deleted file mode 100644 index a7a5cdb..0000000 --- a/MoonlightServers.Daemon/ServerSystem/FileSystems/RawInstallationFs.cs +++ /dev/null @@ -1,58 +0,0 @@ -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; - -namespace MoonlightServers.Daemon.ServerSystem.FileSystems; - -public class RawInstallationFs : IFileSystem -{ - private readonly string BaseDirectory; - - public RawInstallationFs(ServerContext context) - { - BaseDirectory = Path.Combine( - Directory.GetCurrentDirectory(), - "storage", - "install", - context.Configuration.Id.ToString() - ); - } - - public Task InitializeAsync() - => Task.CompletedTask; - - public Task GetPathAsync() - => Task.FromResult(BaseDirectory); - - public Task CheckExistsAsync() - { - var exists = Directory.Exists(BaseDirectory); - return Task.FromResult(exists); - } - - public Task CheckMountedAsync() - => Task.FromResult(true); - - public Task CreateAsync() - { - Directory.CreateDirectory(BaseDirectory); - return Task.CompletedTask; - } - - public Task PerformChecksAsync() - => Task.CompletedTask; - - public Task MountAsync() - => Task.CompletedTask; - - public Task UnmountAsync() - => Task.CompletedTask; - - public Task DestroyAsync() - { - Directory.Delete(BaseDirectory, true); - return Task.CompletedTask; - } - - public ValueTask DisposeAsync() - => ValueTask.CompletedTask; -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/FileSystems/RawRuntimeFs.cs b/MoonlightServers.Daemon/ServerSystem/FileSystems/RawRuntimeFs.cs deleted file mode 100644 index 6f98ac7..0000000 --- a/MoonlightServers.Daemon/ServerSystem/FileSystems/RawRuntimeFs.cs +++ /dev/null @@ -1,58 +0,0 @@ -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; - -namespace MoonlightServers.Daemon.ServerSystem.FileSystems; - -public class RawRuntimeFs : IFileSystem -{ - private readonly string BaseDirectory; - - public RawRuntimeFs(ServerContext context) - { - BaseDirectory = Path.Combine( - Directory.GetCurrentDirectory(), - "storage", - "volumes", - context.Configuration.Id.ToString() - ); - } - - public Task InitializeAsync() - => Task.CompletedTask; - - public Task GetPathAsync() - => Task.FromResult(BaseDirectory); - - public Task CheckExistsAsync() - { - var exists = Directory.Exists(BaseDirectory); - return Task.FromResult(exists); - } - - public Task CheckMountedAsync() - => Task.FromResult(true); - - public Task CreateAsync() - { - Directory.CreateDirectory(BaseDirectory); - return Task.CompletedTask; - } - - public Task PerformChecksAsync() - => Task.CompletedTask; - - public Task MountAsync() - => Task.CompletedTask; - - public Task UnmountAsync() - => Task.CompletedTask; - - public Task DestroyAsync() - { - Directory.Delete(BaseDirectory, true); - return Task.CompletedTask; - } - - public ValueTask DisposeAsync() - => ValueTask.CompletedTask; -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Handlers/DebugHandler.cs b/MoonlightServers.Daemon/ServerSystem/Handlers/DebugHandler.cs deleted file mode 100644 index cb32684..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Handlers/DebugHandler.cs +++ /dev/null @@ -1,35 +0,0 @@ -using MoonlightServers.Daemon.ServerSystem.Enums; -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; -using Stateless; - -namespace MoonlightServers.Daemon.ServerSystem.Handlers; - -public class DebugHandler : IServerStateHandler -{ - private readonly ServerContext Context; - private IAsyncDisposable? StdOutSubscription; - - public DebugHandler(ServerContext context) - { - Context = context; - } - - public async Task ExecuteAsync(StateMachine.Transition transition) - { - if(StdOutSubscription != null) - return; - - StdOutSubscription = await Context.Server.Console.SubscribeStdOutAsync(line => - { - Console.WriteLine($"STD OUT: {line}"); - return ValueTask.CompletedTask; - }); - } - - public async ValueTask DisposeAsync() - { - if (StdOutSubscription != null) - await StdOutSubscription.DisposeAsync(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Handlers/InstallationHandler.cs b/MoonlightServers.Daemon/ServerSystem/Handlers/InstallationHandler.cs deleted file mode 100644 index 8f22f09..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Handlers/InstallationHandler.cs +++ /dev/null @@ -1,125 +0,0 @@ -using MoonlightServers.Daemon.ServerSystem.Enums; -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; -using MoonlightServers.DaemonShared.PanelSide.Http.Responses; -using Stateless; - -namespace MoonlightServers.Daemon.ServerSystem.Handlers; - -public class InstallationHandler : IServerStateHandler -{ - private readonly ServerContext Context; - private Server Server => Context.Server; - - private IAsyncDisposable? ExitSubscription; - - public InstallationHandler(ServerContext context) - { - Context = context; - } - - public async Task ExecuteAsync(StateMachine.Transition transition) - { - if (transition is - { Source: ServerState.Offline, Destination: ServerState.Installing, Trigger: ServerTrigger.Install }) - { - await StartAsync(); - } - else if (transition is - { Source: ServerState.Installing, Destination: ServerState.Offline, Trigger: ServerTrigger.Exited }) - { - await CompleteAsync(); - } - } - - private async Task StartAsync() - { - // Plan: - // 1. Fetch latest configuration - // 2. Check if both file systems exists - // 3. Check if both file systems are mounted - // 4. Run file system checks - // 5. Create installation container - // 6. Attach console - // 7. Start installation container - - // 1. Fetch latest configuration - var installData = new ServerInstallDataResponse() - { - Script = await File.ReadAllTextAsync(Path.Combine("storage", "install.sh")), - Shell = "/bin/ash", - DockerImage = "ghcr.io/parkervcp/installers:alpine" - }; - - // 2. Check if file system exists - if (!await Server.RuntimeFileSystem.CheckExistsAsync()) - await Server.RuntimeFileSystem.CreateAsync(); - - if (!await Server.InstallationFileSystem.CheckExistsAsync()) - await Server.InstallationFileSystem.CreateAsync(); - - // 3. Check if both file systems are mounted - if (!await Server.RuntimeFileSystem.CheckMountedAsync()) - await Server.RuntimeFileSystem.MountAsync(); - - if (!await Server.InstallationFileSystem.CheckMountedAsync()) - await Server.InstallationFileSystem.MountAsync(); - - // 4. Run file system checks - await Server.RuntimeFileSystem.PerformChecksAsync(); - await Server.InstallationFileSystem.PerformChecksAsync(); - - // 5. Create installation - - var runtimePath = await Server.RuntimeFileSystem.GetPathAsync(); - var installationPath = await Server.InstallationFileSystem.GetPathAsync(); - - if (await Server.Installation.CheckExistsAsync()) - await Server.Installation.DestroyAsync(); - - await Server.Installation.CreateAsync(runtimePath, installationPath, installData); - - if (ExitSubscription == null) - ExitSubscription = await Server.Installation.SubscribeExitedAsync(OnInstallationExited); - - // 6. Attach console - - await Server.Console.AttachInstallationAsync(); - - // 7. Start installation container - await Server.Installation.StartAsync(); - } - - private async ValueTask OnInstallationExited(int exitCode) - { - // TODO: Notify the crash handler component of the exit code - - await Server.StateMachine.FireAsync(ServerTrigger.Exited); - } - - private async Task CompleteAsync() - { - // Plan: - // 1. Handle possible crash - // 2. Remove installation container - // 3. Remove installation file system - - // 1. Handle possible crash - // TODO - - // 2. Remove installation container - await Server.Installation.DestroyAsync(); - - // 3. Remove installation file system - await Server.InstallationFileSystem.UnmountAsync(); - await Server.InstallationFileSystem.DestroyAsync(); - - Context.Logger.LogDebug("Completed installation"); - } - - public async ValueTask DisposeAsync() - { - if (ExitSubscription != null) - await ExitSubscription.DisposeAsync(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Handlers/OnlineDetectionHandler.cs b/MoonlightServers.Daemon/ServerSystem/Handlers/OnlineDetectionHandler.cs deleted file mode 100644 index 7aa1dfd..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Handlers/OnlineDetectionHandler.cs +++ /dev/null @@ -1,85 +0,0 @@ -using MoonlightServers.Daemon.ServerSystem.Enums; -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; -using Stateless; - -namespace MoonlightServers.Daemon.ServerSystem.Handlers; - -public class OnlineDetectionHandler : IServerStateHandler -{ - private readonly ServerContext Context; - private IOnlineDetector OnlineDetector => Context.Server.OnlineDetector; - private ILogger Logger => Context.Logger; - - private IAsyncDisposable? ConsoleSubscription; - private bool IsActive = false; - - public OnlineDetectionHandler(ServerContext context) - { - Context = context; - } - - public async Task ExecuteAsync(StateMachine.Transition transition) - { - if ( - transition is - { Source: ServerState.Offline, Destination: ServerState.Starting, Trigger: ServerTrigger.Start } && !IsActive - ) - { - await StartAsync(); - } - else if (transition is { Source: not ServerState.Installing, Destination: ServerState.Offline } && IsActive) - { - await StopAsync(); - } - } - - private async Task StartAsync() - { - IsActive = true; - - await OnlineDetector.CreateAsync(); - - ConsoleSubscription = await Context.Server.Console.SubscribeStdOutAsync(OnHandleOutput); - - Logger.LogTrace("Created online detector. Created console subscription"); - } - - private async ValueTask OnHandleOutput(string line) - { - if(!IsActive) - return; - - if(!await OnlineDetector.HandleOutputAsync(line)) - return; - - if(!Context.Server.StateMachine.CanFire(ServerTrigger.DetectOnline)) - return; - - Logger.LogTrace("Detected server as online. Destroying online detector"); - - await Context.Server.StateMachine.FireAsync(ServerTrigger.DetectOnline); - await StopAsync(); - } - - private async Task StopAsync() - { - IsActive = false; - - if (ConsoleSubscription != null) - { - await ConsoleSubscription.DisposeAsync(); - ConsoleSubscription = null; - } - - await OnlineDetector.DestroyAsync(); - - Logger.LogTrace("Destroyed online detector. Revoked console subscription"); - } - - public async ValueTask DisposeAsync() - { - if (ConsoleSubscription != null) - await ConsoleSubscription.DisposeAsync(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Handlers/ShutdownHandler.cs b/MoonlightServers.Daemon/ServerSystem/Handlers/ShutdownHandler.cs deleted file mode 100644 index 368816d..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Handlers/ShutdownHandler.cs +++ /dev/null @@ -1,42 +0,0 @@ -using MoonlightServers.Daemon.ServerSystem.Enums; -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; -using Stateless; - -namespace MoonlightServers.Daemon.ServerSystem.Handlers; - -public class ShutdownHandler : IServerStateHandler -{ - private readonly ServerContext ServerContext; - - public ShutdownHandler(ServerContext serverContext) - { - ServerContext = serverContext; - } - - public async Task ExecuteAsync(StateMachine.Transition transition) - { - // Filter (we only want to handle exists from the runtime, so we filter out the installing state) - if (transition is not - { - Destination: ServerState.Offline, - Source: not ServerState.Installing, - Trigger: ServerTrigger.Exited // We don't want to handle the fail event here - }) - return; - - // Plan: - // 1. Handle possible crash - // 2. Remove runtime - - // 1. Handle possible crash - // TODO: Handle crash here - - // 2. Remove runtime - - await ServerContext.Server.Runtime.DestroyAsync(); - } - - public ValueTask DisposeAsync() - => ValueTask.CompletedTask; -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Handlers/StartupHandler.cs b/MoonlightServers.Daemon/ServerSystem/Handlers/StartupHandler.cs deleted file mode 100644 index c21e6d7..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Handlers/StartupHandler.cs +++ /dev/null @@ -1,84 +0,0 @@ -using MoonlightServers.Daemon.ServerSystem.Enums; -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; -using Stateless; - -namespace MoonlightServers.Daemon.ServerSystem.Handlers; - -public class StartupHandler : IServerStateHandler -{ - private IAsyncDisposable? ExitSubscription; - - private readonly ServerContext Context; - private Server Server => Context.Server; - - public StartupHandler(ServerContext context) - { - Context = context; - } - - public async Task ExecuteAsync(StateMachine.Transition transition) - { - // Filter - if (transition is not {Source: ServerState.Offline, Destination: ServerState.Starting, Trigger: ServerTrigger.Start}) - return; - - // Plan: - // 1. Fetch latest configuration - // 2. Check if file system exists - // 3. Check if file system is mounted - // 4. Run file system checks - // 5. Create runtime - // 6. Attach console - // 7. Start runtime - - // 1. Fetch latest configuration - // TODO - // Consider moving it out of the startup handler, as other handlers might need - // the updated config as well or add sorting into the handler registration to ensure they are executing in the correct order. - // Sort when building server, not when executing handlers - - // 2. Check if file system exists - if (!await Server.RuntimeFileSystem.CheckExistsAsync()) - await Server.RuntimeFileSystem.CreateAsync(); - - // 3. Check if file system is mounted - if (!await Server.RuntimeFileSystem.CheckMountedAsync()) - await Server.RuntimeFileSystem.CheckMountedAsync(); - - // 4. Run file system checks - await Server.RuntimeFileSystem.PerformChecksAsync(); - - // 5. Create runtime - var hostPath = await Server.RuntimeFileSystem.GetPathAsync(); - - if (await Server.Runtime.CheckExistsAsync()) - await Server.Runtime.DestroyAsync(); - - await Server.Runtime.CreateAsync(hostPath); - - if (ExitSubscription == null) - ExitSubscription = await Server.Runtime.SubscribeExitedAsync(OnRuntimeExited); - - // 6. Attach console - - await Server.Console.AttachRuntimeAsync(); - - // 7. Start runtime - - await Server.Runtime.StartAsync(); - } - - private async ValueTask OnRuntimeExited(int exitCode) - { - // TODO: Notify the crash handler component of the exit code - - await Server.StateMachine.FireAsync(ServerTrigger.Exited); - } - - public async ValueTask DisposeAsync() - { - if (ExitSubscription != null) - await ExitSubscription.DisposeAsync(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/ConsoleSignalRComponent.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/ConsoleSignalRComponent.cs deleted file mode 100644 index 0cc292f..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Implementations/ConsoleSignalRComponent.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Microsoft.AspNetCore.SignalR; -using MoonlightServers.Daemon.Http.Hubs; -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; - -namespace MoonlightServers.Daemon.ServerSystem.Implementations; - -public class ConsoleSignalRComponent : IServerComponent -{ - private readonly IHubContext Hub; - private readonly ServerContext Context; - - private IAsyncDisposable? StdOutSubscription; - private string HubGroup; - - public ConsoleSignalRComponent(IHubContext hub, ServerContext context) - { - Hub = hub; - Context = context; - } - - public async Task InitializeAsync() - { - HubGroup = Context.Configuration.Id.ToString(); - - StdOutSubscription = await Context.Server.Console.SubscribeStdOutAsync(OnStdOut); - } - - private async ValueTask OnStdOut(string output) - { - await Hub.Clients.Group(HubGroup).SendAsync("ConsoleOutput", output); - } - - public async ValueTask DisposeAsync() - { - if (StdOutSubscription != null) - await StdOutSubscription.DisposeAsync(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/ConfigMapper.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/ConfigMapper.cs new file mode 100644 index 0000000..aae853c --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/ConfigMapper.cs @@ -0,0 +1,254 @@ +using System.ComponentModel; +using Docker.DotNet.Models; +using MoonlightServers.Daemon.Models; + +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Docker; + +public static class ConfigMapper +{ + public static CreateContainerParameters GetRuntimeConfig( + string uuid, + string name, + RuntimeConfiguration configuration, + string runtimeStoragePath + ) + { + var parameters = new CreateContainerParameters() + { + HostConfig = new() + }; + + ApplySharedOptions(parameters, configuration); + + // Limits + + if (configuration.Limits.CpuPercent.HasValue) + { + parameters.HostConfig.CPUQuota = configuration.Limits.CpuPercent.Value * 1000; + parameters.HostConfig.CPUPeriod = 100000; + parameters.HostConfig.CPUShares = 1024; + } + + if (configuration.Limits.MemoryMb.HasValue) + { + var memoryLimit = configuration.Limits.MemoryMb.Value; + + // The overhead multiplier gives the container a little bit more memory to prevent crashes + var memoryOverhead = memoryLimit + memoryLimit * 0.05f; + + parameters.HostConfig.Memory = (long)memoryOverhead * 1024L * 1024L; + parameters.HostConfig.MemoryReservation = (long)memoryLimit * 1024L * 1024L; + + if (configuration.Limits.SwapMb.HasValue) + { + var rawSwap = configuration.Limits.SwapMb.Value * 1024L * 1024L; + parameters.HostConfig.MemorySwap = rawSwap + (long)memoryOverhead; + } + } + + parameters.HostConfig.BlkioWeight = 100; + parameters.HostConfig.OomKillDisable = true; + + // Storage + + parameters.HostConfig.Tmpfs = new Dictionary() + { + { "/tmp", "rw,exec,nosuid,size=100M" } // TODO: Config + }; + + parameters.WorkingDir = "/home/container"; + + parameters.HostConfig.Mounts = new List(); + parameters.HostConfig.Mounts.Add(new Mount() + { + Source = runtimeStoragePath, + Target = "/home/container", + Type = "bind", + ReadOnly = false + }); + + // Labels + parameters.Labels = new Dictionary() + { + { "dev.moonlightpanel", "true" }, + { "dev.moonlightpanel.id", uuid } + }; + + foreach (var label in configuration.Environment.Labels) + parameters.Labels.Add(label.Key, label.Value); + + // Security + + parameters.HostConfig.CapDrop = new List() + { + "setpcap", "mknod", "audit_write", "net_raw", "dac_override", + "fowner", "fsetid", "net_bind_service", "sys_chroot", "setfcap" + }; + + parameters.HostConfig.ReadonlyRootfs = true; + parameters.HostConfig.SecurityOpt = new List() + { + "no-new-privileges" + }; + + // Name + + parameters.Name = name; + + // Docker Image + parameters.Image = configuration.Template.DockerImage; + + // Networking + + if (configuration.Network.Ports.Length > 0 && !string.IsNullOrWhiteSpace(configuration.Network.FriendlyName)) + parameters.Hostname = configuration.Network.FriendlyName; + + parameters.ExposedPorts = new Dictionary(); + parameters.HostConfig.PortBindings = new Dictionary>(); + + foreach (var port in configuration.Network.Ports) + { + parameters.ExposedPorts.Add($"{port.Port}/tcp", new()); + parameters.ExposedPorts.Add($"{port.Port}/udp", new()); + + parameters.HostConfig.PortBindings.Add($"{port.Port}/tcp", new List + { + new() + { + HostPort = port.Port.ToString(), + HostIP = port.IpAddress + } + }); + + parameters.HostConfig.PortBindings.Add($"{port.Port}/udp", new List + { + new() + { + HostPort = port.Port.ToString(), + HostIP = port.IpAddress + } + }); + } + + // TODO: Force outgoing ip stuff + + // User + parameters.User = "1000:1000"; + + return parameters; + } + + public static CreateContainerParameters GetInstallConfig( + string uuid, + string name, + RuntimeConfiguration runtimeConfiguration, + InstallConfiguration installConfiguration, + string runtimeStoragePath, + string installStoragePath + ) + { + var parameters = new CreateContainerParameters() + { + HostConfig = new() + }; + + ApplySharedOptions(parameters, runtimeConfiguration); + + // Labels + parameters.Labels = new Dictionary() + { + { "dev.moonlightpanel", "true" }, + { "dev.moonlightpanel.id", uuid } + }; + + foreach (var label in runtimeConfiguration.Environment.Labels) + parameters.Labels.Add(label.Key, label.Value); + + // Name + + parameters.Name = name; + + // Docker Image + parameters.Image = installConfiguration.DockerImage; + + // User + parameters.User = "1000:1000"; + + // Storage + parameters.WorkingDir = "/mnt/server"; + + parameters.HostConfig.Mounts = new List(); + + parameters.HostConfig.Mounts.Add(new Mount() + { + Source = runtimeStoragePath, + Target = "/mnt/server", + ReadOnly = false, + Type = "bind" + }); + + parameters.HostConfig.Mounts.Add(new Mount() + { + Source = installStoragePath, + Target = "/mnt/install", + ReadOnly = false, + Type = "bind" + }); + + // Command + parameters.Cmd = [installConfiguration.Shell, "/mnt/install/install.sh"]; + + return parameters; + } + + private static void ApplySharedOptions( + CreateContainerParameters parameters, + RuntimeConfiguration configuration + ) + { + // Input, output & error streams and TTY + + parameters.Tty = true; + parameters.AttachStderr = true; + parameters.AttachStdin = true; + parameters.AttachStdout = true; + parameters.OpenStdin = true; + + // Logging + + parameters.HostConfig.LogConfig = new() + { + Type = "json-file", // We need to use this provider, as the GetLogs endpoint needs it + Config = new Dictionary() + }; + + // Environment variables + + parameters.Env = new List() + { + $"STARTUP={configuration.Template.StartupCommand}", + //TODO: Add timezone, add server ip + }; + + if (configuration.Limits.MemoryMb.HasValue) + parameters.Env.Add($"SERVER_MEMORY={configuration.Limits.MemoryMb.Value}"); + + if (configuration.Network.MainPort != null) + { + parameters.Env.Add($"SERVER_IP={configuration.Network.MainPort.IpAddress}"); + parameters.Env.Add($"SERVER_PORT={configuration.Network.MainPort.Port}"); + } + + // Handle port variables + var i = 1; + foreach (var port in configuration.Network.Ports) + { + parameters.Env.Add($"ML_PORT_{i}={port.Port}"); + i++; + } + + // Copy variables as env vars + foreach (var variable in configuration.Environment.Variables) + parameters.Env.Add($"{variable.Key}={variable.Value}"); + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerConsole.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerConsole.cs new file mode 100644 index 0000000..087f69a --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerConsole.cs @@ -0,0 +1,195 @@ +using System.Buffers; +using System.Text; +using Docker.DotNet; +using Docker.DotNet.Models; +using MoonlightServers.Daemon.ServerSystem.Abstractions; + +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Docker; + +public class DockerConsole : IRuntimeConsole, IInstallConsole, IAsyncDisposable +{ + public event Func? OnOutput; + + private MultiplexedStream? Stream; + + private readonly string ContainerId; + private readonly DockerClient DockerClient; + private readonly ILogger Logger; + + private readonly List Cache = new(302); + private readonly SemaphoreSlim CacheLock = new(1, 1); + private readonly CancellationTokenSource Cts = new(); + + public DockerConsole( + string containerId, + DockerClient dockerClient, + ILogger logger + ) + { + ContainerId = containerId; + DockerClient = dockerClient; + Logger = logger; + } + + public async Task AttachAsync() + { + // Fetch initial logs + Logger.LogTrace("Fetching pre-existing logs from container"); + + var logResponse = await DockerClient.Containers.GetContainerLogsAsync( + ContainerId, + new() + { + Follow = false, + ShowStderr = true, + ShowStdout = true + } + ); + + // Append to cache + var logs = await logResponse.ReadOutputToEndAsync(Cts.Token); + + await CacheLock.WaitAsync(Cts.Token); + + try + { + Cache.Add(logs.stdout); + Cache.Add(logs.stderr); + } + finally + { + CacheLock.Release(); + } + + // Stream new logs + Logger.LogTrace("Starting log streaming"); + + Task.Run(async () => + { + var capturedCt = Cts.Token; + + Logger.LogTrace("Starting attach loop"); + + while (!capturedCt.IsCancellationRequested) + { + try + { + using var stream = await DockerClient.Containers.AttachContainerAsync( + ContainerId, + new ContainerAttachParameters() + { + Stderr = true, + Stdin = true, + Stdout = true, + Stream = true + }, + capturedCt + ); + + // Make stream accessible from the outside + Stream = stream; + + const int bufferSize = 1024; + var buffer = ArrayPool.Shared.Rent(bufferSize); + + while (!capturedCt.IsCancellationRequested) + { + try + { + var readResult = await stream.ReadOutputAsync(buffer, 0, bufferSize, capturedCt); + + if (readResult.Count > 0) + { + var decodedBuffer = Encoding.UTF8.GetString(buffer, 0, readResult.Count); + + await CacheLock.WaitAsync(capturedCt); + + try + { + if (Cache.Count > 300) + Cache.RemoveRange(0, 50); + + Cache.Add(decodedBuffer); + } + finally + { + CacheLock.Release(); + } + + if (OnOutput != null) + await OnOutput.Invoke(decodedBuffer); + } + + if (readResult.EOF) + break; + } + catch (OperationCanceledException) + { + // Ignored + } + catch (Exception e) + { + Logger.LogError(e, "An unhandled error occured while processing container stream"); + } + } + + ArrayPool.Shared.Return(buffer); + } + catch (OperationCanceledException) + { + // Ignored + } + catch (Exception e) + { + Logger.LogError(e, "An unhandled error occured while handling container attaching"); + } + } + + Logger.LogTrace("Attach loop exited"); + }); + } + + public async Task WriteInputAsync(string value) + { + if (Stream == null) + throw new AggregateException("Stream is not available. Container might not be attached"); + + var buffer = Encoding.UTF8.GetBytes(value); + await Stream.WriteAsync(buffer, 0, buffer.Length, Cts.Token); + } + + public async Task ClearCacheAsync() + { + await CacheLock.WaitAsync(Cts.Token); + + try + { + Cache.Clear(); + } + finally + { + CacheLock.Release(); + } + } + + public async Task GetCacheAsync() + { + await CacheLock.WaitAsync(); + + try + { + return Cache.ToArray(); + } + finally + { + CacheLock.Release(); + } + } + + public async ValueTask DisposeAsync() + { + await Cts.CancelAsync(); + Stream?.Dispose(); + CacheLock.Dispose(); + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerEventService.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerEventService.cs new file mode 100644 index 0000000..440082a --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerEventService.cs @@ -0,0 +1,93 @@ +using Docker.DotNet; +using Docker.DotNet.Models; +using MoonlightServers.Daemon.ServerSystem.Implementations.Docker.Events; + +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Docker; + +public class DockerEventService : BackgroundService +{ + public event Func? OnContainerDied; + + private readonly ILogger Logger; + private readonly DockerClient DockerClient; + + public DockerEventService( + ILogger logger, + DockerClient dockerClient + ) + { + Logger = logger; + DockerClient = dockerClient; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + Logger.LogTrace("Starting up docker event monitor"); + + while (!stoppingToken.IsCancellationRequested) + { + try + { + Logger.LogTrace("Monitoring events"); + + await DockerClient.System.MonitorEventsAsync( + new ContainerEventsParameters(), + new Progress(OnEventAsync), + stoppingToken + ); + } + catch (OperationCanceledException) + { + // ignored + } + catch (Exception e) + { + Logger.LogError(e, "An error occured while processing container event monitoring"); + } + } + + Logger.LogTrace("Closed docker event monitor"); + } + + private async void OnEventAsync(Message message) + { + try + { + switch (message.Type) + { + case "container": + + var containerId = message.Actor.ID; + + switch (message.Action) + { + case "die": + + if ( + !message.Actor.Attributes.TryGetValue("exitCode", out var exitCodeStr) || + !int.TryParse(exitCodeStr, out var exitCode) + ) + { + return; + } + + if (OnContainerDied != null) + await OnContainerDied.Invoke(new ContainerDieEvent(containerId, exitCode)); + + return; + } + + break; + } + } + catch (Exception e) + { + Logger.LogError( + e, + "An error occured while handling event {type} for {action}", + message.Type, + message.Action + ); + } + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerInstallEnv.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerInstallEnv.cs new file mode 100644 index 0000000..6620b6b --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerInstallEnv.cs @@ -0,0 +1,63 @@ +using Docker.DotNet; +using MoonlightServers.Daemon.ServerSystem.Abstractions; +using MoonlightServers.Daemon.ServerSystem.Implementations.Docker.Events; + +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Docker; + +public class DockerInstallEnv : IInstallEnvironment +{ + public IInstallStatistics Statistics => InnerStatistics; + public IInstallConsole Console => InnerConsole; + + public event Func? OnExited; + + public string ContainerId { get; } + + private readonly DockerClient DockerClient; + private readonly ILogger Logger; + private readonly DockerEventService EventService; + + private readonly DockerStatistics InnerStatistics; + private readonly DockerConsole InnerConsole; + + public DockerInstallEnv(string containerId, DockerClient dockerClient, ILogger logger, DockerEventService eventService) + { + ContainerId = containerId; + DockerClient = dockerClient; + Logger = logger; + EventService = eventService; + + InnerStatistics = new DockerStatistics(); + InnerConsole = new DockerConsole(containerId, dockerClient, logger); + + EventService.OnContainerDied += HandleDieEventAsync; + } + + public async Task IsRunningAsync() + { + var container = await DockerClient.Containers.InspectContainerAsync(ContainerId); + return container.State.Running; + } + + public async Task StartAsync() + => await DockerClient.Containers.StartContainerAsync(ContainerId, new()); + + public async Task KillAsync() + => await DockerClient.Containers.KillContainerAsync(ContainerId, new()); + + private async Task HandleDieEventAsync(ContainerDieEvent dieEvent) + { + if(dieEvent.ContainerId != ContainerId) + return; + + if(OnExited != null) + await OnExited.Invoke(); + } + + public async ValueTask DisposeAsync() + { + EventService.OnContainerDied -= HandleDieEventAsync; + + await InnerConsole.DisposeAsync(); + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerInstallEnvService.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerInstallEnvService.cs new file mode 100644 index 0000000..13262d9 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerInstallEnvService.cs @@ -0,0 +1,109 @@ +using Docker.DotNet; +using MoonlightServers.Daemon.Models; +using MoonlightServers.Daemon.ServerSystem.Abstractions; + +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Docker; + +public class DockerInstallEnvService : IInstallEnvironmentService +{ + private readonly DockerClient DockerClient; + private readonly ILoggerFactory LoggerFactory; + private readonly DockerEventService DockerEventService; + + private const string NameTemplate = "ml-install-{0}"; + + public DockerInstallEnvService(DockerClient dockerClient, ILoggerFactory loggerFactory, + DockerEventService dockerEventService) + { + DockerClient = dockerClient; + LoggerFactory = loggerFactory; + DockerEventService = dockerEventService; + } + + public async Task FindAsync(string id) + { + try + { + var dockerInspect = await DockerClient.Containers.InspectContainerAsync( + string.Format(NameTemplate, id) + ); + + var logger = + LoggerFactory.CreateLogger($"MoonlightServers.Daemon.ServerSystem.Implementations.Docker({id})"); + + return new DockerInstallEnv(dockerInspect.ID, DockerClient, logger, DockerEventService); + } + catch (DockerContainerNotFoundException) + { + return null; + } + } + + public async Task CreateAsync( + string id, + RuntimeConfiguration runtimeConfiguration, + InstallConfiguration installConfiguration, + IInstallStorage installStorage, + IRuntimeStorage runtimeStorage + ) + { + try + { + var dockerInspect = await DockerClient.Containers.InspectContainerAsync( + string.Format(NameTemplate, id) + ); + + if (dockerInspect.State.Running) + await DockerClient.Containers.KillContainerAsync(dockerInspect.ID, new()); + + await DockerClient.Containers.RemoveContainerAsync(dockerInspect.ID, new()); + } + catch (DockerContainerNotFoundException) + { + // Ignored + } + + var runtimeStoragePath = await runtimeStorage.GetHostPathAsync(); + var installStoragePath = await installStorage.GetHostPathAsync(); + + var parameters = ConfigMapper.GetInstallConfig( + id, + string.Format(NameTemplate, id), + runtimeConfiguration, + installConfiguration, + runtimeStoragePath, + installStoragePath + ); + + var container = await DockerClient.Containers.CreateContainerAsync(parameters); + + var logger = LoggerFactory.CreateLogger($"MoonlightServers.Daemon.ServerSystem.Implementations.Docker({id})"); + + return new DockerInstallEnv(container.ID, DockerClient, logger, DockerEventService); + } + + public async Task DeleteAsync(IInstallEnvironment environment) + { + if (environment is not DockerInstallEnv dockerInstallEnv) + throw new ArgumentException( + $"You cannot delete runtime environments which haven't been created by {nameof(DockerInstallEnv)}"); + + await dockerInstallEnv.DisposeAsync(); + + try + { + var dockerInspect = await DockerClient.Containers.InspectContainerAsync( + dockerInstallEnv.ContainerId + ); + + if (dockerInspect.State.Running) + await DockerClient.Containers.KillContainerAsync(dockerInspect.ID, new()); + + await DockerClient.Containers.RemoveContainerAsync(dockerInspect.ID, new()); + } + catch (DockerContainerNotFoundException) + { + // Ignored + } + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerRuntimeEnv.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerRuntimeEnv.cs new file mode 100644 index 0000000..7638926 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerRuntimeEnv.cs @@ -0,0 +1,61 @@ +using Docker.DotNet; +using MoonlightServers.Daemon.ServerSystem.Abstractions; +using MoonlightServers.Daemon.ServerSystem.Implementations.Docker.Events; + +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Docker; + +public class DockerRuntimeEnv : IRuntimeEnvironment +{ + public IRuntimeStatistics Statistics => InnerStatistics; + public IRuntimeConsole Console => InnerConsole; + + public string ContainerId { get; } + + public event Func? OnExited; + + private readonly DockerClient DockerClient; + private readonly DockerEventService EventService; + + private readonly DockerConsole InnerConsole; + private readonly DockerStatistics InnerStatistics; + + public DockerRuntimeEnv(string containerId, DockerClient dockerClient, ILogger logger, DockerEventService eventService) + { + ContainerId = containerId; + DockerClient = dockerClient; + EventService = eventService; + + InnerStatistics = new DockerStatistics(); + InnerConsole = new DockerConsole(containerId, dockerClient, logger); + + EventService.OnContainerDied += HandleDieEventAsync; + } + + public async Task IsRunningAsync() + { + var container = await DockerClient.Containers.InspectContainerAsync(ContainerId); + return container.State.Running; + } + + public async Task StartAsync() + => await DockerClient.Containers.StartContainerAsync(ContainerId, new()); + + public async Task KillAsync() + => await DockerClient.Containers.KillContainerAsync(ContainerId, new()); + + private async Task HandleDieEventAsync(ContainerDieEvent dieEvent) + { + if(dieEvent.ContainerId != ContainerId) + return; + + if(OnExited != null) + await OnExited.Invoke(); + } + + public async ValueTask DisposeAsync() + { + EventService.OnContainerDied -= HandleDieEventAsync; + + await InnerConsole.DisposeAsync(); + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerRuntimeEnvService.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerRuntimeEnvService.cs new file mode 100644 index 0000000..7d1875a --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerRuntimeEnvService.cs @@ -0,0 +1,112 @@ +using Docker.DotNet; +using MoonlightServers.Daemon.Models; +using MoonlightServers.Daemon.ServerSystem.Abstractions; + +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Docker; + +public class DockerRuntimeEnvService : IRuntimeEnvironmentService +{ + private readonly DockerClient DockerClient; + private readonly ILoggerFactory LoggerFactory; + private readonly DockerEventService DockerEventService; + + private const string NameTemplate = "ml-runtime-{0}"; + + public DockerRuntimeEnvService(DockerClient dockerClient, ILoggerFactory loggerFactory, + DockerEventService dockerEventService) + { + DockerClient = dockerClient; + LoggerFactory = loggerFactory; + DockerEventService = dockerEventService; + } + + public async Task FindAsync(string id) + { + try + { + var dockerInspect = await DockerClient.Containers.InspectContainerAsync( + string.Format(NameTemplate, id) + ); + + var logger = + LoggerFactory.CreateLogger($"MoonlightServers.Daemon.ServerSystem.Implementations.Docker({id})"); + + return new DockerRuntimeEnv(dockerInspect.ID, DockerClient, logger, DockerEventService); + } + catch (DockerContainerNotFoundException) + { + return null; + } + } + + public async Task CreateAsync( + string id, + RuntimeConfiguration configuration, + IRuntimeStorage storage + ) + { + try + { + var dockerInspect = await DockerClient.Containers.InspectContainerAsync( + string.Format(NameTemplate, id) + ); + + if (dockerInspect.State.Running) + await DockerClient.Containers.KillContainerAsync(dockerInspect.ID, new()); + + await DockerClient.Containers.RemoveContainerAsync(dockerInspect.ID, new()); + } + catch (DockerContainerNotFoundException) + { + // Ignored + } + + var storagePath = await storage.GetHostPathAsync(); + + var parameters = ConfigMapper.GetRuntimeConfig( + id, + string.Format(NameTemplate, id), + configuration, + storagePath + ); + + var container = await DockerClient.Containers.CreateContainerAsync(parameters); + + var logger = LoggerFactory.CreateLogger($"MoonlightServers.Daemon.ServerSystem.Implementations.Docker({id})"); + + return new DockerRuntimeEnv(container.ID, DockerClient, logger, DockerEventService); + } + + public Task UpdateAsync(IRuntimeEnvironment environment, RuntimeConfiguration configuration) + { + throw new NotImplementedException(); + } + + public async Task DeleteAsync(IRuntimeEnvironment environment) + { + if (environment is not DockerRuntimeEnv dockerRuntimeEnv) + { + throw new ArgumentException( + $"You cannot delete runtime environments which haven't been created by {nameof(DockerRuntimeEnvService)}" + ); + } + + await dockerRuntimeEnv.DisposeAsync(); + + try + { + var dockerInspect = await DockerClient.Containers.InspectContainerAsync( + dockerRuntimeEnv.ContainerId + ); + + if (dockerInspect.State.Running) + await DockerClient.Containers.KillContainerAsync(dockerInspect.ID, new()); + + await DockerClient.Containers.RemoveContainerAsync(dockerInspect.ID, new()); + } + catch (DockerContainerNotFoundException) + { + // Ignored + } + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerStatistics.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerStatistics.cs new file mode 100644 index 0000000..0740887 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/DockerStatistics.cs @@ -0,0 +1,14 @@ +using MoonlightServers.Daemon.ServerSystem.Abstractions; + +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Docker; + +public class DockerStatistics : IRuntimeStatistics, IInstallStatistics +{ + public event Func? OnStatisticsReceived; + + public Task AttachAsync() => Task.CompletedTask; + + public Task ClearCacheAsync() => Task.CompletedTask; + + public Task GetCacheAsync() => Task.FromResult([]); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/Events/ContainerDieEvent.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/Events/ContainerDieEvent.cs new file mode 100644 index 0000000..f635550 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/Events/ContainerDieEvent.cs @@ -0,0 +1,3 @@ +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Docker.Events; + +public record ContainerDieEvent(string ContainerId, int ExitCode); \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/Extensions.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/Extensions.cs new file mode 100644 index 0000000..992f685 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Docker/Extensions.cs @@ -0,0 +1,22 @@ +using Docker.DotNet; +using MoonlightServers.Daemon.ServerSystem.Abstractions; + +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Docker; + +public static class Extensions +{ + public static void AddDockerServices(this IServiceCollection collection) + { + var client = new DockerClientBuilder() + .WithEndpoint(new Uri("unix:///var/run/docker.sock")) + .Build(); + + collection.AddSingleton(client); + + collection.AddSingleton(); + collection.AddHostedService(sp => sp.GetRequiredService()); + + collection.AddSingleton(); + collection.AddSingleton(); + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Local/Extensions.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Local/Extensions.cs new file mode 100644 index 0000000..8cc1e54 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Local/Extensions.cs @@ -0,0 +1,12 @@ +using MoonlightServers.Daemon.ServerSystem.Abstractions; + +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Local; + +public static class Extensions +{ + public static void AddLocalServices(this IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Local/LocalInstallStorage.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Local/LocalInstallStorage.cs new file mode 100644 index 0000000..f016ee7 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Local/LocalInstallStorage.cs @@ -0,0 +1,15 @@ +using MoonlightServers.Daemon.ServerSystem.Abstractions; + +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Local; + +public class LocalInstallStorage : IInstallStorage +{ + public string HostPath { get; } + + public LocalInstallStorage(string hostPath) + { + HostPath = hostPath; + } + + public Task GetHostPathAsync() => Task.FromResult(HostPath); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Local/LocalInstallStorageService.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Local/LocalInstallStorageService.cs new file mode 100644 index 0000000..df0f555 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Local/LocalInstallStorageService.cs @@ -0,0 +1,43 @@ +using MoonlightServers.Daemon.Models; +using MoonlightServers.Daemon.ServerSystem.Abstractions; + +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Local; + +public class LocalInstallStorageService : IInstallStorageService +{ + private const string HostPathTemplate = "./mldaemon/install/{0}"; + + public Task FindAsync(string id) + { + var path = string.Format(HostPathTemplate, id); + + if (!Directory.Exists(path)) + return Task.FromResult(null); + + return Task.FromResult(new LocalInstallStorage(path)); + } + + public Task CreateAsync(string id, RuntimeConfiguration runtimeConfiguration, InstallConfiguration installConfiguration) + { + var path = string.Format(HostPathTemplate, id); + + Directory.CreateDirectory(path); + + return Task.FromResult(new LocalInstallStorage(path)); + } + + public Task DeleteAsync(IInstallStorage installStorage) + { + if (installStorage is not LocalInstallStorage localInstallStorage) + { + throw new ArgumentException( + $"You cannot delete install storages which haven't been created by {nameof(LocalInstallStorageService)}" + ); + } + + if(Directory.Exists(localInstallStorage.HostPath)) + Directory.Delete(localInstallStorage.HostPath, true); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Local/LocalRuntimeStorage.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Local/LocalRuntimeStorage.cs new file mode 100644 index 0000000..f780e94 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Local/LocalRuntimeStorage.cs @@ -0,0 +1,15 @@ +using MoonlightServers.Daemon.ServerSystem.Abstractions; + +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Local; + +public class LocalRuntimeStorage : IRuntimeStorage +{ + public string HostPath { get; } + + public LocalRuntimeStorage(string hostPath) + { + HostPath = hostPath; + } + + public Task GetHostPathAsync() => Task.FromResult(HostPath); +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/Local/LocalRuntimeStorageService.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/Local/LocalRuntimeStorageService.cs new file mode 100644 index 0000000..53943fd --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Implementations/Local/LocalRuntimeStorageService.cs @@ -0,0 +1,46 @@ +using MoonlightServers.Daemon.Models; +using MoonlightServers.Daemon.ServerSystem.Abstractions; + +namespace MoonlightServers.Daemon.ServerSystem.Implementations.Local; + +public class LocalRuntimeStorageService : IRuntimeStorageService +{ + private const string HostPathTemplate = "./mldaemon/runtime/{0}"; + + public Task FindAsync(string id) + { + var path = string.Format(HostPathTemplate, id); + + if (!Directory.Exists(path)) + return Task.FromResult(null); + + return Task.FromResult(new LocalRuntimeStorage(path)); + } + + public Task CreateAsync(string id, RuntimeConfiguration configuration) + { + var path = string.Format(HostPathTemplate, id); + + Directory.CreateDirectory(path); + + return Task.FromResult(new LocalRuntimeStorage(path)); + } + + public Task UpdateAsync(IRuntimeStorage runtimeStorage, RuntimeConfiguration configuration) + => Task.CompletedTask; + + public Task DeleteAsync(IRuntimeStorage runtimeStorage) + { + if (runtimeStorage is not LocalRuntimeStorage localRuntimeStorage) + { + throw new ArgumentException( + $"You cannot delete runtime storages which haven't been created by {nameof(LocalRuntimeStorageService)}" + ); + } + + if(Directory.Exists(localRuntimeStorage.HostPath)) + Directory.Delete(localRuntimeStorage.HostPath, true); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/RegexOnlineDetector.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/RegexOnlineDetector.cs deleted file mode 100644 index 085faad..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Implementations/RegexOnlineDetector.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Text.RegularExpressions; -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; - -namespace MoonlightServers.Daemon.ServerSystem.Implementations; - -public class RegexOnlineDetector : IOnlineDetector -{ - private readonly ServerContext Context; - - private Regex? Expression; - - public RegexOnlineDetector(ServerContext context) - { - Context = context; - } - - public Task InitializeAsync() - => Task.CompletedTask; - - public Task CreateAsync() - { - if(string.IsNullOrEmpty(Context.Configuration.OnlineDetection)) - return Task.CompletedTask; - - Expression = new Regex(Context.Configuration.OnlineDetection, RegexOptions.Compiled); - - return Task.CompletedTask; - } - - public Task HandleOutputAsync(string line) - { - if (Expression == null) - return Task.FromResult(false); - - var result = Expression.Matches(line).Count > 0; - - return Task.FromResult(result); - } - - public Task DestroyAsync() - { - Expression = null; - return Task.CompletedTask; - } - - public ValueTask DisposeAsync() - { - Expression = null; - return ValueTask.CompletedTask; - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Implementations/ServerReporter.cs b/MoonlightServers.Daemon/ServerSystem/Implementations/ServerReporter.cs deleted file mode 100644 index 281e301..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Implementations/ServerReporter.cs +++ /dev/null @@ -1,44 +0,0 @@ -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; - -namespace MoonlightServers.Daemon.ServerSystem.Implementations; - -public class ServerReporter : IReporter -{ - private readonly ServerContext Context; - - private const string StatusTemplate = - "\x1b[1;38;2;200;90;200mM\x1b[1;38;2;204;110;230mo\x1b[1;38;2;170;130;245mo\x1b[1;38;2;140;150;255mn\x1b[1;38;2;110;180;255ml\x1b[1;38;2;100;200;255mi\x1b[1;38;2;100;220;255mg\x1b[1;38;2;120;235;255mh\x1b[1;38;2;140;250;255mt\x1b[0m \x1b[3;38;2;200;200;200m{0}\x1b[0m\n\r"; - - private const string ErrorTemplate = - "\x1b[1;38;2;200;90;200mM\x1b[1;38;2;204;110;230mo\x1b[1;38;2;170;130;245mo\x1b[1;38;2;140;150;255mn\x1b[1;38;2;110;180;255ml\x1b[1;38;2;100;200;255mi\x1b[1;38;2;100;220;255mg\x1b[1;38;2;120;235;255mh\x1b[1;38;2;140;250;255mt\x1b[0m \x1b[1;38;2;255;0;0m{0}\x1b[0m\n\r"; - - public ServerReporter(ServerContext context) - { - Context = context; - } - - public Task InitializeAsync() - => Task.CompletedTask; - - public async Task StatusAsync(string message) - { - Context.Logger.LogInformation("Status: {message}", message); - - await Context.Server.Console.WriteStdOutAsync( - string.Format(StatusTemplate, message) - ); - } - - public async Task ErrorAsync(string message) - { - Context.Logger.LogError("Error: {message}", message); - - await Context.Server.Console.WriteStdOutAsync( - string.Format(ErrorTemplate, message) - ); - } - - public ValueTask DisposeAsync() - => ValueTask.CompletedTask; -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Interfaces/IConsole.cs b/MoonlightServers.Daemon/ServerSystem/Interfaces/IConsole.cs deleted file mode 100644 index ba798ce..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Interfaces/IConsole.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace MoonlightServers.Daemon.ServerSystem.Interfaces; - -public interface IConsole : IServerComponent -{ - /// - /// Writes to the standard input of the console. If attached to the runtime when using docker for example this - /// would write into the containers standard input. - /// This method does not add a newline separator at the end of the content. The caller needs to add this themselves if required - /// - /// Content to write - /// - public Task WriteStdInAsync(string content); - /// - /// Writes to the standard output of the console. If attached to the runtime when using docker for example this - /// would write into the containers standard output. - /// This method does not add a newline separator at the end of the content. The caller needs to add this themselves if required - /// - /// Content to write - /// - public Task WriteStdOutAsync(string content); - - /// - /// Attaches the console to the runtime environment - /// - /// - public Task AttachRuntimeAsync(); - - /// - /// Attaches the console to the installation environment - /// - /// - public Task AttachInstallationAsync(); - - /// - /// Fetches all output from the runtime environment and write them into the cache without triggering any events - /// - /// - public Task FetchRuntimeAsync(); - - /// - /// Fetches all output from the installation environment and write them into the cache without triggering any events - /// - /// - public Task FetchInstallationAsync(); - - /// - /// Clears the cache of the standard output received by the environments - /// - /// - public Task ClearCacheAsync(); - - /// - /// Gets the content from the standard output cache - /// - /// Content from the cache - public Task> GetCacheAsync(); - - /// - /// Subscribes to standard output receive events - /// - /// Callback which will be invoked whenever a new line is received - /// Subscription disposable to unsubscribe from the event - public Task SubscribeStdOutAsync(Func callback); -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Interfaces/IFileSystem.cs b/MoonlightServers.Daemon/ServerSystem/Interfaces/IFileSystem.cs deleted file mode 100644 index 1510057..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Interfaces/IFileSystem.cs +++ /dev/null @@ -1,54 +0,0 @@ -namespace MoonlightServers.Daemon.ServerSystem.Interfaces; - -public interface IFileSystem : IServerComponent -{ - /// - /// Gets the path of the file system on the host operating system to be reused by other components - /// - /// Path to the file systems storage location - public Task GetPathAsync(); - - /// - /// Checks if the file system exists - /// - /// True if it does exist. False if it doesn't exist - public Task CheckExistsAsync(); - - /// - /// Checks if the file system is mounted - /// - /// True if its mounted, False if it is not mounted - public Task CheckMountedAsync(); - - /// - /// Creates the file system. E.g. Creating a virtual disk, formatting it - /// - /// - public Task CreateAsync(); - - /// - /// Performs checks and optimisations on the file system. - /// E.g. checking for corrupted files, resizing a virtual disk or adjusting file permissions - /// Requires to be called before or the file system to be in a mounted state - /// - /// - public Task PerformChecksAsync(); - - /// - /// Mounts the file system - /// - /// - public Task MountAsync(); - - /// - /// Unmounts the file system - /// - /// - public Task UnmountAsync(); - - /// - /// Destroys the file system and its contents - /// - /// - public Task DestroyAsync(); -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Interfaces/IInstallation.cs b/MoonlightServers.Daemon/ServerSystem/Interfaces/IInstallation.cs deleted file mode 100644 index c356f31..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Interfaces/IInstallation.cs +++ /dev/null @@ -1,53 +0,0 @@ -using MoonlightServers.DaemonShared.PanelSide.Http.Responses; - -namespace MoonlightServers.Daemon.ServerSystem.Interfaces; - -public interface IInstallation : IServerComponent -{ - /// - /// Checks if the installation environment exists. It doesn't matter if it is currently running or not - /// - /// True if it exists, False if it doesn't - public Task CheckExistsAsync(); - - /// - /// Creates the installation environment - /// - /// Host path of the runtime storage location - /// Host path of the installation file system - /// Installation data for the server - /// - public Task CreateAsync(string runtimePath, string hostPath, ServerInstallDataResponse data); - - /// - /// Starts the installation - /// - /// - public Task StartAsync(); - - /// - /// Kills the current installation immediately - /// - /// - public Task KillAsync(); - - /// - /// Removes the installation. E.g. removes the docker container - /// - /// - public Task DestroyAsync(); - - /// - /// Subscribes to the event when the installation exists - /// - /// Callback to invoke whenever the installation exists - /// Subscription disposable to unsubscribe from the event - public Task SubscribeExitedAsync(Func callback); - - /// - /// Connects an existing installation to this abstraction in order to restore it. - /// E.g. fetching the container id and using it for exit events - /// - /// - public Task RestoreAsync(); -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Interfaces/IOnlineDetector.cs b/MoonlightServers.Daemon/ServerSystem/Interfaces/IOnlineDetector.cs deleted file mode 100644 index dc7314e..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Interfaces/IOnlineDetector.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace MoonlightServers.Daemon.ServerSystem.Interfaces; - -public interface IOnlineDetector : IServerComponent -{ - /// - /// Creates the detection engine for the online state - /// - /// - public Task CreateAsync(); - - /// - /// Handles the detection of the online state based on the received output - /// - /// Excerpt of the output - /// True if the detection showed that the server is online. False if the detection didnt find anything - public Task HandleOutputAsync(string line); - - /// - /// Destroys the detection engine for the online state - /// - /// - public Task DestroyAsync(); -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Interfaces/IReporter.cs b/MoonlightServers.Daemon/ServerSystem/Interfaces/IReporter.cs deleted file mode 100644 index ba9f2bb..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Interfaces/IReporter.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace MoonlightServers.Daemon.ServerSystem.Interfaces; - -public interface IReporter : IServerComponent -{ - /// - /// Writes both in the server logs as well in the server console the provided message as a status update - /// - /// Message to write - /// - public Task StatusAsync(string message); - - /// - /// Writes both in the server logs as well in the server console the provided message as an error - /// - /// Message to write - /// - public Task ErrorAsync(string message); -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Interfaces/IRestorer.cs b/MoonlightServers.Daemon/ServerSystem/Interfaces/IRestorer.cs deleted file mode 100644 index e14ffca..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Interfaces/IRestorer.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace MoonlightServers.Daemon.ServerSystem.Interfaces; - -public interface IRestorer : IServerComponent -{ - /// - /// Checks for any running runtime environment from which the state can be restored from - /// - /// - public Task HandleRuntimeAsync(); - - /// - /// Checks for any running installation environment from which the state can be restored from - /// - /// - public Task HandleInstallationAsync(); -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Interfaces/IRuntime.cs b/MoonlightServers.Daemon/ServerSystem/Interfaces/IRuntime.cs deleted file mode 100644 index d7e9490..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Interfaces/IRuntime.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace MoonlightServers.Daemon.ServerSystem.Interfaces; - -public interface IRuntime : IServerComponent -{ - /// - /// Checks if the runtime does exist. This includes already running instances - /// - /// True if it exists, False if it doesn't - public Task CheckExistsAsync(); - - /// - /// Creates the runtime with the specified path as the storage path where the server files should be stored in - /// - /// Path where the server files are located - /// - public Task CreateAsync(string path); - - /// - /// Starts the runtime. This requires to be called before this function - /// - /// - public Task StartAsync(); - - /// - /// Performs a live update on the runtime. When this method is called the current server configuration has already been updated - /// - /// - public Task UpdateAsync(); - - /// - /// Kills the current runtime immediately - /// - /// - public Task KillAsync(); - - /// - /// Destroys the runtime. When implemented using docker this would remove the container used for hosting the runtime - /// - /// - public Task DestroyAsync(); - - /// - /// This subscribes to the exited event of the runtime - /// - /// Callback gets invoked whenever the runtime exites - /// Subscription disposable to unsubscribe from the event - public Task SubscribeExitedAsync(Func callback); - - /// - /// Connects an existing runtime to this abstraction in order to restore it. - /// E.g. fetching the container id and using it for exit events - /// - /// - public Task RestoreAsync(); -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Interfaces/IServerComponent.cs b/MoonlightServers.Daemon/ServerSystem/Interfaces/IServerComponent.cs deleted file mode 100644 index 0414d2e..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Interfaces/IServerComponent.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace MoonlightServers.Daemon.ServerSystem.Interfaces; - -public interface IServerComponent : IAsyncDisposable -{ - /// - /// Initializes the server component - /// - /// - public Task InitializeAsync(); -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Interfaces/IServerStateHandler.cs b/MoonlightServers.Daemon/ServerSystem/Interfaces/IServerStateHandler.cs deleted file mode 100644 index 6a0c775..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Interfaces/IServerStateHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using MoonlightServers.Daemon.ServerSystem.Enums; -using Stateless; - -namespace MoonlightServers.Daemon.ServerSystem.Interfaces; - -public interface IServerStateHandler : IAsyncDisposable -{ - public Task ExecuteAsync(StateMachine.Transition transition); -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Interfaces/IStatistics.cs b/MoonlightServers.Daemon/ServerSystem/Interfaces/IStatistics.cs deleted file mode 100644 index 1de7e18..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Interfaces/IStatistics.cs +++ /dev/null @@ -1,30 +0,0 @@ -using MoonlightServers.Daemon.ServerSystem.Models; - -namespace MoonlightServers.Daemon.ServerSystem.Interfaces; - -public interface IStatistics : IServerComponent -{ - /// - /// Attaches the statistics collector to the currently running runtime - /// - /// - public Task AttachRuntimeAsync(); - - /// - /// Attaches the statistics collector to the currently running installation - /// - /// - public Task AttachInstallationAsync(); - - /// - /// Clears the statistics cache - /// - /// - public Task ClearCacheAsync(); - - /// - /// Gets the statistics data from the cache - /// - /// All data from the cache - public Task> GetCacheAsync(); -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Models/ServerContext.cs b/MoonlightServers.Daemon/ServerSystem/Models/ServerContext.cs deleted file mode 100644 index 3a63609..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Models/ServerContext.cs +++ /dev/null @@ -1,12 +0,0 @@ -using MoonlightServers.Daemon.Models.Cache; - -namespace MoonlightServers.Daemon.ServerSystem.Models; - -public class ServerContext -{ - public ServerConfiguration Configuration { get; set; } - public int Identifier { get; set; } - public AsyncServiceScope ServiceScope { get; set; } - public Server Server { get; set; } - public ILogger Logger { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Models/StatisticsData.cs b/MoonlightServers.Daemon/ServerSystem/Models/StatisticsData.cs deleted file mode 100644 index 210d121..0000000 --- a/MoonlightServers.Daemon/ServerSystem/Models/StatisticsData.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MoonlightServers.Daemon.ServerSystem.Models; - -public class StatisticsData -{ - -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Server.Delete.cs b/MoonlightServers.Daemon/ServerSystem/Server.Delete.cs new file mode 100644 index 0000000..0a064b7 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Server.Delete.cs @@ -0,0 +1,37 @@ +namespace MoonlightServers.Daemon.ServerSystem; + +public partial class Server +{ + public async Task DeleteAsync() + { + await Lock.WaitAsync(); + + try + { + if(State != ServerState.Offline) + throw new InvalidOperationException("Server is not offline"); + + Logger.LogTrace("Deleting"); + + InstallStorage ??= await InstallStorageService.FindAsync(Uuid); + + if (InstallStorage != null) + { + Logger.LogTrace("Deleting install storage"); + await InstallStorageService.DeleteAsync(InstallStorage); + } + + RuntimeStorage ??= await RuntimeStorageService.FindAsync(Uuid); + + if (RuntimeStorage != null) + { + Logger.LogTrace("Deleting runtime storage"); + await RuntimeStorageService.DeleteAsync(RuntimeStorage); + } + } + finally + { + Lock.Release(); + } + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Server.Install.cs b/MoonlightServers.Daemon/ServerSystem/Server.Install.cs new file mode 100644 index 0000000..aaa31f2 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Server.Install.cs @@ -0,0 +1,157 @@ +namespace MoonlightServers.Daemon.ServerSystem; + +public partial class Server +{ + public async Task InstallAsync() + { + await Lock.WaitAsync(); + + try + { + if (State != ServerState.Offline) + throw new InvalidOperationException("Server is not offline"); + + // Check if any pre-existing install env exists, if we don't have a reference to it already + InstallEnvironment ??= await InstallEnvironmentService.FindAsync(Uuid); + + // Check if storages exist + InstallStorage ??= await InstallStorageService.FindAsync(Uuid); + RuntimeStorage ??= await RuntimeStorageService.FindAsync(Uuid); + + // Remove any pre-existing installation env + if (InstallEnvironment != null) + { + Logger.LogTrace("Destroying pre-existing install environment"); + + if (await InstallEnvironment.IsRunningAsync()) + { + Logger.LogTrace("Pre-existing install environment is still running, killing it"); + await InstallEnvironment.KillAsync(); + } + + // Remove any event handlers if existing + InstallEnvironment.Console.OnOutput -= OnConsoleMessageAsync; + InstallEnvironment.Statistics.OnStatisticsReceived -= OnStatisticsReceivedAsync; + InstallEnvironment.OnExited -= OnInstallExitedAsync; + + // Now remove it + // Finally remove it + await InstallEnvironmentService.DeleteAsync(InstallEnvironment); + InstallEnvironment = null; + + Logger.LogTrace("Pre-existing install environment destroyed"); + } + + // Remove pre-existing installation storage + if (InstallStorage != null) + { + Logger.LogTrace("Destroying pre-existing installation storage"); + + await InstallStorageService.DeleteAsync(InstallStorage); + InstallStorage = null; + } + + // Fetch the latest configuration + Logger.LogTrace("Fetching latest configuration"); + + RuntimeConfiguration = await ConfigurationService.GetRuntimeConfigurationAsync(Uuid); + InstallConfiguration = await ConfigurationService.GetInstallConfigurationAsync(Uuid); + + // Ensure runtime storage + if (RuntimeStorage == null) + { + Logger.LogTrace("Creating runtime storage"); + RuntimeStorage = await RuntimeStorageService.CreateAsync(Uuid, RuntimeConfiguration); + } + else + { + Logger.LogTrace("Updating runtime storage"); + await RuntimeStorageService.UpdateAsync(RuntimeStorage, RuntimeConfiguration); + } + + // Create installation storage + Logger.LogTrace("Creating installation storage"); + InstallStorage = await InstallStorageService.CreateAsync(Uuid, RuntimeConfiguration, InstallConfiguration); + + // Write install script + var installStoragePath = await InstallStorage.GetHostPathAsync(); + + await File.WriteAllTextAsync( + Path.Combine(installStoragePath, "install.sh"), + InstallConfiguration.Script + ); + + // Create env + Logger.LogTrace("Creating install environment"); + + InstallEnvironment = await InstallEnvironmentService.CreateAsync( + Uuid, + RuntimeConfiguration, + InstallConfiguration, + InstallStorage, + RuntimeStorage + ); + + // Add event handlers + Logger.LogTrace("Attaching to install environment"); + + InstallEnvironment.Console.OnOutput += OnConsoleMessageAsync; + InstallEnvironment.Statistics.OnStatisticsReceived += OnStatisticsReceivedAsync; + InstallEnvironment.OnExited += OnInstallExitedAsync; + + // Attach console and statistics + await InstallEnvironment.Console.AttachAsync(); + await InstallEnvironment.Statistics.AttachAsync(); + + // Finally start the env + Logger.LogTrace("Starting install environment"); + + await InstallEnvironment.StartAsync(); + + await ChangeStateAsync(ServerState.Installing); + } + finally + { + Lock.Release(); + } + } + + private async Task OnInstallExitedAsync() + { + Logger.LogTrace("Install environment exited, checking result and cleaning up"); + + await Lock.WaitAsync(); + + try + { + // TODO: Handle crash + + if (InstallEnvironment == null) + throw new InvalidOperationException("Install environment is not set"); + + // Make sure no event handler is there + InstallEnvironment.Console.OnOutput -= OnConsoleMessageAsync; + InstallEnvironment.Statistics.OnStatisticsReceived -= OnStatisticsReceivedAsync; + InstallEnvironment.OnExited -= OnInstallExitedAsync; + + // Remove env + await InstallEnvironmentService.DeleteAsync(InstallEnvironment); + InstallEnvironment = null; + + Logger.LogTrace("Install environment cleaned up"); + + if(InstallStorage == null) + throw new InvalidOperationException("Install storage is not set"); + + Logger.LogTrace("Cleaned up install storage"); + await InstallStorageService.DeleteAsync(InstallStorage); + InstallStorage = null; + } + finally + { + Lock.Release(); + } + + await ChangeStateAsync(ServerState.Offline); + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Server.Power.cs b/MoonlightServers.Daemon/ServerSystem/Server.Power.cs new file mode 100644 index 0000000..3b5d26c --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Server.Power.cs @@ -0,0 +1,163 @@ +namespace MoonlightServers.Daemon.ServerSystem; + +public partial class Server +{ + public async Task StartAsync() + { + await Lock.WaitAsync(); + + try + { + if (State != ServerState.Offline) + throw new InvalidOperationException("Server is not offline"); + + // Check for any pre-existing runtime environment, if we don't have a reference already + RuntimeEnvironment ??= await RuntimeEnvironmentService.FindAsync(Uuid); + RuntimeStorage ??= await RuntimeStorageService.FindAsync(Uuid); + + // Remove any pre-existing environment + if (RuntimeEnvironment != null) + { + Logger.LogTrace("Destroying pre-existing runtime environment"); + + if (await RuntimeEnvironment.IsRunningAsync()) + { + Logger.LogTrace("Pre-existing runtime environment is still running, killing it"); + await RuntimeEnvironment.KillAsync(); + } + + // Make sure no event handler is there anymore + RuntimeEnvironment.Console.OnOutput -= OnConsoleMessageAsync; + RuntimeEnvironment.Statistics.OnStatisticsReceived -= OnStatisticsReceivedAsync; + RuntimeEnvironment.OnExited -= OnRuntimeExitedAsync; + + // Finally remove it + await RuntimeEnvironmentService.DeleteAsync(RuntimeEnvironment); + RuntimeEnvironment = null; + + Logger.LogTrace("Pre-existing runtime environment destroyed"); + } + + // Fetch the latest config + Logger.LogTrace("Fetching latest configuration"); + RuntimeConfiguration = await ConfigurationService.GetRuntimeConfigurationAsync(Uuid); + + // Ensure runtime storage + if (RuntimeStorage == null) + { + Logger.LogTrace("Creating runtime storage"); + RuntimeStorage = await RuntimeStorageService.CreateAsync(Uuid, RuntimeConfiguration); + } + else + { + Logger.LogTrace("Updating runtime storage"); + await RuntimeStorageService.UpdateAsync(RuntimeStorage, RuntimeConfiguration); + } + + // Create the environment + Logger.LogTrace("Creating runtime environment"); + + RuntimeEnvironment = await RuntimeEnvironmentService.CreateAsync(Uuid, RuntimeConfiguration, RuntimeStorage); + + // Set event handlers + Logger.LogTrace("Attaching to runtime environment"); + + RuntimeEnvironment.Console.OnOutput += OnConsoleMessageAsync; + RuntimeEnvironment.Statistics.OnStatisticsReceived += OnStatisticsReceivedAsync; + RuntimeEnvironment.OnExited += OnRuntimeExitedAsync; + + // Attach console & statistics + await RuntimeEnvironment.Console.AttachAsync(); + await RuntimeEnvironment.Statistics.AttachAsync(); + + // Start up + Logger.LogTrace("Starting runtime environment"); + + await RuntimeEnvironment.StartAsync(); + + await ChangeStateAsync(ServerState.Starting); + } + finally + { + Lock.Release(); + } + } + + public async Task StopAsync() + { + await Lock.WaitAsync(); + + try + { + if (State is not (ServerState.Starting or ServerState.Online)) + throw new InvalidOperationException("Server is not starting or online"); + + if (RuntimeEnvironment == null) + throw new InvalidOperationException("Runtime environment is not set"); + + Logger.LogTrace("Sending stop command to runtime environment"); + await RuntimeEnvironment.Console.WriteInputAsync("stop\n\r"); + + await ChangeStateAsync(ServerState.Stopping); + } + finally + { + Lock.Release(); + } + } + + public async Task KillAsync() + { + await Lock.WaitAsync(); + + try + { + if (State is not (ServerState.Starting or ServerState.Online or ServerState.Stopping)) + throw new InvalidOperationException("Server is not starting, stopping or online"); + + if (RuntimeEnvironment == null) + throw new InvalidOperationException("Runtime environment is not set"); + + Logger.LogTrace("Killing runtime environment"); + await RuntimeEnvironment.KillAsync(); + + await ChangeStateAsync(ServerState.Stopping); + } + finally + { + Lock.Release(); + } + } + + private async Task OnRuntimeExitedAsync() + { + Logger.LogTrace("Runtime environment exited, checking result and cleaning up"); + + await Lock.WaitAsync(); + + try + { + // TODO: Handle crash + + if (RuntimeEnvironment == null) + throw new InvalidOperationException("Runtime environment is not set"); + + // Make sure no event handler is there anymore + RuntimeEnvironment.Console.OnOutput -= OnConsoleMessageAsync; + RuntimeEnvironment.Statistics.OnStatisticsReceived -= OnStatisticsReceivedAsync; + RuntimeEnvironment.OnExited -= OnRuntimeExitedAsync; + + // Finally remove it + await RuntimeEnvironmentService.DeleteAsync(RuntimeEnvironment); + RuntimeEnvironment = null; + + Logger.LogTrace("Runtime environment cleaned up"); + } + finally + { + Lock.Release(); + } + + await ChangeStateAsync(ServerState.Offline); + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Server.Restore.cs b/MoonlightServers.Daemon/ServerSystem/Server.Restore.cs new file mode 100644 index 0000000..3bc3eef --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/Server.Restore.cs @@ -0,0 +1,69 @@ +namespace MoonlightServers.Daemon.ServerSystem; + +public partial class Server +{ + // Attempts to reattach to any running install or runtime environment that survived a daemon restart. + // Returns the appropriate state based on what was found, or Offline if nothing is running. + private async Task RestoreAsync() + { + // Install + Logger.LogTrace("Checking for existing install environment"); + + InstallEnvironment = await InstallEnvironmentService.FindAsync(Uuid); + InstallStorage = await InstallStorageService.FindAsync(Uuid); + + if (InstallEnvironment != null) + { + var isRunning = await InstallEnvironment.IsRunningAsync(); + + if (isRunning) + { + Logger.LogTrace("Found running install environment, reattaching"); + + InstallEnvironment.Console.OnOutput += OnConsoleMessageAsync; + InstallEnvironment.Statistics.OnStatisticsReceived += OnStatisticsReceivedAsync; + InstallEnvironment.OnExited += OnInstallExitedAsync; + + await InstallEnvironment.Console.AttachAsync(); + await InstallEnvironment.Statistics.AttachAsync(); + + return ServerState.Installing; + } + + Logger.LogTrace("Install environment exists but is not running, ignoring"); + } + + // Runtime + Logger.LogTrace("Checking for existing runtime environment"); + + RuntimeEnvironment = await RuntimeEnvironmentService.FindAsync(Uuid); + RuntimeStorage = await RuntimeStorageService.FindAsync(Uuid); + + if (RuntimeEnvironment != null) + { + var isRunning = await RuntimeEnvironment.IsRunningAsync(); + + if (isRunning) + { + Logger.LogTrace("Found running runtime environment, reattaching"); + + RuntimeEnvironment.Console.OnOutput += OnConsoleMessageAsync; + RuntimeEnvironment.Statistics.OnStatisticsReceived += OnStatisticsReceivedAsync; + RuntimeEnvironment.OnExited += OnRuntimeExitedAsync; + + await RuntimeEnvironment.Console.AttachAsync(); + await RuntimeEnvironment.Statistics.AttachAsync(); + + // TODO: Use string online check here + + return ServerState.Online; + } + + Logger.LogTrace("Runtime environment exists but is not running, ignoring"); + } + + Logger.LogTrace("No running environments found"); + + return ServerState.Offline; + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/Server.cs b/MoonlightServers.Daemon/ServerSystem/Server.cs index c0664da..87b7ec8 100644 --- a/MoonlightServers.Daemon/ServerSystem/Server.cs +++ b/MoonlightServers.Daemon/ServerSystem/Server.cs @@ -1,161 +1,111 @@ -using MoonlightServers.Daemon.ServerSystem.Enums; -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; -using Stateless; +using MoonlightServers.Daemon.Models; +using MoonlightServers.Daemon.ServerSystem.Abstractions; +using MoonlightServers.Daemon.Services; namespace MoonlightServers.Daemon.ServerSystem; public partial class Server : IAsyncDisposable { - public int Identifier => InnerContext.Identifier; - public ServerContext Context => InnerContext; + public ServerState State { get; private set; } - public IConsole Console { get; } - public IFileSystem RuntimeFileSystem { get; } - public IFileSystem InstallationFileSystem { get; } - public IInstallation Installation { get; } - public IOnlineDetector OnlineDetector { get; } - public IReporter Reporter { get; } - public IRestorer Restorer { get; } - public IRuntime Runtime { get; } - public IStatistics Statistics { get; } - public StateMachine StateMachine { get; private set; } + private IRuntimeEnvironment? RuntimeEnvironment; + private RuntimeConfiguration RuntimeConfiguration; + private IRuntimeStorage? RuntimeStorage; - private readonly IServerStateHandler[] Handlers; + private IInstallEnvironment? InstallEnvironment; + private InstallConfiguration InstallConfiguration; + private IInstallStorage? InstallStorage; - private readonly IServerComponent[] AllComponents; - private readonly ServerContext InnerContext; + private readonly IRuntimeEnvironmentService RuntimeEnvironmentService; + private readonly IInstallEnvironmentService InstallEnvironmentService; + private readonly IRuntimeStorageService RuntimeStorageService; + private readonly IInstallStorageService InstallStorageService; + + private readonly ServerConfigurationService ConfigurationService; + private readonly string Uuid; private readonly ILogger Logger; + private readonly SemaphoreSlim Lock = new(1, 1); + public Server( - ILogger logger, - ServerContext context, - IConsole console, - IFileSystem runtimeFileSystem, - IFileSystem installationFileSystem, - IInstallation installation, - IOnlineDetector onlineDetector, - IReporter reporter, - IRestorer restorer, - IRuntime runtime, - IStatistics statistics, - IEnumerable handlers, - IEnumerable additionalComponents + string uuid, + IRuntimeEnvironmentService runtimeEnvironmentService, + IInstallEnvironmentService installEnvironmentService, + IRuntimeStorageService runtimeStorageService, + IInstallStorageService installStorageService, + ServerConfigurationService configurationService, + ILogger logger ) { + Uuid = uuid; + RuntimeEnvironmentService = runtimeEnvironmentService; + InstallEnvironmentService = installEnvironmentService; + RuntimeStorageService = runtimeStorageService; + InstallStorageService = installStorageService; + ConfigurationService = configurationService; Logger = logger; - InnerContext = context; - Console = console; - RuntimeFileSystem = runtimeFileSystem; - InstallationFileSystem = installationFileSystem; - Installation = installation; - OnlineDetector = onlineDetector; - Reporter = reporter; - Restorer = restorer; - Runtime = runtime; - Statistics = statistics; - - IEnumerable defaultComponents = - [ - Console, RuntimeFileSystem, InstallationFileSystem, Installation, OnlineDetector, Reporter, Restorer, - Runtime, Statistics - ]; - - AllComponents = defaultComponents.Concat(additionalComponents).ToArray(); - - Handlers = handlers.ToArray(); - } - - private void ConfigureStateMachine(ServerState initialState) - { - StateMachine = new StateMachine( - initialState, FiringMode.Queued - ); - - StateMachine.Configure(ServerState.Offline) - .Permit(ServerTrigger.Start, ServerState.Starting) - .Permit(ServerTrigger.Install, ServerState.Installing) - .PermitReentry(ServerTrigger.Fail); - - StateMachine.Configure(ServerState.Starting) - .Permit(ServerTrigger.DetectOnline, ServerState.Online) - .Permit(ServerTrigger.Fail, ServerState.Offline) - .Permit(ServerTrigger.Exited, ServerState.Offline) - .Permit(ServerTrigger.Stop, ServerState.Stopping) - .Permit(ServerTrigger.Kill, ServerState.Stopping); - - StateMachine.Configure(ServerState.Online) - .Permit(ServerTrigger.Stop, ServerState.Stopping) - .Permit(ServerTrigger.Kill, ServerState.Stopping) - .Permit(ServerTrigger.Exited, ServerState.Offline); - - StateMachine.Configure(ServerState.Stopping) - .PermitReentry(ServerTrigger.Fail) - .PermitReentry(ServerTrigger.Kill) - .Permit(ServerTrigger.Exited, ServerState.Offline); - - StateMachine.Configure(ServerState.Installing) - .Permit(ServerTrigger.Fail, ServerState.Offline) // TODO: Add kill - .Permit(ServerTrigger.Exited, ServerState.Offline); - } - - private void ConfigureStateMachineEvents() - { - // Configure the calling of the handlers - StateMachine.OnTransitionedAsync(async transition => - { - var hasFailed = false; - - foreach (var handler in Handlers) - { - try - { - await handler.ExecuteAsync(transition); - } - catch (Exception e) - { - Logger.LogError( - e, - "Handler {name} has thrown an unexpected exception", - handler.GetType().FullName - ); - - hasFailed = true; - break; - } - } - - if(!hasFailed) - return; // Everything went fine, we can exit now - - // Something has failed, lets check if we can handle the error - // via a fail trigger - - if(!StateMachine.CanFire(ServerTrigger.Fail)) - return; - - // Trigger the fail so the server gets a chance to handle the error softly - await StateMachine.FireAsync(ServerTrigger.Fail); - }); } public async Task InitializeAsync() { - foreach (var component in AllComponents) - await component.InitializeAsync(); + Logger.LogTrace("Initializing"); - var restoredState = ServerState.Offline; + await Lock.WaitAsync(); - ConfigureStateMachine(restoredState); - ConfigureStateMachineEvents(); + try + { + // Restore state + State = await RestoreAsync(); + + Logger.LogTrace("Initialization complete, restored to state {State}", State); + } + finally + { + Lock.Release(); + } + } + + private async Task OnConsoleMessageAsync(string message) + { + Console.WriteLine($"Console: {message}"); + } + + private async Task OnStatisticsReceivedAsync(ServerStatistics statistics) + { + } + + private Task ChangeStateAsync(ServerState newState) + { + Logger.LogTrace("State changed from {OldState} to {NewState}", State, newState); + + State = newState; + return Task.CompletedTask; } public async ValueTask DisposeAsync() { - foreach (var handler in Handlers) - await handler.DisposeAsync(); - - foreach (var component in AllComponents) - await component.DisposeAsync(); + Logger.LogTrace("Disposing"); + + if (RuntimeEnvironment != null) + { + Logger.LogTrace("Detaching and disposing runtime environment"); + + RuntimeEnvironment.Console.OnOutput -= OnConsoleMessageAsync; + RuntimeEnvironment.Statistics.OnStatisticsReceived -= OnStatisticsReceivedAsync; + RuntimeEnvironment.OnExited -= OnRuntimeExitedAsync; + + await RuntimeEnvironment.DisposeAsync(); + } + + if (InstallEnvironment != null) + { + Logger.LogTrace("Detaching and disposing install environment"); + + InstallEnvironment.Console.OnOutput -= OnConsoleMessageAsync; + InstallEnvironment.Statistics.OnStatisticsReceived -= OnStatisticsReceivedAsync; + InstallEnvironment.OnExited -= OnInstallExitedAsync; + + await InstallEnvironment.DisposeAsync(); + } } } \ No newline at end of file diff --git a/MoonlightServers.Daemon/ServerSystem/ServerFactory.cs b/MoonlightServers.Daemon/ServerSystem/ServerFactory.cs index 53f8cb2..e061b4c 100644 --- a/MoonlightServers.Daemon/ServerSystem/ServerFactory.cs +++ b/MoonlightServers.Daemon/ServerSystem/ServerFactory.cs @@ -1,97 +1,46 @@ -using MoonlightServers.Daemon.Models.Cache; -using MoonlightServers.Daemon.ServerSystem.Docker; -using MoonlightServers.Daemon.ServerSystem.FileSystems; -using MoonlightServers.Daemon.ServerSystem.Handlers; -using MoonlightServers.Daemon.ServerSystem.Implementations; -using MoonlightServers.Daemon.ServerSystem.Interfaces; -using MoonlightServers.Daemon.ServerSystem.Models; +using MoonlightServers.Daemon.ServerSystem.Abstractions; +using MoonlightServers.Daemon.Services; namespace MoonlightServers.Daemon.ServerSystem; public class ServerFactory { - private readonly IServiceProvider ServiceProvider; + private readonly IRuntimeEnvironmentService RuntimeEnvironmentService; + private readonly IInstallEnvironmentService InstallEnvironmentService; + private readonly IRuntimeStorageService RuntimeStorageService; + private readonly IInstallStorageService InstallStorageService; + private readonly ServerConfigurationService ConfigurationService; + private readonly ILoggerFactory LoggerFactory; - public ServerFactory(IServiceProvider serviceProvider) + public ServerFactory( + IRuntimeEnvironmentService runtimeEnvironmentService, + IInstallEnvironmentService installEnvironmentService, + IRuntimeStorageService runtimeStorageService, + IInstallStorageService installStorageService, + ServerConfigurationService configurationService, + ILoggerFactory loggerFactory + ) { - ServiceProvider = serviceProvider; + RuntimeEnvironmentService = runtimeEnvironmentService; + InstallEnvironmentService = installEnvironmentService; + RuntimeStorageService = runtimeStorageService; + InstallStorageService = installStorageService; + ConfigurationService = configurationService; + LoggerFactory = loggerFactory; } - public async Task CreateAsync(ServerConfiguration configuration) + public async Task CreateAsync(string uuid) { - var scope = ServiceProvider.CreateAsyncScope(); + var logger = LoggerFactory.CreateLogger($"MoonlightServers.Daemon.ServerSystem.Server({uuid})"); - var loggerFactory = scope.ServiceProvider.GetRequiredService(); - var logger = loggerFactory.CreateLogger($"Servers.Instance.{configuration.Id}.{nameof(Server)}"); - - var context = scope.ServiceProvider.GetRequiredService(); - - context.Identifier = configuration.Id; - context.Configuration = configuration; - context.ServiceScope = scope; - context.Logger = logger; - - // Define all required components - - IConsole console; - IFileSystem runtimeFs; - IFileSystem installFs; - IInstallation installation; - IOnlineDetector onlineDetector; - IReporter reporter; - IRestorer restorer; - IRuntime runtime; - IStatistics statistics; - - // Resolve the components - - console = ActivatorUtilities.CreateInstance(scope.ServiceProvider); - reporter = ActivatorUtilities.CreateInstance(scope.ServiceProvider); - runtimeFs = ActivatorUtilities.CreateInstance(scope.ServiceProvider); - installFs = ActivatorUtilities.CreateInstance(scope.ServiceProvider); - installation = ActivatorUtilities.CreateInstance(scope.ServiceProvider); - onlineDetector = ActivatorUtilities.CreateInstance(scope.ServiceProvider); - restorer = ActivatorUtilities.CreateInstance(scope.ServiceProvider); - runtime = ActivatorUtilities.CreateInstance(scope.ServiceProvider); - statistics = ActivatorUtilities.CreateInstance(scope.ServiceProvider); - - // Resolve handlers - var handlers = new List(); - - handlers.Add(ActivatorUtilities.CreateInstance(scope.ServiceProvider)); - handlers.Add(ActivatorUtilities.CreateInstance(scope.ServiceProvider)); - handlers.Add(ActivatorUtilities.CreateInstance(scope.ServiceProvider)); - handlers.Add(ActivatorUtilities.CreateInstance(scope.ServiceProvider)); - handlers.Add(ActivatorUtilities.CreateInstance(scope.ServiceProvider)); - - // Resolve additional components - var components = new List(); - - components.Add(ActivatorUtilities.CreateInstance(scope.ServiceProvider)); - - // TODO: Add a plugin hook for dynamically resolving components and checking if any is unset - - // Resolve server from di - var server = new Server( - logger, - context, - // Now all components - console, - runtimeFs, - installFs, - installation, - onlineDetector, - reporter, - restorer, - runtime, - statistics, - // And now all the handlers - handlers, - components + return new Server( + uuid, + RuntimeEnvironmentService, + InstallEnvironmentService, + RuntimeStorageService, + InstallStorageService, + ConfigurationService, + logger ); - - context.Server = server; - - return server; } } \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/Enums/ServerState.cs b/MoonlightServers.Daemon/ServerSystem/ServerState.cs similarity index 73% rename from MoonlightServers.DaemonShared/Enums/ServerState.cs rename to MoonlightServers.Daemon/ServerSystem/ServerState.cs index ee77f92..0105dfd 100644 --- a/MoonlightServers.DaemonShared/Enums/ServerState.cs +++ b/MoonlightServers.Daemon/ServerSystem/ServerState.cs @@ -1,4 +1,4 @@ -namespace MoonlightServers.DaemonShared.Enums; +namespace MoonlightServers.Daemon.ServerSystem; public enum ServerState { diff --git a/MoonlightServers.Daemon/ServerSystem/ServerStatistics.cs b/MoonlightServers.Daemon/ServerSystem/ServerStatistics.cs new file mode 100644 index 0000000..a513812 --- /dev/null +++ b/MoonlightServers.Daemon/ServerSystem/ServerStatistics.cs @@ -0,0 +1,3 @@ +namespace MoonlightServers.Daemon.ServerSystem; + +public record ServerStatistics(); \ No newline at end of file diff --git a/MoonlightServers.Daemon/Services/DockerEventService.cs b/MoonlightServers.Daemon/Services/DockerEventService.cs deleted file mode 100644 index ac881af..0000000 --- a/MoonlightServers.Daemon/Services/DockerEventService.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Docker.DotNet; -using Docker.DotNet.Models; -using MoonCore.Events; - -namespace MoonlightServers.Daemon.Services; - -public class DockerEventService : BackgroundService -{ - private readonly ILogger Logger; - private readonly DockerClient DockerClient; - - private readonly EventSource ContainerSource = new(); - private readonly EventSource ImageSource = new(); - private readonly EventSource NetworkSource = new(); - - public DockerEventService( - ILogger logger, - DockerClient dockerClient - ) - { - Logger = logger; - DockerClient = dockerClient; - } - - public async ValueTask SubscribeContainerAsync(Func callback) - => await ContainerSource.SubscribeAsync(callback); - - public async ValueTask SubscribeImageAsync(Func callback) - => await ImageSource.SubscribeAsync(callback); - - public async ValueTask SubscribeNetworkAsync(Func callback) - => await NetworkSource.SubscribeAsync(callback); - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - Logger.LogInformation("Starting docker event service"); - - while (!stoppingToken.IsCancellationRequested) - { - try - { - await DockerClient.System.MonitorEventsAsync( - new ContainerEventsParameters(), - new Progress(async message => - { - try - { - switch (message.Type) - { - case "container": - await ContainerSource.InvokeAsync(message); - break; - - case "image": - await ImageSource.InvokeAsync(message); - break; - - case "network": - await NetworkSource.InvokeAsync(message); - break; - } - } - catch (Exception e) - { - Logger.LogError(e, "An error occured while processing docker event"); - } - }), - stoppingToken - ); - } - catch (TaskCanceledException) - { - // ignored - } - catch (Exception e) - { - Logger.LogError(e, "An error occured while listening for docker events: {message}", e.Message); - } - } - - Logger.LogInformation("Stopping docker event service"); - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Services/DockerImageService.cs b/MoonlightServers.Daemon/Services/DockerImageService.cs deleted file mode 100644 index 4fcb6b9..0000000 --- a/MoonlightServers.Daemon/Services/DockerImageService.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Docker.DotNet; -using Docker.DotNet.Models; -using MoonCore.Attributes; -using MoonlightServers.Daemon.Configuration; - -namespace MoonlightServers.Daemon.Services; - -[Singleton] -public class DockerImageService -{ - private readonly DockerClient DockerClient; - private readonly AppConfiguration Configuration; - private readonly ILogger Logger; - - private readonly Dictionary PendingDownloads = new(); - - public DockerImageService( - DockerClient dockerClient, - ILogger logger, - AppConfiguration configuration - ) - { - Configuration = configuration; - DockerClient = dockerClient; - Logger = logger; - } - - public async Task DownloadAsync(string name, Action? onProgressUpdated = null) - { - // If there is already a download for this image occuring, we want to wait for this to complete instead - // of calling docker to download it again - if (PendingDownloads.TryGetValue(name, out var downloadTaskCompletion)) - { - await downloadTaskCompletion.Task; - return; - } - - var tsc = new TaskCompletionSource(); - PendingDownloads.Add(name, tsc); - - try - { - // Figure out if and which credentials to use by checking for the domain - AuthConfig credentials = new(); - - var domain = GetDomainFromDockerImageName(name); - - var configuredCredentials = Configuration.Docker.Credentials.FirstOrDefault(x => - x.Domain.Equals(domain, StringComparison.InvariantCultureIgnoreCase) - ); - - // Apply credentials configuration if specified - if (configuredCredentials != null) - { - credentials.Username = configuredCredentials.Username; - credentials.Password = configuredCredentials.Password; - credentials.Email = configuredCredentials.Email; - } - - // Now we want to pull the image - await DockerClient.Images.CreateImageAsync(new() - { - FromImage = name - }, - credentials, - new Progress(async message => - { - if (message.Progress == null) - return; - - var line = $"[{message.ID}] {message.ProgressMessage}"; - - Logger.LogDebug("{line}", line); - - if (onProgressUpdated != null) - onProgressUpdated.Invoke(line); - }) - ); - - tsc.SetResult(); - PendingDownloads.Remove(name); - } - catch (Exception e) - { - Logger.LogError("An error occured while download image {name}: {e}", name, e); - - tsc.SetException(e); - PendingDownloads.Remove(name); - - throw; - } - } - - private string GetDomainFromDockerImageName(string name) // Method names are my passion ;) - { - var nameParts = name.Split("/"); - - // If it has 1 part -> just the image name (e.g., "ubuntu") - // If it has 2 parts -> usually "user/image" (e.g., "library/ubuntu") - // If it has 3 or more -> assume first part is the registry domain - - if (nameParts.Length >= 3 || - (nameParts.Length >= 2 && nameParts[0].Contains('.') || nameParts[0].Contains(':'))) - return nameParts[0]; // Registry domain is explicitly specified - - return "docker.io"; // Default Docker registry - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Services/DockerInfoService.cs b/MoonlightServers.Daemon/Services/DockerInfoService.cs deleted file mode 100644 index c009f5b..0000000 --- a/MoonlightServers.Daemon/Services/DockerInfoService.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Docker.DotNet; -using MoonCore.Attributes; -using MoonlightServers.Daemon.Helpers; -using MoonlightServers.Daemon.Models.UnsafeDocker; - -namespace MoonlightServers.Daemon.Services; - -[Singleton] -public class DockerInfoService -{ - private readonly DockerClient DockerClient; - private readonly UnsafeDockerClient UnsafeDockerClient; - - public DockerInfoService(DockerClient dockerClient, UnsafeDockerClient unsafeDockerClient) - { - DockerClient = dockerClient; - UnsafeDockerClient = unsafeDockerClient; - } - - public async Task GetDockerVersionAsync() - { - var version = await DockerClient.System.GetVersionAsync(); - - return $"{version.Version} commit {version.GitCommit} ({version.APIVersion})"; - } - - public async Task GetDataUsageAsync() - { - var response = await UnsafeDockerClient.GetDataUsageAsync(); - - var report = new UsageDataReport() - { - Containers = new UsageData() - { - Used = response.Containers.Sum(x => x.SizeRw), - Reclaimable = 0 - }, - Images = new UsageData() - { - Used = response.Images.Sum(x => x.Size), - Reclaimable = response.Images.Where(x => x.Containers == 0).Sum(x => x.Size) - }, - BuildCache = new UsageData() - { - Used = response.BuildCache.Sum(x => x.Size), - Reclaimable = response.BuildCache.Where(x => !x.InUse).Sum(x => x.Size) - } - }; - - return report; - } -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Services/RemoteService.cs b/MoonlightServers.Daemon/Services/RemoteService.cs deleted file mode 100644 index 69282dc..0000000 --- a/MoonlightServers.Daemon/Services/RemoteService.cs +++ /dev/null @@ -1,67 +0,0 @@ -using MoonCore.Attributes; -using MoonCore.Helpers; -using MoonCore.Models; -using MoonlightServers.Daemon.Configuration; -using MoonlightServers.DaemonShared.PanelSide.Http.Responses; - -namespace MoonlightServers.Daemon.Services; - -[Singleton] -public class RemoteService -{ - private readonly HttpApiClient ApiClient; - - public RemoteService(AppConfiguration configuration) - { - ApiClient = CreateHttpClient(configuration); - } - - public async Task GetStatusAsync() - { - await ApiClient.Get("api/remote/servers/node/trip"); - } - - public async Task> GetServersAsync(int startIndex, int count) - { - return await ApiClient.GetJson>( - $"api/remote/servers?startIndex={startIndex}&count={count}" - ); - } - - public async Task GetServerAsync(int serverId) - { - return await ApiClient.GetJson( - $"api/remote/servers/{serverId}" - ); - } - - public async Task GetServerInstallationAsync(int serverId) - { - return await ApiClient.GetJson( - $"api/remote/servers/{serverId}/install" - ); - } - - #region Helpers - - private HttpApiClient CreateHttpClient(AppConfiguration configuration) - { - var formattedUrl = configuration.Remote.Url.EndsWith('/') - ? configuration.Remote.Url - : configuration.Remote.Url + "/"; - - var httpClient = new HttpClient() - { - BaseAddress = new Uri(formattedUrl) - }; - - httpClient.DefaultRequestHeaders.Add( - "Authorization", - $"Bearer {configuration.Security.TokenId}.{configuration.Security.Token}" - ); - - return new HttpApiClient(httpClient); - } - - #endregion -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Services/ServerConfigurationService.cs b/MoonlightServers.Daemon/Services/ServerConfigurationService.cs new file mode 100644 index 0000000..02af6c5 --- /dev/null +++ b/MoonlightServers.Daemon/Services/ServerConfigurationService.cs @@ -0,0 +1,50 @@ +using MoonlightServers.Daemon.Models; + +namespace MoonlightServers.Daemon.Services; + +public class ServerConfigurationService +{ + public async Task GetRuntimeConfigurationAsync(string uuid) + { + return new RuntimeConfiguration( + new RuntimeLimitsConfig(400, null, 4096, null), + new RuntimeStorageConfig("local", [], 10240), + new RuntimeTemplateConfig( + "ghcr.io/pterodactyl/yolks:java_21", + "java -jar -Xmx{{SERVER_MEMORY}}M server.jar", + "stop", + ["Done"] + ), + new RuntimeNetworkConfig( + [], + null, + null, + new RuntimePortConfig("0.0.0.0", 25565), + [ + new RuntimePortConfig("0.0.0.0", 25565) + ] + ), + new RuntimeEnvironmentConfig( + new Dictionary() + { + { "testy", "ytset" } + }, + new Dictionary() + { + { "SERVER_MEMORY", "4096" }, + {"MINECRAFT_VERSION", "latest"}, + {"SERVER_JARFILE", "server.jar"} + } + ) + ); + } + + public async Task GetInstallConfigurationAsync(string uuid) + { + return new InstallConfiguration( + "bash", + "installer", + await File.ReadAllTextAsync("/home/chiara/Documents/daemonScripts/install.sh") + ); + } +} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Services/ServerService.cs b/MoonlightServers.Daemon/Services/ServerService.cs deleted file mode 100644 index d3fe831..0000000 --- a/MoonlightServers.Daemon/Services/ServerService.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System.Collections.Concurrent; -using MoonCore.Models; -using MoonlightServers.Daemon.Mappers; -using MoonlightServers.Daemon.Models.Cache; -using MoonlightServers.Daemon.ServerSystem; -using MoonlightServers.DaemonShared.PanelSide.Http.Responses; - -namespace MoonlightServers.Daemon.Services; - -public class ServerService : IHostedLifecycleService -{ - private readonly ConcurrentDictionary Servers = new(); - private readonly ILogger Logger; - private readonly ServerFactory ServerFactory; - private readonly RemoteService RemoteService; - private readonly ServerConfigurationMapper ConfigurationMapper; - - public ServerService( - ILogger logger, - ServerFactory serverFactory, - RemoteService remoteService, - ServerConfigurationMapper configurationMapper - ) - { - Logger = logger; - ServerFactory = serverFactory; - RemoteService = remoteService; - ConfigurationMapper = configurationMapper; - } - - public Server? GetById(int id) - => Servers.GetValueOrDefault(id); - - public async Task InitializeAsync(ServerConfiguration configuration) - { - var existingServer = Servers.GetValueOrDefault(configuration.Id); - - if (existingServer != null) - { - existingServer.Context.Configuration = configuration; - // TODO: Implement a way for components to get notified - } - else - { - var server = await ServerFactory.CreateAsync(configuration); - Servers[configuration.Id] = server; - - await server.InitializeAsync(); - } - } - - public async Task InitializeByIdAsync(int id) - { - var serverData = await RemoteService.GetServerAsync(id); - var config = ConfigurationMapper.FromServerDataResponse(serverData); - - await InitializeAsync(config); - } - - private async Task InitializeAllAsync() - { - Logger.LogDebug("Initialing servers from panel"); - - var servers = await CountedData.LoadAllAsync(async (startIndex, count) => - await RemoteService.GetServersAsync(startIndex, count) - ); - - foreach (var serverData in servers) - { - try - { - var config = ConfigurationMapper.FromServerDataResponse(serverData); - - await InitializeAsync(config); - } - catch (Exception e) - { - Logger.LogError(e, "An error occured while initializing server {id}", serverData.Id); - } - } - } - - #region Lifetime handlers - - public Task StartAsync(CancellationToken cancellationToken) - => Task.CompletedTask; - - public Task StopAsync(CancellationToken cancellationToken) - => Task.CompletedTask; - - public async Task StartedAsync(CancellationToken cancellationToken) - { - await InitializeAllAsync(); - } - - public Task StartingAsync(CancellationToken cancellationToken) - => Task.CompletedTask; - - public Task StoppedAsync(CancellationToken cancellationToken) - => Task.CompletedTask; - - public async Task StoppingAsync(CancellationToken cancellationToken) - { - Logger.LogDebug("Stopping server service. Disposing servers"); - - foreach (var server in Servers.Values) - await server.DisposeAsync(); - } - - #endregion -} \ No newline at end of file diff --git a/MoonlightServers.Daemon/Startup.cs b/MoonlightServers.Daemon/Startup.cs deleted file mode 100644 index 8507ebb..0000000 --- a/MoonlightServers.Daemon/Startup.cs +++ /dev/null @@ -1,467 +0,0 @@ -using System.Text; -using System.Text.Json; -using Docker.DotNet; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.IdentityModel.Tokens; -using MoonCore.EnvConfiguration; -using MoonCore.Extended.Extensions; -using MoonCore.Extensions; -using MoonCore.Helpers; -using MoonCore.Logging; -using MoonlightServers.Daemon.Configuration; -using MoonlightServers.Daemon.Helpers; -using MoonlightServers.Daemon.Http.Hubs; -using MoonlightServers.Daemon.Mappers; -using MoonlightServers.Daemon.Models.Cache; -using MoonlightServers.Daemon.ServerSystem; -using MoonlightServers.Daemon.ServerSystem.Enums; -using MoonlightServers.Daemon.ServerSystem.Models; -using MoonlightServers.Daemon.Services; - -namespace MoonlightServers.Daemon; - -public class Startup -{ - private string[] Args; - - // Configuration - private AppConfiguration Configuration; - - // Logging - private ILoggerProvider[] LoggerProviders; - private ILoggerFactory LoggerFactory; - private ILogger Logger; - - // WebApplication Stuff - private WebApplication WebApplication; - private WebApplicationBuilder WebApplicationBuilder; - - public async Task RunAsync(string[] args) - { - Args = args; - - await SetupStorageAsync(); - await SetupAppConfigurationAsync(); - await SetupLoggingAsync(); - - await CreateWebApplicationBuilderAsync(); - - await ConfigureKestrelAsync(); - await RegisterAppConfigurationAsync(); - await RegisterLoggingAsync(); - await RegisterBaseAsync(); - await RegisterAuthAsync(); - await RegisterDockerAsync(); - await RegisterServersAsync(); - await RegisterSignalRAsync(); - await RegisterCorsAsync(); - - await BuildWebApplicationAsync(); - - await UseBaseAsync(); - await UseCorsAsync(); - await UseAuthAsync(); - await UseBaseMiddlewareAsync(); - - await MapBaseAsync(); - await MapHubsAsync(); - - Task.Run(async () => - { - try - { - var serverConfig = new ServerConfiguration() - { - Id = 69, - Allocations = - [ - new ServerConfiguration.AllocationConfiguration() - { - IpAddress = "0.0.0.0", - Port = 25565 - } - ], - Cpu = 400, - Disk = 10240, - Memory = 4096, - OnlineDetection = "\\! For help, type ", - StartupCommand = - "java -Xms128M -Xmx{{SERVER_MEMORY}}M -Dterminal.jline=false -Dterminal.ansi=true -jar {{SERVER_JARFILE}}", - DockerImage = "ghcr.io/nexocrew-hq/moonlightdockerimages:java21", - StopCommand = "stop", - Variables = new Dictionary() - { - { "SERVER_JARFILE", "server.jar" }, - { "MINECRAFT_VERSION", "latest" }, - } - }; - - var factory = WebApplication.Services.GetRequiredService(); - - Console.Write("Press enter to create and init server"); - Console.ReadLine(); - - var s = await factory.CreateAsync(serverConfig); - - await s.InitializeAsync(); - - s.StateMachine.OnTransitionCompleted(transition => - { - Console.WriteLine(transition.Destination); - }); - - Console.Write("Press enter to install server"); - Console.ReadLine(); - - await s.StateMachine.FireAsync(ServerTrigger.Install); - - Console.ReadLine(); - - Console.Write("Press enter to start server"); - Console.ReadLine(); - - await s.StateMachine.FireAsync(ServerTrigger.Start); - - Console.ReadLine(); - } - catch (Exception e) - { - Console.WriteLine(e); - } - }); - - await WebApplication.RunAsync(); - } - - private Task SetupStorageAsync() - { - Directory.CreateDirectory("storage"); - Directory.CreateDirectory(Path.Combine("storage", "logs")); - - return Task.CompletedTask; - } - - #region Base - - private Task RegisterBaseAsync() - { - WebApplicationBuilder.Services.AutoAddServices(); - WebApplicationBuilder.Services.AddControllers(); - - WebApplicationBuilder.Services.AddApiExceptionHandler(); - - return Task.CompletedTask; - } - - private Task ConfigureKestrelAsync() - { - WebApplicationBuilder.WebHost.ConfigureKestrel(options => - { - options.Limits.MaxRequestBodySize = - ByteConverter.FromMegaBytes(Configuration.Kestrel.RequestBodySizeLimit).Bytes; - }); - - return Task.CompletedTask; - } - - private Task UseBaseAsync() - { - WebApplication.UseRouting(); - WebApplication.UseExceptionHandler(); - - return Task.CompletedTask; - } - - private Task UseBaseMiddlewareAsync() - { - return Task.CompletedTask; - } - - private Task MapBaseAsync() - { - WebApplication.MapControllers(); - - return Task.CompletedTask; - } - - #endregion - - #region Docker - - private Task RegisterDockerAsync() - { - var dockerClient = new DockerClientConfiguration( - new Uri(Configuration.Docker.Uri) - ).CreateClient(); - - WebApplicationBuilder.Services.AddSingleton(dockerClient); - WebApplicationBuilder.Services.AddScoped(); - - WebApplicationBuilder.Services.AddSingleton(); - WebApplicationBuilder.Services.AddHostedService(sp => sp.GetRequiredService()); - - return Task.CompletedTask; - } - - #endregion - - #region Configurations - - private async Task SetupAppConfigurationAsync() - { - var configurationBuilder = new ConfigurationBuilder(); - - // Ensure configuration file exists - var jsonFilePath = Path.Combine(Directory.GetCurrentDirectory(), "storage", "app.json"); - - if (!File.Exists(jsonFilePath)) - await File.WriteAllTextAsync(jsonFilePath, JsonSerializer.Serialize(new AppConfiguration())); - - configurationBuilder.AddJsonFile( - jsonFilePath - ); - - configurationBuilder.AddEnvironmentVariables(prefix: "MOONLIGHT_", separator: "_"); - - var configurationRoot = configurationBuilder.Build(); - - // Retrieve configuration - Configuration = configurationRoot.Get()!; - } - - private Task RegisterAppConfigurationAsync() - { - WebApplicationBuilder.Services.AddSingleton(Configuration); - - return Task.CompletedTask; - } - - #endregion - - #region Web Application - - private Task CreateWebApplicationBuilderAsync() - { - WebApplicationBuilder = WebApplication.CreateBuilder(Args); - return Task.CompletedTask; - } - - private Task BuildWebApplicationAsync() - { - WebApplication = WebApplicationBuilder.Build(); - return Task.CompletedTask; - } - - #endregion - - #region Logging - - private Task SetupLoggingAsync() - { - LoggerFactory = new LoggerFactory(); - LoggerFactory.AddAnsiConsole(); - - Logger = LoggerFactory.CreateLogger(); - - return Task.CompletedTask; - } - - private async Task RegisterLoggingAsync() - { - // Configure application logging - WebApplicationBuilder.Logging.ClearProviders(); - WebApplicationBuilder.Logging.AddAnsiConsole(); - WebApplicationBuilder.Logging.AddFile( - Path.Combine(Directory.GetCurrentDirectory(), "storage", "logs", "MoonlightServer.Daemon.log") - ); - - // Logging levels - var logConfigPath = Path.Combine("storage", "logConfig.json"); - - // Ensure logging config, add a default one is missing - if (!File.Exists(logConfigPath)) - { - var defaultLogLevels = new Dictionary - { - { "Default", "Information" }, - { "Microsoft.AspNetCore", "Warning" }, - { "System.Net.Http.HttpClient", "Warning" } - }; - - var json = JsonSerializer.Serialize(defaultLogLevels); - await File.WriteAllTextAsync(logConfigPath, json); - } - - var logLevels = JsonSerializer.Deserialize>( - await File.ReadAllTextAsync(logConfigPath) - )!; - - // Configure logging configuration - foreach (var logLevel in logLevels) - WebApplicationBuilder.Logging.AddFilter(logLevel.Key, Enum.Parse(logLevel.Value)); - - // Mute exception handler middleware - // https://github.com/dotnet/aspnetcore/issues/19740 - WebApplicationBuilder.Logging.AddFilter( - "Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware", - LogLevel.Critical - ); - - WebApplicationBuilder.Logging.AddFilter( - "Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware", - LogLevel.Critical - ); - } - - #endregion - - #region Servers - - private Task RegisterServersAsync() - { - WebApplicationBuilder.Services.AddScoped(); - WebApplicationBuilder.Services.AddSingleton(); - WebApplicationBuilder.Services.AddSingleton(); - - WebApplicationBuilder.Services.AddSingleton(); - WebApplicationBuilder.Services.AddHostedService(sp => sp.GetRequiredService()); - - return Task.CompletedTask; - } - - private Task UseServersAsync() - { - return Task.CompletedTask; - } - - #endregion - - #region Hubs - - private Task RegisterSignalRAsync() - { - WebApplicationBuilder.Services.AddSignalR(); - return Task.CompletedTask; - } - - private Task MapHubsAsync() - { - WebApplication.MapHub("api/servers/ws", options => - { - options.AllowStatefulReconnects = false; - options.CloseOnAuthenticationExpiration = true; - }); - - return Task.CompletedTask; - } - - #endregion - - #region Cors - - private Task RegisterCorsAsync() - { - //TODO: IMPORTANT: CHANGE !!! - WebApplicationBuilder.Services.AddCors(x => - x.AddDefaultPolicy(builder => builder - .SetIsOriginAllowed(_ => true) - .AllowAnyMethod() - .AllowAnyHeader() - .AllowCredentials() - ) - ); - - return Task.CompletedTask; - } - - private Task UseCorsAsync() - { - WebApplication.UseCors(); - return Task.CompletedTask; - } - - #endregion - - #region Authentication - - private Task RegisterAuthAsync() - { - WebApplicationBuilder.Services - .AddAuthentication("token") - .AddScheme("token", - options => { options.Token = Configuration.Security.Token; }) - .AddJwtBearer("accessToken", options => - { - options.TokenValidationParameters = new TokenValidationParameters() - { - ClockSkew = TimeSpan.Zero, - ValidateAudience = true, - ValidateIssuer = false, - ValidateLifetime = true, - ValidateIssuerSigningKey = true, - ValidateActor = false, - IssuerSigningKeys = - [ - new SymmetricSecurityKey( - Encoding.UTF8.GetBytes(Configuration.Security.Token) - ) - ], - AudienceValidator = (audiences, _, _) - => audiences.Contains(Configuration.Security.TokenId) - }; - - // Some requests like the signal r websockets or the upload and download endpoints require the user to provide - // the access token via the query instead of the header field due to client limitations. To reuse the authentication - // on all these requests as we do for all other endpoints which require an access token, we are defining - // the custom behaviour to load the access token from the query for these specific endpoints below - options.Events = new JwtBearerEvents() - { - OnMessageReceived = context => - { - if ( - !context.HttpContext.Request.Path.StartsWithSegments("/api/servers/ws") && - !context.HttpContext.Request.Path.StartsWithSegments("/api/servers/upload") && - !context.HttpContext.Request.Path.StartsWithSegments("/api/servers/download") - ) - { - return Task.CompletedTask; - } - - var accessToken = context.Request.Query["access_token"]; - - if (string.IsNullOrEmpty(accessToken)) - return Task.CompletedTask; - - context.Token = accessToken; - - return Task.CompletedTask; - } - }; - }); - - WebApplicationBuilder.Services.AddAuthorization(options => - { - // We are defining the access token policies here. Because the same jwt secret is used by the panel - // to generate jwt access tokens for all sorts of daemon related stuff we need to separate - // the type of access token using the type parameter provided in the claims. - - options.AddPolicy("serverWebsocket", builder => { builder.RequireClaim("type", "websocket"); }); - - options.AddPolicy("serverUpload", builder => { builder.RequireClaim("type", "upload"); }); - - options.AddPolicy("serverDownload", builder => { builder.RequireClaim("type", "download"); }); - }); - - return Task.CompletedTask; - } - - private Task UseAuthAsync() - { - WebApplication.UseAuthentication(); - WebApplication.UseAuthorization(); - - return Task.CompletedTask; - } - - #endregion -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/DaemonSide/Http/Requests/ServerCommandRequest.cs b/MoonlightServers.DaemonShared/DaemonSide/Http/Requests/ServerCommandRequest.cs deleted file mode 100644 index 22f270b..0000000 --- a/MoonlightServers.DaemonShared/DaemonSide/Http/Requests/ServerCommandRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace MoonlightServers.DaemonShared.DaemonSide.Http.Requests; - -public class ServerCommandRequest -{ - [Required(ErrorMessage = "The command is required")] - public string Command { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/DaemonSide/Http/Requests/ServerFilesCompressRequest.cs b/MoonlightServers.DaemonShared/DaemonSide/Http/Requests/ServerFilesCompressRequest.cs deleted file mode 100644 index 887eb09..0000000 --- a/MoonlightServers.DaemonShared/DaemonSide/Http/Requests/ServerFilesCompressRequest.cs +++ /dev/null @@ -1,10 +0,0 @@ -using MoonlightServers.DaemonShared.Enums; - -namespace MoonlightServers.DaemonShared.DaemonSide.Http.Requests; - -public class ServerFilesCompressRequest -{ - public string Destination { get; set; } - public string[] Items { get; set; } - public CompressType Type { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/DaemonSide/Http/Requests/ServerFilesDecompressRequest.cs b/MoonlightServers.DaemonShared/DaemonSide/Http/Requests/ServerFilesDecompressRequest.cs deleted file mode 100644 index d6cb544..0000000 --- a/MoonlightServers.DaemonShared/DaemonSide/Http/Requests/ServerFilesDecompressRequest.cs +++ /dev/null @@ -1,10 +0,0 @@ -using MoonlightServers.DaemonShared.Enums; - -namespace MoonlightServers.DaemonShared.DaemonSide.Http.Requests; - -public class ServerFilesDecompressRequest -{ - public string Destination { get; set; } - public string Path { get; set; } - public CompressType Type { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Servers/ServerFileSystemResponse.cs b/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Servers/ServerFileSystemResponse.cs deleted file mode 100644 index d6ebd02..0000000 --- a/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Servers/ServerFileSystemResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Servers; - -public class ServerFileSystemResponse -{ - public string Name { get; set; } - public bool IsFolder { get; set; } - public long Size { get; set; } - - public DateTime CreatedAt { get; set; } - public DateTime UpdatedAt { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Servers/ServerLogsResponse.cs b/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Servers/ServerLogsResponse.cs deleted file mode 100644 index 26e0b50..0000000 --- a/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Servers/ServerLogsResponse.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Servers; - -public class ServerLogsResponse -{ - public string[] Messages { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Servers/ServerStatsResponse.cs b/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Servers/ServerStatsResponse.cs deleted file mode 100644 index 67e107e..0000000 --- a/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Servers/ServerStatsResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Servers; - -public class ServerStatsResponse -{ - public double CpuUsage { get; set; } - public ulong MemoryUsage { get; set; } - public ulong NetworkRead { get; set; } - public ulong NetworkWrite { get; set; } - public ulong IoRead { get; set; } - public ulong IoWrite { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Servers/ServerStatusResponse.cs b/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Servers/ServerStatusResponse.cs deleted file mode 100644 index c546752..0000000 --- a/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Servers/ServerStatusResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using MoonlightServers.DaemonShared.Enums; - -namespace MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Servers; - -public class ServerStatusResponse -{ - public ServerState State { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Statistics/StatisticsDockerResponse.cs b/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Statistics/StatisticsDockerResponse.cs deleted file mode 100644 index 9bc35e6..0000000 --- a/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Statistics/StatisticsDockerResponse.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Statistics; - -public class StatisticsDockerResponse -{ - public string Version { get; set; } - - public long ImagesUsed { get; set; } - public long ImagesReclaimable { get; set; } - - public long ContainersUsed { get; set; } - public long ContainersReclaimable { get; set; } - - public long BuildCacheUsed { get; set; } - public long BuildCacheReclaimable { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Statistics/StatisticsResponse.cs b/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Statistics/StatisticsResponse.cs deleted file mode 100644 index f8b3dc9..0000000 --- a/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Statistics/StatisticsResponse.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Statistics; - -public class StatisticsResponse -{ - public CpuData Cpu { get; set; } = new(); - public MemoryData Memory { get; set; } = new(); - - public DiskData[] Disks { get; set; } = []; - - public record DiskData - { - public string Device { get; set; } - public string MountPath { get; set; } - public ulong DiskTotal { get; set; } - public ulong DiskFree { get; set; } - public ulong InodesTotal { get; set; } - public ulong InodesFree { get; set; } - } - - public record MemoryData - { - public long Total { get; set; } - public long Available { get; set; } - public long Free { get; set; } - public long Cached { get; set; } - public long SwapTotal { get; set; } - public long SwapFree { get; set; } - } - - public record CpuData - { - public string Model { get; set; } - public double Usage { get; set; } - public double[] UsagePerCore { get; set; } - } -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Sys/SystemStatusResponse.cs b/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Sys/SystemStatusResponse.cs deleted file mode 100644 index 505c519..0000000 --- a/MoonlightServers.DaemonShared/DaemonSide/Http/Responses/Sys/SystemStatusResponse.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Sys; - -public class SystemStatusResponse -{ - public bool TripSuccess { get; set; } - public TimeSpan TripTime { get; set; } - public string? TripError { get; set; } - public string Version { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/DaemonSide/Models/ServerStats.cs b/MoonlightServers.DaemonShared/DaemonSide/Models/ServerStats.cs deleted file mode 100644 index 361fdaa..0000000 --- a/MoonlightServers.DaemonShared/DaemonSide/Models/ServerStats.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MoonlightServers.DaemonShared.DaemonSide.Models; - -public record ServerStats -{ - public double CpuUsage { get; set; } - public ulong MemoryUsage { get; set; } - public ulong NetworkRead { get; set; } - public ulong NetworkWrite { get; set; } - public ulong IoRead { get; set; } - public ulong IoWrite { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/Enums/CompressType.cs b/MoonlightServers.DaemonShared/Enums/CompressType.cs deleted file mode 100644 index 3511d14..0000000 --- a/MoonlightServers.DaemonShared/Enums/CompressType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MoonlightServers.DaemonShared.Enums; - -public enum CompressType -{ - Zip = 0, - TarGz = 1 -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/MoonlightServers.DaemonShared.csproj b/MoonlightServers.DaemonShared/MoonlightServers.DaemonShared.csproj index 11e8781..237d661 100644 --- a/MoonlightServers.DaemonShared/MoonlightServers.DaemonShared.csproj +++ b/MoonlightServers.DaemonShared/MoonlightServers.DaemonShared.csproj @@ -1,21 +1,9 @@  - net9.0 + net10.0 enable enable - - MoonlightServers.DaemonShared - MoonlightServers.DaemonShared - 2.1.0 - shared - true - - - - - - diff --git a/MoonlightServers.DaemonShared/PanelSide/Http/Responses/AllocationDataResponse.cs b/MoonlightServers.DaemonShared/PanelSide/Http/Responses/AllocationDataResponse.cs deleted file mode 100644 index eb8b1fd..0000000 --- a/MoonlightServers.DaemonShared/PanelSide/Http/Responses/AllocationDataResponse.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MoonlightServers.DaemonShared.PanelSide.Http.Responses; - -public class AllocationDataResponse -{ - public string IpAddress { get; set; } - public int Port { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/PanelSide/Http/Responses/ServerDataResponse.cs b/MoonlightServers.DaemonShared/PanelSide/Http/Responses/ServerDataResponse.cs deleted file mode 100644 index c18130d..0000000 --- a/MoonlightServers.DaemonShared/PanelSide/Http/Responses/ServerDataResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace MoonlightServers.DaemonShared.PanelSide.Http.Responses; - -public class ServerDataResponse -{ - public int Id { get; set; } - public string StartupCommand { get; set; } - public string OnlineDetection { get; set; } - public string StopCommand { get; set; } - public string DockerImage { get; set; } - public bool PullDockerImage { get; set; } - public string ParseConiguration { get; set; } - - public int Cpu { get; set; } - public int Memory { get; set; } - public int Disk { get; set; } - - public AllocationDataResponse[] Allocations { get; set; } - public Dictionary Variables { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.DaemonShared/PanelSide/Http/Responses/ServerInstallDataResponse.cs b/MoonlightServers.DaemonShared/PanelSide/Http/Responses/ServerInstallDataResponse.cs deleted file mode 100644 index 2938ab9..0000000 --- a/MoonlightServers.DaemonShared/PanelSide/Http/Responses/ServerInstallDataResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace MoonlightServers.DaemonShared.PanelSide.Http.Responses; - -public class ServerInstallDataResponse -{ - public string Shell { get; set; } - public string DockerImage { get; set; } - public string Script { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend.Runtime/DevPluginLoader.cs b/MoonlightServers.Frontend.Runtime/DevPluginLoader.cs deleted file mode 100644 index f3692cd..0000000 --- a/MoonlightServers.Frontend.Runtime/DevPluginLoader.cs +++ /dev/null @@ -1,10 +0,0 @@ -using MoonCore.PluginFramework; -using Moonlight.Client.Plugins; - -namespace MoonlightServers.Frontend.Runtime; - -[PluginLoader] -public partial class DevPluginLoader : IPluginStartup -{ - -} \ No newline at end of file diff --git a/MoonlightServers.Frontend.Runtime/MoonlightServers.Frontend.Runtime.csproj b/MoonlightServers.Frontend.Runtime/MoonlightServers.Frontend.Runtime.csproj deleted file mode 100644 index e4eff98..0000000 --- a/MoonlightServers.Frontend.Runtime/MoonlightServers.Frontend.Runtime.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - net9.0 - enable - enable - - - - - - - - - - - - - - - - - - diff --git a/MoonlightServers.Frontend.Runtime/Program.cs b/MoonlightServers.Frontend.Runtime/Program.cs deleted file mode 100644 index 26c422b..0000000 --- a/MoonlightServers.Frontend.Runtime/Program.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Moonlight.Client.Startup; -using MoonlightServers.Frontend.Runtime; - -var pluginLoader = new DevPluginLoader(); -pluginLoader.Initialize(); - -var builder = WebAssemblyHostBuilder.CreateDefault(args); - -builder.AddMoonlight(pluginLoader.Instances); - -var app = builder.Build(); - -app.ConfigureMoonlight(pluginLoader.Instances); - -await app.RunAsync(); \ No newline at end of file diff --git a/MoonlightServers.Frontend.Runtime/Styles/extract-classes.js b/MoonlightServers.Frontend.Runtime/Styles/extract-classes.js deleted file mode 100644 index b5d5c2c..0000000 --- a/MoonlightServers.Frontend.Runtime/Styles/extract-classes.js +++ /dev/null @@ -1,30 +0,0 @@ -// extract-classes.js -const fs = require('fs'); - -module.exports = (opts = {}) => { - const classSet = new Set(); - - return { - postcssPlugin: 'extract-tailwind-classes', - Rule(rule) { - const selectorParser = require('postcss-selector-parser'); - selectorParser(selectors => { - selectors.walkClasses(node => { - classSet.add(node.value); - }); - }).processSync(rule.selector); - }, - OnceExit() { - const classArray = Array.from(classSet).sort(); - - if (!fs.existsSync("../../MoonlightServers.Frontend/Styles/mappings")){ - fs.mkdirSync("../../MoonlightServers.Frontend/Styles/mappings"); - } - - fs.writeFileSync('../../MoonlightServers.Frontend/Styles/mappings/classes.map', classArray.join('\n')); - console.log(`Extracted ${classArray.length} Tailwind classes`); - } - }; -}; - -module.exports.postcss = true; \ No newline at end of file diff --git a/MoonlightServers.Frontend.Runtime/Styles/package-lock.json b/MoonlightServers.Frontend.Runtime/Styles/package-lock.json deleted file mode 100644 index 5fedb79..0000000 --- a/MoonlightServers.Frontend.Runtime/Styles/package-lock.json +++ /dev/null @@ -1,1347 +0,0 @@ -{ - "name": "styles", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "styles", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "@tailwindcss/postcss": "^4.1.11", - "flyonui": "^2.2.0", - "postcss": "^8.5.6", - "postcss-cli": "^11.0.1", - "postcss-selector-parser": "^7.1.0", - "tailwindcss": "^4.1.11" - }, - "devDependencies": {} - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz", - "integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==", - "dependencies": { - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.2.tgz", - "integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==", - "dependencies": { - "@floating-ui/core": "^1.7.2", - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==" - }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dependencies": { - "minipass": "^7.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@tailwindcss/node": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", - "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "enhanced-resolve": "^5.18.1", - "jiti": "^2.4.2", - "lightningcss": "1.30.1", - "magic-string": "^0.30.17", - "source-map-js": "^1.2.1", - "tailwindcss": "4.1.11" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", - "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", - "hasInstallScript": true, - "dependencies": { - "detect-libc": "^2.0.4", - "tar": "^7.4.3" - }, - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-arm64": "4.1.11", - "@tailwindcss/oxide-darwin-x64": "4.1.11", - "@tailwindcss/oxide-freebsd-x64": "4.1.11", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", - "@tailwindcss/oxide-linux-x64-musl": "4.1.11", - "@tailwindcss/oxide-wasm32-wasi": "4.1.11", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", - "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", - "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", - "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", - "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", - "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", - "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", - "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", - "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", - "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", - "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.11", - "@tybys/wasm-util": "^0.9.0", - "tslib": "^2.8.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", - "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", - "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/postcss": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.11.tgz", - "integrity": "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.11", - "@tailwindcss/oxide": "4.1.11", - "postcss": "^8.4.41", - "tailwindcss": "4.1.11" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "engines": { - "node": ">=18" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/dependency-graph": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz", - "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==", - "engines": { - "node": ">=4" - } - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/enhanced-resolve": { - "version": "5.18.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", - "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flyonui": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/flyonui/-/flyonui-2.2.0.tgz", - "integrity": "sha512-Gncal89zwklAYpqV8IjSgN/1edsXnbTlxhfKRcYa2WgeY8jBSuoNBQWqdL1DSLuXarxYluimwIv34YaRKNuSzg==", - "dependencies": { - "@floating-ui/dom": "^1.6.13" - } - }, - "node_modules/fs-extra": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", - "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/lightningcss": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", - "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.30.1", - "lightningcss-darwin-x64": "1.30.1", - "lightningcss-freebsd-x64": "1.30.1", - "lightningcss-linux-arm-gnueabihf": "1.30.1", - "lightningcss-linux-arm64-gnu": "1.30.1", - "lightningcss-linux-arm64-musl": "1.30.1", - "lightningcss-linux-x64-gnu": "1.30.1", - "lightningcss-linux-x64-musl": "1.30.1", - "lightningcss-win32-arm64-msvc": "1.30.1", - "lightningcss-win32-x64-msvc": "1.30.1" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", - "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", - "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", - "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", - "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", - "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", - "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", - "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", - "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", - "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", - "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-cli": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-11.0.1.tgz", - "integrity": "sha512-0UnkNPSayHKRe/tc2YGW6XnSqqOA9eqpiRMgRlV1S6HdGi16vwJBx7lviARzbV1HpQHqLLRH3o8vTcB0cLc+5g==", - "dependencies": { - "chokidar": "^3.3.0", - "dependency-graph": "^1.0.0", - "fs-extra": "^11.0.0", - "picocolors": "^1.0.0", - "postcss-load-config": "^5.0.0", - "postcss-reporter": "^7.0.0", - "pretty-hrtime": "^1.0.3", - "read-cache": "^1.0.0", - "slash": "^5.0.0", - "tinyglobby": "^0.2.12", - "yargs": "^17.0.0" - }, - "bin": { - "postcss": "index.js" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-load-config": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-5.1.0.tgz", - "integrity": "sha512-G5AJ+IX0aD0dygOE0yFZQ/huFFMSNneyfp0e3/bT05a8OfPC5FUoZRPfGijUdGOJNMewJiwzcHJXFafFzeKFVA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "lilconfig": "^3.1.1", - "yaml": "^2.4.2" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - } - } - }, - "node_modules/postcss-reporter": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.1.0.tgz", - "integrity": "sha512-/eoEylGWyy6/DOiMP5lmFRdmDKThqgn7D6hP2dXKJI/0rJSO1ADFNngZfDzxL0YAxFvws+Rtpuji1YIHj4mySA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "picocolors": "^1.0.0", - "thenby": "^1.3.4" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pretty-hrtime": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/slash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", - "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tailwindcss": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", - "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==" - }, - "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/thenby": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz", - "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==" - }, - "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "engines": { - "node": ">=18" - } - }, - "node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - } - } -} diff --git a/MoonlightServers.Frontend.Runtime/Styles/package.json b/MoonlightServers.Frontend.Runtime/Styles/package.json deleted file mode 100644 index d528236..0000000 --- a/MoonlightServers.Frontend.Runtime/Styles/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "styles", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "tailwind": "npx postcss styles.css -o ../wwwroot/css/style.min.css --watch", - "tailwind-build": "npx postcss styles.css -o ../wwwroot/css/style.min.css", - "mappings": "EXTRACT_CLASSES=true npx postcss styles.css -o ../wwwroot/css/style.min.css " - }, - "author": "", - "license": "ISC", - "dependencies": { - "@tailwindcss/postcss": "^4.1.11", - "flyonui": "^2.2.0", - "tailwindcss": "^4.1.11", - "postcss": "^8.5.6", - "postcss-cli": "^11.0.1", - "postcss-selector-parser": "^7.1.0" - }, - "devDependencies": { - } -} diff --git a/MoonlightServers.Frontend.Runtime/Styles/postcss.config.js b/MoonlightServers.Frontend.Runtime/Styles/postcss.config.js deleted file mode 100644 index 82078a3..0000000 --- a/MoonlightServers.Frontend.Runtime/Styles/postcss.config.js +++ /dev/null @@ -1,11 +0,0 @@ -const tailwindcss = require('@tailwindcss/postcss'); -const extractClasses = require('./extract-classes'); - -module.exports = { - plugins: [ - tailwindcss - ], -}; - -if(process.env.EXTRACT_CLASSES === "true") - module.exports.plugins.push(extractClasses); \ No newline at end of file diff --git a/MoonlightServers.Frontend.Runtime/Styles/styles.css b/MoonlightServers.Frontend.Runtime/Styles/styles.css deleted file mode 100644 index 13cb719..0000000 --- a/MoonlightServers.Frontend.Runtime/Styles/styles.css +++ /dev/null @@ -1,137 +0,0 @@ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=fallback') layer(base); -@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap') layer(base); -@import url("https://cdn.jsdelivr.net/npm/lucide-static/font/lucide.css") layer(base); - -@import "tailwindcss"; -@import "./node_modules/flyonui/variants.css"; -@import "./theme.css"; - -@theme { - --font-inter: "Inter", var(--font-sans); - --font-scp: "Source Code Pro", var(--font-mono); - - --color-background: var(--mooncore-color-background); - --color-base-150: var(--mooncore-color-base-150); - --color-base-250: var(--mooncore-color-base-250); -} - -@plugin "flyonui" { - themes: mooncore --default; -} - -@source "./node_modules/flyonui/dist/index.js"; - -@source "../**/*.razor"; -@source "../**/*.cs"; -@source "../**/*.html"; -@source "./mappings/*.map"; - -@source "../../MoonlightServers.Frontend/**/*.cs"; -@source "../../MoonlightServers.Frontend/**/*.html"; -@source "../../MoonlightServers.Frontend/**/*.razor"; -@source "../../MoonlightServers.Frontend/Styles/**/*.map"; - -@source "../../MoonlightServers.ApiServer/**/*.razor"; - -#blazor-error-ui { - display: none; -} - -#blazor-loader-label:after { - content: var(--blazor-load-percentage-text, "Loading"); -} - -#blazor-loader-progress { - width: var(--blazor-load-percentage, 0%); -} - -@plugin "flyonui/theme" { - name: "mooncore"; - default: true; - prefersdark: true; - color-scheme: "dark"; - --color-base-100: var(--mooncore-color-base-100); - --color-base-200: var(--mooncore-color-base-200); - --color-base-300: var(--mooncore-color-base-300); - --color-base-content: var(--mooncore-color-base-content); - --color-primary: var(--mooncore-color-primary); - --color-primary-content: var(--mooncore-color-primary-content); - --color-secondary: var(--mooncore-color-secondary); - --color-secondary-content: var(--mooncore-color-secondary-content); - --color-accent: var(--mooncore-color-accent); - --color-accent-content: var(--mooncore-color-accent-content); - --color-neutral: var(--mooncore-color-neutral); - --color-neutral-content: var(--mooncore-color-neutral-content); - --color-info: var(--mooncore-color-info); - --color-info-content: var(--mooncore-color-info-content); - --color-success: var(--mooncore-color-success); - --color-success-content: var(--mooncore-color-success-content); - --color-warning: var(--mooncore-color-warning); - --color-warning-content: var(--mooncore-color-warning-content); - --color-error: var(--mooncore-color-error); - --color-error-content: var(--mooncore-color-error-content); - --radius-selector: var(--mooncore-radius-selector); - --radius-field: var(--mooncore-radius-field); - --radius-box: var(--mooncore-radius-box); - --size-selector: var(--mooncore-size-selector); - --size-field: var(--mooncore-size-field); - --border: var(--mooncore-border); - --depth: var(--mooncore-depth); - --noise: var(--mooncore-noise); -} - -@layer utilities { - .btn { - @apply text-sm font-medium inline-flex items-center justify-center; - } - - .checkbox { - @apply border-base-content/30 bg-base-100; - } - - .input { - @apply !border-base-content/20 border-2 ring-0! outline-0! focus:border-primary! focus-within:border-primary! bg-base-200/50; - } - - .advance-select-toggle { - @apply !border-base-content/20 border-2 ring-0! outline-0! focus:border-primary! focus-within:border-primary! bg-base-200/50; - } - - .select { - @apply !border-base-content/20 border-2 ring-0! outline-0! focus:border-primary! focus-within:border-primary! bg-base-200/50; - } - - .table { - :where(th, td) { - @apply py-1.5; - } - } - - .dropdown-item { - @apply px-2.5 py-1.5 text-sm; - } - - .dropdown-menu { - @apply bg-base-150; - } - - .advance-select-menu { - @apply !border-base-content/20 border-2 ring-0! outline-0! bg-base-200/50 !px-0; - } - - .advance-select-option { - @apply !rounded-none hover:!bg-primary; - } - - .advance-select-option.selected { - @apply !bg-primary !text-primary-content; - } - - .advance-select-toggle { - @apply !border-base-content/20 border-2 ring-0! outline-0! focus:border-primary! focus-within:border-primary! bg-base-200/50; - } - - .table thead { - @apply !normal-case; - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend.Runtime/Styles/theme.css b/MoonlightServers.Frontend.Runtime/Styles/theme.css deleted file mode 100644 index 4401a5f..0000000 --- a/MoonlightServers.Frontend.Runtime/Styles/theme.css +++ /dev/null @@ -1,33 +0,0 @@ -@theme { - --mooncore-color-background: #0c0f18; - --mooncore-color-base-100: #1e2b47; - --mooncore-color-base-150: #1a2640; - --mooncore-color-base-200: #101a2e; - --mooncore-color-base-250: #0f1729; - --mooncore-color-base-300: #0c1221; - --mooncore-color-base-content: #dde5f5; - --mooncore-color-primary: oklch(.511 .262 276.966); - --mooncore-color-primary-content: #dde5f5; - --mooncore-color-secondary: oklch(37% 0.034 259.733); - --mooncore-color-secondary-content: #dde5f5; - --mooncore-color-accent: oklch(.627 .265 303.9); - --mooncore-color-accent-content: #dde5f5; - --mooncore-color-neutral: #dde5f5; - --mooncore-color-neutral-content: oklch(14% 0.005 285.823); - --mooncore-color-info: oklch(.546 .245 262.881); - --mooncore-color-info-content: #dde5f5; - --mooncore-color-success: oklch(.627 .194 149.214); - --mooncore-color-success-content: #dde5f5; - --mooncore-color-warning: oklch(.828 .189 84.429); - --mooncore-color-warning-content: #dde5f5; - --mooncore-color-error: oklch(.586 .253 17.585); - --mooncore-color-error-content: #dde5f5; - --mooncore-radius-selector: 0.25rem; - --mooncore-radius-field: 0.5rem; - --mooncore-radius-box: 0.5rem; - --mooncore-size-selector: 0.25rem; - --mooncore-size-field: 0.25rem; - --mooncore-border: 1px; - --mooncore-depth: 0; - --mooncore-noise: 0; -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Helpers/ServerFileSystemProvider.cs b/MoonlightServers.Frontend/Helpers/ServerFileSystemProvider.cs deleted file mode 100644 index 10357df..0000000 --- a/MoonlightServers.Frontend/Helpers/ServerFileSystemProvider.cs +++ /dev/null @@ -1,147 +0,0 @@ -/* -using System.IO.Enumeration; -using MoonCore.Blazor.FlyonUi.Helpers; -using MoonCore.Helpers; -using MoonlightServers.Frontend.Services; - -namespace MoonlightServers.Frontend.Helpers; - -public class ServerFileSystemProvider : IFileSystemProvider, ICompressFileSystemProvider -{ - private readonly DownloadService DownloadService; - private readonly ServerFileSystemService FileSystemService; - - public CompressType[] CompressTypes { get; } = - [ - new() - { - Extension = "zip", - DisplayName = "ZIP Archive" - }, - new() - { - Extension = "tar.gz", - DisplayName = "GZ Compressed Tar Archive" - } - ]; - - private readonly int ServerId; - - public ServerFileSystemProvider( - int serverId, - ServerFileSystemService fileSystemService, - DownloadService downloadService - ) - { - ServerId = serverId; - FileSystemService = fileSystemService; - DownloadService = downloadService; - } - - public async Task List(string path) - { - var result = await FileSystemService.List(ServerId, path); - - return result - .Select(x => new FileSystemEntry() - { - Name = x.Name, - Size = x.Size, - IsFile = x.IsFile, - CreatedAt = x.CreatedAt, - UpdatedAt = x.UpdatedAt - }) - .ToArray(); - } - - public async Task Create(string path, Stream stream) - { - await Upload(_ => Task.CompletedTask, path, stream); - } - - public async Task Move(string oldPath, string newPath) - { - await FileSystemService.Move(ServerId, oldPath, newPath); - } - - public async Task Delete(string path) - { - await FileSystemService.Delete(ServerId, path); - } - - public async Task CreateDirectory(string path) - { - await FileSystemService.Mkdir(ServerId, path); - } - - public async Task Read(string path) - { - var downloadSession = await FileSystemService.Download(ServerId, path); - - using var httpClient = new HttpClient(); - return await httpClient.GetStreamAsync(downloadSession.DownloadUrl); - } - - public async Task Download(Func updateProgress, string path, string fileName) - { - var downloadSession = await FileSystemService.Download(ServerId, path); - - await DownloadService.DownloadUrl(fileName, downloadSession.DownloadUrl, - async (loaded, total) => - { - var percent = total == 0 ? 0 : (int)Math.Round((float)loaded / total * 100); - await updateProgress.Invoke(percent); - } - ); - } - - public async Task Upload(Func updateProgress, string path, Stream stream) - { - using var httpClient = new HttpClient(); - - var uploadSession = await FileSystemService.Upload(ServerId); - - var size = stream.Length; - var chunkSize = ByteConverter.FromMegaBytes(20).Bytes; - - var chunks = size / chunkSize; - chunks += size % chunkSize > 0 ? 1 : 0; - - for (var chunkId = 0; chunkId < chunks; chunkId++) - { - var percent = (int)Math.Round((chunkId + 1f) / chunks * 100); - await updateProgress.Invoke(percent); - - var buffer = new byte[chunkSize]; - var bytesRead = await stream.ReadAsync(buffer); - - var uploadForm = new MultipartFormDataContent(); - uploadForm.Add(new ByteArrayContent(buffer, 0, bytesRead), "file", "file"); - - await httpClient.PostAsync( - $"{uploadSession.UploadUrl}&totalSize={size}&chunkId={chunkId}&path={path}", - uploadForm - ); - } - } - - public async Task Compress(CompressType type, string path, string[] itemsToCompress) - { - await FileSystemService.Compress( - ServerId, - type.Extension.Replace(".", ""), - itemsToCompress, - path - ); - } - - public async Task Decompress(CompressType type, string path, string destination) - { - await FileSystemService.Decompress( - ServerId, - type.Extension.Replace(".", ""), - path, - destination - ); - } -}*/ \ No newline at end of file diff --git a/MoonlightServers.Frontend/Helpers/ServerFsAccess.cs b/MoonlightServers.Frontend/Helpers/ServerFsAccess.cs deleted file mode 100644 index 349a39c..0000000 --- a/MoonlightServers.Frontend/Helpers/ServerFsAccess.cs +++ /dev/null @@ -1,63 +0,0 @@ -using MoonCore.Blazor.FlyonUi.Files; -using MoonCore.Blazor.FlyonUi.Files.Manager; -using MoonlightServers.Frontend.Services; - -namespace MoonlightServers.Frontend.Helpers; - -public class ServerFsAccess : IFsAccess -{ - private readonly int Id; - private readonly ServerFileSystemService FileSystemService; - - public ServerFsAccess(int id, ServerFileSystemService fileSystemService) - { - Id = id; - FileSystemService = fileSystemService; - } - - public Task CreateFileAsync(string path) - => FileSystemService.TouchAsync(Id, path); - - public Task CreateDirectoryAsync(string path) - => FileSystemService.MkdirAsync(Id, path); - - public async Task ListAsync(string path) - { - var entries = await FileSystemService.ListAsync(Id, path); - - return entries.Select(x => new FsEntry() - { - Name = x.Name, - Size = x.Size, - CreatedAt = x.CreatedAt, - IsFolder = x.IsFolder, - UpdatedAt = x.UpdatedAt - }).ToArray(); - } - - public Task MoveAsync(string oldPath, string newPath) - => FileSystemService.MoveAsync(Id, oldPath, newPath); - - public Task ReadAsync(string path, Func onHandleData) - { - throw new NotImplementedException(); - } - - public Task WriteAsync(string path, Stream dataStream) - { - throw new NotImplementedException(); - } - - public Task DeleteAsync(string path) - => FileSystemService.DeleteAsync(Id, path); - - public Task UploadChunkAsync(string path, int chunkId, long chunkSize, long totalSize, byte[] data) - { - throw new NotImplementedException(); - } - - public Task DownloadChunkAsync(string path, int chunkId, long chunkSize) - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Implementations/DefaultPermissionProvider.cs b/MoonlightServers.Frontend/Implementations/DefaultPermissionProvider.cs deleted file mode 100644 index 8d6b2ad..0000000 --- a/MoonlightServers.Frontend/Implementations/DefaultPermissionProvider.cs +++ /dev/null @@ -1,59 +0,0 @@ -using MoonlightServers.Frontend.Interfaces; -using MoonlightServers.Frontend.Models; -using MoonlightServers.Shared.Constants; -using MoonlightServers.Shared.Http.Responses.Client.Servers; - -namespace MoonlightServers.Frontend.Implementations; - -public class DefaultPermissionProvider : IServerPermissionProvider -{ - public Task GetPermissionsAsync(ServerDetailResponse server) - { - ServerPermission[] permissions = [ - new() - { - Identifier = ServerPermissionConstants.Console, - Description = "Allows access to the console", - DisplayName = "Console", - Icon = "icon-square-terminal" - }, - new() - { - Identifier = ServerPermissionConstants.Files, - Description = "Allows access to the files", - DisplayName = "Files", - Icon = "icon-folder" - }, - new() - { - Identifier = ServerPermissionConstants.Power, - Description = "Allows actions like turning off the server and starting it", - DisplayName = "Power", - Icon = "icon-power" - }, - new() - { - Identifier = ServerPermissionConstants.Settings, - Description = "Gives permissions to perform re-installs or change other general settings", - DisplayName = "Settings", - Icon = "icon-settings" - }, - new() - { - Identifier = ServerPermissionConstants.Shares, - Description = "Dangerous! This allows control to the available shares and their access level for a server", - DisplayName = "Shares", - Icon = "icon-users" - }, - new() - { - Identifier = ServerPermissionConstants.Variables, - Description = "Allows access to the server software specific settings", - DisplayName = "Variables", - Icon = "icon-variable" - } - ]; - - return Task.FromResult(permissions); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Implementations/DefaultServerTabProvider.cs b/MoonlightServers.Frontend/Implementations/DefaultServerTabProvider.cs deleted file mode 100644 index 922d9a1..0000000 --- a/MoonlightServers.Frontend/Implementations/DefaultServerTabProvider.cs +++ /dev/null @@ -1,25 +0,0 @@ -using MoonlightServers.Frontend.Interfaces; -using MoonlightServers.Frontend.Models; -using MoonlightServers.Frontend.UI.Components.Servers.ServerTabs; -using MoonlightServers.Shared.Constants; -using MoonlightServers.Shared.Enums; -using MoonlightServers.Shared.Http.Responses.Client.Servers; - -namespace MoonlightServers.Frontend.Implementations; - -public class DefaultServerTabProvider : IServerTabProvider -{ - public Task GetTabsAsync(ServerDetailResponse server) - { - ServerTab[] tabs = - [ - ServerTab.CreateFromComponent("Console", "console", 0, ServerPermissionConstants.Console, ServerPermissionLevel.Read), - ServerTab.CreateFromComponent("Files", "files", 1, ServerPermissionConstants.Files, ServerPermissionLevel.Read), - ServerTab.CreateFromComponent("Shares", "shares", 2, ServerPermissionConstants.Shares, ServerPermissionLevel.ReadWrite), - ServerTab.CreateFromComponent("Variables", "variables", 9, ServerPermissionConstants.Variables, ServerPermissionLevel.Read), - ServerTab.CreateFromComponent("Settings", "settings", 10, ServerPermissionConstants.Settings, ServerPermissionLevel.Read), - ]; - - return Task.FromResult(tabs); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Implementations/PermissionProvider.cs b/MoonlightServers.Frontend/Implementations/PermissionProvider.cs new file mode 100644 index 0000000..b99cbff --- /dev/null +++ b/MoonlightServers.Frontend/Implementations/PermissionProvider.cs @@ -0,0 +1,17 @@ +using LucideBlazor; +using Moonlight.Frontend.Interfaces; +using Moonlight.Frontend.Models; + +namespace MoonlightServers.Frontend.Implementations; + +public class PermissionProvider : IPermissionProvider +{ + public Task GetPermissionsAsync() + { + return Task.FromResult([ + new PermissionCategory("Demo", typeof(SparklesIcon), [ + new Permission("Permissions:Demo", "Demo", "Access to demo page") + ]) + ]); + } +} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Implementations/SidebarImplementation.cs b/MoonlightServers.Frontend/Implementations/SidebarImplementation.cs deleted file mode 100644 index 74d8a60..0000000 --- a/MoonlightServers.Frontend/Implementations/SidebarImplementation.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Moonlight.Client.Interfaces; -using Moonlight.Client.Models; - -namespace MoonlightServers.Frontend.Implementations; - -public class SidebarImplementation : ISidebarItemProvider -{ - public void ModifySidebar(List items) - { - items.AddRange( - [ - new SidebarItem() - { - Name = "Servers", - Path = "/servers", - Icon = "icon-server", - Priority = 4 - }, - new SidebarItem() - { - Name = "Servers", - Path = "/admin/servers", - Icon = "icon-server", - Group = "Admin", - Policy = "permissions:admin.servers.overview", - Priority = 4 - } - ] - ); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Implementations/SidebarProvider.cs b/MoonlightServers.Frontend/Implementations/SidebarProvider.cs new file mode 100644 index 0000000..fdf4d98 --- /dev/null +++ b/MoonlightServers.Frontend/Implementations/SidebarProvider.cs @@ -0,0 +1,21 @@ +using LucideBlazor; +using Moonlight.Frontend.Interfaces; +using Moonlight.Frontend.Models; + +namespace MoonlightServers.Frontend.Implementations; + +public sealed class SidebarProvider : ISidebarProvider +{ + public Task GetItemsAsync() + { + return Task.FromResult([ + new SidebarItem() + { + Group = "Demo", + Name = "Demo", + IconType = typeof(SparklesIcon), + Path = "/demo" + } + ]); + } +} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Interfaces/IServerPermissionProvider.cs b/MoonlightServers.Frontend/Interfaces/IServerPermissionProvider.cs deleted file mode 100644 index 01d3988..0000000 --- a/MoonlightServers.Frontend/Interfaces/IServerPermissionProvider.cs +++ /dev/null @@ -1,9 +0,0 @@ -using MoonlightServers.Frontend.Models; -using MoonlightServers.Shared.Http.Responses.Client.Servers; - -namespace MoonlightServers.Frontend.Interfaces; - -public interface IServerPermissionProvider -{ - public Task GetPermissionsAsync(ServerDetailResponse server); -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Interfaces/IServerTabProvider.cs b/MoonlightServers.Frontend/Interfaces/IServerTabProvider.cs deleted file mode 100644 index a359ae1..0000000 --- a/MoonlightServers.Frontend/Interfaces/IServerTabProvider.cs +++ /dev/null @@ -1,9 +0,0 @@ -using MoonlightServers.Frontend.Models; -using MoonlightServers.Shared.Http.Responses.Client.Servers; - -namespace MoonlightServers.Frontend.Interfaces; - -public interface IServerTabProvider -{ - public Task GetTabsAsync(ServerDetailResponse server); -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Models/ServerPermission.cs b/MoonlightServers.Frontend/Models/ServerPermission.cs deleted file mode 100644 index 5290bc8..0000000 --- a/MoonlightServers.Frontend/Models/ServerPermission.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MoonlightServers.Frontend.Models; - -public record ServerPermission -{ - public string Identifier { get; set; } - public string DisplayName { get; set; } - public string Description { get; set; } - public string Icon { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Models/ServerTab.cs b/MoonlightServers.Frontend/Models/ServerTab.cs deleted file mode 100644 index 0f45e85..0000000 --- a/MoonlightServers.Frontend/Models/ServerTab.cs +++ /dev/null @@ -1,31 +0,0 @@ -using MoonlightServers.Frontend.UI.Components.Servers.ServerTabs; -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.Frontend.Models; - -public record ServerTab -{ - public string Name { get; private set; } - public string Path { get; private set; } - public string PermissionId { get; set; } - public ServerPermissionLevel PermissionLevel { get; set; } - public int Priority { get; private set; } - public Type ComponentType { get; private set; } - - public static ServerTab CreateFromComponent( - string name, - string path, - int priority, - string permissionId = "", ServerPermissionLevel permissionLevel = ServerPermissionLevel.None) where T : BaseServerTab - { - return new() - { - Name = name, - Path = path, - Priority = priority, - ComponentType = typeof(T), - PermissionLevel = permissionLevel, - PermissionId = permissionId - }; - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/MoonlightServers.Frontend.csproj b/MoonlightServers.Frontend/MoonlightServers.Frontend.csproj index 764684c..ee4b9d9 100644 --- a/MoonlightServers.Frontend/MoonlightServers.Frontend.csproj +++ b/MoonlightServers.Frontend/MoonlightServers.Frontend.csproj @@ -1,42 +1,32 @@ - net9.0 + net10.0 enable enable - false - - MoonlightServers.Frontend - MoonlightServers.Frontend - 2.1.0 - true - - - - + - - - - - - - + - - + - + + + + + + + diff --git a/MoonlightServers.Frontend/MoonlightServers.Frontend.targets b/MoonlightServers.Frontend/MoonlightServers.Frontend.targets deleted file mode 100644 index 77321fd..0000000 --- a/MoonlightServers.Frontend/MoonlightServers.Frontend.targets +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/MoonlightServers.Frontend/PluginStartup.cs b/MoonlightServers.Frontend/PluginStartup.cs deleted file mode 100644 index 7c2cf86..0000000 --- a/MoonlightServers.Frontend/PluginStartup.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Microsoft.Extensions.DependencyInjection; -using MoonCore.Extensions; -using Moonlight.Client.Interfaces; -using Moonlight.Client.Plugins; -using MoonlightServers.Frontend.Implementations; -using MoonlightServers.Frontend.Interfaces; - -namespace MoonlightServers.Frontend; - -public class PluginStartup : IPluginStartup -{ - public void AddPlugin(WebAssemblyHostBuilder builder) - { - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - - builder.Services.AutoAddServices(); - } - - public void ConfigurePlugin(WebAssemblyHost app) - { - - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Servers.Frontend.targets b/MoonlightServers.Frontend/Servers.Frontend.targets new file mode 100644 index 0000000..4505797 --- /dev/null +++ b/MoonlightServers.Frontend/Servers.Frontend.targets @@ -0,0 +1,15 @@ + + + + $(MSBuildProjectDirectory)\bin\Servers + + + + + + + + + + + \ No newline at end of file diff --git a/MoonlightServers.Frontend/Services/NodeService.cs b/MoonlightServers.Frontend/Services/NodeService.cs deleted file mode 100644 index ed1a2eb..0000000 --- a/MoonlightServers.Frontend/Services/NodeService.cs +++ /dev/null @@ -1,36 +0,0 @@ -using MoonCore.Attributes; -using MoonCore.Helpers; -using MoonlightServers.Shared.Http.Responses.Admin.Nodes.Statistics; -using MoonlightServers.Shared.Http.Responses.Admin.Nodes.Sys; - -namespace MoonlightServers.Frontend.Services; - -[Scoped] -public class NodeService -{ - private readonly HttpApiClient HttpApiClient; - - public NodeService(HttpApiClient httpApiClient) - { - HttpApiClient = httpApiClient; - } - - public async Task GetSystemStatusAsync(int nodeId) - { - return await HttpApiClient.GetJson($"api/admin/servers/nodes/{nodeId}/system/status"); - } - - public async Task GetStatisticsAsync(int nodeId) - { - return await HttpApiClient.GetJson( - $"api/admin/servers/nodes/{nodeId}/statistics" - ); - } - - public async Task GetDockerStatisticsAsync(int nodeId) - { - return await HttpApiClient.GetJson( - $"api/admin/servers/nodes/{nodeId}/statistics/docker" - ); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Services/ServerFileSystemService.cs b/MoonlightServers.Frontend/Services/ServerFileSystemService.cs deleted file mode 100644 index 17a13e7..0000000 --- a/MoonlightServers.Frontend/Services/ServerFileSystemService.cs +++ /dev/null @@ -1,92 +0,0 @@ -using MoonCore.Attributes; -using MoonCore.Helpers; -using MoonlightServers.Shared.Http.Requests.Client.Servers.Files; -using MoonlightServers.Shared.Http.Responses.Client.Servers.Files; - -namespace MoonlightServers.Frontend.Services; - -[Scoped] -public class ServerFileSystemService -{ - private readonly HttpApiClient ApiClient; - - public ServerFileSystemService(HttpApiClient apiClient) - { - ApiClient = apiClient; - } - - public async Task ListAsync(int serverId, string path) - { - return await ApiClient.GetJson( - $"api/client/servers/{serverId}/files/list?path={path}" - ); - } - - public async Task MoveAsync(int serverId, string oldPath, string newPath) - { - await ApiClient.Post( - $"api/client/servers/{serverId}/files/move?oldPath={oldPath}&newPath={newPath}" - ); - } - - public async Task DeleteAsync(int serverId, string path) - { - await ApiClient.Delete( - $"api/client/servers/{serverId}/files/delete?path={path}" - ); - } - - public async Task MkdirAsync(int serverId, string path) - { - await ApiClient.Post( - $"api/client/servers/{serverId}/files/mkdir?path={path}" - ); - } - - public async Task TouchAsync(int serverId, string path) - { - await ApiClient.Post( - $"api/client/servers/{serverId}/files/touch?path={path}" - ); - } - - public async Task UploadAsync(int serverId) - { - return await ApiClient.GetJson( - $"api/client/servers/{serverId}/files/upload" - ); - } - - public async Task DownloadAsync(int serverId, string path) - { - return await ApiClient.GetJson( - $"api/client/servers/{serverId}/files/download?path={path}" - ); - } - - public async Task CompressAsync(int serverId, string type, string[] items, string destination) - { - await ApiClient.Post( - $"api/client/servers/{serverId}/files/compress", - new ServerFilesCompressRequest() - { - Type = type, - Items = items, - Destination = destination - } - ); - } - - public async Task DecompressAsync(int serverId, string type, string path, string destination) - { - await ApiClient.Post( - $"api/client/servers/{serverId}/files/decompress", - new ServerFilesDecompressRequest() - { - Type = type, - Path = path, - Destination = destination - } - ); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Services/ServerService.cs b/MoonlightServers.Frontend/Services/ServerService.cs deleted file mode 100644 index 3fe0cfa..0000000 --- a/MoonlightServers.Frontend/Services/ServerService.cs +++ /dev/null @@ -1,139 +0,0 @@ -using MoonCore.Attributes; -using MoonCore.Common; -using MoonCore.Helpers; -using MoonlightServers.Shared.Http.Requests.Client.Servers; -using MoonlightServers.Shared.Http.Requests.Client.Servers.Variables; -using MoonlightServers.Shared.Http.Responses.Client.Servers; -using MoonlightServers.Shared.Http.Responses.Client.Servers.Variables; - -namespace MoonlightServers.Frontend.Services; - -[Scoped] -public class ServerService -{ - private readonly HttpApiClient HttpApiClient; - - public ServerService(HttpApiClient httpApiClient) - { - HttpApiClient = httpApiClient; - } - - public async Task> GetServersAsync(int startIndex, int count) - { - return await HttpApiClient.GetJson>( - $"api/client/servers?startIndex={startIndex}&count={count}" - ); - } - - public async Task> GetSharedServersAsync(int startIndex, int count) - { - return await HttpApiClient.GetJson>( - $"api/client/servers/shared?startIndex={startIndex}&count={count}" - ); - } - - public async Task GetServerAsync(int serverId) - { - return await HttpApiClient.GetJson( - $"api/client/servers/{serverId}" - ); - } - - public async Task GetStatusAsync(int serverId) - { - return await HttpApiClient.GetJson( - $"api/client/servers/{serverId}/status" - ); - } - - public async Task GetLogsAsync(int serverId) - { - return await HttpApiClient.GetJson( - $"api/client/servers/{serverId}/logs" - ); - } - - public async Task GetStatsAsync(int serverId) - { - return await HttpApiClient.GetJson( - $"api/client/servers/{serverId}/stats" - ); - } - - public async Task RunCommandAsync(int serverId, string command) - { - await HttpApiClient.Post( - $"api/client/servers/{serverId}/command", - new ServerCommandRequest() - { - Command = command - } - ); - } - - public async Task GetWebSocketAsync(int serverId) - { - return await HttpApiClient.GetJson( - $"api/client/servers/{serverId}/ws" - ); - } - - public async Task InstallAsync(int serverId) - { - await HttpApiClient.Post( - $"api/client/servers/{serverId}/install" - ); - } - - #region Power actions - - public async Task StartAsync(int serverId) - { - await HttpApiClient.Post( - $"api/client/servers/{serverId}/start" - ); - } - - public async Task StopAsync(int serverId) - { - await HttpApiClient.Post( - $"api/client/servers/{serverId}/stop" - ); - } - - public async Task KillAsync(int serverId) - { - await HttpApiClient.Post( - $"api/client/servers/{serverId}/kill" - ); - } - - #endregion - - #region Variables - - public async Task> GetVariablesAsync(int serverId, int startIndex, int count) - { - return await HttpApiClient.GetJson>( - $"api/client/servers/{serverId}/variables?startIndex={startIndex}&count={count}" - ); - } - - public async Task UpdateVariablesAsync(int serverId, UpdateServerVariableRangeRequest request) - { - await HttpApiClient.Patch( - $"api/client/servers/{serverId}/variables", - request - ); - } - - public async Task UpdateVariableAsync(int serverId, UpdateServerVariableRequest request) - { - await HttpApiClient.Put( - $"api/client/servers/{serverId}/variables", - request - ); - } - - #endregion -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Services/ServerShareService.cs b/MoonlightServers.Frontend/Services/ServerShareService.cs deleted file mode 100644 index 05b5776..0000000 --- a/MoonlightServers.Frontend/Services/ServerShareService.cs +++ /dev/null @@ -1,34 +0,0 @@ -using MoonCore.Attributes; -using MoonCore.Common; -using MoonCore.Helpers; -using MoonlightServers.Shared.Http.Requests.Client.Servers.Shares; -using MoonlightServers.Shared.Http.Responses.Client.Servers.Shares; - -namespace MoonlightServers.Frontend.Services; - -[Scoped] -public class ServerShareService -{ - private readonly HttpApiClient ApiClient; - - public ServerShareService(HttpApiClient apiClient) - { - ApiClient = apiClient; - } - - public async Task> GetAsync(int id, int startIndex, int count) - => await ApiClient.GetJson>( - $"api/client/servers/{id}/shares?startIndex={startIndex}&count={count}"); - - public async Task GetAsync(int id, int shareId) - => await ApiClient.GetJson($"api/client/servers/{id}/shares/{shareId}"); - - public async Task CreateAsync(int id, CreateShareRequest request) - => await ApiClient.PostJson($"api/client/servers/{id}/shares", request); - - public async Task UpdateAsync(int id, int shareId, UpdateShareRequest request) - => await ApiClient.PatchJson($"api/client/servers/{id}/shares/{shareId}", request); - - public async Task DeleteAsync(int id, int shareId) - => await ApiClient.Delete($"api/client/servers/{id}/shares/{shareId}"); -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Startup.cs b/MoonlightServers.Frontend/Startup.cs new file mode 100644 index 0000000..f810c25 --- /dev/null +++ b/MoonlightServers.Frontend/Startup.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Moonlight.Frontend; +using Moonlight.Frontend.Configuration; +using Moonlight.Frontend.Interfaces; +using MoonlightServers.Frontend.Implementations; +using SimplePlugin.Abstractions; + +namespace MoonlightServers.Frontend; + +[PluginModule] +public sealed class Startup : MoonlightPlugin +{ + public override void PreBuild(WebAssemblyHostBuilder builder) + { + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + + builder.Services.Configure(options => + { + options.Assemblies.Add(typeof(Startup).Assembly); + }); + } + + public override void PostBuild(WebAssemblyHost application) + { + } +} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Styles/Moonlight.Client/classes.map b/MoonlightServers.Frontend/Styles/Moonlight.Client/classes.map deleted file mode 100755 index 338ace2..0000000 --- a/MoonlightServers.Frontend/Styles/Moonlight.Client/classes.map +++ /dev/null @@ -1,766 +0,0 @@ -!bg-base-100 -!border-base-content/40 -!border-none -!flex -!font-medium -!font-semibold -!h-2.5 -!justify-between -!me-1.5 -!ms-auto -!px-2.5 -!py-0.5 -!rounded-full -!rounded-xs -!text-sm -!w-2.5 -*:[grid-area:1/1] -*:first:rounded-tl-lg -*:last:rounded-tr-lg --left-4 --ml-4 --translate-x-full --translate-y-1/2 -[animation-duration:0.8s] -[animation-timing-function:ease] -absolute -accordion -accordion-bordered -accordion-toggle -active -active-tab:bg-primary -active-tab:hover:text-primary-content -active-tab:text-base-content -active-tab:text-primary-content -advance-select-menu -advance-select-option -advance-select-tag -advance-select-toggle -alert -alert-error -alert-outline -alert-primary -alert-soft -align-bottom -align-middle -animate-bounce -animate-ping -animate-spin -aria-[current='page']:text-bg-soft-primary -avatar -avatar-away-bottom -avatar-away-top -avatar-busy-bottom -avatar-busy-top -avatar-offline-bottom -avatar-offline-top -avatar-online-bottom -avatar-online-top -avatar-placeholder -badge -badge-error -badge-info -badge-outline -badge-primary -badge-soft -badge-success -basis-full -bg-background -bg-background/60 -bg-base-100 -bg-base-150 -bg-base-200 -bg-base-200! -bg-base-200/50 -bg-base-200/60 -bg-base-250 -bg-base-300 -bg-base-300/45 -bg-base-300/50 -bg-base-300/60 -bg-clip-text -bg-error -bg-gradient-to-r -bg-gray-800 -bg-gray-900 -bg-indigo-600 -bg-info -bg-primary -bg-primary/5 -bg-red-500 -bg-success -bg-transparent -bg-warning -bg-white/5 -block -blur -border -border-0 -border-1 -border-2 -border-3 -border-accent -border-b -border-b-2 -border-b-base-content/20 -border-b-primary -border-base-content -border-base-content/20 -border-base-content/25 -border-base-content/40 -border-base-content/5 -border-base-content/60 -border-base-content/70 -border-dashed -border-dotted -border-e-2 -border-error/30 -border-info/30 -border-l-4 -border-l-transparent -border-primary -border-r-transparent -border-solid -border-success -border-success/30 -border-t -border-t-2 -border-t-transparent -border-transparent -border-warning/30 -bottom-0 -bottom-full -breadcrumbs -breadcrumbs-separator -break-words -btn -btn-accent -btn-active -btn-block -btn-circle -btn-disabled -btn-error -btn-gradient -btn-info -btn-lg -btn-outline -btn-primary -btn-secondary -btn-sm -btn-soft -btn-square -btn-success -btn-text -btn-warning -card -card-alert -card-body -card-border -card-footer -card-header -card-title -carousel -carousel-body -carousel-next -carousel-prev -carousel-slide -chat -chat-avatar -chat-bubble -chat-footer -chat-header -chat-receiver -chat-sender -checkbox -checkbox-primary -checkbox-xs -col-span-1 -col-span-12 -col-span-2 -collapse -combo-box-selected:block -combo-box-selected:dropdown-active -complete -container -contents -cursor-default -cursor-not-allowed -cursor-pointer -cursor-progress -custom-option -diff -disabled -divide-base-150/60 -divide-y -divider -drawer -drawer-body -drawer-bottom -drawer-end -drawer-footer -drawer-header -drawer-start -drawer-title -drawer-top -drop-shadow -dropdown -dropdown-active -dropdown-disabled -dropdown-item -dropdown-menu -dropdown-open:opacity-100 -dropdown-open:rotate-180 -dropdown-toggle -duration-300 -duration-500 -ease-in-out -ease-linear -end-3 -error-message -file-upload-complete:progress-success -fill-base-content -fill-black -fill-gray-200 -filter -filter-reset -fixed -flex -flex-1 -flex-col -flex-grow -flex-nowrap -flex-row -flex-shrink-0 -flex-wrap -focus-visible:outline -focus-visible:outline-2 -focus-visible:outline-indigo-600 -focus-visible:outline-none -focus-visible:outline-offset-2 -focus-within:border-primary -focus:border-primary -focus:outline-1 -focus:outline-none -focus:outline-primary -focus:ring-0 -focus:ring-2 -focus:ring-indigo-600 -focus:ring-inset -font-bold -font-inter -font-medium -font-normal -font-semibold -footer -footer-center -from-violet-400 -gap-0.5 -gap-1 -gap-1.5 -gap-2 -gap-3 -gap-4 -gap-5 -gap-6 -gap-x-1 -gap-x-2 -gap-x-3 -gap-x-6 -gap-y-1 -gap-y-1.5 -gap-y-2 -gap-y-2.5 -gap-y-3 -gap-y-8 -grid -grid-cols-1 -grid-cols-12 -grid-cols-2 -grid-cols-3 -grid-cols-4 -grid-flow-col -grow -grow-0 -h-0 -h-10 -h-12 -h-14 -h-2 -h-3 -h-32 -h-36 -h-6 -h-64 -h-8 -h-auto -h-full -h-screen -helper-text -hidden -hover:bg-indigo-500 -hover:bg-primary/5 -hover:bg-transparent -hover:cursor-pointer -hover:text-base-content -hover:text-base-content/60 -hover:text-indigo-500 -hover:text-primary -image-full -indicator -indicator-item -inline -inline-block -inline-flex -inline-grid -input -input-floating -input-floating-label -input-lg -input-md -input-sm -input-xl -input-xs -inset-0 -inset-y-0 -inset-y-2 -invisible -is-invalid -is-valid -isolate -italic -items-center -items-end -items-start -join -join-item -justify-between -justify-center -justify-end -justify-start -justify-stretch -label-text -leading-3 -leading-3.5 -leading-6 -leading-9 -leading-[1.1] -leading-none -left-0 -lg:bg-base-100/20 -lg:flex -lg:gap-y-0 -lg:grid-cols-2 -lg:hidden -lg:justify-end -lg:justify-start -lg:min-w-0 -lg:p-10 -lg:p-8 -lg:pb-5 -lg:pl-64 -lg:pr-3.5 -lg:pt-5 -lg:px-8 -lg:ring-1 -lg:ring-base-content/10 -lg:rounded-lg -lg:shadow-xs -lg:table-cell -link -link-animated -link-hover -link-primary -list-disc -list-inside -list-none -loading -loading-lg -loading-sm -loading-spinner -loading-xl -loading-xs -lowercase -m-10 -mask -max-h-52 -max-lg:flex-col -max-lg:hidden -max-md:flex-wrap -max-md:justify-center -max-md:w-full -max-sm:hidden -max-w-64 -max-w-7xl -max-w-8 -max-w-80 -max-w-full -max-w-lg -max-w-md -max-w-sm -max-w-xl -mb-0.5 -mb-1 -mb-1.5 -mb-2 -mb-2.5 -mb-25 -mb-3 -mb-4 -mb-5 -mb-8 -md:col-span-1 -md:col-span-6 -md:flex -md:gap-2 -md:grid-cols-2 -md:hidden! -md:items-center -md:min-w-md -md:navbar-end -md:table-cell -md:text-3xl -me-1 -me-1.5 -me-2 -me-2.5 -me-5 -menu -menu-active -menu-disabled -menu-dropdown -menu-dropdown-show -menu-focus -menu-horizontal -menu-sm -menu-title -min-h-0 -min-h-full -min-h-screen -min-h-svh -min-w-0 -min-w-28 -min-w-48 -min-w-60 -min-w-[100px] -min-w-full -min-w-sm -mix-blend-exclusion -ml-3 -ml-4 -modal -modal-content -modal-dialog -modal-middle -modal-title -mr-2 -mr-4 -ms-0.5 -ms-1 -ms-1.5 -ms-2 -ms-2.5 -ms-3 -ms-auto -mt-1 -mt-1.5 -mt-10 -mt-12 -mt-2 -mt-2.5 -mt-3 -mt-3.5 -mt-4 -mt-5 -mt-6 -mt-8 -mx-1 -mx-auto -my-2.5 -my-20 -my-3 -my-5 -my-8 -my-auto -navbar -navbar-start -object-cover -opacity-0 -opacity-100 -opacity-75 -open -origin-top-left -outline -outline-0 -overflow-hidden -overflow-x-auto -overflow-x-hidden -overflow-y-auto -overlay-open:duration-50 -overlay-open:opacity-100 -overlay-open:translate-x-0 -overlay-open:translate-y-0 -overscroll-contain -p-0 -p-0.5 -p-1 -p-1.5 -p-12 -p-2 -p-2.5 -p-3 -p-4 -p-5 -p-6 -p-8 -pb-1 -pe-1 -pe-1.5 -pin-input -pin-input-underline -placeholder-base-content/60 -placeholder:text-gray-600 -pointer-events-auto -pointer-events-none -progress -progress-bar -progress-indeterminate -progress-primary -pt-0 -pt-0.5 -pt-1.5 -pt-2 -pt-3 -px-1.5 -px-2 -px-2.5 -px-3 -px-4 -px-5 -px-6 -py-0.5 -py-1.5 -py-10 -py-12 -py-2 -py-2.5 -py-6 -radial-progress -radio -range -relative -resize -right-0 -ring -ring-0 -ring-1 -ring-gray-700 -ring-inset -ring-white/10 -rounded-box -rounded-field -rounded-full -rounded-lg -rounded-md -rounded-t-lg -rounded-xl -row-active -row-hover -rtl:!mr-0 -select -select-disabled:opacity-40 -select-disabled:pointer-events-none -select-floating -select-floating-label -select-lg -select-md -select-sm -select-xl -select-xs -selected -selected:select-active -shadow -shadow-base-300/20 -shadow-lg -shadow-md -shadow-none -shadow-sm -shadow-xs -shrink-0 -size-10 -size-12 -size-4 -size-5 -size-7 -size-8 -size-8.5 -skeleton -skeleton-animated -sm:auto-cols-max -sm:col-span-2 -sm:flex -sm:flex-nowrap -sm:gap-y-0 -sm:grid-cols-3 -sm:grid-cols-6 -sm:items-center -sm:items-end -sm:justify-between -sm:justify-end -sm:leading-6 -sm:max-w-2xl -sm:max-w-3xl -sm:max-w-4xl -sm:max-w-5xl -sm:max-w-6xl -sm:max-w-7xl -sm:max-w-[480px] -sm:max-w-lg -sm:max-w-md -sm:max-w-xl -sm:mb-0 -sm:min-w-md -sm:mr-3 -sm:mt-5 -sm:mt-6 -sm:mx-auto -sm:p-6 -sm:px-12 -sm:px-6 -sm:py-2 -sm:rounded-lg -sm:text-sm -sm:text-sm/5 -sm:w-1/2 -sm:w-full -space-x-1 -space-x-2.5 -space-y-1 -space-y-4 -space-y-6 -sr-only -stack -stat -stat-actions -stat-desc -stat-figure -stat-title -stat-value -static -stats -stats-border -status -status-error -sticky -success-message -switch -tab -tab-active -tab-content -table -table-pin-cols -table-pin-rows -tabs -tabs-bordered -tabs-lg -tabs-lifted -tabs-md -tabs-sm -tabs-vertical -tabs-xl -tabs-xs -text-2xl -text-3xl -text-4xl -text-5xl -text-accent -text-base -text-base-content -text-base-content/40 -text-base-content/50 -text-base-content/60 -text-base-content/70 -text-base-content/80 -text-base-content/90 -text-base/6 -text-center -text-error -text-error-content -text-gray-100 -text-gray-400 -text-gray-500 -text-indigo-600 -text-info -text-info-content -text-left -text-lg -text-primary -text-primary-content -text-right -text-slate-100 -text-sm -text-sm/5 -text-success -text-success-content -text-transparent -text-warning -text-warning-content -text-white -text-xl -text-xl! -text-xs -text-xs/5 -textarea -textarea-floating -textarea-floating-label -textarea-lg -textarea-md -textarea-sm -textarea-xl -textarea-xs -theme-controller -to-purple-400 -tooltip -tooltip-content -top-0 -top-1/2 -top-3 -top-full -tracking-tight -tracking-wide -transform -transition -transition-all -transition-opacity -transition-transform -translate-x-0 -translate-x-full -truncate -underline -uppercase -validate -via-sky-400 -w-0 -w-0.5 -w-12 -w-13 -w-20 -w-32 -w-4 -w-56 -w-6 -w-64 -w-8 -w-auto -w-fit -w-full -whitespace-nowrap -xl:flex -xl:grid-cols-2 -xl:grid-cols-3 -xl:grid-cols-4 -z-1 -z-10 -z-40 -z-50 -z-69 -z-70 \ No newline at end of file diff --git a/MoonlightServers.Frontend/Styles/Moonlight.Client/mooncore.map b/MoonlightServers.Frontend/Styles/Moonlight.Client/mooncore.map deleted file mode 100755 index b14f36e..0000000 --- a/MoonlightServers.Frontend/Styles/Moonlight.Client/mooncore.map +++ /dev/null @@ -1,516 +0,0 @@ -!bg-base-100 -!border-base-content/40 -!border-none -!flex -!font-medium -!font-semibold -!h-2.5 -!justify-between -!me-1.5 -!ms-auto -!px-2.5 -!rounded-full -!text-sm -!w-2.5 -*:[grid-area:1/1] -*:first:rounded-tl-lg -*:last:rounded-tr-lg --left-4 --ml-4 --translate-x-full --translate-y-1/2 -absolute -accordion -accordion-bordered -accordion-toggle -active -active-tab:bg-primary -active-tab:text-base-content -advance-select-menu -advance-select-option -advance-select-tag -advance-select-toggle -alert -alert-error -alert-outline -alert-soft -align-bottom -align-middle -animate-bounce -animate-ping -aria-[current='page']:text-bg-soft-primary -avatar -avatar-away-bottom -avatar-away-top -avatar-busy-bottom -avatar-busy-top -avatar-offline-bottom -avatar-offline-top -avatar-online-bottom -avatar-online-top -avatar-placeholder -badge -badge-error -badge-info -badge-outline -badge-primary -badge-soft -badge-success -bg-background -bg-background/60 -bg-base-100 -bg-base-150 -bg-base-200 -bg-base-200/50 -bg-base-300 -bg-base-300/45 -bg-base-300/50 -bg-base-300/60 -bg-error -bg-info -bg-primary -bg-primary/5 -bg-success -bg-transparent -bg-warning -block -blur -border -border-0 -border-2 -border-b -border-base-content -border-base-content/20 -border-base-content/40 -border-base-content/5 -border-dashed -border-t -border-transparent -bottom-0 -bottom-full -break-words -btn -btn-accent -btn-active -btn-circle -btn-disabled -btn-error -btn-info -btn-outline -btn-primary -btn-secondary -btn-sm -btn-soft -btn-square -btn-success -btn-text -btn-warning -card -card-alert -card-body -card-border -card-footer -card-header -card-title -carousel -carousel-body -carousel-next -carousel-prev -carousel-slide -chat -chat-avatar -chat-bubble -chat-footer -chat-header -chat-receiver -chat-sender -checkbox -checkbox-primary -checkbox-xs -col-span-1 -collapse -combo-box-selected:block -combo-box-selected:dropdown-active -complete -container -contents -cursor-default -cursor-not-allowed -cursor-pointer -diff -disabled -divide-base-150/60 -divide-y -drop-shadow -dropdown -dropdown-disabled -dropdown-item -dropdown-menu -dropdown-open:opacity-100 -dropdown-open:rotate-180 -dropdown-toggle -duration-300 -duration-500 -ease-in-out -ease-linear -end-3 -file-upload-complete:progress-success -fill-black -filter -filter-reset -fixed -flex -flex-1 -flex-col -flex-grow -flex-nowrap -flex-row -flex-shrink-0 -flex-wrap -focus-visible:outline-none -focus-within:border-primary -focus:border-primary -focus:outline-1 -focus:outline-none -focus:outline-primary -focus:ring-0 -font-bold -font-inter -font-medium -font-normal -font-semibold -gap-0.5 -gap-1 -gap-1.5 -gap-2 -gap-3 -gap-4 -gap-5 -gap-6 -gap-x-1 -gap-x-2 -gap-x-3 -gap-y-1 -gap-y-3 -grid -grid-cols-1 -grid-flow-col -grow -grow-0 -h-12 -h-2 -h-32 -h-64 -h-8 -h-auto -h-full -h-screen -helper-text -hidden -hover:bg-primary/5 -hover:bg-transparent -hover:text-base-content -hover:text-base-content/60 -hover:text-primary -image-full -inline -inline-block -inline-flex -inline-grid -input -input-floating -input-floating-label -input-lg -input-md -input-sm -input-xl -inset-0 -inset-y-0 -inset-y-2 -invisible -is-invalid -is-valid -isolate -italic -items-center -items-end -items-start -join -join-item -justify-between -justify-center -justify-end -justify-start -justify-stretch -label-text -leading-3 -leading-3.5 -leading-6 -left-0 -lg:bg-base-100/20 -lg:flex -lg:gap-y-0 -lg:grid-cols-2 -lg:hidden -lg:justify-end -lg:justify-start -lg:min-w-0 -lg:p-10 -lg:pb-5 -lg:pl-64 -lg:pr-3.5 -lg:pt-5 -lg:ring-1 -lg:ring-base-content/10 -lg:rounded-lg -lg:shadow-xs -list-disc -list-inside -loading -loading-lg -loading-sm -loading-spinner -loading-xl -loading-xs -lowercase -m-10 -mask -max-h-52 -max-lg:flex-col -max-lg:hidden -max-w-7xl -max-w-80 -max-w-full -max-w-lg -max-w-sm -max-w-xl -mb-0.5 -mb-1 -mb-2 -mb-3 -mb-4 -mb-5 -md:table-cell -md:text-3xl -me-1 -me-1.5 -me-2 -me-5 -menu -menu-active -menu-disabled -menu-dropdown -menu-dropdown-show -menu-focus -menu-horizontal -menu-title -min-h-0 -min-h-svh -min-w-0 -min-w-28 -min-w-48 -min-w-60 -min-w-[100px] -ml-3 -ml-4 -modal -modal-content -modal-dialog -modal-middle -modal-title -mr-4 -ms-1 -ms-2 -ms-3 -mt-1 -mt-1.5 -mt-10 -mt-12 -mt-2 -mt-2.5 -mt-3 -mt-4 -mt-5 -mt-8 -mx-1 -mx-auto -my-3 -my-auto -opacity-0 -opacity-100 -open -origin-top-left -outline -outline-0 -overflow-hidden -overflow-x-auto -overflow-y-auto -overlay-open:duration-50 -overlay-open:opacity-100 -p-0.5 -p-1 -p-2 -p-3 -p-4 -p-5 -p-6 -p-8 -pin-input -pin-input-underline -placeholder-base-content/60 -pointer-events-auto -pointer-events-none -progress -progress-bar -progress-indeterminate -progress-primary -pt-0 -pt-0.5 -pt-3 -px-1.5 -px-2 -px-2.5 -px-3 -px-4 -px-5 -py-0.5 -py-1.5 -py-2 -py-2.5 -py-6 -radio -range -relative -resize -ring-0 -ring-1 -ring-white/10 -rounded-box -rounded-field -rounded-full -rounded-lg -rounded-md -rounded-t-lg -row-active -row-hover -rtl:!mr-0 -select -select-disabled:opacity-40 -select-disabled:pointer-events-none -select-floating -select-floating-label -selected -selected:select-active -shadow-base-300/20 -shadow-lg -shadow-xs -shrink-0 -size-10 -size-4 -size-5 -size-8 -skeleton -skeleton-animated -sm:auto-cols-max -sm:flex -sm:items-center -sm:items-end -sm:justify-between -sm:justify-end -sm:max-w-2xl -sm:max-w-3xl -sm:max-w-4xl -sm:max-w-5xl -sm:max-w-6xl -sm:max-w-7xl -sm:max-w-lg -sm:max-w-md -sm:max-w-xl -sm:mb-0 -sm:mt-5 -sm:mt-6 -sm:p-6 -sm:py-2 -sm:text-sm/5 -space-x-1 -space-y-1 -space-y-4 -sr-only -static -status -status-error -sticky -switch -tab -tab-active -table -table-pin-cols -table-pin-rows -tabs -tabs-bordered -tabs-lg -tabs-lifted -tabs-md -tabs-sm -tabs-xl -tabs-xs -text-2xl -text-4xl -text-accent -text-base -text-base-content -text-base-content/40 -text-base-content/50 -text-base-content/60 -text-base-content/70 -text-base-content/80 -text-base/6 -text-center -text-error -text-error-content -text-gray-400 -text-info -text-info-content -text-left -text-lg -text-primary -text-primary-content -text-sm -text-sm/5 -text-success -text-success-content -text-warning -text-warning-content -text-xl -text-xs -text-xs/5 -textarea -textarea-floating -textarea-floating-label -theme-controller -tooltip -tooltip-content -top-0 -top-1/2 -top-full -transform -transition -transition-all -transition-opacity -translate-x-0 -truncate -underline -uppercase -validate -w-0 -w-0.5 -w-12 -w-4 -w-56 -w-64 -w-fit -w-full -whitespace-nowrap -z-10 -z-40 -z-50 \ No newline at end of file diff --git a/MoonlightServers.Frontend/Styles/mappings/classes.map b/MoonlightServers.Frontend/Styles/mappings/classes.map deleted file mode 100644 index a34e84c..0000000 --- a/MoonlightServers.Frontend/Styles/mappings/classes.map +++ /dev/null @@ -1,790 +0,0 @@ -!bg-base-100 -!border-base-content/40 -!border-none -!flex -!font-medium -!font-semibold -!h-2.5 -!justify-between -!me-1.5 -!ms-auto -!px-2.5 -!py-0.5 -!rounded-full -!rounded-xs -!text-sm -!w-2.5 -*:[grid-area:1/1] -*:first:rounded-tl-lg -*:last:rounded-tr-lg --left-4 --mb-3 --ml-4 --translate-x-full --translate-y-1/2 -2xl:col-span-1 -2xl:col-span-2 -2xl:col-span-3 -2xl:flex -[animation-duration:0.8s] -[animation-timing-function:ease] -absolute -accordion -accordion-bordered -accordion-toggle -active -active-tab:bg-primary -active-tab:hover:text-primary-content -active-tab:text-base-content -active-tab:text-primary-content -advance-select-menu -advance-select-option -advance-select-tag -advance-select-toggle -alert -alert-error -alert-outline -alert-primary -alert-soft -align-bottom -align-middle -animate-bounce -animate-ping -animate-spin -aria-[current='page']:text-bg-soft-primary -avatar -avatar-away-bottom -avatar-away-top -avatar-busy-bottom -avatar-busy-top -avatar-offline-bottom -avatar-offline-top -avatar-online-bottom -avatar-online-top -avatar-placeholder -badge -badge-error -badge-info -badge-outline -badge-primary -badge-soft -badge-success -bg-background -bg-background/60 -bg-base-100 -bg-base-150 -bg-base-200 -bg-base-200! -bg-base-200/50 -bg-base-200/75 -bg-base-300 -bg-base-300/45 -bg-base-300/50 -bg-base-300/60 -bg-base-content/10 -bg-black -bg-clip-text -bg-error -bg-gradient-to-r -bg-gray-800 -bg-gray-900 -bg-indigo-600 -bg-info -bg-primary -bg-primary/5 -bg-red-500 -bg-success -bg-transparent -bg-warning -bg-white/5 -block -blur -border -border-0 -border-1 -border-2 -border-3 -border-accent -border-b -border-b-2 -border-b-base-content/20 -border-b-primary -border-base-content -border-base-content/10 -border-base-content/20 -border-base-content/25 -border-base-content/40 -border-base-content/5 -border-base-content/60 -border-base-content/70 -border-base-content/80 -border-dashed -border-dotted -border-e-2 -border-error -border-error/30 -border-gray-600 -border-info/30 -border-l-4 -border-l-8 -border-l-transparent -border-primary -border-r-transparent -border-solid -border-success -border-success/30 -border-t -border-t-2 -border-t-transparent -border-transparent -border-warning -border-warning/30 -bottom-0 -bottom-full -breadcrumbs -breadcrumbs-separator -break-words -btn -btn-accent -btn-active -btn-block -btn-circle -btn-disabled -btn-error -btn-gradient -btn-info -btn-lg -btn-outline -btn-primary -btn-secondary -btn-sm -btn-soft -btn-square -btn-success -btn-text -btn-warning -card -card-alert -card-body -card-border -card-footer -card-header -card-sm -card-title -carousel -carousel-body -carousel-next -carousel-prev -carousel-slide -chat -chat-avatar -chat-bubble -chat-footer -chat-header -chat-receiver -chat-sender -checkbox -checkbox-primary -checkbox-xs -col-span-1 -col-span-12 -col-span-2 -col-span-6 -collapse -combo-box-selected:block -combo-box-selected:dropdown-active -complete -container -contents -cursor-default -cursor-not-allowed -cursor-pointer -custom-option -diff -disabled -divide-base-150/60 -divide-y -divider -drop-shadow -dropdown -dropdown-active -dropdown-disabled -dropdown-item -dropdown-menu -dropdown-open:opacity-100 -dropdown-open:rotate-180 -dropdown-toggle -duration-300 -duration-500 -ease-in-out -ease-linear -end-3 -error-message -file-upload-complete:progress-success -fill-base-content -fill-black -fill-gray-200 -filter -filter-reset -fixed -flex -flex-1 -flex-col -flex-grow -flex-nowrap -flex-row -flex-shrink-0 -flex-wrap -focus-visible:outline -focus-visible:outline-2 -focus-visible:outline-indigo-600 -focus-visible:outline-none -focus-visible:outline-offset-2 -focus-within:border-primary -focus:border-primary -focus:outline-1 -focus:outline-none -focus:outline-primary -focus:ring-0 -focus:ring-2 -focus:ring-indigo-600 -focus:ring-inset -font-bold -font-inter -font-medium -font-normal -font-semibold -footer -footer-center -from-base-100 -from-base-100/20 -from-error/20 -from-gray-600/20 -from-primary/20 -from-success/20 -from-violet-400 -from-warning/20 -gap-0.5 -gap-1 -gap-1.5 -gap-2 -gap-3 -gap-4 -gap-5 -gap-6 -gap-x-1 -gap-x-1.5 -gap-x-2 -gap-x-3 -gap-x-5 -gap-x-6 -gap-y-1 -gap-y-1.5 -gap-y-2 -gap-y-2.5 -gap-y-3 -gap-y-4 -gap-y-5 -gap-y-8 -grid -grid-cols-1 -grid-cols-12 -grid-cols-2 -grid-cols-3 -grid-cols-4 -grid-cols-6 -grid-flow-col -grow -grow-0 -h-0 -h-10 -h-12 -h-14 -h-2 -h-3 -h-32 -h-44 -h-6 -h-64 -h-8 -h-[90vh] -h-auto -h-full -h-screen -helper-text -hidden -hover:bg-indigo-500 -hover:bg-primary/20 -hover:bg-primary/5 -hover:bg-transparent -hover:cursor-pointer -hover:text-base-content -hover:text-base-content/60 -hover:text-indigo-500 -hover:text-primary -image-full -indicator -indicator-item -inline -inline-block -inline-flex -inline-grid -input -input-floating -input-floating-label -input-lg -input-md -input-sm -input-xl -input-xs -inset-0 -inset-y-0 -inset-y-2 -invisible -is-invalid -is-valid -isolate -italic -items-center -items-end -items-start -join -join-item -justify-between -justify-center -justify-end -justify-start -justify-stretch -label-text -leading-3 -leading-3.5 -leading-6 -leading-7 -leading-9 -leading-[1.1] -leading-none -left-0 -lg:bg-base-100/20 -lg:flex -lg:gap-y-0 -lg:grid-cols-2 -lg:grid-cols-3 -lg:hidden -lg:justify-end -lg:justify-start -lg:mb-0 -lg:min-w-0 -lg:p-10 -lg:p-8 -lg:pb-5 -lg:pl-64 -lg:pr-3.5 -lg:pt-5 -lg:px-8 -lg:ring-1 -lg:ring-base-content/10 -lg:rounded-lg -lg:shadow-xs -link -link-animated -link-hover -link-primary -list-disc -list-inside -list-none -loading -loading-lg -loading-sm -loading-spinner -loading-xl -loading-xs -lowercase -m-10 -mask -max-h-52 -max-lg:flex-col -max-lg:hidden -max-md:flex-wrap -max-md:justify-center -max-sm:hidden -max-w-2xl -max-w-7xl -max-w-8 -max-w-80 -max-w-full -max-w-lg -max-w-md -max-w-none -max-w-sm -max-w-xl -mb-0.5 -mb-1 -mb-1.5 -mb-2 -mb-2.5 -mb-3 -mb-4 -mb-5 -mb-8 -md:col-span-1 -md:col-span-2 -md:col-span-6 -md:flex -md:gap-x-5 -md:grid-cols-2 -md:grid-cols-3 -md:min-w-md -md:table-cell -md:text-3xl -me-0.5 -me-1 -me-1.5 -me-2 -me-2.5 -me-3 -me-4 -me-5 -menu -menu-active -menu-disabled -menu-dropdown -menu-dropdown-show -menu-focus -menu-horizontal -menu-title -min-h-0 -min-h-full -min-h-screen -min-h-svh -min-w-0 -min-w-28 -min-w-48 -min-w-60 -min-w-[100px] -min-w-full -min-w-sm -mix-blend-exclusion -ml-3 -ml-4 -modal -modal-content -modal-dialog -modal-middle -modal-title -mr-2 -mr-3 -mr-4 -ms-0.5 -ms-1 -ms-1.5 -ms-2 -ms-2.5 -ms-3 -ms-5 -ms-auto -mt-1 -mt-1.5 -mt-10 -mt-12 -mt-2 -mt-2.5 -mt-3 -mt-3.5 -mt-4 -mt-5 -mt-6 -mt-8 -mt-auto -mx-1 -mx-auto -my-2.5 -my-3 -my-5 -my-8 -my-auto -object-cover -opacity-0 -opacity-100 -opacity-75 -open -origin-top-left -outline -outline-0 -overflow-hidden -overflow-x-auto -overflow-x-hidden -overflow-y-auto -overlay-open:duration-50 -overlay-open:opacity-100 -p-0.5 -p-1 -p-1.5 -p-2 -p-2.5 -p-3 -p-4 -p-5 -p-6 -p-8 -pb-1 -pe-1.5 -pin-input -pin-input-underline -placeholder-base-content/50 -placeholder-base-content/60 -placeholder:text-gray-600 -pointer-events-auto -pointer-events-none -progress -progress-bar -progress-indeterminate -progress-primary -pt-0 -pt-0.5 -pt-1.5 -pt-2 -pt-3 -pt-6 -px-1.5 -px-2 -px-2.5 -px-3 -px-4 -px-5 -px-6 -py-0.5 -py-1 -py-1.5 -py-10 -py-12 -py-2 -py-2.5 -py-3 -py-3.5 -py-6 -radial-progress -radio -range -relative -resize -right-4 -ring -ring-0 -ring-1 -ring-gray-700 -ring-inset -ring-white/10 -rounded-box -rounded-field -rounded-full -rounded-l-none -rounded-lg -rounded-md -rounded-none -rounded-r-none -rounded-t-lg -rounded-xl -row-active -row-hover -rtl:!mr-0 -select -select-disabled:opacity-40 -select-disabled:pointer-events-none -select-floating -select-floating-label -select-lg -select-md -select-sm -select-xl -select-xs -selected -selected:select-active -shadow -shadow-base-300/20 -shadow-lg -shadow-md -shadow-sm -shadow-xs -shrink-0 -size-10 -size-12 -size-4 -size-5 -size-7 -size-8 -skeleton -skeleton-animated -sm:auto-cols-max -sm:col-span-2 -sm:col-span-3 -sm:col-span-4 -sm:col-span-6 -sm:flex -sm:flex-nowrap -sm:gap-y-0 -sm:grid-cols-2 -sm:grid-cols-3 -sm:grid-cols-6 -sm:items-center -sm:items-end -sm:justify-between -sm:justify-end -sm:leading-6 -sm:max-w-2xl -sm:max-w-3xl -sm:max-w-4xl -sm:max-w-5xl -sm:max-w-6xl -sm:max-w-7xl -sm:max-w-[480px] -sm:max-w-lg -sm:max-w-md -sm:max-w-xl -sm:mb-0 -sm:min-w-md -sm:mr-3 -sm:mt-5 -sm:mt-6 -sm:mx-auto -sm:p-6 -sm:px-12 -sm:px-6 -sm:py-2 -sm:rounded-lg -sm:text-sm -sm:text-sm/5 -sm:w-1/2 -sm:w-full -space-x-1 -space-x-2 -space-x-2.5 -space-y-1 -space-y-4 -space-y-6 -sr-only -stack -stat -stat-actions -stat-desc -stat-figure -stat-title -stat-value -static -stats -stats-border -status -status-error -status-primary -status-secondary -status-success -status-warning -status-xl -sticky -success-message -switch -switch-primary -tab -tab-active -tab-content -table -table-pin-cols -table-pin-rows -tabs -tabs-bordered -tabs-lg -tabs-lifted -tabs-md -tabs-sm -tabs-vertical -tabs-xl -tabs-xs -text-2xl -text-3xl -text-4xl -text-accent -text-base -text-base-content -text-base-content/40 -text-base-content/50 -text-base-content/60 -text-base-content/70 -text-base-content/80 -text-base-content/90 -text-base/6 -text-center -text-error -text-error-content -text-gray-100 -text-gray-400 -text-gray-500 -text-gray-700 -text-indigo-600 -text-info -text-info-content -text-left -text-lg -text-primary -text-primary-content -text-right -text-slate-100 -text-sm -text-sm/5 -text-success -text-success-content -text-transparent -text-warning -text-warning-content -text-white -text-xl -text-xl! -text-xs -text-xs/5 -textarea -textarea-floating -textarea-floating-label -textarea-lg -textarea-md -textarea-sm -textarea-xl -textarea-xs -theme-controller -to-25% -to-base-100/75 -to-purple-400 -tooltip -tooltip-content -top-0 -top-1/2 -top-4 -top-full -tracking-tight -tracking-wide -transform -transition -transition-all -transition-opacity -translate-x-0 -truncate -underline -uppercase -validate -via-sky-400 -w-0 -w-0.5 -w-12 -w-13 -w-32 -w-4 -w-56 -w-64 -w-8 -w-auto -w-fit -w-full -whitespace-nowrap -xl:grid-cols-3 -xl:grid-cols-4 -z-1 -z-10 -z-40 -z-50 \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/FormDialog.razor b/MoonlightServers.Frontend/UI/Components/FormDialog.razor new file mode 100644 index 0000000..14c356e --- /dev/null +++ b/MoonlightServers.Frontend/UI/Components/FormDialog.razor @@ -0,0 +1,74 @@ +@using Moonlight.Frontend.Helpers +@using MoonlightServers.Shared +@using MoonlightServers.Shared.Http.Requests +@using MoonlightServers.Shared.Http.Responses +@using ShadcnBlazor.Buttons +@using ShadcnBlazor.Dialogs +@using ShadcnBlazor.Extras.AlertDialogs +@using ShadcnBlazor.Extras.Forms +@using ShadcnBlazor.Fields +@using ShadcnBlazor.Inputs + +@inherits ShadcnBlazor.Extras.Dialogs.DialogBase + +@inject HttpClient HttpClient +@inject AlertDialogService AlertDialogService + + + Example Form + This forms removes all spaces from the input + + + + + +
+ + + + + Form Input + + Input you want to remove the spaces from + + +
+
+ + + + + + +@code +{ + private FormSubmitDto Dto = new(); + private EnhancedEditForm Form; + + private async Task OnSubmit(EditContext editContext, ValidationMessageStore validationMessageStore) + { + var response = await HttpClient.PostAsJsonAsync( + "api/form", + Dto, + SerializationContext.Default.Options + ); + + if (response.IsSuccessStatusCode) + { + var data = await response.Content.ReadFromJsonAsync( + SerializationContext.Default.Options + ); + + if (data == null) + return true; + + await AlertDialogService.InfoAsync("Result", data.Result); + + await CloseAsync(); + return true; + } + + await ProblemDetailsHelper.HandleProblemDetailsAsync(response, Dto, validationMessageStore); + return false; + } +} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Forms/Switch.razor b/MoonlightServers.Frontend/UI/Components/Forms/Switch.razor deleted file mode 100644 index ed51a23..0000000 --- a/MoonlightServers.Frontend/UI/Components/Forms/Switch.razor +++ /dev/null @@ -1,29 +0,0 @@ -@using System.Diagnostics.CodeAnalysis - -@* TODO: Extract to mooncore? *@ - -@inherits InputBase - -
- - -
- -@code -{ - protected override bool TryParseValueFromString(string? value, out bool result, [NotNullWhen(false)] out string? validationErrorMessage) - { - validationErrorMessage = null; - result = string.Equals(value, "true", StringComparison.OrdinalIgnoreCase); - return true; - } -} diff --git a/MoonlightServers.Frontend/UI/Components/FullScreenModal.razor b/MoonlightServers.Frontend/UI/Components/FullScreenModal.razor deleted file mode 100644 index 54fd349..0000000 --- a/MoonlightServers.Frontend/UI/Components/FullScreenModal.razor +++ /dev/null @@ -1,88 +0,0 @@ -@using Microsoft.Extensions.Logging -@using XtermBlazor -@inherits MoonCore.Blazor.FlyonUi.Modals.BaseModal - -@inject IJSRuntime JsRuntime -@inject ILogger Logger - -@implements IAsyncDisposable - -
- @if (IsInitialized) - { - - } - -
- -
-
- -@code -{ - [Parameter] public XtermConsole Parent { get; set; } - - private bool IsInitialized = false; - private bool IsReadyToWrite = false; - private Xterm Terminal; - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (!firstRender) - return; - - // Initialize addons - - try - { - await JsRuntime.InvokeVoidAsync("moonlightServers.loadAddons"); - } - catch (Exception e) - { - Logger.LogError("An error occured while initializing addons: {e}", e); - } - - // Subscribe to parent events - Parent.OnWrite += HandleWriteAsync; - - IsInitialized = true; - await InvokeAsync(StateHasChanged); - } - - private async Task HandleFirstRenderAsync() - { - IsReadyToWrite = true; - - try - { - await Terminal.Addon("addon-fit").InvokeVoidAsync("fit"); - } - catch (Exception e) - { - Logger.LogError("An error occured while calling addons: {e}", e); - } - - var outputToWrite = string.Concat(Parent.OutputCache.ToArray()); - await Terminal.Write(outputToWrite); - } - - private async Task HandleWriteAsync(string content) - { - if (!IsReadyToWrite) - return; - - await Terminal.Write(content); - } - - public async ValueTask DisposeAsync() - { - Parent.OnWrite -= HandleWriteAsync; - await Terminal.DisposeAsync(); - } -} diff --git a/MoonlightServers.Frontend/UI/Components/Nodes/Modals/CreateAllocationModal.razor b/MoonlightServers.Frontend/UI/Components/Nodes/Modals/CreateAllocationModal.razor deleted file mode 100644 index cf44bb7..0000000 --- a/MoonlightServers.Frontend/UI/Components/Nodes/Modals/CreateAllocationModal.razor +++ /dev/null @@ -1,62 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonlightServers.Shared.Http.Requests.Admin.NodeAllocations - -@inherits MoonCore.Blazor.FlyonUi.Modals.BaseModal - -
-
-
-
- -
-
-
-

Add a new allocation

-

Add a new allocation to the selected node

-
-
-
- -
- - -
-
- - -
-
-
-
- - - Create - -
-
- -@code -{ - [Parameter] public Func OnSubmit { get; set; } - - private CreateNodeAllocationRequest Form; - private HandleForm HandleForm; - - protected override void OnInitialized() - { - Form = new() - { - IpAddress = "0.0.0.0" - }; - } - - private async Task OnValidSubmit() - { - await OnSubmit.Invoke(Form); - await HideAsync(); - } - - private Task SubmitAsync() => HandleForm.SubmitAsync(); -} diff --git a/MoonlightServers.Frontend/UI/Components/Nodes/Modals/CreateMultipleAllocationModal.razor b/MoonlightServers.Frontend/UI/Components/Nodes/Modals/CreateMultipleAllocationModal.razor deleted file mode 100644 index 09d6fef..0000000 --- a/MoonlightServers.Frontend/UI/Components/Nodes/Modals/CreateMultipleAllocationModal.razor +++ /dev/null @@ -1,68 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonlightServers.Shared.Http.Requests.Admin.NodeAllocations - -@inherits MoonCore.Blazor.FlyonUi.Modals.BaseModal - -
-
-
-
- -
-
-
-

Add multiple allocations

-

Add a range of new allocations to the selected node

-
-
-
- -
- - -
-
- - -
-
- - -
-
-
-
- - - Create - -
-
- -@code -{ - [Parameter] public Func OnSubmit { get; set; } - - private CreateNodeAllocationRangeRequest Form; - private HandleForm HandleForm; - - protected override void OnInitialized() - { - Form = new() - { - IpAddress = "0.0.0.0", - Start = 2000, - End = 3000 - }; - } - - private async Task OnValidSubmit() - { - await OnSubmit.Invoke(Form); - await HideAsync(); - } - - private Task SubmitAsync() => HandleForm.SubmitAsync(); -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Nodes/Modals/UpdateAllocationModal.razor b/MoonlightServers.Frontend/UI/Components/Nodes/Modals/UpdateAllocationModal.razor deleted file mode 100644 index df2e610..0000000 --- a/MoonlightServers.Frontend/UI/Components/Nodes/Modals/UpdateAllocationModal.razor +++ /dev/null @@ -1,65 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonlightServers.Shared.Http.Requests.Admin.NodeAllocations -@using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations - -@inherits MoonCore.Blazor.FlyonUi.Modals.BaseModal - -
-
-
-
- -
-
-
-

Update allocation

-

Update an existing allocation

-
-
-
- -
- - -
-
- - -
-
-
-
- - - Update - -
-
- -@code -{ - [Parameter] public Func OnSubmit { get; set; } - [Parameter] public NodeAllocationResponse Allocation { get; set; } - - private UpdateNodeAllocationRequest Form; - private HandleForm HandleForm; - - protected override void OnInitialized() - { - Form = new UpdateNodeAllocationRequest() - { - IpAddress = Allocation.IpAddress, - Port = Allocation.Port - }; - } - - private async Task OnValidSubmit() - { - await OnSubmit.Invoke(Form); - await HideAsync(); - } - - private Task SubmitAsync() => HandleForm.SubmitAsync(); -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Nodes/UpdatePartials/Advanced.razor b/MoonlightServers.Frontend/UI/Components/Nodes/UpdatePartials/Advanced.razor deleted file mode 100644 index 8defcd5..0000000 --- a/MoonlightServers.Frontend/UI/Components/Nodes/UpdatePartials/Advanced.razor +++ /dev/null @@ -1,10 +0,0 @@ -@using MoonlightServers.Shared.Http.Requests.Admin.Nodes - -
- -
- -@code -{ - [Parameter] public UpdateNodeRequest Request { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Nodes/UpdatePartials/Allocations.razor b/MoonlightServers.Frontend/UI/Components/Nodes/UpdatePartials/Allocations.razor deleted file mode 100644 index 0475f81..0000000 --- a/MoonlightServers.Frontend/UI/Components/Nodes/UpdatePartials/Allocations.razor +++ /dev/null @@ -1,150 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Alerts -@using MoonCore.Blazor.FlyonUi.Common -@using MoonCore.Blazor.FlyonUi.Grid -@using MoonCore.Blazor.FlyonUi.Grid.Columns -@using MoonCore.Blazor.FlyonUi.Modals -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Common -@using MoonlightServers.Shared.Http.Responses.Admin.Nodes -@using MoonCore.Helpers -@using MoonlightServers.Frontend.UI.Components.Nodes.Modals -@using MoonlightServers.Shared.Http.Requests.Admin.NodeAllocations -@using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations - -@inject HttpApiClient ApiClient -@inject ModalService ModalService -@inject ToastService ToastService -@inject AlertService AlertService - -
-
-
-
- Actions -
-
-
- - - -
-
-
-
-
- - - - - - - - - -
-
- -@code -{ - [Parameter] public NodeResponse Node { get; set; } - - private DataGrid Grid; - - private ItemSource ItemSource => ItemSourceFactory.From(LoadAsync); - - private async Task> LoadAsync(int startIndex, int count) - { - var query = $"?startIndex={startIndex}&count={count}"; - - return await ApiClient.GetJson>( - $"api/admin/servers/nodes/{Node.Id}/allocations{query}" - ); - } - - private async Task AddAllocationRangeAsync() - { - Func onSubmit = async request => - { - await ApiClient.Post($"api/admin/servers/nodes/{Node.Id}/allocations/range", request); - - await ToastService.SuccessAsync("Successfully created allocations"); - await Grid.RefreshAsync(); - }; - - await ModalService.LaunchAsync(parameters => { parameters.Add("OnSubmit", onSubmit); }); - } - - private async Task AddAllocationAsync() - { - Func onSubmit = async request => - { - await ApiClient.Post($"api/admin/servers/nodes/{Node.Id}/allocations", request); - - await ToastService.SuccessAsync("Successfully created allocation"); - await Grid.RefreshAsync(); - }; - - await ModalService.LaunchAsync(parameters => { parameters.Add("OnSubmit", onSubmit); }); - } - - private async Task UpdateAllocationAsync(NodeAllocationResponse allocation) - { - Func onSubmit = async request => - { - await ApiClient.Patch($"api/admin/servers/nodes/{Node.Id}/allocations/{allocation.Id}", request); - - await ToastService.SuccessAsync("Successfully updated allocation"); - await Grid.RefreshAsync(); - }; - - await ModalService.LaunchAsync(parameters => - { - parameters.Add("OnSubmit", onSubmit); - parameters.Add("Allocation", allocation); - }); - } - - private async Task DeleteAllocationAsync(NodeAllocationResponse allocation) - { - await AlertService.ConfirmDangerAsync( - "Delete allocation", - "Do you really want to delete the selected allocation? This cannot be undone", - async () => - { - await ApiClient.Delete($"api/admin/servers/nodes/{Node.Id}/allocations/{allocation.Id}"); - - await ToastService.SuccessAsync("Successfully deleted allocation"); - await Grid.RefreshAsync(); - } - ); - } - - private async Task DeleteAllAllocationsAsync() - { - await AlertService.ConfirmDangerAsync( - "Delete all allocations", - "Do you really want to delete all allocations? This cannot be undone", - async () => - { - await ApiClient.Delete($"api/admin/servers/nodes/{Node.Id}/allocations/all"); - - await ToastService.SuccessAsync("Successfully deleted allocations"); - await Grid.RefreshAsync(); - } - ); - } -} diff --git a/MoonlightServers.Frontend/UI/Components/Nodes/UpdatePartials/General.razor b/MoonlightServers.Frontend/UI/Components/Nodes/UpdatePartials/General.razor deleted file mode 100644 index 66f1ae7..0000000 --- a/MoonlightServers.Frontend/UI/Components/Nodes/UpdatePartials/General.razor +++ /dev/null @@ -1,32 +0,0 @@ -@using MoonlightServers.Shared.Http.Requests.Admin.Nodes - -
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
-
-
- -@code -{ - [Parameter] public UpdateNodeRequest Request { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Nodes/UpdatePartials/Overview.razor b/MoonlightServers.Frontend/UI/Components/Nodes/UpdatePartials/Overview.razor deleted file mode 100644 index 1d82c75..0000000 --- a/MoonlightServers.Frontend/UI/Components/Nodes/UpdatePartials/Overview.razor +++ /dev/null @@ -1,217 +0,0 @@ -@using Microsoft.Extensions.Logging -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Helpers -@using MoonlightServers.Frontend.Services -@using MoonlightServers.Shared.Http.Responses.Admin.Nodes -@using MoonlightServers.Shared.Http.Responses.Admin.Nodes.Statistics - -@inject NodeService NodeService -@inject ToastService ToastService -@inject ILogger Logger - -@implements IDisposable - - - -
- Overview -
- -
-
-
-

- @(Math.Round(Statistics.Cpu.Usage, 2))% -

- -
-
- - CPU: @Statistics.Cpu.Model - -
-
- -
-
-

- @(Formatter.FormatSize(Statistics.Memory.Total - Statistics.Memory.Available)) - / - @(Formatter.FormatSize(Statistics.Memory.Total)) -

- -
-
- Memory -
-
- -
-
-
- @(Formatter.FormatSize(Statistics.Memory.SwapTotal - Statistics.Memory.SwapFree)) - / - @(Formatter.FormatSize(Statistics.Memory.SwapTotal)) -
- -
-
- Swap -
-
-
- -
- CPU -
- -
- @{ - var i = 0; - } - - @foreach (var usage in Statistics.Cpu.UsagePerCore) - { - var percentRounded = Math.Round(usage, 2); - -
-
- #@(i) -
-
-
-
-
-
-
- - i++; - } -
- -
- Disks -
- -
- @foreach (var disk in Statistics.Disks) - { - var usedPercent = Math.Round((disk.DiskTotal - disk.DiskFree) / (double)disk.DiskTotal * 100, 2); - var iNodesPercent = Math.Round((disk.InodesTotal - disk.InodesFree) / (double)disk.InodesTotal * 100, 2); - -
-
-
-
-
-
-
-
-
-
- Device: @disk.Device - Mounted at: @disk.MountPath -
-
- Used: @Formatter.FormatSize(disk.DiskTotal - disk.DiskFree) - Total: @Formatter.FormatSize(disk.DiskTotal) -
-
- INodes: @(iNodesPercent)% -
-
-
- } -
- -
- Docker -
- -
-
-
-

- @Formatter.FormatSize(DockerStatistics.ImagesUsed) (@Formatter.FormatSize(DockerStatistics.ImagesReclaimable) unused) -

- -
-
- Images -
-
- -
-
-

- @Formatter.FormatSize(DockerStatistics.ContainersUsed) ( @Formatter.FormatSize(DockerStatistics.ContainersReclaimable) unused) -

- -
-
- Containers -
-
- -
-
-

- @Formatter.FormatSize(DockerStatistics.BuildCacheUsed) (@Formatter.FormatSize(DockerStatistics.BuildCacheReclaimable) unused) -

- -
-
- Build Cache -
-
-
-
- -@code -{ - [Parameter] public NodeResponse Node { get; set; } - - private StatisticsResponse Statistics; - private DockerStatisticsResponse DockerStatistics; - - private Timer? UpdateTimer; - - private async Task LoadAsync(LazyLoader _) - { - Statistics = await NodeService.GetStatisticsAsync(Node.Id); - DockerStatistics = await NodeService.GetDockerStatisticsAsync(Node.Id); - - UpdateTimer = new Timer(HandleUpdateAsync, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3)); - } - - private async void HandleUpdateAsync(object? _) - { - try - { - Statistics = await NodeService.GetStatisticsAsync(Node.Id); - DockerStatistics = await NodeService.GetDockerStatisticsAsync(Node.Id); - - await InvokeAsync(StateHasChanged); - } - catch (Exception e) - { - Logger.LogWarning("An error occured while fetching status update: {e}", e); - await ToastService.ErrorAsync("Unable to fetch status update", e.Message); - } - } - - private string GetBackgroundColorByPercent(double percent) - { - if (percent < 70) - return "bg-success"; - else if (percent < 80) - return "bg-warning"; - else - return "bg-error"; - } - - public void Dispose() - { - UpdateTimer?.Dispose(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/CreatePartials/Advanced.razor b/MoonlightServers.Frontend/UI/Components/Servers/CreatePartials/Advanced.razor deleted file mode 100644 index 61872ca..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/CreatePartials/Advanced.razor +++ /dev/null @@ -1,10 +0,0 @@ -@using MoonlightServers.Shared.Http.Requests.Admin.Servers - -
- -
- -@code -{ - [Parameter] public CreateServerRequest Request { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/CreatePartials/Allocations.razor b/MoonlightServers.Frontend/UI/Components/Servers/CreatePartials/Allocations.razor deleted file mode 100644 index 5e82390..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/CreatePartials/Allocations.razor +++ /dev/null @@ -1,42 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Common -@using MoonlightServers.Shared.Http.Requests.Admin.Servers -@using MoonCore.Helpers -@using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations -@using MoonCore.Blazor.FlyonUi.Forms -@using MoonCore.Common -@using MoonlightServers.Frontend.UI.Views.Admin.All - -@inject HttpApiClient ApiClient - -
-
- -
- - -
-
-
- -@code -{ - [Parameter] public CreateServerRequest Request { get; set; } - [Parameter] public Create Parent { get; set; } - - private ItemSource ItemSource => ItemSourceFactory.From(LoadAsync); - - private async Task> LoadAsync(int startIndex, int count) - { - // Handle unselected node - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - if (Parent.Node == null) - return []; - - return await ApiClient.GetJson>( - $"api/admin/servers/nodes/{Parent.Node.Id}/allocations/free?startIndex={startIndex}&count={count}" - ); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/CreatePartials/General.razor b/MoonlightServers.Frontend/UI/Components/Servers/CreatePartials/General.razor deleted file mode 100644 index dddb6e6..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/CreatePartials/General.razor +++ /dev/null @@ -1,116 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Common -@using MoonlightServers.Shared.Http.Requests.Admin.Servers -@using MoonCore.Helpers -@using MoonlightServers.Shared.Http.Responses.Admin.Nodes -@using MoonlightServers.Shared.Http.Responses.Admin.Stars -@using Moonlight.Shared.Http.Responses.Admin.Users -@using MoonCore.Blazor.FlyonUi.Forms -@using MoonCore.Common -@using MoonlightServers.Frontend.UI.Views.Admin.All - -@inject HttpApiClient ApiClient - -
-
- -
- -
-
- -
- -
- - -
-
- -
- -
- - -
-
- -
- -
- - -
-
-
- -
-
-

- Resources -

-

Define the servers resource limit

-
- -
-
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
-
-
- -@code -{ - [Parameter] public CreateServerRequest Request { get; set; } - [Parameter] public Create Parent { get; set; } - - private ItemSource StarsItemSource => ItemSourceFactory.From(LoadStarsAsync); - private ItemSource NodesItemSource => ItemSourceFactory.From(LoadNodesAsync); - private ItemSource UsersItemSource => ItemSourceFactory.From(LoadUsersAsync); - - private async Task> LoadStarsAsync(int startIndex, int count) - { - return await ApiClient.GetJson>( - $"api/admin/servers/stars?startIndex={startIndex}&count={count}" - ); - } - - private async Task> LoadNodesAsync(int startIndex, int count) - { - return await ApiClient.GetJson>( - $"api/admin/servers/nodes?startIndex={startIndex}&count={count}" - ); - } - - private async Task> LoadUsersAsync(int startIndex, int count) - { - return await ApiClient.GetJson>( - $"api/admin/users?startIndex={startIndex}&count={count}" - ); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/CreatePartials/Variables.razor b/MoonlightServers.Frontend/UI/Components/Servers/CreatePartials/Variables.razor deleted file mode 100644 index 77b5b2f..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/CreatePartials/Variables.razor +++ /dev/null @@ -1,89 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Common -@using MoonlightServers.Shared.Http.Requests.Admin.Servers -@using MoonCore.Helpers -@using MoonlightServers.Frontend.UI.Views.Admin.All -@using MoonlightServers.Shared.Http.Requests.Admin.ServerVariables -@using MoonlightServers.Shared.Http.Responses.Admin.StarVariables - -@inject HttpApiClient ApiClient - - -
- @foreach (var variable in StarVariables) - { - // Load value of default - var requestVar = Request.Variables.FirstOrDefault(x => x.Key == variable.Key); - var value = requestVar != null - ? requestVar.Value - : variable.DefaultValue; - -
- -
- -
-

- @variable.Description -

-
- } -
-
- -@code -{ - [Parameter] public CreateServerRequest Request { get; set; } - [Parameter] public Create Parent { get; set; } - - private StarVariableResponse[] StarVariables; - - private async Task LoadAsync(LazyLoader _) - { - if (Parent.Star == null) - { - StarVariables = []; - return; - } - - StarVariables = await CountedData.AllAsync(async (index, count) => - await ApiClient.GetJson>( - $"api/admin/servers/stars/{Parent.Star.Id}/variables?startIndex={index}&count={count}" - ) - ); - } - - private async Task UpdateValueAsync(StarVariableResponse starVariable, ChangeEventArgs args) - { - var value = args.Value?.ToString() ?? ""; - - // Remove variable from request when set to its default value - if (value == starVariable.DefaultValue && Request.Variables.Any(x => x.Key == starVariable.Key)) - Request.Variables.RemoveAll(x => x.Key == starVariable.Key); - else - { - var serverVar = Request.Variables - .FirstOrDefault(x => x.Key == starVariable.Key); - - if (serverVar == null) - { - serverVar = new CreateServerVariableRequest() - { - Key = starVariable.Key - }; - - Request.Variables.Add(serverVar); - } - - serverVar.Value = value; - } - - await InvokeAsync(StateHasChanged); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/CreateShareModal.razor b/MoonlightServers.Frontend/UI/Components/Servers/CreateShareModal.razor deleted file mode 100644 index 97a4227..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/CreateShareModal.razor +++ /dev/null @@ -1,76 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonlightServers.Shared.Enums -@using MoonlightServers.Shared.Http.Requests.Client.Servers.Shares -@using MoonlightServers.Shared.Http.Responses.Client.Servers - -@inherits MoonCore.Blazor.FlyonUi.Modals.BaseModal - -
-
-
-
- -
-
-
-

Create a new share

-

Grant access to this server to other users

-
-
-
- -
- - -
-
- -
-
-
-
- - - Create - -
-
- -@code -{ - [Parameter] public ServerDetailResponse Server { get; set; } - [Parameter] public string Username { get; set; } - [Parameter] public Func OnSubmit { get; set; } - - private HandleForm HandleForm; - private CreateShareRequest Request; - - private Dictionary Permissions = new(); - - protected override void OnInitialized() - { - Request = new() - { - Username = Username - }; - } - - private async Task SetAsync(string name, ServerPermissionLevel level) - { - Permissions[name] = level; - await InvokeAsync(StateHasChanged); - } - - private async Task SubmitAsync() - => await HandleForm.SubmitAsync(); - - private async Task OnValidSubmit() - { - Request.Permissions = Permissions; - - await OnSubmit.Invoke(Request); - await HideAsync(); - } -} diff --git a/MoonlightServers.Frontend/UI/Components/Servers/PermissionEditor.razor b/MoonlightServers.Frontend/UI/Components/Servers/PermissionEditor.razor deleted file mode 100644 index 286d665..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/PermissionEditor.razor +++ /dev/null @@ -1,95 +0,0 @@ -@using MoonlightServers.Frontend.Interfaces -@using MoonCore.Blazor.FlyonUi.Components -@using MoonlightServers.Frontend.Models -@using MoonlightServers.Shared.Enums -@using MoonlightServers.Shared.Http.Responses.Client.Servers - -@inject IEnumerable PermissionProviders - - -
- @foreach (var permission in AvailablePermissions) - { - var level = PermissionLevels.GetValueOrDefault(permission.Identifier, ServerPermissionLevel.None); - -
- -
- @permission.DisplayName - @permission.Description -
-
- -
-
- @if (level == ServerPermissionLevel.None) - { - - } - else - { - - } - - @if (level == ServerPermissionLevel.Read) - { - - } - else - { - - } - - @if (level == ServerPermissionLevel.ReadWrite) - { - - } - else - { - - } -
-
- } -
-
- -@code -{ - [Parameter] public ServerDetailResponse Server { get; set; } - [Parameter] public Dictionary PermissionLevels { get; set; } - - private ServerPermission[] AvailablePermissions; - - private async Task LoadAsync(LazyLoader _) - { - var permissions = new List(); - - foreach (var provider in PermissionProviders) - { - permissions.AddRange( - await provider.GetPermissionsAsync(Server) - ); - } - - AvailablePermissions = permissions.ToArray(); - } - - private async Task SetAsync(string name, ServerPermissionLevel level) - { - PermissionLevels[name] = level; - await InvokeAsync(StateHasChanged); - } -} diff --git a/MoonlightServers.Frontend/UI/Components/Servers/ServerCard.razor b/MoonlightServers.Frontend/UI/Components/Servers/ServerCard.razor deleted file mode 100644 index 71d5c52..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/ServerCard.razor +++ /dev/null @@ -1,189 +0,0 @@ -@using Microsoft.Extensions.Logging -@using MoonCore.Helpers -@using MoonlightServers.Frontend.Services -@using MoonlightServers.Shared.Enums -@using MoonlightServers.Shared.Http.Responses.Client.Servers - -@inject ServerService ServerService -@inject ILogger Logger - -@{ - var gradient = "from-base-100/20"; - var border = "border-base-content/80"; - - if (IsLoaded && !IsFailed) - { - gradient = Status.State switch - { - ServerState.Installing => "from-primary/20", - ServerState.Offline => "from-error/20", - ServerState.Starting => "from-warning/20", - ServerState.Stopping => "from-warning/20", - ServerState.Online => "from-success/20", - _ => "from-base-100" - }; - - border = Status.State switch - { - ServerState.Installing => "border-primary", - ServerState.Offline => "border-error", - ServerState.Starting => "border-warning", - ServerState.Stopping => "border-warning", - ServerState.Online => "border-success", - _ => "border-base-content/80" - }; - } -} - - -
-
-
- -
- @Server.Name -
-
-
- - - - -
-
- -@code -{ - [Parameter] public ServerDetailResponse Server { get; set; } - - private ServerStatusResponse Status; - private ServerStatsResponse Stats; - - private bool IsFailed = false; - private bool IsLoaded = false; - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (!firstRender) - return; - - try - { - Status = await ServerService.GetStatusAsync(Server.Id); - Stats = await ServerService.GetStatsAsync(Server.Id); - } - catch (Exception e) - { - IsFailed = true; - Logger.LogWarning("Unable to fetch status from server {id}: {e}", Server.Id, e); - } - - IsLoaded = true; - await InvokeAsync(StateHasChanged); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/BaseServerTab.razor b/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/BaseServerTab.razor deleted file mode 100644 index 0e2e676..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/BaseServerTab.razor +++ /dev/null @@ -1,13 +0,0 @@ -@using Microsoft.AspNetCore.SignalR.Client -@using MoonlightServers.Frontend.UI.Views.Client -@using MoonlightServers.Shared.Enums -@using MoonlightServers.Shared.Http.Responses.Client.Servers - -@code -{ - [Parameter] public ServerDetailResponse Server { get; set; } - [Parameter] public ServerState State { get; set; } - [Parameter] public string InitialConsoleMessage { get; set; } - [Parameter] public HubConnection? HubConnection { get; set; } - [Parameter] public Manage Parent { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/ConsoleTab.razor b/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/ConsoleTab.razor deleted file mode 100644 index cd59b71..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/ConsoleTab.razor +++ /dev/null @@ -1,39 +0,0 @@ -@using Microsoft.AspNetCore.SignalR.Client -@using MoonlightServers.Frontend.Services - -@inherits BaseServerTab - -@inject ServerService ServerService - -
- -
- -@code -{ - private XtermConsole? XtermConsole; - - protected override Task OnInitializedAsync() - { - // We are already connected to the hub at this point - - HubConnection.On("ConsoleOutput", async content => - { - if (XtermConsole != null) - await XtermConsole.WriteAsync(content); - }); - - return Task.CompletedTask; - } - - private async Task OnAfterConsoleInitialized() - { - await XtermConsole!.WriteAsync(InitialConsoleMessage); - } - - private async Task OnCommand(string command) - => await ServerService.RunCommandAsync(Server.Id, command + "\n"); -} diff --git a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/FilesTab.razor b/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/FilesTab.razor deleted file mode 100644 index a3506d3..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/FilesTab.razor +++ /dev/null @@ -1,22 +0,0 @@ -@using MoonlightServers.Frontend.Services -@using MoonCore.Blazor.FlyonUi.Files.Manager -@using MoonlightServers.Frontend.Helpers - -@inherits BaseServerTab - -@inject ServerFileSystemService FileSystemService - - - -@code -{ - private IFsAccess FsAccess; - - protected override void OnInitialized() - { - FsAccess = new ServerFsAccess( - Server.Id, - FileSystemService - ); - } -} diff --git a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/SettingsTab.razor b/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/SettingsTab.razor deleted file mode 100644 index 8cacfc8..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/SettingsTab.razor +++ /dev/null @@ -1,40 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Alerts -@using MoonCore.Blazor.FlyonUi.Components -@using MoonlightServers.Frontend.Services -@using MoonlightServers.Shared.Enums - -@inherits BaseServerTab - -@inject ServerService ServerService -@inject AlertService AlertService - -
-
- @if (State != ServerState.Offline) - { - - } - else - { - - - Reinstall - - } -
-
- -@code -{ - private async Task ReinstallAsync(WButton _) - { - await AlertService.ConfirmDangerAsync( - "Server installation", - "Do you really want to reinstall the server? This can potentially lead to loss of data", - () => ServerService.InstallAsync(Server.Id) - ); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/SharesTab.razor b/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/SharesTab.razor deleted file mode 100644 index 7b86a3a..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/SharesTab.razor +++ /dev/null @@ -1,123 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Alerts -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Blazor.FlyonUi.Modals -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Common -@using MoonlightServers.Frontend.Services -@using MoonlightServers.Shared.Http.Requests.Client.Servers.Shares -@using MoonlightServers.Shared.Http.Responses.Client.Servers.Shares - -@inherits BaseServerTab - -@inject ServerShareService ShareService -@inject ModalService ModalService -@inject ToastService ToastService -@inject AlertService AlertService - -
- - -
- - - @if (Shares.Length == 0) - { - - Enter a username and press invite to share this server to another user - - } - else - { -
- @foreach (var share in Shares) - { -
-
- - @share.Username -
-
- - - Edit - - - - Delete - -
-
- } -
- } -
- -@code -{ - private ServerShareResponse[] Shares; - - private string UsernameInput = ""; - - private LazyLoader LazyLoader; - - private async Task LoadAsync(LazyLoader _) - { - Shares = await CountedData.AllAsync(async (startIndex, count) - => await ShareService.GetAsync(Server.Id, startIndex, count) - ); - } - - private async Task OpenCreateModalAsync() - { - await ModalService.LaunchAsync(parameters => - { - parameters["Username"] = UsernameInput; - parameters["Server"] = Server; - parameters["OnSubmit"] = SubmitCreateAsync; - }, size: "max-w-2xl"); - } - - private async Task SubmitCreateAsync(CreateShareRequest request) - { - await ShareService.CreateAsync(Server.Id, request); - - await ToastService.SuccessAsync("Share successfully created"); - await LazyLoader.ReloadAsync(); - } - - private async Task OpenUpdateModalAsync(ServerShareResponse share) - { - await ModalService.LaunchAsync(parameters => - { - parameters["Share"] = share; - parameters["Server"] = Server; - parameters["OnSubmit"] = (UpdateShareRequest request) => SubmitUpdateAsync(share.Id, request); - }, size: "max-w-2xl"); - } - - private async Task SubmitUpdateAsync(int shareId, UpdateShareRequest request) - { - await ShareService.UpdateAsync(Server.Id, shareId, request); - - await ToastService.SuccessAsync("Share successfully updated"); - await LazyLoader.ReloadAsync(); - } - - private async Task DeleteAsync(ServerShareResponse share) - { - await AlertService.ConfirmDangerAsync( - "Share deletion", - $"Do you really want to delete the share for the user '{share.Username}'? This cannot be undone", - async () => - { - await ShareService.DeleteAsync(Server.Id, share.Id); - - await ToastService.SuccessAsync("Successfully deleted share"); - await LazyLoader.ReloadAsync(); - } - ); - } -} diff --git a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/VariablesTab.razor b/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/VariablesTab.razor deleted file mode 100644 index 8cd6580..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/VariablesTab.razor +++ /dev/null @@ -1,65 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Common -@using MoonlightServers.Frontend.Services -@using MoonlightServers.Shared.Http.Responses.Client.Servers.Variables - -@inherits BaseServerTab - -@inject ServerService ServerService -@inject ToastService ToastService - - -
- @foreach (var variable in Variables) - { -
-
-
- @variable.Name -
-
-
-

- @variable.Description -

-
- -
-
-
- } -
-
- -@code -{ - private ServerVariableDetailResponse[] Variables; - private LazyLoader LazyLoader; - - private async Task LoadAsync(LazyLoader _) - { - Variables = await CountedData.AllAsync(async (startIndex, count) - => await ServerService.GetVariablesAsync(Server.Id, startIndex, count) - ); - } - - private async Task UpdateVariableAsync(ServerVariableDetailResponse variable, ChangeEventArgs args) - { - var value = args.Value?.ToString() ?? ""; - - await ServerService.UpdateVariableAsync(Server.Id, new() - { - Key = variable.Key, - Value = value - }); - - // Fetch the current data to make sure the user sees the latest data - await LazyLoader.ReloadAsync(); - - await ToastService.SuccessAsync("Successfully updated variable"); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/TestServerCrd.razor b/MoonlightServers.Frontend/UI/Components/Servers/TestServerCrd.razor deleted file mode 100644 index 3da7934..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/TestServerCrd.razor +++ /dev/null @@ -1,125 +0,0 @@ -@using MoonlightServers.Shared.Http.Responses.Client.Servers -@{ - var gradient = Status switch - { - 4 => "from-primary/20", - 3 => "from-danger/20", - 2 => "from-warning/20", - 1 => "from-success/20", - _ => "from-gray-600/20" - }; - - var border = Status switch - { - 4 => "border-primary", - 3 => "border-danger", - 2 => "border-warning", - 1 => "border-success", - _ => "border-gray-600" - }; -} - - -
-
-
- -
- @Server.Name -
-
-
- - - - -
- - -
- -@code -{ - [Parameter] - public ServerDetailResponse Server { get; set; } - - [Parameter] - public int Status { get; set; } -} diff --git a/MoonlightServers.Frontend/UI/Components/Servers/UpdatePartials/Advanced.razor b/MoonlightServers.Frontend/UI/Components/Servers/UpdatePartials/Advanced.razor deleted file mode 100644 index 9b5b879..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/UpdatePartials/Advanced.razor +++ /dev/null @@ -1,9 +0,0 @@ -@using MoonlightServers.Shared.Http.Requests.Admin.Servers - -
-
- -@code -{ - [Parameter] public UpdateServerRequest Request { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/UpdatePartials/Allocations.razor b/MoonlightServers.Frontend/UI/Components/Servers/UpdatePartials/Allocations.razor deleted file mode 100644 index d404d5f..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/UpdatePartials/Allocations.razor +++ /dev/null @@ -1,39 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Common -@using MoonlightServers.Shared.Http.Requests.Admin.Servers -@using MoonCore.Helpers -@using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations -@using MoonlightServers.Shared.Http.Responses.Admin.Servers -@using MoonCore.Blazor.FlyonUi.Forms -@using MoonCore.Common -@using MoonlightServers.Frontend.UI.Views.Admin.All - -@inject HttpApiClient ApiClient - -
-
- -
- - -
-
-
- -@code -{ - [Parameter] public UpdateServerRequest Request { get; set; } - [Parameter] public ServerResponse Server { get; set; } - [Parameter] public Update Parent { get; set; } - - private ItemSource ItemSource => ItemSourceFactory.From(LoadAsync); - - private async Task> LoadAsync(int startIndex, int count) - { - return await ApiClient.GetJson>( - $"api/admin/servers/nodes/{Server.NodeId}/allocations/free?startIndex={startIndex}&count={count}&serverId={Server.Id}" - ); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/UpdatePartials/General.razor b/MoonlightServers.Frontend/UI/Components/Servers/UpdatePartials/General.razor deleted file mode 100644 index 7e6d3b3..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/UpdatePartials/General.razor +++ /dev/null @@ -1,75 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Common -@using MoonlightServers.Shared.Http.Requests.Admin.Servers -@using MoonCore.Helpers -@using Moonlight.Shared.Http.Responses.Admin.Users -@using MoonCore.Blazor.FlyonUi.Forms -@using MoonCore.Common -@using MoonlightServers.Frontend.UI.Views.Admin.All - -@inject HttpApiClient ApiClient - -
-
- -
- -
-
- -
- -
- - - -
-
-
- -
-

- Resources -

-

Define the servers resource limit

- -
-
- -
- -
-
- -
- -
- -
-
- -
- -
- -
-
-
-
- -@code -{ - [Parameter] public UpdateServerRequest Request { get; set; } - [Parameter] public Update Parent { get; set; } - - private ItemSource UserItemSource => ItemSourceFactory.From(LoadAsync); - - private async Task> LoadAsync(int startIndex, int count) - { - return await ApiClient.GetJson>( - $"api/admin/users?startIndex={startIndex}&count={count}" - ); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/UpdatePartials/Variables.razor b/MoonlightServers.Frontend/UI/Components/Servers/UpdatePartials/Variables.razor deleted file mode 100644 index 9b6dd73..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/UpdatePartials/Variables.razor +++ /dev/null @@ -1,93 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Common -@using MoonlightServers.Shared.Http.Requests.Admin.Servers -@using MoonCore.Helpers -@using MoonlightServers.Shared.Http.Requests.Admin.ServerVariables -@using MoonlightServers.Shared.Http.Responses.Admin.Servers -@using MoonlightServers.Shared.Http.Responses.Admin.ServerVariables -@using MoonlightServers.Shared.Http.Responses.Admin.StarVariables - -@inject HttpApiClient ApiClient - - -
- @foreach (var variable in ServerVariables) - { - var reqVariable = Request.Variables.FirstOrDefault(x => x.Key == variable.Key); - var starVariable = StarVariables.FirstOrDefault(x => x.Key == variable.Key); - - // Ignore all variables which aren't defined in the star - if (starVariable == null) - continue; - - var value = reqVariable != null - ? reqVariable.Value - : variable.Value; - -
- -
- -
-

- @starVariable.Description -

-
- } -
-
- -@code -{ - [Parameter] public UpdateServerRequest Request { get; set; } - [Parameter] public ServerResponse Server { get; set; } - - private StarVariableResponse[] StarVariables; - private ServerVariableResponse[] ServerVariables; - - private async Task LoadAsync(LazyLoader _) - { - StarVariables = await CountedData.AllAsync(async (startIndex, count) => - await ApiClient.GetJson>( - $"api/admin/servers/stars/{Server.StarId}/variables?startIndex={startIndex}&count={count}" - ) - ); - - ServerVariables = await CountedData.AllAsync(async (startIndex, count) => - await ApiClient.GetJson>( - $"api/admin/servers/{Server.Id}/variables?startIndex={startIndex}&count={count}" - ) - ); - } - - private async Task UpdateValueAsync(ServerVariableResponse serverVariable, ChangeEventArgs args) - { - var value = args.Value?.ToString() ?? ""; - - // Remove variable from request when set to its default value - if (value == serverVariable.Value && Request.Variables.Any(x => x.Key == serverVariable.Key)) - Request.Variables.RemoveAll(x => x.Key == serverVariable.Key); - else - { - var serverVar = Request.Variables - .FirstOrDefault(x => x.Key == serverVariable.Key); - - if (serverVar == null) - { - serverVar = new UpdateServerVariableRequest() - { - Key = serverVariable.Key - }; - - Request.Variables.Add(serverVar); - } - - serverVar.Value = value; - } - - await InvokeAsync(StateHasChanged); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/UpdateShareModal.razor b/MoonlightServers.Frontend/UI/Components/Servers/UpdateShareModal.razor deleted file mode 100644 index 064af66..0000000 --- a/MoonlightServers.Frontend/UI/Components/Servers/UpdateShareModal.razor +++ /dev/null @@ -1,70 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonlightServers.Shared.Enums -@using MoonlightServers.Shared.Http.Requests.Client.Servers.Shares -@using MoonlightServers.Shared.Http.Responses.Client.Servers -@using MoonlightServers.Shared.Http.Responses.Client.Servers.Shares - -@inherits MoonCore.Blazor.FlyonUi.Modals.BaseModal - -
-
-
-
- -
-
-
-

Update share for @Share.Username

-

Grant access to this server to other users

-
-
-
- - - -
-
- - - Create - -
-
- -@code -{ - [Parameter] public ServerDetailResponse Server { get; set; } - [Parameter] public ServerShareResponse Share { get; set; } - [Parameter] public Func OnSubmit { get; set; } - - private HandleForm HandleForm; - private UpdateShareRequest Request; - - private Dictionary Permissions = new(); - - protected override void OnInitialized() - { - Request = new(); - - Permissions = Share.Permissions; - } - - private async Task SetAsync(string name, ServerPermissionLevel level) - { - Permissions[name] = level; - await InvokeAsync(StateHasChanged); - } - - private async Task SubmitAsync() - => await HandleForm.SubmitAsync(); - - private async Task OnValidSubmit() - { - Request.Permissions = Permissions; - - await OnSubmit.Invoke(Request); - await HideAsync(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Stars/Modals/CreateDockerImageModal.razor b/MoonlightServers.Frontend/UI/Components/Stars/Modals/CreateDockerImageModal.razor deleted file mode 100644 index 2730c80..0000000 --- a/MoonlightServers.Frontend/UI/Components/Stars/Modals/CreateDockerImageModal.razor +++ /dev/null @@ -1,68 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonlightServers.Frontend.UI.Components.Forms -@using MoonlightServers.Shared.Http.Requests.Admin.StarDockerImages - -@inherits MoonCore.Blazor.FlyonUi.Modals.BaseModal - -
-
-
-
- -
-
-
-

Add a new docker image

-

Add a new docker image to the star

-
-
-
- -
- - -
-
- - -
-
- - -
-
-
-
- - - Create - -
-
- -@code -{ - [Parameter] public Func OnSubmit { get; set; } - - private CreateStarDockerImageRequest Form; - private HandleForm HandleForm; - - protected override void OnInitialized() - { - // Set default values - Form = new() - { - AutoPulling = true - }; - } - - private async Task OnValidSubmit() - { - await OnSubmit.Invoke(Form); - await HideAsync(); - } - - private Task SubmitAsync() => HandleForm.SubmitAsync(); -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Stars/Modals/CreateParseConfigModal.razor b/MoonlightServers.Frontend/UI/Components/Stars/Modals/CreateParseConfigModal.razor deleted file mode 100644 index ad460ff..0000000 --- a/MoonlightServers.Frontend/UI/Components/Stars/Modals/CreateParseConfigModal.razor +++ /dev/null @@ -1,86 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonlightServers.Shared.Enums -@using MoonlightServers.Shared.Models - -@inherits MoonCore.Blazor.FlyonUi.Modals.BaseModal - -
-
-
-
- -
-
-
-

Add a parse configuration

-

Add a new parse configuration to the star

-
-
-
- -
- - -
-
- - -
-
- -
- - @foreach (var entry in Form.Entries) - { -
- - - -
- } -
-
-
- - - Create - -
-
- -@code -{ - [Parameter] public Func OnSubmit { get; set; } - - private ParseConfiguration Form = new(); - private HandleForm HandleForm; - - private async Task OnValidSubmit() - { - await OnSubmit.Invoke(Form); - await HideAsync(); - } - - private Task SubmitAsync() => HandleForm.SubmitAsync(); - - private async Task AddEntryAsync() - { - Form.Entries.Add(new()); - await InvokeAsync(StateHasChanged); - } - - private async Task RemoveEntryAsync(ParseConfiguration.ParseConfigurationEntry entry) - { - Form.Entries.Remove(entry); - await InvokeAsync(StateHasChanged); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Stars/Modals/CreateVariableModal.razor b/MoonlightServers.Frontend/UI/Components/Stars/Modals/CreateVariableModal.razor deleted file mode 100644 index 4d137d0..0000000 --- a/MoonlightServers.Frontend/UI/Components/Stars/Modals/CreateVariableModal.razor +++ /dev/null @@ -1,94 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonlightServers.Shared.Enums -@using MoonlightServers.Shared.Http.Requests.Admin.StarVariables -@using MoonlightServers.Frontend.UI.Components.Forms - -@inherits MoonCore.Blazor.FlyonUi.Modals.BaseModal - -
-
-
-
- -
-
-
-

Add a new variable

-

Add a new variable to the star

-
-
-
- -
-
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
-
-
-
-
- - - Create - -
-
- -@code -{ - [Parameter] public Func OnSubmit { get; set; } - - private CreateStarVariableRequest Form = new(); - private HandleForm HandleForm; - - private async Task OnValidSubmit() - { - await OnSubmit.Invoke(Form); - await HideAsync(); - } - - private Task SubmitAsync() => HandleForm.SubmitAsync(); -} diff --git a/MoonlightServers.Frontend/UI/Components/Stars/Modals/UpdateDockerImageModal.razor b/MoonlightServers.Frontend/UI/Components/Stars/Modals/UpdateDockerImageModal.razor deleted file mode 100644 index 0a2d1d7..0000000 --- a/MoonlightServers.Frontend/UI/Components/Stars/Modals/UpdateDockerImageModal.razor +++ /dev/null @@ -1,71 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonlightServers.Frontend.UI.Components.Forms -@using MoonlightServers.Shared.Http.Requests.Admin.StarDockerImages -@using MoonlightServers.Shared.Http.Responses.Admin.StarDockerImages - -@inherits MoonCore.Blazor.FlyonUi.Modals.BaseModal - -
-
-
-
- -
-
-
-

Update docker image

-

Update docker image properties

-
-
-
- -
- - -
-
- - -
-
- - -
-
-
-
- - - Update - -
-
- -@code -{ - [Parameter] public Func OnSubmit { get; set; } - [Parameter] public StarDockerImageResponse DockerImage { get; set; } - - private UpdateStarDockerImageRequest Form; - private HandleForm HandleForm; - - protected override void OnInitialized() - { - Form = new UpdateStarDockerImageRequest() - { - AutoPulling = DockerImage.AutoPulling, - DisplayName = DockerImage.DisplayName, - Identifier = DockerImage.Identifier - }; - } - - private async Task OnValidSubmit() - { - await OnSubmit.Invoke(Form); - await HideAsync(); - } - - private Task SubmitAsync() => HandleForm.SubmitAsync(); -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Stars/Modals/UpdateParseConfigModal.razor b/MoonlightServers.Frontend/UI/Components/Stars/Modals/UpdateParseConfigModal.razor deleted file mode 100644 index 6c29cf9..0000000 --- a/MoonlightServers.Frontend/UI/Components/Stars/Modals/UpdateParseConfigModal.razor +++ /dev/null @@ -1,93 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonlightServers.Shared.Enums -@using MoonlightServers.Shared.Models - -@inherits MoonCore.Blazor.FlyonUi.Modals.BaseModal - -
-
-
-
- -
-
-
-

Update parse configuration

-

Update parse configuration properties

-
-
-
- -
- - -
-
- - -
-
- -
- - @foreach (var entry in Form.Entries) - { -
- - - -
- } -
-
-
- - - Update - -
-
- -@code -{ - [Parameter] public Func OnSubmit { get; set; } - [Parameter] public ParseConfiguration Configuration { get; set; } - - private ParseConfiguration Form; - private HandleForm HandleForm; - - protected override void OnInitialized() - { - // Manual mapping :( - Form = Configuration; - } - - private async Task OnValidSubmit() - { - await OnSubmit.Invoke(Form); - await HideAsync(); - } - - private Task SubmitAsync() => HandleForm.SubmitAsync(); - - private async Task AddEntryAsync() - { - Form.Entries.Add(new()); - await InvokeAsync(StateHasChanged); - } - - private async Task RemoveEntryAsync(ParseConfiguration.ParseConfigurationEntry entry) - { - Form.Entries.Remove(entry); - await InvokeAsync(StateHasChanged); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Stars/Modals/UpdateVariableModal.razor b/MoonlightServers.Frontend/UI/Components/Stars/Modals/UpdateVariableModal.razor deleted file mode 100644 index 20750b1..0000000 --- a/MoonlightServers.Frontend/UI/Components/Stars/Modals/UpdateVariableModal.razor +++ /dev/null @@ -1,111 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonlightServers.Shared.Enums -@using MoonlightServers.Shared.Http.Requests.Admin.StarVariables -@using MoonlightServers.Frontend.UI.Components.Forms -@using MoonlightServers.Shared.Http.Responses.Admin.StarVariables - -@inherits MoonCore.Blazor.FlyonUi.Modals.BaseModal - -
-
-
-
- -
-
-
-

Update variable

-

Update variable properties

-
-
-
- -
-
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
-
-
-
-
- - - Update - -
-
- -@code -{ - [Parameter] public Func OnSubmit { get; set; } - [Parameter] public StarVariableResponse Variable { get; set; } - - private UpdateStarVariableRequest Form; - private HandleForm HandleForm; - - protected override void OnInitialized() - { - Form = new() - { - Name = Variable.Name, - AllowEditing = Variable.AllowEditing, - AllowViewing = Variable.AllowViewing, - DefaultValue = Variable.DefaultValue, - Description = Variable.Description, - Filter = Variable.Filter, - Key = Variable.Key, - Type = Variable.Type - }; - } - - private async Task OnValidSubmit() - { - await OnSubmit.Invoke(Form); - await HideAsync(); - } - - private Task SubmitAsync() => HandleForm.SubmitAsync(); -} diff --git a/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/DockerImage.razor b/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/DockerImage.razor deleted file mode 100644 index 75febf5..0000000 --- a/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/DockerImage.razor +++ /dev/null @@ -1,109 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Alerts -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Blazor.FlyonUi.Modals -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Common -@using MoonCore.Helpers -@using MoonlightServers.Frontend.UI.Components.Stars.Modals -@using MoonlightServers.Shared.Http.Requests.Admin.StarDockerImages -@using MoonlightServers.Shared.Http.Responses.Admin.StarDockerImages -@using MoonlightServers.Shared.Http.Responses.Admin.Stars - -@inject HttpApiClient ApiClient -@inject ModalService ModalService -@inject ToastService ToastService -@inject AlertService AlertService - - -
- -
- -
- @foreach (var dockerImage in DockerImages) - { -
-
-
- - @dockerImage.DisplayName -
- -
- - - -
-
-
- } -
-
- -@code -{ - [Parameter] public StarResponse Star { get; set; } - - private StarDockerImageResponse[] DockerImages; - private LazyLoader LazyLoader; - - private async Task LoadAsync(LazyLoader _) - { - DockerImages = await CountedData.AllAsync(async (startIndex, count) => - await ApiClient.GetJson>( - $"api/admin/servers/stars/{Star.Id}/dockerImages?startIndex={startIndex}&count={count}" - ) - ); - } - - private async Task AddDockerImageAsync() - { - Func onSubmit = async request => - { - await ApiClient.Post($"api/admin/servers/stars/{Star.Id}/dockerImages", request); - - await ToastService.SuccessAsync("Successfully created docker image"); - await LazyLoader.ReloadAsync(); - }; - - await ModalService.LaunchAsync(parameters => { parameters.Add("OnSubmit", onSubmit); }); - } - - private async Task UpdateDockerImageAsync(StarDockerImageResponse dockerImage) - { - Func onSubmit = async request => - { - await ApiClient.Patch($"api/admin/servers/stars/{Star.Id}/dockerImages/{dockerImage.Id}", request); - - await ToastService.SuccessAsync("Successfully updated docker image"); - await LazyLoader.ReloadAsync(); - }; - - await ModalService.LaunchAsync(parameters => - { - parameters.Add("OnSubmit", onSubmit); - parameters.Add("DockerImage", dockerImage); - }); - } - - private async Task DeleteDockerImageAsync(StarDockerImageResponse dockerImage) - { - await AlertService.ConfirmDangerAsync( - "Delete docker image", - "Do you really want to delete the selected docker image? This cannot be undone", - async () => - { - await ApiClient.Delete($"api/admin/servers/stars/{Star.Id}/dockerImages/{dockerImage.Id}"); - - await ToastService.SuccessAsync("Successfully deleted docker image"); - await LazyLoader.ReloadAsync(); - } - ); - } -} diff --git a/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/General.razor b/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/General.razor deleted file mode 100644 index 76c7206..0000000 --- a/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/General.razor +++ /dev/null @@ -1,41 +0,0 @@ -@using MoonlightServers.Shared.Http.Requests.Admin.Stars - -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -@code -{ - [Parameter] public UpdateStarRequest Request { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/Installation.razor b/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/Installation.razor deleted file mode 100644 index ee23d2f..0000000 --- a/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/Installation.razor +++ /dev/null @@ -1,42 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Ace -@using MoonlightServers.Shared.Http.Requests.Admin.Stars - -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -@code -{ - [Parameter] public UpdateStarRequest Request { get; set; } - - private CodeEditor CodeEditor; - - private void OnConfigure(CodeEditorOptions options) - { - options.Mode = "ace/mode/sh"; - } - - private async Task OnFocusOut() - { - Request.InstallScript = await CodeEditor.GetValueAsync(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/Misc.razor b/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/Misc.razor deleted file mode 100644 index 40b9c06..0000000 --- a/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/Misc.razor +++ /dev/null @@ -1,69 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Common -@using MoonCore.Helpers -@using MoonlightServers.Shared.Http.Requests.Admin.Stars -@using MoonlightServers.Shared.Http.Responses.Admin.StarDockerImages -@using MoonlightServers.Shared.Http.Responses.Admin.Stars -@using MoonlightServers.Frontend.UI.Components.Forms - -@inject HttpApiClient ApiClient - - -
-
-
- -
- -
-
-
- -
- -
-
-
-
-
- -@code -{ - [Parameter] public UpdateStarRequest Request { get; set; } - [Parameter] public StarResponse Star { get; set; } - - private List DockerImages; - - private async Task LoadAsync(LazyLoader _) - { - var pagedVariables = await ApiClient.GetJson>( - $"api/admin/servers/stars/{Star.Id}/dockerImages?startIndex=0&count=100" - ); - - // TODO: Fix this - - DockerImages = pagedVariables - .Items - .OrderBy(x => x.Id) // Make sure its in the correct order every time - .ToList(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/ParseConfig.razor b/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/ParseConfig.razor deleted file mode 100644 index 0180506..0000000 --- a/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/ParseConfig.razor +++ /dev/null @@ -1,134 +0,0 @@ -@using System.Text.Json -@using Microsoft.Extensions.Logging -@using MoonCore.Blazor.FlyonUi.Alerts -@using MoonCore.Blazor.FlyonUi.Modals -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonlightServers.Frontend.UI.Components.Stars.Modals -@using MoonlightServers.Shared.Http.Requests.Admin.Stars -@using MoonlightServers.Shared.Models - -@inject ILogger Logger -@inject ModalService ModalService -@inject AlertService AlertService -@inject ToastService ToastService - -
- -
- -@if (HasParseError) -{ - -} -else -{ -
- @foreach (var configuration in Configurations) - { -
-
-
- - @configuration.File -
- -
- - - -
-
-
- } -
-} - -@code -{ - [Parameter] public UpdateStarRequest Request { get; set; } - - private List Configurations; - private bool HasParseError = false; - - protected override Task OnInitializedAsync() - { - ReadFromJson(); - return Task.CompletedTask; - } - - private async Task AddConfigAsync() - { - Func onSubmit = async configuration => - { - Configurations.Add(configuration); - SaveChanges(); - - await InvokeAsync(StateHasChanged); - await ToastService.SuccessAsync("Successfully created parse configuration"); - }; - - await ModalService.LaunchAsync(parameters => - { - parameters.Add("OnSubmit", onSubmit); - }, "max-w-xl"); - } - - private async Task UpdateConfigAsync(ParseConfiguration configuration) - { - Func onSubmit = async _ => - { - SaveChanges(); - - await InvokeAsync(StateHasChanged); - await ToastService.SuccessAsync("Successfully updated parse configuration"); - }; - - await ModalService.LaunchAsync(parameters => - { - parameters.Add("OnSubmit", onSubmit); - parameters.Add("Configuration", configuration); - }, "max-w-xl"); - } - - private async Task DeleteConfigAsync(ParseConfiguration configuration) - { - await AlertService.ConfirmDangerAsync( - "Parse configuration deletion", - "Do you really want to delete the selected parse configuration", - async () => - { - Configurations.Remove(configuration); - SaveChanges(); - - await InvokeAsync(StateHasChanged); - await ToastService.SuccessAsync("Successfully deleted parse configuration"); - } - ); - } - - private void SaveChanges() - { - Request.ParseConfiguration = JsonSerializer.Serialize(Configurations); - ReadFromJson(); - } - - private void ReadFromJson() - { - try - { - Configurations = JsonSerializer.Deserialize>(Request.ParseConfiguration) - ?? throw new ArgumentNullException(); - - HasParseError = false; - } - catch (Exception e) - { - Logger.LogWarning("An error occured while reading parse configuration: {e}", e); - HasParseError = true; - } - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/StartStopStatus.razor b/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/StartStopStatus.razor deleted file mode 100644 index c15f4f1..0000000 --- a/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/StartStopStatus.razor +++ /dev/null @@ -1,28 +0,0 @@ -@using MoonlightServers.Shared.Http.Requests.Admin.Stars -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -@code -{ - [Parameter] public UpdateStarRequest Request { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/Variables.razor b/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/Variables.razor deleted file mode 100644 index ce78720..0000000 --- a/MoonlightServers.Frontend/UI/Components/Stars/UpdatePartials/Variables.razor +++ /dev/null @@ -1,107 +0,0 @@ -@using MoonCore.Blazor.FlyonUi.Alerts -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Blazor.FlyonUi.Modals -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Common -@using MoonCore.Helpers -@using MoonlightServers.Frontend.UI.Components.Stars.Modals -@using MoonlightServers.Shared.Http.Requests.Admin.StarVariables -@using MoonlightServers.Shared.Http.Responses.Admin.Stars -@using MoonlightServers.Shared.Http.Responses.Admin.StarVariables - -@inject HttpApiClient ApiClient -@inject ModalService ModalService -@inject AlertService AlertService -@inject ToastService ToastService - -
- -
- - -
- @foreach (var variable in CurrentVariables) - { -
-
-
- - @variable.Name -
- -
- - - -
-
-
- } -
-
- -@code -{ - [Parameter] public StarResponse Star { get; set; } - - private StarVariableResponse[] CurrentVariables; - private LazyLoader LazyLoader; - - private async Task LoadAsync(LazyLoader arg) - { - CurrentVariables = await CountedData.AllAsync(async (startIndex, count) => - await ApiClient.GetJson>( - $"api/admin/servers/stars/{Star.Id}/variables?startIndex={startIndex}&count={count}" - ) - ); - } - - private async Task AddVariableAsync() - { - Func onSubmit = async request => - { - await ApiClient.Post($"api/admin/servers/stars/{Star.Id}/variables", request); - - await ToastService.SuccessAsync("Successfully created variable"); - await LazyLoader.ReloadAsync(); - }; - - await ModalService.LaunchAsync(parameters => { parameters.Add("OnSubmit", onSubmit); }, "max-w-xl"); - } - - private async Task UpdateVariableAsync(StarVariableResponse variable) - { - Func onSubmit = async request => - { - await ApiClient.Patch($"api/admin/servers/stars/{Star.Id}/variables/{variable.Id}", request); - - await ToastService.SuccessAsync("Successfully updated variable"); - await LazyLoader.ReloadAsync(); - }; - - await ModalService.LaunchAsync(parameters => - { - parameters.Add("OnSubmit", onSubmit); - parameters.Add("Variable", variable); - }, "max-w-xl"); - } - - private async Task DeleteVariableAsync(StarVariableResponse variable) - { - await AlertService.ConfirmDangerAsync( - "Delete variable", - "Do you really want to delete the selected variable? This cannot be undone", - async () => - { - await ApiClient.Delete($"api/admin/servers/stars/{Star.Id}/variables/{variable.Id}"); - - await ToastService.SuccessAsync("Successfully deleted variable"); - await LazyLoader.ReloadAsync(); - } - ); - } -} diff --git a/MoonlightServers.Frontend/UI/Components/XtermConsole.razor b/MoonlightServers.Frontend/UI/Components/XtermConsole.razor deleted file mode 100644 index 2b9a124..0000000 --- a/MoonlightServers.Frontend/UI/Components/XtermConsole.razor +++ /dev/null @@ -1,237 +0,0 @@ -@using Microsoft.Extensions.Logging -@using MoonCore.Blazor.FlyonUi.Modals -@using MoonCore.Helpers -@using XtermBlazor -@using MoonCore.Blazor.FlyonUi.Components - -@inject IJSRuntime JsRuntime -@inject ModalService ModalService -@inject ILogger Logger - -@implements IAsyncDisposable - -
- @if (IsInitialized) - { - - } - -
- - - - -
- -
-
- @if (IsPaused) - { - - } - else - { - - } - - -
-
-
- -@code -{ - [Parameter] public Func? OnAfterInitialized { get; set; } - [Parameter] public Func? OnFirstRender { get; set; } - [Parameter] public bool ShowActions { get; set; } = true; - [Parameter] public bool ShowInput { get; set; } = false; - [Parameter] public int MaxOutputCacheSize { get; set; } = 250; - [Parameter] public Func? OnCommand { get; set; } - - [Parameter] public IList CommandHistory { get; set; } = new ConcurrentList(); - - public event Func? OnWrite; - - private Xterm Terminal; - public HashSet Addons { get; } = ["addon-fit"]; - - public TerminalOptions Options { get; } = new() - { - CursorBlink = false, - CursorStyle = CursorStyle.Bar, - CursorWidth = 1, - FontFamily = "Space Mono, monospace", - DisableStdin = true, - CursorInactiveStyle = CursorInactiveStyle.None, - Theme = - { - Background = "#000000" - }, - }; - - public ConcurrentList OutputCache { get; private set; } = new(); - public ConcurrentList WriteQueue { get; private set; } = new(); - private int CommandIndex = -1; - private bool IsReadyToWrite = false; - private bool IsPaused = false; - private bool IsInitialized = false; - - private string CommandInput = ""; - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (!firstRender) - return; - - try - { - await JsRuntime.InvokeVoidAsync("moonlightServers.loadAddons"); - } - catch (Exception e) - { - Logger.LogError("An error occured while initializing addons: {e}", e); - } - - IsInitialized = true; - await InvokeAsync(StateHasChanged); - - if (OnAfterInitialized != null) - await OnAfterInitialized.Invoke(); - } - - private async Task HandleFirstRenderAsync() - { - try - { - await Terminal.Addon("addon-fit").InvokeVoidAsync("fit"); - } - catch (Exception e) - { - Logger.LogError("An error occured while calling addons: {e}", e); - } - - IsReadyToWrite = true; - - // Write queued content since initialisation started - var queueContent = string.Concat(WriteQueue); - WriteQueue.Clear(); - - await Terminal.Write(queueContent); - - if (OnFirstRender != null) - await OnFirstRender.Invoke(); - } - - public async Task WriteAsync(string content) - { - // We cache messages here as there is the chance that the console isn't ready for input while receiving write tasks - - if (IsReadyToWrite && !IsPaused) - await HandleWriteAsync(content); - else - WriteQueue.Add(content); - } - - private async Task HandleWriteAsync(string content) - { - // Update output cache and prune it if required - if (OutputCache.Count > MaxOutputCacheSize) - { - for (var i = 0; i < 50; i++) - OutputCache.RemoveAt(i); - } - - OutputCache.Add(content); - - // Trigger events - if (OnWrite != null) - await OnWrite.Invoke(content); - - // Write in own terminal - await Terminal.Write(content); - } - - private async Task OpenFullscreenAsync() - { - await ModalService.LaunchAsync(parameters => { parameters["Parent"] = this; }, size: "max-w-none"); - } - - private async Task TogglePauseAsync() - { - IsPaused = !IsPaused; - await InvokeAsync(StateHasChanged); - - if (IsPaused) - return; - - var queueContent = string.Concat(WriteQueue); - WriteQueue.Clear(); - - await HandleWriteAsync(queueContent); - } - - private async Task SubmitCommandAsync() - { - CommandHistory.Add(CommandInput); - - if (OnCommand != null) - await OnCommand.Invoke(CommandInput); - - CommandIndex = -1; - CommandInput = ""; - - await InvokeAsync(StateHasChanged); - } - - private async Task HandleKeyAsync(KeyboardEventArgs keyboard) - { - switch (keyboard.Code) - { - case "Enter": - await SubmitCommandAsync(); - break; - - case "ArrowUp" or "ArrowDown": - { - var highestIndex = CommandHistory.Count - 1; - - if (CommandIndex == -1) - CommandIndex = highestIndex; - else - { - if (keyboard.Code is "ArrowUp") - CommandIndex++; - else if (keyboard.Code is "ArrowDown") - CommandIndex--; - } - - if (CommandIndex > highestIndex) - CommandIndex = highestIndex; - - if (CommandIndex < 0) - CommandIndex = 0; - - if (CommandIndex <= highestIndex || CommandHistory.Count > 0) - CommandInput = CommandHistory[CommandIndex]; - - await InvokeAsync(StateHasChanged); - break; - } - } - } - - public async ValueTask DisposeAsync() - { - await Terminal.DisposeAsync(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Views/Admin/All/Create.razor b/MoonlightServers.Frontend/UI/Views/Admin/All/Create.razor deleted file mode 100644 index 9be37f7..0000000 --- a/MoonlightServers.Frontend/UI/Views/Admin/All/Create.razor +++ /dev/null @@ -1,79 +0,0 @@ -@page "/admin/servers/all/create" - -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Helpers -@using Moonlight.Shared.Http.Responses.Admin.Users -@using MoonlightServers.Shared.Http.Requests.Admin.Servers -@using MoonlightServers.Frontend.UI.Components.Servers.CreatePartials -@using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations -@using MoonlightServers.Shared.Http.Responses.Admin.Nodes -@using MoonlightServers.Shared.Http.Responses.Admin.Stars - -@inject HttpApiClient ApiClient -@inject NavigationManager Navigation -@inject ToastService ToastService - -@attribute [Authorize(Policy = "permissions:admin.servers.create")] - - - - - Back - - - - Create - - - -
- - - - - - - - - - - - - - - - -
- -@code -{ - private HandleForm Form; - private CreateServerRequest Request; - - public List Allocations = new(); - public UserResponse? Owner; - public StarResponse? Star; - public NodeResponse? Node; - - protected override void OnInitialized() - { - Request = new(); - } - - private async Task OnSubmit() - { - Request.AllocationIds = Allocations - .Select(x => x.Id) - .ToArray(); - - Request.StarId = Star?.Id ?? -1; - Request.NodeId = Node?.Id ?? -1; - Request.OwnerId = Owner?.Id ?? -1; - - await ApiClient.Post("api/admin/servers", Request); - - await ToastService.SuccessAsync("Successfully created Server"); - Navigation.NavigateTo("/admin/servers/all"); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Views/Admin/All/Index.razor b/MoonlightServers.Frontend/UI/Views/Admin/All/Index.razor deleted file mode 100644 index 89739c8..0000000 --- a/MoonlightServers.Frontend/UI/Views/Admin/All/Index.razor +++ /dev/null @@ -1,162 +0,0 @@ -@page "/admin/servers/all" - -@using MoonCore.Blazor.FlyonUi.Alerts -@using MoonCore.Blazor.FlyonUi.Common -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Helpers -@using MoonlightServers.Shared.Http.Responses.Admin.Nodes -@using MoonlightServers.Shared.Http.Responses.Admin.Servers -@using MoonlightServers.Shared.Http.Responses.Admin.Stars -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Blazor.FlyonUi.Grid -@using MoonCore.Blazor.FlyonUi.Grid.Columns -@using MoonCore.Common -@using Moonlight.Shared.Http.Responses.Admin.Users - -@inject HttpApiClient ApiClient -@inject AlertService AlertService -@inject ToastService ToastService - -@attribute [Authorize(Policy = "permissions:admin.servers.get")] - -
- -
- - - - - - - - - @context.Name - - - - - - @{ - var owner = Users.GetValueOrDefault(context.OwnerId); - } - - - @(owner?.Username ?? "N/A") - - - - - - @{ - var node = Nodes.GetValueOrDefault(context.NodeId); - } - - - @(node?.Name ?? "N/A") - - - - - - @{ - var star = Stars.GetValueOrDefault(context.StarId); - } - - - @(star?.Name ?? "N/A") - - - - - -
- - - - - - - -
- -
-
- -@code -{ - private DataGrid Grid; - - private Dictionary Stars = new(); - private Dictionary Nodes = new(); - private Dictionary Users = new(); - - private ItemSource ItemSource => ItemSourceFactory.From(LoadAsync); - - private async Task> LoadAsync(int startIndex, int count) - { - var query = $"?startIndex={startIndex}&count={count}"; - - var countedData = await ApiClient.GetJson>($"api/admin/servers{query}"); - - // Fetch relations - - var nodesToFetch = countedData.Items - .Where(x => !Nodes.ContainsKey(x.Id)) - .Select(x => x.Id) - .Distinct(); - - foreach (var id in nodesToFetch) - { - var node = await ApiClient.GetJson($"api/admin/servers/nodes/{id}"); - Nodes[node.Id] = node; - } - - var starsToFetch = countedData.Items - .Where(x => !Stars.ContainsKey(x.Id)) - .Select(x => x.Id) - .Distinct(); - - foreach (var id in starsToFetch) - { - var star = await ApiClient.GetJson($"api/admin/servers/stars/{id}"); - Stars[star.Id] = star; - } - - var usersToFetch = countedData.Items - .Where(x => !Users.ContainsKey(x.Id)) - .Select(x => x.Id) - .Distinct(); - - foreach (var id in usersToFetch) - { - var user = await ApiClient.GetJson($"api/admin/users/{id}"); - Users[user.Id] = user; - } - - return countedData; - } - - private async Task DeleteAsync(ServerResponse response) - { - await AlertService.ConfirmDangerAsync( - "Server deletion", - $"Do you really want to delete the server '{response.Name}'", - async () => - { - await ApiClient.Delete($"api/admin/servers/{response.Id}"); - await ToastService.SuccessAsync("Successfully deleted server"); - - await Grid.RefreshAsync(); - } - ); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Views/Admin/All/Update.razor b/MoonlightServers.Frontend/UI/Views/Admin/All/Update.razor deleted file mode 100644 index 7dc0297..0000000 --- a/MoonlightServers.Frontend/UI/Views/Admin/All/Update.razor +++ /dev/null @@ -1,101 +0,0 @@ -@page "/admin/servers/all/update/{Id:int}" - -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Helpers -@using Moonlight.Shared.Http.Responses.Admin.Users -@using MoonlightServers.Shared.Http.Requests.Admin.Servers -@using MoonlightServers.Shared.Http.Responses.Admin.Servers -@using MoonlightServers.Frontend.UI.Components.Servers.UpdatePartials -@using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations - -@inject HttpApiClient ApiClient -@inject NavigationManager Navigation -@inject ToastService ToastService - -@attribute [Authorize(Policy = "permissions:admin.servers.update")] - - - - - - Back - - - - Update - - - -
- - - - - - - - - - - - - - - - -
-
- -@code -{ - [Parameter] public int Id { get; set; } - - private HandleForm Form; - private UpdateServerRequest Request; - private ServerResponse Server; - - public List Allocations = new(); - public UserResponse Owner; - - private async Task LoadAsync(LazyLoader _) - { - Server = await ApiClient.GetJson($"api/admin/servers/{Id}"); - - Request = new() - { - Name = Server.Name, - AllocationIds = Server.AllocationIds, - OwnerId = Server.OwnerId, - Cpu = Server.Cpu, - Disk = Server.Disk, - DockerImageIndex = Server.DockerImageIndex, - Memory = Server.Memory, - StartupOverride = Server.StartupOverride - }; - - foreach (var allocationId in Server.AllocationIds) - { - var allocation = await ApiClient.GetJson( - $"api/admin/servers/nodes/{Server.NodeId}/allocations/{allocationId}" - ); - - Allocations.Add(allocation); - } - - Owner = await ApiClient.GetJson( - $"api/admin/users/{Server.OwnerId}" - ); - } - - private async Task OnSubmit() - { - Request.AllocationIds = Allocations.Select(x => x.Id).ToArray(); - Request.OwnerId = Owner.Id; - - await ApiClient.Patch($"api/admin/servers/{Id}", Request); - - await ToastService.SuccessAsync("Successfully updated server"); - Navigation.NavigateTo("/admin/servers/all"); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Views/Admin/Index.razor b/MoonlightServers.Frontend/UI/Views/Admin/Index.razor deleted file mode 100644 index 9344e4e..0000000 --- a/MoonlightServers.Frontend/UI/Views/Admin/Index.razor +++ /dev/null @@ -1,8 +0,0 @@ -@page "/admin/servers" -@using MoonCore.Blazor.FlyonUi.Components - - -@attribute [Authorize(Policy = "permissions:admin.servers.overview")] - - - diff --git a/MoonlightServers.Frontend/UI/Views/Admin/Nodes/Create.razor b/MoonlightServers.Frontend/UI/Views/Admin/Nodes/Create.razor deleted file mode 100644 index 86ef049..0000000 --- a/MoonlightServers.Frontend/UI/Views/Admin/Nodes/Create.razor +++ /dev/null @@ -1,77 +0,0 @@ -@page "/admin/servers/nodes/create" - -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Helpers -@using MoonlightServers.Shared.Http.Requests.Admin.Nodes - -@inject HttpApiClient ApiClient -@inject NavigationManager Navigation -@inject ToastService ToastService - -@attribute [Authorize(Policy = "permissions:admin.servers.nodes.create")] - - - - - Back - - - - Create - - - -
- -
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
-
- -@* -TODO: EnableTransparentMode, EnableDynamicFirewall -*@ - -@code -{ - private HandleForm Form; - private CreateNodeRequest Request; - - protected override void OnInitialized() - { - Request = new(); - } - - private async Task OnSubmit() - { - await ApiClient.Post("api/admin/servers/nodes", Request); - - await ToastService.SuccessAsync("Successfully created node"); - Navigation.NavigateTo("/admin/servers/nodes"); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Views/Admin/Nodes/Index.razor b/MoonlightServers.Frontend/UI/Views/Admin/Nodes/Index.razor deleted file mode 100644 index b2babc1..0000000 --- a/MoonlightServers.Frontend/UI/Views/Admin/Nodes/Index.razor +++ /dev/null @@ -1,245 +0,0 @@ -@page "/admin/servers/nodes" - -@using Microsoft.Extensions.Logging -@using MoonCore.Blazor.FlyonUi.Alerts -@using MoonCore.Blazor.FlyonUi.Common -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Helpers -@using MoonlightServers.Shared.Http.Responses.Admin.Nodes -@using MoonlightServers.Frontend.Services -@using MoonlightServers.Shared.Http.Responses.Admin.Nodes.Statistics -@using MoonlightServers.Shared.Http.Responses.Admin.Nodes.Sys -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Blazor.FlyonUi.Grid -@using MoonCore.Blazor.FlyonUi.Grid.Columns -@using MoonCore.Common - -@inject HttpApiClient ApiClient -@inject NodeService NodeService -@inject AlertService AlertService -@inject ToastService ToastService -@inject ILogger Logger - -@attribute [Authorize(Policy = "permissions:admin.servers.nodes.get")] - -
- -
- - - - - - - - - @context.Name - - - - - - - @{ - var isFetched = StatusResponses.TryGetValue(context.Id, out var data); - } - - @if (isFetched) - { - if (data == null) - { -
- - - API Error - -
- } - else - { - if (data.RoundtripSuccess) - { -
- - Online (@(data.Version)) -
- } - else - { -
- - - Error - - Details -
- } - } - } - else - { -
- - Loading -
- } - -
- - - @{ - var isFetched = Statistics.TryGetValue(context.Id, out var data); - } - - @if (isFetched) - { - if (data == null) - { -
- - - API Error - -
- } - else - { -
-
- - @(Math.Round(data.Cpu.Usage))% -
- -
- - - @(Math.Round((data.Memory.Total - data.Memory.Free - data.Memory.Cached) / (double)data.Memory.Total * 100))% - -
-
- } - } - else - { -
- - Loading -
- } - -
- - -
- - - - - - - -
- -
-
- -@code -{ - private DataGrid Grid; - - private Dictionary StatusResponses = new(); - private Dictionary Statistics = new(); - - private ItemSource ItemSource => ItemSourceFactory.From(LoadAsync); - - private async Task> LoadAsync(int startIndex, int count) - { - var query = $"?startIndex={startIndex}&count={count}"; - - var countedData = await ApiClient.GetJson>($"api/admin/servers/nodes{query}"); - - Statistics.Clear(); - StatusResponses.Clear(); - - Task.Run(async () => - { - foreach (var item in countedData.Items) - { - try - { - Statistics[item.Id] = await NodeService.GetStatisticsAsync(item.Id); - } - catch (Exception e) - { - Logger.LogWarning( - "An error occured while fetching statistics for node {nodeId}: {e}", - item.Id, - e - ); - - Statistics[item.Id] = null; - } - - await InvokeAsync(StateHasChanged); - - try - { - StatusResponses[item.Id] = await NodeService.GetSystemStatusAsync(item.Id); - } - catch (Exception e) - { - Logger.LogWarning( - "An error occured while fetching status for node {nodeId}: {e}", - item.Id, - e - ); - - StatusResponses[item.Id] = null; - } - - await InvokeAsync(StateHasChanged); - } - }); - - return countedData; - } - - private async Task DeleteAsync(NodeResponse response) - { - await AlertService.ConfirmDangerAsync( - "Node deletion", - $"Do you really want to delete the node '{response.Name}'", - async () => - { - await ApiClient.Delete($"api/admin/servers/nodes/{response.Id}"); - await ToastService.SuccessAsync("Successfully deleted node"); - - await Grid.RefreshAsync(); - } - ); - } - - private async Task ShowErrorDetailsAsync(int id) - { - var data = StatusResponses.GetValueOrDefault(id); - - if (data == null) - return; - - var message = $"Failed after {Math.Round(data.RoundtripTime.TotalSeconds, 2)} seconds: " + - (data.RoundtripRemoteFailure ? "(Failed at node)" : "(Failed at api server)") + - $" {data.RoundtripError}"; - - await AlertService.ErrorAsync("Node error details", message); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Views/Admin/Nodes/Update.razor b/MoonlightServers.Frontend/UI/Views/Admin/Nodes/Update.razor deleted file mode 100644 index 73307c9..0000000 --- a/MoonlightServers.Frontend/UI/Views/Admin/Nodes/Update.razor +++ /dev/null @@ -1,81 +0,0 @@ -@page "/admin/servers/nodes/update/{Id:int}" - -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Helpers -@using MoonlightServers.Shared.Http.Requests.Admin.Nodes -@using MoonlightServers.Shared.Http.Responses.Admin.Nodes -@using MoonlightServers.Frontend.UI.Components.Nodes.UpdatePartials - -@inject HttpApiClient ApiClient -@inject NavigationManager Navigation -@inject ToastService ToastService - -@attribute [Authorize(Policy = "permissions:admin.servers.nodes.update")] - - - - - - Back - - - - Update - - - -
- - - - - - - - - - - - - - - - - - - - - -
-
- -@code -{ - [Parameter] public int Id { get; set; } - - private HandleForm Form; - private UpdateNodeRequest Request; - private NodeResponse Node; - - private async Task LoadAsync(LazyLoader _) - { - Node = await ApiClient.GetJson($"api/admin/servers/nodes/{Id}"); - - Request = new UpdateNodeRequest() - { - Name = Node.Name, - Fqdn = Node.Fqdn, - FtpPort = Node.FtpPort, - HttpPort = Node.HttpPort - }; - } - - private async Task OnSubmit() - { - await ApiClient.Patch($"api/admin/servers/nodes/{Id}", Request); - - await ToastService.SuccessAsync("Successfully updated Node"); - Navigation.NavigateTo("/admin/servers/nodes"); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Views/Admin/Stars/Create.razor b/MoonlightServers.Frontend/UI/Views/Admin/Stars/Create.razor deleted file mode 100644 index b0905ad..0000000 --- a/MoonlightServers.Frontend/UI/Views/Admin/Stars/Create.razor +++ /dev/null @@ -1,67 +0,0 @@ -@page "/admin/servers/stars/create" - -@using Microsoft.AspNetCore.Components.Authorization -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Helpers -@using MoonlightServers.Shared.Http.Requests.Admin.Stars - -@inject HttpApiClient ApiClient -@inject NavigationManager Navigation -@inject ToastService ToastService - -@attribute [Authorize(Policy = "permissions:admin.servers.stars.create")] - - - - - Back - - - - Create - - - -
- -
-
- -
- -
-
-
- -
- -
-
-
-
-
- -@code -{ - [CascadingParameter] public Task AuthState { get; set; } - - private HandleForm Form; - private CreateStarRequest Request; - - protected override async Task OnInitializedAsync() - { - Request = new(); - - var authState = await AuthState; - Request.Author = authState.User.Claims.First(x => x.Type == "email").Value; - } - - private async Task OnSubmit() - { - await ApiClient.Post("api/admin/servers/stars", Request); - - await ToastService.SuccessAsync("Successfully created star"); - Navigation.NavigateTo("/admin/servers/stars"); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Views/Admin/Stars/Index.razor b/MoonlightServers.Frontend/UI/Views/Admin/Stars/Index.razor deleted file mode 100644 index 3b10c88..0000000 --- a/MoonlightServers.Frontend/UI/Views/Admin/Stars/Index.razor +++ /dev/null @@ -1,166 +0,0 @@ -@page "/admin/servers/stars" - -@using MoonCore.Blazor.FlyonUi.Alerts -@using MoonCore.Blazor.FlyonUi.Common -@using MoonCore.Blazor.FlyonUi.Helpers -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Helpers -@using MoonlightServers.Shared.Http.Responses.Admin.Stars -@using MoonCore.Exceptions -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Blazor.FlyonUi.Grid -@using MoonCore.Blazor.FlyonUi.Grid.Columns -@using MoonCore.Common - -@inject HttpApiClient ApiClient -@inject DownloadService DownloadService -@inject ToastService ToastService -@inject AlertService AlertService - -@attribute [Authorize(Policy = "permissions:admin.servers.stars.get")] - -
- -
- -
- - -
- - - - - - - @context.Name - - - - - - - -
- @if (!string.IsNullOrEmpty(context.DonateUrl)) - { - - - Donate - - } - - @if (!string.IsNullOrEmpty(context.UpdateUrl)) - { - - - Update - - } - - - - Export - - - - - - - - - -
- -
-
- -@code -{ - private DataGrid Grid; - - private ItemSource ItemSource => ItemSourceFactory.From(LoadAsync); - - private async Task> LoadAsync(int startIndex, int count) - { - var query = $"?startIndex={startIndex}&count={count}"; - - return await ApiClient.GetJson>($"api/admin/servers/stars{query}"); - } - - private async Task DeleteAsync(StarResponse response) - { - await AlertService.ConfirmDangerAsync( - "Star deletion", - $"Do you really want to delete the star '{response.Name}'", - async () => - { - await ApiClient.Delete($"api/admin/servers/stars/{response.Id}"); - await ToastService.SuccessAsync("Successfully deleted star"); - - await Grid.RefreshAsync(); - } - ); - } - - private async Task ExportAsync(StarResponse star) - { - var json = await ApiClient.GetString($"api/admin/servers/stars/{star.Id}/export"); - - var formattedFileName = star.Name.Replace(" ", "_") + ".json"; - - await DownloadService.DownloadAsync(formattedFileName, json); - await ToastService.SuccessAsync($"Successfully exported '{star.Name}'"); - } - - private async Task OnImportFiles(InputFileChangeEventArgs eventArgs) - { - IBrowserFile[] files; - - if(eventArgs.FileCount == 0) - return; - - if (eventArgs.FileCount > 1) - files = eventArgs.GetMultipleFiles().ToArray(); - else - files = [eventArgs.File]; - - foreach (var file in files) - { - try - { - if (!file.Name.EndsWith(".json")) - { - await ToastService.ErrorAsync($"Failed to import '{file.Name}': Only json files are supported"); - continue; - } - - await using var stream = file.OpenReadStream(); - var content = new MultipartFormDataContent(); - content.Add(new StreamContent(stream), "file", file.Name); - - var star = await ApiClient.PostJson("api/admin/servers/stars/import", content); - - await ToastService.SuccessAsync($"Successfully imported '{star.Name}'"); - } - catch (HttpApiException e) - { - await ToastService.ErrorAsync($"Failed to import '{file.Name}': {e.Title}"); - } - } - - await Grid.RefreshAsync(); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Views/Admin/Stars/Update.razor b/MoonlightServers.Frontend/UI/Views/Admin/Stars/Update.razor deleted file mode 100644 index 44f2ab4..0000000 --- a/MoonlightServers.Frontend/UI/Views/Admin/Stars/Update.razor +++ /dev/null @@ -1,103 +0,0 @@ -@page "/admin/servers/stars/update/{Id:int}" - -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Blazor.FlyonUi.Toasts -@using MoonCore.Helpers -@using MoonlightServers.Shared.Http.Requests.Admin.Stars -@using MoonlightServers.Shared.Http.Responses.Admin.Stars -@using MoonlightServers.Frontend.UI.Components.Stars.UpdatePartials - -@inject HttpApiClient ApiClient -@inject NavigationManager Navigation -@inject ToastService ToastService - -@attribute [Authorize(Policy = "permissions:admin.servers.stars.update")] - - - - - - Back - - - - Update - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -@code -{ - [Parameter] public int Id { get; set; } - - private HandleForm Form; - private UpdateStarRequest Request; - private StarResponse Detail; - - private async Task LoadAsync(LazyLoader _) - { - Detail = await ApiClient.GetJson($"api/admin/servers/stars/{Id}"); - Request = new() - { - Name = Detail.Name, - AllowDockerImageChange = Detail.AllowDockerImageChange, - Author = Detail.Author, - DefaultDockerImage = Detail.DefaultDockerImage, - DonateUrl = Detail.DonateUrl, - InstallDockerImage = Detail.InstallDockerImage, - InstallScript = Detail.InstallScript, - InstallShell = Detail.InstallShell, - OnlineDetection = Detail.OnlineDetection, - ParseConfiguration = Detail.ParseConfiguration, - RequiredAllocations = Detail.RequiredAllocations, - StartupCommand = Detail.StartupCommand, - StopCommand = Detail.StopCommand, - UpdateUrl = Detail.UpdateUrl, - Version = Detail.Version - }; - } - - private async Task OnSubmit() - { - await ApiClient.Patch($"api/admin/servers/stars/{Id}", Request); - - await ToastService.SuccessAsync("Successfully updated Star"); - Navigation.NavigateTo("/admin/servers/stars"); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Views/Client/Index.razor b/MoonlightServers.Frontend/UI/Views/Client/Index.razor deleted file mode 100644 index d648a54..0000000 --- a/MoonlightServers.Frontend/UI/Views/Client/Index.razor +++ /dev/null @@ -1,88 +0,0 @@ -@page "/servers" - -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Common -@using MoonlightServers.Frontend.UI.Components.Servers -@using MoonlightServers.Frontend.Services -@using MoonlightServers.Shared.Http.Responses.Client.Servers - -@inject ServerService ServerService - - - - - @if (OwnServers.Length == 0) - { - - There are no servers linked to your account - - } - else - { -
- @* Folder design idea -
-
-
- -
- My Cool Folder -
-
-
-
- @foreach (var server in Servers) - { - - } -
-
-*@ - @foreach (var server in OwnServers) - { - - } -
- } -
-
- - - @if (SharedServers.Length == 0) - { - - There are no shared servers linked to your account - - } - else - { -
- @foreach (var server in SharedServers) - { - - } -
- } -
-
-
- -@code -{ - private ServerDetailResponse[] OwnServers; - private ServerDetailResponse[] SharedServers; - - private async Task LoadOwnServersAsync(LazyLoader lazyLoader) - { - OwnServers = await CountedData.AllAsync(async (startIndex, count) => - await ServerService.GetServersAsync(startIndex, count) - ); - } - - private async Task LoadSharedServersAsync(LazyLoader lazyLoader) - { - SharedServers = await CountedData.AllAsync(async (startIndex, count) => - await ServerService.GetSharedServersAsync(startIndex, count) - ); - } -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Views/Client/Manage.razor b/MoonlightServers.Frontend/UI/Views/Client/Manage.razor deleted file mode 100644 index 3039f75..0000000 --- a/MoonlightServers.Frontend/UI/Views/Client/Manage.razor +++ /dev/null @@ -1,347 +0,0 @@ -@page "/servers/{ServerId:int}" -@page "/servers/{ServerId:int}/{TabPath:alpha}" - -@using Microsoft.AspNetCore.SignalR.Client -@using MoonCore.Blazor.FlyonUi.Components -@using MoonCore.Exceptions -@using MoonCore.Helpers -@using MoonlightServers.Frontend.Interfaces -@using MoonlightServers.Frontend.Models -@using MoonlightServers.Frontend.Services -@using MoonlightServers.Shared.Enums -@using MoonlightServers.Shared.Http.Responses.Client.Servers - -@inject ServerService ServerService -@inject NavigationManager Navigation -@inject IEnumerable TabProviders - -@implements IAsyncDisposable - - - @if (NotFound) - { - - The requested server could not be found - - } - else - { -
-
- @{ - var statusClass = State switch - { - ServerState.Installing => "status-primary", - ServerState.Offline => "status-error", - ServerState.Starting => "status-warning", - ServerState.Stopping => "status-warning", - ServerState.Online => "status-success", - _ => "status-secondary" - }; - } - -
- @if (State != ServerState.Offline) - { -
- } -
-
- -
- - -
-
-
-
- @if (HasPermissionTo("power", ServerPermissionLevel.ReadWrite)) - { - @if (State == ServerState.Offline) - { - - - Start - - } - else - { - - } - - @if (State == ServerState.Online) - { - - } - else - { - - } - - @if (State == ServerState.Starting || State == ServerState.Online || State == ServerState.Stopping) - { - if (State == ServerState.Stopping) - { - - - Kill - - } - else - { - - - Stop - - } - } - else - { - - } - } - else - { - - - - - - } -
-
-
- -
- - - -
- @if (CurrentTab == null) - { - - Seems like you are missing access to all tabs or no tabs for this server could be found - - } - else - { - var rf = ComponentHelper.FromType(CurrentTab!.ComponentType, parameters => - { - parameters.Add("Server", Server); - parameters.Add("State", State); - parameters.Add("InitialConsoleMessage", InitialConsoleMessage); - parameters.Add("HubConnection", HubConnection!); - parameters.Add("Parent", this); - }); - - @rf - } -
-
- } -
- -@code -{ - [Parameter] public int ServerId { get; set; } - [Parameter] public string? TabPath { get; set; } - - private ServerTab[] Tabs; - private ServerTab? CurrentTab; - - private ServerDetailResponse Server; - private bool NotFound = false; - private ServerState State; - private string InitialConsoleMessage = ""; - - private HubConnection? HubConnection; - - public ConcurrentList CommandHistory = new(); - - private async Task LoadAsync(LazyLoader _) - { - try - { - // Load meta data - Server = await ServerService.GetServerAsync(ServerId); - - // Load server tabs - var tmpTabs = new List(); - - foreach (var serverTabProvider in TabProviders) - tmpTabs.AddRange(await serverTabProvider.GetTabsAsync(Server)); - - // If we are accessing a shared server, we need to handle permissions - if (Server.Share != null) - { - // This removes all tabs where the user doesn't have the required permissions - tmpTabs.RemoveAll(tab => - { - if (string.IsNullOrEmpty(tab.PermissionId) || tab.PermissionLevel == ServerPermissionLevel.None) - return false; - - // If permission is required but not set, we don't have access to it - if (!Server.Share.Permissions.TryGetValue(tab.PermissionId, out var level)) - return true; - - // False if the acquired level is higher or equal than the required permission level for the tab so it won't get removed - return level < tab.PermissionLevel; - }); - } - - Tabs = tmpTabs.OrderBy(x => x.Priority).ToArray(); - - // Find current tab - if (!string.IsNullOrEmpty(TabPath)) - { - CurrentTab = Tabs.FirstOrDefault(x => TabPath.Equals(x.Path, StringComparison.InvariantCultureIgnoreCase) - ); - } - - if (CurrentTab == null) - CurrentTab = Tabs.FirstOrDefault(); - - // Load initial status for first render - var status = await ServerService.GetStatusAsync(ServerId); - - State = status.State; - - if (!HasPermissionTo("console", ServerPermissionLevel.Read)) - return; // Exit early if we don't have permissions to load the console - - // Load initial messages - var initialLogs = await ServerService.GetLogsAsync(ServerId); - - InitialConsoleMessage = ""; - - foreach (var message in initialLogs.Messages) - InitialConsoleMessage += message; - - // Load websocket meta - var websocketDetails = await ServerService.GetWebSocketAsync(ServerId); - - // Build signal r - HubConnection = new HubConnectionBuilder() - .WithUrl(websocketDetails.Target, options => - { - options.AccessTokenProvider = async () => - { - var details = await ServerService.GetWebSocketAsync(ServerId); - return details.AccessToken; - }; - }) - .WithAutomaticReconnect() - .Build(); - - // Define handlers - HubConnection.On("StateChanged", async stateStr => - { - if (!Enum.TryParse(stateStr, out ServerState receivedState)) - return; - - State = receivedState; - await InvokeAsync(StateHasChanged); - }); - - HubConnection.On("ConsoleOutput", async content => - { - // Update initial message - InitialConsoleMessage += content; - await InvokeAsync(StateHasChanged); - }); - - // Connect - await HubConnection.StartAsync(); - } - catch (HttpApiException e) - { - if (e.Status == 404) - NotFound = true; - else - throw; - } - } - - private bool HasPermissionTo(string id, ServerPermissionLevel level) - { - // All non shares have permissions - if (Server.Share == null) - return true; - - if (!Server.Share.Permissions.TryGetValue(id, out var acquiredLevel)) - return false; - - return acquiredLevel >= level; - } - - private async Task SwitchTabAsync(ServerTab tab) - { - CurrentTab = tab; - Navigation.NavigateTo($"/servers/{ServerId}/{tab.Path}"); - - await InvokeAsync(StateHasChanged); - } - - private async Task StartAsync() - => await ServerService.StartAsync(ServerId); - - private async Task StopAsync() - => await ServerService.StopAsync(ServerId); - - private async Task KillAsync() - => await ServerService.KillAsync(ServerId); - - public async ValueTask DisposeAsync() - { - if (HubConnection != null) - { - if (HubConnection.State == HubConnectionState.Connected) - await HubConnection.StopAsync(); - - await HubConnection.DisposeAsync(); - } - } -} diff --git a/MoonlightServers.Frontend/UI/Views/Demo.razor b/MoonlightServers.Frontend/UI/Views/Demo.razor new file mode 100644 index 0000000..56c98dd --- /dev/null +++ b/MoonlightServers.Frontend/UI/Views/Demo.razor @@ -0,0 +1,43 @@ +@page "/demo" +@using LucideBlazor +@using MoonlightServers.Frontend.UI.Components +@using ShadcnBlazor.Buttons +@using ShadcnBlazor.Cards +@using ShadcnBlazor.Extras.Dialogs + +@inject DialogService DialogService + +
+ + + Demo + A cool demo page + + + You successfully used the plugin template to create your moonlight plugin :) + + + + + + + + + + +
+ +@code +{ + private async Task LaunchFormAsync() + => await DialogService.LaunchAsync(); +} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/_Imports.razor b/MoonlightServers.Frontend/UI/_Imports.razor index 45f87d6..3516d7e 100644 --- a/MoonlightServers.Frontend/UI/_Imports.razor +++ b/MoonlightServers.Frontend/UI/_Imports.razor @@ -1,6 +1,6 @@ -@using System.Net.Http -@using Microsoft.AspNetCore.Authorization +@using System.Net.Http +@using System.Net.Http.Json @using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Web -@using Microsoft.JSInterop \ No newline at end of file +@using Microsoft.AspNetCore.Components.Web.Virtualization \ No newline at end of file diff --git a/MoonlightServers.Frontend/UiConstants.cs b/MoonlightServers.Frontend/UiConstants.cs deleted file mode 100644 index 2513bea..0000000 --- a/MoonlightServers.Frontend/UiConstants.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MoonlightServers.Frontend; - -public static class UiConstants -{ - public static readonly string[] AdminNavNames = ["Overview", "Servers", "Nodes", "Stars", "Manager"]; - public static readonly string[] AdminNavLinks = ["/admin/servers", "/admin/servers/all", "/admin/servers/nodes", "/admin/servers/stars", "/admin/servers/manager"]; -} \ No newline at end of file diff --git a/MoonlightServers.Frontend/wwwroot/css/XtermBlazor.min.css b/MoonlightServers.Frontend/wwwroot/css/XtermBlazor.min.css deleted file mode 100644 index 205732d..0000000 --- a/MoonlightServers.Frontend/wwwroot/css/XtermBlazor.min.css +++ /dev/null @@ -1 +0,0 @@ -.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{border:0;height:0;left:-9999em;margin:0;opacity:0;overflow:hidden;padding:0;position:absolute;resize:none;top:0;white-space:nowrap;width:0;z-index:-5}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;bottom:0;cursor:default;left:0;overflow-y:scroll;position:absolute;right:0;top:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{left:0;position:absolute;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;left:-9999em;line-height:normal;position:absolute;top:0;visibility:hidden}.xterm.enable-mouse-events{cursor:default}.xterm .xterm-cursor-pointer,.xterm.xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{bottom:0;color:transparent;left:0;pointer-events:none;position:absolute;right:0;top:0;z-index:10}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:transparent}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}.xterm .live-region{height:1px;left:-9999px;overflow:hidden;position:absolute;width:1px}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{position:absolute;z-index:6}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{pointer-events:none;position:absolute;right:0;top:0;z-index:8}.xterm-decoration-top{position:relative;z-index:2} \ No newline at end of file diff --git a/MoonlightServers.Frontend/wwwroot/js/XtermBlazor.min.js b/MoonlightServers.Frontend/wwwroot/js/XtermBlazor.min.js deleted file mode 100644 index a9b2b05..0000000 --- a/MoonlightServers.Frontend/wwwroot/js/XtermBlazor.min.js +++ /dev/null @@ -1,9 +0,0 @@ -(()=>{var ye={591:Y=>{var Z=Object.defineProperty,ee=Object.getOwnPropertySymbols,_e=Object.prototype.hasOwnProperty,pe=Object.prototype.propertyIsEnumerable,ue=(te,J,q)=>J in te?Z(te,J,{enumerable:!0,configurable:!0,writable:!0,value:q}):te[J]=q,ae=(te,J)=>{for(var q in J||(J={}))_e.call(J,q)&&ue(te,q,J[q]);if(ee)for(var q of ee(J))pe.call(J,q)&&ue(te,q,J[q]);return te};(function(te,J){if(1)Y.exports=J();else var q,T})(globalThis,()=>(()=>{"use strict";var te={4567:function(R,r,o){var c=this&&this.__decorate||function(e,t,a,_){var u,g=arguments.length,h=g<3?t:_===null?_=Object.getOwnPropertyDescriptor(t,a):_;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")h=Reflect.decorate(e,t,a,_);else for(var m=e.length-1;m>=0;m--)(u=e[m])&&(h=(g<3?u(h):g>3?u(t,a,h):u(t,a))||h);return g>3&&h&&Object.defineProperty(t,a,h),h},f=this&&this.__param||function(e,t){return function(a,_){t(a,_,e)}};Object.defineProperty(r,"__esModule",{value:!0}),r.AccessibilityManager=void 0;const n=o(9042),d=o(9924),v=o(844),p=o(4725),l=o(2585),i=o(3656);let s=r.AccessibilityManager=class extends v.Disposable{constructor(e,t,a,_){super(),this._terminal=e,this._coreBrowserService=a,this._renderService=_,this._rowColumns=new WeakMap,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce="",this._accessibilityContainer=this._coreBrowserService.mainDocument.createElement("div"),this._accessibilityContainer.classList.add("xterm-accessibility"),this._rowContainer=this._coreBrowserService.mainDocument.createElement("div"),this._rowContainer.setAttribute("role","list"),this._rowContainer.classList.add("xterm-accessibility-tree"),this._rowElements=[];for(let u=0;uthis._handleBoundaryFocus(u,0),this._bottomBoundaryFocusListener=u=>this._handleBoundaryFocus(u,1),this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions(),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=this._coreBrowserService.mainDocument.createElement("div"),this._liveRegion.classList.add("live-region"),this._liveRegion.setAttribute("aria-live","assertive"),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this.register(new d.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw new Error("Cannot enable accessibility before Terminal.open");this._terminal.element.insertAdjacentElement("afterbegin",this._accessibilityContainer),this.register(this._terminal.onResize(u=>this._handleResize(u.rows))),this.register(this._terminal.onRender(u=>this._refreshRows(u.start,u.end))),this.register(this._terminal.onScroll(()=>this._refreshRows())),this.register(this._terminal.onA11yChar(u=>this._handleChar(u))),this.register(this._terminal.onLineFeed(()=>this._handleChar(` -`))),this.register(this._terminal.onA11yTab(u=>this._handleTab(u))),this.register(this._terminal.onKey(u=>this._handleKey(u.key))),this.register(this._terminal.onBlur(()=>this._clearLiveRegion())),this.register(this._renderService.onDimensionsChange(()=>this._refreshRowsDimensions())),this.register((0,i.addDisposableDomListener)(document,"selectionchange",()=>this._handleSelectionChange())),this.register(this._coreBrowserService.onDprChange(()=>this._refreshRowsDimensions())),this._refreshRows(),this.register((0,v.toDisposable)(()=>{this._accessibilityContainer.remove(),this._rowElements.length=0}))}_handleTab(e){for(let t=0;t0?this._charsToConsume.shift()!==e&&(this._charsToAnnounce+=e):this._charsToAnnounce+=e,e===` -`&&(this._liveRegionLineCount++,this._liveRegionLineCount===21&&(this._liveRegion.textContent+=n.tooMuchOutput)))}_clearLiveRegion(){this._liveRegion.textContent="",this._liveRegionLineCount=0}_handleKey(e){this._clearLiveRegion(),new RegExp("\\p{Control}","u").test(e)||this._charsToConsume.push(e)}_refreshRows(e,t){this._liveRegionDebouncer.refresh(e,t,this._terminal.rows)}_renderRows(e,t){const a=this._terminal.buffer,_=a.lines.length.toString();for(let u=e;u<=t;u++){const g=a.lines.get(a.ydisp+u),h=[],m=g?.translateToString(!0,void 0,void 0,h)||"",y=(a.ydisp+u+1).toString(),k=this._rowElements[u];k&&(m.length===0?(k.innerText="\xA0",this._rowColumns.set(k,[0,1])):(k.textContent=m,this._rowColumns.set(k,h)),k.setAttribute("aria-posinset",y),k.setAttribute("aria-setsize",_))}this._announceCharacters()}_announceCharacters(){this._charsToAnnounce.length!==0&&(this._liveRegion.textContent+=this._charsToAnnounce,this._charsToAnnounce="")}_handleBoundaryFocus(e,t){const a=e.target,_=this._rowElements[t===0?1:this._rowElements.length-2];if(a.getAttribute("aria-posinset")===(t===0?"1":`${this._terminal.buffer.lines.length}`)||e.relatedTarget!==_)return;let u,g;if(t===0?(u=a,g=this._rowElements.pop(),this._rowContainer.removeChild(g)):(u=this._rowElements.shift(),g=a,this._rowContainer.removeChild(u)),u.removeEventListener("focus",this._topBoundaryFocusListener),g.removeEventListener("focus",this._bottomBoundaryFocusListener),t===0){const h=this._createAccessibilityTreeNode();this._rowElements.unshift(h),this._rowContainer.insertAdjacentElement("afterbegin",h)}else{const h=this._createAccessibilityTreeNode();this._rowElements.push(h),this._rowContainer.appendChild(h)}this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._terminal.scrollLines(t===0?-1:1),this._rowElements[t===0?1:this._rowElements.length-2].focus(),e.preventDefault(),e.stopImmediatePropagation()}_handleSelectionChange(){var e,t;if(this._rowElements.length===0)return;const a=document.getSelection();if(!a)return;if(a.isCollapsed)return void(this._rowContainer.contains(a.anchorNode)&&this._terminal.clearSelection());if(!a.anchorNode||!a.focusNode)return void console.error("anchorNode and/or focusNode are null");let _={node:a.anchorNode,offset:a.anchorOffset},u={node:a.focusNode,offset:a.focusOffset};if((_.node.compareDocumentPosition(u.node)&Node.DOCUMENT_POSITION_PRECEDING||_.node===u.node&&_.offset>u.offset)&&([_,u]=[u,_]),_.node.compareDocumentPosition(this._rowElements[0])&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_FOLLOWING)&&(_={node:this._rowElements[0].childNodes[0],offset:0}),!this._rowContainer.contains(_.node))return;const g=this._rowElements.slice(-1)[0];if(u.node.compareDocumentPosition(g)&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_PRECEDING)&&(u={node:g,offset:(t=(e=g.textContent)==null?void 0:e.length)!=null?t:0}),!this._rowContainer.contains(u.node))return;const h=({node:k,offset:D})=>{const b=k instanceof Text?k.parentNode:k;let B=parseInt(b?.getAttribute("aria-posinset"),10)-1;if(isNaN(B))return console.warn("row is invalid. Race condition?"),null;const A=this._rowColumns.get(b);if(!A)return console.warn("columns is null. Race condition?"),null;let H=D=this._terminal.cols&&(++B,H=0),{row:B,column:H}},m=h(_),y=h(u);if(m&&y){if(m.row>y.row||m.row===y.row&&m.column>=y.column)throw new Error("invalid range");this._terminal.select(m.column,m.row,(y.row-m.row)*this._terminal.cols-m.column+y.column)}}_handleResize(e){this._rowElements[this._rowElements.length-1].removeEventListener("focus",this._bottomBoundaryFocusListener);for(let t=this._rowContainer.children.length;te;)this._rowContainer.removeChild(this._rowElements.pop());this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions()}_createAccessibilityTreeNode(){const e=this._coreBrowserService.mainDocument.createElement("div");return e.setAttribute("role","listitem"),e.tabIndex=-1,this._refreshRowDimensions(e),e}_refreshRowsDimensions(){if(this._renderService.dimensions.css.cell.height){this._accessibilityContainer.style.width=`${this._renderService.dimensions.css.canvas.width}px`,this._rowElements.length!==this._terminal.rows&&this._handleResize(this._terminal.rows);for(let e=0;e{function o(d){return d.replace(/\r?\n/g,"\r")}function c(d,v){return v?"\x1B[200~"+d+"\x1B[201~":d}function f(d,v,p,l){d=c(d=o(d),p.decPrivateModes.bracketedPasteMode&&l.rawOptions.ignoreBracketedPasteMode!==!0),p.triggerDataEvent(d,!0),v.value=""}function n(d,v,p){const l=p.getBoundingClientRect(),i=d.clientX-l.left-10,s=d.clientY-l.top-10;v.style.width="20px",v.style.height="20px",v.style.left=`${i}px`,v.style.top=`${s}px`,v.style.zIndex="1000",v.focus()}Object.defineProperty(r,"__esModule",{value:!0}),r.rightClickHandler=r.moveTextAreaUnderMouseCursor=r.paste=r.handlePasteEvent=r.copyHandler=r.bracketTextForPaste=r.prepareTextForTerminal=void 0,r.prepareTextForTerminal=o,r.bracketTextForPaste=c,r.copyHandler=function(d,v){d.clipboardData&&d.clipboardData.setData("text/plain",v.selectionText),d.preventDefault()},r.handlePasteEvent=function(d,v,p,l){d.stopPropagation(),d.clipboardData&&f(d.clipboardData.getData("text/plain"),v,p,l)},r.paste=f,r.moveTextAreaUnderMouseCursor=n,r.rightClickHandler=function(d,v,p,l,i){n(d,v,p),i&&l.rightClickSelect(d),v.value=l.selectionText,v.select()}},7239:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.ColorContrastCache=void 0;const c=o(1505);r.ColorContrastCache=class{constructor(){this._color=new c.TwoKeyMap,this._css=new c.TwoKeyMap}setCss(f,n,d){this._css.set(f,n,d)}getCss(f,n){return this._css.get(f,n)}setColor(f,n,d){this._color.set(f,n,d)}getColor(f,n){return this._color.get(f,n)}clear(){this._color.clear(),this._css.clear()}}},3656:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.addDisposableDomListener=void 0,r.addDisposableDomListener=function(o,c,f,n){o.addEventListener(c,f,n);let d=!1;return{dispose:()=>{d||(d=!0,o.removeEventListener(c,f,n))}}}},3551:function(R,r,o){var c=this&&this.__decorate||function(s,e,t,a){var _,u=arguments.length,g=u<3?e:a===null?a=Object.getOwnPropertyDescriptor(e,t):a;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")g=Reflect.decorate(s,e,t,a);else for(var h=s.length-1;h>=0;h--)(_=s[h])&&(g=(u<3?_(g):u>3?_(e,t,g):_(e,t))||g);return u>3&&g&&Object.defineProperty(e,t,g),g},f=this&&this.__param||function(s,e){return function(t,a){e(t,a,s)}};Object.defineProperty(r,"__esModule",{value:!0}),r.Linkifier=void 0;const n=o(3656),d=o(8460),v=o(844),p=o(2585),l=o(4725);let i=r.Linkifier=class extends v.Disposable{get currentLink(){return this._currentLink}constructor(s,e,t,a,_){super(),this._element=s,this._mouseService=e,this._renderService=t,this._bufferService=a,this._linkProviderService=_,this._linkCacheDisposables=[],this._isMouseOut=!0,this._wasResized=!1,this._activeLine=-1,this._onShowLinkUnderline=this.register(new d.EventEmitter),this.onShowLinkUnderline=this._onShowLinkUnderline.event,this._onHideLinkUnderline=this.register(new d.EventEmitter),this.onHideLinkUnderline=this._onHideLinkUnderline.event,this.register((0,v.getDisposeArrayDisposable)(this._linkCacheDisposables)),this.register((0,v.toDisposable)(()=>{var u;this._lastMouseEvent=void 0,(u=this._activeProviderReplies)==null||u.clear()})),this.register(this._bufferService.onResize(()=>{this._clearCurrentLink(),this._wasResized=!0})),this.register((0,n.addDisposableDomListener)(this._element,"mouseleave",()=>{this._isMouseOut=!0,this._clearCurrentLink()})),this.register((0,n.addDisposableDomListener)(this._element,"mousemove",this._handleMouseMove.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mousedown",this._handleMouseDown.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mouseup",this._handleMouseUp.bind(this)))}_handleMouseMove(s){this._lastMouseEvent=s;const e=this._positionFromMouseEvent(s,this._element,this._mouseService);if(!e)return;this._isMouseOut=!1;const t=s.composedPath();for(let a=0;a{u?.forEach(g=>{g.link.dispose&&g.link.dispose()})}),this._activeProviderReplies=new Map,this._activeLine=s.y);let _=!1;for(const[u,g]of this._linkProviderService.linkProviders.entries())e?(a=this._activeProviderReplies)!=null&&a.get(u)&&(_=this._checkLinkProviderResult(u,s,_)):g.provideLinks(s.y,h=>{var m,y;if(this._isMouseOut)return;const k=h?.map(D=>({link:D}));(m=this._activeProviderReplies)==null||m.set(u,k),_=this._checkLinkProviderResult(u,s,_),((y=this._activeProviderReplies)==null?void 0:y.size)===this._linkProviderService.linkProviders.length&&this._removeIntersectingLinks(s.y,this._activeProviderReplies)})}_removeIntersectingLinks(s,e){const t=new Set;for(let a=0;as?this._bufferService.cols:g.link.range.end.x;for(let y=h;y<=m;y++){if(t.has(y)){_.splice(u--,1);break}t.add(y)}}}}_checkLinkProviderResult(s,e,t){var a;if(!this._activeProviderReplies)return t;const _=this._activeProviderReplies.get(s);let u=!1;for(let g=0;gthis._linkAtPosition(h.link,e));g&&(t=!0,this._handleNewLink(g))}if(this._activeProviderReplies.size===this._linkProviderService.linkProviders.length&&!t)for(let g=0;gthis._linkAtPosition(m.link,e));if(h){t=!0,this._handleNewLink(h);break}}return t}_handleMouseDown(){this._mouseDownLink=this._currentLink}_handleMouseUp(s){if(!this._currentLink)return;const e=this._positionFromMouseEvent(s,this._element,this._mouseService);e&&this._mouseDownLink===this._currentLink&&this._linkAtPosition(this._currentLink.link,e)&&this._currentLink.link.activate(s,this._currentLink.link.text)}_clearCurrentLink(s,e){this._currentLink&&this._lastMouseEvent&&(!s||!e||this._currentLink.link.range.start.y>=s&&this._currentLink.link.range.end.y<=e)&&(this._linkLeave(this._element,this._currentLink.link,this._lastMouseEvent),this._currentLink=void 0,(0,v.disposeArray)(this._linkCacheDisposables))}_handleNewLink(s){if(!this._lastMouseEvent)return;const e=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);e&&this._linkAtPosition(s.link,e)&&(this._currentLink=s,this._currentLink.state={decorations:{underline:s.link.decorations===void 0||s.link.decorations.underline,pointerCursor:s.link.decorations===void 0||s.link.decorations.pointerCursor},isHovered:!0},this._linkHover(this._element,s.link,this._lastMouseEvent),s.link.decorations={},Object.defineProperties(s.link.decorations,{pointerCursor:{get:()=>{var t,a;return(a=(t=this._currentLink)==null?void 0:t.state)==null?void 0:a.decorations.pointerCursor},set:t=>{var a;(a=this._currentLink)!=null&&a.state&&this._currentLink.state.decorations.pointerCursor!==t&&(this._currentLink.state.decorations.pointerCursor=t,this._currentLink.state.isHovered&&this._element.classList.toggle("xterm-cursor-pointer",t))}},underline:{get:()=>{var t,a;return(a=(t=this._currentLink)==null?void 0:t.state)==null?void 0:a.decorations.underline},set:t=>{var a,_,u;(a=this._currentLink)!=null&&a.state&&((u=(_=this._currentLink)==null?void 0:_.state)==null?void 0:u.decorations.underline)!==t&&(this._currentLink.state.decorations.underline=t,this._currentLink.state.isHovered&&this._fireUnderlineEvent(s.link,t))}}}),this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange(t=>{if(!this._currentLink)return;const a=t.start===0?0:t.start+1+this._bufferService.buffer.ydisp,_=this._bufferService.buffer.ydisp+1+t.end;if(this._currentLink.link.range.start.y>=a&&this._currentLink.link.range.end.y<=_&&(this._clearCurrentLink(a,_),this._lastMouseEvent)){const u=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);u&&this._askForLink(u,!1)}})))}_linkHover(s,e,t){var a;(a=this._currentLink)!=null&&a.state&&(this._currentLink.state.isHovered=!0,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(e,!0),this._currentLink.state.decorations.pointerCursor&&s.classList.add("xterm-cursor-pointer")),e.hover&&e.hover(t,e.text)}_fireUnderlineEvent(s,e){const t=s.range,a=this._bufferService.buffer.ydisp,_=this._createLinkUnderlineEvent(t.start.x-1,t.start.y-a-1,t.end.x,t.end.y-a-1,void 0);(e?this._onShowLinkUnderline:this._onHideLinkUnderline).fire(_)}_linkLeave(s,e,t){var a;(a=this._currentLink)!=null&&a.state&&(this._currentLink.state.isHovered=!1,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(e,!1),this._currentLink.state.decorations.pointerCursor&&s.classList.remove("xterm-cursor-pointer")),e.leave&&e.leave(t,e.text)}_linkAtPosition(s,e){const t=s.range.start.y*this._bufferService.cols+s.range.start.x,a=s.range.end.y*this._bufferService.cols+s.range.end.x,_=e.y*this._bufferService.cols+e.x;return t<=_&&_<=a}_positionFromMouseEvent(s,e,t){const a=t.getCoords(s,e,this._bufferService.cols,this._bufferService.rows);if(a)return{x:a[0],y:a[1]+this._bufferService.buffer.ydisp}}_createLinkUnderlineEvent(s,e,t,a,_){return{x1:s,y1:e,x2:t,y2:a,cols:this._bufferService.cols,fg:_}}};r.Linkifier=i=c([f(1,l.IMouseService),f(2,l.IRenderService),f(3,p.IBufferService),f(4,l.ILinkProviderService)],i)},9042:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.tooMuchOutput=r.promptLabel=void 0,r.promptLabel="Terminal input",r.tooMuchOutput="Too much output to announce, navigate to rows manually to read"},3730:function(R,r,o){var c=this&&this.__decorate||function(l,i,s,e){var t,a=arguments.length,_=a<3?i:e===null?e=Object.getOwnPropertyDescriptor(i,s):e;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")_=Reflect.decorate(l,i,s,e);else for(var u=l.length-1;u>=0;u--)(t=l[u])&&(_=(a<3?t(_):a>3?t(i,s,_):t(i,s))||_);return a>3&&_&&Object.defineProperty(i,s,_),_},f=this&&this.__param||function(l,i){return function(s,e){i(s,e,l)}};Object.defineProperty(r,"__esModule",{value:!0}),r.OscLinkProvider=void 0;const n=o(511),d=o(2585);let v=r.OscLinkProvider=class{constructor(l,i,s){this._bufferService=l,this._optionsService=i,this._oscLinkService=s}provideLinks(l,i){var s;const e=this._bufferService.buffer.lines.get(l-1);if(!e)return void i(void 0);const t=[],a=this._optionsService.rawOptions.linkHandler,_=new n.CellData,u=e.getTrimmedLength();let g=-1,h=-1,m=!1;for(let y=0;ya?a.activate(B,A,D):p(0,A),hover:(B,A)=>{var H;return(H=a?.hover)==null?void 0:H.call(a,B,A,D)},leave:(B,A)=>{var H;return(H=a?.leave)==null?void 0:H.call(a,B,A,D)}})}m=!1,_.hasExtendedAttrs()&&_.extended.urlId?(h=y,g=_.extended.urlId):(h=-1,g=-1)}}i(t)}};function p(l,i){if(confirm(`Do you want to navigate to ${i}? - -WARNING: This link could potentially be dangerous`)){const s=window.open();if(s){try{s.opener=null}catch{}s.location.href=i}else console.warn("Opening link blocked as opener could not be cleared")}}r.OscLinkProvider=v=c([f(0,d.IBufferService),f(1,d.IOptionsService),f(2,d.IOscLinkService)],v)},6193:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.RenderDebouncer=void 0,r.RenderDebouncer=class{constructor(o,c){this._renderCallback=o,this._coreBrowserService=c,this._refreshCallbacks=[]}dispose(){this._animationFrame&&(this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame),this._animationFrame=void 0)}addRefreshCallback(o){return this._refreshCallbacks.push(o),this._animationFrame||(this._animationFrame=this._coreBrowserService.window.requestAnimationFrame(()=>this._innerRefresh())),this._animationFrame}refresh(o,c,f){this._rowCount=f,o=o!==void 0?o:0,c=c!==void 0?c:this._rowCount-1,this._rowStart=this._rowStart!==void 0?Math.min(this._rowStart,o):o,this._rowEnd=this._rowEnd!==void 0?Math.max(this._rowEnd,c):c,this._animationFrame||(this._animationFrame=this._coreBrowserService.window.requestAnimationFrame(()=>this._innerRefresh()))}_innerRefresh(){if(this._animationFrame=void 0,this._rowStart===void 0||this._rowEnd===void 0||this._rowCount===void 0)return void this._runRefreshCallbacks();const o=Math.max(this._rowStart,0),c=Math.min(this._rowEnd,this._rowCount-1);this._rowStart=void 0,this._rowEnd=void 0,this._renderCallback(o,c),this._runRefreshCallbacks()}_runRefreshCallbacks(){for(const o of this._refreshCallbacks)o(0);this._refreshCallbacks=[]}}},3236:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.Terminal=void 0;const c=o(3614),f=o(3656),n=o(3551),d=o(9042),v=o(3730),p=o(1680),l=o(3107),i=o(5744),s=o(2950),e=o(1296),t=o(428),a=o(4269),_=o(5114),u=o(8934),g=o(3230),h=o(9312),m=o(4725),y=o(6731),k=o(8055),D=o(8969),b=o(8460),B=o(844),A=o(6114),H=o(8437),U=o(2584),F=o(7399),S=o(5941),w=o(9074),E=o(2585),L=o(5435),P=o(4567),W=o(779);class $ extends D.CoreTerminal{get onFocus(){return this._onFocus.event}get onBlur(){return this._onBlur.event}get onA11yChar(){return this._onA11yCharEmitter.event}get onA11yTab(){return this._onA11yTabEmitter.event}get onWillOpen(){return this._onWillOpen.event}constructor(x={}){super(x),this.browser=A,this._keyDownHandled=!1,this._keyDownSeen=!1,this._keyPressHandled=!1,this._unprocessedDeadKey=!1,this._accessibilityManager=this.register(new B.MutableDisposable),this._onCursorMove=this.register(new b.EventEmitter),this.onCursorMove=this._onCursorMove.event,this._onKey=this.register(new b.EventEmitter),this.onKey=this._onKey.event,this._onRender=this.register(new b.EventEmitter),this.onRender=this._onRender.event,this._onSelectionChange=this.register(new b.EventEmitter),this.onSelectionChange=this._onSelectionChange.event,this._onTitleChange=this.register(new b.EventEmitter),this.onTitleChange=this._onTitleChange.event,this._onBell=this.register(new b.EventEmitter),this.onBell=this._onBell.event,this._onFocus=this.register(new b.EventEmitter),this._onBlur=this.register(new b.EventEmitter),this._onA11yCharEmitter=this.register(new b.EventEmitter),this._onA11yTabEmitter=this.register(new b.EventEmitter),this._onWillOpen=this.register(new b.EventEmitter),this._setup(),this._decorationService=this._instantiationService.createInstance(w.DecorationService),this._instantiationService.setService(E.IDecorationService,this._decorationService),this._linkProviderService=this._instantiationService.createInstance(W.LinkProviderService),this._instantiationService.setService(m.ILinkProviderService,this._linkProviderService),this._linkProviderService.registerLinkProvider(this._instantiationService.createInstance(v.OscLinkProvider)),this.register(this._inputHandler.onRequestBell(()=>this._onBell.fire())),this.register(this._inputHandler.onRequestRefreshRows((C,I)=>this.refresh(C,I))),this.register(this._inputHandler.onRequestSendFocus(()=>this._reportFocus())),this.register(this._inputHandler.onRequestReset(()=>this.reset())),this.register(this._inputHandler.onRequestWindowsOptionsReport(C=>this._reportWindowsOptions(C))),this.register(this._inputHandler.onColor(C=>this._handleColorEvent(C))),this.register((0,b.forwardEvent)(this._inputHandler.onCursorMove,this._onCursorMove)),this.register((0,b.forwardEvent)(this._inputHandler.onTitleChange,this._onTitleChange)),this.register((0,b.forwardEvent)(this._inputHandler.onA11yChar,this._onA11yCharEmitter)),this.register((0,b.forwardEvent)(this._inputHandler.onA11yTab,this._onA11yTabEmitter)),this.register(this._bufferService.onResize(C=>this._afterResize(C.cols,C.rows))),this.register((0,B.toDisposable)(()=>{var C,I;this._customKeyEventHandler=void 0,(I=(C=this.element)==null?void 0:C.parentNode)==null||I.removeChild(this.element)}))}_handleColorEvent(x){if(this._themeService)for(const C of x){let I,O="";switch(C.index){case 256:I="foreground",O="10";break;case 257:I="background",O="11";break;case 258:I="cursor",O="12";break;default:I="ansi",O="4;"+C.index}switch(C.type){case 0:const N=k.color.toColorRGB(I==="ansi"?this._themeService.colors.ansi[C.index]:this._themeService.colors[I]);this.coreService.triggerDataEvent(`${U.C0.ESC}]${O};${(0,S.toRgbString)(N)}${U.C1_ESCAPED.ST}`);break;case 1:if(I==="ansi")this._themeService.modifyColors(M=>M.ansi[C.index]=k.channels.toColor(...C.color));else{const M=I;this._themeService.modifyColors(K=>K[M]=k.channels.toColor(...C.color))}break;case 2:this._themeService.restoreColor(C.index)}}}_setup(){super._setup(),this._customKeyEventHandler=void 0}get buffer(){return this.buffers.active}focus(){this.textarea&&this.textarea.focus({preventScroll:!0})}_handleScreenReaderModeOptionChange(x){x?!this._accessibilityManager.value&&this._renderService&&(this._accessibilityManager.value=this._instantiationService.createInstance(P.AccessibilityManager,this)):this._accessibilityManager.clear()}_handleTextAreaFocus(x){this.coreService.decPrivateModes.sendFocus&&this.coreService.triggerDataEvent(U.C0.ESC+"[I"),this.element.classList.add("focus"),this._showCursor(),this._onFocus.fire()}blur(){var x;return(x=this.textarea)==null?void 0:x.blur()}_handleTextAreaBlur(){this.textarea.value="",this.refresh(this.buffer.y,this.buffer.y),this.coreService.decPrivateModes.sendFocus&&this.coreService.triggerDataEvent(U.C0.ESC+"[O"),this.element.classList.remove("focus"),this._onBlur.fire()}_syncTextArea(){if(!this.textarea||!this.buffer.isCursorInViewport||this._compositionHelper.isComposing||!this._renderService)return;const x=this.buffer.ybase+this.buffer.y,C=this.buffer.lines.get(x);if(!C)return;const I=Math.min(this.buffer.x,this.cols-1),O=this._renderService.dimensions.css.cell.height,N=C.getWidth(I),M=this._renderService.dimensions.css.cell.width*N,K=this.buffer.y*this._renderService.dimensions.css.cell.height,G=I*this._renderService.dimensions.css.cell.width;this.textarea.style.left=G+"px",this.textarea.style.top=K+"px",this.textarea.style.width=M+"px",this.textarea.style.height=O+"px",this.textarea.style.lineHeight=O+"px",this.textarea.style.zIndex="-5"}_initGlobal(){this._bindKeys(),this.register((0,f.addDisposableDomListener)(this.element,"copy",C=>{this.hasSelection()&&(0,c.copyHandler)(C,this._selectionService)}));const x=C=>(0,c.handlePasteEvent)(C,this.textarea,this.coreService,this.optionsService);this.register((0,f.addDisposableDomListener)(this.textarea,"paste",x)),this.register((0,f.addDisposableDomListener)(this.element,"paste",x)),A.isFirefox?this.register((0,f.addDisposableDomListener)(this.element,"mousedown",C=>{C.button===2&&(0,c.rightClickHandler)(C,this.textarea,this.screenElement,this._selectionService,this.options.rightClickSelectsWord)})):this.register((0,f.addDisposableDomListener)(this.element,"contextmenu",C=>{(0,c.rightClickHandler)(C,this.textarea,this.screenElement,this._selectionService,this.options.rightClickSelectsWord)})),A.isLinux&&this.register((0,f.addDisposableDomListener)(this.element,"auxclick",C=>{C.button===1&&(0,c.moveTextAreaUnderMouseCursor)(C,this.textarea,this.screenElement)}))}_bindKeys(){this.register((0,f.addDisposableDomListener)(this.textarea,"keyup",x=>this._keyUp(x),!0)),this.register((0,f.addDisposableDomListener)(this.textarea,"keydown",x=>this._keyDown(x),!0)),this.register((0,f.addDisposableDomListener)(this.textarea,"keypress",x=>this._keyPress(x),!0)),this.register((0,f.addDisposableDomListener)(this.textarea,"compositionstart",()=>this._compositionHelper.compositionstart())),this.register((0,f.addDisposableDomListener)(this.textarea,"compositionupdate",x=>this._compositionHelper.compositionupdate(x))),this.register((0,f.addDisposableDomListener)(this.textarea,"compositionend",()=>this._compositionHelper.compositionend())),this.register((0,f.addDisposableDomListener)(this.textarea,"input",x=>this._inputEvent(x),!0)),this.register(this.onRender(()=>this._compositionHelper.updateCompositionElements()))}open(x){var C,I,O;if(!x)throw new Error("Terminal requires a parent element.");if(x.isConnected||this._logService.debug("Terminal.open was called on an element that was not attached to the DOM"),((C=this.element)==null?void 0:C.ownerDocument.defaultView)&&this._coreBrowserService)return void(this.element.ownerDocument.defaultView!==this._coreBrowserService.window&&(this._coreBrowserService.window=this.element.ownerDocument.defaultView));this._document=x.ownerDocument,this.options.documentOverride&&this.options.documentOverride instanceof Document&&(this._document=this.optionsService.rawOptions.documentOverride),this.element=this._document.createElement("div"),this.element.dir="ltr",this.element.classList.add("terminal"),this.element.classList.add("xterm"),x.appendChild(this.element);const N=this._document.createDocumentFragment();this._viewportElement=this._document.createElement("div"),this._viewportElement.classList.add("xterm-viewport"),N.appendChild(this._viewportElement),this._viewportScrollArea=this._document.createElement("div"),this._viewportScrollArea.classList.add("xterm-scroll-area"),this._viewportElement.appendChild(this._viewportScrollArea),this.screenElement=this._document.createElement("div"),this.screenElement.classList.add("xterm-screen"),this.register((0,f.addDisposableDomListener)(this.screenElement,"mousemove",M=>this.updateCursorStyle(M))),this._helperContainer=this._document.createElement("div"),this._helperContainer.classList.add("xterm-helpers"),this.screenElement.appendChild(this._helperContainer),N.appendChild(this.screenElement),this.textarea=this._document.createElement("textarea"),this.textarea.classList.add("xterm-helper-textarea"),this.textarea.setAttribute("aria-label",d.promptLabel),A.isChromeOS||this.textarea.setAttribute("aria-multiline","false"),this.textarea.setAttribute("autocorrect","off"),this.textarea.setAttribute("autocapitalize","off"),this.textarea.setAttribute("spellcheck","false"),this.textarea.tabIndex=0,this._coreBrowserService=this.register(this._instantiationService.createInstance(_.CoreBrowserService,this.textarea,(I=x.ownerDocument.defaultView)!=null?I:window,((O=this._document)!=null?O:typeof window<"u")?window.document:null)),this._instantiationService.setService(m.ICoreBrowserService,this._coreBrowserService),this.register((0,f.addDisposableDomListener)(this.textarea,"focus",M=>this._handleTextAreaFocus(M))),this.register((0,f.addDisposableDomListener)(this.textarea,"blur",()=>this._handleTextAreaBlur())),this._helperContainer.appendChild(this.textarea),this._charSizeService=this._instantiationService.createInstance(t.CharSizeService,this._document,this._helperContainer),this._instantiationService.setService(m.ICharSizeService,this._charSizeService),this._themeService=this._instantiationService.createInstance(y.ThemeService),this._instantiationService.setService(m.IThemeService,this._themeService),this._characterJoinerService=this._instantiationService.createInstance(a.CharacterJoinerService),this._instantiationService.setService(m.ICharacterJoinerService,this._characterJoinerService),this._renderService=this.register(this._instantiationService.createInstance(g.RenderService,this.rows,this.screenElement)),this._instantiationService.setService(m.IRenderService,this._renderService),this.register(this._renderService.onRenderedViewportChange(M=>this._onRender.fire(M))),this.onResize(M=>this._renderService.resize(M.cols,M.rows)),this._compositionView=this._document.createElement("div"),this._compositionView.classList.add("composition-view"),this._compositionHelper=this._instantiationService.createInstance(s.CompositionHelper,this.textarea,this._compositionView),this._helperContainer.appendChild(this._compositionView),this._mouseService=this._instantiationService.createInstance(u.MouseService),this._instantiationService.setService(m.IMouseService,this._mouseService),this.linkifier=this.register(this._instantiationService.createInstance(n.Linkifier,this.screenElement)),this.element.appendChild(N);try{this._onWillOpen.fire(this.element)}catch{}this._renderService.hasRenderer()||this._renderService.setRenderer(this._createRenderer()),this.viewport=this._instantiationService.createInstance(p.Viewport,this._viewportElement,this._viewportScrollArea),this.viewport.onRequestScrollLines(M=>this.scrollLines(M.amount,M.suppressScrollEvent,1)),this.register(this._inputHandler.onRequestSyncScrollBar(()=>this.viewport.syncScrollArea())),this.register(this.viewport),this.register(this.onCursorMove(()=>{this._renderService.handleCursorMove(),this._syncTextArea()})),this.register(this.onResize(()=>this._renderService.handleResize(this.cols,this.rows))),this.register(this.onBlur(()=>this._renderService.handleBlur())),this.register(this.onFocus(()=>this._renderService.handleFocus())),this.register(this._renderService.onDimensionsChange(()=>this.viewport.syncScrollArea())),this._selectionService=this.register(this._instantiationService.createInstance(h.SelectionService,this.element,this.screenElement,this.linkifier)),this._instantiationService.setService(m.ISelectionService,this._selectionService),this.register(this._selectionService.onRequestScrollLines(M=>this.scrollLines(M.amount,M.suppressScrollEvent))),this.register(this._selectionService.onSelectionChange(()=>this._onSelectionChange.fire())),this.register(this._selectionService.onRequestRedraw(M=>this._renderService.handleSelectionChanged(M.start,M.end,M.columnSelectMode))),this.register(this._selectionService.onLinuxMouseSelection(M=>{this.textarea.value=M,this.textarea.focus(),this.textarea.select()})),this.register(this._onScroll.event(M=>{this.viewport.syncScrollArea(),this._selectionService.refresh()})),this.register((0,f.addDisposableDomListener)(this._viewportElement,"scroll",()=>this._selectionService.refresh())),this.register(this._instantiationService.createInstance(l.BufferDecorationRenderer,this.screenElement)),this.register((0,f.addDisposableDomListener)(this.element,"mousedown",M=>this._selectionService.handleMouseDown(M))),this.coreMouseService.areMouseEventsActive?(this._selectionService.disable(),this.element.classList.add("enable-mouse-events")):this._selectionService.enable(),this.options.screenReaderMode&&(this._accessibilityManager.value=this._instantiationService.createInstance(P.AccessibilityManager,this)),this.register(this.optionsService.onSpecificOptionChange("screenReaderMode",M=>this._handleScreenReaderModeOptionChange(M))),this.options.overviewRulerWidth&&(this._overviewRulerRenderer=this.register(this._instantiationService.createInstance(i.OverviewRulerRenderer,this._viewportElement,this.screenElement))),this.optionsService.onSpecificOptionChange("overviewRulerWidth",M=>{!this._overviewRulerRenderer&&M&&this._viewportElement&&this.screenElement&&(this._overviewRulerRenderer=this.register(this._instantiationService.createInstance(i.OverviewRulerRenderer,this._viewportElement,this.screenElement)))}),this._charSizeService.measure(),this.refresh(0,this.rows-1),this._initGlobal(),this.bindMouse()}_createRenderer(){return this._instantiationService.createInstance(e.DomRenderer,this,this._document,this.element,this.screenElement,this._viewportElement,this._helperContainer,this.linkifier)}bindMouse(){const x=this,C=this.element;function I(M){const K=x._mouseService.getMouseReportCoords(M,x.screenElement);if(!K)return!1;let G,X;switch(M.overrideType||M.type){case"mousemove":X=32,M.buttons===void 0?(G=3,M.button!==void 0&&(G=M.button<3?M.button:3)):G=1&M.buttons?0:4&M.buttons?1:2&M.buttons?2:3;break;case"mouseup":X=0,G=M.button<3?M.button:3;break;case"mousedown":X=1,G=M.button<3?M.button:3;break;case"wheel":if(x._customWheelEventHandler&&x._customWheelEventHandler(M)===!1||x.viewport.getLinesScrolled(M)===0)return!1;X=M.deltaY<0?0:1,G=4;break;default:return!1}return!(X===void 0||G===void 0||G>4)&&x.coreMouseService.triggerMouseEvent({col:K.col,row:K.row,x:K.x,y:K.y,button:G,action:X,ctrl:M.ctrlKey,alt:M.altKey,shift:M.shiftKey})}const O={mouseup:null,wheel:null,mousedrag:null,mousemove:null},N={mouseup:M=>(I(M),M.buttons||(this._document.removeEventListener("mouseup",O.mouseup),O.mousedrag&&this._document.removeEventListener("mousemove",O.mousedrag)),this.cancel(M)),wheel:M=>(I(M),this.cancel(M,!0)),mousedrag:M=>{M.buttons&&I(M)},mousemove:M=>{M.buttons||I(M)}};this.register(this.coreMouseService.onProtocolChange(M=>{M?(this.optionsService.rawOptions.logLevel==="debug"&&this._logService.debug("Binding to mouse events:",this.coreMouseService.explainEvents(M)),this.element.classList.add("enable-mouse-events"),this._selectionService.disable()):(this._logService.debug("Unbinding from mouse events."),this.element.classList.remove("enable-mouse-events"),this._selectionService.enable()),8&M?O.mousemove||(C.addEventListener("mousemove",N.mousemove),O.mousemove=N.mousemove):(C.removeEventListener("mousemove",O.mousemove),O.mousemove=null),16&M?O.wheel||(C.addEventListener("wheel",N.wheel,{passive:!1}),O.wheel=N.wheel):(C.removeEventListener("wheel",O.wheel),O.wheel=null),2&M?O.mouseup||(O.mouseup=N.mouseup):(this._document.removeEventListener("mouseup",O.mouseup),O.mouseup=null),4&M?O.mousedrag||(O.mousedrag=N.mousedrag):(this._document.removeEventListener("mousemove",O.mousedrag),O.mousedrag=null)})),this.coreMouseService.activeProtocol=this.coreMouseService.activeProtocol,this.register((0,f.addDisposableDomListener)(C,"mousedown",M=>{if(M.preventDefault(),this.focus(),this.coreMouseService.areMouseEventsActive&&!this._selectionService.shouldForceSelection(M))return I(M),O.mouseup&&this._document.addEventListener("mouseup",O.mouseup),O.mousedrag&&this._document.addEventListener("mousemove",O.mousedrag),this.cancel(M)})),this.register((0,f.addDisposableDomListener)(C,"wheel",M=>{if(!O.wheel){if(this._customWheelEventHandler&&this._customWheelEventHandler(M)===!1)return!1;if(!this.buffer.hasScrollback){const K=this.viewport.getLinesScrolled(M);if(K===0)return;const G=U.C0.ESC+(this.coreService.decPrivateModes.applicationCursorKeys?"O":"[")+(M.deltaY<0?"A":"B");let X="";for(let Q=0;Q{if(!this.coreMouseService.areMouseEventsActive)return this.viewport.handleTouchStart(M),this.cancel(M)},{passive:!0})),this.register((0,f.addDisposableDomListener)(C,"touchmove",M=>{if(!this.coreMouseService.areMouseEventsActive)return this.viewport.handleTouchMove(M)?void 0:this.cancel(M)},{passive:!1}))}refresh(x,C){var I;(I=this._renderService)==null||I.refreshRows(x,C)}updateCursorStyle(x){var C;(C=this._selectionService)!=null&&C.shouldColumnSelect(x)?this.element.classList.add("column-select"):this.element.classList.remove("column-select")}_showCursor(){this.coreService.isCursorInitialized||(this.coreService.isCursorInitialized=!0,this.refresh(this.buffer.y,this.buffer.y))}scrollLines(x,C,I=0){var O;I===1?(super.scrollLines(x,C,I),this.refresh(0,this.rows-1)):(O=this.viewport)==null||O.scrollLines(x)}paste(x){(0,c.paste)(x,this.textarea,this.coreService,this.optionsService)}attachCustomKeyEventHandler(x){this._customKeyEventHandler=x}attachCustomWheelEventHandler(x){this._customWheelEventHandler=x}registerLinkProvider(x){return this._linkProviderService.registerLinkProvider(x)}registerCharacterJoiner(x){if(!this._characterJoinerService)throw new Error("Terminal must be opened first");const C=this._characterJoinerService.register(x);return this.refresh(0,this.rows-1),C}deregisterCharacterJoiner(x){if(!this._characterJoinerService)throw new Error("Terminal must be opened first");this._characterJoinerService.deregister(x)&&this.refresh(0,this.rows-1)}get markers(){return this.buffer.markers}registerMarker(x){return this.buffer.addMarker(this.buffer.ybase+this.buffer.y+x)}registerDecoration(x){return this._decorationService.registerDecoration(x)}hasSelection(){return!!this._selectionService&&this._selectionService.hasSelection}select(x,C,I){this._selectionService.setSelection(x,C,I)}getSelection(){return this._selectionService?this._selectionService.selectionText:""}getSelectionPosition(){if(this._selectionService&&this._selectionService.hasSelection)return{start:{x:this._selectionService.selectionStart[0],y:this._selectionService.selectionStart[1]},end:{x:this._selectionService.selectionEnd[0],y:this._selectionService.selectionEnd[1]}}}clearSelection(){var x;(x=this._selectionService)==null||x.clearSelection()}selectAll(){var x;(x=this._selectionService)==null||x.selectAll()}selectLines(x,C){var I;(I=this._selectionService)==null||I.selectLines(x,C)}_keyDown(x){if(this._keyDownHandled=!1,this._keyDownSeen=!0,this._customKeyEventHandler&&this._customKeyEventHandler(x)===!1)return!1;const C=this.browser.isMac&&this.options.macOptionIsMeta&&x.altKey;if(!C&&!this._compositionHelper.keydown(x))return this.options.scrollOnUserInput&&this.buffer.ybase!==this.buffer.ydisp&&this.scrollToBottom(),!1;C||x.key!=="Dead"&&x.key!=="AltGraph"||(this._unprocessedDeadKey=!0);const I=(0,F.evaluateKeyboardEvent)(x,this.coreService.decPrivateModes.applicationCursorKeys,this.browser.isMac,this.options.macOptionIsMeta);if(this.updateCursorStyle(x),I.type===3||I.type===2){const O=this.rows-1;return this.scrollLines(I.type===2?-O:O),this.cancel(x,!0)}return I.type===1&&this.selectAll(),!!this._isThirdLevelShift(this.browser,x)||(I.cancel&&this.cancel(x,!0),!I.key||!!(x.key&&!x.ctrlKey&&!x.altKey&&!x.metaKey&&x.key.length===1&&x.key.charCodeAt(0)>=65&&x.key.charCodeAt(0)<=90)||(this._unprocessedDeadKey?(this._unprocessedDeadKey=!1,!0):(I.key!==U.C0.ETX&&I.key!==U.C0.CR||(this.textarea.value=""),this._onKey.fire({key:I.key,domEvent:x}),this._showCursor(),this.coreService.triggerDataEvent(I.key,!0),!this.optionsService.rawOptions.screenReaderMode||x.altKey||x.ctrlKey?this.cancel(x,!0):void(this._keyDownHandled=!0))))}_isThirdLevelShift(x,C){const I=x.isMac&&!this.options.macOptionIsMeta&&C.altKey&&!C.ctrlKey&&!C.metaKey||x.isWindows&&C.altKey&&C.ctrlKey&&!C.metaKey||x.isWindows&&C.getModifierState("AltGraph");return C.type==="keypress"?I:I&&(!C.keyCode||C.keyCode>47)}_keyUp(x){this._keyDownSeen=!1,this._customKeyEventHandler&&this._customKeyEventHandler(x)===!1||(function(C){return C.keyCode===16||C.keyCode===17||C.keyCode===18}(x)||this.focus(),this.updateCursorStyle(x),this._keyPressHandled=!1)}_keyPress(x){let C;if(this._keyPressHandled=!1,this._keyDownHandled||this._customKeyEventHandler&&this._customKeyEventHandler(x)===!1)return!1;if(this.cancel(x),x.charCode)C=x.charCode;else if(x.which===null||x.which===void 0)C=x.keyCode;else{if(x.which===0||x.charCode===0)return!1;C=x.which}return!(!C||(x.altKey||x.ctrlKey||x.metaKey)&&!this._isThirdLevelShift(this.browser,x)||(C=String.fromCharCode(C),this._onKey.fire({key:C,domEvent:x}),this._showCursor(),this.coreService.triggerDataEvent(C,!0),this._keyPressHandled=!0,this._unprocessedDeadKey=!1,0))}_inputEvent(x){if(x.data&&x.inputType==="insertText"&&(!x.composed||!this._keyDownSeen)&&!this.optionsService.rawOptions.screenReaderMode){if(this._keyPressHandled)return!1;this._unprocessedDeadKey=!1;const C=x.data;return this.coreService.triggerDataEvent(C,!0),this.cancel(x),!0}return!1}resize(x,C){x!==this.cols||C!==this.rows?super.resize(x,C):this._charSizeService&&!this._charSizeService.hasValidSize&&this._charSizeService.measure()}_afterResize(x,C){var I,O;(I=this._charSizeService)==null||I.measure(),(O=this.viewport)==null||O.syncScrollArea(!0)}clear(){var x;if(this.buffer.ybase!==0||this.buffer.y!==0){this.buffer.clearAllMarkers(),this.buffer.lines.set(0,this.buffer.lines.get(this.buffer.ybase+this.buffer.y)),this.buffer.lines.length=1,this.buffer.ydisp=0,this.buffer.ybase=0,this.buffer.y=0;for(let C=1;C{Object.defineProperty(r,"__esModule",{value:!0}),r.TimeBasedDebouncer=void 0,r.TimeBasedDebouncer=class{constructor(o,c=1e3){this._renderCallback=o,this._debounceThresholdMS=c,this._lastRefreshMs=0,this._additionalRefreshRequested=!1}dispose(){this._refreshTimeoutID&&clearTimeout(this._refreshTimeoutID)}refresh(o,c,f){this._rowCount=f,o=o!==void 0?o:0,c=c!==void 0?c:this._rowCount-1,this._rowStart=this._rowStart!==void 0?Math.min(this._rowStart,o):o,this._rowEnd=this._rowEnd!==void 0?Math.max(this._rowEnd,c):c;const n=Date.now();if(n-this._lastRefreshMs>=this._debounceThresholdMS)this._lastRefreshMs=n,this._innerRefresh();else if(!this._additionalRefreshRequested){const d=n-this._lastRefreshMs,v=this._debounceThresholdMS-d;this._additionalRefreshRequested=!0,this._refreshTimeoutID=window.setTimeout(()=>{this._lastRefreshMs=Date.now(),this._innerRefresh(),this._additionalRefreshRequested=!1,this._refreshTimeoutID=void 0},v)}}_innerRefresh(){if(this._rowStart===void 0||this._rowEnd===void 0||this._rowCount===void 0)return;const o=Math.max(this._rowStart,0),c=Math.min(this._rowEnd,this._rowCount-1);this._rowStart=void 0,this._rowEnd=void 0,this._renderCallback(o,c)}}},1680:function(R,r,o){var c=this&&this.__decorate||function(s,e,t,a){var _,u=arguments.length,g=u<3?e:a===null?a=Object.getOwnPropertyDescriptor(e,t):a;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")g=Reflect.decorate(s,e,t,a);else for(var h=s.length-1;h>=0;h--)(_=s[h])&&(g=(u<3?_(g):u>3?_(e,t,g):_(e,t))||g);return u>3&&g&&Object.defineProperty(e,t,g),g},f=this&&this.__param||function(s,e){return function(t,a){e(t,a,s)}};Object.defineProperty(r,"__esModule",{value:!0}),r.Viewport=void 0;const n=o(3656),d=o(4725),v=o(8460),p=o(844),l=o(2585);let i=r.Viewport=class extends p.Disposable{constructor(s,e,t,a,_,u,g,h){super(),this._viewportElement=s,this._scrollArea=e,this._bufferService=t,this._optionsService=a,this._charSizeService=_,this._renderService=u,this._coreBrowserService=g,this.scrollBarWidth=0,this._currentRowHeight=0,this._currentDeviceCellHeight=0,this._lastRecordedBufferLength=0,this._lastRecordedViewportHeight=0,this._lastRecordedBufferHeight=0,this._lastTouchY=0,this._lastScrollTop=0,this._wheelPartialScroll=0,this._refreshAnimationFrame=null,this._ignoreNextScrollEvent=!1,this._smoothScrollState={startTime:0,origin:-1,target:-1},this._onRequestScrollLines=this.register(new v.EventEmitter),this.onRequestScrollLines=this._onRequestScrollLines.event,this.scrollBarWidth=this._viewportElement.offsetWidth-this._scrollArea.offsetWidth||15,this.register((0,n.addDisposableDomListener)(this._viewportElement,"scroll",this._handleScroll.bind(this))),this._activeBuffer=this._bufferService.buffer,this.register(this._bufferService.buffers.onBufferActivate(m=>this._activeBuffer=m.activeBuffer)),this._renderDimensions=this._renderService.dimensions,this.register(this._renderService.onDimensionsChange(m=>this._renderDimensions=m)),this._handleThemeChange(h.colors),this.register(h.onChangeColors(m=>this._handleThemeChange(m))),this.register(this._optionsService.onSpecificOptionChange("scrollback",()=>this.syncScrollArea())),setTimeout(()=>this.syncScrollArea())}_handleThemeChange(s){this._viewportElement.style.backgroundColor=s.background.css}reset(){this._currentRowHeight=0,this._currentDeviceCellHeight=0,this._lastRecordedBufferLength=0,this._lastRecordedViewportHeight=0,this._lastRecordedBufferHeight=0,this._lastTouchY=0,this._lastScrollTop=0,this._coreBrowserService.window.requestAnimationFrame(()=>this.syncScrollArea())}_refresh(s){if(s)return this._innerRefresh(),void(this._refreshAnimationFrame!==null&&this._coreBrowserService.window.cancelAnimationFrame(this._refreshAnimationFrame));this._refreshAnimationFrame===null&&(this._refreshAnimationFrame=this._coreBrowserService.window.requestAnimationFrame(()=>this._innerRefresh()))}_innerRefresh(){if(this._charSizeService.height>0){this._currentRowHeight=this._renderDimensions.device.cell.height/this._coreBrowserService.dpr,this._currentDeviceCellHeight=this._renderDimensions.device.cell.height,this._lastRecordedViewportHeight=this._viewportElement.offsetHeight;const e=Math.round(this._currentRowHeight*this._lastRecordedBufferLength)+(this._lastRecordedViewportHeight-this._renderDimensions.css.canvas.height);this._lastRecordedBufferHeight!==e&&(this._lastRecordedBufferHeight=e,this._scrollArea.style.height=this._lastRecordedBufferHeight+"px")}const s=this._bufferService.buffer.ydisp*this._currentRowHeight;this._viewportElement.scrollTop!==s&&(this._ignoreNextScrollEvent=!0,this._viewportElement.scrollTop=s),this._refreshAnimationFrame=null}syncScrollArea(s=!1){if(this._lastRecordedBufferLength!==this._bufferService.buffer.lines.length)return this._lastRecordedBufferLength=this._bufferService.buffer.lines.length,void this._refresh(s);this._lastRecordedViewportHeight===this._renderService.dimensions.css.canvas.height&&this._lastScrollTop===this._activeBuffer.ydisp*this._currentRowHeight&&this._renderDimensions.device.cell.height===this._currentDeviceCellHeight||this._refresh(s)}_handleScroll(s){if(this._lastScrollTop=this._viewportElement.scrollTop,!this._viewportElement.offsetParent)return;if(this._ignoreNextScrollEvent)return this._ignoreNextScrollEvent=!1,void this._onRequestScrollLines.fire({amount:0,suppressScrollEvent:!0});const e=Math.round(this._lastScrollTop/this._currentRowHeight)-this._bufferService.buffer.ydisp;this._onRequestScrollLines.fire({amount:e,suppressScrollEvent:!0})}_smoothScroll(){if(this._isDisposed||this._smoothScrollState.origin===-1||this._smoothScrollState.target===-1)return;const s=this._smoothScrollPercent();this._viewportElement.scrollTop=this._smoothScrollState.origin+Math.round(s*(this._smoothScrollState.target-this._smoothScrollState.origin)),s<1?this._coreBrowserService.window.requestAnimationFrame(()=>this._smoothScroll()):this._clearSmoothScrollState()}_smoothScrollPercent(){return this._optionsService.rawOptions.smoothScrollDuration&&this._smoothScrollState.startTime?Math.max(Math.min((Date.now()-this._smoothScrollState.startTime)/this._optionsService.rawOptions.smoothScrollDuration,1),0):1}_clearSmoothScrollState(){this._smoothScrollState.startTime=0,this._smoothScrollState.origin=-1,this._smoothScrollState.target=-1}_bubbleScroll(s,e){const t=this._viewportElement.scrollTop+this._lastRecordedViewportHeight;return!(e<0&&this._viewportElement.scrollTop!==0||e>0&&t0&&(a=D),_=""}}return{bufferElements:u,cursorElement:a}}getLinesScrolled(s){if(s.deltaY===0||s.shiftKey)return 0;let e=this._applyScrollModifier(s.deltaY,s);return s.deltaMode===WheelEvent.DOM_DELTA_PIXEL?(e/=this._currentRowHeight+0,this._wheelPartialScroll+=e,e=Math.floor(Math.abs(this._wheelPartialScroll))*(this._wheelPartialScroll>0?1:-1),this._wheelPartialScroll%=1):s.deltaMode===WheelEvent.DOM_DELTA_PAGE&&(e*=this._bufferService.rows),e}_applyScrollModifier(s,e){const t=this._optionsService.rawOptions.fastScrollModifier;return t==="alt"&&e.altKey||t==="ctrl"&&e.ctrlKey||t==="shift"&&e.shiftKey?s*this._optionsService.rawOptions.fastScrollSensitivity*this._optionsService.rawOptions.scrollSensitivity:s*this._optionsService.rawOptions.scrollSensitivity}handleTouchStart(s){this._lastTouchY=s.touches[0].pageY}handleTouchMove(s){const e=this._lastTouchY-s.touches[0].pageY;return this._lastTouchY=s.touches[0].pageY,e!==0&&(this._viewportElement.scrollTop+=e,this._bubbleScroll(s,e))}};r.Viewport=i=c([f(2,l.IBufferService),f(3,l.IOptionsService),f(4,d.ICharSizeService),f(5,d.IRenderService),f(6,d.ICoreBrowserService),f(7,d.IThemeService)],i)},3107:function(R,r,o){var c=this&&this.__decorate||function(l,i,s,e){var t,a=arguments.length,_=a<3?i:e===null?e=Object.getOwnPropertyDescriptor(i,s):e;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")_=Reflect.decorate(l,i,s,e);else for(var u=l.length-1;u>=0;u--)(t=l[u])&&(_=(a<3?t(_):a>3?t(i,s,_):t(i,s))||_);return a>3&&_&&Object.defineProperty(i,s,_),_},f=this&&this.__param||function(l,i){return function(s,e){i(s,e,l)}};Object.defineProperty(r,"__esModule",{value:!0}),r.BufferDecorationRenderer=void 0;const n=o(4725),d=o(844),v=o(2585);let p=r.BufferDecorationRenderer=class extends d.Disposable{constructor(l,i,s,e,t){super(),this._screenElement=l,this._bufferService=i,this._coreBrowserService=s,this._decorationService=e,this._renderService=t,this._decorationElements=new Map,this._altBufferIsActive=!1,this._dimensionsChanged=!1,this._container=document.createElement("div"),this._container.classList.add("xterm-decoration-container"),this._screenElement.appendChild(this._container),this.register(this._renderService.onRenderedViewportChange(()=>this._doRefreshDecorations())),this.register(this._renderService.onDimensionsChange(()=>{this._dimensionsChanged=!0,this._queueRefresh()})),this.register(this._coreBrowserService.onDprChange(()=>this._queueRefresh())),this.register(this._bufferService.buffers.onBufferActivate(()=>{this._altBufferIsActive=this._bufferService.buffer===this._bufferService.buffers.alt})),this.register(this._decorationService.onDecorationRegistered(()=>this._queueRefresh())),this.register(this._decorationService.onDecorationRemoved(a=>this._removeDecoration(a))),this.register((0,d.toDisposable)(()=>{this._container.remove(),this._decorationElements.clear()}))}_queueRefresh(){this._animationFrame===void 0&&(this._animationFrame=this._renderService.addRefreshCallback(()=>{this._doRefreshDecorations(),this._animationFrame=void 0}))}_doRefreshDecorations(){for(const l of this._decorationService.decorations)this._renderDecoration(l);this._dimensionsChanged=!1}_renderDecoration(l){this._refreshStyle(l),this._dimensionsChanged&&this._refreshXPosition(l)}_createElement(l){var i,s;const e=this._coreBrowserService.mainDocument.createElement("div");e.classList.add("xterm-decoration"),e.classList.toggle("xterm-decoration-top-layer",((i=l?.options)==null?void 0:i.layer)==="top"),e.style.width=`${Math.round((l.options.width||1)*this._renderService.dimensions.css.cell.width)}px`,e.style.height=(l.options.height||1)*this._renderService.dimensions.css.cell.height+"px",e.style.top=(l.marker.line-this._bufferService.buffers.active.ydisp)*this._renderService.dimensions.css.cell.height+"px",e.style.lineHeight=`${this._renderService.dimensions.css.cell.height}px`;const t=(s=l.options.x)!=null?s:0;return t&&t>this._bufferService.cols&&(e.style.display="none"),this._refreshXPosition(l,e),e}_refreshStyle(l){const i=l.marker.line-this._bufferService.buffers.active.ydisp;if(i<0||i>=this._bufferService.rows)l.element&&(l.element.style.display="none",l.onRenderEmitter.fire(l.element));else{let s=this._decorationElements.get(l);s||(s=this._createElement(l),l.element=s,this._decorationElements.set(l,s),this._container.appendChild(s),l.onDispose(()=>{this._decorationElements.delete(l),s.remove()})),s.style.top=i*this._renderService.dimensions.css.cell.height+"px",s.style.display=this._altBufferIsActive?"none":"block",l.onRenderEmitter.fire(s)}}_refreshXPosition(l,i=l.element){var s;if(!i)return;const e=(s=l.options.x)!=null?s:0;(l.options.anchor||"left")==="right"?i.style.right=e?e*this._renderService.dimensions.css.cell.width+"px":"":i.style.left=e?e*this._renderService.dimensions.css.cell.width+"px":""}_removeDecoration(l){var i;(i=this._decorationElements.get(l))==null||i.remove(),this._decorationElements.delete(l),l.dispose()}};r.BufferDecorationRenderer=p=c([f(1,v.IBufferService),f(2,n.ICoreBrowserService),f(3,v.IDecorationService),f(4,n.IRenderService)],p)},5871:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.ColorZoneStore=void 0,r.ColorZoneStore=class{constructor(){this._zones=[],this._zonePool=[],this._zonePoolIndex=0,this._linePadding={full:0,left:0,center:0,right:0}}get zones(){return this._zonePool.length=Math.min(this._zonePool.length,this._zones.length),this._zones}clear(){this._zones.length=0,this._zonePoolIndex=0}addDecoration(o){if(o.options.overviewRulerOptions){for(const c of this._zones)if(c.color===o.options.overviewRulerOptions.color&&c.position===o.options.overviewRulerOptions.position){if(this._lineIntersectsZone(c,o.marker.line))return;if(this._lineAdjacentToZone(c,o.marker.line,o.options.overviewRulerOptions.position))return void this._addLineToZone(c,o.marker.line)}if(this._zonePoolIndex=o.startBufferLine&&c<=o.endBufferLine}_lineAdjacentToZone(o,c,f){return c>=o.startBufferLine-this._linePadding[f||"full"]&&c<=o.endBufferLine+this._linePadding[f||"full"]}_addLineToZone(o,c){o.startBufferLine=Math.min(o.startBufferLine,c),o.endBufferLine=Math.max(o.endBufferLine,c)}}},5744:function(R,r,o){var c=this&&this.__decorate||function(t,a,_,u){var g,h=arguments.length,m=h<3?a:u===null?u=Object.getOwnPropertyDescriptor(a,_):u;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")m=Reflect.decorate(t,a,_,u);else for(var y=t.length-1;y>=0;y--)(g=t[y])&&(m=(h<3?g(m):h>3?g(a,_,m):g(a,_))||m);return h>3&&m&&Object.defineProperty(a,_,m),m},f=this&&this.__param||function(t,a){return function(_,u){a(_,u,t)}};Object.defineProperty(r,"__esModule",{value:!0}),r.OverviewRulerRenderer=void 0;const n=o(5871),d=o(4725),v=o(844),p=o(2585),l={full:0,left:0,center:0,right:0},i={full:0,left:0,center:0,right:0},s={full:0,left:0,center:0,right:0};let e=r.OverviewRulerRenderer=class extends v.Disposable{get _width(){return this._optionsService.options.overviewRulerWidth||0}constructor(t,a,_,u,g,h,m){var y;super(),this._viewportElement=t,this._screenElement=a,this._bufferService=_,this._decorationService=u,this._renderService=g,this._optionsService=h,this._coreBrowserService=m,this._colorZoneStore=new n.ColorZoneStore,this._shouldUpdateDimensions=!0,this._shouldUpdateAnchor=!0,this._lastKnownBufferLength=0,this._canvas=this._coreBrowserService.mainDocument.createElement("canvas"),this._canvas.classList.add("xterm-decoration-overview-ruler"),this._refreshCanvasDimensions(),(y=this._viewportElement.parentElement)==null||y.insertBefore(this._canvas,this._viewportElement);const k=this._canvas.getContext("2d");if(!k)throw new Error("Ctx cannot be null");this._ctx=k,this._registerDecorationListeners(),this._registerBufferChangeListeners(),this._registerDimensionChangeListeners(),this.register((0,v.toDisposable)(()=>{var D;(D=this._canvas)==null||D.remove()}))}_registerDecorationListeners(){this.register(this._decorationService.onDecorationRegistered(()=>this._queueRefresh(void 0,!0))),this.register(this._decorationService.onDecorationRemoved(()=>this._queueRefresh(void 0,!0)))}_registerBufferChangeListeners(){this.register(this._renderService.onRenderedViewportChange(()=>this._queueRefresh())),this.register(this._bufferService.buffers.onBufferActivate(()=>{this._canvas.style.display=this._bufferService.buffer===this._bufferService.buffers.alt?"none":"block"})),this.register(this._bufferService.onScroll(()=>{this._lastKnownBufferLength!==this._bufferService.buffers.normal.lines.length&&(this._refreshDrawHeightConstants(),this._refreshColorZonePadding())}))}_registerDimensionChangeListeners(){this.register(this._renderService.onRender(()=>{this._containerHeight&&this._containerHeight===this._screenElement.clientHeight||(this._queueRefresh(!0),this._containerHeight=this._screenElement.clientHeight)})),this.register(this._optionsService.onSpecificOptionChange("overviewRulerWidth",()=>this._queueRefresh(!0))),this.register(this._coreBrowserService.onDprChange(()=>this._queueRefresh(!0))),this._queueRefresh(!0)}_refreshDrawConstants(){const t=Math.floor(this._canvas.width/3),a=Math.ceil(this._canvas.width/3);i.full=this._canvas.width,i.left=t,i.center=a,i.right=t,this._refreshDrawHeightConstants(),s.full=0,s.left=0,s.center=i.left,s.right=i.left+i.center}_refreshDrawHeightConstants(){l.full=Math.round(2*this._coreBrowserService.dpr);const t=this._canvas.height/this._bufferService.buffer.lines.length,a=Math.round(Math.max(Math.min(t,12),6)*this._coreBrowserService.dpr);l.left=a,l.center=a,l.right=a}_refreshColorZonePadding(){this._colorZoneStore.setPadding({full:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.full),left:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.left),center:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.center),right:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*l.right)}),this._lastKnownBufferLength=this._bufferService.buffers.normal.lines.length}_refreshCanvasDimensions(){this._canvas.style.width=`${this._width}px`,this._canvas.width=Math.round(this._width*this._coreBrowserService.dpr),this._canvas.style.height=`${this._screenElement.clientHeight}px`,this._canvas.height=Math.round(this._screenElement.clientHeight*this._coreBrowserService.dpr),this._refreshDrawConstants(),this._refreshColorZonePadding()}_refreshDecorations(){this._shouldUpdateDimensions&&this._refreshCanvasDimensions(),this._ctx.clearRect(0,0,this._canvas.width,this._canvas.height),this._colorZoneStore.clear();for(const a of this._decorationService.decorations)this._colorZoneStore.addDecoration(a);this._ctx.lineWidth=1;const t=this._colorZoneStore.zones;for(const a of t)a.position!=="full"&&this._renderColorZone(a);for(const a of t)a.position==="full"&&this._renderColorZone(a);this._shouldUpdateDimensions=!1,this._shouldUpdateAnchor=!1}_renderColorZone(t){this._ctx.fillStyle=t.color,this._ctx.fillRect(s[t.position||"full"],Math.round((this._canvas.height-1)*(t.startBufferLine/this._bufferService.buffers.active.lines.length)-l[t.position||"full"]/2),i[t.position||"full"],Math.round((this._canvas.height-1)*((t.endBufferLine-t.startBufferLine)/this._bufferService.buffers.active.lines.length)+l[t.position||"full"]))}_queueRefresh(t,a){this._shouldUpdateDimensions=t||this._shouldUpdateDimensions,this._shouldUpdateAnchor=a||this._shouldUpdateAnchor,this._animationFrame===void 0&&(this._animationFrame=this._coreBrowserService.window.requestAnimationFrame(()=>{this._refreshDecorations(),this._animationFrame=void 0}))}};r.OverviewRulerRenderer=e=c([f(2,p.IBufferService),f(3,p.IDecorationService),f(4,d.IRenderService),f(5,p.IOptionsService),f(6,d.ICoreBrowserService)],e)},2950:function(R,r,o){var c=this&&this.__decorate||function(l,i,s,e){var t,a=arguments.length,_=a<3?i:e===null?e=Object.getOwnPropertyDescriptor(i,s):e;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")_=Reflect.decorate(l,i,s,e);else for(var u=l.length-1;u>=0;u--)(t=l[u])&&(_=(a<3?t(_):a>3?t(i,s,_):t(i,s))||_);return a>3&&_&&Object.defineProperty(i,s,_),_},f=this&&this.__param||function(l,i){return function(s,e){i(s,e,l)}};Object.defineProperty(r,"__esModule",{value:!0}),r.CompositionHelper=void 0;const n=o(4725),d=o(2585),v=o(2584);let p=r.CompositionHelper=class{get isComposing(){return this._isComposing}constructor(l,i,s,e,t,a){this._textarea=l,this._compositionView=i,this._bufferService=s,this._optionsService=e,this._coreService=t,this._renderService=a,this._isComposing=!1,this._isSendingComposition=!1,this._compositionPosition={start:0,end:0},this._dataAlreadySent=""}compositionstart(){this._isComposing=!0,this._compositionPosition.start=this._textarea.value.length,this._compositionView.textContent="",this._dataAlreadySent="",this._compositionView.classList.add("active")}compositionupdate(l){this._compositionView.textContent=l.data,this.updateCompositionElements(),setTimeout(()=>{this._compositionPosition.end=this._textarea.value.length},0)}compositionend(){this._finalizeComposition(!0)}keydown(l){if(this._isComposing||this._isSendingComposition){if(l.keyCode===229||l.keyCode===16||l.keyCode===17||l.keyCode===18)return!1;this._finalizeComposition(!1)}return l.keyCode!==229||(this._handleAnyTextareaChanges(),!1)}_finalizeComposition(l){if(this._compositionView.classList.remove("active"),this._isComposing=!1,l){const i={start:this._compositionPosition.start,end:this._compositionPosition.end};this._isSendingComposition=!0,setTimeout(()=>{if(this._isSendingComposition){let s;this._isSendingComposition=!1,i.start+=this._dataAlreadySent.length,s=this._isComposing?this._textarea.value.substring(i.start,i.end):this._textarea.value.substring(i.start),s.length>0&&this._coreService.triggerDataEvent(s,!0)}},0)}else{this._isSendingComposition=!1;const i=this._textarea.value.substring(this._compositionPosition.start,this._compositionPosition.end);this._coreService.triggerDataEvent(i,!0)}}_handleAnyTextareaChanges(){const l=this._textarea.value;setTimeout(()=>{if(!this._isComposing){const i=this._textarea.value,s=i.replace(l,"");this._dataAlreadySent=s,i.length>l.length?this._coreService.triggerDataEvent(s,!0):i.lengththis.updateCompositionElements(!0),0)}}};r.CompositionHelper=p=c([f(2,d.IBufferService),f(3,d.IOptionsService),f(4,d.ICoreService),f(5,n.IRenderService)],p)},9806:(R,r)=>{function o(c,f,n){const d=n.getBoundingClientRect(),v=c.getComputedStyle(n),p=parseInt(v.getPropertyValue("padding-left")),l=parseInt(v.getPropertyValue("padding-top"));return[f.clientX-d.left-p,f.clientY-d.top-l]}Object.defineProperty(r,"__esModule",{value:!0}),r.getCoords=r.getCoordsRelativeToElement=void 0,r.getCoordsRelativeToElement=o,r.getCoords=function(c,f,n,d,v,p,l,i,s){if(!p)return;const e=o(c,f,n);return e?(e[0]=Math.ceil((e[0]+(s?l/2:0))/l),e[1]=Math.ceil(e[1]/i),e[0]=Math.min(Math.max(e[0],1),d+(s?1:0)),e[1]=Math.min(Math.max(e[1],1),v),e):void 0}},9504:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.moveToCellSequence=void 0;const c=o(2584);function f(i,s,e,t){const a=i-n(i,e),_=s-n(s,e),u=Math.abs(a-_)-function(g,h,m){let y=0;const k=g-n(g,m),D=h-n(h,m);for(let b=0;b=0&&is?"A":"B"}function v(i,s,e,t,a,_){let u=i,g=s,h="";for(;u!==e||g!==t;)u+=a?1:-1,a&&u>_.cols-1?(h+=_.buffer.translateBufferLineToString(g,!1,i,u),u=0,i=0,g++):!a&&u<0&&(h+=_.buffer.translateBufferLineToString(g,!1,0,i+1),u=_.cols-1,i=u,g--);return h+_.buffer.translateBufferLineToString(g,!1,i,u)}function p(i,s){const e=s?"O":"[";return c.C0.ESC+e+i}function l(i,s){i=Math.floor(i);let e="";for(let t=0;t0?k-n(k,D):m;const A=k,H=function(U,F,S,w,E,L){let P;return P=f(S,w,E,L).length>0?w-n(w,E):F,U=S&&Pi?"D":"C",l(Math.abs(a-i),p(u,t));u=_>s?"D":"C";const g=Math.abs(_-s);return l(function(h,m){return m.cols-h}(_>s?i:a,e)+(g-1)*e.cols+1+((_>s?a:i)-1),p(u,t))}},1296:function(R,r,o){var c=this&&this.__decorate||function(b,B,A,H){var U,F=arguments.length,S=F<3?B:H===null?H=Object.getOwnPropertyDescriptor(B,A):H;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")S=Reflect.decorate(b,B,A,H);else for(var w=b.length-1;w>=0;w--)(U=b[w])&&(S=(F<3?U(S):F>3?U(B,A,S):U(B,A))||S);return F>3&&S&&Object.defineProperty(B,A,S),S},f=this&&this.__param||function(b,B){return function(A,H){B(A,H,b)}};Object.defineProperty(r,"__esModule",{value:!0}),r.DomRenderer=void 0;const n=o(3787),d=o(2550),v=o(2223),p=o(6171),l=o(6052),i=o(4725),s=o(8055),e=o(8460),t=o(844),a=o(2585),_="xterm-dom-renderer-owner-",u="xterm-rows",g="xterm-fg-",h="xterm-bg-",m="xterm-focus",y="xterm-selection";let k=1,D=r.DomRenderer=class extends t.Disposable{constructor(b,B,A,H,U,F,S,w,E,L,P,W,$){super(),this._terminal=b,this._document=B,this._element=A,this._screenElement=H,this._viewportElement=U,this._helperContainer=F,this._linkifier2=S,this._charSizeService=E,this._optionsService=L,this._bufferService=P,this._coreBrowserService=W,this._themeService=$,this._terminalClass=k++,this._rowElements=[],this._selectionRenderModel=(0,l.createSelectionRenderModel)(),this.onRequestRedraw=this.register(new e.EventEmitter).event,this._rowContainer=this._document.createElement("div"),this._rowContainer.classList.add(u),this._rowContainer.style.lineHeight="normal",this._rowContainer.setAttribute("aria-hidden","true"),this._refreshRowElements(this._bufferService.cols,this._bufferService.rows),this._selectionContainer=this._document.createElement("div"),this._selectionContainer.classList.add(y),this._selectionContainer.setAttribute("aria-hidden","true"),this.dimensions=(0,p.createRenderDimensions)(),this._updateDimensions(),this.register(this._optionsService.onOptionChange(()=>this._handleOptionsChanged())),this.register(this._themeService.onChangeColors(j=>this._injectCss(j))),this._injectCss(this._themeService.colors),this._rowFactory=w.createInstance(n.DomRendererRowFactory,document),this._element.classList.add(_+this._terminalClass),this._screenElement.appendChild(this._rowContainer),this._screenElement.appendChild(this._selectionContainer),this.register(this._linkifier2.onShowLinkUnderline(j=>this._handleLinkHover(j))),this.register(this._linkifier2.onHideLinkUnderline(j=>this._handleLinkLeave(j))),this.register((0,t.toDisposable)(()=>{this._element.classList.remove(_+this._terminalClass),this._rowContainer.remove(),this._selectionContainer.remove(),this._widthCache.dispose(),this._themeStyleElement.remove(),this._dimensionsStyleElement.remove()})),this._widthCache=new d.WidthCache(this._document,this._helperContainer),this._widthCache.setFont(this._optionsService.rawOptions.fontFamily,this._optionsService.rawOptions.fontSize,this._optionsService.rawOptions.fontWeight,this._optionsService.rawOptions.fontWeightBold),this._setDefaultSpacing()}_updateDimensions(){const b=this._coreBrowserService.dpr;this.dimensions.device.char.width=this._charSizeService.width*b,this.dimensions.device.char.height=Math.ceil(this._charSizeService.height*b),this.dimensions.device.cell.width=this.dimensions.device.char.width+Math.round(this._optionsService.rawOptions.letterSpacing),this.dimensions.device.cell.height=Math.floor(this.dimensions.device.char.height*this._optionsService.rawOptions.lineHeight),this.dimensions.device.char.left=0,this.dimensions.device.char.top=0,this.dimensions.device.canvas.width=this.dimensions.device.cell.width*this._bufferService.cols,this.dimensions.device.canvas.height=this.dimensions.device.cell.height*this._bufferService.rows,this.dimensions.css.canvas.width=Math.round(this.dimensions.device.canvas.width/b),this.dimensions.css.canvas.height=Math.round(this.dimensions.device.canvas.height/b),this.dimensions.css.cell.width=this.dimensions.css.canvas.width/this._bufferService.cols,this.dimensions.css.cell.height=this.dimensions.css.canvas.height/this._bufferService.rows;for(const A of this._rowElements)A.style.width=`${this.dimensions.css.canvas.width}px`,A.style.height=`${this.dimensions.css.cell.height}px`,A.style.lineHeight=`${this.dimensions.css.cell.height}px`,A.style.overflow="hidden";this._dimensionsStyleElement||(this._dimensionsStyleElement=this._document.createElement("style"),this._screenElement.appendChild(this._dimensionsStyleElement));const B=`${this._terminalSelector} .${u} span { display: inline-block; height: 100%; vertical-align: top;}`;this._dimensionsStyleElement.textContent=B,this._selectionContainer.style.height=this._viewportElement.style.height,this._screenElement.style.width=`${this.dimensions.css.canvas.width}px`,this._screenElement.style.height=`${this.dimensions.css.canvas.height}px`}_injectCss(b){this._themeStyleElement||(this._themeStyleElement=this._document.createElement("style"),this._screenElement.appendChild(this._themeStyleElement));let B=`${this._terminalSelector} .${u} { color: ${b.foreground.css}; font-family: ${this._optionsService.rawOptions.fontFamily}; font-size: ${this._optionsService.rawOptions.fontSize}px; font-kerning: none; white-space: pre}`;B+=`${this._terminalSelector} .${u} .xterm-dim { color: ${s.color.multiplyOpacity(b.foreground,.5).css};}`,B+=`${this._terminalSelector} span:not(.xterm-bold) { font-weight: ${this._optionsService.rawOptions.fontWeight};}${this._terminalSelector} span.xterm-bold { font-weight: ${this._optionsService.rawOptions.fontWeightBold};}${this._terminalSelector} span.xterm-italic { font-style: italic;}`;const A=`blink_underline_${this._terminalClass}`,H=`blink_bar_${this._terminalClass}`,U=`blink_block_${this._terminalClass}`;B+=`@keyframes ${A} { 50% { border-bottom-style: hidden; }}`,B+=`@keyframes ${H} { 50% { box-shadow: none; }}`,B+=`@keyframes ${U} { 0% { background-color: ${b.cursor.css}; color: ${b.cursorAccent.css}; } 50% { background-color: inherit; color: ${b.cursor.css}; }}`,B+=`${this._terminalSelector} .${u}.${m} .xterm-cursor.xterm-cursor-blink.xterm-cursor-underline { animation: ${A} 1s step-end infinite;}${this._terminalSelector} .${u}.${m} .xterm-cursor.xterm-cursor-blink.xterm-cursor-bar { animation: ${H} 1s step-end infinite;}${this._terminalSelector} .${u}.${m} .xterm-cursor.xterm-cursor-blink.xterm-cursor-block { animation: ${U} 1s step-end infinite;}${this._terminalSelector} .${u} .xterm-cursor.xterm-cursor-block { background-color: ${b.cursor.css}; color: ${b.cursorAccent.css};}${this._terminalSelector} .${u} .xterm-cursor.xterm-cursor-block:not(.xterm-cursor-blink) { background-color: ${b.cursor.css} !important; color: ${b.cursorAccent.css} !important;}${this._terminalSelector} .${u} .xterm-cursor.xterm-cursor-outline { outline: 1px solid ${b.cursor.css}; outline-offset: -1px;}${this._terminalSelector} .${u} .xterm-cursor.xterm-cursor-bar { box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${b.cursor.css} inset;}${this._terminalSelector} .${u} .xterm-cursor.xterm-cursor-underline { border-bottom: 1px ${b.cursor.css}; border-bottom-style: solid; height: calc(100% - 1px);}`,B+=`${this._terminalSelector} .${y} { position: absolute; top: 0; left: 0; z-index: 1; pointer-events: none;}${this._terminalSelector}.focus .${y} div { position: absolute; background-color: ${b.selectionBackgroundOpaque.css};}${this._terminalSelector} .${y} div { position: absolute; background-color: ${b.selectionInactiveBackgroundOpaque.css};}`;for(const[F,S]of b.ansi.entries())B+=`${this._terminalSelector} .${g}${F} { color: ${S.css}; }${this._terminalSelector} .${g}${F}.xterm-dim { color: ${s.color.multiplyOpacity(S,.5).css}; }${this._terminalSelector} .${h}${F} { background-color: ${S.css}; }`;B+=`${this._terminalSelector} .${g}${v.INVERTED_DEFAULT_COLOR} { color: ${s.color.opaque(b.background).css}; }${this._terminalSelector} .${g}${v.INVERTED_DEFAULT_COLOR}.xterm-dim { color: ${s.color.multiplyOpacity(s.color.opaque(b.background),.5).css}; }${this._terminalSelector} .${h}${v.INVERTED_DEFAULT_COLOR} { background-color: ${b.foreground.css}; }`,this._themeStyleElement.textContent=B}_setDefaultSpacing(){const b=this.dimensions.css.cell.width-this._widthCache.get("W",!1,!1);this._rowContainer.style.letterSpacing=`${b}px`,this._rowFactory.defaultSpacing=b}handleDevicePixelRatioChange(){this._updateDimensions(),this._widthCache.clear(),this._setDefaultSpacing()}_refreshRowElements(b,B){for(let A=this._rowElements.length;A<=B;A++){const H=this._document.createElement("div");this._rowContainer.appendChild(H),this._rowElements.push(H)}for(;this._rowElements.length>B;)this._rowContainer.removeChild(this._rowElements.pop())}handleResize(b,B){this._refreshRowElements(b,B),this._updateDimensions(),this.handleSelectionChanged(this._selectionRenderModel.selectionStart,this._selectionRenderModel.selectionEnd,this._selectionRenderModel.columnSelectMode)}handleCharSizeChanged(){this._updateDimensions(),this._widthCache.clear(),this._setDefaultSpacing()}handleBlur(){this._rowContainer.classList.remove(m),this.renderRows(0,this._bufferService.rows-1)}handleFocus(){this._rowContainer.classList.add(m),this.renderRows(this._bufferService.buffer.y,this._bufferService.buffer.y)}handleSelectionChanged(b,B,A){if(this._selectionContainer.replaceChildren(),this._rowFactory.handleSelectionChanged(b,B,A),this.renderRows(0,this._bufferService.rows-1),!b||!B)return;this._selectionRenderModel.update(this._terminal,b,B,A);const H=this._selectionRenderModel.viewportStartRow,U=this._selectionRenderModel.viewportEndRow,F=this._selectionRenderModel.viewportCappedStartRow,S=this._selectionRenderModel.viewportCappedEndRow;if(F>=this._bufferService.rows||S<0)return;const w=this._document.createDocumentFragment();if(A){const E=b[0]>B[0];w.appendChild(this._createSelectionElement(F,E?B[0]:b[0],E?b[0]:B[0],S-F+1))}else{const E=H===F?b[0]:0,L=F===U?B[0]:this._bufferService.cols;w.appendChild(this._createSelectionElement(F,E,L));const P=S-F-1;if(w.appendChild(this._createSelectionElement(F+1,0,this._bufferService.cols,P)),F!==S){const W=U===S?B[0]:this._bufferService.cols;w.appendChild(this._createSelectionElement(S,0,W))}}this._selectionContainer.appendChild(w)}_createSelectionElement(b,B,A,H=1){const U=this._document.createElement("div"),F=B*this.dimensions.css.cell.width;let S=this.dimensions.css.cell.width*(A-B);return F+S>this.dimensions.css.canvas.width&&(S=this.dimensions.css.canvas.width-F),U.style.height=H*this.dimensions.css.cell.height+"px",U.style.top=b*this.dimensions.css.cell.height+"px",U.style.left=`${F}px`,U.style.width=`${S}px`,U}handleCursorMove(){}_handleOptionsChanged(){this._updateDimensions(),this._injectCss(this._themeService.colors),this._widthCache.setFont(this._optionsService.rawOptions.fontFamily,this._optionsService.rawOptions.fontSize,this._optionsService.rawOptions.fontWeight,this._optionsService.rawOptions.fontWeightBold),this._setDefaultSpacing()}clear(){for(const b of this._rowElements)b.replaceChildren()}renderRows(b,B){const A=this._bufferService.buffer,H=A.ybase+A.y,U=Math.min(A.x,this._bufferService.cols-1),F=this._optionsService.rawOptions.cursorBlink,S=this._optionsService.rawOptions.cursorStyle,w=this._optionsService.rawOptions.cursorInactiveStyle;for(let E=b;E<=B;E++){const L=E+A.ydisp,P=this._rowElements[E],W=A.lines.get(L);if(!P||!W)break;P.replaceChildren(...this._rowFactory.createRow(W,L,L===H,S,w,U,F,this.dimensions.css.cell.width,this._widthCache,-1,-1))}}get _terminalSelector(){return`.${_}${this._terminalClass}`}_handleLinkHover(b){this._setCellUnderline(b.x1,b.x2,b.y1,b.y2,b.cols,!0)}_handleLinkLeave(b){this._setCellUnderline(b.x1,b.x2,b.y1,b.y2,b.cols,!1)}_setCellUnderline(b,B,A,H,U,F){A<0&&(b=0),H<0&&(B=0);const S=this._bufferService.rows-1;A=Math.max(Math.min(A,S),0),H=Math.max(Math.min(H,S),0),U=Math.min(U,this._bufferService.cols);const w=this._bufferService.buffer,E=w.ybase+w.y,L=Math.min(w.x,U-1),P=this._optionsService.rawOptions.cursorBlink,W=this._optionsService.rawOptions.cursorStyle,$=this._optionsService.rawOptions.cursorInactiveStyle;for(let j=A;j<=H;++j){const x=j+w.ydisp,C=this._rowElements[j],I=w.lines.get(x);if(!C||!I)break;C.replaceChildren(...this._rowFactory.createRow(I,x,x===E,W,$,L,P,this.dimensions.css.cell.width,this._widthCache,F?j===A?b:0:-1,F?(j===H?B:U)-1:-1))}}};r.DomRenderer=D=c([f(7,a.IInstantiationService),f(8,i.ICharSizeService),f(9,a.IOptionsService),f(10,a.IBufferService),f(11,i.ICoreBrowserService),f(12,i.IThemeService)],D)},3787:function(R,r,o){var c=this&&this.__decorate||function(u,g,h,m){var y,k=arguments.length,D=k<3?g:m===null?m=Object.getOwnPropertyDescriptor(g,h):m;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")D=Reflect.decorate(u,g,h,m);else for(var b=u.length-1;b>=0;b--)(y=u[b])&&(D=(k<3?y(D):k>3?y(g,h,D):y(g,h))||D);return k>3&&D&&Object.defineProperty(g,h,D),D},f=this&&this.__param||function(u,g){return function(h,m){g(h,m,u)}};Object.defineProperty(r,"__esModule",{value:!0}),r.DomRendererRowFactory=void 0;const n=o(2223),d=o(643),v=o(511),p=o(2585),l=o(8055),i=o(4725),s=o(4269),e=o(6171),t=o(3734);let a=r.DomRendererRowFactory=class{constructor(u,g,h,m,y,k,D){this._document=u,this._characterJoinerService=g,this._optionsService=h,this._coreBrowserService=m,this._coreService=y,this._decorationService=k,this._themeService=D,this._workCell=new v.CellData,this._columnSelectMode=!1,this.defaultSpacing=0}handleSelectionChanged(u,g,h){this._selectionStart=u,this._selectionEnd=g,this._columnSelectMode=h}createRow(u,g,h,m,y,k,D,b,B,A,H){const U=[],F=this._characterJoinerService.getJoinedCharacters(g),S=this._themeService.colors;let w,E=u.getNoBgTrimmedLength();h&&E0&&K===F[0][0]){X=!0;const V=F.shift();z=new s.JoinedCellData(this._workCell,u.translateToString(!0,V[0],V[1]),V[1]-V[0]),Q=V[1]-1,G=z.getWidth()}const he=this._isCellInSelection(K,g),ge=h&&K===k,me=M&&K>=A&&K<=H;let Se=!1;this._decorationService.forEachDecorationAtCell(K,g,void 0,V=>{Se=!0});let fe=z.getChars()||d.WHITESPACE_CELL_CHAR;if(fe===" "&&(z.isUnderline()||z.isOverline())&&(fe="\xA0"),O=G*b-B.get(fe,z.isBold(),z.isItalic()),w){if(L&&(he&&I||!he&&!I&&z.bg===W)&&(he&&I&&S.selectionForeground||z.fg===$)&&z.extended.ext===j&&me===x&&O===C&&!ge&&!X&&!Se){z.isInvisible()?P+=d.WHITESPACE_CELL_CHAR:P+=fe,L++;continue}L&&(w.textContent=P),w=this._document.createElement("span"),L=0,P=""}else w=this._document.createElement("span");if(W=z.bg,$=z.fg,j=z.extended.ext,x=me,C=O,I=he,X&&k>=K&&k<=Q&&(k=K),!this._coreService.isCursorHidden&&ge&&this._coreService.isCursorInitialized){if(N.push("xterm-cursor"),this._coreBrowserService.isFocused)D&&N.push("xterm-cursor-blink"),N.push(m==="bar"?"xterm-cursor-bar":m==="underline"?"xterm-cursor-underline":"xterm-cursor-block");else if(y)switch(y){case"outline":N.push("xterm-cursor-outline");break;case"block":N.push("xterm-cursor-block");break;case"bar":N.push("xterm-cursor-bar");break;case"underline":N.push("xterm-cursor-underline")}}if(z.isBold()&&N.push("xterm-bold"),z.isItalic()&&N.push("xterm-italic"),z.isDim()&&N.push("xterm-dim"),P=z.isInvisible()?d.WHITESPACE_CELL_CHAR:z.getChars()||d.WHITESPACE_CELL_CHAR,z.isUnderline()&&(N.push(`xterm-underline-${z.extended.underlineStyle}`),P===" "&&(P="\xA0"),!z.isUnderlineColorDefault()))if(z.isUnderlineColorRGB())w.style.textDecorationColor=`rgb(${t.AttributeData.toColorRGB(z.getUnderlineColor()).join(",")})`;else{let V=z.getUnderlineColor();this._optionsService.rawOptions.drawBoldTextInBrightColors&&z.isBold()&&V<8&&(V+=8),w.style.textDecorationColor=S.ansi[V].css}z.isOverline()&&(N.push("xterm-overline"),P===" "&&(P="\xA0")),z.isStrikethrough()&&N.push("xterm-strikethrough"),me&&(w.style.textDecoration="underline");let ie=z.getFgColor(),le=z.getFgColorMode(),se=z.getBgColor(),ce=z.getBgColorMode();const Ce=!!z.isInverse();if(Ce){const V=ie;ie=se,se=V;const Ee=le;le=ce,ce=Ee}let ne,ve,oe,de=!1;switch(this._decorationService.forEachDecorationAtCell(K,g,void 0,V=>{V.options.layer!=="top"&&de||(V.backgroundColorRGB&&(ce=50331648,se=V.backgroundColorRGB.rgba>>8&16777215,ne=V.backgroundColorRGB),V.foregroundColorRGB&&(le=50331648,ie=V.foregroundColorRGB.rgba>>8&16777215,ve=V.foregroundColorRGB),de=V.options.layer==="top")}),!de&&he&&(ne=this._coreBrowserService.isFocused?S.selectionBackgroundOpaque:S.selectionInactiveBackgroundOpaque,se=ne.rgba>>8&16777215,ce=50331648,de=!0,S.selectionForeground&&(le=50331648,ie=S.selectionForeground.rgba>>8&16777215,ve=S.selectionForeground)),de&&N.push("xterm-decoration-top"),ce){case 16777216:case 33554432:oe=S.ansi[se],N.push(`xterm-bg-${se}`);break;case 50331648:oe=l.channels.toColor(se>>16,se>>8&255,255&se),this._addStyle(w,`background-color:#${_((se>>>0).toString(16),"0",6)}`);break;default:Ce?(oe=S.foreground,N.push(`xterm-bg-${n.INVERTED_DEFAULT_COLOR}`)):oe=S.background}switch(ne||z.isDim()&&(ne=l.color.multiplyOpacity(oe,.5)),le){case 16777216:case 33554432:z.isBold()&&ie<8&&this._optionsService.rawOptions.drawBoldTextInBrightColors&&(ie+=8),this._applyMinimumContrast(w,oe,S.ansi[ie],z,ne,void 0)||N.push(`xterm-fg-${ie}`);break;case 50331648:const V=l.channels.toColor(ie>>16&255,ie>>8&255,255&ie);this._applyMinimumContrast(w,oe,V,z,ne,ve)||this._addStyle(w,`color:#${_(ie.toString(16),"0",6)}`);break;default:this._applyMinimumContrast(w,oe,S.foreground,z,ne,ve)||Ce&&N.push(`xterm-fg-${n.INVERTED_DEFAULT_COLOR}`)}N.length&&(w.className=N.join(" "),N.length=0),ge||X||Se?w.textContent=P:L++,O!==this.defaultSpacing&&(w.style.letterSpacing=`${O}px`),U.push(w),K=Q}return w&&L&&(w.textContent=P),U}_applyMinimumContrast(u,g,h,m,y,k){if(this._optionsService.rawOptions.minimumContrastRatio===1||(0,e.treatGlyphAsBackgroundColor)(m.getCode()))return!1;const D=this._getContrastCache(m);let b;if(y||k||(b=D.getColor(g.rgba,h.rgba)),b===void 0){const B=this._optionsService.rawOptions.minimumContrastRatio/(m.isDim()?2:1);b=l.color.ensureContrastRatio(y||g,k||h,B),D.setColor((y||g).rgba,(k||h).rgba,b??null)}return!!b&&(this._addStyle(u,`color:${b.css}`),!0)}_getContrastCache(u){return u.isDim()?this._themeService.colors.halfContrastCache:this._themeService.colors.contrastCache}_addStyle(u,g){u.setAttribute("style",`${u.getAttribute("style")||""}${g};`)}_isCellInSelection(u,g){const h=this._selectionStart,m=this._selectionEnd;return!(!h||!m)&&(this._columnSelectMode?h[0]<=m[0]?u>=h[0]&&g>=h[1]&&u=h[1]&&u>=m[0]&&g<=m[1]:g>h[1]&&g=h[0]&&u=h[0])}};function _(u,g,h){for(;u.length{Object.defineProperty(r,"__esModule",{value:!0}),r.WidthCache=void 0,r.WidthCache=class{constructor(o,c){this._flat=new Float32Array(256),this._font="",this._fontSize=0,this._weight="normal",this._weightBold="bold",this._measureElements=[],this._container=o.createElement("div"),this._container.classList.add("xterm-width-cache-measure-container"),this._container.setAttribute("aria-hidden","true"),this._container.style.whiteSpace="pre",this._container.style.fontKerning="none";const f=o.createElement("span");f.classList.add("xterm-char-measure-element");const n=o.createElement("span");n.classList.add("xterm-char-measure-element"),n.style.fontWeight="bold";const d=o.createElement("span");d.classList.add("xterm-char-measure-element"),d.style.fontStyle="italic";const v=o.createElement("span");v.classList.add("xterm-char-measure-element"),v.style.fontWeight="bold",v.style.fontStyle="italic",this._measureElements=[f,n,d,v],this._container.appendChild(f),this._container.appendChild(n),this._container.appendChild(d),this._container.appendChild(v),c.appendChild(this._container),this.clear()}dispose(){this._container.remove(),this._measureElements.length=0,this._holey=void 0}clear(){this._flat.fill(-9999),this._holey=new Map}setFont(o,c,f,n){o===this._font&&c===this._fontSize&&f===this._weight&&n===this._weightBold||(this._font=o,this._fontSize=c,this._weight=f,this._weightBold=n,this._container.style.fontFamily=this._font,this._container.style.fontSize=`${this._fontSize}px`,this._measureElements[0].style.fontWeight=`${f}`,this._measureElements[1].style.fontWeight=`${n}`,this._measureElements[2].style.fontWeight=`${f}`,this._measureElements[3].style.fontWeight=`${n}`,this.clear())}get(o,c,f){let n=0;if(!c&&!f&&o.length===1&&(n=o.charCodeAt(0))<256){if(this._flat[n]!==-9999)return this._flat[n];const p=this._measure(o,0);return p>0&&(this._flat[n]=p),p}let d=o;c&&(d+="B"),f&&(d+="I");let v=this._holey.get(d);if(v===void 0){let p=0;c&&(p|=1),f&&(p|=2),v=this._measure(o,p),v>0&&this._holey.set(d,v)}return v}_measure(o,c){const f=this._measureElements[c];return f.textContent=o.repeat(32),f.offsetWidth/32}}},2223:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.TEXT_BASELINE=r.DIM_OPACITY=r.INVERTED_DEFAULT_COLOR=void 0;const c=o(6114);r.INVERTED_DEFAULT_COLOR=257,r.DIM_OPACITY=.5,r.TEXT_BASELINE=c.isFirefox||c.isLegacyEdge?"bottom":"ideographic"},6171:(R,r)=>{function o(f){return 57508<=f&&f<=57558}function c(f){return f>=128512&&f<=128591||f>=127744&&f<=128511||f>=128640&&f<=128767||f>=9728&&f<=9983||f>=9984&&f<=10175||f>=65024&&f<=65039||f>=129280&&f<=129535||f>=127462&&f<=127487}Object.defineProperty(r,"__esModule",{value:!0}),r.computeNextVariantOffset=r.createRenderDimensions=r.treatGlyphAsBackgroundColor=r.allowRescaling=r.isEmoji=r.isRestrictedPowerlineGlyph=r.isPowerlineGlyph=r.throwIfFalsy=void 0,r.throwIfFalsy=function(f){if(!f)throw new Error("value must not be falsy");return f},r.isPowerlineGlyph=o,r.isRestrictedPowerlineGlyph=function(f){return 57520<=f&&f<=57527},r.isEmoji=c,r.allowRescaling=function(f,n,d,v){return n===1&&d>Math.ceil(1.5*v)&&f!==void 0&&f>255&&!c(f)&&!o(f)&&!function(p){return 57344<=p&&p<=63743}(f)},r.treatGlyphAsBackgroundColor=function(f){return o(f)||function(n){return 9472<=n&&n<=9631}(f)},r.createRenderDimensions=function(){return{css:{canvas:{width:0,height:0},cell:{width:0,height:0}},device:{canvas:{width:0,height:0},cell:{width:0,height:0},char:{width:0,height:0,left:0,top:0}}}},r.computeNextVariantOffset=function(f,n,d=0){return(f-(2*Math.round(n)-d))%(2*Math.round(n))}},6052:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.createSelectionRenderModel=void 0;class o{constructor(){this.clear()}clear(){this.hasSelection=!1,this.columnSelectMode=!1,this.viewportStartRow=0,this.viewportEndRow=0,this.viewportCappedStartRow=0,this.viewportCappedEndRow=0,this.startCol=0,this.endCol=0,this.selectionStart=void 0,this.selectionEnd=void 0}update(f,n,d,v=!1){if(this.selectionStart=n,this.selectionEnd=d,!n||!d||n[0]===d[0]&&n[1]===d[1])return void this.clear();const p=f.buffers.active.ydisp,l=n[1]-p,i=d[1]-p,s=Math.max(l,0),e=Math.min(i,f.rows-1);s>=f.rows||e<0?this.clear():(this.hasSelection=!0,this.columnSelectMode=v,this.viewportStartRow=l,this.viewportEndRow=i,this.viewportCappedStartRow=s,this.viewportCappedEndRow=e,this.startCol=n[0],this.endCol=d[0])}isCellSelected(f,n,d){return!!this.hasSelection&&(d-=f.buffer.active.viewportY,this.columnSelectMode?this.startCol<=this.endCol?n>=this.startCol&&d>=this.viewportCappedStartRow&&n=this.viewportCappedStartRow&&n>=this.endCol&&d<=this.viewportCappedEndRow:d>this.viewportStartRow&&d=this.startCol&&n=this.startCol)}}r.createSelectionRenderModel=function(){return new o}},456:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.SelectionModel=void 0,r.SelectionModel=class{constructor(o){this._bufferService=o,this.isSelectAllActive=!1,this.selectionStartLength=0}clearSelection(){this.selectionStart=void 0,this.selectionEnd=void 0,this.isSelectAllActive=!1,this.selectionStartLength=0}get finalSelectionStart(){return this.isSelectAllActive?[0,0]:this.selectionEnd&&this.selectionStart&&this.areSelectionValuesReversed()?this.selectionEnd:this.selectionStart}get finalSelectionEnd(){if(this.isSelectAllActive)return[this._bufferService.cols,this._bufferService.buffer.ybase+this._bufferService.rows-1];if(this.selectionStart){if(!this.selectionEnd||this.areSelectionValuesReversed()){const o=this.selectionStart[0]+this.selectionStartLength;return o>this._bufferService.cols?o%this._bufferService.cols==0?[this._bufferService.cols,this.selectionStart[1]+Math.floor(o/this._bufferService.cols)-1]:[o%this._bufferService.cols,this.selectionStart[1]+Math.floor(o/this._bufferService.cols)]:[o,this.selectionStart[1]]}if(this.selectionStartLength&&this.selectionEnd[1]===this.selectionStart[1]){const o=this.selectionStart[0]+this.selectionStartLength;return o>this._bufferService.cols?[o%this._bufferService.cols,this.selectionStart[1]+Math.floor(o/this._bufferService.cols)]:[Math.max(o,this.selectionEnd[0]),this.selectionEnd[1]]}return this.selectionEnd}}areSelectionValuesReversed(){const o=this.selectionStart,c=this.selectionEnd;return!(!o||!c)&&(o[1]>c[1]||o[1]===c[1]&&o[0]>c[0])}handleTrim(o){return this.selectionStart&&(this.selectionStart[1]-=o),this.selectionEnd&&(this.selectionEnd[1]-=o),this.selectionEnd&&this.selectionEnd[1]<0?(this.clearSelection(),!0):(this.selectionStart&&this.selectionStart[1]<0&&(this.selectionStart[1]=0),!1)}}},428:function(R,r,o){var c=this&&this.__decorate||function(e,t,a,_){var u,g=arguments.length,h=g<3?t:_===null?_=Object.getOwnPropertyDescriptor(t,a):_;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")h=Reflect.decorate(e,t,a,_);else for(var m=e.length-1;m>=0;m--)(u=e[m])&&(h=(g<3?u(h):g>3?u(t,a,h):u(t,a))||h);return g>3&&h&&Object.defineProperty(t,a,h),h},f=this&&this.__param||function(e,t){return function(a,_){t(a,_,e)}};Object.defineProperty(r,"__esModule",{value:!0}),r.CharSizeService=void 0;const n=o(2585),d=o(8460),v=o(844);let p=r.CharSizeService=class extends v.Disposable{get hasValidSize(){return this.width>0&&this.height>0}constructor(e,t,a){super(),this._optionsService=a,this.width=0,this.height=0,this._onCharSizeChange=this.register(new d.EventEmitter),this.onCharSizeChange=this._onCharSizeChange.event;try{this._measureStrategy=this.register(new s(this._optionsService))}catch{this._measureStrategy=this.register(new i(e,t,this._optionsService))}this.register(this._optionsService.onMultipleOptionChange(["fontFamily","fontSize"],()=>this.measure()))}measure(){const e=this._measureStrategy.measure();e.width===this.width&&e.height===this.height||(this.width=e.width,this.height=e.height,this._onCharSizeChange.fire())}};r.CharSizeService=p=c([f(2,n.IOptionsService)],p);class l extends v.Disposable{constructor(){super(...arguments),this._result={width:0,height:0}}_validateAndSet(t,a){t!==void 0&&t>0&&a!==void 0&&a>0&&(this._result.width=t,this._result.height=a)}}class i extends l{constructor(t,a,_){super(),this._document=t,this._parentElement=a,this._optionsService=_,this._measureElement=this._document.createElement("span"),this._measureElement.classList.add("xterm-char-measure-element"),this._measureElement.textContent="W".repeat(32),this._measureElement.setAttribute("aria-hidden","true"),this._measureElement.style.whiteSpace="pre",this._measureElement.style.fontKerning="none",this._parentElement.appendChild(this._measureElement)}measure(){return this._measureElement.style.fontFamily=this._optionsService.rawOptions.fontFamily,this._measureElement.style.fontSize=`${this._optionsService.rawOptions.fontSize}px`,this._validateAndSet(Number(this._measureElement.offsetWidth)/32,Number(this._measureElement.offsetHeight)),this._result}}class s extends l{constructor(t){super(),this._optionsService=t,this._canvas=new OffscreenCanvas(100,100),this._ctx=this._canvas.getContext("2d");const a=this._ctx.measureText("W");if(!("width"in a&&"fontBoundingBoxAscent"in a&&"fontBoundingBoxDescent"in a))throw new Error("Required font metrics not supported")}measure(){this._ctx.font=`${this._optionsService.rawOptions.fontSize}px ${this._optionsService.rawOptions.fontFamily}`;const t=this._ctx.measureText("W");return this._validateAndSet(t.width,t.fontBoundingBoxAscent+t.fontBoundingBoxDescent),this._result}}},4269:function(R,r,o){var c=this&&this.__decorate||function(s,e,t,a){var _,u=arguments.length,g=u<3?e:a===null?a=Object.getOwnPropertyDescriptor(e,t):a;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")g=Reflect.decorate(s,e,t,a);else for(var h=s.length-1;h>=0;h--)(_=s[h])&&(g=(u<3?_(g):u>3?_(e,t,g):_(e,t))||g);return u>3&&g&&Object.defineProperty(e,t,g),g},f=this&&this.__param||function(s,e){return function(t,a){e(t,a,s)}};Object.defineProperty(r,"__esModule",{value:!0}),r.CharacterJoinerService=r.JoinedCellData=void 0;const n=o(3734),d=o(643),v=o(511),p=o(2585);class l extends n.AttributeData{constructor(e,t,a){super(),this.content=0,this.combinedData="",this.fg=e.fg,this.bg=e.bg,this.combinedData=t,this._width=a}isCombined(){return 2097152}getWidth(){return this._width}getChars(){return this.combinedData}getCode(){return 2097151}setFromCharData(e){throw new Error("not implemented")}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}}r.JoinedCellData=l;let i=r.CharacterJoinerService=class we{constructor(e){this._bufferService=e,this._characterJoiners=[],this._nextCharacterJoinerId=0,this._workCell=new v.CellData}register(e){const t={id:this._nextCharacterJoinerId++,handler:e};return this._characterJoiners.push(t),t.id}deregister(e){for(let t=0;t1){const D=this._getJoinedRanges(_,h,g,t,u);for(let b=0;b1){const k=this._getJoinedRanges(_,h,g,t,u);for(let D=0;D{Object.defineProperty(r,"__esModule",{value:!0}),r.CoreBrowserService=void 0;const c=o(844),f=o(8460),n=o(3656);class d extends c.Disposable{constructor(l,i,s){super(),this._textarea=l,this._window=i,this.mainDocument=s,this._isFocused=!1,this._cachedIsFocused=void 0,this._screenDprMonitor=new v(this._window),this._onDprChange=this.register(new f.EventEmitter),this.onDprChange=this._onDprChange.event,this._onWindowChange=this.register(new f.EventEmitter),this.onWindowChange=this._onWindowChange.event,this.register(this.onWindowChange(e=>this._screenDprMonitor.setWindow(e))),this.register((0,f.forwardEvent)(this._screenDprMonitor.onDprChange,this._onDprChange)),this._textarea.addEventListener("focus",()=>this._isFocused=!0),this._textarea.addEventListener("blur",()=>this._isFocused=!1)}get window(){return this._window}set window(l){this._window!==l&&(this._window=l,this._onWindowChange.fire(this._window))}get dpr(){return this.window.devicePixelRatio}get isFocused(){return this._cachedIsFocused===void 0&&(this._cachedIsFocused=this._isFocused&&this._textarea.ownerDocument.hasFocus(),queueMicrotask(()=>this._cachedIsFocused=void 0)),this._cachedIsFocused}}r.CoreBrowserService=d;class v extends c.Disposable{constructor(l){super(),this._parentWindow=l,this._windowResizeListener=this.register(new c.MutableDisposable),this._onDprChange=this.register(new f.EventEmitter),this.onDprChange=this._onDprChange.event,this._outerListener=()=>this._setDprAndFireIfDiffers(),this._currentDevicePixelRatio=this._parentWindow.devicePixelRatio,this._updateDpr(),this._setWindowResizeListener(),this.register((0,c.toDisposable)(()=>this.clearListener()))}setWindow(l){this._parentWindow=l,this._setWindowResizeListener(),this._setDprAndFireIfDiffers()}_setWindowResizeListener(){this._windowResizeListener.value=(0,n.addDisposableDomListener)(this._parentWindow,"resize",()=>this._setDprAndFireIfDiffers())}_setDprAndFireIfDiffers(){this._parentWindow.devicePixelRatio!==this._currentDevicePixelRatio&&this._onDprChange.fire(this._parentWindow.devicePixelRatio),this._updateDpr()}_updateDpr(){var l;this._outerListener&&((l=this._resolutionMediaMatchList)==null||l.removeListener(this._outerListener),this._currentDevicePixelRatio=this._parentWindow.devicePixelRatio,this._resolutionMediaMatchList=this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`),this._resolutionMediaMatchList.addListener(this._outerListener))}clearListener(){this._resolutionMediaMatchList&&this._outerListener&&(this._resolutionMediaMatchList.removeListener(this._outerListener),this._resolutionMediaMatchList=void 0,this._outerListener=void 0)}}},779:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.LinkProviderService=void 0;const c=o(844);class f extends c.Disposable{constructor(){super(),this.linkProviders=[],this.register((0,c.toDisposable)(()=>this.linkProviders.length=0))}registerLinkProvider(d){return this.linkProviders.push(d),{dispose:()=>{const v=this.linkProviders.indexOf(d);v!==-1&&this.linkProviders.splice(v,1)}}}}r.LinkProviderService=f},8934:function(R,r,o){var c=this&&this.__decorate||function(p,l,i,s){var e,t=arguments.length,a=t<3?l:s===null?s=Object.getOwnPropertyDescriptor(l,i):s;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")a=Reflect.decorate(p,l,i,s);else for(var _=p.length-1;_>=0;_--)(e=p[_])&&(a=(t<3?e(a):t>3?e(l,i,a):e(l,i))||a);return t>3&&a&&Object.defineProperty(l,i,a),a},f=this&&this.__param||function(p,l){return function(i,s){l(i,s,p)}};Object.defineProperty(r,"__esModule",{value:!0}),r.MouseService=void 0;const n=o(4725),d=o(9806);let v=r.MouseService=class{constructor(p,l){this._renderService=p,this._charSizeService=l}getCoords(p,l,i,s,e){return(0,d.getCoords)(window,p,l,i,s,this._charSizeService.hasValidSize,this._renderService.dimensions.css.cell.width,this._renderService.dimensions.css.cell.height,e)}getMouseReportCoords(p,l){const i=(0,d.getCoordsRelativeToElement)(window,p,l);if(this._charSizeService.hasValidSize)return i[0]=Math.min(Math.max(i[0],0),this._renderService.dimensions.css.canvas.width-1),i[1]=Math.min(Math.max(i[1],0),this._renderService.dimensions.css.canvas.height-1),{col:Math.floor(i[0]/this._renderService.dimensions.css.cell.width),row:Math.floor(i[1]/this._renderService.dimensions.css.cell.height),x:Math.floor(i[0]),y:Math.floor(i[1])}}};r.MouseService=v=c([f(0,n.IRenderService),f(1,n.ICharSizeService)],v)},3230:function(R,r,o){var c=this&&this.__decorate||function(e,t,a,_){var u,g=arguments.length,h=g<3?t:_===null?_=Object.getOwnPropertyDescriptor(t,a):_;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")h=Reflect.decorate(e,t,a,_);else for(var m=e.length-1;m>=0;m--)(u=e[m])&&(h=(g<3?u(h):g>3?u(t,a,h):u(t,a))||h);return g>3&&h&&Object.defineProperty(t,a,h),h},f=this&&this.__param||function(e,t){return function(a,_){t(a,_,e)}};Object.defineProperty(r,"__esModule",{value:!0}),r.RenderService=void 0;const n=o(6193),d=o(4725),v=o(8460),p=o(844),l=o(7226),i=o(2585);let s=r.RenderService=class extends p.Disposable{get dimensions(){return this._renderer.value.dimensions}constructor(e,t,a,_,u,g,h,m){super(),this._rowCount=e,this._charSizeService=_,this._renderer=this.register(new p.MutableDisposable),this._pausedResizeTask=new l.DebouncedIdleTask,this._observerDisposable=this.register(new p.MutableDisposable),this._isPaused=!1,this._needsFullRefresh=!1,this._isNextRenderRedrawOnly=!0,this._needsSelectionRefresh=!1,this._canvasWidth=0,this._canvasHeight=0,this._selectionState={start:void 0,end:void 0,columnSelectMode:!1},this._onDimensionsChange=this.register(new v.EventEmitter),this.onDimensionsChange=this._onDimensionsChange.event,this._onRenderedViewportChange=this.register(new v.EventEmitter),this.onRenderedViewportChange=this._onRenderedViewportChange.event,this._onRender=this.register(new v.EventEmitter),this.onRender=this._onRender.event,this._onRefreshRequest=this.register(new v.EventEmitter),this.onRefreshRequest=this._onRefreshRequest.event,this._renderDebouncer=new n.RenderDebouncer((y,k)=>this._renderRows(y,k),h),this.register(this._renderDebouncer),this.register(h.onDprChange(()=>this.handleDevicePixelRatioChange())),this.register(g.onResize(()=>this._fullRefresh())),this.register(g.buffers.onBufferActivate(()=>{var y;return(y=this._renderer.value)==null?void 0:y.clear()})),this.register(a.onOptionChange(()=>this._handleOptionsChanged())),this.register(this._charSizeService.onCharSizeChange(()=>this.handleCharSizeChanged())),this.register(u.onDecorationRegistered(()=>this._fullRefresh())),this.register(u.onDecorationRemoved(()=>this._fullRefresh())),this.register(a.onMultipleOptionChange(["customGlyphs","drawBoldTextInBrightColors","letterSpacing","lineHeight","fontFamily","fontSize","fontWeight","fontWeightBold","minimumContrastRatio","rescaleOverlappingGlyphs"],()=>{this.clear(),this.handleResize(g.cols,g.rows),this._fullRefresh()})),this.register(a.onMultipleOptionChange(["cursorBlink","cursorStyle"],()=>this.refreshRows(g.buffer.y,g.buffer.y,!0))),this.register(m.onChangeColors(()=>this._fullRefresh())),this._registerIntersectionObserver(h.window,t),this.register(h.onWindowChange(y=>this._registerIntersectionObserver(y,t)))}_registerIntersectionObserver(e,t){if("IntersectionObserver"in e){const a=new e.IntersectionObserver(_=>this._handleIntersectionChange(_[_.length-1]),{threshold:0});a.observe(t),this._observerDisposable.value=(0,p.toDisposable)(()=>a.disconnect())}}_handleIntersectionChange(e){this._isPaused=e.isIntersecting===void 0?e.intersectionRatio===0:!e.isIntersecting,this._isPaused||this._charSizeService.hasValidSize||this._charSizeService.measure(),!this._isPaused&&this._needsFullRefresh&&(this._pausedResizeTask.flush(),this.refreshRows(0,this._rowCount-1),this._needsFullRefresh=!1)}refreshRows(e,t,a=!1){this._isPaused?this._needsFullRefresh=!0:(a||(this._isNextRenderRedrawOnly=!1),this._renderDebouncer.refresh(e,t,this._rowCount))}_renderRows(e,t){this._renderer.value&&(e=Math.min(e,this._rowCount-1),t=Math.min(t,this._rowCount-1),this._renderer.value.renderRows(e,t),this._needsSelectionRefresh&&(this._renderer.value.handleSelectionChanged(this._selectionState.start,this._selectionState.end,this._selectionState.columnSelectMode),this._needsSelectionRefresh=!1),this._isNextRenderRedrawOnly||this._onRenderedViewportChange.fire({start:e,end:t}),this._onRender.fire({start:e,end:t}),this._isNextRenderRedrawOnly=!0)}resize(e,t){this._rowCount=t,this._fireOnCanvasResize()}_handleOptionsChanged(){this._renderer.value&&(this.refreshRows(0,this._rowCount-1),this._fireOnCanvasResize())}_fireOnCanvasResize(){this._renderer.value&&(this._renderer.value.dimensions.css.canvas.width===this._canvasWidth&&this._renderer.value.dimensions.css.canvas.height===this._canvasHeight||this._onDimensionsChange.fire(this._renderer.value.dimensions))}hasRenderer(){return!!this._renderer.value}setRenderer(e){this._renderer.value=e,this._renderer.value&&(this._renderer.value.onRequestRedraw(t=>this.refreshRows(t.start,t.end,!0)),this._needsSelectionRefresh=!0,this._fullRefresh())}addRefreshCallback(e){return this._renderDebouncer.addRefreshCallback(e)}_fullRefresh(){this._isPaused?this._needsFullRefresh=!0:this.refreshRows(0,this._rowCount-1)}clearTextureAtlas(){var e,t;this._renderer.value&&((t=(e=this._renderer.value).clearTextureAtlas)==null||t.call(e),this._fullRefresh())}handleDevicePixelRatioChange(){this._charSizeService.measure(),this._renderer.value&&(this._renderer.value.handleDevicePixelRatioChange(),this.refreshRows(0,this._rowCount-1))}handleResize(e,t){this._renderer.value&&(this._isPaused?this._pausedResizeTask.set(()=>{var a;return(a=this._renderer.value)==null?void 0:a.handleResize(e,t)}):this._renderer.value.handleResize(e,t),this._fullRefresh())}handleCharSizeChanged(){var e;(e=this._renderer.value)==null||e.handleCharSizeChanged()}handleBlur(){var e;(e=this._renderer.value)==null||e.handleBlur()}handleFocus(){var e;(e=this._renderer.value)==null||e.handleFocus()}handleSelectionChanged(e,t,a){var _;this._selectionState.start=e,this._selectionState.end=t,this._selectionState.columnSelectMode=a,(_=this._renderer.value)==null||_.handleSelectionChanged(e,t,a)}handleCursorMove(){var e;(e=this._renderer.value)==null||e.handleCursorMove()}clear(){var e;(e=this._renderer.value)==null||e.clear()}};r.RenderService=s=c([f(2,i.IOptionsService),f(3,d.ICharSizeService),f(4,i.IDecorationService),f(5,i.IBufferService),f(6,d.ICoreBrowserService),f(7,d.IThemeService)],s)},9312:function(R,r,o){var c=this&&this.__decorate||function(h,m,y,k){var D,b=arguments.length,B=b<3?m:k===null?k=Object.getOwnPropertyDescriptor(m,y):k;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")B=Reflect.decorate(h,m,y,k);else for(var A=h.length-1;A>=0;A--)(D=h[A])&&(B=(b<3?D(B):b>3?D(m,y,B):D(m,y))||B);return b>3&&B&&Object.defineProperty(m,y,B),B},f=this&&this.__param||function(h,m){return function(y,k){m(y,k,h)}};Object.defineProperty(r,"__esModule",{value:!0}),r.SelectionService=void 0;const n=o(9806),d=o(9504),v=o(456),p=o(4725),l=o(8460),i=o(844),s=o(6114),e=o(4841),t=o(511),a=o(2585),_="\xA0",u=new RegExp(_,"g");let g=r.SelectionService=class extends i.Disposable{constructor(h,m,y,k,D,b,B,A,H){super(),this._element=h,this._screenElement=m,this._linkifier=y,this._bufferService=k,this._coreService=D,this._mouseService=b,this._optionsService=B,this._renderService=A,this._coreBrowserService=H,this._dragScrollAmount=0,this._enabled=!0,this._workCell=new t.CellData,this._mouseDownTimeStamp=0,this._oldHasSelection=!1,this._oldSelectionStart=void 0,this._oldSelectionEnd=void 0,this._onLinuxMouseSelection=this.register(new l.EventEmitter),this.onLinuxMouseSelection=this._onLinuxMouseSelection.event,this._onRedrawRequest=this.register(new l.EventEmitter),this.onRequestRedraw=this._onRedrawRequest.event,this._onSelectionChange=this.register(new l.EventEmitter),this.onSelectionChange=this._onSelectionChange.event,this._onRequestScrollLines=this.register(new l.EventEmitter),this.onRequestScrollLines=this._onRequestScrollLines.event,this._mouseMoveListener=U=>this._handleMouseMove(U),this._mouseUpListener=U=>this._handleMouseUp(U),this._coreService.onUserInput(()=>{this.hasSelection&&this.clearSelection()}),this._trimListener=this._bufferService.buffer.lines.onTrim(U=>this._handleTrim(U)),this.register(this._bufferService.buffers.onBufferActivate(U=>this._handleBufferActivate(U))),this.enable(),this._model=new v.SelectionModel(this._bufferService),this._activeSelectionMode=0,this.register((0,i.toDisposable)(()=>{this._removeMouseDownListeners()}))}reset(){this.clearSelection()}disable(){this.clearSelection(),this._enabled=!1}enable(){this._enabled=!0}get selectionStart(){return this._model.finalSelectionStart}get selectionEnd(){return this._model.finalSelectionEnd}get hasSelection(){const h=this._model.finalSelectionStart,m=this._model.finalSelectionEnd;return!(!h||!m||h[0]===m[0]&&h[1]===m[1])}get selectionText(){const h=this._model.finalSelectionStart,m=this._model.finalSelectionEnd;if(!h||!m)return"";const y=this._bufferService.buffer,k=[];if(this._activeSelectionMode===3){if(h[0]===m[0])return"";const D=h[0]D.replace(u," ")).join(s.isWindows?`\r -`:` -`)}clearSelection(){this._model.clearSelection(),this._removeMouseDownListeners(),this.refresh(),this._onSelectionChange.fire()}refresh(h){this._refreshAnimationFrame||(this._refreshAnimationFrame=this._coreBrowserService.window.requestAnimationFrame(()=>this._refresh())),s.isLinux&&h&&this.selectionText.length&&this._onLinuxMouseSelection.fire(this.selectionText)}_refresh(){this._refreshAnimationFrame=void 0,this._onRedrawRequest.fire({start:this._model.finalSelectionStart,end:this._model.finalSelectionEnd,columnSelectMode:this._activeSelectionMode===3})}_isClickInSelection(h){const m=this._getMouseBufferCoords(h),y=this._model.finalSelectionStart,k=this._model.finalSelectionEnd;return!!(y&&k&&m)&&this._areCoordsInSelection(m,y,k)}isCellInSelection(h,m){const y=this._model.finalSelectionStart,k=this._model.finalSelectionEnd;return!(!y||!k)&&this._areCoordsInSelection([h,m],y,k)}_areCoordsInSelection(h,m,y){return h[1]>m[1]&&h[1]=m[0]&&h[0]=m[0]}_selectWordAtCursor(h,m){var y,k;const D=(k=(y=this._linkifier.currentLink)==null?void 0:y.link)==null?void 0:k.range;if(D)return this._model.selectionStart=[D.start.x-1,D.start.y-1],this._model.selectionStartLength=(0,e.getRangeLength)(D,this._bufferService.cols),this._model.selectionEnd=void 0,!0;const b=this._getMouseBufferCoords(h);return!!b&&(this._selectWordAt(b,m),this._model.selectionEnd=void 0,!0)}selectAll(){this._model.isSelectAllActive=!0,this.refresh(),this._onSelectionChange.fire()}selectLines(h,m){this._model.clearSelection(),h=Math.max(h,0),m=Math.min(m,this._bufferService.buffer.lines.length-1),this._model.selectionStart=[0,h],this._model.selectionEnd=[this._bufferService.cols,m],this.refresh(),this._onSelectionChange.fire()}_handleTrim(h){this._model.handleTrim(h)&&this.refresh()}_getMouseBufferCoords(h){const m=this._mouseService.getCoords(h,this._screenElement,this._bufferService.cols,this._bufferService.rows,!0);if(m)return m[0]--,m[1]--,m[1]+=this._bufferService.buffer.ydisp,m}_getMouseEventScrollAmount(h){let m=(0,n.getCoordsRelativeToElement)(this._coreBrowserService.window,h,this._screenElement)[1];const y=this._renderService.dimensions.css.canvas.height;return m>=0&&m<=y?0:(m>y&&(m-=y),m=Math.min(Math.max(m,-50),50),m/=50,m/Math.abs(m)+Math.round(14*m))}shouldForceSelection(h){return s.isMac?h.altKey&&this._optionsService.rawOptions.macOptionClickForcesSelection:h.shiftKey}handleMouseDown(h){if(this._mouseDownTimeStamp=h.timeStamp,(h.button!==2||!this.hasSelection)&&h.button===0){if(!this._enabled){if(!this.shouldForceSelection(h))return;h.stopPropagation()}h.preventDefault(),this._dragScrollAmount=0,this._enabled&&h.shiftKey?this._handleIncrementalClick(h):h.detail===1?this._handleSingleClick(h):h.detail===2?this._handleDoubleClick(h):h.detail===3&&this._handleTripleClick(h),this._addMouseDownListeners(),this.refresh(!0)}}_addMouseDownListeners(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.addEventListener("mousemove",this._mouseMoveListener),this._screenElement.ownerDocument.addEventListener("mouseup",this._mouseUpListener)),this._dragScrollIntervalTimer=this._coreBrowserService.window.setInterval(()=>this._dragScroll(),50)}_removeMouseDownListeners(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.removeEventListener("mousemove",this._mouseMoveListener),this._screenElement.ownerDocument.removeEventListener("mouseup",this._mouseUpListener)),this._coreBrowserService.window.clearInterval(this._dragScrollIntervalTimer),this._dragScrollIntervalTimer=void 0}_handleIncrementalClick(h){this._model.selectionStart&&(this._model.selectionEnd=this._getMouseBufferCoords(h))}_handleSingleClick(h){if(this._model.selectionStartLength=0,this._model.isSelectAllActive=!1,this._activeSelectionMode=this.shouldColumnSelect(h)?3:0,this._model.selectionStart=this._getMouseBufferCoords(h),!this._model.selectionStart)return;this._model.selectionEnd=void 0;const m=this._bufferService.buffer.lines.get(this._model.selectionStart[1]);m&&m.length!==this._model.selectionStart[0]&&m.hasWidth(this._model.selectionStart[0])===0&&this._model.selectionStart[0]++}_handleDoubleClick(h){this._selectWordAtCursor(h,!0)&&(this._activeSelectionMode=1)}_handleTripleClick(h){const m=this._getMouseBufferCoords(h);m&&(this._activeSelectionMode=2,this._selectLineAt(m[1]))}shouldColumnSelect(h){return h.altKey&&!(s.isMac&&this._optionsService.rawOptions.macOptionClickForcesSelection)}_handleMouseMove(h){if(h.stopImmediatePropagation(),!this._model.selectionStart)return;const m=this._model.selectionEnd?[this._model.selectionEnd[0],this._model.selectionEnd[1]]:null;if(this._model.selectionEnd=this._getMouseBufferCoords(h),!this._model.selectionEnd)return void this.refresh(!0);this._activeSelectionMode===2?this._model.selectionEnd[1]0?this._model.selectionEnd[0]=this._bufferService.cols:this._dragScrollAmount<0&&(this._model.selectionEnd[0]=0));const y=this._bufferService.buffer;if(this._model.selectionEnd[1]0?(this._activeSelectionMode!==3&&(this._model.selectionEnd[0]=this._bufferService.cols),this._model.selectionEnd[1]=Math.min(h.ydisp+this._bufferService.rows,h.lines.length-1)):(this._activeSelectionMode!==3&&(this._model.selectionEnd[0]=0),this._model.selectionEnd[1]=h.ydisp),this.refresh()}}_handleMouseUp(h){const m=h.timeStamp-this._mouseDownTimeStamp;if(this._removeMouseDownListeners(),this.selectionText.length<=1&&m<500&&h.altKey&&this._optionsService.rawOptions.altClickMovesCursor){if(this._bufferService.buffer.ybase===this._bufferService.buffer.ydisp){const y=this._mouseService.getCoords(h,this._element,this._bufferService.cols,this._bufferService.rows,!1);if(y&&y[0]!==void 0&&y[1]!==void 0){const k=(0,d.moveToCellSequence)(y[0]-1,y[1]-1,this._bufferService,this._coreService.decPrivateModes.applicationCursorKeys);this._coreService.triggerDataEvent(k,!0)}}}else this._fireEventIfSelectionChanged()}_fireEventIfSelectionChanged(){const h=this._model.finalSelectionStart,m=this._model.finalSelectionEnd,y=!(!h||!m||h[0]===m[0]&&h[1]===m[1]);y?h&&m&&(this._oldSelectionStart&&this._oldSelectionEnd&&h[0]===this._oldSelectionStart[0]&&h[1]===this._oldSelectionStart[1]&&m[0]===this._oldSelectionEnd[0]&&m[1]===this._oldSelectionEnd[1]||this._fireOnSelectionChange(h,m,y)):this._oldHasSelection&&this._fireOnSelectionChange(h,m,y)}_fireOnSelectionChange(h,m,y){this._oldSelectionStart=h,this._oldSelectionEnd=m,this._oldHasSelection=y,this._onSelectionChange.fire()}_handleBufferActivate(h){this.clearSelection(),this._trimListener.dispose(),this._trimListener=h.activeBuffer.lines.onTrim(m=>this._handleTrim(m))}_convertViewportColToCharacterIndex(h,m){let y=m;for(let k=0;m>=k;k++){const D=h.loadCell(k,this._workCell).getChars().length;this._workCell.getWidth()===0?y--:D>1&&m!==k&&(y+=D-1)}return y}setSelection(h,m,y){this._model.clearSelection(),this._removeMouseDownListeners(),this._model.selectionStart=[h,m],this._model.selectionStartLength=y,this.refresh(),this._fireEventIfSelectionChanged()}rightClickSelect(h){this._isClickInSelection(h)||(this._selectWordAtCursor(h,!1)&&this.refresh(!0),this._fireEventIfSelectionChanged())}_getWordAt(h,m,y=!0,k=!0){if(h[0]>=this._bufferService.cols)return;const D=this._bufferService.buffer,b=D.lines.get(h[1]);if(!b)return;const B=D.translateBufferLineToString(h[1],!1);let A=this._convertViewportColToCharacterIndex(b,h[0]),H=A;const U=h[0]-A;let F=0,S=0,w=0,E=0;if(B.charAt(A)===" "){for(;A>0&&B.charAt(A-1)===" ";)A--;for(;H1&&(E+=j-1,H+=j-1);W>0&&A>0&&!this._isCharWordSeparator(b.loadCell(W-1,this._workCell));){b.loadCell(W-1,this._workCell);const x=this._workCell.getChars().length;this._workCell.getWidth()===0?(F++,W--):x>1&&(w+=x-1,A-=x-1),A--,W--}for(;$1&&(E+=x-1,H+=x-1),H++,$++}}H++;let L=A+U-F+w,P=Math.min(this._bufferService.cols,H-A+F+S-w-E);if(m||B.slice(A,H).trim()!==""){if(y&&L===0&&b.getCodePoint(0)!==32){const W=D.lines.get(h[1]-1);if(W&&b.isWrapped&&W.getCodePoint(this._bufferService.cols-1)!==32){const $=this._getWordAt([this._bufferService.cols-1,h[1]-1],!1,!0,!1);if($){const j=this._bufferService.cols-$.start;L-=j,P+=j}}}if(k&&L+P===this._bufferService.cols&&b.getCodePoint(this._bufferService.cols-1)!==32){const W=D.lines.get(h[1]+1);if(W?.isWrapped&&W.getCodePoint(0)!==32){const $=this._getWordAt([0,h[1]+1],!1,!1,!0);$&&(P+=$.length)}}return{start:L,length:P}}}_selectWordAt(h,m){const y=this._getWordAt(h,m);if(y){for(;y.start<0;)y.start+=this._bufferService.cols,h[1]--;this._model.selectionStart=[y.start,h[1]],this._model.selectionStartLength=y.length}}_selectToWordAt(h){const m=this._getWordAt(h,!0);if(m){let y=h[1];for(;m.start<0;)m.start+=this._bufferService.cols,y--;if(!this._model.areSelectionValuesReversed())for(;m.start+m.length>this._bufferService.cols;)m.length-=this._bufferService.cols,y++;this._model.selectionEnd=[this._model.areSelectionValuesReversed()?m.start:m.start+m.length,y]}}_isCharWordSeparator(h){return h.getWidth()!==0&&this._optionsService.rawOptions.wordSeparator.indexOf(h.getChars())>=0}_selectLineAt(h){const m=this._bufferService.buffer.getWrappedRangeForLine(h),y={start:{x:0,y:m.first},end:{x:this._bufferService.cols-1,y:m.last}};this._model.selectionStart=[0,m.first],this._model.selectionEnd=void 0,this._model.selectionStartLength=(0,e.getRangeLength)(y,this._bufferService.cols)}};r.SelectionService=g=c([f(3,a.IBufferService),f(4,a.ICoreService),f(5,p.IMouseService),f(6,a.IOptionsService),f(7,p.IRenderService),f(8,p.ICoreBrowserService)],g)},4725:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.ILinkProviderService=r.IThemeService=r.ICharacterJoinerService=r.ISelectionService=r.IRenderService=r.IMouseService=r.ICoreBrowserService=r.ICharSizeService=void 0;const c=o(8343);r.ICharSizeService=(0,c.createDecorator)("CharSizeService"),r.ICoreBrowserService=(0,c.createDecorator)("CoreBrowserService"),r.IMouseService=(0,c.createDecorator)("MouseService"),r.IRenderService=(0,c.createDecorator)("RenderService"),r.ISelectionService=(0,c.createDecorator)("SelectionService"),r.ICharacterJoinerService=(0,c.createDecorator)("CharacterJoinerService"),r.IThemeService=(0,c.createDecorator)("ThemeService"),r.ILinkProviderService=(0,c.createDecorator)("LinkProviderService")},6731:function(R,r,o){var c=this&&this.__decorate||function(g,h,m,y){var k,D=arguments.length,b=D<3?h:y===null?y=Object.getOwnPropertyDescriptor(h,m):y;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")b=Reflect.decorate(g,h,m,y);else for(var B=g.length-1;B>=0;B--)(k=g[B])&&(b=(D<3?k(b):D>3?k(h,m,b):k(h,m))||b);return D>3&&b&&Object.defineProperty(h,m,b),b},f=this&&this.__param||function(g,h){return function(m,y){h(m,y,g)}};Object.defineProperty(r,"__esModule",{value:!0}),r.ThemeService=r.DEFAULT_ANSI_COLORS=void 0;const n=o(7239),d=o(8055),v=o(8460),p=o(844),l=o(2585),i=d.css.toColor("#ffffff"),s=d.css.toColor("#000000"),e=d.css.toColor("#ffffff"),t=d.css.toColor("#000000"),a={css:"rgba(255, 255, 255, 0.3)",rgba:4294967117};r.DEFAULT_ANSI_COLORS=Object.freeze((()=>{const g=[d.css.toColor("#2e3436"),d.css.toColor("#cc0000"),d.css.toColor("#4e9a06"),d.css.toColor("#c4a000"),d.css.toColor("#3465a4"),d.css.toColor("#75507b"),d.css.toColor("#06989a"),d.css.toColor("#d3d7cf"),d.css.toColor("#555753"),d.css.toColor("#ef2929"),d.css.toColor("#8ae234"),d.css.toColor("#fce94f"),d.css.toColor("#729fcf"),d.css.toColor("#ad7fa8"),d.css.toColor("#34e2e2"),d.css.toColor("#eeeeec")],h=[0,95,135,175,215,255];for(let m=0;m<216;m++){const y=h[m/36%6|0],k=h[m/6%6|0],D=h[m%6];g.push({css:d.channels.toCss(y,k,D),rgba:d.channels.toRgba(y,k,D)})}for(let m=0;m<24;m++){const y=8+10*m;g.push({css:d.channels.toCss(y,y,y),rgba:d.channels.toRgba(y,y,y)})}return g})());let _=r.ThemeService=class extends p.Disposable{get colors(){return this._colors}constructor(g){super(),this._optionsService=g,this._contrastCache=new n.ColorContrastCache,this._halfContrastCache=new n.ColorContrastCache,this._onChangeColors=this.register(new v.EventEmitter),this.onChangeColors=this._onChangeColors.event,this._colors={foreground:i,background:s,cursor:e,cursorAccent:t,selectionForeground:void 0,selectionBackgroundTransparent:a,selectionBackgroundOpaque:d.color.blend(s,a),selectionInactiveBackgroundTransparent:a,selectionInactiveBackgroundOpaque:d.color.blend(s,a),ansi:r.DEFAULT_ANSI_COLORS.slice(),contrastCache:this._contrastCache,halfContrastCache:this._halfContrastCache},this._updateRestoreColors(),this._setTheme(this._optionsService.rawOptions.theme),this.register(this._optionsService.onSpecificOptionChange("minimumContrastRatio",()=>this._contrastCache.clear())),this.register(this._optionsService.onSpecificOptionChange("theme",()=>this._setTheme(this._optionsService.rawOptions.theme)))}_setTheme(g={}){const h=this._colors;if(h.foreground=u(g.foreground,i),h.background=u(g.background,s),h.cursor=u(g.cursor,e),h.cursorAccent=u(g.cursorAccent,t),h.selectionBackgroundTransparent=u(g.selectionBackground,a),h.selectionBackgroundOpaque=d.color.blend(h.background,h.selectionBackgroundTransparent),h.selectionInactiveBackgroundTransparent=u(g.selectionInactiveBackground,h.selectionBackgroundTransparent),h.selectionInactiveBackgroundOpaque=d.color.blend(h.background,h.selectionInactiveBackgroundTransparent),h.selectionForeground=g.selectionForeground?u(g.selectionForeground,d.NULL_COLOR):void 0,h.selectionForeground===d.NULL_COLOR&&(h.selectionForeground=void 0),d.color.isOpaque(h.selectionBackgroundTransparent)&&(h.selectionBackgroundTransparent=d.color.opacity(h.selectionBackgroundTransparent,.3)),d.color.isOpaque(h.selectionInactiveBackgroundTransparent)&&(h.selectionInactiveBackgroundTransparent=d.color.opacity(h.selectionInactiveBackgroundTransparent,.3)),h.ansi=r.DEFAULT_ANSI_COLORS.slice(),h.ansi[0]=u(g.black,r.DEFAULT_ANSI_COLORS[0]),h.ansi[1]=u(g.red,r.DEFAULT_ANSI_COLORS[1]),h.ansi[2]=u(g.green,r.DEFAULT_ANSI_COLORS[2]),h.ansi[3]=u(g.yellow,r.DEFAULT_ANSI_COLORS[3]),h.ansi[4]=u(g.blue,r.DEFAULT_ANSI_COLORS[4]),h.ansi[5]=u(g.magenta,r.DEFAULT_ANSI_COLORS[5]),h.ansi[6]=u(g.cyan,r.DEFAULT_ANSI_COLORS[6]),h.ansi[7]=u(g.white,r.DEFAULT_ANSI_COLORS[7]),h.ansi[8]=u(g.brightBlack,r.DEFAULT_ANSI_COLORS[8]),h.ansi[9]=u(g.brightRed,r.DEFAULT_ANSI_COLORS[9]),h.ansi[10]=u(g.brightGreen,r.DEFAULT_ANSI_COLORS[10]),h.ansi[11]=u(g.brightYellow,r.DEFAULT_ANSI_COLORS[11]),h.ansi[12]=u(g.brightBlue,r.DEFAULT_ANSI_COLORS[12]),h.ansi[13]=u(g.brightMagenta,r.DEFAULT_ANSI_COLORS[13]),h.ansi[14]=u(g.brightCyan,r.DEFAULT_ANSI_COLORS[14]),h.ansi[15]=u(g.brightWhite,r.DEFAULT_ANSI_COLORS[15]),g.extendedAnsi){const m=Math.min(h.ansi.length-16,g.extendedAnsi.length);for(let y=0;y{Object.defineProperty(r,"__esModule",{value:!0}),r.CircularList=void 0;const c=o(8460),f=o(844);class n extends f.Disposable{constructor(v){super(),this._maxLength=v,this.onDeleteEmitter=this.register(new c.EventEmitter),this.onDelete=this.onDeleteEmitter.event,this.onInsertEmitter=this.register(new c.EventEmitter),this.onInsert=this.onInsertEmitter.event,this.onTrimEmitter=this.register(new c.EventEmitter),this.onTrim=this.onTrimEmitter.event,this._array=new Array(this._maxLength),this._startIndex=0,this._length=0}get maxLength(){return this._maxLength}set maxLength(v){if(this._maxLength===v)return;const p=new Array(v);for(let l=0;lthis._length)for(let p=this._length;p=v;i--)this._array[this._getCyclicIndex(i+l.length)]=this._array[this._getCyclicIndex(i)];for(let i=0;ithis._maxLength){const i=this._length+l.length-this._maxLength;this._startIndex+=i,this._length=this._maxLength,this.onTrimEmitter.fire(i)}else this._length+=l.length}trimStart(v){v>this._length&&(v=this._length),this._startIndex+=v,this._length-=v,this.onTrimEmitter.fire(v)}shiftElements(v,p,l){if(!(p<=0)){if(v<0||v>=this._length)throw new Error("start argument out of range");if(v+l<0)throw new Error("Cannot shift elements in list beyond index 0");if(l>0){for(let s=p-1;s>=0;s--)this.set(v+s+l,this.get(v+s));const i=v+p+l-this._length;if(i>0)for(this._length+=i;this._length>this._maxLength;)this._length--,this._startIndex++,this.onTrimEmitter.fire(1)}else for(let i=0;i{Object.defineProperty(r,"__esModule",{value:!0}),r.clone=void 0,r.clone=function o(c,f=5){if(typeof c!="object")return c;const n=Array.isArray(c)?[]:{};for(const d in c)n[d]=f<=1?c[d]:c[d]&&o(c[d],f-1);return n}},8055:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.contrastRatio=r.toPaddedHex=r.rgba=r.rgb=r.css=r.color=r.channels=r.NULL_COLOR=void 0;let o=0,c=0,f=0,n=0;var d,v,p,l,i;function s(t){const a=t.toString(16);return a.length<2?"0"+a:a}function e(t,a){return t>>0},t.toColor=function(a,_,u,g){return{css:t.toCss(a,_,u,g),rgba:t.toRgba(a,_,u,g)}}}(d||(r.channels=d={})),function(t){function a(_,u){return n=Math.round(255*u),[o,c,f]=i.toChannels(_.rgba),{css:d.toCss(o,c,f,n),rgba:d.toRgba(o,c,f,n)}}t.blend=function(_,u){if(n=(255&u.rgba)/255,n===1)return{css:u.css,rgba:u.rgba};const g=u.rgba>>24&255,h=u.rgba>>16&255,m=u.rgba>>8&255,y=_.rgba>>24&255,k=_.rgba>>16&255,D=_.rgba>>8&255;return o=y+Math.round((g-y)*n),c=k+Math.round((h-k)*n),f=D+Math.round((m-D)*n),{css:d.toCss(o,c,f),rgba:d.toRgba(o,c,f)}},t.isOpaque=function(_){return(255&_.rgba)==255},t.ensureContrastRatio=function(_,u,g){const h=i.ensureContrastRatio(_.rgba,u.rgba,g);if(h)return d.toColor(h>>24&255,h>>16&255,h>>8&255)},t.opaque=function(_){const u=(255|_.rgba)>>>0;return[o,c,f]=i.toChannels(u),{css:d.toCss(o,c,f),rgba:u}},t.opacity=a,t.multiplyOpacity=function(_,u){return n=255&_.rgba,a(_,n*u/255)},t.toColorRGB=function(_){return[_.rgba>>24&255,_.rgba>>16&255,_.rgba>>8&255]}}(v||(r.color=v={})),function(t){let a,_;try{const u=document.createElement("canvas");u.width=1,u.height=1;const g=u.getContext("2d",{willReadFrequently:!0});g&&(a=g,a.globalCompositeOperation="copy",_=a.createLinearGradient(0,0,1,1))}catch{}t.toColor=function(u){if(u.match(/#[\da-f]{3,8}/i))switch(u.length){case 4:return o=parseInt(u.slice(1,2).repeat(2),16),c=parseInt(u.slice(2,3).repeat(2),16),f=parseInt(u.slice(3,4).repeat(2),16),d.toColor(o,c,f);case 5:return o=parseInt(u.slice(1,2).repeat(2),16),c=parseInt(u.slice(2,3).repeat(2),16),f=parseInt(u.slice(3,4).repeat(2),16),n=parseInt(u.slice(4,5).repeat(2),16),d.toColor(o,c,f,n);case 7:return{css:u,rgba:(parseInt(u.slice(1),16)<<8|255)>>>0};case 9:return{css:u,rgba:parseInt(u.slice(1),16)>>>0}}const g=u.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(,\s*(0|1|\d?\.(\d+))\s*)?\)/);if(g)return o=parseInt(g[1]),c=parseInt(g[2]),f=parseInt(g[3]),n=Math.round(255*(g[5]===void 0?1:parseFloat(g[5]))),d.toColor(o,c,f,n);if(!a||!_)throw new Error("css.toColor: Unsupported css format");if(a.fillStyle=_,a.fillStyle=u,typeof a.fillStyle!="string")throw new Error("css.toColor: Unsupported css format");if(a.fillRect(0,0,1,1),[o,c,f,n]=a.getImageData(0,0,1,1).data,n!==255)throw new Error("css.toColor: Unsupported css format");return{rgba:d.toRgba(o,c,f,n),css:u}}}(p||(r.css=p={})),function(t){function a(_,u,g){const h=_/255,m=u/255,y=g/255;return .2126*(h<=.03928?h/12.92:Math.pow((h+.055)/1.055,2.4))+.7152*(m<=.03928?m/12.92:Math.pow((m+.055)/1.055,2.4))+.0722*(y<=.03928?y/12.92:Math.pow((y+.055)/1.055,2.4))}t.relativeLuminance=function(_){return a(_>>16&255,_>>8&255,255&_)},t.relativeLuminance2=a}(l||(r.rgb=l={})),function(t){function a(u,g,h){const m=u>>24&255,y=u>>16&255,k=u>>8&255;let D=g>>24&255,b=g>>16&255,B=g>>8&255,A=e(l.relativeLuminance2(D,b,B),l.relativeLuminance2(m,y,k));for(;A0||b>0||B>0);)D-=Math.max(0,Math.ceil(.1*D)),b-=Math.max(0,Math.ceil(.1*b)),B-=Math.max(0,Math.ceil(.1*B)),A=e(l.relativeLuminance2(D,b,B),l.relativeLuminance2(m,y,k));return(D<<24|b<<16|B<<8|255)>>>0}function _(u,g,h){const m=u>>24&255,y=u>>16&255,k=u>>8&255;let D=g>>24&255,b=g>>16&255,B=g>>8&255,A=e(l.relativeLuminance2(D,b,B),l.relativeLuminance2(m,y,k));for(;A>>0}t.blend=function(u,g){if(n=(255&g)/255,n===1)return g;const h=g>>24&255,m=g>>16&255,y=g>>8&255,k=u>>24&255,D=u>>16&255,b=u>>8&255;return o=k+Math.round((h-k)*n),c=D+Math.round((m-D)*n),f=b+Math.round((y-b)*n),d.toRgba(o,c,f)},t.ensureContrastRatio=function(u,g,h){const m=l.relativeLuminance(u>>8),y=l.relativeLuminance(g>>8);if(e(m,y)>8));if(Be(m,l.relativeLuminance(A>>8))?b:A}return b}const k=_(u,g,h),D=e(m,l.relativeLuminance(k>>8));if(De(m,l.relativeLuminance(b>>8))?k:b}return k}},t.reduceLuminance=a,t.increaseLuminance=_,t.toChannels=function(u){return[u>>24&255,u>>16&255,u>>8&255,255&u]}}(i||(r.rgba=i={})),r.toPaddedHex=s,r.contrastRatio=e},8969:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.CoreTerminal=void 0;const c=o(844),f=o(2585),n=o(4348),d=o(7866),v=o(744),p=o(7302),l=o(6975),i=o(8460),s=o(1753),e=o(1480),t=o(7994),a=o(9282),_=o(5435),u=o(5981),g=o(2660);let h=!1;class m extends c.Disposable{get onScroll(){return this._onScrollApi||(this._onScrollApi=this.register(new i.EventEmitter),this._onScroll.event(k=>{var D;(D=this._onScrollApi)==null||D.fire(k.position)})),this._onScrollApi.event}get cols(){return this._bufferService.cols}get rows(){return this._bufferService.rows}get buffers(){return this._bufferService.buffers}get options(){return this.optionsService.options}set options(k){for(const D in k)this.optionsService.options[D]=k[D]}constructor(k){super(),this._windowsWrappingHeuristics=this.register(new c.MutableDisposable),this._onBinary=this.register(new i.EventEmitter),this.onBinary=this._onBinary.event,this._onData=this.register(new i.EventEmitter),this.onData=this._onData.event,this._onLineFeed=this.register(new i.EventEmitter),this.onLineFeed=this._onLineFeed.event,this._onResize=this.register(new i.EventEmitter),this.onResize=this._onResize.event,this._onWriteParsed=this.register(new i.EventEmitter),this.onWriteParsed=this._onWriteParsed.event,this._onScroll=this.register(new i.EventEmitter),this._instantiationService=new n.InstantiationService,this.optionsService=this.register(new p.OptionsService(k)),this._instantiationService.setService(f.IOptionsService,this.optionsService),this._bufferService=this.register(this._instantiationService.createInstance(v.BufferService)),this._instantiationService.setService(f.IBufferService,this._bufferService),this._logService=this.register(this._instantiationService.createInstance(d.LogService)),this._instantiationService.setService(f.ILogService,this._logService),this.coreService=this.register(this._instantiationService.createInstance(l.CoreService)),this._instantiationService.setService(f.ICoreService,this.coreService),this.coreMouseService=this.register(this._instantiationService.createInstance(s.CoreMouseService)),this._instantiationService.setService(f.ICoreMouseService,this.coreMouseService),this.unicodeService=this.register(this._instantiationService.createInstance(e.UnicodeService)),this._instantiationService.setService(f.IUnicodeService,this.unicodeService),this._charsetService=this._instantiationService.createInstance(t.CharsetService),this._instantiationService.setService(f.ICharsetService,this._charsetService),this._oscLinkService=this._instantiationService.createInstance(g.OscLinkService),this._instantiationService.setService(f.IOscLinkService,this._oscLinkService),this._inputHandler=this.register(new _.InputHandler(this._bufferService,this._charsetService,this.coreService,this._logService,this.optionsService,this._oscLinkService,this.coreMouseService,this.unicodeService)),this.register((0,i.forwardEvent)(this._inputHandler.onLineFeed,this._onLineFeed)),this.register(this._inputHandler),this.register((0,i.forwardEvent)(this._bufferService.onResize,this._onResize)),this.register((0,i.forwardEvent)(this.coreService.onData,this._onData)),this.register((0,i.forwardEvent)(this.coreService.onBinary,this._onBinary)),this.register(this.coreService.onRequestScrollToBottom(()=>this.scrollToBottom())),this.register(this.coreService.onUserInput(()=>this._writeBuffer.handleUserInput())),this.register(this.optionsService.onMultipleOptionChange(["windowsMode","windowsPty"],()=>this._handleWindowsPtyOptionChange())),this.register(this._bufferService.onScroll(D=>{this._onScroll.fire({position:this._bufferService.buffer.ydisp,source:0}),this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop,this._bufferService.buffer.scrollBottom)})),this.register(this._inputHandler.onScroll(D=>{this._onScroll.fire({position:this._bufferService.buffer.ydisp,source:0}),this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop,this._bufferService.buffer.scrollBottom)})),this._writeBuffer=this.register(new u.WriteBuffer((D,b)=>this._inputHandler.parse(D,b))),this.register((0,i.forwardEvent)(this._writeBuffer.onWriteParsed,this._onWriteParsed))}write(k,D){this._writeBuffer.write(k,D)}writeSync(k,D){this._logService.logLevel<=f.LogLevelEnum.WARN&&!h&&(this._logService.warn("writeSync is unreliable and will be removed soon."),h=!0),this._writeBuffer.writeSync(k,D)}input(k,D=!0){this.coreService.triggerDataEvent(k,D)}resize(k,D){isNaN(k)||isNaN(D)||(k=Math.max(k,v.MINIMUM_COLS),D=Math.max(D,v.MINIMUM_ROWS),this._bufferService.resize(k,D))}scroll(k,D=!1){this._bufferService.scroll(k,D)}scrollLines(k,D,b){this._bufferService.scrollLines(k,D,b)}scrollPages(k){this.scrollLines(k*(this.rows-1))}scrollToTop(){this.scrollLines(-this._bufferService.buffer.ydisp)}scrollToBottom(){this.scrollLines(this._bufferService.buffer.ybase-this._bufferService.buffer.ydisp)}scrollToLine(k){const D=k-this._bufferService.buffer.ydisp;D!==0&&this.scrollLines(D)}registerEscHandler(k,D){return this._inputHandler.registerEscHandler(k,D)}registerDcsHandler(k,D){return this._inputHandler.registerDcsHandler(k,D)}registerCsiHandler(k,D){return this._inputHandler.registerCsiHandler(k,D)}registerOscHandler(k,D){return this._inputHandler.registerOscHandler(k,D)}_setup(){this._handleWindowsPtyOptionChange()}reset(){this._inputHandler.reset(),this._bufferService.reset(),this._charsetService.reset(),this.coreService.reset(),this.coreMouseService.reset()}_handleWindowsPtyOptionChange(){let k=!1;const D=this.optionsService.rawOptions.windowsPty;D&&D.buildNumber!==void 0&&D.buildNumber!==void 0?k=D.backend==="conpty"&&D.buildNumber<21376:this.optionsService.rawOptions.windowsMode&&(k=!0),k?this._enableWindowsWrappingHeuristics():this._windowsWrappingHeuristics.clear()}_enableWindowsWrappingHeuristics(){if(!this._windowsWrappingHeuristics.value){const k=[];k.push(this.onLineFeed(a.updateWindowsModeWrappedState.bind(null,this._bufferService))),k.push(this.registerCsiHandler({final:"H"},()=>((0,a.updateWindowsModeWrappedState)(this._bufferService),!1))),this._windowsWrappingHeuristics.value=(0,c.toDisposable)(()=>{for(const D of k)D.dispose()})}}}r.CoreTerminal=m},8460:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.runAndSubscribe=r.forwardEvent=r.EventEmitter=void 0,r.EventEmitter=class{constructor(){this._listeners=[],this._disposed=!1}get event(){return this._event||(this._event=o=>(this._listeners.push(o),{dispose:()=>{if(!this._disposed){for(let c=0;cc.fire(f))},r.runAndSubscribe=function(o,c){return c(void 0),o(f=>c(f))}},5435:function(R,r,o){var c=this&&this.__decorate||function(F,S,w,E){var L,P=arguments.length,W=P<3?S:E===null?E=Object.getOwnPropertyDescriptor(S,w):E;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")W=Reflect.decorate(F,S,w,E);else for(var $=F.length-1;$>=0;$--)(L=F[$])&&(W=(P<3?L(W):P>3?L(S,w,W):L(S,w))||W);return P>3&&W&&Object.defineProperty(S,w,W),W},f=this&&this.__param||function(F,S){return function(w,E){S(w,E,F)}};Object.defineProperty(r,"__esModule",{value:!0}),r.InputHandler=r.WindowsOptionsReportType=void 0;const n=o(2584),d=o(7116),v=o(2015),p=o(844),l=o(482),i=o(8437),s=o(8460),e=o(643),t=o(511),a=o(3734),_=o(2585),u=o(1480),g=o(6242),h=o(6351),m=o(5941),y={"(":0,")":1,"*":2,"+":3,"-":1,".":2},k=131072;function D(F,S){if(F>24)return S.setWinLines||!1;switch(F){case 1:return!!S.restoreWin;case 2:return!!S.minimizeWin;case 3:return!!S.setWinPosition;case 4:return!!S.setWinSizePixels;case 5:return!!S.raiseWin;case 6:return!!S.lowerWin;case 7:return!!S.refreshWin;case 8:return!!S.setWinSizeChars;case 9:return!!S.maximizeWin;case 10:return!!S.fullscreenWin;case 11:return!!S.getWinState;case 13:return!!S.getWinPosition;case 14:return!!S.getWinSizePixels;case 15:return!!S.getScreenSizePixels;case 16:return!!S.getCellSizePixels;case 18:return!!S.getWinSizeChars;case 19:return!!S.getScreenSizeChars;case 20:return!!S.getIconTitle;case 21:return!!S.getWinTitle;case 22:return!!S.pushTitle;case 23:return!!S.popTitle;case 24:return!!S.setWinLines}return!1}var b;(function(F){F[F.GET_WIN_SIZE_PIXELS=0]="GET_WIN_SIZE_PIXELS",F[F.GET_CELL_SIZE_PIXELS=1]="GET_CELL_SIZE_PIXELS"})(b||(r.WindowsOptionsReportType=b={}));let B=0;class A extends p.Disposable{getAttrData(){return this._curAttrData}constructor(S,w,E,L,P,W,$,j,x=new v.EscapeSequenceParser){super(),this._bufferService=S,this._charsetService=w,this._coreService=E,this._logService=L,this._optionsService=P,this._oscLinkService=W,this._coreMouseService=$,this._unicodeService=j,this._parser=x,this._parseBuffer=new Uint32Array(4096),this._stringDecoder=new l.StringToUtf32,this._utf8Decoder=new l.Utf8ToUtf32,this._workCell=new t.CellData,this._windowTitle="",this._iconName="",this._windowTitleStack=[],this._iconNameStack=[],this._curAttrData=i.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=i.DEFAULT_ATTR_DATA.clone(),this._onRequestBell=this.register(new s.EventEmitter),this.onRequestBell=this._onRequestBell.event,this._onRequestRefreshRows=this.register(new s.EventEmitter),this.onRequestRefreshRows=this._onRequestRefreshRows.event,this._onRequestReset=this.register(new s.EventEmitter),this.onRequestReset=this._onRequestReset.event,this._onRequestSendFocus=this.register(new s.EventEmitter),this.onRequestSendFocus=this._onRequestSendFocus.event,this._onRequestSyncScrollBar=this.register(new s.EventEmitter),this.onRequestSyncScrollBar=this._onRequestSyncScrollBar.event,this._onRequestWindowsOptionsReport=this.register(new s.EventEmitter),this.onRequestWindowsOptionsReport=this._onRequestWindowsOptionsReport.event,this._onA11yChar=this.register(new s.EventEmitter),this.onA11yChar=this._onA11yChar.event,this._onA11yTab=this.register(new s.EventEmitter),this.onA11yTab=this._onA11yTab.event,this._onCursorMove=this.register(new s.EventEmitter),this.onCursorMove=this._onCursorMove.event,this._onLineFeed=this.register(new s.EventEmitter),this.onLineFeed=this._onLineFeed.event,this._onScroll=this.register(new s.EventEmitter),this.onScroll=this._onScroll.event,this._onTitleChange=this.register(new s.EventEmitter),this.onTitleChange=this._onTitleChange.event,this._onColor=this.register(new s.EventEmitter),this.onColor=this._onColor.event,this._parseStack={paused:!1,cursorStartX:0,cursorStartY:0,decodedLength:0,position:0},this._specialColors=[256,257,258],this.register(this._parser),this._dirtyRowTracker=new H(this._bufferService),this._activeBuffer=this._bufferService.buffer,this.register(this._bufferService.buffers.onBufferActivate(C=>this._activeBuffer=C.activeBuffer)),this._parser.setCsiHandlerFallback((C,I)=>{this._logService.debug("Unknown CSI code: ",{identifier:this._parser.identToString(C),params:I.toArray()})}),this._parser.setEscHandlerFallback(C=>{this._logService.debug("Unknown ESC code: ",{identifier:this._parser.identToString(C)})}),this._parser.setExecuteHandlerFallback(C=>{this._logService.debug("Unknown EXECUTE code: ",{code:C})}),this._parser.setOscHandlerFallback((C,I,O)=>{this._logService.debug("Unknown OSC code: ",{identifier:C,action:I,data:O})}),this._parser.setDcsHandlerFallback((C,I,O)=>{I==="HOOK"&&(O=O.toArray()),this._logService.debug("Unknown DCS code: ",{identifier:this._parser.identToString(C),action:I,payload:O})}),this._parser.setPrintHandler((C,I,O)=>this.print(C,I,O)),this._parser.registerCsiHandler({final:"@"},C=>this.insertChars(C)),this._parser.registerCsiHandler({intermediates:" ",final:"@"},C=>this.scrollLeft(C)),this._parser.registerCsiHandler({final:"A"},C=>this.cursorUp(C)),this._parser.registerCsiHandler({intermediates:" ",final:"A"},C=>this.scrollRight(C)),this._parser.registerCsiHandler({final:"B"},C=>this.cursorDown(C)),this._parser.registerCsiHandler({final:"C"},C=>this.cursorForward(C)),this._parser.registerCsiHandler({final:"D"},C=>this.cursorBackward(C)),this._parser.registerCsiHandler({final:"E"},C=>this.cursorNextLine(C)),this._parser.registerCsiHandler({final:"F"},C=>this.cursorPrecedingLine(C)),this._parser.registerCsiHandler({final:"G"},C=>this.cursorCharAbsolute(C)),this._parser.registerCsiHandler({final:"H"},C=>this.cursorPosition(C)),this._parser.registerCsiHandler({final:"I"},C=>this.cursorForwardTab(C)),this._parser.registerCsiHandler({final:"J"},C=>this.eraseInDisplay(C,!1)),this._parser.registerCsiHandler({prefix:"?",final:"J"},C=>this.eraseInDisplay(C,!0)),this._parser.registerCsiHandler({final:"K"},C=>this.eraseInLine(C,!1)),this._parser.registerCsiHandler({prefix:"?",final:"K"},C=>this.eraseInLine(C,!0)),this._parser.registerCsiHandler({final:"L"},C=>this.insertLines(C)),this._parser.registerCsiHandler({final:"M"},C=>this.deleteLines(C)),this._parser.registerCsiHandler({final:"P"},C=>this.deleteChars(C)),this._parser.registerCsiHandler({final:"S"},C=>this.scrollUp(C)),this._parser.registerCsiHandler({final:"T"},C=>this.scrollDown(C)),this._parser.registerCsiHandler({final:"X"},C=>this.eraseChars(C)),this._parser.registerCsiHandler({final:"Z"},C=>this.cursorBackwardTab(C)),this._parser.registerCsiHandler({final:"`"},C=>this.charPosAbsolute(C)),this._parser.registerCsiHandler({final:"a"},C=>this.hPositionRelative(C)),this._parser.registerCsiHandler({final:"b"},C=>this.repeatPrecedingCharacter(C)),this._parser.registerCsiHandler({final:"c"},C=>this.sendDeviceAttributesPrimary(C)),this._parser.registerCsiHandler({prefix:">",final:"c"},C=>this.sendDeviceAttributesSecondary(C)),this._parser.registerCsiHandler({final:"d"},C=>this.linePosAbsolute(C)),this._parser.registerCsiHandler({final:"e"},C=>this.vPositionRelative(C)),this._parser.registerCsiHandler({final:"f"},C=>this.hVPosition(C)),this._parser.registerCsiHandler({final:"g"},C=>this.tabClear(C)),this._parser.registerCsiHandler({final:"h"},C=>this.setMode(C)),this._parser.registerCsiHandler({prefix:"?",final:"h"},C=>this.setModePrivate(C)),this._parser.registerCsiHandler({final:"l"},C=>this.resetMode(C)),this._parser.registerCsiHandler({prefix:"?",final:"l"},C=>this.resetModePrivate(C)),this._parser.registerCsiHandler({final:"m"},C=>this.charAttributes(C)),this._parser.registerCsiHandler({final:"n"},C=>this.deviceStatus(C)),this._parser.registerCsiHandler({prefix:"?",final:"n"},C=>this.deviceStatusPrivate(C)),this._parser.registerCsiHandler({intermediates:"!",final:"p"},C=>this.softReset(C)),this._parser.registerCsiHandler({intermediates:" ",final:"q"},C=>this.setCursorStyle(C)),this._parser.registerCsiHandler({final:"r"},C=>this.setScrollRegion(C)),this._parser.registerCsiHandler({final:"s"},C=>this.saveCursor(C)),this._parser.registerCsiHandler({final:"t"},C=>this.windowOptions(C)),this._parser.registerCsiHandler({final:"u"},C=>this.restoreCursor(C)),this._parser.registerCsiHandler({intermediates:"'",final:"}"},C=>this.insertColumns(C)),this._parser.registerCsiHandler({intermediates:"'",final:"~"},C=>this.deleteColumns(C)),this._parser.registerCsiHandler({intermediates:'"',final:"q"},C=>this.selectProtected(C)),this._parser.registerCsiHandler({intermediates:"$",final:"p"},C=>this.requestMode(C,!0)),this._parser.registerCsiHandler({prefix:"?",intermediates:"$",final:"p"},C=>this.requestMode(C,!1)),this._parser.setExecuteHandler(n.C0.BEL,()=>this.bell()),this._parser.setExecuteHandler(n.C0.LF,()=>this.lineFeed()),this._parser.setExecuteHandler(n.C0.VT,()=>this.lineFeed()),this._parser.setExecuteHandler(n.C0.FF,()=>this.lineFeed()),this._parser.setExecuteHandler(n.C0.CR,()=>this.carriageReturn()),this._parser.setExecuteHandler(n.C0.BS,()=>this.backspace()),this._parser.setExecuteHandler(n.C0.HT,()=>this.tab()),this._parser.setExecuteHandler(n.C0.SO,()=>this.shiftOut()),this._parser.setExecuteHandler(n.C0.SI,()=>this.shiftIn()),this._parser.setExecuteHandler(n.C1.IND,()=>this.index()),this._parser.setExecuteHandler(n.C1.NEL,()=>this.nextLine()),this._parser.setExecuteHandler(n.C1.HTS,()=>this.tabSet()),this._parser.registerOscHandler(0,new g.OscHandler(C=>(this.setTitle(C),this.setIconName(C),!0))),this._parser.registerOscHandler(1,new g.OscHandler(C=>this.setIconName(C))),this._parser.registerOscHandler(2,new g.OscHandler(C=>this.setTitle(C))),this._parser.registerOscHandler(4,new g.OscHandler(C=>this.setOrReportIndexedColor(C))),this._parser.registerOscHandler(8,new g.OscHandler(C=>this.setHyperlink(C))),this._parser.registerOscHandler(10,new g.OscHandler(C=>this.setOrReportFgColor(C))),this._parser.registerOscHandler(11,new g.OscHandler(C=>this.setOrReportBgColor(C))),this._parser.registerOscHandler(12,new g.OscHandler(C=>this.setOrReportCursorColor(C))),this._parser.registerOscHandler(104,new g.OscHandler(C=>this.restoreIndexedColor(C))),this._parser.registerOscHandler(110,new g.OscHandler(C=>this.restoreFgColor(C))),this._parser.registerOscHandler(111,new g.OscHandler(C=>this.restoreBgColor(C))),this._parser.registerOscHandler(112,new g.OscHandler(C=>this.restoreCursorColor(C))),this._parser.registerEscHandler({final:"7"},()=>this.saveCursor()),this._parser.registerEscHandler({final:"8"},()=>this.restoreCursor()),this._parser.registerEscHandler({final:"D"},()=>this.index()),this._parser.registerEscHandler({final:"E"},()=>this.nextLine()),this._parser.registerEscHandler({final:"H"},()=>this.tabSet()),this._parser.registerEscHandler({final:"M"},()=>this.reverseIndex()),this._parser.registerEscHandler({final:"="},()=>this.keypadApplicationMode()),this._parser.registerEscHandler({final:">"},()=>this.keypadNumericMode()),this._parser.registerEscHandler({final:"c"},()=>this.fullReset()),this._parser.registerEscHandler({final:"n"},()=>this.setgLevel(2)),this._parser.registerEscHandler({final:"o"},()=>this.setgLevel(3)),this._parser.registerEscHandler({final:"|"},()=>this.setgLevel(3)),this._parser.registerEscHandler({final:"}"},()=>this.setgLevel(2)),this._parser.registerEscHandler({final:"~"},()=>this.setgLevel(1)),this._parser.registerEscHandler({intermediates:"%",final:"@"},()=>this.selectDefaultCharset()),this._parser.registerEscHandler({intermediates:"%",final:"G"},()=>this.selectDefaultCharset());for(const C in d.CHARSETS)this._parser.registerEscHandler({intermediates:"(",final:C},()=>this.selectCharset("("+C)),this._parser.registerEscHandler({intermediates:")",final:C},()=>this.selectCharset(")"+C)),this._parser.registerEscHandler({intermediates:"*",final:C},()=>this.selectCharset("*"+C)),this._parser.registerEscHandler({intermediates:"+",final:C},()=>this.selectCharset("+"+C)),this._parser.registerEscHandler({intermediates:"-",final:C},()=>this.selectCharset("-"+C)),this._parser.registerEscHandler({intermediates:".",final:C},()=>this.selectCharset("."+C)),this._parser.registerEscHandler({intermediates:"/",final:C},()=>this.selectCharset("/"+C));this._parser.registerEscHandler({intermediates:"#",final:"8"},()=>this.screenAlignmentPattern()),this._parser.setErrorHandler(C=>(this._logService.error("Parsing error: ",C),C)),this._parser.registerDcsHandler({intermediates:"$",final:"q"},new h.DcsHandler((C,I)=>this.requestStatusString(C,I)))}_preserveStack(S,w,E,L){this._parseStack.paused=!0,this._parseStack.cursorStartX=S,this._parseStack.cursorStartY=w,this._parseStack.decodedLength=E,this._parseStack.position=L}_logSlowResolvingAsync(S){this._logService.logLevel<=_.LogLevelEnum.WARN&&Promise.race([S,new Promise((w,E)=>setTimeout(()=>E("#SLOW_TIMEOUT"),5e3))]).catch(w=>{if(w!=="#SLOW_TIMEOUT")throw w;console.warn("async parser handler taking longer than 5000 ms")})}_getCurrentLinkId(){return this._curAttrData.extended.urlId}parse(S,w){let E,L=this._activeBuffer.x,P=this._activeBuffer.y,W=0;const $=this._parseStack.paused;if($){if(E=this._parser.parse(this._parseBuffer,this._parseStack.decodedLength,w))return this._logSlowResolvingAsync(E),E;L=this._parseStack.cursorStartX,P=this._parseStack.cursorStartY,this._parseStack.paused=!1,S.length>k&&(W=this._parseStack.position+k)}if(this._logService.logLevel<=_.LogLevelEnum.DEBUG&&this._logService.debug("parsing data"+(typeof S=="string"?` "${S}"`:` "${Array.prototype.map.call(S,C=>String.fromCharCode(C)).join("")}"`),typeof S=="string"?S.split("").map(C=>C.charCodeAt(0)):S),this._parseBuffer.lengthk)for(let C=W;C0&&O.getWidth(this._activeBuffer.x-1)===2&&O.setCellFromCodepoint(this._activeBuffer.x-1,0,1,I);let N=this._parser.precedingJoinState;for(let M=w;Mj){if(x){const Q=O;let z=this._activeBuffer.x-X;for(this._activeBuffer.x=X,this._activeBuffer.y++,this._activeBuffer.y===this._activeBuffer.scrollBottom+1?(this._activeBuffer.y--,this._bufferService.scroll(this._eraseAttrData(),!0)):(this._activeBuffer.y>=this._bufferService.rows&&(this._activeBuffer.y=this._bufferService.rows-1),this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!0),O=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y),X>0&&O instanceof i.BufferLine&&O.copyCellsFrom(Q,z,0,X,!1);z=0;)O.setCellFromCodepoint(this._activeBuffer.x++,0,0,I)}else if(C&&(O.insertCells(this._activeBuffer.x,P-X,this._activeBuffer.getNullCell(I)),O.getWidth(j-1)===2&&O.setCellFromCodepoint(j-1,e.NULL_CELL_CODE,e.NULL_CELL_WIDTH,I)),O.setCellFromCodepoint(this._activeBuffer.x++,L,P,I),P>0)for(;--P;)O.setCellFromCodepoint(this._activeBuffer.x++,0,0,I)}this._parser.precedingJoinState=N,this._activeBuffer.x0&&O.getWidth(this._activeBuffer.x)===0&&!O.hasContent(this._activeBuffer.x)&&O.setCellFromCodepoint(this._activeBuffer.x,0,1,I),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}registerCsiHandler(S,w){return S.final!=="t"||S.prefix||S.intermediates?this._parser.registerCsiHandler(S,w):this._parser.registerCsiHandler(S,E=>!D(E.params[0],this._optionsService.rawOptions.windowOptions)||w(E))}registerDcsHandler(S,w){return this._parser.registerDcsHandler(S,new h.DcsHandler(w))}registerEscHandler(S,w){return this._parser.registerEscHandler(S,w)}registerOscHandler(S,w){return this._parser.registerOscHandler(S,new g.OscHandler(w))}bell(){return this._onRequestBell.fire(),!0}lineFeed(){return this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._optionsService.rawOptions.convertEol&&(this._activeBuffer.x=0),this._activeBuffer.y++,this._activeBuffer.y===this._activeBuffer.scrollBottom+1?(this._activeBuffer.y--,this._bufferService.scroll(this._eraseAttrData())):this._activeBuffer.y>=this._bufferService.rows?this._activeBuffer.y=this._bufferService.rows-1:this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!1,this._activeBuffer.x>=this._bufferService.cols&&this._activeBuffer.x--,this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._onLineFeed.fire(),!0}carriageReturn(){return this._activeBuffer.x=0,!0}backspace(){var S;if(!this._coreService.decPrivateModes.reverseWraparound)return this._restrictCursor(),this._activeBuffer.x>0&&this._activeBuffer.x--,!0;if(this._restrictCursor(this._bufferService.cols),this._activeBuffer.x>0)this._activeBuffer.x--;else if(this._activeBuffer.x===0&&this._activeBuffer.y>this._activeBuffer.scrollTop&&this._activeBuffer.y<=this._activeBuffer.scrollBottom&&((S=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y))!=null&&S.isWrapped)){this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!1,this._activeBuffer.y--,this._activeBuffer.x=this._bufferService.cols-1;const w=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y);w.hasWidth(this._activeBuffer.x)&&!w.hasContent(this._activeBuffer.x)&&this._activeBuffer.x--}return this._restrictCursor(),!0}tab(){if(this._activeBuffer.x>=this._bufferService.cols)return!0;const S=this._activeBuffer.x;return this._activeBuffer.x=this._activeBuffer.nextStop(),this._optionsService.rawOptions.screenReaderMode&&this._onA11yTab.fire(this._activeBuffer.x-S),!0}shiftOut(){return this._charsetService.setgLevel(1),!0}shiftIn(){return this._charsetService.setgLevel(0),!0}_restrictCursor(S=this._bufferService.cols-1){this._activeBuffer.x=Math.min(S,Math.max(0,this._activeBuffer.x)),this._activeBuffer.y=this._coreService.decPrivateModes.origin?Math.min(this._activeBuffer.scrollBottom,Math.max(this._activeBuffer.scrollTop,this._activeBuffer.y)):Math.min(this._bufferService.rows-1,Math.max(0,this._activeBuffer.y)),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}_setCursor(S,w){this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._coreService.decPrivateModes.origin?(this._activeBuffer.x=S,this._activeBuffer.y=this._activeBuffer.scrollTop+w):(this._activeBuffer.x=S,this._activeBuffer.y=w),this._restrictCursor(),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}_moveCursor(S,w){this._restrictCursor(),this._setCursor(this._activeBuffer.x+S,this._activeBuffer.y+w)}cursorUp(S){const w=this._activeBuffer.y-this._activeBuffer.scrollTop;return w>=0?this._moveCursor(0,-Math.min(w,S.params[0]||1)):this._moveCursor(0,-(S.params[0]||1)),!0}cursorDown(S){const w=this._activeBuffer.scrollBottom-this._activeBuffer.y;return w>=0?this._moveCursor(0,Math.min(w,S.params[0]||1)):this._moveCursor(0,S.params[0]||1),!0}cursorForward(S){return this._moveCursor(S.params[0]||1,0),!0}cursorBackward(S){return this._moveCursor(-(S.params[0]||1),0),!0}cursorNextLine(S){return this.cursorDown(S),this._activeBuffer.x=0,!0}cursorPrecedingLine(S){return this.cursorUp(S),this._activeBuffer.x=0,!0}cursorCharAbsolute(S){return this._setCursor((S.params[0]||1)-1,this._activeBuffer.y),!0}cursorPosition(S){return this._setCursor(S.length>=2?(S.params[1]||1)-1:0,(S.params[0]||1)-1),!0}charPosAbsolute(S){return this._setCursor((S.params[0]||1)-1,this._activeBuffer.y),!0}hPositionRelative(S){return this._moveCursor(S.params[0]||1,0),!0}linePosAbsolute(S){return this._setCursor(this._activeBuffer.x,(S.params[0]||1)-1),!0}vPositionRelative(S){return this._moveCursor(0,S.params[0]||1),!0}hVPosition(S){return this.cursorPosition(S),!0}tabClear(S){const w=S.params[0];return w===0?delete this._activeBuffer.tabs[this._activeBuffer.x]:w===3&&(this._activeBuffer.tabs={}),!0}cursorForwardTab(S){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let w=S.params[0]||1;for(;w--;)this._activeBuffer.x=this._activeBuffer.nextStop();return!0}cursorBackwardTab(S){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let w=S.params[0]||1;for(;w--;)this._activeBuffer.x=this._activeBuffer.prevStop();return!0}selectProtected(S){const w=S.params[0];return w===1&&(this._curAttrData.bg|=536870912),w!==2&&w!==0||(this._curAttrData.bg&=-536870913),!0}_eraseInBufferLine(S,w,E,L=!1,P=!1){const W=this._activeBuffer.lines.get(this._activeBuffer.ybase+S);W.replaceCells(w,E,this._activeBuffer.getNullCell(this._eraseAttrData()),P),L&&(W.isWrapped=!1)}_resetBufferLine(S,w=!1){const E=this._activeBuffer.lines.get(this._activeBuffer.ybase+S);E&&(E.fill(this._activeBuffer.getNullCell(this._eraseAttrData()),w),this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase+S),E.isWrapped=!1)}eraseInDisplay(S,w=!1){let E;switch(this._restrictCursor(this._bufferService.cols),S.params[0]){case 0:for(E=this._activeBuffer.y,this._dirtyRowTracker.markDirty(E),this._eraseInBufferLine(E++,this._activeBuffer.x,this._bufferService.cols,this._activeBuffer.x===0,w);E=this._bufferService.cols&&(this._activeBuffer.lines.get(E+1).isWrapped=!1);E--;)this._resetBufferLine(E,w);this._dirtyRowTracker.markDirty(0);break;case 2:for(E=this._bufferService.rows,this._dirtyRowTracker.markDirty(E-1);E--;)this._resetBufferLine(E,w);this._dirtyRowTracker.markDirty(0);break;case 3:const L=this._activeBuffer.lines.length-this._bufferService.rows;L>0&&(this._activeBuffer.lines.trimStart(L),this._activeBuffer.ybase=Math.max(this._activeBuffer.ybase-L,0),this._activeBuffer.ydisp=Math.max(this._activeBuffer.ydisp-L,0),this._onScroll.fire(0))}return!0}eraseInLine(S,w=!1){switch(this._restrictCursor(this._bufferService.cols),S.params[0]){case 0:this._eraseInBufferLine(this._activeBuffer.y,this._activeBuffer.x,this._bufferService.cols,this._activeBuffer.x===0,w);break;case 1:this._eraseInBufferLine(this._activeBuffer.y,0,this._activeBuffer.x+1,!1,w);break;case 2:this._eraseInBufferLine(this._activeBuffer.y,0,this._bufferService.cols,!0,w)}return this._dirtyRowTracker.markDirty(this._activeBuffer.y),!0}insertLines(S){this._restrictCursor();let w=S.params[0]||1;if(this._activeBuffer.y>this._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.y65535?2:1}let x=j;for(let C=1;C0||(this._is("xterm")||this._is("rxvt-unicode")||this._is("screen")?this._coreService.triggerDataEvent(n.C0.ESC+"[?1;2c"):this._is("linux")&&this._coreService.triggerDataEvent(n.C0.ESC+"[?6c")),!0}sendDeviceAttributesSecondary(S){return S.params[0]>0||(this._is("xterm")?this._coreService.triggerDataEvent(n.C0.ESC+"[>0;276;0c"):this._is("rxvt-unicode")?this._coreService.triggerDataEvent(n.C0.ESC+"[>85;95;0c"):this._is("linux")?this._coreService.triggerDataEvent(S.params[0]+"c"):this._is("screen")&&this._coreService.triggerDataEvent(n.C0.ESC+"[>83;40003;0c")),!0}_is(S){return(this._optionsService.rawOptions.termName+"").indexOf(S)===0}setMode(S){for(let w=0;wG?1:2,N=S.params[0];return M=N,K=w?N===2?4:N===4?O(W.modes.insertMode):N===12?3:N===20?O(I.convertEol):0:N===1?O(E.applicationCursorKeys):N===3?I.windowOptions.setWinLines?j===80?2:j===132?1:0:0:N===6?O(E.origin):N===7?O(E.wraparound):N===8?3:N===9?O(L==="X10"):N===12?O(I.cursorBlink):N===25?O(!W.isCursorHidden):N===45?O(E.reverseWraparound):N===66?O(E.applicationKeypad):N===67?4:N===1e3?O(L==="VT200"):N===1002?O(L==="DRAG"):N===1003?O(L==="ANY"):N===1004?O(E.sendFocus):N===1005?4:N===1006?O(P==="SGR"):N===1015?4:N===1016?O(P==="SGR_PIXELS"):N===1048?1:N===47||N===1047||N===1049?O(x===C):N===2004?O(E.bracketedPasteMode):0,W.triggerDataEvent(`${n.C0.ESC}[${w?"":"?"}${M};${K}$y`),!0;var M,K}_updateAttrColor(S,w,E,L,P){return w===2?(S|=50331648,S&=-16777216,S|=a.AttributeData.fromColorRGB([E,L,P])):w===5&&(S&=-50331904,S|=33554432|255&E),S}_extractColor(S,w,E){const L=[0,0,-1,0,0,0];let P=0,W=0;do{if(L[W+P]=S.params[w+W],S.hasSubParams(w+W)){const $=S.getSubParams(w+W);let j=0;do L[1]===5&&(P=1),L[W+j+1+P]=$[j];while(++j<$.length&&j+W+1+P=2||L[1]===2&&W+P>=5)break;L[1]&&(P=1)}while(++W+w5)&&(S=1),w.extended.underlineStyle=S,w.fg|=268435456,S===0&&(w.fg&=-268435457),w.updateExtended()}_processSGR0(S){S.fg=i.DEFAULT_ATTR_DATA.fg,S.bg=i.DEFAULT_ATTR_DATA.bg,S.extended=S.extended.clone(),S.extended.underlineStyle=0,S.extended.underlineColor&=-67108864,S.updateExtended()}charAttributes(S){if(S.length===1&&S.params[0]===0)return this._processSGR0(this._curAttrData),!0;const w=S.length;let E;const L=this._curAttrData;for(let P=0;P=30&&E<=37?(L.fg&=-50331904,L.fg|=16777216|E-30):E>=40&&E<=47?(L.bg&=-50331904,L.bg|=16777216|E-40):E>=90&&E<=97?(L.fg&=-50331904,L.fg|=16777224|E-90):E>=100&&E<=107?(L.bg&=-50331904,L.bg|=16777224|E-100):E===0?this._processSGR0(L):E===1?L.fg|=134217728:E===3?L.bg|=67108864:E===4?(L.fg|=268435456,this._processUnderline(S.hasSubParams(P)?S.getSubParams(P)[0]:1,L)):E===5?L.fg|=536870912:E===7?L.fg|=67108864:E===8?L.fg|=1073741824:E===9?L.fg|=2147483648:E===2?L.bg|=134217728:E===21?this._processUnderline(2,L):E===22?(L.fg&=-134217729,L.bg&=-134217729):E===23?L.bg&=-67108865:E===24?(L.fg&=-268435457,this._processUnderline(0,L)):E===25?L.fg&=-536870913:E===27?L.fg&=-67108865:E===28?L.fg&=-1073741825:E===29?L.fg&=2147483647:E===39?(L.fg&=-67108864,L.fg|=16777215&i.DEFAULT_ATTR_DATA.fg):E===49?(L.bg&=-67108864,L.bg|=16777215&i.DEFAULT_ATTR_DATA.bg):E===38||E===48||E===58?P+=this._extractColor(S,P,L):E===53?L.bg|=1073741824:E===55?L.bg&=-1073741825:E===59?(L.extended=L.extended.clone(),L.extended.underlineColor=-1,L.updateExtended()):E===100?(L.fg&=-67108864,L.fg|=16777215&i.DEFAULT_ATTR_DATA.fg,L.bg&=-67108864,L.bg|=16777215&i.DEFAULT_ATTR_DATA.bg):this._logService.debug("Unknown SGR attribute: %d.",E);return!0}deviceStatus(S){switch(S.params[0]){case 5:this._coreService.triggerDataEvent(`${n.C0.ESC}[0n`);break;case 6:const w=this._activeBuffer.y+1,E=this._activeBuffer.x+1;this._coreService.triggerDataEvent(`${n.C0.ESC}[${w};${E}R`)}return!0}deviceStatusPrivate(S){if(S.params[0]===6){const w=this._activeBuffer.y+1,E=this._activeBuffer.x+1;this._coreService.triggerDataEvent(`${n.C0.ESC}[?${w};${E}R`)}return!0}softReset(S){return this._coreService.isCursorHidden=!1,this._onRequestSyncScrollBar.fire(),this._activeBuffer.scrollTop=0,this._activeBuffer.scrollBottom=this._bufferService.rows-1,this._curAttrData=i.DEFAULT_ATTR_DATA.clone(),this._coreService.reset(),this._charsetService.reset(),this._activeBuffer.savedX=0,this._activeBuffer.savedY=this._activeBuffer.ybase,this._activeBuffer.savedCurAttrData.fg=this._curAttrData.fg,this._activeBuffer.savedCurAttrData.bg=this._curAttrData.bg,this._activeBuffer.savedCharset=this._charsetService.charset,this._coreService.decPrivateModes.origin=!1,!0}setCursorStyle(S){const w=S.params[0]||1;switch(w){case 1:case 2:this._optionsService.options.cursorStyle="block";break;case 3:case 4:this._optionsService.options.cursorStyle="underline";break;case 5:case 6:this._optionsService.options.cursorStyle="bar"}const E=w%2==1;return this._optionsService.options.cursorBlink=E,!0}setScrollRegion(S){const w=S.params[0]||1;let E;return(S.length<2||(E=S.params[1])>this._bufferService.rows||E===0)&&(E=this._bufferService.rows),E>w&&(this._activeBuffer.scrollTop=w-1,this._activeBuffer.scrollBottom=E-1,this._setCursor(0,0)),!0}windowOptions(S){if(!D(S.params[0],this._optionsService.rawOptions.windowOptions))return!0;const w=S.length>1?S.params[1]:0;switch(S.params[0]){case 14:w!==2&&this._onRequestWindowsOptionsReport.fire(b.GET_WIN_SIZE_PIXELS);break;case 16:this._onRequestWindowsOptionsReport.fire(b.GET_CELL_SIZE_PIXELS);break;case 18:this._bufferService&&this._coreService.triggerDataEvent(`${n.C0.ESC}[8;${this._bufferService.rows};${this._bufferService.cols}t`);break;case 22:w!==0&&w!==2||(this._windowTitleStack.push(this._windowTitle),this._windowTitleStack.length>10&&this._windowTitleStack.shift()),w!==0&&w!==1||(this._iconNameStack.push(this._iconName),this._iconNameStack.length>10&&this._iconNameStack.shift());break;case 23:w!==0&&w!==2||this._windowTitleStack.length&&this.setTitle(this._windowTitleStack.pop()),w!==0&&w!==1||this._iconNameStack.length&&this.setIconName(this._iconNameStack.pop())}return!0}saveCursor(S){return this._activeBuffer.savedX=this._activeBuffer.x,this._activeBuffer.savedY=this._activeBuffer.ybase+this._activeBuffer.y,this._activeBuffer.savedCurAttrData.fg=this._curAttrData.fg,this._activeBuffer.savedCurAttrData.bg=this._curAttrData.bg,this._activeBuffer.savedCharset=this._charsetService.charset,!0}restoreCursor(S){return this._activeBuffer.x=this._activeBuffer.savedX||0,this._activeBuffer.y=Math.max(this._activeBuffer.savedY-this._activeBuffer.ybase,0),this._curAttrData.fg=this._activeBuffer.savedCurAttrData.fg,this._curAttrData.bg=this._activeBuffer.savedCurAttrData.bg,this._charsetService.charset=this._savedCharset,this._activeBuffer.savedCharset&&(this._charsetService.charset=this._activeBuffer.savedCharset),this._restrictCursor(),!0}setTitle(S){return this._windowTitle=S,this._onTitleChange.fire(S),!0}setIconName(S){return this._iconName=S,!0}setOrReportIndexedColor(S){const w=[],E=S.split(";");for(;E.length>1;){const L=E.shift(),P=E.shift();if(/^\d+$/.exec(L)){const W=parseInt(L);if(U(W))if(P==="?")w.push({type:0,index:W});else{const $=(0,m.parseColor)(P);$&&w.push({type:1,index:W,color:$})}}}return w.length&&this._onColor.fire(w),!0}setHyperlink(S){const w=S.split(";");return!(w.length<2)&&(w[1]?this._createHyperlink(w[0],w[1]):!w[0]&&this._finishHyperlink())}_createHyperlink(S,w){this._getCurrentLinkId()&&this._finishHyperlink();const E=S.split(":");let L;const P=E.findIndex(W=>W.startsWith("id="));return P!==-1&&(L=E[P].slice(3)||void 0),this._curAttrData.extended=this._curAttrData.extended.clone(),this._curAttrData.extended.urlId=this._oscLinkService.registerLink({id:L,uri:w}),this._curAttrData.updateExtended(),!0}_finishHyperlink(){return this._curAttrData.extended=this._curAttrData.extended.clone(),this._curAttrData.extended.urlId=0,this._curAttrData.updateExtended(),!0}_setOrReportSpecialColor(S,w){const E=S.split(";");for(let L=0;L=this._specialColors.length);++L,++w)if(E[L]==="?")this._onColor.fire([{type:0,index:this._specialColors[w]}]);else{const P=(0,m.parseColor)(E[L]);P&&this._onColor.fire([{type:1,index:this._specialColors[w],color:P}])}return!0}setOrReportFgColor(S){return this._setOrReportSpecialColor(S,0)}setOrReportBgColor(S){return this._setOrReportSpecialColor(S,1)}setOrReportCursorColor(S){return this._setOrReportSpecialColor(S,2)}restoreIndexedColor(S){if(!S)return this._onColor.fire([{type:2}]),!0;const w=[],E=S.split(";");for(let L=0;L=this._bufferService.rows&&(this._activeBuffer.y=this._bufferService.rows-1),this._restrictCursor(),!0}tabSet(){return this._activeBuffer.tabs[this._activeBuffer.x]=!0,!0}reverseIndex(){if(this._restrictCursor(),this._activeBuffer.y===this._activeBuffer.scrollTop){const S=this._activeBuffer.scrollBottom-this._activeBuffer.scrollTop;this._activeBuffer.lines.shiftElements(this._activeBuffer.ybase+this._activeBuffer.y,S,1),this._activeBuffer.lines.set(this._activeBuffer.ybase+this._activeBuffer.y,this._activeBuffer.getBlankLine(this._eraseAttrData())),this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop,this._activeBuffer.scrollBottom)}else this._activeBuffer.y--,this._restrictCursor();return!0}fullReset(){return this._parser.reset(),this._onRequestReset.fire(),!0}reset(){this._curAttrData=i.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=i.DEFAULT_ATTR_DATA.clone()}_eraseAttrData(){return this._eraseAttrDataInternal.bg&=-67108864,this._eraseAttrDataInternal.bg|=67108863&this._curAttrData.bg,this._eraseAttrDataInternal}setgLevel(S){return this._charsetService.setgLevel(S),!0}screenAlignmentPattern(){const S=new t.CellData;S.content=4194373,S.fg=this._curAttrData.fg,S.bg=this._curAttrData.bg,this._setCursor(0,0);for(let w=0;w(this._coreService.triggerDataEvent(`${n.C0.ESC}${P}${n.C0.ESC}\\`),!0))(S==='"q'?`P1$r${this._curAttrData.isProtected()?1:0}"q`:S==='"p'?'P1$r61;1"p':S==="r"?`P1$r${E.scrollTop+1};${E.scrollBottom+1}r`:S==="m"?"P1$r0m":S===" q"?`P1$r${{block:2,underline:4,bar:6}[L.cursorStyle]-(L.cursorBlink?1:0)} q`:"P0$r")}markRangeDirty(S,w){this._dirtyRowTracker.markRangeDirty(S,w)}}r.InputHandler=A;let H=class{constructor(F){this._bufferService=F,this.clearRange()}clearRange(){this.start=this._bufferService.buffer.y,this.end=this._bufferService.buffer.y}markDirty(F){Fthis.end&&(this.end=F)}markRangeDirty(F,S){F>S&&(B=F,F=S,S=B),Fthis.end&&(this.end=S)}markAllDirty(){this.markRangeDirty(0,this._bufferService.rows-1)}};function U(F){return 0<=F&&F<256}H=c([f(0,_.IBufferService)],H)},844:(R,r)=>{function o(c){for(const f of c)f.dispose();c.length=0}Object.defineProperty(r,"__esModule",{value:!0}),r.getDisposeArrayDisposable=r.disposeArray=r.toDisposable=r.MutableDisposable=r.Disposable=void 0,r.Disposable=class{constructor(){this._disposables=[],this._isDisposed=!1}dispose(){this._isDisposed=!0;for(const c of this._disposables)c.dispose();this._disposables.length=0}register(c){return this._disposables.push(c),c}unregister(c){const f=this._disposables.indexOf(c);f!==-1&&this._disposables.splice(f,1)}},r.MutableDisposable=class{constructor(){this._isDisposed=!1}get value(){return this._isDisposed?void 0:this._value}set value(c){var f;this._isDisposed||c===this._value||((f=this._value)==null||f.dispose(),this._value=c)}clear(){this.value=void 0}dispose(){var c;this._isDisposed=!0,(c=this._value)==null||c.dispose(),this._value=void 0}},r.toDisposable=function(c){return{dispose:c}},r.disposeArray=o,r.getDisposeArrayDisposable=function(c){return{dispose:()=>o(c)}}},1505:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.FourKeyMap=r.TwoKeyMap=void 0;class o{constructor(){this._data={}}set(f,n,d){this._data[f]||(this._data[f]={}),this._data[f][n]=d}get(f,n){return this._data[f]?this._data[f][n]:void 0}clear(){this._data={}}}r.TwoKeyMap=o,r.FourKeyMap=class{constructor(){this._data=new o}set(c,f,n,d,v){this._data.get(c,f)||this._data.set(c,f,new o),this._data.get(c,f).set(n,d,v)}get(c,f,n,d){var v;return(v=this._data.get(c,f))==null?void 0:v.get(n,d)}clear(){this._data.clear()}}},6114:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.isChromeOS=r.isLinux=r.isWindows=r.isIphone=r.isIpad=r.isMac=r.getSafariVersion=r.isSafari=r.isLegacyEdge=r.isFirefox=r.isNode=void 0,r.isNode=typeof process<"u"&&"title"in process;const o=r.isNode?"node":navigator.userAgent,c=r.isNode?"node":navigator.platform;r.isFirefox=o.includes("Firefox"),r.isLegacyEdge=o.includes("Edge"),r.isSafari=/^((?!chrome|android).)*safari/i.test(o),r.getSafariVersion=function(){if(!r.isSafari)return 0;const f=o.match(/Version\/(\d+)/);return f===null||f.length<2?0:parseInt(f[1])},r.isMac=["Macintosh","MacIntel","MacPPC","Mac68K"].includes(c),r.isIpad=c==="iPad",r.isIphone=c==="iPhone",r.isWindows=["Windows","Win16","Win32","WinCE"].includes(c),r.isLinux=c.indexOf("Linux")>=0,r.isChromeOS=/\bCrOS\b/.test(o)},6106:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.SortedList=void 0;let o=0;r.SortedList=class{constructor(c){this._getKey=c,this._array=[]}clear(){this._array.length=0}insert(c){this._array.length!==0?(o=this._search(this._getKey(c)),this._array.splice(o,0,c)):this._array.push(c)}delete(c){if(this._array.length===0)return!1;const f=this._getKey(c);if(f===void 0||(o=this._search(f),o===-1)||this._getKey(this._array[o])!==f)return!1;do if(this._array[o]===c)return this._array.splice(o,1),!0;while(++o=this._array.length)&&this._getKey(this._array[o])===c))do yield this._array[o];while(++o=this._array.length)&&this._getKey(this._array[o])===c))do f(this._array[o]);while(++o=f;){let d=f+n>>1;const v=this._getKey(this._array[d]);if(v>c)n=d-1;else{if(!(v0&&this._getKey(this._array[d-1])===c;)d--;return d}f=d+1}}return f}}},7226:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.DebouncedIdleTask=r.IdleTaskQueue=r.PriorityTaskQueue=void 0;const c=o(6114);class f{constructor(){this._tasks=[],this._i=0}enqueue(v){this._tasks.push(v),this._start()}flush(){for(;this._is)return i-p<-20&&console.warn(`task queue exceeded allotted deadline by ${Math.abs(Math.round(i-p))}ms`),void this._start();i=s}this.clear()}}class n extends f{_requestCallback(v){return setTimeout(()=>v(this._createDeadline(16)))}_cancelCallback(v){clearTimeout(v)}_createDeadline(v){const p=Date.now()+v;return{timeRemaining:()=>Math.max(0,p-Date.now())}}}r.PriorityTaskQueue=n,r.IdleTaskQueue=!c.isNode&&"requestIdleCallback"in window?class extends f{_requestCallback(d){return requestIdleCallback(d)}_cancelCallback(d){cancelIdleCallback(d)}}:n,r.DebouncedIdleTask=class{constructor(){this._queue=new r.IdleTaskQueue}set(d){this._queue.clear(),this._queue.enqueue(d)}flush(){this._queue.flush()}}},9282:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.updateWindowsModeWrappedState=void 0;const c=o(643);r.updateWindowsModeWrappedState=function(f){const n=f.buffer.lines.get(f.buffer.ybase+f.buffer.y-1),d=n?.get(f.cols-1),v=f.buffer.lines.get(f.buffer.ybase+f.buffer.y);v&&d&&(v.isWrapped=d[c.CHAR_DATA_CODE_INDEX]!==c.NULL_CELL_CODE&&d[c.CHAR_DATA_CODE_INDEX]!==c.WHITESPACE_CELL_CODE)}},3734:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.ExtendedAttrs=r.AttributeData=void 0;class o{constructor(){this.fg=0,this.bg=0,this.extended=new c}static toColorRGB(n){return[n>>>16&255,n>>>8&255,255&n]}static fromColorRGB(n){return(255&n[0])<<16|(255&n[1])<<8|255&n[2]}clone(){const n=new o;return n.fg=this.fg,n.bg=this.bg,n.extended=this.extended.clone(),n}isInverse(){return 67108864&this.fg}isBold(){return 134217728&this.fg}isUnderline(){return this.hasExtendedAttrs()&&this.extended.underlineStyle!==0?1:268435456&this.fg}isBlink(){return 536870912&this.fg}isInvisible(){return 1073741824&this.fg}isItalic(){return 67108864&this.bg}isDim(){return 134217728&this.bg}isStrikethrough(){return 2147483648&this.fg}isProtected(){return 536870912&this.bg}isOverline(){return 1073741824&this.bg}getFgColorMode(){return 50331648&this.fg}getBgColorMode(){return 50331648&this.bg}isFgRGB(){return(50331648&this.fg)==50331648}isBgRGB(){return(50331648&this.bg)==50331648}isFgPalette(){return(50331648&this.fg)==16777216||(50331648&this.fg)==33554432}isBgPalette(){return(50331648&this.bg)==16777216||(50331648&this.bg)==33554432}isFgDefault(){return(50331648&this.fg)==0}isBgDefault(){return(50331648&this.bg)==0}isAttributeDefault(){return this.fg===0&&this.bg===0}getFgColor(){switch(50331648&this.fg){case 16777216:case 33554432:return 255&this.fg;case 50331648:return 16777215&this.fg;default:return-1}}getBgColor(){switch(50331648&this.bg){case 16777216:case 33554432:return 255&this.bg;case 50331648:return 16777215&this.bg;default:return-1}}hasExtendedAttrs(){return 268435456&this.bg}updateExtended(){this.extended.isEmpty()?this.bg&=-268435457:this.bg|=268435456}getUnderlineColor(){if(268435456&this.bg&&~this.extended.underlineColor)switch(50331648&this.extended.underlineColor){case 16777216:case 33554432:return 255&this.extended.underlineColor;case 50331648:return 16777215&this.extended.underlineColor;default:return this.getFgColor()}return this.getFgColor()}getUnderlineColorMode(){return 268435456&this.bg&&~this.extended.underlineColor?50331648&this.extended.underlineColor:this.getFgColorMode()}isUnderlineColorRGB(){return 268435456&this.bg&&~this.extended.underlineColor?(50331648&this.extended.underlineColor)==50331648:this.isFgRGB()}isUnderlineColorPalette(){return 268435456&this.bg&&~this.extended.underlineColor?(50331648&this.extended.underlineColor)==16777216||(50331648&this.extended.underlineColor)==33554432:this.isFgPalette()}isUnderlineColorDefault(){return 268435456&this.bg&&~this.extended.underlineColor?(50331648&this.extended.underlineColor)==0:this.isFgDefault()}getUnderlineStyle(){return 268435456&this.fg?268435456&this.bg?this.extended.underlineStyle:1:0}getUnderlineVariantOffset(){return this.extended.underlineVariantOffset}}r.AttributeData=o;class c{get ext(){return this._urlId?-469762049&this._ext|this.underlineStyle<<26:this._ext}set ext(n){this._ext=n}get underlineStyle(){return this._urlId?5:(469762048&this._ext)>>26}set underlineStyle(n){this._ext&=-469762049,this._ext|=n<<26&469762048}get underlineColor(){return 67108863&this._ext}set underlineColor(n){this._ext&=-67108864,this._ext|=67108863&n}get urlId(){return this._urlId}set urlId(n){this._urlId=n}get underlineVariantOffset(){const n=(3758096384&this._ext)>>29;return n<0?4294967288^n:n}set underlineVariantOffset(n){this._ext&=536870911,this._ext|=n<<29&3758096384}constructor(n=0,d=0){this._ext=0,this._urlId=0,this._ext=n,this._urlId=d}clone(){return new c(this._ext,this._urlId)}isEmpty(){return this.underlineStyle===0&&this._urlId===0}}r.ExtendedAttrs=c},9092:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.Buffer=r.MAX_BUFFER_SIZE=void 0;const c=o(6349),f=o(7226),n=o(3734),d=o(8437),v=o(4634),p=o(511),l=o(643),i=o(4863),s=o(7116);r.MAX_BUFFER_SIZE=4294967295,r.Buffer=class{constructor(e,t,a){this._hasScrollback=e,this._optionsService=t,this._bufferService=a,this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.tabs={},this.savedY=0,this.savedX=0,this.savedCurAttrData=d.DEFAULT_ATTR_DATA.clone(),this.savedCharset=s.DEFAULT_CHARSET,this.markers=[],this._nullCell=p.CellData.fromCharData([0,l.NULL_CELL_CHAR,l.NULL_CELL_WIDTH,l.NULL_CELL_CODE]),this._whitespaceCell=p.CellData.fromCharData([0,l.WHITESPACE_CELL_CHAR,l.WHITESPACE_CELL_WIDTH,l.WHITESPACE_CELL_CODE]),this._isClearing=!1,this._memoryCleanupQueue=new f.IdleTaskQueue,this._memoryCleanupPosition=0,this._cols=this._bufferService.cols,this._rows=this._bufferService.rows,this.lines=new c.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}getNullCell(e){return e?(this._nullCell.fg=e.fg,this._nullCell.bg=e.bg,this._nullCell.extended=e.extended):(this._nullCell.fg=0,this._nullCell.bg=0,this._nullCell.extended=new n.ExtendedAttrs),this._nullCell}getWhitespaceCell(e){return e?(this._whitespaceCell.fg=e.fg,this._whitespaceCell.bg=e.bg,this._whitespaceCell.extended=e.extended):(this._whitespaceCell.fg=0,this._whitespaceCell.bg=0,this._whitespaceCell.extended=new n.ExtendedAttrs),this._whitespaceCell}getBlankLine(e,t){return new d.BufferLine(this._bufferService.cols,this.getNullCell(e),t)}get hasScrollback(){return this._hasScrollback&&this.lines.maxLength>this._rows}get isCursorInViewport(){const e=this.ybase+this.y-this.ydisp;return e>=0&&er.MAX_BUFFER_SIZE?r.MAX_BUFFER_SIZE:t}fillViewportRows(e){if(this.lines.length===0){e===void 0&&(e=d.DEFAULT_ATTR_DATA);let t=this._rows;for(;t--;)this.lines.push(this.getBlankLine(e))}}clear(){this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.lines=new c.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}resize(e,t){const a=this.getNullCell(d.DEFAULT_ATTR_DATA);let _=0;const u=this._getCorrectBufferLength(t);if(u>this.lines.maxLength&&(this.lines.maxLength=u),this.lines.length>0){if(this._cols0&&this.lines.length<=this.ybase+this.y+g+1?(this.ybase--,g++,this.ydisp>0&&this.ydisp--):this.lines.push(new d.BufferLine(e,a)));else for(let h=this._rows;h>t;h--)this.lines.length>t+this.ybase&&(this.lines.length>this.ybase+this.y+1?this.lines.pop():(this.ybase++,this.ydisp++));if(u0&&(this.lines.trimStart(h),this.ybase=Math.max(this.ybase-h,0),this.ydisp=Math.max(this.ydisp-h,0),this.savedY=Math.max(this.savedY-h,0)),this.lines.maxLength=u}this.x=Math.min(this.x,e-1),this.y=Math.min(this.y,t-1),g&&(this.y+=g),this.savedX=Math.min(this.savedX,e-1),this.scrollTop=0}if(this.scrollBottom=t-1,this._isReflowEnabled&&(this._reflow(e,t),this._cols>e))for(let g=0;g.1*this.lines.length&&(this._memoryCleanupPosition=0,this._memoryCleanupQueue.enqueue(()=>this._batchedMemoryCleanup()))}_batchedMemoryCleanup(){let e=!0;this._memoryCleanupPosition>=this.lines.length&&(this._memoryCleanupPosition=0,e=!1);let t=0;for(;this._memoryCleanupPosition100)return!0;return e}get _isReflowEnabled(){const e=this._optionsService.rawOptions.windowsPty;return e&&e.buildNumber?this._hasScrollback&&e.backend==="conpty"&&e.buildNumber>=21376:this._hasScrollback&&!this._optionsService.rawOptions.windowsMode}_reflow(e,t){this._cols!==e&&(e>this._cols?this._reflowLarger(e,t):this._reflowSmaller(e,t))}_reflowLarger(e,t){const a=(0,v.reflowLargerGetLinesToRemove)(this.lines,this._cols,e,this.ybase+this.y,this.getNullCell(d.DEFAULT_ATTR_DATA));if(a.length>0){const _=(0,v.reflowLargerCreateNewLayout)(this.lines,a);(0,v.reflowLargerApplyNewLayout)(this.lines,_.layout),this._reflowLargerAdjustViewport(e,t,_.countRemoved)}}_reflowLargerAdjustViewport(e,t,a){const _=this.getNullCell(d.DEFAULT_ATTR_DATA);let u=a;for(;u-- >0;)this.ybase===0?(this.y>0&&this.y--,this.lines.length=0;g--){let h=this.lines.get(g);if(!h||!h.isWrapped&&h.getTrimmedLength()<=e)continue;const m=[h];for(;h.isWrapped&&g>0;)h=this.lines.get(--g),m.unshift(h);const y=this.ybase+this.y;if(y>=g&&y0&&(_.push({start:g+m.length+u,newLines:A}),u+=A.length),m.push(...A);let H=D.length-1,U=D[H];U===0&&(H--,U=D[H]);let F=m.length-b-1,S=k;for(;F>=0;){const E=Math.min(S,U);if(m[H]===void 0)break;if(m[H].copyCellsFrom(m[F],S-E,U-E,E,!0),U-=E,U===0&&(H--,U=D[H]),S-=E,S===0){F--;const L=Math.max(F,0);S=(0,v.getWrappedLineTrimmedLength)(m,L,this._cols)}}for(let E=0;E0;)this.ybase===0?this.y0){const g=[],h=[];for(let H=0;H=0;H--)if(D&&D.start>y+b){for(let U=D.newLines.length-1;U>=0;U--)this.lines.set(H--,D.newLines[U]);H++,g.push({index:y+1,amount:D.newLines.length}),b+=D.newLines.length,D=_[++k]}else this.lines.set(H,h[y--]);let B=0;for(let H=g.length-1;H>=0;H--)g[H].index+=B,this.lines.onInsertEmitter.fire(g[H]),B+=g[H].amount;const A=Math.max(0,m+u-this.lines.maxLength);A>0&&this.lines.onTrimEmitter.fire(A)}}translateBufferLineToString(e,t,a=0,_){const u=this.lines.get(e);return u?u.translateToString(t,a,_):""}getWrappedRangeForLine(e){let t=e,a=e;for(;t>0&&this.lines.get(t).isWrapped;)t--;for(;a+10;);return e>=this._cols?this._cols-1:e<0?0:e}nextStop(e){for(e==null&&(e=this.x);!this.tabs[++e]&&e=this._cols?this._cols-1:e<0?0:e}clearMarkers(e){this._isClearing=!0;for(let t=0;t{t.line-=a,t.line<0&&t.dispose()})),t.register(this.lines.onInsert(a=>{t.line>=a.index&&(t.line+=a.amount)})),t.register(this.lines.onDelete(a=>{t.line>=a.index&&t.linea.index&&(t.line-=a.amount)})),t.register(t.onDispose(()=>this._removeMarker(t))),t}_removeMarker(e){this._isClearing||this.markers.splice(this.markers.indexOf(e),1)}}},8437:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.BufferLine=r.DEFAULT_ATTR_DATA=void 0;const c=o(3734),f=o(511),n=o(643),d=o(482);r.DEFAULT_ATTR_DATA=Object.freeze(new c.AttributeData);let v=0;class p{constructor(i,s,e=!1){this.isWrapped=e,this._combined={},this._extendedAttrs={},this._data=new Uint32Array(3*i);const t=s||f.CellData.fromCharData([0,n.NULL_CELL_CHAR,n.NULL_CELL_WIDTH,n.NULL_CELL_CODE]);for(let a=0;a>22,2097152&s?this._combined[i].charCodeAt(this._combined[i].length-1):e]}set(i,s){this._data[3*i+1]=s[n.CHAR_DATA_ATTR_INDEX],s[n.CHAR_DATA_CHAR_INDEX].length>1?(this._combined[i]=s[1],this._data[3*i+0]=2097152|i|s[n.CHAR_DATA_WIDTH_INDEX]<<22):this._data[3*i+0]=s[n.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|s[n.CHAR_DATA_WIDTH_INDEX]<<22}getWidth(i){return this._data[3*i+0]>>22}hasWidth(i){return 12582912&this._data[3*i+0]}getFg(i){return this._data[3*i+1]}getBg(i){return this._data[3*i+2]}hasContent(i){return 4194303&this._data[3*i+0]}getCodePoint(i){const s=this._data[3*i+0];return 2097152&s?this._combined[i].charCodeAt(this._combined[i].length-1):2097151&s}isCombined(i){return 2097152&this._data[3*i+0]}getString(i){const s=this._data[3*i+0];return 2097152&s?this._combined[i]:2097151&s?(0,d.stringFromCodePoint)(2097151&s):""}isProtected(i){return 536870912&this._data[3*i+2]}loadCell(i,s){return v=3*i,s.content=this._data[v+0],s.fg=this._data[v+1],s.bg=this._data[v+2],2097152&s.content&&(s.combinedData=this._combined[i]),268435456&s.bg&&(s.extended=this._extendedAttrs[i]),s}setCell(i,s){2097152&s.content&&(this._combined[i]=s.combinedData),268435456&s.bg&&(this._extendedAttrs[i]=s.extended),this._data[3*i+0]=s.content,this._data[3*i+1]=s.fg,this._data[3*i+2]=s.bg}setCellFromCodepoint(i,s,e,t){268435456&t.bg&&(this._extendedAttrs[i]=t.extended),this._data[3*i+0]=s|e<<22,this._data[3*i+1]=t.fg,this._data[3*i+2]=t.bg}addCodepointToCell(i,s,e){let t=this._data[3*i+0];2097152&t?this._combined[i]+=(0,d.stringFromCodePoint)(s):2097151&t?(this._combined[i]=(0,d.stringFromCodePoint)(2097151&t)+(0,d.stringFromCodePoint)(s),t&=-2097152,t|=2097152):t=s|4194304,e&&(t&=-12582913,t|=e<<22),this._data[3*i+0]=t}insertCells(i,s,e){if((i%=this.length)&&this.getWidth(i-1)===2&&this.setCellFromCodepoint(i-1,0,1,e),s=0;--a)this.setCell(i+s+a,this.loadCell(i+a,t));for(let a=0;athis.length){if(this._data.buffer.byteLength>=4*e)this._data=new Uint32Array(this._data.buffer,0,e);else{const t=new Uint32Array(e);t.set(this._data),this._data=t}for(let t=this.length;t=i&&delete this._combined[u]}const a=Object.keys(this._extendedAttrs);for(let _=0;_=i&&delete this._extendedAttrs[u]}}return this.length=i,4*e*2=0;--i)if(4194303&this._data[3*i+0])return i+(this._data[3*i+0]>>22);return 0}getNoBgTrimmedLength(){for(let i=this.length-1;i>=0;--i)if(4194303&this._data[3*i+0]||50331648&this._data[3*i+2])return i+(this._data[3*i+0]>>22);return 0}copyCellsFrom(i,s,e,t,a){const _=i._data;if(a)for(let g=t-1;g>=0;g--){for(let h=0;h<3;h++)this._data[3*(e+g)+h]=_[3*(s+g)+h];268435456&_[3*(s+g)+2]&&(this._extendedAttrs[e+g]=i._extendedAttrs[s+g])}else for(let g=0;g=s&&(this._combined[h-s+e]=i._combined[h])}}translateToString(i,s,e,t){s=s??0,e=e??this.length,i&&(e=Math.min(e,this.getTrimmedLength())),t&&(t.length=0);let a="";for(;s>22||1}return t&&t.push(s),a}}r.BufferLine=p},4841:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.getRangeLength=void 0,r.getRangeLength=function(o,c){if(o.start.y>o.end.y)throw new Error(`Buffer range end (${o.end.x}, ${o.end.y}) cannot be before start (${o.start.x}, ${o.start.y})`);return c*(o.end.y-o.start.y)+(o.end.x-o.start.x+1)}},4634:(R,r)=>{function o(c,f,n){if(f===c.length-1)return c[f].getTrimmedLength();const d=!c[f].hasContent(n-1)&&c[f].getWidth(n-1)===1,v=c[f+1].getWidth(0)===2;return d&&v?n-1:n}Object.defineProperty(r,"__esModule",{value:!0}),r.getWrappedLineTrimmedLength=r.reflowSmallerGetNewLineLengths=r.reflowLargerApplyNewLayout=r.reflowLargerCreateNewLayout=r.reflowLargerGetLinesToRemove=void 0,r.reflowLargerGetLinesToRemove=function(c,f,n,d,v){const p=[];for(let l=0;l=l&&d0&&(h>t||e[h].getTrimmedLength()===0);h--)g++;g>0&&(p.push(l+e.length-g),p.push(g)),l+=e.length-1}return p},r.reflowLargerCreateNewLayout=function(c,f){const n=[];let d=0,v=f[d],p=0;for(let l=0;lo(c,e,f)).reduce((s,e)=>s+e);let p=0,l=0,i=0;for(;is&&(p-=s,l++);const e=c[l].getWidth(p-1)===2;e&&p--;const t=e?n-1:n;d.push(t),i+=t}return d},r.getWrappedLineTrimmedLength=o},5295:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.BufferSet=void 0;const c=o(8460),f=o(844),n=o(9092);class d extends f.Disposable{constructor(p,l){super(),this._optionsService=p,this._bufferService=l,this._onBufferActivate=this.register(new c.EventEmitter),this.onBufferActivate=this._onBufferActivate.event,this.reset(),this.register(this._optionsService.onSpecificOptionChange("scrollback",()=>this.resize(this._bufferService.cols,this._bufferService.rows))),this.register(this._optionsService.onSpecificOptionChange("tabStopWidth",()=>this.setupTabStops()))}reset(){this._normal=new n.Buffer(!0,this._optionsService,this._bufferService),this._normal.fillViewportRows(),this._alt=new n.Buffer(!1,this._optionsService,this._bufferService),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}),this.setupTabStops()}get alt(){return this._alt}get active(){return this._activeBuffer}get normal(){return this._normal}activateNormalBuffer(){this._activeBuffer!==this._normal&&(this._normal.x=this._alt.x,this._normal.y=this._alt.y,this._alt.clearAllMarkers(),this._alt.clear(),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}))}activateAltBuffer(p){this._activeBuffer!==this._alt&&(this._alt.fillViewportRows(p),this._alt.x=this._normal.x,this._alt.y=this._normal.y,this._activeBuffer=this._alt,this._onBufferActivate.fire({activeBuffer:this._alt,inactiveBuffer:this._normal}))}resize(p,l){this._normal.resize(p,l),this._alt.resize(p,l),this.setupTabStops(p)}setupTabStops(p){this._normal.setupTabStops(p),this._alt.setupTabStops(p)}}r.BufferSet=d},511:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.CellData=void 0;const c=o(482),f=o(643),n=o(3734);class d extends n.AttributeData{constructor(){super(...arguments),this.content=0,this.fg=0,this.bg=0,this.extended=new n.ExtendedAttrs,this.combinedData=""}static fromCharData(p){const l=new d;return l.setFromCharData(p),l}isCombined(){return 2097152&this.content}getWidth(){return this.content>>22}getChars(){return 2097152&this.content?this.combinedData:2097151&this.content?(0,c.stringFromCodePoint)(2097151&this.content):""}getCode(){return this.isCombined()?this.combinedData.charCodeAt(this.combinedData.length-1):2097151&this.content}setFromCharData(p){this.fg=p[f.CHAR_DATA_ATTR_INDEX],this.bg=0;let l=!1;if(p[f.CHAR_DATA_CHAR_INDEX].length>2)l=!0;else if(p[f.CHAR_DATA_CHAR_INDEX].length===2){const i=p[f.CHAR_DATA_CHAR_INDEX].charCodeAt(0);if(55296<=i&&i<=56319){const s=p[f.CHAR_DATA_CHAR_INDEX].charCodeAt(1);56320<=s&&s<=57343?this.content=1024*(i-55296)+s-56320+65536|p[f.CHAR_DATA_WIDTH_INDEX]<<22:l=!0}else l=!0}else this.content=p[f.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|p[f.CHAR_DATA_WIDTH_INDEX]<<22;l&&(this.combinedData=p[f.CHAR_DATA_CHAR_INDEX],this.content=2097152|p[f.CHAR_DATA_WIDTH_INDEX]<<22)}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}}r.CellData=d},643:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.WHITESPACE_CELL_CODE=r.WHITESPACE_CELL_WIDTH=r.WHITESPACE_CELL_CHAR=r.NULL_CELL_CODE=r.NULL_CELL_WIDTH=r.NULL_CELL_CHAR=r.CHAR_DATA_CODE_INDEX=r.CHAR_DATA_WIDTH_INDEX=r.CHAR_DATA_CHAR_INDEX=r.CHAR_DATA_ATTR_INDEX=r.DEFAULT_EXT=r.DEFAULT_ATTR=r.DEFAULT_COLOR=void 0,r.DEFAULT_COLOR=0,r.DEFAULT_ATTR=256|r.DEFAULT_COLOR<<9,r.DEFAULT_EXT=0,r.CHAR_DATA_ATTR_INDEX=0,r.CHAR_DATA_CHAR_INDEX=1,r.CHAR_DATA_WIDTH_INDEX=2,r.CHAR_DATA_CODE_INDEX=3,r.NULL_CELL_CHAR="",r.NULL_CELL_WIDTH=1,r.NULL_CELL_CODE=0,r.WHITESPACE_CELL_CHAR=" ",r.WHITESPACE_CELL_WIDTH=1,r.WHITESPACE_CELL_CODE=32},4863:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.Marker=void 0;const c=o(8460),f=o(844);class n{get id(){return this._id}constructor(v){this.line=v,this.isDisposed=!1,this._disposables=[],this._id=n._nextId++,this._onDispose=this.register(new c.EventEmitter),this.onDispose=this._onDispose.event}dispose(){this.isDisposed||(this.isDisposed=!0,this.line=-1,this._onDispose.fire(),(0,f.disposeArray)(this._disposables),this._disposables.length=0)}register(v){return this._disposables.push(v),v}}r.Marker=n,n._nextId=1},7116:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.DEFAULT_CHARSET=r.CHARSETS=void 0,r.CHARSETS={},r.DEFAULT_CHARSET=r.CHARSETS.B,r.CHARSETS[0]={"`":"\u25C6",a:"\u2592",b:"\u2409",c:"\u240C",d:"\u240D",e:"\u240A",f:"\xB0",g:"\xB1",h:"\u2424",i:"\u240B",j:"\u2518",k:"\u2510",l:"\u250C",m:"\u2514",n:"\u253C",o:"\u23BA",p:"\u23BB",q:"\u2500",r:"\u23BC",s:"\u23BD",t:"\u251C",u:"\u2524",v:"\u2534",w:"\u252C",x:"\u2502",y:"\u2264",z:"\u2265","{":"\u03C0","|":"\u2260","}":"\xA3","~":"\xB7"},r.CHARSETS.A={"#":"\xA3"},r.CHARSETS.B=void 0,r.CHARSETS[4]={"#":"\xA3","@":"\xBE","[":"ij","\\":"\xBD","]":"|","{":"\xA8","|":"f","}":"\xBC","~":"\xB4"},r.CHARSETS.C=r.CHARSETS[5]={"[":"\xC4","\\":"\xD6","]":"\xC5","^":"\xDC","`":"\xE9","{":"\xE4","|":"\xF6","}":"\xE5","~":"\xFC"},r.CHARSETS.R={"#":"\xA3","@":"\xE0","[":"\xB0","\\":"\xE7","]":"\xA7","{":"\xE9","|":"\xF9","}":"\xE8","~":"\xA8"},r.CHARSETS.Q={"@":"\xE0","[":"\xE2","\\":"\xE7","]":"\xEA","^":"\xEE","`":"\xF4","{":"\xE9","|":"\xF9","}":"\xE8","~":"\xFB"},r.CHARSETS.K={"@":"\xA7","[":"\xC4","\\":"\xD6","]":"\xDC","{":"\xE4","|":"\xF6","}":"\xFC","~":"\xDF"},r.CHARSETS.Y={"#":"\xA3","@":"\xA7","[":"\xB0","\\":"\xE7","]":"\xE9","`":"\xF9","{":"\xE0","|":"\xF2","}":"\xE8","~":"\xEC"},r.CHARSETS.E=r.CHARSETS[6]={"@":"\xC4","[":"\xC6","\\":"\xD8","]":"\xC5","^":"\xDC","`":"\xE4","{":"\xE6","|":"\xF8","}":"\xE5","~":"\xFC"},r.CHARSETS.Z={"#":"\xA3","@":"\xA7","[":"\xA1","\\":"\xD1","]":"\xBF","{":"\xB0","|":"\xF1","}":"\xE7"},r.CHARSETS.H=r.CHARSETS[7]={"@":"\xC9","[":"\xC4","\\":"\xD6","]":"\xC5","^":"\xDC","`":"\xE9","{":"\xE4","|":"\xF6","}":"\xE5","~":"\xFC"},r.CHARSETS["="]={"#":"\xF9","@":"\xE0","[":"\xE9","\\":"\xE7","]":"\xEA","^":"\xEE",_:"\xE8","`":"\xF4","{":"\xE4","|":"\xF6","}":"\xFC","~":"\xFB"}},2584:(R,r)=>{var o,c,f;Object.defineProperty(r,"__esModule",{value:!0}),r.C1_ESCAPED=r.C1=r.C0=void 0,function(n){n.NUL="\0",n.SOH="",n.STX="",n.ETX="",n.EOT="",n.ENQ="",n.ACK="",n.BEL="\x07",n.BS="\b",n.HT=" ",n.LF=` -`,n.VT="\v",n.FF="\f",n.CR="\r",n.SO="",n.SI="",n.DLE="",n.DC1="",n.DC2="",n.DC3="",n.DC4="",n.NAK="",n.SYN="",n.ETB="",n.CAN="",n.EM="",n.SUB="",n.ESC="\x1B",n.FS="",n.GS="",n.RS="",n.US="",n.SP=" ",n.DEL="\x7F"}(o||(r.C0=o={})),function(n){n.PAD="\x80",n.HOP="\x81",n.BPH="\x82",n.NBH="\x83",n.IND="\x84",n.NEL="\x85",n.SSA="\x86",n.ESA="\x87",n.HTS="\x88",n.HTJ="\x89",n.VTS="\x8A",n.PLD="\x8B",n.PLU="\x8C",n.RI="\x8D",n.SS2="\x8E",n.SS3="\x8F",n.DCS="\x90",n.PU1="\x91",n.PU2="\x92",n.STS="\x93",n.CCH="\x94",n.MW="\x95",n.SPA="\x96",n.EPA="\x97",n.SOS="\x98",n.SGCI="\x99",n.SCI="\x9A",n.CSI="\x9B",n.ST="\x9C",n.OSC="\x9D",n.PM="\x9E",n.APC="\x9F"}(c||(r.C1=c={})),function(n){n.ST=`${o.ESC}\\`}(f||(r.C1_ESCAPED=f={}))},7399:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.evaluateKeyboardEvent=void 0;const c=o(2584),f={48:["0",")"],49:["1","!"],50:["2","@"],51:["3","#"],52:["4","$"],53:["5","%"],54:["6","^"],55:["7","&"],56:["8","*"],57:["9","("],186:[";",":"],187:["=","+"],188:[",","<"],189:["-","_"],190:[".",">"],191:["/","?"],192:["`","~"],219:["[","{"],220:["\\","|"],221:["]","}"],222:["'",'"']};r.evaluateKeyboardEvent=function(n,d,v,p){const l={type:0,cancel:!1,key:void 0},i=(n.shiftKey?1:0)|(n.altKey?2:0)|(n.ctrlKey?4:0)|(n.metaKey?8:0);switch(n.keyCode){case 0:n.key==="UIKeyInputUpArrow"?l.key=d?c.C0.ESC+"OA":c.C0.ESC+"[A":n.key==="UIKeyInputLeftArrow"?l.key=d?c.C0.ESC+"OD":c.C0.ESC+"[D":n.key==="UIKeyInputRightArrow"?l.key=d?c.C0.ESC+"OC":c.C0.ESC+"[C":n.key==="UIKeyInputDownArrow"&&(l.key=d?c.C0.ESC+"OB":c.C0.ESC+"[B");break;case 8:l.key=n.ctrlKey?"\b":c.C0.DEL,n.altKey&&(l.key=c.C0.ESC+l.key);break;case 9:if(n.shiftKey){l.key=c.C0.ESC+"[Z";break}l.key=c.C0.HT,l.cancel=!0;break;case 13:l.key=n.altKey?c.C0.ESC+c.C0.CR:c.C0.CR,l.cancel=!0;break;case 27:l.key=c.C0.ESC,n.altKey&&(l.key=c.C0.ESC+c.C0.ESC),l.cancel=!0;break;case 37:if(n.metaKey)break;i?(l.key=c.C0.ESC+"[1;"+(i+1)+"D",l.key===c.C0.ESC+"[1;3D"&&(l.key=c.C0.ESC+(v?"b":"[1;5D"))):l.key=d?c.C0.ESC+"OD":c.C0.ESC+"[D";break;case 39:if(n.metaKey)break;i?(l.key=c.C0.ESC+"[1;"+(i+1)+"C",l.key===c.C0.ESC+"[1;3C"&&(l.key=c.C0.ESC+(v?"f":"[1;5C"))):l.key=d?c.C0.ESC+"OC":c.C0.ESC+"[C";break;case 38:if(n.metaKey)break;i?(l.key=c.C0.ESC+"[1;"+(i+1)+"A",v||l.key!==c.C0.ESC+"[1;3A"||(l.key=c.C0.ESC+"[1;5A")):l.key=d?c.C0.ESC+"OA":c.C0.ESC+"[A";break;case 40:if(n.metaKey)break;i?(l.key=c.C0.ESC+"[1;"+(i+1)+"B",v||l.key!==c.C0.ESC+"[1;3B"||(l.key=c.C0.ESC+"[1;5B")):l.key=d?c.C0.ESC+"OB":c.C0.ESC+"[B";break;case 45:n.shiftKey||n.ctrlKey||(l.key=c.C0.ESC+"[2~");break;case 46:l.key=i?c.C0.ESC+"[3;"+(i+1)+"~":c.C0.ESC+"[3~";break;case 36:l.key=i?c.C0.ESC+"[1;"+(i+1)+"H":d?c.C0.ESC+"OH":c.C0.ESC+"[H";break;case 35:l.key=i?c.C0.ESC+"[1;"+(i+1)+"F":d?c.C0.ESC+"OF":c.C0.ESC+"[F";break;case 33:n.shiftKey?l.type=2:n.ctrlKey?l.key=c.C0.ESC+"[5;"+(i+1)+"~":l.key=c.C0.ESC+"[5~";break;case 34:n.shiftKey?l.type=3:n.ctrlKey?l.key=c.C0.ESC+"[6;"+(i+1)+"~":l.key=c.C0.ESC+"[6~";break;case 112:l.key=i?c.C0.ESC+"[1;"+(i+1)+"P":c.C0.ESC+"OP";break;case 113:l.key=i?c.C0.ESC+"[1;"+(i+1)+"Q":c.C0.ESC+"OQ";break;case 114:l.key=i?c.C0.ESC+"[1;"+(i+1)+"R":c.C0.ESC+"OR";break;case 115:l.key=i?c.C0.ESC+"[1;"+(i+1)+"S":c.C0.ESC+"OS";break;case 116:l.key=i?c.C0.ESC+"[15;"+(i+1)+"~":c.C0.ESC+"[15~";break;case 117:l.key=i?c.C0.ESC+"[17;"+(i+1)+"~":c.C0.ESC+"[17~";break;case 118:l.key=i?c.C0.ESC+"[18;"+(i+1)+"~":c.C0.ESC+"[18~";break;case 119:l.key=i?c.C0.ESC+"[19;"+(i+1)+"~":c.C0.ESC+"[19~";break;case 120:l.key=i?c.C0.ESC+"[20;"+(i+1)+"~":c.C0.ESC+"[20~";break;case 121:l.key=i?c.C0.ESC+"[21;"+(i+1)+"~":c.C0.ESC+"[21~";break;case 122:l.key=i?c.C0.ESC+"[23;"+(i+1)+"~":c.C0.ESC+"[23~";break;case 123:l.key=i?c.C0.ESC+"[24;"+(i+1)+"~":c.C0.ESC+"[24~";break;default:if(!n.ctrlKey||n.shiftKey||n.altKey||n.metaKey)if(v&&!p||!n.altKey||n.metaKey)!v||n.altKey||n.ctrlKey||n.shiftKey||!n.metaKey?n.key&&!n.ctrlKey&&!n.altKey&&!n.metaKey&&n.keyCode>=48&&n.key.length===1?l.key=n.key:n.key&&n.ctrlKey&&(n.key==="_"&&(l.key=c.C0.US),n.key==="@"&&(l.key=c.C0.NUL)):n.keyCode===65&&(l.type=1);else{const s=f[n.keyCode],e=s?.[n.shiftKey?1:0];if(e)l.key=c.C0.ESC+e;else if(n.keyCode>=65&&n.keyCode<=90){const t=n.ctrlKey?n.keyCode-64:n.keyCode+32;let a=String.fromCharCode(t);n.shiftKey&&(a=a.toUpperCase()),l.key=c.C0.ESC+a}else if(n.keyCode===32)l.key=c.C0.ESC+(n.ctrlKey?c.C0.NUL:" ");else if(n.key==="Dead"&&n.code.startsWith("Key")){let t=n.code.slice(3,4);n.shiftKey||(t=t.toLowerCase()),l.key=c.C0.ESC+t,l.cancel=!0}}else n.keyCode>=65&&n.keyCode<=90?l.key=String.fromCharCode(n.keyCode-64):n.keyCode===32?l.key=c.C0.NUL:n.keyCode>=51&&n.keyCode<=55?l.key=String.fromCharCode(n.keyCode-51+27):n.keyCode===56?l.key=c.C0.DEL:n.keyCode===219?l.key=c.C0.ESC:n.keyCode===220?l.key=c.C0.FS:n.keyCode===221&&(l.key=c.C0.GS)}return l}},482:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.Utf8ToUtf32=r.StringToUtf32=r.utf32ToString=r.stringFromCodePoint=void 0,r.stringFromCodePoint=function(o){return o>65535?(o-=65536,String.fromCharCode(55296+(o>>10))+String.fromCharCode(o%1024+56320)):String.fromCharCode(o)},r.utf32ToString=function(o,c=0,f=o.length){let n="";for(let d=c;d65535?(v-=65536,n+=String.fromCharCode(55296+(v>>10))+String.fromCharCode(v%1024+56320)):n+=String.fromCharCode(v)}return n},r.StringToUtf32=class{constructor(){this._interim=0}clear(){this._interim=0}decode(o,c){const f=o.length;if(!f)return 0;let n=0,d=0;if(this._interim){const v=o.charCodeAt(d++);56320<=v&&v<=57343?c[n++]=1024*(this._interim-55296)+v-56320+65536:(c[n++]=this._interim,c[n++]=v),this._interim=0}for(let v=d;v=f)return this._interim=p,n;const l=o.charCodeAt(v);56320<=l&&l<=57343?c[n++]=1024*(p-55296)+l-56320+65536:(c[n++]=p,c[n++]=l)}else p!==65279&&(c[n++]=p)}return n}},r.Utf8ToUtf32=class{constructor(){this.interim=new Uint8Array(3)}clear(){this.interim.fill(0)}decode(o,c){const f=o.length;if(!f)return 0;let n,d,v,p,l=0,i=0,s=0;if(this.interim[0]){let a=!1,_=this.interim[0];_&=(224&_)==192?31:(240&_)==224?15:7;let u,g=0;for(;(u=63&this.interim[++g])&&g<4;)_<<=6,_|=u;const h=(224&this.interim[0])==192?2:(240&this.interim[0])==224?3:4,m=h-g;for(;s=f)return 0;if(u=o[s++],(192&u)!=128){s--,a=!0;break}this.interim[g++]=u,_<<=6,_|=63&u}a||(h===2?_<128?s--:c[l++]=_:h===3?_<2048||_>=55296&&_<=57343||_===65279||(c[l++]=_):_<65536||_>1114111||(c[l++]=_)),this.interim.fill(0)}const e=f-4;let t=s;for(;t=f)return this.interim[0]=n,l;if(d=o[t++],(192&d)!=128){t--;continue}if(i=(31&n)<<6|63&d,i<128){t--;continue}c[l++]=i}else if((240&n)==224){if(t>=f)return this.interim[0]=n,l;if(d=o[t++],(192&d)!=128){t--;continue}if(t>=f)return this.interim[0]=n,this.interim[1]=d,l;if(v=o[t++],(192&v)!=128){t--;continue}if(i=(15&n)<<12|(63&d)<<6|63&v,i<2048||i>=55296&&i<=57343||i===65279)continue;c[l++]=i}else if((248&n)==240){if(t>=f)return this.interim[0]=n,l;if(d=o[t++],(192&d)!=128){t--;continue}if(t>=f)return this.interim[0]=n,this.interim[1]=d,l;if(v=o[t++],(192&v)!=128){t--;continue}if(t>=f)return this.interim[0]=n,this.interim[1]=d,this.interim[2]=v,l;if(p=o[t++],(192&p)!=128){t--;continue}if(i=(7&n)<<18|(63&d)<<12|(63&v)<<6|63&p,i<65536||i>1114111)continue;c[l++]=i}}return l}}},225:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.UnicodeV6=void 0;const c=o(1480),f=[[768,879],[1155,1158],[1160,1161],[1425,1469],[1471,1471],[1473,1474],[1476,1477],[1479,1479],[1536,1539],[1552,1557],[1611,1630],[1648,1648],[1750,1764],[1767,1768],[1770,1773],[1807,1807],[1809,1809],[1840,1866],[1958,1968],[2027,2035],[2305,2306],[2364,2364],[2369,2376],[2381,2381],[2385,2388],[2402,2403],[2433,2433],[2492,2492],[2497,2500],[2509,2509],[2530,2531],[2561,2562],[2620,2620],[2625,2626],[2631,2632],[2635,2637],[2672,2673],[2689,2690],[2748,2748],[2753,2757],[2759,2760],[2765,2765],[2786,2787],[2817,2817],[2876,2876],[2879,2879],[2881,2883],[2893,2893],[2902,2902],[2946,2946],[3008,3008],[3021,3021],[3134,3136],[3142,3144],[3146,3149],[3157,3158],[3260,3260],[3263,3263],[3270,3270],[3276,3277],[3298,3299],[3393,3395],[3405,3405],[3530,3530],[3538,3540],[3542,3542],[3633,3633],[3636,3642],[3655,3662],[3761,3761],[3764,3769],[3771,3772],[3784,3789],[3864,3865],[3893,3893],[3895,3895],[3897,3897],[3953,3966],[3968,3972],[3974,3975],[3984,3991],[3993,4028],[4038,4038],[4141,4144],[4146,4146],[4150,4151],[4153,4153],[4184,4185],[4448,4607],[4959,4959],[5906,5908],[5938,5940],[5970,5971],[6002,6003],[6068,6069],[6071,6077],[6086,6086],[6089,6099],[6109,6109],[6155,6157],[6313,6313],[6432,6434],[6439,6440],[6450,6450],[6457,6459],[6679,6680],[6912,6915],[6964,6964],[6966,6970],[6972,6972],[6978,6978],[7019,7027],[7616,7626],[7678,7679],[8203,8207],[8234,8238],[8288,8291],[8298,8303],[8400,8431],[12330,12335],[12441,12442],[43014,43014],[43019,43019],[43045,43046],[64286,64286],[65024,65039],[65056,65059],[65279,65279],[65529,65531]],n=[[68097,68099],[68101,68102],[68108,68111],[68152,68154],[68159,68159],[119143,119145],[119155,119170],[119173,119179],[119210,119213],[119362,119364],[917505,917505],[917536,917631],[917760,917999]];let d;r.UnicodeV6=class{constructor(){if(this.version="6",!d){d=new Uint8Array(65536),d.fill(1),d[0]=0,d.fill(0,1,32),d.fill(0,127,160),d.fill(2,4352,4448),d[9001]=2,d[9002]=2,d.fill(2,11904,42192),d[12351]=1,d.fill(2,44032,55204),d.fill(2,63744,64256),d.fill(2,65040,65050),d.fill(2,65072,65136),d.fill(2,65280,65377),d.fill(2,65504,65511);for(let v=0;vl[e][1])return!1;for(;e>=s;)if(i=s+e>>1,p>l[i][1])s=i+1;else{if(!(p=131072&&v<=196605||v>=196608&&v<=262141?2:1}charProperties(v,p){let l=this.wcwidth(v),i=l===0&&p!==0;if(i){const s=c.UnicodeService.extractWidth(p);s===0?i=!1:s>l&&(l=s)}return c.UnicodeService.createPropertyValue(0,l,i)}}},5981:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.WriteBuffer=void 0;const c=o(8460),f=o(844);class n extends f.Disposable{constructor(v){super(),this._action=v,this._writeBuffer=[],this._callbacks=[],this._pendingData=0,this._bufferOffset=0,this._isSyncWriting=!1,this._syncCalls=0,this._didUserInput=!1,this._onWriteParsed=this.register(new c.EventEmitter),this.onWriteParsed=this._onWriteParsed.event}handleUserInput(){this._didUserInput=!0}writeSync(v,p){if(p!==void 0&&this._syncCalls>p)return void(this._syncCalls=0);if(this._pendingData+=v.length,this._writeBuffer.push(v),this._callbacks.push(void 0),this._syncCalls++,this._isSyncWriting)return;let l;for(this._isSyncWriting=!0;l=this._writeBuffer.shift();){this._action(l);const i=this._callbacks.shift();i&&i()}this._pendingData=0,this._bufferOffset=2147483647,this._isSyncWriting=!1,this._syncCalls=0}write(v,p){if(this._pendingData>5e7)throw new Error("write data discarded, use flow control to avoid losing data");if(!this._writeBuffer.length){if(this._bufferOffset=0,this._didUserInput)return this._didUserInput=!1,this._pendingData+=v.length,this._writeBuffer.push(v),this._callbacks.push(p),void this._innerWrite();setTimeout(()=>this._innerWrite())}this._pendingData+=v.length,this._writeBuffer.push(v),this._callbacks.push(p)}_innerWrite(v=0,p=!0){const l=v||Date.now();for(;this._writeBuffer.length>this._bufferOffset;){const i=this._writeBuffer[this._bufferOffset],s=this._action(i,p);if(s){const t=a=>Date.now()-l>=12?setTimeout(()=>this._innerWrite(0,a)):this._innerWrite(l,a);return void s.catch(a=>(queueMicrotask(()=>{throw a}),Promise.resolve(!1))).then(t)}const e=this._callbacks[this._bufferOffset];if(e&&e(),this._bufferOffset++,this._pendingData-=i.length,Date.now()-l>=12)break}this._writeBuffer.length>this._bufferOffset?(this._bufferOffset>50&&(this._writeBuffer=this._writeBuffer.slice(this._bufferOffset),this._callbacks=this._callbacks.slice(this._bufferOffset),this._bufferOffset=0),setTimeout(()=>this._innerWrite())):(this._writeBuffer.length=0,this._callbacks.length=0,this._pendingData=0,this._bufferOffset=0),this._onWriteParsed.fire()}}r.WriteBuffer=n},5941:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.toRgbString=r.parseColor=void 0;const o=/^([\da-f])\/([\da-f])\/([\da-f])$|^([\da-f]{2})\/([\da-f]{2})\/([\da-f]{2})$|^([\da-f]{3})\/([\da-f]{3})\/([\da-f]{3})$|^([\da-f]{4})\/([\da-f]{4})\/([\da-f]{4})$/,c=/^[\da-f]+$/;function f(n,d){const v=n.toString(16),p=v.length<2?"0"+v:v;switch(d){case 4:return v[0];case 8:return p;case 12:return(p+p).slice(0,3);default:return p+p}}r.parseColor=function(n){if(!n)return;let d=n.toLowerCase();if(d.indexOf("rgb:")===0){d=d.slice(4);const v=o.exec(d);if(v){const p=v[1]?15:v[4]?255:v[7]?4095:65535;return[Math.round(parseInt(v[1]||v[4]||v[7]||v[10],16)/p*255),Math.round(parseInt(v[2]||v[5]||v[8]||v[11],16)/p*255),Math.round(parseInt(v[3]||v[6]||v[9]||v[12],16)/p*255)]}}else if(d.indexOf("#")===0&&(d=d.slice(1),c.exec(d)&&[3,6,9,12].includes(d.length))){const v=d.length/3,p=[0,0,0];for(let l=0;l<3;++l){const i=parseInt(d.slice(v*l,v*l+v),16);p[l]=v===1?i<<4:v===2?i:v===3?i>>4:i>>8}return p}},r.toRgbString=function(n,d=16){const[v,p,l]=n;return`rgb:${f(v,d)}/${f(p,d)}/${f(l,d)}`}},5770:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.PAYLOAD_LIMIT=void 0,r.PAYLOAD_LIMIT=1e7},6351:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.DcsHandler=r.DcsParser=void 0;const c=o(482),f=o(8742),n=o(5770),d=[];r.DcsParser=class{constructor(){this._handlers=Object.create(null),this._active=d,this._ident=0,this._handlerFb=()=>{},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}dispose(){this._handlers=Object.create(null),this._handlerFb=()=>{},this._active=d}registerHandler(p,l){this._handlers[p]===void 0&&(this._handlers[p]=[]);const i=this._handlers[p];return i.push(l),{dispose:()=>{const s=i.indexOf(l);s!==-1&&i.splice(s,1)}}}clearHandler(p){this._handlers[p]&&delete this._handlers[p]}setHandlerFallback(p){this._handlerFb=p}reset(){if(this._active.length)for(let p=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;p>=0;--p)this._active[p].unhook(!1);this._stack.paused=!1,this._active=d,this._ident=0}hook(p,l){if(this.reset(),this._ident=p,this._active=this._handlers[p]||d,this._active.length)for(let i=this._active.length-1;i>=0;i--)this._active[i].hook(l);else this._handlerFb(this._ident,"HOOK",l)}put(p,l,i){if(this._active.length)for(let s=this._active.length-1;s>=0;s--)this._active[s].put(p,l,i);else this._handlerFb(this._ident,"PUT",(0,c.utf32ToString)(p,l,i))}unhook(p,l=!0){if(this._active.length){let i=!1,s=this._active.length-1,e=!1;if(this._stack.paused&&(s=this._stack.loopPosition-1,i=l,e=this._stack.fallThrough,this._stack.paused=!1),!e&&i===!1){for(;s>=0&&(i=this._active[s].unhook(p),i!==!0);s--)if(i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!1,i;s--}for(;s>=0;s--)if(i=this._active[s].unhook(!1),i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!0,i}else this._handlerFb(this._ident,"UNHOOK",p);this._active=d,this._ident=0}};const v=new f.Params;v.addParam(0),r.DcsHandler=class{constructor(p){this._handler=p,this._data="",this._params=v,this._hitLimit=!1}hook(p){this._params=p.length>1||p.params[0]?p.clone():v,this._data="",this._hitLimit=!1}put(p,l,i){this._hitLimit||(this._data+=(0,c.utf32ToString)(p,l,i),this._data.length>n.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))}unhook(p){let l=!1;if(this._hitLimit)l=!1;else if(p&&(l=this._handler(this._data,this._params),l instanceof Promise))return l.then(i=>(this._params=v,this._data="",this._hitLimit=!1,i));return this._params=v,this._data="",this._hitLimit=!1,l}}},2015:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.EscapeSequenceParser=r.VT500_TRANSITION_TABLE=r.TransitionTable=void 0;const c=o(844),f=o(8742),n=o(6242),d=o(6351);class v{constructor(s){this.table=new Uint8Array(s)}setDefault(s,e){this.table.fill(s<<4|e)}add(s,e,t,a){this.table[e<<8|s]=t<<4|a}addMany(s,e,t,a){for(let _=0;_h),e=(g,h)=>s.slice(g,h),t=e(32,127),a=e(0,24);a.push(25),a.push.apply(a,e(28,32));const _=e(0,14);let u;for(u in i.setDefault(1,0),i.addMany(t,0,2,0),_)i.addMany([24,26,153,154],u,3,0),i.addMany(e(128,144),u,3,0),i.addMany(e(144,152),u,3,0),i.add(156,u,0,0),i.add(27,u,11,1),i.add(157,u,4,8),i.addMany([152,158,159],u,0,7),i.add(155,u,11,3),i.add(144,u,11,9);return i.addMany(a,0,3,0),i.addMany(a,1,3,1),i.add(127,1,0,1),i.addMany(a,8,0,8),i.addMany(a,3,3,3),i.add(127,3,0,3),i.addMany(a,4,3,4),i.add(127,4,0,4),i.addMany(a,6,3,6),i.addMany(a,5,3,5),i.add(127,5,0,5),i.addMany(a,2,3,2),i.add(127,2,0,2),i.add(93,1,4,8),i.addMany(t,8,5,8),i.add(127,8,5,8),i.addMany([156,27,24,26,7],8,6,0),i.addMany(e(28,32),8,0,8),i.addMany([88,94,95],1,0,7),i.addMany(t,7,0,7),i.addMany(a,7,0,7),i.add(156,7,0,0),i.add(127,7,0,7),i.add(91,1,11,3),i.addMany(e(64,127),3,7,0),i.addMany(e(48,60),3,8,4),i.addMany([60,61,62,63],3,9,4),i.addMany(e(48,60),4,8,4),i.addMany(e(64,127),4,7,0),i.addMany([60,61,62,63],4,0,6),i.addMany(e(32,64),6,0,6),i.add(127,6,0,6),i.addMany(e(64,127),6,0,0),i.addMany(e(32,48),3,9,5),i.addMany(e(32,48),5,9,5),i.addMany(e(48,64),5,0,6),i.addMany(e(64,127),5,7,0),i.addMany(e(32,48),4,9,5),i.addMany(e(32,48),1,9,2),i.addMany(e(32,48),2,9,2),i.addMany(e(48,127),2,10,0),i.addMany(e(48,80),1,10,0),i.addMany(e(81,88),1,10,0),i.addMany([89,90,92],1,10,0),i.addMany(e(96,127),1,10,0),i.add(80,1,11,9),i.addMany(a,9,0,9),i.add(127,9,0,9),i.addMany(e(28,32),9,0,9),i.addMany(e(32,48),9,9,12),i.addMany(e(48,60),9,8,10),i.addMany([60,61,62,63],9,9,10),i.addMany(a,11,0,11),i.addMany(e(32,128),11,0,11),i.addMany(e(28,32),11,0,11),i.addMany(a,10,0,10),i.add(127,10,0,10),i.addMany(e(28,32),10,0,10),i.addMany(e(48,60),10,8,10),i.addMany([60,61,62,63],10,0,11),i.addMany(e(32,48),10,9,12),i.addMany(a,12,0,12),i.add(127,12,0,12),i.addMany(e(28,32),12,0,12),i.addMany(e(32,48),12,9,12),i.addMany(e(48,64),12,0,11),i.addMany(e(64,127),12,12,13),i.addMany(e(64,127),10,12,13),i.addMany(e(64,127),9,12,13),i.addMany(a,13,13,13),i.addMany(t,13,13,13),i.add(127,13,0,13),i.addMany([27,156,24,26],13,14,0),i.add(p,0,2,0),i.add(p,8,5,8),i.add(p,6,0,6),i.add(p,11,0,11),i.add(p,13,13,13),i}();class l extends c.Disposable{constructor(s=r.VT500_TRANSITION_TABLE){super(),this._transitions=s,this._parseStack={state:0,handlers:[],handlerPos:0,transition:0,chunkPos:0},this.initialState=0,this.currentState=this.initialState,this._params=new f.Params,this._params.addParam(0),this._collect=0,this.precedingJoinState=0,this._printHandlerFb=(e,t,a)=>{},this._executeHandlerFb=e=>{},this._csiHandlerFb=(e,t)=>{},this._escHandlerFb=e=>{},this._errorHandlerFb=e=>e,this._printHandler=this._printHandlerFb,this._executeHandlers=Object.create(null),this._csiHandlers=Object.create(null),this._escHandlers=Object.create(null),this.register((0,c.toDisposable)(()=>{this._csiHandlers=Object.create(null),this._executeHandlers=Object.create(null),this._escHandlers=Object.create(null)})),this._oscParser=this.register(new n.OscParser),this._dcsParser=this.register(new d.DcsParser),this._errorHandler=this._errorHandlerFb,this.registerEscHandler({final:"\\"},()=>!0)}_identifier(s,e=[64,126]){let t=0;if(s.prefix){if(s.prefix.length>1)throw new Error("only one byte as prefix supported");if(t=s.prefix.charCodeAt(0),t&&60>t||t>63)throw new Error("prefix must be in range 0x3c .. 0x3f")}if(s.intermediates){if(s.intermediates.length>2)throw new Error("only two bytes as intermediates are supported");for(let _=0;_u||u>47)throw new Error("intermediate must be in range 0x20 .. 0x2f");t<<=8,t|=u}}if(s.final.length!==1)throw new Error("final must be a single byte");const a=s.final.charCodeAt(0);if(e[0]>a||a>e[1])throw new Error(`final must be in range ${e[0]} .. ${e[1]}`);return t<<=8,t|=a,t}identToString(s){const e=[];for(;s;)e.push(String.fromCharCode(255&s)),s>>=8;return e.reverse().join("")}setPrintHandler(s){this._printHandler=s}clearPrintHandler(){this._printHandler=this._printHandlerFb}registerEscHandler(s,e){const t=this._identifier(s,[48,126]);this._escHandlers[t]===void 0&&(this._escHandlers[t]=[]);const a=this._escHandlers[t];return a.push(e),{dispose:()=>{const _=a.indexOf(e);_!==-1&&a.splice(_,1)}}}clearEscHandler(s){this._escHandlers[this._identifier(s,[48,126])]&&delete this._escHandlers[this._identifier(s,[48,126])]}setEscHandlerFallback(s){this._escHandlerFb=s}setExecuteHandler(s,e){this._executeHandlers[s.charCodeAt(0)]=e}clearExecuteHandler(s){this._executeHandlers[s.charCodeAt(0)]&&delete this._executeHandlers[s.charCodeAt(0)]}setExecuteHandlerFallback(s){this._executeHandlerFb=s}registerCsiHandler(s,e){const t=this._identifier(s);this._csiHandlers[t]===void 0&&(this._csiHandlers[t]=[]);const a=this._csiHandlers[t];return a.push(e),{dispose:()=>{const _=a.indexOf(e);_!==-1&&a.splice(_,1)}}}clearCsiHandler(s){this._csiHandlers[this._identifier(s)]&&delete this._csiHandlers[this._identifier(s)]}setCsiHandlerFallback(s){this._csiHandlerFb=s}registerDcsHandler(s,e){return this._dcsParser.registerHandler(this._identifier(s),e)}clearDcsHandler(s){this._dcsParser.clearHandler(this._identifier(s))}setDcsHandlerFallback(s){this._dcsParser.setHandlerFallback(s)}registerOscHandler(s,e){return this._oscParser.registerHandler(s,e)}clearOscHandler(s){this._oscParser.clearHandler(s)}setOscHandlerFallback(s){this._oscParser.setHandlerFallback(s)}setErrorHandler(s){this._errorHandler=s}clearErrorHandler(){this._errorHandler=this._errorHandlerFb}reset(){this.currentState=this.initialState,this._oscParser.reset(),this._dcsParser.reset(),this._params.reset(),this._params.addParam(0),this._collect=0,this.precedingJoinState=0,this._parseStack.state!==0&&(this._parseStack.state=2,this._parseStack.handlers=[])}_preserveStack(s,e,t,a,_){this._parseStack.state=s,this._parseStack.handlers=e,this._parseStack.handlerPos=t,this._parseStack.transition=a,this._parseStack.chunkPos=_}parse(s,e,t){let a,_=0,u=0,g=0;if(this._parseStack.state)if(this._parseStack.state===2)this._parseStack.state=0,g=this._parseStack.chunkPos+1;else{if(t===void 0||this._parseStack.state===1)throw this._parseStack.state=1,new Error("improper continuation due to previous async handler, giving up parsing");const h=this._parseStack.handlers;let m=this._parseStack.handlerPos-1;switch(this._parseStack.state){case 3:if(t===!1&&m>-1){for(;m>=0&&(a=h[m](this._params),a!==!0);m--)if(a instanceof Promise)return this._parseStack.handlerPos=m,a}this._parseStack.handlers=[];break;case 4:if(t===!1&&m>-1){for(;m>=0&&(a=h[m](),a!==!0);m--)if(a instanceof Promise)return this._parseStack.handlerPos=m,a}this._parseStack.handlers=[];break;case 6:if(_=s[this._parseStack.chunkPos],a=this._dcsParser.unhook(_!==24&&_!==26,t),a)return a;_===27&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0;break;case 5:if(_=s[this._parseStack.chunkPos],a=this._oscParser.end(_!==24&&_!==26,t),a)return a;_===27&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0}this._parseStack.state=0,g=this._parseStack.chunkPos+1,this.precedingJoinState=0,this.currentState=15&this._parseStack.transition}for(let h=g;h>4){case 2:for(let b=h+1;;++b){if(b>=e||(_=s[b])<32||_>126&&_=e||(_=s[b])<32||_>126&&_=e||(_=s[b])<32||_>126&&_=e||(_=s[b])<32||_>126&&_=0&&(a=m[y](this._params),a!==!0);y--)if(a instanceof Promise)return this._preserveStack(3,m,y,u,h),a;y<0&&this._csiHandlerFb(this._collect<<8|_,this._params),this.precedingJoinState=0;break;case 8:do switch(_){case 59:this._params.addParam(0);break;case 58:this._params.addSubParam(-1);break;default:this._params.addDigit(_-48)}while(++h47&&_<60);h--;break;case 9:this._collect<<=8,this._collect|=_;break;case 10:const k=this._escHandlers[this._collect<<8|_];let D=k?k.length-1:-1;for(;D>=0&&(a=k[D](),a!==!0);D--)if(a instanceof Promise)return this._preserveStack(4,k,D,u,h),a;D<0&&this._escHandlerFb(this._collect<<8|_),this.precedingJoinState=0;break;case 11:this._params.reset(),this._params.addParam(0),this._collect=0;break;case 12:this._dcsParser.hook(this._collect<<8|_,this._params);break;case 13:for(let b=h+1;;++b)if(b>=e||(_=s[b])===24||_===26||_===27||_>127&&_=e||(_=s[b])<32||_>127&&_{Object.defineProperty(r,"__esModule",{value:!0}),r.OscHandler=r.OscParser=void 0;const c=o(5770),f=o(482),n=[];r.OscParser=class{constructor(){this._state=0,this._active=n,this._id=-1,this._handlers=Object.create(null),this._handlerFb=()=>{},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}registerHandler(d,v){this._handlers[d]===void 0&&(this._handlers[d]=[]);const p=this._handlers[d];return p.push(v),{dispose:()=>{const l=p.indexOf(v);l!==-1&&p.splice(l,1)}}}clearHandler(d){this._handlers[d]&&delete this._handlers[d]}setHandlerFallback(d){this._handlerFb=d}dispose(){this._handlers=Object.create(null),this._handlerFb=()=>{},this._active=n}reset(){if(this._state===2)for(let d=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;d>=0;--d)this._active[d].end(!1);this._stack.paused=!1,this._active=n,this._id=-1,this._state=0}_start(){if(this._active=this._handlers[this._id]||n,this._active.length)for(let d=this._active.length-1;d>=0;d--)this._active[d].start();else this._handlerFb(this._id,"START")}_put(d,v,p){if(this._active.length)for(let l=this._active.length-1;l>=0;l--)this._active[l].put(d,v,p);else this._handlerFb(this._id,"PUT",(0,f.utf32ToString)(d,v,p))}start(){this.reset(),this._state=1}put(d,v,p){if(this._state!==3){if(this._state===1)for(;v0&&this._put(d,v,p)}}end(d,v=!0){if(this._state!==0){if(this._state!==3)if(this._state===1&&this._start(),this._active.length){let p=!1,l=this._active.length-1,i=!1;if(this._stack.paused&&(l=this._stack.loopPosition-1,p=v,i=this._stack.fallThrough,this._stack.paused=!1),!i&&p===!1){for(;l>=0&&(p=this._active[l].end(d),p!==!0);l--)if(p instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=l,this._stack.fallThrough=!1,p;l--}for(;l>=0;l--)if(p=this._active[l].end(!1),p instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=l,this._stack.fallThrough=!0,p}else this._handlerFb(this._id,"END",d);this._active=n,this._id=-1,this._state=0}}},r.OscHandler=class{constructor(d){this._handler=d,this._data="",this._hitLimit=!1}start(){this._data="",this._hitLimit=!1}put(d,v,p){this._hitLimit||(this._data+=(0,f.utf32ToString)(d,v,p),this._data.length>c.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))}end(d){let v=!1;if(this._hitLimit)v=!1;else if(d&&(v=this._handler(this._data),v instanceof Promise))return v.then(p=>(this._data="",this._hitLimit=!1,p));return this._data="",this._hitLimit=!1,v}}},8742:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.Params=void 0;const o=2147483647;class c{static fromArray(n){const d=new c;if(!n.length)return d;for(let v=Array.isArray(n[0])?1:0;v256)throw new Error("maxSubParamsLength must not be greater than 256");this.params=new Int32Array(n),this.length=0,this._subParams=new Int32Array(d),this._subParamsLength=0,this._subParamsIdx=new Uint16Array(n),this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}clone(){const n=new c(this.maxLength,this.maxSubParamsLength);return n.params.set(this.params),n.length=this.length,n._subParams.set(this._subParams),n._subParamsLength=this._subParamsLength,n._subParamsIdx.set(this._subParamsIdx),n._rejectDigits=this._rejectDigits,n._rejectSubDigits=this._rejectSubDigits,n._digitIsSub=this._digitIsSub,n}toArray(){const n=[];for(let d=0;d>8,p=255&this._subParamsIdx[d];p-v>0&&n.push(Array.prototype.slice.call(this._subParams,v,p))}return n}reset(){this.length=0,this._subParamsLength=0,this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}addParam(n){if(this._digitIsSub=!1,this.length>=this.maxLength)this._rejectDigits=!0;else{if(n<-1)throw new Error("values lesser than -1 are not allowed");this._subParamsIdx[this.length]=this._subParamsLength<<8|this._subParamsLength,this.params[this.length++]=n>o?o:n}}addSubParam(n){if(this._digitIsSub=!0,this.length)if(this._rejectDigits||this._subParamsLength>=this.maxSubParamsLength)this._rejectSubDigits=!0;else{if(n<-1)throw new Error("values lesser than -1 are not allowed");this._subParams[this._subParamsLength++]=n>o?o:n,this._subParamsIdx[this.length-1]++}}hasSubParams(n){return(255&this._subParamsIdx[n])-(this._subParamsIdx[n]>>8)>0}getSubParams(n){const d=this._subParamsIdx[n]>>8,v=255&this._subParamsIdx[n];return v-d>0?this._subParams.subarray(d,v):null}getSubParamsAll(){const n={};for(let d=0;d>8,p=255&this._subParamsIdx[d];p-v>0&&(n[d]=this._subParams.slice(v,p))}return n}addDigit(n){let d;if(this._rejectDigits||!(d=this._digitIsSub?this._subParamsLength:this.length)||this._digitIsSub&&this._rejectSubDigits)return;const v=this._digitIsSub?this._subParams:this.params,p=v[d-1];v[d-1]=~p?Math.min(10*p+n,o):n}}r.Params=c},5741:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.AddonManager=void 0,r.AddonManager=class{constructor(){this._addons=[]}dispose(){for(let o=this._addons.length-1;o>=0;o--)this._addons[o].instance.dispose()}loadAddon(o,c){const f={instance:c,dispose:c.dispose,isDisposed:!1};this._addons.push(f),c.dispose=()=>this._wrappedAddonDispose(f),c.activate(o)}_wrappedAddonDispose(o){if(o.isDisposed)return;let c=-1;for(let f=0;f{Object.defineProperty(r,"__esModule",{value:!0}),r.BufferApiView=void 0;const c=o(3785),f=o(511);r.BufferApiView=class{constructor(n,d){this._buffer=n,this.type=d}init(n){return this._buffer=n,this}get cursorY(){return this._buffer.y}get cursorX(){return this._buffer.x}get viewportY(){return this._buffer.ydisp}get baseY(){return this._buffer.ybase}get length(){return this._buffer.lines.length}getLine(n){const d=this._buffer.lines.get(n);if(d)return new c.BufferLineApiView(d)}getNullCell(){return new f.CellData}}},3785:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.BufferLineApiView=void 0;const c=o(511);r.BufferLineApiView=class{constructor(f){this._line=f}get isWrapped(){return this._line.isWrapped}get length(){return this._line.length}getCell(f,n){if(!(f<0||f>=this._line.length))return n?(this._line.loadCell(f,n),n):this._line.loadCell(f,new c.CellData)}translateToString(f,n,d){return this._line.translateToString(f,n,d)}}},8285:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.BufferNamespaceApi=void 0;const c=o(8771),f=o(8460),n=o(844);class d extends n.Disposable{constructor(p){super(),this._core=p,this._onBufferChange=this.register(new f.EventEmitter),this.onBufferChange=this._onBufferChange.event,this._normal=new c.BufferApiView(this._core.buffers.normal,"normal"),this._alternate=new c.BufferApiView(this._core.buffers.alt,"alternate"),this._core.buffers.onBufferActivate(()=>this._onBufferChange.fire(this.active))}get active(){if(this._core.buffers.active===this._core.buffers.normal)return this.normal;if(this._core.buffers.active===this._core.buffers.alt)return this.alternate;throw new Error("Active buffer is neither normal nor alternate")}get normal(){return this._normal.init(this._core.buffers.normal)}get alternate(){return this._alternate.init(this._core.buffers.alt)}}r.BufferNamespaceApi=d},7975:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.ParserApi=void 0,r.ParserApi=class{constructor(o){this._core=o}registerCsiHandler(o,c){return this._core.registerCsiHandler(o,f=>c(f.toArray()))}addCsiHandler(o,c){return this.registerCsiHandler(o,c)}registerDcsHandler(o,c){return this._core.registerDcsHandler(o,(f,n)=>c(f,n.toArray()))}addDcsHandler(o,c){return this.registerDcsHandler(o,c)}registerEscHandler(o,c){return this._core.registerEscHandler(o,c)}addEscHandler(o,c){return this.registerEscHandler(o,c)}registerOscHandler(o,c){return this._core.registerOscHandler(o,c)}addOscHandler(o,c){return this.registerOscHandler(o,c)}}},7090:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.UnicodeApi=void 0,r.UnicodeApi=class{constructor(o){this._core=o}register(o){this._core.unicodeService.register(o)}get versions(){return this._core.unicodeService.versions}get activeVersion(){return this._core.unicodeService.activeVersion}set activeVersion(o){this._core.unicodeService.activeVersion=o}}},744:function(R,r,o){var c=this&&this.__decorate||function(i,s,e,t){var a,_=arguments.length,u=_<3?s:t===null?t=Object.getOwnPropertyDescriptor(s,e):t;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")u=Reflect.decorate(i,s,e,t);else for(var g=i.length-1;g>=0;g--)(a=i[g])&&(u=(_<3?a(u):_>3?a(s,e,u):a(s,e))||u);return _>3&&u&&Object.defineProperty(s,e,u),u},f=this&&this.__param||function(i,s){return function(e,t){s(e,t,i)}};Object.defineProperty(r,"__esModule",{value:!0}),r.BufferService=r.MINIMUM_ROWS=r.MINIMUM_COLS=void 0;const n=o(8460),d=o(844),v=o(5295),p=o(2585);r.MINIMUM_COLS=2,r.MINIMUM_ROWS=1;let l=r.BufferService=class extends d.Disposable{get buffer(){return this.buffers.active}constructor(i){super(),this.isUserScrolling=!1,this._onResize=this.register(new n.EventEmitter),this.onResize=this._onResize.event,this._onScroll=this.register(new n.EventEmitter),this.onScroll=this._onScroll.event,this.cols=Math.max(i.rawOptions.cols||0,r.MINIMUM_COLS),this.rows=Math.max(i.rawOptions.rows||0,r.MINIMUM_ROWS),this.buffers=this.register(new v.BufferSet(i,this))}resize(i,s){this.cols=i,this.rows=s,this.buffers.resize(i,s),this._onResize.fire({cols:i,rows:s})}reset(){this.buffers.reset(),this.isUserScrolling=!1}scroll(i,s=!1){const e=this.buffer;let t;t=this._cachedBlankLine,t&&t.length===this.cols&&t.getFg(0)===i.fg&&t.getBg(0)===i.bg||(t=e.getBlankLine(i,s),this._cachedBlankLine=t),t.isWrapped=s;const a=e.ybase+e.scrollTop,_=e.ybase+e.scrollBottom;if(e.scrollTop===0){const u=e.lines.isFull;_===e.lines.length-1?u?e.lines.recycle().copyFrom(t):e.lines.push(t.clone()):e.lines.splice(_+1,0,t.clone()),u?this.isUserScrolling&&(e.ydisp=Math.max(e.ydisp-1,0)):(e.ybase++,this.isUserScrolling||e.ydisp++)}else{const u=_-a+1;e.lines.shiftElements(a+1,u-1,-1),e.lines.set(_,t.clone())}this.isUserScrolling||(e.ydisp=e.ybase),this._onScroll.fire(e.ydisp)}scrollLines(i,s,e){const t=this.buffer;if(i<0){if(t.ydisp===0)return;this.isUserScrolling=!0}else i+t.ydisp>=t.ybase&&(this.isUserScrolling=!1);const a=t.ydisp;t.ydisp=Math.max(Math.min(t.ydisp+i,t.ybase),0),a!==t.ydisp&&(s||this._onScroll.fire(t.ydisp))}};r.BufferService=l=c([f(0,p.IOptionsService)],l)},7994:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.CharsetService=void 0,r.CharsetService=class{constructor(){this.glevel=0,this._charsets=[]}reset(){this.charset=void 0,this._charsets=[],this.glevel=0}setgLevel(o){this.glevel=o,this.charset=this._charsets[o]}setgCharset(o,c){this._charsets[o]=c,this.glevel===o&&(this.charset=c)}}},1753:function(R,r,o){var c=this&&this.__decorate||function(t,a,_,u){var g,h=arguments.length,m=h<3?a:u===null?u=Object.getOwnPropertyDescriptor(a,_):u;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")m=Reflect.decorate(t,a,_,u);else for(var y=t.length-1;y>=0;y--)(g=t[y])&&(m=(h<3?g(m):h>3?g(a,_,m):g(a,_))||m);return h>3&&m&&Object.defineProperty(a,_,m),m},f=this&&this.__param||function(t,a){return function(_,u){a(_,u,t)}};Object.defineProperty(r,"__esModule",{value:!0}),r.CoreMouseService=void 0;const n=o(2585),d=o(8460),v=o(844),p={NONE:{events:0,restrict:()=>!1},X10:{events:1,restrict:t=>t.button!==4&&t.action===1&&(t.ctrl=!1,t.alt=!1,t.shift=!1,!0)},VT200:{events:19,restrict:t=>t.action!==32},DRAG:{events:23,restrict:t=>t.action!==32||t.button!==3},ANY:{events:31,restrict:t=>!0}};function l(t,a){let _=(t.ctrl?16:0)|(t.shift?4:0)|(t.alt?8:0);return t.button===4?(_|=64,_|=t.action):(_|=3&t.button,4&t.button&&(_|=64),8&t.button&&(_|=128),t.action===32?_|=32:t.action!==0||a||(_|=3)),_}const i=String.fromCharCode,s={DEFAULT:t=>{const a=[l(t,!1)+32,t.col+32,t.row+32];return a[0]>255||a[1]>255||a[2]>255?"":`\x1B[M${i(a[0])}${i(a[1])}${i(a[2])}`},SGR:t=>{const a=t.action===0&&t.button!==4?"m":"M";return`\x1B[<${l(t,!0)};${t.col};${t.row}${a}`},SGR_PIXELS:t=>{const a=t.action===0&&t.button!==4?"m":"M";return`\x1B[<${l(t,!0)};${t.x};${t.y}${a}`}};let e=r.CoreMouseService=class extends v.Disposable{constructor(t,a){super(),this._bufferService=t,this._coreService=a,this._protocols={},this._encodings={},this._activeProtocol="",this._activeEncoding="",this._lastEvent=null,this._onProtocolChange=this.register(new d.EventEmitter),this.onProtocolChange=this._onProtocolChange.event;for(const _ of Object.keys(p))this.addProtocol(_,p[_]);for(const _ of Object.keys(s))this.addEncoding(_,s[_]);this.reset()}addProtocol(t,a){this._protocols[t]=a}addEncoding(t,a){this._encodings[t]=a}get activeProtocol(){return this._activeProtocol}get areMouseEventsActive(){return this._protocols[this._activeProtocol].events!==0}set activeProtocol(t){if(!this._protocols[t])throw new Error(`unknown protocol "${t}"`);this._activeProtocol=t,this._onProtocolChange.fire(this._protocols[t].events)}get activeEncoding(){return this._activeEncoding}set activeEncoding(t){if(!this._encodings[t])throw new Error(`unknown encoding "${t}"`);this._activeEncoding=t}reset(){this.activeProtocol="NONE",this.activeEncoding="DEFAULT",this._lastEvent=null}triggerMouseEvent(t){if(t.col<0||t.col>=this._bufferService.cols||t.row<0||t.row>=this._bufferService.rows||t.button===4&&t.action===32||t.button===3&&t.action!==32||t.button!==4&&(t.action===2||t.action===3)||(t.col++,t.row++,t.action===32&&this._lastEvent&&this._equalEvents(this._lastEvent,t,this._activeEncoding==="SGR_PIXELS"))||!this._protocols[this._activeProtocol].restrict(t))return!1;const a=this._encodings[this._activeEncoding](t);return a&&(this._activeEncoding==="DEFAULT"?this._coreService.triggerBinaryEvent(a):this._coreService.triggerDataEvent(a,!0)),this._lastEvent=t,!0}explainEvents(t){return{down:!!(1&t),up:!!(2&t),drag:!!(4&t),move:!!(8&t),wheel:!!(16&t)}}_equalEvents(t,a,_){if(_){if(t.x!==a.x||t.y!==a.y)return!1}else if(t.col!==a.col||t.row!==a.row)return!1;return t.button===a.button&&t.action===a.action&&t.ctrl===a.ctrl&&t.alt===a.alt&&t.shift===a.shift}};r.CoreMouseService=e=c([f(0,n.IBufferService),f(1,n.ICoreService)],e)},6975:function(R,r,o){var c=this&&this.__decorate||function(e,t,a,_){var u,g=arguments.length,h=g<3?t:_===null?_=Object.getOwnPropertyDescriptor(t,a):_;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")h=Reflect.decorate(e,t,a,_);else for(var m=e.length-1;m>=0;m--)(u=e[m])&&(h=(g<3?u(h):g>3?u(t,a,h):u(t,a))||h);return g>3&&h&&Object.defineProperty(t,a,h),h},f=this&&this.__param||function(e,t){return function(a,_){t(a,_,e)}};Object.defineProperty(r,"__esModule",{value:!0}),r.CoreService=void 0;const n=o(1439),d=o(8460),v=o(844),p=o(2585),l=Object.freeze({insertMode:!1}),i=Object.freeze({applicationCursorKeys:!1,applicationKeypad:!1,bracketedPasteMode:!1,origin:!1,reverseWraparound:!1,sendFocus:!1,wraparound:!0});let s=r.CoreService=class extends v.Disposable{constructor(e,t,a){super(),this._bufferService=e,this._logService=t,this._optionsService=a,this.isCursorInitialized=!1,this.isCursorHidden=!1,this._onData=this.register(new d.EventEmitter),this.onData=this._onData.event,this._onUserInput=this.register(new d.EventEmitter),this.onUserInput=this._onUserInput.event,this._onBinary=this.register(new d.EventEmitter),this.onBinary=this._onBinary.event,this._onRequestScrollToBottom=this.register(new d.EventEmitter),this.onRequestScrollToBottom=this._onRequestScrollToBottom.event,this.modes=(0,n.clone)(l),this.decPrivateModes=(0,n.clone)(i)}reset(){this.modes=(0,n.clone)(l),this.decPrivateModes=(0,n.clone)(i)}triggerDataEvent(e,t=!1){if(this._optionsService.rawOptions.disableStdin)return;const a=this._bufferService.buffer;t&&this._optionsService.rawOptions.scrollOnUserInput&&a.ybase!==a.ydisp&&this._onRequestScrollToBottom.fire(),t&&this._onUserInput.fire(),this._logService.debug(`sending data "${e}"`,()=>e.split("").map(_=>_.charCodeAt(0))),this._onData.fire(e)}triggerBinaryEvent(e){this._optionsService.rawOptions.disableStdin||(this._logService.debug(`sending binary "${e}"`,()=>e.split("").map(t=>t.charCodeAt(0))),this._onBinary.fire(e))}};r.CoreService=s=c([f(0,p.IBufferService),f(1,p.ILogService),f(2,p.IOptionsService)],s)},9074:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.DecorationService=void 0;const c=o(8055),f=o(8460),n=o(844),d=o(6106);let v=0,p=0;class l extends n.Disposable{get decorations(){return this._decorations.values()}constructor(){super(),this._decorations=new d.SortedList(e=>e?.marker.line),this._onDecorationRegistered=this.register(new f.EventEmitter),this.onDecorationRegistered=this._onDecorationRegistered.event,this._onDecorationRemoved=this.register(new f.EventEmitter),this.onDecorationRemoved=this._onDecorationRemoved.event,this.register((0,n.toDisposable)(()=>this.reset()))}registerDecoration(e){if(e.marker.isDisposed)return;const t=new i(e);if(t){const a=t.marker.onDispose(()=>t.dispose());t.onDispose(()=>{t&&(this._decorations.delete(t)&&this._onDecorationRemoved.fire(t),a.dispose())}),this._decorations.insert(t),this._onDecorationRegistered.fire(t)}return t}reset(){for(const e of this._decorations.values())e.dispose();this._decorations.clear()}*getDecorationsAtCell(e,t,a){var _,u,g;let h=0,m=0;for(const y of this._decorations.getKeyIterator(t))h=(_=y.options.x)!=null?_:0,m=h+((u=y.options.width)!=null?u:1),e>=h&&e{var g,h,m;v=(g=u.options.x)!=null?g:0,p=v+((h=u.options.width)!=null?h:1),e>=v&&e{Object.defineProperty(r,"__esModule",{value:!0}),r.InstantiationService=r.ServiceCollection=void 0;const c=o(2585),f=o(8343);class n{constructor(...v){this._entries=new Map;for(const[p,l]of v)this.set(p,l)}set(v,p){const l=this._entries.get(v);return this._entries.set(v,p),l}forEach(v){for(const[p,l]of this._entries.entries())v(p,l)}has(v){return this._entries.has(v)}get(v){return this._entries.get(v)}}r.ServiceCollection=n,r.InstantiationService=class{constructor(){this._services=new n,this._services.set(c.IInstantiationService,this)}setService(d,v){this._services.set(d,v)}getService(d){return this._services.get(d)}createInstance(d,...v){const p=(0,f.getServiceDependencies)(d).sort((s,e)=>s.index-e.index),l=[];for(const s of p){const e=this._services.get(s.id);if(!e)throw new Error(`[createInstance] ${d.name} depends on UNKNOWN service ${s.id}.`);l.push(e)}const i=p.length>0?p[0].index:v.length;if(v.length!==i)throw new Error(`[createInstance] First service dependency of ${d.name} at position ${i+1} conflicts with ${v.length} static arguments`);return new d(...v,...l)}}},7866:function(R,r,o){var c=this&&this.__decorate||function(i,s,e,t){var a,_=arguments.length,u=_<3?s:t===null?t=Object.getOwnPropertyDescriptor(s,e):t;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")u=Reflect.decorate(i,s,e,t);else for(var g=i.length-1;g>=0;g--)(a=i[g])&&(u=(_<3?a(u):_>3?a(s,e,u):a(s,e))||u);return _>3&&u&&Object.defineProperty(s,e,u),u},f=this&&this.__param||function(i,s){return function(e,t){s(e,t,i)}};Object.defineProperty(r,"__esModule",{value:!0}),r.traceCall=r.setTraceLogger=r.LogService=void 0;const n=o(844),d=o(2585),v={trace:d.LogLevelEnum.TRACE,debug:d.LogLevelEnum.DEBUG,info:d.LogLevelEnum.INFO,warn:d.LogLevelEnum.WARN,error:d.LogLevelEnum.ERROR,off:d.LogLevelEnum.OFF};let p,l=r.LogService=class extends n.Disposable{get logLevel(){return this._logLevel}constructor(i){super(),this._optionsService=i,this._logLevel=d.LogLevelEnum.OFF,this._updateLogLevel(),this.register(this._optionsService.onSpecificOptionChange("logLevel",()=>this._updateLogLevel())),p=this}_updateLogLevel(){this._logLevel=v[this._optionsService.rawOptions.logLevel]}_evalLazyOptionalParams(i){for(let s=0;sJSON.stringify(u)).join(", ")})`);const _=t.apply(this,a);return p.trace(`GlyphRenderer#${t.name} return`,_),_}}},7302:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.OptionsService=r.DEFAULT_OPTIONS=void 0;const c=o(8460),f=o(844),n=o(6114);r.DEFAULT_OPTIONS={cols:80,rows:24,cursorBlink:!1,cursorStyle:"block",cursorWidth:1,cursorInactiveStyle:"outline",customGlyphs:!0,drawBoldTextInBrightColors:!0,documentOverride:null,fastScrollModifier:"alt",fastScrollSensitivity:5,fontFamily:"courier-new, courier, monospace",fontSize:15,fontWeight:"normal",fontWeightBold:"bold",ignoreBracketedPasteMode:!1,lineHeight:1,letterSpacing:0,linkHandler:null,logLevel:"info",logger:null,scrollback:1e3,scrollOnUserInput:!0,scrollSensitivity:1,screenReaderMode:!1,smoothScrollDuration:0,macOptionIsMeta:!1,macOptionClickForcesSelection:!1,minimumContrastRatio:1,disableStdin:!1,allowProposedApi:!1,allowTransparency:!1,tabStopWidth:8,theme:{},rescaleOverlappingGlyphs:!1,rightClickSelectsWord:n.isMac,windowOptions:{},windowsMode:!1,windowsPty:{},wordSeparator:" ()[]{}',\"`",altClickMovesCursor:!0,convertEol:!1,termName:"xterm",cancelEvents:!1,overviewRulerWidth:0};const d=["normal","bold","100","200","300","400","500","600","700","800","900"];class v extends f.Disposable{constructor(l){super(),this._onOptionChange=this.register(new c.EventEmitter),this.onOptionChange=this._onOptionChange.event;const i=ae({},r.DEFAULT_OPTIONS);for(const s in l)if(s in i)try{const e=l[s];i[s]=this._sanitizeAndValidateOption(s,e)}catch(e){console.error(e)}this.rawOptions=i,this.options=ae({},i),this._setupOptions(),this.register((0,f.toDisposable)(()=>{this.rawOptions.linkHandler=null,this.rawOptions.documentOverride=null}))}onSpecificOptionChange(l,i){return this.onOptionChange(s=>{s===l&&i(this.rawOptions[l])})}onMultipleOptionChange(l,i){return this.onOptionChange(s=>{l.indexOf(s)!==-1&&i()})}_setupOptions(){const l=s=>{if(!(s in r.DEFAULT_OPTIONS))throw new Error(`No option with key "${s}"`);return this.rawOptions[s]},i=(s,e)=>{if(!(s in r.DEFAULT_OPTIONS))throw new Error(`No option with key "${s}"`);e=this._sanitizeAndValidateOption(s,e),this.rawOptions[s]!==e&&(this.rawOptions[s]=e,this._onOptionChange.fire(s))};for(const s in this.rawOptions){const e={get:l.bind(this,s),set:i.bind(this,s)};Object.defineProperty(this.options,s,e)}}_sanitizeAndValidateOption(l,i){switch(l){case"cursorStyle":if(i||(i=r.DEFAULT_OPTIONS[l]),!function(s){return s==="block"||s==="underline"||s==="bar"}(i))throw new Error(`"${i}" is not a valid value for ${l}`);break;case"wordSeparator":i||(i=r.DEFAULT_OPTIONS[l]);break;case"fontWeight":case"fontWeightBold":if(typeof i=="number"&&1<=i&&i<=1e3)break;i=d.includes(i)?i:r.DEFAULT_OPTIONS[l];break;case"cursorWidth":i=Math.floor(i);case"lineHeight":case"tabStopWidth":if(i<1)throw new Error(`${l} cannot be less than 1, value: ${i}`);break;case"minimumContrastRatio":i=Math.max(1,Math.min(21,Math.round(10*i)/10));break;case"scrollback":if((i=Math.min(i,4294967295))<0)throw new Error(`${l} cannot be less than 0, value: ${i}`);break;case"fastScrollSensitivity":case"scrollSensitivity":if(i<=0)throw new Error(`${l} cannot be less than or equal to 0, value: ${i}`);break;case"rows":case"cols":if(!i&&i!==0)throw new Error(`${l} must be numeric, value: ${i}`);break;case"windowsPty":i=i??{}}return i}}r.OptionsService=v},2660:function(R,r,o){var c=this&&this.__decorate||function(v,p,l,i){var s,e=arguments.length,t=e<3?p:i===null?i=Object.getOwnPropertyDescriptor(p,l):i;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")t=Reflect.decorate(v,p,l,i);else for(var a=v.length-1;a>=0;a--)(s=v[a])&&(t=(e<3?s(t):e>3?s(p,l,t):s(p,l))||t);return e>3&&t&&Object.defineProperty(p,l,t),t},f=this&&this.__param||function(v,p){return function(l,i){p(l,i,v)}};Object.defineProperty(r,"__esModule",{value:!0}),r.OscLinkService=void 0;const n=o(2585);let d=r.OscLinkService=class{constructor(v){this._bufferService=v,this._nextId=1,this._entriesWithId=new Map,this._dataByLinkId=new Map}registerLink(v){const p=this._bufferService.buffer;if(v.id===void 0){const a=p.addMarker(p.ybase+p.y),_={data:v,id:this._nextId++,lines:[a]};return a.onDispose(()=>this._removeMarkerFromLink(_,a)),this._dataByLinkId.set(_.id,_),_.id}const l=v,i=this._getEntryIdKey(l),s=this._entriesWithId.get(i);if(s)return this.addLineToLink(s.id,p.ybase+p.y),s.id;const e=p.addMarker(p.ybase+p.y),t={id:this._nextId++,key:this._getEntryIdKey(l),data:l,lines:[e]};return e.onDispose(()=>this._removeMarkerFromLink(t,e)),this._entriesWithId.set(t.key,t),this._dataByLinkId.set(t.id,t),t.id}addLineToLink(v,p){const l=this._dataByLinkId.get(v);if(l&&l.lines.every(i=>i.line!==p)){const i=this._bufferService.buffer.addMarker(p);l.lines.push(i),i.onDispose(()=>this._removeMarkerFromLink(l,i))}}getLinkData(v){var p;return(p=this._dataByLinkId.get(v))==null?void 0:p.data}_getEntryIdKey(v){return`${v.id};;${v.uri}`}_removeMarkerFromLink(v,p){const l=v.lines.indexOf(p);l!==-1&&(v.lines.splice(l,1),v.lines.length===0&&(v.data.id!==void 0&&this._entriesWithId.delete(v.key),this._dataByLinkId.delete(v.id)))}};r.OscLinkService=d=c([f(0,n.IBufferService)],d)},8343:(R,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.createDecorator=r.getServiceDependencies=r.serviceRegistry=void 0;const o="di$target",c="di$dependencies";r.serviceRegistry=new Map,r.getServiceDependencies=function(f){return f[c]||[]},r.createDecorator=function(f){if(r.serviceRegistry.has(f))return r.serviceRegistry.get(f);const n=function(d,v,p){if(arguments.length!==3)throw new Error("@IServiceName-decorator can only be used to decorate a parameter");(function(l,i,s){i[o]===i?i[c].push({id:l,index:s}):(i[c]=[{id:l,index:s}],i[o]=i)})(n,d,p)};return n.toString=()=>f,r.serviceRegistry.set(f,n),n}},2585:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.IDecorationService=r.IUnicodeService=r.IOscLinkService=r.IOptionsService=r.ILogService=r.LogLevelEnum=r.IInstantiationService=r.ICharsetService=r.ICoreService=r.ICoreMouseService=r.IBufferService=void 0;const c=o(8343);var f;r.IBufferService=(0,c.createDecorator)("BufferService"),r.ICoreMouseService=(0,c.createDecorator)("CoreMouseService"),r.ICoreService=(0,c.createDecorator)("CoreService"),r.ICharsetService=(0,c.createDecorator)("CharsetService"),r.IInstantiationService=(0,c.createDecorator)("InstantiationService"),function(n){n[n.TRACE=0]="TRACE",n[n.DEBUG=1]="DEBUG",n[n.INFO=2]="INFO",n[n.WARN=3]="WARN",n[n.ERROR=4]="ERROR",n[n.OFF=5]="OFF"}(f||(r.LogLevelEnum=f={})),r.ILogService=(0,c.createDecorator)("LogService"),r.IOptionsService=(0,c.createDecorator)("OptionsService"),r.IOscLinkService=(0,c.createDecorator)("OscLinkService"),r.IUnicodeService=(0,c.createDecorator)("UnicodeService"),r.IDecorationService=(0,c.createDecorator)("DecorationService")},1480:(R,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.UnicodeService=void 0;const c=o(8460),f=o(225);class n{static extractShouldJoin(v){return(1&v)!=0}static extractWidth(v){return v>>1&3}static extractCharKind(v){return v>>3}static createPropertyValue(v,p,l=!1){return(16777215&v)<<3|(3&p)<<1|(l?1:0)}constructor(){this._providers=Object.create(null),this._active="",this._onChange=new c.EventEmitter,this.onChange=this._onChange.event;const v=new f.UnicodeV6;this.register(v),this._active=v.version,this._activeProvider=v}dispose(){this._onChange.dispose()}get versions(){return Object.keys(this._providers)}get activeVersion(){return this._active}set activeVersion(v){if(!this._providers[v])throw new Error(`unknown Unicode version "${v}"`);this._active=v,this._activeProvider=this._providers[v],this._onChange.fire(v)}register(v){this._providers[v.version]=v}wcwidth(v){return this._activeProvider.wcwidth(v)}getStringCellWidth(v){let p=0,l=0;const i=v.length;for(let s=0;s=i)return p+this.wcwidth(e);const _=v.charCodeAt(s);56320<=_&&_<=57343?e=1024*(e-55296)+_-56320+65536:p+=this.wcwidth(_)}const t=this.charProperties(e,l);let a=n.extractWidth(t);n.extractShouldJoin(t)&&(a-=n.extractWidth(l)),p+=a,l=t}return p}charProperties(v,p){return this._activeProvider.charProperties(v,p)}}r.UnicodeService=n}},J={};function q(R){var r=J[R];if(r!==void 0)return r.exports;var o=J[R]={exports:{}};return te[R].call(o.exports,o,o.exports,q),o.exports}var T={};return(()=>{var R=T;Object.defineProperty(R,"__esModule",{value:!0}),R.Terminal=void 0;const r=q(9042),o=q(3236),c=q(844),f=q(5741),n=q(8285),d=q(7975),v=q(7090),p=["cols","rows"];class l extends c.Disposable{constructor(s){super(),this._core=this.register(new o.Terminal(s)),this._addonManager=this.register(new f.AddonManager),this._publicOptions=ae({},this._core.options);const e=a=>this._core.options[a],t=(a,_)=>{this._checkReadonlyOptions(a),this._core.options[a]=_};for(const a in this._core.options){const _={get:e.bind(this,a),set:t.bind(this,a)};Object.defineProperty(this._publicOptions,a,_)}}_checkReadonlyOptions(s){if(p.includes(s))throw new Error(`Option "${s}" can only be set in the constructor`)}_checkProposedApi(){if(!this._core.optionsService.rawOptions.allowProposedApi)throw new Error("You must set the allowProposedApi option to true to use proposed API")}get onBell(){return this._core.onBell}get onBinary(){return this._core.onBinary}get onCursorMove(){return this._core.onCursorMove}get onData(){return this._core.onData}get onKey(){return this._core.onKey}get onLineFeed(){return this._core.onLineFeed}get onRender(){return this._core.onRender}get onResize(){return this._core.onResize}get onScroll(){return this._core.onScroll}get onSelectionChange(){return this._core.onSelectionChange}get onTitleChange(){return this._core.onTitleChange}get onWriteParsed(){return this._core.onWriteParsed}get element(){return this._core.element}get parser(){return this._parser||(this._parser=new d.ParserApi(this._core)),this._parser}get unicode(){return this._checkProposedApi(),new v.UnicodeApi(this._core)}get textarea(){return this._core.textarea}get rows(){return this._core.rows}get cols(){return this._core.cols}get buffer(){return this._buffer||(this._buffer=this.register(new n.BufferNamespaceApi(this._core))),this._buffer}get markers(){return this._checkProposedApi(),this._core.markers}get modes(){const s=this._core.coreService.decPrivateModes;let e="none";switch(this._core.coreMouseService.activeProtocol){case"X10":e="x10";break;case"VT200":e="vt200";break;case"DRAG":e="drag";break;case"ANY":e="any"}return{applicationCursorKeysMode:s.applicationCursorKeys,applicationKeypadMode:s.applicationKeypad,bracketedPasteMode:s.bracketedPasteMode,insertMode:this._core.coreService.modes.insertMode,mouseTrackingMode:e,originMode:s.origin,reverseWraparoundMode:s.reverseWraparound,sendFocusMode:s.sendFocus,wraparoundMode:s.wraparound}}get options(){return this._publicOptions}set options(s){for(const e in s)this._publicOptions[e]=s[e]}blur(){this._core.blur()}focus(){this._core.focus()}input(s,e=!0){this._core.input(s,e)}resize(s,e){this._verifyIntegers(s,e),this._core.resize(s,e)}open(s){this._core.open(s)}attachCustomKeyEventHandler(s){this._core.attachCustomKeyEventHandler(s)}attachCustomWheelEventHandler(s){this._core.attachCustomWheelEventHandler(s)}registerLinkProvider(s){return this._core.registerLinkProvider(s)}registerCharacterJoiner(s){return this._checkProposedApi(),this._core.registerCharacterJoiner(s)}deregisterCharacterJoiner(s){this._checkProposedApi(),this._core.deregisterCharacterJoiner(s)}registerMarker(s=0){return this._verifyIntegers(s),this._core.registerMarker(s)}registerDecoration(s){var e,t,a;return this._checkProposedApi(),this._verifyPositiveIntegers((e=s.x)!=null?e:0,(t=s.width)!=null?t:0,(a=s.height)!=null?a:0),this._core.registerDecoration(s)}hasSelection(){return this._core.hasSelection()}select(s,e,t){this._verifyIntegers(s,e,t),this._core.select(s,e,t)}getSelection(){return this._core.getSelection()}getSelectionPosition(){return this._core.getSelectionPosition()}clearSelection(){this._core.clearSelection()}selectAll(){this._core.selectAll()}selectLines(s,e){this._verifyIntegers(s,e),this._core.selectLines(s,e)}dispose(){super.dispose()}scrollLines(s){this._verifyIntegers(s),this._core.scrollLines(s)}scrollPages(s){this._verifyIntegers(s),this._core.scrollPages(s)}scrollToTop(){this._core.scrollToTop()}scrollToBottom(){this._core.scrollToBottom()}scrollToLine(s){this._verifyIntegers(s),this._core.scrollToLine(s)}clear(){this._core.clear()}write(s,e){this._core.write(s,e)}writeln(s,e){this._core.write(s),this._core.write(`\r -`,e)}paste(s){this._core.paste(s)}refresh(s,e){this._verifyIntegers(s,e),this._core.refresh(s,e)}reset(){this._core.reset()}clearTextureAtlas(){this._core.clearTextureAtlas()}loadAddon(s){this._addonManager.loadAddon(this,s)}static get strings(){return r}_verifyIntegers(...s){for(const e of s)if(e===1/0||isNaN(e)||e%1!=0)throw new Error("This API only accepts integers")}_verifyPositiveIntegers(...s){for(const e of s)if(e&&(e===1/0||isNaN(e)||e%1!=0||e<0))throw new Error("This API only accepts positive integers")}}R.Terminal=l})(),T})())}},be={};function re(Y){var Z=be[Y];if(Z!==void 0)return Z.exports;var ee=be[Y]={exports:{}};return ye[Y](ee,ee.exports,re),ee.exports}re.n=Y=>{var Z=Y&&Y.__esModule?()=>Y.default:()=>Y;return re.d(Z,{a:Z}),Z},re.d=(Y,Z)=>{for(var ee in Z)re.o(Z,ee)&&!re.o(Y,ee)&&Object.defineProperty(Y,ee,{enumerable:!0,get:Z[ee]})},re.o=(Y,Z)=>Object.prototype.hasOwnProperty.call(Y,Z),(()=>{"use strict";var Y=re(591),Z=re.n(Y),ee=Object.defineProperty,_e=Object.getOwnPropertySymbols,pe=Object.prototype.hasOwnProperty,ue=Object.prototype.propertyIsEnumerable,ae=(q,T,R)=>T in q?ee(q,T,{enumerable:!0,configurable:!0,writable:!0,value:R}):q[T]=R,te=(q,T)=>{for(var R in T||(T={}))pe.call(T,R)&&ae(q,R,T[R]);if(_e)for(var R of _e(T))ue.call(T,R)&&ae(q,R,T[R]);return q};class J{constructor(){this._ASSEMBLY_NAME="XtermBlazor",this._terminals=new Map,this._addonList=new Map,this.getRows=T=>this.getTerminalById(T).rows,this.getCols=T=>this.getTerminalById(T).cols,this.getOptions=T=>this.getTerminalById(T).options,this.setOptions=(T,R)=>this.getTerminalById(T).options=R,this.blur=T=>this.getTerminalById(T).blur(),this.focus=T=>this.getTerminalById(T).focus(),this.input=(T,R,r)=>this.getTerminalById(T).input(R,r),this.resize=(T,R,r)=>this.getTerminalById(T).resize(R,r),this.hasSelection=T=>this.getTerminalById(T).hasSelection(),this.getSelection=T=>this.getTerminalById(T).getSelection(),this.getSelectionPosition=T=>this.getTerminalById(T).getSelectionPosition(),this.clearSelection=T=>this.getTerminalById(T).clearSelection(),this.select=(T,R,r,o)=>this.getTerminalById(T).select(R,r,o),this.selectAll=T=>this.getTerminalById(T).selectAll(),this.selectLines=(T,R,r)=>this.getTerminalById(T).selectLines(R,r),this.scrollLines=(T,R)=>this.getTerminalById(T).scrollLines(R),this.scrollPages=(T,R)=>this.getTerminalById(T).scrollPages(R),this.scrollToTop=T=>this.getTerminalById(T).scrollToTop(),this.scrollToBottom=T=>this.getTerminalById(T).scrollToBottom(),this.scrollToLine=(T,R)=>this.getTerminalById(T).scrollToLine(R),this.clear=T=>this.getTerminalById(T).clear(),this.write=(T,R)=>this.getTerminalById(T).write(R),this.writeln=(T,R)=>this.getTerminalById(T).writeln(R),this.paste=(T,R)=>this.getTerminalById(T).paste(R),this.refresh=(T,R,r)=>this.getTerminalById(T).refresh(R,r),this.reset=T=>this.getTerminalById(T).reset()}registerTerminal(T,R,r,o){const c=new Y.Terminal(r);c.onBinary(n=>DotNet.invokeMethodAsync(this._ASSEMBLY_NAME,"OnBinary",T,n)),c.onCursorMove(()=>DotNet.invokeMethodAsync(this._ASSEMBLY_NAME,"OnCursorMove",T)),c.onData(n=>DotNet.invokeMethodAsync(this._ASSEMBLY_NAME,"OnData",T,n)),c.onKey(({key:n,domEvent:d})=>DotNet.invokeMethodAsync(this._ASSEMBLY_NAME,"OnKey",T,{Key:n,DomEvent:this.parseKeyboardEvent(d)})),c.onLineFeed(()=>DotNet.invokeMethodAsync(this._ASSEMBLY_NAME,"OnLineFeed",T)),c.onScroll(n=>DotNet.invokeMethodAsync(this._ASSEMBLY_NAME,"OnScroll",T,n)),c.onSelectionChange(()=>DotNet.invokeMethodAsync(this._ASSEMBLY_NAME,"OnSelectionChange",T)),c.onRender(n=>DotNet.invokeMethodAsync(this._ASSEMBLY_NAME,"OnRender",T,n)),c.onResize(({cols:n,rows:d})=>DotNet.invokeMethodAsync(this._ASSEMBLY_NAME,"OnResize",T,{columns:n,rows:d})),c.onTitleChange(n=>DotNet.invokeMethodAsync(this._ASSEMBLY_NAME,"OnTitleChange",T,n)),c.onBell(()=>DotNet.invokeMethodAsync(this._ASSEMBLY_NAME,"OnBell",T)),c.attachCustomKeyEventHandler(n=>{try{return DotNet.invokeMethod(this._ASSEMBLY_NAME,"AttachCustomKeyEventHandler",T,this.parseKeyboardEvent(n))}catch{return DotNet.invokeMethodAsync(this._ASSEMBLY_NAME,"AttachCustomKeyEventHandler",T,this.parseKeyboardEvent(n)),this.getTerminalObjectById(T).customKeyEventHandler(n)}}),c.attachCustomWheelEventHandler(n=>{try{return DotNet.invokeMethod(this._ASSEMBLY_NAME,"AttachCustomWheelEventHandler",T,n)}catch{return DotNet.invokeMethodAsync(this._ASSEMBLY_NAME,"AttachCustomWheelEventHandler",T,n),this.getTerminalObjectById(T).customWheelEventHandler(n)}});const f=new Map;o.forEach(n=>{const d=Object.assign(Object.create(Object.getPrototypeOf(this._addonList.get(n))),this._addonList.get(n));c.loadAddon(d),f.set(n,d)}),c.open(R),this._terminals.set(T,{terminal:c,addons:f,customKeyEventHandler:n=>!0,customWheelEventHandler:n=>!0})}registerAddon(T,R){this._addonList.set(T,R)}registerAddons(T){Object.keys(T).forEach(R=>this.registerAddon(R,T[R]))}disposeTerminal(T){var R;return(R=this._terminals.get(T))==null||R.terminal.dispose(),this._terminals.delete(T)}invokeAddonFunction(T,R,r){return this.getTerminalObjectById(T).addons.get(R)[r](...arguments[3])}getTerminalObjectById(T){const R=this._terminals.get(T);if(!R)throw new Error(`Terminal with ID "${T}" not found.`);return R}getTerminalById(T){return this.getTerminalObjectById(T).terminal}parseKeyboardEvent(T){return te(te({},Object.entries(Object.getOwnPropertyDescriptors(KeyboardEvent.prototype)).filter(([r,o])=>typeof o.get=="function").reduce((r,[o,c])=>(r[o]=T[o],r),{})),{type:T.type})}}globalThis.XtermBlazor=new J})()})(); diff --git a/MoonlightServers.Frontend/wwwroot/js/addon-fit.js b/MoonlightServers.Frontend/wwwroot/js/addon-fit.js deleted file mode 100644 index 0388971..0000000 --- a/MoonlightServers.Frontend/wwwroot/js/addon-fit.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(self,(()=>(()=>{"use strict";var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,i=window.getComputedStyle(this._terminal.element.parentElement),o=parseInt(i.getPropertyValue("height")),s=Math.max(0,parseInt(i.getPropertyValue("width"))),n=window.getComputedStyle(this._terminal.element),l=o-(parseInt(n.getPropertyValue("padding-top"))+parseInt(n.getPropertyValue("padding-bottom"))),a=s-(parseInt(n.getPropertyValue("padding-right"))+parseInt(n.getPropertyValue("padding-left")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}}})(),e})())); -//# sourceMappingURL=addon-fit.js.map \ No newline at end of file diff --git a/MoonlightServers.Frontend/wwwroot/js/moonlightServers.js b/MoonlightServers.Frontend/wwwroot/js/moonlightServers.js deleted file mode 100644 index edc4412..0000000 --- a/MoonlightServers.Frontend/wwwroot/js/moonlightServers.js +++ /dev/null @@ -1,11 +0,0 @@ -window.moonlightServers = { - loadAddons: function () { - if(window.moonlightServers.consoleAddonsLoaded) - return; - - window.moonlightServers.consoleAddonsLoaded = true; - XtermBlazor.registerAddons({"addon-fit": new FitAddon.FitAddon()}); - } -} - -window.moonlightServers.consoleAddonsLoaded = false; \ No newline at end of file diff --git a/MoonlightServers.Shared/Constants/ServerPermissionConstants.cs b/MoonlightServers.Shared/Constants/ServerPermissionConstants.cs deleted file mode 100644 index 032b300..0000000 --- a/MoonlightServers.Shared/Constants/ServerPermissionConstants.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MoonlightServers.Shared.Constants; - -public class ServerPermissionConstants -{ - public const string Console = "console"; - public const string Power = "power"; - public const string Shares = "shares"; - public const string Files = "files"; - public const string Variables = "variables"; - public const string Settings = "settings"; -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Enums/FileParsers.cs b/MoonlightServers.Shared/Enums/FileParsers.cs deleted file mode 100644 index dfdc358..0000000 --- a/MoonlightServers.Shared/Enums/FileParsers.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MoonlightServers.Shared.Enums; - -public enum FileParsers -{ - File = 0, - Properties = 1 -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Enums/ServerPermissionLevel.cs b/MoonlightServers.Shared/Enums/ServerPermissionLevel.cs deleted file mode 100644 index 445a000..0000000 --- a/MoonlightServers.Shared/Enums/ServerPermissionLevel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace MoonlightServers.Shared.Enums; - -public enum ServerPermissionLevel -{ - None = -1, - Read = 0, - ReadWrite = 1 -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Enums/ServerState.cs b/MoonlightServers.Shared/Enums/ServerState.cs deleted file mode 100644 index ecb9f59..0000000 --- a/MoonlightServers.Shared/Enums/ServerState.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace MoonlightServers.Shared.Enums; - -public enum ServerState -{ - Offline = 0, - Starting = 1, - Online = 2, - Stopping = 3, - Installing = 4 -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Enums/StarVariableType.cs b/MoonlightServers.Shared/Enums/StarVariableType.cs deleted file mode 100644 index 638148e..0000000 --- a/MoonlightServers.Shared/Enums/StarVariableType.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MoonlightServers.Shared.Enums; - -public enum StarVariableType -{ - Text = 0, - Number = 1, - Toggle = 2, - Select = 3 -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Admin/NodeAllocations/CreateNodeAllocationRangeRequest.cs b/MoonlightServers.Shared/Http/Requests/Admin/NodeAllocations/CreateNodeAllocationRangeRequest.cs deleted file mode 100644 index 7597c17..0000000 --- a/MoonlightServers.Shared/Http/Requests/Admin/NodeAllocations/CreateNodeAllocationRangeRequest.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace MoonlightServers.Shared.Http.Requests.Admin.NodeAllocations; - -public class CreateNodeAllocationRangeRequest -{ - [Required(ErrorMessage = "You need to provide an ip address")] - [RegularExpression(@"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$", ErrorMessage = "You need to provide a valid ip address")] - public string IpAddress { get; set; } - - [Required(ErrorMessage = "You need to provide a start port")] - [Range(1, 65535, ErrorMessage = "You need to provide a valid start port")] - public int Start { get; set; } - - [Required(ErrorMessage = "You need to provide a end port")] - [Range(1, 65535, ErrorMessage = "You need to provide a valid end port")] - public int End { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Admin/NodeAllocations/CreateNodeAllocationRequest.cs b/MoonlightServers.Shared/Http/Requests/Admin/NodeAllocations/CreateNodeAllocationRequest.cs deleted file mode 100644 index 5e99491..0000000 --- a/MoonlightServers.Shared/Http/Requests/Admin/NodeAllocations/CreateNodeAllocationRequest.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace MoonlightServers.Shared.Http.Requests.Admin.NodeAllocations; - -public class CreateNodeAllocationRequest -{ - [Required(ErrorMessage = "You need to provide an ip address")] - [RegularExpression(@"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$", ErrorMessage = "You need to provide a valid ip address")] - public string IpAddress { get; set; } - - [Required(ErrorMessage = "You need to provide a port")] - [Range(1, 65535, ErrorMessage = "You need to provide a valid port")] - public int Port { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Admin/NodeAllocations/UpdateNodeAllocationRequest.cs b/MoonlightServers.Shared/Http/Requests/Admin/NodeAllocations/UpdateNodeAllocationRequest.cs deleted file mode 100644 index c3a1727..0000000 --- a/MoonlightServers.Shared/Http/Requests/Admin/NodeAllocations/UpdateNodeAllocationRequest.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace MoonlightServers.Shared.Http.Requests.Admin.NodeAllocations; - -public class UpdateNodeAllocationRequest -{ - [Required(ErrorMessage = "You need to provide an ip address")] - [RegularExpression(@"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$", ErrorMessage = "You need to provide a valid ip address")] - public string IpAddress { get; set; } - - [Required(ErrorMessage = "You need to provide a port")] - [Range(1, 65535, ErrorMessage = "You need to provide a valid port")] - public int Port { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Admin/Nodes/CreateNodeRequest.cs b/MoonlightServers.Shared/Http/Requests/Admin/Nodes/CreateNodeRequest.cs deleted file mode 100644 index 1ffb218..0000000 --- a/MoonlightServers.Shared/Http/Requests/Admin/Nodes/CreateNodeRequest.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace MoonlightServers.Shared.Http.Requests.Admin.Nodes; - -public class CreateNodeRequest -{ - [Required(ErrorMessage = "You need to provide a name")] - public string Name { get; set; } - - [Required(ErrorMessage = "You need to provide a fqdn")] - public string Fqdn { get; set; } - - [Required(ErrorMessage = "You need to provide a http port")] - [Range(1, 65535, ErrorMessage = "You need to provide a valid http port")] - public int HttpPort { get; set; } = 8080; - - [Required(ErrorMessage = "You need to provide a ftp port")] - [Range(1, 65535, ErrorMessage = "You need to provide a valid ftp port")] - public int FtpPort { get; set; } = 2021; -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Admin/Nodes/UpdateNodeRequest.cs b/MoonlightServers.Shared/Http/Requests/Admin/Nodes/UpdateNodeRequest.cs deleted file mode 100644 index fc2ac49..0000000 --- a/MoonlightServers.Shared/Http/Requests/Admin/Nodes/UpdateNodeRequest.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace MoonlightServers.Shared.Http.Requests.Admin.Nodes; - -public class UpdateNodeRequest -{ - [Required(ErrorMessage = "You need to provide a name")] - public string Name { get; set; } - - [Required(ErrorMessage = "You need to provide a fqdn")] - public string Fqdn { get; set; } - - [Required(ErrorMessage = "You need to provide a http port")] - [Range(1, 65535, ErrorMessage = "You need to provide a valid http port")] - public int HttpPort { get; set; } - - [Required(ErrorMessage = "You need to provide a ftp port")] - [Range(1, 65535, ErrorMessage = "You need to provide a valid ftp port")] - public int FtpPort { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Admin/ServerVariables/CreateServerVariableRequest.cs b/MoonlightServers.Shared/Http/Requests/Admin/ServerVariables/CreateServerVariableRequest.cs deleted file mode 100644 index bda7801..0000000 --- a/MoonlightServers.Shared/Http/Requests/Admin/ServerVariables/CreateServerVariableRequest.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace MoonlightServers.Shared.Http.Requests.Admin.ServerVariables; - -public class CreateServerVariableRequest -{ - [Required(ErrorMessage = "You need to provide a key for the variable")] - public string Key { get; set; } - public string Value { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Admin/ServerVariables/UpdateServerVariableRequest.cs b/MoonlightServers.Shared/Http/Requests/Admin/ServerVariables/UpdateServerVariableRequest.cs deleted file mode 100644 index 4972fa5..0000000 --- a/MoonlightServers.Shared/Http/Requests/Admin/ServerVariables/UpdateServerVariableRequest.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace MoonlightServers.Shared.Http.Requests.Admin.ServerVariables; - -public class UpdateServerVariableRequest -{ - [Required(ErrorMessage = "You need to provide a key for the variable")] - public string Key { get; set; } - public string Value { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Admin/Servers/CreateServerRequest.cs b/MoonlightServers.Shared/Http/Requests/Admin/Servers/CreateServerRequest.cs deleted file mode 100644 index a053fa5..0000000 --- a/MoonlightServers.Shared/Http/Requests/Admin/Servers/CreateServerRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using MoonlightServers.Shared.Http.Requests.Admin.ServerVariables; - -namespace MoonlightServers.Shared.Http.Requests.Admin.Servers; - -public class CreateServerRequest -{ - [Required(ErrorMessage = "You need to provide a name for the server")] - public string Name { get; set; } - public int OwnerId { get; set; } - - [Range(1, int.MaxValue, ErrorMessage = "You need to provide a valid cpu percent amount")] - public int Cpu { get; set; } - - [Range(1, int.MaxValue, ErrorMessage = "You need to provide a valid memory amount")] - public int Memory { get; set; } - - [Range(1, int.MaxValue, ErrorMessage = "You need to provide a valid disk amount")] - public int Disk { get; set; } - - public string? StartupOverride { get; set; } - - public int DockerImageIndex { get; set; } = -1; - - public int StarId { get; set; } - - public int NodeId { get; set; } - - public int[] AllocationIds { get; set; } = []; - public List Variables { get; set; } = new(); -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Admin/Servers/UpdateServerRequest.cs b/MoonlightServers.Shared/Http/Requests/Admin/Servers/UpdateServerRequest.cs deleted file mode 100644 index d3420c1..0000000 --- a/MoonlightServers.Shared/Http/Requests/Admin/Servers/UpdateServerRequest.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using MoonlightServers.Shared.Http.Requests.Admin.ServerVariables; - -namespace MoonlightServers.Shared.Http.Requests.Admin.Servers; - -public class UpdateServerRequest -{ - [Required(ErrorMessage = "You need to provide a name for the server")] - public string Name { get; set; } - public int OwnerId { get; set; } - - [Range(1, int.MaxValue, ErrorMessage = "You need to provide a valid cpu percent amount")] - public int Cpu { get; set; } - - [Range(1, int.MaxValue, ErrorMessage = "You need to provide a valid memory amount")] - public int Memory { get; set; } - - [Range(1, int.MaxValue, ErrorMessage = "You need to provide a valid disk amount")] - public int Disk { get; set; } - - public string? StartupOverride { get; set; } - - public int DockerImageIndex { get; set; } - - // TODO: Add star change - //public int StarId { get; set; } - - // TODO: Add transfer - //public int NodeId { get; set; } - - public int[] AllocationIds { get; set; } = []; - - public List Variables { get; set; } = new(); -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Admin/StarDockerImages/CreateStarDockerImageRequest.cs b/MoonlightServers.Shared/Http/Requests/Admin/StarDockerImages/CreateStarDockerImageRequest.cs deleted file mode 100644 index 655597d..0000000 --- a/MoonlightServers.Shared/Http/Requests/Admin/StarDockerImages/CreateStarDockerImageRequest.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace MoonlightServers.Shared.Http.Requests.Admin.StarDockerImages; - -public class CreateStarDockerImageRequest -{ - [Required(ErrorMessage = "You need to provide a display name")] - public string DisplayName { get; set; } - - [Required(ErrorMessage = "You need to specify a docker image identifier")] - [RegularExpression("^(?:(?=[^:\\/]{1,253})(?!-)[a-zA-Z0-9-]{1,63}(? Permissions { get; set; } = []; -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Client/Servers/Shares/UpdateShareRequest.cs b/MoonlightServers.Shared/Http/Requests/Client/Servers/Shares/UpdateShareRequest.cs deleted file mode 100644 index 064b525..0000000 --- a/MoonlightServers.Shared/Http/Requests/Client/Servers/Shares/UpdateShareRequest.cs +++ /dev/null @@ -1,8 +0,0 @@ -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.Shared.Http.Requests.Client.Servers.Shares; - -public record UpdateShareRequest -{ - public Dictionary Permissions { get; set; } = []; -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Client/Servers/Variables/UpdateServerVariableRangeRequest.cs b/MoonlightServers.Shared/Http/Requests/Client/Servers/Variables/UpdateServerVariableRangeRequest.cs deleted file mode 100644 index 30baf8a..0000000 --- a/MoonlightServers.Shared/Http/Requests/Client/Servers/Variables/UpdateServerVariableRangeRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace MoonlightServers.Shared.Http.Requests.Client.Servers.Variables; - -public class UpdateServerVariableRangeRequest -{ - [Required(ErrorMessage = "You need to provide variables")] - public UpdateServerVariableRequest[] Variables { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Client/Servers/Variables/UpdateServerVariableRequest.cs b/MoonlightServers.Shared/Http/Requests/Client/Servers/Variables/UpdateServerVariableRequest.cs deleted file mode 100644 index 40202d3..0000000 --- a/MoonlightServers.Shared/Http/Requests/Client/Servers/Variables/UpdateServerVariableRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace MoonlightServers.Shared.Http.Requests.Client.Servers.Variables; - -public class UpdateServerVariableRequest -{ - [Required(ErrorMessage = "You need to provide a key")] - public string Key { get; set; } - - [Required(ErrorMessage = "You need to provide a value", AllowEmptyStrings = true)] - public string Value { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/FormSubmitDto.cs b/MoonlightServers.Shared/Http/Requests/FormSubmitDto.cs new file mode 100644 index 0000000..6f38103 --- /dev/null +++ b/MoonlightServers.Shared/Http/Requests/FormSubmitDto.cs @@ -0,0 +1,8 @@ +using System.ComponentModel.DataAnnotations; + +namespace MoonlightServers.Shared.Http.Requests; + +public class FormSubmitDto +{ + [Required] [MaxLength(32)] public string TextField { get; set; } +} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Admin/NodeAllocations/NodeAllocationResponse.cs b/MoonlightServers.Shared/Http/Responses/Admin/NodeAllocations/NodeAllocationResponse.cs deleted file mode 100644 index b040024..0000000 --- a/MoonlightServers.Shared/Http/Responses/Admin/NodeAllocations/NodeAllocationResponse.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations; - -public class NodeAllocationResponse -{ - public int Id { get; set; } - - public string IpAddress { get; set; } - public int Port { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Admin/Nodes/NodeResponse.cs b/MoonlightServers.Shared/Http/Responses/Admin/Nodes/NodeResponse.cs deleted file mode 100644 index 26585bb..0000000 --- a/MoonlightServers.Shared/Http/Responses/Admin/Nodes/NodeResponse.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Admin.Nodes; - -public class NodeResponse -{ - public int Id { get; set; } - - public string Name { get; set; } - - public string Fqdn { get; set; } - public int HttpPort { get; set; } - public int FtpPort { get; set; } - - public bool EnableTransparentMode { get; set; } - public bool EnableDynamicFirewall { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Admin/Nodes/Statistics/DockerStatisticsResponse.cs b/MoonlightServers.Shared/Http/Responses/Admin/Nodes/Statistics/DockerStatisticsResponse.cs deleted file mode 100644 index b12e46d..0000000 --- a/MoonlightServers.Shared/Http/Responses/Admin/Nodes/Statistics/DockerStatisticsResponse.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Admin.Nodes.Statistics; - -public class DockerStatisticsResponse -{ - public string Version { get; set; } - - public long ImagesUsed { get; set; } - public long ImagesReclaimable { get; set; } - - public long ContainersUsed { get; set; } - public long ContainersReclaimable { get; set; } - - public long BuildCacheUsed { get; set; } - public long BuildCacheReclaimable { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Admin/Nodes/Statistics/StatisticsResponse.cs b/MoonlightServers.Shared/Http/Responses/Admin/Nodes/Statistics/StatisticsResponse.cs deleted file mode 100644 index 25f07e5..0000000 --- a/MoonlightServers.Shared/Http/Responses/Admin/Nodes/Statistics/StatisticsResponse.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Admin.Nodes.Statistics; - -public class StatisticsResponse -{ - public CpuData Cpu { get; set; } - public MemoryData Memory { get; set; } - - public DiskData[] Disks { get; set; } - - public record DiskData - { - public string Device { get; set; } - public string MountPath { get; set; } - public ulong DiskTotal { get; set; } - public ulong DiskFree { get; set; } - public ulong InodesTotal { get; set; } - public ulong InodesFree { get; set; } - } - - public record MemoryData - { - public long Total { get; set; } - public long Available { get; set; } - public long Free { get; set; } - public long Cached { get; set; } - public long SwapTotal { get; set; } - public long SwapFree { get; set; } - } - - public record CpuData - { - public string Model { get; set; } - public double Usage { get; set; } - public double[] UsagePerCore { get; set; } - } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Admin/Nodes/Sys/NodeSystemStatusResponse.cs b/MoonlightServers.Shared/Http/Responses/Admin/Nodes/Sys/NodeSystemStatusResponse.cs deleted file mode 100644 index 1984b38..0000000 --- a/MoonlightServers.Shared/Http/Responses/Admin/Nodes/Sys/NodeSystemStatusResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Admin.Nodes.Sys; - -public class NodeSystemStatusResponse -{ - public bool RoundtripSuccess { get; set; } - public bool RoundtripRemoteFailure { get; set; } - public TimeSpan RoundtripTime { get; set; } - public string? RoundtripError { get; set; } - public string Version { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Admin/ServerVariables/ServerVariableResponse.cs b/MoonlightServers.Shared/Http/Responses/Admin/ServerVariables/ServerVariableResponse.cs deleted file mode 100644 index 64d6857..0000000 --- a/MoonlightServers.Shared/Http/Responses/Admin/ServerVariables/ServerVariableResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Admin.ServerVariables; - -public class ServerVariableResponse -{ - public int Id { get; set; } - public string Key { get; set; } - public string Value { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Admin/Servers/ServerResponse.cs b/MoonlightServers.Shared/Http/Responses/Admin/Servers/ServerResponse.cs deleted file mode 100644 index c8ac9aa..0000000 --- a/MoonlightServers.Shared/Http/Responses/Admin/Servers/ServerResponse.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Admin.Servers; - -public class ServerResponse -{ - public int Id { get; set; } - - public string Name { get; set; } - public int OwnerId { get; set; } - public int Cpu { get; set; } - public int Memory { get; set; } - public int Disk { get; set; } - public bool UseVirtualDisk { get; set; } - public int Bandwidth { get; set; } - - public string? StartupOverride { get; set; } - - public int DockerImageIndex { get; set; } - - public int StarId { get; set; } - - public int NodeId { get; set; } - - public int[] AllocationIds { get; set; } = []; -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Admin/StarDockerImages/StarDockerImageResponse.cs b/MoonlightServers.Shared/Http/Responses/Admin/StarDockerImages/StarDockerImageResponse.cs deleted file mode 100644 index 90ac049..0000000 --- a/MoonlightServers.Shared/Http/Responses/Admin/StarDockerImages/StarDockerImageResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Admin.StarDockerImages; - -public class StarDockerImageResponse -{ - public int Id { get; set; } - - public string DisplayName { get; set; } - public string Identifier { get; set; } - public bool AutoPulling { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Admin/StarVariables/StarVariableResponse.cs b/MoonlightServers.Shared/Http/Responses/Admin/StarVariables/StarVariableResponse.cs deleted file mode 100644 index 64375a1..0000000 --- a/MoonlightServers.Shared/Http/Responses/Admin/StarVariables/StarVariableResponse.cs +++ /dev/null @@ -1,20 +0,0 @@ -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.Shared.Http.Responses.Admin.StarVariables; - -public class StarVariableResponse -{ - public int Id { get; set; } - - public string Name { get; set; } - public string Description { get; set; } - - public string Key { get; set; } - public string DefaultValue { get; set; } - - public bool AllowViewing { get; set; } - public bool AllowEditing { get; set; } - - public StarVariableType Type { get; set; } - public string? Filter { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Admin/Stars/StarResponse.cs b/MoonlightServers.Shared/Http/Responses/Admin/Stars/StarResponse.cs deleted file mode 100644 index ca6b0bb..0000000 --- a/MoonlightServers.Shared/Http/Responses/Admin/Stars/StarResponse.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Admin.Stars; - -public class StarResponse -{ - public int Id { get; set; } - - // Meta - public string Name { get; set; } - public string Version { get; set; } - public string Author { get; set; } - public string? UpdateUrl { get; set; } - public string? DonateUrl { get; set; } - - // Start and stop - public string StartupCommand { get; set; } - public string StopCommand { get; set; } - public string OnlineDetection { get; set; } - - // Install - public string InstallShell { get; set; } - public string InstallDockerImage { get; set; } - public string InstallScript { get; set; } - - // Misc - public int RequiredAllocations { get; set; } - public bool AllowDockerImageChange { get; set; } - public int DefaultDockerImage { get; set; } - public string ParseConfiguration { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Client/Servers/Allocations/AllocationDetailResponse.cs b/MoonlightServers.Shared/Http/Responses/Client/Servers/Allocations/AllocationDetailResponse.cs deleted file mode 100644 index c280717..0000000 --- a/MoonlightServers.Shared/Http/Responses/Client/Servers/Allocations/AllocationDetailResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Client.Servers.Allocations; - -public class AllocationDetailResponse -{ - public int Id { get; set; } - public string IpAddress { get; set; } - public int Port { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Client/Servers/Files/ServerFilesDownloadResponse.cs b/MoonlightServers.Shared/Http/Responses/Client/Servers/Files/ServerFilesDownloadResponse.cs deleted file mode 100644 index 7c77fb0..0000000 --- a/MoonlightServers.Shared/Http/Responses/Client/Servers/Files/ServerFilesDownloadResponse.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Client.Servers.Files; - -public class ServerFilesDownloadResponse -{ - public string DownloadUrl { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Client/Servers/Files/ServerFilesEntryResponse.cs b/MoonlightServers.Shared/Http/Responses/Client/Servers/Files/ServerFilesEntryResponse.cs deleted file mode 100644 index 7d54c40..0000000 --- a/MoonlightServers.Shared/Http/Responses/Client/Servers/Files/ServerFilesEntryResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Client.Servers.Files; - -public class ServerFilesEntryResponse -{ - public string Name { get; set; } - public bool IsFolder { get; set; } - public long Size { get; set; } - - public DateTime CreatedAt { get; set; } - public DateTime UpdatedAt { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Client/Servers/Files/ServerFilesUploadResponse.cs b/MoonlightServers.Shared/Http/Responses/Client/Servers/Files/ServerFilesUploadResponse.cs deleted file mode 100644 index 14a9f66..0000000 --- a/MoonlightServers.Shared/Http/Responses/Client/Servers/Files/ServerFilesUploadResponse.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Client.Servers.Files; - -public class ServerFilesUploadResponse -{ - public string UploadUrl { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerDetailResponse.cs b/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerDetailResponse.cs deleted file mode 100644 index ee5516d..0000000 --- a/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerDetailResponse.cs +++ /dev/null @@ -1,28 +0,0 @@ -using MoonlightServers.Shared.Enums; -using MoonlightServers.Shared.Http.Responses.Client.Servers.Allocations; - -namespace MoonlightServers.Shared.Http.Responses.Client.Servers; - -public record ServerDetailResponse -{ - public int Id { get; set; } - - public string Name { get; set; } - - public int Cpu { get; set; } - public int Memory { get; set; } - public int Disk { get; set; } - - public string NodeName { get; set; } - public string StarName { get; set; } - - public AllocationDetailResponse[] Allocations { get; set; } - - public ShareData? Share { get; set; } = null; - - public record ShareData - { - public string SharedBy { get; set; } - public Dictionary Permissions { get; set; } - } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerLogsResponse.cs b/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerLogsResponse.cs deleted file mode 100644 index da36ac7..0000000 --- a/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerLogsResponse.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Client.Servers; - -public class ServerLogsResponse -{ - public string[] Messages { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerStatsResponse.cs b/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerStatsResponse.cs deleted file mode 100644 index 2d159bf..0000000 --- a/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerStatsResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Client.Servers; - -public class ServerStatsResponse -{ - public double CpuUsage { get; set; } - public ulong MemoryUsage { get; set; } - public ulong NetworkRead { get; set; } - public ulong NetworkWrite { get; set; } - public ulong IoRead { get; set; } - public ulong IoWrite { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerStatusResponse.cs b/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerStatusResponse.cs deleted file mode 100644 index 1a256d8..0000000 --- a/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerStatusResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.Shared.Http.Responses.Client.Servers; - -public class ServerStatusResponse -{ - public ServerState State { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerWebSocketResponse.cs b/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerWebSocketResponse.cs deleted file mode 100644 index ba1d227..0000000 --- a/MoonlightServers.Shared/Http/Responses/Client/Servers/ServerWebSocketResponse.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MoonlightServers.Shared.Http.Responses.Client.Servers; - -public class ServerWebSocketResponse -{ - public string Target { get; set; } - public string AccessToken { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Client/Servers/Shares/ServerShareResponse.cs b/MoonlightServers.Shared/Http/Responses/Client/Servers/Shares/ServerShareResponse.cs deleted file mode 100644 index aa96d34..0000000 --- a/MoonlightServers.Shared/Http/Responses/Client/Servers/Shares/ServerShareResponse.cs +++ /dev/null @@ -1,10 +0,0 @@ -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.Shared.Http.Responses.Client.Servers.Shares; - -public class ServerShareResponse -{ - public int Id { get; set; } - public string Username { get; set; } - public Dictionary Permissions { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Client/Servers/Variables/ServerVariableDetailResponse.cs b/MoonlightServers.Shared/Http/Responses/Client/Servers/Variables/ServerVariableDetailResponse.cs deleted file mode 100644 index ae66b11..0000000 --- a/MoonlightServers.Shared/Http/Responses/Client/Servers/Variables/ServerVariableDetailResponse.cs +++ /dev/null @@ -1,14 +0,0 @@ -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.Shared.Http.Responses.Client.Servers.Variables; - -public class ServerVariableDetailResponse -{ - public string Key { get; set; } - public string Value { get; set; } - - public string Name { get; set; } - public string Description { get; set; } - public StarVariableType Type { get; set; } - public string? Filter { get; set; } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/FormResultDto.cs b/MoonlightServers.Shared/Http/Responses/FormResultDto.cs new file mode 100644 index 0000000..a508c8e --- /dev/null +++ b/MoonlightServers.Shared/Http/Responses/FormResultDto.cs @@ -0,0 +1,6 @@ +namespace MoonlightServers.Shared.Http.Responses; + +public class FormResultDto +{ + public string Result { get; set; } +} \ No newline at end of file diff --git a/MoonlightServers.Shared/Models/ParseConfiguration.cs b/MoonlightServers.Shared/Models/ParseConfiguration.cs deleted file mode 100644 index 4cdb5d2..0000000 --- a/MoonlightServers.Shared/Models/ParseConfiguration.cs +++ /dev/null @@ -1,17 +0,0 @@ -using MoonlightServers.Shared.Enums; - -namespace MoonlightServers.Shared.Models; - -public class ParseConfiguration -{ - public string File { get; set; } - public FileParsers Parser { get; set; } - - public List Entries { get; set; } = new(); - - public class ParseConfigurationEntry - { - public string Key { get; set; } - public string Value { get; set; } - } -} \ No newline at end of file diff --git a/MoonlightServers.Shared/MoonlightServers.Shared.csproj b/MoonlightServers.Shared/MoonlightServers.Shared.csproj index 88abb45..237d661 100644 --- a/MoonlightServers.Shared/MoonlightServers.Shared.csproj +++ b/MoonlightServers.Shared/MoonlightServers.Shared.csproj @@ -1,16 +1,9 @@  - net9.0 + net10.0 enable enable - - MoonlightServers.Shared - MoonlightServers.Shared - 2.1.0 - true - - diff --git a/MoonlightServers.Shared/SerializationContext.cs b/MoonlightServers.Shared/SerializationContext.cs new file mode 100644 index 0000000..c28d28d --- /dev/null +++ b/MoonlightServers.Shared/SerializationContext.cs @@ -0,0 +1,13 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using MoonlightServers.Shared.Http.Requests; +using MoonlightServers.Shared.Http.Responses; + +namespace MoonlightServers.Shared; + +[JsonSerializable(typeof(FormSubmitDto))] +[JsonSerializable(typeof(FormResultDto))] +[JsonSourceGenerationOptions(JsonSerializerDefaults.Web)] +public partial class SerializationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/MoonlightServers.sln b/MoonlightServers.sln deleted file mode 100644 index 16b3c37..0000000 --- a/MoonlightServers.sln +++ /dev/null @@ -1,58 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.ApiServer", "MoonlightServers.ApiServer\MoonlightServers.ApiServer.csproj", "{EAF24574-889F-41B8-85AA-7C0B856137BF}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.Frontend", "MoonlightServers.Frontend\MoonlightServers.Frontend.csproj", "{E7E39DA0-F920-4329-87FB-CBAF129B6835}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.Shared", "MoonlightServers.Shared\MoonlightServers.Shared.csproj", "{70FAFFFB-9EA6-4BB7-B4C0-A6BEF9684B32}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.Daemon", "MoonlightServers.Daemon\MoonlightServers.Daemon.csproj", "{5EAFCB32-500B-477C-937A-3DAD63C5BB1A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.DaemonShared", "MoonlightServers.DaemonShared\MoonlightServers.DaemonShared.csproj", "{15EBCC5D-2440-4B5B-A046-F8327E0C1CB8}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Runtime", "Runtime", "{7836BC34-096D-440C-9DF9-81116EACAABA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.ApiServer.Runtime", "MoonlightServers.ApiServer.Runtime\MoonlightServers.ApiServer.Runtime.csproj", "{9F4370FA-C38D-4E84-892F-12ED5DD40FC6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoonlightServers.Frontend.Runtime", "MoonlightServers.Frontend.Runtime\MoonlightServers.Frontend.Runtime.csproj", "{97BDDD86-6FE1-42E0-BF16-152DFABC8287}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EAF24574-889F-41B8-85AA-7C0B856137BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EAF24574-889F-41B8-85AA-7C0B856137BF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EAF24574-889F-41B8-85AA-7C0B856137BF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EAF24574-889F-41B8-85AA-7C0B856137BF}.Release|Any CPU.Build.0 = Release|Any CPU - {E7E39DA0-F920-4329-87FB-CBAF129B6835}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7E39DA0-F920-4329-87FB-CBAF129B6835}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7E39DA0-F920-4329-87FB-CBAF129B6835}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7E39DA0-F920-4329-87FB-CBAF129B6835}.Release|Any CPU.Build.0 = Release|Any CPU - {70FAFFFB-9EA6-4BB7-B4C0-A6BEF9684B32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {70FAFFFB-9EA6-4BB7-B4C0-A6BEF9684B32}.Debug|Any CPU.Build.0 = Debug|Any CPU - {70FAFFFB-9EA6-4BB7-B4C0-A6BEF9684B32}.Release|Any CPU.ActiveCfg = Release|Any CPU - {70FAFFFB-9EA6-4BB7-B4C0-A6BEF9684B32}.Release|Any CPU.Build.0 = Release|Any CPU - {5EAFCB32-500B-477C-937A-3DAD63C5BB1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5EAFCB32-500B-477C-937A-3DAD63C5BB1A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5EAFCB32-500B-477C-937A-3DAD63C5BB1A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5EAFCB32-500B-477C-937A-3DAD63C5BB1A}.Release|Any CPU.Build.0 = Release|Any CPU - {15EBCC5D-2440-4B5B-A046-F8327E0C1CB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {15EBCC5D-2440-4B5B-A046-F8327E0C1CB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {15EBCC5D-2440-4B5B-A046-F8327E0C1CB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {15EBCC5D-2440-4B5B-A046-F8327E0C1CB8}.Release|Any CPU.Build.0 = Release|Any CPU - {9F4370FA-C38D-4E84-892F-12ED5DD40FC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9F4370FA-C38D-4E84-892F-12ED5DD40FC6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9F4370FA-C38D-4E84-892F-12ED5DD40FC6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9F4370FA-C38D-4E84-892F-12ED5DD40FC6}.Release|Any CPU.Build.0 = Release|Any CPU - {97BDDD86-6FE1-42E0-BF16-152DFABC8287}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {97BDDD86-6FE1-42E0-BF16-152DFABC8287}.Debug|Any CPU.Build.0 = Debug|Any CPU - {97BDDD86-6FE1-42E0-BF16-152DFABC8287}.Release|Any CPU.ActiveCfg = Release|Any CPU - {97BDDD86-6FE1-42E0-BF16-152DFABC8287}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {9F4370FA-C38D-4E84-892F-12ED5DD40FC6} = {7836BC34-096D-440C-9DF9-81116EACAABA} - {97BDDD86-6FE1-42E0-BF16-152DFABC8287} = {7836BC34-096D-440C-9DF9-81116EACAABA} - EndGlobalSection -EndGlobal diff --git a/MoonlightServers.slnx b/MoonlightServers.slnx new file mode 100644 index 0000000..33239f1 --- /dev/null +++ b/MoonlightServers.slnx @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/build.sh b/build.sh deleted file mode 100644 index d3ddb84..0000000 --- a/build.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash - -# Define paths -PLUGIN_FILE="plugin.json" -TMP_DIR="./tmp" -WWWROOT_DIR="$TMP_DIR/wwwroot" -ASSEMBLY_BASE_DIR="./MoonlightServers.ApiServer/bin/Debug/net8.0" -STATIC_BASE_DIR="./MoonlightServers.Frontend/wwwroot" - -# Create tmp and wwwroot directories -mkdir -p "$TMP_DIR" -mkdir -p "$WWWROOT_DIR" - -# Function to copy files with preserved structure -copy_files_with_structure() { - local base_dir=$1 - local jq_query=$2 - local dest_root=$3 - local file_type=$4 - - echo "Copying $file_type..." - jq -r "$jq_query[]" "$PLUGIN_FILE" | while read -r rel_path; do - src_path="$base_dir/$rel_path" - dest_path="$dest_root/$rel_path" - - if [ -f "$src_path" ]; then - mkdir -p "$(dirname "$dest_path")" - cp "$src_path" "$dest_path" - echo "Copied $src_path -> $dest_path" - else - echo "Warning: $src_path not found" - fi - done -} - -# Copy assemblies to tmp root -copy_files_with_structure "$ASSEMBLY_BASE_DIR" ".assemblies.apiServer" "$TMP_DIR" "apiServer assemblies" -copy_files_with_structure "$ASSEMBLY_BASE_DIR" ".assemblies.client" "$TMP_DIR" "client assemblies" - -# Copy static files to tmp/wwwroot -copy_files_with_structure "$STATIC_BASE_DIR" ".bundledStyles" "$WWWROOT_DIR" "bundled styles" -copy_files_with_structure "$STATIC_BASE_DIR" ".scripts" "$WWWROOT_DIR" "scripts" - -# Copy the plugin.json into tmp -cp "$PLUGIN_FILE" "$TMP_DIR/plugin.json" -echo "Copied $PLUGIN_FILE to $TMP_DIR/plugin.json" - -# Extract name from plugin.json for the zip file -PLUGIN_NAME=$(jq -r '.name' "$PLUGIN_FILE") -ZIP_NAME="${PLUGIN_NAME}.zip" - -# Create zip file (placed next to plugin.json) -echo "Creating zip file: $ZIP_NAME" -(cd "$TMP_DIR" && zip -r "../$ZIP_NAME" .) - -# Cleanup -rm -rf "$TMP_DIR" -echo "Cleaned up tmp directory." - -echo "Done! Created $ZIP_NAME" \ No newline at end of file