Owner decision pending (STATE.md DC-100 tick): adopt/ship/park. Preserved from fragile git stash to named branch 2026-08-23. NOTE: requires a Windows build machine (WinUI XAML compiler + MSIX do not cross-build on Linux) — see WINDOWS_APP_BUILD.md in this tree. Secret-scanned clean 2026-08-23 (no keys/tokens/PEM in tree).
284 lines
9.1 KiB
C#
284 lines
9.1 KiB
C#
using System.Text.Json;
|
|
using System.Text.Json.Nodes;
|
|
using DashCaddy.Desktop.Models;
|
|
|
|
namespace DashCaddy.Desktop.Services;
|
|
|
|
public static class ComposeParser
|
|
{
|
|
public static List<ServiceModel> Parse(string yamlContent)
|
|
{
|
|
var services = new List<ServiceModel>();
|
|
|
|
// Simple YAML parsing - in production use YamlDotNet
|
|
// For now, we'll do a basic JSON-based approach assuming compose is converted
|
|
|
|
try
|
|
{
|
|
var json = JsonSerializer.Deserialize<JsonNode>(yamlContent); // This won't work for YAML directly
|
|
// Real implementation would use YamlDotNet
|
|
}
|
|
catch
|
|
{
|
|
// Fallback
|
|
}
|
|
|
|
return services;
|
|
}
|
|
|
|
public static List<ServiceModel> ParseJson(string jsonContent)
|
|
{
|
|
var compose = JsonSerializer.Deserialize<ComposeFile>(jsonContent, new JsonSerializerOptions
|
|
{
|
|
PropertyNameCaseInsensitive = true
|
|
});
|
|
|
|
var services = new List<ServiceModel>();
|
|
|
|
if (compose?.Services != null)
|
|
{
|
|
foreach (var (name, svc) in compose.Services)
|
|
{
|
|
var port = ExtractPort(svc);
|
|
var env = svc.Environment ?? new Dictionary<string, string>();
|
|
|
|
services.Add(new ServiceModel
|
|
{
|
|
Id = Guid.NewGuid().ToString("N")[..8],
|
|
Name = name,
|
|
Type = "docker-compose",
|
|
Image = svc.Image ?? "",
|
|
Host = "localhost",
|
|
Port = port,
|
|
Environment = env,
|
|
Volumes = svc.Volumes ?? new List<string>(),
|
|
Labels = svc.Labels ?? new Dictionary<string, string>(),
|
|
ComposeFile = jsonContent
|
|
});
|
|
}
|
|
}
|
|
|
|
return services;
|
|
}
|
|
|
|
private static int ExtractPort(ComposeService svc)
|
|
{
|
|
if (svc.Ports != null)
|
|
{
|
|
foreach (var (key, value) in svc.Ports)
|
|
{
|
|
if (int.TryParse(key.Split(':')[0], out var hostPort))
|
|
return hostPort;
|
|
if (int.TryParse(key, out var containerPort))
|
|
return containerPort;
|
|
}
|
|
}
|
|
return 80;
|
|
}
|
|
}
|
|
|
|
public static class TemplateRegistry
|
|
{
|
|
private static readonly Dictionary<string, ServiceTemplate> Templates = new()
|
|
{
|
|
["plex"] = new ServiceTemplate
|
|
{
|
|
Id = "plex",
|
|
Name = "Plex Media Server",
|
|
Description = "Media server for movies, TV shows, and music",
|
|
Icon = "📺",
|
|
DefaultPort = 32400,
|
|
Image = "plexinc/pms-docker:latest",
|
|
Environment = new Dictionary<string, string>
|
|
{
|
|
["PLEX_CLAIM"] = "",
|
|
["TZ"] = "America/Los_Angeles"
|
|
},
|
|
Volumes = new List<string>
|
|
{
|
|
"E:/dockerdata/plex/config:/config",
|
|
"E:/dockerdata/plex/transcode:/transcode",
|
|
"E:/media:/data"
|
|
},
|
|
RequiredVariables = new[] { "PLEX_CLAIM" }
|
|
},
|
|
["homeassistant"] = new ServiceTemplate
|
|
{
|
|
Id = "homeassistant",
|
|
Name = "Home Assistant",
|
|
Description = "Open source home automation platform",
|
|
Icon = "🏠",
|
|
DefaultPort = 8123,
|
|
Image = "ghcr.io/home-assistant/home-assistant:stable",
|
|
Environment = new Dictionary<string, string>
|
|
{
|
|
["TZ"] = "America/Los_Angeles"
|
|
},
|
|
Volumes = new List<string>
|
|
{
|
|
"E:/dockerdata/homeassistant:/config"
|
|
},
|
|
NetworkMode = "host"
|
|
},
|
|
["jellyfin"] = new ServiceTemplate
|
|
{
|
|
Id = "jellyfin",
|
|
Name = "Jellyfin",
|
|
Description = "Free software media system",
|
|
Icon = "🎬",
|
|
DefaultPort = 8096,
|
|
Image = "jellyfin/jellyfin:latest",
|
|
Environment = new Dictionary<string, string>
|
|
{
|
|
["TZ"] = "America/Los_Angeles"
|
|
},
|
|
Volumes = new List<string>
|
|
{
|
|
"E:/dockerdata/jellyfin/config:/config",
|
|
"E:/dockerdata/jellyfin/cache:/cache",
|
|
"E:/media:/media"
|
|
}
|
|
},
|
|
["portainer"] = new ServiceTemplate
|
|
{
|
|
Id = "portainer",
|
|
Name = "Portainer",
|
|
Description = "Docker container management UI",
|
|
Icon = "🐳",
|
|
DefaultPort = 9443,
|
|
Image = "portainer/portainer-ce:latest",
|
|
Volumes = new List<string>
|
|
{
|
|
"E:/dockerdata/portainer:/data",
|
|
"//./pipe/docker_engine:/var/run/docker.sock"
|
|
}
|
|
},
|
|
["adguard"] = new ServiceTemplate
|
|
{
|
|
Id = "adguard",
|
|
Name = "AdGuard Home",
|
|
Description = "Network-wide ad blocking & DNS",
|
|
Icon = "🛡️",
|
|
DefaultPort = 3000,
|
|
Image = "adguard/adguardhome:latest",
|
|
Volumes = new List<string>
|
|
{
|
|
"E:/dockerdata/adguard/work:/opt/adguardhome/work",
|
|
"E:/dockerdata/adguard/conf:/opt/adguardhome/conf"
|
|
},
|
|
Ports = new Dictionary<int, int> { [53] = 53, [3000] = 3000 }
|
|
},
|
|
["uptime-kuma"] = new ServiceTemplate
|
|
{
|
|
Id = "uptime-kuma",
|
|
Name = "Uptime Kuma",
|
|
Description = "Self-hosted monitoring tool",
|
|
Icon = "📊",
|
|
DefaultPort = 3001,
|
|
Image = "louislam/uptime-kuma:1",
|
|
Volumes = new List<string>
|
|
{
|
|
"E:/dockerdata/uptime-kuma:/app/data"
|
|
}
|
|
},
|
|
["vaultwarden"] = new ServiceTemplate
|
|
{
|
|
Id = "vaultwarden",
|
|
Name = "Vaultwarden (Bitwarden)",
|
|
Description = "Self-hosted password manager",
|
|
Icon = "🔐",
|
|
DefaultPort = 8080,
|
|
Image = "vaultwarden/server:latest",
|
|
Environment = new Dictionary<string, string>
|
|
{
|
|
["SIGNUPS_ALLOWED"] = "true",
|
|
["INVITATIONS_ALLOWED"] = "true"
|
|
},
|
|
Volumes = new List<string>
|
|
{
|
|
"E:/dockerdata/vaultwarden:/data"
|
|
}
|
|
},
|
|
["paperless"] = new ServiceTemplate
|
|
{
|
|
Id = "paperless",
|
|
Name = "Paperless-ngx",
|
|
Description = "Document management system",
|
|
Icon = "📄",
|
|
DefaultPort = 8000,
|
|
Image = "ghcr.io/paperless-ngx/paperless-ngx:latest",
|
|
Environment = new Dictionary<string, string>
|
|
{
|
|
["PAPERLESS_REDIS"] = "redis://localhost:6379",
|
|
["PAPERLESS_DBHOST"] = "localhost",
|
|
["PAPERLESS_DBNAME"] = "paperless",
|
|
["PAPERLESS_DBUSER"] = "paperless",
|
|
["PAPERLESS_DBPASS"] = "paperless"
|
|
},
|
|
Volumes = new List<string>
|
|
{
|
|
"E:/dockerdata/paperless/data:/usr/src/paperless/data",
|
|
"E:/dockerdata/paperless/media:/usr/src/paperless/media",
|
|
"E:/dockerdata/paperless/export:/usr/src/paperless/export",
|
|
"E:/dockerdata/paperless/consume:/usr/src/paperless/consume"
|
|
}
|
|
}
|
|
};
|
|
|
|
public static ServiceTemplate Get(string id)
|
|
{
|
|
return Templates.TryGetValue(id, out var template) ? template : null;
|
|
}
|
|
|
|
public static IEnumerable<ServiceTemplate> GetAll()
|
|
{
|
|
return Templates.Values;
|
|
}
|
|
}
|
|
|
|
public class ServiceTemplate
|
|
{
|
|
public string Id { get; set; }
|
|
public string Name { get; set; }
|
|
public string Description { get; set; }
|
|
public string Icon { get; set; }
|
|
public int DefaultPort { get; set; }
|
|
public string Image { get; set; }
|
|
public Dictionary<string, string> Environment { get; set; } = new();
|
|
public List<string> Volumes { get; set; } = new();
|
|
public Dictionary<int, int> Ports { get; set; } = new();
|
|
public string NetworkMode { get; set; }
|
|
public string[] RequiredVariables { get; set; } = Array.Empty<string>();
|
|
|
|
public ServiceModel Instantiate(Dictionary<string, string> variables)
|
|
{
|
|
var env = new Dictionary<string, string>(Environment);
|
|
foreach (var (key, value) in variables)
|
|
{
|
|
env[key] = value;
|
|
}
|
|
|
|
var port = DefaultPort;
|
|
if (Ports.Count > 0)
|
|
{
|
|
port = Ports.Keys.First();
|
|
}
|
|
|
|
return new ServiceModel
|
|
{
|
|
Id = Guid.NewGuid().ToString("N")[..8],
|
|
Name = Name,
|
|
Type = "template",
|
|
Image = Image,
|
|
Host = "localhost",
|
|
Port = port,
|
|
Environment = env,
|
|
Volumes = Volumes,
|
|
Labels = new Dictionary<string, string>
|
|
{
|
|
["dashcaddy.template"] = Id,
|
|
["dashcaddy.managed"] = "true"
|
|
}
|
|
};
|
|
}
|
|
} |