XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEToolBox

【WPF】XFE工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using XFEToolBox.Core.Downloads;
using XFEToolBox.Core.Models.Users;
using XFEToolBox.Server.Core.Chat;
using XFEToolBox.Server.Core.Options;
using XFEToolBox.Server.Core.Services;
using XFEToolBox.Server.Core.Utilities;
using XFEToolBox.Server.Profiles;
using XFEToolBox.Server.Profiles.Data;
using XFEToolBox.Server.Realtime;
using XFEToolBox.Server.Services;
using XFEToolBox.Server.Services.Chat;
using XFEExtension.NetCore.ServerInteractive.Interfaces;
using XFEExtension.NetCore.ServerInteractive.Utilities.Extensions;
using XFEExtension.NetCore.ServerInteractive.Utilities.Server;

EnsureInitialAdministrator();
EnsureInitialSoftwareCatalog();

AppDomain.CurrentDomain.ProcessExit += (_, _) => SaveProfiles();

SaveProfiles();

var validationOptions = new ToolPackageValidationOptions
{
    MaxPackageBytes = ServerProfile.MaxPackageBytes,
    MaxExpandedBytes = ServerProfile.MaxExpandedBytes,
    MaxFileCount = ServerProfile.MaxFileCount,
    MaxCompressionRatio = ServerProfile.MaxCompressionRatio
};
var configuredStorageRoot = ServerProfile.StorageRoot;
var storageRoot = Path.GetFullPath(Path.IsPathRooted(configuredStorageRoot)
    ? configuredStorageRoot
    : Path.Combine(AppContext.BaseDirectory, configuredStorageRoot));
var configuredSoftwareStorageRoot = ServerProfile.SoftwareStorageRoot;
var softwareStorageRoot = Path.GetFullPath(Path.IsPathRooted(configuredSoftwareStorageRoot)
    ? configuredSoftwareStorageRoot
    : Path.Combine(AppContext.BaseDirectory, configuredSoftwareStorageRoot));
Directory.CreateDirectory(softwareStorageRoot);
var configuredChatDatabasePath = ServerProfile.ChatDatabasePath;
var chatDatabasePath = Path.GetFullPath(Path.IsPathRooted(configuredChatDatabasePath)
    ? configuredChatDatabasePath
    : Path.Combine(AppContext.BaseDirectory, configuredChatDatabasePath));
var configuredChatAttachmentRoot = ServerProfile.ChatAttachmentStorageRoot;
var chatAttachmentRoot = Path.GetFullPath(Path.IsPathRooted(configuredChatAttachmentRoot)
    ? configuredChatAttachmentRoot
    : Path.Combine(AppContext.BaseDirectory, configuredChatAttachmentRoot));
var chatRepository = new ChatRepository(
    chatDatabasePath,
    chatAttachmentRoot,
    ServerProfile.MaxChatAttachmentBytes,
    ServerProfile.MaxChatAttachmentChunkBytes)
{
    UserExists = id => UserDataProfile.UserTable.Any(user => user.Id == id && user.Enable)
};
await chatRepository.InitializeAsync();
var realtimeTicketStore = new ChatRealtimeTicketStore(
    ticketLifetime: TimeSpan.FromSeconds(Math.Clamp(ServerProfile.RealtimeTicketLifetimeSeconds, 30, 60)));
var realtimeBroker = new ChatRealtimeBroker(chatRepository, realtimeTicketStore)
{
    SessionIsActive = claims =>
    {
        var now = DateTimeOffset.UtcNow;
        return !string.IsNullOrWhiteSpace(claims.SessionId) &&
               UserDataProfile.UserTable.Any(user => user.Enable && user.Id == claims.UserId) &&
               UserDataProfile.LoginTable.Any(login =>
                   login.SessionId == claims.SessionId &&
                   login.UserLoginModel.Uid == claims.UserId &&
                   login.RevokedAtUtc is null &&
                   login.ExpiresAtUtc > now);
    }
};
AppDomain.CurrentDomain.ProcessExit += (_, _) =>
{
    realtimeBroker.DisposeAsync().AsTask().GetAwaiter().GetResult();
    chatRepository.Dispose();
};
var packageRepository = new FileSystemToolPackageRepository(
    new ToolPackageValidator(validationOptions),
    validationOptions,
    new ToolPackageStorageOptions { StorageRoot = storageRoot });

var server = XFEServerBuilder.CreateBuilder()
    .UseXFEServer()
    .AddServerCore(XFEServerCoreBuilder.CreateBuilder()
        .AddParameter("ToolPackageRepository", packageRepository)
        .AddParameter("AdminApiKey", ServerProfile.AdminApiKey)
        .AddParameter("MaxPackageBytes", validationOptions.MaxPackageBytes)
        .AddParameter("SoftwareStorageRoot", softwareStorageRoot)
        .AddParameter("MaxSoftwareBytes", ServerProfile.MaxSoftwareBytes)
        .AddParameter("ChatRepository", chatRepository)
        .AddParameter("ChatRealtimeTicketStore", realtimeTicketStore)
        .AddParameter("ChatRealtimeBroker", realtimeBroker)
        .AddService<HealthService>()
        .AddService<SoftwareCatalogService>()
        .AddService<SoftwareAdminService>()
        .AddService<ToolCatalogService>()
        .AddService<ToolAdminService>()
        .AddService<RegistrationService>()
        .AddService<UserProfileService>()
        .AddService<ToolSubmissionService>()
        .AddService<AdminManagementService>()
        .AddService<ChatFriendService>()
        .AddService<ChatGroupService>()
        .AddService<ChatMessageService>()
        .AddService<ChatAttachmentService>()
        .AddService<ChatRealtimeTicketService>()
        .AddOriginalService<ChatRealtimeWebSocketService>()
        .UseXFEStandardServerCore<ToolBoxUserFaceInfo>(options =>
        {
            options.GetUserFunction = static () => UserDataProfile.UserTable;
            options.AddUserFunction = static userInfo =>
            {
                if (userInfo is ToolBoxUser user)
                    UserDataProfile.UserTable.Add(user);
            };
            options.GetEncryptedUserLoginModelFunction = static () => UserDataProfile.LoginTable;
            options.AddEncryptedUserLoginModelFunction = UserDataProfile.LoginTable.Add;
            options.RemoveEncryptedUserLoginModelFunction = UserDataProfile.LoginTable.Remove;
            options.GetLoginKeepDays = static () => ServerProfile.LoginKeepDays;
            options.UpdateUserFunction = static _ => UserDataProfile.SaveProfile();
            options.LoginResultConvertFunction = static user =>
                ToolBoxUserFaceInfo.FromUser((IUserInfo)user);
        })
        .Build(options =>
        {
            options.AcceptGet = true;
            options.AcceptPost = true;
            options.AcceptNonStandardJson = true;
            // 源站必须只允许可信 EdgeOne 节点回源,防止客户端绕过代理伪造请求头。
            options.GetIPFunction = static args => ForwardedClientIpResolver.Resolve(
                args.ClientIP,
                args.RequestHeaders["EO-Connecting-IP"],
                args.RequestHeaders["X-Forwarded-For"]);
            options.BindIP(ServerProfile.HttpAddress);
            options.MainEntryPoint = "api";
            options.ServerCoreName = "XFEToolBoxServer";
        }))
    .Build();

Console.WriteLine("XFEToolBox Server");
Console.WriteLine($"  地址:{ServerProfile.HttpAddress.TrimEnd('/')}/api");
Console.WriteLine($"  数据:{storageRoot}");
Console.WriteLine($"  软件:{softwareStorageRoot}");
Console.WriteLine($"  聊天:{chatDatabasePath}");
Console.WriteLine($"  附件:{chatAttachmentRoot}");
Console.WriteLine($"  用户:{UserDataProfile.UserTable.Count}");
await server.StartAsync();
await server.RunAsync();
return;

static void EnsureInitialAdministrator()
{
    if (UserDataProfile.UserTable.Any(user =>
            user.PermissionLevel >= (int)ToolBoxUserRole.Administrator)) return;

    var userName = ServerProfile.InitialAdminUserName;
    var password = ServerProfile.InitialAdminPassword;
    UserDataProfile.UserTable.Add(new ToolBoxUser
    {
        UserName = userName,
        Password = password,
        NickName = "工具箱管理员",
        Enable = true,
        Role = ToolBoxUserRole.Administrator
    });
    UserDataProfile.SaveProfile();
    Console.WriteLine($"[初始化] 已创建管理员账号:{userName}");
    if (password == ServerProfile.DefaultInitialAdminPassword)
        Console.WriteLine("[安全提示] 当前使用初始密码 ChangeMe_123!,请登录后立即修改。");
}

static void EnsureInitialSoftwareCatalog()
{
    if (MainDataProfile.SoftwareCatalog.Count > 0) return;

    MainDataProfile.SoftwareCatalog.Add(new SoftwareCatalogItem
    {
        Id = "steam",
        Name = "Steam",
        Summary = "游戏购买、下载、更新与社区服务平台。",
        Description = "Valve 推出的 PC 游戏平台,提供游戏商店、自动更新、云存档、创意工坊和社区功能。",
        Publisher = "Valve Corporation",
        Category = "游戏平台",
        Version = "最新版",
        DownloadUrl = "https://cdn.fastly.steamstatic.com/client/installer/SteamSetup.exe",
        WebsiteUrl = "https://store.steampowered.com/about/",
        FileName = "SteamSetup.exe",
        Tags = ["Steam", "游戏", "商店", "社区"],
        DownloadMode = SoftwareDownloadMode.Direct,
        Featured = true
    });
    MainDataProfile.SoftwareCatalog.Add(new SoftwareCatalogItem
    {
        Id = "watt-toolkit",
        Name = "Watt Toolkit",
        Summary = "开源、跨平台的多功能游戏工具箱。",
        Description = "Watt Toolkit(原 Steam++)提供网络加速、账号切换、库存游戏管理等功能。官方发行页会按系统提供当前版本。",
        Publisher = "BeyondDimension",
        Category = "游戏工具",
        Version = "最新版",
        DownloadUrl = "https://github.com/BeyondDimension/SteamTools/releases/latest",
        WebsiteUrl = "https://steampp.net/",
        Tags = ["Steam++", "Watt Toolkit", "网络加速", "开源"],
        DownloadMode = SoftwareDownloadMode.Browser,
        Featured = true
    });
    MainDataProfile.SoftwareCatalog.Add(new SoftwareCatalogItem
    {
        Id = "visual-studio",
        Name = "Visual Studio Community",
        Summary = "面向 .NET、C++ 与桌面开发的完整 IDE。",
        Description = "Microsoft Visual Studio Community 提供代码编辑、调试、测试、设计器和可扩展开发工具。官方页面会下发当前稳定版引导程序。",
        Publisher = "Microsoft",
        Category = "开发工具",
        Version = "最新版",
        DownloadUrl = "https://visualstudio.microsoft.com/downloads/",
        WebsiteUrl = "https://visualstudio.microsoft.com/",
        Tags = ["Visual Studio", ".NET", "C#", "C++", "IDE"],
        DownloadMode = SoftwareDownloadMode.Browser,
        Featured = true
    });
    MainDataProfile.SoftwareCatalog.Add(new SoftwareCatalogItem
    {
        Id = "cheat-engine",
        Name = "Cheat Engine",
        Summary = "用于学习和研究的开源内存扫描与调试工具。",
        Description = "Cheat Engine 提供内存扫描、调试和逆向研究功能。下载和使用前请确认遵守目标软件的许可协议与当地法律。",
        Publisher = "Cheat Engine Project",
        Category = "调试工具",
        Version = "最新版",
        DownloadUrl = "https://www.cheatengine.org/download.php",
        WebsiteUrl = "https://www.cheatengine.org/",
        Notice = "仅用于合法的软件调试、学习和研究;请勿违反第三方软件的 EULA 或服务条款。",
        Tags = ["内存", "调试", "逆向", "开源"],
        DownloadMode = SoftwareDownloadMode.Browser
    });
    MainDataProfile.SaveProfile();
    Console.WriteLine("[初始化] 已创建默认软件下载目录。可通过 MainDataProfile 配置维护。");
}

static void SaveProfiles()
{
    ServerProfile.SaveProfile();
    MainDataProfile.SaveProfile();
    UserDataProfile.SaveProfile();
}