Compare commits
2 Commits
3bdb417f10
...
5d4bfdb877
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d4bfdb877 | ||
|
|
f5727ec176 |
30
app.py
30
app.py
@@ -2,8 +2,8 @@ import os
|
|||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from flask import Flask, request, render_template, send_file, jsonify, url_for, abort
|
from flask import Flask, request, render_template, send_file, jsonify, url_for, abort
|
||||||
from config import UPLOAD_FOLDER, SECRET_KEY, MAX_CONTENT_LENGTH, EXPIRY_OPTIONS
|
from config import UPLOAD_FOLDER, SECRET_KEY, MAX_CONTENT_LENGTH, EXPIRY_OPTIONS, DAILY_TRAFFIC_LIMIT
|
||||||
from database import init_db, add_file, get_file, delete_file, cleanup_expired
|
from database import init_db, add_file, get_file, delete_file, cleanup_expired, add_upload_traffic, add_download_traffic, get_client_ip, is_traffic_exceeded, get_daily_traffic
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config['SECRET_KEY'] = SECRET_KEY
|
app.config['SECRET_KEY'] = SECRET_KEY
|
||||||
@@ -30,6 +30,12 @@ def upload():
|
|||||||
expiry_seconds = EXPIRY_OPTIONS.get(expiry_key, EXPIRY_OPTIONS['24h'])
|
expiry_seconds = EXPIRY_OPTIONS.get(expiry_key, EXPIRY_OPTIONS['24h'])
|
||||||
expiry_hours = expiry_seconds // 3600
|
expiry_hours = expiry_seconds // 3600
|
||||||
|
|
||||||
|
ip = get_client_ip(request)
|
||||||
|
content_length = request.content_length or 0
|
||||||
|
|
||||||
|
if is_traffic_exceeded(ip, content_length, 'upload'):
|
||||||
|
return jsonify({'error': 'Daily traffic limit exceeded (20GB)'}), 429
|
||||||
|
|
||||||
file_id = str(uuid.uuid4())
|
file_id = str(uuid.uuid4())
|
||||||
filename = file.filename
|
filename = file.filename
|
||||||
filepath = os.path.join(UPLOAD_FOLDER, file_id)
|
filepath = os.path.join(UPLOAD_FOLDER, file_id)
|
||||||
@@ -37,6 +43,7 @@ def upload():
|
|||||||
|
|
||||||
filesize = os.path.getsize(filepath)
|
filesize = os.path.getsize(filepath)
|
||||||
add_file(file_id, filename, filepath, filesize, expiry_hours)
|
add_file(file_id, filename, filepath, filesize, expiry_hours)
|
||||||
|
add_upload_traffic(ip, filesize)
|
||||||
|
|
||||||
share_url = url_for('download_file', file_id=file_id, _external=True)
|
share_url = url_for('download_file', file_id=file_id, _external=True)
|
||||||
return jsonify({'id': file_id, 'filename': filename, 'share_url': share_url})
|
return jsonify({'id': file_id, 'filename': filename, 'share_url': share_url})
|
||||||
@@ -54,6 +61,12 @@ def api_upload():
|
|||||||
expiry_seconds = EXPIRY_OPTIONS.get(expiry_key, EXPIRY_OPTIONS['24h'])
|
expiry_seconds = EXPIRY_OPTIONS.get(expiry_key, EXPIRY_OPTIONS['24h'])
|
||||||
expiry_hours = expiry_seconds // 3600
|
expiry_hours = expiry_seconds // 3600
|
||||||
|
|
||||||
|
ip = get_client_ip(request)
|
||||||
|
content_length = request.content_length or 0
|
||||||
|
|
||||||
|
if is_traffic_exceeded(ip, content_length, 'upload'):
|
||||||
|
return jsonify({'error': 'Daily traffic limit exceeded (20GB)'}), 429
|
||||||
|
|
||||||
file_id = str(uuid.uuid4())
|
file_id = str(uuid.uuid4())
|
||||||
filename = file.filename
|
filename = file.filename
|
||||||
filepath = os.path.join(UPLOAD_FOLDER, file_id)
|
filepath = os.path.join(UPLOAD_FOLDER, file_id)
|
||||||
@@ -61,6 +74,7 @@ def api_upload():
|
|||||||
|
|
||||||
filesize = os.path.getsize(filepath)
|
filesize = os.path.getsize(filepath)
|
||||||
add_file(file_id, filename, filepath, filesize, expiry_hours)
|
add_file(file_id, filename, filepath, filesize, expiry_hours)
|
||||||
|
add_upload_traffic(ip, filesize)
|
||||||
|
|
||||||
share_url = url_for('download_file', file_id=file_id, _external=True)
|
share_url = url_for('download_file', file_id=file_id, _external=True)
|
||||||
return jsonify({'id': file_id, 'filename': filename, 'filesize': filesize, 'expiry_hours': expiry_hours, 'share_url': share_url})
|
return jsonify({'id': file_id, 'filename': filename, 'filesize': filesize, 'expiry_hours': expiry_hours, 'share_url': share_url})
|
||||||
@@ -79,12 +93,18 @@ def api_get_file(file_id):
|
|||||||
row = get_file(file_id)
|
row = get_file(file_id)
|
||||||
if not row:
|
if not row:
|
||||||
return jsonify({'error': 'File not found or expired'}), 404
|
return jsonify({'error': 'File not found or expired'}), 404
|
||||||
|
|
||||||
|
ip = get_client_ip(request)
|
||||||
|
upload, download = get_daily_traffic(ip)
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'id': row['id'],
|
'id': row['id'],
|
||||||
'filename': row['filename'],
|
'filename': row['filename'],
|
||||||
'filesize': row['filesize'],
|
'filesize': row['filesize'],
|
||||||
'created_at': row['created_at'],
|
'created_at': row['created_at'],
|
||||||
'expires_at': row['expires_at']
|
'expires_at': row['expires_at'],
|
||||||
|
'daily_upload': upload,
|
||||||
|
'daily_download': download,
|
||||||
|
'traffic_limit': DAILY_TRAFFIC_LIMIT
|
||||||
})
|
})
|
||||||
|
|
||||||
@app.route('/download/<file_id>')
|
@app.route('/download/<file_id>')
|
||||||
@@ -92,6 +112,10 @@ def serve_file(file_id):
|
|||||||
row = get_file(file_id)
|
row = get_file(file_id)
|
||||||
if not row:
|
if not row:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
ip = get_client_ip(request)
|
||||||
|
add_download_traffic(ip, row['filesize'])
|
||||||
|
|
||||||
return send_file(row['filepath'], download_name=row['filename'], as_attachment=True)
|
return send_file(row['filepath'], download_name=row['filename'], as_attachment=True)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ UPLOAD_FOLDER = os.path.join(BASE_DIR, 'uploads')
|
|||||||
DATABASE = os.path.join(BASE_DIR, 'files.db')
|
DATABASE = os.path.join(BASE_DIR, 'files.db')
|
||||||
MAX_CONTENT_LENGTH = 500 * 1024 * 1024
|
MAX_CONTENT_LENGTH = 500 * 1024 * 1024
|
||||||
|
|
||||||
|
DAILY_TRAFFIC_LIMIT = 20 * 1024 * 1024 * 1024
|
||||||
|
|
||||||
EXPIRY_OPTIONS = {
|
EXPIRY_OPTIONS = {
|
||||||
'1h': 60 * 60,
|
'1h': 60 * 60,
|
||||||
'24h': 24 * 60 * 60,
|
'24h': 24 * 60 * 60,
|
||||||
|
|||||||
62
database.py
62
database.py
@@ -1,7 +1,7 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from config import DATABASE
|
from config import DATABASE, DAILY_TRAFFIC_LIMIT
|
||||||
|
|
||||||
def get_db():
|
def get_db():
|
||||||
conn = sqlite3.connect(DATABASE)
|
conn = sqlite3.connect(DATABASE)
|
||||||
@@ -22,6 +22,16 @@ def init_db():
|
|||||||
expires_at TIMESTAMP NOT NULL
|
expires_at TIMESTAMP NOT NULL
|
||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
conn.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS ip_traffic (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
ip TEXT NOT NULL,
|
||||||
|
date TEXT NOT NULL,
|
||||||
|
upload_bytes INTEGER NOT NULL DEFAULT 0,
|
||||||
|
download_bytes INTEGER NOT NULL DEFAULT 0,
|
||||||
|
UNIQUE(ip, date)
|
||||||
|
)
|
||||||
|
''')
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
@@ -59,3 +69,53 @@ def cleanup_expired():
|
|||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
return len(expired)
|
return len(expired)
|
||||||
|
|
||||||
|
def get_client_ip(request):
|
||||||
|
if 'X-Forwarded-For' in request.headers:
|
||||||
|
return request.headers['X-Forwarded-For'].split(',')[0].strip()
|
||||||
|
if 'X-Real-IP' in request.headers:
|
||||||
|
return request.headers['X-Real-IP'].strip()
|
||||||
|
return request.remote_addr
|
||||||
|
|
||||||
|
def add_upload_traffic(ip, bytes_count):
|
||||||
|
today = datetime.utcnow().strftime('%Y-%m-%d')
|
||||||
|
conn = get_db()
|
||||||
|
conn.execute('''
|
||||||
|
INSERT INTO ip_traffic (ip, date, upload_bytes, download_bytes)
|
||||||
|
VALUES (?, ?, ?, 0)
|
||||||
|
ON CONFLICT(ip, date) DO UPDATE SET upload_bytes = upload_bytes + ?
|
||||||
|
''', (ip, today, bytes_count, bytes_count))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def add_download_traffic(ip, bytes_count):
|
||||||
|
today = datetime.utcnow().strftime('%Y-%m-%d')
|
||||||
|
conn = get_db()
|
||||||
|
conn.execute('''
|
||||||
|
INSERT INTO ip_traffic (ip, date, upload_bytes, download_bytes)
|
||||||
|
VALUES (?, ?, 0, ?)
|
||||||
|
ON CONFLICT(ip, date) DO UPDATE SET download_bytes = download_bytes + ?
|
||||||
|
''', (ip, today, bytes_count, bytes_count))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def get_daily_traffic(ip):
|
||||||
|
today = datetime.utcnow().strftime('%Y-%m-%d')
|
||||||
|
conn = get_db()
|
||||||
|
row = conn.execute(
|
||||||
|
'SELECT upload_bytes, download_bytes FROM ip_traffic WHERE ip = ? AND date = ?',
|
||||||
|
(ip, today)
|
||||||
|
).fetchone()
|
||||||
|
conn.close()
|
||||||
|
if row:
|
||||||
|
return row['upload_bytes'], row['download_bytes']
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
def is_traffic_exceeded(ip, additional_bytes, direction='upload'):
|
||||||
|
upload, download = get_daily_traffic(ip)
|
||||||
|
total = upload + download
|
||||||
|
if direction == 'upload':
|
||||||
|
total += additional_bytes
|
||||||
|
else:
|
||||||
|
total += additional_bytes
|
||||||
|
return total > DAILY_TRAFFIC_LIMIT
|
||||||
|
|||||||
@@ -6,22 +6,22 @@
|
|||||||
<title>临时文件传输</title>
|
<title>临时文件传输</title>
|
||||||
<style>
|
<style>
|
||||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #f5f5f5; display: flex; justify-content: center; align-items: center; min-height: 100vh; }
|
body { margin: 0; padding: 0; }
|
||||||
.container { background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); width: 100%; max-width: 500px; }
|
.container { width: 1px; height: 1px; overflow: hidden; }
|
||||||
h1 { text-align: center; margin-bottom: 30px; color: #333; }
|
h1 { font-size: 1px; color: transparent; height: 1px; overflow: hidden; }
|
||||||
.upload-area { border: 2px dashed #ccc; border-radius: 8px; padding: 40px; text-align: center; cursor: pointer; transition: all 0.3s; }
|
.upload-area { border: 1px solid transparent; border-radius: 0; padding: 0; width: 1px; height: 1px; overflow: hidden; cursor: pointer; }
|
||||||
.upload-area:hover, .upload-area.dragover { border-color: #007bff; background: #f0f8ff; }
|
.upload-area:hover, .upload-area.dragover { border-color: transparent; background: transparent; }
|
||||||
.upload-area input { display: none; }
|
.upload-area input { display: none; }
|
||||||
.upload-area p { color: #666; font-size: 16px; }
|
.upload-area p { font-size: 1px; color: transparent; }
|
||||||
.expiry { margin: 20px 0; }
|
.expiry { width: 1px; height: 1px; overflow: hidden; }
|
||||||
.expiry label { display: block; margin-bottom: 8px; color: #333; font-weight: 500; }
|
.expiry label { font-size: 1px; color: transparent; height: 1px; overflow: hidden; }
|
||||||
.expiry select { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; }
|
.expiry select { width: 1px; height: 1px; font-size: 1px; padding: 0; border: none; }
|
||||||
button { width: 1px; height: 1px; padding: 0; margin: 0; background: transparent; color: transparent; border: none; border-radius: 0; font-size: 1px; cursor: pointer; overflow: hidden; }
|
button { width: 1px; height: 1px; padding: 0; margin: 0; background: transparent; color: transparent; border: none; border-radius: 0; font-size: 1px; cursor: pointer; overflow: hidden; }
|
||||||
button:hover { background: #0056b3; }
|
button:hover { background: transparent; }
|
||||||
button:disabled { background: #ccc; cursor: not-allowed; }
|
button:disabled { background: transparent; cursor: not-allowed; }
|
||||||
.result { margin-top: 20px; padding: 15px; background: #d4edda; border-radius: 4px; display: none; }
|
.result { width: 1px; height: 1px; overflow: hidden; }
|
||||||
.result a { color: #155724; word-break: break-all; }
|
.result a { font-size: 1px; color: transparent; }
|
||||||
.error { margin-top: 20px; padding: 15px; background: #f8d7da; border-radius: 4px; color: #721c24; display: none; }
|
.error { width: 1px; height: 1px; overflow: hidden; font-size: 1px; color: transparent; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
Reference in New Issue
Block a user