Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
SECRET_KEY=bN3hZ6LbHG7nH9YXWULCr-crcS3GAaRELbNBdAyHBuiHH5TRctd0Zbd6OuLRHHa4Fbs
SENDER_PASSWORD=TXVU2unpCAE2EtEX
38 changes: 38 additions & 0 deletions alembic/versions/e04ed2119f01_将笔记内容变为text存储.py
Original file line number Diff line number Diff line change
@@ -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 ###
28 changes: 6 additions & 22 deletions app/api/v1/endpoints/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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"}

Expand Down
27 changes: 27 additions & 0 deletions app/api/v1/endpoints/note.py
Original file line number Diff line number Diff line change
@@ -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}
7 changes: 5 additions & 2 deletions app/core/config.py
Original file line number Diff line number Diff line change
@@ -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()
25 changes: 25 additions & 0 deletions app/curd/note.py
Original file line number Diff line number Diff line change
@@ -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
5 changes: 3 additions & 2 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -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()

Expand All @@ -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"])
# 注册路由
include_routers(app)
4 changes: 2 additions & 2 deletions app/models/model.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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')
Expand Down
12 changes: 12 additions & 0 deletions app/routers/router.py
Original file line number Diff line number Diff line change
@@ -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)
13 changes: 13 additions & 0 deletions app/schemas/note.py
Original file line number Diff line number Diff line change
@@ -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

Empty file added app/utils/__init__.py
Empty file.
8 changes: 8 additions & 0 deletions app/utils/get_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from app.db.session import SessionLocal

def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
23 changes: 23 additions & 0 deletions app/utils/redis.py
Original file line number Diff line number Diff line change
@@ -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