diff --git a/backend/app/api/api_v1/routers/auth.py b/backend/app/api/api_v1/routers/auth.py index efc1a4d..31eb2d0 100644 --- a/backend/app/api/api_v1/routers/auth.py +++ b/backend/app/api/api_v1/routers/auth.py @@ -46,6 +46,9 @@ async def login(request: Request,db:Session= Depends(get_db) ,form_data: OAuth2P data={"sub": user.id,"roles":roles,"permissions": permissions,"tenant":user.tenantid,}, expires_delta=access_token_expires, ) + + request.state.user = user.id + return JSONResponse( status_code=200, content={"access_token": access_token, "token_type": "bearer","user_name":user.first_name + " " + user.last_name} diff --git a/backend/app/core/auth.py b/backend/app/core/auth.py index 23c6231..e3fcc21 100644 --- a/backend/app/core/auth.py +++ b/backend/app/core/auth.py @@ -9,7 +9,7 @@ from app.core import security from app.db.cruddb import userService from app.core.dbmanager import get_db -async def get_current_user(security_scopes: SecurityScopes, +async def get_current_user(request: Request,security_scopes: SecurityScopes, db=Depends(get_db), token: str = Depends(security.oauth2_scheme) ): credentials_exception = HTTPException( @@ -42,6 +42,7 @@ async def get_current_user(security_scopes: SecurityScopes, user = userService.get_user(db, token_data.id) if user is None: raise credentials_exception + request.state.user = user.id return user async def get_current_active_user( diff --git a/backend/app/core/cache.py b/backend/app/core/cache.py index 7843d17..071fe33 100644 --- a/backend/app/core/cache.py +++ b/backend/app/core/cache.py @@ -1,8 +1,8 @@ import time from typing import Any from sqlalchemy.orm import Session -from app.db.cruddb import domainService -from app.db.cruddb import tenantService +from app.db.cruddb import domainService,tenantService +from app.db.session import Database class MemoryCache: def __init__(self, max_cache_size: int = 100, ttl: int = 60): @@ -60,12 +60,13 @@ class tenantCache: def __init__(self): self.memoryCache = MemoryCache(max_cache_size=50, ttl=120) - + def get_tenant_db(self,db: Session, tenantid: str): if not self.memoryCache.get(f"TENANT_{tenantid}"): tenant = tenantService.get_tenant(db,tenantid) if tenant: - self.memoryCache.set(f"TENANT_{tenantid}",tenant.db) - return self.memoryCache.get(f"TENANT_{tenantid}") + database = Database(tenant.db) + self.memoryCache.set(f"TENANT_{tenantid}",database) + return self.memoryCache.get(f"TENANT_{tenantid}") tenantCacheService =tenantCache() \ No newline at end of file diff --git a/backend/app/core/dbmanager.py b/backend/app/core/dbmanager.py index a4ccf98..8c0fa4e 100644 --- a/backend/app/core/dbmanager.py +++ b/backend/app/core/dbmanager.py @@ -1,13 +1,15 @@ -from fastapi import Depends -from app.db.session import get_tenant_db,get_user_db +from fastapi import Depends,Request +from app.db.session import get_tenant_db from app.core import tenantCacheService from app.db.session import tenantdb -def get_db(tenant:str = "1",tenantdb = Depends(get_tenant_db)): - db_url = tenantCacheService.get_tenant_db(tenantdb,tenant) - db = get_user_db(db_url) +def get_db(request: Request,tenant:str = "1",tenantdb = Depends(get_tenant_db)): + database = tenantCacheService.get_tenant_db(tenantdb,tenant) try: + db = database.get_db() + request.state.tenant = tenant + request.state.db = db yield db finally: db.close() diff --git a/backend/app/core/operation.py b/backend/app/core/operation.py index 7b5d457..739b165 100644 --- a/backend/app/core/operation.py +++ b/backend/app/core/operation.py @@ -1,52 +1,75 @@ from fastapi import Request +from fastapi.responses import JSONResponse from starlette.middleware.base import BaseHTTPMiddleware from sqlalchemy.orm import Session from app.db.models import OperationLog,User +from app.core.apiexception import APIException +from app.core.dbmanager import get_log_db +from app.db.crud import create_log +import json -from functools import wraps -from fastapi import Request +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() + else: + request.state.body = None - - -def log_operation(func): - """自定义装饰器用于记录操作日志""" - @wraps(func) - async def wrapper(*args, **kwargs): - if 'request' in kwargs and isinstance(kwargs['request'], Request): - request: Request = kwargs['request'] - method = request.method - url = str(request.url) - client_ip = request.client.host - headers = dict(request.headers) - user_agent = headers.get("user-agent", "") - - if 'db' in kwargs and isinstance(kwargs['db'], Session): - db = kwargs['db'] - if 'user' in kwargs and isinstance(kwargs['user'], User): - user = kwargs['user'] - user_id = user.id - tenant_id = user.tenantid - else: - user_id = None - tenant_id = None - - try: - response = await func(*args, **kwargs) - if db: - db_operation = OperationLog(tenantid =tenant_id, - clientip =client_ip, - useragent =user_agent, - userid = user_id, - operation = method, - function = url, - response = f"status_code:{response.status_code }" ) - - db.add(db_operation) - db.commit() + response = await call_next(request) + state = request.state except Exception as e: - raise e - return response - return wrapper + await self.log_error(request, e) + response = JSONResponse( + content={"detail": "Internal Server Error"}, + status_code=500 + ) + + if hasattr(request.state, "user") and hasattr(request.state, "tenant"): + await self.log_request(request, response,state) + return response + + async def log_request(self, request: Request, response,state): + try: + headers = dict(request.headers) + route = request.scope.get("route") + if route: + 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 }" ) + + db = request.state.db + if db: + await self.write_log_to_db(db_operation,db) + + except Exception as e: + print(f"Logging failed: {str(e)}") + + + async def log_error(self, request: Request, e: Exception): + exc = APIException('operation:dispatch',request.url._url,f"Error occurred while writting operation log:",e) + db = get_log_db() + try: + create_log(db,exc.error) + finally: + db.close() + + async def write_log_to_db(self, db_operation,db): + db.add(db_operation) + db.commit() + + \ No newline at end of file diff --git a/backend/app/db/models.py b/backend/app/db/models.py index 6aadb4b..39a3323 100644 --- a/backend/app/db/models.py +++ b/backend/app/db/models.py @@ -1,14 +1,14 @@ from sqlalchemy import Boolean, Column, Integer, String, DateTime,ForeignKey,Table from sqlalchemy.orm import Mapped,relationship,as_declarative,mapped_column -from datetime import datetime +from datetime import datetime,timezone from app.db import Base from app.core.security import chacha20Decrypt @as_declarative() class Base: id = Column(Integer, primary_key=True, index=True) - create_time = Column(DateTime, default=datetime.now) - update_time = Column(DateTime, default=datetime.now, onupdate=datetime.now) + create_time = Column(DateTime, default=datetime.now(timezone.utc)) + update_time = Column(DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc)) userrole = Table( @@ -234,6 +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)) response = mapped_column(String(200)) user = relationship('User') diff --git a/backend/app/main.py b/backend/app/main.py index b718962..479c4f7 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -20,7 +20,7 @@ from app.db.crud import create_log from fastapi.responses import JSONResponse import asyncio from contextlib import asynccontextmanager - +from app.core.operation import LoggingMiddleware #Base.metadata.create_all(bind=engine) @@ -45,6 +45,8 @@ app.add_middleware( allow_headers=["*"], ) +app.add_middleware(LoggingMiddleware) + add_pagination(app) # @app.middleware("http") diff --git a/frontend/quasar.config.js b/frontend/quasar.config.js index 20a249d..f1d5d46 100644 --- a/frontend/quasar.config.js +++ b/frontend/quasar.config.js @@ -105,7 +105,7 @@ module.exports = configure(function (/* ctx */) { config: {}, // iconSet: 'material-icons', // Quasar icon set - // lang: 'en-US', // Quasar language pack + lang: 'ja', // Quasar language pack // For special cases outside of where the auto-import strategy can have an impact // (like functional components as one of the examples), diff --git a/frontend/src/boot/error-handler.ts b/frontend/src/boot/error-handler.ts index 93be80b..6972610 100644 --- a/frontend/src/boot/error-handler.ts +++ b/frontend/src/boot/error-handler.ts @@ -4,14 +4,14 @@ import { Router } from 'vue-router'; import { App } from 'vue'; export default boot(({ app, router }: { app: App; router: Router }) => { - document.documentElement.lang="ja-JP"; + document.documentElement.lang='ja-JP'; app.config.errorHandler = (err: any, instance: any, info: string) => { if (err.response && err.response.status === 401) { // 認証エラーの場合再ログインする console.error('(; ゚Д゚)/認証エラー(401):', err, info); localStorage.removeItem('token'); router.replace({ - path:"/login", + path:'/login', query:{redirect:router.currentRoute.value.fullPath} }); } else { diff --git a/frontend/src/components/ActionSelect.vue b/frontend/src/components/ActionSelect.vue index c3f8eec..f9ea992 100644 --- a/frontend/src/components/ActionSelect.vue +++ b/frontend/src/components/ActionSelect.vue @@ -52,7 +52,7 @@ export default { filter:String }, emits:[ - "clearFilter" + 'clearFilter' ], setup(props,{emit}) { const isLoaded=ref(false); diff --git a/frontend/src/components/AppInfo.vue b/frontend/src/components/AppInfo.vue index 417a651..f0b2ccf 100644 --- a/frontend/src/components/AppInfo.vue +++ b/frontend/src/components/AppInfo.vue @@ -46,9 +46,9 @@ export default defineComponent({ const { app } = toRefs(props); const authStore = useAuthStore(); const appinfo = ref({ - appId: "", - name: "", - description: "" + appId: '', + name: '', + description: '' }); const link= ref(`${authStore.currentDomain.kintoneUrl}/k/${app.value}`); const getAppInfo = async (appId:string|undefined) => { @@ -56,7 +56,7 @@ export default defineComponent({ return; } - let result : any ={appId:"",name:""}; + let result : any ={appId:'',name:''}; let retry =0; while(retry<=3 && result && result.appId!==appId){ await new Promise(resolve => setTimeout(resolve, 1000)); diff --git a/frontend/src/components/CascadingDropDownBox.vue b/frontend/src/components/CascadingDropDownBox.vue index 2e83b92..702da13 100644 --- a/frontend/src/components/CascadingDropDownBox.vue +++ b/frontend/src/components/CascadingDropDownBox.vue @@ -175,7 +175,7 @@ export default defineComponent({ if (flowStore.appInfo?.appId === selected.id) { $q.notify({ type: 'negative', - caption: "エラー", + caption: 'エラー', message: 'データソースを現在のアプリにすることはできません。' }); } else if (selected.id !== data.value.sourceApp.id) { @@ -208,7 +208,7 @@ export default defineComponent({ if (isDuplicate) { $q.notify({ type: 'negative', - caption: "エラー", + caption: 'エラー', message: '重複したフィールドは選択できません' }); } else { diff --git a/frontend/src/components/ConditionEditor/ConditionEditor.vue b/frontend/src/components/ConditionEditor/ConditionEditor.vue index 567f4c6..d6b08d2 100644 --- a/frontend/src/components/ConditionEditor/ConditionEditor.vue +++ b/frontend/src/components/ConditionEditor/ConditionEditor.vue @@ -42,9 +42,9 @@ import { useQuasar } from 'quasar'; } }, emits:[ - "closed", - "update:conditionTree", - "update:show" + 'closed', + 'update:conditionTree', + 'update:show' ], setup(props,context) { const appDg = ref(); @@ -58,11 +58,11 @@ import { useQuasar } from 'quasar'; // message: `条件式を設定してください。` // }); // } - context.emit("update:conditionTree",tree.value); + context.emit('update:conditionTree',tree.value); } showflg.value=false; - context.emit("update:show",false); - context.emit("closed",val); + context.emit('update:show',false); + context.emit('closed',val); }; const showflg =ref(props.show); //条件式をコピーする diff --git a/frontend/src/components/ConditionEditor/NodeCondition.vue b/frontend/src/components/ConditionEditor/NodeCondition.vue index 3b654d1..9a8dd26 100644 --- a/frontend/src/components/ConditionEditor/NodeCondition.vue +++ b/frontend/src/components/ConditionEditor/NodeCondition.vue @@ -217,7 +217,7 @@ export default defineComponent( { const canMerge =(node:INode)=>{ const checkedIndexs:number[] = ticked.value; const findNode = checkedIndexs.find(index=>node.index===index); - console.log("findNode=>",findNode!==undefined,findNode); + console.log('findNode=>',findNode!==undefined,findNode); return findNode!==undefined; } //グループ化解散 diff --git a/frontend/src/components/DocUpload.vue b/frontend/src/components/DocUpload.vue index 38c205f..618edf7 100644 --- a/frontend/src/components/DocUpload.vue +++ b/frontend/src/components/DocUpload.vue @@ -1,98 +1,105 @@ - + diff --git a/frontend/src/components/DomainSelect.vue b/frontend/src/components/DomainSelect.vue index 35cae98..f3e92f9 100644 --- a/frontend/src/components/DomainSelect.vue +++ b/frontend/src/components/DomainSelect.vue @@ -42,7 +42,7 @@ export default { onMounted(() => { loading.value = true; - api.get(`api/domains`).then(res =>{ + api.get('api/domains').then(res =>{ res.data.data.forEach((data) => { const item = { id: data.id, diff --git a/frontend/src/components/FieldSelect.vue b/frontend/src/components/FieldSelect.vue index ad7e243..40d23cf 100644 --- a/frontend/src/components/FieldSelect.vue +++ b/frontend/src/components/FieldSelect.vue @@ -73,14 +73,14 @@ export default { if(!props.blackListLabel.find(blackListItem => blackListItem === fld.label)){ if(props.fieldTypes.length===0 || props.fieldTypes.includes(fld.type)){ rows.push({id:index, name: fld.label || fld.code, ...fld }); - }else if(props.fieldTypes.includes("lookup") && ("lookup" in fld)){ + }else if(props.fieldTypes.includes('lookup') && ('lookup' in fld)){ rows.push({id:index, name: fld.label || fld.code, ...fld }); } } } else { if(props.fieldTypes.length===0 || props.fieldTypes.includes(fld.type)){ rows.push({id:index, name: fld.label || fld.code, ...fld }); - }else if(props.fieldTypes.includes("lookup") && ("lookup" in fld)){ + }else if(props.fieldTypes.includes('lookup') && ('lookup' in fld)){ rows.push({id:index, name: fld.label || fld.code, ...fld }); } } diff --git a/frontend/src/components/ShareDomain/ShareDomainDialog.vue b/frontend/src/components/ShareDomain/ShareDomainDialog.vue index 32182d4..f12fe1e 100644 --- a/frontend/src/components/ShareDomain/ShareDomainDialog.vue +++ b/frontend/src/components/ShareDomain/ShareDomainDialog.vue @@ -242,7 +242,7 @@ function isManager(userId: number) { const getUsers = async () => { loading.value = true; - const result = await api.get(`api/v1/users`); + const result = await api.get('api/v1/users'); allUsers.value = result.data.data.map(itemToDisplay); loading.value = false; } diff --git a/frontend/src/components/ShareDomain/ShareManageDialog.vue b/frontend/src/components/ShareDomain/ShareManageDialog.vue index e225095..e32cd85 100644 --- a/frontend/src/components/ShareDomain/ShareManageDialog.vue +++ b/frontend/src/components/ShareDomain/ShareManageDialog.vue @@ -34,7 +34,7 @@ interface Props { const props = defineProps(); async function shareApi(user: IUserDisplay, domain: IDomainOwnerDisplay) { - return api.post(`api/managedomain`, { + return api.post('api/managedomain', { userid: user.id, domainid: domain.id, }); diff --git a/frontend/src/components/ShareDomain/ShareUsageDialog.vue b/frontend/src/components/ShareDomain/ShareUsageDialog.vue index 2aa9a19..a4533b1 100644 --- a/frontend/src/components/ShareDomain/ShareUsageDialog.vue +++ b/frontend/src/components/ShareDomain/ShareUsageDialog.vue @@ -29,7 +29,7 @@ interface Props { const props = defineProps(); async function shareApi(user: IUserDisplay, domain: IDomainOwnerDisplay) { - return api.post(`api/userdomain`, { + return api.post('api/userdomain', { userid: user.id, domainid: domain.id, }); diff --git a/frontend/src/components/UserList.vue b/frontend/src/components/UserList.vue index 96d5646..f4e776d 100644 --- a/frontend/src/components/UserList.vue +++ b/frontend/src/components/UserList.vue @@ -23,7 +23,7 @@ defineExpose({ }) const getUsers = async (filter = () => true) => { loading.value = true; - const result = await api.get(`api/v1/users`); + const result = await api.get('api/v1/users'); rows.value = result.data.data.map((item) => { return { id: item.id, firstName: item.first_name, lastName: item.last_name, email: item.email, isSuperuser: item.is_superuser, isActive: item.is_active } }).filter(filter); diff --git a/frontend/src/components/dialog/UserSelectBox.vue b/frontend/src/components/dialog/UserSelectBox.vue index 1618f1c..20a75a2 100644 --- a/frontend/src/components/dialog/UserSelectBox.vue +++ b/frontend/src/components/dialog/UserSelectBox.vue @@ -55,7 +55,7 @@ export default { ]; const fetchUsers = async () => { - const result = await api.get(`api/v1/users`); + const result = await api.get('api/v1/users'); return result.data.data.map((item: any) => { return { id: item.id, firstName: item.first_name, lastName: item.last_name, email: item.email, isSuperuser: item.is_superuser, isActive: item.is_active, roles: item.roles.map(role => role.id) } }).filter(user => !props.filterInitRowsFunc || props.filterInitRowsFunc(user)); diff --git a/frontend/src/components/left/AppSelector.vue b/frontend/src/components/left/AppSelector.vue index 08a2897..7fd5721 100644 --- a/frontend/src/components/left/AppSelector.vue +++ b/frontend/src/components/left/AppSelector.vue @@ -44,7 +44,7 @@ import { useAuthStore } from 'src/stores/useAuthStore'; export default defineComponent({ name: 'AppSelector', emits:[ - "appSelected" + 'appSelected' ], components:{ AppSelectBox, @@ -59,7 +59,7 @@ export default defineComponent({ const closeDg=(val :any)=>{ showSelectApp.value=false; - console.log("Dialog closed->",val); + console.log('Dialog closed->',val); if (val == 'OK') { const data = appDg.value.selected[0]; console.log(data); diff --git a/frontend/src/components/left/EventTree.vue b/frontend/src/components/left/EventTree.vue index 9201c63..8b7e519 100644 --- a/frontend/src/components/left/EventTree.vue +++ b/frontend/src/components/left/EventTree.vue @@ -74,7 +74,7 @@ export default defineComponent({ const selectedEvent = ref(store.selectedEvent); const selectedChangeEvent = ref(undefined); const isFieldChange = (node: IKintoneEventNode) => { - return node.header == 'EVENT' && node.eventId.indexOf(".change.") > -1; + return node.header == 'EVENT' && node.eventId.indexOf('.change.') > -1; } const getSelectedClass = (node: IKintoneEventNode) => { @@ -117,7 +117,7 @@ export default defineComponent({ $q.notify({ type: 'positive', - caption: "通知", + caption: '通知', message: `イベント ${node.label} 削除` }) } diff --git a/frontend/src/components/main/NodeItem.vue b/frontend/src/components/main/NodeItem.vue index 0e4cb8b..e4903f9 100644 --- a/frontend/src/components/main/NodeItem.vue +++ b/frontend/src/components/main/NodeItem.vue @@ -92,11 +92,11 @@ export default defineComponent({ }, emits: [ 'addNode', - "nodeSelected", - "nodeEdit", - "deleteNode", - "deleteAllNextNodes", - "copyFlow" + 'nodeSelected', + 'nodeEdit', + 'deleteNode', + 'deleteAllNextNodes', + 'copyFlow' ], setup(props, context) { const store = useFlowEditorStore(); @@ -204,7 +204,7 @@ export default defineComponent({ * 変数名取得 */ const varName =(node:IActionNode)=>{ - const prop = node.actionProps.find((prop) => prop.props.name === "verName"); + const prop = node.actionProps.find((prop) => prop.props.name === 'verName'); return prop?.props.modelValue.name; }; const copyFlow=()=>{ diff --git a/frontend/src/components/main/NodeLine.vue b/frontend/src/components/main/NodeLine.vue index 04af759..d087d0a 100644 --- a/frontend/src/components/main/NodeLine.vue +++ b/frontend/src/components/main/NodeLine.vue @@ -31,11 +31,11 @@ import { ref, defineComponent, computed, PropType } from 'vue'; import { IActionNode, ActionNode, ActionFlow, RootAction } from '../../types/ActionTypes'; export enum Direction { - Default = "None", - Left = "LEFT", - Right = "RIGHT", - LeftNotNext = "LEFTNOTNEXT", - RightNotNext = "RIGHTNOTNEXT", + Default = 'None', + Left = 'LEFT', + Right = 'RIGHT', + LeftNotNext = 'LEFTNOTNEXT', + RightNotNext = 'RIGHTNOTNEXT', } export default defineComponent({ name: 'NodeLine', diff --git a/frontend/src/components/right/ActionProperty.vue b/frontend/src/components/right/ActionProperty.vue index 2768cb6..06dcb8e 100644 --- a/frontend/src/components/right/ActionProperty.vue +++ b/frontend/src/components/right/ActionProperty.vue @@ -55,7 +55,7 @@ export default defineComponent({ }); const connectProps=(props:IProp)=>{ const connProps:any={}; - if(props && "connectProps" in props && props.connectProps!=undefined){ + if(props && 'connectProps' in props && props.connectProps!=undefined){ for(let connProp of props.connectProps){ let targetProp = componentData.value.find((prop)=>prop.props.name===connProp.propName); if(targetProp){ diff --git a/frontend/src/components/right/ColorPicker.vue b/frontend/src/components/right/ColorPicker.vue index 4138b70..a522f6d 100644 --- a/frontend/src/components/right/ColorPicker.vue +++ b/frontend/src/components/right/ColorPicker.vue @@ -72,11 +72,11 @@ export default defineComponent({ } }, setup(props, { emit }) { - const color = ref(props.modelValue??""); - const isSelected = computed(()=>props.modelValue && props.modelValue!==""); + const color = ref(props.modelValue??''); + const isSelected = computed(()=>props.modelValue && props.modelValue!==''); const customExp = props.rules === undefined ? [] : eval(props.rules); const errmsg = props.requiredMessage?props.requiredMessage:`${props.displayName}が必須です。`; - const requiredExp = props.required?[((val:any)=>!!val || errmsg ),"anyColor"]:[]; + const requiredExp = props.required?[((val:any)=>!!val || errmsg ),'anyColor']:[]; const rulesExp=[...requiredExp,...customExp]; watchEffect(()=>{ emit('update:modelValue', color.value); diff --git a/frontend/src/components/right/ConditionInput.vue b/frontend/src/components/right/ConditionInput.vue index 398c84c..341d48d 100644 --- a/frontend/src/components/right/ConditionInput.vue +++ b/frontend/src/components/right/ConditionInput.vue @@ -91,7 +91,7 @@ export default defineComponent({ }, setup(props, { emit }) { - let source = reactive(props.connectProps["source"]); + let source = reactive(props.connectProps['source']); if(!source){ source = props.context.find(element => element.props.name === 'sources'); } diff --git a/frontend/src/components/right/DataProcessing.vue b/frontend/src/components/right/DataProcessing.vue index 5a44818..0ae735b 100644 --- a/frontend/src/components/right/DataProcessing.vue +++ b/frontend/src/components/right/DataProcessing.vue @@ -206,32 +206,32 @@ export default defineComponent({ //集計処理方法 const logicalOperators = ref([ { - "operator": "", - "label": "なし" + 'operator': '', + 'label': 'なし' }, { - "operator": "SUM", - "label": "合計" + 'operator': 'SUM', + 'label': '合計' }, { - "operator": "AVG", - "label": "平均" + 'operator': 'AVG', + 'label': '平均' }, { - "operator": "MAX", - "label": "最大値" + 'operator': 'MAX', + 'label': '最大値' }, { - "operator": "MIN", - "label": "最小値" + 'operator': 'MIN', + 'label': '最小値' }, { - "operator": "COUNT", - "label": "カウント" + 'operator': 'COUNT', + 'label': 'カウント' }, { - "operator": "FIRST", - "label": "最初の値" + 'operator': 'FIRST', + 'label': '最初の値' } ]); const checkInput=(val:ValueType)=>{ @@ -239,13 +239,13 @@ export default defineComponent({ return false; } if(!val.name){ - return "集計結果の変数名を入力してください"; + return '集計結果の変数名を入力してください'; } if(!val.vars || val.vars.length==0){ - return "集計処理を設定してください"; + return '集計処理を設定してください'; } if(val.vars.some((x)=>!x.vName)){ - return "集計結果変数名を入力してください"; + return '集計結果変数名を入力してください'; } return true; } diff --git a/frontend/src/components/right/EventSetter.vue b/frontend/src/components/right/EventSetter.vue index 5ed6227..130d4ea 100644 --- a/frontend/src/components/right/EventSetter.vue +++ b/frontend/src/components/right/EventSetter.vue @@ -70,14 +70,14 @@ export default defineComponent({ const eventId =store.currentFlow?.getRoot()?.name; if(eventId===undefined){return;} let displayName = inputValue.value; - if(props.connectProps!==undefined && "displayName" in props.connectProps){ - displayName =props.connectProps["displayName"].props.modelValue; + if(props.connectProps!==undefined && 'displayName' in props.connectProps){ + displayName =props.connectProps['displayName'].props.modelValue; } const customButtonId=`${eventId}.customButtonClick`; const findedEvent = store.eventTree.findEventById(customButtonId); - if(findedEvent && "events" in findedEvent){ + if(findedEvent && 'events' in findedEvent){ const customEvents = findedEvent as IKintoneEventGroup; - const addEventId = customButtonId+"." + inputValue.value; + const addEventId = customButtonId+'.' + inputValue.value; if(store.eventTree.findEventById(addEventId)){ return; } diff --git a/frontend/src/components/right/NumInput.vue b/frontend/src/components/right/NumInput.vue index 9e65b91..63c4d0b 100644 --- a/frontend/src/components/right/NumInput.vue +++ b/frontend/src/components/right/NumInput.vue @@ -71,7 +71,7 @@ export default defineComponent({ const rulesExp=[...requiredExp,...customExp]; watchEffect(()=>{ - emit("update:modelValue",numValue.value); + emit('update:modelValue',numValue.value); }); return { numValue, diff --git a/frontend/src/components/right/PropertyList.vue b/frontend/src/components/right/PropertyList.vue index 02c2219..bac2211 100644 --- a/frontend/src/components/right/PropertyList.vue +++ b/frontend/src/components/right/PropertyList.vue @@ -59,7 +59,7 @@ export default defineComponent({ const properties=ref(props.nodeProps); const connectProps=(props:IProp)=>{ const connProps:any={context:properties}; - if(props && "connectProps" in props && props.connectProps!=undefined){ + if(props && 'connectProps' in props && props.connectProps!=undefined){ for(let connProp of props.connectProps){ let targetProp = properties.value.find((prop)=>prop.props.name===connProp.propName); if(targetProp){ diff --git a/frontend/src/control/auth.ts b/frontend/src/control/auth.ts index 2ea3423..4bc170e 100644 --- a/frontend/src/control/auth.ts +++ b/frontend/src/control/auth.ts @@ -9,7 +9,7 @@ export class Auth params.append('username', user); params.append('password', pwd); try{ - const result = await api.post(`api/token`,params); + const result = await api.post('api/token',params); console.info(result); localStorage.setItem('Token', result.data.access_token); return true; diff --git a/frontend/src/pages/FlowChart.vue b/frontend/src/pages/FlowChart.vue index 1e3cdb2..a68f8ba 100644 --- a/frontend/src/pages/FlowChart.vue +++ b/frontend/src/pages/FlowChart.vue @@ -130,15 +130,15 @@ const route = useRoute() const appDg = ref(); const prevNodeIfo = ref({ prevNode: {} as IActionNode, - inputPoint: "" + inputPoint: '' }); // const refFlow = ref(null); const showAddAction = ref(false); const saveVersionAction = ref(false); const versionInputRef = ref(); const drawerRight = ref(false); -const filter=ref(""); -const model = ref(""); +const filter=ref(''); +const model = ref(''); const rootNode = computed(()=>{ return store.currentFlow?.getRoot(); @@ -148,11 +148,11 @@ const minPanelWidth=computed(()=>{ if(store.currentFlow && root){ return store.currentFlow?.getColumns(root) * 300 + 'px'; }else{ - return "300px"; + return '300px'; } }); const fixedLeftPosition = computed(()=>{ - return drawerLeft.value?"300px":"0px"; + return drawerLeft.value?'300px':'0px'; }); const addNode = (node: IActionNode, inputPoint: string) => { @@ -195,12 +195,12 @@ const onDeleteAllNextNodes = (node: IActionNode) => { store.currentFlow?.removeAllNext(node.id); } const closeDg = (val: any) => { - console.log("Dialog closed->", val); + console.log('Dialog closed->', val); if (val == 'OK' && appDg?.value?.selected?.length > 0) { const data = appDg.value.selected[0]; const actionProps = JSON.parse(data.property); const outputPoint = JSON.parse(data.outputPoints); - const action = new ActionNode(data.name, data.desc, "", outputPoint, actionProps); + const action = new ActionNode(data.name, data.desc, '', outputPoint, actionProps); store.currentFlow?.addNode(action, prevNodeIfo.value.prevNode, prevNodeIfo.value.inputPoint); } } @@ -227,8 +227,8 @@ const onDeploy = async () => { if (store.appInfo === undefined || store.flows?.length === 0) { $q.notify({ type: 'negative', - caption: "エラー", - message: `設定されたフローがありません。` + caption: 'エラー', + message: '設定されたフローがありません。' }); return; } @@ -238,16 +238,16 @@ const onDeploy = async () => { deployLoading.value = false; $q.notify({ type: 'positive', - caption: "通知", - message: `デプロイを成功しました。` + caption: '通知', + message: 'デプロイを成功しました。' }); } catch (error) { console.error(error); deployLoading.value = false; $q.notify({ type: 'negative', - caption: "エラー", - message: `デプロイが失敗しました。` + caption: 'エラー', + message: 'デプロイが失敗しました。' }) } return; @@ -258,7 +258,7 @@ const onSaveActionProps=(props:IActionProperty[])=>{ store.activeNode.actionProps=props; $q.notify({ type: 'positive', - caption: "通知", + caption: '通知', message: `${store.activeNode?.subTitle}の属性を設定しました。(保存はされていません)` }); } @@ -291,7 +291,7 @@ const onSaveFlow = async () => { $q.notify({ type: 'negative', caption: 'エラー', - message: `選択中のフローがありません。` + message: '選択中のフローがありません。' }); return; } @@ -301,7 +301,7 @@ const onSaveFlow = async () => { saveLoading.value = false; $q.notify({ type: 'positive', - caption: "通知", + caption: '通知', message: `${targetFlow.getRoot()?.subTitle}のフロー設定を保存しました。` }); } catch (error) { @@ -309,7 +309,7 @@ const onSaveFlow = async () => { saveLoading.value = false; $q.notify({ type: 'negative', - caption: "エラー", + caption: 'エラー', message: `${targetFlow.getRoot()?.subTitle}のフローの設定の保存が失敗しました。` }) } @@ -324,7 +324,7 @@ const onSaveAllFlow= async ()=>{ $q.notify({ type: 'negative', caption: 'エラー', - message: `設定されたフローがありません。` + message: '設定されたフローがありません。' }); return; } @@ -338,8 +338,8 @@ const onSaveAllFlow= async ()=>{ } $q.notify({ type: 'positive', - caption: "通知", - message: `すべてのフロー設定を保存しました。` + caption: '通知', + message: 'すべてのフロー設定を保存しました。' }); saveLoading.value = false; }catch (error) { @@ -347,8 +347,8 @@ const onSaveAllFlow= async ()=>{ saveLoading.value = false; $q.notify({ type: 'negative', - caption: "エラー", - message: `フローの設定の保存が失敗しました。` + caption: 'エラー', + message: 'フローの設定の保存が失敗しました。' }); } } diff --git a/frontend/src/pages/FlowChartTest.vue b/frontend/src/pages/FlowChartTest.vue index 46d6b4f..c89202a 100644 --- a/frontend/src/pages/FlowChartTest.vue +++ b/frontend/src/pages/FlowChartTest.vue @@ -27,23 +27,23 @@ import ActionSelect from 'components/ActionSelect.vue'; import PropertyPanel from 'components/right/PropertyPanel.vue'; -const rootNode:RootAction =new RootAction("app.record.create.submit","レコード追加画面","保存するとき"); +const rootNode:RootAction =new RootAction('app.record.create.submit','レコード追加画面','保存するとき'); const actionFlow: ActionFlow = new ActionFlow(rootNode); const saibanProps:IActionProperty[]=[{ - component:"InputText", + component:'InputText', props:{ - displayName:"フォーマット", - modelValue:"", - name:"format", - placeholder:"フォーマットを入力してください", + displayName:'フォーマット', + modelValue:'', + name:'format', + placeholder:'フォーマットを入力してください', } },{ - component:"FieldInput", + component:'FieldInput', props:{ - displayName:"採番項目", - modelValue:"", - name:"field", - placeholder:"採番項目を選択してください", + displayName:'採番項目', + modelValue:'', + name:'field', + placeholder:'採番項目を選択してください', } }]; @@ -91,7 +91,7 @@ const onDeleteAllNextNodes=(node:IActionNode)=>{ refFlow.value.removeAllNext(node.id); } const closeDg=(val :any)=>{ - console.log("Dialog closed->",val); + console.log('Dialog closed->',val); } diff --git a/frontend/src/pages/IndexPage.vue b/frontend/src/pages/IndexPage.vue index cd5d107..8493af5 100644 --- a/frontend/src/pages/IndexPage.vue +++ b/frontend/src/pages/IndexPage.vue @@ -1,12 +1,11 @@ diff --git a/frontend/src/pages/LoginPage.vue b/frontend/src/pages/LoginPage.vue index 46580db..f9da231 100644 --- a/frontend/src/pages/LoginPage.vue +++ b/frontend/src/pages/LoginPage.vue @@ -61,7 +61,7 @@ import { useAuthStore } from 'stores/useAuthStore'; let email = ref(''); let password = ref(''); let visibility = ref(false); - let passwordFieldType = ref('password'); + let passwordFieldType = ref<'text'|'password'>('password'); let visibilityIcon = ref('visibility'); const required = (val:string) => { return (val && val.length > 0 || '必須項目') diff --git a/frontend/src/pages/RuleEditor.vue b/frontend/src/pages/RuleEditor.vue index 03453eb..d6d89e8 100644 --- a/frontend/src/pages/RuleEditor.vue +++ b/frontend/src/pages/RuleEditor.vue @@ -66,8 +66,8 @@ interface Props { actions: string[]; } const props = withDefaults(defineProps(), { - title: "ルールエディター", - actions: () => ["フィールド制御", "一覧画面", "その他"] + title: 'ルールエディター', + actions: () => ['フィールド制御', '一覧画面', 'その他'] }); function onItemClick(evt: Event) { return; diff --git a/frontend/src/pages/TenantDomain.vue b/frontend/src/pages/TenantDomain.vue index 1ad89a7..5d43df8 100644 --- a/frontend/src/pages/TenantDomain.vue +++ b/frontend/src/pages/TenantDomain.vue @@ -250,7 +250,7 @@ const isOwner = (row: IDomainOwnerDisplay) => row.owner.id === Number(authStore. const getDomain = async (filter?: (row: IDomainOwnerDisplay) => boolean) => { loading.value = true; - const { data } = await api.get<{data:IDomain[]}>(`api/domains`); + const { data } = await api.get<{data:IDomain[]}>('api/domains'); rows.value = data.data.map((item) => { return { id: item.id, @@ -309,7 +309,7 @@ const deleteDomain = () => { editId.value = 0; // set in removeRow() }; -function editRow(row) { +function editRow(row: any) { isCreate.value = false editId.value = row.id; // tenantid.value = row.tenantid; @@ -350,7 +350,7 @@ const onSubmit = () => { 'ownerid': authStore.userId || '' } // for search: api.put(`api/domain`)、api.post(`api/domain`) - api[method].apply(api, [`api/domain`, param]).then(async (resp: any) => { + api[method].apply(api, ['api/domain', param]).then(async (resp: any) => { const res = resp.data; if (res.data.id === currentDomainId.value && !res.data.is_active) { await authStore.setCurrentDomain(); diff --git a/frontend/src/pages/UserDomain.vue b/frontend/src/pages/UserDomain.vue index 481a3d2..c8442a8 100644 --- a/frontend/src/pages/UserDomain.vue +++ b/frontend/src/pages/UserDomain.vue @@ -14,7 +14,7 @@
- + @@ -114,7 +114,7 @@ const addUserDomainFinished = async (val: string) => { const selected = addDomainRef.value.selected; if (val == 'OK' && selected.length > 0) { addUserDomainLoading.value = true; - const { data } = await api.post(`api/userdomain`, { + const { data } = await api.post('api/userdomain', { userid: authStore.userId, domainid: selected[0].id, }); @@ -169,9 +169,9 @@ const isActive = computed(() => (id: number) => { const getDomain = async (userId? : string) => { rowIds.clear(); - const resp = await api.get(`api/defaultdomain`); + const resp = await api.get('api/defaultdomain'); activeDomainId.value = resp?.data?.data?.id; - const domainResult = userId ? await api.get(`api/domain?userId=${userId}`) : await api.get(`api/domain`); + const domainResult = userId ? await api.get(`api/domain?userId=${userId}`) : await api.get('api/domain'); const domains = domainResult.data as any[]; rows.value = domains.sort((a, b) => a.id - b.id).reduce((acc, item) => { rowIds.add(item.id); diff --git a/frontend/src/pages/testQursar.vue b/frontend/src/pages/testQursar.vue index 83e7e6b..261a786 100644 --- a/frontend/src/pages/testQursar.vue +++ b/frontend/src/pages/testQursar.vue @@ -54,8 +54,8 @@ const mouseenter = (event: Event) => { let oDivs = oDiv1?.getElementsByClassName('add'); if (oDivs.length === 0) { let oDiv2 = document.createElement('div'); - oDiv2.className = "add"; - oDiv2.setAttribute("style", "display:table-row;height:inherit;position: absolute;left:calc(50% - 19px);"); + oDiv2.className = 'add'; + oDiv2.setAttribute('style', 'display:table-row;height:inherit;position: absolute;left:calc(50% - 19px);'); oDiv2.innerHTML = oAdd; oDiv1?.append(oDiv2); } diff --git a/frontend/src/router/routes.ts b/frontend/src/router/routes.ts index 6a804d5..06fa905 100644 --- a/frontend/src/router/routes.ts +++ b/frontend/src/router/routes.ts @@ -16,7 +16,7 @@ const routes: RouteRecordRaw[] = [ path: '/', component: () => import('layouts/MainLayout.vue'), children: [ - { path: '', component: () => import('pages/IndexPage.vue') }, + { path: '', component: () => import('pages/IndexPage.vue'), props: { app: '' } }, { path: 'ruleEditor', component: () => import('pages/RuleEditor.vue') }, { path: 'flow', component: () => import('pages/testFlow.vue') }, { path: 'FlowChartTest', component: () => import('pages/FlowChartTest.vue') }, diff --git a/frontend/src/stores/useAppStore.ts b/frontend/src/stores/useAppStore.ts index 78dc137..dab3570 100644 --- a/frontend/src/stores/useAppStore.ts +++ b/frontend/src/stores/useAppStore.ts @@ -103,5 +103,5 @@ function appToAppDisplay(app: IManagedApp) { } function formatDate(data: string) { - return date.formatDate(data, 'YYYY/MM/DD HH:mm'); + return date.formatDate(new Date(data + 'Z'), 'YYYY/MM/DD HH:mm'); } diff --git a/frontend/src/stores/useAuthStore.ts b/frontend/src/stores/useAuthStore.ts index 0c3b28a..ad503d7 100644 --- a/frontend/src/stores/useAuthStore.ts +++ b/frontend/src/stores/useAuthStore.ts @@ -64,7 +64,7 @@ export const useAuthStore = defineStore('auth', { params.append('username', username); params.append('password', password); try { - const result = await api.post(`api/token`, params); + const result = await api.post('api/token', params); // console.info(result); this.token = result.data.access_token; const tokenJson = jwtDecode<{sub: number, tenant: string, roles: string}>(result.data.access_token); @@ -86,7 +86,7 @@ export const useAuthStore = defineStore('auth', { } }, async loadCurrentDomain() { - const resp = await api.get>(`api/defaultdomain`); + const resp = await api.get>('api/defaultdomain'); const activedomain = resp?.data?.data; if (!activedomain) { this.currentDomain = {} as IDomainInfo; @@ -99,13 +99,13 @@ export const useAuthStore = defineStore('auth', { } }, async loadUserInfo() { - const resp = (await api.get>(`api/v1/users/me`))?.data?.data; + const resp = (await api.get>('api/v1/users/me'))?.data?.data; this.userInfo = userToUserRolesDisplay(resp) }, async loadPermission() { this.permissions = {} as IPermissions; if (this.isSuperAdmin) return; - const resp = (await api.get>(`api/v1/userpermssions`)).data.data; + const resp = (await api.get>('api/v1/userpermssions')).data.data; resp.forEach((permission) => { this.permissions[permission.link] = permission.menu; this.permissions[permission.privilege] = permission.function; diff --git a/frontend/src/types/ActionTypes.ts b/frontend/src/types/ActionTypes.ts index 3ccdce1..0825d5a 100644 --- a/frontend/src/types/ActionTypes.ts +++ b/frontend/src/types/ActionTypes.ts @@ -129,7 +129,7 @@ export class ActionNode implements IActionNode { id: string; name: string; get title(): string { - const prop = this.actionProps.find((prop) => prop.props.name === "displayName"); + const prop = this.actionProps.find((prop) => prop.props.name === 'displayName'); return prop?.props.modelValue; }; get subTitle(): string { @@ -138,7 +138,7 @@ export class ActionNode implements IActionNode { //変数名 get varName():IProp|undefined{ - const prop = this.actionProps.find((prop) => prop.props.name === "verName"); + const prop = this.actionProps.find((prop) => prop.props.name === 'verName'); return prop?.props; } diff --git a/frontend/src/types/KintoneEvents.ts b/frontend/src/types/KintoneEvents.ts index f0fdf7c..3928a4d 100644 --- a/frontend/src/types/KintoneEvents.ts +++ b/frontend/src/types/KintoneEvents.ts @@ -136,7 +136,7 @@ export class KintoneEventManager { const flows:IActionFlow[]=[]; for (const screen of this.screens) { for (const event of screen.events) { - if (event.header === "EVENT") { + if (event.header === 'EVENT') { const eventNode = event as IKintoneEvent; if(eventNode.flowData!==undefined){ flows.push(eventNode.flowData); @@ -144,7 +144,7 @@ export class KintoneEventManager { }else if (event.header === 'EVENTGROUP' || event.header === 'CHANGE') { const eventGroup = event as IKintoneEventGroup; eventGroup.events.forEach((ev) => { - if (ev.header === "EVENT" || ev.header === "DELETABLE") { + if (ev.header === 'EVENT' || ev.header === 'DELETABLE') { const eventNode = ev as IKintoneEvent; if(eventNode.flowData!==undefined){ flows.push(eventNode.flowData);