Skip to content

fix predev script quotes for windows #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
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
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Welcome to your Convex + React (Vite) app
# Welcome to your Convex + React (Vite) + Convex Auth app

This is a [Convex](https://convex.dev/) project created with [`npm create convex`](https://www.npmjs.com/package/create-convex).

Expand All @@ -7,7 +7,8 @@ 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)
- [Vite](https://vitest.dev/) for optimized web hosting
- [Tailwind](https://tailwindcss.com/) for building great looking accessible UI
- [Tailwind](https://tailwindcss.com/) for building great looking UI
- [Convex Auth](https://labs.convex.dev/auth) for authentication

## Get started

Expand All @@ -21,9 +22,13 @@ npm run dev
If you're reading this README on GitHub and want to use this template, run:

```
npm create convex@latest -- -t react-vite
npm create convex@latest -- -t react-vite-convexauth
```

For more information on how to configure Convex Auth, check out the [Convex Auth docs](https://labs.convex.dev/auth/).

For more examples of different Convex Auth flows, check out this [example repo](https://www.convex.dev/templates/convex-auth).

## Learn more

To learn more about developing your project with Convex, check out:
Expand Down
4 changes: 4 additions & 0 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import type {
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";

/**
Expand All @@ -24,6 +26,8 @@ import type * as myFunctions from "../myFunctions.js";
* ```
*/
declare const fullApi: ApiFromModules<{
auth: typeof auth;
http: typeof http;
myFunctions: typeof myFunctions;
}>;
export declare const api: FilterApi<
Expand Down
3 changes: 2 additions & 1 deletion convex/auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { convexAuth } from "@convex-dev/auth/server";
import { Password } from "@convex-dev/auth/providers/Password";

export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
providers: [],
providers: [Password],
});
5 changes: 4 additions & 1 deletion convex/myFunctions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
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.
Expand All @@ -21,8 +22,10 @@ export const listNumbers = query({
// 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: (await ctx.auth.getUserIdentity())?.name ?? null,
viewer: user?.email ?? null,
numbers: numbers.reverse().map((number) => number.value),
};
},
Expand Down
7 changes: 4 additions & 3 deletions convex/schema.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
import { authTables } from "@convex-dev/auth/server";

// The schema is entirely optional.
// You can delete this file (schema.ts) and the
// app will continue to work.
// 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(),
}),
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dev": "npm-run-all --parallel dev:frontend dev:backend",
"dev:frontend": "vite --open",
"dev:backend": "convex dev",
"predev": "convex dev --until-success && convex dashboard",
"predev": "convex dev --until-success && convex dev --once --run-sh \"node setup.mjs --once\" && convex dashboard",
"build": "tsc -b && vite build",
"lint": "tsc && eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
Expand All @@ -25,6 +25,7 @@
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^4.3.4",
"dotenv": "^16.4.7",
"eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
Expand Down
35 changes: 35 additions & 0 deletions setup.mjs
Original file line number Diff line number Diff line change
@@ -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);
102 changes: 98 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,116 @@
"use client";

import { useMutation, useQuery } from "convex/react";
import {
Authenticated,
Unauthenticated,
useConvexAuth,
useMutation,
useQuery,
} from "convex/react";
import { api } from "../convex/_generated/api";
import { useAuthActions } from "@convex-dev/auth/react";
import { useState } from "react";

export default function App() {
return (
<>
<header className="sticky top-0 z-10 bg-light dark:bg-dark p-4 border-b-2 border-slate-200 dark:border-slate-800">
Convex + React
Convex + React + Convex Auth
<SignOutButton />
</header>
<main className="p-8 flex flex-col gap-16">
<h1 className="text-4xl font-bold text-center">Convex + React</h1>
<Content />
<h1 className="text-4xl font-bold text-center">
Convex + React + Convex Auth
</h1>
<Authenticated>
<Content />
</Authenticated>
<Unauthenticated>
<SignInForm />
</Unauthenticated>
</main>
</>
);
}

function SignOutButton() {
const { isAuthenticated } = useConvexAuth();
const { signOut } = useAuthActions();
return (
<>
{isAuthenticated && (
<button
className="bg-slate-200 dark:bg-slate-800 text-dark dark:text-light rounded-md px-2 py-1"
onClick={() => void signOut()}
>
Sign out
</button>
)}
</>
);
}

function SignInForm() {
const { signIn } = useAuthActions();
const [flow, setFlow] = useState<"signIn" | "signUp">("signIn");
const [error, setError] = useState<string | null>(null);
return (
<div className="flex flex-col gap-8 w-96 mx-auto">
<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);
});
}}
>
<input
className="bg-light dark:bg-dark text-dark dark:text-light rounded-md p-2 border-2 border-slate-200 dark:border-slate-800"
type="email"
name="email"
placeholder="Email"
/>
<input
className="bg-light dark:bg-dark text-dark dark:text-light rounded-md p-2 border-2 border-slate-200 dark:border-slate-800"
type="password"
name="password"
placeholder="Password"
/>
<button
className="bg-dark dark:bg-light text-light dark:text-dark 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-dark dark:text-light 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-dark dark:text-light font-mono text-xs">
Error signing in: {error}
</p>
</div>
)}
</form>
</div>
);
}

function Content() {
const { viewer, numbers } =
useQuery(api.myFunctions.listNumbers, {
Expand Down
8 changes: 5 additions & 3 deletions src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { ConvexProvider, ConvexReactClient } from "convex/react";
import { ConvexAuthProvider } from "@convex-dev/auth/react";
import { ConvexReactClient } from "convex/react";
import "./index.css";
import App from "./App.tsx";

const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);

createRoot(document.getElementById("root")!).render(
<StrictMode>
<ConvexProvider client={convex}>
<ConvexAuthProvider client={convex}>
<App />
</ConvexProvider>
</ConvexAuthProvider>
</StrictMode>,
);