Files
Servers/MoonlightServers.Api/Admin/Templates/PterodactylEggImportService.cs

255 lines
8.7 KiB
C#

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;
}
}