import * as Koa from "koa"; import * as koaStaticServer from "koa-static-server"; import * as path from "path"; import { ECommon_User_Type } from "../../../common/model/user"; import { Permission_Base } from '../../../common/permission/permission'; import fieldApi from "../../../common/routes/field"; import fileApi from "../../../common/routes/file"; import gatewayApi from "../../../common/routes/gateway"; import issueApi from "../../../common/routes/issue"; import issueTypeApi from "../../../common/routes/issueType"; import organizationApi from "../../../common/routes/organization"; import projectApi from "../../../common/routes/project"; import releaseApi from "../../../common/routes/release"; import teamApi from "../../../common/routes/team"; import { ICommon_HttpApi } from "../../../common/routes/types"; import userApi from "../../../common/routes/user"; import workflowApi from "../../../common/routes/workflow"; import { Err } from "../../../common/status/error"; import { ECommon_Services } from "../../../common/types"; import Application, { ECommon_Application_Mode } from "../../common/app/app"; import { proxyRequest } from "../../common/rpc/rpc"; import { EServer_Common_Http_Body_Type, IServer_Common_Http_Proxy } from "../../common/types/http"; import { generateHttpOkResponse } from "../../common/util/http"; import "../http/gateway"; import authRpcApi from '../rpc/auth'; import userRpcApi from '../rpc/user'; import { checkIfNeedInit, handleImageFields } from "../util/util"; import { ECommon_Organization_User_Role } from './../../../common/model/organization_user'; import { ECommon_Application_Deploy } from './../../../common/types'; import { CacheService } from './../cache/service'; var apis:ICommon_HttpApi[]=[userApi,projectApi,teamApi,fileApi,issueTypeApi,workflowApi,fieldApi,issueApi,releaseApi,gatewayApi,organizationApi]; export default class GateWay extends Application { override async config(app: Koa) { let cacheService=new CacheService() await cacheService.start() await checkIfNeedInit() let apiMap=<{ [param:string]:{ [parem:string]:{ service:string, ignoreValidate:boolean, permission:Permission_Base[]|{ data:Permission_Base[], delete:boolean }, onlyAdmin:boolean, onlyOn?: ECommon_Application_Deploy } } }>{} for(let obj of apis){ let baseUrl=obj.baseUrl.substr(1) if(!apiMap[baseUrl]) { apiMap[baseUrl]={} } for(let key in obj.routes) { let objApi=obj.routes[key]; apiMap[baseUrl][objApi.method+" "+objApi.path]={ service:obj.service, ignoreValidate:!!objApi.ignoreValidate, permission:objApi.permission??[], onlyAdmin:!!objApi.onlyAdmin, onlyOn: objApi.onlyOn }; } } app.use(async function (ctx, next) { let path = ctx.path if(path.startsWith("/api/")) { path = path.substring("/api".length) let method = ctx.req.method; let baseUrl = path.substring(1, path.indexOf("/", 1)) let apiPath = path.substring(1 + baseUrl.length) if (apiMap[baseUrl] && apiMap[baseUrl][method + " " + apiPath]) { let microServer = apiMap[baseUrl][method + " " + apiPath].service let ignoreValidate = apiMap[baseUrl][method + " " + apiPath].ignoreValidate let permission=apiMap[baseUrl][method + " " + apiPath].permission let onlyAdmin=apiMap[baseUrl][method + " " + apiPath].onlyAdmin let onlyOn = apiMap[baseUrl][method + " " + apiPath].onlyOn if(onlyOn === ECommon_Application_Deploy.OFFLINE && Application.mode===ECommon_Application_Mode.MICRO) { throw Err.Common.requestForbidden } else if(onlyOn===ECommon_Application_Deploy.PLATFORM && (Application.mode===ECommon_Application_Mode.DOCKER || Application.mode===ECommon_Application_Mode.SINGLE)) { throw Err.Common.requestForbidden } if(!ignoreValidate) { let authorization = ctx.get("Authorization") if(!authorization) { throw Err.User.notAuth } let ret= await userRpcApi.checkSession(authorization.substr(7)) if(!ret) { throw Err.User.notAuth } ctx.state.user=ret; } let obj = {} if (ctx.req.method == "POST" || ctx.req.method == "PUT" || ctx.req.method == "PATCH") { if (!ctx.state.p) { ctx.state.p = ctx.request.body; } } else { ctx.state.p = ctx.request.query; } obj.method = ctx.req.method obj.headers = ctx.req.headers obj.data = ctx.state.formData ?? ctx.state.p obj.path = path obj.user = ctx.state.user if (ctx.state.formData) { obj.bodyType = EServer_Common_Http_Body_Type.FORMDATA } let permissionDelete:object; if(permission && ((Array.isArray(permission) && permission.length>0) || (typeof(permission)=="object" && !Array.isArray(permission) && Array.isArray(permission.data) && permission.data.length>0))) { if(ctx.state.user) { if(ctx.state.user.type==ECommon_User_Type.USER) { let organizationRole:ECommon_Organization_User_Role=null; if(ctx.get("organizationid")) { organizationRole=await authRpcApi.getOrganizationPermission(ctx.get("organizationid"),ctx.state.user.userId); } let per=Array.isArray(permission)?permission:permission.data; let isOrganizationAdmin = organizationRole===ECommon_Organization_User_Role.ADMIN if(!isOrganizationAdmin) { if(onlyAdmin) { throw Err.User.accessDenied } let [isCheck,map] =await authRpcApi.processPermission(per,Object.assign({},obj.data,{ userId:ctx.state.user?ctx.state.user.userId:undefined })) if(isCheck && typeof(permission)=="object" && !Array.isArray(permission) && Array.isArray(permission.data)) { permissionDelete=map; } if(!isCheck) { throw Err.User.accessDenied } } } } else { throw Err.User.accessDenied } } let ret = await proxyRequest(obj, microServer as ECommon_Services) ctx.response.status = ret.status; for (let key in ret.headers) { if (key == "set-cookie") { let obj = JSON.parse(ret.headers[key]) for (let o of obj) { ctx.set("set-cookie", o) } } else { ctx.set(key, ret.headers[key]); } } if(ret.data===undefined || typeof(ret.data)=="string" || typeof(ret.data)=="number" || typeof(ret.data)=="boolean" || typeof(ret.data)=="object") { if(typeof(ret.data)=="object") { await handleImageFields(ret.data,ctx.get("organizationid")); } ctx.body = generateHttpOkResponse(ret.data); } else { ctx.body = ret.data; } if(permissionDelete) { authRpcApi.deletePermission(permissionDelete) } await next() } else { await next() } } else { await next() } }) app.use(koaStaticServer({ rootDir:Application.teamlinkerPath, rootPath:"/file", last:false })) if(Application.mode!=ECommon_Application_Mode.MICRO) { app.use(koaStaticServer({ rootDir:Application.debug?path.resolve(__dirname,"../../../client/dist"):path.join(__dirname,"../../../../../../client/dist"), rootPath:"/", last:false })) } } }