From 0ba75a92c4f4f0dcf56828724bbd2846b52e2aea Mon Sep 17 00:00:00 2001 From: Fantasy lee <129943055+Fantasylee21@users.noreply.github.com> Date: Sat, 12 Apr 2025 18:07:06 +0800 Subject: [PATCH] =?UTF-8?q?[feat]:=20=E5=AE=9E=E7=8E=B0=E7=AC=94=E8=AE=B0?= =?UTF-8?q?=E7=9A=84=E5=A2=9E=E5=88=A0=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 2 + ...44\270\272text\345\255\230\345\202\250.py" | 38 +++++++++++++++++++ app/api/v1/endpoints/auth.py | 28 +++----------- app/api/v1/endpoints/note.py | 27 +++++++++++++ app/core/config.py | 7 +++- app/curd/note.py | 25 ++++++++++++ app/main.py | 5 ++- app/models/model.py | 4 +- app/routers/router.py | 12 ++++++ app/schemas/note.py | 13 +++++++ app/utils/__init__.py | 0 app/utils/get_db.py | 8 ++++ app/utils/redis.py | 23 +++++++++++ 13 files changed, 164 insertions(+), 28 deletions(-) create mode 100644 .env create mode 100644 "alembic/versions/e04ed2119f01_\345\260\206\347\254\224\350\256\260\345\206\205\345\256\271\345\217\230\344\270\272text\345\255\230\345\202\250.py" create mode 100644 app/api/v1/endpoints/note.py create mode 100644 app/curd/note.py create mode 100644 app/routers/router.py create mode 100644 app/schemas/note.py create mode 100644 app/utils/__init__.py create mode 100644 app/utils/get_db.py create mode 100644 app/utils/redis.py diff --git a/.env b/.env new file mode 100644 index 0000000..0b92af2 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +SECRET_KEY=bN3hZ6LbHG7nH9YXWULCr-crcS3GAaRELbNBdAyHBuiHH5TRctd0Zbd6OuLRHHa4Fbs +SENDER_PASSWORD=TXVU2unpCAE2EtEX \ No newline at end of file diff --git "a/alembic/versions/e04ed2119f01_\345\260\206\347\254\224\350\256\260\345\206\205\345\256\271\345\217\230\344\270\272text\345\255\230\345\202\250.py" "b/alembic/versions/e04ed2119f01_\345\260\206\347\254\224\350\256\260\345\206\205\345\256\271\345\217\230\344\270\272text\345\255\230\345\202\250.py" new file mode 100644 index 0000000..5b55eac --- /dev/null +++ "b/alembic/versions/e04ed2119f01_\345\260\206\347\254\224\350\256\260\345\206\205\345\256\271\345\217\230\344\270\272text\345\255\230\345\202\250.py" @@ -0,0 +1,38 @@ +"""将笔记内容变为text存储 + +Revision ID: e04ed2119f01 +Revises: 9af9d4a35bef +Create Date: 2025-04-12 15:42:31.823878 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision: str = 'e04ed2119f01' +down_revision: Union[str, None] = '9af9d4a35bef' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('notes', 'content', + existing_type=mysql.VARCHAR(length=255), + type_=sa.Text(), + existing_nullable=True) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('notes', 'content', + existing_type=sa.Text(), + type_=mysql.VARCHAR(length=255), + existing_nullable=True) + # ### end Alembic commands ### diff --git a/app/api/v1/endpoints/auth.py b/app/api/v1/endpoints/auth.py index af7ee23..b32394b 100644 --- a/app/api/v1/endpoints/auth.py +++ b/app/api/v1/endpoints/auth.py @@ -8,39 +8,23 @@ from email.header import Header import random import time -import redis from email.utils import formataddr -from app.db.session import SessionLocal -from app.models.model import User from app.schemas.auth import UserCreate, UserLogin, UserSendCode from app.core.config import settings from app.curd.user import get_user_by_email, create_user +from app.utils.get_db import get_db +from app.utils.redis import get_redis_client router = APIRouter() -pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") # 使用 bcrypt 加密算法 SECRET_KEY = settings.SECRET_KEY ALGORITHM = settings.ALGORITHM ACCESS_TOKEN_EXPIRE_MINUTES = settings.ACCESS_TOKEN_EXPIRE_MINUTES # 配置 Redis 连接 -while True: - try: - print("Connecting to Redis...") - redis_client = redis.StrictRedis(host='localhost', port=6379, db=0) - redis_client.ping() - break - except redis.ConnectionError: - print("Redis connection failed, retrying...") - time.sleep(1) - -def get_db(): - db = SessionLocal() - try: - yield db - finally: - db.close() +redis_client = get_redis_client() def create_access_token(data: dict, expires_delta: timedelta = None): to_encode = data.copy() @@ -114,8 +98,8 @@ def send_code(user_send_code : UserSendCode, db: Session = Depends(get_db)): server.sendmail(sender_email, [user_send_code.email], message.as_string()) # 将验证码和发送时间存储到 Redis,设置 5 分钟过期时间 - redis_client.setex(f"email:{user_send_code.email}:code", settings.ACCESS_TOKEN_EXPIRE_MINUTES, code) - redis_client.setex(f"email:{user_send_code.email}:time", settings.ACCESS_TOKEN_EXPIRE_MINUTES, int(time.time())) + redis_client.setex(f"email:{user_send_code.email}:code", ACCESS_TOKEN_EXPIRE_MINUTES, code) + redis_client.setex(f"email:{user_send_code.email}:time", ACCESS_TOKEN_EXPIRE_MINUTES, int(time.time())) return {"msg": "Verification code sent"} diff --git a/app/api/v1/endpoints/note.py b/app/api/v1/endpoints/note.py new file mode 100644 index 0000000..b5dc8ae --- /dev/null +++ b/app/api/v1/endpoints/note.py @@ -0,0 +1,27 @@ +from fastapi import APIRouter, HTTPException, Depends +from sqlalchemy.orm import Session +from app.schemas.note import NoteCreate, NoteUpdate +from app.utils.get_db import get_db +from app.curd.note import create_note_in_db, delete_note_in_db, update_note_in_db + +router = APIRouter() + +@router.post("", response_model=dict) +def create_note(note: NoteCreate, db: Session = Depends(get_db)): + new_note = create_note_in_db(note, db) + return {"msg": "Note created successfully", "note_id": new_note.id} + +@router.delete("/{note_id}", response_model=dict) +def delete_note(note_id: int, db: Session = Depends(get_db)): + note = delete_note_in_db(note_id, db) + if not note: + raise HTTPException(status_code=404, detail="Note not found") + return {"msg": "Note deleted successfully"} + +@router.put("/{note_id}", response_model=dict) +def update_note(note_id: int, content: str, db: Session = Depends(get_db)): + note = NoteUpdate(id=note_id, content=content) + updated_note = update_note_in_db(note_id, note, db) + if not updated_note: + raise HTTPException(status_code=404, detail="Note not found") + return {"msg": "Note updated successfully", "note_id": updated_note.id} \ No newline at end of file diff --git a/app/core/config.py b/app/core/config.py index 8280afb..6163c9b 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -1,17 +1,20 @@ import os from datetime import timedelta +from dotenv import load_dotenv + +load_dotenv() class Settings: PROJECT_NAME: str = "JieNote Backend" # 项目名称 VERSION: str = "1.0.0" # 项目版本 SQLALCHEMY_DATABASE_URL = "mysql+pymysql://root:coders007@47.93.172.156:3306/JieNote" # 替换为实际的用户名、密码和数据库名称 - SECRET_KEY: str = os.getenv("SECRET_KEY", "your_secret_key") # 替换为更安全的密钥 + SECRET_KEY: str = os.getenv("SECRET_KEY", "default_secret_key") # JWT密钥 ALGORITHM: str = "HS256" # JWT算法 ACCESS_TOKEN_EXPIRE_MINUTES: int = 300 # token过期时间 SMTP_SERVER: str = "smtp.163.com" # SMTP服务器 SMTP_PORT: int = 465 # SMTP端口 SENDER_EMAIL : str = "jienote_buaa@163.com" - SENDER_PASSWORD: str = "TXVU2unpCAE2EtEX" + SENDER_PASSWORD: str = os.getenv("SENDER_PASSWORD", "default_password") # 发件人邮箱密码 settings = Settings() \ No newline at end of file diff --git a/app/curd/note.py b/app/curd/note.py new file mode 100644 index 0000000..b733f43 --- /dev/null +++ b/app/curd/note.py @@ -0,0 +1,25 @@ +from sqlalchemy.orm import Session +from app.models.model import Note +from app.schemas.note import NoteCreate, NoteUpdate + +def create_note_in_db(note: NoteCreate, db: Session): + new_note = Note(content=note.content, article_id=note.article_id) + db.add(new_note) + db.commit() + db.refresh(new_note) + return new_note + +def delete_note_in_db(note_id: int, db: Session): + note = db.query(Note).filter(Note.id == note_id).first() + if note: + db.delete(note) + db.commit() + return note + +def update_note_in_db(note_id: int, note: NoteUpdate, db: Session): + existing_note = db.query(Note).filter(Note.id == note_id).first() + if existing_note: + existing_note.content = note.content + db.commit() + db.refresh(existing_note) + return existing_note \ No newline at end of file diff --git a/app/main.py b/app/main.py index 078afc5..42d5e8f 100644 --- a/app/main.py +++ b/app/main.py @@ -1,5 +1,5 @@ from fastapi import FastAPI -from app.api.v1.endpoints.auth import router as auth_router +from app.routers.router import include_routers app = FastAPI() @@ -11,4 +11,5 @@ def read_root(): def read_item(item_id: int, q: str = None): return {"item_id": item_id, "q": q} -app.include_router(auth_router, prefix="/public", tags=["auth"]) \ No newline at end of file +# 注册路由 +include_routers(app) \ No newline at end of file diff --git a/app/models/model.py b/app/models/model.py index 83db46a..95bbfee 100644 --- a/app/models/model.py +++ b/app/models/model.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, Integer, String, Boolean, Table, ForeignKey, UniqueConstraint, CheckConstraint +from sqlalchemy import Column, Integer, String, Boolean, Table, ForeignKey, UniqueConstraint, CheckConstraint, Text from sqlalchemy.orm import relationship from app.db.base_class import Base @@ -62,7 +62,7 @@ class Note(Base): __tablename__ = 'notes' id = Column(Integer, primary_key=True, index=True, autoincrement=True) - content = Column(String(255)) # 为 content 字段指定长度 + content = Column(Text) # 将 content 字段类型改为 Text,以支持存储大量文本 article_id = Column(Integer, ForeignKey('articles.id')) article = relationship('Article', back_populates='notes') diff --git a/app/routers/router.py b/app/routers/router.py new file mode 100644 index 0000000..01603d1 --- /dev/null +++ b/app/routers/router.py @@ -0,0 +1,12 @@ +from app.api.v1.endpoints.auth import router as auth_router +from app.api.v1.endpoints.note import router as note_router + +def include_auth_router(app): + app.include_router(auth_router, prefix="/public", tags=["auth"]) + +def include_note_router(app): + app.include_router(note_router, prefix="/notes", tags=["note"]) + +def include_routers(app): + include_auth_router(app) + include_note_router(app) \ No newline at end of file diff --git a/app/schemas/note.py b/app/schemas/note.py new file mode 100644 index 0000000..14c67cd --- /dev/null +++ b/app/schemas/note.py @@ -0,0 +1,13 @@ +from pydantic import BaseModel + +class NoteCreate(BaseModel): + article_id: int + content: str + +class NoteDelete(BaseModel): + id: int + +class NoteUpdate(BaseModel): + id: int + content: str + diff --git a/app/utils/__init__.py b/app/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/utils/get_db.py b/app/utils/get_db.py new file mode 100644 index 0000000..2b23243 --- /dev/null +++ b/app/utils/get_db.py @@ -0,0 +1,8 @@ +from app.db.session import SessionLocal + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() \ No newline at end of file diff --git a/app/utils/redis.py b/app/utils/redis.py new file mode 100644 index 0000000..c6d9312 --- /dev/null +++ b/app/utils/redis.py @@ -0,0 +1,23 @@ +import redis +import time + +redis_client = None # 全局 Redis 客户端变量 + +def get_redis_client(): + """ + 初始化并返回 Redis 客户端。 + 如果 Redis 客户端已存在,则直接返回。 + """ + global redis_client + if redis_client is None: + while True: + try: + print("Connecting to Redis...") + redis_client = redis.StrictRedis(host='localhost', port=6379, db=0, decode_responses=True) + redis_client.ping() + print("Connected to Redis successfully.") + break + except redis.ConnectionError: + print("Redis connection failed, retrying...") + time.sleep(1) + return redis_client