支持访客自助删除信息

This commit is contained in:
root 2024-11-24 17:57:30 +08:00
parent c412eb3ef9
commit 21b721758c
11 changed files with 319 additions and 12 deletions

View file

@ -0,0 +1,51 @@
using iFileProxy.Helpers;
using iFileProxy.Models;
using iFileProxy.Config;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Serilog;
namespace iFileProxy.Controllers
{
/// <summary>
/// 访客信息相关
/// </summary>
[Route("/[controller]")]
[ApiController]
public class VisitorManagementController : ControllerBase
{
static DatabaseHelper _dbHelper = new DatabaseHelper(AppConfig.GetCurrConfig());
private readonly static Serilog.ILogger _logger = Log.Logger.ForContext<VisitorManagementController>();
[HttpDelete("DeleteInfo")]
public ActionResult<CommonRsp> DeleteInfo()
{
string ipAddr = MasterHelper.GetClientIPAddr(HttpContext);
var d = JsonConvert.DeserializeObject<List<TaskInfo>>(_dbHelper.GetTaskListByStateAndIp(ipAddr, TaskState.Running));
int rd = (d != null) ? d.Count : 0;
if (rd <= 0)
{
d = JsonConvert.DeserializeObject<List<TaskInfo>>(_dbHelper.GetTaskListByIP(ipAddr));
foreach (var taskInfo in d)
{
var dep = _dbHelper.CheckCacheDependencies(taskInfo.TaskId,ipAddr);
if (dep.Count <= 0)
{
if (MasterHelper.CheckDownloadFileExists(taskInfo.FileName))
{
MasterHelper.RemoveDownloadFile(taskInfo.FileName);
_logger.Information("[客户端主动申请删除信息数据] 正在删除文件: {f}", taskInfo.FileName);
}
}
else
_logger.Warning($"准备删除的文件正在被以下Task所依赖: {JsonConvert.SerializeObject(dep)} 文件将不会被删除");
}
return Ok(new CommonRsp() { Retcode = 0, Message = "succ", Data = _dbHelper.DeleteTaskInfoByIpAddr(ipAddr)});
}
else
return Ok(new CommonRsp() {Retcode = -1, Message = "你还有正在运行的任务, 你现在不能删除你的数据, 请稍后等待任务完成后重试", Data = rd });
}
}
}

View file

@ -11,7 +11,6 @@ namespace iFileProxy.Controllers
public class iProxyController : ControllerBase
{
static readonly TaskManager taskManager = new ();
static Dictionary<string, Dictionary<string, uint>> IPAccessCountDict = [];
static AppConfig? appConfig = AppConfig.GetCurrConfig();
[HttpPost]
@ -82,10 +81,10 @@ namespace iFileProxy.Controllers
fileName = d.Where(x => x.TaskId == taskID).FirstOrDefault()?.FileName;
}
if (fileName == null)
return Ok(new CommonRsp() { Message = "file not exists", Retcode = -1 });
return Ok(new CommonRsp() { Message = "file not exists or taskId error", Retcode = -1 });
var filePath = Path.Combine(AppConfig.GetCurrConfig().DownloadOptions.SavePath, fileName);
if (!System.IO.File.Exists(filePath))
if (!MasterHelper.CheckDownloadFileExists(fileName))
{
return Ok(new CommonRsp() { Message = "file not exists", Retcode = 1 });
}

View file

@ -146,6 +146,47 @@ namespace iFileProxy.Helpers
return n;
}
public List<TaskInfo> CheckCacheDependencies(string taskId,string ipAddr)
{
string sql = $"SELECT * FROM t_tasks_info WHERE `status` = @status AND `tag` = @tag AND `client_ip` <> @ip_addr";
MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
try
{
using MySqlCommand sqlCmd = new(sql, conn);
sqlCmd.Parameters.AddWithValue("@tag", $"REDIRECT:{taskId}");
sqlCmd.Parameters.AddWithValue("@status", TaskState.Cached);
sqlCmd.Parameters.AddWithValue("@ip_addr", ipAddr);
return JsonConvert.DeserializeObject<List<TaskInfo>>( QueryTableData(sqlCmd));
}
catch (Exception e)
{
_logger.Error($"无法获取任务列表: {e.Message}");
throw;
}
finally { conn.Close(); }
}
public string GetTaskListByStateAndIp(string ipAddr, TaskState status)
{
string sql = $"SELECT * FROM t_tasks_info WHERE client_ip = @ip_addr AND `status` = @status";
MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
try
{
using MySqlCommand sqlCmd = new(sql, conn);
sqlCmd.Parameters.AddWithValue("@ip_addr", ipAddr);
sqlCmd.Parameters.AddWithValue ("@status", status);
return QueryTableData(sqlCmd);
}
catch (Exception e)
{
_logger.Error($"无法获取任务列表: {e.Message}");
throw;
}
finally { conn.Close(); }
}
public string GetTaskListByIP(string ipAddr)
{
string sql = $"SELECT * FROM t_tasks_info WHERE client_ip = @ip_addr";
@ -305,6 +346,24 @@ namespace iFileProxy.Helpers
return UpdateFieldsData("hash", taskInfo.TaskId, MasterHelper.GetFileHash(Path.Combine(_appConfig.DownloadOptions.SavePath, taskInfo.FileName), FileHashAlgorithm.MD5));
}
/// <summary>
/// 删除指定IP的任务信息
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
public int DeleteTaskInfoByIpAddr(string ipAddr)
{
try
{
string sql = $"DELETE FROM `t_tasks_info` WHERE `client_ip` = '{ipAddr}'";
return Query(sql,DbConfigName.iFileProxy);
}
catch (Exception)
{
return -1;
}
}
public void TryInitialDB()
{
string sql = """

View file

@ -1,14 +1,17 @@
using iFileProxy.Config;
using iFileProxy.Models;
using Serilog;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace iFileProxy.Helpers
{
public class MasterHelper
{
private readonly static Serilog.ILogger _logger = Log.Logger.ForContext<MasterHelper>();
/// <summary>
/// 检测链接是否为合法的网址格式
/// </summary>
@ -59,7 +62,10 @@ namespace iFileProxy.Helpers
{
clientIp = c.Connection.RemoteIpAddress?.ToString();
}
return clientIp;
IPAddress? ipAddress = null;
if (IPAddress.TryParse(clientIp, out ipAddress))
return ipAddress.ToString();
return null;
}
/// <summary>
@ -68,12 +74,44 @@ namespace iFileProxy.Helpers
/// <param name="fileName"></param>
/// <returns></returns>
public static bool CheckDownloadFileExists(string fileName)
{
if (File.Exists(Path.Combine(AppConfig.GetCurrConfig().DownloadOptions.SavePath,fileName)))
{
if (File.Exists(Path.Combine(AppConfig.GetCurrConfig().DownloadOptions.SavePath, fileName)))
{
_logger.Debug("文件存在: {fileName}", fileName);
return true;
}
_logger.Debug("文件不存在: {fileName}", fileName);
return false;
}
/// <summary>
/// 删除指定的已下载文件
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public static bool RemoveDownloadFile(string fileName)
{
var f = Path.Combine(AppConfig.GetCurrConfig().DownloadOptions.SavePath, fileName);
if (File.Exists(f))
{
try
{
File.Delete(f);
if (File.Exists(f + ".aria2"))
File.Delete(f + ".aria2"); // aria2c的临时文件
return true;
}
catch (Exception)
{
return false;
throw;
}
}
else
return false;
}
/// <summary>
/// 获取文件哈希
/// </summary>

View file

@ -1,4 +1,5 @@
using Newtonsoft.Json;
using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace iFileProxy.Models
@ -52,6 +53,10 @@ namespace iFileProxy.Models
[JsonProperty("queue_position")]
[JsonPropertyName("queue_position")]
public int QueuePosition { get; set; }
[System.Text.Json.Serialization.JsonIgnore]
[Newtonsoft.Json.JsonIgnore]
public Process Process { get; set; }
}
public class DbConfigName {

View file

@ -1,5 +1,6 @@
using iFileProxy.Config;
using iFileProxy.Helpers;
using iFileProxy.Middleware;
using iFileProxy.Services;
using Serilog;
@ -37,7 +38,7 @@ namespace iFileProxy
{
options.EnrichDiagnosticContext = (diagCtx, httpCtx) =>
{
diagCtx.Set("ClientIp", httpCtx.Connection.RemoteIpAddress?.ToString());
diagCtx.Set("ClientIp", MasterHelper.GetClientIPAddr(httpCtx));
diagCtx.Set("contentType", httpCtx.Request.ContentType);
diagCtx.Set("queryString", httpCtx.Request.QueryString);
};

View file

@ -218,6 +218,7 @@ namespace iFileProxy.Services
{
_runningTasks.Add(taskInfo.TaskId, taskInfo);
aria2c.Start();
_runningTasks[taskInfo.TaskId].Process = aria2c;
if (taskInfo.Status != TaskState.Running)
{
taskInfo.Status = TaskState.Running;
@ -331,6 +332,12 @@ namespace iFileProxy.Services
{
return new ServerTaskLoadInfo { Queuing = _pendingTasks.Count , Running = _runningTasks.Count};
}
public void TryKillTask(TaskInfo taskInfo)
{
}
//public bool DeleteTask(HttpContext c)
//{

View file

@ -0,0 +1,147 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>删除访客信息</title>
<link href="static/css/bootstarp/5/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<link href="static/css/custom/Common.css" rel="stylesheet" crossorigin="anonymous">
<style>
body {
padding: 20px;
background-color: #f8f9fa;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.btn-delete {
background-color: red;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
}
.btn-delete:disabled {
background-color: gray;
cursor: not-allowed;
}
.data-count {
margin-bottom: 10px;
font-size: 16px;
color: #333;
}
</style>
</head>
<body>
<div class="container text-center">
<div id="dataCount" class="data-count">正在加载数据...</div>
<button id="deleteVisitorInfo" class="btn-delete" disabled>删除我的访客信息</button>
</div>
<!-- 模态框 -->
<div class="modal fade" id="confirmDeleteModal" tabindex="-1" aria-labelledby="confirmDeleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="confirmDeleteModalLabel">确认删除</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
如果你删除你的信息,任务管理中的所有数据都会被删除,无法找回。你确定要继续吗?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-danger" id="confirmDeleteButton">确定</button>
</div>
</div>
</div>
</div>
<script src="static/js/bootstarp/5/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="static/js/jquery/2.1.4/jquery.min.js"></script>
<script src="static/js/custom/Common.js"></script>
<script>
$(document).ready(function () {
function showLoading() {
$('#deleteVisitorInfo').text('加载中...');
$('#deleteVisitorInfo').prop('disabled', true);
$('#dataCount').text('正在加载数据...');
}
function hideLoading() {
$('#deleteVisitorInfo').text('删除我的访客信息');
$('#deleteVisitorInfo').prop('disabled', false);
}
function fetchData() {
showLoading();
$.ajax({
type: "GET",
url: "/GetMyTasks",
dataType: "json",
success: function (response) {
hideLoading();
if (response.retcode === 0) {
const data = response.data;
const dataCount = data.length;
$('#dataCount').text(`服务器上有 ${dataCount} 条数据`);
if (dataCount === 0 || data.some(item => item.status === 1)) {
$('#deleteVisitorInfo').prop('disabled', true);
$('#dataCount').text((dataCount === 0) ? "服务器上没有存储任何关于你的数据" : "现在有正在运行中的任务, 你现在无法删除你的访客信息");
} else {
$('#deleteVisitorInfo').prop('disabled', false);
}
} else {
alert(response.message);
}
},
error: function (xhr, status, error) {
hideLoading();
console.error(error);
alert("请求失败,请重试");
}
});
}
function deleteVisitorInfo() {
showLoading();
$.ajax({
type: "DELETE",
url: "/VisitorManagement/DeleteInfo",
dataType: "json",
success: function (response) {
hideLoading();
if (response.retcode === 0) {
alert("删除成功");
fetchData(); // 刷新数据
} else {
alert(response.message);
}
},
error: function (xhr, status, error) {
hideLoading();
console.error(error);
alert("请求失败,请重试");
}
});
}
fetchData();
$('#deleteVisitorInfo').click(function () {
$('#confirmDeleteModal').modal('show');
});
$('#confirmDeleteButton').click(function () {
deleteVisitorInfo();
$('#confirmDeleteModal').modal('hide');
});
});
</script>
</body>
</html>

View file

@ -42,9 +42,9 @@
</div>
<br />
<hr />
<p class="more-content">运行中任务: <span id="running_count">0</span> 排队中任务:<span
<p class="more-content">运行中任务: <span id="running_count">0</span> 排队中任务: <span
id="queuing_count">0</span></p>
<p class="more-content"><a href="/query_download_task.html">查询文件下载任务状态</a> | 捐赠</p>
<p class="more-content"><a target="_blank" href="/query_download_task.html">查询文件下载任务状态</a> | 捐赠 | <a target="_blank" href="/delete_my_info.html">删除我的访客信息</a></p>
</div>
</div>
<p>Tips: <span id="tips">任务列表每个IP相互隔离,不必担心任务信息泄露</span></p>

View file

@ -149,7 +149,7 @@
</tbody>
</table>
<p>Tips: <span id="tips">任务列表每个IP相互隔离,不必担心任务信息泄露</span></p>
<p class="more-content"><a href="/index.html">返回主页</a> | 捐赠</p>
<p class="more-content"><a href="/index.html">返回主页</a> | 捐赠 | <a target="_blank" href="/delete_my_info.html">删除我的访客信息</p>
</div>
</div>

View file

@ -35,7 +35,7 @@ let tipsStr = [
"任务列表每个IP相互隔离,不必担心任务信息泄露。",
"哈希算法为MD5。",
"下载的文件不会永久存储在服务器上, 每隔一段时间服务器会自动清理那些文件, 并且任务状态会转变为 “已被清理”。",
"本站会收集一些访客信息用于访客数据分析, 若你不想被收集相关信息用于分析, 请<a href=\"#\">点我</a>自助删除已经存储在服务器上的信息(新加任务仍然会收集)。",
"本站会收集一些访客信息用于访客数据分析, 若你不想被收集相关信息用于分析, 请<a target=\"_blank\" href=\"delete_my_info.html\">点我</a>自助删除已经存储在服务器上的信息(新加任务仍然会收集)。",
"服务器同时下载的任务达到限制时候, 新的任务会被加入队列(即 排队), 若队列达到管理员设置的最大值则不再接受新任务, 直至队列空出余量。",
"本站为了节省成本和照顾其他访客的感受, 每个IP有一定的下载和任务提交配额, 达到限制后当天内不能再下载文件或者新增任务, 请勿浪费公共资源。"
];