diff --git a/.gitignore b/.gitignore index 3f7cb9d5..519457e6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ docs .DS_Store .vscode .env -.claude/settings.local.json +.claude/ .pnp.* .yarn/* !.yarn/patches diff --git a/next/.env.example b/next/.env.example index 337ec3ce..0523d9df 100644 --- a/next/.env.example +++ b/next/.env.example @@ -3,4 +3,7 @@ ENVIRONMENT=development PORT=3000 NEXT_PUBLIC_API_URL=http://localhost:1337 -PREVIEW_SECRET=preview_secret \ No newline at end of file +PREVIEW_SECRET=preview_secret + +# Must match BETTER_AUTH_SECRET in strapi/.env +BETTER_AUTH_SECRET=tobemodified \ No newline at end of file diff --git a/next/app/[locale]/sign-in/page.tsx b/next/app/[locale]/sign-in/page.tsx new file mode 100644 index 00000000..00460c04 --- /dev/null +++ b/next/app/[locale]/sign-in/page.tsx @@ -0,0 +1,11 @@ +import { AmbientColor } from '@/components/decorations/ambient-color'; +import { SignInForm } from '@/components/sign-in-form'; + +export default function SignInPage() { + return ( +
+ + +
+ ); +} diff --git a/next/components/navbar/desktop-navbar.tsx b/next/components/navbar/desktop-navbar.tsx index fe905a04..31efeab8 100644 --- a/next/components/navbar/desktop-navbar.tsx +++ b/next/components/navbar/desktop-navbar.tsx @@ -11,8 +11,10 @@ import { useState } from 'react'; import { LocaleSwitcher } from '../locale-switcher'; import { NavbarItem } from './navbar-item'; +import { UserMenu } from './user-menu'; import { Button } from '@/components/elements/button'; import { Logo } from '@/components/logo'; +import { useSession } from '@/lib/auth-client'; import { cn } from '@/lib/utils'; type Props = { @@ -37,6 +39,7 @@ export const DesktopNavbar = ({ locale, }: Props) => { const { scrollY } = useScroll(); + const { data: session } = useSession(); const [showBackground, setShowBackground] = useState(false); @@ -90,18 +93,22 @@ export const DesktopNavbar = ({
- {rightNavbarItems.map((item, index) => ( - - ))} + {session?.user ? ( + + ) : ( + rightNavbarItems.map((item, index) => ( + + )) + )}
); diff --git a/next/components/navbar/mobile-navbar.tsx b/next/components/navbar/mobile-navbar.tsx index 27531592..70732718 100644 --- a/next/components/navbar/mobile-navbar.tsx +++ b/next/components/navbar/mobile-navbar.tsx @@ -3,12 +3,13 @@ import { useMotionValueEvent, useScroll } from 'framer-motion'; import { Link } from 'next-view-transitions'; import { useState } from 'react'; -import { IoIosMenu } from 'react-icons/io'; -import { IoIosClose } from 'react-icons/io'; +import { IoIosMenu, IoIosClose } from 'react-icons/io'; import { LocaleSwitcher } from '../locale-switcher'; +import { UserMenu } from './user-menu'; import { Button } from '@/components/elements/button'; import { Logo } from '@/components/logo'; +import { useSession } from '@/lib/auth-client'; import { cn } from '@/lib/utils'; type Props = { @@ -33,6 +34,7 @@ export const MobileNavbar = ({ locale, }: Props) => { const [open, setOpen] = useState(false); + const { data: session } = useSession(); const { scrollY } = useScroll(); @@ -109,18 +111,22 @@ export const MobileNavbar = ({ ))}
- {rightNavbarItems.map((item, index) => ( - - ))} + {session?.user ? ( + + ) : ( + rightNavbarItems.map((item, index) => ( + + )) + )}
)} diff --git a/next/components/navbar/user-menu.tsx b/next/components/navbar/user-menu.tsx new file mode 100644 index 00000000..5b608023 --- /dev/null +++ b/next/components/navbar/user-menu.tsx @@ -0,0 +1,42 @@ +'use client'; + +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; + +import { Button } from '@/components/elements/button'; +import { signOut, useSession } from '@/lib/auth-client'; + +export const UserMenu = ({ locale }: { locale: string }) => { + const router = useRouter(); + const { data: session, isPending } = useSession(); + const [isSigningOut, setIsSigningOut] = useState(false); + + if (isPending) return null; + + if (!session?.user) return null; + + const displayName = session.user.name || session.user.email; + + async function handleSignOut() { + setIsSigningOut(true); + await signOut(); + setIsSigningOut(false); + router.push(`/${locale}`); + router.refresh(); + } + + return ( +
+ + Hi {displayName} + + +
+ ); +}; diff --git a/next/components/register.tsx b/next/components/register.tsx index ec1d3585..b55935f5 100644 --- a/next/components/register.tsx +++ b/next/components/register.tsx @@ -4,13 +4,58 @@ import { IconBrandGithubFilled, IconBrandGoogleFilled, } from '@tabler/icons-react'; -import React from 'react'; +import { Link } from 'next-view-transitions'; +import { useParams, useRouter } from 'next/navigation'; +import React, { useState } from 'react'; import { Container } from './container'; import { Button } from './elements/button'; import { Logo } from './logo'; +import { signIn, signUp } from '@/lib/auth-client'; export const Register = () => { + const router = useRouter(); + const params = useParams<{ locale: string }>(); + const locale = params?.locale ?? 'en'; + const [name, setName] = useState(''); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(null); + const [isSubmitting, setIsSubmitting] = useState(false); + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + setError(null); + setIsSubmitting(true); + + const { error: signUpError } = await signUp.email({ + email, + password, + name: name || email, + }); + + setIsSubmitting(false); + + if (signUpError) { + setError(signUpError.message ?? 'Sign up failed'); + return; + } + + router.push('/'); + router.refresh(); + } + + async function handleSocial(provider: 'github' | 'google') { + setError(null); + const { error: socialError } = await signIn.social({ + provider, + callbackURL: `/${locale}`, + }); + if (socialError) { + setError(socialError.message ?? `${provider} sign-in failed`); + } + } + return ( @@ -18,30 +63,70 @@ export const Register = () => { Sign up for LaunchPad -
+ + setName(e.target.value)} + className="h-10 pl-4 w-full mb-4 rounded-md text-sm bg-charcoal border border-neutral-800 text-white placeholder-neutral-500 outline-none focus:outline-none active:outline-none focus:ring-2 focus:ring-neutral-800" + /> setEmail(e.target.value)} + required className="h-10 pl-4 w-full mb-4 rounded-md text-sm bg-charcoal border border-neutral-800 text-white placeholder-neutral-500 outline-none focus:outline-none active:outline-none focus:ring-2 focus:ring-neutral-800" /> setPassword(e.target.value)} + required + minLength={8} className="h-10 pl-4 w-full mb-4 rounded-md text-sm bg-charcoal border border-neutral-800 text-white placeholder-neutral-500 outline-none focus:outline-none active:outline-none focus:ring-2 focus:ring-neutral-800" /> -
+

+ Already have an account?{' '} + + Sign in + +

+
- - diff --git a/next/components/sign-in-form.tsx b/next/components/sign-in-form.tsx new file mode 100644 index 00000000..e89e7683 --- /dev/null +++ b/next/components/sign-in-form.tsx @@ -0,0 +1,133 @@ +'use client'; + +import { + IconBrandGithubFilled, + IconBrandGoogleFilled, +} from '@tabler/icons-react'; +import { Link } from 'next-view-transitions'; +import { useParams, useRouter } from 'next/navigation'; +import React, { useState } from 'react'; + +import { Container } from './container'; +import { Button } from './elements/button'; +import { Logo } from './logo'; +import { signIn } from '@/lib/auth-client'; + +export const SignInForm = () => { + const router = useRouter(); + const params = useParams<{ locale: string }>(); + const locale = params?.locale ?? 'en'; + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(null); + const [isSubmitting, setIsSubmitting] = useState(false); + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + setError(null); + setIsSubmitting(true); + + const { error: signInError } = await signIn.email({ email, password }); + + setIsSubmitting(false); + + if (signInError) { + setError(signInError.message ?? 'Sign in failed'); + return; + } + + router.push('/'); + router.refresh(); + } + + async function handleSocial(provider: 'github' | 'google') { + setError(null); + const { error: socialError } = await signIn.social({ + provider, + callbackURL: `/${locale}`, + }); + if (socialError) { + setError(socialError.message ?? `${provider} sign-in failed`); + } + } + + return ( + + +

+ Sign in to LaunchPad +

+ +
+ setEmail(e.target.value)} + required + className="h-10 pl-4 w-full mb-4 rounded-md text-sm bg-charcoal border border-neutral-800 text-white placeholder-neutral-500 outline-none focus:outline-none active:outline-none focus:ring-2 focus:ring-neutral-800" + /> + setPassword(e.target.value)} + required + className="h-10 pl-4 w-full mb-4 rounded-md text-sm bg-charcoal border border-neutral-800 text-white placeholder-neutral-500 outline-none focus:outline-none active:outline-none focus:ring-2 focus:ring-neutral-800" + /> + {error &&

{error}

} + +
+ +

+ Don't have an account?{' '} + + Sign up + +

+ + + +
+ + +
+
+ ); +}; + +const Divider = () => { + return ( +
+
+
+
+ OR +
+
+ ); +}; diff --git a/next/lib/auth-client.ts b/next/lib/auth-client.ts new file mode 100644 index 00000000..2e1041cb --- /dev/null +++ b/next/lib/auth-client.ts @@ -0,0 +1,9 @@ +import { createAuthClient } from 'better-auth/react'; + +import { API_URL } from './utils'; + +export const authClient = createAuthClient({ + baseURL: `${API_URL}/api/better-auth`, +}); + +export const { signIn, signUp, signOut, useSession } = authClient; diff --git a/next/package.json b/next/package.json index d97fb1dd..0afe6a82 100644 --- a/next/package.json +++ b/next/package.json @@ -24,6 +24,7 @@ "@tsparticles/react": "^3.0.0", "@tsparticles/slim": "^3.5.0", "@types/fuzzy-search": "^2.1.5", + "better-auth": "^1.6.5", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "date-fns": "^3.6.0", diff --git a/next/yarn.lock b/next/yarn.lock index 400bf3c2..bb0f36ca 100644 --- a/next/yarn.lock +++ b/next/yarn.lock @@ -194,6 +194,125 @@ __metadata: languageName: node linkType: hard +"@better-auth/core@npm:1.6.5": + version: 1.6.5 + resolution: "@better-auth/core@npm:1.6.5" + dependencies: + "@opentelemetry/semantic-conventions": "npm:^1.39.0" + "@standard-schema/spec": "npm:^1.1.0" + zod: "npm:^4.3.6" + peerDependencies: + "@better-auth/utils": 0.4.0 + "@better-fetch/fetch": 1.1.21 + "@cloudflare/workers-types": ">=4" + "@opentelemetry/api": ^1.9.0 + better-call: 1.3.5 + jose: ^6.1.0 + kysely: ^0.28.5 + nanostores: ^1.0.1 + peerDependenciesMeta: + "@cloudflare/workers-types": + optional: true + checksum: 10c0/b45f99a1411d4bb4b5be5d8d310b934c2ce0974331f7bb07a9a84081255367817fa8ee7c05fdae1815405a03489c07ddecdcc066e2403c4127264bdc828fd425 + languageName: node + linkType: hard + +"@better-auth/drizzle-adapter@npm:1.6.5": + version: 1.6.5 + resolution: "@better-auth/drizzle-adapter@npm:1.6.5" + peerDependencies: + "@better-auth/core": ^1.6.5 + "@better-auth/utils": 0.4.0 + drizzle-orm: ^0.45.2 + peerDependenciesMeta: + drizzle-orm: + optional: true + checksum: 10c0/018a167f18356da3ad446e73cd093f3fedaf30b33e790a9796271c1d57fdb2abfda80a783a664b9cd1b8531dc60cbf7dc23e64680fba8d9beaf0659fc8802084 + languageName: node + linkType: hard + +"@better-auth/kysely-adapter@npm:1.6.5": + version: 1.6.5 + resolution: "@better-auth/kysely-adapter@npm:1.6.5" + peerDependencies: + "@better-auth/core": ^1.6.5 + "@better-auth/utils": 0.4.0 + kysely: ^0.28.14 + peerDependenciesMeta: + kysely: + optional: true + checksum: 10c0/c607aab603ce911e066f44cbc980eb319327613c6d2a9767c7247c3fef4063e0ff044c09abb1575739b4393479c5fea2f2bca6e0f6d542ffd195be1fd2998621 + languageName: node + linkType: hard + +"@better-auth/memory-adapter@npm:1.6.5": + version: 1.6.5 + resolution: "@better-auth/memory-adapter@npm:1.6.5" + peerDependencies: + "@better-auth/core": ^1.6.5 + "@better-auth/utils": 0.4.0 + checksum: 10c0/794263419403800eb22a169a83f368b8abb379db8b544d38bb23733a3e1a165c91aef4116211252bf74e62ce77ee70f721c53fef44ae2968b0f846cec7b13feb + languageName: node + linkType: hard + +"@better-auth/mongo-adapter@npm:1.6.5": + version: 1.6.5 + resolution: "@better-auth/mongo-adapter@npm:1.6.5" + peerDependencies: + "@better-auth/core": ^1.6.5 + "@better-auth/utils": 0.4.0 + mongodb: ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + mongodb: + optional: true + checksum: 10c0/3d7a24cb6ab58aed58aaf4998788d2d2686d3753bba9012cf009a417487894882fc855f36805fcb446cea904710a505e60cb9847a152e4dd7166615af99b4da9 + languageName: node + linkType: hard + +"@better-auth/prisma-adapter@npm:1.6.5": + version: 1.6.5 + resolution: "@better-auth/prisma-adapter@npm:1.6.5" + peerDependencies: + "@better-auth/core": ^1.6.5 + "@better-auth/utils": 0.4.0 + "@prisma/client": ^5.0.0 || ^6.0.0 || ^7.0.0 + prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + "@prisma/client": + optional: true + prisma: + optional: true + checksum: 10c0/2fdbab52ca0f7dd5851d05d0b64d93152533fab1ee021e9853a17b66ea412a83ca6999979ed1fa5dba7f2c26c128f924c6b3d6cb1ba49704a60ece41f9ffe405 + languageName: node + linkType: hard + +"@better-auth/telemetry@npm:1.6.5": + version: 1.6.5 + resolution: "@better-auth/telemetry@npm:1.6.5" + peerDependencies: + "@better-auth/core": ^1.6.5 + "@better-auth/utils": 0.4.0 + "@better-fetch/fetch": 1.1.21 + checksum: 10c0/ea48aaa0dc9a3918d03cd1698974f4a3c8b54d8293108197e770b32be5f6957616bd69843b023d6a13cc94a29df764bfd9fa45c49b48baf6ded9961989abe2c5 + languageName: node + linkType: hard + +"@better-auth/utils@npm:0.4.0, @better-auth/utils@npm:^0.4.0": + version: 0.4.0 + resolution: "@better-auth/utils@npm:0.4.0" + dependencies: + "@noble/hashes": "npm:^2.0.1" + checksum: 10c0/1dd66bec52866f127737235c550385454ae980acbde476eeb6663fb92c08a626af7114c7fb7687fab7164c16d60e5536c7379292bb3e920d6764da7956305873 + languageName: node + linkType: hard + +"@better-fetch/fetch@npm:1.1.21, @better-fetch/fetch@npm:^1.1.21": + version: 1.1.21 + resolution: "@better-fetch/fetch@npm:1.1.21" + checksum: 10c0/b23355b9eb397a4e6d3b269b027f19c44e3348b42640fa8f133ea4ca9c07e1d289781d2b18477f56883040e7401fcc751c52a6f0b56ea619bdd3132fff3cbb43 + languageName: node + linkType: hard + "@dimforge/rapier3d-compat@npm:~0.12.0": version: 0.12.0 resolution: "@dimforge/rapier3d-compat@npm:0.12.0" @@ -1007,6 +1126,20 @@ __metadata: languageName: node linkType: hard +"@noble/ciphers@npm:^2.1.1": + version: 2.2.0 + resolution: "@noble/ciphers@npm:2.2.0" + checksum: 10c0/56396fa9b2cd7863999d9d70c6cbfcc03db9644cbbac9bb9d383d18f701bee3eb09f36878d8252a763c8d1fb6f0807339e06e2d28f18b14c525133c57c14508d + languageName: node + linkType: hard + +"@noble/hashes@npm:^2.0.1": + version: 2.2.0 + resolution: "@noble/hashes@npm:2.2.0" + checksum: 10c0/cad8630c504d6b9271984f685cd0af9101b40988fc2dfbe17ccdf068a9941f95cc5c9957d89e9ca4b7ca94c15cb35f93510c5d53a09fbcc83ee503b93d0a1034 + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -1063,6 +1196,13 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/semantic-conventions@npm:^1.39.0": + version: 1.40.0 + resolution: "@opentelemetry/semantic-conventions@npm:1.40.0" + checksum: 10c0/3259de0ea11b52eb70e44c12eba21448392baf9cb74c37b62071c4a5ed7fb89b61e194f3898d40ac6bfa7293617a0e132876cb6e355472b66de0cdb13c50b529 + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -1313,6 +1453,13 @@ __metadata: languageName: node linkType: hard +"@standard-schema/spec@npm:^1.1.0": + version: 1.1.0 + resolution: "@standard-schema/spec@npm:1.1.0" + checksum: 10c0/d90f55acde4b2deb983529c87e8025fa693de1a5e8b49ecc6eb84d1fd96328add0e03d7d551442156c7432fd78165b2c26ff561b970a9a881f046abb78d6a526 + languageName: node + linkType: hard + "@strapi/blocks-react-renderer@npm:^1.0.1": version: 1.0.2 resolution: "@strapi/blocks-react-renderer@npm:1.0.2" @@ -2568,6 +2715,107 @@ __metadata: languageName: node linkType: hard +"better-auth@npm:^1.6.5": + version: 1.6.5 + resolution: "better-auth@npm:1.6.5" + dependencies: + "@better-auth/core": "npm:1.6.5" + "@better-auth/drizzle-adapter": "npm:1.6.5" + "@better-auth/kysely-adapter": "npm:1.6.5" + "@better-auth/memory-adapter": "npm:1.6.5" + "@better-auth/mongo-adapter": "npm:1.6.5" + "@better-auth/prisma-adapter": "npm:1.6.5" + "@better-auth/telemetry": "npm:1.6.5" + "@better-auth/utils": "npm:0.4.0" + "@better-fetch/fetch": "npm:1.1.21" + "@noble/ciphers": "npm:^2.1.1" + "@noble/hashes": "npm:^2.0.1" + better-call: "npm:1.3.5" + defu: "npm:^6.1.4" + jose: "npm:^6.1.3" + kysely: "npm:^0.28.14" + nanostores: "npm:^1.1.1" + zod: "npm:^4.3.6" + peerDependencies: + "@lynx-js/react": "*" + "@prisma/client": ^5.0.0 || ^6.0.0 || ^7.0.0 + "@sveltejs/kit": ^2.0.0 + "@tanstack/react-start": ^1.0.0 + "@tanstack/solid-start": ^1.0.0 + better-sqlite3: ^12.0.0 + drizzle-kit: ">=0.31.4" + drizzle-orm: ^0.45.2 + mongodb: ^6.0.0 || ^7.0.0 + mysql2: ^3.0.0 + next: ^14.0.0 || ^15.0.0 || ^16.0.0 + pg: ^8.0.0 + prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + solid-js: ^1.0.0 + svelte: ^4.0.0 || ^5.0.0 + vitest: ^2.0.0 || ^3.0.0 || ^4.0.0 + vue: ^3.0.0 + peerDependenciesMeta: + "@lynx-js/react": + optional: true + "@prisma/client": + optional: true + "@sveltejs/kit": + optional: true + "@tanstack/react-start": + optional: true + "@tanstack/solid-start": + optional: true + better-sqlite3: + optional: true + drizzle-kit: + optional: true + drizzle-orm: + optional: true + mongodb: + optional: true + mysql2: + optional: true + next: + optional: true + pg: + optional: true + prisma: + optional: true + react: + optional: true + react-dom: + optional: true + solid-js: + optional: true + svelte: + optional: true + vitest: + optional: true + vue: + optional: true + checksum: 10c0/49303d3f3972b32e23c335cd1c6be85395be9f21828acb449b36e5f22adaec572f8471d5c6192e4cdbe6703df641ac7b5d4ef31532ee9229e78329f0da92081f + languageName: node + linkType: hard + +"better-call@npm:1.3.5": + version: 1.3.5 + resolution: "better-call@npm:1.3.5" + dependencies: + "@better-auth/utils": "npm:^0.4.0" + "@better-fetch/fetch": "npm:^1.1.21" + rou3: "npm:^0.7.12" + set-cookie-parser: "npm:^3.0.1" + peerDependencies: + zod: ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + checksum: 10c0/2b629138301b2cf34c3c7c808f658d77510123fd31c0e30622981608c6f8e5571187f0919af37951f92c66f0cbfaa67bc07c5c16139fdf7e5f8e6616964b1628 + languageName: node + linkType: hard + "bidi-js@npm:^1.0.2": version: 1.0.3 resolution: "bidi-js@npm:1.0.3" @@ -3159,6 +3407,13 @@ __metadata: languageName: node linkType: hard +"defu@npm:^6.1.4": + version: 6.1.7 + resolution: "defu@npm:6.1.7" + checksum: 10c0/e6635388103c8be3c574ac31302f6930e5e6eeedba32cb1b30cf993c7d9fb571aec2485446dfa23bfa63e55e66156fe109027a9695db82a50f931e91e8d4bedb + languageName: node + linkType: hard + "delaunator@npm:5": version: 5.0.1 resolution: "delaunator@npm:5.0.1" @@ -4769,6 +5024,13 @@ __metadata: languageName: node linkType: hard +"jose@npm:^6.1.3": + version: 6.2.2 + resolution: "jose@npm:6.2.2" + checksum: 10c0/201f4776d77eccd339de99fb3ba940fdf03db15e64be7a99b511e53c232e3f3818e3f21b95223d62f99315a2ab76b4251cedd94e067de56893e45273a8d2151b + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -4867,6 +5129,13 @@ __metadata: languageName: node linkType: hard +"kysely@npm:^0.28.14": + version: 0.28.16 + resolution: "kysely@npm:0.28.16" + checksum: 10c0/5ab55b7b39c24f7986b1f9ccf3a86ffbf28728ebcb388f062ea01335bcda03dad0f23e75f951322d725405f453875fa29256beefab3ddd8aba0b29bb94035496 + languageName: node + linkType: hard + "language-subtag-registry@npm:^0.3.20": version: 0.3.23 resolution: "language-subtag-registry@npm:0.3.23" @@ -5211,6 +5480,13 @@ __metadata: languageName: node linkType: hard +"nanostores@npm:^1.1.1": + version: 1.3.0 + resolution: "nanostores@npm:1.3.0" + checksum: 10c0/fab57ec59758457f34e32e6af6982bf0f69ec8c732999347170982e39b5beb0391c91d970177608ff613a733024a39f3a767de7a8e25e4bf7aeadbcb76f8a7f6 + languageName: node + linkType: hard + "napi-postinstall@npm:^0.3.0": version: 0.3.3 resolution: "napi-postinstall@npm:0.3.3" @@ -5336,6 +5612,7 @@ __metadata: "@types/react": "npm:^19.2.0" "@types/react-dom": "npm:^19.2.0" "@types/three": "npm:^0.179.0" + better-auth: "npm:^1.6.5" class-variance-authority: "npm:^0.7.0" clsx: "npm:^2.1.1" date-fns: "npm:^3.6.0" @@ -6121,6 +6398,13 @@ __metadata: languageName: node linkType: hard +"rou3@npm:^0.7.12": + version: 0.7.12 + resolution: "rou3@npm:0.7.12" + checksum: 10c0/2ea87ddd91a5d0f9d9671fa2bb714f57566eae33ecb24e8a61bb298aca8c226483e59cafe1c60297ac9aa58b2b6ad506447374cdbff4e99cf0f9f72a9dae09dc + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -6212,6 +6496,13 @@ __metadata: languageName: node linkType: hard +"set-cookie-parser@npm:^3.0.1": + version: 3.1.0 + resolution: "set-cookie-parser@npm:3.1.0" + checksum: 10c0/7465e389ff9fb7ff243fd55f0f48b5648d53a560903db170a7f766c2b82f22f4242c4d366fcdf12afdd376496f84058025d889e718459256ecbfc9a5fe7755f5 + languageName: node + linkType: hard + "set-function-length@npm:^1.2.2": version: 1.2.2 resolution: "set-function-length@npm:1.2.2" @@ -7559,6 +7850,13 @@ __metadata: languageName: node linkType: hard +"zod@npm:^4.3.6": + version: 4.3.6 + resolution: "zod@npm:4.3.6" + checksum: 10c0/860d25a81ab41d33aa25f8d0d07b091a04acb426e605f396227a796e9e800c44723ed96d0f53a512b57be3d1520f45bf69c0cb3b378a232a00787a2609625307 + languageName: node + linkType: hard + "zustand@npm:^4.3.2": version: 4.5.7 resolution: "zustand@npm:4.5.7" diff --git a/strapi/.env.example b/strapi/.env.example index 269ce01a..4c796803 100644 --- a/strapi/.env.example +++ b/strapi/.env.example @@ -7,4 +7,6 @@ TRANSFER_TOKEN_SALT=tobemodified JWT_SECRET=tobemodified CLIENT_URL=http://localhost:3000 -PREVIEW_SECRET=preview_secret # optional, required with Next.js draft mode \ No newline at end of file +PREVIEW_SECRET=preview_secret # optional, required with Next.js draft mode + +BETTER_AUTH_SECRET=tobemodified \ No newline at end of file diff --git a/strapi/config/plugins.ts b/strapi/config/plugins.ts index 56bf55ff..9d54dc6e 100644 --- a/strapi/config/plugins.ts +++ b/strapi/config/plugins.ts @@ -1 +1,20 @@ -export default () => ({}); +export default ({ env }) => ({ + 'better-auth': { + enabled: true, + resolve: '@strapi-community/plugin-better-auth/package.json', + config: { + betterAuthOptions: { + secret: env('BETTER_AUTH_SECRET'), + baseURL: env('STRAPI_URL', 'http://localhost:1337'), + trustedOrigins: [env('CLIENT_URL', 'http://localhost:3000')], + emailAndPassword: { + enabled: true, + requireEmailVerification: false, + }, + session: { + expiresIn: 60 * 60 * 24 * 7, // 7 days + }, + }, + }, + }, +}); diff --git a/strapi/package.json b/strapi/package.json index b315b419..7466287b 100644 --- a/strapi/package.json +++ b/strapi/package.json @@ -20,10 +20,12 @@ "typescript": "^5" }, "dependencies": { + "@strapi-community/plugin-better-auth": "^1.0.0-alpha.2", "@strapi/plugin-cloud": "5.42.1", "@strapi/plugin-seo": "^2.0.4", "@strapi/plugin-users-permissions": "5.42.1", "@strapi/strapi": "5.42.1", + "better-auth": "^1.4.10", "better-sqlite3": "11.7.0", "pluralize": "^8.0.0", "react": "^18.0.0", @@ -36,7 +38,7 @@ "name": "A Strapi developer" }, "strapi": { - "uuid": "LAUNCHPAD" + "uuid": "LAUNCHPAD-LOCAL-b7319935-7ee5-4d22-9c9a-b7abebe54084" }, "engines": { "node": ">=18.0.0 <=22.x.x", diff --git a/strapi/types/generated/contentTypes.d.ts b/strapi/types/generated/contentTypes.d.ts index ec99affd..471c672d 100644 --- a/strapi/types/generated/contentTypes.d.ts +++ b/strapi/types/generated/contentTypes.d.ts @@ -1243,6 +1243,482 @@ export interface ApiTestimonialTestimonial extends Struct.CollectionTypeSchema { }; } +export interface PluginBetterAuthAccount extends Struct.CollectionTypeSchema { + collectionName: 'better_auth_accounts'; + info: { + description: 'Better Auth account'; + displayName: 'Accounts'; + pluralName: 'accounts'; + singularName: 'account'; + }; + options: { + draftAndPublish: false; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + accessToken: Schema.Attribute.Text; + accessTokenExpiresAt: Schema.Attribute.DateTime; + accountId: Schema.Attribute.Integer & Schema.Attribute.Required; + createdAt: Schema.Attribute.DateTime; + createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + idToken: Schema.Attribute.Text; + locale: Schema.Attribute.String & Schema.Attribute.Private; + localizations: Schema.Attribute.Relation< + 'oneToMany', + 'plugin::better-auth.account' + > & + Schema.Attribute.Private; + password: Schema.Attribute.String; + providerId: Schema.Attribute.String & Schema.Attribute.Required; + publishedAt: Schema.Attribute.DateTime; + refreshToken: Schema.Attribute.Text; + refreshTokenExpiresAt: Schema.Attribute.DateTime; + scope: Schema.Attribute.String; + updatedAt: Schema.Attribute.DateTime; + updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + userId: Schema.Attribute.Integer & Schema.Attribute.Required; + }; +} + +export interface PluginBetterAuthJwk extends Struct.CollectionTypeSchema { + collectionName: 'better_auth_jwks'; + info: { + description: 'Better Auth JSON Web Keys'; + displayName: 'JWKs'; + pluralName: 'jwks'; + singularName: 'jwk'; + }; + options: { + draftAndPublish: false; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + createdAt: Schema.Attribute.DateTime; + createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + locale: Schema.Attribute.String & Schema.Attribute.Private; + localizations: Schema.Attribute.Relation< + 'oneToMany', + 'plugin::better-auth.jwk' + > & + Schema.Attribute.Private; + privateKey: Schema.Attribute.Text & Schema.Attribute.Required; + publicKey: Schema.Attribute.Text & Schema.Attribute.Required; + publishedAt: Schema.Attribute.DateTime; + updatedAt: Schema.Attribute.DateTime; + updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + }; +} + +export interface PluginBetterAuthOauthAccessToken + extends Struct.CollectionTypeSchema { + collectionName: 'better_auth_oauth_access_tokens'; + info: { + description: 'Better Auth OAuth access tokens'; + displayName: 'OAuth Access Tokens'; + pluralName: 'oauth-access-tokens'; + singularName: 'oauth-access-token'; + }; + options: { + draftAndPublish: false; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + accessToken: Schema.Attribute.String & Schema.Attribute.Unique; + accessTokenExpiresAt: Schema.Attribute.DateTime; + clientId: Schema.Attribute.String; + createdAt: Schema.Attribute.DateTime; + createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + locale: Schema.Attribute.String & Schema.Attribute.Private; + localizations: Schema.Attribute.Relation< + 'oneToMany', + 'plugin::better-auth.oauth-access-token' + > & + Schema.Attribute.Private; + publishedAt: Schema.Attribute.DateTime; + refreshToken: Schema.Attribute.String & Schema.Attribute.Unique; + refreshTokenExpiresAt: Schema.Attribute.DateTime; + scopes: Schema.Attribute.Text; + updatedAt: Schema.Attribute.DateTime; + updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + userId: Schema.Attribute.Integer; + }; +} + +export interface PluginBetterAuthOauthApplication + extends Struct.CollectionTypeSchema { + collectionName: 'better_auth_oauth_applications'; + info: { + description: 'Better Auth OAuth applications'; + displayName: 'OAuth Applications'; + pluralName: 'oauth-applications'; + singularName: 'oauth-application'; + }; + options: { + draftAndPublish: false; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + clientId: Schema.Attribute.String & Schema.Attribute.Unique; + clientSecret: Schema.Attribute.String; + createdAt: Schema.Attribute.DateTime; + createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + disabled: Schema.Attribute.Boolean; + icon: Schema.Attribute.String; + locale: Schema.Attribute.String & Schema.Attribute.Private; + localizations: Schema.Attribute.Relation< + 'oneToMany', + 'plugin::better-auth.oauth-application' + > & + Schema.Attribute.Private; + metadata: Schema.Attribute.Text; + name: Schema.Attribute.String; + publishedAt: Schema.Attribute.DateTime; + redirectURLs: Schema.Attribute.Text; + type: Schema.Attribute.String; + updatedAt: Schema.Attribute.DateTime; + updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + userId: Schema.Attribute.Integer; + }; +} + +export interface PluginBetterAuthOauthConsent + extends Struct.CollectionTypeSchema { + collectionName: 'better_auth_oauth_consents'; + info: { + description: 'Better Auth OAuth user consents'; + displayName: 'OAuth Consents'; + pluralName: 'oauth-consents'; + singularName: 'oauth-consent'; + }; + options: { + draftAndPublish: false; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + clientId: Schema.Attribute.String; + consentGiven: Schema.Attribute.Boolean; + createdAt: Schema.Attribute.DateTime; + createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + locale: Schema.Attribute.String & Schema.Attribute.Private; + localizations: Schema.Attribute.Relation< + 'oneToMany', + 'plugin::better-auth.oauth-consent' + > & + Schema.Attribute.Private; + publishedAt: Schema.Attribute.DateTime; + scopes: Schema.Attribute.Text; + updatedAt: Schema.Attribute.DateTime; + updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + userId: Schema.Attribute.Integer; + }; +} + +export interface PluginBetterAuthPasskey extends Struct.CollectionTypeSchema { + collectionName: 'better_auth_passkeys'; + info: { + description: 'Better Auth passkey authentication'; + displayName: 'Passkeys'; + pluralName: 'passkeys'; + singularName: 'passkey'; + }; + options: { + draftAndPublish: false; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + aaguid: Schema.Attribute.String; + backedUp: Schema.Attribute.Boolean & Schema.Attribute.Required; + counter: Schema.Attribute.Integer & Schema.Attribute.Required; + createdAt: Schema.Attribute.DateTime; + createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + credentialID: Schema.Attribute.String & + Schema.Attribute.Required & + Schema.Attribute.Unique; + deviceType: Schema.Attribute.String & Schema.Attribute.Required; + locale: Schema.Attribute.String & Schema.Attribute.Private; + localizations: Schema.Attribute.Relation< + 'oneToMany', + 'plugin::better-auth.passkey' + > & + Schema.Attribute.Private; + name: Schema.Attribute.String; + publicKey: Schema.Attribute.String & Schema.Attribute.Required; + publishedAt: Schema.Attribute.DateTime; + transports: Schema.Attribute.String; + updatedAt: Schema.Attribute.DateTime; + updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + userId: Schema.Attribute.Integer & Schema.Attribute.Required; + }; +} + +export interface PluginBetterAuthRateLimit extends Struct.CollectionTypeSchema { + collectionName: 'better_auth_rate_limits'; + info: { + description: 'Better Auth rate limiting'; + displayName: 'Rate Limits'; + pluralName: 'rate-limits'; + singularName: 'rate-limit'; + }; + options: { + draftAndPublish: false; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + count: Schema.Attribute.Integer; + createdAt: Schema.Attribute.DateTime; + createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + key: Schema.Attribute.String & Schema.Attribute.Unique; + lastRequest: Schema.Attribute.BigInteger; + locale: Schema.Attribute.String & Schema.Attribute.Private; + localizations: Schema.Attribute.Relation< + 'oneToMany', + 'plugin::better-auth.rate-limit' + > & + Schema.Attribute.Private; + publishedAt: Schema.Attribute.DateTime; + updatedAt: Schema.Attribute.DateTime; + updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + }; +} + +export interface PluginBetterAuthSession extends Struct.CollectionTypeSchema { + collectionName: 'better_auth_sessions'; + info: { + description: 'Better Auth session'; + displayName: 'Sessions'; + pluralName: 'sessions'; + singularName: 'session'; + }; + options: { + draftAndPublish: false; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + createdAt: Schema.Attribute.DateTime; + createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + expiresAt: Schema.Attribute.DateTime & Schema.Attribute.Required; + ipAddress: Schema.Attribute.String; + locale: Schema.Attribute.String & Schema.Attribute.Private; + localizations: Schema.Attribute.Relation< + 'oneToMany', + 'plugin::better-auth.session' + > & + Schema.Attribute.Private; + publishedAt: Schema.Attribute.DateTime; + token: Schema.Attribute.String & + Schema.Attribute.Required & + Schema.Attribute.Unique; + updatedAt: Schema.Attribute.DateTime; + updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + userAgent: Schema.Attribute.String; + userId: Schema.Attribute.Integer & Schema.Attribute.Required; + }; +} + +export interface PluginBetterAuthTwoFactor extends Struct.CollectionTypeSchema { + collectionName: 'better_auth_two_factor'; + info: { + description: 'Better Auth two factor authentication'; + displayName: 'Two Factors'; + pluralName: 'two-factors'; + singularName: 'two-factor'; + }; + options: { + draftAndPublish: false; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + backupCodes: Schema.Attribute.String & Schema.Attribute.Required; + createdAt: Schema.Attribute.DateTime; + createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + locale: Schema.Attribute.String & Schema.Attribute.Private; + localizations: Schema.Attribute.Relation< + 'oneToMany', + 'plugin::better-auth.two-factor' + > & + Schema.Attribute.Private; + publishedAt: Schema.Attribute.DateTime; + secret: Schema.Attribute.String & Schema.Attribute.Required; + updatedAt: Schema.Attribute.DateTime; + updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + userId: Schema.Attribute.Integer & Schema.Attribute.Required; + }; +} + +export interface PluginBetterAuthUser extends Struct.CollectionTypeSchema { + collectionName: 'better_auth_users'; + info: { + description: 'Better Auth user'; + displayName: 'Users'; + pluralName: 'users'; + singularName: 'user'; + }; + options: { + draftAndPublish: false; + }; + pluginOptions: { + 'content-manager': { + visible: true; + }; + 'content-type-builder': { + visible: true; + }; + }; + attributes: { + createdAt: Schema.Attribute.DateTime; + createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + displayUsername: Schema.Attribute.String; + email: Schema.Attribute.Email & + Schema.Attribute.Required & + Schema.Attribute.Unique; + emailVerified: Schema.Attribute.Boolean & Schema.Attribute.DefaultTo; + image: Schema.Attribute.String; + isAnonymous: Schema.Attribute.Boolean; + locale: Schema.Attribute.String & Schema.Attribute.Private; + localizations: Schema.Attribute.Relation< + 'oneToMany', + 'plugin::better-auth.user' + > & + Schema.Attribute.Private; + name: Schema.Attribute.String; + phoneNumber: Schema.Attribute.String & Schema.Attribute.Unique; + phoneNumberVerified: Schema.Attribute.Boolean; + publishedAt: Schema.Attribute.DateTime; + strapi_assignee: Schema.Attribute.Relation<'oneToOne', 'admin::user'>; + strapi_stage: Schema.Attribute.Relation< + 'oneToOne', + 'plugin::review-workflows.workflow-stage' + >; + twoFactorEnabled: Schema.Attribute.Boolean; + updatedAt: Schema.Attribute.DateTime; + updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + username: Schema.Attribute.String & Schema.Attribute.Unique; + }; +} + +export interface PluginBetterAuthVerification + extends Struct.CollectionTypeSchema { + collectionName: 'better_auth_verifications'; + info: { + description: 'Better Auth verification'; + displayName: 'Verifications'; + pluralName: 'verifications'; + singularName: 'verification'; + }; + options: { + draftAndPublish: false; + }; + pluginOptions: { + 'content-manager': { + visible: false; + }; + 'content-type-builder': { + visible: false; + }; + }; + attributes: { + createdAt: Schema.Attribute.DateTime; + createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + expiresAt: Schema.Attribute.DateTime & Schema.Attribute.Required; + identifier: Schema.Attribute.String & Schema.Attribute.Required; + locale: Schema.Attribute.String & Schema.Attribute.Private; + localizations: Schema.Attribute.Relation< + 'oneToMany', + 'plugin::better-auth.verification' + > & + Schema.Attribute.Private; + publishedAt: Schema.Attribute.DateTime; + updatedAt: Schema.Attribute.DateTime; + updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & + Schema.Attribute.Private; + value: Schema.Attribute.String & Schema.Attribute.Required; + }; +} + export interface PluginContentReleasesRelease extends Struct.CollectionTypeSchema { collectionName: 'strapi_releases'; @@ -1772,6 +2248,17 @@ declare module '@strapi/strapi' { 'api::product.product': ApiProductProduct; 'api::redirection.redirection': ApiRedirectionRedirection; 'api::testimonial.testimonial': ApiTestimonialTestimonial; + 'plugin::better-auth.account': PluginBetterAuthAccount; + 'plugin::better-auth.jwk': PluginBetterAuthJwk; + 'plugin::better-auth.oauth-access-token': PluginBetterAuthOauthAccessToken; + 'plugin::better-auth.oauth-application': PluginBetterAuthOauthApplication; + 'plugin::better-auth.oauth-consent': PluginBetterAuthOauthConsent; + 'plugin::better-auth.passkey': PluginBetterAuthPasskey; + 'plugin::better-auth.rate-limit': PluginBetterAuthRateLimit; + 'plugin::better-auth.session': PluginBetterAuthSession; + 'plugin::better-auth.two-factor': PluginBetterAuthTwoFactor; + 'plugin::better-auth.user': PluginBetterAuthUser; + 'plugin::better-auth.verification': PluginBetterAuthVerification; 'plugin::content-releases.release': PluginContentReleasesRelease; 'plugin::content-releases.release-action': PluginContentReleasesReleaseAction; 'plugin::i18n.locale': PluginI18NLocale; diff --git a/strapi/yarn.lock b/strapi/yarn.lock index b979efad..65286d77 100644 --- a/strapi/yarn.lock +++ b/strapi/yarn.lock @@ -567,6 +567,125 @@ __metadata: languageName: node linkType: hard +"@better-auth/core@npm:1.6.5": + version: 1.6.5 + resolution: "@better-auth/core@npm:1.6.5" + dependencies: + "@opentelemetry/semantic-conventions": "npm:^1.39.0" + "@standard-schema/spec": "npm:^1.1.0" + zod: "npm:^4.3.6" + peerDependencies: + "@better-auth/utils": 0.4.0 + "@better-fetch/fetch": 1.1.21 + "@cloudflare/workers-types": ">=4" + "@opentelemetry/api": ^1.9.0 + better-call: 1.3.5 + jose: ^6.1.0 + kysely: ^0.28.5 + nanostores: ^1.0.1 + peerDependenciesMeta: + "@cloudflare/workers-types": + optional: true + checksum: 10c0/b45f99a1411d4bb4b5be5d8d310b934c2ce0974331f7bb07a9a84081255367817fa8ee7c05fdae1815405a03489c07ddecdcc066e2403c4127264bdc828fd425 + languageName: node + linkType: hard + +"@better-auth/drizzle-adapter@npm:1.6.5": + version: 1.6.5 + resolution: "@better-auth/drizzle-adapter@npm:1.6.5" + peerDependencies: + "@better-auth/core": ^1.6.5 + "@better-auth/utils": 0.4.0 + drizzle-orm: ^0.45.2 + peerDependenciesMeta: + drizzle-orm: + optional: true + checksum: 10c0/018a167f18356da3ad446e73cd093f3fedaf30b33e790a9796271c1d57fdb2abfda80a783a664b9cd1b8531dc60cbf7dc23e64680fba8d9beaf0659fc8802084 + languageName: node + linkType: hard + +"@better-auth/kysely-adapter@npm:1.6.5": + version: 1.6.5 + resolution: "@better-auth/kysely-adapter@npm:1.6.5" + peerDependencies: + "@better-auth/core": ^1.6.5 + "@better-auth/utils": 0.4.0 + kysely: ^0.28.14 + peerDependenciesMeta: + kysely: + optional: true + checksum: 10c0/c607aab603ce911e066f44cbc980eb319327613c6d2a9767c7247c3fef4063e0ff044c09abb1575739b4393479c5fea2f2bca6e0f6d542ffd195be1fd2998621 + languageName: node + linkType: hard + +"@better-auth/memory-adapter@npm:1.6.5": + version: 1.6.5 + resolution: "@better-auth/memory-adapter@npm:1.6.5" + peerDependencies: + "@better-auth/core": ^1.6.5 + "@better-auth/utils": 0.4.0 + checksum: 10c0/794263419403800eb22a169a83f368b8abb379db8b544d38bb23733a3e1a165c91aef4116211252bf74e62ce77ee70f721c53fef44ae2968b0f846cec7b13feb + languageName: node + linkType: hard + +"@better-auth/mongo-adapter@npm:1.6.5": + version: 1.6.5 + resolution: "@better-auth/mongo-adapter@npm:1.6.5" + peerDependencies: + "@better-auth/core": ^1.6.5 + "@better-auth/utils": 0.4.0 + mongodb: ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + mongodb: + optional: true + checksum: 10c0/3d7a24cb6ab58aed58aaf4998788d2d2686d3753bba9012cf009a417487894882fc855f36805fcb446cea904710a505e60cb9847a152e4dd7166615af99b4da9 + languageName: node + linkType: hard + +"@better-auth/prisma-adapter@npm:1.6.5": + version: 1.6.5 + resolution: "@better-auth/prisma-adapter@npm:1.6.5" + peerDependencies: + "@better-auth/core": ^1.6.5 + "@better-auth/utils": 0.4.0 + "@prisma/client": ^5.0.0 || ^6.0.0 || ^7.0.0 + prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + "@prisma/client": + optional: true + prisma: + optional: true + checksum: 10c0/2fdbab52ca0f7dd5851d05d0b64d93152533fab1ee021e9853a17b66ea412a83ca6999979ed1fa5dba7f2c26c128f924c6b3d6cb1ba49704a60ece41f9ffe405 + languageName: node + linkType: hard + +"@better-auth/telemetry@npm:1.6.5": + version: 1.6.5 + resolution: "@better-auth/telemetry@npm:1.6.5" + peerDependencies: + "@better-auth/core": ^1.6.5 + "@better-auth/utils": 0.4.0 + "@better-fetch/fetch": 1.1.21 + checksum: 10c0/ea48aaa0dc9a3918d03cd1698974f4a3c8b54d8293108197e770b32be5f6957616bd69843b023d6a13cc94a29df764bfd9fa45c49b48baf6ded9961989abe2c5 + languageName: node + linkType: hard + +"@better-auth/utils@npm:0.4.0, @better-auth/utils@npm:^0.4.0": + version: 0.4.0 + resolution: "@better-auth/utils@npm:0.4.0" + dependencies: + "@noble/hashes": "npm:^2.0.1" + checksum: 10c0/1dd66bec52866f127737235c550385454ae980acbde476eeb6663fb92c08a626af7114c7fb7687fab7164c16d60e5536c7379292bb3e920d6764da7956305873 + languageName: node + linkType: hard + +"@better-fetch/fetch@npm:1.1.21, @better-fetch/fetch@npm:^1.1.21": + version: 1.1.21 + resolution: "@better-fetch/fetch@npm:1.1.21" + checksum: 10c0/b23355b9eb397a4e6d3b269b027f19c44e3348b42640fa8f133ea4ca9c07e1d289781d2b18477f56883040e7401fcc751c52a6f0b56ea619bdd3132fff3cbb43 + languageName: node + linkType: hard + "@borewit/text-codec@npm:^0.2.1": version: 0.2.2 resolution: "@borewit/text-codec@npm:0.2.2" @@ -1930,6 +2049,13 @@ __metadata: languageName: node linkType: hard +"@noble/ciphers@npm:^2.1.1": + version: 2.2.0 + resolution: "@noble/ciphers@npm:2.2.0" + checksum: 10c0/56396fa9b2cd7863999d9d70c6cbfcc03db9644cbbac9bb9d383d18f701bee3eb09f36878d8252a763c8d1fb6f0807339e06e2d28f18b14c525133c57c14508d + languageName: node + linkType: hard + "@noble/hashes@npm:^1.1.5": version: 1.8.0 resolution: "@noble/hashes@npm:1.8.0" @@ -1937,6 +2063,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:^2.0.1": + version: 2.2.0 + resolution: "@noble/hashes@npm:2.2.0" + checksum: 10c0/cad8630c504d6b9271984f685cd0af9101b40988fc2dfbe17ccdf068a9941f95cc5c9957d89e9ca4b7ca94c15cb35f93510c5d53a09fbcc83ee503b93d0a1034 + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -1993,6 +2126,13 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/semantic-conventions@npm:^1.39.0": + version: 1.40.0 + resolution: "@opentelemetry/semantic-conventions@npm:1.40.0" + checksum: 10c0/3259de0ea11b52eb70e44c12eba21448392baf9cb74c37b62071c4a5ed7fb89b61e194f3898d40ac6bfa7293617a0e132876cb6e355472b66de0cdb13c50b529 + languageName: node + linkType: hard + "@paralleldrive/cuid2@npm:2.2.2, @paralleldrive/cuid2@npm:^2.2.2": version: 2.2.2 resolution: "@paralleldrive/cuid2@npm:2.2.2" @@ -3582,6 +3722,29 @@ __metadata: languageName: node linkType: hard +"@standard-schema/spec@npm:^1.1.0": + version: 1.1.0 + resolution: "@standard-schema/spec@npm:1.1.0" + checksum: 10c0/d90f55acde4b2deb983529c87e8025fa693de1a5e8b49ecc6eb84d1fd96328add0e03d7d551442156c7432fd78165b2c26ff561b970a9a881f046abb78d6a526 + languageName: node + linkType: hard + +"@strapi-community/plugin-better-auth@npm:^1.0.0-alpha.2": + version: 1.0.0-alpha.2 + resolution: "@strapi-community/plugin-better-auth@npm:1.0.0-alpha.2" + dependencies: + better-auth: "npm:^1.4.10" + lodash: "npm:^4.17.21" + peerDependencies: + "@strapi/design-system": ^2.0.0 + "@strapi/icons": ^2.0.0 + "@strapi/sdk-plugin": ^5.0.0 + "@strapi/strapi": ^5.0.0 + "@strapi/utils": ^5.0.0 + checksum: 10c0/a7b5513acbdb7e4e1a7fb9a41a2812377cce3c1bd8754e5623a9f732cd328bd4948dc3db4732dccb3c6c3900f242453ea34a50c39a74001e2b8d2c70976d052d + languageName: node + linkType: hard + "@strapi/admin@npm:5.42.1": version: 5.42.1 resolution: "@strapi/admin@npm:5.42.1" @@ -6002,6 +6165,107 @@ __metadata: languageName: node linkType: hard +"better-auth@npm:^1.4.10": + version: 1.6.5 + resolution: "better-auth@npm:1.6.5" + dependencies: + "@better-auth/core": "npm:1.6.5" + "@better-auth/drizzle-adapter": "npm:1.6.5" + "@better-auth/kysely-adapter": "npm:1.6.5" + "@better-auth/memory-adapter": "npm:1.6.5" + "@better-auth/mongo-adapter": "npm:1.6.5" + "@better-auth/prisma-adapter": "npm:1.6.5" + "@better-auth/telemetry": "npm:1.6.5" + "@better-auth/utils": "npm:0.4.0" + "@better-fetch/fetch": "npm:1.1.21" + "@noble/ciphers": "npm:^2.1.1" + "@noble/hashes": "npm:^2.0.1" + better-call: "npm:1.3.5" + defu: "npm:^6.1.4" + jose: "npm:^6.1.3" + kysely: "npm:^0.28.14" + nanostores: "npm:^1.1.1" + zod: "npm:^4.3.6" + peerDependencies: + "@lynx-js/react": "*" + "@prisma/client": ^5.0.0 || ^6.0.0 || ^7.0.0 + "@sveltejs/kit": ^2.0.0 + "@tanstack/react-start": ^1.0.0 + "@tanstack/solid-start": ^1.0.0 + better-sqlite3: ^12.0.0 + drizzle-kit: ">=0.31.4" + drizzle-orm: ^0.45.2 + mongodb: ^6.0.0 || ^7.0.0 + mysql2: ^3.0.0 + next: ^14.0.0 || ^15.0.0 || ^16.0.0 + pg: ^8.0.0 + prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + solid-js: ^1.0.0 + svelte: ^4.0.0 || ^5.0.0 + vitest: ^2.0.0 || ^3.0.0 || ^4.0.0 + vue: ^3.0.0 + peerDependenciesMeta: + "@lynx-js/react": + optional: true + "@prisma/client": + optional: true + "@sveltejs/kit": + optional: true + "@tanstack/react-start": + optional: true + "@tanstack/solid-start": + optional: true + better-sqlite3: + optional: true + drizzle-kit: + optional: true + drizzle-orm: + optional: true + mongodb: + optional: true + mysql2: + optional: true + next: + optional: true + pg: + optional: true + prisma: + optional: true + react: + optional: true + react-dom: + optional: true + solid-js: + optional: true + svelte: + optional: true + vitest: + optional: true + vue: + optional: true + checksum: 10c0/49303d3f3972b32e23c335cd1c6be85395be9f21828acb449b36e5f22adaec572f8471d5c6192e4cdbe6703df641ac7b5d4ef31532ee9229e78329f0da92081f + languageName: node + linkType: hard + +"better-call@npm:1.3.5": + version: 1.3.5 + resolution: "better-call@npm:1.3.5" + dependencies: + "@better-auth/utils": "npm:^0.4.0" + "@better-fetch/fetch": "npm:^1.1.21" + rou3: "npm:^0.7.12" + set-cookie-parser: "npm:^3.0.1" + peerDependencies: + zod: ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + checksum: 10c0/2b629138301b2cf34c3c7c808f658d77510123fd31c0e30622981608c6f8e5571187f0919af37951f92c66f0cbfaa67bc07c5c16139fdf7e5f8e6616964b1628 + languageName: node + linkType: hard + "better-sqlite3@npm:11.7.0": version: 11.7.0 resolution: "better-sqlite3@npm:11.7.0" @@ -7224,6 +7488,13 @@ __metadata: languageName: node linkType: hard +"defu@npm:^6.1.4": + version: 6.1.7 + resolution: "defu@npm:6.1.7" + checksum: 10c0/e6635388103c8be3c574ac31302f6930e5e6eeedba32cb1b30cf993c7d9fb571aec2485446dfa23bfa63e55e66156fe109027a9695db82a50f931e91e8d4bedb + languageName: node + linkType: hard + "delayed-stream@npm:~1.0.0": version: 1.0.0 resolution: "delayed-stream@npm:1.0.0" @@ -9748,6 +10019,13 @@ __metadata: languageName: node linkType: hard +"jose@npm:^6.1.3": + version: 6.2.2 + resolution: "jose@npm:6.2.2" + checksum: 10c0/201f4776d77eccd339de99fb3ba940fdf03db15e64be7a99b511e53c232e3f3818e3f21b95223d62f99315a2ab76b4251cedd94e067de56893e45273a8d2151b + languageName: node + linkType: hard + "js-sha3@npm:0.8.0": version: 0.8.0 resolution: "js-sha3@npm:0.8.0" @@ -10228,6 +10506,13 @@ __metadata: languageName: node linkType: hard +"kysely@npm:^0.28.14": + version: 0.28.16 + resolution: "kysely@npm:0.28.16" + checksum: 10c0/5ab55b7b39c24f7986b1f9ccf3a86ffbf28728ebcb388f062ea01335bcda03dad0f23e75f951322d725405f453875fa29256beefab3ddd8aba0b29bb94035496 + languageName: node + linkType: hard + "libbase64@npm:0.1.0": version: 0.1.0 resolution: "libbase64@npm:0.1.0" @@ -11509,6 +11794,13 @@ __metadata: languageName: node linkType: hard +"nanostores@npm:^1.1.1": + version: 1.3.0 + resolution: "nanostores@npm:1.3.0" + checksum: 10c0/fab57ec59758457f34e32e6af6982bf0f69ec8c732999347170982e39b5beb0391c91d970177608ff613a733024a39f3a767de7a8e25e4bf7aeadbcb76f8a7f6 + languageName: node + linkType: hard + "napi-build-utils@npm:^2.0.0": version: 2.0.0 resolution: "napi-build-utils@npm:2.0.0" @@ -13520,6 +13812,13 @@ __metadata: languageName: node linkType: hard +"rou3@npm:^0.7.12": + version: 0.7.12 + resolution: "rou3@npm:0.7.12" + checksum: 10c0/2ea87ddd91a5d0f9d9671fa2bb714f57566eae33ecb24e8a61bb298aca8c226483e59cafe1c60297ac9aa58b2b6ad506447374cdbff4e99cf0f9f72a9dae09dc + languageName: node + linkType: hard + "run-async@npm:^3.0.0": version: 3.0.0 resolution: "run-async@npm:3.0.0" @@ -13714,6 +14013,13 @@ __metadata: languageName: node linkType: hard +"set-cookie-parser@npm:^3.0.1": + version: 3.1.0 + resolution: "set-cookie-parser@npm:3.1.0" + checksum: 10c0/7465e389ff9fb7ff243fd55f0f48b5648d53a560903db170a7f766c2b82f22f4242c4d366fcdf12afdd376496f84058025d889e718459256ecbfc9a5fe7755f5 + languageName: node + linkType: hard + "setimmediate@npm:^1.0.5": version: 1.0.5 resolution: "setimmediate@npm:1.0.5" @@ -14211,6 +14517,7 @@ __metadata: version: 0.0.0-use.local resolution: "strapi@workspace:." dependencies: + "@strapi-community/plugin-better-auth": "npm:^1.0.0-alpha.2" "@strapi/plugin-cloud": "npm:5.42.1" "@strapi/plugin-seo": "npm:^2.0.4" "@strapi/plugin-users-permissions": "npm:5.42.1" @@ -14218,6 +14525,7 @@ __metadata: "@types/node": "npm:^20" "@types/react": "npm:^18" "@types/react-dom": "npm:^18" + better-auth: "npm:^1.4.10" better-sqlite3: "npm:11.7.0" pluralize: "npm:^8.0.0" react: "npm:^18.0.0" @@ -15803,6 +16111,13 @@ __metadata: languageName: node linkType: hard +"zod@npm:^4.3.6": + version: 4.3.6 + resolution: "zod@npm:4.3.6" + checksum: 10c0/860d25a81ab41d33aa25f8d0d07b091a04acb426e605f396227a796e9e800c44723ed96d0f53a512b57be3d1520f45bf69c0cb3b378a232a00787a2609625307 + languageName: node + linkType: hard + "zwitch@npm:^2.0.0": version: 2.0.4 resolution: "zwitch@npm:2.0.4"