diff --git a/backend/app/api/api_v1/routers/platform.py b/backend/app/api/api_v1/routers/platform.py
index c6dac46..3e9d218 100644
--- a/backend/app/api/api_v1/routers/platform.py
+++ b/backend/app/api/api_v1/routers/platform.py
@@ -15,6 +15,7 @@ from app.db.cruddb import domainService,appService
import httpx
import app.core.config as config
+from app.core import domainCacheService
platform_router = r = APIRouter()
@@ -82,10 +83,10 @@ async def apps_update(
db=Depends(get_db),
):
try:
- domain = domainService.get_default_domain(db, user.id)
- if not domain:
+ domainurl = domainCacheService.get_default_domainurl(db,user.id)
+ if not domainurl:
return ApiReturnModel(data = None)
- return ApiReturnModel(data =appService.update_appversion(db, domain.url,app,user.id))
+ return ApiReturnModel(data =appService.update_appversion(db, domainurl,app,user.id))
except Exception as e:
raise APIException('platform:apps',request.url._url,f"Error occurred while get create app :",e)
@@ -100,10 +101,10 @@ async def apps_delete(
db=Depends(get_db),
):
try:
- domain = domainService.get_default_domain(db, user.id) #get_activedomain(db, user.id)
- if not domain:
+ domainurl = domainCacheService.get_default_domainurl(db,user.id) #get_activedomain(db, user.id)
+ if not domainurl:
return ApiReturnModel(data = None)
- return ApiReturnModel(data =appService.delete_app(db, domain.url,appid))
+ return ApiReturnModel(data =appService.delete_app(db, domainurl,appid))
except Exception as e:
raise APIException('platform:apps',request.url._url,f"Error occurred while delete app({appid}):",e)
@@ -119,10 +120,10 @@ async def appversions_list(
db=Depends(get_db),
):
try:
- domain = domainService.get_default_domain(db, user.id) #get_activedomain(db, user.id)
- if not domain:
+ domainurl = domainCacheService.get_default_domainurl(db,user.id) #get_activedomain(db, user.id)
+ if not domainurl:
return ApiReturnPage(data = None)
- return appService.get_appversions(db,domain.url,appid)
+ return appService.get_appversions(db,domainurl,appid)
except Exception as e:
raise APIException('platform:appversions',request.url._url,f"Error occurred while get app({appid}) version :",e)
@@ -139,10 +140,10 @@ async def appversions_change(
db=Depends(get_db),
):
try:
- domain = domainService.get_default_domain(db, user.id) #get_activedomain(db, user.id)
- if not domain:
+ domainurl = domainCacheService.get_default_domainurl(db,user.id) #get_activedomain(db, user.id)
+ if not domainurl:
ApiReturnModel(data = None)
- return ApiReturnModel(data = appService.change_appversion(db, domain.url,appid,version,user.id))
+ return ApiReturnModel(data = appService.change_appversion(db, domainurl,appid,version,user.id))
except Exception as e:
raise APIException('platform:appversions',request.url._url,f"Error occurred while change app version:",e)
@@ -237,7 +238,7 @@ async def action_data(
@r.get(
"/flow/{appid}",tags=["App"],
- response_model=ApiReturnModel[List[Flow|None]],
+ response_model=ApiReturnModel[Flow|None],
response_model_exclude_none=True,
)
async def flow_details(
@@ -247,10 +248,10 @@ async def flow_details(
db=Depends(get_db),
):
try:
- domain = domainService.get_default_domain(db, user.id)
- if not domain:
+ domainurl = domainCacheService.get_default_domainurl(db,user.id)
+ if not domainurl:
return ApiReturnModel(data = None)
- return ApiReturnModel(data = appService.get_flow(db, domain.url, appid,user.id))
+ return ApiReturnModel(data = appService.get_flow(db, domainurl, appid,user.id))
except Exception as e:
raise APIException('platform:flow',request.url._url,f"Error occurred while get flow by flowid:",e)
@@ -266,11 +267,10 @@ async def flow_list(
db=Depends(get_db),
):
try:
- domain = domainService.get_default_domain(db, user.id) #get_activedomain(db, user.id)
- if not domain:
+ domainurl = domainCacheService.get_default_domainurl(db,user.id) #get_activedomain(db, user.id)
+ if not domainurl:
return []
- print("domain=>",domain)
- flows = get_flows_by_app(db, domain.url, appid)
+ flows = get_flows_by_app(db, domainurl, appid)
return flows
except Exception as e:
raise APIException('platform:flow',request.url._url,f"Error occurred while get flow by appid:",e)
@@ -286,10 +286,10 @@ async def flow_create(
db=Depends(get_db),
):
try:
- domain = domainService.get_default_domain(db, user.id) #get_activedomain(db, user.id)
- if not domain:
+ domainurl = domainCacheService.get_default_domainurl(db,user.id) #get_activedomain(db, user.id)
+ if not domainurl:
return ApiReturnModel(data = None)
- return ApiReturnModel(data = appService.create_flow(db, domain.url, flow,user.id))
+ return ApiReturnModel(data = appService.create_flow(db, domainurl, flow,user.id))
except Exception as e:
raise APIException('platform:flow',request.url._url,f"Error occurred while create flow:",e)
@@ -306,10 +306,10 @@ async def flow_edit(
db=Depends(get_db),
):
try:
- domain = domainService.get_default_domain(db, user.id)
- if not domain:
+ domainurl = domainCacheService.get_default_domainurl(db,user.id)
+ if not domainurl:
return ApiReturnModel(data = None)
- return ApiReturnModel(data = appService.edit_flow(db,domain.url, flow,user.id))
+ return ApiReturnModel(data = appService.edit_flow(db,domainurl, flow,user.id))
except Exception as e:
raise APIException('platform:flow',request.url._url,f"Error occurred while edit flow:",e)
@@ -326,8 +326,8 @@ async def flow_delete(
db=Depends(get_db),
):
try:
- domain = domainService.get_default_domain(db, user.id)
- if not domain:
+ domainurl = domainCacheService.get_default_domainurl(db,user.id)
+ if not domainurl:
return ApiReturnModel(data = None)
return ApiReturnModel(data = appService.delete_flow(db, flowid))
except Exception as e:
@@ -395,7 +395,10 @@ async def domain_edit(
db=Depends(get_db),
):
try:
- return ApiReturnModel(data = domainService.edit_domain(db, domain,user.id))
+ domain = domainService.edit_domain(db, domain,user.id)
+ if domain :
+ domainCacheService.clear_default_domainurl()
+ return ApiReturnModel(data = domain)
except Exception as e:
raise APIException('platform:domain',request.url._url,f"Error occurred while edit domain:",e)
@@ -482,7 +485,7 @@ async def get_defaultuserdomain(
db=Depends(get_db),
):
try:
- return ApiReturnModel(data =domainService.get_default_domain(db, user.id))
+ return ApiReturnModel(data =domainService.get_default_domain(db,user.id))
except Exception as e:
raise APIException('platform:defaultdomain',request.url._url,f"Error occurred while get user({user.id}) defaultdomain:",e)
@@ -498,7 +501,7 @@ async def set_defualtuserdomain(
db=Depends(get_db),
):
try:
- domain = domainService.set_default_domain(db,user.id,domainid)
+ domain = domainCacheService.set_default_domain(db,user.id,domainid)
return ApiReturnModel(data= domain)
except Exception as e:
raise APIException('platform:defaultdomain',request.url._url,f"Error occurred while update user({user.id}) defaultdomain:",e)
diff --git a/backend/app/core/__init__.py b/backend/app/core/__init__.py
index e69de29..21b6db6 100644
--- a/backend/app/core/__init__.py
+++ b/backend/app/core/__init__.py
@@ -0,0 +1 @@
+from app.core.cache import domainCacheService
\ No newline at end of file
diff --git a/backend/app/core/cache.py b/backend/app/core/cache.py
new file mode 100644
index 0000000..da3f624
--- /dev/null
+++ b/backend/app/core/cache.py
@@ -0,0 +1,55 @@
+import time
+from typing import Any
+from sqlalchemy.orm import Session
+from app.db.cruddb import domainService
+
+class MemoryCache:
+ def __init__(self, max_cache_size: int = 100, ttl: int = 60):
+ self.cache = {}
+ self.max_cache_size = max_cache_size
+ self.ttl = ttl
+
+ def get(self, key: str) -> Any:
+ item = self.cache.get(key)
+ if item:
+ if time.time() - item['timestamp'] > self.ttl:
+ self.cache.pop(key)
+ return None
+ return item['value']
+ return None
+
+ def set(self, key: str, value: Any) -> None:
+ if len(self.cache) >= self.max_cache_size:
+ self.cache.pop(next(iter(self.cache)))
+ self.cache[key] = {'value': value, 'timestamp': time.time()}
+
+ # def clear(self,key) -> None:
+ # self.cache.pop(key,None)
+
+ def clear(self) -> None:
+ self.cache.clear()
+
+
+
+class domainCache:
+
+ def __init__(self):
+ self.memoryCache = MemoryCache(max_cache_size=50, ttl=120)
+
+ def set_default_domain(self, db: Session,userid: int,domainid:str):
+ domain = domainService.set_default_domain(db,userid,domainid)
+ if domain:
+ self.memoryCache.set(f"DOMAIN_{userid}",domain.url)
+ return domain
+
+ def get_default_domainurl(self,db: Session, userid: int):
+ if not self.memoryCache.get(f"DOMAIN_{userid}"):
+ domain = domainService.get_default_domain(db,userid)
+ if domain:
+ self.memoryCache.set(f"DOMAIN_{userid}",domain.url)
+ return self.memoryCache.get(f"DOMAIN_{userid}")
+
+ def clear_default_domainurl(self):
+ self.memoryCache.clear()
+
+domainCacheService =domainCache()
\ No newline at end of file
diff --git a/backend/app/db/cruddb/dbapp.py b/backend/app/db/cruddb/dbapp.py
index f9bfbad..de44cc7 100644
--- a/backend/app/db/cruddb/dbapp.py
+++ b/backend/app/db/cruddb/dbapp.py
@@ -47,7 +47,7 @@ class dbflow(crudbase):
updateuserid = userid
)
db.add(db_flow)
- db_app = db.execute(select(models.App).filter(and_(models.App.domainurl == domainurl,models.App.appid == flow.appid))).scalars().first()
+ db_app = db.execute(select(models.App).where(and_(models.App.domainurl == domainurl,models.App.appid == flow.appid))).scalars().first()
if not db_app:
db_app = models.App(
domainurl = domainurl,
diff --git a/backend/app/tests/test_domain.py b/backend/app/tests/test_domain.py
index 96d8992..f923f3d 100644
--- a/backend/app/tests/test_domain.py
+++ b/backend/app/tests/test_domain.py
@@ -57,8 +57,9 @@ def test_delete_domain(test_client, login_user):
def test_set_defaultuserdomain(test_client, test_domain,login_user):
response = test_client.put("/api/defaultdomain/"+str(test_domain.id), headers={"Authorization": "Bearer " + login_user})
- assert response.status_code == 200
data = response.json()
+ logging.error(data)
+ assert response.status_code == 200
assert "data" in data
assert data["data"] is not None
assert data["data"]["name"] == test_domain.name
@@ -99,8 +100,9 @@ def test_edit_domain(test_client, test_domain, login_user):
"is_active": True
}
response = test_client.put("/api/domain", json=update_domain,headers={"Authorization": "Bearer " + login_user})
- assert response.status_code == 200
data = response.json()
+ logging.error(data)
+ assert response.status_code == 200
assert "data" in data
assert data["data"] is not None
assert data["data"]["name"] == update_domain["name"]
diff --git a/backend/app/tests/test_user_app.py b/backend/app/tests/test_user_app.py
index 297ae7e..d416e73 100644
--- a/backend/app/tests/test_user_app.py
+++ b/backend/app/tests/test_user_app.py
@@ -37,8 +37,9 @@ def test_edit_flow(test_client,test_domain,test_app_id,login_user):
"content": ""
}
response = test_client.put("/api/flow", json=test_flow,headers={"Authorization": "Bearer " + login_user})
- assert response.status_code == 200
data = response.json()
+ logging.error(data)
+ assert response.status_code == 200
assert "data" in data
assert data["data"] is not None
assert data["data"]["domainurl"] == test_domain.url
diff --git a/frontend/src/components/DomainSelector.vue b/frontend/src/components/DomainSelector.vue
index 44676e4..c0ea828 100644
--- a/frontend/src/components/DomainSelector.vue
+++ b/frontend/src/components/DomainSelector.vue
@@ -8,65 +8,23 @@
size="md"
:label="userStore.currentDomain.domainName"
:disable-dropdown="true"
- dropdown-icon='none'
+ dropdown-icon="none"
:disable="true"
>
-
-
-
-
-
-
- {{domain.domainName}}
- {{domain.kintoneUrl}}
-
-
-
-
-
diff --git a/frontend/src/components/TableActionMenu.vue b/frontend/src/components/TableActionMenu.vue
index 432eaa6..9e40f53 100644
--- a/frontend/src/components/TableActionMenu.vue
+++ b/frontend/src/components/TableActionMenu.vue
@@ -1,7 +1,7 @@
diff --git a/frontend/src/components/right/PropertyPanel.vue b/frontend/src/components/right/PropertyPanel.vue
index 5384028..4aa3d87 100644
--- a/frontend/src/components/right/PropertyPanel.vue
+++ b/frontend/src/components/right/PropertyPanel.vue
@@ -40,8 +40,7 @@ import { IActionNode, IActionProperty } from 'src/types/ActionTypes';
},
props: {
actionNode:{
- type:Object as PropType,
- required:true
+ type:Object as PropType
},
drawerRight:{
type:Boolean,
@@ -55,7 +54,7 @@ import { IActionNode, IActionProperty } from 'src/types/ActionTypes';
setup(props,{emit}) {
const showPanel =ref(props.drawerRight);
- const cloneProps = (actionProps:IActionProperty[]):IActionProperty[]|null=>{
+ const cloneProps = (actionProps:IActionProperty[]|undefined):IActionProperty[]|null=>{
if(!actionProps){
return null;
}
diff --git a/frontend/src/pages/AppManagement.vue b/frontend/src/pages/AppManagement.vue
index 9cdda9e..ba63b38 100644
--- a/frontend/src/pages/AppManagement.vue
+++ b/frontend/src/pages/AppManagement.vue
@@ -5,7 +5,7 @@
-
+
@@ -46,12 +46,6 @@
-
-
-
-
-
@@ -69,20 +63,16 @@
+
+
\ No newline at end of file
diff --git a/frontend/src/pages/TenantDomain.vue b/frontend/src/pages/TenantDomain.vue
index 7fbe76b..04a89d9 100644
--- a/frontend/src/pages/TenantDomain.vue
+++ b/frontend/src/pages/TenantDomain.vue
@@ -142,13 +142,11 @@
import { ref, onMounted, computed } from 'vue';
import { api } from 'boot/axios';
import { useAuthStore } from 'stores/useAuthStore';
-import { useDomainStore } from 'stores/useDomainStore';
import ShareDomainDialog from 'components/ShareDomain/ShareDomainDialog.vue';
import TableActionMenu from 'components/TableActionMenu.vue';
import { IDomain, IDomainDisplay, IDomainOwnerDisplay, IDomainSubmit } from '../types/DomainTypes';
const authStore = useAuthStore();
-const domainStore = useDomainStore();
const inactiveRowClass = (row: IDomainOwnerDisplay) => row.domainActive ? '' : 'inactive-row';
const columns = [
@@ -329,7 +327,6 @@ const onSubmit = () => {
await authStore.setCurrentDomain();
}
getDomain();
- domainStore.loadUserDomains();
closeDg();
onReset();
addEditLoading.value = false;
diff --git a/frontend/src/router/routes.ts b/frontend/src/router/routes.ts
index 1b999ad..3740740 100644
--- a/frontend/src/router/routes.ts
+++ b/frontend/src/router/routes.ts
@@ -28,6 +28,7 @@ const routes: RouteRecordRaw[] = [
{ path: 'userdomain', component: () => import('pages/UserDomain.vue')},
{ path: 'user', component: () => import('pages/UserManagement.vue')},
{ path: 'app', component: () => import('pages/AppManagement.vue')},
+ { path: 'app/version/:id', component: () => import('pages/AppVersionManagement.vue')},
{ path: 'condition', component: () => import('pages/conditionPage.vue') }
],
},
diff --git a/frontend/src/stores/useAppStore.ts b/frontend/src/stores/useAppStore.ts
new file mode 100644
index 0000000..6546521
--- /dev/null
+++ b/frontend/src/stores/useAppStore.ts
@@ -0,0 +1,106 @@
+import { defineStore } from 'pinia';
+import { api } from 'boot/axios';
+import { IAppDisplay, IAppVersion, IAppVersionDisplay, IManagedApp } from 'src/types/AppTypes';
+import { IUser } from 'src/types/UserTypes';
+import { date, Notify } from 'quasar'
+
+
+export const useAppStore = defineStore('app', {
+ state: () => ({
+ apps: [] as IAppDisplay[],
+ rowIds: new Set(),
+ }),
+ actions: {
+ async loadApps() {
+ this.reset();
+ try {
+ const { data } = await api.get('api/apps');
+ this.apps = data.data.map((item: IManagedApp) => {
+ this.rowIds.add(item.appid);
+ return appToAppDisplay(item)
+ }).sort((a: IAppDisplay, b: IAppDisplay) => a.sortId - b.sortId); // set default order
+ } catch (error) {
+ Notify.create({
+ icon: 'error',
+ color: 'negative',
+ message: 'アプリ一覧の読み込みに失敗しました'
+ })
+ }
+ },
+
+ getAppById(id: string) {
+ if (!this.rowIds.has(id)) {
+ return null;
+ }
+ return this.apps.find((item: IAppDisplay) => item.id === id);
+ },
+
+ async getVersionsByAppId(app: IAppDisplay) {
+ const { data } = await api.get(`api/appversions/${app.id}`);
+ return data.data.map((item: IAppVersion) => versionToVersionDisplay(item));
+ },
+
+ async changeVersion(app: IAppDisplay, version: IAppVersionDisplay) {
+ await api.put(`api/appversions/${app.id}/${version.id}`);
+ },
+
+ async deleteApp(app: IAppDisplay) {
+ try {
+ await api.delete(`api/apps/${app.id}`);
+ } catch (error) {
+ console.error(error);
+ Notify.create({
+ icon: 'error',
+ color: 'negative',
+ message: 'アプリの削除に失敗しました'
+ });
+ return false;
+ }
+ return true;
+ },
+
+ reset() {
+ this.apps = [];
+ this.rowIds.clear();
+ },
+ },
+});
+
+function versionToVersionDisplay(item: IAppVersion) {
+ return {
+ id: item.version,
+ version: item.version,
+ appid: item.appid,
+ name: item.versionname,
+ comment: item.comment,
+ // updater: toUserDisplay(item.updateuser),
+ // updateTime: formatDate(item.updatetime),
+ // creator: toUserDisplay(item.createuser),
+ // createTime: formatDate(item.createtime),
+ } as IAppVersionDisplay;
+}
+
+function appToAppDisplay(app: IManagedApp) {
+ return {
+ id: app.appid,
+ sortId: parseInt(app.appid, 10),
+ name: app.appname,
+ url: `${app.domainurl}/k/${app.appid}`,
+ version: app.version,
+ updateTime: date.formatDate(app.update_time, 'YYYY/MM/DD HH:mm'),
+ updateUser: userToUserDisplay(app.updateuser)
+ } as IAppDisplay
+}
+
+function userToUserDisplay(user: IUser) {
+ return {
+ id: user.id,
+ firstName: user.first_name,
+ lastName: user.last_name,
+ fullNameSearch: (user.last_name + user.first_name).toLowerCase(),
+ fullName: user.last_name + ' ' + user.first_name,
+ email: user.email,
+ isActive: user.is_active,
+ isSuperuser: user.is_superuser,
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/stores/useAuthStore.ts b/frontend/src/stores/useAuthStore.ts
index 8ce4136..6187b7d 100644
--- a/frontend/src/stores/useAuthStore.ts
+++ b/frontend/src/stores/useAuthStore.ts
@@ -3,6 +3,8 @@ import { api } from 'boot/axios';
import { router } from 'src/router';
import { IDomainInfo } from '../types/DomainTypes';
import { jwtDecode } from 'jwt-decode';
+import { useAppStore } from './useAppStore';
+
interface UserInfo {
firstName: string;
lastName: string;
@@ -87,6 +89,7 @@ export const useAuthStore = defineStore('auth', {
logout() {
this.token = '';
this.currentDomain = {} as IDomainInfo; // 清空当前域
+ useAppStore().reset();
router.push('/login');
},
async setCurrentDomain(domain?: IDomainInfo) {
diff --git a/frontend/src/stores/useDomainStore.ts b/frontend/src/stores/useDomainStore.ts
deleted file mode 100644
index d5c64fa..0000000
--- a/frontend/src/stores/useDomainStore.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { defineStore } from 'pinia';
-import { api } from 'boot/axios';
-import { IDomainInfo, IDomain } from '../types/DomainTypes';
-
-export const useDomainStore = defineStore('domain', {
- state: () => ({
- userDomains: [] as IDomainInfo[],
- }),
- actions: {
- async loadUserDomains(): Promise {
- const resp = await api.get(`api/domain`);
- const domains = resp.data as IDomain[];
- this.userDomains = domains
- .filter(data => data.is_active)
- .map((data) => ({
- id: data.id,
- domainName: data.name,
- kintoneUrl: data.url,
- }));
- return this.userDomains;
- },
- },
-});
diff --git a/frontend/src/types/AppTypes.ts b/frontend/src/types/AppTypes.ts
index 150d06e..0023a31 100644
--- a/frontend/src/types/AppTypes.ts
+++ b/frontend/src/types/AppTypes.ts
@@ -4,18 +4,7 @@ export interface IManagedApp {
appid: string;
appname: string;
domainurl: string;
- version: string;
- user: IUser;
- updateuser: IUser;
- create_time: string;
- update_time: string;
-}
-
-export interface IManagedApp {
- appid: string;
- appname: string;
- domainurl: string;
- version: string;
+ version: number;
user: IUser;
updateuser: IUser;
create_time: string;
@@ -29,7 +18,7 @@ export interface IAppDisplay{
url:string;
updateUser: IUserDisplay;
updateTime:string;
- version:string;
+ version:number;
}
export interface IVersionInfo {