@stacksjs/defaults 0.70.356 → 0.70.358
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 +5 -1
- package/ai/skills/stacks-browse/scripts/browse.ts +61 -6
- package/ai/skills/stacks-orm/SKILL.md +6 -0
- package/app/Actions/Dashboard/Actions/GetActions.ts +15 -9
- package/app/Actions/Dashboard/Analytics/EventStoreAction.ts +21 -9
- package/app/Actions/Dashboard/Commerce/ProductUnitDefaultAction.ts +12 -3
- package/app/Actions/Dashboard/Commerce/TaxRateDefaultAction.ts +12 -3
- package/app/Actions/Dashboard/Content/AuthorDestroyAction.ts +23 -9
- package/app/Actions/Dashboard/Content/AuthorIndexAction.ts +32 -26
- package/app/Actions/Dashboard/Content/AuthorStoreAction.ts +44 -24
- package/app/Actions/Dashboard/Content/AuthorUpdateAction.ts +48 -26
- package/app/Actions/Dashboard/Content/CategoryDestroyAction.ts +27 -8
- package/app/Actions/Dashboard/Content/CategoryIndexAction.ts +21 -15
- package/app/Actions/Dashboard/Content/CategoryStoreAction.ts +47 -29
- package/app/Actions/Dashboard/Content/CommentDestroyAction.ts +17 -4
- package/app/Actions/Dashboard/Content/CommentIndexAction.ts +23 -17
- package/app/Actions/Dashboard/Content/CommentUpdateAction.ts +30 -12
- package/app/Actions/Dashboard/Content/ContentDashboardAction.ts +43 -37
- package/app/Actions/Dashboard/Content/PageDestroyAction.ts +17 -4
- package/app/Actions/Dashboard/Content/PageIndexAction.ts +23 -17
- package/app/Actions/Dashboard/Content/PageStoreAction.ts +33 -18
- package/app/Actions/Dashboard/Content/PageUpdateAction.ts +40 -22
- package/app/Actions/Dashboard/Content/PostDestroyAction.ts +24 -11
- package/app/Actions/Dashboard/Content/PostIndexAction.ts +55 -49
- package/app/Actions/Dashboard/Content/PostStoreAction.ts +43 -33
- package/app/Actions/Dashboard/Content/PostUpdateAction.ts +46 -34
- package/app/Actions/Dashboard/Content/TagDestroyAction.ts +27 -8
- package/app/Actions/Dashboard/Content/TagIndexAction.ts +31 -25
- package/app/Actions/Dashboard/Content/TagStoreAction.ts +44 -23
- package/app/Actions/Dashboard/Content/content-input.ts +4 -4
- package/app/Actions/Dashboard/Content/post-input.ts +60 -8
- package/app/Actions/Dashboard/DashboardHealthAction.ts +34 -14
- package/app/Actions/Dashboard/DashboardHomeAction.test.ts +1 -1
- package/app/Actions/Dashboard/DashboardHomeAction.ts +31 -4
- package/app/Actions/Dashboard/DashboardStatsAction.ts +6 -1
- package/app/Actions/Dashboard/Email/InboxSendAction.ts +14 -7
- package/app/Actions/Dashboard/Infrastructure/CommandIndexAction.ts +33 -27
- package/app/Actions/Dashboard/Infrastructure/EnvironmentIndexAction.ts +10 -4
- package/app/Actions/Dashboard/Infrastructure/EnvironmentUpdateAction.ts +8 -1
- package/app/Actions/Dashboard/Infrastructure/RequestIndexAction.ts +24 -18
- package/app/Actions/Dashboard/Infrastructure/ServerIndexAction.ts +15 -9
- package/app/Actions/Dashboard/Infrastructure/ServerShowAction.ts +9 -2
- package/app/Actions/Dashboard/Marketing/CampaignCancelAction.ts +23 -5
- package/app/Actions/Dashboard/Marketing/CampaignDestroyAction.ts +26 -17
- package/app/Actions/Dashboard/Marketing/CampaignIndexAction.ts +49 -43
- package/app/Actions/Dashboard/Marketing/CampaignScheduleAction.ts +31 -25
- package/app/Actions/Dashboard/Marketing/CampaignSendAction.ts +28 -22
- package/app/Actions/Dashboard/Marketing/CampaignStoreAction.ts +15 -10
- package/app/Actions/Dashboard/Marketing/CampaignUpdateAction.ts +19 -12
- package/app/Actions/Dashboard/Marketing/ListDestroyAction.ts +34 -25
- package/app/Actions/Dashboard/Marketing/ListIndexAction.ts +32 -26
- package/app/Actions/Dashboard/Marketing/ListStoreAction.ts +31 -18
- package/app/Actions/Dashboard/Marketing/ListUpdateAction.ts +35 -19
- package/app/Actions/Dashboard/Marketing/SocialPostDestroyAction.ts +15 -5
- package/app/Actions/Dashboard/Marketing/SocialPostIndexAction.ts +11 -5
- package/app/Actions/Dashboard/Marketing/SocialPostStoreAction.ts +19 -13
- package/app/Actions/Dashboard/Marketing/SocialPostUpdateAction.ts +22 -12
- package/app/Actions/Dashboard/Marketing/campaign-delivery.ts +34 -0
- package/app/Actions/Dashboard/Marketing/marketing-list-records.test.ts +10 -0
- package/app/Actions/Dashboard/Marketing/marketing-list-records.ts +10 -0
- package/app/Actions/Dashboard/Marketing/marketing-response.ts +28 -0
- package/app/Actions/Dashboard/Marketing/social-post-records.test.ts +13 -0
- package/app/Actions/Dashboard/Marketing/social-post-records.ts +22 -4
- package/app/Actions/Dashboard/Models/GetModels.ts +14 -7
- package/app/Actions/Dashboard/Models/GetSubscriberCount.ts +8 -1
- package/app/Actions/Dashboard/Models/GetUserCount.ts +8 -1
- package/app/Actions/Dashboard/Releases/ReleaseIndexAction.ts +11 -5
- package/app/Actions/Dashboard/Settings/MailSettingsGetAction.ts +10 -4
- package/app/Actions/Dashboard/Settings/MailSettingsUpdateAction.ts +8 -1
- package/ide/vscode/package.json +1 -1
- package/package.json +1 -1
- package/resources/components/Dashboard/UI/Modal.stx +60 -10
- package/views/dashboard/stores/auth.ts +18 -2
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions'
|
|
3
|
+
import { db } from '@stacksjs/database'
|
|
4
|
+
import { transaction } from '@stacksjs/orm'
|
|
3
5
|
import { response } from '@stacksjs/router'
|
|
6
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
4
7
|
import { rowId } from './content-input'
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* `DELETE /api/dashboard/categories/{id}` — deletes a CMS category.
|
|
8
11
|
*
|
|
9
|
-
* Detaches related posts through the model
|
|
10
|
-
* category
|
|
12
|
+
* Detaches related posts through the model-declared pivot before removing the
|
|
13
|
+
* category, with both writes committed by the same transaction.
|
|
11
14
|
*/
|
|
12
15
|
export default new Action({
|
|
13
16
|
name: 'CategoryDestroyAction',
|
|
@@ -19,13 +22,29 @@ export default new Action({
|
|
|
19
22
|
if (!id)
|
|
20
23
|
return response.json({ message: 'A valid category id is required.' }, 422)
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
try {
|
|
26
|
+
const deleted = await transaction(async (rawTrx) => {
|
|
27
|
+
const trx = rawTrx as unknown as typeof db
|
|
28
|
+
const category = await trx.selectFrom('categories').select(['id']).where('id', '=', id).executeTakeFirst()
|
|
29
|
+
if (!category)
|
|
30
|
+
return false
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
await trx
|
|
33
|
+
.deleteFrom('categorizable_models')
|
|
34
|
+
.where('category_id', '=', id)
|
|
35
|
+
.where('categorizable_type', '=', 'posts')
|
|
36
|
+
.execute()
|
|
37
|
+
await trx.deleteFrom('categories').where('id', '=', id).execute()
|
|
38
|
+
return true
|
|
39
|
+
})
|
|
28
40
|
|
|
29
|
-
|
|
41
|
+
if (!deleted)
|
|
42
|
+
return response.json({ message: 'Category not found.' }, 404)
|
|
43
|
+
|
|
44
|
+
return response.json({ message: 'Category deleted.', id })
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return dashboardOperationalError(error, 'Category could not be deleted.', 'CategoryDestroyAction', 500)
|
|
48
|
+
}
|
|
30
49
|
},
|
|
31
50
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions'
|
|
2
2
|
import { db } from '@stacksjs/database'
|
|
3
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
3
4
|
|
|
4
5
|
interface CategoryRow {
|
|
5
6
|
id: number
|
|
@@ -29,22 +30,27 @@ export default new Action({
|
|
|
29
30
|
method: 'GET',
|
|
30
31
|
apiResponse: true,
|
|
31
32
|
async handle() {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
try {
|
|
34
|
+
const rows = await db
|
|
35
|
+
.selectFrom('categories')
|
|
36
|
+
.selectAll()
|
|
37
|
+
.orderBy('created_at', 'desc')
|
|
38
|
+
.execute() as unknown as CategoryRow[]
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
const categories = rows.map(row => ({
|
|
41
|
+
id: Number(row.id),
|
|
42
|
+
name: String(row.name || ''),
|
|
43
|
+
slug: String(row.slug || ''),
|
|
44
|
+
description: String(row.description || ''),
|
|
45
|
+
is_active: row.is_active == null ? true : Boolean(row.is_active),
|
|
46
|
+
created_at: row.created_at || null,
|
|
47
|
+
updated_at: row.updated_at || null,
|
|
48
|
+
}))
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
return { categories }
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
return dashboardOperationalError(error, 'Categories could not be loaded.', 'CategoryIndexAction')
|
|
54
|
+
}
|
|
49
55
|
},
|
|
50
56
|
})
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions'
|
|
3
3
|
import { db } from '@stacksjs/database'
|
|
4
|
+
import { transaction } from '@stacksjs/orm'
|
|
4
5
|
import { response } from '@stacksjs/router'
|
|
5
6
|
import { randomUUIDv7 } from 'bun'
|
|
7
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
6
8
|
import { findRow, insertedId, slugify, str, timestamp } from './content-input'
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -26,40 +28,56 @@ export default new Action({
|
|
|
26
28
|
if (!slug)
|
|
27
29
|
return response.json({ message: 'Slug could not be derived from the name; enter one.' }, 422)
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
try {
|
|
32
|
+
const category = await transaction(async (rawTrx) => {
|
|
33
|
+
const trx = rawTrx as unknown as typeof db
|
|
34
|
+
const duplicate = await trx
|
|
35
|
+
.selectFrom('categories')
|
|
36
|
+
.select(['id'])
|
|
37
|
+
.where('slug', '=', slug)
|
|
38
|
+
.executeTakeFirst()
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
if (duplicate)
|
|
41
|
+
return null
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
const now = timestamp()
|
|
44
|
+
const displayOrder = await trx
|
|
45
|
+
.selectFrom('categories')
|
|
46
|
+
.select(db.fn.max('display_order').as('max_display_order'))
|
|
47
|
+
.executeTakeFirst()
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
const result = await trx
|
|
50
|
+
.insertInto('categories')
|
|
51
|
+
.values({
|
|
52
|
+
uuid: randomUUIDv7(),
|
|
53
|
+
name,
|
|
54
|
+
slug,
|
|
55
|
+
description,
|
|
56
|
+
is_active: 1,
|
|
57
|
+
display_order: Number(displayOrder?.max_display_order || 0) + 1,
|
|
58
|
+
created_at: now,
|
|
59
|
+
updated_at: now,
|
|
60
|
+
} as any)
|
|
61
|
+
.executeTakeFirst()
|
|
57
62
|
|
|
58
|
-
|
|
63
|
+
const id = insertedId(result)
|
|
59
64
|
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
if (!id)
|
|
66
|
+
throw new Error('Category insert did not return an id.')
|
|
62
67
|
|
|
63
|
-
|
|
68
|
+
const created = await findRow('categories', id, trx)
|
|
69
|
+
if (!created)
|
|
70
|
+
throw new Error('Created category could not be loaded.')
|
|
71
|
+
return created
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
if (!category)
|
|
75
|
+
return response.json({ message: 'A category with that slug already exists.' }, 422)
|
|
76
|
+
|
|
77
|
+
return response.json(category, 201)
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
return dashboardOperationalError(error, 'Category could not be created.', 'CategoryStoreAction', 500)
|
|
81
|
+
}
|
|
64
82
|
},
|
|
65
83
|
})
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions'
|
|
3
3
|
import { db } from '@stacksjs/database'
|
|
4
|
+
import { transaction } from '@stacksjs/orm'
|
|
4
5
|
import { response } from '@stacksjs/router'
|
|
6
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
5
7
|
import { rowExists, rowId } from './content-input'
|
|
6
8
|
|
|
7
9
|
/**
|
|
@@ -20,11 +22,22 @@ export default new Action({
|
|
|
20
22
|
if (!id)
|
|
21
23
|
return response.json({ message: 'A valid comment id is required.' }, 422)
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
try {
|
|
26
|
+
const deleted = await transaction(async (rawTrx) => {
|
|
27
|
+
const trx = rawTrx as unknown as typeof db
|
|
28
|
+
if (!await rowExists('comments', id, trx))
|
|
29
|
+
return false
|
|
30
|
+
await trx.deleteFrom('comments').where('id', '=', id).execute()
|
|
31
|
+
return true
|
|
32
|
+
})
|
|
25
33
|
|
|
26
|
-
|
|
34
|
+
if (!deleted)
|
|
35
|
+
return response.json({ message: 'Comment not found.' }, 404)
|
|
27
36
|
|
|
28
|
-
|
|
37
|
+
return response.json({ message: 'Comment deleted.', id })
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return dashboardOperationalError(error, 'Comment could not be deleted.', 'CommentDestroyAction', 500)
|
|
41
|
+
}
|
|
29
42
|
},
|
|
30
43
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions'
|
|
2
2
|
import { db } from '@stacksjs/database'
|
|
3
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
3
4
|
import { normalizeCommentStatus } from './comment-input'
|
|
4
5
|
|
|
5
6
|
interface CommentRow {
|
|
@@ -33,24 +34,29 @@ export default new Action({
|
|
|
33
34
|
method: 'GET',
|
|
34
35
|
apiResponse: true,
|
|
35
36
|
async handle() {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
try {
|
|
38
|
+
const rows = await db
|
|
39
|
+
.selectFrom('comments')
|
|
40
|
+
.selectAll()
|
|
41
|
+
.orderBy('created_at', 'desc')
|
|
42
|
+
.execute() as unknown as CommentRow[]
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
const comments = rows.map(row => ({
|
|
45
|
+
id: Number(row.id),
|
|
46
|
+
author_name: String(row.author_name || ''),
|
|
47
|
+
author_email: String(row.author_email || ''),
|
|
48
|
+
content: String(row.content || row.body || ''),
|
|
49
|
+
post_title: String(row.post_title || ''),
|
|
50
|
+
status: normalizeCommentStatus(row.status),
|
|
51
|
+
is_approved: Boolean(row.is_approved),
|
|
52
|
+
created_at: row.created_at || null,
|
|
53
|
+
updated_at: row.updated_at || null,
|
|
54
|
+
}))
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
return { comments }
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
return dashboardOperationalError(error, 'Comments could not be loaded.', 'CommentIndexAction')
|
|
60
|
+
}
|
|
55
61
|
},
|
|
56
62
|
})
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions'
|
|
3
3
|
import { db } from '@stacksjs/database'
|
|
4
|
+
import { transaction } from '@stacksjs/orm'
|
|
4
5
|
import { response } from '@stacksjs/router'
|
|
6
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
5
7
|
import { COMMENT_STATUSES, parseCommentStatus } from './comment-input'
|
|
6
8
|
import { findRow, rowExists, rowId, timestamp } from './content-input'
|
|
7
9
|
|
|
@@ -30,19 +32,35 @@ export default new Action({
|
|
|
30
32
|
if (!status)
|
|
31
33
|
return response.json({ message: `Status must be one of: ${COMMENT_STATUSES.join(', ')}.` }, 422)
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
try {
|
|
36
|
+
const comment = await transaction(async (rawTrx) => {
|
|
37
|
+
const trx = rawTrx as unknown as typeof db
|
|
38
|
+
if (!await rowExists('comments', id, trx))
|
|
39
|
+
return null
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
await trx
|
|
42
|
+
.updateTable('comments')
|
|
43
|
+
.set({
|
|
44
|
+
status,
|
|
45
|
+
is_approved: status === 'approved' ? 1 : 0,
|
|
46
|
+
updated_at: timestamp(),
|
|
47
|
+
} as any)
|
|
48
|
+
.where('id', '=', id)
|
|
49
|
+
.execute()
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
const updated = await findRow('comments', id, trx)
|
|
52
|
+
if (!updated)
|
|
53
|
+
throw new Error('Updated comment could not be loaded.')
|
|
54
|
+
return updated
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
if (!comment)
|
|
58
|
+
return response.json({ message: 'Comment not found.' }, 404)
|
|
59
|
+
|
|
60
|
+
return response.json(comment)
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
return dashboardOperationalError(error, 'Comment could not be updated.', 'CommentUpdateAction', 500)
|
|
64
|
+
}
|
|
47
65
|
},
|
|
48
66
|
})
|
|
@@ -1,6 +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 { dashboardOperationalError } from '../dashboard-response'
|
|
4
5
|
|
|
5
6
|
const ALLOWED_RANGES = new Set([1, 7, 30, 90, 365])
|
|
6
7
|
|
|
@@ -214,42 +215,47 @@ export default new Action({
|
|
|
214
215
|
const days = requestedDays(request)
|
|
215
216
|
const since = threshold(days)
|
|
216
217
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
218
|
+
try {
|
|
219
|
+
const postQuery = db
|
|
220
|
+
.selectFrom('posts')
|
|
221
|
+
.select(['id', 'title', 'status', 'views', 'author_id', 'published_at', 'created_at', 'updated_at'])
|
|
222
|
+
const pageQuery = db
|
|
223
|
+
.selectFrom('pages')
|
|
224
|
+
.select(['id', 'title', 'views', 'published_at', 'created_at'])
|
|
225
|
+
const commentQuery = db
|
|
226
|
+
.selectFrom('comments')
|
|
227
|
+
.select(['id', 'author_name', 'author_email', 'content', 'post_id', 'post_title', 'status', 'created_at'])
|
|
228
|
+
|
|
229
|
+
const [posts, pages, comments, categories, authors] = await Promise.all([
|
|
230
|
+
(since ? postQuery.where('created_at', '>=', since) : postQuery).execute() as unknown as Promise<ContentOverviewPostRow[]>,
|
|
231
|
+
(since ? pageQuery.where('created_at', '>=', since) : pageQuery).execute() as unknown as Promise<ContentOverviewPageRow[]>,
|
|
232
|
+
(since ? commentQuery.where('created_at', '>=', since) : commentQuery).execute() as unknown as Promise<ContentOverviewCommentRow[]>,
|
|
233
|
+
db.selectFrom('categories').select(['id', 'name']).execute() as unknown as Promise<NamedRow[]>,
|
|
234
|
+
db.selectFrom('authors').select(['id', 'name']).execute() as unknown as Promise<NamedRow[]>,
|
|
235
|
+
])
|
|
236
|
+
|
|
237
|
+
const postIds = posts.map(post => Number(post.id))
|
|
238
|
+
const categoryRelations = postIds.length > 0
|
|
239
|
+
? await db
|
|
240
|
+
.selectFrom('categorizable_models')
|
|
241
|
+
.whereIn('categorizable_id', postIds)
|
|
242
|
+
.where('categorizable_type', '=', 'posts')
|
|
243
|
+
.select(['categorizable_id as postId', 'category_id as categoryId'])
|
|
244
|
+
.execute() as unknown as ContentOverviewRelationRow[]
|
|
245
|
+
: []
|
|
246
|
+
|
|
247
|
+
return buildContentOverview({
|
|
248
|
+
posts,
|
|
249
|
+
pages,
|
|
250
|
+
comments,
|
|
251
|
+
categories,
|
|
252
|
+
categoryRelations,
|
|
253
|
+
authors,
|
|
254
|
+
days,
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
return dashboardOperationalError(error, 'Content overview could not be loaded.', 'ContentDashboardAction')
|
|
259
|
+
}
|
|
254
260
|
},
|
|
255
261
|
})
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions'
|
|
3
3
|
import { db } from '@stacksjs/database'
|
|
4
|
+
import { transaction } from '@stacksjs/orm'
|
|
4
5
|
import { response } from '@stacksjs/router'
|
|
6
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
5
7
|
import { rowExists, rowId } from './content-input'
|
|
6
8
|
|
|
7
9
|
/** `DELETE /api/dashboard/pages/{id}` — deletes a CMS page from the dashboard. */
|
|
@@ -15,11 +17,22 @@ export default new Action({
|
|
|
15
17
|
if (!id)
|
|
16
18
|
return response.json({ message: 'A valid page id is required.' }, 422)
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
try {
|
|
21
|
+
const deleted = await transaction(async (rawTrx) => {
|
|
22
|
+
const trx = rawTrx as unknown as typeof db
|
|
23
|
+
if (!await rowExists('pages', id, trx))
|
|
24
|
+
return false
|
|
25
|
+
await trx.deleteFrom('pages').where('id', '=', id).execute()
|
|
26
|
+
return true
|
|
27
|
+
})
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
if (!deleted)
|
|
30
|
+
return response.json({ message: 'Page not found.' }, 404)
|
|
22
31
|
|
|
23
|
-
|
|
32
|
+
return response.json({ message: 'Page deleted.', id })
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
return dashboardOperationalError(error, 'Page could not be deleted.', 'PageDestroyAction', 500)
|
|
36
|
+
}
|
|
24
37
|
},
|
|
25
38
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions'
|
|
2
2
|
import { db } from '@stacksjs/database'
|
|
3
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
3
4
|
|
|
4
5
|
interface PageRow {
|
|
5
6
|
id: number
|
|
@@ -30,24 +31,29 @@ export default new Action({
|
|
|
30
31
|
method: 'GET',
|
|
31
32
|
apiResponse: true,
|
|
32
33
|
async handle() {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
try {
|
|
35
|
+
const rows = await db
|
|
36
|
+
.selectFrom('pages')
|
|
37
|
+
.selectAll()
|
|
38
|
+
.orderBy('created_at', 'desc')
|
|
39
|
+
.execute() as unknown as PageRow[]
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
const pages = rows.map(row => ({
|
|
42
|
+
id: Number(row.id),
|
|
43
|
+
title: String(row.title || ''),
|
|
44
|
+
template: String(row.template || 'default'),
|
|
45
|
+
views: Number(row.views || 0),
|
|
46
|
+
conversions: Number(row.conversions || 0),
|
|
47
|
+
published_at: row.published_at || null,
|
|
48
|
+
author_id: row.author_id ?? null,
|
|
49
|
+
created_at: row.created_at || null,
|
|
50
|
+
updated_at: row.updated_at || null,
|
|
51
|
+
}))
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
return { pages }
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
return dashboardOperationalError(error, 'Pages could not be loaded.', 'PageIndexAction')
|
|
57
|
+
}
|
|
52
58
|
},
|
|
53
59
|
})
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { RequestInstance } from '@stacksjs/types'
|
|
2
2
|
import { Action } from '@stacksjs/actions'
|
|
3
3
|
import { db } from '@stacksjs/database'
|
|
4
|
+
import { transaction } from '@stacksjs/orm'
|
|
4
5
|
import { response } from '@stacksjs/router'
|
|
5
6
|
import { randomUUIDv7 } from 'bun'
|
|
7
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
6
8
|
import { findRow, insertedId, timestamp } from './content-input'
|
|
7
9
|
import { parsePageInput, parsePublished } from './page-input'
|
|
8
10
|
|
|
@@ -22,27 +24,40 @@ export default new Action({
|
|
|
22
24
|
if ('message' in input)
|
|
23
25
|
return response.json({ message: input.message }, 422)
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
try {
|
|
28
|
+
const page = await transaction(async (rawTrx) => {
|
|
29
|
+
const trx = rawTrx as unknown as typeof db
|
|
30
|
+
const now = timestamp()
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
const result = await trx
|
|
33
|
+
.insertInto('pages')
|
|
34
|
+
.values({
|
|
35
|
+
uuid: randomUUIDv7(),
|
|
36
|
+
title: input.data.title,
|
|
37
|
+
template: input.data.template,
|
|
38
|
+
views: 0,
|
|
39
|
+
conversions: 0,
|
|
40
|
+
published_at: parsePublished(request.get('published')) ? now : null,
|
|
41
|
+
created_at: now,
|
|
42
|
+
updated_at: now,
|
|
43
|
+
} as any)
|
|
44
|
+
.executeTakeFirst()
|
|
40
45
|
|
|
41
|
-
|
|
46
|
+
const id = insertedId(result)
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
if (!id)
|
|
49
|
+
throw new Error('Page insert did not return an id.')
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
const created = await findRow('pages', id, trx)
|
|
52
|
+
if (!created)
|
|
53
|
+
throw new Error('Created page could not be loaded.')
|
|
54
|
+
return created
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
return response.json(page, 201)
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
return dashboardOperationalError(error, 'Page could not be created.', 'PageStoreAction', 500)
|
|
61
|
+
}
|
|
47
62
|
},
|
|
48
63
|
})
|