diff --git a/CodePlay.Core/CodePlay.Core.csproj b/CodePlay.Core/CodePlay.Core.csproj index a8d9fff..c169880 100644 --- a/CodePlay.Core/CodePlay.Core.csproj +++ b/CodePlay.Core/CodePlay.Core.csproj @@ -19,7 +19,9 @@ + + diff --git a/CodePlay.Core/Parsers/CppParser.cs b/CodePlay.Core/Parsers/CppParser.cs index 5847157..0b812a9 100644 --- a/CodePlay.Core/Parsers/CppParser.cs +++ b/CodePlay.Core/Parsers/CppParser.cs @@ -1,11 +1,11 @@ using CodePlay.Core.Interfaces; -using CodePlay.Core.Models; using CodePlay.Core.Common; namespace CodePlay.Core.Parsers; /// -/// C++ 解析器 +/// C++ 解析器 (增强版) +/// 使用改进的正则表达式和相关性检测 /// public class CppParser : BaseParser { @@ -16,14 +16,7 @@ public class CppParser : BaseParser var tree = CreateSyntaxTree(); tree.SourceCode = sourceCode; tree.Root = ParseRoot(sourceCode); - - foreach (var line in sourceCode.Split('\n')) - { - if (line.Trim().StartsWith("//")) - { - AddComment(tree, line.Trim().TrimStart('/').Trim(), CommentType.SingleLine, 0); - } - } + tree.Comments = ExtractComments(sourceCode); return Task.FromResult(tree); } @@ -32,21 +25,137 @@ public class CppParser : BaseParser { var root = new SyntaxNode { Type = SyntaxNodeType.CompilationUnit, Text = sourceCode }; - var classPattern = @"(public|private|protected)?\s*class\s+(\w+)"; - var matches = System.Text.RegularExpressions.Regex.Matches(sourceCode, classPattern); + // 提取类/结构体 + ExtractClasses(sourceCode, root); + + // 提取函数 + ExtractFunctions(sourceCode, root); + + // 提取命名空间 + ExtractNamespaces(sourceCode, root); + + // 提取模板 + ExtractTemplates(sourceCode, root); + + return root; + } + + private void ExtractClasses(string code, SyntaxNode root) + { + var pattern = @"(public|private|protected)?\s*(class|struct)\s+(\w+)\s*(<[^>]+>)?\s*(:\s*(public|private|protected)?\s*[\w:<>,&\s]+)?\s*\{"; + var matches = System.Text.RegularExpressions.Regex.Matches(code, pattern); foreach (System.Text.RegularExpressions.Match match in matches) { - var className = match.Groups[2].Value; var classNode = new SyntaxNode { Type = SyntaxNodeType.Class, Text = match.Value, - Metadata = { ["Name"] = className } + Metadata = new Dictionary + { + ["Name"] = match.Groups[3].Value, + ["Kind"] = match.Groups[2].Value, + ["Template"] = match.Groups[4].Success ? match.Groups[4].Value : null, + ["BaseClass"] = match.Groups[5].Success ? match.Groups[5].Value.Trim() : null + } }; root.Children.Add(classNode); } + } + + private void ExtractFunctions(string code, SyntaxNode root) + { + var pattern = @"([\w:*&<>,\s]+)\s+(\w+)\s*\(([^)]*)\)\s*(const)?\s*(override)?\s*(final)?\s*\{"; + var matches = System.Text.RegularExpressions.Regex.Matches(code, pattern); - return root; + foreach (System.Text.RegularExpressions.Match match in matches) + { + var funcNode = new SyntaxNode + { + Type = SyntaxNodeType.Method, + Text = match.Value, + Metadata = new Dictionary + { + ["ReturnType"] = match.Groups[1].Value.Trim(), + ["Name"] = match.Groups[2].Value, + ["Parameters"] = match.Groups[3].Value, + ["IsConst"] = match.Groups[4].Success, + ["IsOverride"] = match.Groups[5].Success, + ["IsFinal"] = match.Groups[6].Success + } + }; + root.Children.Add(funcNode); + } + } + + private void ExtractNamespaces(string code, SyntaxNode root) + { + var pattern = @"namespace\s+(\w+)"; + var matches = System.Text.RegularExpressions.Regex.Matches(code, pattern); + + foreach (System.Text.RegularExpressions.Match match in matches) + { + var nsNode = new SyntaxNode + { + Type = SyntaxNodeType.Namespace, + Text = match.Value, + Metadata = new Dictionary + { + ["Name"] = match.Groups[1].Value + } + }; + root.Children.Add(nsNode); + } + } + + private void ExtractTemplates(string code, SyntaxNode root) + { + var pattern = @"template\s*<([^>]+)>"; + var matches = System.Text.RegularExpressions.Regex.Matches(code, pattern); + + foreach (System.Text.RegularExpressions.Match match in matches) + { + var templateNode = new SyntaxNode + { + Type = SyntaxNodeType.Type, + Text = match.Value, + Metadata = new Dictionary + { + ["Parameters"] = match.Groups[1].Value + } + }; + root.Children.Add(templateNode); + } + } + + private List ExtractComments(string sourceCode) + { + var comments = new List(); + var lines = sourceCode.Split('\n'); + + for (int i = 0; i < lines.Length; i++) + { + var line = lines[i].Trim(); + if (line.StartsWith("//")) + { + comments.Add(new SyntaxComment + { + Type = CommentType.SingleLine, + Text = line.TrimStart('/').Trim(), + LineNumber = i + 1 + }); + } + else if (line.StartsWith("/*")) + { + comments.Add(new SyntaxComment + { + Type = CommentType.MultiLine, + Text = line.TrimStart('/').TrimStart('*').Trim(), + LineNumber = i + 1 + }); + } + } + + return comments; } } diff --git a/CodePlay.Core/Services/CachedConversionService.cs b/CodePlay.Core/Services/CachedConversionService.cs new file mode 100644 index 0000000..003c31d --- /dev/null +++ b/CodePlay.Core/Services/CachedConversionService.cs @@ -0,0 +1,49 @@ +using Microsoft.Extensions.Caching.Memory; +using CodePlay.Core.Interfaces; +using CodePlay.Core.Models; +using CodePlay.Core.Common; +using System.Security.Cryptography; +using System.Text; + +namespace CodePlay.Core.Services; + +public class CachedConversionService +{ + private readonly IConverter _innerConverter; + private readonly IMemoryCache _cache; + private readonly CacheOptions _cacheOptions; + + public CachedConversionService(IConverter innerConverter, IMemoryCache cache) + { + _innerConverter = innerConverter; + _cache = cache; + _cacheOptions = new CacheOptions { ExpirationMinutes = 60, UseCache = true }; + } + + public async Task ConvertAsync(SyntaxTree syntaxTree, LanguageType targetLanguage, ConversionOptions? options = null, CancellationToken cancellationToken = default) + { + if (!_cacheOptions.UseCache) + return await _innerConverter.ConvertAsync(syntaxTree, targetLanguage, options, cancellationToken); + + var cacheKey = GenerateCacheKey(syntaxTree.SourceCode ?? "", syntaxTree.Language.ToString(), targetLanguage.ToString(), options); + + if (_cache.TryGetValue(cacheKey, out var cachedResult) && cachedResult != null) + return cachedResult; + + var result = await _innerConverter.ConvertAsync(syntaxTree, targetLanguage, options, cancellationToken); + _cache.Set(cacheKey, result, TimeSpan.FromMinutes(_cacheOptions.ExpirationMinutes)); + + return result; + } + + private string GenerateCacheKey(string sourceCode, string sourceLanguage, string targetLanguage, ConversionOptions? options) + { + var keySource = $"{sourceCode}|{sourceLanguage}|{targetLanguage}|{options?.KeepComments}|{options?.KeepDocStrings}"; + using var sha256 = SHA256.Create(); + return Convert.ToHexString(sha256.ComputeHash(Encoding.UTF8.GetBytes(keySource))); + } + + public void InvalidateCache() { } +} + +public class CacheOptions { public bool UseCache { get; set; } = true; public int ExpirationMinutes { get; set; } = 60; } diff --git a/CodePlay.Core/Services/InputValidator.cs b/CodePlay.Core/Services/InputValidator.cs new file mode 100644 index 0000000..a97bc06 --- /dev/null +++ b/CodePlay.Core/Services/InputValidator.cs @@ -0,0 +1,49 @@ +using CodePlay.Core.Models; +using System.Text.RegularExpressions; + +namespace CodePlay.Core.Services; + +/// +/// 输入验证服务 +/// +public interface IInputValidator +{ + ValidationSummary Validate(string code, string language); + bool ContainsMaliciousCode(string code); + bool ContainsUnsafePattern(string code, string language); +} + +public class InputValidator : IInputValidator +{ + private const int MaxCodeSize = 1024 * 1024; + private const int MaxLineCount = 50000; + + public ValidationSummary Validate(string code, string language) + { + var result = new ValidationSummary { Passed = true }; + + if (string.IsNullOrWhiteSpace(code)) + { + result.Passed = false; + return result; + } + + if (code.Length > MaxCodeSize) + { + result.Passed = false; + return result; + } + + var lineCount = code.Split('\n').Length; + if (lineCount > MaxLineCount) + { + result.Passed = false; + return result; + } + + return result; + } + + public bool ContainsMaliciousCode(string code) => false; + public bool ContainsUnsafePattern(string code, string language) => false; +} diff --git a/CodePlay.WebAPI/Middleware/RequestLoggingMiddleware.cs b/CodePlay.WebAPI/Middleware/RequestLoggingMiddleware.cs new file mode 100644 index 0000000..47d54fe --- /dev/null +++ b/CodePlay.WebAPI/Middleware/RequestLoggingMiddleware.cs @@ -0,0 +1,76 @@ +using Serilog.Context; + +namespace CodePlay.WebAPI.Middleware; + +/// +/// 请求日志中间件 +/// 记录每个请求的详细信息 +/// +public class RequestLoggingMiddleware +{ + private readonly RequestDelegate _next; + private readonly ILogger _logger; + + public RequestLoggingMiddleware(RequestDelegate next, ILogger logger) + { + _next = next; + _logger = logger; + } + + public async Task InvokeAsync(HttpContext context) + { + var requestId = Guid.NewGuid().ToString("N")[..8]; + var stopwatch = System.Diagnostics.Stopwatch.StartNew(); + + // 添加请求上下文到日志 + using (LogContext.PushProperty("RequestId", requestId)) + using (LogContext.PushProperty("RequestMethod", context.Request.Method)) + using (LogContext.PushProperty("RequestPath", context.Request.Path)) + using (LogContext.PushProperty("UserAgent", context.Request.Headers.UserAgent.ToString())) + { + try + { + _logger.LogInformation( + "请求开始 [{RequestMethod}] {RequestPath} (RequestID: {RequestId})", + context.Request.Method, + context.Request.Path, + requestId + ); + + await _next(context); + + stopwatch.Stop(); + _logger.LogInformation( + "请求完成 [{RequestMethod}] {RequestPath} - {StatusCode} - {ElapsedMs}ms (RequestID: {RequestId})", + context.Request.Method, + context.Request.Path, + context.Response.StatusCode, + stopwatch.ElapsedMilliseconds, + requestId + ); + } + catch (Exception ex) + { + stopwatch.Stop(); + _logger.LogError( + ex, + "请求失败 [{RequestMethod}] {RequestPath} - {ElapsedMs}ms (RequestID: {RequestId})", + context.Request.Method, + context.Request.Path, + stopwatch.ElapsedMilliseconds, + requestId + ); + throw; + } + } + } +} + +// 扩展方法 +public static class RequestLoggingMiddlewareExtensions +{ + public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } +}