Last active
April 17, 2025 09:50
-
-
Save medardm/263b2c933186eb1a496fa743c756edc7 to your computer and use it in GitHub Desktop.
Wasp-lang: An example of splitting/organizing main wasp file (OpenSaaS) into different sections
This file contains hidden or 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
| import { QueryConfig, ActionConfig, App, ApiConfig } from "wasp-config" | |
| export const initActions = (app: App) => { | |
| const { | |
| getPaginatedUsersConfig, | |
| updateCurrentUserConfig, | |
| updateUserByIdConfig, | |
| generateGptResponseConfig, | |
| createTaskConfig, | |
| deleteTaskConfig, | |
| updateTaskConfig, | |
| getGptResponsesConfig, | |
| getAllTasksByUserConfig, | |
| getCustomerPortalUrlConfig, | |
| generateCheckoutSessionConfig, | |
| createFileConfig, | |
| getAllFilesByUserConfig, | |
| getDownloadFileSignedURLConfig, | |
| getDailyStatsConfig, | |
| paymentsWebhookConfig | |
| } = actionsConfig(); | |
| app.query('getPaginatedUsers', getPaginatedUsersConfig); | |
| app.action('updateCurrentUser', updateCurrentUserConfig); | |
| app.action('updateUserById', updateUserByIdConfig); | |
| app.action('generateGptResponse', generateGptResponseConfig); | |
| app.action('createTask', createTaskConfig); | |
| app.action('deleteTask', deleteTaskConfig); | |
| app.action('updateTask', updateTaskConfig); | |
| app.query('getGptResponses', getGptResponsesConfig); | |
| app.query('getAllTasksByUser', getAllTasksByUserConfig); | |
| app.query('getCustomerPortalUrl', getCustomerPortalUrlConfig); | |
| app.action('generateCheckoutSession', generateCheckoutSessionConfig); | |
| app.action('createFile', createFileConfig); | |
| app.query('getAllFilesByUser', getAllFilesByUserConfig); | |
| app.query('getDownloadFileSignedURL', getDownloadFileSignedURLConfig); | |
| app.query('getDailyStats', getDailyStatsConfig); | |
| app.api('paymentsWebhook', paymentsWebhookConfig); | |
| } | |
| const actionsConfig = () => { | |
| const getPaginatedUsersConfig: QueryConfig = { | |
| fn: { import: 'getPaginatedUsers', from: '@src/user/operations' }, | |
| entities: ['User'] | |
| }; | |
| const updateCurrentUserConfig: ActionConfig = { | |
| fn: { import: 'updateCurrentUser', from: '@src/user/operations' }, | |
| entities: ['User'] | |
| }; | |
| const updateUserByIdConfig: ActionConfig = { | |
| fn: { import: 'updateUserById', from: '@src/user/operations' }, | |
| entities: ['User'] | |
| }; | |
| const generateGptResponseConfig: ActionConfig = { | |
| fn: { import: 'generateGptResponse', from: '@src/demo-ai-app/operations' }, | |
| entities: ['User', 'Task', 'GptResponse'] | |
| }; | |
| const createTaskConfig: ActionConfig = { | |
| fn: { import: 'createTask', from: '@src/demo-ai-app/operations' }, | |
| entities: ['Task'] | |
| }; | |
| const deleteTaskConfig: ActionConfig = { | |
| fn: { import: 'deleteTask', from: '@src/demo-ai-app/operations' }, | |
| entities: ['Task'] | |
| }; | |
| const updateTaskConfig: ActionConfig = { | |
| fn: { import: 'updateTask', from: '@src/demo-ai-app/operations' }, | |
| entities: ['Task'] | |
| }; | |
| const getGptResponsesConfig: QueryConfig = { | |
| fn: { import: 'getGptResponses', from: '@src/demo-ai-app/operations' }, | |
| entities: ['User', 'GptResponse'] | |
| }; | |
| const getAllTasksByUserConfig: QueryConfig = { | |
| fn: { import: 'getAllTasksByUser', from: '@src/demo-ai-app/operations' }, | |
| entities: ['Task'] | |
| }; | |
| const getCustomerPortalUrlConfig: QueryConfig = { | |
| fn: { import: 'getCustomerPortalUrl', from: '@src/payment/operations' }, | |
| entities: ['User'] | |
| }; | |
| const generateCheckoutSessionConfig: ActionConfig = { | |
| fn: { import: 'generateCheckoutSession', from: '@src/payment/operations' }, | |
| entities: ['User'] | |
| }; | |
| const createFileConfig: ActionConfig = { | |
| fn: { import: 'createFile', from: '@src/file-upload/operations' }, | |
| entities: ['User', 'File'] | |
| }; | |
| const getAllFilesByUserConfig: QueryConfig = { | |
| fn: { import: 'getAllFilesByUser', from: '@src/file-upload/operations' }, | |
| entities: ['User', 'File'] | |
| }; | |
| const getDownloadFileSignedURLConfig: QueryConfig = { | |
| fn: { import: 'getDownloadFileSignedURL', from: '@src/file-upload/operations' }, | |
| entities: ['User', 'File'] | |
| }; | |
| const getDailyStatsConfig: QueryConfig = { | |
| fn: { import: 'getDailyStats', from: '@src/analytics/operations' }, | |
| entities: ['User', 'DailyStats'] | |
| }; | |
| const paymentsWebhookConfig: ApiConfig = { | |
| fn: { import: 'paymentsWebhook', from: '@src/payment/webhook' }, | |
| entities: ['User'], | |
| middlewareConfigFn: { import: 'paymentsMiddlewareConfigFn', from: '@src/payment/webhook' }, | |
| httpRoute: ['POST', '/payments-webhook'] | |
| }; | |
| return { | |
| getPaginatedUsersConfig, | |
| updateCurrentUserConfig, | |
| updateUserByIdConfig, | |
| generateGptResponseConfig, | |
| createTaskConfig, | |
| deleteTaskConfig, | |
| updateTaskConfig, | |
| getGptResponsesConfig, | |
| getAllTasksByUserConfig, | |
| getCustomerPortalUrlConfig, | |
| generateCheckoutSessionConfig, | |
| createFileConfig, | |
| getAllFilesByUserConfig, | |
| getDownloadFileSignedURLConfig, | |
| getDailyStatsConfig, | |
| paymentsWebhookConfig | |
| } | |
| } |
This file contains hidden or 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
| import { App, AppConfig, AuthConfig, ClientConfig, DbConfig, EmailSenderConfig } from 'wasp-config' | |
| export const initApp = (appName: string, appConfig: AppConfig): App => { | |
| const app = new App(appName, appConfig); | |
| const { authConfig, dbConfig, clientConfig, emailSenderConfig } = config(); | |
| app.auth(authConfig); | |
| app.db(dbConfig); | |
| app.client(clientConfig); | |
| app.emailSender(emailSenderConfig); | |
| return app | |
| } | |
| const config = () => { | |
| const authConfig: AuthConfig = { | |
| userEntity: 'User', | |
| methods: { | |
| email: { | |
| fromField: { | |
| name: "AI Agent Resources API", | |
| email: "[email protected]", | |
| }, | |
| emailVerification: { | |
| clientRoute: 'EmailVerificationRoute', | |
| getEmailContentFn: { import: 'getVerificationEmailContent', from: '@src/auth/email-and-pass/emails' }, | |
| }, | |
| passwordReset: { | |
| clientRoute: 'PasswordResetRoute', | |
| getEmailContentFn: { import: 'getPasswordResetEmailContent', from: '@src/auth/email-and-pass/emails' }, | |
| }, | |
| userSignupFields: { import: 'getEmailUserFields', from: '@src/auth/userSignupFields' }, | |
| }, | |
| }, | |
| onAfterSignup: { import: 'onAfterSignup', from: '@src/auth/hooks' }, | |
| onAuthFailedRedirectTo: '/login', | |
| onAuthSucceededRedirectTo: '/demo-app', | |
| } | |
| const dbConfig: DbConfig = { | |
| seeds: [ | |
| { import: 'seedMockUsers', from: '@src/server/scripts/dbSeeds' }, | |
| ] | |
| } | |
| const clientConfig: ClientConfig = { | |
| rootComponent: { importDefault: 'App', from: '@src/client/App' }, | |
| } | |
| const emailSenderConfig: EmailSenderConfig = { | |
| provider: 'SMTP', | |
| defaultFrom: { | |
| name: "Open SaaS App", | |
| email: "[email protected]" | |
| }, | |
| } | |
| return { authConfig, dbConfig, clientConfig, emailSenderConfig } | |
| } |
This file contains hidden or 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
| import { App, JobConfig } from "wasp-config" | |
| export const initJobs = (app: App) => { | |
| const { | |
| dailyStatsJobConfig, | |
| sendNewsletterJobConfig | |
| } = jobsConfig(); | |
| app.job('dailyStatsJob', dailyStatsJobConfig); | |
| app.job('sendNewsletter', sendNewsletterJobConfig); | |
| } | |
| const jobsConfig = () => { | |
| const dailyStatsJobConfig: JobConfig = { | |
| executor: 'PgBoss', | |
| perform: { | |
| fn: { import: 'calculateDailyStats', from: '@src/analytics/stats' } | |
| }, | |
| entities: ['User', 'DailyStats', 'Logs', 'PageViewSource'] | |
| }; | |
| const sendNewsletterJobConfig: JobConfig = { | |
| executor: 'PgBoss', | |
| perform: { | |
| fn: { import: 'checkAndQueueNewsletterEmails', from: '@src/newsletter/sendNewsletter' } | |
| }, | |
| entities: ['User'] | |
| }; | |
| return { | |
| dailyStatsJobConfig, | |
| sendNewsletterJobConfig | |
| } | |
| } |
This file contains hidden or 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
| import { initApp } from './wasp-config/core.wasp.js' | |
| import { initRoutes } from './wasp-config/routes.wasp.js' | |
| import { initActions } from './wasp-config/actions.wasp.js' | |
| import { initJobs } from './wasp-config/jobs.wasp.js' | |
| // Configure the app here | |
| const app = initApp( | |
| 'OpenSaaS App', | |
| { | |
| title: 'OpenSaaS App', | |
| wasp: { version: '^0.15.0' }, | |
| head: [ | |
| "<meta charset='utf-8' />", | |
| "<meta name='description' content='Your apps main description and features.' />", | |
| "<meta name='author' content='OpenSaaS App' />", | |
| "<meta name='keywords' content='saas, solution, product, app, service' />", | |
| "<meta property='og:type' content='website' />", | |
| "<meta property='og:title' content='OpenSaaS App' />", | |
| "<meta property='og:site_name' content='OpenSaaS App' />", | |
| "<meta property='og:url' content='https://your-saas-app.com' />", | |
| "<meta property='og:description' content='Your apps main description and features.' />", | |
| "<meta property='og:image' content='https://your-saas-app.com/public-banner.webp' />", | |
| "<meta name='twitter:image' content='https://your-saas-app.com/public-banner.webp' />", | |
| "<meta name='twitter:image:width' content='800' />", | |
| "<meta name='twitter:image:height' content='400' />", | |
| "<meta name='twitter:card' content='summary_large_image' />", | |
| "<script defer data-domain='<your-site-id>' src='https://plausible.io/js/script.js'></script>", | |
| "<script defer data-domain='<your-site-id>' src='https://plausible.io/js/script.local.js'></script>", | |
| ], | |
| } | |
| ); | |
| initRoutes(app); | |
| initActions(app); | |
| initJobs(app); | |
| export default app; |
This file contains hidden or 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
| import { App, RouteConfig } from "wasp-config"; | |
| export const initRoutes = (app: App) => { | |
| const { | |
| landingPageConfig, | |
| loginPageConfig, | |
| signupPageConfig, | |
| requestPasswordResetPageConfig, | |
| passwordResetPageConfig, | |
| emailVerificationPageConfig, | |
| accountPageConfig, | |
| demoAppPageConfig, | |
| pricingPageConfig, | |
| checkoutPageConfig, | |
| fileUploadPageConfig, | |
| adminDashboardPageConfig, | |
| adminUsersPageConfig, | |
| adminSettingsPageConfig, | |
| adminChartsPageConfig, | |
| adminFormElementsPageConfig, | |
| adminFormLayoutsPageConfig, | |
| adminCalendarPageConfig, | |
| adminUIAlertsPageConfig, | |
| adminUIButtonsPageConfig, | |
| notFoundPageConfig, | |
| adminMessagesPageConfig | |
| } = routesConfig(app); | |
| app.route('LandingPageRoute', landingPageConfig); | |
| app.route('LoginRoute', loginPageConfig); | |
| app.route('SignupRoute', signupPageConfig); | |
| app.route('RequestPasswordResetRoute', requestPasswordResetPageConfig); | |
| app.route('PasswordResetRoute', passwordResetPageConfig); | |
| app.route('EmailVerificationRoute', emailVerificationPageConfig); | |
| app.route('AccountRoute', accountPageConfig); | |
| app.route('DemoAppRoute', demoAppPageConfig); | |
| app.route('PricingPageRoute', pricingPageConfig); | |
| app.route('CheckoutRoute', checkoutPageConfig); | |
| app.route('FileUploadRoute', fileUploadPageConfig); | |
| app.route('AdminRoute', adminDashboardPageConfig); | |
| app.route('AdminUsersRoute', adminUsersPageConfig); | |
| app.route('AdminSettingsRoute', adminSettingsPageConfig); | |
| app.route('AdminChartsRoute', adminChartsPageConfig); | |
| app.route('AdminFormElementsRoute', adminFormElementsPageConfig); | |
| app.route('AdminFormLayoutsRoute', adminFormLayoutsPageConfig); | |
| app.route('AdminCalendarRoute', adminCalendarPageConfig); | |
| app.route('AdminUIAlertsRoute', adminUIAlertsPageConfig); | |
| app.route('AdminUIButtonsRoute', adminUIButtonsPageConfig); | |
| app.route('NotFoundRoute', notFoundPageConfig); | |
| app.route('AdminMessagesRoute', adminMessagesPageConfig); | |
| } | |
| const routesConfig = (app: App) => { | |
| const landingPageConfig: RouteConfig = { | |
| path: '/', | |
| to: app.page('LandingPage', { | |
| component: { importDefault: 'LandingPage', from: '@src/landing-page/LandingPage' } | |
| }) | |
| }; | |
| const loginPageConfig: RouteConfig = { | |
| path: '/login', | |
| to: app.page('LoginPage', { | |
| component: { importDefault: 'Login', from: '@src/auth/LoginPage' } | |
| }) | |
| }; | |
| const signupPageConfig: RouteConfig = { | |
| path: '/signup', | |
| to: app.page('SignupPage', { | |
| component: { import: 'Signup', from: '@src/auth/SignupPage' } | |
| }) | |
| }; | |
| const requestPasswordResetPageConfig: RouteConfig = { | |
| path: '/request-password-reset', | |
| to: app.page('RequestPasswordResetPage', { | |
| component: { import: 'RequestPasswordResetPage', from: '@src/auth/email-and-pass/RequestPasswordResetPage' } | |
| }) | |
| }; | |
| const passwordResetPageConfig: RouteConfig = { | |
| path: '/password-reset', | |
| to: app.page('PasswordResetPage', { | |
| component: { import: 'PasswordResetPage', from: '@src/auth/email-and-pass/PasswordResetPage' } | |
| }) | |
| }; | |
| const emailVerificationPageConfig: RouteConfig = { | |
| path: '/email-verification', | |
| to: app.page('EmailVerificationPage', { | |
| component: { import: 'EmailVerificationPage', from: '@src/auth/email-and-pass/EmailVerificationPage' } | |
| }) | |
| }; | |
| const accountPageConfig: RouteConfig = { | |
| path: '/account', | |
| to: app.page('AccountPage', { | |
| authRequired: true, | |
| component: { importDefault: 'Account', from: '@src/user/AccountPage' } | |
| }) | |
| }; | |
| const demoAppPageConfig: RouteConfig = { | |
| path: '/demo-app', | |
| to: app.page('DemoAppPage', { | |
| authRequired: true, | |
| component: { importDefault: 'DemoAppPage', from: '@src/demo-ai-app/DemoAppPage' } | |
| }) | |
| }; | |
| const pricingPageConfig: RouteConfig = { | |
| path: '/pricing', | |
| to: app.page('PricingPage', { | |
| component: { importDefault: 'PricingPage', from: '@src/payment/PricingPage' } | |
| }) | |
| }; | |
| const checkoutPageConfig: RouteConfig = { | |
| path: '/checkout', | |
| to: app.page('CheckoutPage', { | |
| authRequired: true, | |
| component: { importDefault: 'Checkout', from: '@src/payment/CheckoutPage' } | |
| }) | |
| }; | |
| const fileUploadPageConfig: RouteConfig = { | |
| path: '/file-upload', | |
| to: app.page('FileUploadPage', { | |
| authRequired: true, | |
| component: { importDefault: 'FileUpload', from: '@src/file-upload/FileUploadPage' } | |
| }) | |
| }; | |
| const adminDashboardPageConfig: RouteConfig = { | |
| path: '/admin', | |
| to: app.page('AnalyticsDashboardPage', { | |
| authRequired: true, | |
| component: { importDefault: 'AnalyticsDashboardPage', from: '@src/admin/dashboards/analytics/AnalyticsDashboardPage' } | |
| }) | |
| }; | |
| const adminUsersPageConfig: RouteConfig = { | |
| path: '/admin/users', | |
| to: app.page('AdminUsersPage', { | |
| authRequired: true, | |
| component: { importDefault: 'AdminUsers', from: '@src/admin/dashboards/users/UsersDashboardPage' } | |
| }) | |
| }; | |
| const adminSettingsPageConfig: RouteConfig = { | |
| path: '/admin/settings', | |
| to: app.page('AdminSettingsPage', { | |
| authRequired: true, | |
| component: { importDefault: 'AdminSettings', from: '@src/admin/elements/settings/SettingsPage' } | |
| }) | |
| }; | |
| const adminChartsPageConfig: RouteConfig = { | |
| path: '/admin/chart', | |
| to: app.page('AdminChartsPage', { | |
| authRequired: true, | |
| component: { importDefault: 'AdminCharts', from: '@src/admin/elements/charts/ChartsPage' } | |
| }) | |
| }; | |
| const adminFormElementsPageConfig: RouteConfig = { | |
| path: '/admin/forms/form-elements', | |
| to: app.page('AdminFormElementsPage', { | |
| authRequired: true, | |
| component: { importDefault: 'AdminForms', from: '@src/admin/elements/forms/FormElementsPage' } | |
| }) | |
| }; | |
| const adminFormLayoutsPageConfig: RouteConfig = { | |
| path: '/admin/forms/form-layouts', | |
| to: app.page('AdminFormLayoutsPage', { | |
| authRequired: true, | |
| component: { importDefault: 'AdminForms', from: '@src/admin/elements/forms/FormLayoutsPage' } | |
| }) | |
| }; | |
| const adminCalendarPageConfig: RouteConfig = { | |
| path: '/admin/calendar', | |
| to: app.page('AdminCalendarPage', { | |
| authRequired: true, | |
| component: { importDefault: 'AdminCalendar', from: '@src/admin/elements/calendar/CalendarPage' } | |
| }) | |
| }; | |
| const adminUIAlertsPageConfig: RouteConfig = { | |
| path: '/admin/ui/alerts', | |
| to: app.page('AdminUIAlertsPage', { | |
| authRequired: true, | |
| component: { importDefault: 'AdminUI', from: '@src/admin/elements/ui-elements/AlertsPage' } | |
| }) | |
| }; | |
| const adminUIButtonsPageConfig: RouteConfig = { | |
| path: '/admin/ui/buttons', | |
| to: app.page('AdminUIButtonsPage', { | |
| authRequired: true, | |
| component: { importDefault: 'AdminUI', from: '@src/admin/elements/ui-elements/ButtonsPage' } | |
| }) | |
| }; | |
| const notFoundPageConfig: RouteConfig = { | |
| path: '*', | |
| to: app.page('NotFoundPage', { | |
| component: { import: 'NotFoundPage', from: '@src/client/components/NotFoundPage' } | |
| }) | |
| }; | |
| const adminMessagesPageConfig: RouteConfig = { | |
| path: '/admin/messages', | |
| to: app.page('AdminMessagesPage', { | |
| authRequired: true, | |
| component: { importDefault: 'AdminMessages', from: '@src/messages/MessagesPage' } | |
| }) | |
| }; | |
| return { | |
| landingPageConfig, | |
| loginPageConfig, | |
| signupPageConfig, | |
| requestPasswordResetPageConfig, | |
| passwordResetPageConfig, | |
| emailVerificationPageConfig, | |
| accountPageConfig, | |
| demoAppPageConfig, | |
| pricingPageConfig, | |
| checkoutPageConfig, | |
| fileUploadPageConfig, | |
| adminDashboardPageConfig, | |
| adminUsersPageConfig, | |
| adminSettingsPageConfig, | |
| adminChartsPageConfig, | |
| adminFormElementsPageConfig, | |
| adminFormLayoutsPageConfig, | |
| adminCalendarPageConfig, | |
| adminUIAlertsPageConfig, | |
| adminUIButtonsPageConfig, | |
| notFoundPageConfig, | |
| adminMessagesPageConfig | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment