diff --git a/Dockerfile.standalone-infisical b/Dockerfile.standalone-infisical index 56d6735b0..75cc2d534 100644 --- a/Dockerfile.standalone-infisical +++ b/Dockerfile.standalone-infisical @@ -2,7 +2,7 @@ ARG POSTHOG_HOST=https://app.posthog.com ARG POSTHOG_API_KEY=posthog-api-key ARG INTERCOM_ID=intercom-id -FROM node:16-alpine AS base +FROM node:20-alpine AS base FROM base AS frontend-dependencies @@ -68,11 +68,12 @@ RUN addgroup --system --gid 1001 nodejs \ WORKDIR /app -COPY backend/package*.json ./ +COPY backend-pg/package*.json ./ RUN npm ci --only-production -COPY /backend . +COPY /backend-pg . COPY --chown=non-root-user:nodejs standalone-entrypoint.sh standalone-entrypoint.sh +RUN npm i -D tsconfig-paths RUN npm run build # Production stage @@ -80,7 +81,7 @@ FROM base AS backend-runner WORKDIR /app -COPY backend/package*.json ./ +COPY backend-pg/package*.json ./ RUN npm ci --only-production COPY --from=backend-build /app . @@ -102,16 +103,18 @@ ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \ WORKDIR / -COPY --from=backend-runner /app /backend +COPY --from=backend-runner /app /backend-pg +COPY --from=backend-runner /app/dist/services/smtp/templates /backend-pg/dist/templates -COPY --from=frontend-runner /app ./backend/frontend-build +COPY --from=frontend-runner /app ./backend-pg/frontend-build ENV PORT 8080 +ENV HOST=0.0.0.0 ENV HTTPS_ENABLED false ENV NODE_ENV production ENV STANDALONE_BUILD true -WORKDIR /backend +WORKDIR /backend-pg ENV TELEMETRY_ENABLED true @@ -123,6 +126,3 @@ EXPOSE 8080 USER non-root-user CMD ["./standalone-entrypoint.sh"] - - - diff --git a/backend-pg/Dockerfile b/backend-pg/Dockerfile new file mode 100644 index 000000000..422ffef6a --- /dev/null +++ b/backend-pg/Dockerfile @@ -0,0 +1,33 @@ +# Build stage +FROM node:20-alpine AS build + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci --only-production + +COPY . . +RUN npm run build + +# Production stage +FROM node:20-alpine + +WORKDIR /app + +ENV npm_config_cache /home/node/.npm + +COPY package*.json ./ +RUN npm ci --only-production && npm cache clean --force + +COPY --from=build /app . + +RUN apk add --no-cache bash curl && curl -1sLf \ + 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.alpine.sh' | bash \ + && apk add infisical=0.8.1 && apk add --no-cache git + +HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \ + CMD node healthcheck.js + +EXPOSE 4000 + +CMD ["npm", "start"] diff --git a/backend-pg/package-lock.json b/backend-pg/package-lock.json index 301e15140..956058621 100644 --- a/backend-pg/package-lock.json +++ b/backend-pg/package-lock.json @@ -12958,9 +12958,9 @@ } }, "node_modules/vite": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.7.tgz", - "integrity": "sha512-B4T4rJCDPihrQo2B+h1MbeGL/k/GMAHzhQ8S0LjQ142s6/+l3hHTT095ORvsshj4QCkoWu3Xtmob5mazvakaOw==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", + "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", "dev": true, "dependencies": { "esbuild": "^0.19.3", diff --git a/backend-pg/package.json b/backend-pg/package.json index 1a1a90af2..30101c105 100644 --- a/backend-pg/package.json +++ b/backend-pg/package.json @@ -7,8 +7,8 @@ "test": "echo \"Error: no test specified\" && exit 1", "dev": "tsx watch --clear-screen=false ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine", "dev:docker": "nodemon", - "build": "rimraf dist && tsup src --out-dir dist --loader '.handlebars=copy' --loader '.md=copy'", - "start": "node dist/server/main.js", + "build": "rimraf dist && tsup", + "start": "node dist/main.mjs", "type:check": "tsc --noEmit", "lint:fix": "eslint --fix --ext js,ts ./src", "lint": "eslint 'src/**/*.ts'", diff --git a/backend-pg/src/hello.ts b/backend-pg/src/hello.ts new file mode 100644 index 000000000..e69de29bb diff --git a/backend-pg/src/lib/config/env.ts b/backend-pg/src/lib/config/env.ts index e14d7f86e..6f8f2d478 100644 --- a/backend-pg/src/lib/config/env.ts +++ b/backend-pg/src/lib/config/env.ts @@ -85,13 +85,18 @@ const envSchema = z // LICENCE LICENSE_SERVER_URL: zpStr(z.string().optional()), LICENSE_SERVER_KEY: zpStr(z.string().optional()), - LICENSE_KEY: zpStr(z.string().optional()) + LICENSE_KEY: zpStr(z.string().optional()), + STANDALONE_MODE: z + .enum(["true", "false"]) + .transform((val) => val === "true") + .optional() }) .transform((data) => ({ ...data, isSmtpConfigured: Boolean(data.SMTP_HOST), isRedisConfigured: Boolean(data.REDIS_URL), isDevelopmentMode: data.NODE_ENV === "development", + isProductionMode: data.NODE_ENV === "production", isSecretScanningConfigured: Boolean(data.SECRET_SCANNING_GIT_APP_ID) && Boolean(data.SECRET_SCANNING_PRIVATE_KEY) && diff --git a/backend-pg/src/server/app.ts b/backend-pg/src/server/app.ts index 1e2b7f0fe..b0a232ee0 100644 --- a/backend-pg/src/server/app.ts +++ b/backend-pg/src/server/app.ts @@ -1,3 +1,6 @@ +/* eslint-disable import/extensions */ +import path from "node:path"; + import type { FastifyCookieOptions } from "@fastify/cookie"; import cookie from "@fastify/cookie"; import type { FastifyCorsOptions } from "@fastify/cors"; @@ -17,6 +20,7 @@ import { getConfig } from "@lib/config/env"; import { globalRateLimiterCfg } from "./config/rateLimiter"; import { fastifyErrHandler } from "./plugins/error-handler"; +import { registerExternalNextjs } from "./plugins/external-nextjs"; import { serializerCompiler, validatorCompiler, ZodTypeProvider } from "./plugins/fastify-zod"; import { fastifyIp } from "./plugins/ip"; import { fastifySwagger } from "./plugins/swagger"; @@ -48,7 +52,7 @@ export const main = async ({ db, smtp, logger, queue }: TMain) => { await server.register(cors, { credentials: true, - origin: true + origin: appCfg.SITE_URL }); // pull ip based on various proxy headers await server.register(fastifyIp); @@ -58,12 +62,21 @@ export const main = async ({ db, smtp, logger, queue }: TMain) => { await server.register(fastifyErrHandler); // Rate limiters and security headers - if (appCfg.NODE_ENV === "production") { + if (appCfg.isProductionMode) { await server.register(ratelimiter, globalRateLimiterCfg()); } await server.register(helmet, { contentSecurityPolicy: false }); await server.register(registerRoutes, { smtp, queue, db }); + + if (appCfg.isProductionMode) { + await server.register(registerExternalNextjs, { + standaloneMode: appCfg.STANDALONE_MODE, + dir: path.join(__dirname, "../"), + port: appCfg.PORT + }); + } + await server.ready(); server.swagger(); return server; diff --git a/backend-pg/src/server/plugins/external-nextjs.ts b/backend-pg/src/server/plugins/external-nextjs.ts new file mode 100644 index 000000000..010e92f96 --- /dev/null +++ b/backend-pg/src/server/plugins/external-nextjs.ts @@ -0,0 +1,56 @@ +// this plugins allows to run infisical in standalone mode +// standalone mode = infisical backend and nextjs frontend in one server +// this way users don't need to deploy two things + +import path from "node:path"; + +// to enabled this u need to set standalone mode to true +export const registerExternalNextjs = async ( + server: FastifyZodProvider, + { + standaloneMode, + dir, + port + }: { + standaloneMode?: boolean; + dir: string; + port: number; + } +) => { + if (standaloneMode) { + const nextJsBuildPath = path.join(dir, "frontend-build"); + + const { default: conf } = await import( + path.join(dir, "frontend-build/.next/required-server-files.json"), + // @ts-expect-error type + { + assert: { type: "json" } + } + ); + + const { default: NextServer } = ( + await import(path.join(dir, "frontend-build/node_modules/next/dist/server/next-server.js")) + ).default; + const nextApp = new NextServer({ + dev: false, + dir: nextJsBuildPath, + port, + conf: conf.config, + hostname: "local", + customServer: false + }); + + server.route({ + method: ["GET", "PUT", "PATCH", "POST", "DELETE"], + url: "/*", + handler: (req, res) => + nextApp + .getRequestHandler()(req.raw, res.raw) + .then(() => { + res.hijack(); + }) + }); + server.addHook("onClose", () => nextApp.close()); + await nextApp.prepare(); + } +}; diff --git a/backend-pg/tsup.config.js b/backend-pg/tsup.config.js new file mode 100644 index 000000000..2cc687afc --- /dev/null +++ b/backend-pg/tsup.config.js @@ -0,0 +1,13 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + shims: true, + format: "esm", + loader: { + ".handlebars": "copy", + ".md": "copy" + }, + external: ["../../../frontend/node_modules/next/dist/server/next-server.js"], + outDir: "dist", + entry: ["./src"] +}); diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 033f65f0f..6666aaabf 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -38,6 +38,7 @@ module.exports = { "react/jsx-props-no-spreading": "off", // switched off for component building // TODO: This rule will be switched ON after complete revamp of frontend "@typescript-eslint/no-explicit-any": "off", + "jsx-a11y/control-has-associated-label": "off", "no-console": "off", "arrow-body-style": "off", "no-underscore-dangle": [ diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4af278f92..f28920f06 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -7098,9 +7098,12 @@ "dev": true }, "node_modules/@swc/helpers": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.11.tgz", - "integrity": "sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz", + "integrity": "sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==", + "dev": true, + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -17118,6 +17121,14 @@ } } }, + "node_modules/next/node_modules/@swc/helpers": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.11.tgz", + "integrity": "sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw==", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.14", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", diff --git a/standalone-entrypoint.sh b/standalone-entrypoint.sh index e15931dc4..3f88260e9 100755 --- a/standalone-entrypoint.sh +++ b/standalone-entrypoint.sh @@ -5,4 +5,4 @@ scripts/initialize-standalone-build.sh cd ../ -exec node build/index.js +exec node dist/main.mjs