-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cli): adds a new template, one-to-many (#15)
* chore(one-to-many): copy of one-to-one * feat(one-to-many): track account membership via join table * chore(one-to-many): remove unused imports * feat(one-to-many): add name field to accounts table * feat(one-to-many): add logic for signing up and setting up a new account * feat(one-to-many): add logic and basic UI for sending invites * feat(one-to-many): display list of users and invites * perf(one-to-many): batch db calls * docs(one-to-many): add jsdocs to actions * fix(one-to-many): remove debugs * fix(one-to-many): some actions were missing use server * fix(one-to-many): add inviterId to invite_tokens * fix(one-to-many): allow removal of users from an account, in addition to removal of invites * fix(one-to-many): perform some integrity checks before removing a user * refactor(one-to-many): abstract repeated action checks into higher order functions * fix(one-to-many): allow changing of user roles * fix(one-to-many): add basic invite page * fix(one-to-many): protect against expiry and existing account * fix(one-to-many): check for invites when signing in a non-account holder user * fix(one-to-many): allow accepting an invite * fix(one-to-many): properly check for empty fetch result * chore(one-to-many): tighten up type checking * chore(one-to-many): linting errors * chore(one-to-many): pnpm update * chore(all): remove lockfiles * fix(one-to-many): drop status column from users_accounts * fix(one-to-many): status is no longer part of users_accounts * refactor(one-to-many): add BASE_URL and cleanup env vars in general * fix(one-to-many): fix env var called for BASE_URL * fix(one-to-many): don't try and write to status * fix(one-to-many): generally improve page styling * fix(one-to-many): move invite email content to component * fix(one-to-many): add account information to invite * fix(one-to-many): send custom magic link emails * chore(one-to-many): such lazy typing * chore(one-to-manu): remove unused * fix(one-to-many): use correct env var * fix(one-to-many): better error messages * docs(one-to-many): add missing jsdocs * fix(one-to-many): redirect to /welcome after invite accept * fix(one-to-many): don't leak email info * fix(one-to-many): add role checks to role changes, deletions, and additions * fix(one-to-many): remove unused import * fix(one-to-many): disable role change select for users * chore(one-to-many): move action to account folder * refactor(one-to-many): simplify fetches when we know we expect only one row result * fix(one-to-many): fix hydration errors * fix(one-to-many): don't clear invite input on validation fails * refactor(one-to-many): abstract invitation page components, and improve UX of buttons * fix(one-to-many): update app welcome message * chore(release): changeset * fix(one-to-many): fix import errors
- Loading branch information
1 parent
749e7a4
commit d5639d8
Showing
105 changed files
with
7,281 additions
and
6,054 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"next-auth-template-one-to-many": minor | ||
"next-auth-template": minor | ||
--- | ||
|
||
Adds one-to-many, a new template that allows multiple users to belong to one account. Also includes invite flows." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# The base URL of the app. Required. | ||
BASE_URL="" | ||
|
||
# The database connection string. Required. https://orm.drizzle.team/docs/connect-overview | ||
DATABASE_URL="" | ||
|
||
# The API key for Resend. Required for magic links. If not set, disables magic link auth. https://resend.com/ https://authjs.dev/guides/configuring-resend | ||
RESEND_KEY="" | ||
|
||
# The email address to send magic links from. Required for magic links. | ||
RESEND_EMAIL_FROM="" | ||
|
||
# A secret used to sign cookies and to sign and verify JSON Web Tokens. See Auth.js docs on how to generate. Required in production. https://authjs.dev/getting-started/deployment#auth_secret | ||
AUTH_SECRET="" | ||
|
||
# The Client ID for your Google OAuth app. Required for social sign in. See Auth.js docs for set up. https://authjs.dev/getting-started/providers/google#setup | ||
AUTH_GOOGLE_ID="" | ||
|
||
# The Client Secret for your Google OAuth app. Required for social sign in. See Auth.js docs for set up. https://authjs.dev/getting-started/providers/google#setup | ||
AUTH_GOOGLE_SECRET="" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"extends": ["next/core-web-vitals", "next/typescript"], | ||
"rules": { | ||
"@typescript-eslint/no-empty-object-type": "off" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
.yarn/install-state.gz | ||
|
||
# testing | ||
/coverage | ||
|
||
# next.js | ||
/.next/ | ||
/out/ | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
# local env files | ||
.env*.local | ||
.env | ||
|
||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# next-auth-template | ||
|
||
![Built on next.js](https://img.shields.io/github/package-json/dependency-version/jakeisonline/next-auth-template/next?style=flat-square) ![Built on next-auth](https://img.shields.io/github/package-json/dependency-version/jakeisonline/next-auth-template/next-auth?style=flat-square) ![Built on drizzle-orm](https://img.shields.io/github/package-json/dependency-version/jakeisonline/next-auth-template/drizzle-orm?style=flat-square) ![Built on zod](https://img.shields.io/github/package-json/dependency-version/jakeisonline/next-auth-template/zod?style=flat-square) | ||
|
||
This template gets you up a running with social sign in, magic links, database-backed sessions, and account creation and setup. It's a good starting point for your next project. | ||
|
||
### [[View the demo]](https://next-auth-template-demo.vercel.app/) | ||
|
||
<sup>_\* The demo database is reset every few hours_ </sup> | ||
|
||
> [!NOTE] | ||
> This template uses major dependencies that are not yet stable. It is not recommended for production use until `next-auth` and `drizzle-orm` are stable | ||
# Documentation | ||
|
||
Visit https://jakeisonline.com/playground/tools/next-auth-template for detailed documentation. | ||
|
||
# License | ||
|
||
Licensed under the [MIT license](./LICENSE). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"$schema": "https://ui.shadcn.com/schema.json", | ||
"style": "default", | ||
"rsc": true, | ||
"tsx": true, | ||
"tailwind": { | ||
"config": "tailwind.config.ts", | ||
"css": "src/app/globals.css", | ||
"baseColor": "zinc", | ||
"cssVariables": true, | ||
"prefix": "" | ||
}, | ||
"aliases": { | ||
"components": "@/components", | ||
"utils": "@/lib/utils", | ||
"ui": "@/components/ui", | ||
"lib": "@/lib", | ||
"hooks": "@/hooks" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { defineConfig } from "drizzle-kit" | ||
|
||
export default defineConfig({ | ||
schema: "./src/db/schema", | ||
out: "./src/db/migrations", | ||
dialect: "postgresql", | ||
dbCredentials: { | ||
url: process.env.DATABASE_URL!, | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/** @type {import('next').NextConfig} */ | ||
const nextConfig = {}; | ||
|
||
export default nextConfig; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
{ | ||
"name": "next-auth-template-one-to-many", | ||
"version": "0.0.1", | ||
"private": true, | ||
"scripts": { | ||
"dev": "next dev", | ||
"build": "next build", | ||
"start": "next start", | ||
"lint": "next lint", | ||
"db:push": "drizzle-kit push", | ||
"db:studio": "drizzle-kit studio", | ||
"db:migrate": "drizzle-kit migrate", | ||
"db:generate": "drizzle-kit generate" | ||
}, | ||
"dependencies": { | ||
"@auth/drizzle-adapter": "^1.7.4", | ||
"@neondatabase/serverless": "^0.10.3", | ||
"@radix-ui/react-alert-dialog": "^1.1.4", | ||
"@radix-ui/react-avatar": "^1.1.2", | ||
"@radix-ui/react-collapsible": "^1.1.2", | ||
"@radix-ui/react-dialog": "^1.1.2", | ||
"@radix-ui/react-dropdown-menu": "^2.1.2", | ||
"@radix-ui/react-label": "^2.1.0", | ||
"@radix-ui/react-select": "^2.1.4", | ||
"@radix-ui/react-separator": "^1.1.1", | ||
"@radix-ui/react-slot": "^1.1.1", | ||
"@radix-ui/react-tooltip": "^1.1.6", | ||
"@types/react": "npm:types-react@rc", | ||
"@types/react-dom": "npm:types-react-dom@rc", | ||
"@types/uuid": "^10.0.0", | ||
"class-variance-authority": "^0.7.0", | ||
"clsx": "^2.1.1", | ||
"drizzle-orm": "^0.38.3", | ||
"lucide-react": "^0.451.0", | ||
"next": "15.1.3", | ||
"next-auth": "5.0.0-beta.25", | ||
"prettier": "^3.3.3", | ||
"prettier-plugin-tailwindcss": "^0.6.8", | ||
"react": "19.0.0-rc.1", | ||
"react-dom": "19.0.0-rc.1", | ||
"resend": "^4.0.1", | ||
"tailwind-merge": "^2.5.3", | ||
"zod": "^3.24.1" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^20", | ||
"@types/pg": "^8.11.10", | ||
"@types/react": "^18", | ||
"@types/react-dom": "^18", | ||
"drizzle-kit": "^0.30.1", | ||
"eslint": "^8", | ||
"eslint-config-next": "15.0.3", | ||
"postcss": "^8", | ||
"tailwindcss": "^3.4.1", | ||
"tailwindcss-animate": "^1.0.7", | ||
"tsx": "^4.19.1", | ||
"typescript": "^5" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
/** @type {import('postcss-load-config').Config} */ | ||
const config = { | ||
plugins: { | ||
tailwindcss: {}, | ||
}, | ||
}; | ||
|
||
export default config; |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
115 changes: 115 additions & 0 deletions
115
templates/one-to-many/src/actions/account/do-account-setup.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
"use server" | ||
|
||
import { db } from "@/db" | ||
import { accountsTable } from "@/db/schema/accounts" | ||
import { z } from "zod" | ||
import { ServerActionResponse } from "@/lib/types" | ||
import { usersAccountsTable } from "@/db/schema/users_accounts" | ||
import { withFormProtection } from "@/actions/action-middleware" | ||
|
||
/** | ||
* Server action to set up a new account for a authenticated user. | ||
* | ||
* @param prevState - Previous server action response state (unused but required for Next.js Server Actions) | ||
* @param formData - Form data containing the account setup information | ||
* @returns {Promise<ServerActionResponse>} Response object containing status and optional messages | ||
* | ||
* Protected by withActionProtection middleware which ensures: | ||
* - User is authenticated | ||
* - FormData is present | ||
* | ||
* The function performs the following: | ||
* 1. Validates the account name | ||
* 2. Creates a new account in the database | ||
* 3. Links the account to the authenticated user as owner | ||
*/ | ||
|
||
export const doAccountSetup = withFormProtection( | ||
async ( | ||
prevState: ServerActionResponse | undefined, | ||
formData?: FormData, | ||
): Promise<ServerActionResponse> => { | ||
const validatedAccountName = z | ||
.string({ | ||
required_error: "required_error", | ||
invalid_type_error: "invalid_type_error", | ||
}) | ||
.trim() | ||
.safeParse(formData!.get("account_name")) | ||
|
||
if (!validatedAccountName.success) { | ||
return { | ||
status: "error", | ||
messages: [ | ||
{ | ||
title: "Invalid account name", | ||
body: "No account name was provided, or type is invalid", | ||
}, | ||
], | ||
} | ||
} | ||
|
||
try { | ||
const createdAccount = await db | ||
.insert(accountsTable) | ||
.values({ | ||
name: validatedAccountName.data, | ||
}) | ||
.returning() | ||
|
||
if (!createdAccount[0].id) { | ||
return { | ||
status: "error", | ||
messages: [ | ||
{ | ||
title: "Account creation failed", | ||
body: "Failed to create account", | ||
}, | ||
], | ||
} | ||
} | ||
|
||
const createdUserAccount = await db | ||
.insert(usersAccountsTable) | ||
.values({ | ||
userId: formData!.get("sessionUserId") as string, | ||
accountId: createdAccount[0].id, | ||
role: "owner", | ||
}) | ||
.returning() | ||
|
||
if (!createdUserAccount[0].id) { | ||
return { | ||
status: "error", | ||
messages: [ | ||
{ | ||
title: "Account setup failed", | ||
body: "Failed to update user account", | ||
}, | ||
], | ||
} | ||
} | ||
|
||
return { | ||
status: "success", | ||
} | ||
} catch (error) { | ||
return { | ||
status: "error", | ||
messages: [ | ||
{ | ||
title: "Account setup failed", | ||
body: | ||
error instanceof Error | ||
? error.message | ||
: "An unknown error occurred", | ||
}, | ||
], | ||
} | ||
} | ||
}, | ||
{ | ||
requireAuth: true, | ||
validateFormData: true, | ||
}, | ||
) |
Oops, something went wrong.