Implement image upload and viewer in v2

This commit is contained in:
LeonspaceX
2026-01-26 21:41:28 +08:00
parent 07bf09949f
commit 0cda40060e
9 changed files with 499 additions and 17 deletions

View File

@@ -1,6 +1,6 @@
# 这里是Sycamore whisper的后端代码喵
# 但愿比V1写的好喵
from flask import Flask, jsonify, request, abort
from flask import Flask, jsonify, request, abort, send_from_directory
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
import os
@@ -14,13 +14,18 @@ CORS(app, resources={r"/api/*": {"origins": "*"}})
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DB_PATH = os.path.join(BASE_DIR, 'data', 'db.sqlite')
IMG_DIR = os.path.join(BASE_DIR, 'data', 'img')
APP_MAX_CONTENT_LENGTH_MB = 10.0
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{DB_PATH}'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['MAX_CONTENT_LENGTH'] = APP_MAX_CONTENT_LENGTH_MB * 1024 * 1024
db = SQLAlchemy(app)
# 全局配置变量
NEED_AUDIT = True
FILE_SIZE_LIMIT_MB = 10.0
FILE_FORMATS = ["png", "jpg", "jpeg", "gif", "webp"]
# --- 定义数据库结构 ---
class SiteSettings(db.Model):
@@ -34,6 +39,8 @@ class SiteSettings(db.Model):
enable_notice = db.Column(db.Boolean, default=False)
need_audit = db.Column(db.Boolean, default=True)
about = db.Column(db.Text)
file_size_limit = db.Column(db.Float) # MB
file_formats = db.Column(db.Text) # JSON list
class Identity(db.Model):
__tablename__ = 'identity'
@@ -74,21 +81,48 @@ class DenyWord(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
word = db.Column(db.String(255), unique=True, nullable=False)
class ImgFile(db.Model):
__tablename__ = 'img_files'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
path = db.Column(db.String(255), nullable=False)
name = db.Column(db.String(255), nullable=True)
identity_token = db.Column(db.String(36), nullable=True)
# 初始化数据库函数
def init_db():
if not os.path.exists('./data'):
os.makedirs('./data')
if not os.path.exists(IMG_DIR):
os.makedirs(IMG_DIR)
with app.app_context():
db.create_all()
def load_config():
global NEED_AUDIT
global NEED_AUDIT, FILE_SIZE_LIMIT_MB, FILE_FORMATS, APP_MAX_CONTENT_LENGTH_MB
with app.app_context():
try:
settings = SiteSettings.query.first()
if settings:
if hasattr(settings, 'need_audit') and settings.need_audit is not None:
NEED_AUDIT = settings.need_audit
if getattr(settings, 'file_size_limit', None) is not None:
try:
FILE_SIZE_LIMIT_MB = float(settings.file_size_limit)
APP_MAX_CONTENT_LENGTH_MB = FILE_SIZE_LIMIT_MB
app.config['MAX_CONTENT_LENGTH'] = APP_MAX_CONTENT_LENGTH_MB * 1024 * 1024
except Exception:
pass
if getattr(settings, 'file_formats', None):
try:
raw = settings.file_formats
if isinstance(raw, str):
parsed = json.loads(raw)
else:
parsed = raw
if isinstance(parsed, list):
FILE_FORMATS = [str(x).strip().lstrip('.').lower() for x in parsed if str(x).strip()]
except Exception:
pass
except Exception as e:
print(f"Warning: Failed to load settings: {e}")
global DENY_WORDS_CACHE
@@ -321,6 +355,57 @@ def get_comments():
except Exception as e:
return jsonify({"code": 2003, "data": str(e)})
@app.route('/api/upload_pic', methods=['POST'])
def upload_pic():
try:
if 'file' not in request.files:
return jsonify({"code": 2000, "data": "参数错误"})
file = request.files['file']
if file.filename == '':
return jsonify({"code": 2000, "data": "参数错误"})
file.seek(0, os.SEEK_END)
file_length = file.tell()
file.seek(0)
if FILE_SIZE_LIMIT_MB is not None:
limit_bytes = float(FILE_SIZE_LIMIT_MB) * 1024 * 1024
if file_length > limit_bytes:
return jsonify({"code": 2006, "data": "上传的图片超出限制大小"})
ext = os.path.splitext(file.filename)[1].lstrip('.').lower()
if not ext or (FILE_FORMATS and ext not in FILE_FORMATS):
return jsonify({"code": 2007, "data": "上传的文件类型不支持"})
filename = f"{uuid.uuid4().hex}.{ext}"
filepath = os.path.join(IMG_DIR, filename)
file.save(filepath)
identity_token = request.form.get('identity_token') or None
name = file.filename or None
db.session.add(ImgFile(path=filename, name=name, identity_token=identity_token))
db.session.commit()
return jsonify({"code": 1001, "data": f"/api/files/{filename}"})
except Exception as e:
return jsonify({"code": 2003, "data": str(e)})
@app.route('/api/files/<path:file_name>', methods=['GET'])
def serve_file(file_name):
return send_from_directory(IMG_DIR, file_name)
@app.route('/api/file_name', methods=['GET'])
def get_file_name():
try:
path = request.args.get("path")
if not path:
return jsonify({"code": 2000, "data": "参数错误"})
record = ImgFile.query.filter_by(path=path).first()
if not record:
return jsonify({"code": 2002, "data": "数据不存在"})
return jsonify({"code": 1000, "data": record.name or ""})
except Exception as e:
return jsonify({"code": 2003, "data": str(e)})
@app.route('/api/up', methods=['POST'])
def upvote():
try: