Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
c06629c9a2 | |||
79bd6cf61b | |||
dee6b40277 |
@ -1,4 +1,6 @@
|
|||||||
# xbackend
|
# xbackend
|
||||||
|
|
||||||
x后端
|
x后端 使用 mongo 数据库
|
||||||
mysql 层级关系处理 过于复杂了 。下一版本使用mongodb
|
|
||||||
|
### casbin 接口权限控制
|
||||||
|
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from api.api_v1.endpoints import login
|
from api.api_v1.endpoints import user
|
||||||
from api.api_v1.endpoints import project
|
from .endpoints import project
|
||||||
from api.api_v1.endpoints import dashboard
|
from .endpoints import folder
|
||||||
|
from .endpoints import space
|
||||||
|
from .endpoints import dashboard
|
||||||
|
|
||||||
api_router = APIRouter()
|
api_router = APIRouter()
|
||||||
|
|
||||||
api_router.include_router(login.router, tags=["登录接口"], prefix='/user')
|
api_router.include_router(user.router, tags=["登录接口"], prefix='/user')
|
||||||
api_router.include_router(project.router, tags=["项目管理"], prefix='/project')
|
api_router.include_router(project.router, tags=["项目接口"], prefix='/project')
|
||||||
api_router.include_router(dashboard.router, tags=["看板管理"], prefix='/dashboard')
|
api_router.include_router(folder.router, tags=["文件夹接口"], prefix='/folder')
|
||||||
|
api_router.include_router(space.router, tags=["空间接口"], prefix='/space')
|
||||||
|
api_router.include_router(dashboard.router, tags=["看板接口"], prefix='/dashboard')
|
||||||
|
@ -1,126 +1,24 @@
|
|||||||
from typing import Any, List
|
import pymongo
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||||
|
import crud, schemas
|
||||||
|
|
||||||
from fastapi import APIRouter, Body, Depends, HTTPException, Request
|
from db import get_database
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
import crud, models, schemas
|
|
||||||
from api import deps
|
from api import deps
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get('/')
|
@router.post("/create")
|
||||||
def read_dashboard(project_id: int = Depends(deps.check_project),
|
async def create(
|
||||||
db: Session = Depends(deps.get_db),
|
data_in: schemas.DashboardCreate,
|
||||||
current_user: models.User = Depends(deps.get_current_active_user)
|
db: AsyncIOMotorDatabase = Depends(get_database),
|
||||||
):
|
current_user: schemas.UserDB = Depends(deps.get_current_user)
|
||||||
dashboard = crud.authority.get_my_dashboard(db=db, project_id=project_id, user_id=current_user.id)
|
) -> schemas.Msg:
|
||||||
return dashboard
|
try:
|
||||||
|
await crud.dashboard.create(db, data_in, user_id=current_user.id)
|
||||||
|
except pymongo.errors.DuplicateKeyError:
|
||||||
|
return schemas.Msg(code=-1, msg='error', detail='看板已存在')
|
||||||
|
# todo 建默认文件夹
|
||||||
|
|
||||||
|
return schemas.Msg(code=0, msg='ok', detail='创建成功')
|
||||||
# 新建看板
|
|
||||||
@router.post("/create-dashboard")
|
|
||||||
def create_dashboard(dashboard_in: schemas.DashboardIn,
|
|
||||||
db: Session = Depends(deps.get_db),
|
|
||||||
current_user: models.User = Depends(deps.get_current_active_user)) -> Any:
|
|
||||||
user_id = current_user.id
|
|
||||||
tree = deps.check_tree(db=db, **dashboard_in.dict(), user_id=user_id)
|
|
||||||
dashboard = crud.dashboard.create_with_dashboard(db=db, obj_in=schemas.DashboardCreate(**dashboard_in.dict()),
|
|
||||||
user_id=current_user.id,
|
|
||||||
)
|
|
||||||
|
|
||||||
# 自己创建的拥有权限
|
|
||||||
crud.authority.create_with_authority(db=db, obj_in=schemas.AuthorityCreate(
|
|
||||||
dashboard_id=dashboard.id,
|
|
||||||
project_id=tree.project_id,
|
|
||||||
authority='rw',
|
|
||||||
space_id=tree.space_id,
|
|
||||||
folder_id=tree.folder_id
|
|
||||||
), user_id=user_id)
|
|
||||||
|
|
||||||
return {"msg": "新建成功", 'code': 0}
|
|
||||||
|
|
||||||
|
|
||||||
# 删除看板
|
|
||||||
@router.post("/delete-dashboard")
|
|
||||||
def create_dashboard(dashboard_del: schemas.DashboardDelete,
|
|
||||||
db: Session = Depends(deps.get_db),
|
|
||||||
current_user: models.User = Depends(deps.get_current_active_user)) -> Any:
|
|
||||||
"""
|
|
||||||
只能删除自己创建的 会级联删除权限表
|
|
||||||
:param dashboard_del:
|
|
||||||
:param db:
|
|
||||||
:param current_user:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
user_id = current_user.id
|
|
||||||
dashboard = crud.dashboard.delete_my(db, id=dashboard_del.id, user_id=user_id)
|
|
||||||
|
|
||||||
|
|
||||||
return {"msg": "删除成功", 'code': 0, 'data': dashboard}
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/create-folder")
|
|
||||||
def create_folder(folder_in: schemas.FolderIn,
|
|
||||||
db: Session = Depends(deps.get_db),
|
|
||||||
current_user: models.User = Depends(deps.get_current_active_user)) -> Any:
|
|
||||||
user_id = current_user.id
|
|
||||||
tree = deps.check_tree(db, **folder_in.dict(), user_id=user_id)
|
|
||||||
folder = crud.folder.create_with_folder(db=db, obj_in=schemas.FolderCreate(**folder_in.dict()),
|
|
||||||
user_id=current_user.id)
|
|
||||||
# 自己创建的拥有权限
|
|
||||||
crud.authority.create_with_authority(db=db, obj_in=schemas.AuthorityCreate(
|
|
||||||
project_id=tree.project_id,
|
|
||||||
authority='rw',
|
|
||||||
space_id=tree.space_id,
|
|
||||||
folder_id=folder.id
|
|
||||||
), user_id=user_id)
|
|
||||||
return {"msg": "新建成功", 'code': 0}
|
|
||||||
|
|
||||||
|
|
||||||
# 删除文件夹
|
|
||||||
@router.post("/delete-folder")
|
|
||||||
def delete_folder(folder_del: schemas.FolderDelete,
|
|
||||||
db: Session = Depends(deps.get_db),
|
|
||||||
current_user: models.User = Depends(deps.get_current_active_user)) -> Any:
|
|
||||||
"""
|
|
||||||
只能删除自己创建的 会级联删除权限表
|
|
||||||
:param dashboard_del:
|
|
||||||
:param db:
|
|
||||||
:param current_user:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
user_id = current_user.id
|
|
||||||
folder = crud.folder.delete_my(db, id=folder_del.id, user_id=user_id)
|
|
||||||
|
|
||||||
return {"msg": "删除成功", 'code': 0, 'data': folder}
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/create-space")
|
|
||||||
def create_space(space_in: schemas.SpaceIn,
|
|
||||||
db: Session = Depends(deps.get_db),
|
|
||||||
current_user: models.User = Depends(deps.get_current_active_user)) -> Any:
|
|
||||||
user_id = current_user.id
|
|
||||||
tree = deps.check_tree(db, **space_in.dict(), user_id=user_id)
|
|
||||||
space = crud.space.create_with_space(db=db, obj_in=schemas.SpaceCreate(**space_in.dict()), user_id=user_id)
|
|
||||||
# 自己创建的拥有权限
|
|
||||||
crud.authority.create_with_authority(db=db, obj_in=schemas.AuthorityCreate(
|
|
||||||
project_id=tree.project_id,
|
|
||||||
authority='rw',
|
|
||||||
space_id=space.id,
|
|
||||||
), user_id=current_user.id)
|
|
||||||
return {"msg": "新建成功", 'code': 0}
|
|
||||||
|
|
||||||
|
|
||||||
# 删除文件夹
|
|
||||||
@router.post("/delete-space")
|
|
||||||
def delete_space(space_del: schemas.SpaceDelete,
|
|
||||||
db: Session = Depends(deps.get_db),
|
|
||||||
current_user: models.User = Depends(deps.get_current_active_user)) -> Any:
|
|
||||||
"""
|
|
||||||
只能删除自己创建的 会级联删除权限表
|
|
||||||
"""
|
|
||||||
user_id = current_user.id
|
|
||||||
space = crud.space.delete_my(db, id=space_del.id, user_id=user_id)
|
|
||||||
|
|
||||||
return {"msg": "删除成功", 'code': 0, 'data': space}
|
|
||||||
|
24
api/api_v1/endpoints/folder.py
Normal file
24
api/api_v1/endpoints/folder.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import pymongo
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||||
|
import crud, schemas
|
||||||
|
|
||||||
|
from db import get_database
|
||||||
|
from api import deps
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/create")
|
||||||
|
async def create(
|
||||||
|
data_in: schemas.FolderCreate,
|
||||||
|
db: AsyncIOMotorDatabase = Depends(get_database),
|
||||||
|
current_user: schemas.UserDB = Depends(deps.get_current_user)
|
||||||
|
) -> schemas.Msg:
|
||||||
|
try:
|
||||||
|
await crud.folder.create(db, data_in, user_id=current_user.id)
|
||||||
|
except pymongo.errors.DuplicateKeyError:
|
||||||
|
return schemas.Msg(code=-1, msg='error', detail='文件夹已存在')
|
||||||
|
# todo 建默认文件夹
|
||||||
|
|
||||||
|
return schemas.Msg(code=0, msg='ok', detail='创建成功')
|
@ -1,93 +0,0 @@
|
|||||||
import json
|
|
||||||
from datetime import timedelta
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Body, Depends, HTTPException, Request
|
|
||||||
from fastapi.security import OAuth2PasswordRequestForm
|
|
||||||
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("/login")
|
|
||||||
def login(
|
|
||||||
# data: schemas.UserLogin,
|
|
||||||
data: OAuth2PasswordRequestForm = Depends(),
|
|
||||||
db: Session = Depends(deps.get_db),
|
|
||||||
) -> Any:
|
|
||||||
"""
|
|
||||||
OAuth2兼容令牌登录,获取将来令牌的访问令牌
|
|
||||||
"""
|
|
||||||
user = crud.user.authenticate(
|
|
||||||
db, name=data.username, password=data.password
|
|
||||||
)
|
|
||||||
if not user:
|
|
||||||
raise HTTPException(status_code=400, detail="Incorrect name or password")
|
|
||||||
elif not crud.user.is_active(user):
|
|
||||||
raise HTTPException(status_code=400, detail="Inactive user")
|
|
||||||
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
|
||||||
|
|
||||||
return {
|
|
||||||
'data': {
|
|
||||||
'name': user.name,
|
|
||||||
'email': user.email,
|
|
||||||
'access_token': security.create_access_token(
|
|
||||||
expires_delta=access_token_expires, id=user.id, email=user.email, is_active=user.is_active,
|
|
||||||
is_superuser=user.is_superuser, name=user.name
|
|
||||||
),
|
|
||||||
"token_type": "bearer",
|
|
||||||
|
|
||||||
},
|
|
||||||
'access_token': security.create_access_token(
|
|
||||||
expires_delta=access_token_expires, id=user.id, email=user.email, is_active=user.is_active,
|
|
||||||
is_superuser=user.is_superuser, name=user.name
|
|
||||||
),
|
|
||||||
"token_type": "bearer",
|
|
||||||
|
|
||||||
'code': 0,
|
|
||||||
'msg': 'success',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/me", response_model=schemas.UserDBBase)
|
|
||||||
def me(current_user: models.User = Depends(deps.get_current_active_user)) -> Any:
|
|
||||||
"""
|
|
||||||
Test access token
|
|
||||||
"""
|
|
||||||
return current_user
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/reset-password", response_model=schemas.Msg)
|
|
||||||
def reset_password(
|
|
||||||
token: str = Body(...),
|
|
||||||
new_password: str = Body(...),
|
|
||||||
db: Session = Depends(deps.get_db),
|
|
||||||
) -> Any:
|
|
||||||
"""
|
|
||||||
重设密码
|
|
||||||
"""
|
|
||||||
user_id = verify_password_reset_token(token)
|
|
||||||
if not user_id:
|
|
||||||
raise HTTPException(status_code=400, detail="Invalid token")
|
|
||||||
user = crud.user.get(db, user_id)
|
|
||||||
if not user:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=404,
|
|
||||||
detail="The user with this username does not exist in the system.",
|
|
||||||
)
|
|
||||||
elif not crud.user.is_active(user):
|
|
||||||
raise HTTPException(status_code=400, detail="Inactive user")
|
|
||||||
hashed_password = get_password_hash(new_password)
|
|
||||||
user.hashed_password = hashed_password
|
|
||||||
db.add(user)
|
|
||||||
db.commit()
|
|
||||||
return {"msg": "Password updated successfully"}
|
|
@ -1,46 +1,96 @@
|
|||||||
from datetime import timedelta
|
import pymongo
|
||||||
from typing import Any, List
|
from fastapi import APIRouter, Depends
|
||||||
|
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||||
from fastapi import APIRouter, Body, Depends, HTTPException, Request
|
import crud, schemas
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
import crud, models, schemas
|
|
||||||
from api import deps
|
from api import deps
|
||||||
|
|
||||||
|
from db import get_database
|
||||||
|
from schemas.project import ProjectCreate
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/", response_model=List[schemas.Project])
|
@router.post("/create")
|
||||||
def read_project(db: Session = Depends(deps.get_db),
|
async def create(
|
||||||
current_user: models.User = Depends(deps.get_current_active_user)
|
data_in: ProjectCreate,
|
||||||
) -> Any:
|
db: AsyncIOMotorDatabase = Depends(get_database),
|
||||||
my_project = crud.project.get_my_project(db=db, user_id=current_user.id)
|
current_user: schemas.UserDB = Depends(deps.get_current_user)
|
||||||
|
) -> schemas.Msg:
|
||||||
|
try:
|
||||||
|
await crud.project.create(db, data_in, user_id=current_user.id)
|
||||||
|
except pymongo.errors.DuplicateKeyError:
|
||||||
|
return schemas.Msg(code=-1, msg='error', detail='项目名已存在')
|
||||||
|
# todo 建默认文件夹
|
||||||
|
|
||||||
return my_project
|
return schemas.Msg(code=0, msg='ok', detail='创建成功')
|
||||||
|
|
||||||
|
|
||||||
@router.post("/create-project", response_model=schemas.Project)
|
@router.get("/")
|
||||||
def create_project(project_in: schemas.ProjectCreate, db: Session = Depends(deps.get_db),
|
async def read_project(
|
||||||
current_user: models.User = Depends(deps.get_current_active_superuser)
|
db: AsyncIOMotorDatabase = Depends(get_database),
|
||||||
) -> Any:
|
current_user: schemas.UserDB = Depends(deps.get_current_user)
|
||||||
project = crud.project.create_with_project(db=db, obj_in=project_in, user_id=current_user.id)
|
):
|
||||||
# 我的看板 新建 未分组 和 共享给我的 文件夹
|
res = await crud.project.read_project(db, user_id=current_user.id)
|
||||||
unknown_folder = schemas.FolderCreate(
|
return res
|
||||||
user_id=current_user.id,
|
|
||||||
name='未分组的'
|
|
||||||
)
|
|
||||||
share_folder = schemas.FolderCreate(
|
|
||||||
user_id=current_user.id,
|
|
||||||
name='共享给我的'
|
|
||||||
)
|
|
||||||
folder1 = crud.folder.create_with_folder(db=db, obj_in=unknown_folder, user_id=current_user.id, )
|
|
||||||
folder2 = crud.folder.create_with_folder(db=db, obj_in=share_folder, user_id=current_user.id)
|
|
||||||
|
|
||||||
# 拥有这俩个文件夹权限
|
|
||||||
authority1 = schemas.AuthorityCreate(project_id=project.id, authority='rw', folder_id=folder1.id)
|
|
||||||
authority2 = schemas.AuthorityCreate(project_id=project.id, authority='rw', folder_id=folder2.id)
|
|
||||||
|
|
||||||
crud.authority.create_with_authority(db=db, obj_in=authority1, user_id=current_user.id)
|
@router.post("/kanban")
|
||||||
crud.authority.create_with_authority(db=db, obj_in=authority2, user_id=current_user.id)
|
async def read_kanban(
|
||||||
|
data_in: schemas.ProjectKanban,
|
||||||
|
db: AsyncIOMotorDatabase = Depends(get_database),
|
||||||
|
current_user: schemas.UserDB = Depends(deps.get_current_user)
|
||||||
|
):
|
||||||
|
res = {'kanban': [], 'space': []}
|
||||||
|
# 我的看板
|
||||||
|
kanban = await crud.folder.read_folder(db, project_id=data_in.id, user_id=current_user.id, cat='kanban')
|
||||||
|
|
||||||
return project
|
for item in kanban:
|
||||||
|
dashboards = await crud.dashboard.find_many(db, pid=item['_id'])
|
||||||
|
res['kanban'].append({
|
||||||
|
'folder_name': item['name'],
|
||||||
|
'dashboards': [],
|
||||||
|
'_id': item['_id']
|
||||||
|
})
|
||||||
|
for d in dashboards:
|
||||||
|
res['kanban'][-1]['dashboards'].append({
|
||||||
|
'name': d['name'],
|
||||||
|
'_id': item['_id']
|
||||||
|
})
|
||||||
|
|
||||||
|
# 我的空间
|
||||||
|
where = {
|
||||||
|
'project_id': data_in.id,
|
||||||
|
'$or': [{'rw_members': current_user.id}, {'r_members': current_user.id}]
|
||||||
|
}
|
||||||
|
spaces = await crud.space.find_many(db, **where)
|
||||||
|
# 空间 文件夹 看板
|
||||||
|
for item in spaces:
|
||||||
|
res['space'].append({
|
||||||
|
'space_name': item['name'],
|
||||||
|
'folders': [],
|
||||||
|
'dashboards': [],
|
||||||
|
'_id': item['_id']
|
||||||
|
})
|
||||||
|
res['space'][-1]['authority'] = 'rw' if current_user.id in item['rw_members'] else 'r'
|
||||||
|
|
||||||
|
for f in await crud.folder.find_many(db, pid=item['_id']):
|
||||||
|
res['space'][-1]['folders'].append({
|
||||||
|
'name': f['name'],
|
||||||
|
'_id': f['_id'],
|
||||||
|
'dashboards': [],
|
||||||
|
})
|
||||||
|
|
||||||
|
for d in await crud.dashboard.find_many(db, pid=f['_id']):
|
||||||
|
res['space'][-1]['folders'][-1]['dashboards'].append({
|
||||||
|
'name': d['name'],
|
||||||
|
'_id': d['_id']
|
||||||
|
})
|
||||||
|
|
||||||
|
# 空间 看板
|
||||||
|
for d in await crud.dashboard.find_many(db, pid=item['_id']):
|
||||||
|
res['space'][-1]['dashboards'].append({
|
||||||
|
'name': d['name'],
|
||||||
|
'_id': d['_id']
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
24
api/api_v1/endpoints/space.py
Normal file
24
api/api_v1/endpoints/space.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import pymongo
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||||
|
import crud, schemas
|
||||||
|
|
||||||
|
from db import get_database
|
||||||
|
from api import deps
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/create")
|
||||||
|
async def create(
|
||||||
|
data_in: schemas.SpaceCreate,
|
||||||
|
db: AsyncIOMotorDatabase = Depends(get_database),
|
||||||
|
current_user: schemas.UserDB = Depends(deps.get_current_user)
|
||||||
|
) -> schemas.Msg:
|
||||||
|
try:
|
||||||
|
await crud.space.create(db, data_in, user_id=current_user.id)
|
||||||
|
except pymongo.errors.DuplicateKeyError:
|
||||||
|
return schemas.Msg(code=-1, msg='error', detail='空间已存在')
|
||||||
|
# todo 建默认文件夹
|
||||||
|
|
||||||
|
return schemas.Msg(code=0, msg='ok', detail='创建成功')
|
64
api/api_v1/endpoints/user.py
Normal file
64
api/api_v1/endpoints/user.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
from datetime import timedelta
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Body, Depends, HTTPException, Request
|
||||||
|
from fastapi.security import OAuth2PasswordRequestForm
|
||||||
|
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
|
||||||
|
|
||||||
|
import crud, schemas
|
||||||
|
from api import deps
|
||||||
|
from core import security
|
||||||
|
from core.config import settings
|
||||||
|
from core.security import get_password_hash
|
||||||
|
from db import get_database
|
||||||
|
from utils import (
|
||||||
|
verify_password_reset_token,
|
||||||
|
)
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/login")
|
||||||
|
async def login(
|
||||||
|
# data: schemas.UserLogin,
|
||||||
|
data: OAuth2PasswordRequestForm = Depends(),
|
||||||
|
db: AsyncIOMotorDatabase = Depends(get_database)
|
||||||
|
) -> dict:
|
||||||
|
"""
|
||||||
|
OAuth2兼容令牌登录,获取将来令牌的访问令牌
|
||||||
|
"""
|
||||||
|
user = await crud.user.authenticate(db,
|
||||||
|
name=data.username, password=data.password
|
||||||
|
)
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=400, detail="Incorrect name or password")
|
||||||
|
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'data': {
|
||||||
|
'name': user.name,
|
||||||
|
'email': user.email,
|
||||||
|
# 'access_token': security.create_access_token(
|
||||||
|
# expires_delta=access_token_expires, id=user.id, email=user.email, is_active=user.is_active,
|
||||||
|
# is_superuser=user.is_superuser, name=user.name
|
||||||
|
# ),
|
||||||
|
# "token_type": "bearer",
|
||||||
|
|
||||||
|
},
|
||||||
|
'access_token': security.create_access_token(
|
||||||
|
expires_delta=access_token_expires, _id=str(user.id), email=user.email,
|
||||||
|
is_superuser=user.is_superuser, name=user.name
|
||||||
|
),
|
||||||
|
"token_type": "bearer",
|
||||||
|
|
||||||
|
'code': 0,
|
||||||
|
'msg': 'success',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/me", response_model=schemas.User)
|
||||||
|
def me(current_user: schemas.User = Depends(deps.get_current_user)) -> Any:
|
||||||
|
"""
|
||||||
|
Test access token
|
||||||
|
"""
|
||||||
|
return current_user
|
80
api/deps.py
80
api/deps.py
@ -1,40 +1,26 @@
|
|||||||
import json
|
|
||||||
from typing import Generator
|
|
||||||
|
|
||||||
from fastapi import Depends, HTTPException, status
|
from fastapi import Depends, HTTPException, status
|
||||||
from fastapi.security import OAuth2PasswordBearer
|
from fastapi.security import OAuth2PasswordBearer
|
||||||
from jose import jwt
|
from jose import jwt
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
import crud, models, schemas
|
import schemas
|
||||||
from core import security
|
from core import security
|
||||||
from core.config import settings
|
from core.config import settings
|
||||||
from db.session import SessionLocal
|
|
||||||
from models.authority import Authority
|
|
||||||
|
|
||||||
reusable_oauth2 = OAuth2PasswordBearer(
|
reusable_oauth2 = OAuth2PasswordBearer(
|
||||||
tokenUrl=f"{settings.API_V1_STR}/user/login/"
|
tokenUrl=f"{settings.API_V1_STR}/user/login"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_db() -> Generator:
|
|
||||||
try:
|
|
||||||
db = SessionLocal()
|
|
||||||
yield db
|
|
||||||
finally:
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_user(token: str = Depends(reusable_oauth2)
|
def get_current_user(token: str = Depends(reusable_oauth2)
|
||||||
) -> schemas.UserDBBase:
|
) -> schemas.UserDB:
|
||||||
# def get_current_user(token: str
|
# def get_current_user(token: str
|
||||||
# ) -> schemas.UserDBBase:
|
# ) -> schemas.UserDBBase:
|
||||||
try:
|
try:
|
||||||
payload = jwt.decode(
|
payload = jwt.decode(
|
||||||
token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
|
token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
|
||||||
)
|
)
|
||||||
user = schemas.UserDBBase(**payload)
|
user = schemas.UserDB(**payload)
|
||||||
except (jwt.JWTError, ValidationError):
|
except (jwt.JWTError, ValidationError):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_403_FORBIDDEN,
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
@ -43,61 +29,3 @@ def get_current_user(token: str = Depends(reusable_oauth2)
|
|||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=404, detail="User not found")
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
def get_current_active_user(
|
|
||||||
current_user: models.User = Depends(get_current_user),
|
|
||||||
) -> models.User:
|
|
||||||
if not crud.user.is_active(current_user):
|
|
||||||
raise HTTPException(status_code=400, detail="Inactive user")
|
|
||||||
return current_user
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_active_superuser(
|
|
||||||
current_user: models.User = Depends(get_current_user),
|
|
||||||
) -> models.User:
|
|
||||||
if not crud.user.is_superuser(current_user):
|
|
||||||
raise HTTPException(
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def check_tree(db, *, project_id: int,
|
|
||||||
user_id: int,
|
|
||||||
space_id: int = None,
|
|
||||||
folder_id: int = None,
|
|
||||||
dashboard_id: int = None,
|
|
||||||
**kwargs
|
|
||||||
) -> schemas.Tree:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
where = [
|
|
||||||
Authority.project_id == project_id,
|
|
||||||
Authority.user_id == user_id
|
|
||||||
]
|
|
||||||
|
|
||||||
if space_id is not None:
|
|
||||||
where.append(Authority.space_id == space_id)
|
|
||||||
|
|
||||||
if folder_id is not None:
|
|
||||||
where.append(Authority.folder_id == folder_id)
|
|
||||||
|
|
||||||
if dashboard_id is not None:
|
|
||||||
where.append(Authority.dashboard_id == dashboard_id)
|
|
||||||
|
|
||||||
if not db.query(Authority).filter(*where).first():
|
|
||||||
raise HTTPException(status_code=406, detail='请检查参数')
|
|
||||||
|
|
||||||
return schemas.Tree(project_id=project_id,
|
|
||||||
space_id=space_id,
|
|
||||||
folder_id=folder_id,
|
|
||||||
dashboard_id=dashboard_id
|
|
||||||
)
|
|
||||||
|
@ -7,17 +7,15 @@ class Settings(BaseSettings):
|
|||||||
PROJECT_NAME: str = 'X数据分析后台'
|
PROJECT_NAME: str = 'X数据分析后台'
|
||||||
API_V1_STR: str = '/api/v1'
|
API_V1_STR: str = '/api/v1'
|
||||||
|
|
||||||
ALLOW_ANONYMOUS: tuple = ('login','openapi.json')
|
|
||||||
|
|
||||||
BACKEND_CORS_ORIGINS: List[str] = ['*']
|
BACKEND_CORS_ORIGINS: List[str] = ['*']
|
||||||
|
|
||||||
MYSQL_HOST: str = '127.0.0.1'
|
MDB_HOST: str = '10.0.0.7'
|
||||||
MYSQL_PORT: int = 3306
|
MDB_PORT: int = 27017
|
||||||
MYSQL_USER: str = 'root'
|
MDB_USER: str = 'root'
|
||||||
MYSQL_PASSWORD: str = 'root'
|
MDB_PASSWORD: str = 'iamciniao'
|
||||||
MYSQL_DB: str = 'xdata'
|
MDB_DB: str = 'xdata'
|
||||||
|
|
||||||
SQLALCHEMY_DATABASE_URI = f'mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DB}'
|
DATABASE_URI = f'mongodb://{MDB_USER}:{MDB_PASSWORD}@{MDB_HOST}:{MDB_PORT}/admin'
|
||||||
|
|
||||||
FIRST_EMAIL: str = '15392746632@qq.com'
|
FIRST_EMAIL: str = '15392746632@qq.com'
|
||||||
FIRST_SUPERUSER_PASSWORD: str = '123456'
|
FIRST_SUPERUSER_PASSWORD: str = '123456'
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Any, Union
|
|
||||||
|
|
||||||
from jose import jwt
|
from jose import jwt
|
||||||
from passlib.context import CryptContext
|
from passlib.context import CryptContext
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
from .crud_user import user
|
from .crud_user import user
|
||||||
from .curd_project import project
|
from .crud_project import project
|
||||||
from .curd_folder import folder
|
from .crud_folder import folder
|
||||||
from .curd_dashboard import dashboard
|
from .crud_space import space
|
||||||
from .curd_space import space
|
from .crud_dashboard import dashboard
|
||||||
from .curd_authority import authority
|
|
||||||
|
84
crud/base.py
84
crud/base.py
@ -1,75 +1,19 @@
|
|||||||
from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, Union
|
from bson import ObjectId
|
||||||
|
|
||||||
from fastapi import HTTPException
|
|
||||||
from fastapi.encoders import jsonable_encoder
|
|
||||||
from pydantic import BaseModel
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
from db.base_class import Base
|
|
||||||
|
|
||||||
ModelType = TypeVar("ModelType", bound=Base)
|
|
||||||
CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel)
|
|
||||||
UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel)
|
|
||||||
|
|
||||||
|
|
||||||
class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
|
class CRUDBase:
|
||||||
def __init__(self, model: Type[ModelType]):
|
def __init__(self, coll_name):
|
||||||
"""
|
self.coll_name = coll_name
|
||||||
CRUD object with default methods to Create, Read, Update, Delete (CRUD).
|
|
||||||
|
|
||||||
**Parameters**
|
async def get(self, coll, id: ObjectId):
|
||||||
|
return await coll.find_one({'_id': id})
|
||||||
|
|
||||||
* `model`: A SQLAlchemy model class
|
async def read_have(self, coll, user_id: str, **kwargs):
|
||||||
* `schema`: A Pydantic model (schema) class
|
where = {'members': user_id}
|
||||||
"""
|
where.update(kwargs)
|
||||||
self.model = model
|
cursor = coll.find(where)
|
||||||
|
return await cursor.to_list(length=999)
|
||||||
|
|
||||||
def get(self, db: Session, id: Any) -> Optional[ModelType]:
|
async def find_many(self, db, **kwargs):
|
||||||
return db.query(self.model).filter(self.model.id == id).first()
|
cursor = db[self.coll_name].find(kwargs)
|
||||||
|
return await cursor.to_list(length=999)
|
||||||
def get_multi(
|
|
||||||
self, db: Session, *, skip: int = 0, limit: int = 100
|
|
||||||
) -> List[ModelType]:
|
|
||||||
return db.query(self.model).offset(skip).limit(limit).all()
|
|
||||||
|
|
||||||
def create(self, db: Session, *, obj_in: CreateSchemaType) -> ModelType:
|
|
||||||
obj_in_data = jsonable_encoder(obj_in)
|
|
||||||
db_obj = self.model(**obj_in_data) # type: ignore
|
|
||||||
db.add(db_obj)
|
|
||||||
db.commit()
|
|
||||||
db.refresh(db_obj)
|
|
||||||
return db_obj
|
|
||||||
|
|
||||||
def update(
|
|
||||||
self,
|
|
||||||
db: Session,
|
|
||||||
*,
|
|
||||||
db_obj: ModelType,
|
|
||||||
obj_in: Union[UpdateSchemaType, Dict[str, Any]]
|
|
||||||
) -> ModelType:
|
|
||||||
obj_data = jsonable_encoder(db_obj)
|
|
||||||
if isinstance(obj_in, dict):
|
|
||||||
update_data = obj_in
|
|
||||||
else:
|
|
||||||
update_data = obj_in.dict(exclude_unset=True)
|
|
||||||
for field in obj_data:
|
|
||||||
if field in update_data:
|
|
||||||
setattr(db_obj, field, update_data[field])
|
|
||||||
db.add(db_obj)
|
|
||||||
db.commit()
|
|
||||||
db.refresh(db_obj)
|
|
||||||
return db_obj
|
|
||||||
|
|
||||||
def remove(self, db: Session, *, id: int) -> ModelType:
|
|
||||||
obj = db.query(self.model).get(id)
|
|
||||||
db.delete(obj)
|
|
||||||
db.commit()
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def delete_my(self, db: Session, *, id: int, user_id: int) -> ModelType:
|
|
||||||
obj = db.query(self.model).filter(self.model.id == id, self.model.user_id == user_id).first()
|
|
||||||
if not obj:
|
|
||||||
raise HTTPException(status_code=406, detail='找不到你要删除的')
|
|
||||||
db.delete(obj)
|
|
||||||
db.commit()
|
|
||||||
return obj
|
|
||||||
|
27
crud/crud_dashboard.py
Normal file
27
crud/crud_dashboard.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import pymongo
|
||||||
|
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||||
|
|
||||||
|
from crud.base import CRUDBase
|
||||||
|
from schemas import *
|
||||||
|
|
||||||
|
__all__ = 'dashboard',
|
||||||
|
|
||||||
|
|
||||||
|
class CRUDDashboard(CRUDBase):
|
||||||
|
|
||||||
|
async def create(self, db: AsyncIOMotorDatabase, obj_in: DashboardCreate, user_id: str):
|
||||||
|
db_obj = DashboardDB(
|
||||||
|
**obj_in.dict(), user_id=user_id,
|
||||||
|
_id=uuid.uuid1().hex
|
||||||
|
|
||||||
|
)
|
||||||
|
await db[self.coll_name].insert_one(db_obj.dict(by_alias=True))
|
||||||
|
|
||||||
|
async def create_index(self, db: AsyncIOMotorDatabase):
|
||||||
|
await db[self.coll_name].create_index(
|
||||||
|
[('project_id', pymongo.DESCENDING), ('name', pymongo.DESCENDING), ('user_id', pymongo.DESCENDING)],
|
||||||
|
unique=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dashboard = CRUDDashboard('dashboard')
|
30
crud/crud_folder.py
Normal file
30
crud/crud_folder.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import pymongo
|
||||||
|
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||||
|
|
||||||
|
from crud.base import CRUDBase
|
||||||
|
from schemas import *
|
||||||
|
|
||||||
|
__all__ = 'folder',
|
||||||
|
|
||||||
|
|
||||||
|
class CRUDFolder(CRUDBase):
|
||||||
|
|
||||||
|
async def create(self, db: AsyncIOMotorDatabase, obj_in: FolderCreate, user_id: str):
|
||||||
|
db_obj = FolderDB(
|
||||||
|
**obj_in.dict(), user_id=user_id,
|
||||||
|
members=[user_id],
|
||||||
|
_id=uuid.uuid1().hex
|
||||||
|
|
||||||
|
)
|
||||||
|
await db[self.coll_name].insert_one(db_obj.dict(by_alias=True))
|
||||||
|
|
||||||
|
async def read_folder(self, db, user_id, project_id, cat):
|
||||||
|
return await self.read_have(db[self.coll_name], user_id=user_id, project_id=project_id, cat=cat)
|
||||||
|
|
||||||
|
async def create_index(self, db: AsyncIOMotorDatabase):
|
||||||
|
await db[self.coll_name].create_index(
|
||||||
|
[('project_id', pymongo.DESCENDING), ('name', pymongo.DESCENDING), ('user_id', pymongo.DESCENDING)],
|
||||||
|
unique=True)
|
||||||
|
|
||||||
|
|
||||||
|
folder = CRUDFolder('folder')
|
@ -1,34 +0,0 @@
|
|||||||
from typing import List
|
|
||||||
|
|
||||||
from fastapi.encoders import jsonable_encoder
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
from app.crud.base import CRUDBase
|
|
||||||
from app.models.item import Item
|
|
||||||
from app.schemas.item import ItemCreate, ItemUpdate
|
|
||||||
|
|
||||||
|
|
||||||
class CRUDItem(CRUDBase[Item, ItemCreate, ItemUpdate]):
|
|
||||||
def create_with_owner(
|
|
||||||
self, db: Session, *, obj_in: ItemCreate, owner_id: int
|
|
||||||
) -> Item:
|
|
||||||
obj_in_data = jsonable_encoder(obj_in)
|
|
||||||
db_obj = self.model(**obj_in_data, owner_id=owner_id)
|
|
||||||
db.add(db_obj)
|
|
||||||
db.commit()
|
|
||||||
db.refresh(db_obj)
|
|
||||||
return db_obj
|
|
||||||
|
|
||||||
def get_multi_by_owner(
|
|
||||||
self, db: Session, *, owner_id: int, skip: int = 0, limit: int = 100
|
|
||||||
) -> List[Item]:
|
|
||||||
return (
|
|
||||||
db.query(self.model)
|
|
||||||
.filter(Item.owner_id == owner_id)
|
|
||||||
.offset(skip)
|
|
||||||
.limit(limit)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
item = CRUDItem(Item)
|
|
25
crud/crud_project.py
Normal file
25
crud/crud_project.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||||
|
|
||||||
|
from crud.base import CRUDBase
|
||||||
|
from schemas import *
|
||||||
|
|
||||||
|
__all__ = 'project',
|
||||||
|
|
||||||
|
|
||||||
|
class CRUDProject(CRUDBase):
|
||||||
|
|
||||||
|
async def create(self, db: AsyncIOMotorDatabase, obj_in: ProjectCreate, user_id: str):
|
||||||
|
db_obj = ProjectDB(
|
||||||
|
**obj_in.dict(), user_id=user_id, members=[user_id],
|
||||||
|
_id=uuid.uuid1().hex
|
||||||
|
)
|
||||||
|
await db[self.coll_name].insert_one(db_obj.dict(by_alias=True))
|
||||||
|
|
||||||
|
async def read_project(self, db: AsyncIOMotorDatabase, user_id: str):
|
||||||
|
return await self.read_have(db[self.coll_name], user_id=user_id)
|
||||||
|
|
||||||
|
async def create_index(self, db: AsyncIOMotorDatabase):
|
||||||
|
await db[self.coll_name].create_index('name', unique=True)
|
||||||
|
|
||||||
|
|
||||||
|
project = CRUDProject('project')
|
30
crud/crud_space.py
Normal file
30
crud/crud_space.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import pymongo
|
||||||
|
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||||
|
|
||||||
|
from crud.base import CRUDBase
|
||||||
|
from schemas import *
|
||||||
|
|
||||||
|
__all__ = 'space',
|
||||||
|
|
||||||
|
|
||||||
|
class CRUDSpace(CRUDBase):
|
||||||
|
|
||||||
|
async def create(self, db: AsyncIOMotorDatabase, obj_in: SpaceCreate, user_id: str):
|
||||||
|
db_obj = SpaceDB(
|
||||||
|
**obj_in.dict(), user_id=user_id,
|
||||||
|
rw_members=[user_id],
|
||||||
|
_id=uuid.uuid1().hex
|
||||||
|
|
||||||
|
)
|
||||||
|
await db[self.coll_name].insert_one(db_obj.dict(by_alias=True))
|
||||||
|
|
||||||
|
async def read_space(self, db, user_id, project_id):
|
||||||
|
return await self.read_have(db[self.coll_name], user_id=user_id, project_id=project_id)
|
||||||
|
|
||||||
|
async def create_index(self, db: AsyncIOMotorDatabase):
|
||||||
|
await db[self.coll_name].create_index(
|
||||||
|
[('project_id', pymongo.DESCENDING), ('name', pymongo.DESCENDING), ('user_id', pymongo.DESCENDING)],
|
||||||
|
unique=True)
|
||||||
|
|
||||||
|
|
||||||
|
space = CRUDSpace('space')
|
@ -1,55 +1,40 @@
|
|||||||
from typing import Any, Dict, Optional, Union
|
import uuid
|
||||||
|
|
||||||
from sqlalchemy.orm import Session
|
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||||
|
|
||||||
from core.security import get_password_hash, verify_password
|
from core.security import get_password_hash, verify_password
|
||||||
from crud.base import CRUDBase
|
from crud.base import CRUDBase
|
||||||
from models.user import User
|
from schemas import UserCreate, UserDBRW
|
||||||
from schemas.user import UserCreate, UserUpdate
|
|
||||||
|
__all__ = 'user',
|
||||||
|
|
||||||
|
|
||||||
class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]):
|
class CRUDUser(CRUDBase):
|
||||||
def get_by_user(self, db: Session, *, name: str) -> Optional[User]:
|
|
||||||
return db.query(User).filter(User.name == name).first()
|
|
||||||
|
|
||||||
def create(self, db: Session, *, obj_in: UserCreate) -> User:
|
async def get_by_user(self, db: AsyncIOMotorDatabase, name: str):
|
||||||
db_obj = User(
|
res = await db[self.coll_name].find_one({'name': name})
|
||||||
|
return res
|
||||||
|
|
||||||
|
async def create(self, db: AsyncIOMotorDatabase, obj_in: UserCreate):
|
||||||
|
db_obj = UserDBRW(
|
||||||
email=obj_in.email,
|
email=obj_in.email,
|
||||||
hashed_password=get_password_hash(obj_in.password),
|
hashed_password=get_password_hash(obj_in.password),
|
||||||
name=obj_in.name,
|
name=obj_in.name,
|
||||||
is_superuser=obj_in.is_superuser,
|
is_superuser=obj_in.is_superuser,
|
||||||
|
_id=uuid.uuid1().hex
|
||||||
)
|
)
|
||||||
db.add(db_obj)
|
return await db[self.coll_name].insert_one(db_obj.dict(by_alias=True))
|
||||||
db.commit()
|
|
||||||
db.refresh(db_obj)
|
|
||||||
return db_obj
|
|
||||||
|
|
||||||
def update(
|
async def authenticate(self, db: AsyncIOMotorDatabase, name: str, password: str):
|
||||||
self, db: Session, *, db_obj: User, obj_in: Union[UserUpdate, Dict[str, Any]]
|
user_obj = UserDBRW(**await self.get_by_user(db, name=name))
|
||||||
) -> User:
|
if not user_obj:
|
||||||
if isinstance(obj_in, dict):
|
|
||||||
update_data = obj_in
|
|
||||||
else:
|
|
||||||
update_data = obj_in.dict(exclude_unset=True)
|
|
||||||
if update_data["password"]:
|
|
||||||
hashed_password = get_password_hash(update_data["password"])
|
|
||||||
del update_data["password"]
|
|
||||||
update_data["hashed_password"] = hashed_password
|
|
||||||
return super().update(db, db_obj=db_obj, obj_in=update_data)
|
|
||||||
|
|
||||||
def authenticate(self, db: Session, *, name: str, password: str) -> Optional[User]:
|
|
||||||
user = self.get_by_user(db, name=name)
|
|
||||||
if not user:
|
|
||||||
return None
|
return None
|
||||||
if not verify_password(password, user.hashed_password):
|
if not verify_password(password, user_obj.hashed_password):
|
||||||
return None
|
return None
|
||||||
return user
|
return user_obj
|
||||||
|
|
||||||
def is_active(self, user: User) -> bool:
|
async def create_index(self, db: AsyncIOMotorDatabase):
|
||||||
return user.is_active
|
await db[self.coll_name].create_index('username', unique=True)
|
||||||
|
|
||||||
def is_superuser(self, user: User) -> bool:
|
|
||||||
return user.is_superuser
|
|
||||||
|
|
||||||
|
|
||||||
user = CRUDUser(User)
|
user = CRUDUser('user')
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
def delete(self, db: Session, user_id: int, project_id: int = None, folder_id: int = None, space_id: int = None,
|
|
||||||
dashboard_id: int = None):
|
|
||||||
where = [
|
|
||||||
Authority.user_id == user_id
|
|
||||||
]
|
|
||||||
if project_id is not None:
|
|
||||||
where.append(Authority.project_id == project_id)
|
|
||||||
|
|
||||||
if space_id is not None:
|
|
||||||
where.append(Authority.space_id == space_id)
|
|
||||||
|
|
||||||
if folder_id is not None:
|
|
||||||
where.append(Authority.folder_id == folder_id)
|
|
||||||
|
|
||||||
if dashboard_id is not None:
|
|
||||||
where.append(Authority.dashboard_id == dashboard_id)
|
|
||||||
|
|
||||||
if not db.query(Authority).filter(*where).delete():
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
authority = CRUDAuthority(Authority)
|
|
@ -1,28 +0,0 @@
|
|||||||
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.authority import Authority
|
|
||||||
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)
|
|
@ -1,27 +0,0 @@
|
|||||||
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 schemas import FolderUpdate, FolderCreate
|
|
||||||
|
|
||||||
|
|
||||||
class CRUDFolder(CRUDBase[Folder, FolderCreate, FolderUpdate]):
|
|
||||||
def create_with_folder(
|
|
||||||
self, db: Session, *, obj_in: FolderCreate, user_id: int
|
|
||||||
) -> Folder:
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
folder = CRUDFolder(Folder)
|
|
@ -1,35 +0,0 @@
|
|||||||
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.authority import Authority
|
|
||||||
|
|
||||||
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:
|
|
||||||
if db.query(self.model).filter(self.model.name == obj_in.name).first():
|
|
||||||
raise HTTPException(status_code=406, detail="项目名已存在")
|
|
||||||
|
|
||||||
obj_in_data = jsonable_encoder(obj_in)
|
|
||||||
db_obj = Project(**obj_in_data, user_id=user_id)
|
|
||||||
db.add(db_obj)
|
|
||||||
db.commit()
|
|
||||||
db.refresh(db_obj)
|
|
||||||
return db_obj
|
|
||||||
|
|
||||||
def get_my_project(
|
|
||||||
self, db: Session, *, user_id: int
|
|
||||||
) -> Project:
|
|
||||||
return db.query(self.model).join(Authority).filter(Authority.user_id == user_id).all()
|
|
||||||
|
|
||||||
|
|
||||||
project = CRUDProject(Project)
|
|
@ -1,23 +0,0 @@
|
|||||||
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
|
|
||||||
) -> Space:
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
space = CRUDSpace(Space)
|
|
@ -0,0 +1,2 @@
|
|||||||
|
from .mongodb_utils import *
|
||||||
|
from .mongodb import get_database
|
@ -1 +0,0 @@
|
|||||||
from .base_class import Base # noqa
|
|
@ -1,14 +0,0 @@
|
|||||||
from typing import Any
|
|
||||||
|
|
||||||
from sqlalchemy.ext.declarative import as_declarative, declared_attr
|
|
||||||
|
|
||||||
|
|
||||||
@as_declarative()
|
|
||||||
class Base:
|
|
||||||
id: Any
|
|
||||||
__name__: str
|
|
||||||
|
|
||||||
# 自动生成表名
|
|
||||||
@declared_attr
|
|
||||||
def __tablename__(cls) -> str:
|
|
||||||
return cls.__name__.lower()
|
|
@ -1,11 +1,17 @@
|
|||||||
from sqlalchemy.orm import Session
|
import crud
|
||||||
import crud, schemas
|
import schemas
|
||||||
from core.config import settings
|
from core.config import settings
|
||||||
from db import base # noqa: F401
|
|
||||||
|
# 创建一个超级用户、、
|
||||||
|
from db import connect_to_mongo, get_database
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
connect_to_mongo()
|
||||||
|
db = get_database()
|
||||||
|
|
||||||
|
|
||||||
def init_db(db: Session) -> None:
|
async def create_superuser():
|
||||||
user = crud.user.get_by_user(db, name=settings.FIRST_NAME)
|
user = await crud.user.get_by_user(db=db, name=settings.FIRST_NAME)
|
||||||
if not user:
|
if not user:
|
||||||
user_in = schemas.UserCreate(
|
user_in = schemas.UserCreate(
|
||||||
name=settings.FIRST_NAME,
|
name=settings.FIRST_NAME,
|
||||||
@ -13,4 +19,33 @@ def init_db(db: Session) -> None:
|
|||||||
password=settings.FIRST_SUPERUSER_PASSWORD,
|
password=settings.FIRST_SUPERUSER_PASSWORD,
|
||||||
is_superuser=True,
|
is_superuser=True,
|
||||||
)
|
)
|
||||||
user = crud.user.create(db, obj_in=user_in) # noqa: F841
|
await crud.user.create(db, user_in)
|
||||||
|
await crud.user.create_index(db)
|
||||||
|
|
||||||
|
|
||||||
|
async def project_index():
|
||||||
|
await crud.project.create_index(db)
|
||||||
|
|
||||||
|
|
||||||
|
async def folder_index():
|
||||||
|
await crud.folder.create_index(db)
|
||||||
|
|
||||||
|
|
||||||
|
async def space_index():
|
||||||
|
await crud.space.create_index(db)
|
||||||
|
|
||||||
|
|
||||||
|
async def dashboard_index():
|
||||||
|
await crud.dashboard.create_index(db)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
await create_superuser()
|
||||||
|
await project_index()
|
||||||
|
await folder_index()
|
||||||
|
await space_index()
|
||||||
|
await dashboard_index()
|
||||||
|
|
||||||
|
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
loop.run_until_complete(main())
|
||||||
|
14
db/mongodb.py
Normal file
14
db/mongodb.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
|
||||||
|
|
||||||
|
from core.config import settings
|
||||||
|
|
||||||
|
|
||||||
|
class DataBase:
|
||||||
|
client: AsyncIOMotorClient = None
|
||||||
|
|
||||||
|
|
||||||
|
db = DataBase()
|
||||||
|
|
||||||
|
|
||||||
|
def get_database() -> AsyncIOMotorDatabase:
|
||||||
|
return db.client[settings.MDB_DB]
|
12
db/mongodb_utils.py
Normal file
12
db/mongodb_utils.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from motor.motor_asyncio import AsyncIOMotorClient
|
||||||
|
|
||||||
|
from core.config import settings
|
||||||
|
from .mongodb import db
|
||||||
|
|
||||||
|
|
||||||
|
def connect_to_mongo():
|
||||||
|
db.client = AsyncIOMotorClient(settings.DATABASE_URI)
|
||||||
|
|
||||||
|
|
||||||
|
def close_mongo_connection():
|
||||||
|
db.client.close()
|
@ -1,7 +0,0 @@
|
|||||||
from sqlalchemy import create_engine
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
|
|
||||||
from core.config import settings
|
|
||||||
|
|
||||||
engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=True)
|
|
||||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from db.init_db import init_db
|
from db.init_db import init_db
|
||||||
from db.session import SessionLocal
|
from db.mongodb_utils import SessionLocal
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
21
main.py
21
main.py
@ -1,17 +1,12 @@
|
|||||||
import time
|
|
||||||
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI, Request, Depends
|
from fastapi import FastAPI
|
||||||
from starlette.middleware.cors import CORSMiddleware
|
|
||||||
from fastapi.responses import JSONResponse
|
|
||||||
from api.api_v1.api import api_router
|
|
||||||
from api.deps import get_current_user
|
|
||||||
from core.config import settings
|
from core.config import settings
|
||||||
|
from starlette.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
app = FastAPI(title=settings.PROJECT_NAME, openapi_url=f"{settings.API_V1_STR}/openapi.json")
|
from db import connect_to_mongo, close_mongo_connection
|
||||||
|
|
||||||
allow_anonymous = [f'{settings.API_V1_STR}/{page}' for page in settings.ALLOW_ANONYMOUS]
|
app = FastAPI(title=settings.PROJECT_NAME)
|
||||||
allow_anonymous.extend(['/docs'])
|
|
||||||
|
|
||||||
if settings.BACKEND_CORS_ORIGINS:
|
if settings.BACKEND_CORS_ORIGINS:
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
@ -21,8 +16,12 @@ if settings.BACKEND_CORS_ORIGINS:
|
|||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
)
|
)
|
||||||
|
app.add_event_handler("startup", connect_to_mongo)
|
||||||
|
app.add_event_handler("shutdown", close_mongo_connection)
|
||||||
|
|
||||||
|
from api.api_v1.api import api_router
|
||||||
|
|
||||||
app.include_router(api_router, prefix=settings.API_V1_STR)
|
app.include_router(api_router, prefix=settings.API_V1_STR)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
uvicorn.run(app='main:app', host="0.0.0.0", port=8888, reload=True, debug=True)
|
uvicorn.run(app='main:app', host="127.0.0.1", port=8889, reload=True, debug=True)
|
||||||
|
@ -1 +0,0 @@
|
|||||||
from .user import User
|
|
@ -1,21 +0,0 @@
|
|||||||
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")
|
|
@ -1,19 +0,0 @@
|
|||||||
import datetime
|
|
||||||
|
|
||||||
from sqlalchemy import Boolean, Column, Integer, String, ForeignKey, DateTime
|
|
||||||
from sqlalchemy.orm import relationship, backref
|
|
||||||
|
|
||||||
from db.base_class import Base
|
|
||||||
from models.authority import Authority
|
|
||||||
|
|
||||||
|
|
||||||
class Dashboard(Base):
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
|
||||||
name = Column(String)
|
|
||||||
user_id = Column(Integer, ForeignKey('user.id'))
|
|
||||||
create_date = Column(DateTime, default=datetime.datetime.now())
|
|
||||||
|
|
||||||
|
|
||||||
# authority = relationship('Authority', backref="dashboard",cascade="all,delete-orphan")
|
|
||||||
|
|
||||||
# authority = relationship('Authority', backref=backref("dashboard", lazy="joined"))
|
|
@ -1,15 +0,0 @@
|
|||||||
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'))
|
|
||||||
create_date = Column(DateTime, default=datetime.datetime.now())
|
|
||||||
|
|
||||||
# authority = relationship('Authority', backref="folder", cascade="all, delete")
|
|
@ -1,18 +0,0 @@
|
|||||||
import datetime
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from sqlalchemy import Boolean, Column, Integer, String, ForeignKey, DateTime
|
|
||||||
from sqlalchemy.orm import relationship
|
|
||||||
|
|
||||||
from db.base_class import Base
|
|
||||||
|
|
||||||
|
|
||||||
class Project(Base):
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
|
||||||
app_id = Column(String, unique=True, default=uuid.uuid1().hex)
|
|
||||||
name = Column(String)
|
|
||||||
user_id = Column(Integer, ForeignKey('user.id'))
|
|
||||||
create_date = Column(DateTime, default=datetime.datetime.now())
|
|
||||||
|
|
||||||
# authority = relationship('Authority', backref="project", cascade="all, delete")
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
|||||||
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'))
|
|
||||||
create_date = Column(DateTime, default=datetime.datetime.now())
|
|
||||||
|
|
||||||
# authority = relationship('Authority', backref="space", cascade="all, delete")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# dashboard = relationship('Dashboard', backref=backref("space", lazy="joined"))
|
|
||||||
# folder = relationship('Folder', backref=backref("space", lazy="joined"))
|
|
||||||
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
|||||||
from sqlalchemy import Boolean, Column, Integer, String
|
|
||||||
from sqlalchemy.orm import relationship
|
|
||||||
from db.base_class import Base
|
|
||||||
|
|
||||||
|
|
||||||
class User(Base):
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
|
||||||
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)
|
|
||||||
# dashboard = relationship('Dashboard', back_populates='user')
|
|
@ -1,10 +1,6 @@
|
|||||||
from .user import User, UserCreate, UserInDB, UserUpdate, UserLogin, UserBase, UserDBBase
|
|
||||||
from .token import Token, TokenPayload
|
|
||||||
from .msg import Msg
|
from .msg import Msg
|
||||||
|
from .user import *
|
||||||
from .project import ProjectCreate, Project
|
from .project import *
|
||||||
from .folders import FolderCreate, FolderUpdate, FolderIn, FolderDelete
|
from .folder import *
|
||||||
from .dashboard import Dashboard, DashboardCreate, DashboardUpdate, DashboardIn, DashboardDelete
|
from .space import *
|
||||||
from .space import SpaceCreate, SpaceUpdate, SpaceIn, SpaceDelete
|
from .dashboard import *
|
||||||
from .authority import AuthorityCreate, AuthorityUpdate
|
|
||||||
from .tree import Tree
|
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
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: int = None
|
|
||||||
folder_id: int = 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
|
|
22
schemas/base.py
Normal file
22
schemas/base.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
from bson import ObjectId
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
# # mongodb _id 类型
|
||||||
|
# class OId(ObjectId):
|
||||||
|
# @classmethod
|
||||||
|
# def __get_validators__(cls):
|
||||||
|
# yield cls.validate
|
||||||
|
#
|
||||||
|
# @classmethod
|
||||||
|
# def validate(cls, v):
|
||||||
|
# try:
|
||||||
|
# return ObjectId(v)
|
||||||
|
# except:
|
||||||
|
# raise ValueError('无效的格式')
|
||||||
|
|
||||||
|
|
||||||
|
class DBBase(BaseModel):
|
||||||
|
id: str = Field(None, alias='_id')
|
@ -1,42 +1,33 @@
|
|||||||
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Optional
|
from enum import Enum
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from pydantic import BaseModel, root_validator, Field
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from schemas import DBBase
|
||||||
|
|
||||||
|
|
||||||
class DashboardBase(BaseModel):
|
class DashboardBase(BaseModel):
|
||||||
name: str = None
|
name: str = None
|
||||||
|
|
||||||
|
|
||||||
class DashboardIn(DashboardBase):
|
# 解析请求json 创建项目
|
||||||
project_id: int
|
|
||||||
space_id: int = None
|
|
||||||
folder_id: int = None
|
|
||||||
|
|
||||||
# @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 DashboardDelete(BaseModel):
|
|
||||||
id: int
|
|
||||||
|
|
||||||
|
|
||||||
class DashboardCreate(DashboardBase):
|
class DashboardCreate(DashboardBase):
|
||||||
name: str
|
name: str
|
||||||
|
project_id: str
|
||||||
|
# cat: str
|
||||||
|
pid: str
|
||||||
|
|
||||||
|
class Category(str, Enum):
|
||||||
class DashboardUpdate(DashboardBase):
|
project = 'kanban'
|
||||||
pass
|
space = 'space'
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
# 数据库模型
|
||||||
class Dashboard(DashboardBase):
|
class DashboardDB(DBBase):
|
||||||
name: str
|
name: str
|
||||||
user_id: int
|
user_id: str
|
||||||
create_date: datetime
|
project_id: str
|
||||||
|
# cat: Category
|
||||||
class Config:
|
pid: str
|
||||||
orm_mode = True
|
create_date: datetime = datetime.now()
|
||||||
|
37
schemas/folder.py
Normal file
37
schemas/folder.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import uuid
|
||||||
|
from datetime import datetime
|
||||||
|
from enum import Enum
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from schemas import DBBase
|
||||||
|
|
||||||
|
|
||||||
|
class FolderBase(BaseModel):
|
||||||
|
name: str = None
|
||||||
|
|
||||||
|
|
||||||
|
# 解析请求json 创建项目
|
||||||
|
class FolderCreate(FolderBase):
|
||||||
|
name: str
|
||||||
|
project_id: str
|
||||||
|
cat: str
|
||||||
|
pid: str
|
||||||
|
|
||||||
|
|
||||||
|
class Category(str, Enum):
|
||||||
|
project = 'kanban'
|
||||||
|
space = 'space'
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
# 数据库模型
|
||||||
|
class FolderDB(DBBase):
|
||||||
|
name: str
|
||||||
|
user_id: str
|
||||||
|
project_id: str
|
||||||
|
cat: Category
|
||||||
|
pid: str
|
||||||
|
members: List[str] = []
|
||||||
|
create_date: datetime = datetime.now()
|
@ -1,35 +0,0 @@
|
|||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from pydantic import BaseModel, root_validator
|
|
||||||
|
|
||||||
|
|
||||||
class FolderBase(BaseModel):
|
|
||||||
name: str = None
|
|
||||||
|
|
||||||
|
|
||||||
class FolderIn(FolderBase):
|
|
||||||
project_id: int
|
|
||||||
space_id: int = None
|
|
||||||
name: str
|
|
||||||
|
|
||||||
|
|
||||||
class FolderDelete(BaseModel):
|
|
||||||
id: int
|
|
||||||
|
|
||||||
|
|
||||||
class FolderCreate(FolderBase):
|
|
||||||
name: str
|
|
||||||
|
|
||||||
|
|
||||||
class FolderUpdate(FolderBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Folder(FolderBase):
|
|
||||||
id: int
|
|
||||||
name: str
|
|
||||||
user_id: int
|
|
||||||
create_date: datetime
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
orm_mode = True
|
|
@ -1,5 +1,9 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
class Msg(BaseModel):
|
class Msg(BaseModel):
|
||||||
|
code: int
|
||||||
msg: str
|
msg: str
|
||||||
|
detail: Any
|
||||||
|
@ -1,27 +1,30 @@
|
|||||||
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from schemas import DBBase
|
||||||
|
|
||||||
|
|
||||||
# 创建项目请求
|
|
||||||
class ProjectBase(BaseModel):
|
class ProjectBase(BaseModel):
|
||||||
name: str = None
|
name: str = None
|
||||||
|
|
||||||
|
|
||||||
|
# 解析请求json 创建项目
|
||||||
class ProjectCreate(ProjectBase):
|
class ProjectCreate(ProjectBase):
|
||||||
name: str
|
name: str
|
||||||
|
|
||||||
|
|
||||||
class ProjectUpdate(ProjectBase):
|
# 查询某个项目看板
|
||||||
|
class ProjectKanban(DBBase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Project(ProjectBase):
|
# --------------------------------------------------------------
|
||||||
id: int
|
# 数据库模型
|
||||||
app_id: str
|
class ProjectDB(DBBase):
|
||||||
name: str
|
name: str
|
||||||
user_id: int
|
user_id: str
|
||||||
create_date: datetime
|
members: List[str] = []
|
||||||
|
create_date: datetime = datetime.now()
|
||||||
class Config:
|
|
||||||
orm_mode = True
|
|
||||||
|
@ -1,35 +1,29 @@
|
|||||||
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from enum import Enum
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from schemas import DBBase
|
||||||
|
|
||||||
|
|
||||||
class SpaceBase(BaseModel):
|
class SpaceBase(BaseModel):
|
||||||
name: str = None
|
name: str = None
|
||||||
|
|
||||||
|
|
||||||
class SpaceIn(SpaceBase):
|
# 解析请求json 创建项目
|
||||||
project_id: int
|
|
||||||
name: str
|
|
||||||
|
|
||||||
|
|
||||||
class SpaceDelete(BaseModel):
|
|
||||||
id: int
|
|
||||||
|
|
||||||
|
|
||||||
class SpaceCreate(SpaceBase):
|
class SpaceCreate(SpaceBase):
|
||||||
name: str
|
name: str
|
||||||
|
project_id: str
|
||||||
|
|
||||||
|
|
||||||
class SpaceUpdate(SpaceBase):
|
# --------------------------------------------------------------
|
||||||
pass
|
# 数据库模型
|
||||||
|
class SpaceDB(DBBase):
|
||||||
|
|
||||||
class Space(SpaceBase):
|
|
||||||
id: int
|
|
||||||
name: str
|
name: str
|
||||||
user_id: int
|
user_id: str
|
||||||
project_id: int
|
project_id: str
|
||||||
create_date: datetime
|
rw_members: List[str] = []
|
||||||
|
r_members: List[str] = []
|
||||||
class Config:
|
create_date: datetime = datetime.now()
|
||||||
orm_mode = True
|
|
||||||
|
@ -11,5 +11,4 @@ class Token(BaseModel):
|
|||||||
msg: str
|
msg: str
|
||||||
|
|
||||||
|
|
||||||
class TokenPayload(BaseModel):
|
|
||||||
user_id: int = None
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
|
|
||||||
class Tree(BaseModel):
|
|
||||||
project_id: int
|
|
||||||
space_id: int = None
|
|
||||||
folder_id: int = None
|
|
||||||
dashboard_id: int = None
|
|
||||||
|
|
@ -1,44 +1,42 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from pydantic import BaseModel, EmailStr
|
from schemas.base import DBBase
|
||||||
|
|
||||||
|
from pydantic import BaseModel, EmailStr, Field
|
||||||
|
|
||||||
|
|
||||||
# Shared properties
|
|
||||||
class UserBase(BaseModel):
|
class UserBase(BaseModel):
|
||||||
email: Optional[EmailStr] = None
|
email: Optional[EmailStr] = None
|
||||||
is_active: Optional[bool] = True
|
|
||||||
is_superuser: bool = False
|
is_superuser: bool = False
|
||||||
name: Optional[str] = None
|
name: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class User(UserBase):
|
||||||
|
name: str
|
||||||
|
|
||||||
|
|
||||||
class UserLogin(BaseModel):
|
class UserLogin(BaseModel):
|
||||||
username: str = ...
|
username: str = ...
|
||||||
password: str = ...
|
password: str = ...
|
||||||
|
|
||||||
|
|
||||||
# Properties to receive via API on creation
|
|
||||||
class UserCreate(UserBase):
|
class UserCreate(UserBase):
|
||||||
email: EmailStr
|
email: EmailStr
|
||||||
password: str
|
password: str
|
||||||
|
|
||||||
|
|
||||||
# Properties to receive via API on update
|
# ****************************************************************************
|
||||||
class UserUpdate(UserBase):
|
# mongodb 模型
|
||||||
password: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class UserDBBase(UserBase):
|
class UserDB(DBBase):
|
||||||
id: Optional[int] = None
|
email: EmailStr
|
||||||
|
is_superuser: bool
|
||||||
class Config:
|
name: str
|
||||||
orm_mode = True
|
|
||||||
|
|
||||||
|
|
||||||
# Additional properties to return via API
|
class UserDBRW(UserDB):
|
||||||
class User(UserDBBase):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# Additional properties stored in DB
|
|
||||||
class UserInDB(UserDBBase):
|
|
||||||
hashed_password: str
|
hashed_password: str
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user