UNPKG

@budibase/server

Version:
175 lines (158 loc) • 5.37 kB
import { CtxFn, middleware, redis } from "@budibase/backend-core" import { SelectableDatabase } from "@budibase/backend-core/src/redis/utils" import { PermissionLevel, PermissionType } from "@budibase/types" import cors from "@koa/cors" import env from "../../../environment" import { authorizedMiddleware as authorized } from "../../../middleware/authorized" import { publicApiMiddleware as publicApi } from "../../../middleware/publicApi" import { paramResource, paramSubResource } from "../../../middleware/resourceId" import appEndpoints_deprecated from "./applications" import metricEndpoints from "./metrics" import mapperMiddleware from "./middleware/mapper" import testErrorHandling from "./middleware/testErrorHandling" import queryEndpoints from "./queries" import roleEndpoints from "./roles" import rowEndpoints from "./rows" import tableEndpoints from "./tables" import userEndpoints from "./users" import viewEndpoints from "./views" import workspaceEndpoints from "./workspaces" // below imports don't have declaration files const Router = require("@koa/router") const { RateLimit, Stores } = require("koa2-ratelimit") interface KoaRateLimitOptions { socket: { host: string port: number } password?: string database?: number } const PREFIX = "/api/public/v1" // type can't be known - untyped libraries let limiter: any, rateLimitStore: any if (!env.DISABLE_RATE_LIMITING) { // allow a lot more requests when in test const DEFAULT_API_REQ_LIMIT_PER_SEC = env.isTest() ? 100 : 10 function getApiLimitPerSecond(): number { if (!env.API_REQ_LIMIT_PER_SEC) { return DEFAULT_API_REQ_LIMIT_PER_SEC } return parseInt(env.API_REQ_LIMIT_PER_SEC) } if (!env.isTest()) { const { password, host, port } = redis.utils.getRedisConnectionDetails() let options: KoaRateLimitOptions = { socket: { host: host, port: port, }, } if (password) { options.password = password } if (!env.REDIS_CLUSTERED) { // Can't set direct redis db in clustered env options.database = SelectableDatabase.RATE_LIMITING } rateLimitStore = new Stores.Redis(options) RateLimit.defaultOptions({ store: rateLimitStore, }) } // rate limiting, allows for 2 requests per second limiter = RateLimit.middleware({ interval: { sec: 1 }, // per ip, per interval max: getApiLimitPerSecond(), }) } else { console.log("**** PUBLIC API RATE LIMITING DISABLED ****") } const publicRouter = new Router({ prefix: PREFIX, }) if (limiter && !env.isDev()) { publicRouter.use(limiter) } publicRouter.use(cors()) function addMiddleware( endpoints: any, middleware: CtxFn, opts: { output: boolean } = { output: false } ) { if (!endpoints) { return } if (!Array.isArray(endpoints)) { endpoints = [endpoints] } for (let endpoint of endpoints) { if (opts?.output) { endpoint.addOutputMiddleware(middleware) } else { endpoint.addMiddleware(middleware) } } } function addToRouter(endpoints: any) { if (endpoints) { for (let endpoint of endpoints) { endpoint.apply(publicRouter) } } } function applyAdminRoutes(endpoints: any) { addMiddleware(endpoints.read, middleware.builderOrAdmin) addMiddleware(endpoints.write, middleware.builderOrAdmin) addToRouter(endpoints.read) addToRouter(endpoints.write) } function applyRoutes( endpoints: any, permType: PermissionType, resource: string, subResource?: string ) { const paramMiddleware = subResource ? paramSubResource(resource, subResource) : paramResource(resource) const publicApiMiddleware = publicApi({ requiresAppId: permType !== PermissionType.WORKSPACE && permType !== PermissionType.USER, }) addMiddleware(endpoints.read, publicApiMiddleware) addMiddleware(endpoints.write, publicApiMiddleware) // add the parameter capture middleware addMiddleware(endpoints.read, paramMiddleware) addMiddleware(endpoints.write, paramMiddleware) // add the authorization middleware, using the correct perm type addMiddleware(endpoints.read, authorized(permType, PermissionLevel.READ)) addMiddleware(endpoints.write, authorized(permType, PermissionLevel.WRITE)) // add the output mapper middleware addMiddleware(endpoints.read, mapperMiddleware, { output: true }) addMiddleware(endpoints.write, mapperMiddleware, { output: true }) if (env.isTest()) { addMiddleware(endpoints.read, testErrorHandling()) addMiddleware(endpoints.write, testErrorHandling()) } addToRouter(endpoints.read) addToRouter(endpoints.write) } applyAdminRoutes(metricEndpoints) applyAdminRoutes(roleEndpoints) applyRoutes(workspaceEndpoints, PermissionType.WORKSPACE, "workspaceId") applyRoutes(appEndpoints_deprecated, PermissionType.WORKSPACE, "appId") applyRoutes(tableEndpoints, PermissionType.TABLE, "tableId") applyRoutes(viewEndpoints, PermissionType.VIEW, "viewId") applyRoutes(userEndpoints, PermissionType.USER, "userId") applyRoutes(queryEndpoints, PermissionType.QUERY, "queryId") // needs to be applied last for routing purposes, don't override other endpoints applyRoutes(rowEndpoints, PermissionType.TABLE, "tableId", "rowId") export default publicRouter export const shutdown = () => { if (rateLimitStore) { rateLimitStore.client.disconnect() rateLimitStore = null } }