-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
fd75d04
commit 4f27451
Showing
32 changed files
with
1,649 additions
and
0 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,128 @@ | ||
--- | ||
title: "Authentication" | ||
description: "How Cascade handles authentication and authorization." | ||
icon: "user" | ||
--- | ||
|
||
## Next Auth | ||
|
||
<Tip> | ||
|
||
Cascade is based on T3 app meaning that it uses NextAuth.js under the hood. Docs are [here](https://create.t3.gg/en/usage/next-auth). | ||
|
||
</Tip> | ||
|
||
This documentation is a brief overview of what is put on top of NextAuth.js to make it work with Cascade. We will focus on interesting parts! | ||
|
||
We extend the `Session` object with some additional types to make it work with Cascade. We need to add `role` and `planId` to the user object. | ||
|
||
```ts | ||
|
||
declare module "next-auth" { | ||
interface Session extends DefaultSession { | ||
user: { | ||
id: string; | ||
planId: string | null; | ||
role: Role; | ||
} & DefaultSession["user"]; | ||
} | ||
|
||
``` | ||
In callbacks we need to fetch the user from the database and add the `role` and `planId` to the session object. This is done to make sure that whenever you are working with Session from NextAuth.js you have all the necessary information about the user. | ||
```ts | ||
callbacks: { | ||
session: async ({ session, user }) => { | ||
const dbUser = await db.user.findUnique({ | ||
where: { | ||
id: user.id, | ||
}, | ||
}); | ||
return { | ||
...session, | ||
user: { | ||
...session.user, | ||
id: user.id, | ||
planId: dbUser?.planId ?? null, | ||
role: dbUser?.role, | ||
}, | ||
}; | ||
}, | ||
}, | ||
``` | ||
## External services | ||
<Tip>This section mentions something about Emails</Tip> | ||
<Tip>This section mentions something about Error Tracking</Tip> | ||
<Tip>This section mentions something about Background jobs</Tip> | ||
Next Auth is a great entrypoint for other services we need to use for our SaaS. | ||
For example, here we use events to manage the following: | ||
- Sentry - to connect user to the Sentry error tracking, so we could easily debug it. | ||
- Trigger.dev - to trigger a background job to send a notification when a new user signs up | ||
- Loops - to send a welcome email to the new users. | ||
```ts | ||
events: { | ||
async signIn({ user, isNewUser }) { | ||
Sentry.setUser({ id: user.id, name: user.name, email: user.email ?? "" }); | ||
if (isNewUser) { | ||
if (isTriggerEnabled) { | ||
await slackNewUserNotification.invoke({ | ||
user: { | ||
name: user.name ?? "unknown", | ||
email: user.email ?? undefined, | ||
id: user.id, | ||
}, | ||
}); | ||
} | ||
if (loops && user.email) { | ||
await loops.sendEvent( | ||
{ | ||
email: user.email, | ||
}, | ||
"cascade_sign_up", | ||
{ | ||
...(user.name && { name: user.name }), | ||
email: user.email, | ||
}, | ||
); | ||
} | ||
} | ||
}, | ||
signOut() { | ||
Sentry.setUser(null); | ||
}, | ||
}, | ||
``` | ||
## Providers | ||
Cascade comes with Discord & Google providers out of the box. You can easily add more providers by following the NextAuth.js documentation. | ||
```ts | ||
providers: [ | ||
DiscordProvider({ | ||
clientId: env.DISCORD_CLIENT_ID!, | ||
clientSecret: env.DISCORD_CLIENT_SECRET!, | ||
}), | ||
GoogleProvider({ | ||
clientId: env.GOOGLE_CLIENT_ID!, | ||
clientSecret: env.GOOGLE_CLIENT_SECRET!, | ||
}), | ||
|
||
/** | ||
* ...add more providers here. | ||
* | ||
* Most other providers require a bit more work than the Discord provider. For example, the | ||
* GitHub provider requires you to add the `refresh_token_expires_in` field to the Account | ||
* model. Refer to the NextAuth.js docs for the provider you want to use. Example: | ||
* | ||
* @see https://next-auth.js.org/providers/github | ||
*/ | ||
], | ||
``` |
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,70 @@ | ||
--- | ||
title: "Background jobs" | ||
description: "How cascade handles background jobs" | ||
icon: "stopwatch" | ||
--- | ||
|
||
## Background jobs tool of choice | ||
|
||
Cascade uses [Trigger.dev](https://trigger.dev) as its background job tool of choice. Trigger.dev is a simple, reliable, and easy-to-use tool that allows you to schedule and run background jobs in a few simple steps. | ||
|
||
## What is a background job | ||
|
||
A background job is a task that is executed outside the normal flow of your application. It is typically used to perform tasks that are not time-sensitive, such as sending emails, sending notifications or even running a generation with AI. | ||
|
||
Cascade uses background jobs to send Slack notifications so if you want to get notified on new users or on new payments - you need to enable Slack integration on Trigger.dev platform. | ||
|
||
In general job definitions are pretty simple and should serve you as a good example if you want to create your own jobs. | ||
|
||
That is the definition of a job: | ||
|
||
```ts | ||
export const slackNewUserNotification = triggerClient.defineJob({ | ||
id: "cascade-new-user", | ||
name: "Cascade new user notification", | ||
version: "0.0.1", | ||
trigger: eventTrigger({ | ||
name: "cascade.new.user", | ||
schema: z.object({ | ||
user: z.object({ | ||
name: z.string(), | ||
email: z.string().email().optional(), | ||
id: z.string(), | ||
}), | ||
}), | ||
}), | ||
integrations: { | ||
slack, | ||
}, | ||
run: async (payload, io, ctx) => { | ||
await io.slack.postMessage("post message", { | ||
channel: "C06RZ0QNP6W", | ||
text: `🔥 *New user signed up*\n\nName: ${payload.user.name}\nEmail: ${payload.user.email}\nID:${payload.user.id}`, | ||
}); | ||
}, | ||
``` | ||
And that's how you invoke a job: | ||
```ts | ||
await slackNewUserNotification.invoke({ | ||
user: { | ||
name: user.name ?? "unknown", | ||
email: user.email ?? undefined, | ||
id: user.id, | ||
}, | ||
}); | ||
``` | ||
|
||
## How to use Trigger.dev with Cascade | ||
|
||
First, you need to create a Trigger.dev account. Once you have an account, you can create a new project and get your API key. | ||
|
||
Populate the related environment variables in your `.env` file: | ||
|
||
```yml | ||
#Trigger.dev | ||
TRIGGER_API_KEY=tr_dev_iTsdlfkjyeD33yimXrW2N | ||
TRIGGER_API_URL=https://api.trigger.dev | ||
NEXT_PUBLIC_TRIGGER_PUBLIC_API_KEY=pk_dev_2AO1S8wxhEfsdlkjlksjdf | ||
``` |
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,80 @@ | ||
--- | ||
title: "Database" | ||
description: "How Cascade handles database" | ||
icon: "database" | ||
--- | ||
|
||
## Database of choice | ||
|
||
Cascade uses [Postgres](https://www.postgresql.org/) as its database of choice. Postgres is a powerful, open source object-relational database system that uses and extends the SQL language combined with many features that safely store and scale the most complicated data workloads. | ||
|
||
## ORM of choice | ||
|
||
<Tip>This section mentions something about Authentication</Tip> | ||
|
||
Cascade uses [Prisma](https://www.prisma.io/) as its ORM of choice. Prisma is a modern database toolkit that makes database access easy with an auto-generated and type-safe query builder that's tailored to your database schema. | ||
|
||
Prisma connects really well to Postgres & NextAuth trough an adapter that makes it easy to use. | ||
|
||
Whenever you want to make changes to database schema, you can do so by changing the schema in the `schema.prisma` file and then running `npx prisma migrate dev` to generate a migration file and apply the changes to the database. | ||
|
||
## Database schema | ||
|
||
Database has a few main overlapping sections, those are clearly visible in prisma schema(content of each model omitted fro brevity) | ||
|
||
User related: | ||
|
||
``` | ||
model Account { | ||
... | ||
} | ||
model Session { | ||
... | ||
} | ||
model User { | ||
... | ||
} | ||
``` | ||
|
||
Payments & Subscriptions related: | ||
|
||
``` | ||
model LemonSqueezySubscription { | ||
... | ||
} | ||
model OneTimePurchase { | ||
... | ||
} | ||
model Plan { | ||
... | ||
} | ||
``` | ||
|
||
Feature usage related: | ||
|
||
``` | ||
model FeatureUsage { | ||
... | ||
} | ||
``` |
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,45 @@ | ||
--- | ||
title: "Emails" | ||
description: "How Cascade handles marketing & transactional emails" | ||
icon: "envelopes" | ||
--- | ||
|
||
## Email tool of choice | ||
|
||
Cascade uses [Loops](https://www.loops.so) for sending marketing and transactional emails. Loops is a powerful email marketing platform that allows you to send emails to your users, track their engagement, and set up funnels to automate your email marketing. | ||
|
||
## Setup with Cascade | ||
|
||
You need to have a Loops account to use the email feature in Cascade. If you don't have one, you can [sign up here](https://www.loops.so). | ||
|
||
Grab your API key [from here](https://app.loops.so/settings?page=api) and add it to your `.env` file: | ||
|
||
``` | ||
#Loops | ||
LOOPS_API_KEY="2a2ee7a090dldskfj4bd73eb3f70a" | ||
``` | ||
|
||
## Sending emails | ||
|
||
Cascade implements an example of a single loop, where an email is sent to the newly created users: | ||
|
||
<Frame> | ||
<img src="/images/loops.jpeg" style={{ borderRadius: "0.5rem" }} /> | ||
</Frame> | ||
|
||
When the loop is hit, it waits for a minute and then sends an email to the user. You can customize the email content and the delay between the loop and the email and set up more complex funnels as WebGLUniformLocation.\_createMdxContent | ||
|
||
This is how you trigger loops: | ||
|
||
```ts | ||
await loops.sendEvent( | ||
{ | ||
email: user.email, | ||
}, | ||
"cascade_sign_up", | ||
{ | ||
...(user.name && { name: user.name }), | ||
email: user.email, | ||
} | ||
); | ||
``` |
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,22 @@ | ||
--- | ||
title: "Error tracking" | ||
description: "How Cascade handles error tracking" | ||
icon: "bug" | ||
--- | ||
|
||
## Error tracking tool of choice | ||
|
||
Cascade uses [Sentry](https://sentry.io) for error tracking. Sentry is an open-source error tracking tool that helps developers monitor and fix crashes in real time. It provides a comprehensive view of the errors that occur in your application, and helps you understand the impact of each error. | ||
|
||
## Set up Sentry with Cascade | ||
|
||
To set up Sentry with Cascade, you need to create a Sentry account and get a DSN (Data Source Name) for your project. The DSN is a unique identifier that Sentry uses to track errors in your application. | ||
|
||
After you have gone trough the setup process, you can add all of the related variabls to your `.env` file: | ||
|
||
```yml | ||
#Sentry | ||
SENTRY_DSN="https://asdjflkjsdkfljksdlfjslkdjf@o4506388280180736.ingest.us.sentry.io/4506978350399488" | ||
SENTRY_PROJECT="YOUR_PROJECT_NAME" | ||
SENTRY_ORG="YOUR_SENTRY_ORG" | ||
``` |
Oops, something went wrong.