@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.
- package/ai/skills/stacks-browse/SKILL.md +17 -2
- package/ai/skills/stacks-browse/scripts/browse.ts +207 -6
- package/ai/skills/stacks-email/SKILL.md +73 -16
- package/app/Actions/ContactAction.ts +2 -2
- package/app/Actions/Dashboard/Analytics/EventAnalyticsAction.ts +2 -3
- package/app/Actions/Dashboard/Analytics/MarketingAnalyticsAction.ts +2 -3
- package/app/Actions/Dashboard/Analytics/SalesAnalyticsAction.ts +2 -3
- package/app/Actions/Dashboard/Analytics/WebAnalyticsAction.ts +9 -3
- package/app/Actions/Dashboard/Auth/MeAction.ts +12 -14
- package/app/Actions/Dashboard/Buddy/BuddyChatAction.ts +2 -3
- package/app/Actions/Dashboard/Ci/RepoRunJobsAction.ts +10 -15
- package/app/Actions/Dashboard/Ci/RepoRunsAction.ts +12 -14
- package/app/Actions/Dashboard/Ci/RunnerHistoryAction.ts +11 -10
- package/app/Actions/Dashboard/Ci/StatusAction.ts +2 -11
- package/app/Actions/Dashboard/Cloud/CloudIndexAction.ts +8 -1
- package/app/Actions/Dashboard/Cloud/ServerlessIndexAction.ts +15 -8
- package/app/Actions/Dashboard/Commerce/CommerceCategoriesAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommerceCouponsAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommerceCustomersAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommerceDashboardAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommerceDeliveryAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommerceGiftCardsAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommerceOrdersAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommercePaymentsAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommercePosAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommercePosCheckoutAction.ts +76 -61
- package/app/Actions/Dashboard/Commerce/CommercePrintDevicesAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommercePrintLogsAction.ts +4 -7
- package/app/Actions/Dashboard/Commerce/CommerceProductDetailAction.ts +4 -4
- package/app/Actions/Dashboard/Commerce/CommerceProductsAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommerceTaxesAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/DeliveryRouteIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/DigitalDeliveryIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/DriverIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/LicenseKeyIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/LicenseKeyOptionsAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/ManufacturerIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/PaymentRefundAction.ts +1 -0
- package/app/Actions/Dashboard/Commerce/ProductUnitDefaultAction.ts +1 -0
- package/app/Actions/Dashboard/Commerce/ProductUnitIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/ProductVariantIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/ProductWaitlistIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/RestaurantWaitlistIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/ReviewIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/ShippingMethodIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/ShippingRateIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/ShippingZoneIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/TaxRateDefaultAction.ts +1 -0
- package/app/Actions/Dashboard/DashboardActivityAction.ts +2 -5
- package/app/Actions/Dashboard/Data/ActivityIndexAction.ts +2 -5
- package/app/Actions/Dashboard/Data/SubscriberIndexAction.ts +2 -5
- package/app/Actions/Dashboard/Data/TeamIndexAction.ts +2 -5
- package/app/Actions/Dashboard/Data/UserIndexAction.ts +2 -5
- package/app/Actions/Dashboard/Deployments/CreateDeployment.ts +26 -21
- package/app/Actions/Dashboard/Deployments/GetAverageDeploymentTime.ts +8 -2
- package/app/Actions/Dashboard/Deployments/GetDeployScript.ts +3 -6
- package/app/Actions/Dashboard/Deployments/GetDeployment.ts +5 -5
- package/app/Actions/Dashboard/Deployments/GetDeploymentCount.ts +7 -1
- package/app/Actions/Dashboard/Deployments/GetDeploymentLiveTerminalOutput.ts +10 -9
- package/app/Actions/Dashboard/Deployments/GetDeployments.ts +2 -4
- package/app/Actions/Dashboard/Deployments/GetRecentDeployments.ts +8 -2
- package/app/Actions/Dashboard/Deployments/UpdateDeployScript.ts +21 -7
- package/app/Actions/Dashboard/Email/CapturedMailIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Email/CapturedMailShowAction.ts +3 -4
- package/app/Actions/Dashboard/Email/InboxActivityAction.ts +28 -16
- package/app/Actions/Dashboard/Email/InboxAttachmentDownloadAction.ts +45 -0
- package/app/Actions/Dashboard/Email/InboxDeleteAction.ts +11 -6
- package/app/Actions/Dashboard/Email/InboxIndexAction.ts +5 -7
- package/app/Actions/Dashboard/Email/InboxMarkReadAction.ts +11 -6
- package/app/Actions/Dashboard/Email/InboxMarkUnreadAction.ts +11 -6
- package/app/Actions/Dashboard/Email/InboxPreferenceShowAction.ts +10 -4
- package/app/Actions/Dashboard/Email/InboxPreferenceUpdateAction.ts +8 -9
- package/app/Actions/Dashboard/Email/InboxShowAction.ts +7 -7
- package/app/Actions/Dashboard/Email/InboxStatsAction.ts +5 -7
- package/app/Actions/Dashboard/Email/inbox-request.ts +33 -0
- package/app/Actions/Dashboard/Infrastructure/InsightsAction.ts +15 -14
- package/app/Actions/Dashboard/Infrastructure/LogIndexAction.ts +4 -11
- package/app/Actions/Dashboard/Infrastructure/ServerShowAction.ts +3 -6
- package/app/Actions/Dashboard/Jobs/JobIndexAction.ts +9 -18
- package/app/Actions/Dashboard/Jobs/JobRetryAction.ts +4 -3
- package/app/Actions/Dashboard/Jobs/JobShowAction.ts +16 -11
- package/app/Actions/Dashboard/Jobs/JobStatsAction.ts +2 -4
- package/app/Actions/Dashboard/Kanban/BoardDestroyAction.ts +7 -8
- package/app/Actions/Dashboard/Kanban/BoardShowAction.ts +11 -19
- package/app/Actions/Dashboard/Kanban/BoardStoreAction.ts +6 -6
- package/app/Actions/Dashboard/Kanban/BoardUpdateAction.ts +7 -8
- package/app/Actions/Dashboard/Kanban/BoardsIndexAction.ts +3 -5
- package/app/Actions/Dashboard/Kanban/BoardsReorderAction.ts +6 -6
- package/app/Actions/Dashboard/Kanban/CardAssigneesSyncAction.ts +13 -12
- package/app/Actions/Dashboard/Kanban/CardCommentDestroyAction.ts +12 -15
- package/app/Actions/Dashboard/Kanban/CardCommentStoreAction.ts +21 -46
- package/app/Actions/Dashboard/Kanban/CardCommentUpdateAction.ts +43 -0
- package/app/Actions/Dashboard/Kanban/CardDestroyAction.ts +7 -8
- package/app/Actions/Dashboard/Kanban/CardLabelsSyncAction.ts +8 -9
- package/app/Actions/Dashboard/Kanban/CardShowAction.ts +6 -7
- package/app/Actions/Dashboard/Kanban/CardStoreAction.ts +9 -9
- package/app/Actions/Dashboard/Kanban/CardUpdateAction.ts +7 -8
- package/app/Actions/Dashboard/Kanban/CardsReorderAction.ts +6 -6
- package/app/Actions/Dashboard/Kanban/ColumnDestroyAction.ts +7 -8
- package/app/Actions/Dashboard/Kanban/ColumnStoreAction.ts +6 -6
- package/app/Actions/Dashboard/Kanban/ColumnUpdateAction.ts +7 -8
- package/app/Actions/Dashboard/Kanban/ColumnsReorderAction.ts +6 -6
- package/app/Actions/Dashboard/Kanban/LabelDestroyAction.ts +6 -7
- package/app/Actions/Dashboard/Kanban/LabelStoreAction.ts +6 -6
- package/app/Actions/Dashboard/Kanban/LabelUpdateAction.ts +7 -8
- package/app/Actions/Dashboard/Kanban/UsersListAction.ts +3 -4
- package/app/Actions/Dashboard/Kanban/kanban-comment.ts +14 -0
- package/app/Actions/Dashboard/Kanban/kanban-model.test.ts +36 -0
- package/app/Actions/Dashboard/Kanban/kanban-model.ts +14 -7
- package/app/Actions/Dashboard/Kanban/kanban-response.ts +5 -0
- package/app/Actions/Dashboard/Library/CreateComponent.ts +15 -6
- package/app/Actions/Dashboard/Library/CreateFunction.ts +15 -6
- package/app/Actions/Dashboard/Library/DependencyIndexAction.ts +88 -77
- package/app/Actions/Dashboard/Library/GetAverageReleaseTime.ts +21 -15
- package/app/Actions/Dashboard/Library/GetComponents.ts +14 -7
- package/app/Actions/Dashboard/Library/GetDownloadCount.ts +14 -5
- package/app/Actions/Dashboard/Library/GetFunctions.ts +12 -5
- package/app/Actions/Dashboard/Library/GetReleaseCount.ts +14 -5
- package/app/Actions/Dashboard/Library/GetReleases.ts +17 -10
- package/app/Actions/Dashboard/Library/PackageIndexAction.ts +14 -7
- package/app/Actions/Dashboard/Models/ModelDestroyAction.ts +11 -8
- package/app/Actions/Dashboard/Models/ModelShowAction.ts +97 -192
- package/app/Actions/Dashboard/Models/ModelStoreAction.ts +14 -12
- package/app/Actions/Dashboard/Models/ModelUpdateAction.ts +18 -16
- package/app/Actions/Dashboard/Models/ModelsIndexAction.ts +50 -19
- package/app/Actions/Dashboard/Models/model-api.ts +27 -0
- package/app/Actions/Dashboard/Models/model-write.ts +22 -15
- package/app/Actions/Dashboard/Notifications/NotificationDeliveryHistoryAction.ts +49 -40
- package/app/Actions/Dashboard/Notifications/NotificationDeliveryIndexAction.ts +20 -10
- package/app/Actions/Dashboard/Notifications/NotificationDeliveryOverviewAction.ts +96 -84
- package/app/Actions/Dashboard/Notifications/NotificationDeliveryRetryAction.ts +34 -21
- package/app/Actions/Dashboard/Queries/QueryDashboardAction.ts +2 -4
- package/app/Actions/Dashboard/Queries/QueryShowAction.ts +3 -3
- package/app/Actions/Dashboard/Queue/QueueStatsAction.ts +2 -4
- package/app/Actions/Dashboard/Queue/QueueWorkersAction.ts +2 -4
- package/app/Actions/Dashboard/Rbac/PermissionDestroyAction.ts +6 -6
- package/app/Actions/Dashboard/Rbac/PermissionStoreAction.ts +5 -4
- package/app/Actions/Dashboard/Rbac/PermissionsIndexAction.ts +2 -3
- package/app/Actions/Dashboard/Rbac/RoleDestroyAction.ts +6 -6
- package/app/Actions/Dashboard/Rbac/RolePermissionsShowAction.ts +6 -6
- package/app/Actions/Dashboard/Rbac/RolePermissionsSyncAction.ts +6 -9
- package/app/Actions/Dashboard/Rbac/RoleStoreAction.ts +5 -4
- package/app/Actions/Dashboard/Rbac/RolesIndexAction.ts +4 -6
- package/app/Actions/Dashboard/Rbac/UserRolesShowAction.ts +6 -7
- package/app/Actions/Dashboard/Rbac/UserRolesSyncAction.ts +6 -13
- package/app/Actions/Dashboard/Rbac/UsersListAction.ts +2 -3
- package/app/Actions/Dashboard/Rbac/rbac-response.ts +10 -0
- package/app/Actions/Dashboard/Realtime/RealtimeStatsAction.ts +2 -4
- package/app/Actions/Dashboard/Search/GlobalSearchAction.ts +69 -55
- package/app/Actions/Dashboard/Teams/TeamInvitationDestroyAction.ts +34 -16
- package/app/Actions/Dashboard/Teams/TeamInvitationResendAction.ts +52 -29
- package/app/Actions/Dashboard/Teams/TeamInviteAction.ts +104 -73
- package/app/Actions/Dashboard/Teams/TeamMemberDestroyAction.ts +36 -17
- package/app/Actions/Dashboard/Teams/TeamMemberUpdateAction.ts +41 -28
- package/app/Actions/Dashboard/Teams/TeamPeopleIndexAction.ts +71 -58
- package/app/Actions/Dashboard/Teams/team-invitation-delivery.ts +29 -7
- package/app/Actions/Dashboard/Teams/team-member-count.ts +4 -2
- package/app/Actions/Dashboard/Teams/team-records.test.ts +2 -0
- package/app/Actions/Dashboard/Teams/team-records.ts +2 -0
- package/app/Actions/Dashboard/dashboard-request.ts +13 -0
- package/app/Actions/Dashboard/dashboard-response.ts +16 -0
- package/app/Actions/SendWelcomeEmail.ts +1 -1
- package/app/Actions/Teams/AcceptInvitationAction.ts +74 -51
- package/app/Actions/Teams/ShowInvitationAction.ts +21 -14
- package/app/Actions/Teams/team-response.ts +23 -0
- package/app/Mail/OrderConfirmation.ts +1 -1
- package/app/Mail/SubscriptionConfirmation.ts +1 -1
- package/app/Mail/TeamInvitation.ts +1 -1
- package/app/Models/CardComment.ts +3 -4
- package/app/Models/EmailIdempotency.ts +64 -0
- package/app/Models/EmailSuppression.ts +52 -0
- package/app/Models/EmailWebhookEvent.ts +51 -0
- package/app/Models/TeamInvitation.ts +13 -0
- package/functions/dashboard-api.ts +51 -10
- package/functions/data.ts +0 -1
- package/functions/inbox.test.ts +2 -0
- package/functions/inbox.ts +27 -2
- package/functions/jobs.ts +0 -1
- package/functions/public-application-url.test.ts +13 -0
- package/functions/public-application-url.ts +27 -0
- package/ide/vscode/package.json +1 -1
- package/package.json +1 -1
- package/resources/components/Dashboard/Auth/AccessTokens.stx +15 -0
- package/resources/components/Dashboard/Ci/CiDashboard.stx +24 -6
- package/resources/components/Dashboard/Commerce/CommerceTaxDialog.stx +1 -1
- package/resources/components/Dashboard/Commerce/ProductWaitlistDashboard.stx +8 -7
- package/resources/components/Dashboard/Commerce/RestaurantWaitlistDashboard.stx +8 -7
- package/resources/components/Dashboard/Content/BlogDashboard.stx +14 -2
- package/resources/components/Dashboard/Content/PostsDashboard.stx +7 -1
- package/resources/components/Dashboard/Data/DataRecords.stx +0 -4
- package/resources/components/Dashboard/Deployments/DeploymentDetail.stx +16 -2
- package/resources/components/Dashboard/Email/EmailNavigation.stx +5 -5
- package/resources/components/Dashboard/Email/EmailSidebar.stx +1 -1
- package/resources/components/Dashboard/Email/InboxAttachmentList.stx +53 -0
- package/resources/components/Dashboard/Email/InboxDashboard.stx +5 -4
- package/resources/components/Dashboard/Email/InboxMessageDetail.stx +6 -4
- package/resources/components/Dashboard/Jobs/JobDetail.stx +3 -2
- package/resources/components/Dashboard/Kanban/KanbanBoardDashboard.stx +63 -6
- package/resources/components/Dashboard/Kanban/KanbanBoardsDashboard.stx +3 -6
- package/resources/components/Dashboard/Management/PermissionsDashboard.stx +4 -4
- package/resources/components/Dashboard/Models/ModelRecordsDashboard.stx +7 -13
- package/resources/components/Dashboard/Models/ModelsOverview.stx +1 -1
- package/resources/components/Dashboard/NotFoundDashboard.stx +2 -5
- package/resources/components/Dashboard/Queries/QueryDashboard.stx +6 -2
- package/resources/components/Dashboard/Settings/AppearanceSettingsDashboard.stx +4 -4
- package/resources/components/Dashboard/UI/EmptyState.stx +2 -2
- package/resources/components/Dashboard/UI/PageLayout.stx +3 -3
- package/resources/functions/dashboard/sidebar.ts +4 -4
- package/routes/dashboard-api.ts +8 -20
- package/views/dashboard/.discovered-models.json +54 -0
- package/views/dashboard/layouts/default.stx +19 -16
- package/views/dashboard/stores/auth.ts +15 -14
- package/views/dashboard/stores/ci.ts +13 -7
- package/views/dashboard/stores/kanban.ts +48 -11
|
@@ -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 {
|
|
5
|
+
import { dashboardMailbox, inboxActionError } from './inbox-request'
|
|
6
6
|
|
|
7
7
|
export default new Action({
|
|
8
8
|
name: 'InboxMarkReadAction',
|
|
@@ -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.get('messageId') || '')
|
|
16
15
|
|
|
17
16
|
if (!messageId)
|
|
18
17
|
return response.json({ message: 'messageId is required.' }, 422)
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
try {
|
|
20
|
+
const mailbox = dashboardMailbox(request)
|
|
21
|
+
const success = await emailSDK.markAsRead(mailbox, messageId)
|
|
22
|
+
if (!success)
|
|
23
|
+
return response.json({ message: 'Email not found or its read state could not be updated.' }, 404)
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
return response.json({ success: true, mailbox, messageId })
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return inboxActionError(error, 'The email read state could not be updated.')
|
|
29
|
+
}
|
|
25
30
|
},
|
|
26
31
|
})
|
|
@@ -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 {
|
|
5
|
+
import { dashboardMailbox, inboxActionError } from './inbox-request'
|
|
6
6
|
|
|
7
7
|
export default new Action({
|
|
8
8
|
name: 'InboxMarkUnreadAction',
|
|
@@ -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.get('messageId') || '')
|
|
16
15
|
|
|
17
16
|
if (!messageId)
|
|
18
17
|
return response.json({ message: 'messageId is required.' }, 422)
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
try {
|
|
20
|
+
const mailbox = dashboardMailbox(request)
|
|
21
|
+
const success = await emailSDK.markAsUnread(mailbox, messageId)
|
|
22
|
+
if (!success)
|
|
23
|
+
return response.json({ message: 'Email not found or its read state could not be updated.' }, 404)
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
return response.json({ success: true, mailbox, messageId })
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return inboxActionError(error, 'The email read state could not be updated.')
|
|
29
|
+
}
|
|
25
30
|
},
|
|
26
31
|
})
|
|
@@ -2,7 +2,8 @@ import type { RequestInstance } from '@stacksjs/types'
|
|
|
2
2
|
import { Action } from '@stacksjs/actions'
|
|
3
3
|
import { MailPreference } from '@stacksjs/orm'
|
|
4
4
|
import { response } from '@stacksjs/router'
|
|
5
|
-
import {
|
|
5
|
+
import { dashboardMailbox, inboxActionError } from './inbox-request'
|
|
6
|
+
import { serializeMailPreference } from './mail-preference'
|
|
6
7
|
|
|
7
8
|
export default new Action({
|
|
8
9
|
name: 'InboxPreferenceShowAction',
|
|
@@ -11,8 +12,13 @@ export default new Action({
|
|
|
11
12
|
apiResponse: true,
|
|
12
13
|
|
|
13
14
|
async handle(request: RequestInstance) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
try {
|
|
16
|
+
const mailbox = dashboardMailbox(request)
|
|
17
|
+
const record = await MailPreference.where('mailbox', '=', mailbox).first()
|
|
18
|
+
return response.json({ preference: serializeMailPreference(record, mailbox) })
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
return inboxActionError(error, 'Mail settings could not be loaded.')
|
|
22
|
+
}
|
|
17
23
|
},
|
|
18
24
|
})
|
|
@@ -2,8 +2,8 @@ import type { RequestInstance } from '@stacksjs/types'
|
|
|
2
2
|
import { Action } from '@stacksjs/actions'
|
|
3
3
|
import { MailPreference } from '@stacksjs/orm'
|
|
4
4
|
import { response } from '@stacksjs/router'
|
|
5
|
+
import { dashboardMailbox, inboxActionError } from './inbox-request'
|
|
5
6
|
import {
|
|
6
|
-
defaultMailbox,
|
|
7
7
|
type MailPreferenceInput,
|
|
8
8
|
mailPreferenceAttributes,
|
|
9
9
|
normalizeStringList,
|
|
@@ -132,14 +132,13 @@ export default new Action({
|
|
|
132
132
|
apiResponse: true,
|
|
133
133
|
|
|
134
134
|
async handle(request: RequestInstance) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
.
|
|
141
|
-
|
|
142
|
-
return response.json({ message: 'The mail settings are invalid.', fields: { mailbox: 'Enter a valid mailbox.' } }, 422)
|
|
135
|
+
let mailbox: string
|
|
136
|
+
try {
|
|
137
|
+
mailbox = dashboardMailbox(request)
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
return inboxActionError(error, 'Mail settings could not be updated.')
|
|
141
|
+
}
|
|
143
142
|
|
|
144
143
|
const { fields, input } = inputFromRequest(request, mailbox)
|
|
145
144
|
if (Object.keys(fields).length)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { emailSDK } from '@stacksjs/email'
|
|
3
4
|
import { response } from '@stacksjs/router'
|
|
4
|
-
import {
|
|
5
|
+
import { dashboardMailbox, inboxActionError } from './inbox-request'
|
|
5
6
|
import { sanitizeInboxHtml } from './sanitize-inbox-html'
|
|
6
7
|
|
|
7
8
|
export default new Action({
|
|
@@ -10,10 +11,10 @@ export default new Action({
|
|
|
10
11
|
method: 'GET',
|
|
11
12
|
apiResponse: true,
|
|
12
13
|
|
|
13
|
-
async handle(request:
|
|
14
|
+
async handle(request: RequestInstance) {
|
|
14
15
|
try {
|
|
15
|
-
const mailbox = request
|
|
16
|
-
const messageId = request
|
|
16
|
+
const mailbox = dashboardMailbox(request)
|
|
17
|
+
const messageId = String(request.getParam('id') || '')
|
|
17
18
|
|
|
18
19
|
if (!messageId)
|
|
19
20
|
return response.json({ message: 'A message ID is required.' }, 422)
|
|
@@ -28,12 +29,11 @@ export default new Action({
|
|
|
28
29
|
html: sanitizeInboxHtml(email.html ?? ''),
|
|
29
30
|
text: email.text ?? '',
|
|
30
31
|
metadata: email.metadata,
|
|
32
|
+
attachments: email.attachments,
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
catch (err) {
|
|
34
|
-
return
|
|
35
|
-
message: err instanceof Error ? err.message : 'Email content could not be loaded.',
|
|
36
|
-
}, 503)
|
|
36
|
+
return inboxActionError(err, 'Email content could not be loaded.')
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
39
|
})
|
|
@@ -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 {
|
|
4
|
-
import { defaultMailbox } from './mail-preference'
|
|
4
|
+
import { dashboardMailbox, inboxActionError } from './inbox-request'
|
|
5
5
|
|
|
6
6
|
export default new Action({
|
|
7
7
|
name: 'InboxStatsAction',
|
|
@@ -9,9 +9,9 @@ export default new Action({
|
|
|
9
9
|
method: 'GET',
|
|
10
10
|
apiResponse: true,
|
|
11
11
|
|
|
12
|
-
async handle(request:
|
|
12
|
+
async handle(request: RequestInstance) {
|
|
13
13
|
try {
|
|
14
|
-
const mailbox = request
|
|
14
|
+
const mailbox = dashboardMailbox(request)
|
|
15
15
|
const stats = await emailSDK.getInboxStats(mailbox)
|
|
16
16
|
|
|
17
17
|
return {
|
|
@@ -20,9 +20,7 @@ export default new Action({
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
catch (err) {
|
|
23
|
-
return
|
|
24
|
-
message: err instanceof Error ? err.message : 'Inbox statistics could not be loaded.',
|
|
25
|
-
}, 503)
|
|
23
|
+
return inboxActionError(err, 'Inbox statistics could not be loaded.')
|
|
26
24
|
}
|
|
27
25
|
},
|
|
28
26
|
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
2
|
+
import { config } from '@stacksjs/config'
|
|
3
|
+
import { inboxMailboxPath, InvalidInboxMailboxError } from '@stacksjs/email'
|
|
4
|
+
import { response } from '@stacksjs/router'
|
|
5
|
+
import { defaultMailbox } from './mail-preference'
|
|
6
|
+
|
|
7
|
+
function configuredMailDomain(): string {
|
|
8
|
+
const email = (config as any)?.email || {}
|
|
9
|
+
const fromAddress = String(email.from?.address || '').trim()
|
|
10
|
+
const fromDomain = fromAddress.includes('@') ? fromAddress.split('@').at(-1) : ''
|
|
11
|
+
return String(email.domain || fromDomain || 'stacksjs.com').trim().toLowerCase()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function dashboardMailbox(request: RequestInstance): string {
|
|
15
|
+
const value = request.get('mailbox')
|
|
16
|
+
if (value !== null && value !== undefined && typeof value !== 'string')
|
|
17
|
+
throw new InvalidInboxMailboxError()
|
|
18
|
+
|
|
19
|
+
return inboxMailboxPath(value?.trim() || defaultMailbox(), configuredMailDomain()).address
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function inboxActionError(error: unknown, fallback: string): Response {
|
|
23
|
+
if (error instanceof InvalidInboxMailboxError) {
|
|
24
|
+
return response.json({
|
|
25
|
+
message: 'Enter a mailbox on the configured email domain.',
|
|
26
|
+
fields: { mailbox: 'Enter a mailbox on the configured email domain.' },
|
|
27
|
+
}, 422)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return response.json({
|
|
31
|
+
message: fallback,
|
|
32
|
+
}, 503)
|
|
33
|
+
}
|
|
@@ -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
|
|
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,15 +1,14 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions'
|
|
3
3
|
import { db } from '@stacksjs/database'
|
|
4
|
-
import {
|
|
4
|
+
import { dashboardRequestValue } from '../dashboard-request'
|
|
5
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
5
6
|
import { DASHBOARD_LOG_TYPES, normalizeDashboardLog, summarizeDashboardLogTypes } from './log-dashboard'
|
|
6
7
|
|
|
7
8
|
const RANGES = ['1', '7', '30', '90', 'all'] as const
|
|
8
9
|
|
|
9
10
|
function queryValue(request: RequestInstance, key: string): string {
|
|
10
|
-
|
|
11
|
-
const value = query[key]
|
|
12
|
-
return String((Array.isArray(value) ? value[0] : value) || request.get(key) || '').trim()
|
|
11
|
+
return dashboardRequestValue(request, key)
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
export default new Action({
|
|
@@ -102,13 +101,7 @@ export default new Action({
|
|
|
102
101
|
}
|
|
103
102
|
}
|
|
104
103
|
catch (error) {
|
|
105
|
-
return
|
|
106
|
-
logs: [],
|
|
107
|
-
summary: { total: 0, error: 0, warning: 0, info: 0, success: 0 },
|
|
108
|
-
pagination: { page: 1, perPage: 25, total: 0, totalPages: 1 },
|
|
109
|
-
options: { sources: [], projects: [], types: [...DASHBOARD_LOG_TYPES], ranges: [...RANGES] },
|
|
110
|
-
error: error instanceof Error ? error.message : 'Logs could not be loaded.',
|
|
111
|
-
}, 500)
|
|
104
|
+
return dashboardOperationalError(error, 'Logs could not be loaded.', 'LogIndexAction')
|
|
112
105
|
}
|
|
113
106
|
},
|
|
114
107
|
})
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { response } from '@stacksjs/router'
|
|
3
4
|
import { tsCloud } from '~/config/cloud'
|
|
@@ -9,12 +10,8 @@ export default new Action({
|
|
|
9
10
|
description: 'Returns one configured server or persisted deployment.',
|
|
10
11
|
method: 'GET',
|
|
11
12
|
apiResponse: true,
|
|
12
|
-
async handle(request) {
|
|
13
|
-
const identifier =
|
|
14
|
-
(request as any)?.params?.id
|
|
15
|
-
?? (request as any)?.param?.('id')
|
|
16
|
-
?? '',
|
|
17
|
-
)
|
|
13
|
+
async handle(request: RequestInstance) {
|
|
14
|
+
const identifier = request.getParam('id')
|
|
18
15
|
if (!identifier)
|
|
19
16
|
return response.json({ error: 'A server identifier is required.' }, 400)
|
|
20
17
|
|
|
@@ -1,26 +1,19 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { FailedJob, Job } from '@stacksjs/orm'
|
|
3
|
-
import {
|
|
4
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
4
5
|
import { matchesJobSearch, normalizeActiveJob, normalizeFailedJob } from './job-records'
|
|
5
6
|
|
|
6
7
|
export default new Action({
|
|
7
8
|
name: 'JobIndexAction',
|
|
8
9
|
description: 'Returns a paginated list of jobs across the active queue and the failed_jobs table.',
|
|
9
10
|
method: 'GET',
|
|
10
|
-
async handle() {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const v = query[key]
|
|
17
|
-
return Array.isArray(v) ? v[0] : v
|
|
18
|
-
}
|
|
19
|
-
const page = Math.max(1, Number(pick('page')) || 1)
|
|
20
|
-
const perPage = Math.min(200, Math.max(1, Number(pick('per_page')) || 25))
|
|
21
|
-
const queueFilter = String(pick('queue') || '').trim()
|
|
22
|
-
const statusFilter = String(pick('status') || '').trim().toLowerCase()
|
|
23
|
-
const search = String(pick('search') || '').trim().toLowerCase()
|
|
11
|
+
async handle(request: RequestInstance) {
|
|
12
|
+
const page = Math.max(1, Number(request.get('page', 1)) || 1)
|
|
13
|
+
const perPage = Math.min(200, Math.max(1, Number(request.get('per_page', 25)) || 25))
|
|
14
|
+
const queueFilter = String(request.get('queue', '')).trim()
|
|
15
|
+
const statusFilter = String(request.get('status', '')).trim().toLowerCase()
|
|
16
|
+
const search = String(request.get('search', '')).trim().toLowerCase()
|
|
24
17
|
|
|
25
18
|
try {
|
|
26
19
|
const [activeJobs, failedJobs] = await Promise.all([
|
|
@@ -50,9 +43,7 @@ export default new Action({
|
|
|
50
43
|
return { data, total, page, perPage, queues, queueConnected: true }
|
|
51
44
|
}
|
|
52
45
|
catch (error) {
|
|
53
|
-
return
|
|
54
|
-
message: error instanceof Error ? error.message : 'Job history could not be loaded.',
|
|
55
|
-
}, 503)
|
|
46
|
+
return dashboardOperationalError(error, 'Job history could not be loaded.', 'JobIndexAction')
|
|
56
47
|
}
|
|
57
48
|
},
|
|
58
49
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
|
-
import {
|
|
3
|
+
import { response } from '@stacksjs/router'
|
|
3
4
|
import { retryFailedJob } from '@stacksjs/queue'
|
|
4
5
|
import { parseJobReference } from './job-records'
|
|
5
6
|
|
|
@@ -7,8 +8,8 @@ export default new Action({
|
|
|
7
8
|
name: 'JobRetryAction',
|
|
8
9
|
description: 'Retries a single failed job by its failed_jobs id.',
|
|
9
10
|
method: 'POST',
|
|
10
|
-
async handle() {
|
|
11
|
-
const reference =
|
|
11
|
+
async handle(request: RequestInstance) {
|
|
12
|
+
const reference = request.getParam('id')
|
|
12
13
|
const parsed = parseJobReference(reference)
|
|
13
14
|
const id = Number(parsed.id)
|
|
14
15
|
if (parsed.source === 'job') {
|
|
@@ -1,29 +1,35 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { FailedJob, Job } from '@stacksjs/orm'
|
|
3
|
-
import {
|
|
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({
|
|
7
9
|
name: 'JobShowAction',
|
|
8
10
|
description: 'Returns one native Job or FailedJob record for dashboard inspection.',
|
|
9
11
|
method: 'GET',
|
|
10
|
-
async handle() {
|
|
11
|
-
const reference =
|
|
12
|
+
async handle(request: RequestInstance) {
|
|
13
|
+
const reference = request.getParam('id')
|
|
12
14
|
const parsed = parseJobReference(reference)
|
|
13
15
|
const id = Number(parsed.id)
|
|
14
16
|
|
|
15
17
|
if (!Number.isFinite(id) || id <= 0)
|
|
16
|
-
return {
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
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,9 +1,10 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { db } from '@stacksjs/database'
|
|
3
|
-
import { kanbanError } from './kanban-response'
|
|
4
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* `DELETE /api/dashboard/kanban/boards/:id
|
|
7
|
+
* `DELETE /api/dashboard/kanban/boards/:id`.
|
|
7
8
|
*
|
|
8
9
|
* Hard delete with cascade: removes the board, its columns, its cards,
|
|
9
10
|
* its labels, and the relevant pivot rows in `card_labels` and
|
|
@@ -23,9 +24,8 @@ export default new Action({
|
|
|
23
24
|
description: 'Hard-deletes a board and all of its columns, cards, labels, and pivot rows.',
|
|
24
25
|
method: 'DELETE',
|
|
25
26
|
apiResponse: true,
|
|
26
|
-
async handle(request) {
|
|
27
|
-
const
|
|
28
|
-
const id = Number(rawId)
|
|
27
|
+
async handle(request: RequestInstance) {
|
|
28
|
+
const id = Number(request.getParam('id'))
|
|
29
29
|
if (!Number.isFinite(id) || id <= 0) {
|
|
30
30
|
return kanbanError('Invalid board id', 400)
|
|
31
31
|
}
|
|
@@ -42,7 +42,7 @@ export default new Action({
|
|
|
42
42
|
'DELETE FROM card_assignees WHERE card_id IN (SELECT id FROM cards WHERE board_id = ?)',
|
|
43
43
|
[id],
|
|
44
44
|
).execute()
|
|
45
|
-
// Card comments
|
|
45
|
+
// Card comments use the same
|
|
46
46
|
// card-scoped pattern as the pivots.
|
|
47
47
|
await qb.unsafe(
|
|
48
48
|
'DELETE FROM card_comments WHERE card_id IN (SELECT id FROM cards WHERE board_id = ?)',
|
|
@@ -60,8 +60,7 @@ export default new Action({
|
|
|
60
60
|
return { deleted: true, id }
|
|
61
61
|
}
|
|
62
62
|
catch (err) {
|
|
63
|
-
|
|
64
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
63
|
+
return kanbanActionError(err, 'BoardDestroyAction')
|
|
65
64
|
}
|
|
66
65
|
},
|
|
67
66
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { db } from '@stacksjs/database'
|
|
3
4
|
import { modelBoolean } from './kanban-model'
|
|
4
|
-
import { kanbanError } from './kanban-response'
|
|
5
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
5
6
|
|
|
6
7
|
interface BoardRow {
|
|
7
8
|
id: number
|
|
@@ -56,8 +57,8 @@ interface LabelRow {
|
|
|
56
57
|
*
|
|
57
58
|
* Returns a single board with its columns + cards + label palette
|
|
58
59
|
* nested in render order. Powers the kanban board page
|
|
59
|
-
* (`/kanban/[id]`)
|
|
60
|
-
*
|
|
60
|
+
* (`/kanban/[id]`). The page loads this on mount, then its drag handler
|
|
61
|
+
* mutates positions through the reorder endpoint and
|
|
61
62
|
* relies on optimistic updates for snappy UX.
|
|
62
63
|
*
|
|
63
64
|
* Query layout (3 queries, all indexed):
|
|
@@ -67,24 +68,16 @@ interface LabelRow {
|
|
|
67
68
|
* `column_id, position`).
|
|
68
69
|
* 4. All labels for the board.
|
|
69
70
|
*
|
|
70
|
-
* Card-label
|
|
71
|
-
*
|
|
72
|
-
* single pivot query batched with the card fetch.
|
|
71
|
+
* Card-label pivots hydrate label chips inline through a single query
|
|
72
|
+
* batched with the card fetch.
|
|
73
73
|
*/
|
|
74
74
|
export default new Action({
|
|
75
75
|
name: 'Kanban Board Show',
|
|
76
76
|
description: 'Returns a single kanban board with its columns + cards + label palette.',
|
|
77
77
|
method: 'GET',
|
|
78
78
|
apiResponse: true,
|
|
79
|
-
async handle(request) {
|
|
80
|
-
|
|
81
|
-
// `request.param('id')` / `request.params.id`. Both shapes appear
|
|
82
|
-
// in this codebase depending on how the route was registered;
|
|
83
|
-
// covering both keeps the action portable.
|
|
84
|
-
const rawId = (request as any)?.params?.id
|
|
85
|
-
?? (request as any)?.param?.('id')
|
|
86
|
-
?? null
|
|
87
|
-
const id = Number(rawId)
|
|
79
|
+
async handle(request: RequestInstance) {
|
|
80
|
+
const id = Number(request.getParam('id'))
|
|
88
81
|
if (!Number.isFinite(id) || id <= 0) {
|
|
89
82
|
return kanbanError('Invalid board id', 400)
|
|
90
83
|
}
|
|
@@ -114,8 +107,8 @@ export default new Action({
|
|
|
114
107
|
).execute() as Promise<LabelRow[]>,
|
|
115
108
|
])
|
|
116
109
|
|
|
117
|
-
// Pivot lookups for label + assignee chips on card previews
|
|
118
|
-
//
|
|
110
|
+
// Pivot lookups for label + assignee chips on card previews.
|
|
111
|
+
// Two batched JOINs keyed off the
|
|
119
112
|
// board's card list — single round-trip per pivot, then group
|
|
120
113
|
// client-side into `cardId → list` maps.
|
|
121
114
|
const [cardLabelRows, cardAssigneeRows] = await Promise.all([
|
|
@@ -208,8 +201,7 @@ export default new Action({
|
|
|
208
201
|
}
|
|
209
202
|
}
|
|
210
203
|
catch (err) {
|
|
211
|
-
|
|
212
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
204
|
+
return kanbanActionError(err, 'BoardShowAction')
|
|
213
205
|
}
|
|
214
206
|
},
|
|
215
207
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { db } from '@stacksjs/database'
|
|
3
4
|
import { Board } from '@stacksjs/orm'
|
|
4
|
-
import { kanbanError } from './kanban-response'
|
|
5
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
5
6
|
|
|
6
7
|
interface BoardInput {
|
|
7
8
|
name?: unknown
|
|
@@ -11,7 +12,7 @@ interface BoardInput {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
|
-
* `POST /api/dashboard/kanban/boards
|
|
15
|
+
* `POST /api/dashboard/kanban/boards`.
|
|
15
16
|
*
|
|
16
17
|
* Creates a new board with sensible defaults and appends it to the
|
|
17
18
|
* end of the boards list (`position = max(position) + 1` so the new
|
|
@@ -28,8 +29,8 @@ export default new Action({
|
|
|
28
29
|
description: 'Creates a new kanban board.',
|
|
29
30
|
method: 'POST',
|
|
30
31
|
apiResponse: true,
|
|
31
|
-
async handle(request) {
|
|
32
|
-
const body = (
|
|
32
|
+
async handle(request: RequestInstance<BoardInput>) {
|
|
33
|
+
const body = request.all()
|
|
33
34
|
|
|
34
35
|
const name = typeof body.name === 'string' ? body.name.trim() : ''
|
|
35
36
|
if (!name || name.length > 120) {
|
|
@@ -76,8 +77,7 @@ export default new Action({
|
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
catch (err) {
|
|
79
|
-
|
|
80
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
80
|
+
return kanbanActionError(err, 'BoardStoreAction')
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
})
|