From bade5e7b269df253e45ad4bed8eebccb41ba8812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E9=BE=99?= Date: Fri, 10 Apr 2026 06:36:41 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E8=AE=A4=E8=AF=81=E5=8A=9F=E8=83=BD=20(JWT)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/__pycache__/main.cpython-312.pyc | Bin 0 -> 15395 bytes backend/main.py | 218 ++++++++++++++++++++++- frontend/dev.log | 11 ++ 3 files changed, 220 insertions(+), 9 deletions(-) create mode 100644 backend/__pycache__/main.cpython-312.pyc create mode 100644 frontend/dev.log diff --git a/backend/__pycache__/main.cpython-312.pyc b/backend/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cff156704818fb950ba6d268d15e686b3f375a76 GIT binary patch literal 15395 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', methods=['PUT']) +@token_required def update_category(category_id): - """更新分类""" + """更新分类(需要登录)""" data = request.get_json() conn = get_db_connection() @@ -104,8 +271,9 @@ def update_category(category_id): conn.close() @app.route('/api/categories/', methods=['DELETE']) +@token_required def delete_category(category_id): - """删除分类""" + """删除分类(需要登录)""" conn = get_db_connection() try: with conn.cursor() as cursor: @@ -159,8 +327,9 @@ def get_websites(): conn.close() @app.route('/api/websites', methods=['POST']) +@token_required def create_website(): - """创建网站""" + """创建网站(需要登录)""" data = request.get_json() conn = get_db_connection() @@ -193,8 +362,9 @@ def create_website(): conn.close() @app.route('/api/websites/', methods=['PUT']) +@token_required def update_website(website_id): - """更新网站""" + """更新网站(需要登录)""" data = request.get_json() conn = get_db_connection() @@ -251,8 +421,9 @@ def update_website(website_id): conn.close() @app.route('/api/websites/', methods=['DELETE']) +@token_required def delete_website(website_id): - """删除网站""" + """删除网站(需要登录)""" conn = get_db_connection() try: with conn.cursor() as cursor: @@ -264,7 +435,7 @@ def delete_website(website_id): @app.route('/api/websites//click', methods=['POST']) def click_website(website_id): - """点击网站""" + """点击网站(无需登录)""" conn = get_db_connection() try: with conn.cursor() as cursor: @@ -287,5 +458,34 @@ def health_check(): def root(): return jsonify({'message': '导航网站 API', 'docs': '/api/health'}) +# ==================== 启动时创建 users 表 ==================== + +def init_db(): + """初始化数据库,创建 users 表(如果不存在)""" + try: + conn = get_db_connection() + try: + with conn.cursor() as cursor: + cursor.execute(""" + CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(50) UNIQUE NOT NULL, + password_hash VARCHAR(64) NOT NULL, + email VARCHAR(100), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + INDEX idx_username (username) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 + """) + conn.commit() + print("Users table initialized successfully") + finally: + conn.close() + except Exception as e: + print(f"Failed to initialize users table: {e}") + +# 应用启动时初始化 +with app.app_context(): + init_db() + if __name__ == '__main__': app.run(host='0.0.0.0', port=5003, debug=False) diff --git a/frontend/dev.log b/frontend/dev.log new file mode 100644 index 0000000..8642e46 --- /dev/null +++ b/frontend/dev.log @@ -0,0 +1,11 @@ + +> favorites-nav-frontend@1.0.0 dev +> vite --host 0.0.0.0 --port 5173 + + + VITE v5.4.21 ready in 131 ms + + ➜ Local: http://localhost:5173/ + ➜ Network: http://192.168.8.207:5173/ + ➜ Network: http://192.168.8.170:5173/ + ➜ Network: http://172.17.0.1:5173/