diff --git a/dnfs/index.html b/dnfs/index.html index 44262df..3b55581 100644 --- a/dnfs/index.html +++ b/dnfs/index.html @@ -9,7 +9,7 @@ - +
diff --git a/static/fsys.py b/static/fsys.py index d3f52a0..5048bb3 100644 --- a/static/fsys.py +++ b/static/fsys.py @@ -60,6 +60,9 @@ SSL_ENABLED = file_config['ssl'].get('enabled', 'false').lower() == 'true' SSL_CERT_FILE = file_config['ssl'].get('cert_file', 'cert.pem') SSL_KEY_FILE = file_config['ssl'].get('key_file', 'key.pem') +# 视图文件名:根路径与目录访问时固定返回此静态文件,由前端 JS 调用 /api/filelist 拉取数据 +VIEW_FILE = '__view.html' + # ==================== 视频文件支持 ==================== VIDEO_MIME_TYPES = { '.mp4': 'video/mp4', '.webm': 'video/webm', '.ogg': 'video/ogg', @@ -106,29 +109,30 @@ def get_file_list(): try: if not FILESYSTEM_ENABLED: return jsonify({'error': '文件系统未启用'}), 403 - + requested_path = request.args.get('path', '') full_path = safe_path_join(HTML_ROOT_DIR, requested_path) - + if full_path is None: return jsonify({'error': '无效的路径'}), 403 - + if not os.path.exists(full_path) or not os.path.isdir(full_path): return jsonify({'error': '目录不存在'}), 404 - + items = [] for item in os.listdir(full_path): - if item == '__pycache__': + # 过滤系统项目:以双下划线开头的文件和文件夹(如 __pycache__、__view.html、__icons 等) + if item.startswith('__'): continue - + item_path = os.path.join(full_path, item) item_stat = os.stat(item_path) is_dir = os.path.isdir(item_path) - + item_rel_path = os.path.join(requested_path, item) if requested_path else item - + file_type = 'dir' if is_dir else ('video' if is_video_file(item) else ('audio' if is_audio_file(item) else 'file')) - + items.append({ 'name': item, 'type': file_type, @@ -137,9 +141,9 @@ def get_file_list(): 'size_formatted': format_file_size(item_stat.st_size) if not is_dir else '', 'modified': datetime.datetime.fromtimestamp(item_stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S') }) - + items.sort(key=lambda x: (x['type'] != 'dir', x['name'].lower())) - + return jsonify({ 'success': True, 'path': requested_path or '/', @@ -155,33 +159,33 @@ def stream_video(file_path): try: if not FILESYSTEM_ENABLED: return jsonify({'error': '文件系统未启用'}), 403 - + full_path = safe_path_join(HTML_ROOT_DIR, file_path) if full_path is None or not os.path.exists(full_path) or not os.path.isfile(full_path): return jsonify({'error': '文件不存在'}), 404 - + if not is_video_file(full_path) and not is_audio_file(full_path): return jsonify({'error': '不支持的文件类型'}), 400 - + file_size = os.path.getsize(full_path) mime_type = get_media_mime_type(full_path) - + range_header = request.headers.get('Range', None) - + if range_header: byte_range = range_header.replace('bytes=', '').split('-') start = int(byte_range[0]) if byte_range[0] else 0 end = int(byte_range[1]) if len(byte_range) > 1 and byte_range[1] else file_size - 1 - + if start >= file_size: return jsonify({'error': '请求范围超出文件大小'}), 416 - + length = end - start + 1 - + with open(full_path, 'rb') as f: f.seek(start) data = f.read(length) - + response = Response(data, 206, mimetype=mime_type, direct_passthrough=True) response.headers.add('Content-Range', f'bytes {start}-{end}/{file_size}') response.headers.add('Accept-Ranges', 'bytes') @@ -194,342 +198,38 @@ def stream_video(file_path): logger.error(f"Video streaming error: {str(e)}") return jsonify({'error': str(e)}), 500 -# ==================== 极简版HTML页面(使用字符串拼接,避免花括号问题) ==================== -def get_html_template(path, breadcrumb_html): - """生成HTML页面""" - return f''' - - - - - 文件浏览 - - - -
-

📁 文件浏览

-
- 🏠 - 🔄 -
-
- - - - - -
- 📊 共 0 项 - 📁 0 - 📄 0 - 🎬 0 - 🎵 0 -
- -
-
- ⏳ -
加载中...
-
-
- -
- - - -''' - # ==================== 主路由 ==================== -@app.route('/', defaults={'path': ''}) +@app.route('/') +def serve_view(): + """根路径固定返回 __view.html 静态文件,由前端 JS 调用 /api/filelist 拉取文件系统信息""" + if not FILESYSTEM_ENABLED: + return "文件系统未启用", 403 + view_path = os.path.join(HTML_ROOT_DIR, VIEW_FILE) + if not os.path.exists(view_path): + return f"视图文件 {VIEW_FILE} 不存在", 500 + return send_file(view_path, mimetype='text/html') + @app.route('/') -def serve_file(path=''): +def serve_file(path): try: if not FILESYSTEM_ENABLED: return "文件系统未启用", 403 - + full_path = safe_path_join(HTML_ROOT_DIR, path) if full_path is None: return "无效的路径", 403 - + if not os.path.exists(full_path): return "路径不存在", 404 - + # 文件处理 if os.path.isfile(full_path): if request.args.get('download') == 'true': return send_file(full_path, as_attachment=True, download_name=os.path.basename(full_path)) - + if is_video_file(full_path) or is_audio_file(full_path): return send_file(full_path, mimetype=get_media_mime_type(full_path)) - + # 普通文件 with open(full_path, 'rb') as f: content = f.read() @@ -541,23 +241,13 @@ def serve_file(path=''): '.pdf': 'application/pdf', '.svg': 'image/svg+xml', '.ico': 'image/x-icon' } return Response(content, mimetype=mime_map.get(ext, 'application/octet-stream')) - - # 目录处理 - 生成面包屑 - breadcrumb = '' - if path: - parts = path.split('/') - current = '' - for i, part in enumerate(parts): - if i == len(parts) - 1: - breadcrumb += f'›{part}' - else: - current += part + '/' - breadcrumb += f'›{part}' - - # 使用函数生成HTML,避免字符串格式化问题 - html = get_html_template(path, breadcrumb) - return Response(html, mimetype='text/html') - + + # 目录处理:返回 __view.html,由前端 JS 根据 URL 路径调用 /api/filelist 拉取文件列表 + view_path = os.path.join(HTML_ROOT_DIR, VIEW_FILE) + if os.path.exists(view_path): + return send_file(view_path, mimetype='text/html') + return "目录无法浏览", 404 + except Exception as e: logger.error(f"Error: {str(e)}") import traceback @@ -575,7 +265,7 @@ def health_check(): if __name__ == '__main__': host = file_config['server']['host'] port = int(file_config['server']['port']) - + print("\n" + "="*50) print("📁 文件托管服务器 (极简版)") print("="*50) @@ -589,3 +279,4 @@ if __name__ == '__main__': app.run(host=host, port=port, ssl_context=(SSL_CERT_FILE, SSL_KEY_FILE), debug=False) else: app.run(host=host, port=port, debug=False) + diff --git a/static/public/__view.html b/static/public/__view.html new file mode 100644 index 0000000..e4bed51 --- /dev/null +++ b/static/public/__view.html @@ -0,0 +1,485 @@ + + + + + + 文件浏览 + + + +
+

📁 文件浏览

+ +
+ + + + + +
+ 📊 共 0 项 + 📁 0 + 📄 0 + 🎬 0 + 🎵 0 +
+ +
+
+ ⏳ +
加载中...
+
+
+ +
+ + + + diff --git a/static/public/css/qing.css b/static/public/css/qing.css new file mode 100644 index 0000000..3874be6 --- /dev/null +++ b/static/public/css/qing.css @@ -0,0 +1,71 @@ +code { + color: red; + background-color: rgb(254, 235, 238); + padding: 5px; + border-radius: 5px; +} +pre > code { + display: block; + color: white; + background-color: rgb(96, 96, 96); + padding: 5px; + border-radius: 5px; +} +blockquote { + margin-right: 0px; + background-color: #cfcfcf; +} + +/* by qing */ +body { + min-height: 100vh; + display: flex; + justify-content: center; + align-items: center; + background: #fffbf0; + font-family: system-ui, -apple-system, "Segoe UI", Roboto,sans-serif; + padding: 1rem; +} + +.box { + width: 90%; +} + +.card { + background-color: #eedeb0; + border: 1px solid #000000; + border-radius: 1.125rem; + padding: 1.5rem 1.25rem; + box-shadow: 0 6px 18px rgba(0, 0, 0, 0.04), 0 2px 4px rgba(0, 0, 0, 0.02); + /*max-width: 480px;*/ + width: 90%; + color: #4d3e2b; + transition: box-shadow 0.2s, border-color 0.2s; + overflow-wrap: break-word; +} + +.card:hover { + border-color: #c7b38a; + box-shadow: 0 10px 24px rgba(0,0,0,0.06), 0 2px 6px rgba(0,0,0,0.03); +} + +/* 移动端微调 */ +@media (max-width: 480px) { + body { + padding: 0.8rem; + } + + .card { + padding: 1.2rem, 1rem; + border-radius: 1rem; + border-width: 1px; + } +} + +/* 极小屏 (宽度 < 360px) */ +@media (max-width: 360px) { + .card { + padding: 1rem 0.8rem; + border-radius: 0.9rem; + } +} diff --git a/static/public/dnfs.html b/static/public/dnfs.html index c720319..09bfc29 100644 --- a/static/public/dnfs.html +++ b/static/public/dnfs.html @@ -5,66 +5,37 @@ DNFS · 树状笔记 - + -
- -

title

+
+
+ +

title

- -
- 发布时间:2026-1-1 + +
+ 发布时间:2026-1-1 +
+
+ 浏览:999 + 点赞:999 + 分享:999 +
+
hello
+
+
+
+

节点操作

+
+
+ + +
+ + +
-
- 浏览:999 - 点赞:999 - 分享:999 -
-
- -
hello
-
-
-

其他推荐

-
- -
-

节点操作

-
-
-
- - -
- - - + diff --git a/static/public/js/dnfs.js b/static/public/js/dnfs.js index aaba971..c233085 100644 --- a/static/public/js/dnfs.js +++ b/static/public/js/dnfs.js @@ -131,8 +131,7 @@ function load_notes(main_node) { recommend_list.replaceChildren(); //alert(stack.length) if (stack.length > 0) { /* 如果不在根节点 */ - var bak = document.createElement('a'); - //ttl.href = '/dnfs.html'; + var bak = document.createElement('center'); bak.addEventListener('click', function (event) { stack.pop(); var id = (stack.length > 1) ? stack[stack.length - 1] : 0; @@ -145,20 +144,22 @@ function load_notes(main_node) { } for (i in main_node) { item = main_node[i] - var ttl = document.createElement('a'); - //ttl.href ="/dnfs.html"; + var card = document.createElement('div'); + card.className = "card"; + var ttl = document.createElement('center'); ttl.addEventListener('click', function(event) { var id = Number(item.id) stack.push(id) getNode(token, id).then(load_notes) }); - var ttl1 =document.createElement('h1'); + var ttl1 =document.createElement('h3'); ttl1.textContent = item.title ttl.appendChild(ttl1) - recommend_list.appendChild(ttl) + card.appendChild(ttl) var ctt = document.createElement('p'); renderMarkdown(ctt, item.content); - recommend_list.appendChild(ctt) + card.appendChild(ctt) + recommend_list.appendChild(card) recommend_list.appendChild(document.createElement('br')) } }; diff --git a/static/public/login.html b/static/public/login.html index d995f3b..c4aef65 100644 --- a/static/public/login.html +++ b/static/public/login.html @@ -5,6 +5,7 @@ 登录 + -

用户登录

-
-
- - - -
- - - -
-
- - - - - - -
-
- - - -
-
- - - -
-
+
+

用户登录

+
+
+ + + +
+ + + +
+
+ + + + + + +
+
+ + + +
+
+ + + +
+
+
diff --git a/static/public/register.html b/static/public/register.html index 82157fa..e4ffc1f 100644 --- a/static/public/register.html +++ b/static/public/register.html @@ -4,6 +4,7 @@ 注册页面 + -

注册页面

-
-
- - - -
- - -
验证码: - - - +
+

注册页面

+ +
+ + + +
+ + +
验证码: + + + +
+ +
+ + + + +
- -
- - - - - -
-
- - - -
- +
+ + + +
+ +