Compare commits

..

1 Commits

Author SHA1 Message Date
dc8eabe7eb init 2021-05-01 13:02:57 +08:00
52 changed files with 1006 additions and 605 deletions

View File

@ -1,6 +1,4 @@
# xbackend
x后端 使用 mongo 数据库
### casbin 接口权限控制
x后端
mysql 层级关系处理 过于复杂,放弃。移步 mongo 分支

View File

@ -1,14 +1,10 @@
from fastapi import APIRouter
from api.api_v1.endpoints import user
from .endpoints import project
from .endpoints import folder
from .endpoints import space
from .endpoints import dashboard
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(user.router, tags=["登录接口"], prefix='/user')
api_router.include_router(project.router, tags=["项目接口"], prefix='/project')
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')
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,24 +1,126 @@
import pymongo
from fastapi import APIRouter, Depends
from motor.motor_asyncio import AsyncIOMotorDatabase
import crud, schemas
from typing import Any, List
from db import get_database
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")
async def create(
data_in: schemas.DashboardCreate,
db: AsyncIOMotorDatabase = Depends(get_database),
current_user: schemas.UserDB = Depends(deps.get_current_user)
) -> schemas.Msg:
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 建默认文件夹
@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
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}

View File

@ -1,24 +0,0 @@
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='创建成功')

View File

@ -0,0 +1,93 @@
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"}

View File

@ -1,96 +1,46 @@
import pymongo
from fastapi import APIRouter, Depends
from motor.motor_asyncio import AsyncIOMotorDatabase
import crud, schemas
from api import deps
from datetime import timedelta
from typing import Any, List
from db import get_database
from schemas.project import ProjectCreate
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")
async def create(
data_in: ProjectCreate,
db: AsyncIOMotorDatabase = Depends(get_database),
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 建默认文件夹
@router.get("/", response_model=List[schemas.Project])
def read_project(db: Session = Depends(deps.get_db),
current_user: models.User = Depends(deps.get_current_active_user)
) -> Any:
my_project = crud.project.get_my_project(db=db, user_id=current_user.id)
return schemas.Msg(code=0, msg='ok', detail='创建成功')
return my_project
@router.get("/")
async def read_project(
db: AsyncIOMotorDatabase = Depends(get_database),
current_user: schemas.UserDB = Depends(deps.get_current_user)
):
res = await crud.project.read_project(db, user_id=current_user.id)
return res
@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_superuser)
) -> Any:
project = crud.project.create_with_project(db=db, obj_in=project_in, user_id=current_user.id)
# 我的看板 新建 未分组 和 共享给我的 文件夹
unknown_folder = schemas.FolderCreate(
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)
@router.post("/kanban")
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')
crud.authority.create_with_authority(db=db, obj_in=authority1, user_id=current_user.id)
crud.authority.create_with_authority(db=db, obj_in=authority2, user_id=current_user.id)
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
return project

View File

@ -1,24 +0,0 @@
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='创建成功')

View File

@ -1,64 +0,0 @@
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

View File

@ -1,26 +1,40 @@
import json
from typing import Generator
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import jwt
from pydantic import ValidationError
from sqlalchemy.orm import Session
import schemas
import crud, models, schemas
from core import security
from core.config import settings
from db.session import SessionLocal
from models.authority import Authority
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)
) -> schemas.UserDB:
) -> schemas.UserDBBase:
# def get_current_user(token: str
# ) -> schemas.UserDBBase:
try:
payload = jwt.decode(
token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
)
user = schemas.UserDB(**payload)
user = schemas.UserDBBase(**payload)
except (jwt.JWTError, ValidationError):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
@ -29,3 +43,61 @@ def get_current_user(token: str = Depends(reusable_oauth2)
if not user:
raise HTTPException(status_code=404, detail="User not found")
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
)

View File

@ -7,15 +7,17 @@ class Settings(BaseSettings):
PROJECT_NAME: str = 'X数据分析后台'
API_V1_STR: str = '/api/v1'
ALLOW_ANONYMOUS: tuple = ('login','openapi.json')
BACKEND_CORS_ORIGINS: List[str] = ['*']
MDB_HOST: str = '10.0.0.7'
MDB_PORT: int = 27017
MDB_USER: str = 'root'
MDB_PASSWORD: str = 'iamciniao'
MDB_DB: str = 'xdata'
MYSQL_HOST: str = '127.0.0.1'
MYSQL_PORT: int = 3306
MYSQL_USER: str = 'root'
MYSQL_PASSWORD: str = 'root'
MYSQL_DB: str = 'xdata'
DATABASE_URI = f'mongodb://{MDB_USER}:{MDB_PASSWORD}@{MDB_HOST}:{MDB_PORT}/admin'
SQLALCHEMY_DATABASE_URI = f'mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DB}'
FIRST_EMAIL: str = '15392746632@qq.com'
FIRST_SUPERUSER_PASSWORD: str = '123456'

View File

@ -1,4 +1,5 @@
from datetime import datetime, timedelta
from typing import Any, Union
from jose import jwt
from passlib.context import CryptContext

View File

@ -1,5 +1,6 @@
from .crud_user import user
from .crud_project import project
from .crud_folder import folder
from .crud_space import space
from .crud_dashboard import dashboard
from .curd_project import project
from .curd_folder import folder
from .curd_dashboard import dashboard
from .curd_space import space
from .curd_authority import authority

View File

@ -1,19 +1,75 @@
from bson import ObjectId
from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, Union
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:
def __init__(self, coll_name):
self.coll_name = coll_name
class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
def __init__(self, model: Type[ModelType]):
"""
CRUD object with default methods to Create, Read, Update, Delete (CRUD).
async def get(self, coll, id: ObjectId):
return await coll.find_one({'_id': id})
**Parameters**
async def read_have(self, coll, user_id: str, **kwargs):
where = {'members': user_id}
where.update(kwargs)
cursor = coll.find(where)
return await cursor.to_list(length=999)
* `model`: A SQLAlchemy model class
* `schema`: A Pydantic model (schema) class
"""
self.model = model
async def find_many(self, db, **kwargs):
cursor = db[self.coll_name].find(kwargs)
return await cursor.to_list(length=999)
def get(self, db: Session, id: Any) -> Optional[ModelType]:
return db.query(self.model).filter(self.model.id == id).first()
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

View File

@ -1,27 +0,0 @@
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')

View File

@ -1,30 +0,0 @@
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')

34
crud/crud_item.py Normal file
View File

@ -0,0 +1,34 @@
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)

View File

@ -1,25 +0,0 @@
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')

View File

@ -1,30 +0,0 @@
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')

View File

@ -1,40 +1,55 @@
import uuid
from typing import Any, Dict, Optional, Union
from motor.motor_asyncio import AsyncIOMotorDatabase
from sqlalchemy.orm import Session
from core.security import get_password_hash, verify_password
from crud.base import CRUDBase
from schemas import UserCreate, UserDBRW
__all__ = 'user',
from models.user import User
from schemas.user import UserCreate, UserUpdate
class CRUDUser(CRUDBase):
class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]):
def get_by_user(self, db: Session, *, name: str) -> Optional[User]:
return db.query(User).filter(User.name == name).first()
async def get_by_user(self, db: AsyncIOMotorDatabase, name: str):
res = await db[self.coll_name].find_one({'name': name})
return res
async def create(self, db: AsyncIOMotorDatabase, obj_in: UserCreate):
db_obj = UserDBRW(
def create(self, db: Session, *, obj_in: UserCreate) -> User:
db_obj = User(
email=obj_in.email,
hashed_password=get_password_hash(obj_in.password),
name=obj_in.name,
is_superuser=obj_in.is_superuser,
_id=uuid.uuid1().hex
)
return await db[self.coll_name].insert_one(db_obj.dict(by_alias=True))
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
async def authenticate(self, db: AsyncIOMotorDatabase, name: str, password: str):
user_obj = UserDBRW(**await self.get_by_user(db, name=name))
if not user_obj:
def update(
self, db: Session, *, db_obj: User, obj_in: Union[UserUpdate, Dict[str, Any]]
) -> User:
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
if not verify_password(password, user_obj.hashed_password):
if not verify_password(password, user.hashed_password):
return None
return user_obj
return user
async def create_index(self, db: AsyncIOMotorDatabase):
await db[self.coll_name].create_index('username', unique=True)
def is_active(self, user: User) -> bool:
return user.is_active
def is_superuser(self, user: User) -> bool:
return user.is_superuser
user = CRUDUser('user')
user = CRUDUser(User)

85
crud/curd_authority.py Normal file
View File

@ -0,0 +1,85 @@
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)

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.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)

27
crud/curd_folder.py Normal file
View File

@ -0,0 +1,27 @@
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)

35
crud/curd_project.py Normal file
View File

@ -0,0 +1,35 @@
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)

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
) -> 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)

View File

@ -1,2 +0,0 @@
from .mongodb_utils import *
from .mongodb import get_database

1
db/base.py Normal file
View File

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

14
db/base_class.py Normal file
View File

@ -0,0 +1,14 @@
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()

View File

@ -1,17 +1,11 @@
import crud
import schemas
from sqlalchemy.orm import Session
import crud, schemas
from core.config import settings
# 创建一个超级用户、、
from db import connect_to_mongo, get_database
import asyncio
connect_to_mongo()
db = get_database()
from db import base # noqa: F401
async def create_superuser():
user = await crud.user.get_by_user(db=db, name=settings.FIRST_NAME)
def init_db(db: Session) -> None:
user = crud.user.get_by_user(db, name=settings.FIRST_NAME)
if not user:
user_in = schemas.UserCreate(
name=settings.FIRST_NAME,
@ -19,33 +13,4 @@ async def create_superuser():
password=settings.FIRST_SUPERUSER_PASSWORD,
is_superuser=True,
)
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())
user = crud.user.create(db, obj_in=user_in) # noqa: F841

View File

@ -1,14 +0,0 @@
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]

View File

@ -1,12 +0,0 @@
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()

7
db/session.py Normal file
View File

@ -0,0 +1,7 @@
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)

View File

@ -1,7 +1,7 @@
import logging
from db.init_db import init_db
from db.mongodb_utils import SessionLocal
from db.session import SessionLocal
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

21
main.py
View File

@ -1,12 +1,17 @@
import time
import uvicorn
from fastapi import FastAPI
from core.config import settings
from fastapi import FastAPI, Request, Depends
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 db import connect_to_mongo, close_mongo_connection
app = FastAPI(title=settings.PROJECT_NAME, openapi_url=f"{settings.API_V1_STR}/openapi.json")
app = FastAPI(title=settings.PROJECT_NAME)
allow_anonymous = [f'{settings.API_V1_STR}/{page}' for page in settings.ALLOW_ANONYMOUS]
allow_anonymous.extend(['/docs'])
if settings.BACKEND_CORS_ORIGINS:
app.add_middleware(
@ -16,12 +21,8 @@ if settings.BACKEND_CORS_ORIGINS:
allow_methods=["*"],
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)
if __name__ == '__main__':
uvicorn.run(app='main:app', host="127.0.0.1", port=8889, reload=True, debug=True)
uvicorn.run(app='main:app', host="0.0.0.0", port=8888, reload=True, debug=True)

1
models/__init__.py Normal file
View File

@ -0,0 +1 @@
from .user import User

21
models/authority.py Normal file
View File

@ -0,0 +1,21 @@
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")

19
models/dashboard.py Normal file
View File

@ -0,0 +1,19 @@
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"))

15
models/folders.py Normal file
View File

@ -0,0 +1,15 @@
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")

18
models/project.py Normal file
View File

@ -0,0 +1,18 @@
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")

22
models/space.py Normal file
View File

@ -0,0 +1,22 @@
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"))

13
models/user.py Normal file
View File

@ -0,0 +1,13 @@
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')

View File

@ -1,6 +1,10 @@
from .user import User, UserCreate, UserInDB, UserUpdate, UserLogin, UserBase, UserDBBase
from .token import Token, TokenPayload
from .msg import Msg
from .user import *
from .project import *
from .folder import *
from .space import *
from .dashboard import *
from .project import ProjectCreate, Project
from .folders import FolderCreate, FolderUpdate, FolderIn, FolderDelete
from .dashboard import Dashboard, DashboardCreate, DashboardUpdate, DashboardIn, DashboardDelete
from .space import SpaceCreate, SpaceUpdate, SpaceIn, SpaceDelete
from .authority import AuthorityCreate, AuthorityUpdate
from .tree import Tree

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: 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

View File

@ -1,22 +0,0 @@
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')

View File

@ -1,33 +1,42 @@
import uuid
from datetime import datetime
from enum import Enum
from typing import List
from typing import Optional
from pydantic import BaseModel
from schemas import DBBase
from pydantic import BaseModel, root_validator, Field
class DashboardBase(BaseModel):
name: str = None
# 解析请求json 创建项目
class DashboardIn(DashboardBase):
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):
name: str
project_id: str
# cat: str
pid: str
class Category(str, Enum):
project = 'kanban'
space = 'space'
# --------------------------------------------------------------
# 数据库模型
class DashboardDB(DBBase):
class DashboardUpdate(DashboardBase):
pass
class Dashboard(DashboardBase):
name: str
user_id: str
project_id: str
# cat: Category
pid: str
create_date: datetime = datetime.now()
user_id: int
create_date: datetime
class Config:
orm_mode = True

View File

@ -1,37 +0,0 @@
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()

35
schemas/folders.py Normal file
View File

@ -0,0 +1,35 @@
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

View File

@ -1,9 +1,5 @@
from typing import Any
from pydantic import BaseModel
class Msg(BaseModel):
code: int
msg: str
detail: Any

View File

@ -1,30 +1,27 @@
import uuid
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel
from schemas import DBBase
# 创建项目请求
class ProjectBase(BaseModel):
name: str = None
# 解析请求json 创建项目
class ProjectCreate(ProjectBase):
name: str
# 查询某个项目看板
class ProjectKanban(DBBase):
class ProjectUpdate(ProjectBase):
pass
# --------------------------------------------------------------
# 数据库模型
class ProjectDB(DBBase):
class Project(ProjectBase):
id: int
app_id: str
name: str
user_id: str
members: List[str] = []
create_date: datetime = datetime.now()
user_id: int
create_date: datetime
class Config:
orm_mode = True

View File

@ -1,29 +1,35 @@
import uuid
from datetime import datetime
from enum import Enum
from typing import List
from pydantic import BaseModel
from schemas import DBBase
class SpaceBase(BaseModel):
name: str = None
# 解析请求json 创建项目
class SpaceIn(SpaceBase):
project_id: int
name: str
class SpaceDelete(BaseModel):
id: int
class SpaceCreate(SpaceBase):
name: str
project_id: str
# --------------------------------------------------------------
# 数据库模型
class SpaceDB(DBBase):
class SpaceUpdate(SpaceBase):
pass
class Space(SpaceBase):
id: int
name: str
user_id: str
project_id: str
rw_members: List[str] = []
r_members: List[str] = []
create_date: datetime = datetime.now()
user_id: int
project_id: int
create_date: datetime
class Config:
orm_mode = True

View File

@ -11,4 +11,5 @@ class Token(BaseModel):
msg: str
class TokenPayload(BaseModel):
user_id: int = None

9
schemas/tree.py Normal file
View File

@ -0,0 +1,9 @@
from pydantic import BaseModel
class Tree(BaseModel):
project_id: int
space_id: int = None
folder_id: int = None
dashboard_id: int = None

View File

@ -1,42 +1,44 @@
from typing import Optional
from schemas.base import DBBase
from pydantic import BaseModel, EmailStr, Field
from pydantic import BaseModel, EmailStr
# Shared properties
class UserBase(BaseModel):
email: Optional[EmailStr] = None
is_active: Optional[bool] = True
is_superuser: bool = False
name: Optional[str] = None
class User(UserBase):
name: str
class UserLogin(BaseModel):
username: str = ...
password: str = ...
# Properties to receive via API on creation
class UserCreate(UserBase):
email: EmailStr
password: str
# ****************************************************************************
# mongodb 模型
# Properties to receive via API on update
class UserUpdate(UserBase):
password: Optional[str] = None
class UserDB(DBBase):
email: EmailStr
is_superuser: bool
name: str
class UserDBBase(UserBase):
id: Optional[int] = None
class Config:
orm_mode = True
class UserDBRW(UserDB):
# Additional properties to return via API
class User(UserDBBase):
pass
# Additional properties stored in DB
class UserInDB(UserDBBase):
hashed_password: str