diff --git a/README.md b/README.md
index 35c0022..4a00822 100644
--- a/README.md
+++ b/README.md
@@ -3,11 +3,10 @@
## Running it locally
-Before you being, install [Wasp](https://wasp-lang.dev) by running `curl -sSL https://get.wasp-lang.dev/installer.sh | sh` in your terminal.
-1. First clone this repo.
-2. Create a `.env.server` file in the root of the project
-3. Copy the `env.server.example` file contents to `.env.server` and fill in your API keys
+1. Make sure you have the latest version of [Wasp](https://wasp-lang.dev) installed by running `curl -sSL https://get.wasp-lang.dev/installer.sh | sh` in your terminal.
+2. Run `wasp new -t saas` to create a new app using this template.
+3. Rename the `env.server.example` file to `.env.server` and fill in your API keys
4. Make sure you have a Database connected and running. Here are two quick options:
- run `wasp start db` if you have Docker installed (if not, on MacOS run `brew install docker-machine docker`). This will start a Postgres database for you. No need to do anything else! 🤯
- or provision a Postgres database on [Railway](https://railway.app), go to settings and copy the `connection url`. Past it as `DATABASE_URL=` into your `env.server` file.
diff --git a/main.wasp b/main.wasp
index 8ced9a0..bb5ec71 100644
--- a/main.wasp
+++ b/main.wasp
@@ -15,10 +15,26 @@ app SaaSTemplate {
userEntity: User,
externalAuthEntity: SocialLogin,
methods: {
+ email: {
+ fromField: {
+ name: "SaaS App",
+ // make sure this address is the same you registered your SendGrid or MailGun account with!
+ email: "vince@wasp-lang.dev"
+ },
+ emailVerification: {
+ clientRoute: EmailVerificationRoute,
+ getEmailContentFn: import { getVerificationEmailContent } from "@server/auth/email.js",
+ },
+ passwordReset: {
+ clientRoute: PasswordResetRoute,
+ getEmailContentFn: import { getPasswordResetEmailContent } from "@server/auth/email.js",
+ },
+ },
google: { // Guide for setting up Auth via Google https://wasp-lang.dev/docs/integrations/google
getUserFieldsFn: import { getUserFields } from "@server/auth/google.js",
configFn: import { config } from "@server/auth/google.js",
},
+ // gitHub: {} // Guide for setting up Auth via Github https://wasp-lang.dev/docs/integrations/github
},
onAuthFailedRedirectTo: "/",
},
@@ -31,10 +47,15 @@ app SaaSTemplate {
emailSender: {
provider: SendGrid,
defaultFrom: {
- name: "MySaaSApp",
- // make sure this address is the same you registered your SendGrid account with!
- email: "email@mysaasapp.com"
+ name: "SaaS App",
+ // make sure this address is the same you registered your SendGrid or MailGun account with!
+ email: "vince@wasp-lang.dev"
},
+ // defaultFrom: {
+ // name: "MySaaSApp",
+ // // make sure this address is the same you registered your SendGrid or MailGun account with!
+ // email: "email@mysaasapp.com"
+ // },
},
dependencies: [
("@headlessui/react", "1.7.13"),
@@ -55,15 +76,19 @@ app SaaSTemplate {
*/
entity User {=psl
- id Int @id @default(autoincrement())
- email String @unique
- stripeId String?
- checkoutSessionId String?
- hasPaid Boolean @default(false)
- sendEmail Boolean @default(false)
- datePaid DateTime?
- credits Int @default(3)
- relatedObject RelatedObject[]
+ id Int @id @default(autoincrement())
+ email String? @unique
+ password String?
+ isEmailVerified Boolean @default(false)
+ emailVerificationSentAt DateTime?
+ passwordResetSentAt DateTime?
+ stripeId String?
+ checkoutSessionId String?
+ hasPaid Boolean @default(false)
+ sendEmail Boolean @default(false)
+ datePaid DateTime?
+ credits Int @default(3)
+ relatedObject RelatedObject[]
externalAuthAssociations SocialLogin[]
psl=}
@@ -99,7 +124,27 @@ page MainPage {
route LoginRoute { path: "/login", to: LoginPage }
page LoginPage {
- component: import Login from "@client/Login"
+ component: import Login from "@client/auth/LoginPage"
+}
+
+route SignupRoute { path: "/signup", to: SignupPage }
+page SignupPage {
+ component: import { Signup } from "@client/auth/SignupPage"
+}
+
+route RequestPasswordResetRoute { path: "/request-password-reset", to: RequestPasswordResetPage }
+page RequestPasswordResetPage {
+ component: import { RequestPasswordReset } from "@client/auth/RequestPasswordReset",
+}
+
+route PasswordResetRoute { path: "/password-reset", to: PasswordResetPage }
+page PasswordResetPage {
+ component: import { PasswordReset } from "@client/auth/PasswordReset",
+}
+
+route EmailVerificationRoute { path: "/email-verification", to: EmailVerificationPage }
+page EmailVerificationPage {
+ component: import { EmailVerification } from "@client/auth/EmailVerification",
}
route GptRoute { path: "/gpt", to: GptPage }
@@ -159,6 +204,7 @@ query getRelatedObjects {
/*
* 📡 These are custom Wasp API Endpoints. Use them for callbacks, webhooks, etc.
+ * https://wasp-lang.dev/docs/language/features#apis
*/
api stripeWebhook {
diff --git a/migrations/20230417122144_email_password/migration.sql b/migrations/20230417122144_email_password/migration.sql
new file mode 100644
index 0000000..983a5dd
--- /dev/null
+++ b/migrations/20230417122144_email_password/migration.sql
@@ -0,0 +1,6 @@
+-- AlterTable
+ALTER TABLE "User" ADD COLUMN "emailVerificationSentAt" TIMESTAMP(3),
+ADD COLUMN "isEmailVerified" BOOLEAN NOT NULL DEFAULT false,
+ADD COLUMN "password" TEXT,
+ADD COLUMN "passwordResetSentAt" TIMESTAMP(3),
+ALTER COLUMN "email" DROP NOT NULL;
diff --git a/src/client/AccountPage.tsx b/src/client/AccountPage.tsx
index 4fa705e..1c4cd7f 100644
--- a/src/client/AccountPage.tsx
+++ b/src/client/AccountPage.tsx
@@ -72,7 +72,7 @@ function BuyMoreButton({ isLoading, setIsLoading }: { isLoading: boolean, setIsL
const handleClick = async () => {
setIsLoading(true);
const stripeResults = await stripePayment();
- if (stripeResults) {
+ if (stripeResults?.sessionUrl) {
window.open(stripeResults.sessionUrl, '_self');
}
setIsLoading(false);
diff --git a/src/client/CheckoutPage.tsx b/src/client/CheckoutPage.tsx
index 2c9738b..bf43baa 100644
--- a/src/client/CheckoutPage.tsx
+++ b/src/client/CheckoutPage.tsx
@@ -8,7 +8,6 @@ export default function CheckoutPage({ user }: { user: User }) {
const history = useHistory();
useEffect(() => {
-
function delayedRedirect() {
return setTimeout(() => {
history.push('/account');
@@ -33,19 +32,23 @@ export default function CheckoutPage({ user }: { user: User }) {
}, []);
return (
- <>
-