Recreated plugin with new project template. Started implementing server system daemon
This commit is contained in:
76
MoonlightServers.Daemon/Helpers/AppConsoleFormatter.cs
Normal file
76
MoonlightServers.Daemon/Helpers/AppConsoleFormatter.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Logging.Console;
|
||||
|
||||
namespace MoonlightServers.Daemon.Helpers;
|
||||
|
||||
public class AppConsoleFormatter : ConsoleFormatter
|
||||
{
|
||||
private const string TimestampColor = "\e[38;2;148;148;148m";
|
||||
private const string CategoryColor = "\e[38;2;198;198;198m";
|
||||
private const string MessageColor = "\e[38;2;255;255;255m";
|
||||
private const string Bold = "\e[1m";
|
||||
|
||||
// Pre-computed ANSI color codes for each log level
|
||||
private const string CriticalColor = "\e[38;2;255;0;0m";
|
||||
private const string ErrorColor = "\e[38;2;255;0;0m";
|
||||
private const string WarningColor = "\e[38;2;215;215;0m";
|
||||
private const string InfoColor = "\e[38;2;135;215;255m";
|
||||
private const string DebugColor = "\e[38;2;198;198;198m";
|
||||
private const string TraceColor = "\e[38;2;68;68;68m";
|
||||
|
||||
public AppConsoleFormatter() : base(nameof(AppConsoleFormatter))
|
||||
{
|
||||
}
|
||||
|
||||
public override void Write<TState>(
|
||||
in LogEntry<TState> logEntry,
|
||||
IExternalScopeProvider? scopeProvider,
|
||||
TextWriter textWriter)
|
||||
{
|
||||
var message = logEntry.Formatter(logEntry.State, logEntry.Exception);
|
||||
|
||||
// Timestamp
|
||||
textWriter.Write(TimestampColor);
|
||||
textWriter.Write(DateTime.Now.ToString("dd.MM.yy HH:mm:ss"));
|
||||
textWriter.Write(' ');
|
||||
|
||||
// Log level with color and bold
|
||||
var (levelText, levelColor) = GetLevelInfo(logEntry.LogLevel);
|
||||
textWriter.Write(levelColor);
|
||||
textWriter.Write(Bold);
|
||||
textWriter.Write(levelText);
|
||||
textWriter.Write(' ');
|
||||
|
||||
// Category
|
||||
textWriter.Write(CategoryColor);
|
||||
textWriter.Write(logEntry.Category);
|
||||
|
||||
// Message
|
||||
textWriter.Write(MessageColor);
|
||||
textWriter.Write(": ");
|
||||
textWriter.Write(message);
|
||||
|
||||
// Exception
|
||||
if (logEntry.Exception != null)
|
||||
{
|
||||
textWriter.Write(MessageColor);
|
||||
textWriter.WriteLine(logEntry.Exception.ToString());
|
||||
}
|
||||
else
|
||||
textWriter.WriteLine();
|
||||
}
|
||||
|
||||
private static (string text, string color) GetLevelInfo(LogLevel logLevel)
|
||||
{
|
||||
return logLevel switch
|
||||
{
|
||||
LogLevel.Critical => ("CRIT", CriticalColor),
|
||||
LogLevel.Error => ("ERRO", ErrorColor),
|
||||
LogLevel.Warning => ("WARN", WarningColor),
|
||||
LogLevel.Information => ("INFO", InfoColor),
|
||||
LogLevel.Debug => ("DEBG", DebugColor),
|
||||
LogLevel.Trace => ("TRCE", TraceColor),
|
||||
_ => ("NONE", "")
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
namespace MoonlightServers.Daemon.Helpers;
|
||||
|
||||
public class CompositeServiceProvider : IServiceProvider
|
||||
{
|
||||
private readonly List<IServiceProvider> ServiceProviders;
|
||||
|
||||
public CompositeServiceProvider(params IServiceProvider[] serviceProviders)
|
||||
{
|
||||
ServiceProviders = new List<IServiceProvider>(serviceProviders);
|
||||
}
|
||||
|
||||
public object? GetService(Type serviceType)
|
||||
{
|
||||
foreach (var provider in ServiceProviders)
|
||||
{
|
||||
try
|
||||
{
|
||||
var service = provider.GetService(serviceType);
|
||||
|
||||
if (service != null)
|
||||
return service;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Mono.Unix.Native;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Helpers;
|
||||
using MoonlightServers.Daemon.Models;
|
||||
|
||||
namespace MoonlightServers.Daemon.Helpers;
|
||||
|
||||
[Singleton]
|
||||
public class HostSystemHelper
|
||||
{
|
||||
private readonly ILogger<HostSystemHelper> Logger;
|
||||
|
||||
public HostSystemHelper(ILogger<HostSystemHelper> logger)
|
||||
{
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
public string GetOsName()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
// Windows platform detected
|
||||
var osVersion = Environment.OSVersion.Version;
|
||||
return $"Windows {osVersion.Major}.{osVersion.Minor}.{osVersion.Build}";
|
||||
}
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
var releaseRaw = File
|
||||
.ReadAllLines("/etc/os-release")
|
||||
.FirstOrDefault(x => x.StartsWith("PRETTY_NAME="));
|
||||
|
||||
if (string.IsNullOrEmpty(releaseRaw))
|
||||
return "Linux (unknown release)";
|
||||
|
||||
var release = releaseRaw
|
||||
.Replace("PRETTY_NAME=", "")
|
||||
.Replace("\"", "");
|
||||
|
||||
if (string.IsNullOrEmpty(release))
|
||||
return "Linux (unknown release)";
|
||||
|
||||
return release;
|
||||
}
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
// macOS platform detected
|
||||
var osVersion = Environment.OSVersion.Version;
|
||||
return $"Shitty macOS {osVersion.Major}.{osVersion.Minor}.{osVersion.Build}";
|
||||
}
|
||||
|
||||
// Unknown platform
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
#region CPU Usage
|
||||
|
||||
public async Task<CpuUsageDetails> GetCpuUsageAsync()
|
||||
{
|
||||
var result = new CpuUsageDetails();
|
||||
var perCoreUsages = new List<double>();
|
||||
|
||||
// Initial read
|
||||
var (cpuLastStats, cpuLastSums) = await ReadAllCpuStatsAsync();
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Second read
|
||||
var (cpuNowStats, cpuNowSums) = await ReadAllCpuStatsAsync();
|
||||
|
||||
for (var i = 0; i < cpuNowStats.Length; i++)
|
||||
{
|
||||
var cpuDelta = cpuNowSums[i] - cpuLastSums[i];
|
||||
var cpuIdle = cpuNowStats[i][3] - cpuLastStats[i][3];
|
||||
var cpuUsed = cpuDelta - cpuIdle;
|
||||
|
||||
var usage = 100.0 * cpuUsed / cpuDelta;
|
||||
|
||||
if (i == 0)
|
||||
result.OverallUsage = usage;
|
||||
else
|
||||
perCoreUsages.Add(usage);
|
||||
}
|
||||
|
||||
result.PerCoreUsage = perCoreUsages.ToArray();
|
||||
|
||||
// Get model name
|
||||
var cpuInfoLines = await File.ReadAllLinesAsync("/proc/cpuinfo");
|
||||
var modelLine = cpuInfoLines.FirstOrDefault(x => x.StartsWith("model name"));
|
||||
result.Model = modelLine?.Split(":")[1].Trim() ?? "N/A";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<(long[][] cpuStatsList, long[] cpuSums)> ReadAllCpuStatsAsync()
|
||||
{
|
||||
var lines = await File.ReadAllLinesAsync("/proc/stat");
|
||||
|
||||
lines = lines.Where(line => line.StartsWith("cpu"))
|
||||
.TakeWhile(line => line.StartsWith("cpu")) // Ensures only CPU lines are read
|
||||
.ToArray();
|
||||
|
||||
var statsList = new List<long[]>();
|
||||
var sumList = new List<long>();
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Skip(1) // Skip the "cpu" label
|
||||
.ToArray();
|
||||
|
||||
var cpuTimes = parts
|
||||
.Select(long.Parse)
|
||||
.ToArray();
|
||||
|
||||
var sum = cpuTimes.Sum();
|
||||
|
||||
statsList.Add(cpuTimes);
|
||||
sumList.Add(sum);
|
||||
}
|
||||
|
||||
return (statsList.ToArray(), sumList.ToArray());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Memory
|
||||
|
||||
public async Task ClearCachedMemoryAsync()
|
||||
{
|
||||
await File.WriteAllTextAsync("/proc/sys/vm/drop_caches", "3");
|
||||
}
|
||||
|
||||
public async Task<MemoryUsageDetails> GetMemoryUsageAsync()
|
||||
{
|
||||
var details = new MemoryUsageDetails();
|
||||
|
||||
var lines = await File.ReadAllLinesAsync("/proc/meminfo");
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
// We want to ignore all non kilobyte values
|
||||
if (!line.Contains("kB"))
|
||||
continue;
|
||||
|
||||
// Split the line up so we can extract the id and the value
|
||||
// to map it to the model field
|
||||
var parts = line.Split(":");
|
||||
|
||||
var id = parts[0];
|
||||
var value = parts[1]
|
||||
.Replace("kB", "")
|
||||
.Trim();
|
||||
|
||||
if (!long.TryParse(value, out var longValue))
|
||||
continue;
|
||||
|
||||
var bytes = ByteConverter.FromKiloBytes(longValue).Bytes;
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case "MemTotal":
|
||||
details.Total = bytes;
|
||||
break;
|
||||
|
||||
case "MemFree":
|
||||
details.Free = bytes;
|
||||
break;
|
||||
|
||||
case "MemAvailable":
|
||||
details.Available = bytes;
|
||||
break;
|
||||
|
||||
case "Cached":
|
||||
details.Cached = bytes;
|
||||
break;
|
||||
|
||||
case "SwapTotal":
|
||||
details.SwapTotal = bytes;
|
||||
break;
|
||||
|
||||
case "SwapFree":
|
||||
details.SwapFree = bytes;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Disks
|
||||
|
||||
public async Task<DiskUsageDetails[]> GetDiskUsagesAsync()
|
||||
{
|
||||
var details = new List<DiskUsageDetails>();
|
||||
|
||||
// First we need to check which mounts actually exist
|
||||
var diskDevices = new Dictionary<string, string>();
|
||||
string[] ignoredMounts = ["/boot/efi", "/boot"];
|
||||
|
||||
var mountLines = await File.ReadAllLinesAsync("/proc/mounts");
|
||||
|
||||
foreach (var mountLine in mountLines)
|
||||
{
|
||||
var parts = mountLine.Split(" ");
|
||||
|
||||
var device = parts[0];
|
||||
var mountedAt = parts[1];
|
||||
|
||||
// We only want to handle mounted physical devices
|
||||
if (!device.StartsWith("/dev/"))
|
||||
continue;
|
||||
|
||||
// Ignore certain mounts which we dont want to show
|
||||
if (ignoredMounts.Contains(mountedAt))
|
||||
continue;
|
||||
|
||||
diskDevices.Add(device, mountedAt);
|
||||
}
|
||||
|
||||
foreach (var diskMount in diskDevices)
|
||||
{
|
||||
var device = diskMount.Key;
|
||||
var mount = diskMount.Value;
|
||||
|
||||
var statusCode = Syscall.statvfs(mount, out var statvfs);
|
||||
|
||||
if (statusCode != 0)
|
||||
{
|
||||
var error = Stdlib.GetLastError();
|
||||
|
||||
Logger.LogError(
|
||||
"An error occured while checking disk stats for mount {mount}: {error}",
|
||||
mount,
|
||||
error
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Source: https://man7.org/linux/man-pages/man3/statvfs.3.html
|
||||
var detail = new DiskUsageDetails()
|
||||
{
|
||||
Device = device,
|
||||
MountPath = mount,
|
||||
DiskTotal = statvfs.f_blocks * statvfs.f_frsize,
|
||||
DiskFree = statvfs.f_bfree * statvfs.f_frsize,
|
||||
InodesTotal = statvfs.f_files,
|
||||
InodesFree = statvfs.f_ffree
|
||||
};
|
||||
|
||||
details.Add(detail);
|
||||
}
|
||||
|
||||
return details.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
using System.Text;
|
||||
using ICSharpCode.SharpZipLib.GZip;
|
||||
using ICSharpCode.SharpZipLib.Tar;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using Mono.Unix.Native;
|
||||
using MoonCore.Unix.SecureFs;
|
||||
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Servers;
|
||||
using MoonlightServers.DaemonShared.Enums;
|
||||
|
||||
namespace MoonlightServers.Daemon.Helpers;
|
||||
|
||||
public class ServerFileSystem
|
||||
{
|
||||
private readonly SecureFileSystem FileSystem;
|
||||
|
||||
public ServerFileSystem(SecureFileSystem fileSystem)
|
||||
{
|
||||
FileSystem = fileSystem;
|
||||
}
|
||||
|
||||
public Task<ServerFileSystemResponse[]> ListAsync(string inputPath)
|
||||
{
|
||||
var path = Normalize(inputPath);
|
||||
var entries = FileSystem.ReadDir(path);
|
||||
|
||||
IEnumerable<SecureFsEntry> entryQuery = entries;
|
||||
|
||||
// Filter all lost+found directories on the root of the file system
|
||||
// to hide the folder shown by virtual disk volumes
|
||||
if (string.IsNullOrEmpty(inputPath) || inputPath == "/")
|
||||
entryQuery = entryQuery.Where(x => x.Name != "lost+found");
|
||||
|
||||
var result = entryQuery
|
||||
.Select(x => new ServerFileSystemResponse()
|
||||
{
|
||||
Name = x.Name,
|
||||
IsFolder = x.IsDirectory,
|
||||
Size = x.Size,
|
||||
UpdatedAt = x.LastChanged,
|
||||
CreatedAt = x.CreatedAt
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
public Task MoveAsync(string inputOldPath, string inputNewPath)
|
||||
{
|
||||
var oldPath = Normalize(inputOldPath);
|
||||
var newPath = Normalize(inputNewPath);
|
||||
|
||||
FileSystem.Rename(oldPath, newPath);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task DeleteAsync(string inputPath)
|
||||
{
|
||||
var path = Normalize(inputPath);
|
||||
|
||||
FileSystem.RemoveAll(path);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task MkdirAsync(string inputPath)
|
||||
{
|
||||
var path = Normalize(inputPath);
|
||||
|
||||
FileSystem.MkdirAll(path, FilePermissions.ACCESSPERMS);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task TouchAsync(string inputPath)
|
||||
{
|
||||
var path = Normalize(inputPath);
|
||||
|
||||
var parentDirectory = Path.GetDirectoryName(path);
|
||||
|
||||
if (!string.IsNullOrEmpty(parentDirectory) && parentDirectory != "/")
|
||||
FileSystem.MkdirAll(parentDirectory, FilePermissions.ACCESSPERMS);
|
||||
|
||||
FileSystem.OpenFileWrite(
|
||||
path,
|
||||
_ => { },
|
||||
OpenFlags.O_CREAT
|
||||
); // We use these custom flags to ensure we aren't overwriting the file
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task CreateChunkAsync(string inputPath, long totalSize, long positionToSkip, Stream chunkStream)
|
||||
{
|
||||
var path = Normalize(inputPath);
|
||||
|
||||
var parentDirectory = Path.GetDirectoryName(path);
|
||||
|
||||
if (!string.IsNullOrEmpty(parentDirectory) && parentDirectory != "/")
|
||||
FileSystem.MkdirAll(parentDirectory, FilePermissions.ACCESSPERMS);
|
||||
|
||||
FileSystem.OpenFileWrite(path, fileStream =>
|
||||
{
|
||||
if (fileStream.Length != totalSize)
|
||||
fileStream.SetLength(totalSize);
|
||||
|
||||
fileStream.Position = positionToSkip;
|
||||
|
||||
chunkStream.CopyTo(fileStream);
|
||||
fileStream.Flush();
|
||||
}, OpenFlags.O_CREAT | OpenFlags.O_RDWR); // We use these custom flags to ensure we aren't overwriting the file
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task CreateAsync(string inputPath, Stream dataStream)
|
||||
{
|
||||
var path = Normalize(inputPath);
|
||||
|
||||
var parentDirectory = Path.GetDirectoryName(path);
|
||||
|
||||
if (!string.IsNullOrEmpty(parentDirectory) && parentDirectory != "/")
|
||||
FileSystem.MkdirAll(parentDirectory, FilePermissions.ACCESSPERMS);
|
||||
|
||||
FileSystem.OpenFileWrite(path, stream =>
|
||||
{
|
||||
stream.Position = 0;
|
||||
dataStream.CopyTo(stream);
|
||||
|
||||
stream.Flush();
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task ReadAsync(string inputPath, Func<Stream, Task> onHandle)
|
||||
{
|
||||
var path = Normalize(inputPath);
|
||||
|
||||
FileSystem.OpenFileRead(path, stream =>
|
||||
{
|
||||
// No try catch here because the safe fs abstraction already handles every error occuring in the handle
|
||||
onHandle.Invoke(stream).Wait();
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#region Compression
|
||||
|
||||
public Task CompressAsync(string[] itemsInput, string destinationInput, CompressType type)
|
||||
{
|
||||
var destination = Normalize(destinationInput);
|
||||
var items = itemsInput.Select(Normalize);
|
||||
|
||||
if (type == CompressType.Zip)
|
||||
{
|
||||
FileSystem.OpenFileWrite(destination, stream =>
|
||||
{
|
||||
using var zipStream = new ZipOutputStream(stream);
|
||||
|
||||
foreach (var item in items)
|
||||
AddItemToZip(item, zipStream);
|
||||
|
||||
zipStream.Flush();
|
||||
stream.Flush();
|
||||
|
||||
zipStream.Close();
|
||||
});
|
||||
}
|
||||
else if (type == CompressType.TarGz)
|
||||
{
|
||||
FileSystem.OpenFileWrite(destination, stream =>
|
||||
{
|
||||
using var gzStream = new GZipOutputStream(stream);
|
||||
using var tarStream = new TarOutputStream(gzStream, Encoding.UTF8);
|
||||
|
||||
foreach (var item in items)
|
||||
AddItemToTar(item, tarStream);
|
||||
|
||||
tarStream.Flush();
|
||||
gzStream.Flush();
|
||||
stream.Flush();
|
||||
|
||||
tarStream.Close();
|
||||
gzStream.Close();
|
||||
});
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task DecompressAsync(string pathInput, string destinationInput, CompressType type)
|
||||
{
|
||||
var path = Normalize(pathInput);
|
||||
var destination = Normalize(destinationInput);
|
||||
|
||||
if (type == CompressType.Zip)
|
||||
{
|
||||
FileSystem.OpenFileRead(path, fileStream =>
|
||||
{
|
||||
var zipInputStream = new ZipInputStream(fileStream);
|
||||
|
||||
ExtractZip(zipInputStream, destination);
|
||||
});
|
||||
}
|
||||
else if (type == CompressType.TarGz)
|
||||
{
|
||||
FileSystem.OpenFileRead(path, fileStream =>
|
||||
{
|
||||
var gzInputStream = new GZipInputStream(fileStream);
|
||||
var zipInputStream = new TarInputStream(gzInputStream, Encoding.UTF8);
|
||||
|
||||
ExtractTar(zipInputStream, destination);
|
||||
});
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void AddItemToZip(string path, ZipOutputStream outputStream)
|
||||
{
|
||||
var item = FileSystem.Stat(path);
|
||||
|
||||
if (item.IsDirectory)
|
||||
{
|
||||
var contents = FileSystem.ReadDir(path);
|
||||
|
||||
foreach (var content in contents)
|
||||
{
|
||||
AddItemToZip(
|
||||
Path.Combine(path, content.Name),
|
||||
outputStream
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var entry = new ZipEntry(path)
|
||||
{
|
||||
Size = item.Size,
|
||||
DateTime = item.LastChanged
|
||||
};
|
||||
|
||||
outputStream.PutNextEntry(entry);
|
||||
|
||||
FileSystem.OpenFileRead(path, stream => { stream.CopyTo(outputStream); });
|
||||
|
||||
outputStream.CloseEntry();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddItemToTar(string path, TarOutputStream outputStream)
|
||||
{
|
||||
var item = FileSystem.Stat(path);
|
||||
|
||||
if (item.IsDirectory)
|
||||
{
|
||||
var contents = FileSystem.ReadDir(path);
|
||||
|
||||
foreach (var content in contents)
|
||||
{
|
||||
AddItemToTar(
|
||||
Path.Combine(path, content.Name),
|
||||
outputStream
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var entry = TarEntry.CreateTarEntry(path);
|
||||
|
||||
entry.Name = path;
|
||||
entry.Size = item.Size;
|
||||
entry.ModTime = item.LastChanged;
|
||||
|
||||
outputStream.PutNextEntry(entry);
|
||||
|
||||
FileSystem.OpenFileRead(path, stream => { stream.CopyTo(outputStream); });
|
||||
|
||||
outputStream.CloseEntry();
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractZip(ZipInputStream inputStream, string destination)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var entry = inputStream.GetNextEntry();
|
||||
|
||||
if (entry == null)
|
||||
break;
|
||||
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
var fileDestination = Path.Combine(destination, entry.Name);
|
||||
|
||||
var parentDirectory = Path.GetDirectoryName(fileDestination);
|
||||
|
||||
if (!string.IsNullOrEmpty(parentDirectory) && parentDirectory != "/")
|
||||
FileSystem.MkdirAll(parentDirectory, FilePermissions.ACCESSPERMS);
|
||||
|
||||
FileSystem.OpenFileWrite(fileDestination, stream =>
|
||||
{
|
||||
stream.Position = 0;
|
||||
|
||||
inputStream.CopyTo(stream);
|
||||
|
||||
stream.Flush();
|
||||
}); // This will override the file if it exists
|
||||
}
|
||||
}
|
||||
|
||||
private void ExtractTar(TarInputStream inputStream, string destination)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var entry = inputStream.GetNextEntry();
|
||||
|
||||
if (entry == null)
|
||||
break;
|
||||
|
||||
if (entry.IsDirectory)
|
||||
continue;
|
||||
|
||||
var fileDestination = Path.Combine(destination, entry.Name);
|
||||
|
||||
var parentDirectory = Path.GetDirectoryName(fileDestination);
|
||||
|
||||
if (!string.IsNullOrEmpty(parentDirectory) && parentDirectory != "/")
|
||||
FileSystem.MkdirAll(parentDirectory, FilePermissions.ACCESSPERMS);
|
||||
|
||||
FileSystem.OpenFileWrite(fileDestination, stream =>
|
||||
{
|
||||
stream.Position = 0;
|
||||
|
||||
inputStream.CopyTo(stream);
|
||||
|
||||
stream.Flush();
|
||||
}); // This will override the file if it exists
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string Normalize(string path)
|
||||
{
|
||||
return path
|
||||
.Replace("//", "/")
|
||||
.Replace("..", "")
|
||||
.TrimStart('/');
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
|
||||
namespace MoonlightServers.Daemon.Helpers;
|
||||
|
||||
public class TokenAuthOptions : AuthenticationSchemeOptions
|
||||
{
|
||||
public string Token { get; set; }
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace MoonlightServers.Daemon.Helpers;
|
||||
|
||||
public class TokenAuthScheme : AuthenticationHandler<TokenAuthOptions>
|
||||
{
|
||||
public TokenAuthScheme(IOptionsMonitor<TokenAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder,
|
||||
ISystemClock clock) : base(options, logger, encoder, clock)
|
||||
{
|
||||
}
|
||||
|
||||
public TokenAuthScheme(IOptionsMonitor<TokenAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder) : base(
|
||||
options, logger, encoder)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
if (!Request.Headers.ContainsKey("Authorization"))
|
||||
return Task.FromResult(AuthenticateResult.NoResult());
|
||||
|
||||
var authHeaderValue = Request.Headers["Authorization"].FirstOrDefault();
|
||||
|
||||
if (string.IsNullOrEmpty(authHeaderValue))
|
||||
return Task.FromResult(AuthenticateResult.NoResult());
|
||||
|
||||
if (!authHeaderValue.Contains("Bearer "))
|
||||
return Task.FromResult(AuthenticateResult.NoResult());
|
||||
|
||||
var providedToken = authHeaderValue
|
||||
.Replace("Bearer ", "")
|
||||
.Trim();
|
||||
|
||||
if (providedToken != Options.Token)
|
||||
return Task.FromResult(AuthenticateResult.NoResult());
|
||||
|
||||
return Task.FromResult(AuthenticateResult.Success(
|
||||
new AuthenticationTicket(
|
||||
new ClaimsPrincipal(
|
||||
new ClaimsIdentity("token")
|
||||
),
|
||||
"token"
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
using System.Net.Sockets;
|
||||
using System.Text.Json;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Helpers;
|
||||
using MoonlightServers.Daemon.Configuration;
|
||||
using MoonlightServers.Daemon.Models.UnsafeDocker;
|
||||
|
||||
namespace MoonlightServers.Daemon.Helpers;
|
||||
|
||||
[Singleton]
|
||||
public class UnsafeDockerClient
|
||||
{
|
||||
private readonly AppConfiguration Configuration;
|
||||
|
||||
public UnsafeDockerClient(AppConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public Task<HttpClient> CreateHttpClientAsync()
|
||||
{
|
||||
var client = new HttpClient(new SocketsHttpHandler()
|
||||
{
|
||||
ConnectCallback = async (context, token) =>
|
||||
{
|
||||
var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP);
|
||||
var endpoint = new UnixDomainSocketEndPoint(
|
||||
Formatter.ReplaceStart(Configuration.Docker.Uri, "unix://", "")
|
||||
);
|
||||
await socket.ConnectAsync(endpoint, token);
|
||||
return new NetworkStream(socket, ownsSocket: true);
|
||||
}
|
||||
});
|
||||
|
||||
return Task.FromResult(client);
|
||||
}
|
||||
|
||||
public async Task<DataUsageResponse> GetDataUsageAsync()
|
||||
{
|
||||
using var client = await CreateHttpClientAsync();
|
||||
var responseJson = await client.GetStringAsync("http://some.random.domain/v1.47/system/df");
|
||||
var response = JsonSerializer.Deserialize<DataUsageResponse>(responseJson)!;
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user