This commit is contained in:
wuaho 2021-04-30 09:36:12 +08:00
parent 3b662873c7
commit 4c15ded189
30 changed files with 620 additions and 85 deletions

View File

@ -1,6 +1,10 @@
from fastapi import APIRouter
from api.api_v1.endpoints import login
from api.api_v1.endpoints import project
from api.api_v1.endpoints import dashboard
api_router = APIRouter()
api_router.include_router(login.router, tags=["登录接口"])
api_router.include_router(login.router, tags=["登录接口"], prefix='/user')
api_router.include_router(project.router, tags=["项目管理"], prefix='/project')
api_router.include_router(dashboard.router, tags=["看板管理"], prefix='/dashboard')

View File

@ -1,31 +1,68 @@
from datetime import timedelta
from typing import Any
from typing import Any, List
from fastapi import APIRouter, Body, Depends, HTTPException, Request
from sqlalchemy.orm import Session
import crud, models, schemas
from api import deps
from core import security
from core.config import settings
from core.security import get_password_hash
from utils import (
verify_password_reset_token,
)
router = APIRouter()
@router.get('/')
def read_dashboard(project_id: int = Depends(deps.check_project),
db: Session = Depends(deps.get_db),
current_user: models.User = Depends(deps.get_current_active_user)
):
dashboard = crud.authority.get_my_dashboard(db=db, project_id=project_id, user_id=current_user.id)
return dashboard
# 新建看板
@router.post("/create-dashboard")
def create_dashboard(dashboard_in: schemas.DashboardCreate,
project_id: int = Depends(deps.check_project),
db: Session = Depends(deps.get_db),
current_user: models.User = Depends(deps.get_current_active_user)) -> Any:
dashboard = crud.dashboard.create_with_dashboard(db=db, obj_in=dashboard_in, user_id=current_user.id)
# 自己创建的拥有权限
crud.authority.create_with_authority(db=db, obj_in=schemas.AuthorityCreate(
dashboard_id=dashboard.id,
project_id=project_id,
authority='rw',
space_id=dashboard.space_id,
folder_id=dashboard.folder_id
), user_id=current_user.id)
return {"msg": "新建成功", 'code': 0}
@router.post("/create-folder")
def create_folder(folder_in: schemas.FolderCreate, project_id: int = Depends(deps.check_project),
db: Session = Depends(deps.get_db),
current_user: models.User = Depends(deps.get_current_active_user)) -> Any:
folder = crud.folder.create_with_folder(db=db, obj_in=folder_in, user_id=current_user.id, project_id=project_id)
# 自己创建的拥有权限
crud.authority.create_with_authority(db=db, obj_in=schemas.AuthorityCreate(
project_id=project_id,
authority='rw',
space_id=folder.space_id,
folder_id=folder.id
), user_id=current_user.id)
return {"msg": "新建成功", 'code': 0}
@router.post("/create-space")
def create_space() -> Any:
pass
@router.post("/create-folder")
def create_folder() -> Any:
pass
@router.post("/create-folder")
def create_folder() -> Any:
pass
def create_space(space_in: schemas.SpaceCreate, project_id: int = Depends(deps.check_project),
db: Session = Depends(deps.get_db),
current_user: models.User = Depends(deps.get_current_active_user)) -> Any:
space = crud.space.create_with_space(db=db, obj_in=space_in, user_id=current_user.id,project_id=project_id)
# 自己创建的拥有权限
crud.authority.create_with_authority(db=db, obj_in=schemas.AuthorityCreate(
project_id=project_id,
authority='rw',
space_id=space.id,
), user_id=current_user.id)
return {"msg": "新建成功", 'code': 0}

View File

@ -40,7 +40,7 @@ def login(
'name': user.name,
'email': user.email,
'token': security.create_access_token(
expires_delta=access_token_expires, user_id=user.id, email=user.email, is_active=user.is_active,
expires_delta=access_token_expires, id=user.id, email=user.email, is_active=user.is_active,
is_superuser=user.is_superuser, name=user.name
),
},
@ -50,13 +50,12 @@ def login(
}
@router.post("/me", response_model=schemas.UserBase)
# @router.post("/me/")
def me(request: Request) -> Any:
@router.get("/me", response_model=schemas.UserDBBase)
def me(current_user: models.User = Depends(deps.get_current_active_user)) -> Any:
"""
Test access token
"""
return request.state.user
return current_user
@router.post("/reset-password", response_model=schemas.Msg)

View File

@ -1,22 +0,0 @@
from datetime import timedelta
from typing import Any
from fastapi import APIRouter, Body, Depends, HTTPException, Request
from sqlalchemy.orm import Session
import crud, models, schemas
from api import deps
from core import security
from core.config import settings
from core.security import get_password_hash
from utils import (
verify_password_reset_token,
)
router = APIRouter()
@router.post("/create-project")
def create_project() -> Any:
pass

View File

@ -0,0 +1,35 @@
from datetime import timedelta
from typing import Any
from fastapi import APIRouter, Body, Depends, HTTPException, Request
from sqlalchemy.orm import Session
import crud, models, schemas
from api import deps
router = APIRouter()
@router.post("/create-project", response_model=schemas.Project)
def create_project(project_in: schemas.ProjectCreate, db: Session = Depends(deps.get_db),
current_user: models.User = Depends(deps.get_current_active_user)
) -> Any:
project = crud.project.create_with_project(db=db, obj_in=project_in, user_id=current_user.id)
# 我的看板 新建 未分组 和 共享给我的 文件夹
kanban = crud.kanban.create(db=db, obj_in=schemas.KanBanCreate(project_id=project.id, user_id=current_user.id))
unknown_folder = schemas.FolderCreate(
project_id=project.id,
user_id=current_user.id,
kanban_id=kanban.id,
name='未分组的'
)
share_folder = schemas.FolderCreate(
project_id=project.id,
kanban_id=kanban.id,
name='共享给我的'
)
crud.folder.create_with_folder(db=db, obj_in=unknown_folder, user_id=current_user.id, )
crud.folder.create_with_folder(db=db, obj_in=share_folder, user_id=current_user.id)
return project

View File

@ -25,13 +25,15 @@ def get_db() -> Generator:
db.close()
def get_current_user(token:str
) -> schemas.UserBase:
# def get_current_user(token: str = Depends(reusable_oauth2)
# ) -> schemas.UserDBBase:
def get_current_user(token: str
) -> schemas.UserDBBase:
try:
payload = jwt.decode(
token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
)
user = schemas.UserBase(**payload)
user = schemas.UserDBBase(**payload)
except (jwt.JWTError, ValidationError):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
@ -58,3 +60,9 @@ def get_current_active_superuser(
status_code=400, detail="The user doesn't have enough privileges"
)
return current_user
def check_project(project_id: int, db: Session = Depends(get_db)):
if not crud.project.get(db, id=project_id):
raise HTTPException(status_code=404, detail="没有这个项目")
return project_id

View File

@ -1,3 +1,7 @@
from .crud_user import user
from .curd_project import project
from .curd_kanban import kanban
from .curd_folder import folder
from .curd_dashboard import dashboard
from .curd_space import space
from .curd_authority import authority

70
crud/curd_authority.py Normal file
View File

@ -0,0 +1,70 @@
from typing import Any, Dict, Optional, Union
from fastapi.encoders import jsonable_encoder
from sqlalchemy.orm import Session
from crud.base import CRUDBase
from models.authority import Authority
from schemas import AuthorityCreate, AuthorityUpdate
class CRUDAuthority(CRUDBase[Authority, AuthorityCreate, AuthorityUpdate]):
def create_with_authority(
self, db: Session, *, obj_in: AuthorityCreate, user_id: int
) -> Authority:
obj_in_data = jsonable_encoder(obj_in)
db_obj = self.model(**obj_in_data, user_id=user_id)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
def get_my_dashboard(self, db: Session, project_id: int, user_id: int):
dashboards = db.query(Authority).filter(Authority.project_id == project_id, Authority.user_id == user_id).all()
res = {'kanban': dict(), 'space': dict()}
for item in dashboards:
# 是空间的
dashboard = item.dashboard
folder = item.folder
space = item.space
if space:
s = res['space'].setdefault(space.id, {'dashboard': [], 'folder': {}})
s['name'] = space.name
s['id'] = space.id
if folder:
f = s['folder'].setdefault(folder.id, {'dashboard': []})
f['name'] = folder.name
f['id'] = folder.id
if dashboard:
f['dashboard'].append({
'name': dashboard.name,
'id': dashboard.id
})
elif dashboard:
d = s['dashboard']
d.append({
'name': dashboard.name,
'id': dashboard.id
})
else:
# 是看板的文件夹
folder = item.folder
f = res['kanban'].setdefault(folder.id, {'dashboard': []})
f['name'] = folder.name
f['id'] = folder.id
if dashboard:
f['dashboard'].append({
'name': dashboard.name,
'id': dashboard.id
})
return res
# return {
# 'kanban': list(res['kanban'].values()),
# 'space': list(res['space'].values())
# }
authority = CRUDAuthority(Authority)

28
crud/curd_dashboard.py Normal file
View File

@ -0,0 +1,28 @@
from typing import Any, Dict, Optional, Union
from fastapi import HTTPException
from fastapi.encoders import jsonable_encoder
from sqlalchemy.orm import Session
from crud.base import CRUDBase
from models.dashboard import Dashboard
from models.folders import Folder
from models.space import Space
from schemas import DashboardCreate, DashboardUpdate
class CRUDDashboard(CRUDBase[Dashboard, DashboardCreate, DashboardUpdate]):
def create_with_dashboard(
self, db: Session, *, obj_in: DashboardCreate, user_id: int
) -> Dashboard:
obj_in_data = jsonable_encoder(obj_in)
db_obj = self.model(**obj_in_data, user_id=user_id)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
dashboard = CRUDDashboard(Dashboard)

31
crud/curd_folder.py Normal file
View File

@ -0,0 +1,31 @@
from typing import Any, Dict, Optional, Union
from fastapi import HTTPException
from fastapi.encoders import jsonable_encoder
from sqlalchemy.orm import Session
from crud.base import CRUDBase
from models.folders import Folder
from models.kanban import KanBan
from models.space import Space
from schemas import FolderUpdate, FolderCreate
class CRUDFolder(CRUDBase[Folder, FolderCreate, FolderUpdate]):
def create_with_folder(
self, db: Session, *, obj_in: FolderCreate, user_id: int, project_id: int
) -> Folder:
if (not (obj_in.space_id and db.query(Space).get(obj_in.space_id))) ^ \
(not (obj_in.kanban_id and db.query(KanBan).get(obj_in.kanban_id))):
obj_in_data = jsonable_encoder(obj_in)
db_obj = self.model(**obj_in_data, user_id=user_id, project_id=project_id)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
else:
raise HTTPException(status_code=404, detail="找不到上级")
folder = CRUDFolder(Folder)

15
crud/curd_kanban.py Normal file
View File

@ -0,0 +1,15 @@
from typing import Any, Dict, Optional, Union
from fastapi.encoders import jsonable_encoder
from sqlalchemy.orm import Session
from crud.base import CRUDBase
from models.kanban import KanBan
from schemas import KanBanUpdate,KanBanCreate
class CRUDKanBan(CRUDBase[KanBan, KanBanCreate, KanBanUpdate]):
pass
kanban = CRUDKanBan(KanBan)

27
crud/curd_project.py Normal file
View File

@ -0,0 +1,27 @@
from typing import Any, Dict, Optional, Union
from fastapi.encoders import jsonable_encoder
from sqlalchemy.orm import Session
from crud.base import CRUDBase
from models.project import Project
from schemas.project import ProjectCreate, ProjectUpdate
class CRUDProject(CRUDBase[Project, ProjectCreate, ProjectUpdate]):
def create_with_project(
self, db: Session, *, obj_in: ProjectCreate, user_id: int
) -> Project:
obj_in_data = jsonable_encoder(obj_in)
db_obj = self.model(**obj_in_data, user_id=user_id)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
project = CRUDProject(Project)

23
crud/curd_space.py Normal file
View File

@ -0,0 +1,23 @@
from typing import Any, Dict, Optional, Union
from fastapi.encoders import jsonable_encoder
from sqlalchemy.orm import Session
from crud.base import CRUDBase
from models.space import Space
from schemas import SpaceCreate, SpaceUpdate
class CRUDSpace(CRUDBase[Space, SpaceCreate, SpaceUpdate]):
def create_with_space(
self, db: Session, *, obj_in: SpaceCreate, user_id: int, project_id: int
) -> Space:
obj_in_data = jsonable_encoder(obj_in)
db_obj = self.model(**obj_in_data, user_id=user_id, project_id=project_id)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
space = CRUDSpace(Space)

View File

@ -0,0 +1 @@
from .base_class import Base # noqa

17
main.py
View File

@ -13,23 +13,6 @@ app = FastAPI(title=settings.PROJECT_NAME, openapi_url=f"{settings.API_V1_STR}/o
allow_anonymous = [f'{settings.API_V1_STR}/{page}' for page in settings.ALLOW_ANONYMOUS]
allow_anonymous.extend(['/docs'])
@app.middleware("http")
async def add_jwt_auth(request: Request, call_next):
fail = {'code': -1, 'msg': '身份验证失败'}
if request.scope.get('path') not in allow_anonymous:
token = request.headers.get("Authorization")
try:
user = get_current_user(token)
except:
return JSONResponse(fail)
#
# request.state.user = user
response = await call_next(request)
return response
if settings.BACKEND_CORS_ORIGINS:
app.add_middleware(
CORSMiddleware,

20
models/authority.py Normal file
View File

@ -0,0 +1,20 @@
import datetime
from sqlalchemy import Boolean, Column, Integer, String, ForeignKey, DateTime, Enum
from sqlalchemy.orm import relationship, backref
from db.base_class import Base
class Authority(Base):
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey('user.id'))
project_id = Column(Integer, ForeignKey('project.id'))
dashboard_id = Column(Integer, ForeignKey('dashboard.id'))
folder_id = Column(Integer, ForeignKey('folder.id'))
space_id = Column(Integer, ForeignKey('space.id'))
authority = Column(Enum('rw', 'r'))
create_date = Column(DateTime, default=datetime.datetime.now())
dashboard = relationship('Dashboard', backref="authority")
folder = relationship('Folder', backref="authority")
space = relationship('Space', backref="authority")

View File

@ -1,15 +1,21 @@
from sqlalchemy import Boolean, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
import datetime
from sqlalchemy import Boolean, Column, Integer, String, ForeignKey, DateTime
from sqlalchemy.orm import relationship, backref
from db.base_class import Base
class Dashboard(Base):
id = Column(Integer, primary_key=True, index=True)
folder_type = Column(String)
pid = Column(Integer, ForeignKey('dashboard.id'))
parent = relationship('Dashboard', remote_side=[id])
children = relationship('Dashboard', remote_side=[pid])
name = Column(String, nullable=False)
name = Column(String)
user_id = Column(Integer, ForeignKey('user.id'))
project_id = Column(Integer, ForeignKey('project.id'))
folder_id = Column(Integer, ForeignKey('folder.id'))
space_id = Column(Integer, ForeignKey('space.id'))
create_date = Column(DateTime, default=datetime.datetime.now())
folder = relationship('Folder', backref="dashboard")
space = relationship('Space', backref="dashboard")
# authority = relationship('Authority', backref=backref("dashboard", lazy="joined"))

21
models/folders.py Normal file
View File

@ -0,0 +1,21 @@
import datetime
from sqlalchemy import Boolean, Column, Integer, String, ForeignKey, DateTime
from sqlalchemy.orm import relationship, backref
from db.base_class import Base
class Folder(Base):
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
user_id = Column(Integer, ForeignKey('user.id'))
kanban_id = Column(Integer, ForeignKey('kanban.id'))
project_id = Column(Integer, ForeignKey('project.id'))
space_id = Column(Integer, ForeignKey('space.id'))
create_date = Column(DateTime, default=datetime.datetime.now())
kanban = relationship('KanBan', backref='folder')
space = relationship('Space', backref='folder')
# dashboard = relationship('Dashboard', backref=backref("folder", lazy="joined"))

18
models/kanban.py Normal file
View File

@ -0,0 +1,18 @@
import datetime
from sqlalchemy import Boolean, Column, Integer, String, ForeignKey, DateTime
from sqlalchemy.orm import relationship, backref
from db.base_class import Base
class KanBan(Base):
id = Column(Integer, primary_key=True, index=True)
project_id = Column(Integer, ForeignKey('project.id'))
user_id = Column(Integer, ForeignKey('user.id'))
create_date = Column(DateTime, default=datetime.datetime.now())
project = relationship('Project', backref='kanban')
# folder = relationship('Folder', backref=backref("kanban", lazy="joined"))

View File

@ -1,8 +1,15 @@
from sqlalchemy import Boolean, Column, Integer, String, ForeignKey
import datetime
from sqlalchemy import Boolean, Column, Integer, String, ForeignKey, DateTime
from sqlalchemy.orm import relationship
from db.base_class import Base
class Dashboard(Base):
class Project(Base):
id = Column(Integer, primary_key=True, index=True)
game = Column(String, unique=True)
name = Column(String)
user_id = Column(Integer, ForeignKey('user.id'))
create_date = Column(DateTime, default=datetime.datetime.now())

21
models/space.py Normal file
View File

@ -0,0 +1,21 @@
import datetime
from sqlalchemy import Boolean, Column, Integer, String, ForeignKey, DateTime
from sqlalchemy.orm import relationship, backref
from db.base_class import Base
class Space(Base):
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
user_id = Column(Integer, ForeignKey('user.id'))
project_id = Column(Integer, ForeignKey('project.id'))
create_date = Column(DateTime, default=datetime.datetime.now())
project = relationship('Project', backref='space')
# dashboard = relationship('Dashboard', backref=backref("space", lazy="joined"))
# folder = relationship('Folder', backref=backref("space", lazy="joined"))

View File

@ -10,4 +10,4 @@ class User(Base):
hashed_password = Column(String, nullable=False)
is_active = Column(Boolean(), default=True)
is_superuser = Column(Boolean(), default=False)
dashboard = relationship('Dashboard', back_populates='user')
# dashboard = relationship('Dashboard', back_populates='user')

View File

@ -1,3 +1,10 @@
from .user import User, UserCreate, UserInDB, UserUpdate,UserLogin,UserBase
from .user import User, UserCreate, UserInDB, UserUpdate, UserLogin, UserBase, UserDBBase
from .token import Token, TokenPayload
from .msg import Msg
from .msg import Msg
from .project import ProjectCreate, Project
from .kanban import KanBanCreate, KanBanUpdate
from .folders import FolderCreate, FolderUpdate
from .dashboard import Dashboard,DashboardCreate, DashboardUpdate
from .space import SpaceCreate, SpaceUpdate
from .authority import AuthorityCreate, AuthorityUpdate

38
schemas/authority.py Normal file
View File

@ -0,0 +1,38 @@
from datetime import datetime
from enum import Enum
from pydantic import BaseModel
class AuthorityBase(BaseModel):
project_id: int = None
dashboard_id: int = None
authority: str = None
space_id: str = None
folder_id: str = None
class AuthorityCreate(AuthorityBase):
project_id: int
authority: str
class AuthorityUpdate(AuthorityBase):
pass
class AuthorityEnum(str, Enum):
write = 'rw'
read = 'r'
class Authority(AuthorityBase):
id = int
user_id = int
project_id = int
dashboard_id = int
authority = AuthorityEnum
create_date = datetime
class Config:
orm_mode = True

35
schemas/dashboard.py Normal file
View File

@ -0,0 +1,35 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, root_validator, Field
class DashboardBase(BaseModel):
name: str = None
folder_id: int = None
space_id: int = None
class DashboardCreate(DashboardBase):
name: str
# @root_validator(pre=True)
# def check_parent(cls, values):
# if (values.get('folder_id') is None) ^ (values.get('space_id') is None):
# return values
# else:
# raise ValueError('看板必须有一个上级')
class DashboardUpdate(DashboardBase):
pass
class Dashboard(DashboardBase):
name: str=None
user_id: int=None
folder_id: int=None
space_id: int=None
class Config:
orm_mode = True

36
schemas/folders.py Normal file
View File

@ -0,0 +1,36 @@
from datetime import datetime
from pydantic import BaseModel, root_validator
class FolderBase(BaseModel):
kanban_id: int = None
name: str = None
space_id: int = None
class FolderCreate(FolderBase):
name: str
@root_validator(pre=True)
def check_parent(cls, values):
if (values.get('kanban_id') is None) ^ (values.get('space_id') is None):
return values
else:
raise ValueError('必须只有一个上级')
class FolderUpdate(FolderBase):
pass
class Folder(FolderBase):
id: int
name: str
kanban_id: str
user_id: int
project_id: int
create_date: datetime
class Config:
orm_mode = True

28
schemas/kanban.py Normal file
View File

@ -0,0 +1,28 @@
from datetime import datetime
from pydantic import BaseModel
class KanBanBase(BaseModel):
project_id: int = None
user_id: int = None
class KanBanCreate(KanBanBase):
pass
class KanBanUpdate(KanBanBase):
pass
class KanBan(KanBanBase):
id: int
game: str
name: str
user_id: int
create_date: datetime
class Config:
orm_mode = True

29
schemas/project.py Normal file
View File

@ -0,0 +1,29 @@
from datetime import datetime
from pydantic import BaseModel
# 创建项目请求
class ProjectBase(BaseModel):
game: str = None
name: str = None
class ProjectCreate(ProjectBase):
game: str
name: str
class ProjectUpdate(ProjectBase):
pass
class Project(ProjectBase):
id: int
game: str
name: str
user_id: int
create_date: datetime
class Config:
orm_mode = True

26
schemas/space.py Normal file
View File

@ -0,0 +1,26 @@
from datetime import datetime
from pydantic import BaseModel
class SpaceBase(BaseModel):
name: str = None
class SpaceCreate(SpaceBase):
name: str
class SpaceUpdate(SpaceBase):
pass
class Space(SpaceBase):
id: int
name: str
user_id: int
project_id: int
create_date: datetime
class Config:
orm_mode = True

View File

@ -27,7 +27,7 @@ class UserUpdate(UserBase):
password: Optional[str] = None
class UserInDBBase(UserBase):
class UserDBBase(UserBase):
id: Optional[int] = None
class Config:
@ -35,10 +35,10 @@ class UserInDBBase(UserBase):
# Additional properties to return via API
class User(UserInDBBase):
class User(UserDBBase):
pass
# Additional properties stored in DB
class UserInDB(UserInDBBase):
class UserInDB(UserDBBase):
hashed_password: str