@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
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { createPermission, findPermission } from '@stacksjs/auth'
|
|
3
4
|
import { response } from '@stacksjs/router'
|
|
5
|
+
import { rbacActionError } from './rbac-response'
|
|
4
6
|
|
|
5
7
|
interface PermissionInput {
|
|
6
8
|
name?: unknown
|
|
@@ -20,8 +22,8 @@ export default new Action({
|
|
|
20
22
|
description: 'Create a new permission.',
|
|
21
23
|
method: 'POST',
|
|
22
24
|
apiResponse: true,
|
|
23
|
-
async handle(request) {
|
|
24
|
-
const body = (
|
|
25
|
+
async handle(request: RequestInstance<PermissionInput>) {
|
|
26
|
+
const body = request.all()
|
|
25
27
|
|
|
26
28
|
const name = typeof body.name === 'string' ? body.name.trim() : ''
|
|
27
29
|
if (!name || name.length > 100) {
|
|
@@ -53,8 +55,7 @@ export default new Action({
|
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
catch (err) {
|
|
56
|
-
|
|
57
|
-
return response.json({ error: err instanceof Error ? err.message : 'unknown error' }, 500)
|
|
58
|
+
return rbacActionError(err, 'The permission could not be created.', 'PermissionStoreAction')
|
|
58
59
|
}
|
|
59
60
|
},
|
|
60
61
|
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions'
|
|
2
2
|
import { getAllPermissions } from '@stacksjs/auth'
|
|
3
|
-
import {
|
|
3
|
+
import { rbacActionError } from './rbac-response'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* `GET /api/dashboard/rbac/permissions` (stacksjs/stacks#1845).
|
|
@@ -28,8 +28,7 @@ export default new Action({
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
catch (err) {
|
|
31
|
-
|
|
32
|
-
return response.json({ permissions: [], error: err instanceof Error ? err.message : 'unknown error' }, 500)
|
|
31
|
+
return rbacActionError(err, 'Permissions could not be loaded.', 'PermissionsIndexAction')
|
|
33
32
|
}
|
|
34
33
|
},
|
|
35
34
|
})
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { deleteRole } from '@stacksjs/auth'
|
|
3
4
|
import { response } from '@stacksjs/router'
|
|
5
|
+
import { rbacActionError } from './rbac-response'
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* `DELETE /api/dashboard/rbac/roles/:name` (stacksjs/stacks#1845).
|
|
@@ -16,13 +18,12 @@ export default new Action({
|
|
|
16
18
|
description: 'Hard-delete a role + its pivot rows.',
|
|
17
19
|
method: 'DELETE',
|
|
18
20
|
apiResponse: true,
|
|
19
|
-
async handle(request) {
|
|
20
|
-
const name =
|
|
21
|
+
async handle(request: RequestInstance) {
|
|
22
|
+
const name = request.getParam('name').trim()
|
|
21
23
|
if (!name) {
|
|
22
24
|
return response.json({ error: '`name` route param is required.' }, 400)
|
|
23
25
|
}
|
|
24
|
-
const
|
|
25
|
-
const guardName = (url.searchParams.get('guard') || 'web').trim()
|
|
26
|
+
const guardName = String(request.get('guard', 'web')).trim()
|
|
26
27
|
if (!guardName || guardName.length > 60) {
|
|
27
28
|
return response.json({ error: 'Invalid guard name.' }, 400)
|
|
28
29
|
}
|
|
@@ -32,8 +33,7 @@ export default new Action({
|
|
|
32
33
|
return { deleted: true, name, guardName }
|
|
33
34
|
}
|
|
34
35
|
catch (err) {
|
|
35
|
-
|
|
36
|
-
return response.json({ error: err instanceof Error ? err.message : 'unknown error' }, 500)
|
|
36
|
+
return rbacActionError(err, 'The role could not be deleted.', 'RoleDestroyAction')
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
39
|
})
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { findRole, getRolePermissions } from '@stacksjs/auth'
|
|
3
4
|
import { response } from '@stacksjs/router'
|
|
5
|
+
import { rbacActionError } from './rbac-response'
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* `GET /api/dashboard/rbac/roles/:name/permissions` (stacksjs/stacks#1845).
|
|
@@ -15,13 +17,12 @@ export default new Action({
|
|
|
15
17
|
description: 'List permissions attached to one role.',
|
|
16
18
|
method: 'GET',
|
|
17
19
|
apiResponse: true,
|
|
18
|
-
async handle(request) {
|
|
19
|
-
const name =
|
|
20
|
+
async handle(request: RequestInstance) {
|
|
21
|
+
const name = request.getParam('name').trim()
|
|
20
22
|
if (!name) {
|
|
21
23
|
return response.json({ error: '`name` route param is required.' }, 400)
|
|
22
24
|
}
|
|
23
|
-
const
|
|
24
|
-
const guardName = (url.searchParams.get('guard') || 'web').trim()
|
|
25
|
+
const guardName = String(request.get('guard', 'web')).trim()
|
|
25
26
|
if (!guardName || guardName.length > 60) {
|
|
26
27
|
return response.json({ error: 'Invalid guard name.' }, 400)
|
|
27
28
|
}
|
|
@@ -42,8 +43,7 @@ export default new Action({
|
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
catch (err) {
|
|
45
|
-
|
|
46
|
-
return response.json({ permissions: [], error: err instanceof Error ? err.message : 'unknown error' }, 500)
|
|
46
|
+
return rbacActionError(err, 'Role permissions could not be loaded.', 'RolePermissionsShowAction')
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
49
|
})
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { getRolePermissions, findRole, syncRolePermissions } from '@stacksjs/auth'
|
|
3
4
|
import { response } from '@stacksjs/router'
|
|
5
|
+
import { rbacActionError } from './rbac-response'
|
|
4
6
|
|
|
5
7
|
interface SyncInput {
|
|
6
8
|
permissions?: unknown
|
|
@@ -19,13 +21,13 @@ export default new Action({
|
|
|
19
21
|
description: 'Replace the permission set attached to one role.',
|
|
20
22
|
method: 'POST',
|
|
21
23
|
apiResponse: true,
|
|
22
|
-
async handle(request) {
|
|
23
|
-
const roleName =
|
|
24
|
+
async handle(request: RequestInstance<SyncInput>) {
|
|
25
|
+
const roleName = request.getParam('name').trim()
|
|
24
26
|
if (!roleName) {
|
|
25
27
|
return response.json({ error: '`name` route param is required.' }, 400)
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
const body = (
|
|
30
|
+
const body = request.all()
|
|
29
31
|
if (!Array.isArray(body.permissions)) {
|
|
30
32
|
return response.json({ error: '`permissions` must be an array of permission names (possibly empty).' }, 400)
|
|
31
33
|
}
|
|
@@ -56,12 +58,7 @@ export default new Action({
|
|
|
56
58
|
}
|
|
57
59
|
}
|
|
58
60
|
catch (err) {
|
|
59
|
-
|
|
60
|
-
if (msg.includes('not found')) {
|
|
61
|
-
return response.json({ error: msg }, 400)
|
|
62
|
-
}
|
|
63
|
-
console.error('[dashboard/rbac] RolePermissionsSyncAction failed:', err)
|
|
64
|
-
return response.json({ error: msg }, 500)
|
|
61
|
+
return rbacActionError(err, 'Role permissions could not be updated.', 'RolePermissionsSyncAction')
|
|
65
62
|
}
|
|
66
63
|
},
|
|
67
64
|
})
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { createRole, findRole } from '@stacksjs/auth'
|
|
3
4
|
import { response } from '@stacksjs/router'
|
|
5
|
+
import { rbacActionError } from './rbac-response'
|
|
4
6
|
|
|
5
7
|
interface RoleInput {
|
|
6
8
|
name?: unknown
|
|
@@ -25,8 +27,8 @@ export default new Action({
|
|
|
25
27
|
description: 'Create a new role.',
|
|
26
28
|
method: 'POST',
|
|
27
29
|
apiResponse: true,
|
|
28
|
-
async handle(request) {
|
|
29
|
-
const body = (
|
|
30
|
+
async handle(request: RequestInstance<RoleInput>) {
|
|
31
|
+
const body = request.all()
|
|
30
32
|
|
|
31
33
|
const name = typeof body.name === 'string' ? body.name.trim() : ''
|
|
32
34
|
if (!name || name.length > 60) {
|
|
@@ -58,8 +60,7 @@ export default new Action({
|
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
62
|
catch (err) {
|
|
61
|
-
|
|
62
|
-
return response.json({ error: err instanceof Error ? err.message : 'unknown error' }, 500)
|
|
63
|
+
return rbacActionError(err, 'The role could not be created.', 'RoleStoreAction')
|
|
63
64
|
}
|
|
64
65
|
},
|
|
65
66
|
})
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions'
|
|
2
2
|
import { getAllRoles } from '@stacksjs/auth'
|
|
3
|
-
import {
|
|
3
|
+
import { rbacActionError } from './rbac-response'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* `GET /api/dashboard/rbac/roles` (stacksjs/stacks#1845).
|
|
7
7
|
*
|
|
8
8
|
* Returns every role in the system. The dashboard's permissions page
|
|
9
9
|
* renders this as a list users can pick from when assigning roles.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* yelling at the user.
|
|
10
|
+
* Operational store failures use a real 500 response so the client can
|
|
11
|
+
* distinguish an unavailable RBAC store from a valid empty role list.
|
|
13
12
|
*/
|
|
14
13
|
export default new Action({
|
|
15
14
|
name: 'Dashboard RBAC Roles Index',
|
|
@@ -30,8 +29,7 @@ export default new Action({
|
|
|
30
29
|
}
|
|
31
30
|
}
|
|
32
31
|
catch (err) {
|
|
33
|
-
|
|
34
|
-
return response.json({ roles: [], error: err instanceof Error ? err.message : 'unknown error' }, 500)
|
|
32
|
+
return rbacActionError(err, 'Roles could not be loaded.', 'RolesIndexAction')
|
|
35
33
|
}
|
|
36
34
|
},
|
|
37
35
|
})
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { getUserRoles } from '@stacksjs/auth'
|
|
3
4
|
import { User } from '@stacksjs/orm'
|
|
4
5
|
import { response } from '@stacksjs/router'
|
|
6
|
+
import { rbacActionError } from './rbac-response'
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* `GET /api/dashboard/rbac/users/:id/roles` (stacksjs/stacks#1845).
|
|
@@ -16,14 +18,12 @@ export default new Action({
|
|
|
16
18
|
description: 'List roles attached to one user.',
|
|
17
19
|
method: 'GET',
|
|
18
20
|
apiResponse: true,
|
|
19
|
-
async handle(request) {
|
|
20
|
-
const
|
|
21
|
-
const userId = Number(rawId)
|
|
21
|
+
async handle(request: RequestInstance) {
|
|
22
|
+
const userId = Number(request.getParam('id'))
|
|
22
23
|
if (!Number.isFinite(userId) || userId <= 0) {
|
|
23
24
|
return response.json({ error: 'Invalid user id.' }, 400)
|
|
24
25
|
}
|
|
25
|
-
const
|
|
26
|
-
const guardName = (url.searchParams.get('guard') || 'web').trim()
|
|
26
|
+
const guardName = String(request.get('guard', 'web')).trim()
|
|
27
27
|
if (!guardName || guardName.length > 60) {
|
|
28
28
|
return response.json({ error: 'Invalid guard name.' }, 400)
|
|
29
29
|
}
|
|
@@ -44,8 +44,7 @@ export default new Action({
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
catch (err) {
|
|
47
|
-
|
|
48
|
-
return response.json({ userId, roles: [], error: err instanceof Error ? err.message : 'unknown error' }, 500)
|
|
47
|
+
return rbacActionError(err, 'User roles could not be loaded.', 'UserRolesShowAction')
|
|
49
48
|
}
|
|
50
49
|
},
|
|
51
50
|
})
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { getUserRoles, syncRoles } from '@stacksjs/auth'
|
|
3
4
|
import { User } from '@stacksjs/orm'
|
|
4
5
|
import { response } from '@stacksjs/router'
|
|
6
|
+
import { rbacActionError } from './rbac-response'
|
|
5
7
|
|
|
6
8
|
interface SyncInput {
|
|
7
9
|
roles?: unknown
|
|
@@ -24,14 +26,13 @@ export default new Action({
|
|
|
24
26
|
description: 'Replace the role set attached to one user.',
|
|
25
27
|
method: 'POST',
|
|
26
28
|
apiResponse: true,
|
|
27
|
-
async handle(request) {
|
|
28
|
-
const
|
|
29
|
-
const userId = Number(rawId)
|
|
29
|
+
async handle(request: RequestInstance<SyncInput>) {
|
|
30
|
+
const userId = Number(request.getParam('id'))
|
|
30
31
|
if (!Number.isFinite(userId) || userId <= 0) {
|
|
31
32
|
return response.json({ error: 'Invalid user id.' }, 400)
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
const body = (
|
|
35
|
+
const body = request.all()
|
|
35
36
|
if (!Array.isArray(body.roles)) {
|
|
36
37
|
return response.json({ error: '`roles` must be an array of role names (possibly empty).' }, 400)
|
|
37
38
|
}
|
|
@@ -60,15 +61,7 @@ export default new Action({
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
catch (err) {
|
|
63
|
-
|
|
64
|
-
// The facade throws "Role 'X' not found" — surface as 400 so
|
|
65
|
-
// the UI can show "this role doesn't exist anymore" rather
|
|
66
|
-
// than a 500.
|
|
67
|
-
if (msg.includes('not found')) {
|
|
68
|
-
return response.json({ error: msg }, 400)
|
|
69
|
-
}
|
|
70
|
-
console.error('[dashboard/rbac] UserRolesSyncAction failed:', err)
|
|
71
|
-
return response.json({ error: msg }, 500)
|
|
64
|
+
return rbacActionError(err, 'User roles could not be updated.', 'UserRolesSyncAction')
|
|
72
65
|
}
|
|
73
66
|
},
|
|
74
67
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions'
|
|
2
2
|
import { User } from '@stacksjs/orm'
|
|
3
|
-
import { response } from '@stacksjs/router'
|
|
4
3
|
import { safeGet } from '../../../../resources/functions/dashboard/data'
|
|
4
|
+
import { rbacActionError } from './rbac-response'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* `GET /api/dashboard/rbac/users` (stacksjs/stacks#1845).
|
|
@@ -40,8 +40,7 @@ export default new Action({
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
catch (err) {
|
|
43
|
-
|
|
44
|
-
return response.json({ users: [], error: err instanceof Error ? err.message : 'unknown error' }, 500)
|
|
43
|
+
return rbacActionError(err, 'Users could not be loaded.', 'UsersListAction')
|
|
45
44
|
}
|
|
46
45
|
},
|
|
47
46
|
})
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { RbacEntityNotFoundError } from '@stacksjs/auth'
|
|
2
|
+
import { response } from '@stacksjs/router'
|
|
3
|
+
|
|
4
|
+
export function rbacActionError(error: unknown, fallback: string, action: string): Response {
|
|
5
|
+
if (error instanceof RbacEntityNotFoundError)
|
|
6
|
+
return response.json({ error: error.message }, 400)
|
|
7
|
+
|
|
8
|
+
console.error(`[dashboard/rbac] ${action} failed:`, error)
|
|
9
|
+
return response.json({ error: fallback }, 500)
|
|
10
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions'
|
|
2
2
|
import { config } from '@stacksjs/config'
|
|
3
3
|
import { Websocket } from '@stacksjs/orm'
|
|
4
|
-
import {
|
|
4
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
5
5
|
import { buildRealtimeStats } from './realtime-stats'
|
|
6
6
|
|
|
7
7
|
export default new Action({
|
|
@@ -36,9 +36,7 @@ export default new Action({
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
catch (error) {
|
|
39
|
-
return
|
|
40
|
-
message: error instanceof Error ? error.message : 'Realtime events could not be loaded.',
|
|
41
|
-
}, 503)
|
|
39
|
+
return dashboardOperationalError(error, 'Realtime events could not be loaded.', 'RealtimeStatsAction')
|
|
42
40
|
}
|
|
43
41
|
},
|
|
44
42
|
})
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { env } from '@stacksjs/env'
|
|
3
4
|
import { readdirSync } from 'node:fs'
|
|
4
5
|
import { basename, extname, join } from 'node:path'
|
|
5
6
|
import process from 'node:process'
|
|
7
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
6
8
|
|
|
7
9
|
interface SearchableModel {
|
|
8
10
|
name: string
|
|
@@ -152,8 +154,15 @@ async function loadSearchableModels(): Promise<SearchableModel[]> {
|
|
|
152
154
|
}
|
|
153
155
|
|
|
154
156
|
function catalog(): Promise<SearchableModel[]> {
|
|
155
|
-
modelCatalog.promise
|
|
156
|
-
|
|
157
|
+
if (modelCatalog.promise)
|
|
158
|
+
return modelCatalog.promise
|
|
159
|
+
|
|
160
|
+
const loading = loadSearchableModels().catch((error) => {
|
|
161
|
+
modelCatalog.promise = null
|
|
162
|
+
throw error
|
|
163
|
+
})
|
|
164
|
+
modelCatalog.promise = loading
|
|
165
|
+
return loading
|
|
157
166
|
}
|
|
158
167
|
|
|
159
168
|
function rowValue(row: Record<string, unknown>, field: string): string {
|
|
@@ -184,67 +193,72 @@ export default new Action({
|
|
|
184
193
|
method: 'GET',
|
|
185
194
|
apiResponse: true,
|
|
186
195
|
async handle(request: RequestInstance) {
|
|
187
|
-
const q = String(request
|
|
196
|
+
const q = String(request.get('q') || '').trim().slice(0, MAX_QUERY_LENGTH)
|
|
188
197
|
if (!q) return { results: {} }
|
|
189
198
|
|
|
190
|
-
const { Database } = await import('bun:sqlite')
|
|
191
|
-
const db = new Database(env.DB_DATABASE_PATH || 'database/stacks.sqlite', { readonly: true })
|
|
192
|
-
const results: Record<string, SearchResult[]> = {}
|
|
193
|
-
const unavailable: SearchUnavailable[] = []
|
|
194
|
-
|
|
195
199
|
try {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
200
|
+
const { Database } = await import('bun:sqlite')
|
|
201
|
+
const db = new Database(env.DB_DATABASE_PATH || 'database/stacks.sqlite', { readonly: true })
|
|
202
|
+
const results: Record<string, SearchResult[]> = {}
|
|
203
|
+
const unavailable: SearchUnavailable[] = []
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
for (const model of await catalog()) {
|
|
207
|
+
if (Object.keys(results).length >= MAX_GROUPS) break
|
|
208
|
+
|
|
209
|
+
const columns = db.query(`PRAGMA table_info(${model.table})`).all() as Array<{ name: string }>
|
|
210
|
+
if (columns.length === 0) {
|
|
211
|
+
unavailable.push({
|
|
212
|
+
model: model.name,
|
|
213
|
+
reason: `Table ${model.table} has not been migrated.`,
|
|
214
|
+
})
|
|
215
|
+
continue
|
|
216
|
+
}
|
|
207
217
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
218
|
+
const available = new Set(columns.map(column => column.name))
|
|
219
|
+
const fields = model.fields.filter(field => available.has(field))
|
|
220
|
+
if (fields.length === 0) {
|
|
221
|
+
unavailable.push({
|
|
222
|
+
model: model.name,
|
|
223
|
+
reason: 'Declared useSearch fields are not present in the migrated table.',
|
|
224
|
+
})
|
|
225
|
+
continue
|
|
226
|
+
}
|
|
217
227
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
if (rows.length === 0) continue
|
|
231
|
-
|
|
232
|
-
results[model.name] = rows.map((row, index) => {
|
|
233
|
-
const title = resultTitle(model, row)
|
|
234
|
-
return {
|
|
235
|
-
id: (row.id as string | number | undefined) ?? index,
|
|
236
|
-
title,
|
|
237
|
-
subtitle: resultSubtitle(model, row, title),
|
|
238
|
-
href: `/models/${model.slug}?q=${encodeURIComponent(q)}`,
|
|
239
|
-
icon: model.icon,
|
|
228
|
+
const selected = [...new Set(['id', ...fields])].filter(field => available.has(field))
|
|
229
|
+
const where = fields.map(field => `${field} LIKE ? COLLATE NOCASE`).join(' OR ')
|
|
230
|
+
const bindings = fields.map(() => `%${q}%`)
|
|
231
|
+
let rows: Array<Record<string, unknown>>
|
|
232
|
+
try {
|
|
233
|
+
rows = db
|
|
234
|
+
.query(`SELECT ${selected.join(', ')} FROM ${model.table} WHERE ${where} LIMIT ?`)
|
|
235
|
+
.all(...bindings, RESULTS_PER_MODEL) as Array<Record<string, unknown>>
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
throw new Error(`Could not search model ${model.name}: ${error instanceof Error ? error.message : String(error)}`)
|
|
240
239
|
}
|
|
241
|
-
|
|
240
|
+
if (rows.length === 0) continue
|
|
241
|
+
|
|
242
|
+
results[model.name] = rows.map((row, index) => {
|
|
243
|
+
const title = resultTitle(model, row)
|
|
244
|
+
return {
|
|
245
|
+
id: (row.id as string | number | undefined) ?? index,
|
|
246
|
+
title,
|
|
247
|
+
subtitle: resultSubtitle(model, row, title),
|
|
248
|
+
href: `/models/${model.slug}?q=${encodeURIComponent(q)}`,
|
|
249
|
+
icon: model.icon,
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
}
|
|
242
253
|
}
|
|
254
|
+
finally {
|
|
255
|
+
db.close()
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return { results, unavailable }
|
|
243
259
|
}
|
|
244
|
-
|
|
245
|
-
|
|
260
|
+
catch (error) {
|
|
261
|
+
return dashboardOperationalError(error, 'Search results could not be loaded.', 'GlobalSearchAction')
|
|
246
262
|
}
|
|
247
|
-
|
|
248
|
-
return { results, unavailable }
|
|
249
263
|
},
|
|
250
264
|
})
|
|
@@ -2,7 +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 { response } from '@stacksjs/router'
|
|
5
|
+
import { teamOperationalError } from '../../Teams/team-response'
|
|
5
6
|
import {
|
|
7
|
+
changedRows,
|
|
6
8
|
generateInvitationToken,
|
|
7
9
|
hashInvitationToken,
|
|
8
10
|
parsePositiveId,
|
|
@@ -21,27 +23,43 @@ export default new Action({
|
|
|
21
23
|
if (!teamId || !invitationId)
|
|
22
24
|
return response.json({ message: 'Invalid team or invitation id.' }, 400)
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
let invitation
|
|
27
|
+
try {
|
|
28
|
+
invitation = await (db as any)
|
|
29
|
+
.selectFrom('team_invitations')
|
|
30
|
+
.where('id', '=', invitationId)
|
|
31
|
+
.where('team_id', '=', teamId)
|
|
32
|
+
.select(['id', 'status', 'token_hash'])
|
|
33
|
+
.executeTakeFirst()
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return teamOperationalError(error, 'Invitation could not be loaded.', 'TeamInvitationDestroyAction.lookup', 500)
|
|
37
|
+
}
|
|
30
38
|
if (!invitation)
|
|
31
39
|
return response.json({ message: 'Invitation not found.' }, 404)
|
|
32
40
|
if (invitation.status !== 'pending')
|
|
33
41
|
return response.json({ message: 'Only pending invitations can be revoked.' }, 409)
|
|
34
42
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
try {
|
|
44
|
+
const result = await (db as any)
|
|
45
|
+
.updateTable('team_invitations')
|
|
46
|
+
.set({
|
|
47
|
+
status: 'revoked',
|
|
48
|
+
token_hash: hashInvitationToken(generateInvitationToken()),
|
|
49
|
+
pending_key: null,
|
|
50
|
+
updated_at: sqlTimestamp(),
|
|
51
|
+
})
|
|
52
|
+
.where('id', '=', invitationId)
|
|
53
|
+
.where('team_id', '=', teamId)
|
|
54
|
+
.where('token_hash', '=', String(invitation.token_hash))
|
|
55
|
+
.where('status', '=', 'pending')
|
|
56
|
+
.executeTakeFirst()
|
|
57
|
+
if (changedRows(result) !== 1)
|
|
58
|
+
return response.json({ message: 'The invitation changed before it could be revoked.' }, 409)
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
return teamOperationalError(error, 'Invitation could not be revoked.', 'TeamInvitationDestroyAction.update', 500)
|
|
62
|
+
}
|
|
45
63
|
|
|
46
64
|
return response.noContent()
|
|
47
65
|
},
|