app OpenSaaS { wasp: { version: "^0.12.0" }, title: "My Open SaaS App", head: [ "", "", "", "", "", "", "", "", "", // you can put your analytics scripts here "", // plausible has script extension `script.local.js` for local development "", // google analytics only needs one script and will automatically detect if you are in dev mode "" ], // 🔐 Auth out of the box! https://wasp-lang.dev/docs/auth/overview auth: { userEntity: User, methods: { usernameAndPassword: { // !IMPORTANT: this method is only suitable for dev/testing. Use social or email methods in production. userSignupFields: import { getUsernameAndPasswordUserFields } from "@src/server/auth/setUsername.js", }, // google: { // Guide for setting up Auth via Google https://wasp-lang.dev/docs/auth/social-auth/overview // userSignupFields: import { getGoogleUserFields } from "@src/server/auth/setUsername.js", // configFn: import { getGoogleAuthConfig } from "@src/server/auth/setUsername.js", // }, // gitHub: { // userSignupFields: import { getGitHubUserFields } from "@src/server/auth/setUsername.js", // configFn: import { getGitHubAuthConfig } from "@src/server/auth/setUsername.js", // }, // 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 "@src/server/auth/email.js", // }, // passwordReset: { // clientRoute: PasswordResetRoute, // getEmailContentFn: import { getPasswordResetEmailContent } from "@src/server/auth/email.js", // }, // userSignupFields: import { getEmailUserFields } from "@src/server/auth/setUsername.js", // }, }, onAuthFailedRedirectTo: "/login", onAuthSucceededRedirectTo: "/demo-app", }, db: { system: PostgreSQL, seeds: [ import { devSeedUsers } from "@src/server/scripts/usersSeed.js", ] }, client: { rootComponent: import App from "@src/client/App", }, emailSender: { provider: SendGrid, defaultFrom: { name: "Open SaaS App", // make sure this address is the same you registered your SendGrid or MailGun account with! email: "me@example.com" }, }, } /* 💽 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 username String? @unique createdAt DateTime @default(now()) lastActiveTimestamp DateTime @default(now()) isAdmin Boolean @default(false) stripeId String? checkoutSessionId String? hasPaid Boolean @default(false) subscriptionTier String? subscriptionStatus String? sendEmail Boolean @default(false) datePaid DateTime? credits Int @default(3) gptResponses GptResponse[] contactFormMessages ContactFormMessage[] tasks Task[] files File[] psl=} 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 Task {=psl id String @id @default(uuid()) description String time String @default("1") isDone Boolean @default(false) user User @relation(fields: [userId], references: [id]) userId Int createdAt DateTime @default(now()) psl=} entity File {=psl id String @id @default(uuid()) name String type String key String uploadUrl String user User @relation(fields: [userId], references: [id]) userId Int createdAt DateTime @default(now()) psl=} // TODO: add functionality to allow users to send messages to admin // and make them accessible via the admin dashboard 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 "@src/client/landing-page/LandingPage" } route LoginRoute { path: "/login", to: LoginPage } page LoginPage { component: import Login from "@src/client/auth/LoginPage" } route SignupRoute { path: "/signup", to: SignupPage } page SignupPage { component: import { Signup } from "@src/client/auth/SignupPage" } // route RequestPasswordResetRoute { path: "/request-password-reset", to: RequestPasswordResetPage } // page RequestPasswordResetPage { // component: import { RequestPasswordReset } from "@src/client/auth/RequestPasswordReset", // } // route PasswordResetRoute { path: "/password-reset", to: PasswordResetPage } // page PasswordResetPage { // component: import { PasswordReset } from "@src/client/auth/PasswordReset", // } // route EmailVerificationRoute { path: "/email-verification", to: EmailVerificationPage } // page EmailVerificationPage { // component: import { EmailVerification } from "@src/client/auth/EmailVerification", // } route DemoAppRoute { path: "/demo-app", to: DemoAppPage } page DemoAppPage { authRequired: true, component: import DemoAppPage from "@src/client/app/DemoAppPage" } route PricingPageRoute { path: "/pricing", to: PricingPage } page PricingPage { component: import PricingPage from "@src/client/app/PricingPage" } route AccountRoute { path: "/account", to: AccountPage } page AccountPage { authRequired: true, component: import Account from "@src/client/app/AccountPage" } route CheckoutRoute { path: "/checkout", to: CheckoutPage } page CheckoutPage { authRequired: true, component: import Checkout from "@src/client/app/CheckoutPage" } route FileUploadRoute { path: "/file-upload", to: FileUploadPage } page FileUploadPage { authRequired: true, component: import FileUpload from "@src/client/app/FileUploadPage" } route AdminRoute { path: "/admin", to: DashboardPage } page DashboardPage { authRequired: true, component: import Dashboard from "@src/client/admin/pages/DashboardPage" } route AdminUsersRoute { path: "/admin/users", to: AdminUsersPage } page AdminUsersPage { authRequired: true, component: import AdminUsers from "@src/client/admin/pages/Users" } route AdminSettingsRoute { path: "/admin/settings", to: AdminSettingsPage } page AdminSettingsPage { authRequired: true, component: import AdminSettings from "@src/client/admin/pages/Settings" } route AdminChartsRoute { path: "/admin/chart", to: AdminChartsPage } page AdminChartsPage { authRequired: true, component: import AdminCharts from "@src/client/admin/pages/Chart" } route AdminMessagesRoute { path: "/admin/messages", to: AdminMessagesPage } page AdminMessagesPage { authRequired: true, component: import AdminMessages from "@src/client/admin/pages/Messages" } route AdminFormElementsRoute { path: "/admin/forms/form-elements", to: AdminFormElementsPage } page AdminFormElementsPage { authRequired: true, component: import AdminForms from "@src/client/admin/pages/Form/FormElements" } route AdminFormLayoutsRoute { path: "/admin/forms/form-layouts", to: AdminFormLayoutsPage } page AdminFormLayoutsPage { authRequired: true, component: import AdminForms from "@src/client/admin/pages/Form/FormLayout" } route AdminCalendarRoute { path: "/admin/calendar", to: AdminCalendarPage } page AdminCalendarPage { authRequired: true, component: import AdminCalendar from "@src/client/admin/pages/Calendar" } route AdminUIAlertsRoute { path: "/admin/ui/alerts", to: AdminUIAlertsPage } page AdminUIAlertsPage { authRequired: true, component: import AdminUI from "@src/client/admin/pages/UiElements/Alerts" } route AdminUIButtonsRoute { path: "/admin/ui/buttons", to: AdminUIButtonsPage } page AdminUIButtonsPage { authRequired: true, component: import AdminUI from "@src/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 action generateGptResponse { fn: import { generateGptResponse } from "@src/server/actions.js", entities: [User, Task, GptResponse] } action createTask { fn: import { createTask } from "@src/server/actions.js", entities: [Task] } action deleteTask { fn: import { deleteTask } from "@src/server/actions.js", entities: [Task] } action updateTask { fn: import { updateTask } from "@src/server/actions.js", entities: [Task] } action stripePayment { fn: import { stripePayment } from "@src/server/actions.js", entities: [User] } action updateCurrentUserLastActiveTimestamp { fn: import { updateCurrentUserLastActiveTimestamp } from "@src/server/actions.js", entities: [User] } action updateHasPaidByUserId { fn: import { updateHasPaidByUserId } from "@src/server/actions.js", entities: [User] } action createFile { fn: import { createFile } from "@src/server/actions.js", entities: [User, File] } // 📚 Queries query getGptResponses { fn: import { getGptResponses } from "@src/server/queries.js", entities: [User, GptResponse] } query getAllTasksByUser { fn: import { getAllTasksByUser } from "@src/server/queries.js", entities: [Task] } query getAllFilesByUser { fn: import { getAllFilesByUser } from "@src/server/queries.js", entities: [User, File] } query getDownloadFileSignedURL { fn: import { getDownloadFileSignedURL } from "@src/server/queries.js", entities: [User, File] } query getDailyStats { fn: import { getDailyStats } from "@src/server/queries.js", entities: [User, DailyStats] } query getPaginatedUsers { fn: import { getPaginatedUsers } from "@src/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 "@src/server/webhooks/stripe.js", entities: [User], middlewareConfigFn: import { stripeMiddlewareFn } from "@src/server/webhooks/stripe.js", httpRoute: (POST, "/stripe-webhook") } /* 🕵️‍♂️ These are the Wasp 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 "@src/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 "@src/server/workers/calculateDailyStats.js" }, schedule: { cron: "0 * * * *" // every hour. useful in production // cron: "* * * * *" // every minute. useful for debugging }, entities: [User, DailyStats, Logs, PageViewSource] }