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