open-saas/main.wasp
2023-11-30 16:16:18 +01:00

368 lines
13 KiB
Plaintext

app SaaSTemplate {
wasp: {
version: "^0.11.6"
},
title: "My Open SaaS App",
head: [
"<meta property='og:type' content='website' />",
"<meta property='og:url' content='https://mySaaSapp.com' />", // TODO change url
"<meta property='og:description' content='I made a SaaS App. Buy my stuff.' />",
"<meta property='og:image' content='src/client/static/image.png' />",
"<meta name='twitter:image' content='https://mySaaSapp.com/gptsaastemplate.png' />", // TODO change url and image
"<meta name='twitter:image:width' content='800' />",
"<meta name='twitter:image:height' content='400' />",
"<meta name='twitter:card' content='summary_large_image' />",
// you can put your analytics scripts here, too!
"<script defer data-domain='localhost' src='https://plausible.apps.twoducks.dev/js/script.js'></script>",
// plausible has script extension `script.local.js` for local development
"<script defer data-domain='localhost' src='https://plausible.apps.twoducks.dev/js/script.local.js'></script>",
// google analytics automatically detects if you are in dev mode and
"<!-- Google tag (gtag.js) --><script async src='https://www.googletagmanager.com/gtag/js?id=G-H3LSJCK95H'></script><script>window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'G-H3LSJCK95H');</script>"
],
// 🔐 Auth out of the box! https://wasp-lang.dev/docs/auth/overview
auth: {
userEntity: User,
externalAuthEntity: SocialLogin,
methods: {
email: {
fromField: {
name: "Open 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/auth/social-auth/overview
getUserFieldsFn: import { getUserFields } from "@server/auth/google.js",
configFn: import { config } from "@server/auth/google.js",
},
},
signup: {
additionalFields: import setAdminUsers from "@server/auth/setAdminUsers.js",
},
onAuthFailedRedirectTo: "/",
},
db: {
system: PostgreSQL,
seeds: [
import { devSeedUsers } from "@server/scripts/usersSeed.js",
]
},
client: {
rootComponent: import App from "@client/App",
},
emailSender: {
provider: SendGrid,
defaultFrom: {
name: "SaaS App",
// make sure this address is the same you registered your SendGrid or MailGun account with!
email: "vince@wasp-lang.dev" // TODO change to generic email before pushing to github
},
},
// add your dependencies here. the quickest way to find the latest version is `npm view <package-name> version`
dependencies: [
("@headlessui/react", "1.7.13"),
("@tailwindcss/forms", "^0.5.3"),
("@tailwindcss/typography", "^0.5.7"),
("react-hook-form", "7.43.1"),
("react-icons", "4.11.0"),
("node-fetch", "3.3.0"),
("react-hook-form", "^7.45.4"),
("stripe", "11.15.0"),
("react-hot-toast", "^2.4.1"),
("react-apexcharts", "^1.4.1"),
("apexcharts", "^3.41.0"),
("headlessui", "^0.0.0"),
("@faker-js/faker", "8.3.1"),
("@google-analytics/data", "4.1.0")
],
}
/* 💽 Wasp defines DB entities via Prisma Database Models:
* https://wasp-lang.dev/docs/data-model/entities
*/
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)
gptResponses GptResponse[]
externalAuthAssociations SocialLogin[]
contactFormMessages ContactFormMessage[]
psl=}
entity SocialLogin {=psl
id String @id @default(uuid())
provider String
providerId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId Int
createdAt DateTime @default(now())
@@unique([provider, providerId, userId])
psl=}
// This can be anything. In most cases, this will be your product
entity GptResponse {=psl
id String @id @default(uuid())
content String
user User @relation(fields: [userId], references: [id])
userId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
psl=}
entity ContactFormMessage {=psl
id String @id @default(uuid())
content String
user User @relation(fields: [userId], references: [id])
userId Int
createdAt DateTime @default(now())
isRead Boolean @default(false)
repliedAt DateTime?
psl=}
entity DailyStats {=psl
id Int @id @default(autoincrement())
date DateTime @default(now()) @unique
totalViews Int @default(0)
prevDayViewsChangePercent String @default("0")
userCount Int @default(0)
paidUserCount Int @default(0)
userDelta Int @default(0)
paidUserDelta Int @default(0)
totalRevenue Float @default(0)
totalProfit Float @default(0)
sources PageViewSource[]
psl=}
entity PageViewSource {=psl
date DateTime @default(now())
name String
visitors Int
dailyStats DailyStats? @relation(fields: [dailyStatsId], references: [id])
dailyStatsId Int?
@@id([date, name])
psl=}
entity Logs {=psl
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
message String
level String
psl=}
/* 📡 These are the Wasp Routes (You can protect them easily w/ 'authRequired: true');
* https://wasp-lang.dev/docs/tutorial/pages
*/
route LandingPageRoute { path: "/", to: LandingPage }
page LandingPage {
component: import LandingPage from "@client/landing-page/LandingPage"
}
route LoginRoute { path: "/login", to: LoginPage }
page LoginPage {
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 }
page GptPage {
authRequired: true,
component: import GptPage from "@client/app/GptPage"
}
route PricingRoute { path: "/pricing", to: PricingPage }
page PricingPage {
component: import Pricing from "@client/app/PricingPage"
}
route AccountRoute { path: "/account", to: AccountPage }
page AccountPage {
authRequired: true,
component: import Account from "@client/app/AccountPage"
}
route CheckoutRoute { path: "/checkout", to: CheckoutPage }
page CheckoutPage {
authRequired: true,
component: import Checkout from "@client/app/CheckoutPage"
}
route AdminRoute { path: "/admin", to: DashboardPage }
page DashboardPage {
authRequired: true,
component: import Dashboard from "@client/admin/pages/Dashboard/Ecommerce"
}
route AdminUsersRoute { path: "/admin/users", to: AdminUsersPage }
page AdminUsersPage {
authRequired: true,
component: import AdminUsers from "@client/admin/pages/Users"
}
route AdminSettingsRoute { path: "/admin/settings", to: AdminSettingsPage }
page AdminSettingsPage {
authRequired: true,
component: import AdminSettings from "@client/admin/pages/Settings"
}
route AdminChartsRoute { path: "/admin/chart", to: AdminChartsPage }
page AdminChartsPage {
authRequired: true,
component: import AdminCharts from "@client/admin/pages/Chart"
}
route AdminFormElementsRoute { path: "/admin/forms/form-elements", to: AdminFormElementsPage }
page AdminFormElementsPage {
authRequired: true,
component: import AdminForms from "@client/admin/pages/Form/FormElements"
}
route AdminFormLayoutsRoute { path: "/admin/forms/form-layouts", to: AdminFormLayoutsPage }
page AdminFormLayoutsPage {
authRequired: true,
component: import AdminForms from "@client/admin/pages/Form/FormLayout"
}
route AdminCalendarRoute { path: "/admin/calendar", to: AdminCalendarPage }
page AdminCalendarPage {
authRequired: true,
component: import AdminCalendar from "@client/admin/pages/Calendar"
}
route AdminUIAlertsRoute { path: "/admin/ui/alerts", to: AdminUIAlertsPage }
page AdminUIAlertsPage {
authRequired: true,
component: import AdminUI from "@client/admin/pages/UiElements/Alerts"
}
route AdminUIButtonsRoute { path: "/admin/ui/buttons", to: AdminUIButtonsPage }
page AdminUIButtonsPage {
authRequired: true,
component: import AdminUI from "@client/admin/pages/UiElements/Buttons"
}
/* ⛑ These are the Wasp Operations, which allow the client and server to interact:
* https://wasp-lang.dev/docs/data-model/operations/overview
*/
// 📝 Actions aka Mutations
action generateGptResponse {
fn: import { generateGptResponse } from "@server/actions.js",
entities: [User, GptResponse]
}
action stripePayment {
fn: import { stripePayment } from "@server/actions.js",
entities: [User]
}
action updateCurrentUser {
fn: import { updateCurrentUser } from "@server/actions.js",
entities: [User]
}
action updateUserById {
fn: import { updateUserById } from "@server/actions.js",
entities: [User]
}
// 📚 Queries
query getGptResponses {
fn: import { getGptResponses } from "@server/queries.js",
entities: [User, GptResponse]
}
query getDailyStats {
fn: import { getDailyStats } from "@server/queries.js",
entities: [User, DailyStats]
}
query getPaginatedUsers {
fn: import { getPaginatedUsers } from "@server/queries.js",
entities: [User]
}
/*
* 📡 These are custom Wasp API Endpoints. Use them for callbacks, webhooks, etc.
* https://wasp-lang.dev/docs/advanced/apis
*/
api stripeWebhook {
fn: import { stripeWebhook } from "@server/webhooks/stripe.js",
entities: [User],
middlewareConfigFn: import { stripeMiddlewareFn } from "@server/webhooks/stripe.js",
httpRoute: (POST, "/stripe-webhook")
}
/* 🕵️‍♂️ These are the Wasp Cron Jobs. Use them to set up recurring tasks and/or queues:
* https://wasp-lang.dev/docs/advanced/jobs
*/
job emailChecker {
executor: PgBoss,
perform: {
fn: import { checkAndQueueEmails } from "@server/workers/checkAndQueueEmails.js"
},
schedule: {
cron: "0 7 * * 1" // at 7:00 am every Monday
},
entities: [User]
}
job dailyStatsJob {
executor: PgBoss,
perform: {
fn: import { calculateDailyStats } from "@server/workers/calculateDailyStats.js"
},
schedule: {
// every hour
// cron: "0 * * * *"
cron: "* * * * *"
},
entities: [User, DailyStats, Logs, PageViewSource]
}