Compare commits
4 Commits
feature-do
...
devForBF
| Author | SHA1 | Date | |
|---|---|---|---|
| 15cc48cd40 | |||
| dd814993f1 | |||
| 9dce750ee5 | |||
| 2ffa1d9438 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,7 @@
|
||||
.vscode
|
||||
.mypy_cache
|
||||
docker-stack.yml
|
||||
backend/pyvenv.cfg
|
||||
backend/Include/
|
||||
backend/Scripts/
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from fastapi import Query, Request,Depends, APIRouter, UploadFile,HTTPException,File
|
||||
from app.core.operation import log_operation
|
||||
from app.db import Base,engine
|
||||
from app.db.session import get_db
|
||||
from app.db.crud import *
|
||||
@@ -7,8 +8,69 @@ from typing import List, Optional
|
||||
from app.core.auth import get_current_active_user,get_current_user
|
||||
from app.core.apiexception import APIException
|
||||
|
||||
import httpx
|
||||
import app.core.config as config
|
||||
|
||||
platform_router = r = APIRouter()
|
||||
|
||||
|
||||
@r.get(
|
||||
"/apps",
|
||||
response_model=List[AppList],
|
||||
response_model_exclude_none=True,
|
||||
)
|
||||
async def apps_list(
|
||||
request: Request,
|
||||
user = Depends(get_current_user),
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
platformapps = get_apps(db)
|
||||
domain = get_activedomain(db, user.id)
|
||||
|
||||
kintoneevn = config.KINTONE_ENV(domain)
|
||||
headers={config.API_V1_AUTH_KEY:kintoneevn.API_V1_AUTH_VALUE}
|
||||
url = f"{kintoneevn.BASE_URL}{config.API_V1_STR}/apps.json"
|
||||
offset = 0
|
||||
limit = 100
|
||||
all_apps = []
|
||||
|
||||
while True:
|
||||
r = httpx.get(f"{url}?limit={limit}&offset={offset}", headers=headers)
|
||||
json_data = r.json()
|
||||
apps = json_data.get("apps",[])
|
||||
all_apps.extend(apps)
|
||||
if len(apps)<limit:
|
||||
break
|
||||
offset += limit
|
||||
|
||||
tempapps = platformapps.copy()
|
||||
for papp in tempapps:
|
||||
exist = False
|
||||
for kapp in all_apps:
|
||||
if kapp['appId'] == papp.appid:
|
||||
exist = True
|
||||
break
|
||||
if not exist:
|
||||
platformapps.remove(papp)
|
||||
|
||||
return platformapps
|
||||
except Exception as e:
|
||||
raise APIException('platform:apps',request.url._url,f"Error occurred while get apps:",e)
|
||||
|
||||
@r.post("/apps", response_model=AppList, response_model_exclude_none=True)
|
||||
async def apps_update(
|
||||
request: Request,
|
||||
app: AppVersion,
|
||||
user=Depends(get_current_user),
|
||||
db=Depends(get_db),
|
||||
):
|
||||
try:
|
||||
return update_appversion(db, app,user.id)
|
||||
except Exception as e:
|
||||
raise APIException('platform:apps',request.url._url,f"Error occurred while get create app :",e)
|
||||
|
||||
|
||||
@r.get(
|
||||
"/appsettings/{id}",
|
||||
response_model=App,
|
||||
@@ -129,7 +191,7 @@ async def flow_list(
|
||||
try:
|
||||
domain = get_activedomain(db, user.id)
|
||||
print("domain=>",domain)
|
||||
flows = get_flows_by_app(db, domain.id, appid)
|
||||
flows = get_flows_by_app(db, domain.url, appid)
|
||||
return flows
|
||||
except Exception as e:
|
||||
raise APIException('platform:flow',request.url._url,f"Error occurred while get flow by appid:",e)
|
||||
@@ -144,7 +206,7 @@ async def flow_create(
|
||||
):
|
||||
try:
|
||||
domain = get_activedomain(db, user.id)
|
||||
return create_flow(db, domain.id, flow)
|
||||
return create_flow(db, domain.url, flow)
|
||||
except Exception as e:
|
||||
raise APIException('platform:flow',request.url._url,f"Error occurred while create flow:",e)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import base64
|
||||
PROJECT_NAME = "KintoneAppBuilder"
|
||||
|
||||
#SQLALCHEMY_DATABASE_URI = "postgres://kabAdmin:P@ssw0rd!@kintonetooldb.postgres.database.azure.com/dev"
|
||||
SQLALCHEMY_DATABASE_URI = "postgres://kabAdmin:P@ssw0rd!@kintonetooldb.postgres.database.azure.com/postgres"
|
||||
SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://kabAdmin:P%40ssw0rd!@kintonetooldb.postgres.database.azure.com/dev_v2"
|
||||
#SQLALCHEMY_DATABASE_URI = "postgres://kabAdmin:P@ssw0rd!@kintonetooldb.postgres.database.azure.com/test"
|
||||
#SQLALCHEMY_DATABASE_URI = "postgres://kabAdmin:P@ssw0rd!@ktune-prod-db.postgres.database.azure.com/postgres"
|
||||
API_V1_STR = "/k/v1"
|
||||
|
||||
28
backend/app/core/operation.py
Normal file
28
backend/app/core/operation.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from functools import wraps
|
||||
|
||||
from app.db.crud import create_operationlog
|
||||
from app.db.schemas import OperationCreate
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
|
||||
def log_operation(operation):
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
async def wrapper(*args,**kwargs):
|
||||
db = kwargs.get('db')
|
||||
request = kwargs.get('request')
|
||||
result = await func(*args,**kwargs)
|
||||
detial = f"Request: {request.method} {request.url}\
|
||||
Request Headers: {request.headers}\
|
||||
Request Body: {request.body()}\
|
||||
Result: {jsonable_encoder(result,exclude_unset=True)}"
|
||||
function = func.__name__
|
||||
db_operation = OperationCreate(tenantid ="t",
|
||||
domainurl="d",
|
||||
userid=3,
|
||||
operation=operation,
|
||||
function=function,
|
||||
detail=detial)
|
||||
create_operationlog(db,db_operation)
|
||||
return result
|
||||
return wrapper
|
||||
return decorator
|
||||
@@ -69,6 +69,46 @@ def edit_user(
|
||||
db.refresh(db_user)
|
||||
return db_user
|
||||
|
||||
def get_apps(
|
||||
db: Session
|
||||
) -> t.List[schemas.AppList]:
|
||||
return db.query(models.App).all()
|
||||
|
||||
def update_appversion(db: Session, appedit: schemas.AppVersion,userid:int):
|
||||
app = db.query(models.App).filter(and_(models.App.domainurl == appedit.domainurl,models.App.appid == appedit.appid)).first()
|
||||
if app:
|
||||
app.version = app.version + 1
|
||||
db_app = app
|
||||
appver = app.version
|
||||
else:
|
||||
appver = 1
|
||||
db_app = models.App(
|
||||
domainurl = appedit.domainurl,
|
||||
appid=appedit.appid,
|
||||
appname=appedit.appname,
|
||||
version = 1,
|
||||
updateuser= userid
|
||||
)
|
||||
|
||||
db.add(db_app)
|
||||
|
||||
flows = db.query(models.Flow).filter(and_(models.Flow.domainurl == appedit.domainurl,models.App.appid == appedit.appid))
|
||||
for flow in flows:
|
||||
db_flowhistory = models.FlowHistory(
|
||||
flowid = flow.flowid,
|
||||
appid = flow.appid,
|
||||
eventid = flow.eventid,
|
||||
domainurl = flow.domainurl,
|
||||
name = flow.name,
|
||||
content = flow.content,
|
||||
createuser = userid,
|
||||
version = appver
|
||||
)
|
||||
db.add(db_flowhistory)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_app)
|
||||
return db_app
|
||||
|
||||
def get_appsetting(db: Session, id: int):
|
||||
app = db.query(models.AppSetting).get(id)
|
||||
@@ -125,12 +165,12 @@ def get_actions(db: Session):
|
||||
return actions
|
||||
|
||||
|
||||
def create_flow(db: Session, domainid: int, flow: schemas.FlowBase):
|
||||
def create_flow(db: Session, domainurl: str, flow: schemas.FlowBase):
|
||||
db_flow = models.Flow(
|
||||
flowid=flow.flowid,
|
||||
appid=flow.appid,
|
||||
eventid=flow.eventid,
|
||||
domainid=domainid,
|
||||
domainurl=domainurl,
|
||||
name=flow.name,
|
||||
content=flow.content
|
||||
)
|
||||
@@ -177,8 +217,8 @@ def get_flow(db: Session, flowid: str):
|
||||
raise HTTPException(status_code=404, detail="Data not found")
|
||||
return flow
|
||||
|
||||
def get_flows_by_app(db: Session, domainid: int, appid: str):
|
||||
flows = db.query(models.Flow).filter(and_(models.Flow.domainid == domainid,models.Flow.appid == appid)).all()
|
||||
def get_flows_by_app(db: Session,domainurl: str, appid: str):
|
||||
flows = db.query(models.Flow).filter(and_(models.Flow.domainurl == domainurl,models.Flow.appid == appid)).all()
|
||||
if not flows:
|
||||
raise Exception("Data not found")
|
||||
return flows
|
||||
@@ -324,4 +364,18 @@ def create_log(db: Session, error:schemas.ErrorCreate):
|
||||
|
||||
def get_kintoneformat(db: Session):
|
||||
formats = db.query(models.KintoneFormat).order_by(models.KintoneFormat.id).all()
|
||||
return formats
|
||||
return formats
|
||||
|
||||
def create_operationlog(db: Session, log: schemas.OperationCreate):
|
||||
db_log = models.OperationLog(
|
||||
tenantid=log.tenantid,
|
||||
domainurl=log.domainurl,
|
||||
userid = log.userid,
|
||||
operation = log.operation,
|
||||
function = log.function,
|
||||
detail = log.detail
|
||||
)
|
||||
db.add(db_log)
|
||||
db.commit()
|
||||
db.refresh(db_log)
|
||||
return db_log
|
||||
@@ -1,5 +1,6 @@
|
||||
from sqlalchemy import Boolean, Column, Integer, String, DateTime,ForeignKey
|
||||
from sqlalchemy.ext.declarative import as_declarative
|
||||
from sqlalchemy.orm import relationship
|
||||
from datetime import datetime
|
||||
|
||||
from app.core.security import chacha20Decrypt
|
||||
@@ -20,6 +21,16 @@ class User(Base):
|
||||
is_active = Column(Boolean, default=True)
|
||||
is_superuser = Column(Boolean, default=False)
|
||||
|
||||
class App(Base):
|
||||
__tablename__ = "app"
|
||||
|
||||
domainurl = Column(String(200), nullable=False)
|
||||
appname = Column(String(200), nullable=False)
|
||||
appid = Column(String(100), index=True, nullable=False)
|
||||
version = Column(Integer)
|
||||
updateuser = Column(Integer,ForeignKey("user.id"))
|
||||
user = relationship('User')
|
||||
|
||||
class AppSetting(Base):
|
||||
__tablename__ = "appsetting"
|
||||
|
||||
@@ -51,10 +62,22 @@ class Flow(Base):
|
||||
flowid = Column(String(100), index=True, nullable=False)
|
||||
appid = Column(String(100), index=True, nullable=False)
|
||||
eventid = Column(String(100), index=True, nullable=False)
|
||||
domainid = Column(Integer,ForeignKey("domain.id"))
|
||||
domainurl = Column(String(200))
|
||||
name = Column(String(200))
|
||||
content = Column(String)
|
||||
|
||||
class FlowHistory(Base):
|
||||
__tablename__ = "flowhistory"
|
||||
|
||||
flowid = Column(String(100), index=True, nullable=False)
|
||||
appid = Column(String(100), index=True, nullable=False)
|
||||
eventid = Column(String(100), index=True, nullable=False)
|
||||
domainurl = Column(String(200))
|
||||
name = Column(String(200))
|
||||
content = Column(String)
|
||||
createuser = Column(Integer,ForeignKey("user.id"))
|
||||
version = Column(Integer)
|
||||
|
||||
class Tenant(Base):
|
||||
__tablename__ = "tenant"
|
||||
|
||||
@@ -108,6 +131,17 @@ class ErrorLog(Base):
|
||||
location = Column(String(500))
|
||||
content = Column(String(5000))
|
||||
|
||||
class OperationLog(Base):
|
||||
__tablename__ = "operationlog"
|
||||
|
||||
tenantid = Column(String(100))
|
||||
domainurl = Column(String(200))
|
||||
userid = Column(Integer,ForeignKey("user.id"))
|
||||
operation = Column(String(200))
|
||||
function = Column(String(200))
|
||||
detail = Column(String(200))
|
||||
user = relationship('User')
|
||||
|
||||
class KintoneFormat(Base):
|
||||
__tablename__ = "kintoneformat"
|
||||
|
||||
|
||||
@@ -28,21 +28,21 @@ class UserCreate(UserBase):
|
||||
is_active:bool
|
||||
is_superuser:bool
|
||||
|
||||
class Config:
|
||||
class ConfigDict:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class UserEdit(UserBase):
|
||||
password: t.Optional[str] = None
|
||||
|
||||
class Config:
|
||||
class ConfigDict:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class User(UserBase):
|
||||
id: int
|
||||
|
||||
class Config:
|
||||
class ConfigDict:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
@@ -50,6 +50,17 @@ class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str
|
||||
|
||||
class AppList(Base):
|
||||
domainurl: str
|
||||
appname: str
|
||||
appid:str
|
||||
version:int
|
||||
user:UserOut
|
||||
|
||||
class AppVersion(BaseModel):
|
||||
domainurl: str
|
||||
appname: str
|
||||
appid:str
|
||||
|
||||
class TokenData(BaseModel):
|
||||
id:int = 0
|
||||
@@ -68,7 +79,7 @@ class AppBase(BaseModel):
|
||||
class App(AppBase):
|
||||
id: int
|
||||
|
||||
class Config:
|
||||
class ConfigDict:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
@@ -79,7 +90,7 @@ class Kintone(BaseModel):
|
||||
desc: str = None
|
||||
content: str = None
|
||||
|
||||
class Config:
|
||||
class ConfigDict:
|
||||
orm_mode = True
|
||||
|
||||
class Action(BaseModel):
|
||||
@@ -92,7 +103,7 @@ class Action(BaseModel):
|
||||
categoryid: int = None
|
||||
nosort: int
|
||||
categoryname : str =None
|
||||
class Config:
|
||||
class ConfigDict:
|
||||
orm_mode = True
|
||||
|
||||
class FlowBase(BaseModel):
|
||||
@@ -107,11 +118,11 @@ class Flow(Base):
|
||||
flowid: str
|
||||
appid: str
|
||||
eventid: str
|
||||
domainid: int
|
||||
domainurl: str
|
||||
name: str = None
|
||||
content: str = None
|
||||
|
||||
class Config:
|
||||
class ConfigDict:
|
||||
orm_mode = True
|
||||
|
||||
class DomainBase(BaseModel):
|
||||
@@ -133,7 +144,7 @@ class Domain(Base):
|
||||
url: str
|
||||
kintoneuser: str
|
||||
kintonepwd: str
|
||||
class Config:
|
||||
class ConfigDict:
|
||||
orm_mode = True
|
||||
|
||||
class Event(Base):
|
||||
@@ -145,10 +156,18 @@ class Event(Base):
|
||||
mobile: bool
|
||||
eventgroup: bool
|
||||
|
||||
class Config:
|
||||
class ConfigDict:
|
||||
orm_mode = True
|
||||
|
||||
class ErrorCreate(BaseModel):
|
||||
title:str
|
||||
location:str
|
||||
content:str
|
||||
content:str
|
||||
|
||||
class OperationCreate(BaseModel):
|
||||
tenantid:str
|
||||
domainurl:str
|
||||
userid:int
|
||||
operation:str
|
||||
function:str
|
||||
detail:str
|
||||
|
||||
Binary file not shown.
@@ -4,7 +4,6 @@
|
||||
tag="a"
|
||||
:target="target?target:'_blank'"
|
||||
:href="link"
|
||||
:disable="disable"
|
||||
v-if="!isSeparator"
|
||||
>
|
||||
<q-item-section
|
||||
@@ -34,7 +33,6 @@ export interface EssentialLinkProps {
|
||||
icon?: string;
|
||||
isSeparator?: boolean;
|
||||
target?:string;
|
||||
disable?:boolean;
|
||||
}
|
||||
withDefaults(defineProps<EssentialLinkProps>(), {
|
||||
caption: '',
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
<div v-if="isAdmin()">
|
||||
<EssentialLink v-for="link in adminLinks" :key="link.title" v-bind="link" />
|
||||
</div>
|
||||
<EssentialLink v-for="link in domainLinks" :key="link.title" v-bind="link" />
|
||||
|
||||
</q-list>
|
||||
</q-drawer>
|
||||
|
||||
@@ -34,7 +32,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, computed } from 'vue';
|
||||
import { onMounted } from 'vue';
|
||||
import EssentialLink, { EssentialLinkProps } from 'components/EssentialLink.vue';
|
||||
import DomainSelector from 'components/DomainSelector.vue';
|
||||
import { useAuthStore } from 'stores/useAuthStore';
|
||||
@@ -47,16 +45,14 @@ const essentialLinks: EssentialLinkProps[] = [
|
||||
caption: '設計書から導入する',
|
||||
icon: 'home',
|
||||
link: '/',
|
||||
target: '_self',
|
||||
disable: !authStore.hasDomain,
|
||||
target: '_self'
|
||||
},
|
||||
{
|
||||
title: 'フローエディター',
|
||||
caption: 'イベントを設定する',
|
||||
icon: 'account_tree',
|
||||
link: '/#/FlowChart',
|
||||
target: '_self',
|
||||
disable: !authStore.hasDomain,
|
||||
target: '_self'
|
||||
},
|
||||
// {
|
||||
// title: '条件エディター',
|
||||
@@ -87,23 +83,58 @@ const essentialLinks: EssentialLinkProps[] = [
|
||||
// link:'https://cybozu.dev/ja/kintone/docs/',
|
||||
// icon:'help_outline'
|
||||
// },
|
||||
];
|
||||
|
||||
const domainLinks: EssentialLinkProps[] = [
|
||||
{
|
||||
title: 'ドメイン管理',
|
||||
caption: 'kintoneのドメイン設定',
|
||||
icon: 'domain',
|
||||
link: '/#/domain',
|
||||
target: '_self'
|
||||
},
|
||||
// {
|
||||
// title: 'ドメイン適用',
|
||||
// caption: 'ユーザー使用可能なドメインの設定',
|
||||
// icon: 'assignment_ind',
|
||||
// link: '/#/userDomain',
|
||||
// target: '_self'
|
||||
// title:'',
|
||||
// isSeparator:true
|
||||
// },
|
||||
// {
|
||||
// title: 'Docs',
|
||||
// caption: 'quasar.dev',
|
||||
// icon: 'school',
|
||||
// link: 'https://quasar.dev'
|
||||
// },
|
||||
// {
|
||||
// title: 'Icons',
|
||||
// caption: 'Material Icons',
|
||||
// icon: 'insert_emoticon',
|
||||
// link: 'https://fonts.google.com/icons?selected=Material+Icons:insert_emoticon:'
|
||||
// },
|
||||
// {
|
||||
// title: 'Github',
|
||||
// caption: 'github.com/quasarframework',
|
||||
// icon: 'code',
|
||||
// link: 'https://github.com/quasarframework'
|
||||
// },
|
||||
// {
|
||||
// title: 'Discord Chat Channel',
|
||||
// caption: 'chat.quasar.dev',
|
||||
// icon: 'chat',
|
||||
// link: 'https://chat.quasar.dev'
|
||||
// },
|
||||
// {
|
||||
// title: 'Forum',
|
||||
// caption: 'forum.quasar.dev',
|
||||
// icon: 'record_voice_over',
|
||||
// link: 'https://forum.quasar.dev'
|
||||
// },
|
||||
// {
|
||||
// title: 'Twitter',
|
||||
// caption: '@quasarframework',
|
||||
// icon: 'rss_feed',
|
||||
// link: 'https://twitter.quasar.dev'
|
||||
// },
|
||||
// {
|
||||
// title: 'Facebook',
|
||||
// caption: '@QuasarFramework',
|
||||
// icon: 'public',
|
||||
// link: 'https://facebook.quasar.dev'
|
||||
// },
|
||||
// {
|
||||
// title: 'Quasar Awesome',
|
||||
// caption: 'Community Quasar projects',
|
||||
// icon: 'favorite',
|
||||
// link: 'https://awesome.quasar.dev'
|
||||
// }
|
||||
];
|
||||
|
||||
const adminLinks: EssentialLinkProps[] = [
|
||||
@@ -114,6 +145,20 @@ const adminLinks: EssentialLinkProps[] = [
|
||||
link: '/#/user',
|
||||
target: '_self'
|
||||
},
|
||||
{
|
||||
title: 'ドメイン管理',
|
||||
caption: 'kintoneのドメイン設定',
|
||||
icon: 'domain',
|
||||
link: '/#/domain',
|
||||
target: '_self'
|
||||
},
|
||||
{
|
||||
title: 'ドメイン適用',
|
||||
caption: 'ユーザー使用可能なドメインの設定',
|
||||
icon: 'assignment_ind',
|
||||
link: '/#/userDomain',
|
||||
target: '_self'
|
||||
},
|
||||
]
|
||||
|
||||
const version = process.env.version;
|
||||
|
||||
@@ -47,13 +47,6 @@ export default route(function (/* { store, ssrContext } */) {
|
||||
authStore.returnUrl = to.fullPath;
|
||||
return '/login';
|
||||
}
|
||||
|
||||
// redirect to domain setting page if no domain exist
|
||||
const domainPages = [...publicPages, '/domain'];
|
||||
if (!authStore.hasDomain && !domainPages.includes(to.path)) {
|
||||
authStore.returnUrl = to.fullPath;
|
||||
return '/domain';
|
||||
}
|
||||
});
|
||||
return routerInstance;
|
||||
});
|
||||
|
||||
@@ -33,9 +33,6 @@ export const useAuthStore = defineStore('auth', {
|
||||
toggleLeftDrawer(): boolean {
|
||||
return this.LeftDrawer;
|
||||
},
|
||||
hasDomain(): boolean {
|
||||
return this.currentDomain.id === null;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
toggleLeftMenu() {
|
||||
@@ -63,11 +60,11 @@ export const useAuthStore = defineStore('auth', {
|
||||
}
|
||||
},
|
||||
async getCurrentDomain(): Promise<IDomainInfo> {
|
||||
const activedomain = (await api.get(`api/activedomain`))?.data;
|
||||
const activedomain = await api.get(`api/activedomain`);
|
||||
return {
|
||||
id: activedomain?.id,
|
||||
domainName: activedomain?.name,
|
||||
kintoneUrl: activedomain?.url,
|
||||
id: activedomain.data.id,
|
||||
domainName: activedomain.data.name,
|
||||
kintoneUrl: activedomain.data.url,
|
||||
};
|
||||
},
|
||||
async getUserDomains(): Promise<IDomainInfo[]> {
|
||||
|
||||
Reference in New Issue
Block a user