支持重复文件利用

This commit is contained in:
root 2024-11-22 23:59:40 +08:00
parent d22404bdbb
commit e530cb46fe
10 changed files with 193 additions and 22 deletions

View file

@ -9,5 +9,5 @@
- [x] 基于IP查询提交的任务状态
- [x] 基于IP和路由的请求次数限制
- [ ] 任务队列
- [ ] 已经存在对应文件时候直接跳过代理下载 直接下载已经缓存的内容
- [x] 已经存在对应文件时候直接跳过代理下载 直接下载已经缓存的内容
- [ ] 捐赠

View file

@ -110,7 +110,7 @@ namespace iFileProxy.Helpers
/// <param name="sql"></param>
/// <param name="conn"></param>
/// <returns></returns>
public static string GetTableData(MySqlCommand sqlCmd)
public static string QueryTableData(MySqlCommand sqlCmd)
{
DataTable dataTable = new();
using (MySqlDataAdapter adapter = new (sqlCmd))
@ -118,6 +118,34 @@ namespace iFileProxy.Helpers
return JsonConvert.SerializeObject(dataTable);
}
/// <summary>
/// 获取一个json格式的数据表
/// </summary>
/// <param name="sql"></param>
/// <param name="conn"></param>
/// <returns></returns>
public string QueryTableData(string sql,string dbConfName)
{
DataTable dataTable = new();
using (MySqlDataAdapter adapter = new(new MySqlCommand(sql,GetAndOpenDBConn(dbConfName))))
adapter.Fill(dataTable);
return JsonConvert.SerializeObject(dataTable);
}
/// <summary>
/// 内部查询数据专用 当此方法暴露给C端可能造成sql注入等安全问题
/// </summary>
/// <param name="sql">SQL语句</param>
/// <param name="dbConfName">配置文件中的Description字段</param>
/// <returns>影响的行数</returns>
public int Query(string sql, string dbConfName)
{
using MySqlCommand sqlCmd = new (sql, GetAndOpenDBConn(dbConfName));
int n = sqlCmd.ExecuteNonQuery();
_logger.Debug($"查询完成, 受影响的行数: {n}");
return n;
}
public string GetTaskListByIP(string ipAddr)
{
string sql = $"SELECT * FROM t_tasks_info WHERE client_ip = @ip_addr";
@ -126,7 +154,7 @@ namespace iFileProxy.Helpers
{
using MySqlCommand sqlCmd = new (sql,conn);
sqlCmd.Parameters.AddWithValue("@ip_addr", ipAddr);
return GetTableData(sqlCmd);
return QueryTableData(sqlCmd);
}
catch (Exception e)
{
@ -143,7 +171,7 @@ namespace iFileProxy.Helpers
{
using MySqlCommand sqlCmd = new(sql, conn);
sqlCmd.Parameters.AddWithValue("@tid", tid);
return GetTableData(sqlCmd);
return QueryTableData(sqlCmd);
}
catch (Exception e)
{
@ -153,13 +181,39 @@ namespace iFileProxy.Helpers
finally { conn.Close(); }
}
public TaskInfo? QueryTaskInfo(string fileName, string url, long size, TaskState status)
{
string sql = $"SELECT * FROM t_tasks_info WHERE url = @url AND size = @size AND `status` = @status AND file_name = @fileName";
MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
try {
MySqlCommand sqlCmd = new MySqlCommand(sql, conn);
sqlCmd.Parameters.AddWithValue("@url", url);
sqlCmd.Parameters.AddWithValue("@size", size);
sqlCmd.Parameters.AddWithValue("@status", status);
sqlCmd.Parameters.AddWithValue("@fileName", fileName);
string result = QueryTableData(sqlCmd);
List<TaskInfo>? r = JsonConvert.DeserializeObject<List<TaskInfo>>(result);
if (r != null)
{
return r[0];
}
else
return null;
}
catch (Exception e){
return null;
}
finally {
conn.Close();
}
}
public bool InsertTaskData(TaskInfo taskInfo)
{
_logger.Debug(System.Text.Json.JsonSerializer.Serialize(taskInfo));
string sql = "INSERT INTO `t_tasks_info` (`tid`, `file_name`, `client_ip`, `add_time`, `update_time`, `status`, `url`, `size`, `hash`) " +
"VALUES (@tid, @file_name, @client_ip, @add_time, @update_time, @status, @url, @size, @hash)";
string sql = "INSERT INTO `t_tasks_info` (`tid`, `file_name`, `client_ip`, `add_time`, `update_time`, `status`, `url`, `size`, `hash`, `tag`) " +
"VALUES (@tid, @file_name, @client_ip, @add_time, @update_time, @status, @url, @size, @hash, @tag)";
MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
try
@ -174,6 +228,7 @@ namespace iFileProxy.Helpers
sqlCmd.Parameters.AddWithValue("@url", taskInfo.Url);
sqlCmd.Parameters.AddWithValue("@size", taskInfo.Size);
sqlCmd.Parameters.AddWithValue("@hash", taskInfo.Hash);
sqlCmd.Parameters.AddWithValue("@tag", taskInfo.Tag);
sqlCmd.ExecuteNonQuery();
}
@ -225,13 +280,13 @@ namespace iFileProxy.Helpers
using MySqlCommand sqlCmd = new (sql, conn);
sqlCmd.Parameters.AddWithValue("@status", taskInfo.Status);
sqlCmd.Parameters.AddWithValue("@tid", taskInfo.TaskId);
if (sqlCmd.ExecuteNonQuery() == 1)
if (sqlCmd.ExecuteNonQuery() >= 1)
{
_logger.Debug($"Task: {taskInfo.TaskId} Task Status Change to {taskInfo.Status}");
_logger.Debug($"Task: {taskInfo.TaskId} Status Change to {taskInfo.Status}");
return true;
}
else
_logger.Warning($"Task: {taskInfo.TaskId} Task Status Change Failed.");
_logger.Warning($"Task: {taskInfo.TaskId} Status Change Failed.");
return false;
}
catch (Exception)

View file

@ -1,4 +1,5 @@
using iFileProxy.Models;
using iFileProxy.Config;
using iFileProxy.Models;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
@ -56,6 +57,13 @@ namespace iFileProxy.Helpers
return clientIp;
}
public static bool CheckDownloadFileExists(string fileName)
{
if (File.Exists(Path.Combine(AppConfig.GetCurrConfig().DownloadOptions.SavePath,fileName)))
return true;
return false;
}
public static string GetFileHash(string fileName, FileHashAlgorithm algorithm)
{
byte[] hash = [];

View file

@ -1,37 +1,57 @@
using System.Text.Json;
using Newtonsoft.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace iFileProxy.Models
{
public class TaskInfo
{
[JsonProperty("id")]
[JsonPropertyName("id")]
public uint Id { get; set; }
[JsonProperty("tid")]
[JsonPropertyName("tid")]
public string TaskId { get; set; }
[JsonProperty("file_name")]
[JsonPropertyName("file_name")]
public string FileName { get; set; }
[JsonProperty("client_ip")]
[JsonPropertyName("client_ip")]
public string? ClientIp { get; set; }
[JsonProperty("add_time")]
[JsonPropertyName("add_time")]
public DateTime AddTime { get; set; }
[JsonProperty("update_time")]
[JsonPropertyName("update_time")]
public DateTime UpdateTime { get; set; }
[JsonProperty("status")]
[JsonPropertyName("status")]
public TaskState Status { get; set; }
[JsonProperty("url")]
[JsonPropertyName("url")]
public string Url { get; set; }
[JsonProperty("size")]
[JsonPropertyName("size")]
public long Size { get; set; }
[JsonProperty("hash")]
[JsonPropertyName("hash")]
public string Hash { get; set; }
[JsonProperty("tag")]
[JsonPropertyName("tag")]
public string Tag { get; set; }
}
public class DbConfigName {
public static string iFileProxy = "iFileProxy_Db";
}
}

View file

@ -6,6 +6,7 @@
Error = 2, // 任务执行时候发生错误 已经结束
End = 3, // 任务正常结束
Cached = 4, // 要下载的内容已经缓存
Cleaned =5, // 内容过期已被清理
}
public enum TaskAddState {
Success = 0,

View file

@ -1,6 +1,7 @@
using iFileProxy.Config;
using iFileProxy.Middleware;
using iFileProxy.Services;
using Serilog;
namespace iFileProxy
@ -12,10 +13,13 @@ namespace iFileProxy
SerilogConfig.CreateLogger();
Serilog.ILogger logger = Log.Logger.ForContext<Program>();
Console.Write(" ");
Console.Write(" "); // 强迫症,看着开始的那条日志不对齐不得劲
AppConfig.CheckAppConfig();
// 初始化缓存管理服务
LocalCacheManager localCacheManager = new ();
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.

View file

@ -15,7 +15,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<_TargetId>Folder</_TargetId>
<SiteUrlToLaunchAfterPublish />
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<ProjectGuid>e343bd8a-27ed-47e2-b50d-e3000730e65e</ProjectGuid>
<SelfContained>false</SelfContained>
<PublishSingleFile>true</PublishSingleFile>

View file

@ -1,9 +1,74 @@
namespace iFileProxy.Services
using iFileProxy.Config;
using iFileProxy.Helpers;
using iFileProxy.Models;
using Newtonsoft.Json;
using Serilog;
namespace iFileProxy.Services
{
/// <summary>
/// 本地缓存管理器
/// </summary>
public class LocalCacheManager
{
// 禁用空警告 因为初始化的时候就已经检查过相关的内容了
#pragma warning disable CS8601
private readonly AppConfig _appConfig = AppConfig.GetCurrConfig();
private readonly static Serilog.ILogger _logger = Log.Logger.ForContext<LocalCacheManager>();
private readonly Timer _timer;
private readonly DatabaseHelper _dbHelper;
private readonly int CACHE_LIFETIME;
public LocalCacheManager()
{
_logger.Information("Initializing LocalCacheManager.");
CACHE_LIFETIME = _appConfig.DownloadOptions.CacheLifetime;
_dbHelper = new DatabaseHelper(_appConfig);
_timer = new Timer(CheckAndCleanCache, null, TimeSpan.FromSeconds(6), TimeSpan.FromSeconds(60));
_logger.Information("succ.");
}
/// <summary>
/// 检查并且清理缓存数据
/// </summary>
public void CheckAndCleanCache(object state)
{
// 获取数据库中超出生命周期的缓存数据
string result = _dbHelper.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<TaskInfo>? taskInfos = JsonConvert.DeserializeObject<List<TaskInfo>>(result);
if (taskInfos != null)
{
foreach (TaskInfo taskInfo in taskInfos)
{
string cacheFileName = Path.Combine(_appConfig.DownloadOptions.SavePath, taskInfo.FileName);
if (File.Exists(cacheFileName))
{
_logger.Information($"正在清理缓存文件: {cacheFileName}");
try
{
File.Delete(cacheFileName);
}
catch (Exception e)
{
_logger.Error("缓存文件删除失败: {e}", e);
throw;
}
}
_dbHelper.Query($"UPDATE t_tasks_info SET `tag` = \"CLEANED\" WHERE `tid` = '{taskInfo.TaskId}'", DbConfigName.iFileProxy);
taskInfo.Status = TaskState.Cleaned;
_dbHelper.UpdateTaskStatus(taskInfo);
}
}
}
/// <summary>
/// 停止定时清理服务
/// </summary>
public void StopScheduledCleanupService()
{
_timer.Dispose();
}
}
}

View file

@ -61,7 +61,7 @@ namespace iFileProxy.Services
if (_appConfig.SecurityOptions.BlockedFileName.IndexOf(fileInfo.FileName) != -1)
return TaskAddState.ErrFileNameForbidden;
TaskInfo taskInfo = new()
TaskInfo taskInfo = new()
{
Url = t_url,
TaskId = Guid.NewGuid().ToString(),
@ -72,12 +72,28 @@ namespace iFileProxy.Services
Status = TaskState.NoInit,
UpdateTime = DateTime.Now
};
if (MasterHelper.CheckDownloadFileExists(taskInfo.FileName))
{
var r = _dbHelper.QueryTaskInfo(taskInfo.FileName, taskInfo.Url, taskInfo.Size, TaskState.End);
if (r != null)
{
taskInfo.Status = TaskState.Cached;
taskInfo.Hash = r.Hash;
taskInfo.Size = r.Size;
taskInfo.Tag = $"REDIRECT:{r.TaskId}";
}
}
if (_dbHelper.InsertTaskData(taskInfo))
{
StartTask(taskInfo);
taskInfo.Status = TaskState.Running;
_dbHelper.UpdateTaskStatus(taskInfo);
_logger.Debug("数据插入成功");
if (taskInfo.Status != TaskState.Cached)
{
StartTask(taskInfo);
taskInfo.Status = TaskState.Running;
_dbHelper.UpdateTaskStatus(taskInfo);
}
_logger.Debug("任务添加成功.");
return TaskAddState.Success;
}
else
@ -160,7 +176,7 @@ namespace iFileProxy.Services
public List<TaskInfo> GetTaskInfo(string taskId)
{
_logger.Debug(_dbHelper.GetTaskInfoByTid(taskId));
return JsonSerializer.Deserialize<List<TaskInfo>>(_dbHelper.GetTaskInfoByTid(taskId)) ?? [] ;
return JsonSerializer.Deserialize<List<TaskInfo>>(_dbHelper.GetTaskInfoByTid(taskId)) ?? [];
}
//public bool DeleteTask(HttpContext c)

View file

@ -122,7 +122,9 @@
[0, "排队中"],
[1, "进行中"],
[2, "错误"],
[3, "已完成"]
[3, "已完成"],
[4, "已缓存"],
[5, "已被清理"]
])
data = [];
$.ajax({
@ -159,7 +161,7 @@
data.forEach(item => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${item.status == 3 ? `<a href="/Download/${item.tid}">${item.file_name}</a>` : item.file_name}</td>
<td>${item.status == 3 || item.status == 4 ? `<a href="/Download/${item.tid}">${item.file_name}</a>` : item.file_name}</td>
<td class="hidden-on-small">${formatBytes(item.size)}</td>
<td>${item.add_time}</td>
<td>${statusStrDict.get(item.status)}</td>