Compare commits

..

6 Commits

Author SHA1 Message Date
xiaozhe.ma
1626091e36 backend変更マージ 2024-11-23 18:36:01 +09:00
fa1d3b01b0 app&appversion&flowhistory&role&permission 2024-11-22 15:19:49 +09:00
xiaozhe.ma
4563274789 backend bug fix 2024-11-20 15:09:45 +09:00
xue jiahao
3b9f08b43d Merged PR 6: [bugfix] id format error when saving flow
[bugfix] id format error when saving flow
2024-11-19 04:03:07 +00:00
xue jiahao
4c8cc1def9 [bugfix] id format error when saving flow 2024-11-19 11:25:55 +08:00
xue jiahao
7284f982a3 Merged PR 5: some fix for apps management page
1. 修改了 /apps 下的时间列格式
2. 修复了 /apps 下切换 domain 时更新 table
3. 修复了 /apps 下的 id 排序(使用数值,而非字符串字典序)
4. /flowChart 添加 id,从而在页面上支持刷新
5. /flowChart 添加了返回按钮

---

# 更新:
1. /flowChart 更新了面包屑导航
2. /flowChart 下禁止切换 domain

![image (7).png](https://dev.azure.com/alicorn-dev/96136197-fa1c-44c2-b522-b9ab8b541f34/_apis/git/repositories/11e363ac-4aa8-4076-9a9a-eaac160866ff/pullRequests/5/attachments/image%20%287%29.png)

Related work items: #63, #64
2024-11-19 01:00:14 +00:00
11 changed files with 353 additions and 155 deletions

View File

@@ -25,11 +25,15 @@ async def login(
minutes=security.ACCESS_TOKEN_EXPIRE_MINUTES
)
if user.is_superuser:
permissions = "admin"
roles = "super"
permissions = "ALL"
else:
permissions = "user"
roles = ";".join(role.name for role in user.roles)
perlst = [perm.privilege for role in user.roles for perm in role.permissions]
permissions =";".join(list(set(perlst)))
access_token = security.create_access_token(
data={"sub": user.id, "permissions": permissions},
data={"sub": user.id, "roles":roles,"permissions": permissions ,},
expires_delta=access_token_expires,
)

View File

@@ -156,10 +156,10 @@ def getsettingfromexcel(df):
des = df.iloc[2,2]
return {"name":appname,"description":des}
def getsettingfromkintone(app:str,c:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
def getsettingfromkintone(app:str,env:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE}
params = {"app":app}
url = f"{c.BASE_URL}{config.API_V1_STR}/app/settings.json"
url = f"{env.BASE_URL}{config.API_V1_STR}/app/settings.json"
r = httpx.get(url,headers=headers,params=params)
return r.json()
@@ -171,24 +171,24 @@ def analysesettings(excel,kintone):
updatesettings[key] = excel[key]
return updatesettings
def createkintoneapp(name:str,c:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
def createkintoneapp(name:str,env:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
data = {"name":name}
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app.json"
url = f"{env.BASE_URL}{config.API_V1_STR}/preview/app.json"
r = httpx.post(url,headers=headers,data=json.dumps(data))
return r.json()
def updateappsettingstokintone(app:str,updates:dict,c:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/settings.json"
def updateappsettingstokintone(app:str,updates:dict,env:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
url = f"{env.BASE_URL}{config.API_V1_STR}/preview/app/settings.json"
data = {"app":app}
data.update(updates)
r = httpx.put(url,headers=headers,data=json.dumps(data))
return r.json()
def addfieldstokintone(app:str,fields:dict,c:config.KINTONE_ENV,revision:str = None):
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/form/fields.json"
def addfieldstokintone(app:str,fields:dict,env:config.KINTONE_ENV,revision:str = None):
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
url = f"{env.BASE_URL}{config.API_V1_STR}/preview/app/form/fields.json"
if revision != None:
data = {"app":app,"revision":revision,"properties":fields}
else:
@@ -197,43 +197,43 @@ def addfieldstokintone(app:str,fields:dict,c:config.KINTONE_ENV,revision:str = N
r.raise_for_status()
return r.json()
def updatefieldstokintone(app:str,revision:str,fields:dict,c:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/form/fields.json"
def updatefieldstokintone(app:str,revision:str,fields:dict,env:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
url = f"{env.BASE_URL}{config.API_V1_STR}/preview/app/form/fields.json"
data = {"app":app,"properties":fields}
r = httpx.put(url,headers=headers,data=json.dumps(data))
return r.json()
def deletefieldsfromkintone(app:str,revision:str,fields:dict,c:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/form/fields.json"
def deletefieldsfromkintone(app:str,revision:str,fields:dict,env:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
url = f"{env.BASE_URL}{config.API_V1_STR}/preview/app/form/fields.json"
params = {"app":app,"revision":revision,"fields":fields}
#r = httpx.delete(url,headers=headers,content=json.dumps(params))
r = httpx.request(method="DELETE",url=url,headers=headers,content=json.dumps(params))
return r.json()
def deoployappfromkintone(app:str,revision:str,c:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/deploy.json"
def deoployappfromkintone(app:str,revision:str,env:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
url = f"{env.BASE_URL}{config.API_V1_STR}/preview/app/deploy.json"
data = {"apps":[{"app":app,"revision":revision}],"revert": False}
r = httpx.post(url,headers=headers,data=json.dumps(data))
return r.json
# 既定項目に含めるアプリのフィールドのみ取得する
# スペース、枠線、ラベルを含まない
def getfieldsfromkintone(app:str,c:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
def getfieldsfromkintone(app:str,env:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE}
params = {"app":app}
url = f"{c.BASE_URL}{config.API_V1_STR}/app/form/fields.json"
url = f"{env.BASE_URL}{config.API_V1_STR}/app/form/fields.json"
r = httpx.get(url,headers=headers,params=params)
return r.json()
# フォームに配置するフィールドのみ取得する
# スペース、枠線、ラベルも含める
def getformfromkintone(app:str,c:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
def getformfromkintone(app:str,env:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE}
params = {"app":app}
url = f"{c.BASE_URL}{config.API_V1_STR}/form.json"
url = f"{env.BASE_URL}{config.API_V1_STR}/form.json"
r = httpx.get(url,headers=headers,params=params)
return r.json()
@@ -286,10 +286,10 @@ def analysefields(excel,kintone):
return {"update":updatefields,"add":addfields,"del":delfields}
def getprocessfromkintone(app:str,c:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
def getprocessfromkintone(app:str,env:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE}
params = {"app":app}
url = f"{c.BASE_URL}{config.API_V1_STR}/app/status.json"
url = f"{env.BASE_URL}{config.API_V1_STR}/app/status.json"
r = httpx.get(url,headers=headers,params=params)
return r.json()
@@ -374,24 +374,24 @@ def getkintoneorgs(c:config.KINTONE_ENV):
r = httpx.get(url,headers=headers,params=params)
return r.json()
def uploadkintonefiles(file,c:config.KINTONE_ENV):
def uploadkintonefiles(file,env:config.KINTONE_ENV):
if (file.endswith('alc_runtime.js') and config.DEPLOY_MODE == "DEV"):
return {'fileKey':file}
upload_files = {'file': open(file,'rb')}
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE}
data ={'name':'file','filename':os.path.basename(file)}
url = f"{c.BASE_URL}/k/v1/file.json"
url = f"{env.BASE_URL}/k/v1/file.json"
r = httpx.post(url,headers=headers,data=data,files=upload_files)
#{"name":data['filename'],'fileKey':r['fileKey']}
return r.json()
def updateappjscss(app,uploads,c:config.KINTONE_ENV):
def updateappjscss(app,uploads,env:config.KINTONE_ENV):
dsjs = []
dscss = []
#mobile側
mbjs = []
mbcss = []
customize = getappcustomize(app, c)
customize = getappcustomize(app, env)
current_js = customize['desktop'].get('js', [])
current_css = customize['desktop'].get('css', [])
current_mobile_js = customize['mobile'].get('js', [])
@@ -430,16 +430,16 @@ def updateappjscss(app,uploads,c:config.KINTONE_ENV):
ds ={'js':dsjs,'css':dscss}
mb ={'js':mbjs,'css':mbcss}
data = {'app':app,'scope':'ALL','desktop':ds,'mobile':mb,'revision':customize["revision"]}
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/customize.json"
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
url = f"{env.BASE_URL}{config.API_V1_STR}/preview/app/customize.json"
print(json.dumps(data))
r = httpx.put(url,headers=headers,data=json.dumps(data))
return r.json()
#kintone カスタマイズ情報
def getappcustomize(app,c:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/customize.json"
def getappcustomize(app,env:config.KINTONE_ENV):
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE}
url = f"{env.BASE_URL}{config.API_V1_STR}/preview/app/customize.json"
params = {"app":app}
r = httpx.get(url,headers=headers,params=params)
return r.json()
@@ -451,9 +451,9 @@ def getTempPath(filename):
fpath = os.path.join(rootdir,"Temp",filename)
return fpath
def createappjs(domainid,app):
def createappjs(domain_url,app):
db = SessionLocal()
flows = get_flows_by_app(db,domainid,app)
flows = get_flows_by_app(db,domain_url,app)
db.close()
content={}
for flow in flows:
@@ -521,7 +521,7 @@ async def upload(request:Request,files:t.List[UploadFile] = File(...)):
return {"files": [file.filename for file in files]}
@r.post("/updatejscss")
async def jscss(request:Request,app:str,files:t.List[UploadFile] = File(...),env = Depends(getkintoneenv)):
async def jscss(request:Request,app:str,files:t.List[UploadFile] = File(...),env:config.KINTONE_ENV = Depends(getkintoneenv)):
try:
jscs=[]
for file in files:
@@ -542,21 +542,21 @@ async def jscss(request:Request,app:str,files:t.List[UploadFile] = File(...),env
raise APIException('kintone:updatejscss',request.url._url, f"Error occurred while update js/css {file.filename} is not an Excel file",e)
@r.get("/app")
async def app(request:Request,app:str,c:config.KINTONE_ENV=Depends(getkintoneenv)):
async def app(request:Request,app:str,env:config.KINTONE_ENV=Depends(getkintoneenv)):
try:
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
url = f"{c.BASE_URL}{config.API_V1_STR}/app.json"
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE}
url = f"{env.BASE_URL}{config.API_V1_STR}/app.json"
params ={"id":app}
r = httpx.get(url,headers=headers,params=params)
return r.json()
except Exception as e:
raise APIException('kintone:app',request.url._url, f"Error occurred while get app({c.DOMAIN_NAME}->{app}):",e)
raise APIException('kintone:app',request.url._url, f"Error occurred while get app({env.DOMAIN_NAME}->{app}):",e)
@r.get("/allapps")
async def allapps(request:Request,c:config.KINTONE_ENV=Depends(getkintoneenv)):
async def allapps(request:Request,env:config.KINTONE_ENV=Depends(getkintoneenv)):
try:
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
url = f"{c.BASE_URL}{config.API_V1_STR}/apps.json"
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE}
url = f"{env.BASE_URL}{config.API_V1_STR}/apps.json"
offset = 0
limit = 100
all_apps = []
@@ -572,17 +572,17 @@ async def allapps(request:Request,c:config.KINTONE_ENV=Depends(getkintoneenv)):
return {"apps": all_apps}
except Exception as e:
raise APIException('kintone:allapps', request.url._url, f"Error occurred while get allapps({c.DOMAIN_NAME}):", e)
raise APIException('kintone:allapps', request.url._url, f"Error occurred while get allapps({env.DOMAIN_NAME}):", e)
@r.get("/appfields")
async def appfields(request:Request,app:str,env = Depends(getkintoneenv)):
async def appfields(request:Request,app:str,env:config.KINTONE_ENV = Depends(getkintoneenv)):
try:
return getfieldsfromkintone(app,env)
except Exception as e:
raise APIException('kintone:appfields',request.url._url, f"Error occurred while get app fileds({env.DOMAIN_NAME}->{app}):",e)
@r.get("/allfields")
async def allfields(request:Request,app:str,env = Depends(getkintoneenv)):
async def allfields(request:Request,app:str,env:config.KINTONE_ENV = Depends(getkintoneenv)):
try:
field_resp = getfieldsfromkintone(app,env)
form_resp = getformfromkintone(app,env)
@@ -591,38 +591,38 @@ async def allfields(request:Request,app:str,env = Depends(getkintoneenv)):
raise APIException('kintone:allfields',request.url._url, f"Error occurred while get form fileds({env.DOMAIN_NAME}->{app}):",e)
@r.get("/appprocess")
async def appprocess(request:Request,app:str,env = Depends(getkintoneenv)):
async def appprocess(request:Request,app:str,env:config.KINTONE_ENV = Depends(getkintoneenv)):
try:
return getprocessfromkintone(app,env)
except Exception as e:
raise APIException('kintone:appprocess',request.url._url, f"Error occurred while get app process({env.DOMAIN_NAME}->{app}):",e)
@r.get("/alljscss")
async def alljscs(request:Request,app:str,c:config.KINTONE_ENV=Depends(getkintoneenv)):
async def alljscs(request:Request,app:str,env:config.KINTONE_ENV=Depends(getkintoneenv)):
try:
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
url = f"{c.BASE_URL}{config.API_V1_STR}/app/customize.json"
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE}
url = f"{env.BASE_URL}{config.API_V1_STR}/app/customize.json"
params = {"app":app}
r = httpx.get(url,headers=headers,params=params)
return r.json()
except Exception as e:
raise APIException('kintone:alljscss',request.url._url, f"Error occurred while get app js/css({c.DOMAIN_NAME}->{app}):",e)
raise APIException('kintone:alljscss',request.url._url, f"Error occurred while get app js/css({env.DOMAIN_NAME}->{app}):",e)
@r.post("/createapp",)
async def createapp(request:Request,name:str,c:config.KINTONE_ENV=Depends(getkintoneenv)):
async def createapp(request:Request,name:str,env:config.KINTONE_ENV=Depends(getkintoneenv)):
try:
headers={config.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
headers={config.API_V1_AUTH_KEY:env.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
data = {"name":name}
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app.json"
url = f"{env.BASE_URL}{config.API_V1_STR}/preview/app.json"
r = httpx.post(url,headers=headers,data=json.dumps(data))
result = r.json()
if result.get("app") != None:
url = f"{c.BASE_URL}{config.API_V1_STR}/preview/app/deploy.json"
url = f"{env.BASE_URL}{config.API_V1_STR}/preview/app/deploy.json"
data = {"apps":[result],"revert": False}
r = httpx.post(url,headers=headers,data=json.dumps(data))
return r.json
except Exception as e:
raise APIException('kintone:createapp',request.url._url, f"Error occurred while create app({c.DOMAIN_NAME}->{name}):",e)
raise APIException('kintone:createapp',request.url._url, f"Error occurred while create app({env.DOMAIN_NAME}->{name}):",e)
@r.post("/createappfromexcel",)
@@ -761,7 +761,7 @@ async def createjstokintone(request:Request,app:str,env:config.KINTONE_ENV = Dep
try:
jscs=[]
files=[]
files.append(createappjs(env.DOMAIN_ID, app))
files.append(createappjs(env.BASE_URL, app))
files.append(getTempPath('alc_runtime.js'))
files.append(getTempPath('alc_runtime.css'))
for file in files:

View File

@@ -23,7 +23,7 @@ platform_router = r = APIRouter()
)
async def apps_list(
request: Request,
user = Depends(get_current_user),
user = Depends(get_current_active_user),
db=Depends(get_db),
):
try:
@@ -60,7 +60,7 @@ async def apps_list(
async def apps_update(
request: Request,
app: AppVersion,
user=Depends(get_current_user),
user=Depends(get_current_active_user),
db=Depends(get_db),
):
try:
@@ -68,7 +68,21 @@ async def apps_update(
except Exception as e:
raise APIException('platform:apps',request.url._url,f"Error occurred while get create app :",e)
@r.delete(
"/apps/{domainurl}/{appid}", response_model_exclude_none=True
)
async def apps_delete(
request: Request,
domainurl:str,
appid: str,
user=Depends(get_current_active_user),
db=Depends(get_db),
):
try:
return delete_apps(db, domainurl,appid)
except Exception as e:
raise APIException('platform:apps',request.url._url,f"Error occurred while delete apps({domainurl}:{appid}):",e)
@r.get(
"/appsettings/{id}",
response_model=App,
@@ -183,7 +197,7 @@ async def flow_details(
async def flow_list(
request: Request,
appid: str,
user=Depends(get_current_user),
user=Depends(get_current_active_user),
db=Depends(get_db),
):
try:
@@ -198,8 +212,8 @@ async def flow_list(
@r.post("/flow", response_model=Flow, response_model_exclude_none=True)
async def flow_create(
request: Request,
flow: FlowBase,
user=Depends(get_current_user),
flow: FlowIn,
user=Depends(get_current_active_user),
db=Depends(get_db),
):
try:
@@ -214,11 +228,14 @@ async def flow_create(
)
async def flow_edit(
request: Request,
flow: FlowBase,
flowid: str,
flow: FlowIn,
user=Depends(get_current_active_user),
db=Depends(get_db),
):
try:
return edit_flow(db, flow)
domain = 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)
@@ -256,7 +273,7 @@ async def domain_details(
async def domain_create(
request: Request,
domain: DomainBase,
user=Depends(get_current_user),
user=Depends(get_current_active_user),
db=Depends(get_db),
):
try:
@@ -271,10 +288,11 @@ async def domain_create(
async def domain_edit(
request: Request,
domain: DomainBase,
user=Depends(get_current_active_user),
db=Depends(get_db),
):
try:
return edit_domain(db, domain)
return edit_domain(db, domain,user.id)
except Exception as e:
raise APIException('platform:domain',request.url._url,f"Error occurred while edit domain:",e)
@@ -300,7 +318,7 @@ async def domain_delete(
async def userdomain_details(
request: Request,
userId: Optional[int] = Query(None, alias="userId"),
user=Depends(get_current_user),
user=Depends(get_current_active_user),
db=Depends(get_db),
):
try:
@@ -348,7 +366,7 @@ async def userdomain_delete(
async def get_useractivedomain(
request: Request,
userId: Optional[int] = Query(None, alias="userId"),
user=Depends(get_current_user),
user=Depends(get_current_active_user),
db=Depends(get_db),
):
try:
@@ -368,7 +386,7 @@ async def update_activeuserdomain(
request: Request,
domainid:int,
userId: Optional[int] = Query(None, alias="userId"),
user=Depends(get_current_user),
user=Depends(get_current_active_user),
db=Depends(get_db),
):
try:

View File

@@ -1,4 +1,4 @@
from fastapi import APIRouter, Request, Depends, Response, encoders
from fastapi import APIRouter, Request, Depends, Response, Security, encoders
import typing as t
from app.db.session import get_db
@@ -8,9 +8,11 @@ from app.db.crud import (
create_user,
delete_user,
edit_user,
assign_userrole,
get_roles,
)
from app.db.schemas import UserCreate, UserEdit, User, UserOut
from app.core.auth import get_current_active_user, get_current_active_superuser
from app.db.schemas import UserCreate, UserEdit, User, UserOut,Role
from app.core.auth import get_current_user,get_current_active_user, get_current_active_superuser
users_router = r = APIRouter()
@@ -23,14 +25,14 @@ users_router = r = APIRouter()
async def users_list(
response: Response,
db=Depends(get_db),
current_user=Depends(get_current_active_superuser),
current_user=Depends(get_current_active_user),
):
"""
Get all users
"""
users = get_users(db)
users = get_users(db,current_user.is_superuser)
# This is necessary for react-admin to work
response.headers["Content-Range"] = f"0-9/{len(users)}"
#response.headers["Content-Range"] = f"0-9/{len(users)}"
return users
@@ -105,3 +107,30 @@ async def user_delete(
Delete existing user
"""
return delete_user(db, user_id)
@r.post("/userrole",
response_model=User,
response_model_exclude_none=True,)
async def assign_role(
request: Request,
userid:int,
roles:t.List[int],
db=Depends(get_db)
):
return assign_userrole(db,userid,roles)
@r.get(
"/roles",
response_model=t.List[Role],
response_model_exclude_none=True,
)
async def roles_list(
response: Response,
db=Depends(get_db),
current_user=Security(get_current_active_user, scopes=["role_list"]),
):
roles = get_roles(db)
return roles

View File

@@ -1,5 +1,6 @@
from fastapi.security import SecurityScopes
import jwt
from fastapi import Depends, HTTPException, status
from fastapi import Depends, HTTPException, Request, Security, status
from jwt import PyJWTError
from app.db import models, schemas, session
@@ -7,7 +8,7 @@ from app.db.crud import get_user_by_email, create_user,get_user
from app.core import security
async def get_current_user(
async def get_current_user(security_scopes: SecurityScopes,
db=Depends(session.get_db), token: str = Depends(security.oauth2_scheme)
):
credentials_exception = HTTPException(
@@ -16,13 +17,21 @@ async def get_current_user(
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(
token, security.SECRET_KEY, algorithms=[security.ALGORITHM]
)
id: int = payload.get("sub")
if id is None:
raise credentials_exception
permissions: str = payload.get("permissions")
if not permissions =="ALL":
for scope in security_scopes.scopes:
if scope not in permissions.split(";"):
raise HTTPException(
status_code=403, detail="The user doesn't have enough privileges"
)
token_data = schemas.TokenData(id = id, permissions=permissions)
except PyJWTError:
raise credentials_exception

View File

@@ -1,3 +1,4 @@
import datetime
from fastapi import HTTPException, status
from sqlalchemy.orm import Session
from sqlalchemy import and_
@@ -19,9 +20,12 @@ def get_user_by_email(db: Session, email: str) -> schemas.UserBase:
def get_users(
db: Session, skip: int = 0, limit: int = 100
db: Session, super:bool
) -> t.List[schemas.UserOut]:
return db.query(models.User).offset(skip).limit(limit).all()
if super:
return db.query(models.User).all()
else:
return db.query(models.User).filter(models.User.is_superuser == False)
def create_user(db: Session, user: schemas.UserCreate):
@@ -69,28 +73,48 @@ def edit_user(
db.refresh(db_user)
return db_user
def get_roles(
db: Session
) -> t.List[schemas.Role]:
return db.query(models.Role).all()
def assign_userrole( db: Session, user_id: int, roles: t.List[int]):
db_user = db.query(models.User).get(user_id)
if db_user:
for role in db_user.roles:
db_user.roles.remove(role)
for roleid in roles:
role = db.query(models.Role).get(roleid)
if role:
db_user.roles.append(role)
db.commit()
db.refresh(db_user)
return db_user
def get_apps(
db: Session,
domain_url:str
domainurl:str
) -> t.List[schemas.AppList]:
return db.query(models.App).filter(models.App.domainurl == domain_url).all()
return db.query(models.App).filter(models.App.domainurl == domainurl).all()
def update_appversion(db: Session, appedit: schemas.AppVersion,userid:int):
app = db.query(models.App).filter(and_(models.App.domainurl == appedit.domainurl,models.App.appid == appedit.appid)).first()
if app:
app.version = app.version + 1
db_app = app
appver = app.version
else:
appver = 1
db_app = models.App(
db_app = db.query(models.App).filter(and_(models.App.domainurl == appedit.domainurl,models.App.appid == appedit.appid)).first()
if not db_app:
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="User not found")
db_app.version = db_app.version + 1
appversion = models.AppVersion(
domainurl = appedit.domainurl,
appid=appedit.appid,
appname=appedit.appname,
version = 1,
updateuser= userid
)
appname=db_app.appname,
version = db_app.version,
versionname = appedit.versionname,
comment = appedit.comment,
updateuserid = userid,
createuserid = userid
)
db.add(appversion)
db.add(db_app)
flows = db.query(models.Flow).filter(and_(models.Flow.domainurl == appedit.domainurl,models.App.appid == appedit.appid))
@@ -103,7 +127,9 @@ def update_appversion(db: Session, appedit: schemas.AppVersion,userid:int):
name = flow.name,
content = flow.content,
createuser = userid,
version = appver
version = db_app.version,
updateuserid = userid,
createuserid = userid
)
db.add(db_flowhistory)
@@ -111,6 +137,17 @@ def update_appversion(db: Session, appedit: schemas.AppVersion,userid:int):
db.refresh(db_app)
return db_app
def delete_apps(db: Session, domainurl: str,appid: str ):
db_app = db.query(models.App).filter(and_(models.App.domainurl == domainurl,models.App.appid ==appid)).first()
if not db_app:
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="App not found")
db.delete(db_app)
db_flows = db.query(models.Flow).filter(and_(models.Flow.domainurl == domainurl,models.Flow.appid ==appid))
for flow in db_flows:
db.delete(flow)
db.commit()
return db_app
def get_appsetting(db: Session, id: int):
app = db.query(models.AppSetting).get(id)
if not app:
@@ -166,16 +203,28 @@ def get_actions(db: Session):
return actions
def create_flow(db: Session, domainurl: str, flow: schemas.FlowBase):
def create_flow(db: Session, domainurl: str, flow: schemas.FlowIn,userid:int):
db_flow = models.Flow(
flowid=flow.flowid,
appid=flow.appid,
eventid=flow.eventid,
domainurl=domainurl,
name=flow.name,
content=flow.content
content=flow.content,
createuserid = userid,
updateuserid = userid
)
db.add(db_flow)
db_app = db.query(models.App).filter(and_(models.App.domainurl == domainurl,models.App.appid == flow.appid)).first()
if not db_app:
db_app = models.App(
domainurl = domainurl,
appid=flow.appid,
appname=flow.appname,
version = 0,
createuserid= userid,
updateuserid = userid
)
db.commit()
db.refresh(db_flow)
return db_flow
@@ -190,15 +239,20 @@ def delete_flow(db: Session, flowid: str):
def edit_flow(
db: Session, flow: schemas.FlowBase
db: Session, domainurl: str, flow: schemas.FlowIn,userid:int
) -> schemas.Flow:
db_flow = get_flow(db, flow.flowid)
if not db_flow:
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Flow not found")
update_data = flow.dict(exclude_unset=True)
for key, value in update_data.items():
setattr(db_flow, key, value)
#見つからない時新規作成
return create_flow(db,domainurl,flow,userid)
db_flow.appid =flow.appid,
db_flow.eventid=flow.eventid,
db_flow.domainurl=domainurl,
db_flow.name=flow.name,
db_flow.content=flow.content,
db_flow.updateuserid = userid,
db_flow.update_time = datetime.now
db.add(db_flow)
db.commit()
@@ -214,8 +268,8 @@ def get_flows(db: Session, flowid: str):
def get_flow(db: Session, flowid: str):
flow = db.query(models.Flow).filter(models.Flow.flowid == flowid).first()
if not flow:
raise HTTPException(status_code=404, detail="Data not found")
# if not flow:
# raise HTTPException(status_code=404, detail="Data not found")
return flow
def get_flows_by_app(db: Session,domainurl: str, appid: str):
@@ -231,7 +285,9 @@ def create_domain(db: Session, domain: schemas.DomainBase,userid:int):
name=domain.name,
url=domain.url,
kintoneuser=domain.kintoneuser,
kintonepwd=domain.kintonepwd
kintonepwd=domain.kintonepwd,
createuserid = userid,
updateuserid = userid
)
db.add(db_domain)
db.flush()
@@ -250,18 +306,19 @@ def delete_domain(db: Session,id: int):
def edit_domain(
db: Session, domain: schemas.DomainBase
db: Session, domain: schemas.DomainBase,userid:int
) -> schemas.Domain:
domain.encrypt_kintonepwd()
db_domain = db.query(models.Domain).get(domain.id)
if not db_domain:
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Domain not found")
update_data = domain.dict(exclude_unset=True)
for key, value in update_data.items():
if key != "id" and not (key == "kintonepwd" and (value is None or value == "")):
setattr(db_domain, key, value)
print(str(db_domain))
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Domain not found")
db_domain.tenantid = domain.tenantid
db_domain.name=domain.name
db_domain.url=domain.url
db_domain.kintoneuser=domain.kintoneuser
db_domain.kintonepwd = domain.kintonepwd
db_domain.updateuserid = userid
db_domain.update_time = datetime.now
db.add(db_domain)
db.commit()
db.refresh(db_domain)

View File

@@ -1,8 +1,8 @@
from sqlalchemy import Boolean, Column, Integer, String, DateTime,ForeignKey
from sqlalchemy import Boolean, Column, Integer, String, DateTime,ForeignKey,Table
from sqlalchemy.ext.declarative import as_declarative
from sqlalchemy.orm import relationship
from datetime import datetime
from app.db.session import Base
from app.core.security import chacha20Decrypt
@as_declarative()
@@ -11,6 +11,21 @@ class Base:
create_time = Column(DateTime, default=datetime.now)
update_time = Column(DateTime, default=datetime.now, onupdate=datetime.now)
userrole = Table(
"userrole",
Base.metadata,
Column("userid",Integer,ForeignKey("user.id")),
Column("roleid",Integer,ForeignKey("role.id")),
)
rolepermission = Table(
"rolepermission",
Base.metadata,
Column("roleid",Integer,ForeignKey("role.id")),
Column("permissionid",Integer,ForeignKey("permission.id")),
)
class User(Base):
__tablename__ = "user"
@@ -20,6 +35,25 @@ class User(Base):
hashed_password = Column(String(200), nullable=False)
is_active = Column(Boolean, default=True)
is_superuser = Column(Boolean, default=False)
roles = relationship("Role",secondary=userrole,back_populates="users")
class Role(Base):
__tablename__ = "role"
name = Column(String(100))
description = Column(String(255))
users = relationship("User",secondary=userrole,back_populates="roles")
permissions = relationship("Permission",secondary=rolepermission,back_populates="roles")
class Permission(Base):
__tablename__ = "permission"
menu = Column(String(100))
function = Column(String(255))
privilege = Column(String(100))
roles = relationship("Role",secondary=rolepermission,back_populates="permissions")
class App(Base):
__tablename__ = "app"
@@ -28,8 +62,25 @@ class App(Base):
appname = Column(String(200), nullable=False)
appid = Column(String(100), index=True, nullable=False)
version = Column(Integer)
updateuser = Column(Integer,ForeignKey("user.id"))
user = relationship('User')
createuserid = Column(Integer,ForeignKey("user.id"))
updateuserid = Column(Integer,ForeignKey("user.id"))
createuser = relationship('User',foreign_keys=[createuserid])
updateuser = relationship('User',foreign_keys=[updateuserid])
class AppVersion(Base):
__tablename__ = "appversion"
domainurl = Column(String(200), nullable=False)
appname = Column(String(200), nullable=False)
appid = Column(String(100), index=True, nullable=False)
version = Column(Integer)
versionname = Column(String(200), nullable=False)
comment = Column(String(200), nullable=False)
createuserid = Column(Integer,ForeignKey("user.id"))
updateuserid = Column(Integer,ForeignKey("user.id"))
createuser = relationship('User',foreign_keys=[createuserid])
updateuser = relationship('User',foreign_keys=[updateuserid])
class AppSetting(Base):
__tablename__ = "appsetting"
@@ -64,7 +115,11 @@ class Flow(Base):
eventid = Column(String(100), index=True, nullable=False)
domainurl = Column(String(200))
name = Column(String(200))
content = Column(String)
content = Column(String)
createuserid = Column(Integer,ForeignKey("user.id"))
updateuserid = Column(Integer,ForeignKey("user.id"))
createuser = relationship('User',foreign_keys=[createuserid])
updateuser = relationship('User',foreign_keys=[updateuserid])
class FlowHistory(Base):
__tablename__ = "flowhistory"
@@ -75,8 +130,11 @@ class FlowHistory(Base):
domainurl = Column(String(200))
name = Column(String(200))
content = Column(String)
createuser = Column(Integer,ForeignKey("user.id"))
version = Column(Integer)
createuserid = Column(Integer,ForeignKey("user.id"))
updateuserid = Column(Integer,ForeignKey("user.id"))
createuser = relationship('User',foreign_keys=[createuserid])
updateuser = relationship('User',foreign_keys=[updateuserid])
class Tenant(Base):
__tablename__ = "tenant"
@@ -98,7 +156,10 @@ class Domain(Base):
def decrypt_kintonepwd(self):
decrypted_pwd = chacha20Decrypt(self.kintonepwd)
return decrypted_pwd
createuserid = Column(Integer,ForeignKey("user.id"))
updateuserid = Column(Integer,ForeignKey("user.id"))
createuser = relationship('User',foreign_keys=[createuserid])
updateuser = relationship('User',foreign_keys=[updateuserid])
class UserDomain(Base):
__tablename__ = "userdomain"

View File

@@ -8,13 +8,26 @@ class Base(BaseModel):
create_time: datetime
update_time: datetime
class Permission(BaseModel):
id: int
menu:str
function:str
privilege:str
class Role(BaseModel):
id: int
name:str
description:str
permissions:t.List[Permission] = []
class UserBase(BaseModel):
email: str
is_active: bool = True
is_superuser: bool = False
first_name: str = None
last_name: str = None
roles:t.List[Role] = []
class UserOut(UserBase):
pass
@@ -54,13 +67,16 @@ class AppList(Base):
domainurl: str
appname: str
appid:str
updateuser: UserOut
version:int
user:UserOut
class AppVersion(BaseModel):
domainurl: str
appname: str
versionname: str
comment:str
appid:str
class TokenData(BaseModel):
id:int = 0
@@ -106,9 +122,11 @@ class Action(BaseModel):
class ConfigDict:
orm_mode = True
class FlowBase(BaseModel):
class FlowIn(BaseModel):
flowid: str
# domainurl:str
appid: str
appname:str
eventid: str
name: str = None
content: str = None

View File

@@ -57,9 +57,10 @@ interface IAppDisplay{
}
const authStore = useAuthStore();
const numberStringSorting = (a: string, b: string) => parseInt(a, 10) - parseInt(b, 10);
const columns = [
{ name: 'id', label: 'アプリID', field: 'id', align: 'left', sortable: true },
{ name: 'id', label: 'アプリID', field: 'id', align: 'left', sortable: true, sort: numberStringSorting },
{ name: 'name', label: 'アプリ名', field: 'name', align: 'left', sortable: true },
{ name: 'url', label: 'URL', field: 'url', align: 'left', sortable: true },
{ name: 'user', label: '最後更新者', field: 'user', align: 'left', sortable: true},
@@ -77,16 +78,16 @@ const store = useFlowEditorStore();
const getApps = async () => {
loading.value = true;
const result = await api.get('api/apps');
rows.value = result.data.map((item:IManagedApp) => {
rows.value = result.data.map((item: IManagedApp) => {
return {
id: Number(item.appid),
id: item.appid,
name: item.appname,
url: `${item.domainurl}/k/${item.appid}`,
user: `${item.user.first_name} ${item.user.last_name}` ,
user: `${item.updateuser.first_name} ${item.updateuser.last_name}` ,
updatetime:date.formatDate(item.update_time, 'YYYY/MM/DD HH:mm'),
version: Number(item.version)
}
}).sort((a, b) => a.id - b.id); // set default order
}).sort((a: IAppDisplay, b: IAppDisplay) => numberStringSorting(a.id, b.id)); // set default order
loading.value = false;
}

View File

@@ -128,6 +128,7 @@ export const useFlowEditorStore = defineStore('flowEditor', {
const jsonData = {
flowid: isNew ? flow.createNewId() : flow.id,
appid: this.appInfo?.appId,
appname: this.appInfo?.name,
eventid: root?.name,
name: root?.subTitle,
content: JSON.stringify(flow),

View File

@@ -1,14 +1,14 @@
interface IUser {
first_name: string;
last_name: string;
email: string;
}
export interface IManagedApp {
appid: string;
appname: string;
domainurl: string;
version: string;
user: IUser;
update_time: string;
}
interface IUser {
first_name: string;
last_name: string;
email: string;
}
export interface IManagedApp {
appid: string;
appname: string;
domainurl: string;
version: string;
updateuser: IUser;
update_time: string;
}