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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,7 @@ jobs:

- name: Build Project
run: pnpm run build
env:
NEXT_PUBLIC_CONVEX_URL: "https://insightful-pheasant-237.convex.cloud"
NEXT_PUBLIC_CONVEX_SITE_URL: "https://insightful-pheasant-237.convex.site"
NEXT_PUBLIC_SITE_URL: "http://localhost:3000"
15 changes: 15 additions & 0 deletions app/(shared-layout)/test/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"use client";

import { api } from "@/convex/_generated/api";
import { useQuery } from "convex/react";

export default function Page() {
const tasks = useQuery(api.tasks.get);
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
{tasks?.map(({ _id, text }) => (
<div key={_id}>{text}</div>
))}
</main>
);
}
3 changes: 3 additions & 0 deletions app/api/auth/[...all]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { handler } from "@/lib/auth-server";

export const { GET, POST } = handler;
9 changes: 7 additions & 2 deletions app/auth/sign-up/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
FieldLabel,
} from "@/components/ui/field";
import { Button } from "@/components/ui/button";
import { authClient } from "@/lib/auth-client";

export default function SignupPage() {
const form = useForm<z.infer<typeof signUpSchema>>({
Expand All @@ -31,8 +32,12 @@ export default function SignupPage() {
},
});

const onSubmit = () => {
console.log("yoo");
const onSubmit = async (data: z.infer<typeof signUpSchema>) => {
await authClient.signUp.email({
email: data.email,
name: data.name,
password: data.password,
});
};

return (
Expand Down
3 changes: 2 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from "react";
import { Outfit, Geist_Mono } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "@/components/ui/theme-provider";
import { ConvexClientProvider } from "../components/web/ConvexClientProvider";

const outfit = Outfit({
variable: "--font-sans",
Expand Down Expand Up @@ -35,7 +36,7 @@ export default function RootLayout({
disableTransitionOnChange
>
<main className={"w-full max-auto px-4 md:px-6 lg:px-8"}>
{children}
<ConvexClientProvider>{children}</ConvexClientProvider>
</main>
</ThemeProvider>
</body>
Expand Down
26 changes: 26 additions & 0 deletions components/web/ConvexClientProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"use client";

import { ReactNode } from "react";
import { ConvexReactClient } from "convex/react";
import { authClient } from "@/lib/auth-client";
import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";

const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);

export function ConvexClientProvider({
children,
initialToken,
}: {
children: ReactNode;
initialToken?: string | null;
}) {
return (
<ConvexBetterAuthProvider
client={convex}
authClient={authClient}
initialToken={initialToken}
>
{children}
</ConvexBetterAuthProvider>
);
}
31 changes: 21 additions & 10 deletions components/web/navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
"use client";

import Link from "next/link";
import { buttonVariants } from "@/components/ui/button";
import { Button, buttonVariants } from "@/components/ui/button";
import { ThemeToggle } from "@/components/ui/theme-toggle";
import { useConvexAuth } from "convex/react";
import { authClient } from "@/lib/auth-client";

export default function Navbar() {
const { isAuthenticated, isLoading } = useConvexAuth();
return (
<nav className={"w-full py-5 flex items-center justify-between"}>
<div className={"flex items-center gap-8"}>
Expand Down Expand Up @@ -30,15 +35,21 @@ export default function Navbar() {
</div>
</div>
<div className={"flex items-center gap-2"}>
<Link href={"/auth/sign-up"} className={buttonVariants()}>
Sign up
</Link>
<Link
href={"/auth/login"}
className={buttonVariants({ variant: "secondary" })}
>
Login
</Link>
{isLoading ? null : isAuthenticated ? (
<Button onClick={() => authClient.signOut({})}>LogOut</Button>
) : (
<>
<Link href={"/auth/sign-up"} className={buttonVariants()}>
Sign up
</Link>
<Link
href={"/auth/login"}
className={buttonVariants({ variant: "secondary" })}
>
Login
</Link>
</>
)}
<ThemeToggle />
</div>
</nav>
Expand Down
14 changes: 12 additions & 2 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@
* @module
*/

import type * as auth from "../auth.js";
import type * as http from "../http.js";
import type * as tasks from "../tasks.js";

import type {
ApiFromModules,
FilterApi,
FunctionReference,
} from "convex/server";

declare const fullApi: ApiFromModules<{}>;
declare const fullApi: ApiFromModules<{
auth: typeof auth;
http: typeof http;
tasks: typeof tasks;
}>;

/**
* A utility for referencing Convex functions in your app's public API.
Expand Down Expand Up @@ -42,4 +50,6 @@ export declare const internal: FilterApi<
FunctionReference<any, "internal">
>;

export declare const components: {};
export declare const components: {
betterAuth: import("@convex-dev/better-auth/_generated/component.js").ComponentApi<"betterAuth">;
};
6 changes: 6 additions & 0 deletions convex/auth.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { getAuthConfigProvider } from "@convex-dev/better-auth/auth-config";
import type { AuthConfig } from "convex/server";

export default {
providers: [getAuthConfigProvider()],
} satisfies AuthConfig;
38 changes: 38 additions & 0 deletions convex/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { createClient, type GenericCtx } from "@convex-dev/better-auth";
import { convex } from "@convex-dev/better-auth/plugins";
import { components } from "./_generated/api";
import { DataModel } from "./_generated/dataModel";
import { query } from "./_generated/server";
import { betterAuth } from "better-auth/minimal";
import authConfig from "./auth.config";

const siteUrl = process.env.SITE_URL!;

// The component client has methods needed for integrating Convex with Better Auth,
// as well as helper methods for general use.
export const authComponent = createClient<DataModel>(components.betterAuth);

export const createAuth = (ctx: GenericCtx<DataModel>) => {
return betterAuth({
baseURL: siteUrl,
database: authComponent.adapter(ctx),
// Configure simple, non-verified email/password to get started
emailAndPassword: {
enabled: true,
requireEmailVerification: false,
},
plugins: [
// The Convex plugin is required for Convex compatibility
convex({ authConfig }),
],
});
};

// Example function for getting the current user
// Feel free to edit, omit, etc.
export const getCurrentUser = query({
args: {},
handler: async (ctx) => {
return authComponent.getAuthUser(ctx);
},
});
7 changes: 7 additions & 0 deletions convex/convex.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineApp } from "convex/server";
import betterAuth from "@convex-dev/better-auth/convex.config";

const app = defineApp();
app.use(betterAuth);

export default app;
8 changes: 8 additions & 0 deletions convex/http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { httpRouter } from "convex/server";
import { authComponent, createAuth } from "./auth";

const http = httpRouter();

authComponent.registerRoutes(http, createAuth);

export default http;
8 changes: 8 additions & 0 deletions convex/tasks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { query } from "./_generated/server";

export const get = query({
args: {},
handler: async (ctx) => {
return await ctx.db.query("tasks").collect();
},
});
6 changes: 6 additions & 0 deletions lib/auth-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createAuthClient } from "better-auth/react";
import { convexClient } from "@convex-dev/better-auth/client/plugins";

export const authClient = createAuthClient({
plugins: [convexClient()],
});
14 changes: 14 additions & 0 deletions lib/auth-server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { convexBetterAuthNextJs } from "@convex-dev/better-auth/nextjs";

export const {
handler,
preloadAuthQuery,
isAuthenticated,
getToken,
fetchAuthQuery,
fetchAuthMutation,
fetchAuthAction,
} = convexBetterAuthNextJs({
convexUrl: process.env.NEXT_PUBLIC_CONVEX_URL!,
convexSiteUrl: process.env.NEXT_PUBLIC_CONVEX_SITE_URL!,
});
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"format:check": "prettier --check ."
},
"dependencies": {
"@convex-dev/better-auth": "^0.11.4",
"@hookform/resolvers": "^5.2.2",
"better-auth": "1.5.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"convex": "^1.35.1",
Expand Down
Loading
Loading