From 77516b881459eba08ef9bde206851391ba39eba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=20=E6=9F=8F?= Date: Sun, 1 Dec 2024 11:19:51 +0900 Subject: [PATCH 01/24] refactor the crud & paginate --- backend/app/api/api_v1/routers/platform.py | 102 +++++++++++----- backend/app/core/apiexception.py | 2 +- backend/app/core/common.py | 37 +++++- backend/app/db/cruddb/crudbase.py | 101 +++++++++++++++ backend/app/db/cruddb/dbdomain.py | 135 +++++++++++++++++++++ backend/app/db/models.py | 12 +- backend/app/db/schemas.py | 31 +++-- backend/app/main.py | 4 + 8 files changed, 376 insertions(+), 48 deletions(-) create mode 100644 backend/app/db/cruddb/crudbase.py create mode 100644 backend/app/db/cruddb/dbdomain.py diff --git a/backend/app/api/api_v1/routers/platform.py b/backend/app/api/api_v1/routers/platform.py index fcc2c9b..f5f876c 100644 --- a/backend/app/api/api_v1/routers/platform.py +++ b/backend/app/api/api_v1/routers/platform.py @@ -9,13 +9,26 @@ from app.db.schemas import * from typing import List, Optional from app.core.auth import get_current_active_user,get_current_user from app.core.apiexception import APIException -from app.core.common import ApiReturnModel +from app.core.common import ApiReturnModel,ApiReturnPage +#from fastapi_pagination import Page +from app.db.cruddb.dbdomain import dbdomain import httpx import app.core.config as config platform_router = r = APIRouter() +@r.get( + "/test", + response_model_exclude_none=True, +) +async def test( + request: Request, + user = Depends(get_current_active_user), + db=Depends(get_db), +): + dbdomain.select(db,{"tenantid":1,"name":["b","c"]}) + @r.get( "/apps", @@ -255,8 +268,8 @@ async def flow_delete( raise APIException('platform:flow',request.url._url,f"Error occurred while delete flow:",e) @r.get( - "/domains", - response_model=ApiReturnModel[List[Domain]], + "/domains",tags=["Domain"], + response_model=ApiReturnPage[Domain], response_model_exclude_none=True, ) async def domain_details( @@ -266,14 +279,16 @@ async def domain_details( ): try: if user.is_superuser: - domains =get_alldomains(db) + domains = dbdomain.get_domains(db) else: - domains = get_domains(db,user.id) - return ApiReturnModel(data = domains) + domains = dbdomain.get_domains_by_owner(db,user.id) + return domains except Exception as e: raise APIException('platform:domains',request.url._url,f"Error occurred while get domains:",e) -@r.post("/domain", response_model=ApiReturnModel[Domain], response_model_exclude_none=True) +@r.post("/domain", tags=["Domain"], + response_model=ApiReturnModel[Domain], + response_model_exclude_none=True) async def domain_create( request: Request, domain: DomainIn, @@ -281,13 +296,15 @@ async def domain_create( db=Depends(get_db), ): try: - return ApiReturnModel(data = create_domain(db, domain,user.id)) + return ApiReturnModel(data = dbdomain.create_domain(db, domain,user.id)) except Exception as e: raise APIException('platform:domain',request.url._url,f"Error occurred while create domain:",e) @r.put( - "/domain", response_model=ApiReturnModel[Domain], response_model_exclude_none=True + "/domain", tags=["Domain"], + response_model=ApiReturnModel[Domain|None], + response_model_exclude_none=True ) async def domain_edit( request: Request, @@ -296,13 +313,14 @@ async def domain_edit( db=Depends(get_db), ): try: - return ApiReturnModel(data = edit_domain(db, domain,user.id)) + return ApiReturnModel(data = dbdomain.edit_domain(db, domain,user.id)) except Exception as e: raise APIException('platform:domain',request.url._url,f"Error occurred while edit domain:",e) @r.delete( - "/domain/{id}", + "/domain/{id}",tags=["Domain"], + response_model=ApiReturnModel[Domain|None], response_model_exclude_none=True, ) async def domain_delete( @@ -311,8 +329,7 @@ async def domain_delete( db=Depends(get_db), ): try: - if delete_domain(db,id): - return ApiReturnModel(data = None) + return ApiReturnModel(data = dbdomain.delete_domain(db,id)) except Exception as e: raise APIException('platform:domain',request.url._url,f"Error occurred while delete domain:",e) @@ -334,7 +351,7 @@ async def userdomain_details( raise APIException('platform:domain',request.url._url,f"Error occurred while get user({user.id}) domain:",e) @r.post( - "/domain/{userid}", + "/domain/{userid}",tags=["Domain"], response_model=ApiReturnModel[DomainOut|None], response_model_exclude_none=True, ) @@ -347,52 +364,53 @@ async def create_userdomain( ): try: if user.is_superuser: - domain = add_admindomain(db,userid,domainid) + domain = dbdomain.add_userdomain(db,user.id,userid,domainid) else: - domain = add_userdomain(db,user.id,userid,domainid) + domain = dbdomain.add_userdomain_by_owner(db,user.id,userid,domainid) return ApiReturnModel(data = domain) except Exception as e: raise APIException('platform:domain',request.url._url,f"Error occurred while add user({userid}) domain:",e) @r.delete( - "/domain/{domainid}/{userid}", + "/domain/{domainid}/{userid}",tags=["Domain"], + response_model=ApiReturnModel[DomainOut|None], response_model_exclude_none=True, ) -async def userdomain_delete( +async def delete_userdomain( request: Request, domainid:int, userid: int, + user=Depends(get_current_active_user), db=Depends(get_db), ): try: - if delete_userdomain(db, userid,domainid): - return ApiReturnModel(data = None) + return ApiReturnModel(data = dbdomain.delete_userdomain(db,userid,domainid)) except Exception as e: raise APIException('platform:delete',request.url._url,f"Error occurred while delete user({userid}) domain:",e) @r.get( - "/activedomain", - response_model=ApiReturnModel[ActiveDomain|None], + "/defaultdomain",tags=["Domain"], + response_model=ApiReturnModel[DomainOut|None], response_model_exclude_none=True, ) -async def get_activeuserdomain( +async def get_defaultuserdomain( request: Request, user=Depends(get_current_active_user), db=Depends(get_db), ): try: - # domain = get_activedomain(db, user.id) - domain = get_activedomain(db, user.id) - # if domain is None: - # return JSONResponse(content=None,status_code=HTTPStatus.OK) - return ApiReturnModel(data = domain) + userdomain = dbdomain.get_default_domain(db, user.id) + if userdomain: + return ApiReturnModel(data = userdomain.domain) + else: + return ApiReturnModel(data = None) except Exception as e: - raise APIException('platform:activedomain',request.url._url,f"Error occurred while get user({user.id}) activedomain:",e) - + raise APIException('platform:defaultdomain',request.url._url,f"Error occurred while get user({user.id}) defaultdomain:",e) + @r.put( - "/activedomain/{domainid}", + "/defaultdomain/{domainid}",tags=["Domain"], response_model=ApiReturnModel[DomainOut|None], response_model_exclude_none=True, ) @@ -403,10 +421,28 @@ async def update_activeuserdomain( db=Depends(get_db), ): try: - domain = active_userdomain(db,user.id,domainid) + domain = dbdomain.set_default_domain(db,user.id,domainid) return ApiReturnModel(data= domain) except Exception as e: - raise APIException('platform:activedomain',request.url._url,f"Error occurred while update user({user.id}) activedomain:",e) + raise APIException('platform:defaultdomain',request.url._url,f"Error occurred while update user({user.id}) defaultdomain:",e) + + +@r.get( + "/domainshareduser/{domainid}",tags=["Domain"], + response_model=ApiReturnPage[UserOut|None], + response_model_exclude_none=True, +) +async def get_domainshareduser( + request: Request, + domainid:int, + user=Depends(get_current_active_user), + db=Depends(get_db), +): + try: + return dbdomain.get_shareddomain_users(db,user.id,domainid) + except Exception as e: + raise APIException('platform:sharedomain',request.url._url,f"Error occurred while get user({user.id}) sharedomain:",e) + @r.get( "/events", diff --git a/backend/app/core/apiexception.py b/backend/app/core/apiexception.py index 808caef..3183ccb 100644 --- a/backend/app/core/apiexception.py +++ b/backend/app/core/apiexception.py @@ -19,7 +19,7 @@ class APIException(Exception): elif hasattr(e, 'detail'): self.detail = e.detail self.status_code = e.status_code if hasattr(e, 'status_code') else 500 - content += e.detail + content += str(e.detail) else: self.detail = str(e) self.status_code = 500 diff --git a/backend/app/core/common.py b/backend/app/core/common.py index 60ed5f6..c6ba12a 100644 --- a/backend/app/core/common.py +++ b/backend/app/core/common.py @@ -1,10 +1,43 @@ +import math +from fastapi import Query +from fastapi_pagination.bases import AbstractPage,AbstractParams,RawParams from pydantic import BaseModel -from typing import Generic,TypeVar,Optional +from typing import Generic, List,TypeVar,Generic,Sequence +from fastapi_pagination import Page,utils T = TypeVar('T') class ApiReturnModel(BaseModel,Generic[T]): code:int = 0 msg:str ="OK" - data:T \ No newline at end of file + data:T + +class Params(BaseModel, AbstractParams): + page:int = Query(1,get=1, description="Page number") + size:int = Query(20,get=0, le=100,description="Page size") + + def to_raw_params(self) -> RawParams: + return RawParams( + limit=self.size, + offset=self.size*(self.page-1) + ) + +class ApiReturnPage(AbstractPage[T],Generic[T]): + code:int =0 + msg:str ="OK" + data:Sequence[T] + total:int + page:int + size:int + # next:str + # previous:str + total_pages:int + + __params_type__ =Params + + @classmethod + def create(cls,items:Sequence[T],total:int,params:Params) -> Page[T]: + total_pages = math.ceil(total/params.size) + + return utils.create_pydantic_model(cls,data=items,total=total,page=params.page,size=params.size,total_pages=total_pages) diff --git a/backend/app/db/cruddb/crudbase.py b/backend/app/db/cruddb/crudbase.py new file mode 100644 index 0000000..f4196fe --- /dev/null +++ b/backend/app/db/cruddb/crudbase.py @@ -0,0 +1,101 @@ +from sqlalchemy import asc, desc +from sqlalchemy.orm import Session +from sqlalchemy.orm.query import Query +from typing import Type, List, Optional +from app.core.common import ApiReturnPage +from fastapi_pagination.ext.sqlalchemy import paginate +from sqlalchemy import and_ ,or_ +from pydantic import BaseModel +from .. import models, schemas + +class crudbase: + def __init__(self, model: Type[models.Base]): + self.model = model + + def _apply_filters(self, query: Query, filters: dict) -> Query: + and_conditions = [] + or_conditions = [] + for column_name, value in filters.items(): + column = getattr(self.model, column_name, None) + if column: + if isinstance(value, dict): + if 'operator' in value: + operator = value['operator'] + filter_value = value['value'] + if operator == '!=': + and_conditions.append(column != filter_value) + elif operator == 'like': + and_conditions.append(column.like(f"%{filter_value}%")) + elif operator == '=': + and_conditions.append(column == filter_value) + elif operator == '>': + and_conditions.append(column > filter_value) + elif operator == '<': + and_conditions.append(column < filter_value) + elif operator == 'in': + if isinstance(filter_value, list): + or_conditions.append(column.in_(filter_value)) + else: + and_conditions.append(column == filter_value) + else: + and_conditions.append(column == value) + else: + and_conditions.append(column == value) + + if and_conditions: + query = query.filter(*and_conditions) + if or_conditions: + query = query.filter(or_(*or_conditions)) + return query + + def _apply_sorting(self, query: Query, sort_by: Optional[str], sort_order: Optional[str]) -> Query: + if sort_by: + column = getattr(self.model, sort_by, None) + if column: + if sort_order == "desc": + query = query.order_by(desc(column)) + else: + query = query.order_by(asc(column)) + return query + + def get_all(self, db: Session) -> ApiReturnPage[models.Base]: + return paginate(db.query(self.model)) + + + def get(self, db: Session, item_id: int) -> Optional[models.Base]: + return db.query(self.model).get(item_id) + + def create(self, db: Session, obj_in: BaseModel) -> models.Base: + db_obj = self.model(**obj_in.model_dump()) + db.add(db_obj) + db.commit() + db.refresh(db_obj) + return db_obj + + def update(self, db: Session, item_id: int, obj_in: BaseModel) -> Optional[models.Base]: + db_obj = db.query(self.model).filter(self.model.id == item_id).first() + if db_obj: + for key, value in obj_in.model_dump(exclude_unset=True).items(): + setattr(db_obj, key, value) + db.commit() + db.refresh(db_obj) + return db_obj + return None + + def delete(self, db: Session, item_id: int) -> Optional[models.Base]: + db_obj = db.query(self.model).get(item_id) + if db_obj: + db.delete(db_obj) + db.commit() + return db_obj + return None + + def get_by_conditions(self, db: Session, filters: Optional[dict] = None, sort_by: Optional[str] = None, + sort_order: Optional[str] = "asc") -> Query: + query = db.query(self.model) + if filters: + query = self._apply_filters(query, filters) + if sort_by: + query = self._apply_sorting(query, sort_by, sort_order) + print(str(query)) + return query \ No newline at end of file diff --git a/backend/app/db/cruddb/dbdomain.py b/backend/app/db/cruddb/dbdomain.py new file mode 100644 index 0000000..5b8178c --- /dev/null +++ b/backend/app/db/cruddb/dbdomain.py @@ -0,0 +1,135 @@ +from datetime import datetime +from fastapi import HTTPException, status +from sqlalchemy.orm import Session +from sqlalchemy import and_ +import typing as t + +from app.db.cruddb.crudbase import crudbase +from fastapi_pagination.ext.sqlalchemy import paginate +from app.core.common import ApiReturnPage + +from app.db import models, schemas +from app.core.security import chacha20Decrypt, get_password_hash + +class dbuserdomain(crudbase): + def __init__(self): + super().__init__(model=models.UserDomain) + + def get_userdomain(self,db: Session,userid:int,domainid:int): + return super().get_by_conditions(db,{"userid":userid,"domainid":domainid}).first() + + def get_userdomain_by_domainid(self,db: Session,ownerid:int,domainid:int): + return super().get_by_conditions(db,{"domainid":domainid}) + + + def get_default_domains(self,db: Session,domainid:int): + return super().get_by_conditions(db,{"domainid":domainid,"is_default":True}).all() + + def get_user_default_domain(self,db: Session,userid:int): + return super().get_by_conditions(db,{"userid":userid,"is_default":True}).first() + + +dbuserdomain = dbuserdomain() + +class dbdomain(crudbase): + def __init__(self): + super().__init__(model=models.Domain) + + def get_domains(self,db: Session)-> ApiReturnPage[models.Base]: + return super().get_all(db) + + def get_domains_by_owner(self,db: Session,ownerid:int)-> ApiReturnPage[models.Base]: + return paginate( super().get_by_conditions(db,{"ownerid":ownerid})) + + def create_domain(self,db: Session, domain: schemas.DomainIn,userid:int): + db_domain = super().get_by_conditions(db,{"url":domain.url,"kintoneuser":domain.kintoneuser,"onwerid":userid}).first() + if not db_domain: + domain.encrypt_kintonepwd() + domain.id = None + domain.createuserid = userid + domain.updateuserid = userid + domain.ownerid = userid + return super().create(db,domain) + return db_domain + + def delete_domain(self,db: Session,id: int): + return super().delete(db,id) + + def edit_domain(self,db: Session, domain: schemas.DomainIn,userid:int) -> schemas.DomainOut: + db_domain = super().get(db,domain.id) + if db_domain: + db_domain.tenantid = domain.tenantid + db_domain.name=domain.name + db_domain.url=domain.url + if db_domain.is_active == True and domain.is_active == False: + db_userdomains = dbuserdomain.get_default_domains(db,domain.id) + for userdomain in db_userdomains: + userdomain.active = False + db.add(userdomain) + db_domain.is_active=domain.is_active + db_domain.kintoneuser=domain.kintoneuser + if domain.kintonepwd != "" and domain.kintonepwd != None: + domain.encrypt_kintonepwd() + db_domain.kintonepwd = domain.kintonepwd + db_domain.updateuserid = userid + db.add(db_domain) + db.commit() + db.refresh(db_domain) + return db_domain + return None + + def add_userdomain(self,db: Session,ownerid:int,userid:int,domainid:int) -> schemas.DomainOut: + db_domain = super().get_by_conditions(db,{"id":domainid,"is_active":True}).first() + if db_domain: + db_userdomain = dbuserdomain.get_userdomain(db,userid,domainid) + if not db_userdomain: + user_domain = models.UserDomain(userid = userid, domainid = domainid ,createuserid = ownerid,updateuserid = ownerid) + db.add(user_domain) + db.commit() + return db_domain + return None + + def add_userdomain_by_owner(self,db: Session,ownerid:int, userid:int,domainid:int) -> schemas.DomainOut: + db_domain = super().get_by_conditions(db,{"id":domainid,"ownerid":ownerid,"is_active":True}).first() + if db_domain: + db_userdomain = dbuserdomain.get_userdomain(db,userid,domainid) + if not db_userdomain: + user_domain = models.UserDomain(userid = userid, domainid = domainid ,createuserid =ownerid,updateuserid = ownerid) + db.add(user_domain) + db.commit() + return db_domain + return None + + def delete_userdomain(self,db: Session, userid: int,domainid: int) -> schemas.DomainOut: + db_userdomain = dbuserdomain.get_userdomain(db,userid,domainid) + if db_userdomain: + domain = db_userdomain.domain + db.delete(db_userdomain) + db.commit() + return domain + return None + + def get_default_domain(self,db: Session, userid: int) -> schemas.UserDomain: + return dbuserdomain.get_user_default_domain(db,userid) + + def set_default_domain(self,db: Session, userid: int,domainid: int): + db_domain =super().get_by_conditions(db,{"id":domainid,"is_active":True}).first() + if db_domain: + db_default_domain = dbuserdomain.get_user_default_domain(db,userid) + db_userdomain =dbuserdomain.get_userdomain(db,userid,domainid) + if db_default_domain: + db_default_domain.is_default = False + db_default_domain.updateuserid = userid + db.add(db_default_domain) + if db_userdomain: + db_userdomain.is_default = True + db_userdomain.updateuserid = userid + db.add(db_userdomain) + db.commit() + return db_domain + + def get_shareddomain_users(self,db: Session,ownerid:int,domainid: int) -> ApiReturnPage[models.Base]: + users = db.query(models.User).join(models.UserDomain,models.UserDomain.userid == models.User.id).filter(models.UserDomain.domainid ==domainid) + return paginate(users) + +dbdomain = dbdomain() \ No newline at end of file diff --git a/backend/app/db/models.py b/backend/app/db/models.py index 8433d46..ff38fd0 100644 --- a/backend/app/db/models.py +++ b/backend/app/db/models.py @@ -145,6 +145,7 @@ class Tenant(Base): startdate = Column(DateTime) enddate = Column(DateTime) + class Domain(Base): __tablename__ = "domain" @@ -153,6 +154,7 @@ class Domain(Base): url = Column(String(200), nullable=False) kintoneuser = Column(String(100), nullable=False) kintonepwd = Column(String(100), nullable=False) + is_active = Column(Boolean, default=True) def decrypt_kintonepwd(self): decrypted_pwd = chacha20Decrypt(self.kintonepwd) return decrypted_pwd @@ -162,14 +164,20 @@ class Domain(Base): createuser = relationship('User',foreign_keys=[createuserid]) updateuser = relationship('User',foreign_keys=[updateuserid]) owner = relationship('User',foreign_keys=[ownerid]) - is_active = Column(Boolean, default=True) + class UserDomain(Base): __tablename__ = "userdomain" userid = Column(Integer,ForeignKey("user.id")) domainid = Column(Integer,ForeignKey("domain.id")) - active = Column(Boolean, default=False) + is_default = Column(Boolean, default=False) + createuserid = Column(Integer,ForeignKey("user.id")) + updateuserid = Column(Integer,ForeignKey("user.id")) + domain = relationship("Domain") + user = relationship("User",foreign_keys=[userid]) + createuser = relationship('User',foreign_keys=[createuserid]) + updateuser = relationship('User',foreign_keys=[updateuserid]) class Event(Base): __tablename__ = "event" diff --git a/backend/app/db/schemas.py b/backend/app/db/schemas.py index c8e7d2d..5fc1f3b 100644 --- a/backend/app/db/schemas.py +++ b/backend/app/db/schemas.py @@ -28,9 +28,14 @@ class UserBase(BaseModel): first_name: str = None last_name: str = None roles:t.List[Role] = [] + -class UserOut(UserBase): - pass +class UserOut(BaseModel): + email: str + is_active: bool = True + is_superuser: bool = False + first_name: str = None + last_name: str = None class UserCreate(UserBase): @@ -149,9 +154,11 @@ class DomainIn(BaseModel): name: str url: str kintoneuser: str - kintonepwd: str + kintonepwd: t.Optional[str] = None is_active: bool - ownerid:int + createuserid:t.Optional[int] = None + updateuserid:t.Optional[int] = None + ownerid:t.Optional[int] = None def encrypt_kintonepwd(self): encrypted_pwd = chacha20Encrypt(self.kintonepwd) @@ -162,16 +169,18 @@ class DomainOut(BaseModel): tenantid: str name: str url: str + kintoneuser: str is_active: bool ownerid:int + class ConfigDict: + orm_mode = True -class ActiveDomain(BaseModel): +class UserDomain(BaseModel): id: int - tenantid: str - name: str - url: str - is_active: bool + is_default: bool + domain:DomainOut + user:UserOut class Domain(Base): id: int @@ -179,12 +188,14 @@ class Domain(Base): name: str url: str kintoneuser: str - kintonepwd: str is_active: bool + updateuser:UserOut owner:UserOut + class ConfigDict: orm_mode = True + class Event(Base): id: int category: str diff --git a/backend/app/main.py b/backend/app/main.py index e55b192..7aaf644 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,5 +1,6 @@ import os from fastapi import FastAPI, Depends +from fastapi_pagination import add_pagination from starlette.requests import Request import uvicorn from app.api.api_v1.routers.kintone import kinton_router @@ -18,6 +19,7 @@ from app.db.crud import create_log from fastapi.responses import JSONResponse import asyncio + Base.metadata.create_all(bind=engine) app = FastAPI( @@ -36,6 +38,8 @@ app.add_middleware( allow_headers=["*"], ) +add_pagination(app) + # @app.middleware("http") # async def db_session_middleware(request: Request, call_next): # request.state.db = SessionLocal() From f3b93dc4262a22839b5edb7dab8b1213942c0c3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=20=E6=9F=8F?= Date: Sun, 1 Dec 2024 11:40:46 +0900 Subject: [PATCH 02/24] bugfix delete_domain --- backend/app/api/api_v1/routers/platform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/api/api_v1/routers/platform.py b/backend/app/api/api_v1/routers/platform.py index f5f876c..f4a1075 100644 --- a/backend/app/api/api_v1/routers/platform.py +++ b/backend/app/api/api_v1/routers/platform.py @@ -320,7 +320,7 @@ async def domain_edit( @r.delete( "/domain/{id}",tags=["Domain"], - response_model=ApiReturnModel[Domain|None], + response_model=ApiReturnModel[DomainOut|None], response_model_exclude_none=True, ) async def domain_delete( From 647a5f4b8e3f22dcfce2babb7aa2c9a24aa78a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=20=E6=9F=8F?= Date: Sun, 1 Dec 2024 11:46:23 +0900 Subject: [PATCH 03/24] delete create_domain repeat check --- backend/app/db/cruddb/dbdomain.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/app/db/cruddb/dbdomain.py b/backend/app/db/cruddb/dbdomain.py index 5b8178c..de8af6c 100644 --- a/backend/app/db/cruddb/dbdomain.py +++ b/backend/app/db/cruddb/dbdomain.py @@ -42,15 +42,15 @@ class dbdomain(crudbase): return paginate( super().get_by_conditions(db,{"ownerid":ownerid})) def create_domain(self,db: Session, domain: schemas.DomainIn,userid:int): - db_domain = super().get_by_conditions(db,{"url":domain.url,"kintoneuser":domain.kintoneuser,"onwerid":userid}).first() - if not db_domain: - domain.encrypt_kintonepwd() - domain.id = None - domain.createuserid = userid - domain.updateuserid = userid - domain.ownerid = userid - return super().create(db,domain) - return db_domain + #db_domain = super().get_by_conditions(db,{"url":domain.url,"kintoneuser":domain.kintoneuser,"onwerid":userid}).first() + #if not db_domain: + domain.encrypt_kintonepwd() + domain.id = None + domain.createuserid = userid + domain.updateuserid = userid + domain.ownerid = userid + return super().create(db,domain) + #return db_domain def delete_domain(self,db: Session,id: int): return super().delete(db,id) From 39b02e0a8ed707faec18c1d2b5cba8ed0edbc8d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=20=E6=9F=8F?= Date: Sun, 1 Dec 2024 12:01:31 +0900 Subject: [PATCH 04/24] get_activedomain->dbdomain.get_default_domain --- backend/app/api/api_v1/routers/platform.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/app/api/api_v1/routers/platform.py b/backend/app/api/api_v1/routers/platform.py index f4a1075..118a7aa 100644 --- a/backend/app/api/api_v1/routers/platform.py +++ b/backend/app/api/api_v1/routers/platform.py @@ -42,7 +42,7 @@ async def apps_list( ): try: - domain = get_activedomain(db, user.id) + domain = dbdomain.get_default_domain(db,user.id) #get_activedomain(db, user.id) platformapps = get_apps(db,domain.url) kintoneevn = config.KINTONE_ENV(domain) headers={config.API_V1_AUTH_KEY:kintoneevn.API_V1_AUTH_VALUE} @@ -215,7 +215,7 @@ async def flow_list( db=Depends(get_db), ): try: - domain = get_activedomain(db, user.id) + domain = dbdomain.get_default_domain(db, user.id) #get_activedomain(db, user.id) print("domain=>",domain) flows = get_flows_by_app(db, domain.url, appid) return flows @@ -231,7 +231,7 @@ async def flow_create( db=Depends(get_db), ): try: - domain = get_activedomain(db, user.id) + domain = dbdomain.get_default_domain(db, user.id) #get_activedomain(db, user.id) return create_flow(db, domain.url, flow) except Exception as e: raise APIException('platform:flow',request.url._url,f"Error occurred while create flow:",e) @@ -248,7 +248,7 @@ async def flow_edit( db=Depends(get_db), ): try: - domain = get_activedomain(db, user.id) + domain = dbdomain.get_default_domain(db, user.id) #get_activedomain(db, user.id) return edit_flow(db,domain.url, flow,user.id) except Exception as e: raise APIException('platform:flow',request.url._url,f"Error occurred while edit flow:",e) From 8ee013527a006fe3af885068673424c117869ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=20=E6=9F=8F?= Date: Sun, 1 Dec 2024 12:21:13 +0900 Subject: [PATCH 05/24] bugfix get_default_domain --- backend/app/api/api_v1/routers/platform.py | 25 ++++++++++++---------- backend/app/db/cruddb/dbdomain.py | 8 +++++-- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/backend/app/api/api_v1/routers/platform.py b/backend/app/api/api_v1/routers/platform.py index 118a7aa..8285a70 100644 --- a/backend/app/api/api_v1/routers/platform.py +++ b/backend/app/api/api_v1/routers/platform.py @@ -41,8 +41,11 @@ async def apps_list( db=Depends(get_db), ): try: - + filtered_apps = [] domain = dbdomain.get_default_domain(db,user.id) #get_activedomain(db, user.id) + if not domain: + return filtered_apps + platformapps = get_apps(db,domain.url) kintoneevn = config.KINTONE_ENV(domain) headers={config.API_V1_AUTH_KEY:kintoneevn.API_V1_AUTH_VALUE} @@ -61,7 +64,6 @@ async def apps_list( offset += limit kintone_apps_dict = {app['appId']: app for app in all_apps} - filtered_apps = [] for papp in platformapps: if papp.appid in kintone_apps_dict: papp.appname = kintone_apps_dict[papp.appid]["name"] @@ -205,7 +207,7 @@ async def flow_details( @r.get( "/flows/{appid}", - response_model=List[Flow], + response_model=List[Flow|None], response_model_exclude_none=True, ) async def flow_list( @@ -216,6 +218,8 @@ async def flow_list( ): try: domain = dbdomain.get_default_domain(db, user.id) #get_activedomain(db, user.id) + if not domain: + return [] print("domain=>",domain) flows = get_flows_by_app(db, domain.url, appid) return flows @@ -223,7 +227,7 @@ async def flow_list( raise APIException('platform:flow',request.url._url,f"Error occurred while get flow by appid:",e) -@r.post("/flow", response_model=Flow, response_model_exclude_none=True) +@r.post("/flow", response_model=Flow|None, response_model_exclude_none=True) async def flow_create( request: Request, flow: FlowIn, @@ -232,13 +236,15 @@ async def flow_create( ): try: domain = dbdomain.get_default_domain(db, user.id) #get_activedomain(db, user.id) + if not domain: + return None return create_flow(db, domain.url, flow) except Exception as e: raise APIException('platform:flow',request.url._url,f"Error occurred while create flow:",e) @r.put( - "/flow/{flowid}", response_model=Flow, response_model_exclude_none=True + "/flow/{flowid}", response_model=Flow|None, response_model_exclude_none=True ) async def flow_edit( request: Request, @@ -249,6 +255,8 @@ async def flow_edit( ): try: domain = dbdomain.get_default_domain(db, user.id) #get_activedomain(db, user.id) + if not domain: + return None return edit_flow(db,domain.url, flow,user.id) except Exception as e: raise APIException('platform:flow',request.url._url,f"Error occurred while edit flow:",e) @@ -400,12 +408,7 @@ async def get_defaultuserdomain( db=Depends(get_db), ): try: - userdomain = dbdomain.get_default_domain(db, user.id) - if userdomain: - return ApiReturnModel(data = userdomain.domain) - else: - return ApiReturnModel(data = None) - + return ApiReturnModel(data =dbdomain.get_default_domain(db, user.id)) except Exception as e: raise APIException('platform:defaultdomain',request.url._url,f"Error occurred while get user({user.id}) defaultdomain:",e) diff --git a/backend/app/db/cruddb/dbdomain.py b/backend/app/db/cruddb/dbdomain.py index de8af6c..ff0a189 100644 --- a/backend/app/db/cruddb/dbdomain.py +++ b/backend/app/db/cruddb/dbdomain.py @@ -109,8 +109,12 @@ class dbdomain(crudbase): return domain return None - def get_default_domain(self,db: Session, userid: int) -> schemas.UserDomain: - return dbuserdomain.get_user_default_domain(db,userid) + def get_default_domain(self,db: Session, userid: int) -> schemas.DomainOut: + userdomain = dbuserdomain.get_user_default_domain(db,userid) + if userdomain: + return userdomain.domain + else: + return None def set_default_domain(self,db: Session, userid: int,domainid: int): db_domain =super().get_by_conditions(db,{"id":domainid,"is_active":True}).first() From d23e16d1ebc0dc4a99d3a58dd923018aabf3a070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=20=E6=9F=8F?= Date: Sun, 1 Dec 2024 12:36:46 +0900 Subject: [PATCH 06/24] kintone: get_activedomain->get_default_domain --- backend/app/api/api_v1/routers/kintone.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/app/api/api_v1/routers/kintone.py b/backend/app/api/api_v1/routers/kintone.py index b1c826a..e85e443 100644 --- a/backend/app/api/api_v1/routers/kintone.py +++ b/backend/app/api/api_v1/routers/kintone.py @@ -9,15 +9,16 @@ import app.core.config as config import os from pathlib import Path from app.db.session import SessionLocal -from app.db.crud import get_flows_by_app,get_activedomain,get_kintoneformat +from app.db.crud import get_flows_by_app,get_kintoneformat from app.core.auth import get_current_active_user,get_current_user from app.core.apiexception import APIException +from app.db.cruddb.dbdomain import get_default_domain kinton_router = r = APIRouter() def getkintoneenv(user = Depends(get_current_user)): db = SessionLocal() - domain = get_activedomain(db, user.id) + domain = get_default_domain(db,user.id) #get_activedomain(db, user.id) db.close() kintoneevn = config.KINTONE_ENV(domain) return kintoneevn From ff46485498c031b5d58f527670c2a6d778f67cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=20=E6=9F=8F?= Date: Sun, 1 Dec 2024 12:42:14 +0900 Subject: [PATCH 07/24] bugfix kintone:get_default_domain --- backend/app/api/api_v1/routers/kintone.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/app/api/api_v1/routers/kintone.py b/backend/app/api/api_v1/routers/kintone.py index e85e443..ff95f1f 100644 --- a/backend/app/api/api_v1/routers/kintone.py +++ b/backend/app/api/api_v1/routers/kintone.py @@ -12,13 +12,13 @@ from app.db.session import SessionLocal from app.db.crud import get_flows_by_app,get_kintoneformat from app.core.auth import get_current_active_user,get_current_user from app.core.apiexception import APIException -from app.db.cruddb.dbdomain import get_default_domain +from app.db.cruddb.dbdomain import dbdomain kinton_router = r = APIRouter() def getkintoneenv(user = Depends(get_current_user)): db = SessionLocal() - domain = get_default_domain(db,user.id) #get_activedomain(db, user.id) + domain = dbdomain.get_default_domain(db,user.id) #get_activedomain(db, user.id) db.close() kintoneevn = config.KINTONE_ENV(domain) return kintoneevn From e3d842de159393b9ebee8ca6800f47c43f1fea69 Mon Sep 17 00:00:00 2001 From: xue jiahao Date: Mon, 2 Dec 2024 12:52:59 +0800 Subject: [PATCH 08/24] Fix api call and delete tenantid on domain page --- frontend/src/pages/TenantDomain.vue | 35 ++++++++++++++------------- frontend/src/pages/UserDomain.vue | 3 +-- frontend/src/pages/UserManagement.vue | 7 ++++-- frontend/src/stores/useAuthStore.ts | 4 +-- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/frontend/src/pages/TenantDomain.vue b/frontend/src/pages/TenantDomain.vue index 2b5e32e..3564ff5 100644 --- a/frontend/src/pages/TenantDomain.vue +++ b/frontend/src/pages/TenantDomain.vue @@ -46,10 +46,6 @@
- - - @@ -103,7 +99,7 @@ - + @@ -141,23 +137,25 @@ const inactiveRowClass = (row: IDomainDisplay) => row.domainActive ? '' : 'inact const columns = [ { name: 'id', label: 'ID', field: 'id', align: 'left', sortable: true, classes: inactiveRowClass }, - { - name: 'tenantid', - required: true, - label: 'テナントID', - field: 'tenantid', - align: 'left', - sortable: true, - classes: inactiveRowClass - }, + // { + // name: 'tenantid', + // required: true, + // label: 'テナントID', + // field: 'tenantid', + // align: 'left', + // sortable: true, + // classes: inactiveRowClass + // }, { name: 'name', label: '環境名', field: 'name', align: 'left', sortable: true, classes: inactiveRowClass }, { name: 'url', label: 'URL', field: 'url', align: 'left', sortable: true, classes: inactiveRowClass }, + // { name: 'owner', label: '所有者', field: 'owner', align: 'left', classes: inactiveRowClass }, { name: 'user', label: 'ログイン名', field: 'user', align: 'left', classes: inactiveRowClass }, { name: 'actions', label: '操作', field: 'actions' } ]; const pagination = ref({ sortBy: 'id', descending: true, rowsPerPage: 20 }); const loading = ref(false); +const addEditLoading = ref(false); const filter = ref(''); const rows = ref([]); const show = ref(false); @@ -165,7 +163,7 @@ const confirm = ref(false); const resetPsw = ref(false); const currentDomainId = computed(() => authStore.currentDomain.id); -const tenantid = ref(authStore.currentDomain.id); +// const tenantid = ref(authStore.currentDomain.id); const name = ref(''); const url = ref(''); const isPwd = ref(true); @@ -221,7 +219,7 @@ const deleteDomain = () => { const editRow = (row) => { isCreate.value = false editId.value = row.id; - tenantid.value = row.tenantid; + // tenantid.value = row.tenantid; name.value = row.name; url.value = row.url; kintoneuser.value = row.user; @@ -246,10 +244,11 @@ const closeDg = () => { } const onSubmit = () => { + addEditLoading.value = true; const method = editId.value !== 0 ? 'put' : 'post'; const param: IDomainSubmit = { 'id': editId.value, - 'tenantid': tenantid.value, + 'tenantid': '1', // TODO: テナントIDを取得する 'name': name.value, 'url': url.value, 'kintoneuser': kintoneuser.value, @@ -263,6 +262,7 @@ const onSubmit = () => { domainStore.loadUserDomains(); closeDg(); onReset(); + addEditLoading.value = false; }) } @@ -277,6 +277,7 @@ const onReset = () => { isCreate.value = true; domainActive.value = true; resetPsw.value = false + addEditLoading.value = false; } \ No newline at end of file From 660ffe36c21519d06b00e11c8d2c984b49752109 Mon Sep 17 00:00:00 2001 From: xue jiahao Date: Mon, 2 Dec 2024 16:21:23 +0800 Subject: [PATCH 11/24] Add shared dialog --- .../components/Domain/ShareDomainDialog.vue | 186 ++++++++++++++++ .../ShareDomain/ShareDomainDialog.vue | 202 ++++++++++++++++++ .../ShareDomain/SharingUserList.vue | 41 ++++ .../src/components/UserDomain/DomainCard.vue | 56 +++++ frontend/src/pages/TenantDomain.vue | 28 ++- frontend/src/pages/UserDomain.vue | 39 +--- frontend/src/types/UserTypes.ts | 15 +- 7 files changed, 532 insertions(+), 35 deletions(-) create mode 100644 frontend/src/components/Domain/ShareDomainDialog.vue create mode 100644 frontend/src/components/ShareDomain/ShareDomainDialog.vue create mode 100644 frontend/src/components/ShareDomain/SharingUserList.vue create mode 100644 frontend/src/components/UserDomain/DomainCard.vue diff --git a/frontend/src/components/Domain/ShareDomainDialog.vue b/frontend/src/components/Domain/ShareDomainDialog.vue new file mode 100644 index 0000000..0b3aa49 --- /dev/null +++ b/frontend/src/components/Domain/ShareDomainDialog.vue @@ -0,0 +1,186 @@ + + + + + diff --git a/frontend/src/components/ShareDomain/ShareDomainDialog.vue b/frontend/src/components/ShareDomain/ShareDomainDialog.vue new file mode 100644 index 0000000..8d2738c --- /dev/null +++ b/frontend/src/components/ShareDomain/ShareDomainDialog.vue @@ -0,0 +1,202 @@ + + + + + diff --git a/frontend/src/components/ShareDomain/SharingUserList.vue b/frontend/src/components/ShareDomain/SharingUserList.vue new file mode 100644 index 0000000..ed85c32 --- /dev/null +++ b/frontend/src/components/ShareDomain/SharingUserList.vue @@ -0,0 +1,41 @@ + + \ No newline at end of file diff --git a/frontend/src/components/UserDomain/DomainCard.vue b/frontend/src/components/UserDomain/DomainCard.vue new file mode 100644 index 0000000..144d7ff --- /dev/null +++ b/frontend/src/components/UserDomain/DomainCard.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/frontend/src/pages/TenantDomain.vue b/frontend/src/pages/TenantDomain.vue index 3564ff5..8e041a0 100644 --- a/frontend/src/pages/TenantDomain.vue +++ b/frontend/src/pages/TenantDomain.vue @@ -19,7 +19,10 @@ @@ -129,6 +135,7 @@ import { ref, onMounted, computed } from 'vue'; import { api } from 'boot/axios'; import { useAuthStore } from 'stores/useAuthStore'; import { useDomainStore } from 'stores/useDomainStore'; +import ShareDomainDialog from 'components/ShareDomain/ShareDomainDialog.vue'; import { IDomain, IDomainDisplay, IDomainSubmit } from '../types/DomainTypes'; const authStore = useAuthStore(); @@ -150,7 +157,7 @@ const columns = [ { name: 'url', label: 'URL', field: 'url', align: 'left', sortable: true, classes: inactiveRowClass }, // { name: 'owner', label: '所有者', field: 'owner', align: 'left', classes: inactiveRowClass }, { name: 'user', label: 'ログイン名', field: 'user', align: 'left', classes: inactiveRowClass }, - { name: 'actions', label: '操作', field: 'actions' } + { name: 'actions', label: '操作', field: 'actions', classes: inactiveRowClass } ]; const pagination = ref({ sortBy: 'id', descending: true, rowsPerPage: 20 }); @@ -174,6 +181,8 @@ const domainActive = ref(true); const isCreate = ref(true); let editId = ref(0); let ownerid = ref(''); +const shareDg = ref(false); +const shareDomain = ref({}); const getDomain = async () => { loading.value = true; @@ -209,7 +218,10 @@ const removeRow = (row: IDomainDisplay) => { } const deleteDomain = () => { - api.delete(`api/domain/${editId.value}`).then(() => { + api.delete(`api/domain/${editId.value}`).then(({ data }) => { + if (!data.data) { + // TODO dialog + } getDomain(); // authStore.setCurrentDomain(); }) @@ -266,6 +278,15 @@ const onSubmit = () => { }) } +const openShareDg = (row: IDomainDisplay) => { + shareDomain.value = row; + shareDg.value = true; +}; + +const closeShareDg = () => { + shareDg.value = false; +} + const onReset = () => { name.value = ''; url.value = ''; @@ -283,5 +304,6 @@ const onReset = () => { \ No newline at end of file diff --git a/frontend/src/pages/UserDomain.vue b/frontend/src/pages/UserDomain.vue index e4ba99c..4d0edb5 100644 --- a/frontend/src/pages/UserDomain.vue +++ b/frontend/src/pages/UserDomain.vue @@ -28,32 +28,8 @@ @@ -94,6 +70,7 @@ import { api } from 'boot/axios'; import { useAuthStore } from 'stores/useAuthStore'; import ShowDialog from 'components/ShowDialog.vue'; +import DomainCard from 'components/UserDomain/DomainCard.vue'; import DomainSelect from 'components/DomainSelect.vue'; const authStore = useAuthStore(); @@ -129,9 +106,9 @@ const clickAddDomain = () => { const addUserDomainFinished = async (val: string) => { showAddDomainDg.value = true; - addUserDomainLoading.value = true; const selected = addDomainRef.value.selected; if (val == 'OK' && selected.length > 0) { + addUserDomainLoading.value = true; const { data } = await api.post(`api/domain/${authStore.userId}?domainid=${selected[0].id}`) if (rows.value.length === 0 && data.data) { const domain = data.data; @@ -142,9 +119,9 @@ const addUserDomainFinished = async (val: string) => { }); } await getDomain(); - addUserDomainLoading.value = false; - showAddDomainDg.value = false; } + addUserDomainLoading.value = false; + showAddDomainDg.value = false; }; const showDeleteConfirm = ref(false); diff --git a/frontend/src/types/UserTypes.ts b/frontend/src/types/UserTypes.ts index 54250b2..cf7b275 100644 --- a/frontend/src/types/UserTypes.ts +++ b/frontend/src/types/UserTypes.ts @@ -1,8 +1,21 @@ export interface IUser { + id: number; first_name: string; last_name: string; email: string; is_active: boolean, is_superuser: boolean, - roles: string[] + roles: object[] } + +export interface IUserDisplay { + id: number; + firstName: string; + lastName: string; + fullName: string; + fullNameSearch: string; + email: string; + isActive: boolean, + isSuperuser: boolean, + roles: object[] +} \ No newline at end of file From dcfe0d44fdf2dac7c6b9dca93fdfe3cdcc107333 Mon Sep 17 00:00:00 2001 From: xue jiahao Date: Tue, 3 Dec 2024 08:31:00 +0800 Subject: [PATCH 12/24] add delete hint --- .../components/Domain/ShareDomainDialog.vue | 6 -- .../ShareDomain/ShareDomainDialog.vue | 20 ++---- .../ShareDomain/SharingUserList.vue | 12 ++-- .../src/components/UserDomain/DomainCard.vue | 72 ++++++++++--------- frontend/src/pages/TenantDomain.vue | 29 ++++++-- frontend/src/pages/UserDomain.vue | 24 ++++--- 6 files changed, 90 insertions(+), 73 deletions(-) diff --git a/frontend/src/components/Domain/ShareDomainDialog.vue b/frontend/src/components/Domain/ShareDomainDialog.vue index 0b3aa49..30dfe15 100644 --- a/frontend/src/components/Domain/ShareDomainDialog.vue +++ b/frontend/src/components/Domain/ShareDomainDialog.vue @@ -7,12 +7,6 @@ - - - {{ props.domain.url }} - - - + @@ -38,12 +33,6 @@ - -