@stacksjs/defaults 0.70.355 → 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 (155) hide show
  1. package/ai/skills/stacks-email/SKILL.md +38 -1
  2. package/app/Actions/ContactAction.ts +2 -2
  3. package/app/Actions/Dashboard/Analytics/EventAnalyticsAction.ts +2 -3
  4. package/app/Actions/Dashboard/Analytics/MarketingAnalyticsAction.ts +2 -3
  5. package/app/Actions/Dashboard/Analytics/SalesAnalyticsAction.ts +2 -3
  6. package/app/Actions/Dashboard/Analytics/WebAnalyticsAction.ts +9 -3
  7. package/app/Actions/Dashboard/Auth/MeAction.ts +4 -7
  8. package/app/Actions/Dashboard/Buddy/BuddyChatAction.ts +2 -3
  9. package/app/Actions/Dashboard/Cloud/CloudIndexAction.ts +8 -1
  10. package/app/Actions/Dashboard/Cloud/ServerlessIndexAction.ts +15 -8
  11. package/app/Actions/Dashboard/Commerce/CommerceCategoriesAction.ts +2 -4
  12. package/app/Actions/Dashboard/Commerce/CommerceCouponsAction.ts +2 -4
  13. package/app/Actions/Dashboard/Commerce/CommerceCustomersAction.ts +2 -4
  14. package/app/Actions/Dashboard/Commerce/CommerceDashboardAction.ts +2 -4
  15. package/app/Actions/Dashboard/Commerce/CommerceDeliveryAction.ts +2 -4
  16. package/app/Actions/Dashboard/Commerce/CommerceGiftCardsAction.ts +2 -4
  17. package/app/Actions/Dashboard/Commerce/CommerceOrdersAction.ts +2 -4
  18. package/app/Actions/Dashboard/Commerce/CommercePaymentsAction.ts +2 -4
  19. package/app/Actions/Dashboard/Commerce/CommercePosAction.ts +2 -4
  20. package/app/Actions/Dashboard/Commerce/CommercePosCheckoutAction.ts +75 -61
  21. package/app/Actions/Dashboard/Commerce/CommercePrintDevicesAction.ts +2 -4
  22. package/app/Actions/Dashboard/Commerce/CommercePrintLogsAction.ts +2 -4
  23. package/app/Actions/Dashboard/Commerce/CommerceProductDetailAction.ts +3 -4
  24. package/app/Actions/Dashboard/Commerce/CommerceProductsAction.ts +2 -4
  25. package/app/Actions/Dashboard/Commerce/CommerceTaxesAction.ts +2 -4
  26. package/app/Actions/Dashboard/Commerce/DeliveryRouteIndexAction.ts +2 -4
  27. package/app/Actions/Dashboard/Commerce/DigitalDeliveryIndexAction.ts +2 -4
  28. package/app/Actions/Dashboard/Commerce/DriverIndexAction.ts +2 -4
  29. package/app/Actions/Dashboard/Commerce/LicenseKeyIndexAction.ts +2 -4
  30. package/app/Actions/Dashboard/Commerce/LicenseKeyOptionsAction.ts +2 -4
  31. package/app/Actions/Dashboard/Commerce/ManufacturerIndexAction.ts +2 -4
  32. package/app/Actions/Dashboard/Commerce/ProductUnitIndexAction.ts +2 -4
  33. package/app/Actions/Dashboard/Commerce/ProductVariantIndexAction.ts +2 -4
  34. package/app/Actions/Dashboard/Commerce/ProductWaitlistIndexAction.ts +2 -4
  35. package/app/Actions/Dashboard/Commerce/RestaurantWaitlistIndexAction.ts +2 -4
  36. package/app/Actions/Dashboard/Commerce/ReviewIndexAction.ts +2 -4
  37. package/app/Actions/Dashboard/Commerce/ShippingMethodIndexAction.ts +2 -4
  38. package/app/Actions/Dashboard/Commerce/ShippingRateIndexAction.ts +2 -4
  39. package/app/Actions/Dashboard/Commerce/ShippingZoneIndexAction.ts +2 -4
  40. package/app/Actions/Dashboard/DashboardActivityAction.ts +2 -5
  41. package/app/Actions/Dashboard/Data/ActivityIndexAction.ts +2 -5
  42. package/app/Actions/Dashboard/Data/SubscriberIndexAction.ts +2 -5
  43. package/app/Actions/Dashboard/Data/TeamIndexAction.ts +2 -5
  44. package/app/Actions/Dashboard/Data/UserIndexAction.ts +2 -5
  45. package/app/Actions/Dashboard/Deployments/CreateDeployment.ts +23 -19
  46. package/app/Actions/Dashboard/Deployments/GetAverageDeploymentTime.ts +8 -2
  47. package/app/Actions/Dashboard/Deployments/GetDeployScript.ts +3 -6
  48. package/app/Actions/Dashboard/Deployments/GetDeployment.ts +4 -5
  49. package/app/Actions/Dashboard/Deployments/GetDeploymentCount.ts +7 -1
  50. package/app/Actions/Dashboard/Deployments/GetDeploymentLiveTerminalOutput.ts +10 -9
  51. package/app/Actions/Dashboard/Deployments/GetDeployments.ts +2 -4
  52. package/app/Actions/Dashboard/Deployments/GetRecentDeployments.ts +8 -2
  53. package/app/Actions/Dashboard/Deployments/UpdateDeployScript.ts +18 -5
  54. package/app/Actions/Dashboard/Email/CapturedMailIndexAction.ts +2 -4
  55. package/app/Actions/Dashboard/Email/CapturedMailShowAction.ts +3 -4
  56. package/app/Actions/Dashboard/Infrastructure/InsightsAction.ts +15 -14
  57. package/app/Actions/Dashboard/Infrastructure/LogIndexAction.ts +2 -8
  58. package/app/Actions/Dashboard/Jobs/JobIndexAction.ts +2 -4
  59. package/app/Actions/Dashboard/Jobs/JobShowAction.ts +13 -8
  60. package/app/Actions/Dashboard/Jobs/JobStatsAction.ts +2 -4
  61. package/app/Actions/Dashboard/Kanban/BoardDestroyAction.ts +2 -3
  62. package/app/Actions/Dashboard/Kanban/BoardShowAction.ts +2 -3
  63. package/app/Actions/Dashboard/Kanban/BoardStoreAction.ts +2 -3
  64. package/app/Actions/Dashboard/Kanban/BoardUpdateAction.ts +2 -3
  65. package/app/Actions/Dashboard/Kanban/BoardsIndexAction.ts +2 -3
  66. package/app/Actions/Dashboard/Kanban/BoardsReorderAction.ts +2 -3
  67. package/app/Actions/Dashboard/Kanban/CardAssigneesSyncAction.ts +2 -3
  68. package/app/Actions/Dashboard/Kanban/CardCommentDestroyAction.ts +2 -3
  69. package/app/Actions/Dashboard/Kanban/CardCommentStoreAction.ts +2 -3
  70. package/app/Actions/Dashboard/Kanban/CardCommentUpdateAction.ts +2 -3
  71. package/app/Actions/Dashboard/Kanban/CardDestroyAction.ts +2 -3
  72. package/app/Actions/Dashboard/Kanban/CardLabelsSyncAction.ts +2 -3
  73. package/app/Actions/Dashboard/Kanban/CardShowAction.ts +2 -3
  74. package/app/Actions/Dashboard/Kanban/CardStoreAction.ts +2 -3
  75. package/app/Actions/Dashboard/Kanban/CardUpdateAction.ts +2 -3
  76. package/app/Actions/Dashboard/Kanban/CardsReorderAction.ts +2 -3
  77. package/app/Actions/Dashboard/Kanban/ColumnDestroyAction.ts +2 -3
  78. package/app/Actions/Dashboard/Kanban/ColumnStoreAction.ts +2 -3
  79. package/app/Actions/Dashboard/Kanban/ColumnUpdateAction.ts +2 -3
  80. package/app/Actions/Dashboard/Kanban/ColumnsReorderAction.ts +2 -3
  81. package/app/Actions/Dashboard/Kanban/LabelDestroyAction.ts +2 -3
  82. package/app/Actions/Dashboard/Kanban/LabelStoreAction.ts +2 -3
  83. package/app/Actions/Dashboard/Kanban/LabelUpdateAction.ts +2 -3
  84. package/app/Actions/Dashboard/Kanban/UsersListAction.ts +2 -3
  85. package/app/Actions/Dashboard/Kanban/kanban-response.ts +5 -0
  86. package/app/Actions/Dashboard/Library/CreateComponent.ts +15 -6
  87. package/app/Actions/Dashboard/Library/CreateFunction.ts +15 -6
  88. package/app/Actions/Dashboard/Library/DependencyIndexAction.ts +88 -77
  89. package/app/Actions/Dashboard/Library/GetAverageReleaseTime.ts +21 -15
  90. package/app/Actions/Dashboard/Library/GetComponents.ts +14 -7
  91. package/app/Actions/Dashboard/Library/GetDownloadCount.ts +14 -5
  92. package/app/Actions/Dashboard/Library/GetFunctions.ts +12 -5
  93. package/app/Actions/Dashboard/Library/GetReleaseCount.ts +14 -5
  94. package/app/Actions/Dashboard/Library/GetReleases.ts +17 -10
  95. package/app/Actions/Dashboard/Library/PackageIndexAction.ts +14 -7
  96. package/app/Actions/Dashboard/Models/ModelDestroyAction.ts +7 -5
  97. package/app/Actions/Dashboard/Models/ModelShowAction.ts +82 -174
  98. package/app/Actions/Dashboard/Models/ModelStoreAction.ts +6 -4
  99. package/app/Actions/Dashboard/Models/ModelUpdateAction.ts +9 -7
  100. package/app/Actions/Dashboard/Models/ModelsIndexAction.ts +47 -17
  101. package/app/Actions/Dashboard/Models/model-write.ts +20 -5
  102. package/app/Actions/Dashboard/Notifications/NotificationDeliveryHistoryAction.ts +46 -36
  103. package/app/Actions/Dashboard/Notifications/NotificationDeliveryIndexAction.ts +14 -6
  104. package/app/Actions/Dashboard/Notifications/NotificationDeliveryOverviewAction.ts +96 -84
  105. package/app/Actions/Dashboard/Notifications/NotificationDeliveryRetryAction.ts +34 -21
  106. package/app/Actions/Dashboard/Queries/QueryDashboardAction.ts +2 -4
  107. package/app/Actions/Dashboard/Queries/QueryShowAction.ts +2 -3
  108. package/app/Actions/Dashboard/Queue/QueueStatsAction.ts +2 -4
  109. package/app/Actions/Dashboard/Queue/QueueWorkersAction.ts +2 -4
  110. package/app/Actions/Dashboard/Rbac/PermissionDestroyAction.ts +2 -2
  111. package/app/Actions/Dashboard/Rbac/PermissionStoreAction.ts +2 -2
  112. package/app/Actions/Dashboard/Rbac/PermissionsIndexAction.ts +2 -3
  113. package/app/Actions/Dashboard/Rbac/RoleDestroyAction.ts +2 -2
  114. package/app/Actions/Dashboard/Rbac/RolePermissionsShowAction.ts +2 -2
  115. package/app/Actions/Dashboard/Rbac/RolePermissionsSyncAction.ts +2 -6
  116. package/app/Actions/Dashboard/Rbac/RoleStoreAction.ts +2 -2
  117. package/app/Actions/Dashboard/Rbac/RolesIndexAction.ts +4 -6
  118. package/app/Actions/Dashboard/Rbac/UserRolesShowAction.ts +2 -2
  119. package/app/Actions/Dashboard/Rbac/UserRolesSyncAction.ts +2 -9
  120. package/app/Actions/Dashboard/Rbac/UsersListAction.ts +2 -3
  121. package/app/Actions/Dashboard/Rbac/rbac-response.ts +10 -0
  122. package/app/Actions/Dashboard/Realtime/RealtimeStatsAction.ts +2 -4
  123. package/app/Actions/Dashboard/Search/GlobalSearchAction.ts +68 -55
  124. package/app/Actions/Dashboard/Teams/TeamInvitationDestroyAction.ts +34 -16
  125. package/app/Actions/Dashboard/Teams/TeamInvitationResendAction.ts +52 -29
  126. package/app/Actions/Dashboard/Teams/TeamInviteAction.ts +102 -71
  127. package/app/Actions/Dashboard/Teams/TeamMemberDestroyAction.ts +36 -17
  128. package/app/Actions/Dashboard/Teams/TeamMemberUpdateAction.ts +39 -26
  129. package/app/Actions/Dashboard/Teams/TeamPeopleIndexAction.ts +71 -58
  130. package/app/Actions/Dashboard/Teams/team-invitation-delivery.ts +29 -7
  131. package/app/Actions/Dashboard/Teams/team-member-count.ts +4 -2
  132. package/app/Actions/Dashboard/Teams/team-records.test.ts +2 -0
  133. package/app/Actions/Dashboard/Teams/team-records.ts +2 -0
  134. package/app/Actions/Dashboard/dashboard-response.ts +16 -0
  135. package/app/Actions/SendWelcomeEmail.ts +1 -1
  136. package/app/Actions/Teams/AcceptInvitationAction.ts +74 -51
  137. package/app/Actions/Teams/ShowInvitationAction.ts +21 -14
  138. package/app/Actions/Teams/team-response.ts +23 -0
  139. package/app/Mail/OrderConfirmation.ts +1 -1
  140. package/app/Mail/SubscriptionConfirmation.ts +1 -1
  141. package/app/Mail/TeamInvitation.ts +1 -1
  142. package/app/Models/EmailIdempotency.ts +64 -0
  143. package/app/Models/EmailSuppression.ts +52 -0
  144. package/app/Models/EmailWebhookEvent.ts +51 -0
  145. package/app/Models/TeamInvitation.ts +13 -0
  146. package/functions/data.ts +0 -1
  147. package/functions/jobs.ts +0 -1
  148. package/ide/vscode/package.json +1 -1
  149. package/package.json +1 -1
  150. package/resources/components/Dashboard/Data/DataRecords.stx +0 -4
  151. package/resources/components/Dashboard/Jobs/JobDetail.stx +3 -2
  152. package/resources/components/Dashboard/Models/ModelRecordsDashboard.stx +7 -13
  153. package/resources/components/Dashboard/Models/ModelsOverview.stx +1 -1
  154. package/views/dashboard/.discovered-models.json +30 -0
  155. package/views/dashboard/stores/auth.ts +15 -14
@@ -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
  })
@@ -4,6 +4,7 @@ import { join } from 'node:path'
4
4
  import process from 'node:process'
5
5
  import { Deployment } from '@stacksjs/orm'
6
6
  import { response } from '@stacksjs/router'
7
+ import { dashboardOperationalError, dashboardOperationalIssue } from '../dashboard-response'
7
8
  import { booleanValue, deploymentCommandArgs } from './deployment-input'
8
9
 
9
10
  function gitValue(args: string[]): string {
@@ -63,10 +64,7 @@ export default new Action({
63
64
  })
64
65
  }
65
66
  catch (error) {
66
- return response.json({
67
- success: false,
68
- message: error instanceof Error ? error.message : 'Deployment record could not be created.',
69
- }, { status: 500 })
67
+ return dashboardOperationalError(error, 'Deployment record could not be created.', 'CreateDeployment.record', 500)
70
68
  }
71
69
  }
72
70
 
@@ -81,27 +79,33 @@ export default new Action({
81
79
  }
82
80
  catch (error) {
83
81
  if (deployment) {
84
- await deployment.update({
85
- status: 'failed',
86
- duration: 0,
87
- errorLog: error instanceof Error ? error.message : String(error),
88
- })
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
+ }
89
92
  }
90
- return response.json({
91
- success: false,
92
- message: error instanceof Error ? error.message : 'Deployment process could not be started.',
93
- }, { status: 500 })
93
+ return dashboardOperationalError(error, 'Deployment process could not be started.', 'CreateDeployment.process', 500)
94
94
  }
95
95
 
96
96
  if (deployment) {
97
97
  const record = deployment
98
- void child.exited.then(async (exitCode) => {
99
- await record.update({
100
- status: exitCode === 0 ? 'success' : 'failed',
101
- duration: Math.max(0, Math.round((Date.now() - startedAt) / 1000)),
102
- 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')
103
108
  })
104
- })
105
109
  }
106
110
 
107
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',
@@ -2,6 +2,7 @@ import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { Deployment } from '@stacksjs/orm'
4
4
  import { response } from '@stacksjs/router'
5
+ import { dashboardOperationalError } from '../dashboard-response'
5
6
 
6
7
  export default new Action({
7
8
  name: 'GetDeployment',
@@ -11,8 +12,8 @@ export default new Action({
11
12
 
12
13
  async handle(request: RequestInstance) {
13
14
  const id = Number(request.getParam('id'))
14
- if (!Number.isFinite(id) || id <= 0)
15
- 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)
16
17
 
17
18
  try {
18
19
  const deployment = await Deployment.find(id)
@@ -24,9 +25,7 @@ export default new Action({
24
25
  }
25
26
  }
26
27
  catch (error) {
27
- return response.json({
28
- message: error instanceof Error ? error.message : 'Deployment could not be loaded.',
29
- }, 503)
28
+ return dashboardOperationalError(error, 'Deployment could not be loaded.', 'GetDeployment')
30
29
  }
31
30
  },
32
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,9 +1,11 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
+ import { randomUUIDv7 } from 'bun'
2
3
  import { Action } from '@stacksjs/actions'
3
- import { mkdir, rename, writeFile } from 'node:fs/promises'
4
+ import { mkdir, rename, rm, writeFile } from 'node:fs/promises'
4
5
  import { dirname, join } from 'node:path'
5
6
  import process from 'node:process'
6
7
  import { response } from '@stacksjs/router'
8
+ import { dashboardOperationalError, dashboardOperationalIssue } from '../dashboard-response'
7
9
 
8
10
  export default new Action({
9
11
  name: 'UpdateDeployScript',
@@ -29,10 +31,21 @@ export default new Action({
29
31
  }
30
32
 
31
33
  const filePath = join(process.cwd(), 'cloud', 'deploy-script.ts')
32
- const temporaryPath = `${filePath}.${process.pid}.tmp`
33
- await mkdir(dirname(filePath), { recursive: true })
34
- await writeFile(temporaryPath, content, { encoding: 'utf8', mode: 0o644 })
35
- 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
+ }
36
49
 
37
50
  return {
38
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
  })
@@ -6,6 +6,7 @@ import { cache } from '@stacksjs/cache'
6
6
  import { config } from '@stacksjs/config'
7
7
  import { db } from '@stacksjs/database'
8
8
  import { checkQueueHealth } from '@stacksjs/queue'
9
+ import { dashboardOperationalIssue } from '../dashboard-response'
9
10
  import {
10
11
  compactSql,
11
12
  countValue,
@@ -23,14 +24,14 @@ interface SourceResult<T> {
23
24
  error: string
24
25
  }
25
26
 
26
- async function inspectSource<T>(work: Promise<T>, fallback: T): Promise<SourceResult<T>> {
27
+ async function inspectSource<T>(work: Promise<T>, fallback: T, message: string, source: string): Promise<SourceResult<T>> {
27
28
  try {
28
29
  return { value: await work, error: '' }
29
30
  }
30
31
  catch (error) {
31
32
  return {
32
33
  value: fallback,
33
- error: error instanceof Error ? error.message : 'The source could not be inspected.',
34
+ error: dashboardOperationalIssue(error, message, `InsightsAction.${source}`),
34
35
  }
35
36
  }
36
37
  }
@@ -135,18 +136,18 @@ export default new Action({
135
136
  filesystem,
136
137
  databaseFile,
137
138
  ] = await Promise.all([
138
- inspectSource(requestSummaryQuery, undefined),
139
- inspectSource(requestSuccessQuery, undefined),
140
- inspectSource(slowRequestsQuery, []),
141
- inspectSource(querySummaryQuery, undefined),
142
- inspectSource(queryStatusesQuery, []),
143
- inspectSource(slowQueriesQuery, []),
144
- inspectSource(errorSummaryQuery, undefined),
145
- inspectSource(recentErrorsQuery, []),
146
- inspectSource(checkQueueHealth(), null),
147
- inspectSource(cache.getStats(), null),
148
- inspectSource(filesystemQuery, null),
149
- inspectSource(databaseFileQuery, null),
139
+ inspectSource(requestSummaryQuery, undefined, 'Request telemetry is unavailable.', 'requests.summary'),
140
+ inspectSource(requestSuccessQuery, undefined, 'Request telemetry is unavailable.', 'requests.success'),
141
+ inspectSource(slowRequestsQuery, [], 'Request telemetry is unavailable.', 'requests.slowest'),
142
+ inspectSource(querySummaryQuery, undefined, 'Query telemetry is unavailable.', 'queries.summary'),
143
+ inspectSource(queryStatusesQuery, [], 'Query telemetry is unavailable.', 'queries.statuses'),
144
+ inspectSource(slowQueriesQuery, [], 'Query telemetry is unavailable.', 'queries.slowest'),
145
+ inspectSource(errorSummaryQuery, undefined, 'Error telemetry is unavailable.', 'errors.summary'),
146
+ inspectSource(recentErrorsQuery, [], 'Error telemetry is unavailable.', 'errors.recent'),
147
+ inspectSource(checkQueueHealth(), null, 'Queue telemetry is unavailable.', 'queue'),
148
+ inspectSource(cache.getStats(), null, 'Cache telemetry is unavailable.', 'cache'),
149
+ inspectSource(filesystemQuery, null, 'Filesystem telemetry is unavailable.', 'filesystem'),
150
+ inspectSource(databaseFileQuery, null, 'Database file telemetry is unavailable.', 'database-file'),
150
151
  ])
151
152
 
152
153
  const requestTotal = countValue(requestSummary.value?.total)
@@ -1,8 +1,8 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { db } from '@stacksjs/database'
4
- import { response } from '@stacksjs/router'
5
4
  import { dashboardRequestValue } from '../dashboard-request'
5
+ import { dashboardOperationalError } from '../dashboard-response'
6
6
  import { DASHBOARD_LOG_TYPES, normalizeDashboardLog, summarizeDashboardLogTypes } from './log-dashboard'
7
7
 
8
8
  const RANGES = ['1', '7', '30', '90', 'all'] as const
@@ -101,13 +101,7 @@ export default new Action({
101
101
  }
102
102
  }
103
103
  catch (error) {
104
- return response.json({
105
- logs: [],
106
- summary: { total: 0, error: 0, warning: 0, info: 0, success: 0 },
107
- pagination: { page: 1, perPage: 25, total: 0, totalPages: 1 },
108
- options: { sources: [], projects: [], types: [...DASHBOARD_LOG_TYPES], ranges: [...RANGES] },
109
- error: error instanceof Error ? error.message : 'Logs could not be loaded.',
110
- }, 500)
104
+ return dashboardOperationalError(error, 'Logs could not be loaded.', 'LogIndexAction')
111
105
  }
112
106
  },
113
107
  })
@@ -1,7 +1,7 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { FailedJob, Job } from '@stacksjs/orm'
4
- import { response } from '@stacksjs/router'
4
+ import { dashboardOperationalError } from '../dashboard-response'
5
5
  import { matchesJobSearch, normalizeActiveJob, normalizeFailedJob } from './job-records'
6
6
 
7
7
  export default new Action({
@@ -43,9 +43,7 @@ export default new Action({
43
43
  return { data, total, page, perPage, queues, queueConnected: true }
44
44
  }
45
45
  catch (error) {
46
- return response.json({
47
- message: error instanceof Error ? error.message : 'Job history could not be loaded.',
48
- }, 503)
46
+ return dashboardOperationalError(error, 'Job history could not be loaded.', 'JobIndexAction')
49
47
  }
50
48
  },
51
49
  })
@@ -1,6 +1,8 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { FailedJob, Job } from '@stacksjs/orm'
4
+ import { response } from '@stacksjs/router'
5
+ import { dashboardOperationalError } from '../dashboard-response'
4
6
  import { normalizeActiveJob, normalizeFailedJob, parseJobReference } from './job-records'
5
7
 
6
8
  export default new Action({
@@ -13,17 +15,21 @@ export default new Action({
13
15
  const id = Number(parsed.id)
14
16
 
15
17
  if (!Number.isFinite(id) || id <= 0)
16
- return { job: null, error: 'Invalid job id.' }
18
+ return response.json({ message: 'Invalid job id.' }, 400)
17
19
 
18
20
  try {
19
21
  if (parsed.source === 'failed') {
20
22
  const record = await FailedJob.find(id)
21
- return { job: record ? normalizeFailedJob(record) : null }
23
+ return record
24
+ ? { job: normalizeFailedJob(record) }
25
+ : response.json({ message: 'Job not found.' }, 404)
22
26
  }
23
27
 
24
28
  if (parsed.source === 'job') {
25
29
  const record = await Job.find(id)
26
- return { job: record ? normalizeActiveJob(record) : null }
30
+ return record
31
+ ? { job: normalizeActiveJob(record) }
32
+ : response.json({ message: 'Job not found.' }, 404)
27
33
  }
28
34
 
29
35
  const activeRecord = await Job.find(id)
@@ -31,13 +37,12 @@ export default new Action({
31
37
  return { job: normalizeActiveJob(activeRecord) }
32
38
 
33
39
  const failedRecord = await FailedJob.find(id)
34
- return { job: failedRecord ? normalizeFailedJob(failedRecord) : null }
40
+ return failedRecord
41
+ ? { job: normalizeFailedJob(failedRecord) }
42
+ : response.json({ message: 'Job not found.' }, 404)
35
43
  }
36
44
  catch (error) {
37
- return {
38
- job: null,
39
- error: error instanceof Error ? error.message : 'Job could not be loaded.',
40
- }
45
+ return dashboardOperationalError(error, 'Job could not be loaded.', 'JobShowAction')
41
46
  }
42
47
  },
43
48
  })
@@ -1,7 +1,7 @@
1
1
  import { Action } from '@stacksjs/actions'
2
2
  import { FailedJob, Job } from '@stacksjs/orm'
3
3
  import { getGlobalMetrics } from '@stacksjs/queue'
4
- import { response } from '@stacksjs/router'
4
+ import { dashboardOperationalError } from '../dashboard-response'
5
5
 
6
6
  export default new Action({
7
7
  name: 'JobStatsAction',
@@ -38,9 +38,7 @@ export default new Action({
38
38
  }
39
39
  }
40
40
  catch (error) {
41
- return response.json({
42
- message: error instanceof Error ? error.message : 'Job metrics could not be loaded.',
43
- }, 503)
41
+ return dashboardOperationalError(error, 'Job metrics could not be loaded.', 'JobStatsAction')
44
42
  }
45
43
  },
46
44
  })
@@ -1,7 +1,7 @@
1
1
  import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { db } from '@stacksjs/database'
4
- import { kanbanError } from './kanban-response'
4
+ import { kanbanActionError, kanbanError } from './kanban-response'
5
5
 
6
6
  /**
7
7
  * `DELETE /api/dashboard/kanban/boards/:id`.
@@ -60,8 +60,7 @@ export default new Action({
60
60
  return { deleted: true, id }
61
61
  }
62
62
  catch (err) {
63
- console.error('[dashboard/kanban] BoardDestroyAction failed:', err)
64
- return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
63
+ return kanbanActionError(err, 'BoardDestroyAction')
65
64
  }
66
65
  },
67
66
  })
@@ -2,7 +2,7 @@ import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { db } from '@stacksjs/database'
4
4
  import { modelBoolean } from './kanban-model'
5
- import { kanbanError } from './kanban-response'
5
+ import { kanbanActionError, kanbanError } from './kanban-response'
6
6
 
7
7
  interface BoardRow {
8
8
  id: number
@@ -201,8 +201,7 @@ export default new Action({
201
201
  }
202
202
  }
203
203
  catch (err) {
204
- console.error('[dashboard/kanban] BoardShowAction failed:', err)
205
- return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
204
+ return kanbanActionError(err, 'BoardShowAction')
206
205
  }
207
206
  },
208
207
  })
@@ -2,7 +2,7 @@ import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { db } from '@stacksjs/database'
4
4
  import { Board } from '@stacksjs/orm'
5
- import { kanbanError } from './kanban-response'
5
+ import { kanbanActionError, kanbanError } from './kanban-response'
6
6
 
7
7
  interface BoardInput {
8
8
  name?: unknown
@@ -77,8 +77,7 @@ export default new Action({
77
77
  }
78
78
  }
79
79
  catch (err) {
80
- console.error('[dashboard/kanban] BoardStoreAction failed:', err)
81
- return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
80
+ return kanbanActionError(err, 'BoardStoreAction')
82
81
  }
83
82
  },
84
83
  })
@@ -2,7 +2,7 @@ import type { RequestInstance } from '@stacksjs/types'
2
2
  import { Action } from '@stacksjs/actions'
3
3
  import { Board } from '@stacksjs/orm'
4
4
  import { modelBoolean, modelNullableString, modelNumber, modelString, refreshModel } from './kanban-model'
5
- import { kanbanError } from './kanban-response'
5
+ import { kanbanActionError, kanbanError } from './kanban-response'
6
6
 
7
7
  interface BoardInput {
8
8
  name?: unknown
@@ -83,8 +83,7 @@ export default new Action({
83
83
  }
84
84
  }
85
85
  catch (err) {
86
- console.error('[dashboard/kanban] BoardUpdateAction failed:', err)
87
- return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
86
+ return kanbanActionError(err, 'BoardUpdateAction')
88
87
  }
89
88
  },
90
89
  })