auto backup: 2026-06-01 08:10:02
This commit is contained in:
parent
734068fef9
commit
3da7fc67ac
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,8 @@
|
||||
"""轻量 CORS 代理服务 - 转发推送请求到目标 API + 单组件重新生成"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import cgi
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
@ -13,6 +15,102 @@ from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
||||
UPLOAD_DIR = os.path.expanduser("~/.openclaw/workspace-xiaoban/tmp/uploads")
|
||||
|
||||
UPLOAD_HTML = r"""<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>上传文件到大麦</title>
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#0f172a;color:#e2e8f0;min-height:100vh;padding:20px}
|
||||
.container{max-width:600px;margin:0 auto;text-align:center}
|
||||
h1{font-size:24px;margin:16px 0 4px}
|
||||
.sub{color:#94a3b8;margin-bottom:24px;font-size:14px}
|
||||
.upload-box{background:#1e293b;border:2px dashed #475569;border-radius:16px;padding:40px 20px;margin-bottom:16px}
|
||||
.upload-box p{font-size:15px;color:#94a3b8;margin-bottom:16px}
|
||||
.btn{display:inline-block;background:#6366f1;color:#fff;border:none;padding:12px 32px;border-radius:8px;font-size:16px;cursor:pointer;transition:background .2s}
|
||||
.btn:active{background:#4f46e5}
|
||||
.btn-wrap{position:relative;overflow:hidden}
|
||||
.btn-wrap input[type=file]{position:absolute;left:0;top:0;width:100%;height:100%;opacity:0;cursor:pointer}
|
||||
#progress{display:none;margin:12px 0}
|
||||
.bar-bg{background:#334155;border-radius:8px;height:8px;overflow:hidden}
|
||||
.bar{background:linear-gradient(90deg,#6366f1,#a855f7);height:100%;width:0;transition:width .3s}
|
||||
#status{font-size:13px;color:#94a3b8;margin-bottom:8px}
|
||||
#result{margin:12px 0;font-size:14px;line-height:1.8}
|
||||
.success{color:#4ade80}
|
||||
.error{color:#f87171}
|
||||
.recent{margin-top:24px;text-align:left;max-height:180px;overflow-y:auto}
|
||||
.recent h3{font-size:14px;color:#64748b;margin-bottom:8px}
|
||||
.recent li{font-size:12px;color:#94a3b8;padding:4px 0;border-bottom:1px solid #1e293b}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>📤 上传文件到大麦</h1>
|
||||
<p class="sub">点击下方按钮选择文件,支持多选</p>
|
||||
<div class="upload-box">
|
||||
<p>📁 选择视频、压缩包等文件上传</p>
|
||||
<div class="btn-wrap">
|
||||
<span class="btn">选择文件</span>
|
||||
<input type="file" id="file" multiple>
|
||||
</div>
|
||||
</div>
|
||||
<div id="progress">
|
||||
<p id="status">上传中...</p>
|
||||
<div class="bar-bg"><div class="bar" id="bar"></div></div>
|
||||
</div>
|
||||
<div id="result"></div>
|
||||
<div class="recent" id="recent"><h3>📋 最近上传</h3><ul id="filelist"></ul></div>
|
||||
</div>
|
||||
<script>
|
||||
document.getElementById('file').addEventListener('change', function(){
|
||||
if(this.files.length) upload(this.files);
|
||||
});
|
||||
|
||||
async function upload(files){
|
||||
var progress = document.getElementById('progress');
|
||||
var bar = document.getElementById('bar');
|
||||
var status = document.getElementById('status');
|
||||
var result = document.getElementById('result');
|
||||
progress.style.display='block'; bar.style.width='0'; result.innerHTML='';
|
||||
|
||||
var ok=0, fail=0;
|
||||
for(var i=0;i<files.length;i++){
|
||||
var f = files[i];
|
||||
status.textContent = '正在上传: ' + f.name + ' (' + (f.size/1024/1024).toFixed(1) + 'MB)...';
|
||||
bar.style.width = ((i/files.length)*100)+'%';
|
||||
var fd = new FormData(); fd.append('file', f);
|
||||
try{
|
||||
var r = await fetch('/upload', {method:'POST', body:fd});
|
||||
var t = await r.json();
|
||||
if(r.ok){ ok++; } else { fail++; result.innerHTML+='<div class="error">❌ '+f.name+': '+(t.error||'失败')+'</div>'; }
|
||||
}catch(e){ fail++; result.innerHTML+='<div class="error">❌ '+f.name+': 网络错误</div>'; }
|
||||
}
|
||||
bar.style.width='100%';
|
||||
if(fail===0) result.innerHTML+='<div class="success">✅ 全部 '+ok+' 个文件上传成功!</div>';
|
||||
else result.innerHTML+='<div class="success">✅ '+ok+' 个成功</div><div class="error">❌ '+fail+' 个失败</div>';
|
||||
status.textContent='上传完成!';
|
||||
loadFiles();
|
||||
setTimeout(function(){ progress.style.display='none'; result.innerHTML=''; }, 5000);
|
||||
}
|
||||
|
||||
async function loadFiles(){
|
||||
try{
|
||||
var r = await fetch('/upload/files');
|
||||
var files = await r.json();
|
||||
document.getElementById('filelist').innerHTML = files.map(function(f){
|
||||
return '<li>📄 '+f.name+' <span style="color:#64748b">'+(f.size/1024/1024).toFixed(1)+'MB · '+f.time+'</span></li>';
|
||||
}).join('');
|
||||
}catch(e){}
|
||||
}
|
||||
loadFiles();
|
||||
</script>
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
SCRIPTS_DIR = Path(__file__).resolve().parent
|
||||
LOG_DIR = PROJECT_ROOT / 'logs'
|
||||
@ -131,6 +229,10 @@ class ProxyHandler(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
if self.path == '/health':
|
||||
self._send_json(200, {'status': 'ok'})
|
||||
elif self.path == '/upload':
|
||||
self._serve_upload_page()
|
||||
elif self.path == '/upload/files':
|
||||
self._list_uploaded_files()
|
||||
else:
|
||||
self.send_error(404)
|
||||
|
||||
@ -139,6 +241,8 @@ class ProxyHandler(BaseHTTPRequestHandler):
|
||||
self._handle_push()
|
||||
elif self.path == '/api/regenerate':
|
||||
self._handle_regenerate()
|
||||
elif self.path == '/upload':
|
||||
self._handle_upload()
|
||||
else:
|
||||
self.send_error(404)
|
||||
|
||||
@ -221,6 +325,61 @@ class ProxyHandler(BaseHTTPRequestHandler):
|
||||
self._send_json(code, result)
|
||||
logger.info(f'[{client_ip}] Regenerate done: status={code}')
|
||||
|
||||
def _serve_upload_page(self):
|
||||
html = UPLOAD_HTML.encode('utf-8')
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'text/html; charset=utf-8')
|
||||
self.send_header('Content-Length', str(len(html)))
|
||||
self.end_headers()
|
||||
self.wfile.write(html)
|
||||
|
||||
def _list_uploaded_files(self):
|
||||
import time as _time
|
||||
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
||||
files = []
|
||||
for name in sorted(os.listdir(UPLOAD_DIR), reverse=True):
|
||||
path = os.path.join(UPLOAD_DIR, name)
|
||||
if os.path.isfile(path):
|
||||
st = os.stat(path)
|
||||
files.append({
|
||||
'name': name,
|
||||
'size': st.st_size,
|
||||
'time': _time.strftime('%m-%d %H:%M', _time.localtime(st.st_mtime))
|
||||
})
|
||||
self._send_json(200, files[:20])
|
||||
|
||||
def _handle_upload(self):
|
||||
content_type = self.headers.get('Content-Type', '')
|
||||
if 'multipart/form-data' not in content_type:
|
||||
self._send_json(400, {'error': '需要 multipart/form-data'})
|
||||
return
|
||||
|
||||
form = cgi.FieldStorage(
|
||||
fp=self.rfile,
|
||||
headers=self.headers,
|
||||
environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': content_type}
|
||||
)
|
||||
|
||||
file_item = form['file']
|
||||
if file_item is None or not file_item.filename:
|
||||
self._send_json(400, {'error': '未选择文件'})
|
||||
return
|
||||
|
||||
safe_name = os.path.basename(file_item.filename)
|
||||
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
||||
path = os.path.join(UPLOAD_DIR, safe_name)
|
||||
|
||||
base, ext = os.path.splitext(safe_name)
|
||||
counter = 1
|
||||
while os.path.exists(path):
|
||||
path = os.path.join(UPLOAD_DIR, f"{base}_{counter}{ext}")
|
||||
counter += 1
|
||||
|
||||
with open(path, 'wb') as f:
|
||||
f.write(file_item.file.read())
|
||||
|
||||
self._send_json(200, {'ok': True, 'filename': os.path.basename(path)})
|
||||
|
||||
def log_message(self, format, *args):
|
||||
logger.debug(f'{self.client_address[0]} - {format % args}')
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user