diff --git a/src/Attributes/CommandLineArgumentAttribute.cs b/src/Attributes/CommandLineArgumentAttribute.cs new file mode 100644 index 0000000..4cbfa04 --- /dev/null +++ b/src/Attributes/CommandLineArgumentAttribute.cs @@ -0,0 +1,26 @@ +namespace iFileProxy.Attributes +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] + public class CommandLineArgumentAttribute : Attribute + { + /// + /// 长参数名 如: --dev-logger + /// + public string FullArgumentName { get; set; } = string.Empty; + + /// + /// 短参数名 如: -d + /// + public string ShortArgumentName { get; set; } = new string(' ',4); + + /// + /// 参数帮助文本 + /// + public string Description { get; set; } = string.Empty; + + /// + /// 是否需要参数值 + /// + public bool NeedValue { get; set; } = false; + } +} diff --git a/src/Handlers/CmdArgsHandler.cs b/src/Handlers/CmdArgsHandler.cs deleted file mode 100644 index d38270b..0000000 --- a/src/Handlers/CmdArgsHandler.cs +++ /dev/null @@ -1,36 +0,0 @@ -using iFileProxy.Config; -using iFileProxy.Helpers; -using iFileProxy.Services; -namespace iFileProxy.Handlers -{ - public class CmdArgsHandler - { - public static void ParseArgs(string[] args, IServiceProvider serviceProvider) - { - if (args.Length == 0) - return; - Dictionary helpStrings = []; - helpStrings.Add("--init-db", "初始化数据库"); - helpStrings.Add("--dev-logger", "启用开发者logger"); - helpStrings.Add("--disable-startup-check", "禁用启动时程序配置自检"); - helpStrings.Add("--url=", "设置程序app.run() Url 如: http://0.0.0.0:1145"); - - if (args.Contains("-h") || args.Contains("--help")) - { - Console.WriteLine("命令行参数帮助: "); - foreach (var hs in helpStrings) - { - Console.WriteLine($"\t{hs.Key}\t{hs.Value}"); - } - Environment.Exit(0); - } - - CommandLineArgsHelper cmdlineHelper = new(args); - - if (cmdlineHelper.GetBooleanValue("--init-db")) - { - serviceProvider.GetRequiredService().TryInitialDB(); - } - } - } -} diff --git a/src/Handlers/CommandLine/CustomBindUrl.cs b/src/Handlers/CommandLine/CustomBindUrl.cs new file mode 100644 index 0000000..15f6850 --- /dev/null +++ b/src/Handlers/CommandLine/CustomBindUrl.cs @@ -0,0 +1,15 @@ +using iFileProxy.Attributes; +using iFileProxy.Interfaces; + +namespace iFileProxy.Handlers.CommandLine +{ + [CommandLineArgument(FullArgumentName = "--bind-url", ShortArgumentName = "-B", Description = "自定义绑定URL", NeedValue = true)] + public class CustomBindUrl(string[] args) : ICommandLineHandler + { + public void Process() + { + Console.WriteLine("当前正使用自定义URL绑定"); + // 具体的实现在Program.cs中 此处仅作提示 + } + } +} diff --git a/src/Handlers/CommandLine/DisablePreStartupCheck.cs b/src/Handlers/CommandLine/DisablePreStartupCheck.cs new file mode 100644 index 0000000..f8ab29b --- /dev/null +++ b/src/Handlers/CommandLine/DisablePreStartupCheck.cs @@ -0,0 +1,15 @@ +using iFileProxy.Attributes; +using iFileProxy.Interfaces; + +namespace iFileProxy.Handlers.CommandLine +{ + [CommandLineArgument(FullArgumentName = "--disable-startup-check", ShortArgumentName = "-D", Description = "禁用配置文件启动前校验")] + public class DisablePreStartupCheck(string[] args) : ICommandLineHandler + { + public void Process() + { + Console.WriteLine("已禁用启动前配置文件校验"); + // 具体的实现在Program.cs中 此处仅作提示 + } + } +} diff --git a/src/Handlers/CommandLine/EnableDevLogger.cs b/src/Handlers/CommandLine/EnableDevLogger.cs new file mode 100644 index 0000000..3208f60 --- /dev/null +++ b/src/Handlers/CommandLine/EnableDevLogger.cs @@ -0,0 +1,15 @@ +using iFileProxy.Attributes; +using iFileProxy.Interfaces; + +namespace iFileProxy.Handlers.CommandLine +{ + [CommandLineArgument(FullArgumentName = "--dev-logging", ShortArgumentName = "-d", Description = "启用dev级别日志记录")] + public class EnableDevLogger(string[] args) : ICommandLineHandler + { + public void Process() + { + Console.WriteLine("developer logging is enabled."); + // 具体的实现在SerilogConfig.cs中 此处仅作提示 + } + } +} diff --git a/src/Handlers/CommandLine/HelpHandler.cs b/src/Handlers/CommandLine/HelpHandler.cs new file mode 100644 index 0000000..fcb78e1 --- /dev/null +++ b/src/Handlers/CommandLine/HelpHandler.cs @@ -0,0 +1,39 @@ +using iFileProxy.Attributes; +using iFileProxy.Helpers; +using iFileProxy.Interfaces; +using iFileProxy.Services; +using System.Reflection; + +namespace iFileProxy.Handlers.CommandLine +{ + [CommandLineArgument(FullArgumentName ="--help",ShortArgumentName = "-h", Description = "显示帮助文本")] + public class HelpHandler : ICommandLineHandler + { + string[] _args = []; + public HelpHandler(string[] args) { + _args = args; + } + + public void Process() + { + Dictionary helpText = CommandLineArgumentDispatcher.GetHelpTextDict(); + // 找到最长的键长度 + int maxKeyLength = helpText.Keys.Max(key => key.Length); + + // 打印表头 + Console.WriteLine("参数(短参数, 长参数)\t解释"); + + // 打印每一行的帮助信息,确保对齐 + foreach (var ht in helpText) + { + Console.WriteLine($" {ht.Key}{new string(' ', maxKeyLength - ht.Key.Length)}\t{ht.Value}"); + } + Console.WriteLine(new string('\n',2)); + + Console.WriteLine($"程序编译日期(UTC+8): {MasterHelper.GetBuildTime(Assembly.GetExecutingAssembly())}"); + + Console.WriteLine(); + Environment.Exit(0 ); + } + } +} diff --git a/src/Handlers/CommandLine/InitDBHandler.cs b/src/Handlers/CommandLine/InitDBHandler.cs new file mode 100644 index 0000000..7f9497c --- /dev/null +++ b/src/Handlers/CommandLine/InitDBHandler.cs @@ -0,0 +1,21 @@ +using iFileProxy.Attributes; +using iFileProxy.Interfaces; +using iFileProxy.Services; + +namespace iFileProxy.Handlers.CommandLine +{ + [CommandLineArgument(FullArgumentName = "--init-db", ShortArgumentName = "-i", Description = "初始化数据库表结构")] + public class InitDBHandler : ICommandLineHandler + { + string[] _cmdArgs = []; + public InitDBHandler(string[] args) { + _cmdArgs = args; + } + public void Process() + { + DatabaseGateService gateService = new(new AppConfigService()); + gateService.TryInitialDB(); + Environment.Exit(0); + } + } +} diff --git a/src/Handlers/CommandLineArgumentDispatcher.cs b/src/Handlers/CommandLineArgumentDispatcher.cs new file mode 100644 index 0000000..cbc0256 --- /dev/null +++ b/src/Handlers/CommandLineArgumentDispatcher.cs @@ -0,0 +1,90 @@ +using iFileProxy.Attributes; +using iFileProxy.Config; +using iFileProxy.Helpers; +using iFileProxy.Interfaces; +using Serilog; +using System.Reflection; + +namespace iFileProxy.Handlers +{ + /// + /// 命令行参数调度器 + /// + public class CommandLineArgumentDispatcher + { + private readonly static Serilog.ILogger _logger = Log.Logger.ForContext(); + + // 获取当前执行的程序集 + static Assembly assembly = Assembly.GetExecutingAssembly(); + + // 获取程序集中所有的类型 + static Type[] types = []; + + public CommandLineArgumentDispatcher() + { + LoadAssemblyData(); + } + + static void LoadAssemblyData() + { + assembly = Assembly.GetExecutingAssembly(); + types = assembly.GetTypes(); + } + + + public static void Parse(string[] args) + { + LoadAssemblyData(); + // 遍历所有类型,查找带有 CommandLineArgumentAttribute 的类 + foreach (Type type in types) + { + // 检查类型是否具有 CommandLineArgumentAttribute + var attribute = type.GetCustomAttribute(); + if (attribute != null && (args.Contains(attribute.FullArgumentName) || args.Contains(attribute.ShortArgumentName))) + { + var commandLineHandler = (ICommandLineHandler?)Activator.CreateInstance(type, [args]); + commandLineHandler?.Process(); + } + } + } + + public static Dictionary GetHelpTextDict() + { + static string GetHelpText(CommandLineArgumentAttribute attribute) + { + if (attribute.NeedValue && attribute.FullArgumentName.Trim() == string.Empty) // 需要值且没有长参数 + { + return $"{attribute.ShortArgumentName}="; + } + else if (attribute.NeedValue && attribute.ShortArgumentName.Trim() == string.Empty)// 需要值且没有短参数 + { + return $"{attribute.ShortArgumentName} {attribute.FullArgumentName}="; + } + else if (attribute.NeedValue && attribute.FullArgumentName.Trim() != "" && attribute.ShortArgumentName.Trim() != "") // 都需要 + { + return $"{attribute.ShortArgumentName}=,{attribute.FullArgumentName}="; + } + else if (attribute.FullArgumentName.Trim() == "" && attribute.ShortArgumentName.Trim() == "") // 都没有 + { + throw new ArgumentException("该参数长短参数名均为空 请联系软件开发者反馈"); + } + else if (!attribute.NeedValue && attribute.FullArgumentName.Trim() == string.Empty) + return attribute.ShortArgumentName ; + else if (!attribute.NeedValue && attribute.ShortArgumentName.Trim() == string.Empty) + return $"{attribute.ShortArgumentName} {attribute.FullArgumentName}"; + else + return $"{attribute.ShortArgumentName},{attribute.FullArgumentName}"; + } + Dictionary r = []; + foreach (var handler in types) + { + var h = handler.GetCustomAttribute(); + if (h != null) + { + r.Add(GetHelpText(h), h.Description); + } + } + return r; + } + } +} diff --git a/src/Helpers/CommandLineArgsHelper.cs b/src/Helpers/CommandLineArgsHelper.cs index 30efed3..19a378e 100644 --- a/src/Helpers/CommandLineArgsHelper.cs +++ b/src/Helpers/CommandLineArgsHelper.cs @@ -23,13 +23,13 @@ namespace iFileProxy.Helpers var parts = arg.Split(['='], 2); if (parts.Length == 2) { - parameters[parts[0].TrimStart('-')] = parts[1]; + parameters[parts[0]] = parts[1]; } } else { // 如果参数是没有值的标志性参数(比如 --dev-logger) - parameters[arg.TrimStart('-')] = "true"; + parameters[arg] = "true"; } } @@ -39,12 +39,34 @@ namespace iFileProxy.Helpers // 获取指定参数的值,若无值则返回null public string? GetStringValue(string key) { - return _parameters.ContainsKey(key) ? _parameters[key] : null; + // 如果 key 包含多个等效键(用 | 分隔),则拆分为数组 + string[] keys = key.Contains("|") + ? key.Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + : [key]; + + foreach (var k in keys) + { + if (_parameters.TryGetValue(k, out string? value)) + { + return value; + } + } + + // 如果没有任何键匹配,默认返回 null + return null; } // 获取指定参数的布尔值,若参数值是 "true" 则返回true,否则返回false public bool GetBooleanValue(string key) { + string[] args = []; + if (key.Contains("|")) + args = key.Split('|',StringSplitOptions.RemoveEmptyEntries|StringSplitOptions.TrimEntries); + foreach (var arg in args) + { + if (_parameters.ContainsKey(arg) && _parameters[arg].Equals("true", StringComparison.CurrentCultureIgnoreCase)) + return true; + } return _parameters.ContainsKey(key) && _parameters[key].Equals("true", StringComparison.CurrentCultureIgnoreCase); } } diff --git a/src/Helpers/MasterHelper.cs b/src/Helpers/MasterHelper.cs index 10dd71d..2536ffd 100644 --- a/src/Helpers/MasterHelper.cs +++ b/src/Helpers/MasterHelper.cs @@ -1,7 +1,9 @@ using iFileProxy.Config; using iFileProxy.Models; using Serilog; +using System.Globalization; using System.Net; +using System.Reflection; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; @@ -228,5 +230,25 @@ namespace iFileProxy.Helpers return exp1.IsMatch(url) || exp2.IsMatch(url) || exp3.IsMatch(url) || exp4.IsMatch(url) || exp5.IsMatch(url); } + // copy from https://summerlight.com/archives/184 + public static DateTime GetBuildTime(Assembly assembly) + { + const string buildVersionMetadataPrefix = "+build"; + + var attribute = assembly.GetCustomAttribute(); + if (attribute?.InformationalVersion != null) + { + var value = attribute.InformationalVersion; + var index = value.IndexOf(buildVersionMetadataPrefix, StringComparison.Ordinal); + if (index > 0) + { + value = value[(index + buildVersionMetadataPrefix.Length)..]; + if (DateTime.TryParseExact(value, "yyyyMMddHHmmssZ", CultureInfo.InvariantCulture, DateTimeStyles.None, out var result)) + return result; + } + } + return default; + } + } } diff --git a/src/Interfaces/ICommandLineHandler.cs b/src/Interfaces/ICommandLineHandler.cs new file mode 100644 index 0000000..fe60139 --- /dev/null +++ b/src/Interfaces/ICommandLineHandler.cs @@ -0,0 +1,9 @@ +using iFileProxy.Services; + +namespace iFileProxy.Interfaces +{ + public interface ICommandLineHandler + { + public void Process(); + } +} diff --git a/src/Program.cs b/src/Program.cs index e49fe04..eb0061f 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -8,6 +8,7 @@ using Serilog; using System.Text; using MySql.Data.MySqlClient; using iFileProxy.Middlewares; +using System.Reflection; namespace iFileProxy { @@ -23,9 +24,24 @@ namespace iFileProxy CommandLineArgsHelper argsHelper = new(args); + // 解析命令行参数 + CommandLineArgumentDispatcher.Parse(args); + + if (argsHelper.GetBooleanValue("--version") || argsHelper.GetBooleanValue("-v")) + { + var buildTime = MasterHelper.GetBuildTime(Assembly.GetEntryAssembly()); + StringBuilder sb = new StringBuilder(); + sb.AppendLine(new string('=', 10)); + sb.AppendLine($"Build DateTime:\t{buildTime}"); + sb.AppendLine(new string('=', 10)); + + Console.WriteLine(sb.ToString()); + Environment.Exit(0); + } + Console.Write(" "); // 补全日志第一行开头的空白 - var builder = WebApplication.CreateBuilder(args); + var builder = WebApplication.CreateBuilder(); // CORS配置 builder.Services.AddCors(options => @@ -81,14 +97,11 @@ namespace iFileProxy // 初始化缓存管理服务 LocalCacheManager localCacheManager = new(app.Services); - // 解析命令行参数 - CmdArgsHandler.ParseArgs(args, app.Services); - // 开启配置热重载 app.Services.GetRequiredService().EnableHotReload(); - if (!argsHelper.GetBooleanValue("disable-startup-check")) + if (!argsHelper.GetBooleanValue("--disable-startup-check|-D")) // 初始化验证配置文件 AppConfig.CheckAppConfig(app.Services); @@ -141,8 +154,8 @@ namespace iFileProxy var dbGateService = app.Services.GetRequiredService(); SerilogConfig.CreateLogger(dbGateService, args); - if (!argsHelper.GetStringValue("url").IsNullOrEmpty()) - app.Run(argsHelper.GetStringValue("url")); + if (!argsHelper.GetStringValue("--bind-url|-B").IsNullOrEmpty()) + app.Run(argsHelper.GetStringValue("--bind-url|-B")); else app.Run(); } diff --git a/src/Properties/PublishProfiles/FolderProfile.pubxml b/src/Properties/PublishProfiles/FolderProfile.pubxml index 1296e0c..8285a16 100644 --- a/src/Properties/PublishProfiles/FolderProfile.pubxml +++ b/src/Properties/PublishProfiles/FolderProfile.pubxml @@ -15,7 +15,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. <_TargetId>Folder net8.0 - linux-x64 + win-x64 e343bd8a-27ed-47e2-b50d-e3000730e65e false true diff --git a/src/SerilogConfig.cs b/src/SerilogConfig.cs index f0495e8..a6ec49a 100644 --- a/src/SerilogConfig.cs +++ b/src/SerilogConfig.cs @@ -26,7 +26,7 @@ var loggerConfiguration = new LoggerConfiguration(); - if (args != null && new CommandLineArgsHelper(args).GetBooleanValue("dev-logger")) + if (args != null && new CommandLineArgsHelper(args).GetBooleanValue("--dev-logging|-d")) { loggerConfiguration.MinimumLevel.Debug(); } diff --git a/src/iFileProxy.csproj b/src/iFileProxy.csproj index 9969e7d..4982a72 100644 --- a/src/iFileProxy.csproj +++ b/src/iFileProxy.csproj @@ -1,22 +1,26 @@ - - net8.0 - enable - enable - true - + + net8.0 + enable + enable + true + - - - - - - - - - - - + + build$([System.DateTime]::UtcNow.ToString("yyyyMMddHHmmssZ")) + + + + + + + + + + + + +