Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
Binary file added backend/__pycache__/config.cpython-314.pyc
Binary file not shown.
Binary file added backend/__pycache__/models.cpython-314.pyc
Binary file not shown.
Binary file added backend/__pycache__/utils.cpython-314.pyc
Binary file not shown.
45 changes: 45 additions & 0 deletions backend/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from flask import Flask, jsonify
from flask_cors import CORS
from config import Config
from models import db
from routes.auth import auth_bp
from routes.posts import posts_bp
from routes.comments import comments_bp
from routes.reports import reports_bp
from routes.admin import admin_bp

def create_app():
app = Flask(__name__)
app.config.from_object(Config)

CORS(app, resources={r"/api/*": {"origins": "*"}})

db.init_app(app)

with app.app_context():
db.create_all()

app.register_blueprint(auth_bp, url_prefix='/api/auth')
app.register_blueprint(posts_bp, url_prefix='/api/posts')
app.register_blueprint(comments_bp, url_prefix='/api/comments')
app.register_blueprint(reports_bp, url_prefix='/api/reports')
app.register_blueprint(admin_bp, url_prefix='/api/admin')

@app.route('/api/health')
def health():
return jsonify({'status': 'ok', 'message': '服务运行中'})

@app.errorhandler(404)
def not_found_error(error):
return jsonify({'error': '资源未找到'}), 404

@app.errorhandler(500)
def internal_error(error):
return jsonify({'error': '服务器内部错误'}), 500

return app

app = create_app()

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001, debug=True)
39 changes: 39 additions & 0 deletions backend/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import os
from datetime import timedelta

basedir = os.path.abspath(os.path.dirname(__file__))

class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'xinqing-emotion-secret-key-2024'

SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'xinqing.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False

CORS_HEADERS = 'Content-Type'

POSTS_PER_PAGE = 20

ADMIN_USERNAME = 'admin'
ADMIN_PASSWORD = 'admin123'

EMOTION_CATEGORIES = [
{'id': 'happy', 'name': '开心', 'color': '#FFD700'},
{'id': 'sad', 'name': '难过', 'color': '#4169E1'},
{'id': 'angry', 'name': '生气', 'color': '#FF4500'},
{'id': 'anxious', 'name': '焦虑', 'color': '#9370DB'},
{'id': 'confused', 'name': '困惑', 'color': '#708090'},
{'id': 'grateful', 'name': '感恩', 'color': '#32CD32'},
{'id': 'lonely', 'name': '孤独', 'color': '#696969'},
{'id': 'hopeful', 'name': '期待', 'color': '#FF69B4'},
{'id': 'tired', 'name': '疲惫', 'color': '#8B7355'},
{'id': 'other', 'name': '其他', 'color': '#808080'}
]

REPORT_REASONS = [
'spam',
'violence',
'hate_speech',
'explicit_content',
'other'
]
121 changes: 121 additions & 0 deletions backend/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

db = SQLAlchemy()

class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
anonymous_id = db.Column(db.String(64), unique=True, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
last_active = db.Column(db.DateTime, default=datetime.utcnow)

posts = db.relationship('Post', backref='author', lazy='dynamic',
foreign_keys='Post.user_id')
comments = db.relationship('Comment', backref='commenter', lazy='dynamic',
foreign_keys='Comment.user_id')
favorites = db.relationship('Favorite', backref='user', lazy='dynamic')
reports_made = db.relationship('Report', backref='reporter', lazy='dynamic',
foreign_keys='Report.user_id')
is_admin = db.Column(db.Boolean, default=False)

class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
content = db.Column(db.Text, nullable=False)
emotion_category = db.Column(db.String(32), nullable=False)
is_anonymous = db.Column(db.Boolean, default=True)

status = db.Column(db.String(20), default='pending')
is_removed = db.Column(db.Boolean, default=False)
removed_reason = db.Column(db.Text, nullable=True)
removed_at = db.Column(db.DateTime, nullable=True)
removed_by = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True)

created_at = db.Column(db.DateTime, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

comments = db.relationship('Comment', backref='post', lazy='dynamic',
foreign_keys='Comment.post_id',
order_by='Comment.created_at.asc()')
favorites = db.relationship('Favorite', backref='post', lazy='dynamic')
reports = db.relationship('Report', backref='post', lazy='dynamic',
foreign_keys='Report.post_id')

def to_dict(self, include_comments=False, current_user_id=None):
data = {
'id': self.id,
'content': self.content,
'emotion_category': self.emotion_category,
'is_anonymous': self.is_anonymous,
'status': self.status,
'is_removed': self.is_removed,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None,
'comments_count': self.comments.count(),
'favorites_count': self.favorites.count()
}

if include_comments and current_user_id:
visible_comments = [
c for c in self.comments.all()
if c.user_id == current_user_id or c.post.user_id == current_user_id
]
data['comments'] = [c.to_dict() for c in visible_comments]

return data

class Comment(db.Model):
id = db.Column(db.Integer, primary_key=True)
post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
content = db.Column(db.Text, nullable=False)
comment_type = db.Column(db.String(20), default='comment')

is_anonymous = db.Column(db.Boolean, default=True)
is_removed = db.Column(db.Boolean, default=False)

created_at = db.Column(db.DateTime, default=datetime.utcnow)

def to_dict(self):
return {
'id': self.id,
'post_id': self.post_id,
'content': self.content,
'comment_type': self.comment_type,
'is_anonymous': self.is_anonymous,
'is_removed': self.is_removed,
'created_at': self.created_at.isoformat() if self.created_at else None
}

class Favorite(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)

__table_args__ = (db.UniqueConstraint('user_id', 'post_id'),)

class Report(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
reason = db.Column(db.String(50), nullable=False)
description = db.Column(db.Text, nullable=True)

status = db.Column(db.String(20), default='pending')
handled_at = db.Column(db.DateTime, nullable=True)
handled_by = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True)
handling_notes = db.Column(db.Text, nullable=True)

created_at = db.Column(db.DateTime, default=datetime.utcnow)

def to_dict(self):
return {
'id': self.id,
'post_id': self.post_id,
'reason': self.reason,
'description': self.description,
'status': self.status,
'created_at': self.created_at.isoformat() if self.created_at else None,
'handled_at': self.handled_at.isoformat() if self.handled_at else None
}
5 changes: 5 additions & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Flask==3.0.0
Flask-CORS==4.0.0
Flask-SQLAlchemy==3.1.1
Werkzeug==3.0.1
python-dateutil==2.8.2
Empty file added backend/routes/__init__.py
Empty file.
Binary file added backend/routes/__pycache__/__init__.cpython-314.pyc
Binary file not shown.
Binary file not shown.
Binary file added backend/routes/__pycache__/auth.cpython-314.pyc
Binary file not shown.
Binary file not shown.
Binary file added backend/routes/__pycache__/posts.cpython-314.pyc
Binary file not shown.
Binary file not shown.
Loading