diff --git a/.env.docker b/.env.docker
index 8700d35a5d..33ecc2c051 100644
--- a/.env.docker
+++ b/.env.docker
@@ -4,20 +4,40 @@
############
-# Basics #
+# BASICS #
############
+NEXT_PUBLIC_WEBAPP_URL=http://localhost:3000
+
+##############
+# DATABASE #
+##############
+
+DATABASE_URL='postgresql://postgres:postgres@postgres:5432/formbricks?schema=public'
+
+# Uncomment to enable a dedicated connection pool for Prisma using Prisma Data Proxy
+# Cold boots will be faster and you'll be able to scale your DB independently of your app.
+# @see https://www.prisma.io/docs/data-platform/data-proxy/use-data-proxy
+# PRISMA_GENERATE_DATAPROXY=true
+PRISMA_GENERATE_DATAPROXY=
+
+###############
+# NEXT AUTH #
+###############
+
+# @see: https://next-auth.js.org/configuration/options#nextauth_secret
+# You can use: `openssl rand -base64 32` to generate one
NEXTAUTH_SECRET=RANDOM_STRING
+# Set this to your public-facing URL, e.g., https://example.com
+# You do not need the NEXTAUTH_URL environment variable in Vercel.
NEXTAUTH_URL=http://localhost:3000
# If you encounter NEXT_AUTH URL problems this should always be localhost:3000 (or whatever port your app is running on)
# NEXTAUTH_URL_INTERNAL=http://localhost:3000
-DATABASE_URL='postgresql://postgres:postgres@postgres:5432/postgres?schema=public'
-
################
-# Mail Setup #
+# MAIL SETUP #
################
# Necessary if email verification and password reset are enabled.
diff --git a/.env.example b/.env.example
index 7a36a72bc9..34a9507bb5 100644
--- a/.env.example
+++ b/.env.example
@@ -4,18 +4,16 @@
############
-# Basics #
+# BASICS #
############
-NEXTAUTH_SECRET=RANDOM_STRING
+NEXT_PUBLIC_WEBAPP_URL=http://localhost:3000
-# Set this to your public-facing URL, e.g., https://example.com
-NEXTAUTH_URL=http://localhost:3000
+##############
+# DATABASE #
+##############
-# If you encounter NEXT_AUTH URL problems this should always be localhost:3000 (or whatever port your app is running on)
-# NEXTAUTH_URL_INTERNAL=http://localhost:3000
-
-DATABASE_URL='postgresql://postgres:postgres@localhost:5432/postgres?schema=public'
+DATABASE_URL='postgresql://postgres:postgres@localhost:5432/formbricks?schema=public'
# Uncomment to enable a dedicated connection pool for Prisma using Prisma Data Proxy
# Cold boots will be faster and you'll be able to scale your DB independently of your app.
@@ -23,8 +21,23 @@ DATABASE_URL='postgresql://postgres:postgres@localhost:5432/postgres?schema=publ
# PRISMA_GENERATE_DATAPROXY=true
PRISMA_GENERATE_DATAPROXY=
+###############
+# NEXT AUTH #
+###############
+
+# @see: https://next-auth.js.org/configuration/options#nextauth_secret
+# You can use: `openssl rand -base64 32` to generate one
+NEXTAUTH_SECRET=RANDOM_STRING
+
+# Set this to your public-facing URL, e.g., https://example.com
+# You do not need the NEXTAUTH_URL environment variable in Vercel.
+NEXTAUTH_URL=http://localhost:3000
+
+# If you encounter NEXT_AUTH URL problems this should always be localhost:3000 (or whatever port your app is running on)
+# NEXTAUTH_URL_INTERNAL=http://localhost:3000
+
################
-# Mail Setup #
+# MAIL SETUP #
################
# Necessary if email verification and password reset are enabled.
diff --git a/apps/web/app/HomeRedirect.tsx b/apps/web/app/HomeRedirect.tsx
deleted file mode 100644
index 299d98db1f..0000000000
--- a/apps/web/app/HomeRedirect.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-"use client";
-
-import LoadingSpinner from "@/components/shared/LoadingSpinner";
-import { fetcher } from "@formbricks/lib/fetcher";
-import type { Session } from "next-auth";
-import { signOut } from "next-auth/react";
-import { redirect } from "next/navigation";
-import { useEffect } from "react";
-import useSWR from "swr";
-
-interface HomeRedirectProps {
- session: Session;
-}
-
-export function HomeRedirect({ session }: HomeRedirectProps) {
- const { data, error } = useSWR(`/api/v1/environments/find-first`, fetcher);
-
- useEffect(() => {
- if (session) {
- if (!session.user?.onboardingDisplayed) {
- return redirect(`/onboarding`);
- }
-
- if (data && !error) {
- return redirect(`/environments/${data.id}`);
- } else if (error) {
- console.error(error);
- }
- } else {
- return redirect(`/auth/login`);
- }
- }, [data, error, session]);
-
- if (error) {
- setTimeout(() => {
- signOut();
- }, 3000);
- return
There was an error with your current Session. You are getting redirected to the login.
;
- }
-
- return (
-
-
-
- );
-}
diff --git a/apps/web/pages/api/auth/[...nextauth].ts b/apps/web/app/api/auth/[...nextauth]/authOptions.ts
similarity index 98%
rename from apps/web/pages/api/auth/[...nextauth].ts
rename to apps/web/app/api/auth/[...nextauth]/authOptions.ts
index 21c4b50a94..2741b21ee7 100644
--- a/apps/web/pages/api/auth/[...nextauth].ts
+++ b/apps/web/app/api/auth/[...nextauth]/authOptions.ts
@@ -2,9 +2,7 @@ import { verifyPassword } from "@/lib/auth";
import { verifyToken } from "@/lib/jwt";
import { prisma } from "@formbricks/database";
import { IdentityProvider } from "@prisma/client";
-import { NextApiRequest, NextApiResponse } from "next";
import type { NextAuthOptions } from "next-auth";
-import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import GitHubProvider from "next-auth/providers/github";
@@ -350,7 +348,3 @@ export const authOptions: NextAuthOptions = {
error: "/auth/login", // Error code passed in query string as ?error=
},
};
-
-export default async function auth(req: NextApiRequest, res: NextApiResponse) {
- return await NextAuth(req, res, authOptions);
-}
diff --git a/apps/web/app/api/auth/[...nextauth]/route.ts b/apps/web/app/api/auth/[...nextauth]/route.ts
new file mode 100644
index 0000000000..f1cc59ec9c
--- /dev/null
+++ b/apps/web/app/api/auth/[...nextauth]/route.ts
@@ -0,0 +1,6 @@
+import NextAuth from "next-auth";
+import { authOptions } from "./authOptions";
+
+const handler = NextAuth(authOptions);
+
+export { handler as GET, handler as POST };
diff --git a/apps/web/app/api/v1/memberships/route.ts b/apps/web/app/api/v1/memberships/route.ts
new file mode 100644
index 0000000000..ed2ec86e8f
--- /dev/null
+++ b/apps/web/app/api/v1/memberships/route.ts
@@ -0,0 +1,21 @@
+import { getSessionUser } from "@/lib/api/apiHelper";
+import { prisma } from "@formbricks/database";
+import { NextResponse } from "next/server";
+
+export async function GET() {
+ const sessionUser = await getSessionUser();
+ if (!sessionUser) {
+ return new Response("Not authenticated", {
+ status: 401,
+ });
+ }
+
+ // get memberships
+ const memberships = await prisma.membership.findMany({
+ where: {
+ userId: sessionUser.id,
+ },
+ });
+
+ return NextResponse.json(memberships);
+}
diff --git a/apps/web/app/api/v1/users/me/route.ts b/apps/web/app/api/v1/users/me/route.ts
new file mode 100644
index 0000000000..1695486259
--- /dev/null
+++ b/apps/web/app/api/v1/users/me/route.ts
@@ -0,0 +1,39 @@
+import { getSessionUser } from "@/lib/api/apiHelper";
+import { prisma } from "@formbricks/database";
+import { NextRequest, NextResponse } from "next/server";
+
+export async function GET() {
+ const sessionUser = await getSessionUser();
+ if (!sessionUser) {
+ return new Response("Not authenticated", {
+ status: 401,
+ });
+ }
+
+ const user = await prisma.user.findUnique({
+ where: {
+ email: sessionUser.email,
+ },
+ });
+
+ return NextResponse.json(user);
+}
+
+export async function PUT(request: NextRequest) {
+ const sessionUser = await getSessionUser();
+ if (!sessionUser) {
+ return new Response("Not authenticated", {
+ status: 401,
+ });
+ }
+ const body = await request.json();
+
+ const user = await prisma.user.update({
+ where: {
+ email: sessionUser.email,
+ },
+ data: body,
+ });
+
+ return NextResponse.json(user);
+}
diff --git a/apps/web/app/environments/[environmentId]/layout.tsx b/apps/web/app/environments/[environmentId]/layout.tsx
index 75661d3e3e..a8ddc748d8 100644
--- a/apps/web/app/environments/[environmentId]/layout.tsx
+++ b/apps/web/app/environments/[environmentId]/layout.tsx
@@ -2,7 +2,7 @@ import EnvironmentsNavbar from "@/app/environments/[environmentId]/EnvironmentsN
import ToasterClient from "@/components/ToasterClient";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
-import { authOptions } from "pages/api/auth/[...nextauth]";
+import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
import PosthogIdentify from "./PosthogIdentify";
import FormbricksClient from "./FormbricksClient";
import { PosthogClientWrapper } from "../../PosthogClientWrapper";
diff --git a/apps/web/app/environments/[environmentId]/settings/billing/page.tsx b/apps/web/app/environments/[environmentId]/settings/billing/page.tsx
index 994145e537..ab429915a7 100644
--- a/apps/web/app/environments/[environmentId]/settings/billing/page.tsx
+++ b/apps/web/app/environments/[environmentId]/settings/billing/page.tsx
@@ -3,7 +3,7 @@ import SettingsTitle from "../SettingsTitle";
import { Button } from "@formbricks/ui";
import PricingTable from "./PricingTable";
import { getServerSession } from "next-auth";
-import { authOptions } from "@/pages/api/auth/[...nextauth]";
+import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
const proPlan = false;
diff --git a/apps/web/app/environments/[environmentId]/settings/profile/page.tsx b/apps/web/app/environments/[environmentId]/settings/profile/page.tsx
index 76d0443e7e..cc0498bfe0 100644
--- a/apps/web/app/environments/[environmentId]/settings/profile/page.tsx
+++ b/apps/web/app/environments/[environmentId]/settings/profile/page.tsx
@@ -2,7 +2,7 @@ import SettingsCard from "../SettingsCard";
import SettingsTitle from "../SettingsTitle";
import { getServerSession } from "next-auth";
import { EditName, EditAvatar } from "./editProfile";
-import { authOptions } from "@/pages/api/auth/[...nextauth]";
+import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
export default async function ProfileSettingsPage() {
const session = await getServerSession(authOptions);
diff --git a/apps/web/app/error.tsx b/apps/web/app/error.tsx
new file mode 100644
index 0000000000..57d5ca76ee
--- /dev/null
+++ b/apps/web/app/error.tsx
@@ -0,0 +1,26 @@
+"use client"; // Error components must be Client components
+
+import { Button, ErrorComponent } from "@/../../packages/ui";
+import { useEffect } from "react";
+
+export default function Error({ error, reset }: { error: Error; reset: () => void }) {
+ useEffect(() => {
+ // Log the error to an error reporting service
+ console.error(error);
+ }, [error]);
+
+ return (
+