init commit
This commit is contained in:
47
.gitignore
vendored
Normal file
47
.gitignore
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# Hosting
|
||||
/host/convex/docker/data
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# Ignored for the template, you probably want to remove it:
|
||||
package-lock.json
|
1
.prettierrc
Normal file
1
.prettierrc
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
46
README.md
Normal file
46
README.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Welcome to your Convex + Next.js + Convex Auth app
|
||||
|
||||
This is a [Convex](https://convex.dev/) project created with [`npm create convex`](https://www.npmjs.com/package/create-convex).
|
||||
|
||||
After the initial setup (<2 minutes) you'll have a working full-stack app using:
|
||||
|
||||
- Convex as your backend (database, server logic)
|
||||
- [React](https://react.dev/) as your frontend (web page interactivity)
|
||||
- [Next.js](https://nextjs.org/) for optimized web hosting and page routing
|
||||
- [Tailwind](https://tailwindcss.com/) for building great looking accessible UI
|
||||
- [Convex Auth](https://labs.convex.dev/auth) for authentication
|
||||
|
||||
## Get started
|
||||
|
||||
If you just cloned this codebase and didn't use `npm create convex`, run:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
If you're reading this README on GitHub and want to use this template, run:
|
||||
|
||||
```
|
||||
npm create convex@latest -- -t nextjs-convexauth
|
||||
```
|
||||
|
||||
## Learn more
|
||||
|
||||
To learn more about developing your project with Convex, check out:
|
||||
|
||||
- The [Tour of Convex](https://docs.convex.dev/get-started) for a thorough introduction to Convex principles.
|
||||
- The rest of [Convex docs](https://docs.convex.dev/) to learn about all Convex features.
|
||||
- [Stack](https://stack.convex.dev/) for in-depth articles on advanced topics.
|
||||
- [Convex Auth docs](https://labs.convex.dev/auth) for documentation on the Convex Auth library.
|
||||
|
||||
## Configuring other authentication methods
|
||||
|
||||
To configure different authentication methods, see [Configuration](https://labs.convex.dev/auth/config) in the Convex Auth docs.
|
||||
|
||||
## Join the community
|
||||
|
||||
Join thousands of developers building full-stack apps with Convex:
|
||||
|
||||
- Join the [Convex Discord community](https://convex.dev/community) to get help in real-time.
|
||||
- Follow [Convex on GitHub](https://github.com/get-convex/), star and contribute to the open-source implementation of Convex.
|
26
app/globals.css
Normal file
26
app/globals.css
Normal file
@@ -0,0 +1,26 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--foreground);
|
||||
background: var(--background);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
40
app/layout.tsx
Normal file
40
app/layout.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { ConvexAuthNextjsServerProvider } from "@convex-dev/auth/nextjs/server";
|
||||
import ConvexClientProvider from "@/components/ConvexClientProvider";
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
icons: {
|
||||
icon: "/convex.svg",
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<ConvexAuthNextjsServerProvider>
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
<ConvexClientProvider>{children}</ConvexClientProvider>
|
||||
</body>
|
||||
</html>
|
||||
</ConvexAuthNextjsServerProvider>
|
||||
);
|
||||
}
|
159
app/page.tsx
Normal file
159
app/page.tsx
Normal file
@@ -0,0 +1,159 @@
|
||||
"use client";
|
||||
|
||||
import { useConvexAuth, useMutation, useQuery } from "convex/react";
|
||||
import { api } from "../convex/_generated/api";
|
||||
import Link from "next/link";
|
||||
import { useAuthActions } from "@convex-dev/auth/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<header className="sticky top-0 z-10 bg-background p-4 border-b-2 border-slate-200 dark:border-slate-800 flex flex-row justify-between items-center">
|
||||
Convex + Next.js + Convex Auth
|
||||
<SignOutButton />
|
||||
</header>
|
||||
<main className="p-8 flex flex-col gap-8">
|
||||
<h1 className="text-4xl font-bold text-center">
|
||||
Convex + Next.js + Convex Auth
|
||||
</h1>
|
||||
<Content />
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function SignOutButton() {
|
||||
const { isAuthenticated } = useConvexAuth();
|
||||
const { signOut } = useAuthActions();
|
||||
const router = useRouter();
|
||||
return (
|
||||
<>
|
||||
{isAuthenticated && (
|
||||
<button
|
||||
className="bg-slate-200 dark:bg-slate-800 text-foreground rounded-md px-2 py-1"
|
||||
onClick={() =>
|
||||
void signOut().then(() => {
|
||||
router.push("/signin");
|
||||
})
|
||||
}
|
||||
>
|
||||
Sign out
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Content() {
|
||||
const { viewer, numbers } =
|
||||
useQuery(api.myFunctions.listNumbers, {
|
||||
count: 10,
|
||||
}) ?? {};
|
||||
const addNumber = useMutation(api.myFunctions.addNumber);
|
||||
|
||||
if (viewer === undefined || numbers === undefined) {
|
||||
return (
|
||||
<div className="mx-auto">
|
||||
<p>loading... (consider a loading skeleton)</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-8 max-w-lg mx-auto">
|
||||
<p>Welcome {viewer ?? "Anonymous"}!</p>
|
||||
<p>
|
||||
Click the button below and open this page in another window - this data
|
||||
is persisted in the Convex cloud database!
|
||||
</p>
|
||||
<p>
|
||||
<button
|
||||
className="bg-foreground text-background text-sm px-4 py-2 rounded-md"
|
||||
onClick={() => {
|
||||
void addNumber({ value: Math.floor(Math.random() * 10) });
|
||||
}}
|
||||
>
|
||||
Add a random number
|
||||
</button>
|
||||
</p>
|
||||
<p>
|
||||
Numbers:{" "}
|
||||
{numbers?.length === 0
|
||||
? "Click the button!"
|
||||
: (numbers?.join(", ") ?? "...")}
|
||||
</p>
|
||||
<p>
|
||||
Edit{" "}
|
||||
<code className="text-sm font-bold font-mono bg-slate-200 dark:bg-slate-800 px-1 py-0.5 rounded-md">
|
||||
convex/myFunctions.ts
|
||||
</code>{" "}
|
||||
to change your backend
|
||||
</p>
|
||||
<p>
|
||||
Edit{" "}
|
||||
<code className="text-sm font-bold font-mono bg-slate-200 dark:bg-slate-800 px-1 py-0.5 rounded-md">
|
||||
app/page.tsx
|
||||
</code>{" "}
|
||||
to change your frontend
|
||||
</p>
|
||||
<p>
|
||||
See the{" "}
|
||||
<Link href="/server" className="underline hover:no-underline">
|
||||
/server route
|
||||
</Link>{" "}
|
||||
for an example of loading data in a server component
|
||||
</p>
|
||||
<div className="flex flex-col">
|
||||
<p className="text-lg font-bold">Useful resources:</p>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex flex-col gap-2 w-1/2">
|
||||
<ResourceCard
|
||||
title="Convex docs"
|
||||
description="Read comprehensive documentation for all Convex features."
|
||||
href="https://docs.convex.dev/home"
|
||||
/>
|
||||
<ResourceCard
|
||||
title="Stack articles"
|
||||
description="Learn about best practices, use cases, and more from a growing
|
||||
collection of articles, videos, and walkthroughs."
|
||||
href="https://www.typescriptlang.org/docs/handbook/2/basic-types.html"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 w-1/2">
|
||||
<ResourceCard
|
||||
title="Templates"
|
||||
description="Browse our collection of templates to get started quickly."
|
||||
href="https://www.convex.dev/templates"
|
||||
/>
|
||||
<ResourceCard
|
||||
title="Discord"
|
||||
description="Join our developer community to ask questions, trade tips & tricks,
|
||||
and show off your projects."
|
||||
href="https://www.convex.dev/community"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ResourceCard({
|
||||
title,
|
||||
description,
|
||||
href,
|
||||
}: {
|
||||
title: string;
|
||||
description: string;
|
||||
href: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex flex-col gap-2 bg-slate-200 dark:bg-slate-800 p-4 rounded-md h-28 overflow-auto">
|
||||
<a href={href} className="text-sm underline hover:no-underline">
|
||||
{title}
|
||||
</a>
|
||||
<p className="text-xs">{description}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
31
app/server/inner.tsx
Normal file
31
app/server/inner.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
"use client";
|
||||
|
||||
import { Preloaded, useMutation, usePreloadedQuery } from "convex/react";
|
||||
import { api } from "../../convex/_generated/api";
|
||||
|
||||
export default function Home({
|
||||
preloaded,
|
||||
}: {
|
||||
preloaded: Preloaded<typeof api.myFunctions.listNumbers>;
|
||||
}) {
|
||||
const data = usePreloadedQuery(preloaded);
|
||||
const addNumber = useMutation(api.myFunctions.addNumber);
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-4 bg-slate-200 dark:bg-slate-800 p-4 rounded-md">
|
||||
<h2 className="text-xl font-bold">Reactive client-loaded data</h2>
|
||||
<code>
|
||||
<pre>{JSON.stringify(data, null, 2)}</pre>
|
||||
</code>
|
||||
</div>
|
||||
<button
|
||||
className="bg-foreground text-background px-4 py-2 rounded-md mx-auto"
|
||||
onClick={() => {
|
||||
void addNumber({ value: Math.floor(Math.random() * 10) });
|
||||
}}
|
||||
>
|
||||
Add a random number
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
24
app/server/page.tsx
Normal file
24
app/server/page.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import Home from "./inner";
|
||||
import { preloadQuery, preloadedQueryResult } from "convex/nextjs";
|
||||
import { api } from "@/convex/_generated/api";
|
||||
|
||||
export default async function ServerPage() {
|
||||
const preloaded = await preloadQuery(api.myFunctions.listNumbers, {
|
||||
count: 3,
|
||||
});
|
||||
|
||||
const data = preloadedQueryResult(preloaded);
|
||||
|
||||
return (
|
||||
<main className="p-8 flex flex-col gap-4 mx-auto max-w-2xl">
|
||||
<h1 className="text-4xl font-bold text-center">Convex + Next.js</h1>
|
||||
<div className="flex flex-col gap-4 bg-slate-200 dark:bg-slate-800 p-4 rounded-md">
|
||||
<h2 className="text-xl font-bold">Non-reactive server-loaded data</h2>
|
||||
<code>
|
||||
<pre>{JSON.stringify(data, null, 2)}</pre>
|
||||
</code>
|
||||
</div>
|
||||
<Home preloaded={preloaded} />
|
||||
</main>
|
||||
);
|
||||
}
|
71
app/signin/page.tsx
Normal file
71
app/signin/page.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
"use client";
|
||||
|
||||
import { useAuthActions } from "@convex-dev/auth/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function SignIn() {
|
||||
const { signIn } = useAuthActions();
|
||||
const [flow, setFlow] = useState<"signIn" | "signUp">("signIn");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const router = useRouter();
|
||||
return (
|
||||
<div className="flex flex-col gap-8 w-96 mx-auto h-screen justify-center items-center">
|
||||
<p>Log in to see the numbers</p>
|
||||
<form
|
||||
className="flex flex-col gap-2"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.target as HTMLFormElement);
|
||||
formData.set("flow", flow);
|
||||
void signIn("password", formData)
|
||||
.catch((error) => {
|
||||
setError(error.message);
|
||||
})
|
||||
.then(() => {
|
||||
router.push("/");
|
||||
});
|
||||
}}
|
||||
>
|
||||
<input
|
||||
className="bg-background text-foreground rounded-md p-2 border-2 border-slate-200 dark:border-slate-800"
|
||||
type="email"
|
||||
name="email"
|
||||
placeholder="Email"
|
||||
/>
|
||||
<input
|
||||
className="bg-background text-foreground rounded-md p-2 border-2 border-slate-200 dark:border-slate-800"
|
||||
type="password"
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
/>
|
||||
<button
|
||||
className="bg-foreground text-background rounded-md"
|
||||
type="submit"
|
||||
>
|
||||
{flow === "signIn" ? "Sign in" : "Sign up"}
|
||||
</button>
|
||||
<div className="flex flex-row gap-2">
|
||||
<span>
|
||||
{flow === "signIn"
|
||||
? "Don't have an account?"
|
||||
: "Already have an account?"}
|
||||
</span>
|
||||
<span
|
||||
className="text-foreground underline hover:no-underline cursor-pointer"
|
||||
onClick={() => setFlow(flow === "signIn" ? "signUp" : "signIn")}
|
||||
>
|
||||
{flow === "signIn" ? "Sign up instead" : "Sign in instead"}
|
||||
</span>
|
||||
</div>
|
||||
{error && (
|
||||
<div className="bg-red-500/20 border-2 border-red-500/50 rounded-md p-2">
|
||||
<p className="text-foreground font-mono text-xs">
|
||||
Error signing in: {error}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
19
components/ConvexClientProvider.tsx
Normal file
19
components/ConvexClientProvider.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
"use client";
|
||||
|
||||
import { ConvexAuthNextjsProvider } from "@convex-dev/auth/nextjs";
|
||||
import { ConvexReactClient } from "convex/react";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
|
||||
|
||||
export default function ConvexClientProvider({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<ConvexAuthNextjsProvider client={convex}>
|
||||
{children}
|
||||
</ConvexAuthNextjsProvider>
|
||||
);
|
||||
}
|
90
convex/README.md
Normal file
90
convex/README.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Welcome to your Convex functions directory!
|
||||
|
||||
Write your Convex functions here.
|
||||
See https://docs.convex.dev/functions for more.
|
||||
|
||||
A query function that takes two arguments looks like:
|
||||
|
||||
```ts
|
||||
// functions.js
|
||||
import { query } from "./_generated/server";
|
||||
import { v } from "convex/values";
|
||||
|
||||
export const myQueryFunction = query({
|
||||
// Validators for arguments.
|
||||
args: {
|
||||
first: v.number(),
|
||||
second: v.string(),
|
||||
},
|
||||
|
||||
// Function implementation.
|
||||
handler: async (ctx, args) => {
|
||||
// Read the database as many times as you need here.
|
||||
// See https://docs.convex.dev/database/reading-data.
|
||||
const documents = await ctx.db.query("tablename").collect();
|
||||
|
||||
// Arguments passed from the client are properties of the args object.
|
||||
console.log(args.first, args.second);
|
||||
|
||||
// Write arbitrary JavaScript here: filter, aggregate, build derived data,
|
||||
// remove non-public properties, or create new objects.
|
||||
return documents;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Using this query function in a React component looks like:
|
||||
|
||||
```ts
|
||||
const data = useQuery(api.functions.myQueryFunction, {
|
||||
first: 10,
|
||||
second: "hello",
|
||||
});
|
||||
```
|
||||
|
||||
A mutation function looks like:
|
||||
|
||||
```ts
|
||||
// functions.js
|
||||
import { mutation } from "./_generated/server";
|
||||
import { v } from "convex/values";
|
||||
|
||||
export const myMutationFunction = mutation({
|
||||
// Validators for arguments.
|
||||
args: {
|
||||
first: v.string(),
|
||||
second: v.string(),
|
||||
},
|
||||
|
||||
// Function implementation.
|
||||
handler: async (ctx, args) => {
|
||||
// Insert or modify documents in the database here.
|
||||
// Mutations can also read from the database like queries.
|
||||
// See https://docs.convex.dev/database/writing-data.
|
||||
const message = { body: args.first, author: args.second };
|
||||
const id = await ctx.db.insert("messages", message);
|
||||
|
||||
// Optionally, return a value from your mutation.
|
||||
return await ctx.db.get(id);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Using this mutation function in a React component looks like:
|
||||
|
||||
```ts
|
||||
const mutation = useMutation(api.functions.myMutationFunction);
|
||||
function handleButtonPress() {
|
||||
// fire and forget, the most common way to use mutations
|
||||
mutation({ first: "Hello!", second: "me" });
|
||||
// OR
|
||||
// use the result once the mutation has completed
|
||||
mutation({ first: "Hello!", second: "me" }).then((result) =>
|
||||
console.log(result),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Use the Convex CLI to push your functions to a deployment. See everything
|
||||
the Convex CLI can do by running `npx convex -h` in your project root
|
||||
directory. To learn more, launch the docs with `npx convex docs`.
|
40
convex/_generated/api.d.ts
vendored
Normal file
40
convex/_generated/api.d.ts
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Generated `api` utility.
|
||||
*
|
||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
||||
*
|
||||
* To regenerate, run `npx convex dev`.
|
||||
* @module
|
||||
*/
|
||||
|
||||
import type {
|
||||
ApiFromModules,
|
||||
FilterApi,
|
||||
FunctionReference,
|
||||
} from "convex/server";
|
||||
import type * as auth from "../auth.js";
|
||||
import type * as http from "../http.js";
|
||||
import type * as myFunctions from "../myFunctions.js";
|
||||
|
||||
/**
|
||||
* A utility for referencing Convex functions in your app's API.
|
||||
*
|
||||
* Usage:
|
||||
* ```js
|
||||
* const myFunctionReference = api.myModule.myFunction;
|
||||
* ```
|
||||
*/
|
||||
declare const fullApi: ApiFromModules<{
|
||||
auth: typeof auth;
|
||||
http: typeof http;
|
||||
myFunctions: typeof myFunctions;
|
||||
}>;
|
||||
export declare const api: FilterApi<
|
||||
typeof fullApi,
|
||||
FunctionReference<any, "public">
|
||||
>;
|
||||
export declare const internal: FilterApi<
|
||||
typeof fullApi,
|
||||
FunctionReference<any, "internal">
|
||||
>;
|
22
convex/_generated/api.js
Normal file
22
convex/_generated/api.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Generated `api` utility.
|
||||
*
|
||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
||||
*
|
||||
* To regenerate, run `npx convex dev`.
|
||||
* @module
|
||||
*/
|
||||
|
||||
import { anyApi } from "convex/server";
|
||||
|
||||
/**
|
||||
* A utility for referencing Convex functions in your app's API.
|
||||
*
|
||||
* Usage:
|
||||
* ```js
|
||||
* const myFunctionReference = api.myModule.myFunction;
|
||||
* ```
|
||||
*/
|
||||
export const api = anyApi;
|
||||
export const internal = anyApi;
|
60
convex/_generated/dataModel.d.ts
vendored
Normal file
60
convex/_generated/dataModel.d.ts
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Generated data model types.
|
||||
*
|
||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
||||
*
|
||||
* To regenerate, run `npx convex dev`.
|
||||
* @module
|
||||
*/
|
||||
|
||||
import type {
|
||||
DataModelFromSchemaDefinition,
|
||||
DocumentByName,
|
||||
TableNamesInDataModel,
|
||||
SystemTableNames,
|
||||
} from "convex/server";
|
||||
import type { GenericId } from "convex/values";
|
||||
import schema from "../schema.js";
|
||||
|
||||
/**
|
||||
* The names of all of your Convex tables.
|
||||
*/
|
||||
export type TableNames = TableNamesInDataModel<DataModel>;
|
||||
|
||||
/**
|
||||
* The type of a document stored in Convex.
|
||||
*
|
||||
* @typeParam TableName - A string literal type of the table name (like "users").
|
||||
*/
|
||||
export type Doc<TableName extends TableNames> = DocumentByName<
|
||||
DataModel,
|
||||
TableName
|
||||
>;
|
||||
|
||||
/**
|
||||
* An identifier for a document in Convex.
|
||||
*
|
||||
* Convex documents are uniquely identified by their `Id`, which is accessible
|
||||
* on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).
|
||||
*
|
||||
* Documents can be loaded using `db.get(id)` in query and mutation functions.
|
||||
*
|
||||
* IDs are just strings at runtime, but this type can be used to distinguish them from other
|
||||
* strings when type checking.
|
||||
*
|
||||
* @typeParam TableName - A string literal type of the table name (like "users").
|
||||
*/
|
||||
export type Id<TableName extends TableNames | SystemTableNames> =
|
||||
GenericId<TableName>;
|
||||
|
||||
/**
|
||||
* A type describing your Convex data model.
|
||||
*
|
||||
* This type includes information about what tables you have, the type of
|
||||
* documents stored in those tables, and the indexes defined on them.
|
||||
*
|
||||
* This type is used to parameterize methods like `queryGeneric` and
|
||||
* `mutationGeneric` to make them type-safe.
|
||||
*/
|
||||
export type DataModel = DataModelFromSchemaDefinition<typeof schema>;
|
142
convex/_generated/server.d.ts
vendored
Normal file
142
convex/_generated/server.d.ts
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Generated utilities for implementing server-side Convex query and mutation functions.
|
||||
*
|
||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
||||
*
|
||||
* To regenerate, run `npx convex dev`.
|
||||
* @module
|
||||
*/
|
||||
|
||||
import {
|
||||
ActionBuilder,
|
||||
HttpActionBuilder,
|
||||
MutationBuilder,
|
||||
QueryBuilder,
|
||||
GenericActionCtx,
|
||||
GenericMutationCtx,
|
||||
GenericQueryCtx,
|
||||
GenericDatabaseReader,
|
||||
GenericDatabaseWriter,
|
||||
} from "convex/server";
|
||||
import type { DataModel } from "./dataModel.js";
|
||||
|
||||
/**
|
||||
* Define a query in this Convex app's public API.
|
||||
*
|
||||
* This function will be allowed to read your Convex database and will be accessible from the client.
|
||||
*
|
||||
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
||||
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
||||
*/
|
||||
export declare const query: QueryBuilder<DataModel, "public">;
|
||||
|
||||
/**
|
||||
* Define a query that is only accessible from other Convex functions (but not from the client).
|
||||
*
|
||||
* This function will be allowed to read from your Convex database. It will not be accessible from the client.
|
||||
*
|
||||
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
||||
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
||||
*/
|
||||
export declare const internalQuery: QueryBuilder<DataModel, "internal">;
|
||||
|
||||
/**
|
||||
* Define a mutation in this Convex app's public API.
|
||||
*
|
||||
* This function will be allowed to modify your Convex database and will be accessible from the client.
|
||||
*
|
||||
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
||||
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
||||
*/
|
||||
export declare const mutation: MutationBuilder<DataModel, "public">;
|
||||
|
||||
/**
|
||||
* Define a mutation that is only accessible from other Convex functions (but not from the client).
|
||||
*
|
||||
* This function will be allowed to modify your Convex database. It will not be accessible from the client.
|
||||
*
|
||||
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
||||
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
||||
*/
|
||||
export declare const internalMutation: MutationBuilder<DataModel, "internal">;
|
||||
|
||||
/**
|
||||
* Define an action in this Convex app's public API.
|
||||
*
|
||||
* An action is a function which can execute any JavaScript code, including non-deterministic
|
||||
* code and code with side-effects, like calling third-party services.
|
||||
* They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
|
||||
* They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
|
||||
*
|
||||
* @param func - The action. It receives an {@link ActionCtx} as its first argument.
|
||||
* @returns The wrapped action. Include this as an `export` to name it and make it accessible.
|
||||
*/
|
||||
export declare const action: ActionBuilder<DataModel, "public">;
|
||||
|
||||
/**
|
||||
* Define an action that is only accessible from other Convex functions (but not from the client).
|
||||
*
|
||||
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
|
||||
* @returns The wrapped function. Include this as an `export` to name it and make it accessible.
|
||||
*/
|
||||
export declare const internalAction: ActionBuilder<DataModel, "internal">;
|
||||
|
||||
/**
|
||||
* Define an HTTP action.
|
||||
*
|
||||
* This function will be used to respond to HTTP requests received by a Convex
|
||||
* deployment if the requests matches the path and method where this action
|
||||
* is routed. Be sure to route your action in `convex/http.js`.
|
||||
*
|
||||
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
|
||||
* @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.
|
||||
*/
|
||||
export declare const httpAction: HttpActionBuilder;
|
||||
|
||||
/**
|
||||
* A set of services for use within Convex query functions.
|
||||
*
|
||||
* The query context is passed as the first argument to any Convex query
|
||||
* function run on the server.
|
||||
*
|
||||
* This differs from the {@link MutationCtx} because all of the services are
|
||||
* read-only.
|
||||
*/
|
||||
export type QueryCtx = GenericQueryCtx<DataModel>;
|
||||
|
||||
/**
|
||||
* A set of services for use within Convex mutation functions.
|
||||
*
|
||||
* The mutation context is passed as the first argument to any Convex mutation
|
||||
* function run on the server.
|
||||
*/
|
||||
export type MutationCtx = GenericMutationCtx<DataModel>;
|
||||
|
||||
/**
|
||||
* A set of services for use within Convex action functions.
|
||||
*
|
||||
* The action context is passed as the first argument to any Convex action
|
||||
* function run on the server.
|
||||
*/
|
||||
export type ActionCtx = GenericActionCtx<DataModel>;
|
||||
|
||||
/**
|
||||
* An interface to read from the database within Convex query functions.
|
||||
*
|
||||
* The two entry points are {@link DatabaseReader.get}, which fetches a single
|
||||
* document by its {@link Id}, or {@link DatabaseReader.query}, which starts
|
||||
* building a query.
|
||||
*/
|
||||
export type DatabaseReader = GenericDatabaseReader<DataModel>;
|
||||
|
||||
/**
|
||||
* An interface to read from and write to the database within Convex mutation
|
||||
* functions.
|
||||
*
|
||||
* Convex guarantees that all writes within a single mutation are
|
||||
* executed atomically, so you never have to worry about partial writes leaving
|
||||
* your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control)
|
||||
* for the guarantees Convex provides your functions.
|
||||
*/
|
||||
export type DatabaseWriter = GenericDatabaseWriter<DataModel>;
|
89
convex/_generated/server.js
Normal file
89
convex/_generated/server.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Generated utilities for implementing server-side Convex query and mutation functions.
|
||||
*
|
||||
* THIS CODE IS AUTOMATICALLY GENERATED.
|
||||
*
|
||||
* To regenerate, run `npx convex dev`.
|
||||
* @module
|
||||
*/
|
||||
|
||||
import {
|
||||
actionGeneric,
|
||||
httpActionGeneric,
|
||||
queryGeneric,
|
||||
mutationGeneric,
|
||||
internalActionGeneric,
|
||||
internalMutationGeneric,
|
||||
internalQueryGeneric,
|
||||
} from "convex/server";
|
||||
|
||||
/**
|
||||
* Define a query in this Convex app's public API.
|
||||
*
|
||||
* This function will be allowed to read your Convex database and will be accessible from the client.
|
||||
*
|
||||
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
||||
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
||||
*/
|
||||
export const query = queryGeneric;
|
||||
|
||||
/**
|
||||
* Define a query that is only accessible from other Convex functions (but not from the client).
|
||||
*
|
||||
* This function will be allowed to read from your Convex database. It will not be accessible from the client.
|
||||
*
|
||||
* @param func - The query function. It receives a {@link QueryCtx} as its first argument.
|
||||
* @returns The wrapped query. Include this as an `export` to name it and make it accessible.
|
||||
*/
|
||||
export const internalQuery = internalQueryGeneric;
|
||||
|
||||
/**
|
||||
* Define a mutation in this Convex app's public API.
|
||||
*
|
||||
* This function will be allowed to modify your Convex database and will be accessible from the client.
|
||||
*
|
||||
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
||||
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
||||
*/
|
||||
export const mutation = mutationGeneric;
|
||||
|
||||
/**
|
||||
* Define a mutation that is only accessible from other Convex functions (but not from the client).
|
||||
*
|
||||
* This function will be allowed to modify your Convex database. It will not be accessible from the client.
|
||||
*
|
||||
* @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
|
||||
* @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
|
||||
*/
|
||||
export const internalMutation = internalMutationGeneric;
|
||||
|
||||
/**
|
||||
* Define an action in this Convex app's public API.
|
||||
*
|
||||
* An action is a function which can execute any JavaScript code, including non-deterministic
|
||||
* code and code with side-effects, like calling third-party services.
|
||||
* They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
|
||||
* They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
|
||||
*
|
||||
* @param func - The action. It receives an {@link ActionCtx} as its first argument.
|
||||
* @returns The wrapped action. Include this as an `export` to name it and make it accessible.
|
||||
*/
|
||||
export const action = actionGeneric;
|
||||
|
||||
/**
|
||||
* Define an action that is only accessible from other Convex functions (but not from the client).
|
||||
*
|
||||
* @param func - The function. It receives an {@link ActionCtx} as its first argument.
|
||||
* @returns The wrapped function. Include this as an `export` to name it and make it accessible.
|
||||
*/
|
||||
export const internalAction = internalActionGeneric;
|
||||
|
||||
/**
|
||||
* Define a Convex HTTP action.
|
||||
*
|
||||
* @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object
|
||||
* as its second.
|
||||
* @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.
|
||||
*/
|
||||
export const httpAction = httpActionGeneric;
|
8
convex/auth.config.ts
Normal file
8
convex/auth.config.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export default {
|
||||
providers: [
|
||||
{
|
||||
domain: process.env.CONVEX_SITE_URL,
|
||||
applicationID: "convex",
|
||||
},
|
||||
],
|
||||
};
|
6
convex/auth.ts
Normal file
6
convex/auth.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Password } from "@convex-dev/auth/providers/Password";
|
||||
import { convexAuth } from "@convex-dev/auth/server";
|
||||
|
||||
export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
|
||||
providers: [Password],
|
||||
});
|
8
convex/http.ts
Normal file
8
convex/http.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { httpRouter } from "convex/server";
|
||||
import { auth } from "./auth";
|
||||
|
||||
const http = httpRouter();
|
||||
|
||||
auth.addHttpRoutes(http);
|
||||
|
||||
export default http;
|
81
convex/myFunctions.ts
Normal file
81
convex/myFunctions.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { v } from "convex/values";
|
||||
import { query, mutation, action } from "./_generated/server";
|
||||
import { api } from "./_generated/api";
|
||||
import { getAuthUserId } from "@convex-dev/auth/server";
|
||||
|
||||
// Write your Convex functions in any file inside this directory (`convex`).
|
||||
// See https://docs.convex.dev/functions for more.
|
||||
|
||||
// You can read data from the database via a query:
|
||||
export const listNumbers = query({
|
||||
// Validators for arguments.
|
||||
args: {
|
||||
count: v.number(),
|
||||
},
|
||||
|
||||
// Query implementation.
|
||||
handler: async (ctx, args) => {
|
||||
//// Read the database as many times as you need here.
|
||||
//// See https://docs.convex.dev/database/reading-data.
|
||||
const numbers = await ctx.db
|
||||
.query("numbers")
|
||||
// Ordered by _creationTime, return most recent
|
||||
.order("desc")
|
||||
.take(args.count);
|
||||
const userId = await getAuthUserId(ctx);
|
||||
const user = userId === null ? null : await ctx.db.get(userId);
|
||||
return {
|
||||
viewer: user?.email ?? null,
|
||||
numbers: numbers.reverse().map((number) => number.value),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
// You can write data to the database via a mutation:
|
||||
export const addNumber = mutation({
|
||||
// Validators for arguments.
|
||||
args: {
|
||||
value: v.number(),
|
||||
},
|
||||
|
||||
// Mutation implementation.
|
||||
handler: async (ctx, args) => {
|
||||
//// Insert or modify documents in the database here.
|
||||
//// Mutations can also read from the database like queries.
|
||||
//// See https://docs.convex.dev/database/writing-data.
|
||||
|
||||
const id = await ctx.db.insert("numbers", { value: args.value });
|
||||
|
||||
console.log("Added new document with id:", id);
|
||||
// Optionally, return a value from your mutation.
|
||||
// return id;
|
||||
},
|
||||
});
|
||||
|
||||
// You can fetch data from and send data to third-party APIs via an action:
|
||||
export const myAction = action({
|
||||
// Validators for arguments.
|
||||
args: {
|
||||
first: v.number(),
|
||||
second: v.string(),
|
||||
},
|
||||
|
||||
// Action implementation.
|
||||
handler: async (ctx, args) => {
|
||||
//// Use the browser-like `fetch` API to send HTTP requests.
|
||||
//// See https://docs.convex.dev/functions/actions#calling-third-party-apis-and-using-npm-packages.
|
||||
// const response = await ctx.fetch("https://api.thirdpartyservice.com");
|
||||
// const data = await response.json();
|
||||
|
||||
//// Query data by running Convex queries.
|
||||
const data = await ctx.runQuery(api.myFunctions.listNumbers, {
|
||||
count: 10,
|
||||
});
|
||||
console.log(data);
|
||||
|
||||
//// Write data by running Convex mutations.
|
||||
await ctx.runMutation(api.myFunctions.addNumber, {
|
||||
value: args.first,
|
||||
});
|
||||
},
|
||||
});
|
13
convex/schema.ts
Normal file
13
convex/schema.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { defineSchema, defineTable } from "convex/server";
|
||||
import { v } from "convex/values";
|
||||
import { authTables } from "@convex-dev/auth/server";
|
||||
|
||||
// The schema is normally optional, but Convex Auth
|
||||
// requires indexes defined on `authTables`.
|
||||
// The schema provides more precise TypeScript types.
|
||||
export default defineSchema({
|
||||
...authTables,
|
||||
numbers: defineTable({
|
||||
value: v.number(),
|
||||
}),
|
||||
});
|
25
convex/tsconfig.json
Normal file
25
convex/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
/* This TypeScript project config describes the environment that
|
||||
* Convex functions run in and is used to typecheck them.
|
||||
* You can modify it, but some settings required to use Convex.
|
||||
*/
|
||||
"compilerOptions": {
|
||||
/* These settings are not required by Convex and can be modified. */
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "Bundler",
|
||||
"jsx": "react-jsx",
|
||||
"skipLibCheck": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
|
||||
/* These compiler options are required by Convex */
|
||||
"target": "ESNext",
|
||||
"lib": ["ES2021", "dom"],
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "ESNext",
|
||||
"isolatedModules": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["./**/*"],
|
||||
"exclude": ["./_generated"]
|
||||
}
|
16
eslint.config.mjs
Normal file
16
eslint.config.mjs
Normal file
@@ -0,0 +1,16 @@
|
||||
import { dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
});
|
||||
|
||||
const eslintConfig = [
|
||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||
];
|
||||
|
||||
export default eslintConfig;
|
51
host/convex/docker/compose.yml
Normal file
51
host/convex/docker/compose.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
networks:
|
||||
nginx-bridge:
|
||||
external: true
|
||||
services:
|
||||
backend:
|
||||
image: ghcr.io/get-convex/convex-backend:08139ef318b1898dad7731910f49ba631631c902
|
||||
container_name: ${BACKEND_CONTAINER_NAME:-convex-backend}
|
||||
hostname: convex-backend
|
||||
domainname: convex.gbrown.org
|
||||
networks: [nginx-bridge]
|
||||
#ports: [3210:3210,3211:3211] # 3210: API | 3211: Base URL
|
||||
volumes: [./data:/convex/data]
|
||||
labels: ['com.centurylinklabs.watchtower.enable=true']
|
||||
env_file: [./.env]
|
||||
environment:
|
||||
- INSTANCE_NAME=${INSTANCE_NAME:-}
|
||||
- INSTANCE_SECRET=${INSTANCE_SECRET:-}
|
||||
- CONVEX_CLOUD_ORIGIN=${CONVEX_CLOUD_ORIGIN:-}
|
||||
- CONVEX_SITE_ORIGIN=${CONVEX_SITE_ORIGIN:-}
|
||||
- DISABLE_BEACON=${DISABLE_BEACON:true}
|
||||
- REDACT_LOGS_TO_CLIENT=${REDACT_LOGS_TO_CLIENT:true}
|
||||
- DO_NOT_REQUIRE_SSL=${DO_NOT_REQUIRE_SSL:true}
|
||||
stdin_open: true
|
||||
tty: true
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: curl -f http://localhost:3210/version
|
||||
interval: 5s
|
||||
start_period: 10s
|
||||
stop_grace_period: 10s
|
||||
stop_signal: SIGINT
|
||||
|
||||
dashboard:
|
||||
image: ghcr.io/get-convex/convex-dashboard:33cef775a8a6228cbacee4a09ac2c4073d62ed13
|
||||
container_name: ${DASHBOARD_CONTAINER_NAME:-convex-dashboard}
|
||||
hostname: convex-dashboard
|
||||
domainname: dashboard.convex.gbrown.org
|
||||
networks: [nginx-bridge]
|
||||
stop_grace_period: 10s
|
||||
stop_signal: SIGINT
|
||||
#ports: [6791:6791] # Dashboard
|
||||
labels: ['com.centurylinklabs.watchtower.enable=true']
|
||||
env_file: [.env]
|
||||
environment:
|
||||
- NEXT_PUBLIC_DEPLOYMENT_URL=${NEXT_PUBLIC_DEPLOYMENT_URL:-http://${BACKEND_CONTAINER_NAME:-convex-backend}:${PORT:-3210}}
|
||||
depends_on:
|
||||
backend:
|
||||
condition: service_healthy
|
||||
stdin_open: true
|
||||
tty: true
|
||||
restart: unless-stopped
|
23
middleware.ts
Normal file
23
middleware.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
convexAuthNextjsMiddleware,
|
||||
createRouteMatcher,
|
||||
nextjsMiddlewareRedirect,
|
||||
} from "@convex-dev/auth/nextjs/server";
|
||||
|
||||
const isSignInPage = createRouteMatcher(["/signin"]);
|
||||
const isProtectedRoute = createRouteMatcher(["/", "/server"]);
|
||||
|
||||
export default convexAuthNextjsMiddleware(async (request, { convexAuth }) => {
|
||||
if (isSignInPage(request) && (await convexAuth.isAuthenticated())) {
|
||||
return nextjsMiddlewareRedirect(request, "/");
|
||||
}
|
||||
if (isProtectedRoute(request) && !(await convexAuth.isAuthenticated())) {
|
||||
return nextjsMiddlewareRedirect(request, "/signin");
|
||||
}
|
||||
});
|
||||
|
||||
export const config = {
|
||||
// The following matcher runs middleware on all routes
|
||||
// except static assets.
|
||||
matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
|
||||
};
|
7
next.config.ts
Normal file
7
next.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
35
package.json
Normal file
35
package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "example",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm-run-all --parallel dev:frontend dev:backend",
|
||||
"dev:frontend": "next dev",
|
||||
"dev:backend": "convex dev",
|
||||
"predev": "convex dev --until-success && convex dev --once --run-sh \"node setup.mjs --once\" && convex dashboard",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@convex-dev/auth": "^0.0.81",
|
||||
"convex": "^1.26.0",
|
||||
"next": "15.2.3",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"dotenv": "^16.4.7",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.2.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.5.3",
|
||||
"tailwindcss": "^4",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
5
postcss.config.mjs
Normal file
5
postcss.config.mjs
Normal file
@@ -0,0 +1,5 @@
|
||||
const config = {
|
||||
plugins: ["@tailwindcss/postcss"],
|
||||
};
|
||||
|
||||
export default config;
|
17
public/convex.svg
Normal file
17
public/convex.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 367 370" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1,0,0,1,-129.225,-127.948)">
|
||||
<g id="Layer-1" serif:id="Layer 1" transform="matrix(4.16667,0,0,4.16667,0,0)">
|
||||
<g transform="matrix(1,0,0,1,86.6099,107.074)">
|
||||
<path d="M0,-6.544C13.098,-7.973 25.449,-14.834 32.255,-26.287C29.037,2.033 -2.48,19.936 -28.196,8.94C-30.569,7.925 -32.605,6.254 -34.008,4.088C-39.789,-4.83 -41.69,-16.18 -38.963,-26.48C-31.158,-13.247 -15.3,-5.131 0,-6.544" style="fill:rgb(245,176,26);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,47.1708,74.7779)">
|
||||
<path d="M0,-2.489C-5.312,9.568 -5.545,23.695 0.971,35.316C-21.946,18.37 -21.692,-17.876 0.689,-34.65C2.754,-36.197 5.219,-37.124 7.797,-37.257C18.41,-37.805 29.19,-33.775 36.747,-26.264C21.384,-26.121 6.427,-16.446 0,-2.489" style="fill:rgb(141,37,118);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,91.325,66.4152)">
|
||||
<path d="M0,-14.199C-7.749,-24.821 -19.884,-32.044 -33.173,-32.264C-7.482,-43.726 24.112,-25.143 27.557,2.322C27.877,4.876 27.458,7.469 26.305,9.769C21.503,19.345 12.602,26.776 2.203,29.527C9.838,15.64 8.889,-1.328 0,-14.199" style="fill:rgb(238,52,47);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
35
setup.mjs
Normal file
35
setup.mjs
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* This script runs `npx @convex-dev/auth` to help with setting up
|
||||
* environment variables for Convex Auth.
|
||||
*
|
||||
* You can safely delete it and remove it from package.json scripts.
|
||||
*/
|
||||
|
||||
import fs from "fs";
|
||||
import { config as loadEnvFile } from "dotenv";
|
||||
import { spawnSync } from "child_process";
|
||||
|
||||
if (!fs.existsSync(".env.local")) {
|
||||
// Something is off, skip the script.
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const config = {};
|
||||
loadEnvFile({ path: ".env.local", processEnv: config });
|
||||
|
||||
const runOnceWorkflow = process.argv.includes("--once");
|
||||
|
||||
if (runOnceWorkflow && config.SETUP_SCRIPT_RAN !== undefined) {
|
||||
// The script has already ran once, skip.
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const result = spawnSync("npx", ["@convex-dev/auth", "--skip-git-check"], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
if (runOnceWorkflow) {
|
||||
fs.writeFileSync(".env.local", `\nSETUP_SCRIPT_RAN=1\n`, { flag: "a" });
|
||||
}
|
||||
|
||||
process.exit(result.status);
|
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
Reference in New Issue
Block a user