修复Login Token的IP地址获取异常的问题,更新SQL
This commit is contained in:
parent
94642457a8
commit
f1e43edde7
10 changed files with 281 additions and 8 deletions
|
@ -23,6 +23,9 @@ namespace iFileProxy.Config
|
|||
[JsonPropertyName("Database")]
|
||||
public Database Database { get; set; }
|
||||
|
||||
[JsonPropertyName("GithubProxy")]
|
||||
public GithubProxyOptions GithubProxyOptions { get; set; } = new();
|
||||
|
||||
public static AppConfig GetCurrConfig(string configPath = "iFileProxy.json")
|
||||
{
|
||||
if (File.Exists(configPath))
|
||||
|
@ -140,4 +143,22 @@ namespace iFileProxy.Config
|
|||
[JsonPropertyName("Description")]
|
||||
public string Description { get; set; }
|
||||
}
|
||||
|
||||
public class GithubProxyOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件大小限制(字节)
|
||||
/// </summary>
|
||||
public long SizeLimit { get; set; } = 1024L * 1024L * 1024L; // 默认1GB
|
||||
|
||||
/// <summary>
|
||||
/// 黑名单列表
|
||||
/// </summary>
|
||||
public List<string> Blacklist { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用 jsDelivr 加速
|
||||
/// </summary>
|
||||
public bool EnableJsDelivr { get; set; } = false;
|
||||
}
|
||||
}
|
||||
|
|
87
src/Controllers/GithubProxyController.cs
Normal file
87
src/Controllers/GithubProxyController.cs
Normal file
|
@ -0,0 +1,87 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using iFileProxy.Services;
|
||||
using iFileProxy.Models;
|
||||
|
||||
namespace iFileProxy.Controllers
|
||||
{
|
||||
[Route("[controller]")]
|
||||
[ApiController]
|
||||
public class GithubProxyController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<GithubProxyController> _logger;
|
||||
private readonly GithubProxyService _proxyService;
|
||||
|
||||
public GithubProxyController(
|
||||
ILogger<GithubProxyController> logger,
|
||||
GithubProxyService proxyService)
|
||||
{
|
||||
_logger = logger;
|
||||
_proxyService = proxyService;
|
||||
}
|
||||
|
||||
[HttpGet("{**url}")]
|
||||
public async Task<IActionResult> ProxyDownload(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. URL 格式化
|
||||
url = url.StartsWith("http") ? url : $"https://{url}";
|
||||
|
||||
// 2. 验证 URL
|
||||
var (isValid, author, repo) = _proxyService.ValidateUrl(url);
|
||||
if (!isValid || author == null || repo == null)
|
||||
{
|
||||
return BadRequest(new CommonRsp
|
||||
{
|
||||
Retcode = 1,
|
||||
Message = "Invalid GitHub URL"
|
||||
});
|
||||
}
|
||||
|
||||
// 3. 检查黑名单
|
||||
if (_proxyService.IsBlocked(author, repo))
|
||||
{
|
||||
return Forbid();
|
||||
}
|
||||
|
||||
// 4. 处理URL(CDN或Raw)
|
||||
url = _proxyService.ProcessUrl(url);
|
||||
|
||||
// 5. 代理请求
|
||||
var response = await _proxyService.ProxyRequestAsync(url, Request.Headers);
|
||||
if (!response.Success)
|
||||
{
|
||||
return StatusCode(response.StatusCode, new CommonRsp
|
||||
{
|
||||
Retcode = 1,
|
||||
Message = response.Message
|
||||
});
|
||||
}
|
||||
|
||||
// 6. 设置响应头
|
||||
if (response.Headers != null)
|
||||
{
|
||||
foreach (var header in response.Headers)
|
||||
{
|
||||
Response.Headers[header.Key] = header.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// 7. 返回流式响应
|
||||
return new FileStreamResult(
|
||||
response.Stream!,
|
||||
response.ContentType ?? "application/octet-stream"
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Proxy download failed");
|
||||
return StatusCode(500, new CommonRsp
|
||||
{
|
||||
Retcode = 1,
|
||||
Message = "Internal server error"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ namespace iFileProxy.Controllers
|
|||
{
|
||||
try
|
||||
{
|
||||
var ip = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown";
|
||||
var ip = MasterHelper.GetClientIPAddr(HttpContext);
|
||||
var userAgent = HttpContext.Request.Headers["User-Agent"].FirstOrDefault() ?? "unknown";
|
||||
var fingerprint = HttpContext.Request.Headers["X-Device-Fingerprint"].FirstOrDefault() ?? "unknown";
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using iFileProxy.Helpers;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
|
@ -21,7 +22,7 @@ namespace iFileProxy.Middleware
|
|||
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
|
||||
var fingerprint = context.Request.Headers["X-Device-Fingerprint"].FirstOrDefault();
|
||||
var userAgent = context.Request.Headers["User-Agent"].FirstOrDefault();
|
||||
var ip = context.Connection.RemoteIpAddress?.ToString();
|
||||
var ip = MasterHelper.GetClientIPAddr(context);
|
||||
|
||||
if (token != null)
|
||||
{
|
||||
|
|
|
@ -19,7 +19,6 @@ namespace iFileProxy
|
|||
Console.Write(" "); // 补全日志第一行开头的空白
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// CORS配置
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
|
@ -27,7 +26,7 @@ namespace iFileProxy
|
|||
builder =>
|
||||
{
|
||||
builder
|
||||
.WithOrigins("http://localhost:3000", "http://admin.gitdl.cn", "https://admin.gitdl.cn","http://47.243.56.137:50050", "http://localhost:4173")
|
||||
.WithOrigins("http://localhost:3000", "http://admin.gitdl.cn", "https://admin.gitdl.cn", "https://github.linxi.info", "http://github.linxi.info/", "http://localhost:4173" )
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader()
|
||||
.AllowCredentials();
|
||||
|
|
|
@ -995,7 +995,7 @@ namespace iFileProxy.Services
|
|||
{ "@level", log.Level },
|
||||
{ "@message", log.Message },
|
||||
{ "@exception", log.Exception },
|
||||
{ "@properties", JsonConvert.SerializeObject(log.Properties) },
|
||||
{ "@properties", log.Properties },
|
||||
{ "@timestamp", log.Timestamp }
|
||||
};
|
||||
|
||||
|
|
130
src/Services/GithubProxyService.cs
Normal file
130
src/Services/GithubProxyService.cs
Normal file
|
@ -0,0 +1,130 @@
|
|||
using System.Text.RegularExpressions;
|
||||
using iFileProxy.Config;
|
||||
using iFileProxy.Models;
|
||||
|
||||
namespace iFileProxy.Services
|
||||
{
|
||||
public class GithubProxyService
|
||||
{
|
||||
private readonly ILogger<GithubProxyService> _logger;
|
||||
private readonly AppConfig _appConfig;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
private static readonly Regex[] URL_PATTERNS =
|
||||
{
|
||||
new(@"^(?:https?://)?github\.com/(?<author>.+?)/(?<repo>.+?)/(?:releases|archive)/.*$"),
|
||||
new(@"^(?:https?://)?github\.com/(?<author>.+?)/(?<repo>.+?)/(?:blob|raw)/.*$"),
|
||||
new(@"^(?:https?://)?raw\.(?:githubusercontent|github)\.com/(?<author>.+?)/(?<repo>.+?)/.+?/.+$"),
|
||||
new(@"^(?:https?://)?gist\.(?:githubusercontent|github)\.com/(?<author>.+?)/.+?/.+$")
|
||||
};
|
||||
|
||||
public GithubProxyService(
|
||||
ILogger<GithubProxyService> logger,
|
||||
AppConfig appConfig,
|
||||
HttpClient httpClient)
|
||||
{
|
||||
_logger = logger;
|
||||
_appConfig = appConfig;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public (bool isValid, string? author, string? repo) ValidateUrl(string url)
|
||||
{
|
||||
foreach (var pattern in URL_PATTERNS)
|
||||
{
|
||||
var match = pattern.Match(url);
|
||||
if (match.Success)
|
||||
{
|
||||
return (true, match.Groups["author"].Value, match.Groups["repo"].Value);
|
||||
}
|
||||
}
|
||||
return (false, null, null);
|
||||
}
|
||||
|
||||
public bool IsBlocked(string author, string repo)
|
||||
{
|
||||
return _appConfig.GithubProxyOptions.Blacklist.Contains($"{author}/{repo}") ||
|
||||
_appConfig.GithubProxyOptions.Blacklist.Contains(author);
|
||||
}
|
||||
|
||||
public string ProcessUrl(string url)
|
||||
{
|
||||
if (_appConfig.GithubProxyOptions.EnableJsDelivr && url.Contains("/blob/"))
|
||||
{
|
||||
return url.Replace("/blob/", "@")
|
||||
.Replace("github.com", "cdn.jsdelivr.net/gh");
|
||||
}
|
||||
else if (url.Contains("/blob/"))
|
||||
{
|
||||
return url.Replace("/blob/", "/raw/");
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public async Task<ProxyResponse> ProxyRequestAsync(
|
||||
string url,
|
||||
IHeaderDictionary headers,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
|
||||
// 复制请求头
|
||||
foreach (var header in headers)
|
||||
{
|
||||
if (!header.Key.Equals("Host", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
request.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
var response = await _httpClient.SendAsync(
|
||||
request,
|
||||
HttpCompletionOption.ResponseHeadersRead,
|
||||
cancellationToken);
|
||||
|
||||
// 检查文件大小
|
||||
var contentLength = response.Content.Headers.ContentLength;
|
||||
if (contentLength.HasValue && contentLength.Value > _appConfig.GithubProxyOptions.SizeLimit)
|
||||
{
|
||||
return new ProxyResponse
|
||||
{
|
||||
Success = false,
|
||||
StatusCode = StatusCodes.Status413PayloadTooLarge,
|
||||
Message = "File too large"
|
||||
};
|
||||
}
|
||||
|
||||
return new ProxyResponse
|
||||
{
|
||||
Success = true,
|
||||
StatusCode = (int)response.StatusCode,
|
||||
Headers = response.Headers.ToDictionary(h => h.Key, h => h.Value.ToArray()),
|
||||
Stream = await response.Content.ReadAsStreamAsync(cancellationToken),
|
||||
ContentType = response.Content.Headers.ContentType?.ToString()
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Proxy request failed");
|
||||
return new ProxyResponse
|
||||
{
|
||||
Success = false,
|
||||
StatusCode = StatusCodes.Status500InternalServerError,
|
||||
Message = "Internal server error"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ProxyResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public int StatusCode { get; set; }
|
||||
public string? Message { get; set; }
|
||||
public Dictionary<string, string[]>? Headers { get; set; }
|
||||
public Stream? Stream { get; set; }
|
||||
public string? ContentType { get; set; }
|
||||
}
|
||||
}
|
|
@ -10,5 +10,13 @@
|
|||
"Key": "iFileProxy-JWT-Secret-Key-2024-Very-Long-Secret-Key-For-Security",
|
||||
"Issuer": "iFileProxy",
|
||||
"Audience": "iFileProxy.Client"
|
||||
},
|
||||
"GithubProxy": {
|
||||
"SizeLimit": 1073741824, // 1GB in bytes
|
||||
"Blacklist": [
|
||||
"blockedUser1",
|
||||
"blockedUser2/blockedRepo",
|
||||
"blockedOrg/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,12 +11,28 @@
|
|||
Target Server Version : 50743
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 01/12/2024 01:28:13
|
||||
Date: 04/12/2024 00:13:59
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_system_logs
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `t_system_logs`;
|
||||
CREATE TABLE `t_system_logs` (
|
||||
`log_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
|
||||
`level` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
|
||||
`message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
|
||||
`exception` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
|
||||
`properties` json NULL,
|
||||
`timestamp` datetime NOT NULL,
|
||||
PRIMARY KEY (`log_id`) USING BTREE,
|
||||
INDEX `idx_timestamp`(`timestamp`) USING BTREE,
|
||||
INDEX `idx_level`(`level`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_tasks_info
|
||||
-- ----------------------------
|
||||
|
@ -35,7 +51,7 @@ CREATE TABLE `t_tasks_info` (
|
|||
`tag` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '标记',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE INDEX `tid`(`tid`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 9130 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 111135 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for t_user_events
|
||||
|
@ -60,6 +76,7 @@ DROP TABLE IF EXISTS `t_users`;
|
|||
CREATE TABLE `t_users` (
|
||||
`user_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
|
||||
`nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '昵称',
|
||||
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '电子邮箱',
|
||||
`username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名',
|
||||
`password_hash` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '密码哈希值',
|
||||
`mask` int(11) NOT NULL DEFAULT 0 COMMENT '权限掩码',
|
||||
|
@ -68,7 +85,8 @@ CREATE TABLE `t_users` (
|
|||
`last_login_time` datetime NULL DEFAULT NULL COMMENT '上次登录时间',
|
||||
`last_login_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '上次登录IP',
|
||||
PRIMARY KEY (`user_id`) USING BTREE,
|
||||
UNIQUE INDEX `username`(`username`) USING BTREE
|
||||
UNIQUE INDEX `username`(`username`) USING BTREE,
|
||||
UNIQUE INDEX `email`(`email`) USING BTREE
|
||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
|
|
@ -39,5 +39,14 @@
|
|||
"/AddOfflineTask"
|
||||
],
|
||||
"AllowDifferentIPsForDownload": true // 下载者与任务数据提交者IP不同是否允许下载文件
|
||||
},
|
||||
"GithubProxy": {
|
||||
"SizeLimit": 1073741824,
|
||||
"Blacklist": [
|
||||
"blockedUser1",
|
||||
"blockedUser2/blockedRepo",
|
||||
"blockedOrg/*"
|
||||
],
|
||||
"EnableJsDelivr": false
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue