mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-05 10:36:06 -06:00
Add t3 env for env validation (#498)
* add t3 env for env validation * fix env variables that should be optional * update gitignore * add vercel ignore
This commit is contained in:
@@ -46,6 +46,7 @@ NEXTAUTH_URL=http://localhost:3000
|
||||
# MAIL_FROM=noreply@example.com
|
||||
# SMTP_HOST=localhost
|
||||
# SMTP_PORT=1025
|
||||
# Enable SMTP_SECURE_ENABLED for TLS (port 465)
|
||||
# SMTP_SECURE_ENABLED=0 # Enable for TLS (port 465)
|
||||
# SMTP_USER=smtpUser
|
||||
# SMTP_PASSWORD=smtpPassword
|
||||
|
||||
@@ -82,12 +82,6 @@ NEXT_PUBLIC_PRIVACY_URL=
|
||||
NEXT_PUBLIC_TERMS_URL=
|
||||
NEXT_PUBLIC_IMPRINT_URL=
|
||||
|
||||
# Disable Sentry warning
|
||||
SENTRY_IGNORE_API_RESOLUTION_ERROR=1
|
||||
|
||||
# Enable Sentry Error Tracking
|
||||
NEXT_PUBLIC_SENTRY_DSN=
|
||||
|
||||
# Configure Github Login
|
||||
NEXT_PUBLIC_GITHUB_AUTH_ENABLED=0
|
||||
GITHUB_ID=
|
||||
@@ -100,7 +94,6 @@ GOOGLE_CLIENT_SECRET=
|
||||
|
||||
|
||||
# Stripe Billing Variables
|
||||
NEXT_PUBLIC_STRIPE_PRICING_TABLE_ID=
|
||||
NEXT_PUBLIC_STRIPE_PUBLIC_KEY=
|
||||
STRIPE_SECRET_KEY=
|
||||
STRIPE_WEBHOOK_SECRET=
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -33,6 +33,7 @@ yarn-error.log*
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
!packages/database/.env
|
||||
!apps/web/.env
|
||||
|
||||
# Prisma generated files
|
||||
packages/database/zod
|
||||
|
||||
1
.vercelignore
Normal file
1
.vercelignore
Normal file
@@ -0,0 +1 @@
|
||||
apps/web/.env
|
||||
1
apps/web/.env
Symbolic link
1
apps/web/.env
Symbolic link
@@ -0,0 +1 @@
|
||||
../../.env
|
||||
@@ -1,13 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import { env } from "@/env.mjs";
|
||||
import { formbricksEnabled } from "@/lib/formbricks";
|
||||
import formbricks from "@formbricks/js";
|
||||
import { useEffect } from "react";
|
||||
|
||||
/* if (typeof window !== "undefined" && formbricksEnabled) {
|
||||
formbricks.init({
|
||||
environmentId: process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID || "",
|
||||
apiHost: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST || "",
|
||||
environmentId: env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID || "",
|
||||
apiHost: env.NEXT_PUBLIC_FORMBRICKS_API_HOST || "",
|
||||
logLevel: "debug",
|
||||
});
|
||||
} */
|
||||
@@ -16,8 +17,8 @@ export default function FormbricksClient({ session }) {
|
||||
useEffect(() => {
|
||||
if (formbricksEnabled && session.user && formbricks) {
|
||||
formbricks.init({
|
||||
environmentId: process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID || "",
|
||||
apiHost: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST || "",
|
||||
environmentId: env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID || "",
|
||||
apiHost: env.NEXT_PUBLIC_FORMBRICKS_API_HOST || "",
|
||||
});
|
||||
formbricks.setUserId(session.user.id);
|
||||
formbricks.setEmail(session.user.email);
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
"use client";
|
||||
|
||||
import { env } from "@/env.mjs";
|
||||
import { usePathname, useSearchParams } from "next/navigation";
|
||||
import posthog from "posthog-js";
|
||||
import { PostHogProvider } from "posthog-js/react";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const posthogEnabled = process.env.NEXT_PUBLIC_POSTHOG_API_KEY && process.env.NEXT_PUBLIC_POSTHOG_API_HOST;
|
||||
const posthogEnabled = env.NEXT_PUBLIC_POSTHOG_API_KEY && env.NEXT_PUBLIC_POSTHOG_API_HOST;
|
||||
|
||||
// Check that PostHog is client-side (used to handle Next.js SSR)
|
||||
if (
|
||||
typeof window !== "undefined" &&
|
||||
posthogEnabled &&
|
||||
typeof process.env.NEXT_PUBLIC_POSTHOG_API_KEY === "string" &&
|
||||
typeof process.env.NEXT_PUBLIC_POSTHOG_API_HOST === "string"
|
||||
typeof env.NEXT_PUBLIC_POSTHOG_API_KEY === "string" &&
|
||||
typeof env.NEXT_PUBLIC_POSTHOG_API_HOST === "string"
|
||||
) {
|
||||
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_API_KEY, {
|
||||
api_host: process.env.NEXT_PUBLIC_POSTHOG_API_HOST,
|
||||
posthog.init(env.NEXT_PUBLIC_POSTHOG_API_KEY, {
|
||||
api_host: env.NEXT_PUBLIC_POSTHOG_API_HOST,
|
||||
// Disable in development
|
||||
loaded: (posthog) => {
|
||||
if (process.env.NODE_ENV === "development") posthog.opt_out_capturing();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { env } from "@/env.mjs";
|
||||
import { verifyPassword } from "@/lib/auth";
|
||||
import { verifyToken } from "@/lib/jwt";
|
||||
import { prisma } from "@formbricks/database";
|
||||
@@ -120,12 +121,12 @@ export const authOptions: NextAuthOptions = {
|
||||
},
|
||||
}),
|
||||
GitHubProvider({
|
||||
clientId: process.env.GITHUB_ID || "",
|
||||
clientSecret: process.env.GITHUB_SECRET || "",
|
||||
clientId: env.GITHUB_ID || "",
|
||||
clientSecret: env.GITHUB_SECRET || "",
|
||||
}),
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID || "",
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET || "",
|
||||
clientId: env.GOOGLE_CLIENT_ID || "",
|
||||
clientSecret: env.GOOGLE_CLIENT_SECRET || "",
|
||||
allowDangerousEmailAccountLinking: true,
|
||||
}),
|
||||
],
|
||||
@@ -189,7 +190,7 @@ export const authOptions: NextAuthOptions = {
|
||||
},
|
||||
async signIn({ user, account }: any) {
|
||||
if (account.provider === "credentials" || account.provider === "token") {
|
||||
if (!user.emailVerified && process.env.NEXT_PUBLIC_EMAIL_VERIFICATION_DISABLED !== "1") {
|
||||
if (!user.emailVerified && env.NEXT_PUBLIC_EMAIL_VERIFICATION_DISABLED !== "1") {
|
||||
return `/auth/verification-requested?email=${encodeURIComponent(user.email)}`;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -3,14 +3,11 @@ import { verifyInviteToken } from "@/lib/jwt";
|
||||
import { populateEnvironment } from "@/lib/populate";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { NextResponse } from "next/server";
|
||||
import { env } from "@/env.mjs";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
let { inviteToken, ...user } = await request.json();
|
||||
if (
|
||||
inviteToken
|
||||
? process.env.NEXT_PUBLIC_INVITE_DISABLED === "1"
|
||||
: process.env.NEXT_PUBLIC_SIGNUP_DISABLED === "1"
|
||||
) {
|
||||
if (inviteToken ? env.NEXT_PUBLIC_INVITE_DISABLED === "1" : env.NEXT_PUBLIC_SIGNUP_DISABLED === "1") {
|
||||
return NextResponse.json({ error: "Signup disabled" }, { status: 403 });
|
||||
}
|
||||
user = { ...user, ...{ email: user.email.toLowerCase() } };
|
||||
@@ -99,7 +96,7 @@ export async function POST(request: Request) {
|
||||
await prisma.invite.delete({ where: { id: inviteId } });
|
||||
}
|
||||
|
||||
if (process.env.NEXT_PUBLIC_EMAIL_VERIFICATION_DISABLED !== "1") {
|
||||
if (env.NEXT_PUBLIC_EMAIL_VERIFICATION_DISABLED !== "1") {
|
||||
await sendVerificationEmail(userData);
|
||||
}
|
||||
return NextResponse.json(userData);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useSearchParams } from "next/navigation";
|
||||
import { SignupForm } from "@/components/auth/SignupForm";
|
||||
import FormWrapper from "@/components/auth/FormWrapper";
|
||||
import Testimonial from "@/components/auth/Testimonial";
|
||||
import { env } from "@/env.mjs";
|
||||
|
||||
export default function SignUpPage() {
|
||||
const searchParams = useSearchParams();
|
||||
@@ -18,9 +19,7 @@ export default function SignUpPage() {
|
||||
<div className="col-span-3 flex flex-col items-center justify-center">
|
||||
<FormWrapper>
|
||||
{(
|
||||
inviteToken
|
||||
? process.env.NEXT_PUBLIC_INVITE_DISABLED === "1"
|
||||
: process.env.NEXT_PUBLIC_SIGNUP_DISABLED === "1"
|
||||
inviteToken ? env.NEXT_PUBLIC_INVITE_DISABLED === "1" : env.NEXT_PUBLIC_SIGNUP_DISABLED === "1"
|
||||
) ? (
|
||||
<>
|
||||
<h1 className="leading-2 mb-4 text-center font-bold">Sign up disabled</h1>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
"use client";
|
||||
import { env } from "@/env.mjs";
|
||||
import type { Session } from "next-auth";
|
||||
import { usePostHog } from "posthog-js/react";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const posthogEnabled = process.env.NEXT_PUBLIC_POSTHOG_API_KEY && process.env.NEXT_PUBLIC_POSTHOG_API_HOST;
|
||||
const posthogEnabled = env.NEXT_PUBLIC_POSTHOG_API_KEY && env.NEXT_PUBLIC_POSTHOG_API_HOST;
|
||||
|
||||
export default function PosthogIdentify({ session }: { session: Session }) {
|
||||
const posthog = usePostHog();
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import ShareInviteModal from "@/app/environments/[environmentId]/settings/members/ShareInviteModal";
|
||||
import DeleteDialog from "@/components/shared/DeleteDialog";
|
||||
import LoadingSpinner from "@/components/shared/LoadingSpinner";
|
||||
import CreateTeamModal from "@/components/team/CreateTeamModal";
|
||||
import { env } from "@/env.mjs";
|
||||
import {
|
||||
addMember,
|
||||
deleteInvite,
|
||||
@@ -12,6 +15,8 @@ import {
|
||||
updateMemberRole,
|
||||
useMembers,
|
||||
} from "@/lib/members";
|
||||
import { useProfile } from "@/lib/profile";
|
||||
import { capitalizeFirstLetter } from "@/lib/utils";
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
@@ -28,15 +33,11 @@ import {
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@formbricks/ui";
|
||||
import { ChevronDownIcon } from "@heroicons/react/20/solid";
|
||||
import { PaperAirplaneIcon, ShareIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import AddMemberModal from "./AddMemberModal";
|
||||
import CreateTeamModal from "@/components/team/CreateTeamModal";
|
||||
import { capitalizeFirstLetter } from "@/lib/utils";
|
||||
import { useProfile } from "@/lib/profile";
|
||||
import { ChevronDownIcon } from "@heroicons/react/20/solid";
|
||||
import ShareInviteModal from "@/app/environments/[environmentId]/settings/members/ShareInviteModal";
|
||||
|
||||
type EditMembershipsProps = {
|
||||
environmentId: string;
|
||||
@@ -191,7 +192,7 @@ export function EditMemberships({ environmentId }: EditMembershipsProps) {
|
||||
}}>
|
||||
Create New Team
|
||||
</Button>
|
||||
{process.env.NEXT_PUBLIC_INVITE_DISABLED !== "1" && isAdminOrOwner && (
|
||||
{env.NEXT_PUBLIC_INVITE_DISABLED !== "1" && isAdminOrOwner && (
|
||||
<Button
|
||||
variant="darkCTA"
|
||||
onClick={() => {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import Headline from "@/components/preview/Headline";
|
||||
import Subheader from "@/components/preview/Subheader";
|
||||
import { env } from "@/env.mjs";
|
||||
import { formbricksEnabled, updateResponse } from "@/lib/formbricks";
|
||||
import { useProfile } from "@/lib/profile";
|
||||
import { useProfileMutation } from "@/lib/profile/mutateProfile";
|
||||
@@ -49,11 +50,7 @@ const Objective: React.FC<ObjectiveProps> = ({ next, skip, formbricksResponseId
|
||||
console.error(e);
|
||||
toast.error("An error occured saving your settings");
|
||||
}
|
||||
if (
|
||||
formbricksEnabled &&
|
||||
process.env.NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID &&
|
||||
formbricksResponseId
|
||||
) {
|
||||
if (formbricksEnabled && env.NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID && formbricksResponseId) {
|
||||
const res = await updateResponse(
|
||||
formbricksResponseId,
|
||||
{
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
import { cn } from "@/../../packages/lib/cn";
|
||||
import Headline from "@/components/preview/Headline";
|
||||
import Subheader from "@/components/preview/Subheader";
|
||||
import { env } from "@/env.mjs";
|
||||
import { createResponse, formbricksEnabled } from "@/lib/formbricks";
|
||||
import { useProfile } from "@/lib/profile";
|
||||
import { useProfileMutation } from "@/lib/profile/mutateProfile";
|
||||
import { SurveyId } from "@formbricks/js";
|
||||
import { ResponseId, SurveyId } from "@formbricks/js";
|
||||
import { Button } from "@formbricks/ui";
|
||||
import { useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { ResponseId } from "@formbricks/js";
|
||||
|
||||
type RoleProps = {
|
||||
next: () => void;
|
||||
@@ -48,13 +48,10 @@ const Role: React.FC<RoleProps> = ({ next, skip, setFormbricksResponseId }) => {
|
||||
toast.error("An error occured saving your settings");
|
||||
console.error(e);
|
||||
}
|
||||
if (formbricksEnabled && process.env.NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID) {
|
||||
const res = await createResponse(
|
||||
process.env.NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID as SurveyId,
|
||||
{
|
||||
role: selectedRole.label,
|
||||
}
|
||||
);
|
||||
if (formbricksEnabled && env.NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID) {
|
||||
const res = await createResponse(env.NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID as SurveyId, {
|
||||
role: selectedRole.label,
|
||||
});
|
||||
if (res.ok) {
|
||||
const response = res.data;
|
||||
setFormbricksResponseId(response.id);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { GoogleButton } from "@/components/auth/GoogleButton";
|
||||
import { env } from "@/env.mjs";
|
||||
import { Button, PasswordInput } from "@formbricks/ui";
|
||||
import { XCircleIcon } from "@heroicons/react/24/solid";
|
||||
import { signIn } from "next-auth/react";
|
||||
@@ -75,7 +76,7 @@ export const SigninForm = () => {
|
||||
className="focus:border-brand focus:ring-brand block w-full rounded-md border-slate-300 shadow-sm sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
{process.env.NEXT_PUBLIC_PASSWORD_RESET_DISABLED !== "1" && isPasswordFocused && (
|
||||
{env.NEXT_PUBLIC_PASSWORD_RESET_DISABLED !== "1" && isPasswordFocused && (
|
||||
<div className="ml-1 text-right transition-all duration-500 ease-in-out">
|
||||
<Link
|
||||
href="/auth/forgot-password"
|
||||
@@ -105,18 +106,18 @@ export const SigninForm = () => {
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
{process.env.NEXT_PUBLIC_GOOGLE_AUTH_ENABLED === "1" && (
|
||||
{env.NEXT_PUBLIC_GOOGLE_AUTH_ENABLED === "1" && (
|
||||
<>
|
||||
<GoogleButton />
|
||||
</>
|
||||
)}
|
||||
{process.env.NEXT_PUBLIC_GITHUB_AUTH_ENABLED === "1" && (
|
||||
{env.NEXT_PUBLIC_GITHUB_AUTH_ENABLED === "1" && (
|
||||
<>
|
||||
<GithubButton />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{process.env.NEXT_PUBLIC_SIGNUP_DISABLED !== "1" && (
|
||||
{env.NEXT_PUBLIC_SIGNUP_DISABLED !== "1" && (
|
||||
<div className="mt-9 text-center text-xs ">
|
||||
<span className="leading-5 text-slate-500">New to Formbricks?</span>
|
||||
<br />
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@formbricks/ui";
|
||||
import { PasswordInput } from "@formbricks/ui";
|
||||
import { GoogleButton } from "@/components/auth/GoogleButton";
|
||||
import IsPasswordValid from "@/components/auth/IsPasswordValid";
|
||||
import { env } from "@/env.mjs";
|
||||
import { createUser } from "@/lib/users/users";
|
||||
import { Button, PasswordInput } from "@formbricks/ui";
|
||||
import { XCircleIcon } from "@heroicons/react/24/solid";
|
||||
import Link from "next/link";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useRef, useState } from "react";
|
||||
import { GithubButton } from "./GithubButton";
|
||||
import { GoogleButton } from "@/components/auth/GoogleButton";
|
||||
import IsPasswordValid from "@/components/auth/IsPasswordValid";
|
||||
|
||||
export const SignupForm = () => {
|
||||
const searchParams = useSearchParams();
|
||||
@@ -33,7 +33,7 @@ export const SignupForm = () => {
|
||||
searchParams?.get("inviteToken")
|
||||
);
|
||||
const url =
|
||||
process.env.NEXT_PUBLIC_EMAIL_VERIFICATION_DISABLED === "1"
|
||||
env.NEXT_PUBLIC_EMAIL_VERIFICATION_DISABLED === "1"
|
||||
? `/auth/signup-without-verification-success`
|
||||
: `/auth/verification-requested?email=${encodeURIComponent(e.target.elements.email.value)}`;
|
||||
|
||||
@@ -131,7 +131,7 @@ export const SignupForm = () => {
|
||||
className="focus:border-brand focus:ring-brand block w-full rounded-md shadow-sm sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
{process.env.NEXT_PUBLIC_PASSWORD_RESET_DISABLED !== "1" && isPasswordFocused && (
|
||||
{env.NEXT_PUBLIC_PASSWORD_RESET_DISABLED !== "1" && isPasswordFocused && (
|
||||
<div className="ml-1 text-right transition-all duration-500 ease-in-out">
|
||||
<Link
|
||||
href="/auth/forgot-password"
|
||||
@@ -163,36 +163,36 @@ export const SignupForm = () => {
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
{process.env.NEXT_PUBLIC_GOOGLE_AUTH_ENABLED === "1" && (
|
||||
{env.NEXT_PUBLIC_GOOGLE_AUTH_ENABLED === "1" && (
|
||||
<>
|
||||
<GoogleButton />
|
||||
</>
|
||||
)}
|
||||
{process.env.NEXT_PUBLIC_GITHUB_AUTH_ENABLED === "1" && (
|
||||
{env.NEXT_PUBLIC_GITHUB_AUTH_ENABLED === "1" && (
|
||||
<>
|
||||
<GithubButton />{" "}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{(process.env.NEXT_PUBLIC_TERMS_URL || process.env.NEXT_PUBLIC_PRIVACY_URL) && (
|
||||
{(env.NEXT_PUBLIC_TERMS_URL || env.NEXT_PUBLIC_PRIVACY_URL) && (
|
||||
<div className="mt-3 text-center text-xs text-slate-500">
|
||||
By signing up, you agree to our
|
||||
<br />
|
||||
{process.env.NEXT_PUBLIC_TERMS_URL && (
|
||||
{env.NEXT_PUBLIC_TERMS_URL && (
|
||||
<Link
|
||||
className="font-semibold"
|
||||
href="google.com" /* {process.env.NEXT_PUBLIC_TERMS_URL} */
|
||||
href="google.com" /* {env.NEXT_PUBLIC_TERMS_URL} */
|
||||
rel="noreferrer"
|
||||
target="_blank">
|
||||
Terms of Service
|
||||
</Link>
|
||||
)}
|
||||
{process.env.NEXT_PUBLIC_TERMS_URL && process.env.NEXT_PUBLIC_PRIVACY_URL && <span> and </span>}
|
||||
{process.env.NEXT_PUBLIC_PRIVACY_URL && (
|
||||
{env.NEXT_PUBLIC_TERMS_URL && env.NEXT_PUBLIC_PRIVACY_URL && <span> and </span>}
|
||||
{env.NEXT_PUBLIC_PRIVACY_URL && (
|
||||
<Link
|
||||
className="font-semibold"
|
||||
href="google.com" /* {/* process.env.NEXT_PUBLIC_PRIVACY_URL }*/
|
||||
href="google.com" /* {/* env.NEXT_PUBLIC_PRIVACY_URL }*/
|
||||
rel="noreferrer"
|
||||
target="_blank">
|
||||
Privacy Policy.
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { env } from "@/env.mjs";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function LegalFooter() {
|
||||
if (!process.env.NEXT_PUBLIC_IMPRINT_URL && !process.env.NEXT_PUBLIC_PRIVACY_URL) return null;
|
||||
if (!env.NEXT_PUBLIC_IMPRINT_URL && !env.NEXT_PUBLIC_PRIVACY_URL) return null;
|
||||
return (
|
||||
<div className="top-0 z-10 w-full border-b bg-white">
|
||||
<div className="mx-auto max-w-lg p-3 text-center text-sm text-slate-400">
|
||||
{process.env.NEXT_PUBLIC_IMPRINT_URL && (
|
||||
<Link href={process.env.NEXT_PUBLIC_IMPRINT_URL} target="_blank">
|
||||
{env.NEXT_PUBLIC_IMPRINT_URL && (
|
||||
<Link href={env.NEXT_PUBLIC_IMPRINT_URL} target="_blank">
|
||||
Imprint
|
||||
</Link>
|
||||
)}
|
||||
{process.env.NEXT_PUBLIC_IMPRINT_URL && process.env.NEXT_PUBLIC_PRIVACY_URL && <span> | </span>}
|
||||
{process.env.NEXT_PUBLIC_PRIVACY_URL && (
|
||||
<Link href={process.env.NEXT_PUBLIC_PRIVACY_URL} target="_blank">
|
||||
{env.NEXT_PUBLIC_IMPRINT_URL && env.NEXT_PUBLIC_PRIVACY_URL && <span> | </span>}
|
||||
{env.NEXT_PUBLIC_PRIVACY_URL && (
|
||||
<Link href={env.NEXT_PUBLIC_PRIVACY_URL} target="_blank">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
)}
|
||||
|
||||
110
apps/web/env.mjs
Normal file
110
apps/web/env.mjs
Normal file
@@ -0,0 +1,110 @@
|
||||
import { createEnv } from "@t3-oss/env-nextjs";
|
||||
import { z } from "zod";
|
||||
|
||||
export const env = createEnv({
|
||||
/*
|
||||
* Serverside Environment variables, not available on the client.
|
||||
* Will throw if you access these variables on the client.
|
||||
*/
|
||||
server: {
|
||||
DATABASE_URL: z.string().url(),
|
||||
PRISMA_GENERATE_DATAPROXY: z.enum(["1", "0", ""]).optional(),
|
||||
NEXTAUTH_SECRET: z.string().min(1),
|
||||
NEXTAUTH_URL: z.string().url().optional(),
|
||||
MAIL_FROM: z.string().email().optional(),
|
||||
SMTP_HOST: z.string().min(1).optional(),
|
||||
SMTP_PORT: z.string().min(1).optional(),
|
||||
SMTP_USER: z.string().min(1).optional(),
|
||||
SMTP_PASSWORD: z.string().min(1).optional(),
|
||||
SMTP_SECURE_ENABLED: z.enum(["1", "0"]).optional(),
|
||||
GITHUB_ID: z.string().optional(),
|
||||
GITHUB_SECRET: z.string().optional(),
|
||||
GOOGLE_CLIENT_ID: z.string().optional(),
|
||||
GOOGLE_CLIENT_SECRET: z.string().optional(),
|
||||
STRIPE_SECRET_KEY: z.string().optional(),
|
||||
STRIPE_WEBHOOK_SECRET: z.string().optional(),
|
||||
CRON_SECRET: z.string().optional(),
|
||||
NEXT_PUBLIC_POSTHOG_API_KEY: z.string().optional(),
|
||||
NEXT_PUBLIC_POSTHOG_API_HOST: z.string().optional(),
|
||||
},
|
||||
/*
|
||||
* Environment variables available on the client (and server).
|
||||
*
|
||||
* 💡 You'll get type errors if these are not prefixed with NEXT_PUBLIC_.
|
||||
*/
|
||||
client: {
|
||||
NEXT_PUBLIC_WEBAPP_URL: z.string().url().optional(),
|
||||
NEXT_PUBLIC_EMAIL_VERIFICATION_DISABLED: z.enum(["1", "0"]).optional(),
|
||||
NEXT_PUBLIC_PASSWORD_RESET_DISABLED: z.enum(["1", "0"]).optional(),
|
||||
NEXT_PUBLIC_SIGNUP_DISABLED: z.enum(["1", "0"]).optional(),
|
||||
NEXT_PUBLIC_INVITE_DISABLED: z.enum(["1", "0"]).optional(),
|
||||
NEXT_PUBLIC_PRIVACY_URL: z
|
||||
.string()
|
||||
.url()
|
||||
.optional()
|
||||
.or(z.string().refine((str) => str === "")),
|
||||
NEXT_PUBLIC_TERMS_URL: z
|
||||
.string()
|
||||
.url()
|
||||
.optional()
|
||||
.or(z.string().refine((str) => str === "")),
|
||||
NEXT_PUBLIC_IMPRINT_URL: z
|
||||
.string()
|
||||
.url()
|
||||
.optional()
|
||||
.or(z.string().refine((str) => str === "")),
|
||||
NEXT_PUBLIC_GITHUB_AUTH_ENABLED: z.enum(["1", "0"]).optional(),
|
||||
NEXT_PUBLIC_GOOGLE_AUTH_ENABLED: z.enum(["1", "0"]).optional(),
|
||||
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: z.string().optional(),
|
||||
NEXT_PUBLIC_FORMBRICKS_API_HOST: z
|
||||
.string()
|
||||
.url()
|
||||
.optional()
|
||||
.or(z.string().refine((str) => str === "")),
|
||||
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID: z.string().optional(),
|
||||
NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID: z.string().optional(),
|
||||
NEXT_PUBLIC_IS_FORMBRICKS_CLOUD: z.enum(["1", "0"]).optional(),
|
||||
},
|
||||
/*
|
||||
* Due to how Next.js bundles environment variables on Edge and Client,
|
||||
* we need to manually destructure them to make sure all are included in bundle.
|
||||
*
|
||||
* 💡 You'll get type errors if not all variables from `server` & `client` are included here.
|
||||
*/
|
||||
runtimeEnv: {
|
||||
DATABASE_URL: process.env.DATABASE_URL,
|
||||
PRISMA_GENERATE_DATAPROXY: process.env.PRISMA_GENERATE_DATAPROXY,
|
||||
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
|
||||
NEXTAUTH_URL: process.env.NEXTAUTH_URL,
|
||||
MAIL_FROM: process.env.MAIL_FROM,
|
||||
SMTP_HOST: process.env.SMTP_HOST,
|
||||
SMTP_PORT: process.env.SMTP_PORT,
|
||||
SMTP_USER: process.env.SMTP_USER,
|
||||
SMTP_PASSWORD: process.env.SMTP_PASSWORD,
|
||||
SMTP_SECURE_ENABLED: process.env.SMTP_SECURE_ENABLED,
|
||||
GITHUB_ID: process.env.GITHUB_ID,
|
||||
GITHUB_SECRET: process.env.GITHUB_SECRET,
|
||||
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
|
||||
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
|
||||
STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
|
||||
STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET,
|
||||
CRON_SECRET: process.env.CRON_SECRET,
|
||||
NEXT_PUBLIC_WEBAPP_URL: process.env.NEXT_PUBLIC_WEBAPP_URL,
|
||||
NEXT_PUBLIC_EMAIL_VERIFICATION_DISABLED: process.env.NEXT_PUBLIC_EMAIL_VERIFICATION_DISABLED,
|
||||
NEXT_PUBLIC_PASSWORD_RESET_DISABLED: process.env.NEXT_PUBLIC_PASSWORD_RESET_DISABLED,
|
||||
NEXT_PUBLIC_SIGNUP_DISABLED: process.env.NEXT_PUBLIC_SIGNUP_DISABLED,
|
||||
NEXT_PUBLIC_INVITE_DISABLED: process.env.NEXT_PUBLIC_INVITE_DISABLED,
|
||||
NEXT_PUBLIC_PRIVACY_URL: process.env.NEXT_PUBLIC_PRIVACY_URL,
|
||||
NEXT_PUBLIC_TERMS_URL: process.env.NEXT_PUBLIC_TERMS_URL,
|
||||
NEXT_PUBLIC_IMPRINT_URL: process.env.NEXT_PUBLIC_IMPRINT_URL,
|
||||
NEXT_PUBLIC_GITHUB_AUTH_ENABLED: process.env.NEXT_PUBLIC_GITHUB_AUTH_ENABLED,
|
||||
NEXT_PUBLIC_GOOGLE_AUTH_ENABLED: process.env.NEXT_PUBLIC_GOOGLE_AUTH_ENABLED,
|
||||
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY,
|
||||
NEXT_PUBLIC_FORMBRICKS_API_HOST: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST,
|
||||
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID: process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID,
|
||||
NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID: process.env.NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID,
|
||||
NEXT_PUBLIC_IS_FORMBRICKS_CLOUD: process.env.NEXT_PUBLIC_IS_FORMBRICKS_CLOUD,
|
||||
NEXT_PUBLIC_POSTHOG_API_KEY: process.env.NEXT_PUBLIC_POSTHOG_API_KEY,
|
||||
NEXT_PUBLIC_POSTHOG_API_HOST: process.env.NEXT_PUBLIC_POSTHOG_API_HOST,
|
||||
},
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
import { env } from "@/env.mjs";
|
||||
import { getQuestionResponseMapping } from "@/lib/responses/questionResponseMapping";
|
||||
import { WEBAPP_URL } from "@formbricks/lib/constants";
|
||||
import { Question } from "@formbricks/types/questions";
|
||||
@@ -18,18 +19,18 @@ interface sendEmailData {
|
||||
|
||||
export const sendEmail = async (emailData: sendEmailData) => {
|
||||
let transporter = nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST,
|
||||
port: process.env.SMTP_PORT,
|
||||
secure: process.env.SMTP_SECURE_ENABLED === "1", // true for 465, false for other ports
|
||||
host: env.SMTP_HOST,
|
||||
port: env.SMTP_PORT,
|
||||
secure: env.SMTP_SECURE_ENABLED === "1", // true for 465, false for other ports
|
||||
auth: {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASSWORD,
|
||||
user: env.SMTP_USER,
|
||||
pass: env.SMTP_PASSWORD,
|
||||
},
|
||||
// logger: true,
|
||||
// debug: true,
|
||||
});
|
||||
const emailDefaults = {
|
||||
from: `Formbricks <${process.env.MAIL_FROM || "noreply@formbricks.com"}>`,
|
||||
from: `Formbricks <${env.MAIL_FROM || "noreply@formbricks.com"}>`,
|
||||
};
|
||||
await transporter.sendMail({ ...emailDefaults, ...emailData });
|
||||
};
|
||||
@@ -128,7 +129,7 @@ export const sendResponseFinishedEmail = async (
|
||||
subject: personEmail
|
||||
? `${personEmail} just completed your ${survey.name} survey ✅`
|
||||
: `A response for ${survey.name} was completed ✅`,
|
||||
replyTo: personEmail || process.env.MAIL_FROM,
|
||||
replyTo: personEmail || env.MAIL_FROM,
|
||||
html: withEmailTemplate(`<h1>Survey completed</h1>Someone just completed your survey "${survey.name}"<br/>
|
||||
|
||||
<hr/>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import formbricks, { PersonId, SurveyId, ResponseId } from "@formbricks/js";
|
||||
import { env } from "@/env.mjs";
|
||||
|
||||
export const formbricksEnabled =
|
||||
typeof process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST && process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID;
|
||||
typeof env.NEXT_PUBLIC_FORMBRICKS_API_HOST && env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID;
|
||||
|
||||
export const createResponse = async (
|
||||
surveyId: SurveyId,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { env } from "@/env.mjs";
|
||||
|
||||
export function createToken(userId, userEmail, options = {}) {
|
||||
return jwt.sign({ id: userId }, process.env.NEXTAUTH_SECRET + userEmail, options);
|
||||
return jwt.sign({ id: userId }, env.NEXTAUTH_SECRET + userEmail, options);
|
||||
}
|
||||
|
||||
export async function verifyToken(token, userEmail = "") {
|
||||
@@ -20,11 +21,11 @@ export async function verifyToken(token, userEmail = "") {
|
||||
userEmail = foundUser.email;
|
||||
}
|
||||
|
||||
return jwt.verify(token, process.env.NEXTAUTH_SECRET + userEmail);
|
||||
return jwt.verify(token, env.NEXTAUTH_SECRET + userEmail);
|
||||
}
|
||||
|
||||
export const createInviteToken = (inviteId: string, email: string, options = {}) => {
|
||||
return jwt.sign({ inviteId, email }, process.env.NEXTAUTH_SECRET, options);
|
||||
return jwt.sign({ inviteId, email }, env.NEXTAUTH_SECRET, options);
|
||||
};
|
||||
|
||||
export const verifyInviteToken = async (token: string) => {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import "./env.mjs";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
|
||||
require("@next/env").loadEnvConfig("../../");
|
||||
|
||||
const { createId } = require("@paralleldrive/cuid2");
|
||||
|
||||
const nextConfig = {
|
||||
output: "standalone",
|
||||
experimental: {
|
||||
@@ -66,4 +65,4 @@ const nextConfig = {
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
export default nextConfig;
|
||||
@@ -21,10 +21,10 @@
|
||||
"@headlessui/react": "^1.7.15",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"@json2csv/node": "^7.0.1",
|
||||
"@next/env": "^13.4.8",
|
||||
"@paralleldrive/cuid2": "^2.2.1",
|
||||
"@radix-ui/react-collapsible": "^1.0.3",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.5",
|
||||
"@t3-oss/env-nextjs": "^0.6.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"eslint-config-next": "^13.4.8",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { env } from "@/env.mjs";
|
||||
import { getPlan, hasEnvironmentAccess } from "@/lib/api/apiHelper";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { RESPONSES_LIMIT_FREE } from "@formbricks/lib/constants";
|
||||
@@ -71,7 +72,7 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
},
|
||||
});
|
||||
|
||||
if (process.env.NEXT_PUBLIC_IS_FORMBRICKS_CLOUD === "1") {
|
||||
if (env.NEXT_PUBLIC_IS_FORMBRICKS_CLOUD === "1") {
|
||||
const plan = await getPlan(req, res);
|
||||
if (plan === "free" && responses.length > RESPONSES_LIMIT_FREE) {
|
||||
return res.json({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { env } from "@/env.mjs";
|
||||
import { getSessionUser, hasTeamAccess, isAdminOrOwner } from "@/lib/api/apiHelper";
|
||||
import { sendInviteMemberEmail } from "@/lib/email";
|
||||
import { prisma } from "@formbricks/database";
|
||||
@@ -20,7 +21,7 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
return res.status(403).json({ message: "Not authorized" });
|
||||
}
|
||||
|
||||
if (process.env.NEXT_PUBLIC_INVITE_DISABLED === "1") {
|
||||
if (env.NEXT_PUBLIC_INVITE_DISABLED === "1") {
|
||||
return res.status(403).json({ message: "Invite Disabled" });
|
||||
}
|
||||
|
||||
|
||||
35
pnpm-lock.yaml
generated
35
pnpm-lock.yaml
generated
@@ -1,5 +1,9 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@@ -162,9 +166,6 @@ importers:
|
||||
'@json2csv/node':
|
||||
specifier: ^7.0.1
|
||||
version: 7.0.1
|
||||
'@next/env':
|
||||
specifier: ^13.4.8
|
||||
version: 13.4.8
|
||||
'@paralleldrive/cuid2':
|
||||
specifier: ^2.2.1
|
||||
version: 2.2.1
|
||||
@@ -174,6 +175,9 @@ importers:
|
||||
'@radix-ui/react-dropdown-menu':
|
||||
specifier: ^2.0.5
|
||||
version: 2.0.5(react-dom@18.2.0)(react@18.2.0)
|
||||
'@t3-oss/env-nextjs':
|
||||
specifier: ^0.6.0
|
||||
version: 0.6.0(typescript@5.1.6)(zod@3.21.4)
|
||||
bcryptjs:
|
||||
specifier: ^2.4.3
|
||||
version: 2.4.3
|
||||
@@ -5149,6 +5153,27 @@ packages:
|
||||
dependencies:
|
||||
defer-to-connect: 1.1.3
|
||||
|
||||
/@t3-oss/env-core@0.6.0(typescript@5.1.6)(zod@3.21.4):
|
||||
resolution: {integrity: sha512-3FkPAba069WRZVVab/sB1m3eSGn/rZeypx5k+sWEu1d+k0OQdRDnvFS+7MtxYgqVrwaRk3b7yVnX2dgSPVmWPQ==}
|
||||
peerDependencies:
|
||||
typescript: '>=4.7.2'
|
||||
zod: ^3.0.0
|
||||
dependencies:
|
||||
typescript: 5.1.6
|
||||
zod: 3.21.4
|
||||
dev: false
|
||||
|
||||
/@t3-oss/env-nextjs@0.6.0(typescript@5.1.6)(zod@3.21.4):
|
||||
resolution: {integrity: sha512-SpzcGNIbUYcQw4zPPFeRJqCC1560zL7QmB0puIqOnuCsmykPkqHPX+n9CNZLXVQerboHzfvb7Kd+jAdouk72Vw==}
|
||||
peerDependencies:
|
||||
typescript: '>=4.7.2'
|
||||
zod: ^3.0.0
|
||||
dependencies:
|
||||
'@t3-oss/env-core': 0.6.0(typescript@5.1.6)(zod@3.21.4)
|
||||
typescript: 5.1.6
|
||||
zod: 3.21.4
|
||||
dev: false
|
||||
|
||||
/@tailwindcss/forms@0.5.3(tailwindcss@3.3.2):
|
||||
resolution: {integrity: sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==}
|
||||
peerDependencies:
|
||||
@@ -20726,7 +20751,3 @@ packages:
|
||||
/zwitch@2.0.4:
|
||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||
dev: false
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
@@ -56,6 +56,8 @@
|
||||
"RAILWAY_STATIC_URL",
|
||||
"RENDER_EXTERNAL_URL",
|
||||
"SENTRY_DSN",
|
||||
"STRIPE_SECRET_KEY",
|
||||
"STRIPE_WEBHOOK_SECRET",
|
||||
"TELEMETRY_DISABLED",
|
||||
"VERCEL_URL"
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user