mirror of
https://github.com/wasp-lang/open-saas.git
synced 2025-05-03 08:50:26 +02:00
update blog
This commit is contained in:
parent
71886777d0
commit
d77d5440ac
@ -18,14 +18,14 @@ export default defineConfig({
|
||||
},
|
||||
}),
|
||||
starlight({
|
||||
title: 'OpenSaaS.sh',
|
||||
description: 'Open SaaS is a free, open-source, full-stack SaaS starter kit for React + NodeJS.',
|
||||
title: 'Your SaaS',
|
||||
description: 'Documentation for your SaaS.',
|
||||
logo: {
|
||||
src: '/src/assets/logo.png',
|
||||
alt: 'Open SaaS',
|
||||
alt: 'Your SaaS',
|
||||
},
|
||||
editLink: {
|
||||
baseUrl: 'https://github.com/wasp-lang/open-saas/edit/main',
|
||||
baseUrl: 'https://github.com/<your-repo>',
|
||||
},
|
||||
components: {
|
||||
SiteTitle: './src/components/MyHeader.astro',
|
||||
@ -43,27 +43,13 @@ export default defineConfig({
|
||||
label: 'Start Here',
|
||||
items: [
|
||||
{ label: 'Introduction', link: '/' },
|
||||
{ label: 'Getting Started', link: '/start/getting-started/' },
|
||||
{ label: 'Guided Tour', link: '/start/guided-tour/' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Guides',
|
||||
items: [
|
||||
{ label: 'Authentication', link: '/guides/authentication/' },
|
||||
{ label: 'Authorization', link: '/guides/authorization/' },
|
||||
{ label: 'Stripe Integration', link: '/guides/stripe-integration/' },
|
||||
{ label: 'Stripe Testing', link: '/guides/stripe-testing/' },
|
||||
{ label: 'Analytics', link: '/guides/analytics/' },
|
||||
{ label: 'Email Sending', link: '/guides/email-sending/' },
|
||||
{ label: 'Deploying', link: '/guides/deploying/' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'General',
|
||||
items: [
|
||||
{ label: 'Admin Dashboard', link: '/general/admin-dashboard/' },
|
||||
{ label: 'User Overview', link: '/general/user-overview/' },
|
||||
{ label: 'Example Guide', link: '/guides/example/' },
|
||||
|
||||
],
|
||||
},
|
||||
],
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
Before Width: | Height: | Size: 62 KiB |
Binary file not shown.
Before Width: | Height: | Size: 231 KiB |
Binary file not shown.
Before Width: | Height: | Size: 251 KiB |
@ -1,61 +0,0 @@
|
||||
---
|
||||
title: Admin Dashboard
|
||||
---
|
||||
This is a reference on how the Admin dashboard is set up and works.
|
||||
|
||||
## Permissions
|
||||
|
||||
The Admin dashboard is only accessible to users with the `isAdmin` field set to true.
|
||||
|
||||
```tsx title="main.wasp" {5}
|
||||
entity User {=psl
|
||||
id Int @id @default(autoincrement())
|
||||
email String? @unique
|
||||
password String?
|
||||
isAdmin Boolean @default(false)
|
||||
//...
|
||||
```
|
||||
|
||||
To give yourself administrator priveledges, make sure you add your email adderesses to the `ADMIN_EMAILS` environment variable in `.env.server` file before registering/logging in with that email address.
|
||||
|
||||
```sh title=".env.server"
|
||||
ADMIN_EMAILS=me@example.com
|
||||
```
|
||||
|
||||
if you want to give administrator priveledges to other users, you can do so by adding them to `ADMIN_EMAILS` as a comma-separated list.
|
||||
|
||||
```sh title=".env.server"
|
||||
ADMIN_EMAILS=me@example.com,you@example.com,them@example.com
|
||||
```
|
||||
|
||||
## Admin Dashboard Pages
|
||||
|
||||
### Dashboard
|
||||
The Admin dashboard is a single place for you to view your most important metrics and perform some admin tasks. At the moment, it pulls data from:
|
||||
|
||||
<!-- TODO: add photo -->
|
||||
|
||||
- [Stripe](/guides/stripe-integration):
|
||||
- total revenue
|
||||
- revenue for each day of the past week
|
||||
- [Google or Plausible](/guides/analytics):
|
||||
- total number of page views (non-unique)
|
||||
- percentage change in page views from the previous day
|
||||
- top sources/referrers with unique visitor count (i.e. how many people came from that source to your app)
|
||||
- Database:
|
||||
- total number of registered users
|
||||
- daily change in number of registered users
|
||||
- total number of paying users
|
||||
- daily change in number of paying users
|
||||
|
||||
For a guide on how to integrate these services, check out the [Stripe](/guides/stripe-integration) and [Analytics guide](/guides/analytics) of the docs.
|
||||
|
||||
:::tip[Help us improve]
|
||||
We're always looking to improve the Admin dashboard. If you feel something is missing or could be improved, consider [opening an issue](https://github.com/wasp-lang/open-saas/issues) or [submitting a pull request](https://github.com/wasp-lang/open-saas/pulls)
|
||||
:::
|
||||
|
||||
### Users
|
||||
The Users page is where you can view all your users and their most important details. You can also search and filter users by:
|
||||
- email address
|
||||
- subscription status
|
||||
|
@ -1,134 +0,0 @@
|
||||
---
|
||||
title: User Overview
|
||||
---
|
||||
|
||||
This reference will help you understand how the User entity works in this template.
|
||||
This includes the user roles, subscription tiers and statuses, and how to authorize access to certain pages and components.
|
||||
|
||||
## User Entity
|
||||
|
||||
The `User` entity within your app is defined in the `main.wasp` file:
|
||||
|
||||
```tsx title="main.wasp" ins="User: {}"
|
||||
entity User {=psl
|
||||
id Int @id @default(autoincrement())
|
||||
email String? @unique
|
||||
password String?
|
||||
createdAt DateTime @default(now())
|
||||
lastActiveTimestamp DateTime @default(now())
|
||||
isAdmin Boolean @default(false)
|
||||
isEmailVerified Boolean @default(false)
|
||||
emailVerificationSentAt DateTime?
|
||||
passwordResetSentAt DateTime?
|
||||
stripeId String?
|
||||
checkoutSessionId String?
|
||||
hasPaid Boolean @default(false)
|
||||
subscriptionTier String?
|
||||
subscriptionStatus String?
|
||||
sendEmail Boolean @default(false)
|
||||
datePaid DateTime?
|
||||
credits Int @default(3)
|
||||
relatedObject RelatedObject[]
|
||||
externalAuthAssociations SocialLogin[]
|
||||
contactFormMessages ContactFormMessage[]
|
||||
psl=}
|
||||
```
|
||||
|
||||
We store all pertinent information to the user, including Auth, Subscription, and Stripe information.
|
||||
|
||||
## Stripe and Subscriptions
|
||||
|
||||
We use Stripe to handle all of our subscription payments. The `User` entity has a number of fields that are related to Stripe and their ability to access features behind the paywall:
|
||||
|
||||
```tsx title="main.wasp" {4-10}
|
||||
entity User {=psl
|
||||
id Int @id @default(autoincrement())
|
||||
//...
|
||||
stripeId String?
|
||||
checkoutSessionId String?
|
||||
hasPaid Boolean @default(false)
|
||||
subscriptionTier String?
|
||||
subscriptionStatus String?
|
||||
datePaid DateTime?
|
||||
credits Int @default(3)
|
||||
//...
|
||||
psl=}
|
||||
```
|
||||
|
||||
- `stripeId`: The Stripe customer ID. This is created by Stripe on checkout and used to identify the customer.
|
||||
- `checkoutSessionId`: The Stripe checkout session ID. This is created by Stripe on checkout and used to identify the checkout session.
|
||||
- `hasPaid`: A boolean that indicates whether the user has paid for a subscription or not.
|
||||
- `subscriptionTier`: The subscription tier the user is on. This is set by the app and is used to determine what features the user has access to. By default, we have two tiers: `hobby-tier` and `pro-tier`.
|
||||
- `subscriptionStatus`: The subscription status of the user. This is set by Stripe and is used to determine whether the user has access to the app or not. By default, we have four statuses: `active`, `past_due`, `canceled`, and `deleted`.
|
||||
- `credits` (optional): You can allow a user to trial your product with a limited number of credits before they have to pay.
|
||||
|
||||
### Subscription Statuses
|
||||
|
||||
In general, we determine if a user has paid for an initial subscription by checking if the `hasPaid` field is true. If it is, we know that the user has paid for a subscription and we can grant them access to the app.
|
||||
|
||||
The `subscriptionStatus` field is set by Stripe within your webhook handler and is used to signify more detailed information on the user's current status. By default, the template handles four statuses: `active`, `past_due`, `canceled`, and `deleted`.
|
||||
|
||||
- When `active` the user has paid for a subscription and has full access to the app.
|
||||
|
||||
- When `canceled`, the user has canceled their subscription and has access to the app until the end of their billing period.
|
||||
|
||||
- When `deleted`, the user has reached the end of their subscription period after canceling and no longer has access to the app.
|
||||
|
||||
- When `past_due`, the user's automatic subscription renewal payment was declined (e.g. their credit card expired). You can choose how to handle this status within your app. For example, you can send the user an email to update their payment information:
|
||||
```tsx title="src/server/webhooks/stripe.ts"
|
||||
import { emailSender } from '@wasp/email/index.js';
|
||||
//...
|
||||
|
||||
if (subscription.status === 'past_due') {
|
||||
const updatedCustomer = await context.entities.User.update({
|
||||
where: {
|
||||
id: customer.id,
|
||||
},
|
||||
data: {
|
||||
subscriptionStatus: 'past_due',
|
||||
},
|
||||
});
|
||||
|
||||
if (updatedCustomer.email) {
|
||||
await emailSender.send({
|
||||
to: updatedCustomer.email,
|
||||
subject: 'Your Payment is Past Due',
|
||||
text: 'Please update your payment information to continue using our service.',
|
||||
html: '...',
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See the client-side [authorization section](/guides/authorization) below for more info on how to handle these statuses within your app.
|
||||
|
||||
### Subscription Tiers
|
||||
|
||||
The `subscriptionTier` field is used to determine what features the user has access to.
|
||||
|
||||
By default, we have two tiers: `hobby-tier` and `pro-tier`.
|
||||
|
||||
You can add more tiers by adding more products and price IDs to your Stripe product and updating environment variables in your `.env.server` file as well as the relevant code in your app.
|
||||
|
||||
See the [Stripe Integration Guide](/guides/stripe-integration) for more info on how to do this.
|
||||
|
||||
## User Roles
|
||||
|
||||
At the moment, we have two user roles: `admin` and `user`. This is defined within the `isAdmin` field in the `User` entity:
|
||||
|
||||
```tsx title="main.wasp" {7}
|
||||
entity User {=psl
|
||||
id Int @id @default(autoincrement())
|
||||
email String? @unique
|
||||
password String?
|
||||
createdAt DateTime @default(now())
|
||||
lastActiveTimestamp DateTime @default(now())
|
||||
isAdmin Boolean @default(false)
|
||||
//...
|
||||
psl=}
|
||||
```
|
||||
<!-- TODO: add screenshot of user table -->
|
||||
|
||||
As an Admin, a user has access to the Admin dashboard, along with the user table where they can view and search for users, and edit and update information manually if necessary.
|
||||
|
||||
As a general User, a user has access to the user-facing app that sits behind the login, but not the Admin dashboard. You can further restrict access to certain features within the app by following the [authorization guide](/guides/authorization).
|
@ -1,90 +0,0 @@
|
||||
---
|
||||
title: Analytics
|
||||
---
|
||||
This guide will show you how to integrate analytics for your app. You can choose between [Google Analytics](#google-analytics) and [Plausible](#plausible).
|
||||
|
||||
Google Analytics is free, but tends to be more cumbersome to use.
|
||||
|
||||
Plausible is an open-source, privacy-friendly alternative to Google Analytics. It's also easier to use than Google if you use their hosted service, which is a paid feature. But, it is completely free if you want to self-host it, although this comes with some additional setup steps.
|
||||
|
||||
## Plausible
|
||||
|
||||
### Hosted Plausible
|
||||
Sign up for a hosted Plausible account [here](https://plausible.io/).
|
||||
|
||||
Once you've signed up, you'll be taken to your dashboard. Create your site by adding your domain. Your domain is also your `PLAUSIBLE_SITE_ID` in your `.env.server` file. Make sure to add it.
|
||||
|
||||
```sh
|
||||
PLAUSIBLE_SITE_ID=<your domain without www>
|
||||
```
|
||||
|
||||
After adding your domain, you'll be taken to a page with your Plausible script tag. Copy and paste this script tag into the `main.wasp` file's head section.
|
||||
|
||||
```js {7}
|
||||
app SaaSTemplate {
|
||||
wasp: {
|
||||
version: "^0.11.6"
|
||||
},
|
||||
title: "My SaaS App",
|
||||
head: [
|
||||
"<your plausible script tag here>",
|
||||
],
|
||||
//...
|
||||
```
|
||||
|
||||
Go back to your Plausible dashboard, click on your username in the top right, and click on the `Settings` tab. Scroll down, find your API key and paste it into your `.env.server` file under the `PLAUSIBLE_API_KEY` variable.
|
||||
|
||||
|
||||
### Self-hosted Plausible
|
||||
|
||||
Plausible, being an open-source project, allows you to self-host your analytics. This is a great option if you want to keep your data private and not pay for the hosted service.
|
||||
|
||||
*coming soon...*
|
||||
*until then, check out the [official documentation](https://plausible.io/docs)*
|
||||
|
||||
:::tip[Contribute!]
|
||||
If you'd like to help us write this guide, click the "Edit page" button at the bottom of this page
|
||||
|
||||
As a completely free, open-source project, we appreciate any help 🙏
|
||||
:::
|
||||
|
||||
## Google Analytics
|
||||
|
||||
After you sign up for [Google analytics](https://analytics.google.com/), go to your `Admin` panel in the bottom of the left sidebar and then create a "Property" for your app.
|
||||
|
||||
Once you've completed the steps to create a new Property, some Installation Instructions will pop up. Select `install manually` and copy and paste the Google script tag into the `main.wasp` file's head section.
|
||||
|
||||
```js {7}
|
||||
app SaaSTemplate {
|
||||
wasp: {
|
||||
version: "^0.11.6"
|
||||
},
|
||||
title: "My SaaS App",
|
||||
head: [
|
||||
"<your google analytics script tag here>",
|
||||
],
|
||||
//...
|
||||
```
|
||||
|
||||
Then, set up the Google Analytics API access by following these steps:
|
||||
|
||||
1. **Set up a Google Cloud project:** If you haven't already, start by setting up a project in the [Google Cloud Console](https://console.cloud.google.com/).
|
||||
|
||||
2. **Enable the Google Analytics API for your project:** Navigate to the "Library" in the Google Cloud Console and search for the "Google Analytics Data API" (for Google Analytics 4 properties) and enable it.
|
||||
|
||||
3. **Create credentials:** Now go to the "Credentials" tab within your Google Cloud project, click on `+ credentials`, and create a new service account key. First, give it a name. Then, under "Grant this service account access to project", choose `viewer`.
|
||||
|
||||
4. **Create Credentials:** When you go back to `Credentials` page, you should see a new service account listed under "Service Accounts". It will be a long email address to ends with `@your-project-id.iam.gserviceaccount.com`. Click on the service account name to go to the service account details page.
|
||||
|
||||
- Under “Keys” in the service account details page, click “Add Key” and choose `Create new key`.
|
||||
|
||||
- Select "JSON", then click “Create” to download your new service account’s JSON key file. Keep this file secure and don't add it to your git repo – it grants access to your Google Analytics data.
|
||||
5. **Update your Google Anayltics Settings:** Go back to your Google Analytics dashboard, and click on the `Admin` section in the left sidebar. Under `Property Settings > Property > Property Access Management` Add the service account email address (the one that ends with `@your-project-id.iam.gserviceaccount.com`) and give it `Viewer` permissions.
|
||||
|
||||
6. **Encode and add the Credentials:** Add the `client_email` and the `private_key` from your JSON Key file into your `.env.server` file. But be careful! Because Google uses a special PEM private key, you need to first convert the key to base64, otherwise you will run into errors parsing the key. To do this, in a terminal window, run the command below and paste the output into your `.env.server` file under the `GOOGLE_ANALYTICS_PRIVATE_KEY` variable:
|
||||
```sh
|
||||
echo -n "PRIVATE_KEY" | base64
|
||||
```
|
||||
|
||||
7. **Add your Google Analytics Property ID:** You will find the Property ID in your Google Analytics dashboard in the `Admin > Property > Property Settings > Property Details` section of your Google Analytics property (**not** your Google Cloud console). Add this 9-digit number to your `.env.server` file under the `GOOGLE_ANALYTICS_PROPERTY_ID` variable.
|
||||
|
@ -1,59 +0,0 @@
|
||||
---
|
||||
title: Authentication
|
||||
---
|
||||
|
||||
Setting up your app's authentication is easy with Wasp. In fact, it's aready set up for your in the `main.wasp` file:
|
||||
|
||||
```tsx title="main.wasp" ins="usernameAndPassword: {}" ins="email: {}" ins="google: {}" ins="gitHub: {}"
|
||||
auth: {
|
||||
userEntity: User,
|
||||
externalAuthEntity: SocialLogin,
|
||||
methods: {
|
||||
usernameAndPassword: {},
|
||||
email: {},
|
||||
google: {},
|
||||
gitHub: {},
|
||||
},
|
||||
onAuthFailedRedirectTo: "/",
|
||||
},
|
||||
```
|
||||
|
||||
The great part is, by defining your auth config in the `main.wasp` file, not only does Wasp handle Auth for you, but you also get auto-generated client components for your app on the fly (aka AuthUI)! You can see them in the `src/client/auth` folder.
|
||||
|
||||
## Migrating to a different Auth method
|
||||
|
||||
If you want to use a different method or combinations of Auth methods, you can easily do so by changing the `auth.methods` object in the `main.wasp` file.
|
||||
|
||||
We've set up the template to get you started with Wasp's simplest auth method, `usernameAndPassword`, but we suggest you only use it to get your app developlment going and opt for `email`, `google`, `gitHub`, or a combination of these in production.
|
||||
|
||||
### Email Verified Auth
|
||||
|
||||
The `email` method, with it's use of an Email Sending provider to verify a user's email, is preferrable to `usernameAndPassword` because it's more secure and allows for password reset options. Note, you cannot use both `email` and `usernameAndPassword` methods at the same time.
|
||||
|
||||
We've pre-configured the `email` auth method for you in a number of different files but commented out the code in case you'd like to quickly implement it in your app. To do so, you'll first need to fill in your Email Sending provider's API keys. We chose [SendGrid](https://sendgrid.com) as the provider, but Wasp can also handle [MailGun](https://mailgun.com), or SMTP.
|
||||
|
||||
After you've signed up for a Sendgrid account, perform the following steps:
|
||||
|
||||
1. Add your `SENDGRID_API_KEY` to the `.env.server` file
|
||||
2. Uncomment
|
||||
`RequestPasswordResetRoute`,
|
||||
`ResetPasswordRoute`,
|
||||
`EmailVerificationRoute`
|
||||
in the `main.wasp` file
|
||||
3. Make sure to also uncomment out these routes respective code in the `/src/client/auth` folder, as well as in the `app/src/server/auth/email.ts` file.
|
||||
|
||||
And that's it. Wasp will take care of the rest and update your AuthUI components accordingly.
|
||||
|
||||
Check out the [Wasp Auth docs](https://wasp-lang.dev/docs/auth/overview) for more info.
|
||||
|
||||
## Google Auth
|
||||
|
||||
We've also customized and pre-built the Google auth flow for you. To start using it you just need to uncomment out the `google` method in `main.wasp` file and fill in your API keys in the `.env.server` file.
|
||||
|
||||
To get your Google API keys, follow the instructions in Wasp's [Google Auth docs](https://wasp-lang.dev/docs/auth/social-auth/google#3-creating-a-google-oauth-app).
|
||||
|
||||
Again, Wasp will take care of the rest and update your AuthUI components accordingly.
|
||||
|
||||
## GitHub Auth
|
||||
|
||||
To easily add GitHub as a login option, you can follow the instructions in Wasp's [GitHub Auth docs](https://wasp-lang.dev/docs/auth/social-auth/github#3-creating-a-github-oauth-app).
|
@ -1,79 +0,0 @@
|
||||
---
|
||||
title: Authorization
|
||||
---
|
||||
|
||||
This guide will help you get started with authorization in your SaaS app.
|
||||
|
||||
Authorization refers to what users can access in your app. This is useful for differentiating between users who have paid for different subscription tiers (e.g. "hobby" vs "pro"), or between users who have admin privileges and those who do not.
|
||||
|
||||
Authorization differs from [authentication](/guides/authentication) in that authentication refers to the process of verifying that a user is who they say they are (e.g. logging in with a username and password).
|
||||
|
||||
To learn more about the different types of user permissions built into this SaaS template, including Stripe subscription tiers and statuses, check out the [User Permissions Reference](/general/user-permissions).
|
||||
|
||||
### Client-side Authorization
|
||||
|
||||
All authenticated users have access to the user-facing app, which is the app that sits behind the login. You can easily authorize access to general users from within the `main.wasp` file by adding the `authRequired: true` property to the `page` definition:
|
||||
|
||||
```tsx title="main.wasp" {3}
|
||||
route AccountRoute { path: "/account", to: AccountPage }
|
||||
page AccountPage {
|
||||
authRequired: true,
|
||||
component: import Account from "@client/app/AccountPage"
|
||||
}
|
||||
```
|
||||
|
||||
This will automatically redirect users to the login page if they are not logged in.
|
||||
|
||||
If you want more fine-grained control over what users can access, there are two Wasp-specific options:
|
||||
1. When you define the `authRequired: true` property on the `page` definition, Wasp automatically passes the User object to the page component. Here you can check for certain user properties before authorizing access:
|
||||
|
||||
```tsx title="ExamplePage.tsx" "{ user }: { user: User }"
|
||||
import type { User } from '@wasp/entities';
|
||||
|
||||
export default function Example({ user }: { user: User }) {
|
||||
|
||||
if (user.subscriptionStatus === 'past_due') {
|
||||
return (<span>Your subscription is past due. Please update your payment information.</span>)
|
||||
}
|
||||
if (user.subscriptionStatus === 'canceled') {
|
||||
return (<span>Your will susbscription end on 01.01.2024</span>)
|
||||
}
|
||||
if (user.subscriptionStatus === 'active') {
|
||||
return (<span>Thanks so much for your support!</span>)
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
2. Or you can take advantage of the `useAuth` hook and check for certain user properties before authorizing access to certain pages or components:
|
||||
|
||||
```tsx title="ExamplePage.tsx" {1, 4}
|
||||
import useAuth from '@wasp/auth/useAuth';
|
||||
|
||||
export default function ExampleHomePage() {
|
||||
const { data: user } = useAuth();
|
||||
|
||||
return (
|
||||
<h1> Hi {user.email || 'there'} 👋 </h1>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Server-side Authorization
|
||||
|
||||
You can also authorize access to server-side operations by adding a check to for a logged in user on the `context.user` object which is passed to all operations in Wasp:
|
||||
|
||||
```tsx title="src/server/actions.ts"
|
||||
export const updateCurrentUser: UpdateCurrentUser<...> = async (args, context) => {
|
||||
if (!context.user) {
|
||||
throw new HttpError(401); // throw an error if user is not logged in
|
||||
}
|
||||
|
||||
if (context.user.subscriptionStatus === 'past_due') {
|
||||
throw new HttpError(403, 'Your subscription is past due. Please update your payment information.');
|
||||
}
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1,27 +0,0 @@
|
||||
---
|
||||
title: Deploying
|
||||
---
|
||||
|
||||
Because this SaaS app is a React/NodeJS/Postgres app built on top of [Wasp](https://wasp-lang.dev), you can deploy it anywhere where static files, NodeJS and Postgres can be hosted and served.
|
||||
|
||||
The simplest and quickest option is to take advantage of Wasp's one-command deploy to [Fly.io](https://fly.io).
|
||||
|
||||
Or if you prefer to deploy to a different provider, or your frontend and backend separately, you can follow any of the other deployment guides below.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Make sure you've got all your API keys and environment variables set up before you deploy. For example, in the [Stripe integration guide](/guides/stripe-integration), you set up your Stripe API keys using test keys and product ids. You'll need to get the production-ready keys and create actual Product IDs.
|
||||
|
||||
## Deploying to Fly.io
|
||||
|
||||
[Fly.io](https://fly.io) is a platform for running your apps globally. It's a great choice for deploying your SaaS app because it's free to get started, can host your entire full-stack app in one place, scales well, and has one-command deploy integration with Wasp.
|
||||
|
||||
```sh
|
||||
wasp deploy fly launch <app-name> <region>
|
||||
```
|
||||
|
||||
There are a few prequisites to follow before you can initiate the deploy command. For a detailed guide, check out the [Wasp CLI deployment guide](https://wasp-lang.dev/docs/advanced/deployment/cli).
|
||||
|
||||
## Deploying Manually / to Other Providers
|
||||
|
||||
If you prefer to deploy manually, your frontend and backend separately, or just prefer using your favorite provider you can follow the [Manual Deployment Guide](https://wasp-lang.dev/docs/advanced/deployment/manually).
|
@ -1,81 +0,0 @@
|
||||
---
|
||||
title: Email Sending
|
||||
---
|
||||
import { Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
This guide explains how to use the integrated email sender and how you can integrate your own account in this template.
|
||||
|
||||
## Sending Emails
|
||||
|
||||
In the `main.wasp` config file, you'll see we've got the email sender set up for you:
|
||||
|
||||
```tsx title="main.wasp"
|
||||
app SaaSTemplate {
|
||||
// ...
|
||||
emailSender: {
|
||||
provider: SendGrid,
|
||||
defaultFrom: {
|
||||
name: "Open SaaS App",
|
||||
// make sure this address is the same you registered your SendGrid or MailGun account with!
|
||||
email: "my@email.com"
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
This means that you can send emails from your app using the `send` function from the `email` module:
|
||||
|
||||
```tsx title="src/server/webhooks.ts"
|
||||
import { emailSender } from "@wasp/email/index.js";
|
||||
|
||||
//...
|
||||
|
||||
if (subscription.cancel_at_period_end) {
|
||||
await emailSender.send({
|
||||
to: customer.email,
|
||||
subject: 'We hate to see you go :(',
|
||||
text: 'We hate to see you go. Here is a sweet offer...',
|
||||
html: 'We hate to see you go. Here is a sweet offer...',
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
In the example above, you can see that we're sending an email to the customer when we receive a cancel subscription event within the Stripe webhook.
|
||||
|
||||
This is a powerful feature and super simple to use.
|
||||
|
||||
:::tip[Sending Emails in Development]
|
||||
In the `.env.server` file, we've set the `SEND_EMAILS_IN_DEVELOPMENT` env variable to true. This means that emails will be sent in development mode.
|
||||
|
||||
This is useful for testing, but you can turn it off by setting it to false, and the emails will be logged to the console instead.
|
||||
:::
|
||||
|
||||
## Integrate your email sender
|
||||
|
||||
To set up your email sender, you first need an account with one of the supported email providers.
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="SendGrid">
|
||||
- Register at SendGrid.com and then get your [API KEYS](https://app.sendgrid.com/settings/api_keys).
|
||||
- Copy yours to the `.env.server` file under the `SENDGRID_API_KEY` variable.
|
||||
</TabItem>
|
||||
<TabItem label="MailGun">
|
||||
- Go to [Mailgun](https://mailgun.com) and create an account.
|
||||
- Go to [API Keys](https://app.mailgun.com/app/account/security/api_keys) and create a new API key.
|
||||
- Copy the API key and add it to your .env.server file under the `MAILGUN_API_KEY=` variable.
|
||||
- Go to [Domains](https://app.mailgun.com/app/domains) and create a new domain.
|
||||
- Copy the domain and add it to your .env.server file as `MAILGUN_DOMAIN=`.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Make sure to change the `defaultFrom` email address in the `main.wasp` file to the same email address you used to register your account with.
|
||||
|
||||
```tsx title="main.wasp" {5}
|
||||
emailSender: {
|
||||
provider: SendGrid,
|
||||
defaultFrom: {
|
||||
name: "Open SaaS App",
|
||||
email: "my@email.com"
|
||||
},
|
||||
```
|
||||
|
||||
If you want more detailed info, or would like to use SMTP, check out the [Wasp docs](https://wasp-lang.dev/docs/advanced/email).
|
9
blog/src/content/docs/guides/example.md
Normal file
9
blog/src/content/docs/guides/example.md
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Example Guide
|
||||
---
|
||||
|
||||
This is an example guide. It's a good idea to have a guide for each major feature of your project. Guides should be short and to the point. They should also be written in a way that is easy to understand for beginners.
|
||||
|
||||
## Example Heading
|
||||
|
||||
...
|
@ -1,88 +0,0 @@
|
||||
---
|
||||
title: Stripe Integration
|
||||
---
|
||||
|
||||
This guide will show you how to set up your Stripe account for testing and local development.
|
||||
|
||||
Once you deploy your app, you can follow the same steps, just make sure you're using your live Stripe API keys and product IDs and you are no longer in test mode within the Stripe Dashboard.
|
||||
|
||||
To get started, you'll need to create a Stripe account. You can do that [here](https://dashboard.stripe.com/register).
|
||||
|
||||
## Get your test Stripe API Keys
|
||||
|
||||
Once you've created your account, you'll need to get your test API keys. You can do that by navigating to [https://dashboard.stripe.com/test/apikeys](https://dashboard.stripe.com/test/apikeys) or by going to the [Stripe Dashboard](https://dashboard.stripe.com/test/dashboard) and clicking on the `Developers`.
|
||||
|
||||

|
||||
|
||||
- Click on the `Reveal test key token` button and copy the `Secret key`.
|
||||
- Paste it in your `.env.server` file under `STRIPE_KEY=`
|
||||
|
||||
## Create a Test Product
|
||||
|
||||
To create a test product, go to the test products url [https://dashboard.stripe.com/test/products](https://dashboard.stripe.com/test/products), or after navigating to your dashboard, click the `test mode` toggle.
|
||||
|
||||

|
||||
|
||||
- Click on the `Add a product` button and fill in the relevant information for your product.
|
||||
- Make sure you select `Software as a service (SaaS)` as the product type.
|
||||
- If you want to add different price tiers for the same product, click the `Add another price` button at the buttom.
|
||||
|
||||

|
||||
|
||||
- After you save the product, you'll be directed to the product page.
|
||||
- Copy the price IDs and paste them in the `.env.server` file under `HOBBY_SUBSCRIPTION_PRICE_ID=` and `PRO_SUBSCRIPTION_PRICE_ID=`. Note that if you change the names of the price IDs, you'll need to update your server code to match these names as well
|
||||
|
||||
## Create a Test Customer
|
||||
|
||||
To create a test customer, go to the test customers url [https://dashboard.stripe.com/test/customers](https://dashboard.stripe.com/test/customers).
|
||||
|
||||
- Click on the `Add a customer` button and fill in the relevant information for your test customer.
|
||||
:::note
|
||||
When filling in the test customer email address, use an address you have access to and will use when logging into your SaaS app. This is important because the email address is used to identify the customer when creating a subscription and allows you to manage your test user's payments/subscriptions via the test customer portal
|
||||
:::
|
||||
|
||||
## Get your Customer Portal Link
|
||||
|
||||
Go to https://dashboard.stripe.com/test/settings/billing/portal in the Stripe Dashboard and activate and copy the `Customer portal link`. Paste it in your `src/shared/constants.ts` file.
|
||||
|
||||
```ts title="src/shared/constants.ts"
|
||||
const customerPortalTestUrl = 'https//billing.stripe.com/p/login/test_...'
|
||||
const customerPortalProdUrl = undefined
|
||||
|
||||
export const STRIPE_CUSTOMER_PORTAL_LINK = isDev ? customerPortalTestUrl : customerPortalProdUrl;
|
||||
|
||||
checkStripePortalLinkExists(STRIPE_CUSTOMER_PORTAL_LINK); // throws an error if the link is not set in production
|
||||
```
|
||||
|
||||
Note that there are variables set aside for your test portal link, as well as a production portal link. You will be warned in the console if you there is no link in the development environment, but the app will throw an error if there is no link in the production environment!
|
||||
|
||||
```sh
|
||||
[Server] 🔍 Validating environment variables...
|
||||
[Server!] 🚫 STRIPE_CUSTOMER_PORTAL_LINK is not defined.
|
||||
[Server] 🚀 "Email and password" auth initialized
|
||||
[Server] 🚀 "Google" auth initialized
|
||||
```
|
||||
|
||||
## Install the Stripe CLI
|
||||
|
||||
To install the Stripe CLI with homebrew, run the following command in your terminal:
|
||||
|
||||
```sh
|
||||
brew install stripe/stripe-cli/stripe
|
||||
```
|
||||
|
||||
or for other install scripts or OSes, follow the instructions [here](https://stripe.com/docs/stripe-cli#install).
|
||||
|
||||
Now, let's start the webhook server and get our webhook signing secret.
|
||||
|
||||
```sh
|
||||
stripe listen --forward-to localhost:3001/stripe-webhook
|
||||
```
|
||||
|
||||
You should see a message like this:
|
||||
|
||||
```sh
|
||||
> Ready! You are using Stripe API Version [2023-08-16]. Your webhook signing secret is whsec_8a... (^C to quit)
|
||||
```
|
||||
|
||||
copy this secret to your `.env.server` file under `STRIPE_WEBHOOK_SECRET=`.
|
@ -1,68 +0,0 @@
|
||||
---
|
||||
title: Stripe Testing
|
||||
---
|
||||
This guide will show you how to test and try out your checkout, payments, and webhooks locally.
|
||||
|
||||
First, make sure you've set up your Stripe account for local development. You can find the guide [here](/guides/stripe-integration).
|
||||
|
||||
## Testing Webhooks via the Stripe CLI
|
||||
|
||||
- In a new terminal window, run the following command:
|
||||
|
||||
```sh
|
||||
stripe login
|
||||
```
|
||||
|
||||
- start the Stripe CLI webhook forwarding on port 3001 where your Node server is running.
|
||||
|
||||
```sh
|
||||
stripe listen --forward-to localhost:3001/stripe-webhook
|
||||
```
|
||||
|
||||
remember to copy and paste the outputted webhook signing secret (`whsec_...`) into your `.env.server` file under `STRIPE_WEBHOOK_SECRET=` if you haven't already.
|
||||
|
||||
- In another terminal window, trigger a test event:
|
||||
|
||||
```sh
|
||||
stripe trigger payment_intent.succeeded
|
||||
```
|
||||
|
||||
The results of the event firing will be visible in the initial terminal window. You should see messages like this:
|
||||
|
||||
```sh
|
||||
...
|
||||
2023-11-21 09:31:09 --> invoice.paid [evt_1OEpMPILOQf67J5TjrUgRpk4]
|
||||
2023-11-21 09:31:09 <-- [200] POST http://localhost:3001/stripe-webhook [evt_1OEpMPILOQf67J5TjrUgRpk4]
|
||||
2023-11-21 09:31:10 --> invoice.payment_succeeded [evt_1OEpMPILOQf67J5T3MFBr1bq]
|
||||
2023-11-21 09:31:10 <-- [200] POST http://localhost:3001/stripe-webhook [evt_1OEpMPILOQf67J5T3MFBr1bq]
|
||||
2023-11-21 09:31:10 --> checkout.session.completed [evt_1OEpMQILOQf67J5ThTZ0999r]
|
||||
2023-11-21 09:31:11 <-- [200] POST http://localhost:3001/stripe-webhook [evt_1OEpMQILOQf67J5ThTZ0999r]
|
||||
```
|
||||
|
||||
For more info on testing webhooks, check out https://stripe.com/docs/webhooks#test-webhook
|
||||
|
||||
## Testing Checkout and Payments via the Client
|
||||
|
||||
Make sure the **Stripe CLI is running** by following the steps above.
|
||||
You can then test the payment flow via the client by doing the following:
|
||||
|
||||
- Click on a Buy button on the for any of the products on the homepage. You should be redirected to the checkout page.
|
||||
- Fill in the form with the following test credit card number `4242 4242 4242 4242` and any future date for the expiration date and any 3 digits for the CVC.
|
||||
|
||||
- Click on the "Pay" button. You should be redirected to the success page.
|
||||
|
||||
- Check your terminal window for status messages and logs
|
||||
|
||||
- You can also check your Database via the DB Studio to see if the user entity has been updated by running:
|
||||
|
||||
```sh
|
||||
wasp db studio
|
||||
```
|
||||
|
||||

|
||||
|
||||
- Navigate to `localhost:5555` and click on the `users` table. You should see `hasPaid`is true and `subscriptionStatus` is `active` for the user that just made the purchase.
|
||||
|
||||
:::note
|
||||
If you want to learn more about how a user's payment status, subscription status, and subscription tier affect a user's priveledges within the app, check out the [User Permissions](/general/user-permissions) reference.
|
||||
:::
|
@ -2,47 +2,4 @@
|
||||
title: Introduction
|
||||
---
|
||||
|
||||
## Welcome to your new SaaS App!
|
||||
|
||||
<!-- {/* TODO: add a screenshot of the app */} -->
|
||||
|
||||
You've decided to build a SaaS app with this template. Great choice! 🎉
|
||||
|
||||
This template is:
|
||||
|
||||
1. fully open-source
|
||||
2. completely free to use and distribute
|
||||
3. comes with a ton of features out of the box!
|
||||
|
||||
Check it out in action here: [OpenSaaS.sh](https://opensaas.sh)
|
||||
Check out the Code: [Open SaaS GitHub Repo](https://github.com/wasp-lang/open-saas)
|
||||
|
||||
## What's inside?
|
||||
|
||||
The template itself is built on top of some very powerful tools and frameworks, including:
|
||||
|
||||
- 🐝 [Wasp](https://wasp-lang.dev) - a full-stack React, NodeJS, Prisma framework with superpowers
|
||||
- 🚀 [Astro](https://starlight.astro.build/) - Astro's lightweight "Starlight" template for documentation and blog
|
||||
- 💸 [Stripe](https://stripe.com) - for products and payments
|
||||
- 📈 [Plausible](https://plausible.io) or [Google](https://analytics.google.com/) Analytics
|
||||
- 🤖 [OpenAI](https://openai.com) - OpenAI API integrated into the app
|
||||
- 📧 [SendGrid](https://sendgrid.com), [MailGun](https://mailgun.com), or SMTP - for email sending
|
||||
- 💅 [TailwindCSS](https://tailwindcss.com) - for styling
|
||||
- 🧑💼 [TailAdmin](https://tailadmin.com/) - admin dashboard & components for TailwindCSS
|
||||
|
||||
Because we're using Wasp as the full-stack framework, we can leverage a lot of its features to build our SaaS in record time, including:
|
||||
|
||||
- 🔐 [Full-stack Authentication](https://wasp-lang.dev/docs/auth/overview) - Email verified + social Auth in a few lines of code.
|
||||
- ⛑ [End-to-end Type Safety](https://wasp-lang.dev/docs/data-model/operations/overview) - Type your backend functions and get inferred types on the front-end automatically, without the need to install or configure any third-party libraries. Oh, and type-safe Links, too!
|
||||
- 🤖 [Jobs](https://wasp-lang.dev/docs/language/features#jobs) - Run cron jobs in the background or set up queues simply by defining a function in the config file.
|
||||
- 🚀 [One-command Deploy](https://wasp-lang.dev/docs/advanced/deployment/overview) - Easily deploy via the CLI to [Fly.io](https://fly.io), or to other provides like [Railway](https://railway.app) and [Netlify](https://netlify.com).
|
||||
|
||||
You also get access to Wasp's diverse, helpful community if you get stuck or need help.
|
||||
- 🤝 [Wasp Discord](https://discord.gg/aCamt5wCpS)
|
||||
|
||||
:::tip["Work In Progress"]
|
||||
We've tried to get as many of the core features of a SaaS app into this template as possible, but there still might be some missing features or functionality.
|
||||
|
||||
We could always use some help tying up loose ends, so consider [contributing](https://github.com/wasp-lang/open-saas/blob/main/CONTRIBUTING.md)!
|
||||
:::
|
||||
|
||||
Hello World! 👋
|
@ -1,90 +0,0 @@
|
||||
---
|
||||
title: Getting Started
|
||||
---
|
||||
|
||||
This guide will help you get your new SaaS app up and running.
|
||||
|
||||
## Setting up
|
||||
|
||||
### Install Wasp
|
||||
|
||||
Install Wasp by running this command in your terminal:
|
||||
```sh
|
||||
curl -sSL https://get.wasp-lang.dev/installer.sh | sh
|
||||
```
|
||||
|
||||
:::tip
|
||||
Make sure to install the Wasp VSCode extension to get the best DX, e.g. syntax highlighting, code scaffolding, autocomplete, etc:
|
||||
|
||||
🐝 [Wasp VSCode Extension](https://marketplace.visualstudio.com/items?itemName=wasp-lang.wasp) 🧑💻
|
||||
:::
|
||||
|
||||
### Clone the OpenSaaS repo
|
||||
|
||||
Clone this repo by running this command in your terminal:
|
||||
```sh
|
||||
git clone https://github.com/wasp-lang/open-saas.git
|
||||
```
|
||||
|
||||
Then position yourself in the root of the project:
|
||||
```sh
|
||||
cd open-saas
|
||||
```
|
||||
|
||||
### Start your DB
|
||||
Before you start your app, you need to have a Postgres Database connected and running. With Wasp, that's super easy!
|
||||
|
||||
First, make sure you have Docker installed and running. If not, download and install it [here](https://www.docker.com/products/docker-desktop/)
|
||||
|
||||
With Docker running, open a new terminal window/tab and from within the root of the project, run:
|
||||
```sh
|
||||
wasp start db
|
||||
```
|
||||
This will start and connect your app to a Postgres database for you. No need to do anything else! 🤯
|
||||
|
||||
Whenever you make any changes to your schema, you can migrate them with:
|
||||
```sh
|
||||
wasp db migrate-dev
|
||||
```
|
||||
|
||||
If you want to see or manage your DB via Prisma's DB Studio GUI, run:
|
||||
```sh
|
||||
wasp db studio
|
||||
```
|
||||
|
||||
### Start your app
|
||||
In a new terminal window/tab, run:
|
||||
```sh
|
||||
wasp start
|
||||
```
|
||||
This will install all dependencies and start the client and server for you :)
|
||||
|
||||
Go to `localhost:3000` in your browser to view it (your NodeJS server will be running on port `3001`)
|
||||
|
||||
### More Help/Further Info
|
||||
For more info on Wasp as a full-stack React, NodeJS, Prisma framework, check out the [Wasp docs](https://wasp-lang.dev/docs/).
|
||||
|
||||
If you get stuck or need help, join the [Wasp Discord](https://discord.gg/aCamt5wCpS).
|
||||
|
||||
#### Run Blog and Docs
|
||||
|
||||
This SaaS app comes with a docs and blog section built with the [Starlight template on top of the Astro](https://starlight.astro.build) framework. You can use this as a starting point for your own blog and documentation, if necessary.
|
||||
|
||||
If you do not need this, you can simply delete the `blog` folder from the root of the project.
|
||||
|
||||
If you want to run the Starlight docs and blog, first navigate to the `blog` folder:
|
||||
|
||||
```sh
|
||||
cd blog
|
||||
```
|
||||
|
||||
Then run:
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
Then start the development server:
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
@ -1,202 +0,0 @@
|
||||
---
|
||||
title: Guided Tour
|
||||
---
|
||||
|
||||
Let's get to know our new SaaS app.
|
||||
|
||||
First, we'll take a look at the project's file structure, then dive into its main features and how you can get started customizing them.
|
||||
|
||||
## Getting acquainted with the codebase
|
||||
|
||||
At the root of our project, you will see two folders:
|
||||
```sh
|
||||
.
|
||||
├── app
|
||||
└── blog
|
||||
```
|
||||
|
||||
`app` contains the Wasp project files, which is your full-stack React + NodeJS + Prisma app along with a Wasp config file, which will be explained in more detail below.
|
||||
|
||||
`blog` contains the [Astro Starlight template](https://starlight.astro.build/) for the blog and documentation section.
|
||||
|
||||
Let's check out what's in the `app` folder in more detail:
|
||||
```sh
|
||||
.
|
||||
├── main.wasp # Wasp Config file. You define your app structure here.
|
||||
├── src
|
||||
│ ├── client # Your client code (React) goes here.
|
||||
│ ├── server # Your server code (NodeJS) goes here.
|
||||
│ ├── shared # Your shared (runtime independent) code goes here.
|
||||
│ └── .waspignore
|
||||
├── .env.server # Environment variables for your server code.
|
||||
├── .env.client # Environment variables for your client code.
|
||||
└── .wasproot
|
||||
```
|
||||
|
||||
### The Wasp Config file
|
||||
|
||||
This template at its core is a Wasp template, where [Wasp](https://wasp-lang.dev) is a full-stack web app framework that let’s you write your app in React, NodeJS, and Prisma and will manage the "boilerplatey" work for you, allowing you to just take care of the fun stuff!
|
||||
|
||||
[Wasp's secret sauce](https://wasp-lang.dev/docs) is its use of a config file (`main.wasp`) and compiler which takes your code and outputs the client app, server app and deployment code for you.
|
||||
|
||||
In this template, we've already defined a number of things in the `main.wasp` config file, including:
|
||||
|
||||
- Auth
|
||||
- Routes and Pages
|
||||
- Prisma Database Models
|
||||
- Operations (data read and write functions)
|
||||
- Background Jobs
|
||||
- Email Sending
|
||||
|
||||
By defining these things in the config file, Wasp continuously handles the boilerplate necessary with putting all these features together. You just need to focus on the business logic of your app.
|
||||
|
||||
Wasp abstracts away some things that you would normally be used to doing during development, so don't be surprised if you don't see some of the things you're used to seeing.
|
||||
|
||||
:::note
|
||||
It's possible to learn Wasp's feature set simply through using this template, but if you find yourself unsure how to implement a Wasp-specific feature and/or just want to learn more, a great starting point is the intro tutorial in the [Wasp docs](https://wasp-lang.dev/docs/) which takes ~20 minutes.
|
||||
:::
|
||||
|
||||
### Client
|
||||
|
||||
The `src/client` folder contains all the code that runs in the browser. It's a standard React app, with a few Wasp-specific things sprinkled in.
|
||||
|
||||
```sh
|
||||
.
|
||||
└── client
|
||||
├── admin # Admin dashboard pages and components
|
||||
├── app # Your user-facing app that sits behind the paywall/login.
|
||||
├── auth # All auth-related pages and components.
|
||||
├── components # Your shared React components.
|
||||
├── hooks # Your shared React hooks.
|
||||
├── landing-page # Landing page related code
|
||||
├── public # Assets that are publicly accessible, e.g. www.yourdomain.com/banner.png
|
||||
├── static # Assets that you need access to in your code, e.g. import logo from 'static/logo.png'
|
||||
├── App.tsx # Main app component to wrap all child components. Useful for global state, navbars, etc.
|
||||
└── Main.css
|
||||
|
||||
```
|
||||
|
||||
### Server
|
||||
|
||||
The `src/server` folder contains all the code that runs on the server. Wasp compiles everything into a NodeJS server for you.
|
||||
|
||||
All you have to do is define your server-side functions in the `main.wasp` file, write the logic in a function within `src/server` and Wasp will generate the boilerplate code for you.
|
||||
|
||||
```sh
|
||||
└── server
|
||||
├── auth # Some small auth-related functions to customize the auth flow.
|
||||
├── scripts # Scripts to run via Wasp, e.g. database seeding.
|
||||
├── webhooks # The webhook handler for Stripe.
|
||||
├── workers # Functions that run in the background as Wasp Jobs, e.g. daily stats calculation.
|
||||
├── actions.ts # Your server-side write/mutation functions.
|
||||
├── queries.ts # Your server-side read functions.
|
||||
├── stripeUtils.ts
|
||||
└── types.ts
|
||||
```
|
||||
|
||||
## Main Features
|
||||
|
||||
### Auth
|
||||
|
||||
This template comes with a fully functional auth flow out of the box. It takes advantages of Wasp's built-in [Auth features](https://wasp-lang.dev/docs/auth/overview), which do the dirty work of rolling your own full-stack auth for you!
|
||||
|
||||
```js title="main.wasp"
|
||||
auth: {
|
||||
userEntity: User,
|
||||
externalAuthEntity: SocialLogin,
|
||||
methods: {
|
||||
email: {},
|
||||
google: {},
|
||||
github: {},
|
||||
},
|
||||
onAuthFailedRedirectTo: "/",
|
||||
},
|
||||
```
|
||||
|
||||
By defining the auth structure in your `main.wasp` file, Wasp generates all the necessary code for you, including:
|
||||
- Email verified login with reset password
|
||||
- Social login with Google and/or GitHub
|
||||
- Custom-generated AuthUI components for login, signup, and reset password
|
||||
- Auth hooks for fetching user data
|
||||
|
||||
<!-- TODO: add pic of AuthUI components -->
|
||||
|
||||
We've set the template up with Wasp's simplest auth flow, `usernameAndPassword`, but we suggest you only use it to get your app developlment going and opt for `email`, `google`, `gitHub`, or a combination of them in production.
|
||||
|
||||
You'll notice that `google` and `email` methods are also pre-configured. If you'd like to use these configurations in your app, make sure to check out the [Authentication Guide](/guides/authentication) which gives you details on obtaining necessary API keys and integrations.
|
||||
|
||||
### Subscription Payments with Stripe
|
||||
|
||||
No SaaS is complete without payments, specifically subscription payments. That's why this template comes with a fully functional Stripe integration.
|
||||
|
||||
Let's take a quick look at how payments are handled in this template.
|
||||
|
||||
1. a user clicks the `BUY` button and a **Stripe Checkout session** is created on the server
|
||||
2. the user is redirected to the Stripe Checkout page where they enter their payment info
|
||||
3. the user is redirected back to the app and the Stripe Checkout session is completed
|
||||
4. Stripe sends a webhook event to the server with the payment info
|
||||
5. The app server's **webhook handler** handles the event and updates the user's subscription status
|
||||
|
||||
The logic for creating the Stripe Checkout session is defined in the `src/server/actions.ts` file. [Actions](https://wasp-lang.dev/docs/data-model/operations/actions) are your server-side functions that are used to write or update data to the database. Once they're defined in the `main.wasp` file, you can easily call them on the client-side:
|
||||
|
||||
a) define the action in the `main.wasp` file
|
||||
```js title="main.wasp"
|
||||
action stripePayment {
|
||||
fn: import { stripePayment } from "@server/actions.js",
|
||||
entities: [User]
|
||||
}
|
||||
```
|
||||
|
||||
b) implement the action in the `src/server/actions.ts` file
|
||||
```js title="src/server/actions.ts"
|
||||
export const stripePayment = async (tier, context) => {
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
c) call the action on the client-side
|
||||
```js title="src/client/app/SubscriptionPage.tsx"
|
||||
import stripePayment from '@wasp/actions/stripePayment';
|
||||
|
||||
const handleBuyClick = async (tierId) => {
|
||||
const stripeResults = await stripePayment(tierId);
|
||||
};
|
||||
```
|
||||
|
||||
The webhook handler is defined in the `src/server/webhooks/stripe.ts` file. Unlike Actions and Queries in Wasp which are only to be used internally, we define the webhook handler in the `main.wasp` file as an API endpoint in order to expose it externally to Stripe
|
||||
|
||||
```js title="main.wasp"
|
||||
api stripeWebhook {
|
||||
fn: import { stripeWebhook } from "@server/webhooks/stripe.js",
|
||||
httpRoute: (POST, "/stripe-webhook")
|
||||
entities: [User],
|
||||
}
|
||||
```
|
||||
|
||||
Within the webhook handler, we look for specific events that Stripe sends us to let us know which payment was completed and for which user. Then we update the user's subscription status in the database.
|
||||
|
||||
To learn more about configuring the app to handle your products and payments, check out the [Stripe Integration guide](/guides/stripe-integration).
|
||||
|
||||
### Analytics and Admin Dashboard
|
||||
|
||||
Keeping an eye on your metrics is crucial for any SaaS. That's why we've built an administrator's dashboard where you can view your app's stats, user data, and Stripe revenue all in one place.
|
||||
|
||||
<!-- TODO: add pic of admin dash -->
|
||||
|
||||
To do that, we've leveraged Wasp's [Jobs feature](https://wasp-lang.dev/docs/advanced/jobs) to run a cron job that calculates your daily stats. The app stats, such as page views and sources, can be pulled from either Plausible or Google Analytics. All you have to do is create an project with the analytics provider of your choice and import the repsective pre-built helper functions!
|
||||
|
||||
```js title="main.wasp"
|
||||
job dailyStatsJob {
|
||||
executor: PgBoss,
|
||||
perform: {
|
||||
fn: import { calculateDailyStats } from "@server/workers/calculateDailyStats.js"
|
||||
},
|
||||
schedule: {
|
||||
cron: "0 * * * *" // runs every hour
|
||||
},
|
||||
entities: [User, DailyStats, Logs, PageViewSource]
|
||||
}
|
||||
```
|
||||
|
||||
For more info on integrating Plausible or Google Analytics, check out the [Analytics guide](/guides/analytics).
|
||||
|
Loading…
x
Reference in New Issue
Block a user