Compare commits
47 Commits
main
...
ecf0e01914
| Author | SHA1 | Date | |
|---|---|---|---|
| ecf0e01914 | |||
| 6d447a0ff9 | |||
| ba5e364c05 | |||
| 2a2ce28b5f | |||
| 91887ec047 | |||
| 2fc371c219 | |||
| 9470e06c0f | |||
| 2f8665f1d4 | |||
| 4d4f35e2be | |||
| 609ea3a443 | |||
| 3e19b29cde | |||
| 1475b89660 | |||
| 3bb9a08630 | |||
| 252c4103f3 | |||
| e7b1e77d0a | |||
| 7c5dc657dc | |||
| 2d1b48b0d4 | |||
| 52dbd13fb5 | |||
| f6b71f4de6 | |||
| 85392208c4 | |||
| 91fb15a03e | |||
| 32f447d268 | |||
| 160446eed0 | |||
| b90100d250 | |||
| 282096595d | |||
| 348e9560ab | |||
| 7587a7e8e3 | |||
| 7c2bc9d19b | |||
| 2e4c933fbe | |||
| 5c170935b4 | |||
| eaf8c36f7f | |||
| bb81ca9674 | |||
| f57d33cb1e | |||
| b546a168d2 | |||
| 84b3d1caf6 | |||
| 0bef60dbc8 | |||
| bdc4ad8265 | |||
| 431cdcb260 | |||
| 1f94752c54 | |||
| a2db7be26f | |||
| 265a4b280b | |||
| f8c11b2dd8 | |||
| e83d1351cb | |||
| 61253919cf | |||
| 383d4bb24b | |||
| f22f0c0e51 | |||
| 514f862a9d |
69
.gitea/workflows/publish-nuget.yml
Normal file
69
.gitea/workflows/publish-nuget.yml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
name: "Dev Publish: Nuget"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- v2.1
|
||||||
|
paths:
|
||||||
|
- 'MoonlightServers.*/*.csproj'
|
||||||
|
|
||||||
|
env:
|
||||||
|
NUGET_SOURCE: https://git.battlestati.one/api/packages/Moonlight-Panel/nuget/index.json
|
||||||
|
NUGET_PUBLIC: https://api.nuget.org/v3/index.json
|
||||||
|
CONFIGURATION: Debug
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
name: Publish ${{ matrix.project }}
|
||||||
|
runs-on: linux_amd64
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
project:
|
||||||
|
- MoonlightServers.Api
|
||||||
|
- MoonlightServers.Shared
|
||||||
|
- MoonlightServers.DaemonShared
|
||||||
|
- MoonlightServers.Frontend
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Restore NuGet packages
|
||||||
|
run: >
|
||||||
|
dotnet restore ${{ matrix.project }}
|
||||||
|
--source ${{ env.NUGET_PUBLIC }}
|
||||||
|
--source ${{ env.NUGET_SOURCE }}
|
||||||
|
|
||||||
|
# Frontend requires a host build + Tailwind compilation first
|
||||||
|
- name: Build frontend host (Frontend only)
|
||||||
|
if: matrix.project == 'MoonlightServers.Frontend'
|
||||||
|
run: >
|
||||||
|
dotnet build Hosts/MoonlightServers.Frontend.Host
|
||||||
|
--configuration ${{ env.CONFIGURATION }}
|
||||||
|
|
||||||
|
- name: Build Tailwind styles (Frontend only)
|
||||||
|
if: matrix.project == 'MoonlightServers.Frontend'
|
||||||
|
working-directory: Hosts/MoonlightServers.Frontend.Host/Styles
|
||||||
|
run: npm install && npm run build
|
||||||
|
|
||||||
|
- name: Build project
|
||||||
|
run: >
|
||||||
|
dotnet build ${{ matrix.project }}
|
||||||
|
--configuration ${{ env.CONFIGURATION }}
|
||||||
|
--no-restore
|
||||||
|
|
||||||
|
- name: Pack NuGet package
|
||||||
|
run: >
|
||||||
|
dotnet pack ${{ matrix.project }}
|
||||||
|
--configuration ${{ env.CONFIGURATION }}
|
||||||
|
--output ./artifacts
|
||||||
|
--no-build
|
||||||
|
|
||||||
|
- name: Push NuGet package
|
||||||
|
run: >
|
||||||
|
dotnet nuget push ./artifacts/*.nupkg
|
||||||
|
--skip-duplicate
|
||||||
|
--source ${{ env.NUGET_SOURCE }}
|
||||||
|
--api-key ${{ secrets.ACCESS_TOKEN }}
|
||||||
51
.gitignore
vendored
51
.gitignore
vendored
@@ -301,7 +301,7 @@ node_modules/
|
|||||||
*.dsw
|
*.dsw
|
||||||
*.dsp
|
*.dsp
|
||||||
|
|
||||||
# Visual Studio 6 technical files
|
# Visual Studio 6 technical files
|
||||||
*.ncb
|
*.ncb
|
||||||
*.aps
|
*.aps
|
||||||
|
|
||||||
@@ -395,44 +395,15 @@ FodyWeavers.xsd
|
|||||||
*.msp
|
*.msp
|
||||||
|
|
||||||
# JetBrains Rider
|
# JetBrains Rider
|
||||||
*.sln.iml
|
**/.idea/**
|
||||||
|
|
||||||
# User-specific stuff
|
# Style builds
|
||||||
.idea/**/workspace.xml
|
**/style.min.css
|
||||||
.idea/**/tasks.xml
|
**/package-lock.json
|
||||||
.idea/**/usage.statistics.xml
|
**/bun.lock
|
||||||
.idea/**/dictionaries
|
|
||||||
.idea/**/shelf
|
|
||||||
|
|
||||||
# Generated files
|
# Secrets
|
||||||
.idea/**/contentModel.xml
|
**/.env
|
||||||
|
**/appsettings.json
|
||||||
# Sensitive or high-churn files
|
**/appsettings.Development.json
|
||||||
.idea/**/dataSources/
|
**/storage
|
||||||
.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
|
|
||||||
|
|||||||
6
Hosts/MoonlightServers.Api.Host/Api.props
Normal file
6
Hosts/MoonlightServers.Api.Host/Api.props
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<Project>
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- Put your plugin references here -->
|
||||||
|
<!-- E.g. <PackageReference Include="MoonlightServers.Api" Version="2.1.0" /> -->
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.5"/>
|
||||||
|
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.5">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
|
||||||
|
<PackageReference Include="SimplePlugin" Version="1.0.2"/>
|
||||||
|
<PackageReference Include="SimplePlugin.Abstractions" Version="1.0.2"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\MoonlightServers.Api\MoonlightServers.Api.csproj" />
|
||||||
|
<ProjectReference Include="..\MoonlightServers.Frontend.Host\MoonlightServers.Frontend.Host.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Import Project="Api.props"/>
|
||||||
|
</Project>
|
||||||
9
Hosts/MoonlightServers.Api.Host/Program.cs
Normal file
9
Hosts/MoonlightServers.Api.Host/Program.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using Moonlight.Api;
|
||||||
|
using SimplePlugin.Generated;
|
||||||
|
|
||||||
|
var plugins = PluginRegistry
|
||||||
|
.Modules
|
||||||
|
.OfType<MoonlightPlugin>()
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
await StartupHandler.RunAsync(args, plugins);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": false,
|
||||||
|
"applicationUrl": "http://localhost:5031",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
Hosts/MoonlightServers.Api.Host/appsettings.Example.json
Normal file
23
Hosts/MoonlightServers.Api.Host/appsettings.Example.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
Hosts/MoonlightServers.Frontend.Host/Frontend.props
Normal file
6
Hosts/MoonlightServers.Frontend.Host/Frontend.props
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<Project>
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- Put your plugin references here -->
|
||||||
|
<!-- E.g. <PackageReference Include="MoonlightServers.Frontend" Version="2.1.0" /> -->
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.5"/>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.5" PrivateAssets="all"/>
|
||||||
|
<PackageReference Include="SimplePlugin" Version="1.0.2"/>
|
||||||
|
<PackageReference Include="SimplePlugin.Abstractions" Version="1.0.2"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\MoonlightServers.Frontend\MoonlightServers.Frontend.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Import Project="Frontend.props"/>
|
||||||
|
</Project>
|
||||||
9
Hosts/MoonlightServers.Frontend.Host/Program.cs
Normal file
9
Hosts/MoonlightServers.Frontend.Host/Program.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using Moonlight.Frontend;
|
||||||
|
using SimplePlugin.Generated;
|
||||||
|
|
||||||
|
var plugins = PluginRegistry
|
||||||
|
.Modules
|
||||||
|
.OfType<MoonlightPlugin>()
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
await StartupHandler.RunAsync(args, plugins);
|
||||||
@@ -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('../../../MoonlightServers.Frontend/Styles', { recursive: true });
|
||||||
|
fs.writeFileSync('../../../MoonlightServers.Frontend/Styles/MoonlightServers.Frontend.map', classArray.join('\n'));
|
||||||
|
console.log(`Extracted classes ${classArray.length}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
extractTailwindClasses.postcss = true;
|
||||||
19
Hosts/MoonlightServers.Frontend.Host/Styles/package.json
Normal file
19
Hosts/MoonlightServers.Frontend.Host/Styles/package.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
};
|
||||||
31
Hosts/MoonlightServers.Frontend.Host/Styles/styles.css
Normal file
31
Hosts/MoonlightServers.Frontend.Host/Styles/styles.css
Normal file
@@ -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/**/*.map";
|
||||||
|
|
||||||
|
@source "../../../MoonlightServers.Api/**/*.razor";
|
||||||
|
@source "../../../MoonlightServers.Api/**/*.cs";
|
||||||
|
@source "../../../MoonlightServers.Api/**/*.html";
|
||||||
|
|
||||||
|
@source "../../../MoonlightServers.Frontend/**/*.razor";
|
||||||
|
@source "../../../MoonlightServers.Frontend/**/*.cs";
|
||||||
|
@source "../../../MoonlightServers.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
132
Hosts/MoonlightServers.Frontend.Host/Styles/theme.css
Normal file
132
Hosts/MoonlightServers.Frontend.Host/Styles/theme.css
Normal file
@@ -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);
|
||||||
|
}
|
||||||
111
Hosts/MoonlightServers.Frontend.Host/wwwroot/index.html
Normal file
111
Hosts/MoonlightServers.Frontend.Host/wwwroot/index.html
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class="dark">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Moonlight</title>
|
||||||
|
<base href="/" />
|
||||||
|
<link rel="preload" id="webassembly" />
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=fallback" />
|
||||||
|
<link rel="stylesheet" href="style.min.css" />
|
||||||
|
<script type="importmap"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.frontendConfig = {
|
||||||
|
STYLE_TAG_ID: 'theme-variables',
|
||||||
|
configuration: {},
|
||||||
|
|
||||||
|
applyTheme: function(cssContent) {
|
||||||
|
// Find or create the style tag
|
||||||
|
let styleTag = document.getElementById(this.STYLE_TAG_ID);
|
||||||
|
|
||||||
|
if (!styleTag) {
|
||||||
|
styleTag = document.createElement('style');
|
||||||
|
styleTag.id = this.STYLE_TAG_ID;
|
||||||
|
document.head.appendChild(styleTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the style tag content
|
||||||
|
styleTag.textContent = cssContent;
|
||||||
|
},
|
||||||
|
|
||||||
|
reloadConfiguration: function (){
|
||||||
|
try {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', '/api/frontend/config', false);
|
||||||
|
xhr.send(null);
|
||||||
|
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
this.configuration = JSON.parse(xhr.responseText);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load initial theme:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getConfiguration: function (){
|
||||||
|
return this.configuration;
|
||||||
|
},
|
||||||
|
|
||||||
|
reload: function () {
|
||||||
|
this.reloadConfiguration();
|
||||||
|
|
||||||
|
document.title = this.configuration.name;
|
||||||
|
this.applyTheme(this.configuration.themeCss);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.frontendConfig.reload();
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="bg-background text-foreground">
|
||||||
|
<div id="app">
|
||||||
|
<div class="h-screen w-full flex items-center justify-center">
|
||||||
|
|
||||||
|
<div class="flex min-w-0 flex-1 flex-col items-center justify-center gap-3 rounded-lg border-dashed p-6 text-center text-balance md:p-12">
|
||||||
|
<div class="flex max-w-sm flex-col items-center gap-2 text-center">
|
||||||
|
<div class="flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0 bg-muted text-foreground size-10 rounded-lg [&_svg:not([class*='size-'])]:size-6">
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
class="lucide lucide-zap-icon lucide-zap size-6">
|
||||||
|
<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-lg font-medium tracking-tight">
|
||||||
|
Loading application
|
||||||
|
</div>
|
||||||
|
<div class="flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance">
|
||||||
|
|
||||||
|
<div class="bg-primary/20 w-full relative h-2 overflow-hidden rounded-full">
|
||||||
|
<div class="bg-primary h-full w-[var(--blazor-load-percentage,0)] flex-1 transition-all">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="blazor-error-ui">
|
||||||
|
An unhandled error has occurred.
|
||||||
|
<a href="." class="reload">Reload</a>
|
||||||
|
<span class="dismiss">🗙</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/_content/ShadcnBlazor/interop.js" defer></script>
|
||||||
|
<script src="/_content/ShadcnBlazor.Extras/interop.js" defer></script>
|
||||||
|
<script src="/_content/ShadcnBlazor.Extras/codemirror-bundle.js" defer></script>
|
||||||
|
|
||||||
|
<script src="/_content/Moonlight.Frontend/chart.umd.js" defer></script>
|
||||||
|
|
||||||
|
<script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
157
MoonlightServers.Api/Admin/Nodes/CrudController.cs
Normal file
157
MoonlightServers.Api/Admin/Nodes/CrudController.cs
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Caching.Hybrid;
|
||||||
|
using Moonlight.Shared.Shared;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Implementations.NodeToken;
|
||||||
|
using MoonlightServers.Shared;
|
||||||
|
using MoonlightServers.Shared.Admin.Nodes;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Admin.Nodes;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/admin/servers/nodes")]
|
||||||
|
public class CrudController : Controller
|
||||||
|
{
|
||||||
|
private readonly DatabaseRepository<Node> DatabaseRepository;
|
||||||
|
private readonly HybridCache Cache;
|
||||||
|
|
||||||
|
public CrudController(
|
||||||
|
DatabaseRepository<Node> databaseRepository,
|
||||||
|
HybridCache cache
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DatabaseRepository = databaseRepository;
|
||||||
|
Cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize(Policy = Permissions.Nodes.View)]
|
||||||
|
public async Task<ActionResult<PagedData<NodeDto>>> GetAsync(
|
||||||
|
[FromQuery] int startIndex,
|
||||||
|
[FromQuery] int length,
|
||||||
|
[FromQuery] FilterOptions? filterOptions
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Validation
|
||||||
|
if (startIndex < 0)
|
||||||
|
return Problem("Invalid start index specified", statusCode: 400);
|
||||||
|
|
||||||
|
if (length is < 1 or > 100)
|
||||||
|
return Problem("Invalid length specified");
|
||||||
|
|
||||||
|
// Query building
|
||||||
|
|
||||||
|
var query = DatabaseRepository
|
||||||
|
.Query();
|
||||||
|
|
||||||
|
// Filters
|
||||||
|
if (filterOptions != null)
|
||||||
|
{
|
||||||
|
foreach (var filterOption in filterOptions.Filters)
|
||||||
|
{
|
||||||
|
query = filterOption.Key switch
|
||||||
|
{
|
||||||
|
nameof(Node.Name) =>
|
||||||
|
query.Where(role => EF.Functions.ILike(role.Name, $"%{filterOption.Value}%")),
|
||||||
|
|
||||||
|
_ => query
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
var data = await query
|
||||||
|
.OrderBy(x => x.Id)
|
||||||
|
.ProjectToDto()
|
||||||
|
.Skip(startIndex)
|
||||||
|
.Take(length)
|
||||||
|
.ToArrayAsync();
|
||||||
|
|
||||||
|
var total = await query.CountAsync();
|
||||||
|
|
||||||
|
return new PagedData<NodeDto>(data, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id:int}")]
|
||||||
|
[Authorize(Policy = Permissions.Nodes.View)]
|
||||||
|
public async Task<ActionResult<NodeDto>> GetAsync([FromRoute] int id)
|
||||||
|
{
|
||||||
|
var node = await DatabaseRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
|
if (node == null)
|
||||||
|
return Problem("No node with this id found", statusCode: 404);
|
||||||
|
|
||||||
|
return NodeMapper.ToDto(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Authorize(Policy = Permissions.Nodes.Create)]
|
||||||
|
public async Task<ActionResult<NodeDto>> CreateAsync([FromBody] CreateNodeDto request)
|
||||||
|
{
|
||||||
|
var node = NodeMapper.ToEntity(request);
|
||||||
|
|
||||||
|
node.TokenId = GenerateString(10);
|
||||||
|
node.Token = GenerateString(64);
|
||||||
|
|
||||||
|
var finalRole = await DatabaseRepository.AddAsync(node);
|
||||||
|
|
||||||
|
return NodeMapper.ToDto(finalRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id:int}")]
|
||||||
|
[Authorize(Policy = Permissions.Nodes.Edit)]
|
||||||
|
public async Task<ActionResult<NodeDto>> UpdateAsync([FromRoute] int id, [FromBody] UpdateNodeDto request)
|
||||||
|
{
|
||||||
|
var node = await DatabaseRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
|
if (node == null)
|
||||||
|
return Problem("No node with this id found", statusCode: 404);
|
||||||
|
|
||||||
|
NodeMapper.Merge(node, request);
|
||||||
|
|
||||||
|
await DatabaseRepository.UpdateAsync(node);
|
||||||
|
|
||||||
|
return NodeMapper.ToDto(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id:int}")]
|
||||||
|
[Authorize(Policy = Permissions.Nodes.Delete)]
|
||||||
|
public async Task<ActionResult> DeleteAsync([FromRoute] int id)
|
||||||
|
{
|
||||||
|
var node = await DatabaseRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
|
if (node == null)
|
||||||
|
return Problem("No node with this id found", statusCode: 404);
|
||||||
|
|
||||||
|
await DatabaseRepository.RemoveAsync(node);
|
||||||
|
|
||||||
|
// Remove cache for node token auth scheme
|
||||||
|
await Cache.RemoveAsync(string.Format(NodeTokenSchemeHandler.CacheKeyFormat, node.TokenId));
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GenerateString(int length)
|
||||||
|
{
|
||||||
|
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
var stringBuilder = new StringBuilder();
|
||||||
|
var random = new Random();
|
||||||
|
|
||||||
|
for (var i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
stringBuilder.Append(chars[random.Next(chars.Length)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringBuilder.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
51
MoonlightServers.Api/Admin/Nodes/HealthController.cs
Normal file
51
MoonlightServers.Api/Admin/Nodes/HealthController.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
using MoonlightServers.Shared;
|
||||||
|
using MoonlightServers.Shared.Admin.Nodes;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Admin.Nodes;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Authorize(Policy = Permissions.Nodes.View)]
|
||||||
|
[Route("api/admin/servers/nodes/{id:int}/health")]
|
||||||
|
public class HealthController : Controller
|
||||||
|
{
|
||||||
|
private readonly DatabaseRepository<Node> DatabaseRepository;
|
||||||
|
private readonly NodeService NodeService;
|
||||||
|
private readonly ILogger<HealthController> Logger;
|
||||||
|
|
||||||
|
public HealthController(
|
||||||
|
DatabaseRepository<Node> databaseRepository,
|
||||||
|
NodeService nodeService,
|
||||||
|
ILogger<HealthController> logger
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DatabaseRepository = databaseRepository;
|
||||||
|
NodeService = nodeService;
|
||||||
|
Logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<NodeHealthDto>> GetAsync([FromRoute] int id)
|
||||||
|
{
|
||||||
|
var node = await DatabaseRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
|
if (node == null)
|
||||||
|
return Problem("No node with this id found", statusCode: 404);
|
||||||
|
|
||||||
|
var health = await NodeService.GetHealthAsync(node);
|
||||||
|
|
||||||
|
return new NodeHealthDto()
|
||||||
|
{
|
||||||
|
StatusCode = health.StatusCode,
|
||||||
|
RemoteStatusCode = health.Dto?.RemoteStatusCode ?? 0,
|
||||||
|
IsHealthy = health is { StatusCode: >= 200 and <= 299, Dto.RemoteStatusCode: >= 200 and <= 299 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
20
MoonlightServers.Api/Admin/Nodes/NodeMapper.cs
Normal file
20
MoonlightServers.Api/Admin/Nodes/NodeMapper.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
using MoonlightServers.DaemonShared.Http.Daemon;
|
||||||
|
using MoonlightServers.Shared.Admin.Nodes;
|
||||||
|
using Riok.Mapperly.Abstractions;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Admin.Nodes;
|
||||||
|
|
||||||
|
[Mapper]
|
||||||
|
[SuppressMessage("Mapper", "RMG020:No members are mapped in an object mapping")]
|
||||||
|
[SuppressMessage("Mapper", "RMG012:No members are mapped in an object mapping")]
|
||||||
|
public static partial class NodeMapper
|
||||||
|
{
|
||||||
|
public static partial NodeDto ToDto(Node node);
|
||||||
|
public static partial IQueryable<NodeDto> ProjectToDto(this IQueryable<Node> nodes);
|
||||||
|
public static partial Node ToEntity(CreateNodeDto dto);
|
||||||
|
public static partial void Merge([MappingTarget] Node node, UpdateNodeDto dto);
|
||||||
|
|
||||||
|
public static partial NodeStatisticsDto ToDto(SystemStatisticsDto dto);
|
||||||
|
}
|
||||||
145
MoonlightServers.Api/Admin/Nodes/NodeService.cs
Normal file
145
MoonlightServers.Api/Admin/Nodes/NodeService.cs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
using MoonlightServers.DaemonShared.Http;
|
||||||
|
using MoonlightServers.DaemonShared.Http.Daemon;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Admin.Nodes;
|
||||||
|
|
||||||
|
public class NodeService
|
||||||
|
{
|
||||||
|
private readonly IHttpClientFactory ClientFactory;
|
||||||
|
private readonly ILogger<NodeService> Logger;
|
||||||
|
public NodeService(IHttpClientFactory clientFactory, ILogger<NodeService> logger)
|
||||||
|
{
|
||||||
|
ClientFactory = clientFactory;
|
||||||
|
Logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<NodeHealthStatus> GetHealthAsync(Node node)
|
||||||
|
{
|
||||||
|
var client = ClientFactory.CreateClient();
|
||||||
|
|
||||||
|
var request = CreateBaseRequest(node, HttpMethod.Get, "api/health");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
return new NodeHealthStatus((int)response.StatusCode, null);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var health = await response
|
||||||
|
.Content
|
||||||
|
.ReadFromJsonAsync<HealthDto>(SerializationContext.Default.Options);
|
||||||
|
|
||||||
|
return new NodeHealthStatus((int)response.StatusCode, health);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.LogTrace(e, "An unhandled error occured while processing health response of node {id}", node.Id);
|
||||||
|
|
||||||
|
return new NodeHealthStatus((int)response.StatusCode, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.LogTrace(e, "An error occured while fetching health status of node {id}", node.Id);
|
||||||
|
return new NodeHealthStatus(0, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SystemStatisticsDto> GetStatisticsAsync(Node node)
|
||||||
|
{
|
||||||
|
var client = ClientFactory.CreateClient();
|
||||||
|
|
||||||
|
var request = CreateBaseRequest(node, HttpMethod.Get, "api/system/statistics");
|
||||||
|
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
await EnsureSuccessAsync(response);
|
||||||
|
|
||||||
|
return (await response.Content.ReadFromJsonAsync<SystemStatisticsDto>(SerializationContext.Default.Options))!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HttpRequestMessage CreateBaseRequest(
|
||||||
|
Node node,
|
||||||
|
[StringSyntax(StringSyntaxAttribute.Uri)]
|
||||||
|
HttpMethod method,
|
||||||
|
string endpoint
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var request = new HttpRequestMessage();
|
||||||
|
|
||||||
|
request.Headers.Add(HeaderNames.Authorization, node.Token);
|
||||||
|
request.RequestUri = new Uri(new Uri(node.HttpEndpointUrl), endpoint);
|
||||||
|
request.Method = method;
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task EnsureSuccessAsync(HttpResponseMessage message)
|
||||||
|
{
|
||||||
|
if (message.IsSuccessStatusCode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var problemDetails = await message.Content.ReadFromJsonAsync<ProblemDetails>(
|
||||||
|
SerializationContext.Default.Options
|
||||||
|
);
|
||||||
|
|
||||||
|
if (problemDetails == null)
|
||||||
|
{
|
||||||
|
// If we cant handle problem details, we handle it natively
|
||||||
|
message.EnsureSuccessStatusCode();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse into exception
|
||||||
|
throw new NodeException(
|
||||||
|
problemDetails.Type,
|
||||||
|
problemDetails.Title,
|
||||||
|
problemDetails.Status,
|
||||||
|
problemDetails.Detail,
|
||||||
|
problemDetails.Errors
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (JsonException)
|
||||||
|
{
|
||||||
|
// If we cant handle problem details, we handle it natively
|
||||||
|
message.EnsureSuccessStatusCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record NodeHealthStatus(int StatusCode, HealthDto? Dto);
|
||||||
|
|
||||||
|
public class NodeException : Exception
|
||||||
|
{
|
||||||
|
public string Type { get; }
|
||||||
|
public string Title { get; }
|
||||||
|
public int Status { get; }
|
||||||
|
public string? Detail { get; }
|
||||||
|
public Dictionary<string, string[]>? Errors { get; }
|
||||||
|
|
||||||
|
public NodeException(
|
||||||
|
string type,
|
||||||
|
string title,
|
||||||
|
int status,
|
||||||
|
string? detail = null,
|
||||||
|
Dictionary<string, string[]>? errors = null)
|
||||||
|
: base(detail ?? title)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
Title = title;
|
||||||
|
Status = status;
|
||||||
|
Detail = detail;
|
||||||
|
Errors = errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
40
MoonlightServers.Api/Admin/Nodes/StatisticsController.cs
Normal file
40
MoonlightServers.Api/Admin/Nodes/StatisticsController.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
using MoonlightServers.Shared;
|
||||||
|
using MoonlightServers.Shared.Admin.Nodes;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Admin.Nodes;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Authorize(Policy = Permissions.Nodes.View)]
|
||||||
|
[Route("api/admin/servers/nodes/{id:int}/statistics")]
|
||||||
|
public class StatisticsController : Controller
|
||||||
|
{
|
||||||
|
private readonly NodeService NodeService;
|
||||||
|
private readonly DatabaseRepository<Node> NodeRepository;
|
||||||
|
|
||||||
|
public StatisticsController(NodeService nodeService, DatabaseRepository<Node> nodeRepository)
|
||||||
|
{
|
||||||
|
NodeService = nodeService;
|
||||||
|
NodeRepository = nodeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<NodeStatisticsDto>> GetAsync([FromRoute] int id)
|
||||||
|
{
|
||||||
|
var node = await NodeRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
|
if (node == null)
|
||||||
|
return Problem("No node with this id found", statusCode: 404);
|
||||||
|
|
||||||
|
var statistics = await NodeService.GetStatisticsAsync(node);
|
||||||
|
var dto = NodeMapper.ToDto(statistics);
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
}
|
||||||
172
MoonlightServers.Api/Admin/Templates/CrudController.cs
Normal file
172
MoonlightServers.Api/Admin/Templates/CrudController.cs
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Moonlight.Shared.Shared;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Json;
|
||||||
|
using MoonlightServers.Shared;
|
||||||
|
using MoonlightServers.Shared.Admin.Templates;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Admin.Templates;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/admin/servers/templates")]
|
||||||
|
public class CrudController : Controller
|
||||||
|
{
|
||||||
|
private readonly DatabaseRepository<Template> DatabaseRepository;
|
||||||
|
private readonly DatabaseRepository<TemplateDockerImage> DockerImageRepository;
|
||||||
|
|
||||||
|
public CrudController(
|
||||||
|
DatabaseRepository<Template> databaseRepository,
|
||||||
|
DatabaseRepository<TemplateDockerImage> dockerImageRepository
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DatabaseRepository = databaseRepository;
|
||||||
|
DockerImageRepository = dockerImageRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize(Policy = Permissions.Templates.View)]
|
||||||
|
public async Task<ActionResult<PagedData<TemplateDto>>> GetAsync(
|
||||||
|
[FromQuery] int startIndex,
|
||||||
|
[FromQuery] int length,
|
||||||
|
[FromQuery] FilterOptions? filterOptions
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Validation
|
||||||
|
if (startIndex < 0)
|
||||||
|
return Problem("Invalid start index specified", statusCode: 400);
|
||||||
|
|
||||||
|
if (length is < 1 or > 100)
|
||||||
|
return Problem("Invalid length specified");
|
||||||
|
|
||||||
|
// Query building
|
||||||
|
|
||||||
|
var query = DatabaseRepository
|
||||||
|
.Query();
|
||||||
|
|
||||||
|
// Filters
|
||||||
|
if (filterOptions != null)
|
||||||
|
{
|
||||||
|
foreach (var filterOption in filterOptions.Filters)
|
||||||
|
{
|
||||||
|
query = filterOption.Key switch
|
||||||
|
{
|
||||||
|
nameof(Template.Name) =>
|
||||||
|
query.Where(role => EF.Functions.ILike(role.Name, $"%{filterOption.Value}%")),
|
||||||
|
|
||||||
|
_ => query
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
var data = await query
|
||||||
|
.OrderBy(x => x.Id)
|
||||||
|
.ProjectToDto()
|
||||||
|
.Skip(startIndex)
|
||||||
|
.Take(length)
|
||||||
|
.ToArrayAsync();
|
||||||
|
|
||||||
|
var total = await query.CountAsync();
|
||||||
|
|
||||||
|
return new PagedData<TemplateDto>(data, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id:int}")]
|
||||||
|
[Authorize(Policy = Permissions.Templates.View)]
|
||||||
|
public async Task<ActionResult<DetailedTemplateDto>> GetAsync([FromRoute] int id)
|
||||||
|
{
|
||||||
|
var template = await DatabaseRepository
|
||||||
|
.Query()
|
||||||
|
.Include(x => x.DefaultDockerImage)
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
|
if (template == null)
|
||||||
|
return Problem("No template with this id found", statusCode: 404);
|
||||||
|
|
||||||
|
return TemplateMapper.ToDetailedDto(template);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Authorize(Policy = Permissions.Templates.Create)]
|
||||||
|
public async Task<ActionResult<TemplateDto>> CreateAsync([FromBody] CreateTemplateDto request)
|
||||||
|
{
|
||||||
|
var template = TemplateMapper.ToEntity(request);
|
||||||
|
|
||||||
|
// Fill in default values
|
||||||
|
template.LifecycleConfig = new()
|
||||||
|
{
|
||||||
|
StartupCommands = [
|
||||||
|
new StartupCommand
|
||||||
|
{
|
||||||
|
DisplayName = "Default Startup",
|
||||||
|
Command = "bash startup.sh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
StopCommand = "^C",
|
||||||
|
OnlineLogPatterns = ["I am online"]
|
||||||
|
};
|
||||||
|
|
||||||
|
template.InstallationConfig = new()
|
||||||
|
{
|
||||||
|
DockerImage = "debian",
|
||||||
|
Script = "#!/bin/bash\necho Installing",
|
||||||
|
Shell = "/bin/bash"
|
||||||
|
};
|
||||||
|
|
||||||
|
template.FilesConfig = new()
|
||||||
|
{
|
||||||
|
ConfigurationFiles = []
|
||||||
|
};
|
||||||
|
|
||||||
|
template.MiscellaneousConfig = new()
|
||||||
|
{
|
||||||
|
UseLegacyStartup = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var finalRole = await DatabaseRepository.AddAsync(template);
|
||||||
|
|
||||||
|
return TemplateMapper.ToDto(finalRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id:int}")]
|
||||||
|
[Authorize(Policy = Permissions.Templates.Edit)]
|
||||||
|
public async Task<ActionResult<DetailedTemplateDto>> UpdateAsync([FromRoute] int id, [FromBody] UpdateTemplateDto request)
|
||||||
|
{
|
||||||
|
var template = await DatabaseRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
|
if (template == null)
|
||||||
|
return Problem("No template with this id found", statusCode: 404);
|
||||||
|
|
||||||
|
TemplateMapper.Merge(template, request);
|
||||||
|
|
||||||
|
template.DefaultDockerImage = await DockerImageRepository
|
||||||
|
.Query()
|
||||||
|
.Where(x => x.Template.Id == id)
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == request.DefaultDockerImageId);
|
||||||
|
|
||||||
|
await DatabaseRepository.UpdateAsync(template);
|
||||||
|
|
||||||
|
return TemplateMapper.ToDetailedDto(template);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id:int}")]
|
||||||
|
[Authorize(Policy = Permissions.Templates.Delete)]
|
||||||
|
public async Task<ActionResult> DeleteAsync([FromRoute] int id)
|
||||||
|
{
|
||||||
|
var template = await DatabaseRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
|
if (template == null)
|
||||||
|
return Problem("No template with this id found", statusCode: 404);
|
||||||
|
|
||||||
|
await DatabaseRepository.RemoveAsync(template);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
144
MoonlightServers.Api/Admin/Templates/DockerImagesController.cs
Normal file
144
MoonlightServers.Api/Admin/Templates/DockerImagesController.cs
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Moonlight.Shared.Shared;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
using MoonlightServers.Shared;
|
||||||
|
using MoonlightServers.Shared.Admin.Templates;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Admin.Templates;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/admin/servers/templates/{templateId:int}/dockerImages")]
|
||||||
|
public class DockerImagesController : Controller
|
||||||
|
{
|
||||||
|
private readonly DatabaseRepository<TemplateDockerImage> DockerImageRepository;
|
||||||
|
private readonly DatabaseRepository<Template> TemplateRepository;
|
||||||
|
|
||||||
|
public DockerImagesController(
|
||||||
|
DatabaseRepository<TemplateDockerImage> dockerImageRepository,
|
||||||
|
DatabaseRepository<Template> templateRepository
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DockerImageRepository = dockerImageRepository;
|
||||||
|
TemplateRepository = templateRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize(Policy = Permissions.Templates.View)]
|
||||||
|
public async Task<ActionResult<PagedData<DockerImageDto>>> GetAsync(
|
||||||
|
[FromRoute] int templateId,
|
||||||
|
[FromQuery] int startIndex,
|
||||||
|
[FromQuery] int length
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Validation
|
||||||
|
if (startIndex < 0)
|
||||||
|
return Problem("Invalid start index specified", statusCode: 400);
|
||||||
|
|
||||||
|
if (length is < 1 or > 100)
|
||||||
|
return Problem("Invalid length specified");
|
||||||
|
|
||||||
|
if (!await TemplateRepository.Query().AnyAsync(x => x.Id == templateId))
|
||||||
|
return Problem("No template with that id found", statusCode: 404);
|
||||||
|
|
||||||
|
// Query building
|
||||||
|
|
||||||
|
var query = DockerImageRepository
|
||||||
|
.Query()
|
||||||
|
.Where(x => x.Template.Id == templateId);
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
var data = await query
|
||||||
|
.OrderBy(x => x.Id)
|
||||||
|
.ProjectToDto()
|
||||||
|
.Skip(startIndex)
|
||||||
|
.Take(length)
|
||||||
|
.ToArrayAsync();
|
||||||
|
|
||||||
|
var total = await query.CountAsync();
|
||||||
|
|
||||||
|
return new PagedData<DockerImageDto>(data, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id:int}")]
|
||||||
|
[Authorize(Policy = Permissions.Templates.View)]
|
||||||
|
public async Task<ActionResult<DockerImageDto>> GetAsync(
|
||||||
|
[FromRoute] int templateId,
|
||||||
|
[FromRoute] int id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var templateDockerImage = await DockerImageRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id && x.Template.Id == templateId);
|
||||||
|
|
||||||
|
if (templateDockerImage == null)
|
||||||
|
return Problem("No template or template dockerImage found with that id");
|
||||||
|
|
||||||
|
return TemplateMapper.ToDto(templateDockerImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Authorize(Policy = Permissions.Templates.Create)]
|
||||||
|
public async Task<ActionResult<DockerImageDto>> CreateAsync(
|
||||||
|
[FromRoute] int templateId,
|
||||||
|
[FromBody] CreateDockerImageDto dto
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var template = await TemplateRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == templateId);
|
||||||
|
|
||||||
|
if (template == null)
|
||||||
|
return Problem("No template with that id found", statusCode: 404);
|
||||||
|
|
||||||
|
var dockerImage = TemplateMapper.ToEntity(dto);
|
||||||
|
|
||||||
|
dockerImage.Template = template;
|
||||||
|
|
||||||
|
var finalDockerImage = await DockerImageRepository.AddAsync(dockerImage);
|
||||||
|
|
||||||
|
return TemplateMapper.ToDto(finalDockerImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id:int}")]
|
||||||
|
[Authorize(Policy = Permissions.Templates.Edit)]
|
||||||
|
public async Task<ActionResult<DockerImageDto>> UpdateAsync(
|
||||||
|
[FromRoute] int templateId,
|
||||||
|
[FromRoute] int id,
|
||||||
|
[FromBody] UpdateDockerImageDto dto
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var templateDockerImage = await DockerImageRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id && x.Template.Id == templateId);
|
||||||
|
|
||||||
|
if (templateDockerImage == null)
|
||||||
|
return Problem("No template or template dockerImage found with that id");
|
||||||
|
|
||||||
|
TemplateMapper.Merge(templateDockerImage, dto);
|
||||||
|
|
||||||
|
await DockerImageRepository.UpdateAsync(templateDockerImage);
|
||||||
|
|
||||||
|
return TemplateMapper.ToDto(templateDockerImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id:int}")]
|
||||||
|
[Authorize(Policy = Permissions.Templates.Delete)]
|
||||||
|
public async Task<ActionResult> DeleteAsync(
|
||||||
|
[FromRoute] int templateId,
|
||||||
|
[FromRoute] int id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var templateDockerImage = await DockerImageRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id && x.Template.Id == templateId);
|
||||||
|
|
||||||
|
if (templateDockerImage == null)
|
||||||
|
return Problem("No template or template dockerImage found with that id");
|
||||||
|
|
||||||
|
await DockerImageRepository.RemoveAsync(templateDockerImage);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
207
MoonlightServers.Api/Admin/Templates/PelicanEggImportService.cs
Normal file
207
MoonlightServers.Api/Admin/Templates/PelicanEggImportService.cs
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
using System.Text;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Json;
|
||||||
|
using VYaml.Annotations;
|
||||||
|
using VYaml.Serialization;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Admin.Templates;
|
||||||
|
|
||||||
|
public class PelicanEggImportService
|
||||||
|
{
|
||||||
|
private readonly DatabaseRepository<Template> TemplateRepository;
|
||||||
|
private readonly DatabaseRepository<TemplateDockerImage> DockerImageRepository;
|
||||||
|
|
||||||
|
public PelicanEggImportService(
|
||||||
|
DatabaseRepository<Template> templateRepository,
|
||||||
|
DatabaseRepository<TemplateDockerImage> dockerImageRepository
|
||||||
|
)
|
||||||
|
{
|
||||||
|
TemplateRepository = templateRepository;
|
||||||
|
DockerImageRepository = dockerImageRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Template> ImportAsync(string content)
|
||||||
|
{
|
||||||
|
var egg = YamlSerializer.Deserialize<Egg>(Encoding.UTF8.GetBytes(content));
|
||||||
|
|
||||||
|
var template = new Template()
|
||||||
|
{
|
||||||
|
AllowUserDockerImageChange = true,
|
||||||
|
Author = egg.Author,
|
||||||
|
Description = egg.Description,
|
||||||
|
DonateUrl = null,
|
||||||
|
Name = egg.Name,
|
||||||
|
UpdateUrl = egg.Meta.UpdateUrl,
|
||||||
|
Version = "1.0.0",
|
||||||
|
FilesConfig = new FilesConfig()
|
||||||
|
{
|
||||||
|
ConfigurationFiles = egg.Config.Files.Select(file => new ConfigurationFile()
|
||||||
|
{
|
||||||
|
Path = file.Key,
|
||||||
|
Parser = file.Value.Parser,
|
||||||
|
Mappings = file.Value.Find.Select(pair => new ConfigurationFileMapping()
|
||||||
|
{
|
||||||
|
Key = pair.Key,
|
||||||
|
Value = pair.Value
|
||||||
|
}).ToList()
|
||||||
|
}).ToList()
|
||||||
|
},
|
||||||
|
InstallationConfig = new InstallationConfig()
|
||||||
|
{
|
||||||
|
DockerImage = egg.Scripts.Installation.Container,
|
||||||
|
Script = egg.Scripts.Installation.Script,
|
||||||
|
Shell = egg.Scripts.Installation.Entrypoint
|
||||||
|
},
|
||||||
|
LifecycleConfig = new LifecycleConfig()
|
||||||
|
{
|
||||||
|
OnlineLogPatterns = egg.Config.Startup.Values.ToList(),
|
||||||
|
StopCommand = egg.Config.Stop,
|
||||||
|
StartupCommands = egg.StartupCommands.Select(x => new StartupCommand()
|
||||||
|
{
|
||||||
|
DisplayName = x.Key,
|
||||||
|
Command = x.Value
|
||||||
|
}).ToList()
|
||||||
|
},
|
||||||
|
MiscellaneousConfig = new MiscellaneousConfig()
|
||||||
|
{
|
||||||
|
UseLegacyStartup = true
|
||||||
|
},
|
||||||
|
Variables = egg.Variables.Select(variable => new TemplateVariable()
|
||||||
|
{
|
||||||
|
Description = variable.Description,
|
||||||
|
DisplayName = variable.Name,
|
||||||
|
DefaultValue = variable.DefaultValue,
|
||||||
|
EnvName = variable.EnvVariable
|
||||||
|
}).ToList()
|
||||||
|
};
|
||||||
|
|
||||||
|
var finalTemplate = await TemplateRepository.AddAsync(template);
|
||||||
|
|
||||||
|
var isFirst = true;
|
||||||
|
TemplateDockerImage? defaultDockerImage = null;
|
||||||
|
|
||||||
|
foreach (var dockerImage in egg.DockerImages)
|
||||||
|
{
|
||||||
|
var finalDockerImage = await DockerImageRepository.AddAsync(new TemplateDockerImage()
|
||||||
|
{
|
||||||
|
DisplayName = dockerImage.Key,
|
||||||
|
ImageName = dockerImage.Value,
|
||||||
|
SkipPulling = false,
|
||||||
|
Template = finalTemplate
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isFirst)
|
||||||
|
{
|
||||||
|
isFirst = false;
|
||||||
|
defaultDockerImage = finalDockerImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finalTemplate.DefaultDockerImage = defaultDockerImage;
|
||||||
|
|
||||||
|
await TemplateRepository.UpdateAsync(finalTemplate);
|
||||||
|
|
||||||
|
return finalTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class Egg
|
||||||
|
{
|
||||||
|
[YamlMember("_comment")] public string? Comment { get; set; }
|
||||||
|
|
||||||
|
[YamlMember("meta")] public EggMeta Meta { get; set; } = new();
|
||||||
|
|
||||||
|
[YamlMember("exported_at")] public string? ExportedAt { get; set; }
|
||||||
|
|
||||||
|
[YamlMember("name")] public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[YamlMember("author")] public string Author { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[YamlMember("uuid")] public string Uuid { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[YamlMember("description")] public string Description { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[YamlMember("image")] public string? Image { get; set; }
|
||||||
|
|
||||||
|
[YamlMember("tags")] public List<string> Tags { get; set; } = new();
|
||||||
|
|
||||||
|
[YamlMember("features")] public List<string> Features { get; set; } = new();
|
||||||
|
|
||||||
|
[YamlMember("docker_images")] public Dictionary<string, string> DockerImages { get; set; } = new();
|
||||||
|
|
||||||
|
[YamlMember("file_denylist")] public Dictionary<string, string> FileDenylist { get; set; } = new();
|
||||||
|
|
||||||
|
[YamlMember("startup_commands")] public Dictionary<string, string> StartupCommands { get; set; } = new();
|
||||||
|
|
||||||
|
[YamlMember("config")] public EggConfig Config { get; set; } = new();
|
||||||
|
|
||||||
|
[YamlMember("scripts")] public EggScripts Scripts { get; set; } = new();
|
||||||
|
|
||||||
|
[YamlMember("variables")] public List<EggVariable> Variables { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class EggMeta
|
||||||
|
{
|
||||||
|
[YamlMember("version")] public string Version { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[YamlMember("update_url")] public string? UpdateUrl { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class EggConfig
|
||||||
|
{
|
||||||
|
[YamlMember("files")] public Dictionary<string, EggConfigFile> Files { get; set; } = new();
|
||||||
|
|
||||||
|
[YamlMember("startup")] public Dictionary<string, string> Startup { get; set; } = new();
|
||||||
|
|
||||||
|
[YamlMember("logs")] public Dictionary<string, string> Logs { get; set; } = new();
|
||||||
|
|
||||||
|
[YamlMember("stop")] public string Stop { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class EggConfigFile
|
||||||
|
{
|
||||||
|
[YamlMember("parser")] public string Parser { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[YamlMember("find")] public Dictionary<string, string> Find { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class EggScripts
|
||||||
|
{
|
||||||
|
[YamlMember("installation")] public EggInstallationScript Installation { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class EggInstallationScript
|
||||||
|
{
|
||||||
|
[YamlMember("script")] public string Script { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[YamlMember("container")] public string Container { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[YamlMember("entrypoint")] public string Entrypoint { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class EggVariable
|
||||||
|
{
|
||||||
|
[YamlMember("name")] public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[YamlMember("description")] public string Description { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[YamlMember("env_variable")] public string EnvVariable { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[YamlMember("default_value")] public string DefaultValue { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[YamlMember("user_viewable")] public bool UserViewable { get; set; }
|
||||||
|
|
||||||
|
[YamlMember("user_editable")] public bool UserEditable { get; set; }
|
||||||
|
|
||||||
|
[YamlMember("rules")] public List<string> Rules { get; set; } = new();
|
||||||
|
|
||||||
|
[YamlMember("sort")] public int Sort { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Json;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Admin.Templates;
|
||||||
|
|
||||||
|
public class PterodactylEggImportService
|
||||||
|
{
|
||||||
|
private readonly DatabaseRepository<Template> TemplateRepository;
|
||||||
|
private readonly DatabaseRepository<TemplateDockerImage> DockerImageRepository;
|
||||||
|
|
||||||
|
public PterodactylEggImportService(
|
||||||
|
DatabaseRepository<Template> templateRepository,
|
||||||
|
DatabaseRepository<TemplateDockerImage> dockerImageRepository
|
||||||
|
)
|
||||||
|
{
|
||||||
|
TemplateRepository = templateRepository;
|
||||||
|
DockerImageRepository = dockerImageRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Template> ImportAsync(string json)
|
||||||
|
{
|
||||||
|
using var doc = JsonDocument.Parse(json);
|
||||||
|
var root = doc.RootElement;
|
||||||
|
|
||||||
|
var template = new Template
|
||||||
|
{
|
||||||
|
Name = Truncate(root.GetStringOrDefault("name") ?? "Unknown", 30),
|
||||||
|
Description = Truncate(root.GetStringOrDefault("description") ?? "", 255),
|
||||||
|
Author = Truncate(root.GetStringOrDefault("author") ?? "", 30),
|
||||||
|
Version = "1.0.0",
|
||||||
|
UpdateUrl = root.TryGetProperty("meta", out var meta)
|
||||||
|
? meta.GetStringOrDefault("update_url")
|
||||||
|
: null,
|
||||||
|
DonateUrl = null,
|
||||||
|
|
||||||
|
FilesConfig = ParseFilesConfig(root),
|
||||||
|
LifecycleConfig = ParseLifecycleConfig(root),
|
||||||
|
InstallationConfig = ParseInstallationConfig(root),
|
||||||
|
MiscellaneousConfig = new MiscellaneousConfig { UseLegacyStartup = true },
|
||||||
|
|
||||||
|
AllowUserDockerImageChange = true,
|
||||||
|
Variables = ParseVariables(root)
|
||||||
|
};
|
||||||
|
|
||||||
|
var finalTemplate = await TemplateRepository.AddAsync(template);
|
||||||
|
|
||||||
|
var dockerImages = ParseDockerImageModels(root);
|
||||||
|
TemplateDockerImage? defaultDockerImage = null;
|
||||||
|
|
||||||
|
var isFirst = true;
|
||||||
|
|
||||||
|
foreach (var (displayName, imageName) in dockerImages)
|
||||||
|
{
|
||||||
|
var entity = new TemplateDockerImage
|
||||||
|
{
|
||||||
|
DisplayName = displayName,
|
||||||
|
ImageName = imageName,
|
||||||
|
SkipPulling = false,
|
||||||
|
Template = finalTemplate
|
||||||
|
};
|
||||||
|
|
||||||
|
var finalEntity = await DockerImageRepository.AddAsync(entity);
|
||||||
|
|
||||||
|
if (isFirst)
|
||||||
|
{
|
||||||
|
isFirst = false;
|
||||||
|
defaultDockerImage = finalEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finalTemplate.DefaultDockerImage = defaultDockerImage;
|
||||||
|
await TemplateRepository.UpdateAsync(finalTemplate);
|
||||||
|
|
||||||
|
return finalTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FilesConfig ParseFilesConfig(JsonElement root)
|
||||||
|
{
|
||||||
|
var configFiles = new List<ConfigurationFile>();
|
||||||
|
|
||||||
|
if (!root.TryGetProperty("config", out var config))
|
||||||
|
return new FilesConfig { ConfigurationFiles = configFiles };
|
||||||
|
|
||||||
|
if (!config.TryGetProperty("files", out var filesElement))
|
||||||
|
return new FilesConfig { ConfigurationFiles = configFiles };
|
||||||
|
|
||||||
|
var filesJson = filesElement.ValueKind == JsonValueKind.String
|
||||||
|
? filesElement.GetString()
|
||||||
|
: filesElement.GetRawText();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(filesJson) || filesJson == "{}" || filesJson == "[]")
|
||||||
|
return new FilesConfig { ConfigurationFiles = configFiles };
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var filesDoc = JsonDocument.Parse(filesJson);
|
||||||
|
|
||||||
|
foreach (var fileProperty in filesDoc.RootElement.EnumerateObject())
|
||||||
|
{
|
||||||
|
var parser = fileProperty.Value.GetStringOrDefault("parser") ?? "json";
|
||||||
|
var mappings = new List<ConfigurationFileMapping>();
|
||||||
|
|
||||||
|
if (fileProperty.Value.TryGetProperty("find", out var find))
|
||||||
|
{
|
||||||
|
foreach (var mapping in find.EnumerateObject())
|
||||||
|
{
|
||||||
|
mappings.Add(new ConfigurationFileMapping
|
||||||
|
{
|
||||||
|
Key = mapping.Name,
|
||||||
|
Value = mapping.Value.ValueKind == JsonValueKind.String
|
||||||
|
? mapping.Value.GetString() ?? ""
|
||||||
|
: mapping.Value.GetRawText()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configFiles.Add(new ConfigurationFile
|
||||||
|
{
|
||||||
|
Path = fileProperty.Name,
|
||||||
|
Parser = parser,
|
||||||
|
Mappings = mappings
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (JsonException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FilesConfig { ConfigurationFiles = configFiles };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LifecycleConfig ParseLifecycleConfig(JsonElement root)
|
||||||
|
{
|
||||||
|
var stopCommand = "";
|
||||||
|
var onlinePatterns = new List<string>();
|
||||||
|
|
||||||
|
if (root.TryGetProperty("config", out var config))
|
||||||
|
{
|
||||||
|
stopCommand = config.GetStringOrDefault("stop") ?? "";
|
||||||
|
|
||||||
|
if (config.TryGetProperty("startup", out var startupElement))
|
||||||
|
{
|
||||||
|
var startupJson = startupElement.ValueKind == JsonValueKind.String
|
||||||
|
? startupElement.GetString()
|
||||||
|
: startupElement.GetRawText();
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(startupJson))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var startupDoc = JsonDocument.Parse(startupJson);
|
||||||
|
|
||||||
|
if (startupDoc.RootElement.TryGetProperty("done", out var done))
|
||||||
|
{
|
||||||
|
var doneValue = done.ValueKind == JsonValueKind.String
|
||||||
|
? done.GetString()
|
||||||
|
: done.GetRawText();
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(doneValue))
|
||||||
|
onlinePatterns.Add(doneValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (JsonException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LifecycleConfig
|
||||||
|
{
|
||||||
|
StartupCommands =
|
||||||
|
[
|
||||||
|
new StartupCommand
|
||||||
|
{
|
||||||
|
DisplayName = "Startup",
|
||||||
|
Command = root.GetStringOrDefault("startup") ?? ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
StopCommand = stopCommand,
|
||||||
|
OnlineLogPatterns = onlinePatterns
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InstallationConfig ParseInstallationConfig(JsonElement root)
|
||||||
|
{
|
||||||
|
if (!root.TryGetProperty("scripts", out var scripts))
|
||||||
|
return new InstallationConfig();
|
||||||
|
|
||||||
|
if (!scripts.TryGetProperty("installation", out var installation))
|
||||||
|
return new InstallationConfig();
|
||||||
|
|
||||||
|
return new InstallationConfig
|
||||||
|
{
|
||||||
|
DockerImage = installation.GetStringOrDefault("container") ?? "",
|
||||||
|
Shell = installation.GetStringOrDefault("entrypoint") ?? "bash",
|
||||||
|
Script = installation.GetStringOrDefault("script") ?? ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns (DisplayName, ImageName, IsFirst) tuples to avoid a temporary model
|
||||||
|
private static List<(string DisplayName, string ImageName)> ParseDockerImageModels(JsonElement root)
|
||||||
|
{
|
||||||
|
var result = new List<(string, string)>();
|
||||||
|
|
||||||
|
if (!root.TryGetProperty("docker_images", out var dockerImages))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
foreach (var img in dockerImages.EnumerateObject())
|
||||||
|
{
|
||||||
|
result.Add((
|
||||||
|
Truncate(img.Name, 30),
|
||||||
|
Truncate(img.Value.GetString() ?? img.Name, 255)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<TemplateVariable> ParseVariables(JsonElement root)
|
||||||
|
{
|
||||||
|
var variables = new List<TemplateVariable>();
|
||||||
|
|
||||||
|
if (!root.TryGetProperty("variables", out var vars))
|
||||||
|
return variables;
|
||||||
|
|
||||||
|
foreach (var v in vars.EnumerateArray())
|
||||||
|
{
|
||||||
|
variables.Add(new TemplateVariable
|
||||||
|
{
|
||||||
|
DisplayName = Truncate(v.GetStringOrDefault("name") ?? "Variable", 30),
|
||||||
|
Description = Truncate(v.GetStringOrDefault("description") ?? "", 255),
|
||||||
|
EnvName = Truncate(v.GetStringOrDefault("env_variable") ?? "", 60),
|
||||||
|
DefaultValue = Truncate(v.GetStringOrDefault("default_value") ?? "", 1024)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Truncate(string value, int maxLength) =>
|
||||||
|
value.Length <= maxLength ? value : value[..maxLength];
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class JsonElementExtensions
|
||||||
|
{
|
||||||
|
public static string? GetStringOrDefault(this JsonElement element, string propertyName)
|
||||||
|
{
|
||||||
|
return element.TryGetProperty(propertyName, out var prop) && prop.ValueKind == JsonValueKind.String
|
||||||
|
? prop.GetString()
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
28
MoonlightServers.Api/Admin/Templates/TemplateMapper.cs
Normal file
28
MoonlightServers.Api/Admin/Templates/TemplateMapper.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
using MoonlightServers.Shared.Admin.Templates;
|
||||||
|
using Riok.Mapperly.Abstractions;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Admin.Templates;
|
||||||
|
|
||||||
|
[Mapper]
|
||||||
|
[SuppressMessage("Mapper", "RMG020:No members are mapped in an object mapping")]
|
||||||
|
[SuppressMessage("Mapper", "RMG012:No members are mapped in an object mapping")]
|
||||||
|
public static partial class TemplateMapper
|
||||||
|
{
|
||||||
|
public static partial TemplateDto ToDto(Template template);
|
||||||
|
public static partial DetailedTemplateDto ToDetailedDto(Template template);
|
||||||
|
public static partial IQueryable<TemplateDto> ProjectToDto(this IQueryable<Template> templates);
|
||||||
|
public static partial Template ToEntity(CreateTemplateDto dto);
|
||||||
|
public static partial void Merge([MappingTarget] Template template, UpdateTemplateDto dto);
|
||||||
|
|
||||||
|
public static partial IQueryable<VariableDto> ProjectToDto(this IQueryable<TemplateVariable> variables);
|
||||||
|
public static partial VariableDto ToDto(TemplateVariable variable);
|
||||||
|
public static partial TemplateVariable ToEntity(CreateVariableDto dto);
|
||||||
|
public static partial void Merge([MappingTarget] TemplateVariable variable, UpdateVariableDto dto);
|
||||||
|
|
||||||
|
public static partial IQueryable<DockerImageDto> ProjectToDto(this IQueryable<TemplateDockerImage> dockerImages);
|
||||||
|
public static partial DockerImageDto ToDto(TemplateDockerImage dockerImage);
|
||||||
|
public static partial TemplateDockerImage ToEntity(CreateDockerImageDto dto);
|
||||||
|
public static partial void Merge([MappingTarget] TemplateDockerImage dockerImage, UpdateDockerImageDto dto);
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
using VYaml.Annotations;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Admin.Templates;
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class TemplateTransferModel
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
public string Description { get; set; } = "";
|
||||||
|
public string Author { get; set; } = "";
|
||||||
|
public string Version { get; set; } = "";
|
||||||
|
public string? UpdateUrl { get; set; }
|
||||||
|
public string? DonateUrl { get; set; }
|
||||||
|
|
||||||
|
public FilesConfigTransferModel Files { get; set; } = new();
|
||||||
|
public LifecycleConfigTransferModel Lifecycle { get; set; } = new();
|
||||||
|
public InstallationConfigTransferModel Installation { get; set; } = new();
|
||||||
|
public MiscellaneousConfigTransferModel Miscellaneous { get; set; } = new();
|
||||||
|
|
||||||
|
public bool AllowUserDockerImageChange { get; set; }
|
||||||
|
public List<TemplateDockerImageTransferModel> DockerImages { get; set; } = new();
|
||||||
|
|
||||||
|
public List<TemplateVariableTransferModel> Variables { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class TemplateDockerImageTransferModel
|
||||||
|
{
|
||||||
|
public string DisplayName { get; set; } = "";
|
||||||
|
public string ImageName { get; set; } = "";
|
||||||
|
public bool SkipPulling { get; set; }
|
||||||
|
public bool IsDefault { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class TemplateVariableTransferModel
|
||||||
|
{
|
||||||
|
public string DisplayName { get; set; } = "";
|
||||||
|
public string Description { get; set; } = "";
|
||||||
|
public string EnvName { get; set; } = "";
|
||||||
|
public string? DefaultValue { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class FilesConfigTransferModel
|
||||||
|
{
|
||||||
|
public List<ConfigurationFileTransferModel> ConfigurationFiles { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class ConfigurationFileTransferModel
|
||||||
|
{
|
||||||
|
public string Path { get; set; } = "";
|
||||||
|
public string Parser { get; set; } = "";
|
||||||
|
public List<ConfigurationFileMappingTransferModel> Mappings { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class ConfigurationFileMappingTransferModel
|
||||||
|
{
|
||||||
|
public string Key { get; set; } = "";
|
||||||
|
public string? Value { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class LifecycleConfigTransferModel
|
||||||
|
{
|
||||||
|
public List<StartupCommandTransferModel> StartupCommands { get; set; } = new();
|
||||||
|
public string StopCommand { get; set; } = "";
|
||||||
|
public List<string> OnlineLogPatterns { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class StartupCommandTransferModel
|
||||||
|
{
|
||||||
|
public string DisplayName { get; set; } = "";
|
||||||
|
public string Command { get; set; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class InstallationConfigTransferModel
|
||||||
|
{
|
||||||
|
public string DockerImage { get; set; } = "";
|
||||||
|
public string Shell { get; set; } = "";
|
||||||
|
public string Script { get; set; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
[YamlObject]
|
||||||
|
public partial class MiscellaneousConfigTransferModel
|
||||||
|
{
|
||||||
|
public bool UseLegacyStartup { get; set; }
|
||||||
|
}
|
||||||
182
MoonlightServers.Api/Admin/Templates/TemplateTransferService.cs
Normal file
182
MoonlightServers.Api/Admin/Templates/TemplateTransferService.cs
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Json;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Admin.Templates;
|
||||||
|
|
||||||
|
public class TemplateTransferService
|
||||||
|
{
|
||||||
|
private readonly DatabaseRepository<Template> TemplateRepository;
|
||||||
|
private readonly DatabaseRepository<TemplateDockerImage> DockerImageRepository;
|
||||||
|
|
||||||
|
public TemplateTransferService(DatabaseRepository<Template> templateRepository,
|
||||||
|
DatabaseRepository<TemplateDockerImage> dockerImageRepository)
|
||||||
|
{
|
||||||
|
TemplateRepository = templateRepository;
|
||||||
|
DockerImageRepository = dockerImageRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TemplateTransferModel?> ExportAsync(int id)
|
||||||
|
{
|
||||||
|
var template = await TemplateRepository
|
||||||
|
.Query()
|
||||||
|
.Include(x => x.Variables)
|
||||||
|
.Include(x => x.DockerImages)
|
||||||
|
.Include(x => x.DefaultDockerImage)
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
|
if (template == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Name = template.Name,
|
||||||
|
Description = template.Description,
|
||||||
|
Author = template.Author,
|
||||||
|
Version = template.Version,
|
||||||
|
UpdateUrl = template.UpdateUrl,
|
||||||
|
DonateUrl = template.DonateUrl,
|
||||||
|
|
||||||
|
Files = new FilesConfigTransferModel
|
||||||
|
{
|
||||||
|
ConfigurationFiles = template.FilesConfig.ConfigurationFiles
|
||||||
|
.Select(cf => new ConfigurationFileTransferModel
|
||||||
|
{
|
||||||
|
Path = cf.Path,
|
||||||
|
Parser = cf.Parser,
|
||||||
|
Mappings = cf.Mappings
|
||||||
|
.Select(m => new ConfigurationFileMappingTransferModel { Key = m.Key, Value = m.Value })
|
||||||
|
.ToList()
|
||||||
|
})
|
||||||
|
.ToList()
|
||||||
|
},
|
||||||
|
|
||||||
|
Lifecycle = new LifecycleConfigTransferModel
|
||||||
|
{
|
||||||
|
StartupCommands = template.LifecycleConfig.StartupCommands
|
||||||
|
.Select(sc => new StartupCommandTransferModel
|
||||||
|
{ DisplayName = sc.DisplayName, Command = sc.Command })
|
||||||
|
.ToList(),
|
||||||
|
StopCommand = template.LifecycleConfig.StopCommand,
|
||||||
|
OnlineLogPatterns = template.LifecycleConfig.OnlineLogPatterns.ToList()
|
||||||
|
},
|
||||||
|
|
||||||
|
Installation = new InstallationConfigTransferModel
|
||||||
|
{
|
||||||
|
DockerImage = template.InstallationConfig.DockerImage,
|
||||||
|
Shell = template.InstallationConfig.Shell,
|
||||||
|
Script = template.InstallationConfig.Script
|
||||||
|
},
|
||||||
|
|
||||||
|
Miscellaneous = new MiscellaneousConfigTransferModel
|
||||||
|
{
|
||||||
|
UseLegacyStartup = template.MiscellaneousConfig.UseLegacyStartup
|
||||||
|
},
|
||||||
|
|
||||||
|
AllowUserDockerImageChange = template.AllowUserDockerImageChange,
|
||||||
|
DockerImages = template.DockerImages
|
||||||
|
.Select(img => new TemplateDockerImageTransferModel
|
||||||
|
{
|
||||||
|
DisplayName = img.DisplayName,
|
||||||
|
ImageName = img.ImageName,
|
||||||
|
SkipPulling = img.SkipPulling,
|
||||||
|
IsDefault = template.DefaultDockerImage != null && img.Id == template.DefaultDockerImage.Id
|
||||||
|
})
|
||||||
|
.ToList(),
|
||||||
|
|
||||||
|
Variables = template.Variables
|
||||||
|
.Select(v => new TemplateVariableTransferModel
|
||||||
|
{
|
||||||
|
DisplayName = v.DisplayName,
|
||||||
|
Description = v.Description,
|
||||||
|
EnvName = v.EnvName,
|
||||||
|
DefaultValue = v.DefaultValue
|
||||||
|
})
|
||||||
|
.ToList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Template> ImportAsync(TemplateTransferModel m)
|
||||||
|
{
|
||||||
|
var template = new Template
|
||||||
|
{
|
||||||
|
Name = m.Name,
|
||||||
|
Description = m.Description,
|
||||||
|
Author = m.Author,
|
||||||
|
Version = m.Version,
|
||||||
|
UpdateUrl = m.UpdateUrl,
|
||||||
|
DonateUrl = m.DonateUrl,
|
||||||
|
|
||||||
|
FilesConfig = new FilesConfig
|
||||||
|
{
|
||||||
|
ConfigurationFiles = m.Files.ConfigurationFiles
|
||||||
|
.Select(cf => new ConfigurationFile
|
||||||
|
{
|
||||||
|
Path = cf.Path,
|
||||||
|
Parser = cf.Parser,
|
||||||
|
Mappings = cf.Mappings
|
||||||
|
.Select(mp => new ConfigurationFileMapping { Key = mp.Key, Value = mp.Value })
|
||||||
|
.ToList()
|
||||||
|
})
|
||||||
|
.ToList()
|
||||||
|
},
|
||||||
|
|
||||||
|
LifecycleConfig = new LifecycleConfig
|
||||||
|
{
|
||||||
|
StartupCommands = m.Lifecycle.StartupCommands
|
||||||
|
.Select(sc => new StartupCommand { DisplayName = sc.DisplayName, Command = sc.Command })
|
||||||
|
.ToList(),
|
||||||
|
StopCommand = m.Lifecycle.StopCommand,
|
||||||
|
OnlineLogPatterns = m.Lifecycle.OnlineLogPatterns.ToList()
|
||||||
|
},
|
||||||
|
|
||||||
|
InstallationConfig = new InstallationConfig
|
||||||
|
{
|
||||||
|
DockerImage = m.Installation.DockerImage,
|
||||||
|
Shell = m.Installation.Shell,
|
||||||
|
Script = m.Installation.Script
|
||||||
|
},
|
||||||
|
|
||||||
|
MiscellaneousConfig = new MiscellaneousConfig { UseLegacyStartup = m.Miscellaneous.UseLegacyStartup },
|
||||||
|
|
||||||
|
AllowUserDockerImageChange = m.AllowUserDockerImageChange,
|
||||||
|
|
||||||
|
Variables = m.Variables
|
||||||
|
.Select(v => new TemplateVariable
|
||||||
|
{
|
||||||
|
DisplayName = v.DisplayName,
|
||||||
|
Description = v.Description,
|
||||||
|
EnvName = v.EnvName,
|
||||||
|
DefaultValue = v.DefaultValue
|
||||||
|
})
|
||||||
|
.ToList()
|
||||||
|
};
|
||||||
|
|
||||||
|
var finalTemplate = await TemplateRepository.AddAsync(template);
|
||||||
|
|
||||||
|
TemplateDockerImage? defaultDockerImage = null;
|
||||||
|
|
||||||
|
foreach (var img in m.DockerImages)
|
||||||
|
{
|
||||||
|
var entity = new TemplateDockerImage
|
||||||
|
{
|
||||||
|
DisplayName = img.DisplayName,
|
||||||
|
ImageName = img.ImageName,
|
||||||
|
SkipPulling = img.SkipPulling,
|
||||||
|
Template = template
|
||||||
|
};
|
||||||
|
|
||||||
|
var finalEntity = await DockerImageRepository.AddAsync(entity);
|
||||||
|
|
||||||
|
if (img.IsDefault)
|
||||||
|
defaultDockerImage = finalEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalTemplate.DefaultDockerImage = defaultDockerImage;
|
||||||
|
|
||||||
|
await TemplateRepository.UpdateAsync(finalTemplate);
|
||||||
|
|
||||||
|
return finalTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
88
MoonlightServers.Api/Admin/Templates/TransferController.cs
Normal file
88
MoonlightServers.Api/Admin/Templates/TransferController.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using MoonlightServers.Shared.Admin.Templates;
|
||||||
|
using VYaml.Serialization;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Admin.Templates;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/admin/servers/templates")]
|
||||||
|
public class TransferController : Controller
|
||||||
|
{
|
||||||
|
private readonly TemplateTransferService TransferService;
|
||||||
|
|
||||||
|
public TransferController(TemplateTransferService transferService)
|
||||||
|
{
|
||||||
|
TransferService = transferService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id:int}/export")]
|
||||||
|
public async Task<ActionResult> ExportAsync([FromRoute] int id)
|
||||||
|
{
|
||||||
|
var transferModel = await TransferService.ExportAsync(id);
|
||||||
|
|
||||||
|
if (transferModel == null)
|
||||||
|
return Problem("No template with that id found", statusCode: 404);
|
||||||
|
|
||||||
|
var yml = YamlSerializer.Serialize(transferModel, new YamlSerializerOptions
|
||||||
|
{
|
||||||
|
Resolver = CompositeResolver.Create([
|
||||||
|
GeneratedResolver.Instance,
|
||||||
|
StandardResolver.Instance
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
return File(yml.ToArray(), "text/yaml", $"{transferModel.Name}.yml");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("import")]
|
||||||
|
public async Task<ActionResult<TemplateDto>> ImportAsync()
|
||||||
|
{
|
||||||
|
string content;
|
||||||
|
|
||||||
|
await using (Stream receiveStream = Request.Body)
|
||||||
|
|
||||||
|
using (StreamReader readStream = new StreamReader(receiveStream))
|
||||||
|
content = await readStream.ReadToEndAsync();
|
||||||
|
|
||||||
|
if(content.Contains("version: PLCN_v3"))
|
||||||
|
{
|
||||||
|
var importService = HttpContext.RequestServices.GetRequiredService<PelicanEggImportService>();
|
||||||
|
|
||||||
|
var template = await importService.ImportAsync(content);
|
||||||
|
|
||||||
|
return TemplateMapper.ToDto(template);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
content.Contains("PTDL_v2", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
content.Contains("PLCN_v1", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
content.Contains("PLCN_v2", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
content.Contains("PLCN_v3", StringComparison.OrdinalIgnoreCase)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var importService = HttpContext.RequestServices.GetRequiredService<PterodactylEggImportService>();
|
||||||
|
|
||||||
|
var template = await importService.ImportAsync(content);
|
||||||
|
|
||||||
|
return TemplateMapper.ToDto(template);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var transferModel = YamlSerializer.Deserialize<TemplateTransferModel>(
|
||||||
|
Encoding.UTF8.GetBytes(content),
|
||||||
|
new YamlSerializerOptions
|
||||||
|
{
|
||||||
|
Resolver = CompositeResolver.Create([
|
||||||
|
GeneratedResolver.Instance,
|
||||||
|
StandardResolver.Instance
|
||||||
|
])
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var template = await TransferService.ImportAsync(transferModel);
|
||||||
|
|
||||||
|
return TemplateMapper.ToDto(template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
144
MoonlightServers.Api/Admin/Templates/VariablesController.cs
Normal file
144
MoonlightServers.Api/Admin/Templates/VariablesController.cs
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Moonlight.Shared.Shared;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
using MoonlightServers.Shared;
|
||||||
|
using MoonlightServers.Shared.Admin.Templates;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Admin.Templates;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/admin/servers/templates/{templateId:int}/variables")]
|
||||||
|
public class VariablesController : Controller
|
||||||
|
{
|
||||||
|
private readonly DatabaseRepository<TemplateVariable> VariableRepository;
|
||||||
|
private readonly DatabaseRepository<Template> TemplateRepository;
|
||||||
|
|
||||||
|
public VariablesController(
|
||||||
|
DatabaseRepository<TemplateVariable> variableRepository,
|
||||||
|
DatabaseRepository<Template> templateRepository
|
||||||
|
)
|
||||||
|
{
|
||||||
|
VariableRepository = variableRepository;
|
||||||
|
TemplateRepository = templateRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize(Policy = Permissions.Templates.View)]
|
||||||
|
public async Task<ActionResult<PagedData<VariableDto>>> GetAsync(
|
||||||
|
[FromRoute] int templateId,
|
||||||
|
[FromQuery] int startIndex,
|
||||||
|
[FromQuery] int length
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Validation
|
||||||
|
if (startIndex < 0)
|
||||||
|
return Problem("Invalid start index specified", statusCode: 400);
|
||||||
|
|
||||||
|
if (length is < 1 or > 100)
|
||||||
|
return Problem("Invalid length specified");
|
||||||
|
|
||||||
|
if (!await TemplateRepository.Query().AnyAsync(x => x.Id == templateId))
|
||||||
|
return Problem("No template with that id found", statusCode: 404);
|
||||||
|
|
||||||
|
// Query building
|
||||||
|
|
||||||
|
var query = VariableRepository
|
||||||
|
.Query()
|
||||||
|
.Where(x => x.Template.Id == templateId);
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
var data = await query
|
||||||
|
.OrderBy(x => x.Id)
|
||||||
|
.ProjectToDto()
|
||||||
|
.Skip(startIndex)
|
||||||
|
.Take(length)
|
||||||
|
.ToArrayAsync();
|
||||||
|
|
||||||
|
var total = await query.CountAsync();
|
||||||
|
|
||||||
|
return new PagedData<VariableDto>(data, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id:int}")]
|
||||||
|
[Authorize(Policy = Permissions.Templates.View)]
|
||||||
|
public async Task<ActionResult<VariableDto>> GetAsync(
|
||||||
|
[FromRoute] int templateId,
|
||||||
|
[FromRoute] int id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var templateVariable = await VariableRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id && x.Template.Id == templateId);
|
||||||
|
|
||||||
|
if (templateVariable == null)
|
||||||
|
return Problem("No template or template variable found with that id");
|
||||||
|
|
||||||
|
return TemplateMapper.ToDto(templateVariable);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Authorize(Policy = Permissions.Templates.Create)]
|
||||||
|
public async Task<ActionResult<VariableDto>> CreateAsync(
|
||||||
|
[FromRoute] int templateId,
|
||||||
|
[FromBody] CreateVariableDto dto
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var template = await TemplateRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == templateId);
|
||||||
|
|
||||||
|
if (template == null)
|
||||||
|
return Problem("No template with that id found", statusCode: 404);
|
||||||
|
|
||||||
|
var variable = TemplateMapper.ToEntity(dto);
|
||||||
|
|
||||||
|
variable.Template = template;
|
||||||
|
|
||||||
|
var finalVariable = await VariableRepository.AddAsync(variable);
|
||||||
|
|
||||||
|
return TemplateMapper.ToDto(finalVariable);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id:int}")]
|
||||||
|
[Authorize(Policy = Permissions.Templates.Edit)]
|
||||||
|
public async Task<ActionResult<VariableDto>> UpdateAsync(
|
||||||
|
[FromRoute] int templateId,
|
||||||
|
[FromRoute] int id,
|
||||||
|
[FromBody] UpdateVariableDto dto
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var templateVariable = await VariableRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id && x.Template.Id == templateId);
|
||||||
|
|
||||||
|
if (templateVariable == null)
|
||||||
|
return Problem("No template or template variable found with that id");
|
||||||
|
|
||||||
|
TemplateMapper.Merge(templateVariable, dto);
|
||||||
|
|
||||||
|
await VariableRepository.UpdateAsync(templateVariable);
|
||||||
|
|
||||||
|
return TemplateMapper.ToDto(templateVariable);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id:int}")]
|
||||||
|
[Authorize(Policy = Permissions.Templates.Delete)]
|
||||||
|
public async Task<ActionResult> DeleteAsync(
|
||||||
|
[FromRoute] int templateId,
|
||||||
|
[FromRoute] int id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var templateVariable = await VariableRepository
|
||||||
|
.Query()
|
||||||
|
.FirstOrDefaultAsync(x => x.Id == id && x.Template.Id == templateId);
|
||||||
|
|
||||||
|
if (templateVariable == null)
|
||||||
|
return Problem("No template or template variable found with that id");
|
||||||
|
|
||||||
|
await VariableRepository.RemoveAsync(templateVariable);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace MoonlightServers.Api.Infrastructure.Configuration;
|
||||||
|
|
||||||
|
public class NodeTokenOptions
|
||||||
|
{
|
||||||
|
public TimeSpan LookupCacheL1Expiry { get; set; } = TimeSpan.FromSeconds(30);
|
||||||
|
public TimeSpan LookupCacheL2Expiry { get; set; } = TimeSpan.FromMinutes(3);
|
||||||
|
}
|
||||||
66
MoonlightServers.Api/Infrastructure/Database/DataContext.cs
Normal file
66
MoonlightServers.Api/Infrastructure/Database/DataContext.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Moonlight.Api.Infrastructure.Database;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
|
||||||
|
public class DataContext : DbContext
|
||||||
|
{
|
||||||
|
public DbSet<Node> Nodes { get; set; }
|
||||||
|
public DbSet<Template> Templates { get; set; }
|
||||||
|
public DbSet<TemplateDockerImage> TemplateDockerImages { get; set; }
|
||||||
|
public DbSet<TemplateVariable> TemplateVariablesVariables { get; set; }
|
||||||
|
|
||||||
|
private readonly IOptions<DatabaseOptions> Options;
|
||||||
|
public DataContext(IOptions<DatabaseOptions> options)
|
||||||
|
{
|
||||||
|
Options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
|
{
|
||||||
|
if (optionsBuilder.IsConfigured)
|
||||||
|
return;
|
||||||
|
|
||||||
|
optionsBuilder.UseNpgsql(
|
||||||
|
$"Host={Options.Value.Host};" +
|
||||||
|
$"Port={Options.Value.Port};" +
|
||||||
|
$"Username={Options.Value.Username};" +
|
||||||
|
$"Password={Options.Value.Password};" +
|
||||||
|
$"Database={Options.Value.Database}",
|
||||||
|
builder =>
|
||||||
|
{
|
||||||
|
builder.MigrationsAssembly(typeof(DataContext).Assembly);
|
||||||
|
builder.MigrationsHistoryTable("MigrationsHistory", "servers");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.HasDefaultSchema("servers");
|
||||||
|
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Template>()
|
||||||
|
.ComplexProperty(x => x.FilesConfig, builder => builder.ToJson())
|
||||||
|
.ComplexProperty(x => x.LifecycleConfig, builder => builder.ToJson())
|
||||||
|
.ComplexProperty(x => x.InstallationConfig, builder => builder.ToJson())
|
||||||
|
.ComplexProperty(x => x.MiscellaneousConfig, builder => builder.ToJson());
|
||||||
|
|
||||||
|
// One-to-many: Template => DockerImages
|
||||||
|
modelBuilder.Entity<Template>()
|
||||||
|
.HasMany(t => t.DockerImages)
|
||||||
|
.WithOne(d => d.Template)
|
||||||
|
.HasForeignKey("TemplateId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
// One-to-one: Template => DefaultDockerImage
|
||||||
|
modelBuilder.Entity<Template>()
|
||||||
|
.HasOne(t => t.DefaultDockerImage)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey<Template>("DefaultDockerImageId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Interfaces;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
|
||||||
|
public class DatabaseRepository<T> where T : class
|
||||||
|
{
|
||||||
|
private readonly DataContext DataContext;
|
||||||
|
private readonly DbSet<T> Set;
|
||||||
|
|
||||||
|
public DatabaseRepository(DataContext dataContext)
|
||||||
|
{
|
||||||
|
DataContext = dataContext;
|
||||||
|
Set = DataContext.Set<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQueryable<T> Query() => Set;
|
||||||
|
|
||||||
|
public async Task<T> AddAsync(T entity)
|
||||||
|
{
|
||||||
|
if (entity is IActionTimestamps actionTimestamps)
|
||||||
|
{
|
||||||
|
actionTimestamps.CreatedAt = DateTimeOffset.UtcNow;
|
||||||
|
actionTimestamps.UpdatedAt = DateTimeOffset.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
var final = Set.Add(entity);
|
||||||
|
await DataContext.SaveChangesAsync();
|
||||||
|
return final.Entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateAsync(T entity)
|
||||||
|
{
|
||||||
|
if (entity is IActionTimestamps actionTimestamps)
|
||||||
|
actionTimestamps.UpdatedAt = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
Set.Update(entity);
|
||||||
|
await DataContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RemoveAsync(T entity)
|
||||||
|
{
|
||||||
|
Set.Remove(entity);
|
||||||
|
await DataContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
|
||||||
|
public class DbMigrationService : IHostedLifecycleService
|
||||||
|
{
|
||||||
|
private readonly ILogger<DbMigrationService> Logger;
|
||||||
|
private readonly IServiceProvider ServiceProvider;
|
||||||
|
|
||||||
|
public DbMigrationService(ILogger<DbMigrationService> logger, IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
Logger = logger;
|
||||||
|
ServiceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartingAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Logger.LogTrace("Checking for pending migrations");
|
||||||
|
|
||||||
|
await using var scope = ServiceProvider.CreateAsyncScope();
|
||||||
|
var context = scope.ServiceProvider.GetRequiredService<DataContext>();
|
||||||
|
|
||||||
|
var availableMigrations = context.Database.GetMigrations();
|
||||||
|
Logger.LogTrace("Available migrations: {names}", string.Join(", ", availableMigrations));
|
||||||
|
|
||||||
|
var pendingMigrations = await context.Database.GetPendingMigrationsAsync(cancellationToken);
|
||||||
|
var migrationNames = pendingMigrations.ToArray();
|
||||||
|
|
||||||
|
if (migrationNames.Length == 0)
|
||||||
|
{
|
||||||
|
Logger.LogTrace("No pending migrations found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogInformation("Pending migrations: {names}", string.Join(", ", migrationNames));
|
||||||
|
Logger.LogInformation("Migration started");
|
||||||
|
|
||||||
|
await context.Database.MigrateAsync(cancellationToken);
|
||||||
|
|
||||||
|
Logger.LogInformation("Migration complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
|
public Task StartedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
|
public Task StoppedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
|
public Task StoppingAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Interfaces;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
|
||||||
|
public class Node : IActionTimestamps
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string HttpEndpointUrl { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(10)]
|
||||||
|
public string TokenId { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(64)]
|
||||||
|
public string Token { get; set; }
|
||||||
|
|
||||||
|
// Action timestamps
|
||||||
|
public DateTimeOffset CreatedAt { get; set; }
|
||||||
|
public DateTimeOffset UpdatedAt { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Json;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
|
||||||
|
public class Template
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
// Meta
|
||||||
|
|
||||||
|
[MaxLength(30)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(30)]
|
||||||
|
public string Author { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(30)]
|
||||||
|
public string Version { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(2048)]
|
||||||
|
public string? UpdateUrl { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(2048)]
|
||||||
|
public string? DonateUrl { get; set; }
|
||||||
|
|
||||||
|
// JSON Options
|
||||||
|
public FilesConfig FilesConfig { get; set; }
|
||||||
|
public LifecycleConfig LifecycleConfig { get; set; }
|
||||||
|
public InstallationConfig InstallationConfig { get; set; }
|
||||||
|
public MiscellaneousConfig MiscellaneousConfig { get; set; }
|
||||||
|
|
||||||
|
// Docker Images
|
||||||
|
public bool AllowUserDockerImageChange { get; set; }
|
||||||
|
public TemplateDockerImage? DefaultDockerImage { get; set; }
|
||||||
|
public List<TemplateDockerImage> DockerImages { get; set; } = new();
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
public List<TemplateVariable> Variables { get; set; } = new();
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
|
||||||
|
public class TemplateDockerImage
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(30)]
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string ImageName { get; set; }
|
||||||
|
|
||||||
|
public bool SkipPulling { get; set; }
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
public Template Template { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
|
||||||
|
public class TemplateVariable
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(30)]
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(60)]
|
||||||
|
public string EnvName { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(1024)]
|
||||||
|
public string? DefaultValue { get; set; }
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
public Template Template { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Interfaces;
|
||||||
|
|
||||||
|
internal interface IActionTimestamps
|
||||||
|
{
|
||||||
|
public DateTimeOffset CreatedAt { get; set; }
|
||||||
|
public DateTimeOffset UpdatedAt { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Json;
|
||||||
|
|
||||||
|
public class FilesConfig
|
||||||
|
{
|
||||||
|
public List<ConfigurationFile> ConfigurationFiles { get; set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConfigurationFile
|
||||||
|
{
|
||||||
|
public string Path { get; set; } = string.Empty;
|
||||||
|
public string Parser { get; set; } = string.Empty;
|
||||||
|
public List<ConfigurationFileMapping> Mappings { get; set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConfigurationFileMapping
|
||||||
|
{
|
||||||
|
public string Key { get; set; } = string.Empty;
|
||||||
|
public string? Value { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Json;
|
||||||
|
|
||||||
|
public class InstallationConfig
|
||||||
|
{
|
||||||
|
public string DockerImage { get; set; } = string.Empty;
|
||||||
|
public string Shell { get; set; } = string.Empty;
|
||||||
|
public string Script { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Json;
|
||||||
|
|
||||||
|
public class LifecycleConfig
|
||||||
|
{
|
||||||
|
public List<StartupCommand> StartupCommands { get; set; } = [];
|
||||||
|
public string StopCommand { get; set; } = string.Empty;
|
||||||
|
public List<string> OnlineLogPatterns { get; set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StartupCommand
|
||||||
|
{
|
||||||
|
public string DisplayName { get; set; } = string.Empty;
|
||||||
|
public string Command { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Json;
|
||||||
|
|
||||||
|
public class MiscellaneousConfig
|
||||||
|
{
|
||||||
|
public bool UseLegacyStartup { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(DataContext))]
|
||||||
|
[Migration("20260305104238_AddedBasicNodeEntity")]
|
||||||
|
partial class AddedBasicNodeEntity
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasDefaultSchema("servers")
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.1")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.Node", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("HttpEndpointUrl")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("Token")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("character varying(64)");
|
||||||
|
|
||||||
|
b.Property<string>("TokenId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(10)
|
||||||
|
.HasColumnType("character varying(10)");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Nodes", "servers");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddedBasicNodeEntity : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.EnsureSchema(
|
||||||
|
name: "servers");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Nodes",
|
||||||
|
schema: "servers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
Name = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||||
|
HttpEndpointUrl = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
TokenId = table.Column<string>(type: "character varying(10)", maxLength: 10, nullable: false),
|
||||||
|
Token = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Nodes", x => x.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Nodes",
|
||||||
|
schema: "servers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
315
MoonlightServers.Api/Infrastructure/Database/Migrations/20260312075719_AddedTemplateEntities.Designer.cs
generated
Normal file
315
MoonlightServers.Api/Infrastructure/Database/Migrations/20260312075719_AddedTemplateEntities.Designer.cs
generated
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(DataContext))]
|
||||||
|
[Migration("20260312075719_AddedTemplateEntities")]
|
||||||
|
partial class AddedTemplateEntities
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasDefaultSchema("servers")
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.3")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.Node", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("HttpEndpointUrl")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("Token")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("character varying(64)");
|
||||||
|
|
||||||
|
b.Property<string>("TokenId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(10)
|
||||||
|
.HasColumnType("character varying(10)");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Nodes", "servers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.Template", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<bool>("AllowUserDockerImageChange")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Author")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.Property<int?>("DefaultDockerImageId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<string>("DonateUrl")
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("character varying(2048)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.Property<string>("UpdateUrl")
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("character varying(2048)");
|
||||||
|
|
||||||
|
b.Property<string>("Version")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.ComplexProperty(typeof(Dictionary<string, object>), "FilesConfig", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.FilesConfig#FilesConfig", b1 =>
|
||||||
|
{
|
||||||
|
b1.IsRequired();
|
||||||
|
|
||||||
|
b1.ComplexCollection(typeof(List<Dictionary<string, object>>), "ConfigurationFiles", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.FilesConfig#FilesConfig.ConfigurationFiles#ConfigurationFile", b2 =>
|
||||||
|
{
|
||||||
|
b2.IsRequired();
|
||||||
|
|
||||||
|
b2.Property<string>("Parser")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b2.Property<string>("Path")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b2.ComplexCollection(typeof(List<Dictionary<string, object>>), "Mappings", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.FilesConfig#FilesConfig.ConfigurationFiles#ConfigurationFile.Mappings#ConfigurationFileMapping", b3 =>
|
||||||
|
{
|
||||||
|
b3.IsRequired();
|
||||||
|
|
||||||
|
b3.Property<string>("Key")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b3.Property<string>("Value")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
b1
|
||||||
|
.ToJson("FilesConfig")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.ComplexProperty(typeof(Dictionary<string, object>), "InstallationConfig", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.InstallationConfig#InstallationConfig", b1 =>
|
||||||
|
{
|
||||||
|
b1.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<string>("DockerImage")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<string>("Script")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<string>("Shell")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1
|
||||||
|
.ToJson("InstallationConfig")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.ComplexProperty(typeof(Dictionary<string, object>), "LifecycleConfig", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.LifecycleConfig#LifecycleConfig", b1 =>
|
||||||
|
{
|
||||||
|
b1.IsRequired();
|
||||||
|
|
||||||
|
b1.PrimitiveCollection<string>("OnlineLogPatterns")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<string>("StopCommand")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1.ComplexCollection(typeof(List<Dictionary<string, object>>), "StartupCommands", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.LifecycleConfig#LifecycleConfig.StartupCommands#StartupCommand", b2 =>
|
||||||
|
{
|
||||||
|
b2.IsRequired();
|
||||||
|
|
||||||
|
b2.Property<string>("Command")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b2.Property<string>("DisplayName")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
b1
|
||||||
|
.ToJson("LifecycleConfig")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.ComplexProperty(typeof(Dictionary<string, object>), "MiscellaneousConfig", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.MiscellaneousConfig#MiscellaneousConfig", b1 =>
|
||||||
|
{
|
||||||
|
b1.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<bool>("UseLegacyStartup");
|
||||||
|
|
||||||
|
b1
|
||||||
|
.ToJson("MiscellaneousConfig")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("DefaultDockerImageId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Templates", "servers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateDockerImage", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.Property<string>("ImageName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<bool>("SkipPulling")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<int>("TemplateId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TemplateId");
|
||||||
|
|
||||||
|
b.ToTable("TemplateDockerImages", "servers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateVariable", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("DefaultValue")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.Property<string>("EnvName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(60)
|
||||||
|
.HasColumnType("character varying(60)");
|
||||||
|
|
||||||
|
b.Property<int>("TemplateId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TemplateId");
|
||||||
|
|
||||||
|
b.ToTable("TemplateVariablesVariables", "servers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.Template", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateDockerImage", "DefaultDockerImage")
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("MoonlightServers.Api.Infrastructure.Database.Entities.Template", "DefaultDockerImageId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("DefaultDockerImage");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateDockerImage", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MoonlightServers.Api.Infrastructure.Database.Entities.Template", "Template")
|
||||||
|
.WithMany("DockerImages")
|
||||||
|
.HasForeignKey("TemplateId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Template");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateVariable", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MoonlightServers.Api.Infrastructure.Database.Entities.Template", "Template")
|
||||||
|
.WithMany("Variables")
|
||||||
|
.HasForeignKey("TemplateId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Template");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.Template", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("DockerImages");
|
||||||
|
|
||||||
|
b.Navigation("Variables");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddedTemplateEntities : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "TemplateDockerImages",
|
||||||
|
schema: "servers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
DisplayName = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false),
|
||||||
|
ImageName = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||||
|
SkipPulling = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
TemplateId = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_TemplateDockerImages", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Templates",
|
||||||
|
schema: "servers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
Name = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||||
|
Author = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false),
|
||||||
|
Version = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false),
|
||||||
|
UpdateUrl = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
|
||||||
|
DonateUrl = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
|
||||||
|
AllowUserDockerImageChange = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
DefaultDockerImageId = table.Column<int>(type: "integer", nullable: true),
|
||||||
|
FilesConfig = table.Column<string>(type: "jsonb", nullable: false),
|
||||||
|
InstallationConfig = table.Column<string>(type: "jsonb", nullable: false),
|
||||||
|
LifecycleConfig = table.Column<string>(type: "jsonb", nullable: false),
|
||||||
|
MiscellaneousConfig = table.Column<string>(type: "jsonb", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Templates", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Templates_TemplateDockerImages_DefaultDockerImageId",
|
||||||
|
column: x => x.DefaultDockerImageId,
|
||||||
|
principalSchema: "servers",
|
||||||
|
principalTable: "TemplateDockerImages",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.SetNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "TemplateVariablesVariables",
|
||||||
|
schema: "servers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
DisplayName = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||||
|
EnvName = table.Column<string>(type: "character varying(60)", maxLength: 60, nullable: false),
|
||||||
|
DefaultValue = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false),
|
||||||
|
TemplateId = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_TemplateVariablesVariables", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_TemplateVariablesVariables_Templates_TemplateId",
|
||||||
|
column: x => x.TemplateId,
|
||||||
|
principalSchema: "servers",
|
||||||
|
principalTable: "Templates",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TemplateDockerImages_TemplateId",
|
||||||
|
schema: "servers",
|
||||||
|
table: "TemplateDockerImages",
|
||||||
|
column: "TemplateId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Templates_DefaultDockerImageId",
|
||||||
|
schema: "servers",
|
||||||
|
table: "Templates",
|
||||||
|
column: "DefaultDockerImageId",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_TemplateVariablesVariables_TemplateId",
|
||||||
|
schema: "servers",
|
||||||
|
table: "TemplateVariablesVariables",
|
||||||
|
column: "TemplateId");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_TemplateDockerImages_Templates_TemplateId",
|
||||||
|
schema: "servers",
|
||||||
|
table: "TemplateDockerImages",
|
||||||
|
column: "TemplateId",
|
||||||
|
principalSchema: "servers",
|
||||||
|
principalTable: "Templates",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_TemplateDockerImages_Templates_TemplateId",
|
||||||
|
schema: "servers",
|
||||||
|
table: "TemplateDockerImages");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "TemplateVariablesVariables",
|
||||||
|
schema: "servers");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Templates",
|
||||||
|
schema: "servers");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "TemplateDockerImages",
|
||||||
|
schema: "servers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(DataContext))]
|
||||||
|
[Migration("20260312153948_AddedNullabilityForTemplateVariableDefaultValue")]
|
||||||
|
partial class AddedNullabilityForTemplateVariableDefaultValue
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasDefaultSchema("servers")
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.3")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.Node", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("HttpEndpointUrl")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("Token")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("character varying(64)");
|
||||||
|
|
||||||
|
b.Property<string>("TokenId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(10)
|
||||||
|
.HasColumnType("character varying(10)");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Nodes", "servers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.Template", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<bool>("AllowUserDockerImageChange")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Author")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.Property<int?>("DefaultDockerImageId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<string>("DonateUrl")
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("character varying(2048)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.Property<string>("UpdateUrl")
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("character varying(2048)");
|
||||||
|
|
||||||
|
b.Property<string>("Version")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.ComplexProperty(typeof(Dictionary<string, object>), "FilesConfig", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.FilesConfig#FilesConfig", b1 =>
|
||||||
|
{
|
||||||
|
b1.IsRequired();
|
||||||
|
|
||||||
|
b1.ComplexCollection(typeof(List<Dictionary<string, object>>), "ConfigurationFiles", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.FilesConfig#FilesConfig.ConfigurationFiles#ConfigurationFile", b2 =>
|
||||||
|
{
|
||||||
|
b2.IsRequired();
|
||||||
|
|
||||||
|
b2.Property<string>("Parser")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b2.Property<string>("Path")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b2.ComplexCollection(typeof(List<Dictionary<string, object>>), "Mappings", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.FilesConfig#FilesConfig.ConfigurationFiles#ConfigurationFile.Mappings#ConfigurationFileMapping", b3 =>
|
||||||
|
{
|
||||||
|
b3.IsRequired();
|
||||||
|
|
||||||
|
b3.Property<string>("Key")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b3.Property<string>("Value");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
b1
|
||||||
|
.ToJson("FilesConfig")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.ComplexProperty(typeof(Dictionary<string, object>), "InstallationConfig", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.InstallationConfig#InstallationConfig", b1 =>
|
||||||
|
{
|
||||||
|
b1.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<string>("DockerImage")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<string>("Script")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<string>("Shell")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1
|
||||||
|
.ToJson("InstallationConfig")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.ComplexProperty(typeof(Dictionary<string, object>), "LifecycleConfig", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.LifecycleConfig#LifecycleConfig", b1 =>
|
||||||
|
{
|
||||||
|
b1.IsRequired();
|
||||||
|
|
||||||
|
b1.PrimitiveCollection<string>("OnlineLogPatterns")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<string>("StopCommand")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1.ComplexCollection(typeof(List<Dictionary<string, object>>), "StartupCommands", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.LifecycleConfig#LifecycleConfig.StartupCommands#StartupCommand", b2 =>
|
||||||
|
{
|
||||||
|
b2.IsRequired();
|
||||||
|
|
||||||
|
b2.Property<string>("Command")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b2.Property<string>("DisplayName")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
b1
|
||||||
|
.ToJson("LifecycleConfig")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.ComplexProperty(typeof(Dictionary<string, object>), "MiscellaneousConfig", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.MiscellaneousConfig#MiscellaneousConfig", b1 =>
|
||||||
|
{
|
||||||
|
b1.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<bool>("UseLegacyStartup");
|
||||||
|
|
||||||
|
b1
|
||||||
|
.ToJson("MiscellaneousConfig")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("DefaultDockerImageId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Templates", "servers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateDockerImage", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.Property<string>("ImageName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<bool>("SkipPulling")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<int>("TemplateId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TemplateId");
|
||||||
|
|
||||||
|
b.ToTable("TemplateDockerImages", "servers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateVariable", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("DefaultValue")
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.Property<string>("EnvName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(60)
|
||||||
|
.HasColumnType("character varying(60)");
|
||||||
|
|
||||||
|
b.Property<int>("TemplateId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TemplateId");
|
||||||
|
|
||||||
|
b.ToTable("TemplateVariablesVariables", "servers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.Template", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateDockerImage", "DefaultDockerImage")
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("MoonlightServers.Api.Infrastructure.Database.Entities.Template", "DefaultDockerImageId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("DefaultDockerImage");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateDockerImage", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MoonlightServers.Api.Infrastructure.Database.Entities.Template", "Template")
|
||||||
|
.WithMany("DockerImages")
|
||||||
|
.HasForeignKey("TemplateId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Template");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateVariable", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MoonlightServers.Api.Infrastructure.Database.Entities.Template", "Template")
|
||||||
|
.WithMany("Variables")
|
||||||
|
.HasForeignKey("TemplateId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Template");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.Template", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("DockerImages");
|
||||||
|
|
||||||
|
b.Navigation("Variables");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddedNullabilityForTemplateVariableDefaultValue : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "DefaultValue",
|
||||||
|
schema: "servers",
|
||||||
|
table: "TemplateVariablesVariables",
|
||||||
|
type: "character varying(1024)",
|
||||||
|
maxLength: 1024,
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(1024)",
|
||||||
|
oldMaxLength: 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "DefaultValue",
|
||||||
|
schema: "servers",
|
||||||
|
table: "TemplateVariablesVariables",
|
||||||
|
type: "character varying(1024)",
|
||||||
|
maxLength: 1024,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "",
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "character varying(1024)",
|
||||||
|
oldMaxLength: 1024,
|
||||||
|
oldNullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,310 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(DataContext))]
|
||||||
|
partial class DataContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasDefaultSchema("servers")
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.3")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.Node", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("HttpEndpointUrl")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("Token")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("character varying(64)");
|
||||||
|
|
||||||
|
b.Property<string>("TokenId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(10)
|
||||||
|
.HasColumnType("character varying(10)");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Nodes", "servers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.Template", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<bool>("AllowUserDockerImageChange")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Author")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.Property<int?>("DefaultDockerImageId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<string>("DonateUrl")
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("character varying(2048)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.Property<string>("UpdateUrl")
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("character varying(2048)");
|
||||||
|
|
||||||
|
b.Property<string>("Version")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.ComplexProperty(typeof(Dictionary<string, object>), "FilesConfig", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.FilesConfig#FilesConfig", b1 =>
|
||||||
|
{
|
||||||
|
b1.IsRequired();
|
||||||
|
|
||||||
|
b1.ComplexCollection(typeof(List<Dictionary<string, object>>), "ConfigurationFiles", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.FilesConfig#FilesConfig.ConfigurationFiles#ConfigurationFile", b2 =>
|
||||||
|
{
|
||||||
|
b2.IsRequired();
|
||||||
|
|
||||||
|
b2.Property<string>("Parser")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b2.Property<string>("Path")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b2.ComplexCollection(typeof(List<Dictionary<string, object>>), "Mappings", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.FilesConfig#FilesConfig.ConfigurationFiles#ConfigurationFile.Mappings#ConfigurationFileMapping", b3 =>
|
||||||
|
{
|
||||||
|
b3.IsRequired();
|
||||||
|
|
||||||
|
b3.Property<string>("Key")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b3.Property<string>("Value");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
b1
|
||||||
|
.ToJson("FilesConfig")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.ComplexProperty(typeof(Dictionary<string, object>), "InstallationConfig", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.InstallationConfig#InstallationConfig", b1 =>
|
||||||
|
{
|
||||||
|
b1.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<string>("DockerImage")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<string>("Script")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<string>("Shell")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1
|
||||||
|
.ToJson("InstallationConfig")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.ComplexProperty(typeof(Dictionary<string, object>), "LifecycleConfig", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.LifecycleConfig#LifecycleConfig", b1 =>
|
||||||
|
{
|
||||||
|
b1.IsRequired();
|
||||||
|
|
||||||
|
b1.PrimitiveCollection<string>("OnlineLogPatterns")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<string>("StopCommand")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b1.ComplexCollection(typeof(List<Dictionary<string, object>>), "StartupCommands", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.LifecycleConfig#LifecycleConfig.StartupCommands#StartupCommand", b2 =>
|
||||||
|
{
|
||||||
|
b2.IsRequired();
|
||||||
|
|
||||||
|
b2.Property<string>("Command")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b2.Property<string>("DisplayName")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
b1
|
||||||
|
.ToJson("LifecycleConfig")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.ComplexProperty(typeof(Dictionary<string, object>), "MiscellaneousConfig", "MoonlightServers.Api.Infrastructure.Database.Entities.Template.MiscellaneousConfig#MiscellaneousConfig", b1 =>
|
||||||
|
{
|
||||||
|
b1.IsRequired();
|
||||||
|
|
||||||
|
b1.Property<bool>("UseLegacyStartup");
|
||||||
|
|
||||||
|
b1
|
||||||
|
.ToJson("MiscellaneousConfig")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("DefaultDockerImageId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Templates", "servers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateDockerImage", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.Property<string>("ImageName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<bool>("SkipPulling")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<int>("TemplateId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TemplateId");
|
||||||
|
|
||||||
|
b.ToTable("TemplateDockerImages", "servers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateVariable", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("DefaultValue")
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("character varying(255)");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(30)
|
||||||
|
.HasColumnType("character varying(30)");
|
||||||
|
|
||||||
|
b.Property<string>("EnvName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(60)
|
||||||
|
.HasColumnType("character varying(60)");
|
||||||
|
|
||||||
|
b.Property<int>("TemplateId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TemplateId");
|
||||||
|
|
||||||
|
b.ToTable("TemplateVariablesVariables", "servers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.Template", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateDockerImage", "DefaultDockerImage")
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("MoonlightServers.Api.Infrastructure.Database.Entities.Template", "DefaultDockerImageId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("DefaultDockerImage");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateDockerImage", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MoonlightServers.Api.Infrastructure.Database.Entities.Template", "Template")
|
||||||
|
.WithMany("DockerImages")
|
||||||
|
.HasForeignKey("TemplateId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Template");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.TemplateVariable", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("MoonlightServers.Api.Infrastructure.Database.Entities.Template", "Template")
|
||||||
|
.WithMany("Variables")
|
||||||
|
.HasForeignKey("TemplateId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Template");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("MoonlightServers.Api.Infrastructure.Database.Entities.Template", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("DockerImages");
|
||||||
|
|
||||||
|
b.Navigation("Variables");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Caching.Hybrid;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Implementations.NodeToken;
|
||||||
|
|
||||||
|
public class NodeTokenSchemeHandler : AuthenticationHandler<NodeTokenSchemeOptions>
|
||||||
|
{
|
||||||
|
public const string SchemeName = "MoonlightServers.NodeToken";
|
||||||
|
public const string CacheKeyFormat = $"MoonlightServers.{nameof(NodeTokenSchemeHandler)}.{{0}}";
|
||||||
|
|
||||||
|
private readonly DatabaseRepository<Node> DatabaseRepository;
|
||||||
|
private readonly HybridCache Cache;
|
||||||
|
|
||||||
|
public NodeTokenSchemeHandler(
|
||||||
|
IOptionsMonitor<NodeTokenSchemeOptions> options,
|
||||||
|
ILoggerFactory logger,
|
||||||
|
UrlEncoder encoder,
|
||||||
|
DatabaseRepository<Node> databaseRepository,
|
||||||
|
HybridCache cache
|
||||||
|
) : base(options, logger, encoder)
|
||||||
|
{
|
||||||
|
DatabaseRepository = databaseRepository;
|
||||||
|
Cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||||
|
{
|
||||||
|
// Basic format validation
|
||||||
|
|
||||||
|
if (!Context.Request.Headers.TryGetValue(HeaderNames.Authorization, out var authHeaderValues))
|
||||||
|
return AuthenticateResult.Fail("No authorization header present");
|
||||||
|
|
||||||
|
if (authHeaderValues.Count != 1)
|
||||||
|
return AuthenticateResult.Fail("No authorization value present");
|
||||||
|
|
||||||
|
var authHeaderValue = authHeaderValues[0];
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(authHeaderValue))
|
||||||
|
return AuthenticateResult.Fail("No authorization value present");
|
||||||
|
|
||||||
|
var authHeaderParts = authHeaderValue.Split(
|
||||||
|
' ',
|
||||||
|
StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries
|
||||||
|
);
|
||||||
|
|
||||||
|
// Validate parts
|
||||||
|
if (authHeaderParts.Length < 2)
|
||||||
|
return AuthenticateResult.Fail("Malformed authorization header");
|
||||||
|
|
||||||
|
var tokenId = authHeaderParts[0];
|
||||||
|
var token = authHeaderParts[1];
|
||||||
|
|
||||||
|
if (tokenId.Length != 10 && token.Length != 64)
|
||||||
|
return AuthenticateResult.Fail("Malformed authorization header");
|
||||||
|
|
||||||
|
// Real validation
|
||||||
|
|
||||||
|
var cacheKey = string.Format(CacheKeyFormat, tokenId);
|
||||||
|
|
||||||
|
var session = await Cache.GetOrCreateAsync<NodeTokenSession?>(cacheKey, async cancellationToken =>
|
||||||
|
{
|
||||||
|
return await DatabaseRepository
|
||||||
|
.Query()
|
||||||
|
.Where(x => x.TokenId == tokenId)
|
||||||
|
.Select(x => new NodeTokenSession(x.Id, x.Token))
|
||||||
|
.FirstOrDefaultAsync(cancellationToken: cancellationToken);
|
||||||
|
},
|
||||||
|
new HybridCacheEntryOptions()
|
||||||
|
{
|
||||||
|
LocalCacheExpiration = Options.LookupCacheL1Expiry,
|
||||||
|
Expiration = Options.LookupCacheL2Expiry
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if(session == null || token != session.Token)
|
||||||
|
return AuthenticateResult.Fail("Invalid authorization header");
|
||||||
|
|
||||||
|
// All checks have passed, create auth ticket
|
||||||
|
return AuthenticateResult.Success(new AuthenticationTicket(
|
||||||
|
new ClaimsPrincipal(
|
||||||
|
new ClaimsIdentity(
|
||||||
|
[
|
||||||
|
new Claim("NodeId", session.Id.ToString())
|
||||||
|
],
|
||||||
|
SchemeName
|
||||||
|
)
|
||||||
|
),
|
||||||
|
SchemeName
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private record NodeTokenSession(int Id, string Token);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Infrastructure.Implementations.NodeToken;
|
||||||
|
|
||||||
|
public class NodeTokenSchemeOptions : AuthenticationSchemeOptions
|
||||||
|
{
|
||||||
|
public TimeSpan LookupCacheL1Expiry { get; set; } = TimeSpan.FromSeconds(30);
|
||||||
|
public TimeSpan LookupCacheL2Expiry { get; set; } = TimeSpan.FromMinutes(3);
|
||||||
|
}
|
||||||
40
MoonlightServers.Api/MoonlightServers.Api.csproj
Normal file
40
MoonlightServers.Api/MoonlightServers.Api.csproj
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Label="Nuget Package Settings">
|
||||||
|
<Version>2.1.0</Version>
|
||||||
|
<Title>MoonlightServers.Api</Title>
|
||||||
|
<Authors>Moonlight Panel</Authors>
|
||||||
|
<Description>Development package of MoonlightServers.Api</Description>
|
||||||
|
<Copyright>Moonlight Panel</Copyright>
|
||||||
|
<PackageProjectUrl>https://git.battlestati.one/Moonlight-Panel/Servers</PackageProjectUrl>
|
||||||
|
<PackageLicenseUrl>https://git.battlestati.one/Moonlight-Panel/Servers/src/branch/v2.1/LICENSE</PackageLicenseUrl>
|
||||||
|
<RepositoryUrl>https://git.battlestati.one/Moonlight-Panel/Servers</RepositoryUrl>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<SupportedPlatform Include="browser"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Moonlight.Api" Version="2.1.0">
|
||||||
|
<ExcludeAssets>content;contentfiles</ExcludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Client\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\MoonlightServers.DaemonShared\MoonlightServers.DaemonShared.csproj" />
|
||||||
|
<ProjectReference Include="..\MoonlightServers.Shared\MoonlightServers.Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
14
MoonlightServers.Api/Remote/Nodes/PingController.cs
Normal file
14
MoonlightServers.Api/Remote/Nodes/PingController.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Implementations.NodeToken;
|
||||||
|
|
||||||
|
namespace MoonlightServers.Api.Remote.Nodes;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/remote/servers/nodes/ping")]
|
||||||
|
[Authorize(AuthenticationSchemes = NodeTokenSchemeHandler.SchemeName)]
|
||||||
|
public class PingController : Controller
|
||||||
|
{
|
||||||
|
[HttpGet]
|
||||||
|
public ActionResult Get() => NoContent();
|
||||||
|
}
|
||||||
55
MoonlightServers.Api/Startup.cs
Normal file
55
MoonlightServers.Api/Startup.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Moonlight.Api;
|
||||||
|
using MoonlightServers.Api.Admin.Nodes;
|
||||||
|
using MoonlightServers.Api.Admin.Templates;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Configuration;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Database;
|
||||||
|
using MoonlightServers.Api.Infrastructure.Implementations.NodeToken;
|
||||||
|
using MoonlightServers.Shared;
|
||||||
|
using SimplePlugin.Abstractions;
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddScoped(typeof(DatabaseRepository<>));
|
||||||
|
|
||||||
|
builder.Services.AddDbContext<DataContext>();
|
||||||
|
builder.Services.AddHostedService<DbMigrationService>();
|
||||||
|
|
||||||
|
builder.Services.AddScoped<TemplateTransferService>();
|
||||||
|
builder.Services.AddScoped<PterodactylEggImportService>();
|
||||||
|
builder.Services.AddScoped<PelicanEggImportService>();
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<NodeService>();
|
||||||
|
|
||||||
|
var nodeTokenOptions = new NodeTokenOptions();
|
||||||
|
builder.Configuration.Bind("Moonlight:Servers:NodeToken", nodeTokenOptions);
|
||||||
|
|
||||||
|
builder.Services
|
||||||
|
.AddAuthentication()
|
||||||
|
.AddScheme<NodeTokenSchemeOptions, NodeTokenSchemeHandler>(NodeTokenSchemeHandler.SchemeName, options =>
|
||||||
|
{
|
||||||
|
options.LookupCacheL1Expiry = nodeTokenOptions.LookupCacheL1Expiry;
|
||||||
|
options.LookupCacheL2Expiry = nodeTokenOptions.LookupCacheL2Expiry;
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Logging.AddFilter(
|
||||||
|
"MoonlightServers.Api.Infrastructure.Implementations.NodeToken.NodeTokenSchemeHandler",
|
||||||
|
LogLevel.Warning
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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; }
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
namespace MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
|
|
||||||
public class Node
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
// Relations
|
|
||||||
public List<Server> Servers { get; set; } = new();
|
|
||||||
public List<Allocation> 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; }
|
|
||||||
|
|
||||||
// Misc
|
|
||||||
public bool EnableTransparentMode { get; set; }
|
|
||||||
public bool EnableDynamicFirewall { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,29 +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<Allocation> Allocations { get; set; } = new();
|
|
||||||
public List<ServerVariable> Variables { get; set; } = new();
|
|
||||||
public List<ServerBackup> Backups { get; set; } = new();
|
|
||||||
public List<ServerShare> 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; }
|
|
||||||
public bool UseVirtualDisk { get; set; }
|
|
||||||
public int Bandwidth { get; set; }
|
|
||||||
}
|
|
||||||
@@ -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; }
|
|
||||||
}
|
|
||||||
@@ -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; }
|
|
||||||
}
|
|
||||||
@@ -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; }
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
namespace MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
|
|
||||||
public class Star
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
// References
|
|
||||||
public List<StarVariable> Variables { get; set; } = new();
|
|
||||||
public List<StarDockerImage> 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; }
|
|
||||||
}
|
|
||||||
@@ -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; }
|
|
||||||
}
|
|
||||||
@@ -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; }
|
|
||||||
}
|
|
||||||
@@ -1,452 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
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("20250226210232_RecreatedMigrationsForPostgresql")]
|
|
||||||
partial class RecreatedMigrationsForPostgresql
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "8.0.11")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("IpAddress")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("NodeId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Port")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int?>("ServerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("NodeId");
|
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Allocations", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("EnableDynamicFirewall")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<bool>("EnableTransparentMode")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("Fqdn")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("FtpPort")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("HttpPort")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Token")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<bool>("UseSsl")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Nodes", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<int>("Bandwidth")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Cpu")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Disk")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("DockerImageIndex")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Memory")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("NodeId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("OwnerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("StarId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("StartupOverride")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<bool>("UseVirtualDisk")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("NodeId");
|
|
||||||
|
|
||||||
b.HasIndex("StarId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Servers", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerBackup", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("Completed")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CompletedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int?>("ServerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<long>("Size")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<bool>("Successful")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_ServerBackups", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("ServerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_ServerVariables", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("AllowDockerImageChange")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("Author")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("DefaultDockerImage")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("DonateUrl")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("InstallDockerImage")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("InstallScript")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("InstallShell")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("OnlineDetection")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("ParseConfiguration")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("RequiredAllocations")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("StartupCommand")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("StopCommand")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("UpdateUrl")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Version")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Stars", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("AutoPulling")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("DisplayName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Identifier")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("StarId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("StarId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_StarDockerImages", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("AllowEditing")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<bool>("AllowViewing")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("DefaultValue")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Filter")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("StarId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Type")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("StarId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_StarVariables", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
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.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("Variables");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("DockerImages");
|
|
||||||
|
|
||||||
b.Navigation("Variables");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,286 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class RecreatedMigrationsForPostgresql : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Servers_Nodes",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
Name = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Fqdn = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Token = table.Column<string>(type: "text", nullable: false),
|
|
||||||
HttpPort = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
FtpPort = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
UseSsl = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
EnableTransparentMode = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
EnableDynamicFirewall = table.Column<bool>(type: "boolean", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Servers_Nodes", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Servers_Stars",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
Name = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Version = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Author = table.Column<string>(type: "text", nullable: false),
|
|
||||||
UpdateUrl = table.Column<string>(type: "text", nullable: true),
|
|
||||||
DonateUrl = table.Column<string>(type: "text", nullable: true),
|
|
||||||
StartupCommand = table.Column<string>(type: "text", nullable: false),
|
|
||||||
StopCommand = table.Column<string>(type: "text", nullable: false),
|
|
||||||
OnlineDetection = table.Column<string>(type: "text", nullable: false),
|
|
||||||
InstallShell = table.Column<string>(type: "text", nullable: false),
|
|
||||||
InstallDockerImage = table.Column<string>(type: "text", nullable: false),
|
|
||||||
InstallScript = table.Column<string>(type: "text", nullable: false),
|
|
||||||
RequiredAllocations = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
AllowDockerImageChange = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
DefaultDockerImage = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
ParseConfiguration = table.Column<string>(type: "text", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Servers_Stars", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Servers_Servers",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
StarId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
NodeId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
Name = table.Column<string>(type: "text", nullable: false),
|
|
||||||
OwnerId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
StartupOverride = table.Column<string>(type: "text", nullable: true),
|
|
||||||
DockerImageIndex = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
Cpu = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
Memory = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
Disk = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
UseVirtualDisk = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
Bandwidth = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Servers_Servers", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Servers_Servers_Servers_Nodes_NodeId",
|
|
||||||
column: x => x.NodeId,
|
|
||||||
principalTable: "Servers_Nodes",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Servers_Servers_Servers_Stars_StarId",
|
|
||||||
column: x => x.StarId,
|
|
||||||
principalTable: "Servers_Stars",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Servers_StarDockerImages",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
StarId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
DisplayName = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Identifier = table.Column<string>(type: "text", nullable: false),
|
|
||||||
AutoPulling = table.Column<bool>(type: "boolean", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Servers_StarDockerImages", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Servers_StarDockerImages_Servers_Stars_StarId",
|
|
||||||
column: x => x.StarId,
|
|
||||||
principalTable: "Servers_Stars",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Servers_StarVariables",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
StarId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
Name = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Description = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Key = table.Column<string>(type: "text", nullable: false),
|
|
||||||
DefaultValue = table.Column<string>(type: "text", nullable: false),
|
|
||||||
AllowViewing = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
AllowEditing = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
Type = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
Filter = table.Column<string>(type: "text", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Servers_StarVariables", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Servers_StarVariables_Servers_Stars_StarId",
|
|
||||||
column: x => x.StarId,
|
|
||||||
principalTable: "Servers_Stars",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Servers_Allocations",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
NodeId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
ServerId = table.Column<int>(type: "integer", nullable: true),
|
|
||||||
IpAddress = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Port = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Servers_Allocations", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Servers_Allocations_Servers_Nodes_NodeId",
|
|
||||||
column: x => x.NodeId,
|
|
||||||
principalTable: "Servers_Nodes",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Servers_Allocations_Servers_Servers_ServerId",
|
|
||||||
column: x => x.ServerId,
|
|
||||||
principalTable: "Servers_Servers",
|
|
||||||
principalColumn: "Id");
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Servers_ServerBackups",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
|
||||||
CompletedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
|
||||||
Size = table.Column<long>(type: "bigint", nullable: false),
|
|
||||||
Successful = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
Completed = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
ServerId = table.Column<int>(type: "integer", nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Servers_ServerBackups", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Servers_ServerBackups_Servers_Servers_ServerId",
|
|
||||||
column: x => x.ServerId,
|
|
||||||
principalTable: "Servers_Servers",
|
|
||||||
principalColumn: "Id");
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Servers_ServerVariables",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
ServerId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
Key = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Value = table.Column<string>(type: "text", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Servers_ServerVariables", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Servers_ServerVariables_Servers_Servers_ServerId",
|
|
||||||
column: x => x.ServerId,
|
|
||||||
principalTable: "Servers_Servers",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Servers_Allocations_NodeId",
|
|
||||||
table: "Servers_Allocations",
|
|
||||||
column: "NodeId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Servers_Allocations_ServerId",
|
|
||||||
table: "Servers_Allocations",
|
|
||||||
column: "ServerId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Servers_ServerBackups_ServerId",
|
|
||||||
table: "Servers_ServerBackups",
|
|
||||||
column: "ServerId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Servers_Servers_NodeId",
|
|
||||||
table: "Servers_Servers",
|
|
||||||
column: "NodeId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Servers_Servers_StarId",
|
|
||||||
table: "Servers_Servers",
|
|
||||||
column: "StarId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Servers_ServerVariables_ServerId",
|
|
||||||
table: "Servers_ServerVariables",
|
|
||||||
column: "ServerId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Servers_StarDockerImages_StarId",
|
|
||||||
table: "Servers_StarDockerImages",
|
|
||||||
column: "StarId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Servers_StarVariables_StarId",
|
|
||||||
table: "Servers_StarVariables",
|
|
||||||
column: "StarId");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Servers_Allocations");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Servers_ServerBackups");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Servers_ServerVariables");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Servers_StarDockerImages");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Servers_StarVariables");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Servers_Servers");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Servers_Nodes");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Servers_Stars");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,456 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
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("20250301142415_AddedTokenIdField")]
|
|
||||||
partial class AddedTokenIdField
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "8.0.11")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("IpAddress")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("NodeId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Port")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int?>("ServerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("NodeId");
|
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Allocations", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("EnableDynamicFirewall")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<bool>("EnableTransparentMode")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("Fqdn")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("FtpPort")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("HttpPort")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Token")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("TokenId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<bool>("UseSsl")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Nodes", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<int>("Bandwidth")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Cpu")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Disk")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("DockerImageIndex")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Memory")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("NodeId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("OwnerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("StarId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("StartupOverride")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<bool>("UseVirtualDisk")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("NodeId");
|
|
||||||
|
|
||||||
b.HasIndex("StarId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Servers", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerBackup", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("Completed")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CompletedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int?>("ServerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<long>("Size")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<bool>("Successful")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_ServerBackups", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("ServerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_ServerVariables", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("AllowDockerImageChange")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("Author")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("DefaultDockerImage")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("DonateUrl")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("InstallDockerImage")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("InstallScript")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("InstallShell")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("OnlineDetection")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("ParseConfiguration")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("RequiredAllocations")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("StartupCommand")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("StopCommand")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("UpdateUrl")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Version")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Stars", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("AutoPulling")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("DisplayName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Identifier")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("StarId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("StarId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_StarDockerImages", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("AllowEditing")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<bool>("AllowViewing")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("DefaultValue")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Filter")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("StarId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Type")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("StarId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_StarVariables", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
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.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("Variables");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("DockerImages");
|
|
||||||
|
|
||||||
b.Navigation("Variables");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddedTokenIdField : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "TokenId",
|
|
||||||
table: "Servers_Nodes",
|
|
||||||
type: "text",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "TokenId",
|
|
||||||
table: "Servers_Nodes");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,540 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
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("20250606121013_AddedShares")]
|
|
||||||
partial class AddedShares
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "9.0.5")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("IpAddress")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("NodeId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Port")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int?>("ServerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("NodeId");
|
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Allocations", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("EnableDynamicFirewall")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<bool>("EnableTransparentMode")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("Fqdn")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("FtpPort")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("HttpPort")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Token")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("TokenId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<bool>("UseSsl")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Nodes", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<int>("Bandwidth")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Cpu")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Disk")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("DockerImageIndex")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Memory")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("NodeId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("OwnerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("StarId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("StartupOverride")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<bool>("UseVirtualDisk")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("NodeId");
|
|
||||||
|
|
||||||
b.HasIndex("StarId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Servers", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerBackup", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("Completed")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CompletedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int?>("ServerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<long>("Size")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<bool>("Successful")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_ServerBackups", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerShare", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("ServerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_ServerShares", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("ServerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_ServerVariables", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("AllowDockerImageChange")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("Author")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("DefaultDockerImage")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("DonateUrl")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("InstallDockerImage")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("InstallScript")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("InstallShell")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("OnlineDetection")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("ParseConfiguration")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("RequiredAllocations")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("StartupCommand")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("StopCommand")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("UpdateUrl")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Version")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Stars", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("AutoPulling")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("DisplayName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Identifier")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("StarId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("StarId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_StarDockerImages", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("AllowEditing")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<bool>("AllowViewing")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("DefaultValue")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Filter")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("StarId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Type")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("StarId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_StarVariables", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
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<int>("ServerShareId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b1.HasKey("ServerShareId");
|
|
||||||
|
|
||||||
b1.ToTable("Servers_ServerShares");
|
|
||||||
|
|
||||||
b1.ToJson("Content");
|
|
||||||
|
|
||||||
b1.WithOwner()
|
|
||||||
.HasForeignKey("ServerShareId");
|
|
||||||
|
|
||||||
b1.OwnsMany("MoonlightServers.ApiServer.Models.ServerSharePermission", "Permissions", b2 =>
|
|
||||||
{
|
|
||||||
b2.Property<int>("ServerShareContentServerShareId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b2.Property<int>("__synthesizedOrdinal")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b2.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b2.Property<int>("Type")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b2.HasKey("ServerShareContentServerShareId", "__synthesizedOrdinal");
|
|
||||||
|
|
||||||
b2.ToTable("Servers_ServerShares");
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddedShares : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Servers_ServerShares",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
UserId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
ServerId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
|
||||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
|
||||||
Content = table.Column<string>(type: "jsonb", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Servers_ServerShares", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Servers_ServerShares_Servers_Servers_ServerId",
|
|
||||||
column: x => x.ServerId,
|
|
||||||
principalTable: "Servers_Servers",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Servers_ServerShares_ServerId",
|
|
||||||
table: "Servers_ServerShares",
|
|
||||||
column: "ServerId");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Servers_ServerShares");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,537 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
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
|
|
||||||
.HasAnnotation("ProductVersion", "9.0.5")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("IpAddress")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("NodeId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Port")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int?>("ServerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("NodeId");
|
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Allocations", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("EnableDynamicFirewall")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<bool>("EnableTransparentMode")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("Fqdn")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("FtpPort")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("HttpPort")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Token")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("TokenId")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<bool>("UseSsl")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Nodes", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<int>("Bandwidth")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Cpu")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Disk")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("DockerImageIndex")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Memory")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("NodeId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("OwnerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("StarId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("StartupOverride")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<bool>("UseVirtualDisk")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("NodeId");
|
|
||||||
|
|
||||||
b.HasIndex("StarId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Servers", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerBackup", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("Completed")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CompletedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int?>("ServerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<long>("Size")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<bool>("Successful")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_ServerBackups", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerShare", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("ServerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_ServerShares", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("ServerId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ServerId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_ServerVariables", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("AllowDockerImageChange")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("Author")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("DefaultDockerImage")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("DonateUrl")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("InstallDockerImage")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("InstallScript")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("InstallShell")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("OnlineDetection")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("ParseConfiguration")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("RequiredAllocations")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<string>("StartupCommand")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("StopCommand")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("UpdateUrl")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Version")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Servers_Stars", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("AutoPulling")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("DisplayName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Identifier")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("StarId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("StarId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_StarDockerImages", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<bool>("AllowEditing")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<bool>("AllowViewing")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("DefaultValue")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Filter")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<int>("StarId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<int>("Type")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("StarId");
|
|
||||||
|
|
||||||
b.ToTable("Servers_StarVariables", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
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<int>("ServerShareId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b1.HasKey("ServerShareId");
|
|
||||||
|
|
||||||
b1.ToTable("Servers_ServerShares");
|
|
||||||
|
|
||||||
b1.ToJson("Content");
|
|
||||||
|
|
||||||
b1.WithOwner()
|
|
||||||
.HasForeignKey("ServerShareId");
|
|
||||||
|
|
||||||
b1.OwnsMany("MoonlightServers.ApiServer.Models.ServerSharePermission", "Permissions", b2 =>
|
|
||||||
{
|
|
||||||
b2.Property<int>("ServerShareContentServerShareId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b2.Property<int>("__synthesizedOrdinal")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b2.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b2.Property<int>("Type")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b2.HasKey("ServerShareContentServerShareId", "__synthesizedOrdinal");
|
|
||||||
|
|
||||||
b2.ToTable("Servers_ServerShares");
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MoonCore.Extended.SingleDb;
|
|
||||||
using Moonlight.ApiServer.Configuration;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Models;
|
|
||||||
using MoonlightServers.Shared.Models;
|
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Database;
|
|
||||||
|
|
||||||
public class ServersDataContext : DatabaseContext
|
|
||||||
{
|
|
||||||
public override string Prefix { get; } = "Servers";
|
|
||||||
|
|
||||||
public DbSet<Allocation> Allocations { get; set; }
|
|
||||||
public DbSet<Node> Nodes { get; set; }
|
|
||||||
public DbSet<Server> Servers { get; set; }
|
|
||||||
public DbSet<ServerBackup> ServerBackups { get; set; }
|
|
||||||
public DbSet<ServerShare> ServerShares { get; set; }
|
|
||||||
public DbSet<ServerVariable> ServerVariables { get; set; }
|
|
||||||
public DbSet<Star> Stars { get; set; }
|
|
||||||
public DbSet<StarDockerImage> StarDockerImages { get; set; }
|
|
||||||
public DbSet<StarVariable> StarVariables { get; set; }
|
|
||||||
|
|
||||||
public ServersDataContext(AppConfiguration configuration)
|
|
||||||
{
|
|
||||||
Options = new()
|
|
||||||
{
|
|
||||||
Host = configuration.Database.Host,
|
|
||||||
Port = configuration.Database.Port,
|
|
||||||
Username = configuration.Database.Username,
|
|
||||||
Password = configuration.Database.Password,
|
|
||||||
Database = configuration.Database.Database
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
base.OnModelCreating(modelBuilder);
|
|
||||||
|
|
||||||
#region Shares
|
|
||||||
|
|
||||||
modelBuilder.Ignore<ServerShareContent>();
|
|
||||||
modelBuilder.Ignore<ServerSharePermission>();
|
|
||||||
|
|
||||||
modelBuilder.Entity<ServerShare>(builder =>
|
|
||||||
{
|
|
||||||
builder.OwnsOne(x => x.Content, navigationBuilder =>
|
|
||||||
{
|
|
||||||
navigationBuilder.ToJson();
|
|
||||||
|
|
||||||
navigationBuilder.OwnsMany(x => x.Permissions);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using MoonlightServers.DaemonShared.Enums;
|
|
||||||
using MoonlightServers.Shared.Enums;
|
|
||||||
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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Helpers;
|
|
||||||
|
|
||||||
public class NodeAuthOptions : AuthenticationSchemeOptions
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
using System.Security.Claims;
|
|
||||||
using System.Text.Encodings.Web;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Helpers;
|
|
||||||
|
|
||||||
public class NodeAuthScheme : AuthenticationHandler<NodeAuthOptions>
|
|
||||||
{
|
|
||||||
public NodeAuthScheme(IOptionsMonitor<NodeAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder,
|
|
||||||
ISystemClock clock) : base(options, logger, encoder, clock)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public NodeAuthScheme(IOptionsMonitor<NodeAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder) : base(
|
|
||||||
options, logger, encoder)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task<AuthenticateResult> 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<DatabaseRepository<Node>>();
|
|
||||||
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonCore.Models;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
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")]
|
|
||||||
public class NodeAllocationsController : Controller
|
|
||||||
{
|
|
||||||
private readonly DatabaseRepository<Node> NodeRepository;
|
|
||||||
private readonly DatabaseRepository<Allocation> AllocationRepository;
|
|
||||||
|
|
||||||
public NodeAllocationsController(
|
|
||||||
DatabaseRepository<Node> nodeRepository,
|
|
||||||
DatabaseRepository<Allocation> allocationRepository
|
|
||||||
)
|
|
||||||
{
|
|
||||||
NodeRepository = nodeRepository;
|
|
||||||
AllocationRepository = allocationRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{nodeId:int}/allocations")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.nodes.get")]
|
|
||||||
public async Task<IPagedData<NodeAllocationDetailResponse>> Get(
|
|
||||||
[FromRoute] int nodeId,
|
|
||||||
[FromQuery] int page,
|
|
||||||
[FromQuery] [Range(1, 100)] int pageSize
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var count = await AllocationRepository.Get().CountAsync(x => x.Node.Id == nodeId);
|
|
||||||
|
|
||||||
var allocations = await AllocationRepository
|
|
||||||
.Get()
|
|
||||||
.Skip(page * pageSize)
|
|
||||||
.Take(pageSize)
|
|
||||||
.Where(x => x.Node.Id == nodeId)
|
|
||||||
.ToArrayAsync();
|
|
||||||
|
|
||||||
var mappedAllocations = allocations.Select(x => new NodeAllocationDetailResponse()
|
|
||||||
{
|
|
||||||
Id = x.Id,
|
|
||||||
IpAddress = x.IpAddress,
|
|
||||||
Port = x.Port
|
|
||||||
}).ToArray();
|
|
||||||
|
|
||||||
return new PagedData<NodeAllocationDetailResponse>()
|
|
||||||
{
|
|
||||||
Items = mappedAllocations,
|
|
||||||
CurrentPage = page,
|
|
||||||
PageSize = pageSize,
|
|
||||||
TotalItems = count,
|
|
||||||
TotalPages = count == 0 ? 0 : (count - 1) / pageSize
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{nodeId:int}/allocations/{id:int}")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.nodes.get")]
|
|
||||||
public async Task<NodeAllocationDetailResponse> GetSingle([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)
|
|
||||||
throw new HttpApiException("No allocation with that id found", 404);
|
|
||||||
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
Id = allocation.Id,
|
|
||||||
IpAddress = allocation.IpAddress,
|
|
||||||
Port = allocation.Port
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{nodeId:int}/allocations")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.nodes.create")]
|
|
||||||
public async Task<NodeAllocationDetailResponse> Create(
|
|
||||||
[FromRoute] int nodeId,
|
|
||||||
[FromBody] CreateNodeAllocationRequest request
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var node = await NodeRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == nodeId);
|
|
||||||
|
|
||||||
if (node == null)
|
|
||||||
throw new HttpApiException("No node with that id found", 404);
|
|
||||||
|
|
||||||
var allocation = new Allocation
|
|
||||||
{
|
|
||||||
IpAddress = request.IpAddress,
|
|
||||||
Port = request.Port,
|
|
||||||
Node = node
|
|
||||||
};
|
|
||||||
|
|
||||||
var finalVariable = await AllocationRepository.Add(allocation);
|
|
||||||
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
Id = finalVariable.Id,
|
|
||||||
IpAddress = finalVariable.IpAddress,
|
|
||||||
Port = finalVariable.Port
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPatch("{nodeId:int}/allocations/{id:int}")]
|
|
||||||
public async Task<NodeAllocationDetailResponse> Update([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)
|
|
||||||
throw new HttpApiException("No allocation with that id found", 404);
|
|
||||||
|
|
||||||
allocation.IpAddress = request.IpAddress;
|
|
||||||
allocation.Port = request.Port;
|
|
||||||
|
|
||||||
await AllocationRepository.Update(allocation);
|
|
||||||
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
Id = allocation.Id,
|
|
||||||
IpAddress = allocation.IpAddress,
|
|
||||||
Port = allocation.Port
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{nodeId:int}/allocations/{id:int}")]
|
|
||||||
public async Task Delete([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)
|
|
||||||
throw new HttpApiException("No allocation with that id found", 404);
|
|
||||||
|
|
||||||
await AllocationRepository.Remove(allocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{nodeId:int}/allocations/range")]
|
|
||||||
public async Task CreateRange([FromRoute] int nodeId, [FromBody] CreateNodeAllocationRangeRequest rangeRequest)
|
|
||||||
{
|
|
||||||
var node = await NodeRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == nodeId);
|
|
||||||
|
|
||||||
if (node == null)
|
|
||||||
throw new HttpApiException("No node with that id found", 404);
|
|
||||||
|
|
||||||
var existingAllocations = AllocationRepository
|
|
||||||
.Get()
|
|
||||||
.Where(x => x.Node.Id == nodeId)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var ports = new List<int>();
|
|
||||||
|
|
||||||
for (var i = rangeRequest.Start; i < rangeRequest.End; i++)
|
|
||||||
{
|
|
||||||
// Skip existing allocations
|
|
||||||
if (existingAllocations.Any(x => x.Port == i && x.IpAddress == rangeRequest.IpAddress))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ports.Add(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
var allocations = ports
|
|
||||||
.Select(port => new Allocation()
|
|
||||||
{
|
|
||||||
IpAddress = rangeRequest.IpAddress,
|
|
||||||
Port = port,
|
|
||||||
Node = node
|
|
||||||
})
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
await AllocationRepository.RunTransaction(async set => { await set.AddRangeAsync(allocations); });
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{nodeId:int}/allocations/all")]
|
|
||||||
public async Task DeleteAll([FromRoute] int nodeId)
|
|
||||||
{
|
|
||||||
var allocations = AllocationRepository
|
|
||||||
.Get()
|
|
||||||
.Where(x => x.Node.Id == nodeId)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
await AllocationRepository.RunTransaction(set => { set.RemoveRange(allocations); });
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{nodeId:int}/allocations/free")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.nodes.get")]
|
|
||||||
public async Task<IPagedData<NodeAllocationDetailResponse>> GetFree([FromRoute] int nodeId, [FromQuery] int page,
|
|
||||||
[FromQuery][Range(1, 100)] int pageSize, [FromQuery] int serverId = -1)
|
|
||||||
{
|
|
||||||
var node = NodeRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefault(x => x.Id == nodeId);
|
|
||||||
|
|
||||||
if (node == null)
|
|
||||||
throw new HttpApiException("A node with this id could not be found", 404);
|
|
||||||
|
|
||||||
var freeAllocationsQuery = AllocationRepository
|
|
||||||
.Get()
|
|
||||||
.Where(x => x.Node.Id == node.Id)
|
|
||||||
.Where(x => x.Server == null || x.Server.Id == serverId);
|
|
||||||
|
|
||||||
var count = await freeAllocationsQuery.CountAsync();
|
|
||||||
var allocations = await freeAllocationsQuery.ToArrayAsync();
|
|
||||||
|
|
||||||
var mappedAllocations = allocations.Select(x => new NodeAllocationDetailResponse()
|
|
||||||
{
|
|
||||||
Id = x.Id,
|
|
||||||
IpAddress = x.IpAddress,
|
|
||||||
Port = x.Port
|
|
||||||
}).ToArray();
|
|
||||||
|
|
||||||
return new PagedData<NodeAllocationDetailResponse>()
|
|
||||||
{
|
|
||||||
Items = mappedAllocations,
|
|
||||||
CurrentPage = page,
|
|
||||||
PageSize = pageSize,
|
|
||||||
TotalItems = count,
|
|
||||||
TotalPages = count == 0 ? 0 : (count - 1) / pageSize
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
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<Node> NodeRepository;
|
|
||||||
private readonly NodeService NodeService;
|
|
||||||
|
|
||||||
public NodeStatusController(DatabaseRepository<Node> nodeRepository, NodeService nodeService)
|
|
||||||
{
|
|
||||||
NodeRepository = nodeRepository;
|
|
||||||
NodeService = nodeService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{nodeId}/system/status")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.nodes.status")]
|
|
||||||
public async Task<NodeSystemStatusResponse> GetStatus([FromRoute] int nodeId)
|
|
||||||
{
|
|
||||||
var node = GetNode(nodeId);
|
|
||||||
|
|
||||||
NodeSystemStatusResponse response;
|
|
||||||
|
|
||||||
var sw = new Stopwatch();
|
|
||||||
sw.Start();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var statusResponse = await NodeService.GetSystemStatus(node);
|
|
||||||
|
|
||||||
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 Node GetNode(int nodeId)
|
|
||||||
{
|
|
||||||
var result = NodeRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefault(x => x.Id == nodeId);
|
|
||||||
|
|
||||||
if (result == null)
|
|
||||||
throw new HttpApiException("A node with this id could not be found", 404);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonCore.Extended.Helpers;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using MoonCore.Helpers;
|
|
||||||
using MoonCore.Models;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.Shared.Http.Requests.Admin.Nodes;
|
|
||||||
using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations;
|
|
||||||
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 CrudHelper<Node, NodeDetailResponse> CrudHelper;
|
|
||||||
private readonly DatabaseRepository<Node> NodeRepository;
|
|
||||||
|
|
||||||
public NodesController(
|
|
||||||
CrudHelper<Node, NodeDetailResponse> crudHelper,
|
|
||||||
DatabaseRepository<Node> nodeRepository
|
|
||||||
)
|
|
||||||
{
|
|
||||||
CrudHelper = crudHelper;
|
|
||||||
NodeRepository = nodeRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.nodes.get")]
|
|
||||||
public async Task<IPagedData<NodeDetailResponse>> Get([FromQuery] int page, [FromQuery] int pageSize)
|
|
||||||
{
|
|
||||||
return await CrudHelper.Get(page, pageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id:int}")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.nodes.get")]
|
|
||||||
public async Task<NodeDetailResponse> GetSingle([FromRoute] int id)
|
|
||||||
{
|
|
||||||
return await CrudHelper.GetSingle(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.nodes.create")]
|
|
||||||
public async Task<NodeDetailResponse> Create([FromBody] CreateNodeRequest request)
|
|
||||||
{
|
|
||||||
var node = Mapper.Map<Node>(request);
|
|
||||||
|
|
||||||
node.TokenId = Formatter.GenerateString(6);
|
|
||||||
node.Token = Formatter.GenerateString(32);
|
|
||||||
|
|
||||||
var finalNode = await NodeRepository.Add(node);
|
|
||||||
|
|
||||||
return CrudHelper.MapToResult(finalNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPatch("{id:int}")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.nodes.update")]
|
|
||||||
public async Task<NodeDetailResponse> Update([FromRoute] int id, [FromBody] UpdateNodeRequest request)
|
|
||||||
{
|
|
||||||
return await CrudHelper.Update(id, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{id:int}")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.nodes.delete")]
|
|
||||||
public async Task Delete([FromRoute] int id)
|
|
||||||
{
|
|
||||||
await CrudHelper.Delete(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
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")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.nodes.statistics")]
|
|
||||||
public class StatisticsController : Controller
|
|
||||||
{
|
|
||||||
private readonly NodeService NodeService;
|
|
||||||
private readonly DatabaseRepository<Node> NodeRepository;
|
|
||||||
|
|
||||||
public StatisticsController(NodeService nodeService, DatabaseRepository<Node> nodeRepository)
|
|
||||||
{
|
|
||||||
NodeService = nodeService;
|
|
||||||
NodeRepository = nodeRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{nodeId:int}/statistics")]
|
|
||||||
public async Task<StatisticsResponse> Get([FromRoute] int nodeId)
|
|
||||||
{
|
|
||||||
var node = await GetNode(nodeId);
|
|
||||||
var statistics = await NodeService.GetStatistics(node);
|
|
||||||
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
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("{nodeId:int}/statistics/docker")]
|
|
||||||
public async Task<DockerStatisticsResponse> GetDocker([FromRoute] int nodeId)
|
|
||||||
{
|
|
||||||
var node = await GetNode(nodeId);
|
|
||||||
var statistics = await NodeService.GetDockerStatistics(node);
|
|
||||||
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
BuildCacheReclaimable = statistics.BuildCacheReclaimable,
|
|
||||||
BuildCacheUsed = statistics.BuildCacheUsed,
|
|
||||||
ContainersReclaimable = statistics.ContainersReclaimable,
|
|
||||||
ContainersUsed = statistics.ContainersUsed,
|
|
||||||
ImagesReclaimable = statistics.ImagesReclaimable,
|
|
||||||
ImagesUsed = statistics.ImagesUsed,
|
|
||||||
Version = statistics.Version
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<Node> GetNode(int nodeId)
|
|
||||||
{
|
|
||||||
var result = await NodeRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == nodeId);
|
|
||||||
|
|
||||||
if (result == null)
|
|
||||||
throw new HttpApiException("A node with this id could not be found", 404);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonCore.Helpers;
|
|
||||||
using MoonCore.Models;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
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<ServerVariable> VariableRepository;
|
|
||||||
private readonly DatabaseRepository<Server> ServerRepository;
|
|
||||||
|
|
||||||
public ServerVariablesController(DatabaseRepository<ServerVariable> variableRepository, DatabaseRepository<Server> serverRepository)
|
|
||||||
{
|
|
||||||
VariableRepository = variableRepository;
|
|
||||||
ServerRepository = serverRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{serverId}/variables")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.get")]
|
|
||||||
public async Task<PagedData<ServerVariableDetailResponse>> Get([FromRoute] int serverId, [FromQuery] int page, [FromQuery] int pageSize)
|
|
||||||
{
|
|
||||||
var server = await ServerRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == serverId);
|
|
||||||
|
|
||||||
if (server == null)
|
|
||||||
throw new HttpApiException("No server with this id found", 404);
|
|
||||||
|
|
||||||
var variables = await VariableRepository
|
|
||||||
.Get()
|
|
||||||
.Where(x => x.Server.Id == server.Id)
|
|
||||||
.ToArrayAsync();
|
|
||||||
|
|
||||||
var castedVariables = variables
|
|
||||||
.Select(x => Mapper.Map<ServerVariableDetailResponse>(x))
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
return PagedData<ServerVariableDetailResponse>.Create(castedVariables, page, pageSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,282 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonCore.Extended.Helpers;
|
|
||||||
using MoonCore.Helpers;
|
|
||||||
using MoonCore.Models;
|
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
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 CrudHelper<Server, ServerDetailResponse> CrudHelper;
|
|
||||||
private readonly DatabaseRepository<Star> StarRepository;
|
|
||||||
private readonly DatabaseRepository<Node> NodeRepository;
|
|
||||||
private readonly DatabaseRepository<Allocation> AllocationRepository;
|
|
||||||
private readonly DatabaseRepository<ServerVariable> VariableRepository;
|
|
||||||
private readonly DatabaseRepository<Server> ServerRepository;
|
|
||||||
private readonly DatabaseRepository<User> UserRepository;
|
|
||||||
private readonly ILogger<ServersController> Logger;
|
|
||||||
private readonly ServerService ServerService;
|
|
||||||
|
|
||||||
public ServersController(
|
|
||||||
CrudHelper<Server, ServerDetailResponse> crudHelper,
|
|
||||||
DatabaseRepository<Star> starRepository,
|
|
||||||
DatabaseRepository<Node> nodeRepository,
|
|
||||||
DatabaseRepository<Allocation> allocationRepository,
|
|
||||||
DatabaseRepository<ServerVariable> variableRepository,
|
|
||||||
DatabaseRepository<Server> serverRepository,
|
|
||||||
DatabaseRepository<User> userRepository,
|
|
||||||
ILogger<ServersController> logger,
|
|
||||||
ServerService serverService
|
|
||||||
)
|
|
||||||
{
|
|
||||||
CrudHelper = crudHelper;
|
|
||||||
StarRepository = starRepository;
|
|
||||||
NodeRepository = nodeRepository;
|
|
||||||
AllocationRepository = allocationRepository;
|
|
||||||
VariableRepository = variableRepository;
|
|
||||||
ServerRepository = serverRepository;
|
|
||||||
UserRepository = userRepository;
|
|
||||||
ServerService = serverService;
|
|
||||||
Logger = logger;
|
|
||||||
|
|
||||||
CrudHelper.QueryModifier = servers => servers
|
|
||||||
.Include(x => x.Node)
|
|
||||||
.Include(x => x.Allocations)
|
|
||||||
.Include(x => x.Variables)
|
|
||||||
.Include(x => x.Star);
|
|
||||||
|
|
||||||
CrudHelper.LateMapper = (server, response) =>
|
|
||||||
{
|
|
||||||
response.NodeId = server.Node.Id;
|
|
||||||
response.StarId = server.Star.Id;
|
|
||||||
response.AllocationIds = server.Allocations.Select(x => x.Id).ToArray();
|
|
||||||
|
|
||||||
return response;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.get")]
|
|
||||||
public async Task<IPagedData<ServerDetailResponse>> Get([FromQuery] int page, [FromQuery] int pageSize)
|
|
||||||
{
|
|
||||||
return await CrudHelper.Get(page, pageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id:int}")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.get")]
|
|
||||||
public async Task<ServerDetailResponse> GetSingle([FromRoute] int id)
|
|
||||||
{
|
|
||||||
return await CrudHelper.GetSingle(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.create")]
|
|
||||||
public async Task<ServerDetailResponse> Create([FromBody] CreateServerRequest request)
|
|
||||||
{
|
|
||||||
// Construct model
|
|
||||||
var server = Mapper.Map<Server>(request);
|
|
||||||
|
|
||||||
// Check if owner user exist
|
|
||||||
if (UserRepository.Get().All(x => x.Id != request.OwnerId))
|
|
||||||
throw new HttpApiException("No user with this id found", 400);
|
|
||||||
|
|
||||||
var star = await StarRepository
|
|
||||||
.Get()
|
|
||||||
.Include(x => x.Variables)
|
|
||||||
.Include(x => x.DockerImages)
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == request.StarId);
|
|
||||||
|
|
||||||
if (star == null)
|
|
||||||
throw new HttpApiException("No star with this id found", 400);
|
|
||||||
|
|
||||||
var node = await NodeRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == request.NodeId);
|
|
||||||
|
|
||||||
if (node == null)
|
|
||||||
throw new HttpApiException("No node with this id found", 400);
|
|
||||||
|
|
||||||
var allocations = new List<Allocation>();
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
{
|
|
||||||
throw new HttpApiException(
|
|
||||||
$"Unable to find enough free allocations. Found: {allocations.Count}, Required: {star.RequiredAllocations}",
|
|
||||||
400
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.Add(server);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await ServerService.Sync(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 wont have a bugged server in the database which doesnt exist on the node
|
|
||||||
await ServerRepository.Remove(finalServer);
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CrudHelper.MapToResult(finalServer);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPatch("{id:int}")]
|
|
||||||
public async Task<ServerDetailResponse> Update([FromRoute] int id, [FromBody] UpdateServerRequest request)
|
|
||||||
{
|
|
||||||
//TODO: Handle shrinking virtual disk
|
|
||||||
|
|
||||||
var server = await CrudHelper.GetSingleModel(id);
|
|
||||||
|
|
||||||
server = Mapper.Map(server, request);
|
|
||||||
|
|
||||||
var allocations = new List<Allocation>();
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
{
|
|
||||||
throw new HttpApiException(
|
|
||||||
$"You need to specify at least {server.Star.RequiredAllocations} allocation(s)",
|
|
||||||
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.Update(server);
|
|
||||||
|
|
||||||
// Notify the node about the changes
|
|
||||||
await ServerService.Sync(server);
|
|
||||||
|
|
||||||
return CrudHelper.MapToResult(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{id:int}")]
|
|
||||||
public async Task Delete([FromRoute] int id, [FromQuery] bool force = false)
|
|
||||||
{
|
|
||||||
var server = await CrudHelper.GetSingleModel(id);
|
|
||||||
|
|
||||||
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.SyncDelete(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 CrudHelper.Delete(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonCore.Extended.Helpers;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using MoonCore.Helpers;
|
|
||||||
using MoonCore.Models;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
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")]
|
|
||||||
public class StarDockerImagesController : Controller
|
|
||||||
{
|
|
||||||
private readonly CrudHelper<StarDockerImage, StarDockerImageDetailResponse> CrudHelper;
|
|
||||||
private readonly DatabaseRepository<Star> StarRepository;
|
|
||||||
private readonly DatabaseRepository<StarDockerImage> StarDockerImageRepository;
|
|
||||||
|
|
||||||
private Star Star;
|
|
||||||
|
|
||||||
public StarDockerImagesController(
|
|
||||||
CrudHelper<StarDockerImage, StarDockerImageDetailResponse> crudHelper,
|
|
||||||
DatabaseRepository<Star> starRepository,
|
|
||||||
DatabaseRepository<StarDockerImage> starDockerImageRepository
|
|
||||||
)
|
|
||||||
{
|
|
||||||
CrudHelper = crudHelper;
|
|
||||||
StarRepository = starRepository;
|
|
||||||
StarDockerImageRepository = starDockerImageRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ApplyStar(int id)
|
|
||||||
{
|
|
||||||
var star = await StarRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
|
||||||
|
|
||||||
if (star == null)
|
|
||||||
throw new HttpApiException("A star with this id could not be found", 404);
|
|
||||||
|
|
||||||
Star = star;
|
|
||||||
|
|
||||||
CrudHelper.QueryModifier = dockerImages =>
|
|
||||||
dockerImages.Where(x => x.Star.Id == star.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{starId:int}/dockerImages")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.get")]
|
|
||||||
public async Task<IPagedData<StarDockerImageDetailResponse>> Get([FromRoute] int starId, [FromQuery] int page, [FromQuery] int pageSize)
|
|
||||||
{
|
|
||||||
await ApplyStar(starId);
|
|
||||||
|
|
||||||
return await CrudHelper.Get(page, pageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{starId:int}/dockerImages/{id:int}")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.get")]
|
|
||||||
public async Task<StarDockerImageDetailResponse> GetSingle([FromRoute] int starId, [FromRoute] int id)
|
|
||||||
{
|
|
||||||
await ApplyStar(starId);
|
|
||||||
|
|
||||||
return await CrudHelper.GetSingle(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{starId:int}/dockerImages")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.create")]
|
|
||||||
public async Task<StarDockerImageDetailResponse> Create([FromRoute] int starId, [FromBody] CreateStarDockerImageRequest request)
|
|
||||||
{
|
|
||||||
await ApplyStar(starId);
|
|
||||||
|
|
||||||
var starDockerImage = Mapper.Map<StarDockerImage>(request);
|
|
||||||
starDockerImage.Star = Star;
|
|
||||||
|
|
||||||
var finalVariable = await StarDockerImageRepository.Add(starDockerImage);
|
|
||||||
|
|
||||||
return CrudHelper.MapToResult(finalVariable);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPatch("{starId:int}/dockerImages/{id:int}")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.update")]
|
|
||||||
public async Task<StarDockerImageDetailResponse> Update([FromRoute] int starId, [FromRoute] int id,
|
|
||||||
[FromBody] UpdateStarDockerImageRequest request)
|
|
||||||
{
|
|
||||||
await ApplyStar(starId);
|
|
||||||
|
|
||||||
return await CrudHelper.Update(id, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{starId:int}/dockerImages/{id:int}")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.delete")]
|
|
||||||
public async Task Delete([FromRoute] int starId, [FromRoute] int id)
|
|
||||||
{
|
|
||||||
await ApplyStar(starId);
|
|
||||||
|
|
||||||
await CrudHelper.Delete(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
using System.Text;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Helpers;
|
|
||||||
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 Export([FromRoute] int starId)
|
|
||||||
{
|
|
||||||
var exportedStar = await ImportExportService.Export(starId);
|
|
||||||
|
|
||||||
Response.StatusCode = 200;
|
|
||||||
Response.ContentType = "application/json";
|
|
||||||
await Response.WriteAsync(exportedStar);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("import")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.create")]
|
|
||||||
public async Task<StarDetailResponse> Import()
|
|
||||||
{
|
|
||||||
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.Import(content);
|
|
||||||
|
|
||||||
return Mapper.Map<StarDetailResponse>(star);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonCore.Extended.Helpers;
|
|
||||||
using MoonCore.Helpers;
|
|
||||||
using MoonCore.Models;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
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")]
|
|
||||||
public class StarVariablesController : Controller
|
|
||||||
{
|
|
||||||
private readonly CrudHelper<StarVariable, StarVariableDetailResponse> CrudHelper;
|
|
||||||
private readonly DatabaseRepository<Star> StarRepository;
|
|
||||||
private readonly DatabaseRepository<StarVariable> StarVariableRepository;
|
|
||||||
|
|
||||||
private Star Star;
|
|
||||||
|
|
||||||
public StarVariablesController(
|
|
||||||
CrudHelper<StarVariable, StarVariableDetailResponse> crudHelper,
|
|
||||||
DatabaseRepository<Star> starRepository,
|
|
||||||
DatabaseRepository<StarVariable> starVariableRepository)
|
|
||||||
{
|
|
||||||
CrudHelper = crudHelper;
|
|
||||||
StarRepository = starRepository;
|
|
||||||
StarVariableRepository = starVariableRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ApplyStar(int id)
|
|
||||||
{
|
|
||||||
var star = await StarRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
|
||||||
|
|
||||||
if (star == null)
|
|
||||||
throw new HttpApiException("A star with this id could not be found", 404);
|
|
||||||
|
|
||||||
Star = star;
|
|
||||||
|
|
||||||
CrudHelper.QueryModifier = variables =>
|
|
||||||
variables.Where(x => x.Star.Id == star.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{starId:int}/variables")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.get")]
|
|
||||||
public async Task<IPagedData<StarVariableDetailResponse>> Get([FromRoute] int starId, [FromQuery] int page, [FromQuery] int pageSize)
|
|
||||||
{
|
|
||||||
await ApplyStar(starId);
|
|
||||||
|
|
||||||
return await CrudHelper.Get(page, pageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{starId:int}/variables/{id:int}")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.get")]
|
|
||||||
public async Task<StarVariableDetailResponse> GetSingle([FromRoute] int starId, [FromRoute] int id)
|
|
||||||
{
|
|
||||||
await ApplyStar(starId);
|
|
||||||
|
|
||||||
return await CrudHelper.GetSingle(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{starId:int}/variables")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.create")]
|
|
||||||
public async Task<StarVariableDetailResponse> Create([FromRoute] int starId, [FromBody] CreateStarVariableRequest request)
|
|
||||||
{
|
|
||||||
await ApplyStar(starId);
|
|
||||||
|
|
||||||
var starVariable = Mapper.Map<StarVariable>(request);
|
|
||||||
starVariable.Star = Star;
|
|
||||||
|
|
||||||
var finalVariable = await StarVariableRepository.Add(starVariable);
|
|
||||||
|
|
||||||
return CrudHelper.MapToResult(finalVariable);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPatch("{starId:int}/variables/{id:int}")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.update")]
|
|
||||||
public async Task<StarVariableDetailResponse> Update([FromRoute] int starId, [FromRoute] int id,
|
|
||||||
[FromBody] UpdateStarVariableRequest request)
|
|
||||||
{
|
|
||||||
await ApplyStar(starId);
|
|
||||||
|
|
||||||
var variable = await CrudHelper.GetSingleModel(id);
|
|
||||||
|
|
||||||
variable = Mapper.Map(variable, request);
|
|
||||||
|
|
||||||
await StarVariableRepository.Update(variable);
|
|
||||||
|
|
||||||
return CrudHelper.MapToResult(variable);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{starId:int}/variables/{id:int}")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.delete")]
|
|
||||||
public async Task Delete([FromRoute] int starId, [FromRoute] int id)
|
|
||||||
{
|
|
||||||
await ApplyStar(starId);
|
|
||||||
|
|
||||||
await CrudHelper.Delete(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonCore.Extended.Helpers;
|
|
||||||
using MoonCore.Helpers;
|
|
||||||
using MoonCore.Models;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.Shared.Http.Requests.Admin.Stars;
|
|
||||||
using MoonlightServers.Shared.Http.Responses.Admin.StarDockerImages;
|
|
||||||
using MoonlightServers.Shared.Http.Responses.Admin.Stars;
|
|
||||||
using MoonlightServers.Shared.Http.Responses.Admin.StarVariables;
|
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Http.Controllers.Admin.Stars;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/admin/servers/stars")]
|
|
||||||
public class StarsController : Controller
|
|
||||||
{
|
|
||||||
private readonly CrudHelper<Star, StarDetailResponse> CrudHelper;
|
|
||||||
private readonly DatabaseRepository<Star> StarRepository;
|
|
||||||
|
|
||||||
public StarsController(CrudHelper<Star, StarDetailResponse> crudHelper, DatabaseRepository<Star> starRepository)
|
|
||||||
{
|
|
||||||
CrudHelper = crudHelper;
|
|
||||||
StarRepository = starRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.get")]
|
|
||||||
public async Task<IPagedData<StarDetailResponse>> Get([FromQuery] int page, [FromQuery] int pageSize)
|
|
||||||
{
|
|
||||||
return await CrudHelper.Get(page, pageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id:int}")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.get")]
|
|
||||||
public async Task<StarDetailResponse> GetSingle([FromRoute] int id)
|
|
||||||
{
|
|
||||||
return await CrudHelper.GetSingle(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.create")]
|
|
||||||
public async Task<StarDetailResponse> Create([FromBody] CreateStarRequest request)
|
|
||||||
{
|
|
||||||
var star = Mapper.Map<Star>(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.Add(star);
|
|
||||||
|
|
||||||
return CrudHelper.MapToResult(finalStar);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPatch("{id:int}")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.update")]
|
|
||||||
public async Task<StarDetailResponse> Update([FromRoute] int id, [FromBody] UpdateStarRequest request)
|
|
||||||
{
|
|
||||||
return await CrudHelper.Update(id, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{id:int}")]
|
|
||||||
[Authorize(Policy = "permissions:admin.servers.stars.delete")]
|
|
||||||
public async Task Delete([FromRoute] int id)
|
|
||||||
{
|
|
||||||
await CrudHelper.Delete(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Services;
|
|
||||||
using MoonlightServers.DaemonShared.Enums;
|
|
||||||
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")]
|
|
||||||
public class FilesController : Controller
|
|
||||||
{
|
|
||||||
private readonly DatabaseRepository<Server> ServerRepository;
|
|
||||||
private readonly ServerFileSystemService ServerFileSystemService;
|
|
||||||
private readonly NodeService NodeService;
|
|
||||||
private readonly ServerAuthorizeService AuthorizeService;
|
|
||||||
|
|
||||||
public FilesController(
|
|
||||||
DatabaseRepository<Server> serverRepository,
|
|
||||||
ServerFileSystemService serverFileSystemService,
|
|
||||||
NodeService nodeService,
|
|
||||||
ServerAuthorizeService authorizeService
|
|
||||||
)
|
|
||||||
{
|
|
||||||
ServerRepository = serverRepository;
|
|
||||||
ServerFileSystemService = serverFileSystemService;
|
|
||||||
NodeService = nodeService;
|
|
||||||
AuthorizeService = authorizeService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{serverId:int}/files/list")]
|
|
||||||
public async Task<ServerFilesEntryResponse[]> List([FromRoute] int serverId, [FromQuery] string path)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId, ServerPermissionType.Read);
|
|
||||||
|
|
||||||
var entries = await ServerFileSystemService.List(server, path);
|
|
||||||
|
|
||||||
return entries.Select(x => new ServerFilesEntryResponse()
|
|
||||||
{
|
|
||||||
Name = x.Name,
|
|
||||||
Size = x.Size,
|
|
||||||
IsFile = x.IsFile,
|
|
||||||
CreatedAt = x.CreatedAt,
|
|
||||||
UpdatedAt = x.UpdatedAt
|
|
||||||
}).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{serverId:int}/files/move")]
|
|
||||||
public async Task Move([FromRoute] int serverId, [FromQuery] string oldPath, [FromQuery] string newPath)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId, ServerPermissionType.ReadWrite);
|
|
||||||
|
|
||||||
await ServerFileSystemService.Move(server, oldPath, newPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{serverId:int}/files/delete")]
|
|
||||||
public async Task Delete([FromRoute] int serverId, [FromQuery] string path)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId, ServerPermissionType.ReadWrite);
|
|
||||||
|
|
||||||
await ServerFileSystemService.Delete(server, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{serverId:int}/files/mkdir")]
|
|
||||||
public async Task Mkdir([FromRoute] int serverId, [FromQuery] string path)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId, ServerPermissionType.ReadWrite);
|
|
||||||
|
|
||||||
await ServerFileSystemService.Mkdir(server, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{serverId:int}/files/upload")]
|
|
||||||
public async Task<ServerFilesUploadResponse> Upload([FromRoute] int serverId)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId, ServerPermissionType.ReadWrite);
|
|
||||||
|
|
||||||
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("{serverId:int}/files/download")]
|
|
||||||
public async Task<ServerFilesDownloadResponse> Download([FromRoute] int serverId, [FromQuery] string path)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId, ServerPermissionType.Read);
|
|
||||||
|
|
||||||
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("{serverId:int}/files/compress")]
|
|
||||||
public async Task Compress([FromRoute] int serverId, [FromBody] ServerFilesCompressRequest request)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId, ServerPermissionType.ReadWrite);
|
|
||||||
|
|
||||||
if (!Enum.TryParse(request.Type, true, out CompressType type))
|
|
||||||
throw new HttpApiException("Invalid compress type provided", 400);
|
|
||||||
|
|
||||||
await ServerFileSystemService.Compress(server, type, request.Items, request.Destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{serverId:int}/files/decompress")]
|
|
||||||
public async Task Decompress([FromRoute] int serverId, [FromBody] ServerFilesDecompressRequest request)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId, ServerPermissionType.ReadWrite);
|
|
||||||
|
|
||||||
if (!Enum.TryParse(request.Type, true, out CompressType type))
|
|
||||||
throw new HttpApiException("Invalid compress type provided", 400);
|
|
||||||
|
|
||||||
await ServerFileSystemService.Decompress(server, type, request.Path, request.Destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<Server> GetServerById(int serverId, ServerPermissionType type)
|
|
||||||
{
|
|
||||||
var server = await ServerRepository
|
|
||||||
.Get()
|
|
||||||
.Include(x => x.Node)
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == serverId);
|
|
||||||
|
|
||||||
if (server == null)
|
|
||||||
throw new HttpApiException("No server with this id found", 404);
|
|
||||||
|
|
||||||
var authorizeResult = await AuthorizeService.Authorize(
|
|
||||||
User, server,
|
|
||||||
permission => permission.Name == "files" && permission.Type >= type
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!authorizeResult.Succeeded)
|
|
||||||
{
|
|
||||||
throw new HttpApiException(
|
|
||||||
authorizeResult.Message ?? "No permission for the requested resource",
|
|
||||||
403
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonCore.Helpers;
|
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Services;
|
|
||||||
using MoonlightServers.Shared.Enums;
|
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Http.Controllers.Client;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Authorize]
|
|
||||||
[Route("api/client/servers")]
|
|
||||||
public class PowerController : Controller
|
|
||||||
{
|
|
||||||
private readonly DatabaseRepository<Server> ServerRepository;
|
|
||||||
private readonly DatabaseRepository<User> UserRepository;
|
|
||||||
private readonly ServerService ServerService;
|
|
||||||
private readonly ServerAuthorizeService AuthorizeService;
|
|
||||||
|
|
||||||
public PowerController(
|
|
||||||
DatabaseRepository<Server> serverRepository,
|
|
||||||
DatabaseRepository<User> userRepository,
|
|
||||||
ServerService serverService,
|
|
||||||
ServerAuthorizeService authorizeService
|
|
||||||
)
|
|
||||||
{
|
|
||||||
ServerRepository = serverRepository;
|
|
||||||
UserRepository = userRepository;
|
|
||||||
ServerService = serverService;
|
|
||||||
AuthorizeService = authorizeService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{serverId:int}/start")]
|
|
||||||
[Authorize]
|
|
||||||
public async Task Start([FromRoute] int serverId)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId);
|
|
||||||
await ServerService.Start(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{serverId:int}/stop")]
|
|
||||||
[Authorize]
|
|
||||||
public async Task Stop([FromRoute] int serverId)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId);
|
|
||||||
await ServerService.Stop(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{serverId:int}/kill")]
|
|
||||||
[Authorize]
|
|
||||||
public async Task Kill([FromRoute] int serverId)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId);
|
|
||||||
await ServerService.Kill(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<Server> GetServerById(int serverId)
|
|
||||||
{
|
|
||||||
var server = await ServerRepository
|
|
||||||
.Get()
|
|
||||||
.Include(x => x.Node)
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == serverId);
|
|
||||||
|
|
||||||
if (server == null)
|
|
||||||
throw new HttpApiException("No server with this id found", 404);
|
|
||||||
|
|
||||||
var authorizeResult = await AuthorizeService.Authorize(
|
|
||||||
User, server,
|
|
||||||
permission => permission.Name == "power" && permission.Type >= ServerPermissionType.ReadWrite
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!authorizeResult.Succeeded)
|
|
||||||
{
|
|
||||||
throw new HttpApiException(
|
|
||||||
authorizeResult.Message ?? "No permission for the requested resource",
|
|
||||||
403
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,318 +0,0 @@
|
|||||||
using System.Security.Claims;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonCore.Models;
|
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Extensions;
|
|
||||||
using MoonlightServers.ApiServer.Models;
|
|
||||||
using MoonlightServers.ApiServer.Services;
|
|
||||||
using MoonlightServers.Shared.Enums;
|
|
||||||
using MoonlightServers.Shared.Http.Responses.Client.Servers;
|
|
||||||
using MoonlightServers.Shared.Http.Responses.Client.Servers.Allocations;
|
|
||||||
using MoonlightServers.Shared.Models;
|
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Http.Controllers.Client;
|
|
||||||
|
|
||||||
[Authorize]
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/client/servers")]
|
|
||||||
public class ServersController : Controller
|
|
||||||
{
|
|
||||||
private readonly ServerService ServerService;
|
|
||||||
private readonly DatabaseRepository<Server> ServerRepository;
|
|
||||||
private readonly DatabaseRepository<ServerShare> ShareRepository;
|
|
||||||
private readonly DatabaseRepository<User> UserRepository;
|
|
||||||
private readonly NodeService NodeService;
|
|
||||||
private readonly ServerAuthorizeService AuthorizeService;
|
|
||||||
|
|
||||||
public ServersController(
|
|
||||||
DatabaseRepository<Server> serverRepository,
|
|
||||||
NodeService nodeService,
|
|
||||||
ServerService serverService,
|
|
||||||
ServerAuthorizeService authorizeService,
|
|
||||||
DatabaseRepository<ServerShare> shareRepository,
|
|
||||||
DatabaseRepository<User> userRepository
|
|
||||||
)
|
|
||||||
{
|
|
||||||
ServerRepository = serverRepository;
|
|
||||||
NodeService = nodeService;
|
|
||||||
ServerService = serverService;
|
|
||||||
AuthorizeService = authorizeService;
|
|
||||||
ShareRepository = shareRepository;
|
|
||||||
UserRepository = userRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<PagedData<ServerDetailResponse>> GetAll([FromQuery] int page, [FromQuery] int pageSize)
|
|
||||||
{
|
|
||||||
var userIdClaim = User.FindFirstValue("userId");
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(userIdClaim))
|
|
||||||
throw new HttpApiException("Only users are able to use this endpoint", 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 count = await query.CountAsync();
|
|
||||||
var items = await query.Skip(page * pageSize).Take(pageSize).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 PagedData<ServerDetailResponse>()
|
|
||||||
{
|
|
||||||
Items = mappedItems,
|
|
||||||
CurrentPage = page,
|
|
||||||
PageSize = pageSize,
|
|
||||||
TotalItems = count,
|
|
||||||
TotalPages = count == 0 ? 0 : count / pageSize
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("shared")]
|
|
||||||
public async Task<PagedData<ServerDetailResponse>> GetAllShared([FromQuery] int page, [FromQuery] int pageSize)
|
|
||||||
{
|
|
||||||
var userIdClaim = User.FindFirstValue("userId");
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(userIdClaim))
|
|
||||||
throw new HttpApiException("Only users are able to use this endpoint", 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 count = await query.CountAsync();
|
|
||||||
var items = await query.Skip(page * pageSize).Take(pageSize).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 = x.Content.Permissions.ToArray()
|
|
||||||
}
|
|
||||||
}).ToArray();
|
|
||||||
|
|
||||||
return new PagedData<ServerDetailResponse>()
|
|
||||||
{
|
|
||||||
Items = mappedItems,
|
|
||||||
CurrentPage = page,
|
|
||||||
PageSize = pageSize,
|
|
||||||
TotalItems = count,
|
|
||||||
TotalPages = count == 0 ? 0 : count / pageSize
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{serverId:int}")]
|
|
||||||
public async Task<ServerDetailResponse> Get([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)
|
|
||||||
throw new HttpApiException("No server with this id found", 404);
|
|
||||||
|
|
||||||
var authorizationResult = await AuthorizeService.Authorize(User, server);
|
|
||||||
|
|
||||||
if (!authorizationResult.Succeeded)
|
|
||||||
{
|
|
||||||
throw new HttpApiException(
|
|
||||||
authorizationResult.Message ?? "No server with this id found",
|
|
||||||
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 = authorizationResult.Share.Content.Permissions.ToArray()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{serverId:int}/status")]
|
|
||||||
public async Task<ServerStatusResponse> GetStatus([FromRoute] int serverId)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId);
|
|
||||||
|
|
||||||
var status = await ServerService.GetStatus(server);
|
|
||||||
|
|
||||||
return new ServerStatusResponse()
|
|
||||||
{
|
|
||||||
State = status.State.ToServerPowerState()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{serverId:int}/ws")]
|
|
||||||
public async Task<ServerWebSocketResponse> GetWebSocket([FromRoute] int serverId)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(
|
|
||||||
serverId,
|
|
||||||
permission => permission is { Name: "console", Type: >= ServerPermissionType.Read }
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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<ServerLogsResponse> GetLogs([FromRoute] int serverId)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(
|
|
||||||
serverId,
|
|
||||||
permission => permission is { Name: "console", Type: >= ServerPermissionType.Read }
|
|
||||||
);
|
|
||||||
|
|
||||||
var logs = await ServerService.GetLogs(server);
|
|
||||||
|
|
||||||
return new ServerLogsResponse()
|
|
||||||
{
|
|
||||||
Messages = logs.Messages
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{serverId:int}/stats")]
|
|
||||||
public async Task<ServerStatsResponse> GetStats([FromRoute] int serverId)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(
|
|
||||||
serverId
|
|
||||||
);
|
|
||||||
|
|
||||||
var stats = await ServerService.GetStats(server);
|
|
||||||
|
|
||||||
return new ServerStatsResponse()
|
|
||||||
{
|
|
||||||
CpuUsage = stats.CpuUsage,
|
|
||||||
MemoryUsage = stats.MemoryUsage,
|
|
||||||
NetworkRead = stats.NetworkRead,
|
|
||||||
NetworkWrite = stats.NetworkWrite,
|
|
||||||
IoRead = stats.IoRead,
|
|
||||||
IoWrite = stats.IoWrite
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<Server> GetServerById(int serverId, Func<ServerSharePermission, bool>? filter = null)
|
|
||||||
{
|
|
||||||
var server = await ServerRepository
|
|
||||||
.Get()
|
|
||||||
.Include(x => x.Node)
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == serverId);
|
|
||||||
|
|
||||||
if (server == null)
|
|
||||||
throw new HttpApiException("No server with this id found", 404);
|
|
||||||
|
|
||||||
var authorizeResult = await AuthorizeService.Authorize(User, server, filter);
|
|
||||||
|
|
||||||
if (!authorizeResult.Succeeded)
|
|
||||||
{
|
|
||||||
throw new HttpApiException(
|
|
||||||
authorizeResult.Message ?? "No permission for the requested resource",
|
|
||||||
403
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Services;
|
|
||||||
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<Server> ServerRepository;
|
|
||||||
private readonly ServerAuthorizeService AuthorizeService;
|
|
||||||
|
|
||||||
public SettingsController(
|
|
||||||
ServerService serverService,
|
|
||||||
DatabaseRepository<Server> serverRepository,
|
|
||||||
ServerAuthorizeService authorizeService
|
|
||||||
)
|
|
||||||
{
|
|
||||||
ServerService = serverService;
|
|
||||||
ServerRepository = serverRepository;
|
|
||||||
AuthorizeService = authorizeService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{serverId:int}/install")]
|
|
||||||
[Authorize]
|
|
||||||
public async Task Install([FromRoute] int serverId)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId);
|
|
||||||
await ServerService.Install(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<Server> GetServerById(int serverId)
|
|
||||||
{
|
|
||||||
var server = await ServerRepository
|
|
||||||
.Get()
|
|
||||||
.Include(x => x.Node)
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == serverId);
|
|
||||||
|
|
||||||
if (server == null)
|
|
||||||
throw new HttpApiException("No server with this id found", 404);
|
|
||||||
|
|
||||||
var authorizeResult = await AuthorizeService.Authorize(
|
|
||||||
User, server,
|
|
||||||
permission => permission is { Name: "settings", Type: >= ServerPermissionType.ReadWrite }
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!authorizeResult.Succeeded)
|
|
||||||
{
|
|
||||||
throw new HttpApiException(
|
|
||||||
authorizeResult.Message ?? "No permission for the requested resource",
|
|
||||||
403
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,232 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonCore.Models;
|
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Services;
|
|
||||||
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")]
|
|
||||||
public class SharesController : Controller
|
|
||||||
{
|
|
||||||
private readonly DatabaseRepository<Server> ServerRepository;
|
|
||||||
private readonly DatabaseRepository<ServerShare> ShareRepository;
|
|
||||||
private readonly DatabaseRepository<User> UserRepository;
|
|
||||||
private readonly ServerAuthorizeService AuthorizeService;
|
|
||||||
|
|
||||||
public SharesController(
|
|
||||||
DatabaseRepository<Server> serverRepository,
|
|
||||||
DatabaseRepository<ServerShare> shareRepository,
|
|
||||||
DatabaseRepository<User> userRepository,
|
|
||||||
ServerAuthorizeService authorizeService
|
|
||||||
)
|
|
||||||
{
|
|
||||||
ServerRepository = serverRepository;
|
|
||||||
ShareRepository = shareRepository;
|
|
||||||
UserRepository = userRepository;
|
|
||||||
AuthorizeService = authorizeService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{serverId:int}/shares")]
|
|
||||||
public async Task<PagedData<ServerShareResponse>> GetAll(
|
|
||||||
[FromRoute] int serverId,
|
|
||||||
[FromQuery] [Range(0, int.MaxValue)] int page,
|
|
||||||
[FromQuery] [Range(1, 100)] int pageSize
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId);
|
|
||||||
|
|
||||||
var query = ShareRepository
|
|
||||||
.Get()
|
|
||||||
.Where(x => x.Server.Id == server.Id);
|
|
||||||
|
|
||||||
var count = await query.CountAsync();
|
|
||||||
var items = await query.Skip(page * pageSize).Take(pageSize).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 = x.Content.Permissions.ToArray()
|
|
||||||
}).ToArray();
|
|
||||||
|
|
||||||
return new PagedData<ServerShareResponse>()
|
|
||||||
{
|
|
||||||
Items = mappedItems,
|
|
||||||
CurrentPage = page,
|
|
||||||
PageSize = pageSize,
|
|
||||||
TotalItems = count,
|
|
||||||
TotalPages = count == 0 ? 0 : count / pageSize
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{serverId:int}/shares/{id:int}")]
|
|
||||||
public async Task<ServerShareResponse> Get(
|
|
||||||
[FromRoute] int serverId,
|
|
||||||
[FromRoute] int id
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId);
|
|
||||||
|
|
||||||
var share = await ShareRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefaultAsync(x => x.Server.Id == server.Id && x.Id == id);
|
|
||||||
|
|
||||||
if (share == null)
|
|
||||||
throw new HttpApiException("A share with that id cannot be found", 404);
|
|
||||||
|
|
||||||
var user = await UserRepository
|
|
||||||
.Get()
|
|
||||||
.FirstAsync(x => x.Id == share.UserId);
|
|
||||||
|
|
||||||
var mappedItem = new ServerShareResponse()
|
|
||||||
{
|
|
||||||
Id = share.Id,
|
|
||||||
Username = user.Username,
|
|
||||||
Permissions = share.Content.Permissions.ToArray()
|
|
||||||
};
|
|
||||||
|
|
||||||
return mappedItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{serverId:int}/shares")]
|
|
||||||
public async Task<ServerShareResponse> Create(
|
|
||||||
[FromRoute] int serverId,
|
|
||||||
[FromBody] CreateShareRequest request
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId);
|
|
||||||
|
|
||||||
var user = await UserRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefaultAsync(x => x.Username == request.Username);
|
|
||||||
|
|
||||||
if (user == null)
|
|
||||||
throw new HttpApiException("A user with that username could not be found", 400);
|
|
||||||
|
|
||||||
var share = new ServerShare()
|
|
||||||
{
|
|
||||||
Server = server,
|
|
||||||
Content = new()
|
|
||||||
{
|
|
||||||
Permissions = request.Permissions
|
|
||||||
},
|
|
||||||
CreatedAt = DateTime.UtcNow,
|
|
||||||
UpdatedAt = DateTime.UtcNow,
|
|
||||||
UserId = user.Id
|
|
||||||
};
|
|
||||||
|
|
||||||
var finalShare = await ShareRepository.Add(share);
|
|
||||||
|
|
||||||
var mappedItem = new ServerShareResponse()
|
|
||||||
{
|
|
||||||
Id = finalShare.Id,
|
|
||||||
Username = user.Username,
|
|
||||||
Permissions = finalShare.Content.Permissions.ToArray()
|
|
||||||
};
|
|
||||||
|
|
||||||
return mappedItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPatch("{serverId:int}/shares/{id:int}")]
|
|
||||||
public async Task<ServerShareResponse> Update(
|
|
||||||
[FromRoute] int serverId,
|
|
||||||
[FromRoute] int id,
|
|
||||||
[FromBody] UpdateShareRequest request
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId);
|
|
||||||
|
|
||||||
var share = await ShareRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefaultAsync(x => x.Server.Id == server.Id && x.Id == id);
|
|
||||||
|
|
||||||
if (share == null)
|
|
||||||
throw new HttpApiException("A share with that id cannot be found", 404);
|
|
||||||
|
|
||||||
share.Content.Permissions = request.Permissions;
|
|
||||||
share.UpdatedAt = DateTime.UtcNow;
|
|
||||||
|
|
||||||
await ShareRepository.Update(share);
|
|
||||||
|
|
||||||
var user = await UserRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == share.UserId);
|
|
||||||
|
|
||||||
if (user == null)
|
|
||||||
throw new HttpApiException("A user with that id could not be found", 400);
|
|
||||||
|
|
||||||
var mappedItem = new ServerShareResponse()
|
|
||||||
{
|
|
||||||
Id = share.Id,
|
|
||||||
Username = user.Username,
|
|
||||||
Permissions = share.Content.Permissions.ToArray()
|
|
||||||
};
|
|
||||||
|
|
||||||
return mappedItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{serverId:int}/shares/{id:int}")]
|
|
||||||
public async Task Delete(
|
|
||||||
[FromRoute] int serverId,
|
|
||||||
[FromRoute] int id
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId);
|
|
||||||
|
|
||||||
var share = await ShareRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefaultAsync(x => x.Server.Id == server.Id && x.Id == id);
|
|
||||||
|
|
||||||
if (share == null)
|
|
||||||
throw new HttpApiException("A share with that id cannot be found", 404);
|
|
||||||
|
|
||||||
await ShareRepository.Remove(share);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<Server> GetServerById(int serverId)
|
|
||||||
{
|
|
||||||
var server = await ServerRepository
|
|
||||||
.Get()
|
|
||||||
.Include(x => x.Node)
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == serverId);
|
|
||||||
|
|
||||||
if (server == null)
|
|
||||||
throw new HttpApiException("No server with this id found", 404);
|
|
||||||
|
|
||||||
var authorizeResult = await AuthorizeService.Authorize(
|
|
||||||
User, server,
|
|
||||||
permission => permission is { Name: "shares", Type: >= ServerPermissionType.ReadWrite }
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!authorizeResult.Succeeded)
|
|
||||||
{
|
|
||||||
throw new HttpApiException(
|
|
||||||
authorizeResult.Message ?? "No permission for the requested resource",
|
|
||||||
403
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Services;
|
|
||||||
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")]
|
|
||||||
public class VariablesController : Controller
|
|
||||||
{
|
|
||||||
private readonly DatabaseRepository<Server> ServerRepository;
|
|
||||||
private readonly ServerAuthorizeService AuthorizeService;
|
|
||||||
|
|
||||||
public VariablesController(
|
|
||||||
DatabaseRepository<Server> serverRepository,
|
|
||||||
ServerAuthorizeService authorizeService
|
|
||||||
)
|
|
||||||
{
|
|
||||||
ServerRepository = serverRepository;
|
|
||||||
AuthorizeService = authorizeService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{serverId:int}/variables")]
|
|
||||||
public async Task<ServerVariableDetailResponse[]> Get([FromRoute] int serverId)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId, ServerPermissionType.Read);
|
|
||||||
|
|
||||||
return server.Star.Variables.Select(starVariable =>
|
|
||||||
{
|
|
||||||
var serverVariable = server.Variables.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();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPut("{serverId:int}/variables")]
|
|
||||||
public async Task<ServerVariableDetailResponse> UpdateSingle(
|
|
||||||
[FromRoute] int serverId,
|
|
||||||
[FromBody] UpdateServerVariableRequest request
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// TODO: Handle filter
|
|
||||||
|
|
||||||
var server = await GetServerById(serverId, ServerPermissionType.ReadWrite);
|
|
||||||
|
|
||||||
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.Update(server);
|
|
||||||
|
|
||||||
return new ServerVariableDetailResponse()
|
|
||||||
{
|
|
||||||
Key = starVariable.Key,
|
|
||||||
Value = serverVariable.Value,
|
|
||||||
Type = starVariable.Type,
|
|
||||||
Name = starVariable.Name,
|
|
||||||
Description = starVariable.Description,
|
|
||||||
Filter = starVariable.Filter
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPatch("{serverId:int}/variables")]
|
|
||||||
public async Task<ServerVariableDetailResponse[]> Update(
|
|
||||||
[FromRoute] int serverId,
|
|
||||||
[FromBody] UpdateServerVariableRangeRequest request
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var server = await GetServerById(serverId, ServerPermissionType.ReadWrite);
|
|
||||||
|
|
||||||
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.Update(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<Server> GetServerById(int serverId, ServerPermissionType type)
|
|
||||||
{
|
|
||||||
var server = await ServerRepository
|
|
||||||
.Get()
|
|
||||||
.Include(x => x.Variables)
|
|
||||||
.Include(x => x.Star)
|
|
||||||
.ThenInclude(x => x.Variables)
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == serverId);
|
|
||||||
|
|
||||||
if (server == null)
|
|
||||||
throw new HttpApiException("No server with this id found", 404);
|
|
||||||
|
|
||||||
var authorizeResult = await AuthorizeService.Authorize(
|
|
||||||
User, server,
|
|
||||||
permission => permission.Name == "variables" && permission.Type >= type
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!authorizeResult.Succeeded)
|
|
||||||
{
|
|
||||||
throw new HttpApiException(
|
|
||||||
authorizeResult.Message ?? "No permission for the requested resource",
|
|
||||||
403
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 Get() => Task.CompletedTask;
|
|
||||||
}
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonCore.Models;
|
|
||||||
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<Server> ServerRepository;
|
|
||||||
private readonly DatabaseRepository<Node> NodeRepository;
|
|
||||||
private readonly ILogger<ServersController> Logger;
|
|
||||||
|
|
||||||
public ServersController(
|
|
||||||
DatabaseRepository<Server> serverRepository,
|
|
||||||
DatabaseRepository<Node> nodeRepository,
|
|
||||||
ILogger<ServersController> logger
|
|
||||||
)
|
|
||||||
{
|
|
||||||
ServerRepository = serverRepository;
|
|
||||||
NodeRepository = nodeRepository;
|
|
||||||
Logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<PagedData<ServerDataResponse>> Get([FromQuery] int page, [FromQuery] int pageSize)
|
|
||||||
{
|
|
||||||
// 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(page * pageSize)
|
|
||||||
.Take(pageSize)
|
|
||||||
.ToArrayAsync();
|
|
||||||
|
|
||||||
var serverData = new List<ServerDataResponse>();
|
|
||||||
|
|
||||||
foreach (var server in servers)
|
|
||||||
{
|
|
||||||
var convertedData = ConvertToServerData(server);
|
|
||||||
|
|
||||||
if (convertedData == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
serverData.Add(convertedData);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PagedData<ServerDataResponse>()
|
|
||||||
{
|
|
||||||
Items = serverData.ToArray(),
|
|
||||||
CurrentPage = page,
|
|
||||||
PageSize = pageSize,
|
|
||||||
TotalItems = total,
|
|
||||||
TotalPages = total == 0 ? 0 : total / pageSize
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id:int}")]
|
|
||||||
public async Task<ServerDataResponse> Get([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<ServerInstallDataResponse> GetInstall([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),
|
|
||||||
Bandwidth = server.Bandwidth,
|
|
||||||
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,
|
|
||||||
UseVirtualDisk = server.UseVirtualDisk
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
using System.Security.Claims;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using MoonCore.Attributes;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Interfaces;
|
|
||||||
using MoonlightServers.ApiServer.Models;
|
|
||||||
using MoonlightServers.Shared.Models;
|
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Implementations.ServerAuthFilters;
|
|
||||||
|
|
||||||
public class AdminAuthFilter : IServerAuthorizationFilter
|
|
||||||
{
|
|
||||||
private readonly IAuthorizationService AuthorizationService;
|
|
||||||
|
|
||||||
public AdminAuthFilter(IAuthorizationService authorizationService)
|
|
||||||
{
|
|
||||||
AuthorizationService = authorizationService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<ServerAuthorizationResult?> Process(
|
|
||||||
ClaimsPrincipal user,
|
|
||||||
Server server,
|
|
||||||
Func<ServerSharePermission, bool>? filter = null
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var authResult = await AuthorizationService.AuthorizeAsync(
|
|
||||||
user,
|
|
||||||
"permissions:admin.servers.manage"
|
|
||||||
);
|
|
||||||
|
|
||||||
return authResult.Succeeded ? ServerAuthorizationResult.Success() : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +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.Models;
|
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Implementations.ServerAuthFilters;
|
|
||||||
|
|
||||||
public class OwnerAuthFilter : IServerAuthorizationFilter
|
|
||||||
{
|
|
||||||
public Task<ServerAuthorizationResult?> Process(ClaimsPrincipal user, Server server, Func<ServerSharePermission, bool>? filter = null)
|
|
||||||
{
|
|
||||||
var userIdValue = user.FindFirstValue("userId");
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(userIdValue)) // This is the case for api keys
|
|
||||||
return Task.FromResult<ServerAuthorizationResult?>(null);
|
|
||||||
|
|
||||||
var userId = int.Parse(userIdValue);
|
|
||||||
|
|
||||||
if(server.OwnerId != userId)
|
|
||||||
return Task.FromResult<ServerAuthorizationResult?>(null);
|
|
||||||
|
|
||||||
return Task.FromResult<ServerAuthorizationResult?>(
|
|
||||||
ServerAuthorizationResult.Success()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
using System.Security.Claims;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MoonCore.Attributes;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Interfaces;
|
|
||||||
using MoonlightServers.ApiServer.Models;
|
|
||||||
using MoonlightServers.Shared.Models;
|
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Implementations.ServerAuthFilters;
|
|
||||||
|
|
||||||
public class ShareAuthFilter : IServerAuthorizationFilter
|
|
||||||
{
|
|
||||||
private readonly DatabaseRepository<ServerShare> ShareRepository;
|
|
||||||
|
|
||||||
public ShareAuthFilter(DatabaseRepository<ServerShare> shareRepository)
|
|
||||||
{
|
|
||||||
ShareRepository = shareRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<ServerAuthorizationResult?> Process(
|
|
||||||
ClaimsPrincipal user,
|
|
||||||
Server server,
|
|
||||||
Func<ServerSharePermission, bool>? filter = null
|
|
||||||
)
|
|
||||||
{
|
|
||||||
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(filter == null)
|
|
||||||
return ServerAuthorizationResult.Success(share);
|
|
||||||
|
|
||||||
if(share.Content.Permissions.Any(filter))
|
|
||||||
return ServerAuthorizationResult.Success(share);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using System.Security.Claims;
|
|
||||||
using MoonlightServers.ApiServer.Database.Entities;
|
|
||||||
using MoonlightServers.ApiServer.Models;
|
|
||||||
using MoonlightServers.Shared.Models;
|
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Interfaces;
|
|
||||||
|
|
||||||
public interface IServerAuthorizationFilter
|
|
||||||
{
|
|
||||||
// Return null => skip to next filter / handler
|
|
||||||
// Return any value, instant return
|
|
||||||
public Task<ServerAuthorizationResult?> Process(
|
|
||||||
ClaimsPrincipal user,
|
|
||||||
Server server,
|
|
||||||
Func<ServerSharePermission, bool>? filter = 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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using MoonlightServers.Shared.Models;
|
|
||||||
|
|
||||||
namespace MoonlightServers.ApiServer.Models;
|
|
||||||
|
|
||||||
public class ServerShareContent
|
|
||||||
{
|
|
||||||
public List<ServerSharePermission> Permissions { get; set; } = [];
|
|
||||||
}
|
|
||||||
@@ -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<ImageVariable> Variables { get; set; } = new();
|
|
||||||
|
|
||||||
public int DefaultDockerImage { get; set; } = 0;
|
|
||||||
public bool AllowDockerImageChange { get; set; } = false;
|
|
||||||
public List<ImageDockerImage> 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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user