mirror of
https://github.com/lukevella/rallly.git
synced 2025-07-29 06:07:25 +02:00
🔑 Add option to log in with google account (#997)
This commit is contained in:
parent
c185e73825
commit
1e4fe071aa
10 changed files with 217 additions and 70 deletions
|
@ -61,7 +61,8 @@
|
||||||
"next-seo": "^5.15.0",
|
"next-seo": "^5.15.0",
|
||||||
"php-serialize": "^4.1.1",
|
"php-serialize": "^4.1.1",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.31",
|
||||||
"posthog-js": "^1.57.2",
|
"posthog-js": "^1.102.1",
|
||||||
|
"posthog-node": "^3.6.0",
|
||||||
"react-big-calendar": "^1.8.1",
|
"react-big-calendar": "^1.8.1",
|
||||||
"react-hook-form": "^7.42.1",
|
"react-hook-form": "^7.42.1",
|
||||||
"react-hook-form-persist": "^3.0.0",
|
"react-hook-form-persist": "^3.0.0",
|
||||||
|
|
1
apps/web/public/static/google.svg
Normal file
1
apps/web/public/static/google.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/><path d="M1 1h22v22H1z" fill="none"/></svg>
|
After Width: | Height: | Size: 742 B |
|
@ -1,22 +1,25 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { Button } from "@rallly/ui/button";
|
import { Button } from "@rallly/ui/button";
|
||||||
import { LogInIcon, UserIcon } from "lucide-react";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import Link from "next/link";
|
import { UserIcon } from "lucide-react";
|
||||||
|
import Image from "next/image";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import { signIn, useSession } from "next-auth/react";
|
import { getProviders, signIn, useSession } from "next-auth/react";
|
||||||
import { usePostHog } from "posthog-js/react";
|
import { usePostHog } from "posthog-js/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { trpc } from "@/app/providers";
|
import { trpc } from "@/app/providers";
|
||||||
import { VerifyCode, verifyCode } from "@/components/auth/auth-forms";
|
import { VerifyCode, verifyCode } from "@/components/auth/auth-forms";
|
||||||
|
import { Spinner } from "@/components/spinner";
|
||||||
import { TextInput } from "@/components/text-input";
|
import { TextInput } from "@/components/text-input";
|
||||||
import { IfCloudHosted } from "@/contexts/environment";
|
|
||||||
import { isSelfHosted } from "@/utils/constants";
|
import { isSelfHosted } from "@/utils/constants";
|
||||||
import { validEmail } from "@/utils/form-validation";
|
import { validEmail } from "@/utils/form-validation";
|
||||||
|
|
||||||
export function LoginForm({ oidcConfig }: { oidcConfig?: { name: string } }) {
|
const allowGuestAccess = !isSelfHosted;
|
||||||
|
|
||||||
|
export function LoginForm() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { register, handleSubmit, getValues, formState, setError } = useForm<{
|
const { register, handleSubmit, getValues, formState, setError } = useForm<{
|
||||||
|
@ -25,6 +28,11 @@ export function LoginForm({ oidcConfig }: { oidcConfig?: { name: string } }) {
|
||||||
defaultValues: { email: "" },
|
defaultValues: { email: "" },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { data: providers } = useQuery(["providers"], getProviders, {
|
||||||
|
cacheTime: Infinity,
|
||||||
|
staleTime: Infinity,
|
||||||
|
});
|
||||||
|
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const queryClient = trpc.useUtils();
|
const queryClient = trpc.useUtils();
|
||||||
const [email, setEmail] = React.useState<string>();
|
const [email, setEmail] = React.useState<string>();
|
||||||
|
@ -32,9 +40,55 @@ export function LoginForm({ oidcConfig }: { oidcConfig?: { name: string } }) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const callbackUrl = (useSearchParams()?.get("callbackUrl") as string) ?? "/";
|
const callbackUrl = (useSearchParams()?.get("callbackUrl") as string) ?? "/";
|
||||||
|
|
||||||
const hasOIDCProvider = !!oidcConfig;
|
const alternativeLoginMethods = React.useMemo(() => {
|
||||||
const allowGuestAccess = !isSelfHosted;
|
const res: Array<{ login: () => void; icon: JSX.Element; name: string }> =
|
||||||
const hasAlternativeLoginMethods = hasOIDCProvider || allowGuestAccess;
|
[];
|
||||||
|
if (allowGuestAccess) {
|
||||||
|
res.push({
|
||||||
|
login: () => {
|
||||||
|
router.push(callbackUrl);
|
||||||
|
},
|
||||||
|
icon: <UserIcon className="text-muted-foreground size-5" />,
|
||||||
|
name: t("continueAsGuest"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (providers?.oidc) {
|
||||||
|
res.push({
|
||||||
|
login: () => {
|
||||||
|
signIn("oidc", {
|
||||||
|
callbackUrl,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: <UserIcon className="text-muted-foreground size-5" />,
|
||||||
|
name: t("loginWith", { provider: providers.oidc.name }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (providers?.google) {
|
||||||
|
res.push({
|
||||||
|
login: () => {
|
||||||
|
signIn("google", {
|
||||||
|
callbackUrl,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: (
|
||||||
|
<Image src="/static/google.svg" width={20} height={20} alt="Google" />
|
||||||
|
),
|
||||||
|
name: t("loginWith", { provider: providers.google.name }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}, [callbackUrl, providers, router, t]);
|
||||||
|
|
||||||
|
if (!providers) {
|
||||||
|
return (
|
||||||
|
<div className="flex h-72 items-center justify-center">
|
||||||
|
<Spinner className="text-muted-foreground" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const sendVerificationEmail = (email: string) => {
|
const sendVerificationEmail = (email: string) => {
|
||||||
return signIn("email", {
|
return signIn("email", {
|
||||||
redirect: false,
|
redirect: false,
|
||||||
|
@ -127,30 +181,16 @@ export function LoginForm({ oidcConfig }: { oidcConfig?: { name: string } }) {
|
||||||
>
|
>
|
||||||
{t("continue")}
|
{t("continue")}
|
||||||
</Button>
|
</Button>
|
||||||
{hasAlternativeLoginMethods ? (
|
{alternativeLoginMethods.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<hr className="border-grey-500 my-4 border-t" />
|
<hr className="border-grey-500 my-4 border-t" />
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
<IfCloudHosted>
|
{alternativeLoginMethods.map((method, i) => (
|
||||||
<Button size="lg" asChild>
|
<Button size="lg" key={i} onClick={method.login}>
|
||||||
<Link href={callbackUrl}>
|
{method.icon}
|
||||||
<UserIcon className="size-4" />
|
{method.name}
|
||||||
<Trans i18nKey="continueAsGuest" />
|
|
||||||
</Link>
|
|
||||||
</Button>
|
</Button>
|
||||||
</IfCloudHosted>
|
))}
|
||||||
{hasOIDCProvider ? (
|
|
||||||
<Button
|
|
||||||
icon={LogInIcon}
|
|
||||||
size="lg"
|
|
||||||
onClick={() => signIn("oidc")}
|
|
||||||
>
|
|
||||||
<Trans
|
|
||||||
i18nKey="loginWith"
|
|
||||||
values={{ provider: oidcConfig.name }}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -5,20 +5,13 @@ import { LoginForm } from "@/app/[locale]/(auth)/login/login-form";
|
||||||
import { Params } from "@/app/[locale]/types";
|
import { Params } from "@/app/[locale]/types";
|
||||||
import { getTranslation } from "@/app/i18n";
|
import { getTranslation } from "@/app/i18n";
|
||||||
import { AuthCard } from "@/components/auth/auth-layout";
|
import { AuthCard } from "@/components/auth/auth-layout";
|
||||||
import { isOIDCEnabled, oidcName } from "@/utils/constants";
|
|
||||||
|
|
||||||
// Self-hosted instances only have env vars for OIDC at runtime, so we need to
|
|
||||||
// use force-dynamic to avoid statically rendering this page during build time.
|
|
||||||
export const dynamic = "force-dynamic";
|
|
||||||
|
|
||||||
export default async function LoginPage({ params }: { params: Params }) {
|
export default async function LoginPage({ params }: { params: Params }) {
|
||||||
const { t } = await getTranslation(params.locale);
|
const { t } = await getTranslation(params.locale);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<AuthCard>
|
<AuthCard>
|
||||||
<LoginForm
|
<LoginForm />
|
||||||
oidcConfig={isOIDCEnabled ? { name: oidcName } : undefined}
|
|
||||||
/>
|
|
||||||
</AuthCard>
|
</AuthCard>
|
||||||
<div className="mt-4 pt-4 text-center text-gray-500 sm:text-base">
|
<div className="mt-4 pt-4 text-center text-gray-500 sm:text-base">
|
||||||
<Trans
|
<Trans
|
||||||
|
|
12
apps/web/src/app/posthog.ts
Normal file
12
apps/web/src/app/posthog.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { PostHog } from "posthog-node";
|
||||||
|
|
||||||
|
export function PostHogClient() {
|
||||||
|
if (!process.env.NEXT_PUBLIC_POSTHOG_API_KEY) return null;
|
||||||
|
|
||||||
|
const posthogClient = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_API_KEY, {
|
||||||
|
host: process.env.NEXT_PUBLIC_POSTHOG_API_HOST,
|
||||||
|
flushAt: 1,
|
||||||
|
flushInterval: 0,
|
||||||
|
});
|
||||||
|
return posthogClient;
|
||||||
|
}
|
|
@ -15,12 +15,13 @@ import NextAuth, {
|
||||||
} from "next-auth/next";
|
} from "next-auth/next";
|
||||||
import CredentialsProvider from "next-auth/providers/credentials";
|
import CredentialsProvider from "next-auth/providers/credentials";
|
||||||
import EmailProvider from "next-auth/providers/email";
|
import EmailProvider from "next-auth/providers/email";
|
||||||
|
import GoogleProvider from "next-auth/providers/google";
|
||||||
import { Provider } from "next-auth/providers/index";
|
import { Provider } from "next-auth/providers/index";
|
||||||
|
|
||||||
|
import { PostHogClient } from "@/app/posthog";
|
||||||
import { absoluteUrl } from "@/utils/absolute-url";
|
import { absoluteUrl } from "@/utils/absolute-url";
|
||||||
import { CustomPrismaAdapter } from "@/utils/auth/custom-prisma-adapter";
|
import { CustomPrismaAdapter } from "@/utils/auth/custom-prisma-adapter";
|
||||||
import { mergeGuestsIntoUser } from "@/utils/auth/merge-user";
|
import { mergeGuestsIntoUser } from "@/utils/auth/merge-user";
|
||||||
import { isOIDCEnabled, oidcName } from "@/utils/constants";
|
|
||||||
import { emailClient } from "@/utils/emails";
|
import { emailClient } from "@/utils/emails";
|
||||||
|
|
||||||
const providers: Provider[] = [
|
const providers: Provider[] = [
|
||||||
|
@ -109,10 +110,14 @@ const providers: Provider[] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
// If we have an OAuth provider configured, we add it to the list of providers
|
// If we have an OAuth provider configured, we add it to the list of providers
|
||||||
if (isOIDCEnabled) {
|
if (
|
||||||
|
process.env.OIDC_DISCOVERY_URL &&
|
||||||
|
process.env.OIDC_CLIENT_ID &&
|
||||||
|
process.env.OIDC_CLIENT_SECRET
|
||||||
|
) {
|
||||||
providers.push({
|
providers.push({
|
||||||
id: "oidc",
|
id: "oidc",
|
||||||
name: oidcName,
|
name: process.env.OIDC_NAME ?? "OpenID Connect",
|
||||||
type: "oauth",
|
type: "oauth",
|
||||||
wellKnown: process.env.OIDC_DISCOVERY_URL,
|
wellKnown: process.env.OIDC_DISCOVERY_URL,
|
||||||
authorization: { params: { scope: "openid email profile" } },
|
authorization: { params: { scope: "openid email profile" } },
|
||||||
|
@ -131,6 +136,16 @@ if (isOIDCEnabled) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
|
||||||
|
providers.push(
|
||||||
|
GoogleProvider({
|
||||||
|
clientId: process.env.GOOGLE_CLIENT_ID,
|
||||||
|
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||||
|
allowDangerousEmailAccountLinking: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const getAuthOptions = (...args: GetServerSessionParams) =>
|
const getAuthOptions = (...args: GetServerSessionParams) =>
|
||||||
({
|
({
|
||||||
adapter: CustomPrismaAdapter(prisma),
|
adapter: CustomPrismaAdapter(prisma),
|
||||||
|
@ -145,7 +160,24 @@ const getAuthOptions = (...args: GetServerSessionParams) =>
|
||||||
error: "/auth/error",
|
error: "/auth/error",
|
||||||
},
|
},
|
||||||
callbacks: {
|
callbacks: {
|
||||||
async signIn({ user, email }) {
|
async signIn({ user, email, account, profile }) {
|
||||||
|
const posthog = PostHogClient();
|
||||||
|
// prevent sign in if email is not verified
|
||||||
|
if (
|
||||||
|
profile &&
|
||||||
|
"email_verified" in profile &&
|
||||||
|
profile.email_verified === false
|
||||||
|
) {
|
||||||
|
posthog?.capture({
|
||||||
|
distinctId: user.id,
|
||||||
|
event: "login failed",
|
||||||
|
properties: {
|
||||||
|
reason: "email not verified",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await posthog?.shutdownAsync();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// Make sure email is allowed
|
// Make sure email is allowed
|
||||||
if (user.email) {
|
if (user.email) {
|
||||||
const isBlocked = isEmailBlocked(user.email);
|
const isBlocked = isEmailBlocked(user.email);
|
||||||
|
@ -175,6 +207,15 @@ const getAuthOptions = (...args: GetServerSessionParams) =>
|
||||||
if (session && session.user.email === null) {
|
if (session && session.user.email === null) {
|
||||||
await mergeGuestsIntoUser(user.id, [session.user.id]);
|
await mergeGuestsIntoUser(user.id, [session.user.id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
posthog?.capture({
|
||||||
|
distinctId: user.id,
|
||||||
|
event: "login",
|
||||||
|
properties: {
|
||||||
|
method: account?.provider,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await posthog?.shutdownAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -12,11 +12,3 @@ export const monthlyPriceUsd = 7;
|
||||||
|
|
||||||
export const annualPriceUsd = 42;
|
export const annualPriceUsd = 42;
|
||||||
export const appVersion = process.env.NEXT_PUBLIC_APP_VERSION;
|
export const appVersion = process.env.NEXT_PUBLIC_APP_VERSION;
|
||||||
|
|
||||||
export const isOIDCEnabled = Boolean(
|
|
||||||
process.env.OIDC_DISCOVERY_URL &&
|
|
||||||
process.env.OIDC_CLIENT_ID &&
|
|
||||||
process.env.OIDC_CLIENT_SECRET,
|
|
||||||
);
|
|
||||||
|
|
||||||
export const oidcName = process.env.OIDC_NAME ?? "OpenID Connect";
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ test.describe.serial(() => {
|
||||||
.getByPlaceholder("jessie.smith@example.com")
|
.getByPlaceholder("jessie.smith@example.com")
|
||||||
.type(testUserEmail);
|
.type(testUserEmail);
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Continue" }).click();
|
await page.getByRole("button", { name: "Continue", exact: true }).click();
|
||||||
|
|
||||||
// Make sure the user doesn't exist yet and that logging in is not possible
|
// Make sure the user doesn't exist yet and that logging in is not possible
|
||||||
await expect(
|
await expect(
|
||||||
|
@ -64,7 +64,7 @@ test.describe.serial(() => {
|
||||||
.getByPlaceholder("jessie.smith@example.com")
|
.getByPlaceholder("jessie.smith@example.com")
|
||||||
.type(testUserEmail);
|
.type(testUserEmail);
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Continue" }).click();
|
await page.getByRole("button", { name: "Continue", exact: true }).click();
|
||||||
|
|
||||||
const codeInput = page.getByPlaceholder("Enter your 6-digit code");
|
const codeInput = page.getByPlaceholder("Enter your 6-digit code");
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ test.describe.serial(() => {
|
||||||
|
|
||||||
await codeInput.type(code);
|
await codeInput.type(code);
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Continue" }).click();
|
await page.getByRole("button", { name: "Continue", exact: true }).click();
|
||||||
|
|
||||||
await page.waitForURL("/polls");
|
await page.waitForURL("/polls");
|
||||||
});
|
});
|
||||||
|
@ -89,7 +89,7 @@ test.describe.serial(() => {
|
||||||
.getByPlaceholder("jessie.smith@example.com")
|
.getByPlaceholder("jessie.smith@example.com")
|
||||||
.type(testUserEmail);
|
.type(testUserEmail);
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Continue" }).click();
|
await page.getByRole("button", { name: "Continue", exact: true }).click();
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByText("A user with that email already exists"),
|
page.getByText("A user with that email already exists"),
|
||||||
|
@ -103,7 +103,7 @@ test.describe.serial(() => {
|
||||||
.getByPlaceholder("jessie.smith@example.com")
|
.getByPlaceholder("jessie.smith@example.com")
|
||||||
.type(testUserEmail);
|
.type(testUserEmail);
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Continue" }).click();
|
await page.getByRole("button", { name: "Continue", exact: true }).click();
|
||||||
|
|
||||||
const { email } = await mailServer.captureOne(testUserEmail, {
|
const { email } = await mailServer.captureOne(testUserEmail, {
|
||||||
wait: 5000,
|
wait: 5000,
|
||||||
|
@ -119,7 +119,7 @@ test.describe.serial(() => {
|
||||||
|
|
||||||
await page.goto(magicLink);
|
await page.goto(magicLink);
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Continue" }).click();
|
await page.getByRole("button", { name: "Continue", exact: true }).click();
|
||||||
|
|
||||||
await page.waitForURL("/polls");
|
await page.waitForURL("/polls");
|
||||||
|
|
||||||
|
@ -133,13 +133,13 @@ test.describe.serial(() => {
|
||||||
.getByPlaceholder("jessie.smith@example.com")
|
.getByPlaceholder("jessie.smith@example.com")
|
||||||
.type(testUserEmail);
|
.type(testUserEmail);
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Continue" }).click();
|
await page.getByRole("button", { name: "Continue", exact: true }).click();
|
||||||
|
|
||||||
const code = await getCode();
|
const code = await getCode();
|
||||||
|
|
||||||
await page.getByPlaceholder("Enter your 6-digit code").type(code);
|
await page.getByPlaceholder("Enter your 6-digit code").type(code);
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Continue" }).click();
|
await page.getByRole("button", { name: "Continue", exact: true }).click();
|
||||||
|
|
||||||
await page.waitForURL("/polls");
|
await page.waitForURL("/polls");
|
||||||
|
|
||||||
|
@ -153,13 +153,13 @@ test.describe.serial(() => {
|
||||||
.getByPlaceholder("jessie.smith@example.com")
|
.getByPlaceholder("jessie.smith@example.com")
|
||||||
.type("Test@example.com");
|
.type("Test@example.com");
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Continue" }).click();
|
await page.getByRole("button", { name: "Continue", exact: true }).click();
|
||||||
|
|
||||||
const code = await getCode();
|
const code = await getCode();
|
||||||
|
|
||||||
await page.getByPlaceholder("Enter your 6-digit code").type(code);
|
await page.getByPlaceholder("Enter your 6-digit code").type(code);
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Continue" }).click();
|
await page.getByRole("button", { name: "Continue", exact: true }).click();
|
||||||
|
|
||||||
await page.waitForURL("/polls");
|
await page.waitForURL("/polls");
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,8 @@
|
||||||
"OIDC_DISCOVERY_URL",
|
"OIDC_DISCOVERY_URL",
|
||||||
"OIDC_CLIENT_ID",
|
"OIDC_CLIENT_ID",
|
||||||
"OIDC_CLIENT_SECRET",
|
"OIDC_CLIENT_SECRET",
|
||||||
|
"GOOGLE_CLIENT_ID",
|
||||||
|
"GOOGLE_CLIENT_SECRET",
|
||||||
"PADDLE_PUBLIC_KEY",
|
"PADDLE_PUBLIC_KEY",
|
||||||
"PORT",
|
"PORT",
|
||||||
"SECRET_PASSWORD",
|
"SECRET_PASSWORD",
|
||||||
|
|
85
yarn.lock
85
yarn.lock
|
@ -4725,6 +4725,11 @@ asynciterator.prototype@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
has-symbols "^1.0.3"
|
has-symbols "^1.0.3"
|
||||||
|
|
||||||
|
asynckit@^0.4.0:
|
||||||
|
version "0.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||||
|
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||||
|
|
||||||
autoprefixer@^10.4.13:
|
autoprefixer@^10.4.13:
|
||||||
version "10.4.13"
|
version "10.4.13"
|
||||||
resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz"
|
resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz"
|
||||||
|
@ -4754,6 +4759,15 @@ axios@^0.25.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects "^1.14.7"
|
follow-redirects "^1.14.7"
|
||||||
|
|
||||||
|
axios@^1.6.2:
|
||||||
|
version "1.6.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7"
|
||||||
|
integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.15.4"
|
||||||
|
form-data "^4.0.0"
|
||||||
|
proxy-from-env "^1.1.0"
|
||||||
|
|
||||||
axobject-query@^3.1.1:
|
axobject-query@^3.1.1:
|
||||||
version "3.2.1"
|
version "3.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a"
|
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a"
|
||||||
|
@ -5178,6 +5192,13 @@ color-name@~1.1.4:
|
||||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
|
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
|
||||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||||
|
|
||||||
|
combined-stream@^1.0.8:
|
||||||
|
version "1.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||||
|
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||||
|
dependencies:
|
||||||
|
delayed-stream "~1.0.0"
|
||||||
|
|
||||||
comma-separated-tokens@^2.0.0:
|
comma-separated-tokens@^2.0.0:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz"
|
resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz"
|
||||||
|
@ -5500,6 +5521,11 @@ define-properties@^1.2.0, define-properties@^1.2.1:
|
||||||
has-property-descriptors "^1.0.0"
|
has-property-descriptors "^1.0.0"
|
||||||
object-keys "^1.1.1"
|
object-keys "^1.1.1"
|
||||||
|
|
||||||
|
delayed-stream@~1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||||
|
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
||||||
|
|
||||||
depd@~1.1.2:
|
depd@~1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz"
|
resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz"
|
||||||
|
@ -6504,6 +6530,11 @@ follow-redirects@^1.14.7:
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf"
|
||||||
integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==
|
integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==
|
||||||
|
|
||||||
|
follow-redirects@^1.15.4:
|
||||||
|
version "1.15.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020"
|
||||||
|
integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==
|
||||||
|
|
||||||
for-each@^0.3.3:
|
for-each@^0.3.3:
|
||||||
version "0.3.3"
|
version "0.3.3"
|
||||||
resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz"
|
resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz"
|
||||||
|
@ -6528,6 +6559,15 @@ for-own@^0.1.1, for-own@^0.1.2, for-own@^0.1.3, for-own@^0.1.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
for-in "^1.0.1"
|
for-in "^1.0.1"
|
||||||
|
|
||||||
|
form-data@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||||
|
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||||
|
dependencies:
|
||||||
|
asynckit "^0.4.0"
|
||||||
|
combined-stream "^1.0.8"
|
||||||
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
fraction.js@^4.2.0:
|
fraction.js@^4.2.0:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz"
|
resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz"
|
||||||
|
@ -8499,6 +8539,18 @@ micromatch@^4.0.4, micromatch@^4.0.5:
|
||||||
braces "^3.0.2"
|
braces "^3.0.2"
|
||||||
picomatch "^2.3.1"
|
picomatch "^2.3.1"
|
||||||
|
|
||||||
|
mime-db@1.52.0:
|
||||||
|
version "1.52.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||||
|
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||||
|
|
||||||
|
mime-types@^2.1.12:
|
||||||
|
version "2.1.35"
|
||||||
|
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||||
|
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||||
|
dependencies:
|
||||||
|
mime-db "1.52.0"
|
||||||
|
|
||||||
mimic-fn@^2.1.0:
|
mimic-fn@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz"
|
resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz"
|
||||||
|
@ -9295,13 +9347,21 @@ postcss@8.4.31, postcss@^8.4.23, postcss@^8.4.31:
|
||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
posthog-js@^1.57.2:
|
posthog-js@^1.102.1:
|
||||||
version "1.57.2"
|
version "1.102.1"
|
||||||
resolved "https://registry.npmjs.org/posthog-js/-/posthog-js-1.57.2.tgz"
|
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.102.1.tgz#ac9e97703f4bba61785b1d1fa7f699516e97c6fb"
|
||||||
integrity sha512-ER4gkYZasrd2Zwmt/yLeZ5G/nZJ6tpaYBCpx3CvocDx+3F16WdawJlYMT0IyLKHXDniC5+AsjzFd6fi8uyYlJA==
|
integrity sha512-vHkLtnjDce8qxoKX9K4HOWEvCW/xPTEzHDBJIPrhjWCfXLPa5NePEeSiQjr64BV4DFMBbvyNtUUPVstZoIqQcw==
|
||||||
dependencies:
|
dependencies:
|
||||||
fflate "^0.4.1"
|
fflate "^0.4.1"
|
||||||
rrweb-snapshot "^1.1.14"
|
preact "^10.19.3"
|
||||||
|
|
||||||
|
posthog-node@^3.6.0:
|
||||||
|
version "3.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-3.6.0.tgz#dab0bd2d1c974b0f11052115e8cd0f3e61f7db30"
|
||||||
|
integrity sha512-N/4//SIQR4fhwbHnDdJ2rQCYdu9wo0EVPK4lVgZswp5R/E42RKlpuO6ZfPsBl+Bcg06OYiOd/WR/jLV90FCoSw==
|
||||||
|
dependencies:
|
||||||
|
axios "^1.6.2"
|
||||||
|
rusha "^0.8.14"
|
||||||
|
|
||||||
preact-render-to-string@5.2.3:
|
preact-render-to-string@5.2.3:
|
||||||
version "5.2.3"
|
version "5.2.3"
|
||||||
|
@ -9322,6 +9382,11 @@ preact@10.11.3:
|
||||||
resolved "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz"
|
resolved "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz"
|
||||||
integrity sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==
|
integrity sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==
|
||||||
|
|
||||||
|
preact@^10.19.3:
|
||||||
|
version "10.19.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/preact/-/preact-10.19.3.tgz#7a7107ed2598a60676c943709ea3efb8aaafa899"
|
||||||
|
integrity sha512-nHHTeFVBTHRGxJXKkKu5hT8C/YWBkPso4/Gad6xuj5dbptt9iF9NZr9pHbPhBrnT2klheu7mHTxTZ/LjwJiEiQ==
|
||||||
|
|
||||||
preact@^10.6.3:
|
preact@^10.6.3:
|
||||||
version "10.18.1"
|
version "10.18.1"
|
||||||
resolved "https://registry.npmjs.org/preact/-/preact-10.18.1.tgz"
|
resolved "https://registry.npmjs.org/preact/-/preact-10.18.1.tgz"
|
||||||
|
@ -10134,11 +10199,6 @@ rollup@2.78.0:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
rrweb-snapshot@^1.1.14:
|
|
||||||
version "1.1.14"
|
|
||||||
resolved "https://registry.npmjs.org/rrweb-snapshot/-/rrweb-snapshot-1.1.14.tgz"
|
|
||||||
integrity sha512-eP5pirNjP5+GewQfcOQY4uBiDnpqxNRc65yKPW0eSoU1XamDfc4M8oqpXGMyUyvLyxFDB0q0+DChuxxiU2FXBQ==
|
|
||||||
|
|
||||||
rtl-css-js@^1.14.0:
|
rtl-css-js@^1.14.0:
|
||||||
version "1.16.1"
|
version "1.16.1"
|
||||||
resolved "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz"
|
resolved "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz"
|
||||||
|
@ -10160,6 +10220,11 @@ run-parallel@^1.1.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
queue-microtask "^1.2.2"
|
queue-microtask "^1.2.2"
|
||||||
|
|
||||||
|
rusha@^0.8.14:
|
||||||
|
version "0.8.14"
|
||||||
|
resolved "https://registry.yarnpkg.com/rusha/-/rusha-0.8.14.tgz#a977d0de9428406138b7bb90d3de5dcd024e2f68"
|
||||||
|
integrity sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA==
|
||||||
|
|
||||||
rxjs@^7.5.4:
|
rxjs@^7.5.4:
|
||||||
version "7.8.0"
|
version "7.8.0"
|
||||||
resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz"
|
resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue