Files
infisical/backend/src/index.ts
T
2023-12-05 17:46:50 +07:00

330 lines
11 KiB
TypeScript

import dotenv from "dotenv";
dotenv.config();
import express from "express";
// eslint-disable-next-line @typescript-eslint/no-var-requires
import "express-async-errors";
import helmet from "helmet";
import cors from "cors";
import { initLogger, logger } from "./utils/logging";
import httpLogger from "pino-http";
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 {
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 v1SecretApprovalPolicyRouter,
secretApprovalRequest as v1SecretApprovalRequestRouter,
secretRotation as v1SecretRotation,
secretRotationProvider as v1SecretRotationProviderRouter,
secretScanning as v1SecretScanningRouter
} from "./ee/routes/v1";
import { apiKeyData as v3apiKeyDataRouter } from "./ee/routes/v3";
import { machineIdentity as v3MachineIdentityRouter } from "./ee/routes/v3";
import {
admin as v1AdminRouter,
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,
membership as v2MembershipController
} from "./routes/v2";
import {
auth as v3AuthRouter,
secrets as v3SecretsRouter,
signup as v3SignupRouter,
users as v3UsersRouter,
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 {
getMongoURL,
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 { serverConfigInit } from "./config/serverConfig";
import { initRedis } from "./services/RedisService";
let handler: null | any = null;
const main = async () => {
await initLogger();
const port = await getPort();
// initializing the database connection + redis
await initRedis()
await DatabaseService.initDatabase(await getMongoURL());
const serverCfg = await serverConfigInit();
await setup();
await EELicenseService.initGlobalFeatureSet();
const app = express();
app.enable("trust proxy");
app.use(
httpLogger({
logger,
autoLogging: false
})
);
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();
});
if ((await getNodeEnv()) === "production" && process.env.STANDALONE_BUILD === "true") {
const nextJsBuildPath = path.join(__dirname, "../frontend-build");
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-var-requires
const conf = require("../frontend-build/.next/required-server-files.json").config;
const NextServer =
// eslint-disable-next-line @typescript-eslint/no-var-requires
require("../frontend-build/node_modules/next/dist/server/next-server").default;
const nextApp = new NextServer({
dev: false,
dir: nextJsBuildPath,
port,
conf,
hostname: "local",
customServer: false
});
handler = nextApp.getRequestHandler();
}
// (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/organizations", eeOrganizationsRouter);
app.use("/api/v1/sso", eeSSORouter);
app.use("/api/v1/cloud-products", eeCloudProductsRouter);
app.use("/api/v3/api-key", v3apiKeyDataRouter);
app.use("/api/v3/machines", v3MachineIdentityRouter); // TODO: consider moving to v1
app.use("/api/v1/secret-rotation-providers", v1SecretRotationProviderRouter);
app.use("/api/v1/secret-rotations", v1SecretRotation);
// v1 routes
app.use("/api/v1/signup", v1SignupRouter);
app.use("/api/v1/auth", v1AuthRouter);
app.use("/api/v1/admin", v1AdminRouter);
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", v1SecretApprovalPolicyRouter);
app.use("/api/v1/sso", v1SSORouter);
app.use("/api/v1/secret-approval-requests", v1SecretApprovalRequestRouter);
// 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", v2MembershipController);
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);
// 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);
app.use("/api/v3/us", v3UsersRouter);
// api docs
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerFile));
// server status
app.use("/api", healthCheck);
if (handler) {
app.all("*", (req, res) => {
return handler(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(port, async () => {
if (!serverCfg.initialized) {
logger.info(`Welcome to Infisical
Create your Infisical administrator account at:
http://localhost:${port}/admin/signup
`);
} else {
logger.info(`Welcome back!
To access Infisical Administrator Panel open
http://localhost:${port}/admin
To access Infisical server
http://localhost:${port}
`);
}
});
// 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();