You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
273 lines
8.8 KiB
273 lines
8.8 KiB
import dotenv from "dotenv";
|
|
dotenv.config();
|
|
import express from "express";
|
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
require("express-async-errors");
|
|
import helmet from "helmet";
|
|
import cors from "cors";
|
|
import { DatabaseService } from "./services";
|
|
import { EELicenseService, GithubSecretScanningService } from "./ee/services";
|
|
import { setUpHealthEndpoint } from "./services/health";
|
|
import cookieParser from "cookie-parser";
|
|
import swaggerUi = require("swagger-ui-express");
|
|
import { Probot, createNodeMiddleware } from "probot";
|
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
const swaggerFile = require("../spec.json");
|
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
import { apiLimiter } from "./helpers/rateLimiter";
|
|
import {
|
|
action as eeActionRouter,
|
|
cloudProducts as eeCloudProductsRouter,
|
|
organizations as eeOrganizationsRouter,
|
|
sso as eeSSORouter,
|
|
secret as eeSecretRouter,
|
|
secretSnapshot as eeSecretSnapshotRouter,
|
|
users as eeUsersRouter,
|
|
workspace as eeWorkspaceRouter,
|
|
roles as v1RoleRouter,
|
|
secretApprovalPolicy as v1SecretApprovalPolicy,
|
|
secretApprovalRequest as v1SecretApprovalRequest,
|
|
secretScanning as v1SecretScanningRouter
|
|
} from "./ee/routes/v1";
|
|
import { serviceTokenData as v3ServiceTokenDataRouter } from "./ee/routes/v3";
|
|
import {
|
|
auth as v1AuthRouter,
|
|
bot as v1BotRouter,
|
|
integrationAuth as v1IntegrationAuthRouter,
|
|
integration as v1IntegrationRouter,
|
|
inviteOrg as v1InviteOrgRouter,
|
|
key as v1KeyRouter,
|
|
membershipOrg as v1MembershipOrgRouter,
|
|
membership as v1MembershipRouter,
|
|
organization as v1OrganizationRouter,
|
|
password as v1PasswordRouter,
|
|
sso as v1SSORouter,
|
|
secretImps as v1SecretImpsRouter,
|
|
secret as v1SecretRouter,
|
|
secretsFolder as v1SecretsFolder,
|
|
serviceToken as v1ServiceTokenRouter,
|
|
signup as v1SignupRouter,
|
|
userAction as v1UserActionRouter,
|
|
user as v1UserRouter,
|
|
webhooks as v1WebhooksRouter,
|
|
workspace as v1WorkspaceRouter
|
|
} from "./routes/v1";
|
|
import {
|
|
auth as v2AuthRouter,
|
|
environment as v2EnvironmentRouter,
|
|
organizations as v2OrganizationsRouter,
|
|
secret as v2SecretRouter, // begin to phase out
|
|
secrets as v2SecretsRouter,
|
|
serviceTokenData as v2ServiceTokenDataRouter,
|
|
signup as v2SignupRouter,
|
|
tags as v2TagsRouter,
|
|
users as v2UsersRouter,
|
|
workspace as v2WorkspaceRouter
|
|
} from "./routes/v2";
|
|
import {
|
|
auth as v3AuthRouter,
|
|
secrets as v3SecretsRouter,
|
|
signup as v3SignupRouter,
|
|
workspaces as v3WorkspacesRouter
|
|
} from "./routes/v3";
|
|
import { healthCheck } from "./routes/status";
|
|
import { getLogger } from "./utils/logger";
|
|
import { RouteNotFoundError } from "./utils/errors";
|
|
import { requestErrorHandler } from "./middleware/requestErrorHandler";
|
|
import {
|
|
getNodeEnv,
|
|
getPort,
|
|
getSecretScanningGitAppId,
|
|
getSecretScanningPrivateKey,
|
|
getSecretScanningWebhookProxy,
|
|
getSecretScanningWebhookSecret,
|
|
getSiteURL
|
|
} from "./config";
|
|
import { setup } from "./utils/setup";
|
|
import { syncSecretsToThirdPartyServices } from "./queues/integrations/syncSecretsToThirdPartyServices";
|
|
import { githubPushEventSecretScan } from "./queues/secret-scanning/githubScanPushEvent";
|
|
const SmeeClient = require("smee-client"); // eslint-disable-line
|
|
import path from "path";
|
|
import next from "next";
|
|
|
|
const main = async () => {
|
|
await setup();
|
|
|
|
await EELicenseService.initGlobalFeatureSet();
|
|
|
|
const app = express();
|
|
app.enable("trust proxy");
|
|
app.use(express.json());
|
|
app.use(express.urlencoded({ extended: false }));
|
|
app.use(cookieParser());
|
|
app.use(
|
|
cors({
|
|
credentials: true,
|
|
origin: await getSiteURL()
|
|
})
|
|
);
|
|
|
|
if (
|
|
(await getSecretScanningGitAppId()) &&
|
|
(await getSecretScanningWebhookSecret()) &&
|
|
(await getSecretScanningPrivateKey())
|
|
) {
|
|
const probot = new Probot({
|
|
appId: await getSecretScanningGitAppId(),
|
|
privateKey: await getSecretScanningPrivateKey(),
|
|
secret: await getSecretScanningWebhookSecret()
|
|
});
|
|
|
|
if ((await getNodeEnv()) != "production") {
|
|
const smee = new SmeeClient({
|
|
source: await getSecretScanningWebhookProxy(),
|
|
target: "http://backend:4000/ss-webhook",
|
|
logger: console
|
|
});
|
|
|
|
smee.start();
|
|
}
|
|
|
|
app.use(
|
|
createNodeMiddleware(GithubSecretScanningService, { probot, webhooksPath: "/ss-webhook" })
|
|
); // secret scanning webhook
|
|
}
|
|
|
|
if ((await getNodeEnv()) === "production") {
|
|
// enable app-wide rate-limiting + helmet security
|
|
// in production
|
|
app.disable("x-powered-by");
|
|
app.use(apiLimiter);
|
|
app.use(helmet());
|
|
}
|
|
|
|
app.use((req, res, next) => {
|
|
// default to IP address provided by Cloudflare
|
|
// #swagger.ignore = true
|
|
const cfIp = req.headers["cf-connecting-ip"];
|
|
req.realIP = Array.isArray(cfIp) ? cfIp[0] : (cfIp as string) || req.ip;
|
|
next();
|
|
});
|
|
|
|
// (EE) routes
|
|
app.use("/api/v1/secret", eeSecretRouter);
|
|
app.use("/api/v1/secret-snapshot", eeSecretSnapshotRouter);
|
|
app.use("/api/v1/users", eeUsersRouter);
|
|
app.use("/api/v1/workspace", eeWorkspaceRouter);
|
|
app.use("/api/v1/action", eeActionRouter);
|
|
app.use("/api/v1/organizations", eeOrganizationsRouter);
|
|
app.use("/api/v1/sso", eeSSORouter);
|
|
app.use("/api/v1/cloud-products", eeCloudProductsRouter);
|
|
app.use("/api/v3/service-token", v3ServiceTokenDataRouter);
|
|
|
|
// v1 routes
|
|
app.use("/api/v1/signup", v1SignupRouter);
|
|
app.use("/api/v1/auth", v1AuthRouter);
|
|
app.use("/api/v1/bot", v1BotRouter);
|
|
app.use("/api/v1/user", v1UserRouter);
|
|
app.use("/api/v1/user-action", v1UserActionRouter);
|
|
app.use("/api/v1/organization", v1OrganizationRouter);
|
|
app.use("/api/v1/workspace", v1WorkspaceRouter);
|
|
app.use("/api/v1/membership-org", v1MembershipOrgRouter);
|
|
app.use("/api/v1/membership", v1MembershipRouter); //
|
|
app.use("/api/v1/key", v1KeyRouter);
|
|
app.use("/api/v1/invite-org", v1InviteOrgRouter);
|
|
app.use("/api/v1/secret", v1SecretRouter); // deprecate
|
|
app.use("/api/v1/service-token", v1ServiceTokenRouter); // deprecate
|
|
app.use("/api/v1/password", v1PasswordRouter);
|
|
app.use("/api/v1/integration", v1IntegrationRouter);
|
|
app.use("/api/v1/integration-auth", v1IntegrationAuthRouter);
|
|
app.use("/api/v1/folders", v1SecretsFolder);
|
|
app.use("/api/v1/secret-scanning", v1SecretScanningRouter);
|
|
app.use("/api/v1/webhooks", v1WebhooksRouter);
|
|
app.use("/api/v1/secret-imports", v1SecretImpsRouter);
|
|
app.use("/api/v1/roles", v1RoleRouter);
|
|
app.use("/api/v1/secret-approvals", v1SecretApprovalPolicy);
|
|
app.use("/api/v1/sso", v1SSORouter);
|
|
app.use("/api/v1/secret-approval-requests", v1SecretApprovalRequest);
|
|
|
|
// v2 routes (improvements)
|
|
app.use("/api/v2/signup", v2SignupRouter);
|
|
app.use("/api/v2/auth", v2AuthRouter);
|
|
app.use("/api/v2/users", v2UsersRouter);
|
|
app.use("/api/v2/organizations", v2OrganizationsRouter);
|
|
app.use("/api/v2/workspace", v2EnvironmentRouter);
|
|
app.use("/api/v2/workspace", v2TagsRouter);
|
|
app.use("/api/v2/workspace", v2WorkspaceRouter);
|
|
app.use("/api/v2/secret", v2SecretRouter); // deprecate
|
|
app.use("/api/v2/secrets", v2SecretsRouter); // note: in the process of moving to v3/secrets
|
|
app.use("/api/v2/service-token", v2ServiceTokenDataRouter);
|
|
// app.use("/api/v2/service-accounts", v2ServiceAccountsRouter); // new
|
|
|
|
// v3 routes (experimental)
|
|
app.use("/api/v3/auth", v3AuthRouter);
|
|
app.use("/api/v3/secrets", v3SecretsRouter);
|
|
app.use("/api/v3/workspaces", v3WorkspacesRouter);
|
|
app.use("/api/v3/signup", v3SignupRouter);
|
|
|
|
// api docs
|
|
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerFile));
|
|
|
|
// server status
|
|
app.use("/api", healthCheck);
|
|
|
|
if (process.env.NODE_ENV == "production") {
|
|
const dir = path.join(__dirname, "../frontend-build");
|
|
process.chdir(dir);
|
|
const nextApp = next({
|
|
dev: false,
|
|
dir
|
|
});
|
|
|
|
const nextHandler = nextApp.getRequestHandler();
|
|
await nextApp.prepare();
|
|
|
|
app.all("*", (req, res) => {
|
|
return nextHandler(req, res);
|
|
});
|
|
}
|
|
|
|
//* Handle unrouted requests and respond with proper error message as well as status code
|
|
app.use((req, res, next) => {
|
|
if (res.headersSent) return next();
|
|
next(
|
|
RouteNotFoundError({
|
|
message: `The requested source '(${req.method})${req.url}' was not found`
|
|
})
|
|
);
|
|
});
|
|
|
|
app.use(requestErrorHandler);
|
|
|
|
const server = app.listen(await getPort(), async () => {
|
|
(await getLogger("backend-main")).info(`Server started listening at port ${await getPort()}`);
|
|
});
|
|
|
|
// await createTestUserForDevelopment();
|
|
setUpHealthEndpoint(server);
|
|
|
|
const serverCleanup = async () => {
|
|
await DatabaseService.closeDatabase();
|
|
syncSecretsToThirdPartyServices.close();
|
|
githubPushEventSecretScan.close();
|
|
|
|
process.exit(0);
|
|
};
|
|
|
|
process.on("SIGINT", function () {
|
|
server.close(async () => {
|
|
await serverCleanup();
|
|
});
|
|
});
|
|
|
|
process.on("SIGTERM", function () {
|
|
server.close(async () => {
|
|
await serverCleanup();
|
|
});
|
|
});
|
|
|
|
return server;
|
|
};
|
|
|
|
export default main();
|