@stacksjs/defaults 0.70.354 → 0.70.356

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.
Files changed (214) hide show
  1. package/ai/skills/stacks-browse/SKILL.md +17 -2
  2. package/ai/skills/stacks-browse/scripts/browse.ts +207 -6
  3. package/ai/skills/stacks-email/SKILL.md +73 -16
  4. package/app/Actions/ContactAction.ts +2 -2
  5. package/app/Actions/Dashboard/Analytics/EventAnalyticsAction.ts +2 -3
  6. package/app/Actions/Dashboard/Analytics/MarketingAnalyticsAction.ts +2 -3
  7. package/app/Actions/Dashboard/Analytics/SalesAnalyticsAction.ts +2 -3
  8. package/app/Actions/Dashboard/Analytics/WebAnalyticsAction.ts +9 -3
  9. package/app/Actions/Dashboard/Auth/MeAction.ts +12 -14
  10. package/app/Actions/Dashboard/Buddy/BuddyChatAction.ts +2 -3
  11. package/app/Actions/Dashboard/Ci/RepoRunJobsAction.ts +10 -15
  12. package/app/Actions/Dashboard/Ci/RepoRunsAction.ts +12 -14
  13. package/app/Actions/Dashboard/Ci/RunnerHistoryAction.ts +11 -10
  14. package/app/Actions/Dashboard/Ci/StatusAction.ts +2 -11
  15. package/app/Actions/Dashboard/Cloud/CloudIndexAction.ts +8 -1
  16. package/app/Actions/Dashboard/Cloud/ServerlessIndexAction.ts +15 -8
  17. package/app/Actions/Dashboard/Commerce/CommerceCategoriesAction.ts +2 -4
  18. package/app/Actions/Dashboard/Commerce/CommerceCouponsAction.ts +2 -4
  19. package/app/Actions/Dashboard/Commerce/CommerceCustomersAction.ts +2 -4
  20. package/app/Actions/Dashboard/Commerce/CommerceDashboardAction.ts +2 -4
  21. package/app/Actions/Dashboard/Commerce/CommerceDeliveryAction.ts +2 -4
  22. package/app/Actions/Dashboard/Commerce/CommerceGiftCardsAction.ts +2 -4
  23. package/app/Actions/Dashboard/Commerce/CommerceOrdersAction.ts +2 -4
  24. package/app/Actions/Dashboard/Commerce/CommercePaymentsAction.ts +2 -4
  25. package/app/Actions/Dashboard/Commerce/CommercePosAction.ts +2 -4
  26. package/app/Actions/Dashboard/Commerce/CommercePosCheckoutAction.ts +76 -61
  27. package/app/Actions/Dashboard/Commerce/CommercePrintDevicesAction.ts +2 -4
  28. package/app/Actions/Dashboard/Commerce/CommercePrintLogsAction.ts +4 -7
  29. package/app/Actions/Dashboard/Commerce/CommerceProductDetailAction.ts +4 -4
  30. package/app/Actions/Dashboard/Commerce/CommerceProductsAction.ts +2 -4
  31. package/app/Actions/Dashboard/Commerce/CommerceTaxesAction.ts +2 -4
  32. package/app/Actions/Dashboard/Commerce/DeliveryRouteIndexAction.ts +2 -4
  33. package/app/Actions/Dashboard/Commerce/DigitalDeliveryIndexAction.ts +2 -4
  34. package/app/Actions/Dashboard/Commerce/DriverIndexAction.ts +2 -4
  35. package/app/Actions/Dashboard/Commerce/LicenseKeyIndexAction.ts +2 -4
  36. package/app/Actions/Dashboard/Commerce/LicenseKeyOptionsAction.ts +2 -4
  37. package/app/Actions/Dashboard/Commerce/ManufacturerIndexAction.ts +2 -4
  38. package/app/Actions/Dashboard/Commerce/PaymentRefundAction.ts +1 -0
  39. package/app/Actions/Dashboard/Commerce/ProductUnitDefaultAction.ts +1 -0
  40. package/app/Actions/Dashboard/Commerce/ProductUnitIndexAction.ts +2 -4
  41. package/app/Actions/Dashboard/Commerce/ProductVariantIndexAction.ts +2 -4
  42. package/app/Actions/Dashboard/Commerce/ProductWaitlistIndexAction.ts +2 -4
  43. package/app/Actions/Dashboard/Commerce/RestaurantWaitlistIndexAction.ts +2 -4
  44. package/app/Actions/Dashboard/Commerce/ReviewIndexAction.ts +2 -4
  45. package/app/Actions/Dashboard/Commerce/ShippingMethodIndexAction.ts +2 -4
  46. package/app/Actions/Dashboard/Commerce/ShippingRateIndexAction.ts +2 -4
  47. package/app/Actions/Dashboard/Commerce/ShippingZoneIndexAction.ts +2 -4
  48. package/app/Actions/Dashboard/Commerce/TaxRateDefaultAction.ts +1 -0
  49. package/app/Actions/Dashboard/DashboardActivityAction.ts +2 -5
  50. package/app/Actions/Dashboard/Data/ActivityIndexAction.ts +2 -5
  51. package/app/Actions/Dashboard/Data/SubscriberIndexAction.ts +2 -5
  52. package/app/Actions/Dashboard/Data/TeamIndexAction.ts +2 -5
  53. package/app/Actions/Dashboard/Data/UserIndexAction.ts +2 -5
  54. package/app/Actions/Dashboard/Deployments/CreateDeployment.ts +26 -21
  55. package/app/Actions/Dashboard/Deployments/GetAverageDeploymentTime.ts +8 -2
  56. package/app/Actions/Dashboard/Deployments/GetDeployScript.ts +3 -6
  57. package/app/Actions/Dashboard/Deployments/GetDeployment.ts +5 -5
  58. package/app/Actions/Dashboard/Deployments/GetDeploymentCount.ts +7 -1
  59. package/app/Actions/Dashboard/Deployments/GetDeploymentLiveTerminalOutput.ts +10 -9
  60. package/app/Actions/Dashboard/Deployments/GetDeployments.ts +2 -4
  61. package/app/Actions/Dashboard/Deployments/GetRecentDeployments.ts +8 -2
  62. package/app/Actions/Dashboard/Deployments/UpdateDeployScript.ts +21 -7
  63. package/app/Actions/Dashboard/Email/CapturedMailIndexAction.ts +2 -4
  64. package/app/Actions/Dashboard/Email/CapturedMailShowAction.ts +3 -4
  65. package/app/Actions/Dashboard/Email/InboxActivityAction.ts +28 -16
  66. package/app/Actions/Dashboard/Email/InboxAttachmentDownloadAction.ts +45 -0
  67. package/app/Actions/Dashboard/Email/InboxDeleteAction.ts +11 -6
  68. package/app/Actions/Dashboard/Email/InboxIndexAction.ts +5 -7
  69. package/app/Actions/Dashboard/Email/InboxMarkReadAction.ts +11 -6
  70. package/app/Actions/Dashboard/Email/InboxMarkUnreadAction.ts +11 -6
  71. package/app/Actions/Dashboard/Email/InboxPreferenceShowAction.ts +10 -4
  72. package/app/Actions/Dashboard/Email/InboxPreferenceUpdateAction.ts +8 -9
  73. package/app/Actions/Dashboard/Email/InboxShowAction.ts +7 -7
  74. package/app/Actions/Dashboard/Email/InboxStatsAction.ts +5 -7
  75. package/app/Actions/Dashboard/Email/inbox-request.ts +33 -0
  76. package/app/Actions/Dashboard/Infrastructure/InsightsAction.ts +15 -14
  77. package/app/Actions/Dashboard/Infrastructure/LogIndexAction.ts +4 -11
  78. package/app/Actions/Dashboard/Infrastructure/ServerShowAction.ts +3 -6
  79. package/app/Actions/Dashboard/Jobs/JobIndexAction.ts +9 -18
  80. package/app/Actions/Dashboard/Jobs/JobRetryAction.ts +4 -3
  81. package/app/Actions/Dashboard/Jobs/JobShowAction.ts +16 -11
  82. package/app/Actions/Dashboard/Jobs/JobStatsAction.ts +2 -4
  83. package/app/Actions/Dashboard/Kanban/BoardDestroyAction.ts +7 -8
  84. package/app/Actions/Dashboard/Kanban/BoardShowAction.ts +11 -19
  85. package/app/Actions/Dashboard/Kanban/BoardStoreAction.ts +6 -6
  86. package/app/Actions/Dashboard/Kanban/BoardUpdateAction.ts +7 -8
  87. package/app/Actions/Dashboard/Kanban/BoardsIndexAction.ts +3 -5
  88. package/app/Actions/Dashboard/Kanban/BoardsReorderAction.ts +6 -6
  89. package/app/Actions/Dashboard/Kanban/CardAssigneesSyncAction.ts +13 -12
  90. package/app/Actions/Dashboard/Kanban/CardCommentDestroyAction.ts +12 -15
  91. package/app/Actions/Dashboard/Kanban/CardCommentStoreAction.ts +21 -46
  92. package/app/Actions/Dashboard/Kanban/CardCommentUpdateAction.ts +43 -0
  93. package/app/Actions/Dashboard/Kanban/CardDestroyAction.ts +7 -8
  94. package/app/Actions/Dashboard/Kanban/CardLabelsSyncAction.ts +8 -9
  95. package/app/Actions/Dashboard/Kanban/CardShowAction.ts +6 -7
  96. package/app/Actions/Dashboard/Kanban/CardStoreAction.ts +9 -9
  97. package/app/Actions/Dashboard/Kanban/CardUpdateAction.ts +7 -8
  98. package/app/Actions/Dashboard/Kanban/CardsReorderAction.ts +6 -6
  99. package/app/Actions/Dashboard/Kanban/ColumnDestroyAction.ts +7 -8
  100. package/app/Actions/Dashboard/Kanban/ColumnStoreAction.ts +6 -6
  101. package/app/Actions/Dashboard/Kanban/ColumnUpdateAction.ts +7 -8
  102. package/app/Actions/Dashboard/Kanban/ColumnsReorderAction.ts +6 -6
  103. package/app/Actions/Dashboard/Kanban/LabelDestroyAction.ts +6 -7
  104. package/app/Actions/Dashboard/Kanban/LabelStoreAction.ts +6 -6
  105. package/app/Actions/Dashboard/Kanban/LabelUpdateAction.ts +7 -8
  106. package/app/Actions/Dashboard/Kanban/UsersListAction.ts +3 -4
  107. package/app/Actions/Dashboard/Kanban/kanban-comment.ts +14 -0
  108. package/app/Actions/Dashboard/Kanban/kanban-model.test.ts +36 -0
  109. package/app/Actions/Dashboard/Kanban/kanban-model.ts +14 -7
  110. package/app/Actions/Dashboard/Kanban/kanban-response.ts +5 -0
  111. package/app/Actions/Dashboard/Library/CreateComponent.ts +15 -6
  112. package/app/Actions/Dashboard/Library/CreateFunction.ts +15 -6
  113. package/app/Actions/Dashboard/Library/DependencyIndexAction.ts +88 -77
  114. package/app/Actions/Dashboard/Library/GetAverageReleaseTime.ts +21 -15
  115. package/app/Actions/Dashboard/Library/GetComponents.ts +14 -7
  116. package/app/Actions/Dashboard/Library/GetDownloadCount.ts +14 -5
  117. package/app/Actions/Dashboard/Library/GetFunctions.ts +12 -5
  118. package/app/Actions/Dashboard/Library/GetReleaseCount.ts +14 -5
  119. package/app/Actions/Dashboard/Library/GetReleases.ts +17 -10
  120. package/app/Actions/Dashboard/Library/PackageIndexAction.ts +14 -7
  121. package/app/Actions/Dashboard/Models/ModelDestroyAction.ts +11 -8
  122. package/app/Actions/Dashboard/Models/ModelShowAction.ts +97 -192
  123. package/app/Actions/Dashboard/Models/ModelStoreAction.ts +14 -12
  124. package/app/Actions/Dashboard/Models/ModelUpdateAction.ts +18 -16
  125. package/app/Actions/Dashboard/Models/ModelsIndexAction.ts +50 -19
  126. package/app/Actions/Dashboard/Models/model-api.ts +27 -0
  127. package/app/Actions/Dashboard/Models/model-write.ts +22 -15
  128. package/app/Actions/Dashboard/Notifications/NotificationDeliveryHistoryAction.ts +49 -40
  129. package/app/Actions/Dashboard/Notifications/NotificationDeliveryIndexAction.ts +20 -10
  130. package/app/Actions/Dashboard/Notifications/NotificationDeliveryOverviewAction.ts +96 -84
  131. package/app/Actions/Dashboard/Notifications/NotificationDeliveryRetryAction.ts +34 -21
  132. package/app/Actions/Dashboard/Queries/QueryDashboardAction.ts +2 -4
  133. package/app/Actions/Dashboard/Queries/QueryShowAction.ts +3 -3
  134. package/app/Actions/Dashboard/Queue/QueueStatsAction.ts +2 -4
  135. package/app/Actions/Dashboard/Queue/QueueWorkersAction.ts +2 -4
  136. package/app/Actions/Dashboard/Rbac/PermissionDestroyAction.ts +6 -6
  137. package/app/Actions/Dashboard/Rbac/PermissionStoreAction.ts +5 -4
  138. package/app/Actions/Dashboard/Rbac/PermissionsIndexAction.ts +2 -3
  139. package/app/Actions/Dashboard/Rbac/RoleDestroyAction.ts +6 -6
  140. package/app/Actions/Dashboard/Rbac/RolePermissionsShowAction.ts +6 -6
  141. package/app/Actions/Dashboard/Rbac/RolePermissionsSyncAction.ts +6 -9
  142. package/app/Actions/Dashboard/Rbac/RoleStoreAction.ts +5 -4
  143. package/app/Actions/Dashboard/Rbac/RolesIndexAction.ts +4 -6
  144. package/app/Actions/Dashboard/Rbac/UserRolesShowAction.ts +6 -7
  145. package/app/Actions/Dashboard/Rbac/UserRolesSyncAction.ts +6 -13
  146. package/app/Actions/Dashboard/Rbac/UsersListAction.ts +2 -3
  147. package/app/Actions/Dashboard/Rbac/rbac-response.ts +10 -0
  148. package/app/Actions/Dashboard/Realtime/RealtimeStatsAction.ts +2 -4
  149. package/app/Actions/Dashboard/Search/GlobalSearchAction.ts +69 -55
  150. package/app/Actions/Dashboard/Teams/TeamInvitationDestroyAction.ts +34 -16
  151. package/app/Actions/Dashboard/Teams/TeamInvitationResendAction.ts +52 -29
  152. package/app/Actions/Dashboard/Teams/TeamInviteAction.ts +104 -73
  153. package/app/Actions/Dashboard/Teams/TeamMemberDestroyAction.ts +36 -17
  154. package/app/Actions/Dashboard/Teams/TeamMemberUpdateAction.ts +41 -28
  155. package/app/Actions/Dashboard/Teams/TeamPeopleIndexAction.ts +71 -58
  156. package/app/Actions/Dashboard/Teams/team-invitation-delivery.ts +29 -7
  157. package/app/Actions/Dashboard/Teams/team-member-count.ts +4 -2
  158. package/app/Actions/Dashboard/Teams/team-records.test.ts +2 -0
  159. package/app/Actions/Dashboard/Teams/team-records.ts +2 -0
  160. package/app/Actions/Dashboard/dashboard-request.ts +13 -0
  161. package/app/Actions/Dashboard/dashboard-response.ts +16 -0
  162. package/app/Actions/SendWelcomeEmail.ts +1 -1
  163. package/app/Actions/Teams/AcceptInvitationAction.ts +74 -51
  164. package/app/Actions/Teams/ShowInvitationAction.ts +21 -14
  165. package/app/Actions/Teams/team-response.ts +23 -0
  166. package/app/Mail/OrderConfirmation.ts +1 -1
  167. package/app/Mail/SubscriptionConfirmation.ts +1 -1
  168. package/app/Mail/TeamInvitation.ts +1 -1
  169. package/app/Models/CardComment.ts +3 -4
  170. package/app/Models/EmailIdempotency.ts +64 -0
  171. package/app/Models/EmailSuppression.ts +52 -0
  172. package/app/Models/EmailWebhookEvent.ts +51 -0
  173. package/app/Models/TeamInvitation.ts +13 -0
  174. package/functions/dashboard-api.ts +51 -10
  175. package/functions/data.ts +0 -1
  176. package/functions/inbox.test.ts +2 -0
  177. package/functions/inbox.ts +27 -2
  178. package/functions/jobs.ts +0 -1
  179. package/functions/public-application-url.test.ts +13 -0
  180. package/functions/public-application-url.ts +27 -0
  181. package/ide/vscode/package.json +1 -1
  182. package/package.json +1 -1
  183. package/resources/components/Dashboard/Auth/AccessTokens.stx +15 -0
  184. package/resources/components/Dashboard/Ci/CiDashboard.stx +24 -6
  185. package/resources/components/Dashboard/Commerce/CommerceTaxDialog.stx +1 -1
  186. package/resources/components/Dashboard/Commerce/ProductWaitlistDashboard.stx +8 -7
  187. package/resources/components/Dashboard/Commerce/RestaurantWaitlistDashboard.stx +8 -7
  188. package/resources/components/Dashboard/Content/BlogDashboard.stx +14 -2
  189. package/resources/components/Dashboard/Content/PostsDashboard.stx +7 -1
  190. package/resources/components/Dashboard/Data/DataRecords.stx +0 -4
  191. package/resources/components/Dashboard/Deployments/DeploymentDetail.stx +16 -2
  192. package/resources/components/Dashboard/Email/EmailNavigation.stx +5 -5
  193. package/resources/components/Dashboard/Email/EmailSidebar.stx +1 -1
  194. package/resources/components/Dashboard/Email/InboxAttachmentList.stx +53 -0
  195. package/resources/components/Dashboard/Email/InboxDashboard.stx +5 -4
  196. package/resources/components/Dashboard/Email/InboxMessageDetail.stx +6 -4
  197. package/resources/components/Dashboard/Jobs/JobDetail.stx +3 -2
  198. package/resources/components/Dashboard/Kanban/KanbanBoardDashboard.stx +63 -6
  199. package/resources/components/Dashboard/Kanban/KanbanBoardsDashboard.stx +3 -6
  200. package/resources/components/Dashboard/Management/PermissionsDashboard.stx +4 -4
  201. package/resources/components/Dashboard/Models/ModelRecordsDashboard.stx +7 -13
  202. package/resources/components/Dashboard/Models/ModelsOverview.stx +1 -1
  203. package/resources/components/Dashboard/NotFoundDashboard.stx +2 -5
  204. package/resources/components/Dashboard/Queries/QueryDashboard.stx +6 -2
  205. package/resources/components/Dashboard/Settings/AppearanceSettingsDashboard.stx +4 -4
  206. package/resources/components/Dashboard/UI/EmptyState.stx +2 -2
  207. package/resources/components/Dashboard/UI/PageLayout.stx +3 -3
  208. package/resources/functions/dashboard/sidebar.ts +4 -4
  209. package/routes/dashboard-api.ts +8 -20
  210. package/views/dashboard/.discovered-models.json +54 -0
  211. package/views/dashboard/layouts/default.stx +19 -16
  212. package/views/dashboard/stores/auth.ts +15 -14
  213. package/views/dashboard/stores/ci.ts +13 -7
  214. package/views/dashboard/stores/kanban.ts +48 -11
@@ -1,6 +1,7 @@
1
1
  import { Action } from '@stacksjs/actions'
2
2
  import { Activity } from '@stacksjs/orm'
3
3
  import { safeGet } from '../../../../resources/functions/dashboard/data'
4
+ import { dashboardOperationalError } from '../dashboard-response'
4
5
  import { dateValue, daysAgoIso, numberValue, textValue } from './data-records'
5
6
 
6
7
  export default new Action({
@@ -37,11 +38,7 @@ export default new Action({
37
38
  }
38
39
  }
39
40
  catch (error) {
40
- return {
41
- activities: [],
42
- stats: { total: 0, last24Hours: 0, last7Days: 0 },
43
- error: error instanceof Error ? error.message : 'Activity could not be loaded.',
44
- }
41
+ return dashboardOperationalError(error, 'Activity could not be loaded.', 'ActivityIndexAction')
45
42
  }
46
43
  },
47
44
  })
@@ -1,6 +1,7 @@
1
1
  import { Action } from '@stacksjs/actions'
2
2
  import { Subscriber } from '@stacksjs/orm'
3
3
  import { safeGet } from '../../../../resources/functions/dashboard/data'
4
+ import { dashboardOperationalError } from '../dashboard-response'
4
5
  import { dateValue, daysAgoIso, numberValue, textValue } from './data-records'
5
6
 
6
7
  export default new Action({
@@ -38,11 +39,7 @@ export default new Action({
38
39
  }
39
40
  }
40
41
  catch (error) {
41
- return {
42
- subscribers: [],
43
- stats: { total: 0, subscribed: 0, unsubscribed: 0, newThisMonth: 0 },
44
- error: error instanceof Error ? error.message : 'Subscribers could not be loaded.',
45
- }
42
+ return dashboardOperationalError(error, 'Subscribers could not be loaded.', 'SubscriberIndexAction')
46
43
  }
47
44
  },
48
45
  })
@@ -1,6 +1,7 @@
1
1
  import { Action } from '@stacksjs/actions'
2
2
  import { Team } from '@stacksjs/orm'
3
3
  import { safeGet } from '../../../../resources/functions/dashboard/data'
4
+ import { dashboardOperationalError } from '../dashboard-response'
4
5
  import { dateValue, daysAgoIso, numberValue, textValue } from './data-records'
5
6
 
6
7
  export default new Action({
@@ -39,11 +40,7 @@ export default new Action({
39
40
  }
40
41
  }
41
42
  catch (error) {
42
- return {
43
- teams: [],
44
- stats: { total: 0, active: 0, members: 0, newThisMonth: 0 },
45
- error: error instanceof Error ? error.message : 'Teams could not be loaded.',
46
- }
43
+ return dashboardOperationalError(error, 'Teams could not be loaded.', 'TeamIndexAction')
47
44
  }
48
45
  },
49
46
  })
@@ -1,6 +1,7 @@
1
1
  import { Action } from '@stacksjs/actions'
2
2
  import { User } from '@stacksjs/orm'
3
3
  import { safeGet } from '../../../../resources/functions/dashboard/data'
4
+ import { dashboardOperationalError } from '../dashboard-response'
4
5
  import { dateValue, daysAgoIso, numberValue, textValue } from './data-records'
5
6
 
6
7
  export default new Action({
@@ -36,11 +37,7 @@ export default new Action({
36
37
  }
37
38
  }
38
39
  catch (error) {
39
- return {
40
- users: [],
41
- stats: { total: 0, newThisWeek: 0, verified: 0 },
42
- error: error instanceof Error ? error.message : 'Users could not be loaded.',
43
- }
40
+ return dashboardOperationalError(error, 'Users could not be loaded.', 'UserIndexAction')
44
41
  }
45
42
  },
46
43
  })
@@ -1,8 +1,10 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
1
2
  import { Action } from '@stacksjs/actions'
2
3
  import { join } from 'node:path'
3
4
  import process from 'node:process'
4
5
  import { Deployment } from '@stacksjs/orm'
5
- import { request, response } from '@stacksjs/router'
6
+ import { response } from '@stacksjs/router'
7
+ import { dashboardOperationalError, dashboardOperationalIssue } from '../dashboard-response'
6
8
  import { booleanValue, deploymentCommandArgs } from './deployment-input'
7
9
 
8
10
  function gitValue(args: string[]): string {
@@ -22,7 +24,7 @@ export default new Action({
22
24
  method: 'POST',
23
25
  apiResponse: true,
24
26
 
25
- async handle() {
27
+ async handle(request: RequestInstance) {
26
28
  const dryRun = booleanValue(request.get('dryRun') || request.get('dry_run'))
27
29
  const confirmed = booleanValue(request.get('confirmed'))
28
30
  if (!dryRun && !confirmed) {
@@ -62,10 +64,7 @@ export default new Action({
62
64
  })
63
65
  }
64
66
  catch (error) {
65
- return response.json({
66
- success: false,
67
- message: error instanceof Error ? error.message : 'Deployment record could not be created.',
68
- }, { status: 500 })
67
+ return dashboardOperationalError(error, 'Deployment record could not be created.', 'CreateDeployment.record', 500)
69
68
  }
70
69
  }
71
70
 
@@ -80,27 +79,33 @@ export default new Action({
80
79
  }
81
80
  catch (error) {
82
81
  if (deployment) {
83
- await deployment.update({
84
- status: 'failed',
85
- duration: 0,
86
- errorLog: error instanceof Error ? error.message : String(error),
87
- })
82
+ try {
83
+ await deployment.update({
84
+ status: 'failed',
85
+ duration: 0,
86
+ errorLog: 'Deployment process could not be started.',
87
+ })
88
+ }
89
+ catch (updateError) {
90
+ dashboardOperationalIssue(updateError, 'Deployment failure status could not be recorded.', 'CreateDeployment.failureRecord')
91
+ }
88
92
  }
89
- return response.json({
90
- success: false,
91
- message: error instanceof Error ? error.message : 'Deployment process could not be started.',
92
- }, { status: 500 })
93
+ return dashboardOperationalError(error, 'Deployment process could not be started.', 'CreateDeployment.process', 500)
93
94
  }
94
95
 
95
96
  if (deployment) {
96
97
  const record = deployment
97
- void child.exited.then(async (exitCode) => {
98
- await record.update({
99
- status: exitCode === 0 ? 'success' : 'failed',
100
- duration: Math.max(0, Math.round((Date.now() - startedAt) / 1000)),
101
- errorLog: exitCode === 0 ? '' : `buddy deploy exited with code ${exitCode}`,
98
+ void child.exited
99
+ .then(async (exitCode) => {
100
+ await record.update({
101
+ status: exitCode === 0 ? 'success' : 'failed',
102
+ duration: Math.max(0, Math.round((Date.now() - startedAt) / 1000)),
103
+ errorLog: exitCode === 0 ? '' : `buddy deploy exited with code ${exitCode}`,
104
+ })
105
+ })
106
+ .catch((error) => {
107
+ dashboardOperationalIssue(error, 'Deployment completion could not be recorded.', 'CreateDeployment.completionRecord')
102
108
  })
103
- })
104
109
  }
105
110
 
106
111
  return {
@@ -1,5 +1,6 @@
1
1
  import { Action } from '@stacksjs/actions'
2
2
  import { Deployment } from '@stacksjs/orm'
3
+ import { dashboardOperationalError } from '../dashboard-response'
3
4
  import { averageRecordedDuration } from './deployment-input'
4
5
 
5
6
  export default new Action({
@@ -9,7 +10,12 @@ export default new Action({
9
10
  apiResponse: true,
10
11
 
11
12
  async handle() {
12
- const deployments = await Deployment.all()
13
- return { average_seconds: averageRecordedDuration(deployments) }
13
+ try {
14
+ const deployments = await Deployment.all()
15
+ return { average_seconds: averageRecordedDuration(deployments) }
16
+ }
17
+ catch (error) {
18
+ return dashboardOperationalError(error, 'Average deployment time could not be loaded.', 'GetAverageDeploymentTime')
19
+ }
14
20
  },
15
21
  })
@@ -2,7 +2,7 @@ import { Action } from '@stacksjs/actions'
2
2
  import { readFile } from 'node:fs/promises'
3
3
  import { join } from 'node:path'
4
4
  import process from 'node:process'
5
- import { response } from '@stacksjs/router'
5
+ import { dashboardOperationalError } from '../dashboard-response'
6
6
 
7
7
  export default new Action({
8
8
  name: 'GetDeployScript',
@@ -20,11 +20,8 @@ export default new Action({
20
20
  }
21
21
  }
22
22
  catch (error) {
23
- if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
24
- return response.json({
25
- message: error instanceof Error ? error.message : 'Deploy script could not be read.',
26
- }, 500)
27
- }
23
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT')
24
+ return dashboardOperationalError(error, 'Deploy script could not be read.', 'GetDeployScript', 500)
28
25
 
29
26
  return {
30
27
  path: 'cloud/deploy-script.ts',
@@ -1,6 +1,8 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
1
2
  import { Action } from '@stacksjs/actions'
2
3
  import { Deployment } from '@stacksjs/orm'
3
4
  import { response } from '@stacksjs/router'
5
+ import { dashboardOperationalError } from '../dashboard-response'
4
6
 
5
7
  export default new Action({
6
8
  name: 'GetDeployment',
@@ -10,8 +12,8 @@ export default new Action({
10
12
 
11
13
  async handle(request: RequestInstance) {
12
14
  const id = Number(request.getParam('id'))
13
- if (!Number.isFinite(id) || id <= 0)
14
- return response.json({ message: 'Deployment id must be a positive number.' }, 422)
15
+ if (!Number.isSafeInteger(id) || id <= 0)
16
+ return response.json({ message: 'Deployment id must be a positive integer.' }, 400)
15
17
 
16
18
  try {
17
19
  const deployment = await Deployment.find(id)
@@ -23,9 +25,7 @@ export default new Action({
23
25
  }
24
26
  }
25
27
  catch (error) {
26
- return response.json({
27
- message: error instanceof Error ? error.message : 'Deployment could not be loaded.',
28
- }, 503)
28
+ return dashboardOperationalError(error, 'Deployment could not be loaded.', 'GetDeployment')
29
29
  }
30
30
  },
31
31
  })
@@ -1,5 +1,6 @@
1
1
  import { Action } from '@stacksjs/actions'
2
2
  import { Deployment } from '@stacksjs/orm'
3
+ import { dashboardOperationalError } from '../dashboard-response'
3
4
 
4
5
  export default new Action({
5
6
  name: 'GetDeploymentCount',
@@ -8,6 +9,11 @@ export default new Action({
8
9
  apiResponse: true,
9
10
 
10
11
  async handle() {
11
- return await Deployment.count()
12
+ try {
13
+ return await Deployment.count()
14
+ }
15
+ catch (error) {
16
+ return dashboardOperationalError(error, 'Deployment count could not be loaded.', 'GetDeploymentCount')
17
+ }
12
18
  },
13
19
  })
@@ -1,9 +1,9 @@
1
1
  import { Action } from '@stacksjs/actions'
2
2
  import { readFile } from 'node:fs/promises'
3
- import { resolve } from 'node:path'
3
+ import { isAbsolute, relative, resolve, sep } from 'node:path'
4
4
  import process from 'node:process'
5
5
  import { logging } from '@stacksjs/config'
6
- import { response } from '@stacksjs/router'
6
+ import { dashboardOperationalError } from '../dashboard-response'
7
7
  import { tailLines } from './deployment-input'
8
8
 
9
9
  export default new Action({
@@ -15,22 +15,23 @@ export default new Action({
15
15
  async handle() {
16
16
  const configuredPath = logging.deploymentsPath || 'storage/logs/deployments.log'
17
17
  const filePath = resolve(process.cwd(), configuredPath)
18
+ const relativePath = relative(process.cwd(), filePath)
19
+ const publicPath = relativePath && relativePath !== '..' && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath)
20
+ ? relativePath
21
+ : 'deployments.log'
18
22
  try {
19
23
  return {
20
- path: configuredPath,
24
+ path: publicPath,
21
25
  output: tailLines(await readFile(filePath, 'utf8')),
22
26
  exists: true,
23
27
  }
24
28
  }
25
29
  catch (error) {
26
- if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
27
- return response.json({
28
- message: error instanceof Error ? error.message : 'Deployment output could not be read.',
29
- }, 500)
30
- }
30
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT')
31
+ return dashboardOperationalError(error, 'Deployment output could not be read.', 'GetDeploymentLiveTerminalOutput', 500)
31
32
 
32
33
  return {
33
- path: configuredPath,
34
+ path: publicPath,
34
35
  output: '',
35
36
  exists: false,
36
37
  }
@@ -1,6 +1,6 @@
1
1
  import { Action } from '@stacksjs/actions'
2
2
  import { Deployment } from '@stacksjs/orm'
3
- import { response } from '@stacksjs/router'
3
+ import { dashboardOperationalError } from '../dashboard-response'
4
4
 
5
5
  export default new Action({
6
6
  name: 'GetDeployments',
@@ -17,9 +17,7 @@ export default new Action({
17
17
  }
18
18
  }
19
19
  catch (error) {
20
- return response.json({
21
- message: error instanceof Error ? error.message : 'Deployment records could not be loaded.',
22
- }, 503)
20
+ return dashboardOperationalError(error, 'Deployment records could not be loaded.', 'GetDeployments')
23
21
  }
24
22
  },
25
23
  })
@@ -1,5 +1,6 @@
1
1
  import { Action } from '@stacksjs/actions'
2
2
  import { Deployment } from '@stacksjs/orm'
3
+ import { dashboardOperationalError } from '../dashboard-response'
3
4
 
4
5
  export default new Action({
5
6
  name: 'GetRecentDeployments',
@@ -8,7 +9,12 @@ export default new Action({
8
9
  apiResponse: true,
9
10
 
10
11
  async handle() {
11
- const deployments = await Deployment.orderByDesc('created_at').limit(3).get()
12
- return deployments.map(deployment => deployment.toJSON())
12
+ try {
13
+ const deployments = await Deployment.orderByDesc('created_at').limit(3).get()
14
+ return deployments.map(deployment => deployment.toJSON())
15
+ }
16
+ catch (error) {
17
+ return dashboardOperationalError(error, 'Recent deployments could not be loaded.', 'GetRecentDeployments')
18
+ }
13
19
  },
14
20
  })
@@ -1,8 +1,11 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
2
+ import { randomUUIDv7 } from 'bun'
1
3
  import { Action } from '@stacksjs/actions'
2
- import { mkdir, rename, writeFile } from 'node:fs/promises'
4
+ import { mkdir, rename, rm, writeFile } from 'node:fs/promises'
3
5
  import { dirname, join } from 'node:path'
4
6
  import process from 'node:process'
5
- import { request, response } from '@stacksjs/router'
7
+ import { response } from '@stacksjs/router'
8
+ import { dashboardOperationalError, dashboardOperationalIssue } from '../dashboard-response'
6
9
 
7
10
  export default new Action({
8
11
  name: 'UpdateDeployScript',
@@ -10,7 +13,7 @@ export default new Action({
10
13
  method: 'PUT',
11
14
  apiResponse: true,
12
15
 
13
- async handle() {
16
+ async handle(request: RequestInstance) {
14
17
  const content = String(request.get('content') || '')
15
18
  if (!content.trim())
16
19
  return response.json({ success: false, message: 'Deploy script content is required.' }, { status: 422 })
@@ -28,10 +31,21 @@ export default new Action({
28
31
  }
29
32
 
30
33
  const filePath = join(process.cwd(), 'cloud', 'deploy-script.ts')
31
- const temporaryPath = `${filePath}.${process.pid}.tmp`
32
- await mkdir(dirname(filePath), { recursive: true })
33
- await writeFile(temporaryPath, content, { encoding: 'utf8', mode: 0o644 })
34
- await rename(temporaryPath, filePath)
34
+ const temporaryPath = `${filePath}.${randomUUIDv7()}.tmp`
35
+ try {
36
+ await mkdir(dirname(filePath), { recursive: true })
37
+ await writeFile(temporaryPath, content, { encoding: 'utf8', mode: 0o644 })
38
+ await rename(temporaryPath, filePath)
39
+ }
40
+ catch (error) {
41
+ try {
42
+ await rm(temporaryPath, { force: true })
43
+ }
44
+ catch (cleanupError) {
45
+ dashboardOperationalIssue(cleanupError, 'Temporary deploy script could not be removed.', 'UpdateDeployScript.cleanup')
46
+ }
47
+ return dashboardOperationalError(error, 'Deploy script could not be saved.', 'UpdateDeployScript', 500)
48
+ }
35
49
 
36
50
  return {
37
51
  success: true,
@@ -1,5 +1,5 @@
1
1
  import { Action } from '@stacksjs/actions'
2
- import { response } from '@stacksjs/router'
2
+ import { dashboardOperationalError } from '../dashboard-response'
3
3
  import { listCapturedMail } from './captured-mail'
4
4
 
5
5
  export default new Action({
@@ -19,9 +19,7 @@ export default new Action({
19
19
  }
20
20
  }
21
21
  catch (error) {
22
- return response.json({
23
- message: error instanceof Error ? error.message : 'Captured emails could not be loaded.',
24
- }, 503)
22
+ return dashboardOperationalError(error, 'Captured emails could not be loaded.', 'CapturedMailIndexAction')
25
23
  }
26
24
  },
27
25
  })
@@ -1,6 +1,7 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { response } from '@stacksjs/router'
4
+ import { dashboardOperationalError } from '../dashboard-response'
4
5
  import { showCapturedMail } from './captured-mail'
5
6
 
6
7
  export default new Action({
@@ -10,7 +11,7 @@ export default new Action({
10
11
  apiResponse: true,
11
12
 
12
13
  async handle(request: RequestInstance) {
13
- const id = request.params?.id
14
+ const id = request.getParam('id')
14
15
  if (typeof id !== 'string' || !id)
15
16
  return response.json({ message: 'Captured email id is required.' }, 422)
16
17
  if (!/^disk:[^/\\]+\.html$/.test(id) && !/^mem:\d+:\d+$/.test(id))
@@ -23,9 +24,7 @@ export default new Action({
23
24
  return { message }
24
25
  }
25
26
  catch (error) {
26
- return response.json({
27
- message: error instanceof Error ? error.message : 'Captured email could not be loaded.',
28
- }, 503)
27
+ return dashboardOperationalError(error, 'Captured email could not be loaded.', 'CapturedMailShowAction')
29
28
  }
30
29
  },
31
30
  })
@@ -2,8 +2,9 @@ import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { db } from '@stacksjs/database'
4
4
  import { emailSDK } from '@stacksjs/email'
5
- import { request as routerRequest, response } from '@stacksjs/router'
6
- import { defaultMailbox } from './mail-preference'
5
+ import { response } from '@stacksjs/router'
6
+ import { dashboardRequestValue } from '../dashboard-request'
7
+ import { dashboardMailbox, inboxActionError } from './inbox-request'
7
8
 
8
9
  const RANGES = ['day', 'week', 'month', 'year'] as const
9
10
  type ActivityRange = typeof RANGES[number]
@@ -34,9 +35,7 @@ interface Bucket {
34
35
  }
35
36
 
36
37
  function queryValue(request: RequestInstance, key: string): string {
37
- const query = ((routerRequest as any).query || {}) as Record<string, string | string[] | undefined>
38
- const value = query[key]
39
- return String((Array.isArray(value) ? value[0] : value) || request.get(key) || '').trim()
38
+ return dashboardRequestValue(request, key)
40
39
  }
41
40
 
42
41
  function startOfHour(date: Date): Date {
@@ -144,23 +143,36 @@ export default new Action({
144
143
  const range = RANGES.includes(requestedRange as ActivityRange)
145
144
  ? requestedRange as ActivityRange
146
145
  : 'week'
147
- const mailbox = queryValue(request, 'mailbox') || defaultMailbox()
146
+ let mailbox: string
147
+ try {
148
+ mailbox = dashboardMailbox(request)
149
+ }
150
+ catch (error) {
151
+ return inboxActionError(error, 'Inbox activity could not be loaded.')
152
+ }
148
153
  const buckets = buildBuckets(range, new Date())
149
154
  const periodStart = buckets[0]!.start
150
155
  const periodEnd = buckets.at(-1)!.end
151
156
  const periodLength = periodEnd.getTime() - periodStart.getTime()
152
157
  const previousStart = new Date(periodStart.getTime() - periodLength)
153
158
 
154
- const [inbox, deliveryRows] = await Promise.all([
155
- emailSDK.getInbox(mailbox, { limit: 1000 }),
156
- db
157
- .selectFrom('notification_deliveries')
158
- .select(['id', 'recipient', 'subject', 'status', 'sent_at', 'created_at'])
159
- .where('channel', '=', 'email')
160
- .where('created_at', '>=', previousStart.toISOString())
161
- .orderBy('created_at', 'desc')
162
- .execute() as Promise<DeliveryRow[]>,
163
- ])
159
+ let inbox
160
+ let deliveryRows: DeliveryRow[]
161
+ try {
162
+ [inbox, deliveryRows] = await Promise.all([
163
+ emailSDK.getInbox(mailbox, { limit: 1000 }),
164
+ db
165
+ .selectFrom('notification_deliveries')
166
+ .select(['id', 'recipient', 'subject', 'status', 'sent_at', 'created_at'])
167
+ .where('channel', '=', 'email')
168
+ .where('created_at', '>=', previousStart.toISOString())
169
+ .orderBy('created_at', 'desc')
170
+ .execute() as Promise<DeliveryRow[]>,
171
+ ])
172
+ }
173
+ catch (error) {
174
+ return inboxActionError(error, 'Inbox activity could not be loaded.')
175
+ }
164
176
 
165
177
  const currentInbound = inbox.filter(email => inPeriod(email.date, periodStart, periodEnd))
166
178
  const previousInbound = inbox.filter(email => inPeriod(email.date, previousStart, periodStart))
@@ -0,0 +1,45 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
2
+ import { Action } from '@stacksjs/actions'
3
+ import {
4
+ emailSDK,
5
+ inboxAttachmentContentDisposition,
6
+ inboxAttachmentContentType,
7
+ } from '@stacksjs/email'
8
+ import { response } from '@stacksjs/router'
9
+ import { dashboardMailbox, inboxActionError } from './inbox-request'
10
+
11
+ export default new Action({
12
+ name: 'InboxAttachmentDownloadAction',
13
+ description: 'Downloads one attachment from a guarded inbound email.',
14
+ method: 'GET',
15
+ apiResponse: false,
16
+
17
+ async handle(request: RequestInstance) {
18
+ const messageId = String(request.getParam('id') || '')
19
+ const attachmentId = String(request.getParam('attachmentId') || '')
20
+
21
+ if (!messageId || !attachmentId)
22
+ return response.json({ message: 'A message ID and attachment ID are required.' }, 422)
23
+
24
+ try {
25
+ const mailbox = dashboardMailbox(request)
26
+ const result = await emailSDK.getAttachment(mailbox, messageId, attachmentId)
27
+ if (!result)
28
+ return response.json({ message: 'Attachment not found.' }, 404)
29
+
30
+ return new Response(result.body as BodyInit, {
31
+ status: 200,
32
+ headers: {
33
+ 'Cache-Control': 'private, no-store',
34
+ 'Content-Disposition': inboxAttachmentContentDisposition(result.attachment.name),
35
+ 'Content-Length': String(result.body.byteLength),
36
+ 'Content-Type': inboxAttachmentContentType(result.contentType),
37
+ 'X-Content-Type-Options': 'nosniff',
38
+ },
39
+ })
40
+ }
41
+ catch (error) {
42
+ return inboxActionError(error, 'The attachment could not be downloaded.')
43
+ }
44
+ },
45
+ })
@@ -2,7 +2,7 @@ import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { emailSDK } from '@stacksjs/email'
4
4
  import { response } from '@stacksjs/router'
5
- import { defaultMailbox } from './mail-preference'
5
+ import { dashboardMailbox, inboxActionError } from './inbox-request'
6
6
 
7
7
  export default new Action({
8
8
  name: 'InboxDeleteAction',
@@ -11,16 +11,21 @@ export default new Action({
11
11
  apiResponse: true,
12
12
 
13
13
  async handle(request: RequestInstance) {
14
- const mailbox = String(request.get('mailbox') || defaultMailbox())
15
14
  const messageId = String(request.getParam('id') || '')
16
15
 
17
16
  if (!messageId)
18
17
  return response.json({ message: 'A message ID is required.' }, 422)
19
18
 
20
- const success = await emailSDK.delete(mailbox, messageId)
21
- if (!success)
22
- return response.json({ message: 'Email not found or it could not be deleted.' }, 404)
19
+ try {
20
+ const mailbox = dashboardMailbox(request)
21
+ const success = await emailSDK.delete(mailbox, messageId)
22
+ if (!success)
23
+ return response.json({ message: 'Email not found or it could not be deleted.' }, 404)
23
24
 
24
- return response.json({ success: true, mailbox, messageId })
25
+ return response.json({ success: true, mailbox, messageId })
26
+ }
27
+ catch (error) {
28
+ return inboxActionError(error, 'The email could not be deleted.')
29
+ }
25
30
  },
26
31
  })
@@ -1,7 +1,7 @@
1
+ import type { RequestInstance } from '@stacksjs/types'
1
2
  import { Action } from '@stacksjs/actions'
2
3
  import { emailSDK } from '@stacksjs/email'
3
- import { response } from '@stacksjs/router'
4
- import { defaultMailbox } from './mail-preference'
4
+ import { dashboardMailbox, inboxActionError } from './inbox-request'
5
5
 
6
6
  export interface InboxItem {
7
7
  messageId: string
@@ -22,9 +22,9 @@ export default new Action({
22
22
  method: 'GET',
23
23
  apiResponse: true,
24
24
 
25
- async handle(request: any) {
25
+ async handle(request: RequestInstance) {
26
26
  try {
27
- const mailbox = request?.query?.mailbox || defaultMailbox()
27
+ const mailbox = dashboardMailbox(request)
28
28
  const emails = await emailSDK.getInbox(mailbox, { limit: 1000 })
29
29
 
30
30
  return {
@@ -34,9 +34,7 @@ export default new Action({
34
34
  }
35
35
  }
36
36
  catch (err) {
37
- return response.json({
38
- message: err instanceof Error ? err.message : 'Inbox messages could not be loaded.',
39
- }, 503)
37
+ return inboxActionError(err, 'Inbox messages could not be loaded.')
40
38
  }
41
39
  },
42
40
  })