@stacksjs/defaults 0.70.57 → 0.70.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/Actions/AI/AskAction.ts +40 -0
- package/app/Actions/AI/SummaryAction.ts +40 -0
- package/app/Actions/Auth/AuthUserAction.ts +13 -0
- package/app/Actions/Auth/CreateTokenAction.ts +64 -0
- package/app/Actions/Auth/DisableTwoFactorAction.ts +37 -0
- package/app/Actions/Auth/EnableTwoFactorAction.ts +37 -0
- package/app/Actions/Auth/GenerateAuthenticationAction.ts +51 -0
- package/app/Actions/Auth/GenerateRegistrationAction.ts +53 -0
- package/app/Actions/Auth/GenerateTwoFactorSecretAction.ts +26 -0
- package/app/Actions/Auth/ListTokensAction.ts +30 -0
- package/app/Actions/Auth/LoginAction.ts +73 -0
- package/app/Actions/Auth/LogoutAction.ts +16 -0
- package/app/Actions/Auth/LogoutAllAction.ts +28 -0
- package/app/Actions/Auth/RefreshTokenAction.ts +39 -0
- package/app/Actions/Auth/RegisterAction.ts +57 -0
- package/app/Actions/Auth/ResendVerificationAction.ts +24 -0
- package/app/Actions/Auth/RevokeTokenAction.ts +43 -0
- package/app/Actions/Auth/TestAbilitiesAction.ts +61 -0
- package/app/Actions/Auth/VerifyAuthenticationAction.ts +94 -0
- package/app/Actions/Auth/VerifyEmailAction.ts +25 -0
- package/app/Actions/Auth/VerifyRegistrationAction.ts +59 -0
- package/app/Actions/Auth/VerifyTwoFactorLoginAction.ts +57 -0
- package/app/Actions/Billable/CheckoutAction.ts +25 -0
- package/app/Actions/Buddy/BuddyBrowseAction.ts +34 -0
- package/app/Actions/Buddy/BuddyCancelAction.ts +32 -0
- package/app/Actions/Buddy/BuddyCommitAction.ts +25 -0
- package/app/Actions/Buddy/BuddyGitHubConnectAction.ts +40 -0
- package/app/Actions/Buddy/BuddyGitHubDisconnectAction.ts +19 -0
- package/app/Actions/Buddy/BuddyProcessAction.ts +65 -0
- package/app/Actions/Buddy/BuddyProcessStreamAction.ts +128 -0
- package/app/Actions/Buddy/BuddyPushAction.ts +21 -0
- package/app/Actions/Buddy/BuddyRepoOpenAction.ts +44 -0
- package/app/Actions/Buddy/BuddyRepoValidateAction.ts +35 -0
- package/app/Actions/Buddy/BuddySettingsAction.ts +21 -0
- package/app/Actions/Buddy/BuddySettingsUpdateAction.ts +46 -0
- package/app/Actions/Buddy/BuddyStateAction.ts +28 -0
- package/app/Actions/Buddy/BuddyTitleAction.ts +49 -0
- package/app/Actions/Buddy/CommandsAction.ts +802 -0
- package/app/Actions/Buddy/JobCancelAction.ts +32 -0
- package/app/Actions/Buddy/JobRetryAction.ts +49 -0
- package/app/Actions/Buddy/JobsListAction.ts +141 -0
- package/app/Actions/Buddy/ScheduleListAction.ts +52 -0
- package/app/Actions/Buddy/VersionsAction.ts +12 -0
- package/app/Actions/Cms/AuthorDestroyAction.ts +21 -0
- package/app/Actions/Cms/AuthorIndexAction.ts +14 -0
- package/app/Actions/Cms/AuthorShowAction.ts +19 -0
- package/app/Actions/Cms/AuthorStoreAction.ts +20 -0
- package/app/Actions/Cms/AuthorUpdateAction.ts +24 -0
- package/app/Actions/Cms/CategorizableDestroyAction.ts +16 -0
- package/app/Actions/Cms/CategorizableIndexAction.ts +14 -0
- package/app/Actions/Cms/CategorizableShowAction.ts +16 -0
- package/app/Actions/Cms/CategorizableStoreAction.ts +42 -0
- package/app/Actions/Cms/CategorizableUpdateAction.ts +38 -0
- package/app/Actions/Cms/CommentDestroyAction.ts +16 -0
- package/app/Actions/Cms/CommentIndexAction.ts +17 -0
- package/app/Actions/Cms/CommentShowAction.ts +16 -0
- package/app/Actions/Cms/CommentStoreAction.ts +39 -0
- package/app/Actions/Cms/CommentUpdateAction.ts +37 -0
- package/app/Actions/Cms/PageDestroyAction.ts +16 -0
- package/app/Actions/Cms/PageIndexAction.ts +14 -0
- package/app/Actions/Cms/PageStoreAction.ts +31 -0
- package/app/Actions/Cms/PageUpdateAction.ts +27 -0
- package/app/Actions/Cms/PostDestroyAction.ts +17 -0
- package/app/Actions/Cms/PostIndexAction.ts +14 -0
- package/app/Actions/Cms/PostShowAction.ts +16 -0
- package/app/Actions/Cms/PostStoreAction.ts +37 -0
- package/app/Actions/Cms/PostUpdateAction.ts +32 -0
- package/app/Actions/Cms/PostViewsUpdateAction.ts +34 -0
- package/app/Actions/Cms/RssFeedAction.ts +59 -0
- package/app/Actions/Cms/SitemapAction.ts +60 -0
- package/app/Actions/Cms/TaggableDestroyAction.ts +16 -0
- package/app/Actions/Cms/TaggableIndexAction.ts +14 -0
- package/app/Actions/Cms/TaggableShowAction.ts +16 -0
- package/app/Actions/Cms/TaggableStoreAction.ts +36 -0
- package/app/Actions/Cms/TaggableUpdateAction.ts +37 -0
- package/app/Actions/Commerce/CouponDestroyAction.ts +18 -0
- package/app/Actions/Commerce/CouponIndexAction.ts +16 -0
- package/app/Actions/Commerce/CouponShowAction.ts +18 -0
- package/app/Actions/Commerce/CouponStoreAction.ts +21 -0
- package/app/Actions/Commerce/CouponUpdateAction.ts +31 -0
- package/app/Actions/Commerce/CustomerDestroyAction.ts +16 -0
- package/app/Actions/Commerce/CustomerIndexAction.ts +15 -0
- package/app/Actions/Commerce/CustomerShowAction.ts +18 -0
- package/app/Actions/Commerce/CustomerStoreAction.ts +29 -0
- package/app/Actions/Commerce/CustomerUpdateAction.ts +27 -0
- package/app/Actions/Commerce/DriverDestroyAction.ts +18 -0
- package/app/Actions/Commerce/GiftCardDestroyAction.ts +18 -0
- package/app/Actions/Commerce/GiftCardIndexAction.ts +16 -0
- package/app/Actions/Commerce/GiftCardShowAction.ts +18 -0
- package/app/Actions/Commerce/GiftCardStatsAction.ts +30 -0
- package/app/Actions/Commerce/GiftCardStoreAction.ts +35 -0
- package/app/Actions/Commerce/GiftCardUpdateAction.ts +26 -0
- package/app/Actions/Commerce/GiftCardUpdateBalanceAction.ts +36 -0
- package/app/Actions/Commerce/OrderDestroyAction.ts +18 -0
- package/app/Actions/Commerce/OrderExportAction.ts +15 -0
- package/app/Actions/Commerce/OrderIndexAction.ts +15 -0
- package/app/Actions/Commerce/OrderShowAction.ts +18 -0
- package/app/Actions/Commerce/OrderStoreAction.ts +32 -0
- package/app/Actions/Commerce/OrderUpdateAction.ts +34 -0
- package/app/Actions/Commerce/PaymentDestroyAction.ts +16 -0
- package/app/Actions/Commerce/PaymentFetchStatsAction.ts +31 -0
- package/app/Actions/Commerce/PaymentIndexAction.ts +15 -0
- package/app/Actions/Commerce/PaymentMonthlyTrendsAction.ts +21 -0
- package/app/Actions/Commerce/PaymentShowAction.ts +17 -0
- package/app/Actions/Commerce/PaymentStoreAction.ts +24 -0
- package/app/Actions/Commerce/PaymentUpdateAction.ts +35 -0
- package/app/Actions/Commerce/PrintDeviceDestroyAction.ts +18 -0
- package/app/Actions/Commerce/PrintDeviceIndexAction.ts +14 -0
- package/app/Actions/Commerce/PrintDeviceShowAction.ts +17 -0
- package/app/Actions/Commerce/PrintDeviceStoreAction.ts +27 -0
- package/app/Actions/Commerce/PrintDeviceUpdateAction.ts +27 -0
- package/app/Actions/Commerce/Product/ManufacturerDestroyAction.ts +20 -0
- package/app/Actions/Commerce/Product/ManufacturerIndexAction.ts +16 -0
- package/app/Actions/Commerce/Product/ManufacturerShowAction.ts +18 -0
- package/app/Actions/Commerce/Product/ManufacturerStoreAction.ts +14 -0
- package/app/Actions/Commerce/Product/ProductCategoryIndexAction.ts +16 -0
- package/app/Actions/Commerce/Product/ProductCategoryStoreAction.ts +16 -0
- package/app/Actions/Commerce/Product/ProductDestroyAction.ts +16 -0
- package/app/Actions/Commerce/Product/ProductIndexAction.ts +14 -0
- package/app/Actions/Commerce/Product/ProductManufacturerDestroyAction.ts +18 -0
- package/app/Actions/Commerce/Product/ProductManufacturerIndexAction.ts +14 -0
- package/app/Actions/Commerce/Product/ProductManufacturerShowAction.ts +16 -0
- package/app/Actions/Commerce/Product/ProductManufacturerStoreAction.ts +14 -0
- package/app/Actions/Commerce/Product/ProductManufacturerUpdateAction.ts +18 -0
- package/app/Actions/Commerce/Product/ProductShowAction.ts +18 -0
- package/app/Actions/Commerce/Product/ProductStoreAction.ts +17 -0
- package/app/Actions/Commerce/Product/ProductUnitDestroyAction.ts +18 -0
- package/app/Actions/Commerce/Product/ProductUnitIndexAction.ts +14 -0
- package/app/Actions/Commerce/Product/ProductUnitShowAction.ts +18 -0
- package/app/Actions/Commerce/Product/ProductUnitStoreAction.ts +15 -0
- package/app/Actions/Commerce/Product/ProductUpdateAction.ts +17 -0
- package/app/Actions/Commerce/Product/ProductVariantDestroyAction.ts +18 -0
- package/app/Actions/Commerce/Product/ProductVariantIndexAction.ts +14 -0
- package/app/Actions/Commerce/Product/ProductVariantShowAction.ts +17 -0
- package/app/Actions/Commerce/Product/ProductVariantStoreAction.ts +14 -0
- package/app/Actions/Commerce/Product/ProductVariantUpdateAction.ts +16 -0
- package/app/Actions/Commerce/ReceiptDestroyAction.ts +18 -0
- package/app/Actions/Commerce/ReceiptIndexAction.ts +14 -0
- package/app/Actions/Commerce/ReceiptShowAction.ts +18 -0
- package/app/Actions/Commerce/ReceiptStoreAction.ts +28 -0
- package/app/Actions/Commerce/ReceiptUpdateAction.ts +30 -0
- package/app/Actions/Commerce/ReviewIndexAction.ts +14 -0
- package/app/Actions/Commerce/ReviewShowAction.ts +16 -0
- package/app/Actions/Commerce/ReviewStoreAction.ts +26 -0
- package/app/Actions/Commerce/ReviewUpdateAction.ts +28 -0
- package/app/Actions/Commerce/Shipping/DeliveryRouteDestroyAction.ts +18 -0
- package/app/Actions/Commerce/Shipping/DeliveryRouteIndexAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/DeliveryRouteShowAction.ts +18 -0
- package/app/Actions/Commerce/Shipping/DeliveryRouteStoreAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/DeliveryRouteUpdateAction.ts +18 -0
- package/app/Actions/Commerce/Shipping/DigitalDeliveryDestroyAction.ts +18 -0
- package/app/Actions/Commerce/Shipping/DigitalDeliveryIndexAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/DigitalDeliveryShowAction.ts +18 -0
- package/app/Actions/Commerce/Shipping/DigitalDeliveryStoreAction.ts +18 -0
- package/app/Actions/Commerce/Shipping/DigitalDeliveryUpdateAction.ts +18 -0
- package/app/Actions/Commerce/Shipping/DriverIndexAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/DriverShowAction.ts +18 -0
- package/app/Actions/Commerce/Shipping/DriverStoreAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/DriverUpdateAction.ts +18 -0
- package/app/Actions/Commerce/Shipping/LicenseKeyDestroyAction.ts +20 -0
- package/app/Actions/Commerce/Shipping/LicenseKeyIndexAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/LicenseKeyShowAction.ts +18 -0
- package/app/Actions/Commerce/Shipping/LicenseKeyStoreAction.ts +18 -0
- package/app/Actions/Commerce/Shipping/LicenseKeyUpdateAction.ts +18 -0
- package/app/Actions/Commerce/Shipping/ShippingMethodDestroyAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/ShippingMethodIndexAction.ts +14 -0
- package/app/Actions/Commerce/Shipping/ShippingMethodShowAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/ShippingMethodStoreAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/ShippingMethodUpdateAction.ts +15 -0
- package/app/Actions/Commerce/Shipping/ShippingRateDestroyAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/ShippingRateIndexAction.ts +14 -0
- package/app/Actions/Commerce/Shipping/ShippingRateShowAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/ShippingRateStoreAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/ShippingRateUpdateAction.ts +17 -0
- package/app/Actions/Commerce/Shipping/ShippingZoneDestroyAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/ShippingZoneIndexAction.ts +14 -0
- package/app/Actions/Commerce/Shipping/ShippingZoneShowAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/ShippingZoneStoreAction.ts +16 -0
- package/app/Actions/Commerce/Shipping/ShippingZoneUpdateAction.ts +15 -0
- package/app/Actions/Commerce/TaxRateDestroyAction.ts +16 -0
- package/app/Actions/Commerce/TaxRateIndexAction.ts +14 -0
- package/app/Actions/Commerce/TaxRateShowAction.ts +17 -0
- package/app/Actions/Commerce/TaxRateStoreAction.ts +24 -0
- package/app/Actions/Commerce/TaxRateUpdateAction.ts +24 -0
- package/app/Actions/Commerce/WaitlistProductAnalyticsAction.ts +29 -0
- package/app/Actions/Commerce/WaitlistProductDestroyAction.ts +16 -0
- package/app/Actions/Commerce/WaitlistProductIndexAction.ts +14 -0
- package/app/Actions/Commerce/WaitlistProductQuantityDistributionAction.ts +32 -0
- package/app/Actions/Commerce/WaitlistProductShowAction.ts +16 -0
- package/app/Actions/Commerce/WaitlistProductStatusAction.ts +37 -0
- package/app/Actions/Commerce/WaitlistProductStoreAction.ts +27 -0
- package/app/Actions/Commerce/WaitlistProductTimeSeriesAction.ts +25 -0
- package/app/Actions/Commerce/WaitlistProductUpdateAction.ts +29 -0
- package/app/Actions/Commerce/WaitlistRestaurantDashboardAction.ts +29 -0
- package/app/Actions/Commerce/WaitlistRestaurantDestroyAction.ts +16 -0
- package/app/Actions/Commerce/WaitlistRestaurantIndexAction.ts +14 -0
- package/app/Actions/Commerce/WaitlistRestaurantShowAction.ts +16 -0
- package/app/Actions/Commerce/WaitlistRestaurantStoreAction.ts +31 -0
- package/app/Actions/Commerce/WaitlistRestaurantUpdateAction.ts +33 -0
- package/app/Actions/ContactAction.ts +72 -0
- package/app/Actions/Dashboard/Actions/CreateAction.ts +11 -0
- package/app/Actions/Dashboard/Actions/GetActionCount.ts +12 -0
- package/app/Actions/Dashboard/Actions/GetActions.ts +19 -0
- package/app/Actions/Dashboard/Analytics/BlogAnalyticsAction.ts +58 -0
- package/app/Actions/Dashboard/Analytics/BrowserAnalyticsAction.ts +42 -0
- package/app/Actions/Dashboard/Analytics/CommerceAnalyticsAction.ts +64 -0
- package/app/Actions/Dashboard/Analytics/CountryAnalyticsAction.ts +41 -0
- package/app/Actions/Dashboard/Analytics/DeviceAnalyticsAction.ts +58 -0
- package/app/Actions/Dashboard/Analytics/EventAnalyticsAction.ts +90 -0
- package/app/Actions/Dashboard/Analytics/MarketingAnalyticsAction.ts +76 -0
- package/app/Actions/Dashboard/Analytics/PageAnalyticsAction.ts +45 -0
- package/app/Actions/Dashboard/Analytics/ReferrerAnalyticsAction.ts +72 -0
- package/app/Actions/Dashboard/Analytics/SalesAnalyticsAction.ts +73 -0
- package/app/Actions/Dashboard/Analytics/WebAnalyticsAction.ts +114 -0
- package/app/Actions/Dashboard/Auth/MeAction.ts +84 -0
- package/app/Actions/Dashboard/BuddyDashboardAction.ts +34 -0
- package/app/Actions/Dashboard/Ci/RepoRunJobsAction.ts +60 -0
- package/app/Actions/Dashboard/Ci/RepoRunsAction.ts +67 -0
- package/app/Actions/Dashboard/Ci/RunnerHistoryAction.ts +58 -0
- package/app/Actions/Dashboard/Ci/StatusAction.ts +73 -0
- package/app/Actions/Dashboard/Ci/failure-notifier.ts +214 -0
- package/app/Actions/Dashboard/Ci/runner-pressure-monitor.ts +259 -0
- package/app/Actions/Dashboard/Cloud/CloudIndexAction.ts +61 -0
- package/app/Actions/Dashboard/Commands/CreateCommand.ts +11 -0
- package/app/Actions/Dashboard/Commands/GetAverageCommandTime.ts +12 -0
- package/app/Actions/Dashboard/Commands/GetCommandCount.ts +12 -0
- package/app/Actions/Dashboard/Commands/GetCommandSuccessRate.ts +12 -0
- package/app/Actions/Dashboard/Commands/GetCommands.ts +12 -0
- package/app/Actions/Dashboard/Commerce/CommerceCouponsAction.ts +48 -0
- package/app/Actions/Dashboard/Commerce/CommerceCustomersAction.ts +48 -0
- package/app/Actions/Dashboard/Commerce/CommerceDashboardAction.ts +98 -0
- package/app/Actions/Dashboard/Commerce/CommerceDeliveryAction.ts +65 -0
- package/app/Actions/Dashboard/Commerce/CommerceGiftCardsAction.ts +48 -0
- package/app/Actions/Dashboard/Commerce/CommerceOrdersAction.ts +51 -0
- package/app/Actions/Dashboard/Commerce/CommercePaymentsAction.ts +59 -0
- package/app/Actions/Dashboard/Commerce/CommerceProductsAction.ts +50 -0
- package/app/Actions/Dashboard/Commerce/CommerceTaxesAction.ts +46 -0
- package/app/Actions/Dashboard/Commerce/PosIndexAction.ts +57 -0
- package/app/Actions/Dashboard/Commerce/ReviewIndexAction.ts +51 -0
- package/app/Actions/Dashboard/Content/AuthorIndexAction.ts +51 -0
- package/app/Actions/Dashboard/Content/CategoryIndexAction.ts +27 -0
- package/app/Actions/Dashboard/Content/CommentIndexAction.ts +41 -0
- package/app/Actions/Dashboard/Content/ContentDashboardAction.ts +54 -0
- package/app/Actions/Dashboard/Content/FileIndexAction.ts +24 -0
- package/app/Actions/Dashboard/Content/PageIndexAction.ts +25 -0
- package/app/Actions/Dashboard/Content/PostIndexAction.ts +102 -0
- package/app/Actions/Dashboard/Content/SeoIndexAction.ts +17 -0
- package/app/Actions/Dashboard/Content/TagIndexAction.ts +23 -0
- package/app/Actions/Dashboard/CreateDnsRecords.ts +11 -0
- package/app/Actions/Dashboard/CreateMailbox.ts +11 -0
- package/app/Actions/Dashboard/DashboardActivityAction.ts +58 -0
- package/app/Actions/Dashboard/DashboardHealthAction.ts +52 -0
- package/app/Actions/Dashboard/DashboardHomeAction.ts +83 -0
- package/app/Actions/Dashboard/DashboardStatsAction.ts +48 -0
- package/app/Actions/Dashboard/Data/AccessTokenIndexAction.ts +65 -0
- package/app/Actions/Dashboard/Data/ActivityIndexAction.ts +28 -0
- package/app/Actions/Dashboard/Data/DataDashboardAction.ts +67 -0
- package/app/Actions/Dashboard/Data/SubscriberIndexAction.ts +48 -0
- package/app/Actions/Dashboard/Data/TeamIndexAction.ts +43 -0
- package/app/Actions/Dashboard/Data/UserIndexAction.ts +48 -0
- package/app/Actions/Dashboard/Deployments/CreateDeployment.ts +11 -0
- package/app/Actions/Dashboard/Deployments/GetAverageDeploymentTime.ts +15 -0
- package/app/Actions/Dashboard/Deployments/GetDeployScript.ts +12 -0
- package/app/Actions/Dashboard/Deployments/GetDeploymentCount.ts +12 -0
- package/app/Actions/Dashboard/Deployments/GetDeploymentLiveTerminalOutput.ts +12 -0
- package/app/Actions/Dashboard/Deployments/GetDeployments.ts +20 -0
- package/app/Actions/Dashboard/Deployments/GetRecentDeployments.ts +12 -0
- package/app/Actions/Dashboard/Deployments/UpdateDeployScript.ts +11 -0
- package/app/Actions/Dashboard/GetDnsRecords.ts +12 -0
- package/app/Actions/Dashboard/GetEmailInboxes.ts +12 -0
- package/app/Actions/Dashboard/GetProjects.ts +13 -0
- package/app/Actions/Dashboard/GetRecentCommits.ts +13 -0
- package/app/Actions/Dashboard/Inbox/InboxIndexAction.ts +144 -0
- package/app/Actions/Dashboard/Inbox/InboxShowAction.ts +89 -0
- package/app/Actions/Dashboard/Infrastructure/CommandIndexAction.ts +18 -0
- package/app/Actions/Dashboard/Infrastructure/DnsIndexAction.ts +101 -0
- package/app/Actions/Dashboard/Infrastructure/EnvironmentIndexAction.ts +67 -0
- package/app/Actions/Dashboard/Infrastructure/InsightsAction.ts +29 -0
- package/app/Actions/Dashboard/Infrastructure/LogIndexAction.ts +50 -0
- package/app/Actions/Dashboard/Infrastructure/MailboxIndexAction.ts +85 -0
- package/app/Actions/Dashboard/Infrastructure/PermissionIndexAction.ts +54 -0
- package/app/Actions/Dashboard/Infrastructure/RequestIndexAction.ts +44 -0
- package/app/Actions/Dashboard/Infrastructure/ServerIndexAction.ts +101 -0
- package/app/Actions/Dashboard/Jobs/CreateJob.ts +11 -0
- package/app/Actions/Dashboard/Jobs/GetAverageJobTime.ts +12 -0
- package/app/Actions/Dashboard/Jobs/GetJobHistory.ts +12 -0
- package/app/Actions/Dashboard/Jobs/GetJobs.ts +11 -0
- package/app/Actions/Dashboard/Jobs/GetJobsPerMinute.ts +12 -0
- package/app/Actions/Dashboard/Jobs/JobIndexAction.ts +136 -0
- package/app/Actions/Dashboard/Jobs/JobRetryAction.ts +23 -0
- package/app/Actions/Dashboard/Jobs/JobStatsAction.ts +49 -0
- package/app/Actions/Dashboard/Kanban/BoardDestroyAction.ts +84 -0
- package/app/Actions/Dashboard/Kanban/BoardShowAction.ts +215 -0
- package/app/Actions/Dashboard/Kanban/BoardStoreAction.ts +98 -0
- package/app/Actions/Dashboard/Kanban/BoardUpdateAction.ts +96 -0
- package/app/Actions/Dashboard/Kanban/BoardsIndexAction.ts +81 -0
- package/app/Actions/Dashboard/Kanban/BoardsReorderAction.ts +80 -0
- package/app/Actions/Dashboard/Kanban/CardAssigneesSyncAction.ts +103 -0
- package/app/Actions/Dashboard/Kanban/CardCommentDestroyAction.ts +33 -0
- package/app/Actions/Dashboard/Kanban/CardCommentStoreAction.ts +82 -0
- package/app/Actions/Dashboard/Kanban/CardDestroyAction.ts +48 -0
- package/app/Actions/Dashboard/Kanban/CardLabelsSyncAction.ts +103 -0
- package/app/Actions/Dashboard/Kanban/CardShowAction.ts +122 -0
- package/app/Actions/Dashboard/Kanban/CardStoreAction.ts +112 -0
- package/app/Actions/Dashboard/Kanban/CardUpdateAction.ts +91 -0
- package/app/Actions/Dashboard/Kanban/CardsReorderAction.ts +148 -0
- package/app/Actions/Dashboard/Kanban/ColumnDestroyAction.ts +59 -0
- package/app/Actions/Dashboard/Kanban/ColumnStoreAction.ts +94 -0
- package/app/Actions/Dashboard/Kanban/ColumnUpdateAction.ts +89 -0
- package/app/Actions/Dashboard/Kanban/ColumnsReorderAction.ts +85 -0
- package/app/Actions/Dashboard/Kanban/LabelDestroyAction.ts +41 -0
- package/app/Actions/Dashboard/Kanban/LabelStoreAction.ts +70 -0
- package/app/Actions/Dashboard/Kanban/LabelUpdateAction.ts +83 -0
- package/app/Actions/Dashboard/Kanban/UsersListAction.ts +36 -0
- package/app/Actions/Dashboard/Library/CreateComponent.ts +11 -0
- package/app/Actions/Dashboard/Library/CreateFunction.ts +11 -0
- package/app/Actions/Dashboard/Library/DependencyIndexAction.ts +92 -0
- package/app/Actions/Dashboard/Library/GetAverageReleaseTime.ts +12 -0
- package/app/Actions/Dashboard/Library/GetComponents.ts +24 -0
- package/app/Actions/Dashboard/Library/GetComponentsDownloadCount.ts +12 -0
- package/app/Actions/Dashboard/Library/GetDownloadCount.ts +12 -0
- package/app/Actions/Dashboard/Library/GetFunctions.ts +22 -0
- package/app/Actions/Dashboard/Library/GetFunctionsDownloadCount.ts +12 -0
- package/app/Actions/Dashboard/Library/GetReleaseCount.ts +12 -0
- package/app/Actions/Dashboard/Library/GetReleases.ts +28 -0
- package/app/Actions/Dashboard/Library/PackageIndexAction.ts +26 -0
- package/app/Actions/Dashboard/Marketing/CampaignIndexAction.ts +45 -0
- package/app/Actions/Dashboard/Marketing/ListIndexAction.ts +43 -0
- package/app/Actions/Dashboard/Marketing/SocialPostIndexAction.ts +47 -0
- package/app/Actions/Dashboard/Models/GetModels.ts +39 -0
- package/app/Actions/Dashboard/Models/GetSubscriberCount.ts +12 -0
- package/app/Actions/Dashboard/Models/GetUserCount.ts +12 -0
- package/app/Actions/Dashboard/Models/ModelShowAction.ts +131 -0
- package/app/Actions/Dashboard/Models/ModelsIndexAction.ts +169 -0
- package/app/Actions/Dashboard/Notifications/CreateNotification.ts +11 -0
- package/app/Actions/Dashboard/Notifications/GetAverageNotificationTime.ts +12 -0
- package/app/Actions/Dashboard/Notifications/GetNotificationCount.ts +12 -0
- package/app/Actions/Dashboard/Notifications/GetNotificationDeliveryRate.ts +12 -0
- package/app/Actions/Dashboard/Notifications/GetNotificationHistory.ts +12 -0
- package/app/Actions/Dashboard/Notifications/GetNotifications.ts +12 -0
- package/app/Actions/Dashboard/Notifications/NotificationDashboardAction.ts +43 -0
- package/app/Actions/Dashboard/Queries/QueryIndexAction.ts +22 -0
- package/app/Actions/Dashboard/Queue/QueueRetryFailedAction.ts +22 -0
- package/app/Actions/Dashboard/Queue/QueueStatsAction.ts +98 -0
- package/app/Actions/Dashboard/Queue/QueueWorkersAction.ts +46 -0
- package/app/Actions/Dashboard/Rbac/PermissionDestroyAction.ts +33 -0
- package/app/Actions/Dashboard/Rbac/PermissionStoreAction.ts +53 -0
- package/app/Actions/Dashboard/Rbac/PermissionsIndexAction.ts +34 -0
- package/app/Actions/Dashboard/Rbac/RoleDestroyAction.ts +35 -0
- package/app/Actions/Dashboard/Rbac/RolePermissionsShowAction.ts +45 -0
- package/app/Actions/Dashboard/Rbac/RolePermissionsSyncAction.ts +63 -0
- package/app/Actions/Dashboard/Rbac/RoleStoreAction.ts +58 -0
- package/app/Actions/Dashboard/Rbac/RolesIndexAction.ts +36 -0
- package/app/Actions/Dashboard/Rbac/UserRolesShowAction.ts +40 -0
- package/app/Actions/Dashboard/Rbac/UserRolesSyncAction.ts +65 -0
- package/app/Actions/Dashboard/Rbac/UsersListAction.ts +39 -0
- package/app/Actions/Dashboard/Realtime/RealtimeStatsAction.ts +50 -0
- package/app/Actions/Dashboard/Releases/ReleaseIndexAction.ts +21 -0
- package/app/Actions/Dashboard/Requests/GetAverageRequestTime.ts +12 -0
- package/app/Actions/Dashboard/Requests/GetRequestCount.ts +12 -0
- package/app/Actions/Dashboard/Requests/GetRequestSuccessRate.ts +12 -0
- package/app/Actions/Dashboard/Requests/GetRequests.ts +12 -0
- package/app/Actions/Dashboard/Search/GlobalSearchAction.ts +26 -0
- package/app/Actions/Dashboard/ServiceHealthAction.ts +243 -0
- package/app/Actions/Dashboard/Settings/MailSettingsGetAction.ts +20 -0
- package/app/Actions/Dashboard/Settings/MailSettingsUpdateAction.ts +10 -0
- package/app/Actions/Dashboard/Settings/SettingsIndexAction.ts +40 -0
- package/app/Actions/Dashboard/Settings/UpdateAiConfig.ts +27 -0
- package/app/Actions/Dashboard/Settings/UpdateAnalyticsConfig.ts +38 -0
- package/app/Actions/Dashboard/Settings/UpdateAppConfig.ts +38 -0
- package/app/Actions/Dashboard/Settings/UpdateCacheConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdateCliConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdateCloudConfig.ts +38 -0
- package/app/Actions/Dashboard/Settings/UpdateDatabaseConfig.ts +38 -0
- package/app/Actions/Dashboard/Settings/UpdateDnsConfig.ts +38 -0
- package/app/Actions/Dashboard/Settings/UpdateDocsConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdateEmailConfig.ts +38 -0
- package/app/Actions/Dashboard/Settings/UpdateEnvConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdateErrorsConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdateFileSystemsConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdateGitConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdateHashingConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdateLibraryConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdateLoggerConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdateNotificationConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdatePaymentConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdatePortsConfig.ts +38 -0
- package/app/Actions/Dashboard/Settings/UpdateQueueConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdateSearchEngineConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdateSecurityConfig.ts +38 -0
- package/app/Actions/Dashboard/Settings/UpdateServicesConfig.ts +38 -0
- package/app/Actions/Dashboard/Settings/UpdateStorageConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdateTeamConfig.ts +11 -0
- package/app/Actions/Dashboard/Settings/UpdateUiConfig.ts +11 -0
- package/app/Actions/HealthAction.ts +112 -0
- package/app/Actions/InstallAction.ts +83 -0
- package/app/Actions/Locale/SetLocaleAction.ts +51 -0
- package/app/Actions/LogAction.ts +42 -0
- package/app/Actions/Logs/LogFetchStatsAction.ts +50 -0
- package/app/Actions/Logs/LogIndexOrmAction.ts +13 -0
- package/app/Actions/Logs/LogShowOrmAction.ts +17 -0
- package/app/Actions/Logs/LogStoreOrmAction.ts +16 -0
- package/app/Actions/Logs/LogUpdateOrmAction.ts +20 -0
- package/app/Actions/Monitoring/ErrorDestroyAction.ts +25 -0
- package/app/Actions/Monitoring/ErrorGroupAction.ts +21 -0
- package/app/Actions/Monitoring/ErrorIgnoreAction.ts +25 -0
- package/app/Actions/Monitoring/ErrorIndexAction.ts +14 -0
- package/app/Actions/Monitoring/ErrorResolveAction.ts +25 -0
- package/app/Actions/Monitoring/ErrorShowAction.ts +19 -0
- package/app/Actions/Monitoring/ErrorStatsAction.ts +14 -0
- package/app/Actions/Monitoring/ErrorTimelineAction.ts +14 -0
- package/app/Actions/Monitoring/ErrorUnresolveAction.ts +25 -0
- package/app/Actions/NotifyUser.ts +10 -0
- package/app/Actions/Password/PasswordResetAction.ts +54 -0
- package/app/Actions/Password/SendPasswordResetEmailAction.ts +66 -0
- package/app/Actions/Password/VerifyResetTokenAction.ts +27 -0
- package/app/Actions/Payment/CancelSubscriptionAction.ts +20 -0
- package/app/Actions/Payment/CreateCheckoutAction.ts +28 -0
- package/app/Actions/Payment/CreateInvoiceSubscription.ts +18 -0
- package/app/Actions/Payment/CreatePaymentIntentAction.ts +31 -0
- package/app/Actions/Payment/CreateSetupIntentAction.ts +20 -0
- package/app/Actions/Payment/CreateSubscriptionAction.ts +23 -0
- package/app/Actions/Payment/DeleteDefaultPaymentAction.ts +18 -0
- package/app/Actions/Payment/FetchActiveSubscriptionAction.ts +18 -0
- package/app/Actions/Payment/FetchDefaultPaymentMethodAction.ts +18 -0
- package/app/Actions/Payment/FetchPaymentCustomerAction.ts +18 -0
- package/app/Actions/Payment/FetchPaymentMethodsAction.ts +18 -0
- package/app/Actions/Payment/FetchProductAction.ts +15 -0
- package/app/Actions/Payment/FetchTransactionHistoryAction.ts +18 -0
- package/app/Actions/Payment/FetchUserSubscriptionsAction.ts +18 -0
- package/app/Actions/Payment/SetDefaultPaymentAction.ts +20 -0
- package/app/Actions/Payment/SetUserDefaultPaymentAction.ts +20 -0
- package/app/Actions/Payment/StorePaymentMethodAction.ts +20 -0
- package/app/Actions/Payment/StoreTransactionAction.ts +20 -0
- package/app/Actions/Payment/UpdateCustomerAction.ts +24 -0
- package/app/Actions/Payment/UpdateDefaultPaymentMethodAction.ts +18 -0
- package/app/Actions/Payment/UpdateSubscriptionAction.ts +22 -0
- package/app/Actions/Queue/FetchQueuesAction.ts +14 -0
- package/app/Actions/README.md +61 -0
- package/app/Actions/Realtime/FetchWebsocketsAction.ts +14 -0
- package/app/Actions/RobotsAction.ts +61 -0
- package/app/Actions/SendWelcomeEmail.ts +40 -0
- package/app/Actions/SitemapAction.ts +234 -0
- package/app/Actions/Storefront/AddToCartAction.ts +153 -0
- package/app/Actions/Storefront/CheckoutContactAction.ts +50 -0
- package/app/Actions/Storefront/CheckoutShippingAction.ts +61 -0
- package/app/Actions/Storefront/GetCartAction.ts +58 -0
- package/app/Actions/Storefront/PlaceOrderAction.ts +195 -0
- package/app/Actions/Storefront/SubmitReviewAction.ts +140 -0
- package/app/Actions/Storefront/UpdateCartItemAction.ts +83 -0
- package/app/Actions/Storefront/_shipping.ts +64 -0
- package/app/Actions/SubscriberEmailAction.ts +60 -0
- package/app/Actions/TestErrorAction.ts +163 -0
- package/app/Actions/UnsubscribeAction.ts +107 -0
- package/app/Actions/UploadTestAction.ts +57 -0
- package/app/Broadcasts/OrderShipped.ts +57 -0
- package/app/Commands/Inspire.ts +57 -0
- package/app/Commands/README.md +84 -0
- package/app/Commands.ts +38 -0
- package/app/Controllers/ComingSoonController.ts +9 -0
- package/app/Controllers/QueryController.ts +373 -0
- package/app/Events.ts +14 -0
- package/app/Gates.ts +107 -0
- package/app/Jobs/ExampleJob.ts +20 -0
- package/app/Jobs/ExampleTwoJob.ts +12 -0
- package/app/Jobs/PruneModelsJob.ts +61 -0
- package/app/Jobs/PruneQueryLogsJob.ts +51 -0
- package/app/Jobs/README.md +79 -0
- package/app/Jobs/SendCampaignJob.ts +201 -0
- package/app/Jobs/SendEmailJob.ts +52 -0
- package/app/Jobs/SendPasswordResetEmailJob.ts +44 -0
- package/app/Jobs/SyncSearchIndexJob.ts +64 -0
- package/app/Listener.ts +84 -0
- package/app/Listeners/Console.ts +22 -0
- package/app/Listeners/README.md +47 -0
- package/app/Mail/OrderConfirmation.ts +60 -0
- package/app/Mail/SubscriptionConfirmation.ts +41 -0
- package/app/Middleware/Abilities.ts +53 -0
- package/app/Middleware/Api.ts +26 -0
- package/app/Middleware/Auth.ts +59 -0
- package/app/Middleware/Can.ts +68 -0
- package/app/Middleware/Compress.ts +213 -0
- package/app/Middleware/Cors.ts +320 -0
- package/app/Middleware/Csrf.ts +224 -0
- package/app/Middleware/EnsureEmailIsVerified.ts +28 -0
- package/app/Middleware/Env.ts +24 -0
- package/app/Middleware/EnvDevelopment.ts +19 -0
- package/app/Middleware/EnvLocal.ts +19 -0
- package/app/Middleware/EnvNotDevelopment.ts +19 -0
- package/app/Middleware/EnvNotLocal.ts +19 -0
- package/app/Middleware/EnvNotProduction.ts +19 -0
- package/app/Middleware/EnvNotStaging.ts +19 -0
- package/app/Middleware/EnvProduction.ts +19 -0
- package/app/Middleware/EnvStaging.ts +19 -0
- package/app/Middleware/Guest.ts +25 -0
- package/app/Middleware/Logger.ts +20 -0
- package/app/Middleware/Maintenance.ts +36 -0
- package/app/Middleware/Permission.ts +42 -0
- package/app/Middleware/README.md +48 -0
- package/app/Middleware/Role.ts +42 -0
- package/app/Middleware/Signed.ts +31 -0
- package/app/Middleware/Team.ts +36 -0
- package/app/Middleware/Throttle.ts +96 -0
- package/app/Middleware.ts +35 -0
- package/app/Models/Activity.ts +94 -0
- package/app/Models/Board.ts +103 -0
- package/app/Models/BoardColumn.ts +87 -0
- package/app/Models/Campaign.ts +239 -0
- package/app/Models/CampaignSend.ts +107 -0
- package/app/Models/Card.ts +117 -0
- package/app/Models/CardComment.ts +68 -0
- package/app/Models/Comment.ts +108 -0
- package/app/Models/Content/Author.ts +104 -0
- package/app/Models/Content/Page.ts +99 -0
- package/app/Models/Content/Post.ts +144 -0
- package/app/Models/Deployment.ts +116 -0
- package/app/Models/EmailList.ts +126 -0
- package/app/Models/EmailListSubscriber.ts +91 -0
- package/app/Models/Error.ts +70 -0
- package/app/Models/FailedJob.ts +62 -0
- package/app/Models/Job.ts +63 -0
- package/app/Models/Label.ts +66 -0
- package/app/Models/Log.ts +110 -0
- package/app/Models/Notification.ts +118 -0
- package/app/Models/PaymentMethod.ts +98 -0
- package/app/Models/PaymentProduct.ts +91 -0
- package/app/Models/PaymentTransaction.ts +78 -0
- package/app/Models/README.md +31 -0
- package/app/Models/Release.ts +89 -0
- package/app/Models/Request.ts +114 -0
- package/app/Models/SocialPost.ts +136 -0
- package/app/Models/Subscriber.ts +89 -0
- package/app/Models/SubscriberEmail.ts +55 -0
- package/app/Models/Subscription.ts +153 -0
- package/app/Models/Tag.ts +82 -0
- package/app/Models/Team.ts +72 -0
- package/app/Models/User.ts +134 -0
- package/app/Models/commerce/Cart.ts +139 -0
- package/app/Models/commerce/CartItem.ts +137 -0
- package/app/Models/commerce/Category.ts +105 -0
- package/app/Models/commerce/Coupon.ts +181 -0
- package/app/Models/commerce/Customer.ts +132 -0
- package/app/Models/commerce/DeliveryRoute.ts +93 -0
- package/app/Models/commerce/DigitalDelivery.ts +105 -0
- package/app/Models/commerce/Driver.ts +97 -0
- package/app/Models/commerce/GiftCard.ts +191 -0
- package/app/Models/commerce/LicenseKey.ts +87 -0
- package/app/Models/commerce/LoyaltyPoint.ts +107 -0
- package/app/Models/commerce/LoyaltyReward.ts +124 -0
- package/app/Models/commerce/Manufacturer.ts +84 -0
- package/app/Models/commerce/Order.ts +142 -0
- package/app/Models/commerce/OrderItem.ts +51 -0
- package/app/Models/commerce/Payment.ts +163 -0
- package/app/Models/commerce/PrintDevice.ts +117 -0
- package/app/Models/commerce/Product.ts +148 -0
- package/app/Models/commerce/ProductUnit.ts +107 -0
- package/app/Models/commerce/ProductVariant.ts +106 -0
- package/app/Models/commerce/Receipt.ts +128 -0
- package/app/Models/commerce/Review.ts +140 -0
- package/app/Models/commerce/ShippingMethod.ts +97 -0
- package/app/Models/commerce/ShippingRate.ts +74 -0
- package/app/Models/commerce/ShippingZone.ts +111 -0
- package/app/Models/commerce/TaxRate.ts +114 -0
- package/app/Models/commerce/Transaction.ts +126 -0
- package/app/Models/commerce/WaitlistProduct.ts +149 -0
- package/app/Models/commerce/WaitlistRestaurant.ts +187 -0
- package/app/Models/realtime/Websocket.ts +77 -0
- package/app/Notifications/README.md +31 -0
- package/app/Notifications/Welcome.ts +32 -0
- package/app/Policies/PostPolicy.ts +131 -0
- package/app/README.md +23 -0
- package/app/Routes.ts +47 -0
- package/app/Scheduler.ts +28 -0
- package/app/Storefront/CartCookie.ts +109 -0
- package/package.json +3 -2
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import type { CorsConfig, ResolvedCorsConfig } from '@stacksjs/types'
|
|
2
|
+
import { config } from '@stacksjs/config'
|
|
3
|
+
import { Middleware } from '@stacksjs/router'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* CORS Middleware
|
|
7
|
+
*
|
|
8
|
+
* Implements the Cross-Origin Resource Sharing protocol on top of the
|
|
9
|
+
* Stacks middleware pipeline. Two responsibilities:
|
|
10
|
+
*
|
|
11
|
+
* 1. **Preflight (OPTIONS)** — short-circuit with a 204 + CORS headers.
|
|
12
|
+
* No further middleware runs, no action runs.
|
|
13
|
+
* 2. **Regular requests** — pass through to the action, then attach the
|
|
14
|
+
* configured CORS headers to the outgoing response.
|
|
15
|
+
*
|
|
16
|
+
* Because the Stacks middleware runner only invokes `handle()` *before* the
|
|
17
|
+
* action (no native post-response hook), the response-mutation half of
|
|
18
|
+
* this middleware uses the same marker pattern as `Compress.ts`: `handle`
|
|
19
|
+
* stamps the request with `_corsOptions`, and the router applies the
|
|
20
|
+
* headers via {@link applyCorsHeaders} after the action returns.
|
|
21
|
+
*
|
|
22
|
+
* **Configuration**
|
|
23
|
+
*
|
|
24
|
+
* Reads from `config.cors`. If unset, sensible defaults are used:
|
|
25
|
+
* - `origin: '*'`
|
|
26
|
+
* - `methods: ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']`
|
|
27
|
+
* - `allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']`
|
|
28
|
+
* - `exposedHeaders: []`
|
|
29
|
+
* - `credentials: false`
|
|
30
|
+
* - `maxAge: 86400` (24h)
|
|
31
|
+
*
|
|
32
|
+
* Expected config shape (add to `config/cors.ts` or under `config/app.ts`):
|
|
33
|
+
* ```ts
|
|
34
|
+
* export default {
|
|
35
|
+
* cors: {
|
|
36
|
+
* origin: '*' | string[] | (origin: string) => boolean,
|
|
37
|
+
* methods?: string[],
|
|
38
|
+
* allowedHeaders?: string[],
|
|
39
|
+
* exposedHeaders?: string[],
|
|
40
|
+
* credentials?: boolean,
|
|
41
|
+
* maxAge?: number,
|
|
42
|
+
* }
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* **Vary header**
|
|
47
|
+
*
|
|
48
|
+
* `Vary: Origin` is set unconditionally on responses that include CORS
|
|
49
|
+
* headers. Without it, an HTTP cache could serve a response with
|
|
50
|
+
* `Access-Control-Allow-Origin: https://a.com` to a request from
|
|
51
|
+
* `https://b.com`. We also append `Access-Control-Request-Headers` and
|
|
52
|
+
* `Access-Control-Request-Method` to Vary on preflight so caches don't
|
|
53
|
+
* conflate two preflights with different requested headers/methods.
|
|
54
|
+
*
|
|
55
|
+
* **Credentials safety**
|
|
56
|
+
*
|
|
57
|
+
* When `credentials: true`, the spec forbids `Access-Control-Allow-Origin: *`.
|
|
58
|
+
* In that case we echo the request's Origin (only if it matches the
|
|
59
|
+
* configured allow-list) or omit the header entirely — never `*`. This
|
|
60
|
+
* prevents the most common credentials-bypass misconfiguration.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* route.get('/api/*', 'Actions/Api').middleware('cors')
|
|
65
|
+
* route.options('/api/*', () => new Response(null)).middleware('cors')
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
// `CorsConfig` + `ResolvedCorsConfig` live in `@stacksjs/types` so
|
|
70
|
+
// `config/cors.ts` files can import them by package name and get
|
|
71
|
+
// IntelliSense. The middleware re-exports them here for backwards
|
|
72
|
+
// compatibility with consumers that previously imported them from this
|
|
73
|
+
// file directly (stacksjs/stacks#1859 H-2).
|
|
74
|
+
export type { CorsConfig, ResolvedCorsConfig }
|
|
75
|
+
|
|
76
|
+
/** Defaults used when `config.cors` is missing or partial. */
|
|
77
|
+
const DEFAULT_CORS: ResolvedCorsConfig = {
|
|
78
|
+
origin: '*',
|
|
79
|
+
methods: ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
|
80
|
+
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
|
|
81
|
+
exposedHeaders: [],
|
|
82
|
+
credentials: false,
|
|
83
|
+
maxAge: 86_400,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Read `config.cors` and merge with defaults. Fail-soft — if config access
|
|
88
|
+
* throws (e.g. before bootstrap), we fall back to defaults rather than
|
|
89
|
+
* crashing the request.
|
|
90
|
+
*/
|
|
91
|
+
function resolveCorsConfig(): ResolvedCorsConfig {
|
|
92
|
+
try {
|
|
93
|
+
// `config.cors` is now typed via `StacksOptions.cors?: CorsConfig`
|
|
94
|
+
// (stacksjs/stacks#1859 H-2). The proxy returns undefined when the
|
|
95
|
+
// key is missing, which the fall-through below handles.
|
|
96
|
+
const userConfig: CorsConfig | undefined = config.cors
|
|
97
|
+
if (!userConfig)
|
|
98
|
+
return DEFAULT_CORS
|
|
99
|
+
return {
|
|
100
|
+
origin: userConfig.origin ?? DEFAULT_CORS.origin,
|
|
101
|
+
methods: userConfig.methods ?? DEFAULT_CORS.methods,
|
|
102
|
+
allowedHeaders: userConfig.allowedHeaders ?? DEFAULT_CORS.allowedHeaders,
|
|
103
|
+
exposedHeaders: userConfig.exposedHeaders ?? DEFAULT_CORS.exposedHeaders,
|
|
104
|
+
credentials: userConfig.credentials ?? DEFAULT_CORS.credentials,
|
|
105
|
+
maxAge: userConfig.maxAge ?? DEFAULT_CORS.maxAge,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return DEFAULT_CORS
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Decide whether a given Origin is allowed under the configured policy.
|
|
115
|
+
*
|
|
116
|
+
* - `'*'` — every origin allowed (but see credentials note below).
|
|
117
|
+
* - `string[]` — exact-match list.
|
|
118
|
+
* - `(origin) => boolean` — caller-defined predicate.
|
|
119
|
+
*/
|
|
120
|
+
function isOriginAllowed(origin: string, policy: ResolvedCorsConfig['origin']): boolean {
|
|
121
|
+
if (policy === '*')
|
|
122
|
+
return true
|
|
123
|
+
if (Array.isArray(policy))
|
|
124
|
+
return policy.includes(origin)
|
|
125
|
+
if (typeof policy === 'function')
|
|
126
|
+
return policy(origin)
|
|
127
|
+
return false
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Compute the value to send in `Access-Control-Allow-Origin`, honoring the
|
|
132
|
+
* "no `*` with credentials" rule.
|
|
133
|
+
*
|
|
134
|
+
* Returns:
|
|
135
|
+
* - the matched origin string (echoes the request, or `*` when no creds + wildcard policy)
|
|
136
|
+
* - `null` if the origin is disallowed (caller should omit the header entirely)
|
|
137
|
+
*/
|
|
138
|
+
function computeAllowOrigin(
|
|
139
|
+
requestOrigin: string | null,
|
|
140
|
+
cfg: ResolvedCorsConfig,
|
|
141
|
+
): string | null {
|
|
142
|
+
// No Origin header → not a CORS request from the browser's perspective;
|
|
143
|
+
// we still set Vary in case a cache later serves the same URL.
|
|
144
|
+
if (!requestOrigin)
|
|
145
|
+
return cfg.origin === '*' && !cfg.credentials ? '*' : null
|
|
146
|
+
|
|
147
|
+
if (!isOriginAllowed(requestOrigin, cfg.origin))
|
|
148
|
+
return null
|
|
149
|
+
|
|
150
|
+
// Credentials mode forbids the literal `*` per the CORS spec — browsers
|
|
151
|
+
// reject the response if both are present. Echo the actual origin
|
|
152
|
+
// instead so the Allow-Credentials response is usable.
|
|
153
|
+
if (cfg.credentials)
|
|
154
|
+
return requestOrigin
|
|
155
|
+
|
|
156
|
+
// Non-credentials wildcard policy: prefer `*` so caches collapse all
|
|
157
|
+
// origins onto one stored response (more cacheable than per-origin echo).
|
|
158
|
+
if (cfg.origin === '*')
|
|
159
|
+
return '*'
|
|
160
|
+
|
|
161
|
+
return requestOrigin
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Append a token to a comma-separated header value if not already present
|
|
166
|
+
* (case-insensitive). Returns the new header value.
|
|
167
|
+
*/
|
|
168
|
+
function appendVary(existing: string | null, token: string): string {
|
|
169
|
+
if (!existing)
|
|
170
|
+
return token
|
|
171
|
+
const tokens = existing.split(',').map(s => s.trim().toLowerCase())
|
|
172
|
+
if (tokens.includes(token.toLowerCase()))
|
|
173
|
+
return existing
|
|
174
|
+
return `${existing}, ${token}`
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Build the preflight (OPTIONS) response. Returns 204 with the configured
|
|
179
|
+
* CORS headers — does NOT proceed to the next middleware or action.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```ts
|
|
183
|
+
* if (request.method === 'OPTIONS')
|
|
184
|
+
* throw buildPreflightResponse(request, resolveCorsConfig())
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export function buildPreflightResponse(request: Request, cfg: ResolvedCorsConfig): Response {
|
|
188
|
+
const headers = new Headers()
|
|
189
|
+
const requestOrigin = request.headers.get('origin')
|
|
190
|
+
const allowOrigin = computeAllowOrigin(requestOrigin, cfg)
|
|
191
|
+
|
|
192
|
+
if (allowOrigin !== null)
|
|
193
|
+
headers.set('Access-Control-Allow-Origin', allowOrigin)
|
|
194
|
+
|
|
195
|
+
// Vary on every dimension that influences the preflight response so
|
|
196
|
+
// shared caches don't blend two clients' answers.
|
|
197
|
+
headers.set('Vary', 'Origin, Access-Control-Request-Method, Access-Control-Request-Headers')
|
|
198
|
+
|
|
199
|
+
if (cfg.credentials)
|
|
200
|
+
headers.set('Access-Control-Allow-Credentials', 'true')
|
|
201
|
+
|
|
202
|
+
// Echo the requested method if it's in our allow-list; otherwise advertise
|
|
203
|
+
// the configured methods. This keeps the preflight tight when the client
|
|
204
|
+
// is asking about a specific method.
|
|
205
|
+
const requestedMethod = request.headers.get('access-control-request-method')
|
|
206
|
+
const methods
|
|
207
|
+
= requestedMethod && cfg.methods.includes(requestedMethod.toUpperCase())
|
|
208
|
+
? [requestedMethod.toUpperCase()]
|
|
209
|
+
: cfg.methods
|
|
210
|
+
headers.set('Access-Control-Allow-Methods', methods.join(', '))
|
|
211
|
+
|
|
212
|
+
// Echo the requested headers (typically lowercase) — the browser sends
|
|
213
|
+
// them and the spec allows us to reflect rather than enumerate.
|
|
214
|
+
const requestedHeaders = request.headers.get('access-control-request-headers')
|
|
215
|
+
if (requestedHeaders)
|
|
216
|
+
headers.set('Access-Control-Allow-Headers', requestedHeaders)
|
|
217
|
+
else if (cfg.allowedHeaders.length > 0)
|
|
218
|
+
headers.set('Access-Control-Allow-Headers', cfg.allowedHeaders.join(', '))
|
|
219
|
+
|
|
220
|
+
if (cfg.maxAge > 0)
|
|
221
|
+
headers.set('Access-Control-Max-Age', String(cfg.maxAge))
|
|
222
|
+
|
|
223
|
+
// 204 No Content is the canonical preflight response — it's what
|
|
224
|
+
// express/cors, fastify-cors, and Hono all emit.
|
|
225
|
+
return new Response(null, { status: 204, headers })
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Apply CORS headers to a non-preflight response. Mutates a clone of the
|
|
230
|
+
* original (Headers may be immutable on some Response shapes) and returns
|
|
231
|
+
* the new instance. Always sets `Vary: Origin` so caches don't serve
|
|
232
|
+
* cross-origin responses for the wrong origin.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```ts
|
|
236
|
+
* const response = await action.handle(req)
|
|
237
|
+
* return applyCorsHeaders(req, response)
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
export function applyCorsHeaders(request: Request, response: Response, cfg?: ResolvedCorsConfig): Response {
|
|
241
|
+
const config = cfg ?? resolveCorsConfig()
|
|
242
|
+
const requestOrigin = request.headers.get('origin')
|
|
243
|
+
const allowOrigin = computeAllowOrigin(requestOrigin, config)
|
|
244
|
+
|
|
245
|
+
// Try to mutate in place first — fastest path. If headers are immutable
|
|
246
|
+
// (some Response implementations), fall through to the rebuild path.
|
|
247
|
+
try {
|
|
248
|
+
if (allowOrigin !== null)
|
|
249
|
+
response.headers.set('Access-Control-Allow-Origin', allowOrigin)
|
|
250
|
+
response.headers.set('Vary', appendVary(response.headers.get('Vary'), 'Origin'))
|
|
251
|
+
if (config.credentials)
|
|
252
|
+
response.headers.set('Access-Control-Allow-Credentials', 'true')
|
|
253
|
+
if (config.exposedHeaders.length > 0)
|
|
254
|
+
response.headers.set('Access-Control-Expose-Headers', config.exposedHeaders.join(', '))
|
|
255
|
+
return response
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
// Fall through: response headers are immutable — rebuild.
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const newHeaders = new Headers(response.headers)
|
|
262
|
+
if (allowOrigin !== null)
|
|
263
|
+
newHeaders.set('Access-Control-Allow-Origin', allowOrigin)
|
|
264
|
+
newHeaders.set('Vary', appendVary(newHeaders.get('Vary'), 'Origin'))
|
|
265
|
+
if (config.credentials)
|
|
266
|
+
newHeaders.set('Access-Control-Allow-Credentials', 'true')
|
|
267
|
+
if (config.exposedHeaders.length > 0)
|
|
268
|
+
newHeaders.set('Access-Control-Expose-Headers', config.exposedHeaders.join(', '))
|
|
269
|
+
|
|
270
|
+
return new Response(response.body, {
|
|
271
|
+
status: response.status,
|
|
272
|
+
statusText: response.statusText,
|
|
273
|
+
headers: newHeaders,
|
|
274
|
+
})
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Public re-export for the router to consume without re-deriving config.
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```ts
|
|
282
|
+
* const cfg = getResolvedCorsConfig()
|
|
283
|
+
* ```
|
|
284
|
+
*/
|
|
285
|
+
export function getResolvedCorsConfig(): ResolvedCorsConfig {
|
|
286
|
+
return resolveCorsConfig()
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export default new Middleware({
|
|
290
|
+
name: 'cors',
|
|
291
|
+
// CORS gating must run before auth/throttle so a 401/429 response still
|
|
292
|
+
// carries the right CORS headers (otherwise the browser hides the body
|
|
293
|
+
// from the SPA and the user sees a confusing "CORS error" instead of the
|
|
294
|
+
// actual auth failure).
|
|
295
|
+
priority: 0,
|
|
296
|
+
|
|
297
|
+
async handle(request) {
|
|
298
|
+
const cfg = resolveCorsConfig()
|
|
299
|
+
|
|
300
|
+
// Preflight short-circuit — throwing a Response is the documented way
|
|
301
|
+
// to halt the chain and return immediately. The router catches it and
|
|
302
|
+
// sends it as-is.
|
|
303
|
+
if (request.method === 'OPTIONS') {
|
|
304
|
+
// Only treat as preflight if the spec-required header is present.
|
|
305
|
+
// A bare OPTIONS without `Access-Control-Request-Method` may be a
|
|
306
|
+
// server-status probe or method-discovery request — let the action
|
|
307
|
+
// handle it.
|
|
308
|
+
if (request.headers.get('access-control-request-method')) {
|
|
309
|
+
// eslint-disable-next-line no-throw-literal
|
|
310
|
+
throw buildPreflightResponse(request as unknown as Request, cfg)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Mark the request so the router's response-finalization step picks
|
|
315
|
+
// up the CORS config. We stash the resolved config to avoid resolving
|
|
316
|
+
// it twice per request.
|
|
317
|
+
// eslint-disable-next-line ts/no-explicit-any
|
|
318
|
+
;request._corsConfig = cfg
|
|
319
|
+
},
|
|
320
|
+
})
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { randomBytes, timingSafeEqual } from 'node:crypto'
|
|
2
|
+
import { Buffer } from 'node:buffer'
|
|
3
|
+
import { HttpError } from '@stacksjs/error-handling'
|
|
4
|
+
import { Middleware } from '@stacksjs/router'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* CSRF Protection Middleware (default-on for unsafe methods)
|
|
8
|
+
*
|
|
9
|
+
* Design decision: enforce by default.
|
|
10
|
+
* Historical Stacks ran the CSRF middleware as opt-in, which left every
|
|
11
|
+
* POST/PUT/PATCH/DELETE route undefended unless an author remembered to
|
|
12
|
+
* add `.middleware('csrf')`. Forgetting is a security regression by
|
|
13
|
+
* default — flipping the polarity (default-on, explicit opt-out) is the
|
|
14
|
+
* modern Rails/Laravel posture.
|
|
15
|
+
*
|
|
16
|
+
* Pattern: stateless double-submit cookie.
|
|
17
|
+
* 1. Server issues a 32-byte random token in a non-HttpOnly cookie
|
|
18
|
+
* (`X-CSRF-Token`) on safe (GET/HEAD/OPTIONS) responses.
|
|
19
|
+
* 2. Client-side JS reads that cookie and echoes it back on unsafe
|
|
20
|
+
* requests via the `X-CSRF-Token` header (or a `_token` body field
|
|
21
|
+
* for traditional form submissions).
|
|
22
|
+
* 3. Server compares header/body value to cookie value with a
|
|
23
|
+
* timing-safe equality check.
|
|
24
|
+
*
|
|
25
|
+
* Why double-submit (and not server-stored tokens):
|
|
26
|
+
* - Stateless. No session/cache lookup on the hot path — just a
|
|
27
|
+
* constant-time string compare.
|
|
28
|
+
* - Same origin policy means a cross-site attacker can't read the
|
|
29
|
+
* cookie value, so they can't forge the matching header.
|
|
30
|
+
* - Cookie is `SameSite=Lax`, so most cross-site contexts won't even
|
|
31
|
+
* ship it; the double-submit check is a belt-and-braces layer for
|
|
32
|
+
* the gaps SameSite leaves (e.g. top-level GET → POST chains via
|
|
33
|
+
* forms).
|
|
34
|
+
*
|
|
35
|
+
* Bypass (in priority order):
|
|
36
|
+
* 1. Safe methods (GET/HEAD/OPTIONS) — no body to forge, no check.
|
|
37
|
+
* 2. `Authorization: Bearer …` present — API token clients are
|
|
38
|
+
* immune by definition (a CSRF attacker can't read tokens from
|
|
39
|
+
* Authorization headers, only forge cookie-auth requests).
|
|
40
|
+
* 3. Per-action `skipCsrf: true` — webhooks/callbacks that legitimately
|
|
41
|
+
* can't carry a CSRF cookie. The action sets this on its default
|
|
42
|
+
* export and the router's CSRF check skips it.
|
|
43
|
+
* 4. Per-route `.skipCsrf()` — same intent, route-level.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* // Implicit — every POST is CSRF-checked
|
|
48
|
+
* route.post('/posts', 'Actions/CreatePost')
|
|
49
|
+
*
|
|
50
|
+
* // Explicit opt-out for a webhook
|
|
51
|
+
* route.post('/webhooks/stripe', 'Actions/StripeWebhook').skipCsrf()
|
|
52
|
+
*
|
|
53
|
+
* // Or via the action itself
|
|
54
|
+
* export default {
|
|
55
|
+
* skipCsrf: true,
|
|
56
|
+
* async handle(req) { … },
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
export const CSRF_COOKIE_NAME = 'X-CSRF-Token'
|
|
62
|
+
const CSRF_HEADER_NAME = 'x-csrf-token'
|
|
63
|
+
const TOKEN_BYTES = 32
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generate a fresh CSRF token (hex-encoded, 32 random bytes → 64 chars).
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* const token = generateCsrfToken()
|
|
71
|
+
* // → 'a3f1...cd9e'
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export function generateCsrfToken(): string {
|
|
75
|
+
return randomBytes(TOKEN_BYTES).toString('hex')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Seed the CSRF cookie on the outgoing response if the incoming request
|
|
80
|
+
* didn't already carry one. Used by the router as a post-response step
|
|
81
|
+
* on safe-method (GET/HEAD/OPTIONS) responses so SPAs get a usable
|
|
82
|
+
* cookie value before they ever submit an unsafe request.
|
|
83
|
+
*
|
|
84
|
+
* The audit (stacksjs/stacks#1859 INVESTIGATE → confirmed) found that
|
|
85
|
+
* the CSRF middleware was reading the cookie but nothing was ever
|
|
86
|
+
* writing it — every browser POST without a Bearer token therefore
|
|
87
|
+
* failed CSRF by default. This helper closes that gap.
|
|
88
|
+
*
|
|
89
|
+
* The cookie is:
|
|
90
|
+
* - `SameSite=Lax` (sufficient for cross-site CSRF mitigation while
|
|
91
|
+
* still allowing top-level navigation to set it)
|
|
92
|
+
* - **NOT** `HttpOnly` (SPA JS needs to read the value and echo it as
|
|
93
|
+
* a header — the whole point of the double-submit pattern)
|
|
94
|
+
* - `Secure` only over HTTPS (so localhost dev still works)
|
|
95
|
+
* - 2-hour `Max-Age` — short enough to limit replay if leaked, long
|
|
96
|
+
* enough that idle tabs don't constantly re-fetch tokens
|
|
97
|
+
*/
|
|
98
|
+
export function seedCsrfCookieIfMissing(req: Request, response: Response): Response {
|
|
99
|
+
const cookieHeader = req.headers.get('cookie') || ''
|
|
100
|
+
if (cookieHeader.includes(`${CSRF_COOKIE_NAME}=`) || cookieHeader.includes('csrf-token=')) {
|
|
101
|
+
return response
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const token = generateCsrfToken()
|
|
105
|
+
const isSecure = req.url.startsWith('https://')
|
|
106
|
+
const cookie = [
|
|
107
|
+
`${CSRF_COOKIE_NAME}=${token}`,
|
|
108
|
+
'Path=/',
|
|
109
|
+
'SameSite=Lax',
|
|
110
|
+
'Max-Age=7200',
|
|
111
|
+
isSecure ? 'Secure' : null,
|
|
112
|
+
].filter(Boolean).join('; ')
|
|
113
|
+
|
|
114
|
+
// Append (not Set) so multiple Set-Cookie headers can coexist with any
|
|
115
|
+
// cookies the action handler set itself.
|
|
116
|
+
try {
|
|
117
|
+
response.headers.append('Set-Cookie', cookie)
|
|
118
|
+
return response
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Response headers can be immutable (e.g., when the response was
|
|
122
|
+
// built from a static asset). Clone with a fresh Headers object
|
|
123
|
+
// to attach the cookie.
|
|
124
|
+
const headers = new Headers(response.headers)
|
|
125
|
+
headers.append('Set-Cookie', cookie)
|
|
126
|
+
return new Response(response.body, { status: response.status, statusText: response.statusText, headers })
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Parse the Cookie header into a key→value map.
|
|
132
|
+
* Lenient: malformed pairs are skipped, not thrown.
|
|
133
|
+
*/
|
|
134
|
+
function parseCookies(req: Request): Record<string, string> {
|
|
135
|
+
const header = req.headers.get('cookie')
|
|
136
|
+
if (!header) return {}
|
|
137
|
+
const out: Record<string, string> = {}
|
|
138
|
+
for (const part of header.split(';')) {
|
|
139
|
+
const idx = part.indexOf('=')
|
|
140
|
+
if (idx === -1) continue
|
|
141
|
+
const k = part.slice(0, idx).trim()
|
|
142
|
+
const v = part.slice(idx + 1).trim()
|
|
143
|
+
if (k) out[k] = v
|
|
144
|
+
}
|
|
145
|
+
return out
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Constant-time string compare. `===` leaks token length & match-prefix
|
|
150
|
+
* length via timing — `timingSafeEqual` does not.
|
|
151
|
+
*/
|
|
152
|
+
function safeEqual(a: string, b: string): boolean {
|
|
153
|
+
if (typeof a !== 'string' || typeof b !== 'string') return false
|
|
154
|
+
if (a.length !== b.length) return false
|
|
155
|
+
try {
|
|
156
|
+
return timingSafeEqual(Buffer.from(a), Buffer.from(b))
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return false
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Returns true when the request carries a `Authorization: Bearer …`
|
|
165
|
+
* credential — those clients are exempt from CSRF (they're not riding
|
|
166
|
+
* an ambient cookie credential, so cross-site forgery doesn't apply).
|
|
167
|
+
*/
|
|
168
|
+
function hasBearerToken(req: Request): boolean {
|
|
169
|
+
const auth = req.headers.get('authorization') || req.headers.get('Authorization')
|
|
170
|
+
return typeof auth === 'string' && auth.toLowerCase().startsWith('bearer ')
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS'])
|
|
174
|
+
|
|
175
|
+
export default new Middleware({
|
|
176
|
+
name: 'csrf',
|
|
177
|
+
// Run early — before auth, so we don't waste auth work on a request
|
|
178
|
+
// we're going to reject anyway. After maintenance/throttle though.
|
|
179
|
+
priority: 2,
|
|
180
|
+
|
|
181
|
+
async handle(request) {
|
|
182
|
+
const method = request.method.toUpperCase()
|
|
183
|
+
|
|
184
|
+
// Safe methods don't mutate state — no token check needed.
|
|
185
|
+
// Token *seeding* (set the cookie if it's missing) happens after
|
|
186
|
+
// the response is built; we don't have a post-response hook
|
|
187
|
+
// here, so action handlers / SPAs can call `generateCsrfToken()`
|
|
188
|
+
// themselves on the first GET they need it for.
|
|
189
|
+
if (SAFE_METHODS.has(method)) return
|
|
190
|
+
|
|
191
|
+
// API clients with a bearer token are exempt — see header docstring.
|
|
192
|
+
if (hasBearerToken(request)) return
|
|
193
|
+
|
|
194
|
+
// Per-action opt-out: an action exporting `skipCsrf: true` (or
|
|
195
|
+
// `csrf: false`) has declared it can't participate in CSRF
|
|
196
|
+
// (webhooks, third-party callbacks). The router stamps a hint on
|
|
197
|
+
// the request when it resolves such an action; if that hint is
|
|
198
|
+
// present, skip enforcement.
|
|
199
|
+
if (request._skipCsrf === true) return
|
|
200
|
+
|
|
201
|
+
// Look up the submitted token. Header is the SPA path; body field
|
|
202
|
+
// is the traditional form-post path. We accept either.
|
|
203
|
+
const headerToken = request.headers.get(CSRF_HEADER_NAME)
|
|
204
|
+
|| request.headers.get('X-CSRF-Token')
|
|
205
|
+
|| request.headers.get('X-Csrf-Token')
|
|
206
|
+
const body = request.jsonBody || request.formBody || {}
|
|
207
|
+
const bodyToken: string | undefined = body?._token ?? body?.csrf_token
|
|
208
|
+
|
|
209
|
+
const submitted = (typeof headerToken === 'string' && headerToken)
|
|
210
|
+
|| (typeof bodyToken === 'string' && bodyToken)
|
|
211
|
+
|| ''
|
|
212
|
+
|
|
213
|
+
const cookies = parseCookies(request)
|
|
214
|
+
const cookieToken = cookies[CSRF_COOKIE_NAME] || cookies['csrf-token'] || ''
|
|
215
|
+
|
|
216
|
+
if (!submitted || !cookieToken || !safeEqual(submitted, cookieToken)) {
|
|
217
|
+
// 419 is the convention Laravel popularized for "CSRF token
|
|
218
|
+
// mismatch" — it's not in the IANA list but most SPAs already
|
|
219
|
+
// know how to refresh on 419. We use 403 for the strict-correct
|
|
220
|
+
// status code instead (419 is non-standard).
|
|
221
|
+
throw new HttpError(403, 'CSRF token mismatch')
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { HttpError } from '@stacksjs/error-handling'
|
|
2
|
+
import { Middleware } from '@stacksjs/router'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Ensure Email Is Verified Middleware
|
|
6
|
+
*
|
|
7
|
+
* Checks that the authenticated user has a verified email address.
|
|
8
|
+
* Must be used after the 'auth' middleware.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* route.get('/dashboard', 'DashboardAction').middleware('auth').middleware('verified')
|
|
12
|
+
*/
|
|
13
|
+
export default new Middleware({
|
|
14
|
+
name: 'verified',
|
|
15
|
+
priority: 4,
|
|
16
|
+
|
|
17
|
+
async handle(request) {
|
|
18
|
+
const user = request.user || request._user || null
|
|
19
|
+
|
|
20
|
+
if (!user) {
|
|
21
|
+
throw new HttpError(401, 'Unauthenticated.')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!user.email_verified_at) {
|
|
25
|
+
throw new HttpError(403, 'Your email address is not verified.')
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { config } from '@stacksjs/config'
|
|
2
|
+
import { HttpError } from '@stacksjs/error-handling'
|
|
3
|
+
import { Middleware } from '@stacksjs/router'
|
|
4
|
+
|
|
5
|
+
export default new Middleware({
|
|
6
|
+
name: 'Env',
|
|
7
|
+
priority: 1,
|
|
8
|
+
async handle() {
|
|
9
|
+
// Get the current environment from config
|
|
10
|
+
const currentEnv = config.app.env || 'local'
|
|
11
|
+
|
|
12
|
+
// For now, we'll create separate middleware for each environment
|
|
13
|
+
// This can be extended later to support parameters
|
|
14
|
+
const allowedEnvs = ['local', 'dev', 'development', 'staging', 'prod', 'production']
|
|
15
|
+
|
|
16
|
+
// Check if current environment is in allowed environments
|
|
17
|
+
if (!allowedEnvs.includes(currentEnv)) {
|
|
18
|
+
throw new HttpError(
|
|
19
|
+
403,
|
|
20
|
+
`Access denied. This route is only available in specific environments. Current environment: ${currentEnv}`,
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { config } from '@stacksjs/config'
|
|
2
|
+
import { HttpError } from '@stacksjs/error-handling'
|
|
3
|
+
import { Middleware } from '@stacksjs/router'
|
|
4
|
+
|
|
5
|
+
export default new Middleware({
|
|
6
|
+
name: 'EnvDevelopment',
|
|
7
|
+
priority: 1,
|
|
8
|
+
async handle() {
|
|
9
|
+
const currentEnv = config.app.env || 'local'
|
|
10
|
+
|
|
11
|
+
// Only allow access in development environment
|
|
12
|
+
if (currentEnv !== 'development' && currentEnv !== 'dev') {
|
|
13
|
+
throw new HttpError(
|
|
14
|
+
403,
|
|
15
|
+
`Access denied. This route is only available in development environment. Current environment: ${currentEnv}`,
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { config } from '@stacksjs/config'
|
|
2
|
+
import { HttpError } from '@stacksjs/error-handling'
|
|
3
|
+
import { Middleware } from '@stacksjs/router'
|
|
4
|
+
|
|
5
|
+
export default new Middleware({
|
|
6
|
+
name: 'EnvLocal',
|
|
7
|
+
priority: 1,
|
|
8
|
+
async handle() {
|
|
9
|
+
const currentEnv = config.app.env || 'local'
|
|
10
|
+
|
|
11
|
+
// Only allow access in local environment
|
|
12
|
+
if (currentEnv !== 'local') {
|
|
13
|
+
throw new HttpError(
|
|
14
|
+
403,
|
|
15
|
+
`Access denied. This route is only available in local environment. Current environment: ${currentEnv}`,
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { config } from '@stacksjs/config'
|
|
2
|
+
import { HttpError } from '@stacksjs/error-handling'
|
|
3
|
+
import { Middleware } from '@stacksjs/router'
|
|
4
|
+
|
|
5
|
+
export default new Middleware({
|
|
6
|
+
name: 'EnvNotDevelopment',
|
|
7
|
+
priority: 1,
|
|
8
|
+
async handle() {
|
|
9
|
+
const currentEnv = config.app.env || 'local'
|
|
10
|
+
|
|
11
|
+
// Block access in development environment (allow all others)
|
|
12
|
+
if (currentEnv === 'development' || currentEnv === 'dev') {
|
|
13
|
+
throw new HttpError(
|
|
14
|
+
403,
|
|
15
|
+
`Access denied. This route is not available in development environment. Current environment: ${currentEnv}`,
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { config } from '@stacksjs/config'
|
|
2
|
+
import { HttpError } from '@stacksjs/error-handling'
|
|
3
|
+
import { Middleware } from '@stacksjs/router'
|
|
4
|
+
|
|
5
|
+
export default new Middleware({
|
|
6
|
+
name: 'EnvNotLocal',
|
|
7
|
+
priority: 1,
|
|
8
|
+
async handle() {
|
|
9
|
+
const currentEnv = config.app.env || 'local'
|
|
10
|
+
|
|
11
|
+
// Block access in local environment (allow all others)
|
|
12
|
+
if (currentEnv === 'local') {
|
|
13
|
+
throw new HttpError(
|
|
14
|
+
403,
|
|
15
|
+
`Access denied. This route is not available in local environment. Current environment: ${currentEnv}`,
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
})
|