Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/release-helm-chart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ jobs:
set -euo pipefail

echo "Updating Chart.yaml with version: ${VERSION}"
yq -i ".version = \"${VERSION}\"" helm-chart/Chart.yaml
yq -i ".appVersion = \"${VERSION}\"" helm-chart/Chart.yaml
yq -i ".version = \"${VERSION}\"" charts/formbricks/Chart.yaml
yq -i ".appVersion = \"${VERSION}\"" charts/formbricks/Chart.yaml

echo "✅ Successfully updated Chart.yaml"

Expand All @@ -77,7 +77,7 @@ jobs:
set -euo pipefail

echo "Packaging Helm chart version: ${VERSION}"
helm package ./helm-chart
helm package ./charts/formbricks

echo "✅ Successfully packaged formbricks-${VERSION}.tgz"

Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/sonarqube.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
merge_group:
permissions:
contents: read
pull-requests: read
jobs:
sonarqube:
name: SonarQube
Expand Down Expand Up @@ -50,6 +51,9 @@ jobs:
pnpm test:coverage
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@2500896589ef8f7247069a56136f8dc177c27ccf
with:
args: >
-Dsonar.verbose=true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
16 changes: 16 additions & 0 deletions apps/web/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,22 @@ RUN chmod -R 755 ./node_modules/@noble/hashes
COPY --from=installer /app/node_modules/zod ./node_modules/zod
RUN chmod -R 755 ./node_modules/zod

# Pino loads transport code in worker threads via dynamic require().
# Next.js file tracing only traces static imports, missing runtime-loaded files
# (e.g. pino/lib/transport-stream.js, transport targets).
# Copy the full packages to ensure all runtime files are available.
COPY --from=installer /app/node_modules/pino ./node_modules/pino
RUN chmod -R 755 ./node_modules/pino

COPY --from=installer /app/node_modules/pino-opentelemetry-transport ./node_modules/pino-opentelemetry-transport
RUN chmod -R 755 ./node_modules/pino-opentelemetry-transport

COPY --from=installer /app/node_modules/pino-abstract-transport ./node_modules/pino-abstract-transport
RUN chmod -R 755 ./node_modules/pino-abstract-transport

COPY --from=installer /app/node_modules/otlp-logger ./node_modules/otlp-logger
RUN chmod -R 755 ./node_modules/otlp-logger

# Install prisma CLI globally for database migrations and fix permissions for nextjs user
RUN npm install --ignore-scripts -g prisma@6 \
&& chown -R nextjs:nextjs /usr/local/lib/node_modules/prisma
Expand Down
20 changes: 16 additions & 4 deletions apps/web/app/api/google-sheet/callback/route.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
import { google } from "googleapis";
import { getServerSession } from "next-auth";
import { responses } from "@/app/lib/api/response";
import {
GOOGLE_SHEETS_CLIENT_ID,
GOOGLE_SHEETS_CLIENT_SECRET,
GOOGLE_SHEETS_REDIRECT_URL,
WEBAPP_URL,
} from "@/lib/constants";
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
import { createOrUpdateIntegration } from "@/lib/integration/service";
import { authOptions } from "@/modules/auth/lib/authOptions";

export const GET = async (req: Request) => {
const url = req.url;
const queryParams = new URLSearchParams(url.split("?")[1]); // Split the URL and get the query parameters
const environmentId = queryParams.get("state"); // Get the value of the 'state' parameter
const code = queryParams.get("code");
const url = new URL(req.url);
const environmentId = url.searchParams.get("state");
const code = url.searchParams.get("code");

if (!environmentId) {
return responses.badRequestResponse("Invalid environmentId");
}

const session = await getServerSession(authOptions);
if (!session) {
return responses.notAuthenticatedResponse();
}

const canUserAccessEnvironment = await hasUserEnvironmentAccess(session.user.id, environmentId);
if (!canUserAccessEnvironment) {
return responses.unauthorizedResponse();
}

if (code && typeof code !== "string") {
return responses.badRequestResponse("`code` must be a string");
}
Expand Down
18 changes: 16 additions & 2 deletions apps/web/app/api/v1/integrations/notion/callback/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NextRequest } from "next/server";
import { TIntegrationNotionConfigData, TIntegrationNotionInput } from "@formbricks/types/integration/notion";
import { responses } from "@/app/lib/api/response";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { TSessionAuthentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import {
ENCRYPTION_KEY,
NOTION_OAUTH_CLIENT_ID,
Expand All @@ -10,10 +10,17 @@ import {
WEBAPP_URL,
} from "@/lib/constants";
import { symmetricEncrypt } from "@/lib/crypto";
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";

export const GET = withV1ApiWrapper({
handler: async ({ req }: { req: NextRequest }) => {
handler: async ({
req,
authentication,
}: {
req: NextRequest;
authentication: NonNullable<TSessionAuthentication>;
}) => {
const url = req.url;
const queryParams = new URLSearchParams(url.split("?")[1]); // Split the URL and get the query parameters
const environmentId = queryParams.get("state"); // Get the value of the 'state' parameter
Expand All @@ -26,6 +33,13 @@ export const GET = withV1ApiWrapper({
};
}

const canUserAccessEnvironment = await hasUserEnvironmentAccess(authentication.user.id, environmentId);
if (!canUserAccessEnvironment) {
return {
response: responses.unauthorizedResponse(),
};
}

if (code && typeof code !== "string") {
return {
response: responses.badRequestResponse("`code` must be a string"),
Expand Down
18 changes: 16 additions & 2 deletions apps/web/app/api/v1/integrations/slack/callback/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ import {
TIntegrationSlackCredential,
} from "@formbricks/types/integration/slack";
import { responses } from "@/app/lib/api/response";
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { TSessionAuthentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, WEBAPP_URL } from "@/lib/constants";
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";

export const GET = withV1ApiWrapper({
handler: async ({ req }: { req: NextRequest }) => {
handler: async ({
req,
authentication,
}: {
req: NextRequest;
authentication: NonNullable<TSessionAuthentication>;
}) => {
const url = req.url;
const queryParams = new URLSearchParams(url.split("?")[1]); // Split the URL and get the query parameters
const environmentId = queryParams.get("state"); // Get the value of the 'state' parameter
Expand All @@ -23,6 +30,13 @@ export const GET = withV1ApiWrapper({
};
}

const canUserAccessEnvironment = await hasUserEnvironmentAccess(authentication.user.id, environmentId);
if (!canUserAccessEnvironment) {
return {
response: responses.unauthorizedResponse(),
};
}

if (code && typeof code !== "string") {
return {
response: responses.badRequestResponse("`code` must be a string"),
Expand Down
10 changes: 10 additions & 0 deletions apps/web/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ const nextConfig = {
],
outputFileTracingIncludes: {
"/api/auth/**/*": ["../../node_modules/jose/**/*"],
// pino loads transport code in worker threads via dynamic require() — the file tracer
// only traces static imports and misses these runtime-loaded files.
// Include the full pino package (worker.js needs transport-stream.js, etc.)
// and its transport targets with their dependencies.
"/*": [
"../../node_modules/pino/**/*",
"../../node_modules/pino-opentelemetry-transport/**/*",
"../../node_modules/pino-abstract-transport/**/*",
"../../node_modules/otlp-logger/**/*",
],
},
turbopack: {},
experimental: {},
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"uuid": "11.1.0",
"node-forge": ">=1.3.2",
"tar-fs": "2.1.4",
"tar": ">=7.5.7",
"typeorm": ">=0.3.26",
"systeminformation": "5.27.14",
"qs": ">=6.14.1",
Expand All @@ -97,7 +98,7 @@
"diff": ">=8.0.3"
},
"comments": {
"overrides": "Security fixes for transitive dependencies. Remove when upstream packages update: axios (CVE-2025-58754) - awaiting @boxyhq/saml-jackson update | node-forge (Dependabot #230) - awaiting @boxyhq/saml-jackson update | tar-fs (Dependabot #205) - awaiting upstream dependency updates | typeorm (Dependabot #223) - awaiting @boxyhq/saml-jackson update | systeminformation (Dependabot #241) - awaiting @opentelemetry/host-metrics update | qs (Dependabot #245) - awaiting googleapis-common and stripe updates | preact (Dependabot #247) - awaiting next-auth update | fast-xml-parser (Dependabot #270) - awaiting @boxyhq/saml-jackson update | diff (Dependabot #269) - awaiting @microsoft/api-extractor update"
"overrides": "Security fixes for transitive dependencies. Remove when upstream packages update: axios (CVE-2025-58754) - awaiting @boxyhq/saml-jackson update | node-forge (Dependabot #230) - awaiting @boxyhq/saml-jackson update | tar-fs (Dependabot #205) - awaiting upstream dependency updates | tar (Dependabot #249/#264) - awaiting @boxyhq/saml-jackson/sqlite3 dependency updates | typeorm (Dependabot #223) - awaiting @boxyhq/saml-jackson update | systeminformation (Dependabot #241) - awaiting @opentelemetry/host-metrics update | qs (Dependabot #245) - awaiting googleapis-common and stripe updates | preact (Dependabot #247) - awaiting next-auth update | fast-xml-parser (Dependabot #270) - awaiting @boxyhq/saml-jackson update | diff (Dependabot #269) - awaiting @microsoft/api-extractor update"
},
"patchedDependencies": {
"next-auth@4.24.12": "patches/next-auth@4.24.12.patch"
Expand Down
19 changes: 13 additions & 6 deletions packages/logger/src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ const baseLoggerConfig: LoggerOptions = {
},
useOnlyCustomLevels: true,
timestamp: true,
formatters: {
level: (label) => {
return { level: label };
},
},
name: "formbricks",
};

Expand Down Expand Up @@ -102,9 +97,21 @@ const buildTransport = (): LoggerOptions["transport"] => {
return undefined; // Default JSON to stdout
};

const transport = buildTransport();

// Pino does not allow custom `formatters` (functions) when using multi-target transports
// because targets run in worker threads and functions cannot be serialized.
// Only attach the level formatter when we're NOT using `{ targets: [...] }`.
const useMultiTransport = transport !== undefined && "targets" in transport;

const loggerConfig: LoggerOptions = {
...baseLoggerConfig,
transport: buildTransport(),
transport,
...(!useMultiTransport && {
formatters: {
level: (label) => ({ level: label }),
},
}),
};

const pinoLogger: Logger = Pino(loggerConfig);
Expand Down
2 changes: 1 addition & 1 deletion packages/logger/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default defineConfig({
formats: ["es", "cjs"],
},
rollupOptions: {
external: ["pino", "pino-pretty", "zod"],
external: ["pino", "pino-pretty", "pino-opentelemetry-transport", "zod"],
output: {
exports: "named",
globals: {
Expand Down
Loading
Loading