完善页面内容,支持路径匹配计数

This commit is contained in:
root 2024-11-20 22:13:27 +08:00
parent be8303cd63
commit 3323eef81e
9 changed files with 326 additions and 130 deletions

View file

@ -1 +1,12 @@
### 文件代理下载工具
### 文件代理下载工具
#### 功能列表
- [x] 代理下载文件
- [x] 浏览器下载文件
- [x] 文件名黑名单
- [x] 目标Host黑名单
- [x] 文件大小限制
- [x] 基于IP查询提交的任务状态
- [x] 基于IP和路由的请求次数限制
- [ ] 已经存在对应文件时候直接跳过代理下载 直接下载已经缓存的内容
- [ ] 捐赠

View file

@ -55,6 +55,7 @@ namespace iFileProxy.Config
public List<string> BlockedHost { get; set; } = [];
public List<string> BlockedFileName { get; set; } = [];
public List<string> BlockedClientIP { get; set; } = [];
public List<string> RoutesToTrack { get; set; } = [];
public int DailyRequestLimitPerIP { get; set; } = -1;
}

View file

@ -176,7 +176,7 @@ namespace iFileProxy.Helpers
public bool UpdateTaskStatus(TaskInfo taskInfo)
{
string sql = @"UPDATE t_tasks_info set `status` = @status WHERE `tid` = @tid";
string sql = @"UPDATE t_tasks_info set `status` = @status , update_time = Now() WHERE `tid` = @tid";
MySqlConnection conn = GetAndOpenDBConn("iFileProxy_Db");
try
{

View file

@ -1,5 +1,6 @@
namespace iFileProxy.Middleware
{
using iFileProxy.Config;
using iFileProxy.Helpers;
using iFileProxy.Models;
using Microsoft.AspNetCore.Http;
@ -13,6 +14,7 @@
private readonly RequestDelegate _next;
private readonly Dictionary<string, Dictionary<string, uint>> _IPAccessCountDict;
private readonly int _dailyRequestLimitPerIP;
private readonly AppConfig _appConfig = AppConfig.GetCurrConfig();
public IPAccessLimitMiddleware(RequestDelegate next, Dictionary<string, Dictionary<string, uint>> IPAccessCountDict, int dailyRequestLimitPerIP)
{
@ -23,6 +25,26 @@
public async Task InvokeAsync(HttpContext context)
{
// 获取需要跟踪的路由列表
var routesToTrack = _appConfig.SecurityOptions.RoutesToTrack;
// 检查当前请求的路径是否在需要跟踪的路由列表中
foreach (var p in routesToTrack)
{
if (context.Request.Path.ToString().StartsWith(p, StringComparison.OrdinalIgnoreCase))
{
// 如果匹配到需要跟踪的路由,继续处理请求
break;
}
}
// 如果没有匹配到需要跟踪的路由,直接调用下一个中间件
if (!routesToTrack.Any(p => context.Request.Path.ToString().StartsWith(p, StringComparison.OrdinalIgnoreCase)))
{
await _next(context);
return;
}
string dateStr = DateTime.Now.ToString("yyyy-MM-dd");
string ipStr = MasterHelper.GetClientIPAddr(context);

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

@ -37,7 +37,7 @@ namespace iFileProxy.Services
public TaskAddState AddTask(HttpContext c)
{
string? clientIp = MasterHelper.GetClientIPAddr(c);
string? t_url = c.Request.Query["url"].FirstOrDefault();
string? t_url = c.Request.Query["url"].FirstOrDefault() ?? c.Request.Form["url"].FirstOrDefault();
if (_appConfig.DownloadOptions.MaxParallelTasks != 0 && runningTasks.Count >= _appConfig.DownloadOptions.MaxParallelTasks)
return TaskAddState.ErrMaxParallelTasksLimit;

View file

@ -30,6 +30,10 @@
"BlockedClientIP": [ // 使IP
"127.0.0.1"
],
"DailyRequestLimitPerIP": 200 // IP
"DailyRequestLimitPerIP": 200, // IP
"RoutesToTrack": [ // IP
"/Download",
"/AddOfflineTask"
]
}
}

View file

@ -1,70 +1,19 @@
<!DOCTYPE html>
<html lang="zh-CN">
<html lang="cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>离线下载任务管理</title>
<title>Github文件下载加速</title>
<!-- 引入 Bootstrap 5 CSS -->
<link href="static/css/bootstarp/5/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<style>
body {
padding: 20px;
background-color: #f8f9fa;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
h1 {
.more-content {
text-align: center;
color: #343a40;
}
.table-responsive {
overflow-x: auto;
}
.table {
width: 100%;
max-width: 100%;
margin-bottom: 1rem;
background-color: transparent;
border-collapse: collapse;
border: 1px solid #dee2e6;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.table th, .table td {
padding: 15px;
vertical-align: middle;
border: 1px solid #dee2e6;
text-align: center;
max-width: 90px; /* 设置最大宽度 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.table thead th {
background-color: #007bff;
color: white;
font-weight: bold;
}
.table tbody tr:nth-child(even) {
background-color: #f1f3f5;
}
.table tbody tr:hover {
background-color: #e2e6ea;
}
@media (max-width: 768px) {
.table-responsive {
overflow-x: auto;
}
.table th, .table td {
padding: 10px;
font-size: 0.9em;
}
.hidden-on-small {
display: none !important;
}
}
a {
text-decoration: none;
}
@ -72,24 +21,36 @@
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5 class="card-title text-center">Github文件下载加速</h5>
<div class="container">
<h1 class="text-center">离线下载任务管理</h1>
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>文件名</th>
<th class="hidden-on-small">大小</th>
<th>提交时间</th>
<th>状态</th>
<th class="hidden-on-small">哈希</th>
</tr>
</thead>
<tbody id="taskTableBody">
<!-- 数据将在这里动态填充 -->
</tbody>
</table>
<!-- URL 输入框 -->
<div class="mb-3">
<label for="url_ipt" class="form-label">目标文件URL</label>
<input type="text" required="required" class="form-control" id="url_ipt"
placeholder="请输入要下载的文件链接">
</div>
<!-- 验证码 输入框 -->
<!-- <div class="mb-3 input-group">
<input type="text" class="form-control" id="vcode" placeholder="交易验证码">
<button type="button" id="send_vcode_btn" class="btn btn-primary">发送验证码</button>
</div> -->
<!-- 提交按钮 -->
<div class="d-grid gap-2">
<button type="button" id="sub_btn" class="btn btn-primary">提交</button>
</div>
<br />
<hr />
<p class="more-content"><a href="/query_download_task.html">查询文件下载任务状态</a> | 捐赠</p>
</div>
</div>
</div>
</div>
</div>
@ -97,57 +58,25 @@
<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>
let statusStrDict = new Map([
[0,"排队中"],
[1,"进行中"],
[2,"错误"],
[3,"已完成"]
])
data = [];
$.ajax({
type: "GET",
url: "/GetMyTasks",
dataType: "json",
success: function (response) {
if (response.retcode == 0) {
data = response.data;
populateTable(data);
}
else
alert(response.message);
},
error(xhr, status, error) {
alert(error);
}
});
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals || 2;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
function populateTable(data) {
const tableBody = document.getElementById('taskTableBody');
tableBody.innerHTML = ''; // 清空现有内容
data.forEach(item => {
const row = document.createElement('tr');
row.innerHTML = `
<td><a href="/Download/${item.tid}"> ${item.file_name}</td>
<td class="hidden-on-small">${formatBytes(item.size)}</td>
<td>${item.add_time}</td>
<td>${statusStrDict.get(item.status)}</td>
<td class="hidden-on-small">${item.hash || 'N/A'}</td>
`;
tableBody.appendChild(row);
$(document).ready(function () {
console.log("document ready")
sub_btn.addEventListener('click', function (param) {
$.ajax({
type: "POST",
url: "/AddOfflineTask",
data: {
url: url_ipt.value
},
dataType: "json",
success: function (response) {
if (response.retcode == 0)
alert("任务提交成功! 请稍后点击页面下方的 \"查询文件下载任务状态\" 超链接查询任务状态!");
else
alert(response.message);
}
});
});
}
});
</script>
</body>

View file

@ -0,0 +1,229 @@
<!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">
<style>
body {
padding: 20px;
background-color: #f8f9fa;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
h1 {
text-align: center;
color: #343a40;
}
.table-responsive {
overflow-x: auto;
}
.table {
width: 100%;
max-width: 100%;
margin-bottom: 1rem;
background-color: transparent;
border-collapse: collapse;
border: 1px solid #dee2e6;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.table th,
.table td {
padding: 15px;
vertical-align: middle;
border: 1px solid #dee2e6;
text-align: center;
max-width: 256px;
/* 设置最大宽度 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.table thead th {
background-color: #007bff;
color: white;
font-weight: bold;
}
.table tbody tr:nth-child(even) {
background-color: #f1f3f5;
}
.table tbody tr:hover {
background-color: #e2e6ea;
}
@media (max-width: 768px) {
.table-responsive {
overflow-x: auto;
}
.table th,
.table td {
padding: 10px;
font-size: 0.9em;
}
.hidden-on-small {
display: none !important;
}
}
a {
text-decoration: none;
}
.more-content {
text-align: center;
}
#loading-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.loading-spinner {
border: 16px solid #f3f3f3;
/* Light grey */
border-top: 16px solid #3498db;
/* Blue */
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<div class="container">
<h1 class="text-center">离线下载任务管理</h1>
<div class="table-responsive">
<div id="loading-mask" style="display: none;">
<div class="loading-spinner"></div>
</div>
<table id="data-table" class="table table-striped table-bordered">
<thead>
<tr>
<th>文件名</th>
<th class="hidden-on-small">大小</th>
<th>提交时间</th>
<th>状态</th>
<th class="hidden-on-small">哈希</th>
</tr>
</thead>
<tbody id="taskTableBody">
<!-- 数据将在这里动态填充 -->
</tbody>
</table>
<p class="more-content"><a href="/index.html">返回主页</a> | 捐赠</p>
</div>
</div>
<!-- 优先加载jq一类的三方库 -->
<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>
const $loadingMask = $('#loading-mask');
const $dataTable = $('#data-table');
// 显示遮罩层
function showLoadingMask() {
$loadingMask.show();
}
// 隐藏遮罩层
function hideLoadingMask() {
$loadingMask.hide();
}
showLoadingMask();
let statusStrDict = new Map([
[0, "排队中"],
[1, "进行中"],
[2, "错误"],
[3, "已完成"]
])
data = [];
$.ajax({
type: "GET",
url: "/GetMyTasks",
dataType: "json",
success: function (response) {
hideLoadingMask();
if (response.retcode == 0) {
data = response.data;
populateTable(data);
}
else
alert(response.message);
},
error(xhr, status, error) {
alert(error);
}
});
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals || 2;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
function populateTable(data) {
const tableBody = document.getElementById('taskTableBody');
tableBody.innerHTML = ''; // 清空现有内容
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 class="hidden-on-small">${formatBytes(item.size)}</td>
<td>${item.add_time}</td>
<td>${statusStrDict.get(item.status)}</td>
<td class="hidden-on-small">${item.hash || 'N/A'}</td>
`;
tableBody.appendChild(row);
});
}
</script>
</body>
</html>