Compare commits

...

25 Commits

Author SHA1 Message Date
2a290c3142 bugfix for auto-lookup 2025-09-17 14:22:12 +08:00
de717f25a5 bugfix for AppSelectBox 2025-09-17 13:27:39 +08:00
9c4adc48ba fix insert-check 2025-09-17 11:35:58 +08:00
171f0dfa89 Merge branch 'dev2' of https://dev.azure.com/alicorn-dev/KintoneAppBuilder/_git/KintoneAppBuilder into dev2 2025-09-10 10:08:27 +09:00
6869505d9a 文言修正& DB 2025-09-10 10:06:28 +09:00
a8ec97969f var2仕様 2025-09-08 03:30:21 +09:00
c58887942b Merge branch 'dev2' of https://dev.azure.com/alicorn-dev/KintoneAppBuilder/_git/KintoneAppBuilder into dev2 2025-08-04 11:23:35 +09:00
7fccf97eaf env 2025-08-04 11:23:27 +09:00
e233e08857 Add sql file 2025-05-16 14:18:45 +08:00
85f1a7e569 [docs] remove doc ナレッジ構成.drawio 2025-05-16 11:59:40 +08:00
d9fd738a19 [bugfix] unselect table row when change tab in action select dialog 2025-05-13 22:20:04 +08:00
5cc2b6111b bugfix 2025-05-13 17:29:06 +08:00
409db1e111 bugifx 2025-05-13 15:41:37 +08:00
803d3b05a0 fix last update user id 2025-04-07 15:59:00 +08:00
044934ea28 fix update time cached problem 2025-04-07 15:54:12 +08:00
058d402643 fix admin 2025-04-03 22:57:28 +08:00
179d6e1106 add get defaultgroup 2025-03-30 12:23:12 +09:00
974f90eb2a bugfix uploadfile&app update_time 2025-03-30 10:58:27 +09:00
0f6494acdc env 2025-03-29 13:09:05 +09:00
Shohtetsu Ma
3c6e4a6faa Merged PR 164: BUG916:ユーザーのパスワードをログに出さないようにする
BUG916:ユーザーのパスワードをログに出さないようにする

Related work items: #916
2025-03-26 10:14:44 +00:00
e1b416060f BUG916:ユーザーのパスワードをログに出さないようにする 2025-03-25 17:09:08 +09:00
Shohtetsu Ma
a78f403d29 Merged PR 161: BUG919:文言修正
BUG919:画面の文言を修正しました。
2025-03-18 04:30:46 +00:00
47a2fd588e Merge branch 'dev3' into dev2 2025-03-18 13:11:04 +09:00
3279959bdb Merge branch 'dev3' into dev2 2025-03-04 16:53:41 +09:00
b68d58fd0f deploy change 2025-02-01 14:29:59 +09:00
23 changed files with 7652 additions and 316 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@ backend/pyvenv.cfg
backend/Include/
backend/Scripts/
log/api.log

2
backend/.deployment Normal file
View File

@@ -0,0 +1,2 @@
[config]
SCM_DO_BUILD_DURING_DEPLOYMENT=true

File diff suppressed because one or more lines are too long

View File

@@ -3,6 +3,7 @@ from io import BytesIO
import typing as t
import pandas as pd
import json
import base64
import httpx
import deepdiff
import app.core.config as config
@@ -493,6 +494,17 @@ async def test(file:UploadFile= File(...),app:str=None):
return test
@r.get("/group")
async def group(request:Request,kintoneurl:str,kintoneuser:str,kintonepwd:str):
try:
auth_value = base64.b64encode(bytes(f"{kintoneuser}:{kintonepwd}","utf-8"))
headers={config.API_V1_AUTH_KEY:auth_value}
url = f"{kintoneurl}/v1/user/groups.json?code={kintoneuser}"
r = httpx.get(url,headers=headers)
return r.json()
except Exception as e:
raise APIException('kintone:group',request.url._url, f"Error occurred while get group(url:{kintoneurl} user:{kintoneuser}):",e)
@r.post("/download",)
async def download(request:Request,key,c:config.KINTONE_ENV=Depends(getkintoneenv)):
try:
@@ -502,7 +514,7 @@ async def download(request:Request,key,c:config.KINTONE_ENV=Depends(getkintoneen
r = httpx.get(url,headers=headers,params=params)
return r.json()
except Exception as e:
raise APIException('kintone:upload',request.url._url,f"Error occurred while download file.json:",e)
raise APIException('kintone:download',request.url._url,f"Error occurred while download file.json:",e)
@r.post("/upload")
async def upload(request:Request,files:t.List[UploadFile] = File(...)):
@@ -625,7 +637,17 @@ async def createapp(request:Request,name:str,env:config.KINTONE_ENV=Depends(getk
except Exception as e:
raise APIException('kintone:createapp',request.url._url, f"Error occurred while create app({env.DOMAIN_NAME}->{name}):",e)
@r.get("/defaultgroup")
async def currentgroup(request:Request,env:config.KINTONE_ENV=Depends(getkintoneenv)):
try:
auth_value = env.API_V1_AUTH_VALUE
headers={config.API_V1_AUTH_KEY:auth_value}
url = f"{env.BASE_URL}/v1/user/groups.json?code={env.KINTONE_USER}"
r = httpx.get(url,headers=headers)
return r.json()
except Exception as e:
raise APIException('kintone:currentgroup',request.url._url, f"Error occurred while get default domain group(domain:{env.DOMAIN_NAME} url:{env.BASE_URL} user:{env.KINTONE_USER}):",e)
@r.post("/createappfromexcel",)
async def createappfromexcel(request:Request,files:t.List[UploadFile] = File(...),format:int = 0,env = Depends(getkintoneenv),db = Depends(get_db)):
try:

View File

@@ -1,4 +1,5 @@
from urllib.parse import parse_qs, urlencode
from fastapi import Request
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
@@ -12,10 +13,14 @@ import json
class LoggingMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
if request.method in ("POST", "PUT", "PATCH","DELETE"):
try:
request.state.body = await request.json()
except json.JSONDecodeError:
request.state.body = await request.body()
content_type = request.headers.get('content-type', '')
if content_type.startswith('multipart/form-data'):
request.state.body = None
else:
try:
request.state.body = await request.json()
except json.JSONDecodeError:
request.state.body = await request.body()
else:
request.state.body = None
@@ -33,6 +38,48 @@ class LoggingMiddleware(BaseHTTPMiddleware):
await self.log_request(request, response,state)
return response
def sanitize_password(self,data):
"""
データ内の password パラメータをフィルタリングする機能。
dict、JSON 文字列、URL エンコード文字列、QueryDict をサポート。
"""
if data is None:
return data
elif isinstance(data, dict):
data.pop('password', None)
return data
elif isinstance(data, list):
return [self.sanitize_password(item) for item in data]
elif isinstance(data, (str, bytes)):
if isinstance(data, bytes):
data = data.decode('utf-8') # bytes to str
# JSON解析
try:
parsed_json = json.loads(data)
sanitized_json = self.sanitize_password(parsed_json)
return json.dumps(sanitized_json, separators=(',', ':'))
except json.JSONDecodeError:
# URL 解析
try:
parsed_dict = parse_qs(data)
parsed_dict.pop('password', None)
return urlencode(parsed_dict, doseq=True)
except:
parts = data.split('&')
filtered_parts = []
for part in parts:
if '=' in part:
key, _ = part.split('=', 1)
if key == 'password':
continue
filtered_parts.append(part)
return '&'.join(filtered_parts)
# QueryDict 例えば FastAPI の request.query_params
elif hasattr(data, 'items'):
return {k: v for k, v in data.items() if k != 'password'}
return data
async def log_request(self, request: Request, response,state):
try:
@@ -42,15 +89,26 @@ class LoggingMiddleware(BaseHTTPMiddleware):
path_template = route.path
else:
path_template = request.url.path
db_operation = OperationLog(tenantid =request.state.tenant,
clientip = request.client.host if request.client else None,
useragent =headers.get("user-agent", ""),
userid = request.state.user,
operation = request.method,
function = path_template,
parameters = str({"path": request.path_params,"query": dict(request.query_params),"body": request.state.body}),
response = f"status_code:{response.status_code }" )
# passwordのパラメータを除外する
safe_query = self.sanitize_password(request.query_params.items())
# passwordのパラメータを除外する
safe_body = self.sanitize_password(request.state.body)
db_operation = OperationLog(
tenantid =request.state.tenant,
clientip = request.client.host if request.client else None,
useragent =headers.get("user-agent", ""),
userid = request.state.user,
operation = request.method,
function = path_template,
parameters = str({
"path": request.path_params,
"query": safe_query,
"body": safe_body
}),
response = f"status_code:{response.status_code }" )
db = request.state.db
if db:
@@ -71,5 +129,3 @@ class LoggingMiddleware(BaseHTTPMiddleware):
async def write_log_to_db(self, db_operation,db):
db.add(db_operation)
db.commit()

View File

@@ -1,6 +1,7 @@
from datetime import datetime
from fastapi import HTTPException, status
from sqlalchemy.orm import Session
from sqlalchemy.orm.attributes import flag_modified
from sqlalchemy import select,and_
import typing as t
@@ -202,6 +203,8 @@ class dbapp(crudbase):
db_app = self.get_app(db, domainurl, flow.appid)
if db_app and db_app.version > 0:
db_app.is_saved = True
flag_modified(db_app, 'is_saved')
db_app.updateuserid = userid
db.add(db_app)
db.commit()
db.refresh(db_flow)

View File

@@ -8,7 +8,7 @@ from app.core.security import chacha20Decrypt
class Base:
id = Column(Integer, primary_key=True, index=True)
create_time = Column(DateTime, default=datetime.now(timezone.utc))
update_time = Column(DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
update_time = Column(DateTime, default=datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc))
userrole = Table(
@@ -234,7 +234,7 @@ class OperationLog(Base):
userid = mapped_column(Integer,ForeignKey("user.id"))
operation = mapped_column(String(200))
function = mapped_column(String(200))
parameters = mapped_column(String(200))
parameters = mapped_column(String)
response = mapped_column(String(200))
user = relationship('User')

7285
db/kintone-dev2-db.sql Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -1,211 +0,0 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" version="24.9.2">
<diagram name="ページ1" id="oKiyF3b1qzm0IX1SNAWJ">
<mxGraphModel dx="1395" dy="1078" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="0" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="Clf12EPbcqlM1huzJpPN-73" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d0cee2;strokeColor=#56517e;" vertex="1" parent="1">
<mxGeometry x="92" y="645" width="720" height="180" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-71" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="110" y="605" width="720" height="180" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-53" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#ffcccc;strokeColor=#36393d;" vertex="1" parent="1">
<mxGeometry x="140" y="570" width="720" height="180" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-52" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#cdeb8b;strokeColor=#36393d;" vertex="1" parent="1">
<mxGeometry x="170" y="530" width="720" height="180" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-51" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#ffcc99;strokeColor=#36393d;" vertex="1" parent="1">
<mxGeometry x="210" y="490" width="720" height="180" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-49" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="90" y="20" width="1080" height="290" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-10" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-1" target="Clf12EPbcqlM1huzJpPN-9">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-1" value="タスク開始" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="120" y="215" width="90" height="50" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-45" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;curved=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-8" target="Clf12EPbcqlM1huzJpPN-9">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-47" value="DBからクロールのタスクを取得する" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="Clf12EPbcqlM1huzJpPN-45">
<mxGeometry x="0.2449" y="53" relative="1" as="geometry">
<mxPoint x="17" y="-44" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-79" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-8" target="Clf12EPbcqlM1huzJpPN-78">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-8" value="DB" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#cce5ff;strokeColor=#36393d;" vertex="1" parent="1">
<mxGeometry x="570" y="-110" width="100" height="70" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-9" value="&lt;div style=&quot;background-color: rgb(255, 255, 254); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px; white-space: pre;&quot;&gt;&lt;span style=&quot;&quot;&gt;タスクスケジューラ&lt;/span&gt;&lt;/div&gt;" style="whiteSpace=wrap;html=1;rounded=1;fontColor=default;" vertex="1" parent="1">
<mxGeometry x="260" y="217.5" width="140" height="45" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;curved=1;startArrow=block;startFill=1;endArrow=none;endFill=0;dashed=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-15" target="Clf12EPbcqlM1huzJpPN-9">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-18" value="タスクの割り当て" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="Clf12EPbcqlM1huzJpPN-17">
<mxGeometry x="0.04" relative="1" as="geometry">
<mxPoint x="-18" y="30" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-32" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;dashed=1;curved=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-15" target="Clf12EPbcqlM1huzJpPN-31">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-15" value="&lt;b&gt;RabbitMQ&lt;/b&gt;" style="whiteSpace=wrap;html=1;rounded=1;fillColor=#1ba1e2;fontColor=#ffffff;strokeColor=#006EAF;" vertex="1" parent="1">
<mxGeometry x="510" y="380" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-22" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-19" target="Clf12EPbcqlM1huzJpPN-21">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-19" value="MQコマンドの受信" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="250" y="560" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;curved=1;dashed=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-15" target="Clf12EPbcqlM1huzJpPN-19">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-24" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-21" target="Clf12EPbcqlM1huzJpPN-23">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-21" value="データ収集" style="whiteSpace=wrap;html=1;rounded=0;" vertex="1" parent="1">
<mxGeometry x="490" y="560" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;curved=1;dashed=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-23" target="Clf12EPbcqlM1huzJpPN-15">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-23" value="結果をMQメッセージを変換" style="whiteSpace=wrap;html=1;rounded=0;" vertex="1" parent="1">
<mxGeometry x="740" y="560" width="170" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-33" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;dashed=1;curved=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-26" target="Clf12EPbcqlM1huzJpPN-31">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="980" y="390" />
<mxPoint x="980" y="350" />
<mxPoint x="745" y="350" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-26" value="データ収集結果&lt;br&gt;一時保存" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#eeeeee;strokeColor=#36393d;" vertex="1" parent="1">
<mxGeometry x="970" y="390" width="100" height="70" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-27" value="タスクコマンド" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="1">
<mxGeometry x="340" y="450" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-23" target="Clf12EPbcqlM1huzJpPN-26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-29" value="コンテンツなど大きい情報を一時保存" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="950" y="500" width="230" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-30" value="収集結果通知MQ" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="740" y="450" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-37" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-31" target="Clf12EPbcqlM1huzJpPN-36">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-31" value="メッセージキューから&lt;div&gt;データ受信&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="677" y="212.5" width="150" height="50" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-34" value="MQのキーで収集結果を取得" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="760" y="350" width="170" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-39" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-36" target="Clf12EPbcqlM1huzJpPN-38">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-36" value="&lt;div style=&quot;background-color: rgb(255, 255, 254); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; line-height: 19px; white-space: pre;&quot;&gt;&lt;span&gt;データクリーニング&lt;br&gt;と重複排除&lt;/span&gt;&lt;/div&gt;" style="whiteSpace=wrap;html=1;rounded=1;fontColor=default;fontSize=11;" vertex="1" parent="1">
<mxGeometry x="890" y="207.5" width="148" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-43" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-38" target="Clf12EPbcqlM1huzJpPN-42">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-38" value="データベースに保存" style="whiteSpace=wrap;html=1;fontSize=11;rounded=1;" vertex="1" parent="1">
<mxGeometry x="904" y="60" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-44" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-42" target="Clf12EPbcqlM1huzJpPN-31">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-42" value="収集結果からサブ項目を抽出" style="whiteSpace=wrap;html=1;fontSize=11;rounded=1;" vertex="1" parent="1">
<mxGeometry x="692" y="60" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-46" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;curved=1;dashed=1;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-38" target="Clf12EPbcqlM1huzJpPN-8">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-48" value="クロール結果をDBに登録する" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="Clf12EPbcqlM1huzJpPN-46">
<mxGeometry x="0.1149" y="13" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-50" value="&lt;b&gt;メインプログラム&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="540" y="20" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-54" value="&lt;b&gt;クローラー サブプログラム&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="485" y="490" width="185" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-55" value="&lt;b&gt;SharePointクローラー&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="220" y="640" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-60" value="MQ情報" style="swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;align=center;fontSize=14;fillColor=#dae8fc;strokeColor=#6c8ebf;gradientColor=#7ea6e0;" vertex="1" parent="1">
<mxGeometry x="1024" y="630" width="160" height="236" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-61" value="MQキー" style="text;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;whiteSpace=wrap;html=1;" vertex="1" parent="Clf12EPbcqlM1huzJpPN-60">
<mxGeometry y="26" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-70" value="クローラーID" style="text;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;whiteSpace=wrap;html=1;" vertex="1" parent="Clf12EPbcqlM1huzJpPN-60">
<mxGeometry y="56" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-62" value="分類" style="text;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;whiteSpace=wrap;html=1;" vertex="1" parent="Clf12EPbcqlM1huzJpPN-60">
<mxGeometry y="86" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-63" value="タイトル" style="text;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;whiteSpace=wrap;html=1;" vertex="1" parent="Clf12EPbcqlM1huzJpPN-60">
<mxGeometry y="116" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-64" value="URL" style="text;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;whiteSpace=wrap;html=1;" vertex="1" parent="Clf12EPbcqlM1huzJpPN-60">
<mxGeometry y="146" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-65" value="サブ項目" style="text;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;whiteSpace=wrap;html=1;" vertex="1" parent="Clf12EPbcqlM1huzJpPN-60">
<mxGeometry y="176" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-66" value="コンテンツ" style="text;strokeColor=none;fillColor=none;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;whiteSpace=wrap;html=1;" vertex="1" parent="Clf12EPbcqlM1huzJpPN-60">
<mxGeometry y="206" width="160" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-67" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-60" target="Clf12EPbcqlM1huzJpPN-23">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="930" y="840" as="sourcePoint" />
<mxPoint x="980" y="790" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-68" value="&lt;b&gt;OneNodeクローラー&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="180" y="680" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-69" value="&lt;b&gt;EIM クローラー&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="150" y="720" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-72" value="&lt;b&gt;Coredasuクローラー&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="120" y="755" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-74" value="&lt;b&gt;その他クローラー&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="92" y="795" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-75" value="PostGre SQL" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="585" y="-40" width="90" height="30" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-76" value="再帰的な処理" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="1030" y="140" width="80" height="50" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-77" value="NEO4J" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;" vertex="1" parent="1">
<mxGeometry x="570" y="-390" width="100" height="80" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-78" value="データ抽出" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="560" y="-250" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="Clf12EPbcqlM1huzJpPN-80" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="Clf12EPbcqlM1huzJpPN-78" target="Clf12EPbcqlM1huzJpPN-77">
<mxGeometry relative="1" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -1,5 +1,5 @@
#開発環境
#KAB_BACKEND_URL="https://kab-backend.azurewebsites.net/"
#KAB_BACKEND_URL="https://ktune-backend-dev-eba8fkeyffegc3cz.japanwest-01.azurewebsites.net/"
#単体テスト環境
#KAB_BACKEND_URL="https://kab-backend-unittest.azurewebsites.net/"
#ローカル開発環境

View File

@@ -18,6 +18,7 @@
indicator-color="primary"
active-bg-color="primary"
class="bg-grey-2 text-grey-8"
@update:model-value="() => selected = []"
dense
>
<q-tab :name="cate"

View File

@@ -11,7 +11,7 @@
</template>
<script lang="ts">
import { ref, PropType } from 'vue';
import { ref, PropType, watchEffect } from 'vue';
import { api } from 'boot/axios';
import DetailFieldTable from './dialog/DetailFieldTable.vue';
@@ -33,6 +33,9 @@ export default {
filter: String,
filterInitRowsFunc: {
type: Function as PropType<(app: IAppDisplay) => boolean>,
},
updateSelectApp: {
type: Function
}
},
setup(props) {
@@ -44,6 +47,12 @@ export default {
{ name: 'createdate', label: '作成日時', field: 'createdate', align: 'left' }
];
watchEffect(()=>{
if (selected.value && selected.value[0] && props.updateSelectApp) {
props.updateSelectApp(selected.value[0])
}
});
const fetchApps = async () => {
const res = await api.get('api/v1/allapps');
return res.data.apps.map((item: any) => ({

View File

@@ -18,7 +18,7 @@
input-debounce="0"
:options="canSharedUserFilteredOptions"
clearable
:placeholder="canSharedUserFilter ? '' : domain.domainActive ? '権限を付与するユーザーを選択' : 'ドメインが無効なため、権限を付与できません'"
:placeholder="canSharedUserFilter ? '' : domain.domainActive ? '権限を付与するユーザーを選択' : '接続先が無効なため、権限を付与できません'"
@filter="filterFn">
<template v-slot:selected-item="scope">

View File

@@ -1,7 +1,7 @@
<template>
<share-domain-dialog
:dialogTitle="`「${domain.name}」のドメイン利用権限設定`"
userListTitle="ドメイン利用権限を持つユーザー"
:dialogTitle="`「${domain.name}」の接続先利用権限設定`"
userListTitle="接続先利用権限を持つユーザー"
:domain="domain"
:share-api="shareApi"
:remove-shared-api="removeSharedApi"

View File

@@ -96,7 +96,7 @@ const essentialLinks: EssentialLinkProps[] = reactive([
target: '_self',
permission: MenuMapping.role
},
// ------------ドメイン-------------
// ------------接続先管理-------------
{
title: '接続先管理',
caption: 'kintoneの接続先設定',

View File

@@ -232,8 +232,38 @@ const onDeploy = async () => {
});
return;
}
deployLoading.value = true;
try {
const { data }: {data: {code: string, groups: {code: string}[]}} = await api.get('api/v1/defaultgroup');
if (data.code === 'CB_WA01') {
$q.notify({
type: 'negative',
caption: 'エラー',
message: 'ユーザーのパスワード認証に失敗しました。'
});
deployLoading.value = false;
return;
} else if (!data.groups || !data.groups.some((group: {code: string}) => group.code === 'Administrators')){
$q.notify({
type: 'negative',
caption: 'エラー',
message: 'この操作には管理者権限が必要です。'
});
deployLoading.value = false;
return;
}
} catch (e) {
$q.notify({
type: 'negative',
caption: 'エラー',
message: 'サーバーに接続できませんでした。'
});
deployLoading.value = false;
return;
}
try {
deployLoading.value = true;
await store.deploy();
deployLoading.value = false;
$q.notify({

View File

@@ -53,21 +53,21 @@
<div class="text-h6 q-ma-sm">Kintone Account</div>
</q-card-section>
<q-card-section class="q-pt-none q-mt-none">
<q-card-section class="q-py-none q-mt-none">
<div class="q-gutter-lg">
<q-input filled v-model="name" label="環境名 *" hint="kintoneの環境名を入力してください" lazy-rules
:rules="[val => val && val.length > 0 || 'kintoneの環境名を入力してください。']" />
<q-input filled type="url" v-model.trim="url" label="Kintone url" hint="KintoneのURLを入力してください" lazy-rules
<q-input filled v-model.trim="url" :readonly="!isCreate" label="Kintone url" :hint="isCreate ? 'KintoneのURLを入力してください':'KintoneのURLは変更できません。新規作成してください。'" lazy-rules
:rules="[val => val && val.length > 0 || 'KintoneのURLを入力してください']" />
<q-input filled v-model="kintoneuser" label="ログイン名 *" hint="Kintoneのログイン名を入力してください" lazy-rules
<q-input v-if="isCreate" filled v-model="kintoneuser" label="ログイン名 *" hint="Kintoneのログイン名を入力してください" lazy-rules
:rules="[val => val && val.length > 0 || 'Kintoneのログイン名を入力してください']" autocomplete="new-password" />
<q-input v-if="isCreate" v-model="kintonepwd" filled :type="isPwd ? 'password' : 'text'"
hint="パスワード" label="パスワード" :disable="!isCreate" lazy-rules
:rules="[val => val && val.length > 0 || 'Please type something']" autocomplete="new-password">
hint="Kintoneのパスワードを入力してください" label="パスワード" :disable="!isCreate" lazy-rules
:rules="[val => val && val.length > 0 || 'Kintoneのパスワードを入力してください']" autocomplete="new-password">
<template v-slot:append>
<q-icon :name="isPwd ? 'visibility_off' : 'visibility'" class="cursor-pointer"
@click="isPwd = !isPwd" />
@@ -76,7 +76,7 @@
<q-item tag="label" class="q-pl-sm q-pr-none q-py-xs">
<q-item-section>
<q-item-label>ドメインの有効化</q-item-label>
<q-item-label>接続先の有効化</q-item-label>
</q-item-section>
<q-item-section avatar>
<q-toggle v-model="domainActive" />
@@ -88,30 +88,51 @@
<q-item tag="label" class="q-pl-sm q-pr-none q-py-xs">
<q-item-section>
<q-item-label>パスワードリセット</q-item-label>
<q-item-label>管理者アカウントの変更</q-item-label>
</q-item-section>
<q-item-section avatar>
<q-toggle v-model="resetPsw" @update:model-value="updateResetPsw" />
<q-toggle v-model="changeAccount" @update:model-value="updateChangeAccount" />
</q-item-section>
</q-item>
<q-input v-model="kintonepwd" filled :type="isPwd ? 'password' : 'text'" hint="パスワードを入力してください"
label="パスワード" :disable="!resetPsw" lazy-rules
:rules="[val => val && val.length > 0 || 'Please type something']" autocomplete="new-password">
<template v-slot:append>
<q-icon :name="isPwd ? 'visibility_off' : 'visibility'" class="cursor-pointer"
@click="isPwd = !isPwd" />
</template>
</q-input>
<!-- <q-btn label="asdf"/> -->
<q-expansion-item
header-class="hidden"
class="q-mt-none"
v-model="changeAccount"
>
<q-input filled v-model="kintoneuser" label="ログイン名 *" hint="Kintoneのログイン名を入力してください" :disable="!changeAccount"
:rules="[val => val && val.length > 0 || 'Kintoneのログイン名を入力してください']" lazy-rules class="q-mt-md"/>
<q-input v-model="kintonepwd" filled :type="isPwd ? 'password' : 'text'" hint="Kintoneのパスワードを入力してください"
label="パスワード" :disable="!changeAccount" lazy-rules class="q-mt-lg q-mb-md"
:rules="[val => val && val.length > 0 || 'Kintoneのパスワードを入力してください']" autocomplete="new-password">
<template v-slot:append>
<q-icon :name="isPwd ? 'visibility_off' : 'visibility'" class="cursor-pointer"
@click="isPwd = !isPwd" />
</template>
</q-input>
<!-- <q-btn label="asdf"/> -->
</q-expansion-item>
</div>
</div>
</q-card-section>
<q-card-actions align="right" class="text-primary q-mb-md q-mx-sm">
<q-btn :loading="addEditLoading" label="保存" type="submit" color="primary" />
<q-btn label="キャンセル" type="cancel" color="primary" flat class="q-ml-sm" @click="closeDg()" />
</q-card-actions>
<div class="q-mt-none">
<q-banner dense :class="['text-white q-mt-sm q-py-xs q-mx-md', testResult?.success ? 'bg-green':'bg-red', testResult?.msg ? '':'invisible']">
{{ testResult?.msg }}
</q-banner>
<q-card-actions class="text-primary q-mb-md q-mx-sm relative-position">
<q-btn v-if="isCreate || changeAccount" :disable="!isConnectable" :loading="!!connectLoading" padding="xs md" :label="!!connectLoading ? '確認中...' : '接続確認'"
color="primary" outline @click="tryConnect()"/>
<q-btn v-if="!!connectLoading" label="停止" color="negative" flat class="q-ml-sm" @click="stopConnect" />
<q-space />
<q-btn :loading="addEditLoading" padding="xs md" label="保存" type="submit" color="primary" />
<q-btn label="キャンセル" type="cancel" color="primary" flat class="q-ml-sm" @click="closeDg()" />
</q-card-actions>
</div>
</q-form>
</q-card>
@@ -122,12 +143,12 @@
<!-- -1 loading -->
<q-card-section v-if="deleteLoadingState == -1" class="row items-center">
<q-spinner color="primary" size="2em"/>
<span class="q-ml-sm">ドメイン利用権限を確認中</span>
<span class="q-ml-sm">接続先利用権限を確認中</span>
</q-card-section>
<!-- > 0 can't delete -->
<q-card-section v-else-if="deleteLoadingState > 0" class="row items-center">
<q-icon name="error" color="negative" size="2em" />
<span class="q-ml-sm">ドメインは使用中です。削除してもよろしいですか?</span>
<span class="q-ml-sm">接続先は使用中です。削除してもよろしいですか?</span>
</q-card-section>
<!-- 0/-2 can delete -->
<q-card-section v-else class="row items-center">
@@ -187,12 +208,14 @@ const pagination = ref({ sortBy: 'id', descending: true, rowsPerPage: 20 });
const loading = ref(false);
const addEditLoading = ref(false);
const deleteLoadingState = ref<number>(-1); // -2: deleteLoading, -1: loading, 0: allow, > 0: user count
const connectLoading = ref<AbortController|null>(null);
const filter = ref('');
const rows = ref<IDomainOwnerDisplay[]>([]);
const show = ref(false);
const confirm = ref(false);
const resetPsw = ref(false);
const changeAccount = ref(false);
const testResult = ref<{msg: string, success?: boolean}|null>(null);
const currentDomainId = computed(() => authStore.currentDomain.id);
// const tenantid = ref(authStore.currentDomain.id);
@@ -200,8 +223,8 @@ const name = ref('');
const url = ref('');
const isPwd = ref(true);
const kintoneuser = ref('');
const kintoneuserBK = ref('');
const kintonepwd = ref('');
const kintonepwdBK = ref('');
const domainActive = ref(true);
const isCreate = ref(true);
let editId = ref(0);
@@ -239,7 +262,7 @@ const actionList = [
action: (row: IDomainOwnerDisplay) => {openShareDg(SHARE_USE, row)} },
{ label: '管理権限設定', icon: 'add_moderator', permission: Actions.domain.grantManage,
disable: (row: IDomainOwnerDisplay) => !isOwner(row),
tooltip: (row: IDomainOwnerDisplay) => isOwner(row) ? '' : 'ドメイン所有者でないため、操作できません',
tooltip: (row: IDomainOwnerDisplay) => isOwner(row) ? '' : '接続先の所有者でないため、操作できません',
action: (row: IDomainOwnerDisplay) => {openShareDg(SHARE_MANAGE, row)}
},
{ separator: true },
@@ -316,19 +339,20 @@ function editRow(row: any) {
name.value = row.name;
url.value = row.url;
kintoneuser.value = row.user;
kintonepwd.value = row.password;
kintoneuserBK.value = row.user;
kintonepwd.value = ''
domainActive.value = row.domainActive;
isPwd.value = true;
show.value = true;
};
const updateResetPsw = (value: boolean) => {
if (value === true) {
kintonepwd.value = ''
isPwd.value = true
} else {
kintonepwd.value = kintonepwdBK.value
}
const updateChangeAccount = () => {
kintoneuser.value = kintoneuserBK.value;
kintonepwd.value = ''
connectLoading.value = null
isPwd.value = true
stopConnect();
testResult.value = null;
}
const closeDg = () => {
@@ -336,8 +360,61 @@ const closeDg = () => {
onReset();
}
const onSubmit = () => {
const tryConnect = async (isTest = true) => {
testResult.value = null;
if (isTest) {
connectLoading.value = new AbortController();
}
try {
const { data }: {data: {code: string, groups: {code: string}[]}} = await api.get('api/v1/group', {
params:{
kintoneurl: ensureHttps(url.value),
kintoneuser: kintoneuser.value,
kintonepwd: kintonepwd.value,
},
signal: (isTest && connectLoading.value) ? connectLoading.value.signal : undefined
});
if (data.code === 'CB_WA01') {
testResult.value = { msg: 'ユーザーのパスワード認証に失敗しました。' }
} else if (data.groups && data.groups.some((group: {code: string}) => group.code === 'Administrators')){
testResult.value = { success: true, msg: isTest ? 'kintoneの管理者アカウントで接続に成功しました。' : '' }
return true;
} else {
testResult.value = { msg: 'このアカウントはkintoneの管理者アカウントではありません。' }
}
return false;
} catch (e) {
const error = e as { code?: string };
if (error.code === 'ERR_CANCELED') {
console.log('Aborted');
} else {
testResult.value = { msg: 'サーバーに接続できませんでした。' }
throw error;
}
return false;
} finally {
connectLoading.value = null;
}
}
const ensureHttps = (url: string) => {
return !/^https?:\/\//i.test(url) ? 'https://' + url : url;
}
const stopConnect = () => {
if (!connectLoading.value) return;
connectLoading.value?.abort();
connectLoading.value = null;
}
const onSubmit = async () => {
addEditLoading.value = true;
try {
await tryConnect(false);
} catch (e) {
addEditLoading.value = false;
return;
}
const method = editId.value !== 0 ? 'put' : 'post';
const param: IDomainSubmit = {
'id': editId.value,
@@ -345,7 +422,7 @@ const onSubmit = () => {
'name': name.value,
'url': url.value,
'kintoneuser': kintoneuser.value,
'kintonepwd': ((isCreate.value && editId.value == 0) || resetPsw.value) ? kintonepwd.value : '',
'kintonepwd': ((isCreate.value && editId.value == 0) || changeAccount.value) ? kintonepwd.value : '',
'is_active': domainActive.value,
'ownerid': authStore.userId || ''
}
@@ -374,17 +451,24 @@ function openShareDg(type: typeof SHARE_MANAGE|typeof SHARE_USE, row: IDomainOwn
}
};
const isConnectable = computed(()=> {
return url.value && kintoneuser.value && kintonepwd.value
})
const onReset = () => {
name.value = '';
url.value = '';
kintoneuser.value = '';
kintoneuserBK.value = '';
kintonepwd.value = '';
isPwd.value = true;
editId.value = 0;
isCreate.value = true;
domainActive.value = true;
resetPsw.value = false
changeAccount.value = false;
addEditLoading.value = false;
connectLoading.value = null
testResult.value = null;
}
</script>
<style lang="scss">

View File

@@ -80,14 +80,14 @@
<q-input v-if="isCreate" v-model="pwd" filled :type="isPwd ? 'password' : 'text'" hint="パスワード"
label="パスワード" :disable="!isCreate" lazy-rules
:rules="[val => val && val.length > 0 || 'Please type something']" autocomplete="new-password">
:rules="[val => val && val.length > 0 || 'パスワードを入力してください']" autocomplete="new-password">
<template v-slot:append>
<q-icon :name="isPwd ? 'visibility_off' : 'visibility'" class="cursor-pointer"
@click="isPwd = !isPwd" />
</template>
</q-input>
<q-item tag="label" class="q-pl-sm q-pr-none q-py-xs">
<q-item tag="label" v-if="authStore.isSuperAdmin" class="q-pl-sm q-pr-none q-py-xs">
<q-item-section>
<q-item-label>システム管理者</q-item-label>
</q-item-section>
@@ -118,7 +118,7 @@
</q-item>
<q-input v-model="pwd" filled :type="isPwd ? 'password' : 'text'" hint="パスワードを入力してください" label="パスワード"
:disable="!resetPsw" lazy-rules :rules="[val => val && val.length > 0 || 'Please type something']"
:disable="!resetPsw" lazy-rules :rules="[val => val && val.length > 0 || 'パスワードを入力してください']"
autocomplete="new-password">
<template v-slot:append>
<q-icon :name="isPwd ? 'visibility_off' : 'visibility'" class="cursor-pointer"
@@ -131,7 +131,7 @@
</q-card-section>
<q-card-actions align="right" class="text-primary q-mb-md q-mx-sm">
<q-btn :loading="addEditLoading" label="保存" type="submit" color="primary" />
<q-btn :loading="addEditLoading" padding="xs md" label="保存" type="submit" color="primary" />
<q-btn label="キャンセル" type="cancel" color="primary" flat class="q-ml-sm" @click="closeDg()" />
</q-card-actions>
</q-form>
@@ -160,10 +160,12 @@
import { ref, onMounted, computed } from 'vue';
import TableActionMenu from 'components/TableActionMenu.vue';
import { useUserStore } from 'stores/useUserStore';
import { useAuthStore } from 'stores/useAuthStore';
import { IUserDisplay, IUserRolesDisplay } from 'src/types/UserTypes';
import { Actions } from 'boot/permissions';
const userStore = useUserStore();
const authStore = useAuthStore();
const columns = [
{ name: 'id', label: 'ID', field: 'id', align: 'left', sortable: true },

View File

@@ -59,7 +59,7 @@ export default route(function (/* { store, ssrContext } */) {
if (!authStore.hasDomain && !domainPages.includes(to.path)) {
Dialog.create({
title: '注意',
message: '既定/利用可能なドメインはありません。<br>接続先管理ページに遷移して処理します。',
message: '既定/利用可能な接続先はありません。<br>接続先管理ページに遷移して処理します。',
html: true,
persistent: true,
})

View File

@@ -103,7 +103,7 @@ export class AutoLookUpAction implements IAction {
this.actionProps = actionNode.actionProps;
this.props = {
...actionNode.ActionValue,
condition: JSON.parse((actionNode.ActionValue as any).condition),
condition: JSON.parse((actionNode.ActionValue as any).condition || '{}'),
} as IAutoLookUpProps;
// console.log(context);
@@ -111,16 +111,17 @@ export class AutoLookUpAction implements IAction {
canNext: true,
result: "",
} as IActionResult;
try {
const lookUpFields = this.props.lookupField.fields.filter(
(f) => f.lookup && f.lookup.relatedApp.app === String(kintone.app.getId())
const lookUpFields = this.props.lookupField.fields.filter(
(f) => f.lookup && f.lookup.relatedApp.app === String(kintone.app.getId())
);
if (!lookUpFields || lookUpFields.length===0) {
throw new Error(
`ルックアップの設定は不正です。${this.props.lookupField.fields[0].label} `
);
if (!lookUpFields || lookUpFields.length===0) {
throw new Error(
`ルックアップの設定は不正です。${this.props.lookupField.fields[0].label} `
);
}
}
try {
const lookUpField = this.props.lookupField.fields[0];
this.showSpinnerModel(this.props.lookupField.app, lookUpField);
const key = event.record[lookUpField.lookup.relatedKeyField].value;
const targetRecords = await this.getUpdateRecords(lookUpField, key);
//更新対象がない時にスキップ
@@ -129,7 +130,6 @@ export class AutoLookUpAction implements IAction {
}
const updateRecords = this.convertForLookup(targetRecords,lookUpField,key);
console.log("updateRecords", updateRecords);
this.showSpinnerModel(this.props.lookupField.app,lookUpField);
const updateResult = await this.updateLookupTarget(updateRecords);
if(updateResult){
this.showResult(this.props.lookupField.app,lookUpField,updateRecords.length);
@@ -157,7 +157,7 @@ export class AutoLookUpAction implements IAction {
if(typeof key==='string'){
query = `${lookUpField.code} = "${key}"`
}
if(this.props.condition.queryString!==''){
if(!!this.props.condition.queryString){
query = `${query} and (${this.props.condition.queryString})`
}
return query;

View File

@@ -645,7 +645,7 @@ export class InsertValueAction implements IAction{
const conditionResult = this.getConditionResult(context);
//保存成功イベントの場合、kintone async/await による非同期処理でフィールドに値を挿入する
if(!event.type.includes('success')){
if(!event.type?.includes('success')){
//条件式の結果がtrueかつ挿入する値が変換できた場合、フィールドラジオボタン・ドロップダウン・チェックボックス・複数選択・文字列一行・文字列複数行・リッチエディタ・数値・日付・日時・時刻にセット
if(conditionResult){

View File

@@ -0,0 +1,52 @@
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
-- event テーブルに欠落している app.record.index.delete.submit を追加します。
DO $$
DECLARE
max_id INTEGER;
BEGIN
SELECT MAX(id) INTO max_id FROM public."event";
PERFORM pg_catalog.setval('public.event_id_seq', max_id, true);
INSERT INTO public."event" (create_time, update_time, category, "type", eventid, "function", mobile)
VALUES(NOW(), NOW(), 'Kintone', 'レコード一覧画面', 'app.record.index.delete.submit', 'レコードを削除するとき', true)
ON CONFLICT (eventid) DO NOTHING;
END $$;
-- eventaction テーブル
DO $$
DECLARE
max_id INTEGER;
BEGIN
-- constraint: unique_eventid_actionid
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conrelid = 'public.eventaction'::regclass
AND conname = 'unique_eventid_actionid'
) THEN
ALTER TABLE public.eventaction
ADD CONSTRAINT unique_eventid_actionid UNIQUE (eventid, actionid);
END IF;
SELECT MAX(id) INTO max_id FROM public.eventaction;
PERFORM pg_catalog.setval('public.eventaction_id_seq', max_id, true);
-- /must-input.ts
INSERT INTO public.eventaction (create_time, update_time, eventid, actionid)
VALUES(NOW(), NOW(), 'app.record.detail.delete.submit', 1)
, (NOW(), NOW(), 'app.record.index.delete.submit', 1)
ON CONFLICT (eventid, actionid) DO NOTHING;
END $$;