Implement image upload and viewer in v2
This commit is contained in:
89
back/main.py
89
back/main.py
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user