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