This commit is contained in:
jxxghp
2023-06-06 07:15:17 +08:00
commit 4d06f86e62
217 changed files with 13959 additions and 0 deletions

29
app/db/__init__.py Normal file
View File

@ -0,0 +1,29 @@
from sqlalchemy import create_engine, QueuePool
from sqlalchemy.orm import sessionmaker
from app.core.config import settings
# 数据库引擎
Engine = create_engine(f"sqlite:///{settings.CONFIG_PATH}/user.db",
pool_pre_ping=True,
echo=False,
poolclass=QueuePool,
pool_size=1000,
pool_recycle=60 * 10,
max_overflow=0)
# 数据库会话
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=Engine)
def get_db():
"""
获取数据库会话
:return: Session
"""
db = None
try:
db = SessionLocal()
yield db
finally:
if db:
db.close()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

42
app/db/init.py Normal file
View File

@ -0,0 +1,42 @@
from alembic.command import upgrade
from alembic.config import Config
from app.core.config import settings
from app.core.security import get_password_hash
from app.db import Engine, SessionLocal
from app.db.models import Base
from app.db.models.user import User
from app.log import logger
def init_db():
"""
初始化数据库
"""
Base.metadata.create_all(bind=Engine)
# 初始化超级管理员
_db = SessionLocal()
user = User.get_by_email(db=_db, email=settings.SUPERUSER)
if not user:
user = User(
full_name="Admin",
email=settings.SUPERUSER,
hashed_password=get_password_hash(settings.SUPERUSER_PASSWORD),
is_superuser=True,
)
user.create(_db)
def update_db():
"""
更新数据库
"""
db_location = settings.CONFIG_PATH / 'user.db'
script_location = settings.ROOT_PATH / 'alembic'
try:
alembic_cfg = Config()
alembic_cfg.set_main_option('script_location', str(script_location))
alembic_cfg.set_main_option('sqlalchemy.url', f"sqlite:///{db_location}")
upgrade(alembic_cfg, 'head')
except Exception as e:
logger(f'数据库更新失败:{e}')

42
app/db/models/__init__.py Normal file
View File

@ -0,0 +1,42 @@
from typing import Any
from sqlalchemy.orm import as_declarative, declared_attr
@as_declarative()
class Base:
id: Any
__name__: str
def create(self, db):
db.add(self)
db.commit()
db.refresh(self)
return self
@classmethod
def get(cls, db, rid: int):
return db.query(cls).filter(cls.id == rid).first()
def update(self, db, payload: dict):
payload = {k: v for k, v in payload.items() if v is not None}
for key, value in payload.items():
setattr(self, key, value)
db.commit()
db.refresh(self)
@classmethod
def delete(cls, db, rid):
db.query(cls).filter(cls.id == rid).delete()
db.commit()
@classmethod
def list(cls, db):
return db.query(cls).all()
def to_dict(self):
return {c.name: getattr(self, c.name, None) for c in self.__table__.columns}
@declared_attr
def __tablename__(self) -> str:
return self.__name__.lower()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

28
app/db/models/site.py Normal file
View File

@ -0,0 +1,28 @@
from datetime import datetime
from sqlalchemy import Boolean, Column, Integer, String, Sequence
from sqlalchemy.orm import Session
from app.db.models import Base
class Site(Base):
id = Column(Integer, Sequence('id'), primary_key=True, index=True)
name = Column(String, nullable=False)
domain = Column(String, index=True)
url = Column(String, nullable=False)
pri = Column(Integer)
rss = Column(String)
cookie = Column(String)
ua = Column(String)
filter = Column(String)
note = Column(String)
limit_interval = Column(Integer)
limit_count = Column(Integer)
limit_seconds = Column(Integer)
is_active = Column(Boolean(), default=True)
lst_mod_date = Column(String, default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
@staticmethod
def get_by_domain(db: Session, domain: str):
return db.query(Site).filter(Site.domain == domain).first()

View File

@ -0,0 +1,36 @@
from sqlalchemy import Column, Integer, String, Sequence
from sqlalchemy.orm import Session
from app.db.models import Base
class Subscribe(Base):
id = Column(Integer, Sequence('id'), primary_key=True, index=True)
name = Column(String, nullable=False, index=True)
year = Column(String)
type = Column(String)
keyword = Column(String)
tmdbid = Column(String, index=True)
doubanid = Column(String)
season = Column(Integer)
image = Column(String)
description = Column(String)
filter = Column(String)
include = Column(String)
exclude = Column(String)
total_episode = Column(Integer)
start_episode = Column(Integer)
lack_episode = Column(Integer)
note = Column(String)
state = Column(String, nullable=False, index=True, default='N')
@staticmethod
def exists(db: Session, tmdbid: str, season: int = None):
if season:
return db.query(Subscribe).filter(Subscribe.tmdbid == tmdbid,
Subscribe.season == season).first()
return db.query(Subscribe).filter(Subscribe.tmdbid == tmdbid).first()
@staticmethod
def get_by_state(db: Session, state: str):
return db.query(Subscribe).filter(Subscribe.state == state).all()

View File

@ -0,0 +1,19 @@
from sqlalchemy import Column, Integer, String, Sequence
from sqlalchemy.orm import Session
from app.db.models import Base
class SystemConfig(Base):
id = Column(Integer, Sequence('id'), primary_key=True, index=True)
key = Column(String, index=True)
value = Column(String, nullable=True)
@staticmethod
def get_by_key(db: Session, key: str):
return db.query(SystemConfig).filter(SystemConfig.key == key).first()
@staticmethod
def delete_by_key(db: Session, key: str):
db.query(SystemConfig).filter(SystemConfig.key == key).delete()
db.commit()

27
app/db/models/user.py Normal file
View File

@ -0,0 +1,27 @@
from sqlalchemy import Boolean, Column, Integer, String, Sequence
from sqlalchemy.orm import Session
from app.core.security import verify_password
from app.db.models import Base
class User(Base):
id = Column(Integer, Sequence('id'), primary_key=True, index=True)
full_name = Column(String, index=True)
email = Column(String, unique=True, index=True, nullable=False)
hashed_password = Column(String, nullable=False)
is_active = Column(Boolean(), default=True)
is_superuser = Column(Boolean(), default=False)
@staticmethod
def authenticate(db: Session, email: str, password: str):
user = db.query(User).filter(User.email == email).first()
if not user:
return None
if not verify_password(password, str(user.hashed_password)):
return None
return user
@staticmethod
def get_by_email(db: Session, email: str):
return db.query(User).filter(User.email == email).first()

56
app/db/sites.py Normal file
View File

@ -0,0 +1,56 @@
from typing import Tuple, List
from sqlalchemy.orm import Session
from app.db import SessionLocal
from app.db.models.site import Site
class Sites:
"""
站点管理
"""
_db: Session = None
def __init__(self, _db=SessionLocal()):
self._db = _db
def add(self, **kwargs) -> Tuple[bool, str]:
"""
新增站点
"""
site = Site(**kwargs)
if not site.get_by_domain(self._db, kwargs.get("domain")):
site.create(self._db)
return True, "新增站点成功"
return False, "站点已存在"
def list(self) -> List[Site]:
"""
获取站点列表
"""
return Site.list(self._db)
def get_by_domain(self, domain: str) -> Site:
"""
按域名获取站点
"""
return Site.get_by_domain(self._db, domain)
def exists(self, domain: str) -> bool:
"""
判断站点是否存在
"""
return Site.get_by_domain(self._db, domain) is not None
def update_cookie(self, domain: str, cookies: str) -> Tuple[bool, str]:
"""
更新站点Cookie
"""
site = Site.get_by_domain(self._db, domain)
if not site:
return False, "站点不存在"
site.update(self._db, {
"cookie": cookies
})
return True, "更新站点Cookie成功"

76
app/db/subscribes.py Normal file
View File

@ -0,0 +1,76 @@
from typing import Tuple, List
from sqlalchemy.orm import Session
from app.core import MediaInfo
from app.db import SessionLocal
from app.db.models.subscribe import Subscribe
from app.utils.types import MediaType
class Subscribes:
"""
订阅管理
"""
_db: Session = None
def __init__(self, _db=SessionLocal()):
self._db = _db
def add(self, mediainfo: MediaInfo, **kwargs) -> Tuple[bool, str]:
"""
新增订阅
"""
# 总集数
if mediainfo.type == MediaType.TV:
if not kwargs.get('season'):
kwargs.update({
'season': 1
})
if not kwargs.get('total_episode'):
total_episode = len(mediainfo.seasons.get(kwargs.get('season')) or [])
if not total_episode:
return False, "未识别到总集数"
kwargs.update({
'total_episode': total_episode
})
subscribe = Subscribe(name=mediainfo.title,
year=mediainfo.year,
type=mediainfo.type.value,
tmdbid=mediainfo.tmdb_id,
image=mediainfo.get_poster_image(),
description=mediainfo.overview,
**kwargs)
if not subscribe.exists(self._db, tmdbid=mediainfo.tmdb_id, season=kwargs.get('season')):
subscribe.create(self._db)
return True, "新增订阅成功"
else:
return False, "订阅已存在"
def get(self, sid: int) -> Subscribe:
"""
获取订阅
"""
return Subscribe.get(self._db, rid=sid)
def list(self, state: str = None) -> List[Subscribe]:
"""
获取订阅列表
"""
if state:
return Subscribe.get_by_state(self._db, state)
return Subscribe.list(self._db)
def delete(self, sid: int):
"""
删除订阅
"""
Subscribe.delete(self._db, rid=sid)
def update(self, sid: int, payload: dict):
"""
更新订阅
"""
subscribe = self.get(sid)
subscribe.update(self._db, payload)
return subscribe

58
app/db/systemconfigs.py Normal file
View File

@ -0,0 +1,58 @@
import json
from typing import Any, Union
from sqlalchemy.orm import Session
from app.db import SessionLocal
from app.db.models.systemconfig import SystemConfig
from app.utils.object import ObjectUtils
from app.utils.singleton import Singleton
from app.utils.types import SystemConfigKey
class SystemConfigs(metaclass=Singleton):
# 配置对象
__SYSTEMCONF: dict = {}
_db: Session = None
def __init__(self, _db=SessionLocal()):
"""
加载配置到内存
"""
self._db = _db
for item in SystemConfig.list(self._db):
if ObjectUtils.is_obj(item.value):
self.__SYSTEMCONF[item.key] = json.loads(item.value)
else:
self.__SYSTEMCONF[item.key] = item.value
def set(self, key: Union[str, SystemConfigKey], value: Any):
"""
设置系统设置
"""
if isinstance(key, SystemConfigKey):
key = key.value
# 更新内存
self.__SYSTEMCONF[key] = value
# 写入数据库
if ObjectUtils.is_obj(value):
if value is not None:
value = json.dumps(value)
else:
value = ''
conf = SystemConfig.get_by_key(self._db, key)
if conf:
conf.update(self._db, {"value": value})
else:
conf = SystemConfig(key=key, value=value)
conf.create(self._db)
def get(self, key: Union[str, SystemConfigKey] = None):
"""
获取系统设置
"""
if isinstance(key, SystemConfigKey):
key = key.value
if not key:
return self.__SYSTEMCONF
return self.__SYSTEMCONF.get(key)

46
app/db/userauth.py Normal file
View File

@ -0,0 +1,46 @@
import jwt
from fastapi import Depends, HTTPException, status
from sqlalchemy.orm import Session
from app import schemas
from app.core import settings, security
from app.core.security import reusable_oauth2
from app.db import get_db
from app.db.models.user import User
def get_current_user(
db: Session = Depends(get_db), token: str = Depends(reusable_oauth2)
) -> User:
try:
payload = jwt.decode(
token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
)
token_data = schemas.TokenPayload(**payload)
except (jwt.DecodeError, jwt.InvalidTokenError, jwt.ImmatureSignatureError):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="token校验不通过",
)
user = User.get(db, rid=token_data.sub)
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
return user
def get_current_active_user(
current_user: User = Depends(get_current_user),
) -> User:
if not current_user.is_active:
raise HTTPException(status_code=400, detail="用户未激活")
return current_user
def get_current_active_superuser(
current_user: User = Depends(get_current_user),
) -> User:
if not current_user.is_superuser:
raise HTTPException(
status_code=400, detail="用户权限不足"
)
return current_user