diff --git a/src/Controllers/ManagementController.cs b/src/Controllers/ManagementController.cs
index bb8003c..346e172 100644
--- a/src/Controllers/ManagementController.cs
+++ b/src/Controllers/ManagementController.cs
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc;
using Serilog;
using System.Text.Json;
using iFileProxy.Attributes;
+
namespace iFileProxy.Controllers
{
[Authorize(UserMask.Admin,UserMask.SuperAdmin)]
diff --git a/src/Controllers/UserController.cs b/src/Controllers/UserController.cs
index 74217f5..75a1ea9 100644
--- a/src/Controllers/UserController.cs
+++ b/src/Controllers/UserController.cs
@@ -1,8 +1,8 @@
using iFileProxy.Attributes;
+using iFileProxy.Helpers;
using iFileProxy.Models;
using iFileProxy.Services;
using Microsoft.AspNetCore.Mvc;
-using Newtonsoft.Json;
namespace iFileProxy.Controllers
{
@@ -27,8 +27,8 @@ namespace iFileProxy.Controllers
{
try
{
- var ip = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown";
- var (success, message) = await _authService.RegisterAsync(request.Username, request.Password, ip, request.NickName);
+ var ip = MasterHelper.GetClientIPAddr(HttpContext);
+ var (success, message) = await _authService.RegisterAsync(request,ip);
return Ok(new CommonRsp
{
@@ -106,9 +106,6 @@ namespace iFileProxy.Controllers
});
}
-
- _logger.LogInformation(JsonConvert.SerializeObject(user));
-
// 不返回敏感信息
return Ok(new CommonRsp
{
@@ -592,16 +589,4 @@ namespace iFileProxy.Controllers
}
}
- public class RegisterRequest
- {
- public string Username { get; set; } = string.Empty;
- public string Password { get; set; } = string.Empty;
- public string NickName { get; set; } = string.Empty;
- }
-
- public class LoginRequest
- {
- public string Username { get; set; } = string.Empty;
- public string Password { get; set; } = string.Empty;
- }
}
\ No newline at end of file
diff --git a/src/Models/User.cs b/src/Models/User.cs
index dbd01e5..b0dadf9 100644
--- a/src/Models/User.cs
+++ b/src/Models/User.cs
@@ -4,9 +4,10 @@ namespace iFileProxy.Models
{
public enum UserMask
{
+ Guest = -1, // 访客
User = 0,
Admin = 1,
- SuperAdmin = 2 // 超级管理员
+ SuperAdmin = 2, // 超级管理员
}
public enum UserState
{
@@ -77,6 +78,12 @@ namespace iFileProxy.Models
///
[JsonProperty("last_login_ip")]
public string LastLoginIP { get; set; } = string.Empty;
+
+ ///
+ /// 电子邮箱
+ ///
+ [JsonProperty("email")]
+ public string Email { get; set; } = string.Empty;
}
public class UserEvent
@@ -132,4 +139,29 @@ namespace iFileProxy.Models
[JsonProperty("nickname")]
public string Nickname { get; set; } = string.Empty;
}
+
+
+ ///
+ /// 注册请求数据结构
+ ///
+ public class RegisterRequest
+ {
+ public string Username { get; set; } = string.Empty;
+ public string Password { get; set; } = string.Empty;
+ public string NickName { get; set; } = string.Empty;
+ public string Email { get; set; } = string.Empty;
+ }
+
+ ///
+ /// 登录请求数据结构
+ ///
+ public class LoginRequest
+ {
+ ///
+ /// 用户名或邮箱
+ ///
+ public string Username { get; set; } = string.Empty;
+ public string Account { get; set; } = string.Empty;
+ public string Password { get; set; } = string.Empty;
+ }
}
diff --git a/src/Program.cs b/src/Program.cs
index 8aebddf..babdb49 100644
--- a/src/Program.cs
+++ b/src/Program.cs
@@ -16,18 +16,18 @@ namespace iFileProxy
SerilogConfig.CreateLogger();
Serilog.ILogger logger = Log.Logger.ForContext();
- Console.Write(" "); // ǿ֢ſʼ־벻þ
+ Console.Write(" "); // ǿ��֢�����ſ�ʼ��������־�����벻�þ�
var builder = WebApplication.CreateBuilder(args);
- // CORS
+ // ���� CORS ����
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowFrontend",
builder =>
{
builder
- .WithOrigins("http://localhost:3000", "http://admin.gitdl.cn", "https://admin.gitdl.cn","http://47.243.56.137:50050") // ǰ˵ַ
+ .WithOrigins("http://localhost:3000", "http://admin.gitdl.cn", "https://admin.gitdl.cn","http://47.243.56.137:50050") // ǰ�˵�ַ
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
@@ -55,10 +55,10 @@ namespace iFileProxy
builder.Services.AddSingleton();
- // ֤
+ // ������֤����
builder.Services.AddScoped();
- // JWT֤
+ // ����JWT��֤
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
@@ -98,17 +98,17 @@ namespace iFileProxy
app.UseSwaggerUI();
}
- // м
+ // ��������
app.UseMiddleware();
app.UseCors("AllowFrontend");
app.UseHttpsRedirection();
- // Ĭļѡ
+ // ����Ĭ���ļ�ѡ��
var defaultFilesOptions = new DefaultFilesOptions();
- defaultFilesOptions.DefaultFileNames.Clear(); // Ĭб
- defaultFilesOptions.DefaultFileNames.Add("index.html"); // ԶĬļ
+ defaultFilesOptions.DefaultFileNames.Clear(); // ���Ĭ���б�
+ defaultFilesOptions.DefaultFileNames.Add("index.html"); // �����Զ���Ĭ���ļ�
app.UseDefaultFiles(defaultFilesOptions);
@@ -120,7 +120,7 @@ namespace iFileProxy
app.Services.GetRequiredService>>(),
AppConfig.GetCurrConfig().SecurityOptions.DailyRequestLimitPerIP);
- // м
+ // ������
app.UseMiddleware();
app.MapControllers();
diff --git a/src/Services/AuthService.cs b/src/Services/AuthService.cs
index 7b2a169..b07cf8f 100644
--- a/src/Services/AuthService.cs
+++ b/src/Services/AuthService.cs
@@ -20,14 +20,14 @@ namespace iFileProxy.Services
_logger = logger;
}
- public async Task<(bool success, string message)> RegisterAsync(string username, string password, string ip, string nickname)
+ public async Task<(bool success, string message)> RegisterAsync(RegisterRequest request, string ipAddr)
{
try
{
// 检查用户名是否已存在
- if (await _dbGateService.UserExistsAsync(username))
+ if (await _dbGateService.UserExistsAsync(request.Username, request.Email))
{
- return (false, "用户名已存在");
+ return (false, "用户名或电子邮件已存在");
}
// 检查是否是第一个用户
@@ -36,12 +36,13 @@ namespace iFileProxy.Services
// 创建新用户
var user = new User
{
- Username = username,
- PasswordHash = BCrypt.Net.BCrypt.HashPassword(password),
- LastLoginIP = ip,
+ Username = request.Username,
+ PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.Password),
+ LastLoginIP = ipAddr,
// 如果是第一个用户,设置为超级管理员
Mask = isFirstUser ? UserMask.SuperAdmin : UserMask.User,
- Nickname = nickname
+ Nickname = request.NickName,
+ Email = request.Email,
};
// 保存用户
@@ -52,12 +53,12 @@ namespace iFileProxy.Services
{
UserId = user.UserId,
EventType = UserEventType.Registry,
- EventIP = ip,
- EventDetail = isFirstUser ? "管理员注册" : "用户注册"
+ EventIP = ipAddr,
+ EventDetail = isFirstUser ? "超级管理员注册" : "用户注册"
};
await _dbGateService.CreateUserEventAsync(userEvent);
- return (true, isFirstUser ? "管理员注册成功" : "注册成功");
+ return (true, isFirstUser ? "超级管理员注册成功" : "注册成功");
}
catch (Exception ex)
{
@@ -66,11 +67,11 @@ namespace iFileProxy.Services
}
}
- public async Task<(bool success, string token, string message)> LoginAsync(string username, string password, string ip)
+ public async Task<(bool success, string token, string message)> LoginAsync(string account, string password, string ip)
{
try
{
- var user = await _dbGateService.GetUserByUsernameAsync(username);
+ var user = await _dbGateService.GetUserByAccountAsync(account);
if (user == null)
{
return (false, string.Empty, "用户不存在");
@@ -97,7 +98,7 @@ namespace iFileProxy.Services
UserId = user.UserId,
EventType = UserEventType.Login,
EventIP = ip,
- EventDetail = "用户登录"
+ EventDetail = $"用户通过{(account.Contains('@') ? "邮箱" : "用户名")}登录"
};
await _dbGateService.CreateUserEventAsync(userEvent);
diff --git a/src/Services/DatabaseGateService.cs b/src/Services/DatabaseGateService.cs
index 9d76e73..c9664b0 100644
--- a/src/Services/DatabaseGateService.cs
+++ b/src/Services/DatabaseGateService.cs
@@ -39,6 +39,7 @@ namespace iFileProxy.Services
`user_id` varchar(36) NOT NULL,
`username` varchar(50) NOT NULL,
`nickname` varchar(50) NOT NULL,
+ `email` varchar(100) NOT NULL,
`password_hash` varchar(255) NOT NULL,
`mask` int NOT NULL DEFAULT 0,
`state` int NOT NULL DEFAULT 0,
@@ -46,7 +47,8 @@ namespace iFileProxy.Services
`last_login_time` datetime NULL,
`last_login_ip` varchar(45) NULL,
PRIMARY KEY (`user_id`),
- UNIQUE KEY `username` (`username`)
+ UNIQUE KEY `username` (`username`),
+ UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
""";
@@ -196,6 +198,20 @@ namespace iFileProxy.Services
return n;
}
+ ///
+ /// 内部查询数据专用 当此方法暴露给C端可能造成sql注入等安全问题
+ ///
+ /// SQL语句
+ /// 配置文件中的Description字段
+ /// 影响的行数
+ public int Query(string sql, MySqlConnection connection)
+ {
+ using MySqlCommand sqlCmd = new(sql, connection);
+ int n = sqlCmd.ExecuteNonQuery();
+ _logger.Debug($"查询完成, 受影响的行数: {n}");
+ return n;
+ }
+
public List CheckCacheDependencies(string taskId,string ipAddr)
{
string sql = $"SELECT * FROM t_tasks_info WHERE `status` = @status AND `tag` = @tag AND `client_ip` <> @ip_addr";
@@ -362,10 +378,11 @@ namespace iFileProxy.Services
}
- public bool UpdateTaskStatus(TaskInfo taskInfo)
+ public bool UpdateTaskStatus(TaskInfo taskInfo, MySqlConnection? connection = null)
{
string sql = @"UPDATE t_tasks_info set `status` = @status , update_time = Now() WHERE `tid` = @tid";
- MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
+ MySqlConnection conn = connection ?? GetAndOpenDBConn("iFileProxy_Db");
+
try
{
using MySqlCommand sqlCmd = new (sql, conn);
@@ -623,12 +640,13 @@ namespace iFileProxy.Services
return users.FirstOrDefault();
}
- public async Task UserExistsAsync(string username)
+ public async Task UserExistsAsync(string username, string email)
{
- var sql = "SELECT COUNT(*) FROM t_users WHERE username = @username";
+ var sql = "SELECT COUNT(*) FROM t_users WHERE username = @username OR email = @email";
var parameters = new Dictionary
{
- { "@username", username }
+ { "@username", username },
+ { "@email", email }
};
var count = await ExecuteScalarAsync(sql, parameters);
return count > 0;
@@ -636,8 +654,8 @@ namespace iFileProxy.Services
public async Task CreateUserAsync(User user)
{
- var sql = @"INSERT INTO t_users (user_id, username, password_hash, mask, state, create_time, last_login_time, last_login_ip, nickname)
- VALUES (@userId, @username, @passwordHash, @mask, @state, @createTime, @lastLoginTime, @lastLoginIp, @nickname)";
+ var sql = @"INSERT INTO t_users (user_id, username, password_hash, mask, state, create_time, last_login_time, last_login_ip, nickname, email)
+ VALUES (@userId, @username, @passwordHash, @mask, @state, @createTime, @lastLoginTime, @lastLoginIp, @nickname, @email)";
var parameters = new Dictionary
{
@@ -650,7 +668,7 @@ namespace iFileProxy.Services
{ "@lastLoginTime", user.LastLoginTime },
{ "@lastLoginIp", user.LastLoginIP },
{ "@nickname", user.Nickname },
-
+ { "@email", user.Email }
};
var result = await ExecuteNonQueryAsync(sql, parameters);
@@ -870,5 +888,17 @@ namespace iFileProxy.Services
Data = events
};
}
+
+ public async Task GetUserByAccountAsync(string account)
+ {
+ var sql = "SELECT * FROM t_users WHERE username = @account OR email = @account";
+ var parameters = new Dictionary
+ {
+ { "@account", account }
+ };
+
+ var users = await ExecuteQueryAsync(sql, parameters);
+ return users.FirstOrDefault();
+ }
}
}
diff --git a/src/Services/LocalCacheManager.cs b/src/Services/LocalCacheManager.cs
index a003150..aa44e43 100644
--- a/src/Services/LocalCacheManager.cs
+++ b/src/Services/LocalCacheManager.cs
@@ -38,6 +38,9 @@ namespace iFileProxy.Services
///
public void CheckAndCleanCache(object state)
{
+ // 初始化并打开一个MySQL连接 防止后续数据过多导致程序crush
+ var dbConn = _dbGateService.GetAndOpenDBConn(DbConfigName.iFileProxy);
+
// 获取数据库中超出生命周期的缓存数据
string result = _dbGateService.QueryTableData($"SELECT * FROM t_tasks_info WHERE UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(update_time) > {CACHE_LIFETIME} AND (tag <> 'CLEANED' OR tag IS NULL)", DbConfigName.iFileProxy);
List? taskInfos = JsonConvert.DeserializeObject>(result);
@@ -59,9 +62,9 @@ namespace iFileProxy.Services
throw;
}
}
- _dbGateService.Query($"UPDATE t_tasks_info SET `tag` = \"CLEANED\" WHERE `tid` = '{taskInfo.TaskId}'", DbConfigName.iFileProxy);
+ _dbGateService.Query($"UPDATE t_tasks_info SET `tag` = \"CLEANED\" WHERE `tid` = '{taskInfo.TaskId}'", dbConn);
taskInfo.Status = TaskState.Cleaned;
- _dbGateService.UpdateTaskStatus(taskInfo);
+ _dbGateService.UpdateTaskStatus(taskInfo,dbConn);
}
}
}
diff --git a/src/iFileProxy.json b/src/iFileProxy.json
index 27e45a4..bf3f1b0 100644
--- a/src/iFileProxy.json
+++ b/src/iFileProxy.json
@@ -16,7 +16,7 @@
"Download": {
"SavePath": "./download/", // 临时文件保存位置
"ThreadNum": 4, // 下载线程数
- "MaxAllowedFileSize": 65536, // 允许代理的最大文件尺寸
+ "MaxAllowedFileSize": 1000000000, // 允许代理的最大文件尺寸
"MaxParallelTasks": 4, // 同一时间最大并行任务数
"MaxQueueLength": 60, // 最大等待队列长度
"Aria2cPath": "./lib/aria2c",