@stacksjs/defaults 0.75.482 → 0.75.483
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-actions/SKILL.md +1 -1
- package/ai/skills/stacks-native/CATALOG.md +4 -4
- package/ai/skills/stacks-queue/DURABLE-WORKFLOWS.md +12 -0
- package/app/Actions/Dashboard/Content/CommentIndexAction.ts +22 -112
- package/app/Actions/Dashboard/Content/PageIndexAction.ts +26 -25
- package/app/Actions/Dashboard/Content/PostIndexAction.ts +30 -35
- package/app/Actions/Dashboard/Content/PostShowAction.ts +41 -0
- package/app/Actions/Dashboard/Content/comment-input.ts +25 -1
- package/app/Actions/Dashboard/Content/comment-list.ts +311 -0
- package/app/Actions/Dashboard/Content/content-list.ts +69 -0
- package/app/Actions/Dashboard/Content/page-list.ts +108 -0
- package/app/Actions/Dashboard/Content/post-list.ts +140 -0
- package/app/Actions/Dashboard/Jobs/JobIndexAction.ts +12 -24
- package/app/Actions/Dashboard/Jobs/job-list.ts +168 -0
- package/app/Actions/Dashboard/Jobs/job-records.ts +8 -3
- package/app/Actions/Dashboard/Models/ModelShowAction.ts +8 -2
- package/app/Actions/Dashboard/Queries/QueryDashboardAction.ts +20 -10
- package/app/Actions/Dashboard/Queries/query-dashboard.ts +56 -9
- package/app/Actions/Dashboard/Queries/query-list.ts +161 -0
- package/app/Actions/Dashboard/Queue/QueueStatsAction.ts +14 -52
- package/app/Actions/Dashboard/Queue/queue-stats.ts +69 -0
- package/app/Actions/Monitoring/ErrorIndexAction.ts +9 -4
- package/app/Actions/Monitoring/error-provider.ts +85 -7
- package/functions/api.ts +14 -10
- package/functions/commerce/coupons.ts +4 -6
- package/functions/commerce/customers.ts +4 -6
- package/functions/commerce/gift-cards.ts +4 -2
- package/functions/commerce/orders.ts +4 -2
- package/functions/commerce/payments.ts +4 -2
- package/functions/commerce/products/categories.ts +4 -6
- package/functions/commerce/products/manufacturers.ts +4 -6
- package/functions/commerce/products/products.ts +4 -6
- package/functions/commerce/products/reviews.ts +4 -6
- package/functions/commerce/products/units.ts +4 -6
- package/functions/commerce/products/variants.ts +4 -6
- package/functions/commerce/taxes.ts +4 -2
- package/functions/commerce/waitlist/products.ts +4 -6
- package/functions/commerce/waitlist/restaurants.ts +4 -6
- package/functions/monitoring/errors.ts +77 -49
- package/functions/queries.ts +34 -2
- package/ide/vscode/package.json +1 -1
- package/package.json +2 -2
- package/resources/components/Dashboard/Analytics/AnalyticsHub.stx +4 -0
- package/resources/components/Dashboard/Auth/MagicLink.stx +6 -1
- package/resources/components/Dashboard/Content/CommentsDashboard.stx +90 -12
- package/resources/components/Dashboard/Content/PagesDashboard.stx +100 -16
- package/resources/components/Dashboard/Content/PostEditorDialog.stx +28 -8
- package/resources/components/Dashboard/Content/PostsDashboard.stx +179 -21
- package/resources/components/Dashboard/Monitoring/ErrorDashboard.stx +121 -36
- package/resources/components/Dashboard/Monitoring/ErrorTable.stx +29 -8
- package/resources/components/Dashboard/Queries/QueryDashboard.stx +73 -64
- package/resources/components/Dashboard/UI/Table.stx +30 -7
- package/routes/dashboard-api.ts +1 -0
- package/app/Actions/Buddy/JobsListAction.ts +0 -141
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { db } from '@stacksjs/database/runtime'
|
|
2
|
+
|
|
3
|
+
/** One queue's jobs, as the Queue page shows them. */
|
|
4
|
+
export interface QueueCounts {
|
|
5
|
+
name: string
|
|
6
|
+
/** Waiting for a worker, delayed or not. */
|
|
7
|
+
pending: number
|
|
8
|
+
/** Reserved by a worker. */
|
|
9
|
+
active: number
|
|
10
|
+
/** Rows in `failed_jobs` for this queue. */
|
|
11
|
+
failed: number
|
|
12
|
+
total: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const queueName = (value: unknown): string => String(value || 'default')
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Every queue that has jobs, counted by the database (stacksjs/stacks#3200).
|
|
19
|
+
*
|
|
20
|
+
* This read every row of `jobs` and `failed_jobs`, payloads included, to count
|
|
21
|
+
* them per queue, on every visit to the Queue page. A job is active when a
|
|
22
|
+
* worker has reserved it, which is `queuedJobState`'s rule in
|
|
23
|
+
* `@stacksjs/queue`: a `reserved_at` that is set and not 0, so the filter
|
|
24
|
+
* below is `!= 0`, which NULL never matches. Queues are listed in the order of
|
|
25
|
+
* their first job, and a queue with only failures is not listed, as before.
|
|
26
|
+
*/
|
|
27
|
+
export async function readQueueCounts(database: typeof db = db): Promise<QueueCounts[]> {
|
|
28
|
+
const [jobRows, failedRows] = await Promise.all([
|
|
29
|
+
database.selectFrom('jobs')
|
|
30
|
+
.select([
|
|
31
|
+
'queue',
|
|
32
|
+
database.fn.count('id').as('total'),
|
|
33
|
+
database.fn.count('id').filterWhere('reserved_at', '!=', 0).as('active'),
|
|
34
|
+
database.fn.min('id').as('first_id'),
|
|
35
|
+
])
|
|
36
|
+
.groupBy('queue')
|
|
37
|
+
.execute() as Promise<Array<{ queue: unknown, total: unknown, active: unknown, first_id: unknown }>>,
|
|
38
|
+
database.selectFrom('failed_jobs')
|
|
39
|
+
.select(['queue', database.fn.count('id').as('failed')])
|
|
40
|
+
.groupBy('queue')
|
|
41
|
+
.execute() as Promise<Array<{ queue: unknown, failed: unknown }>>,
|
|
42
|
+
])
|
|
43
|
+
|
|
44
|
+
const failed = new Map<string, number>()
|
|
45
|
+
for (const row of failedRows)
|
|
46
|
+
failed.set(queueName(row.queue), (failed.get(queueName(row.queue)) ?? 0) + Number(row.failed))
|
|
47
|
+
|
|
48
|
+
// A NULL, an empty and a 'default' queue are all the default queue.
|
|
49
|
+
const queues = new Map<string, { total: number, active: number, firstId: number }>()
|
|
50
|
+
for (const row of jobRows) {
|
|
51
|
+
const name = queueName(row.queue)
|
|
52
|
+
const current = queues.get(name) ?? { total: 0, active: 0, firstId: Number.POSITIVE_INFINITY }
|
|
53
|
+
queues.set(name, {
|
|
54
|
+
total: current.total + Number(row.total),
|
|
55
|
+
active: current.active + Number(row.active),
|
|
56
|
+
firstId: Math.min(current.firstId, Number(row.first_id)),
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return [...queues.entries()]
|
|
61
|
+
.sort(([, a], [, b]) => a.firstId - b.firstId)
|
|
62
|
+
.map(([name, counts]) => ({
|
|
63
|
+
name,
|
|
64
|
+
pending: counts.total - counts.active,
|
|
65
|
+
active: counts.active,
|
|
66
|
+
failed: failed.get(name) ?? 0,
|
|
67
|
+
total: counts.total,
|
|
68
|
+
}))
|
|
69
|
+
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions/runtime'
|
|
2
3
|
import { response } from '@stacksjs/router'
|
|
3
|
-
import { readGroupedErrors } from './error-provider'
|
|
4
|
+
import { parseErrorGroupQuery, readGroupedErrors } from './error-provider'
|
|
4
5
|
|
|
5
6
|
export default new Action({
|
|
6
7
|
name: 'Error Index',
|
|
7
|
-
description: 'Fetch
|
|
8
|
+
description: 'Fetch one page of grouped errors (Sentry-like aggregation)',
|
|
8
9
|
method: 'GET',
|
|
9
|
-
async handle() {
|
|
10
|
-
|
|
10
|
+
async handle(request: RequestInstance) {
|
|
11
|
+
const query = parseErrorGroupQuery(key => request.get<unknown>(key, ''))
|
|
12
|
+
if ('error' in query)
|
|
13
|
+
return response.json({ message: query.error }, 422)
|
|
14
|
+
|
|
15
|
+
return response.json(await readGroupedErrors(query))
|
|
11
16
|
},
|
|
12
17
|
})
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { DashboardProviderUnavailable } from '../Dashboard/dashboard-provider'
|
|
2
2
|
import { errors } from '@stacksjs/commerce'
|
|
3
|
+
import type { QueryReader } from '../Dashboard/Content/content-list'
|
|
4
|
+
import { LIST_PAGE_SIZES, queryText } from '../Dashboard/Content/content-list'
|
|
3
5
|
import { HQ_READ_UNAVAILABLE, readThroughProvider, resolveDashboardDriver } from '../Dashboard/dashboard-provider'
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -81,7 +83,39 @@ export interface DashboardGroupedError {
|
|
|
81
83
|
count: number
|
|
82
84
|
first_seen: string
|
|
83
85
|
last_seen: string
|
|
84
|
-
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* What a page of error groups asks for. A group's state is its newest
|
|
90
|
+
* occurrence's, so `status` filters groups, not rows.
|
|
91
|
+
*/
|
|
92
|
+
export interface DashboardErrorGroupQuery {
|
|
93
|
+
page: number
|
|
94
|
+
perPage: number
|
|
95
|
+
search: string
|
|
96
|
+
status: DashboardErrorStatus | 'all'
|
|
97
|
+
type: string
|
|
98
|
+
sort: typeof ERROR_GROUP_SORTS[number]
|
|
99
|
+
direction: 'asc' | 'desc'
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const ERROR_GROUP_SORTS = ['last_seen', 'count', 'type', 'status'] as const
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* One page of error groups, most recently seen first (stacksjs/stacks#3195).
|
|
106
|
+
*
|
|
107
|
+
* The section used to take every group, each with the id of every one of its
|
|
108
|
+
* occurrences, to show ten. Nothing read those ids, and a busy group has
|
|
109
|
+
* thousands, so a page carries the groups' counts instead.
|
|
110
|
+
*/
|
|
111
|
+
export interface DashboardGroupedErrorPage {
|
|
112
|
+
groups: DashboardGroupedError[]
|
|
113
|
+
/** Groups matching the query, which the pager counts. */
|
|
114
|
+
total: number
|
|
115
|
+
/** The page the groups are, moved back to the last one when it ran past it. */
|
|
116
|
+
page: number
|
|
117
|
+
/** Every recorded error type, for the type filter. */
|
|
118
|
+
types: string[]
|
|
85
119
|
}
|
|
86
120
|
|
|
87
121
|
export interface DashboardErrorStats {
|
|
@@ -99,7 +133,7 @@ export interface DashboardErrorTimelinePoint {
|
|
|
99
133
|
}
|
|
100
134
|
|
|
101
135
|
export interface DashboardErrorsProvider {
|
|
102
|
-
grouped: () => Promise<
|
|
136
|
+
grouped: (query: DashboardErrorGroupQuery) => Promise<DashboardGroupedErrorPage>
|
|
103
137
|
stats: () => Promise<DashboardErrorStats>
|
|
104
138
|
timeline: () => Promise<DashboardErrorTimelinePoint[]>
|
|
105
139
|
byGroup: (type: string, message: string) => Promise<DashboardErrorRecord[]>
|
|
@@ -141,7 +175,7 @@ function toErrorStatus(value: string): DashboardErrorStatus {
|
|
|
141
175
|
* next moves, this file stops compiling instead of quietly changing what a
|
|
142
176
|
* remote provider is required to match.
|
|
143
177
|
*/
|
|
144
|
-
function toGroupedError(row: Awaited<ReturnType<typeof errors.
|
|
178
|
+
function toGroupedError(row: Awaited<ReturnType<typeof errors.fetchGroupedPage>>['groups'][number]): DashboardGroupedError {
|
|
145
179
|
return {
|
|
146
180
|
id: row.id,
|
|
147
181
|
type: row.type,
|
|
@@ -151,7 +185,6 @@ function toGroupedError(row: Awaited<ReturnType<typeof errors.fetchGrouped>>[num
|
|
|
151
185
|
count: row.count,
|
|
152
186
|
first_seen: row.first_seen,
|
|
153
187
|
last_seen: row.last_seen,
|
|
154
|
-
error_ids: row.error_ids,
|
|
155
188
|
}
|
|
156
189
|
}
|
|
157
190
|
|
|
@@ -169,7 +202,10 @@ export function localErrorId(id: string | number): number | undefined {
|
|
|
169
202
|
}
|
|
170
203
|
|
|
171
204
|
export const localErrorsProvider: DashboardErrorsProvider = {
|
|
172
|
-
grouped: async () =>
|
|
205
|
+
grouped: async (query) => {
|
|
206
|
+
const page = await errors.fetchGroupedPage(query)
|
|
207
|
+
return { ...page, groups: page.groups.map(toGroupedError) }
|
|
208
|
+
},
|
|
173
209
|
stats: () => errors.fetchStats(),
|
|
174
210
|
timeline: () => errors.fetchTimeline(),
|
|
175
211
|
byGroup: (type, message) => errors.fetchByGroup(type, message),
|
|
@@ -226,8 +262,50 @@ async function readErrors<T>(
|
|
|
226
262
|
return payload
|
|
227
263
|
}
|
|
228
264
|
|
|
229
|
-
|
|
230
|
-
|
|
265
|
+
const EMPTY_GROUP_PAGE: DashboardGroupedErrorPage = { groups: [], total: 0, page: 1, types: [] }
|
|
266
|
+
|
|
267
|
+
export function readGroupedErrors(query: DashboardErrorGroupQuery): Promise<DashboardErrorsEnvelope<DashboardGroupedErrorPage>> {
|
|
268
|
+
return readErrors(
|
|
269
|
+
EMPTY_GROUP_PAGE,
|
|
270
|
+
provider => provider.grouped(query),
|
|
271
|
+
payload => Boolean(payload) && Array.isArray((payload as DashboardGroupedErrorPage).groups),
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** The page query, or the reason it is invalid. Absent values: page 1 of 25, every group. */
|
|
276
|
+
export function parseErrorGroupQuery(read: QueryReader): DashboardErrorGroupQuery | { error: string } {
|
|
277
|
+
const pageText = queryText(read, 'page')
|
|
278
|
+
const page = pageText === '' ? 1 : Number(pageText)
|
|
279
|
+
if (!Number.isInteger(page) || page < 1)
|
|
280
|
+
return { error: 'page must be a whole number of 1 or more.' }
|
|
281
|
+
|
|
282
|
+
const sizeText = queryText(read, 'per_page')
|
|
283
|
+
const perPage = sizeText === '' ? 25 : Number(sizeText)
|
|
284
|
+
if (!(LIST_PAGE_SIZES as readonly number[]).includes(perPage))
|
|
285
|
+
return { error: `per_page must be one of ${LIST_PAGE_SIZES.join(', ')}.` }
|
|
286
|
+
|
|
287
|
+
const status = queryText(read, 'status') || 'all'
|
|
288
|
+
if (status !== 'all' && !ERROR_STATUSES.includes(status as DashboardErrorStatus))
|
|
289
|
+
return { error: `status must be all or one of ${ERROR_STATUSES.join(', ')}.` }
|
|
290
|
+
|
|
291
|
+
const search = queryText(read, 'search')
|
|
292
|
+
if (search.length > 200)
|
|
293
|
+
return { error: 'search must be 200 characters or fewer.' }
|
|
294
|
+
|
|
295
|
+
const type = queryText(read, 'type')
|
|
296
|
+
if (type.length > 255)
|
|
297
|
+
return { error: 'type must be 255 characters or fewer.' }
|
|
298
|
+
|
|
299
|
+
const sort = queryText(read, 'sort') || 'last_seen'
|
|
300
|
+
if (!(ERROR_GROUP_SORTS as readonly string[]).includes(sort))
|
|
301
|
+
return { error: `sort must be one of ${ERROR_GROUP_SORTS.join(', ')}.` }
|
|
302
|
+
|
|
303
|
+
const directionText = queryText(read, 'direction').toLowerCase()
|
|
304
|
+
const direction = directionText === '' ? (sort === 'last_seen' || sort === 'count' ? 'desc' : 'asc') : directionText
|
|
305
|
+
if (direction !== 'asc' && direction !== 'desc')
|
|
306
|
+
return { error: 'direction must be asc or desc.' }
|
|
307
|
+
|
|
308
|
+
return { page, perPage, search, status: status as DashboardErrorGroupQuery['status'], type, sort: sort as DashboardErrorGroupQuery['sort'], direction }
|
|
231
309
|
}
|
|
232
310
|
|
|
233
311
|
export function readErrorStats(): Promise<DashboardErrorsEnvelope<DashboardErrorStats>> {
|
package/functions/api.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { resolveApiBaseUrl } from './api-url'
|
|
|
5
5
|
* Provides a simple fetch wrapper with authentication and error handling.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { withCsrfHeader } from '@stacksjs/browser/composables/csrf'
|
|
8
9
|
import { useAuth } from './auth'
|
|
9
10
|
|
|
10
11
|
const baseUrl = resolveApiBaseUrl()
|
|
@@ -69,14 +70,17 @@ export interface ApiError {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
/**
|
|
72
|
-
* Create headers with authentication
|
|
73
|
+
* Create headers with authentication.
|
|
74
|
+
*
|
|
75
|
+
* A write also carries the CSRF token: the server checks it on every write
|
|
76
|
+
* that rides the session cookie rather than a Bearer token, so without it
|
|
77
|
+
* each post, put, patch and del from a signed-in browser answered 403
|
|
78
|
+
* (stacksjs/stacks#3197), as `dashboardApi` already knew.
|
|
73
79
|
*/
|
|
74
|
-
function createHeaders(contentType = 'application/json'): Headers {
|
|
80
|
+
function createHeaders(contentType = 'application/json', write = false): Headers {
|
|
75
81
|
const { getToken } = useAuth()
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
'Content-Type': contentType,
|
|
79
|
-
})
|
|
82
|
+
const base = { 'Accept': 'application/json', 'Content-Type': contentType }
|
|
83
|
+
const headers = new Headers(write ? withCsrfHeader(base) : base)
|
|
80
84
|
|
|
81
85
|
const token = getToken()
|
|
82
86
|
if (token) {
|
|
@@ -118,7 +122,7 @@ export async function get<T>(endpoint: string): Promise<T> {
|
|
|
118
122
|
export async function post<T>(endpoint: string, body?: unknown): Promise<T> {
|
|
119
123
|
const response = await fetch(requestUrl(endpoint), {
|
|
120
124
|
method: 'POST',
|
|
121
|
-
headers: createHeaders(),
|
|
125
|
+
headers: createHeaders('application/json', true),
|
|
122
126
|
body: body ? JSON.stringify(body) : undefined,
|
|
123
127
|
})
|
|
124
128
|
|
|
@@ -131,7 +135,7 @@ export async function post<T>(endpoint: string, body?: unknown): Promise<T> {
|
|
|
131
135
|
export async function put<T>(endpoint: string, body?: unknown): Promise<T> {
|
|
132
136
|
const response = await fetch(requestUrl(endpoint), {
|
|
133
137
|
method: 'PUT',
|
|
134
|
-
headers: createHeaders(),
|
|
138
|
+
headers: createHeaders('application/json', true),
|
|
135
139
|
body: body ? JSON.stringify(body) : undefined,
|
|
136
140
|
})
|
|
137
141
|
|
|
@@ -144,7 +148,7 @@ export async function put<T>(endpoint: string, body?: unknown): Promise<T> {
|
|
|
144
148
|
export async function patch<T>(endpoint: string, body?: unknown): Promise<T> {
|
|
145
149
|
const response = await fetch(requestUrl(endpoint), {
|
|
146
150
|
method: 'PATCH',
|
|
147
|
-
headers: createHeaders(),
|
|
151
|
+
headers: createHeaders('application/json', true),
|
|
148
152
|
body: body ? JSON.stringify(body) : undefined,
|
|
149
153
|
})
|
|
150
154
|
|
|
@@ -157,7 +161,7 @@ export async function patch<T>(endpoint: string, body?: unknown): Promise<T> {
|
|
|
157
161
|
export async function del<T>(endpoint: string): Promise<T> {
|
|
158
162
|
const response = await fetch(requestUrl(endpoint), {
|
|
159
163
|
method: 'DELETE',
|
|
160
|
-
headers: createHeaders(),
|
|
164
|
+
headers: createHeaders('application/json', true),
|
|
161
165
|
})
|
|
162
166
|
|
|
163
167
|
return handleResponse<T>(response)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveApiBaseUrl } from '../api-url'
|
|
2
2
|
import type { Coupons, NewCoupon } from '../../types/defaults'
|
|
3
|
+
import { withCsrfHeader } from '@stacksjs/browser/composables/csrf'
|
|
3
4
|
import { useStorage } from '@stacksjs/browser/composables/useStorage'
|
|
4
5
|
import { pushToast } from '../toasts'
|
|
5
6
|
|
|
@@ -38,9 +39,7 @@ async function createCoupon(coupon: Omit<Coupons, 'id'>) {
|
|
|
38
39
|
try {
|
|
39
40
|
const response = await fetch(`${baseURL}/commerce/coupons`, {
|
|
40
41
|
method: 'POST',
|
|
41
|
-
headers: {
|
|
42
|
-
'Content-Type': 'application/json',
|
|
43
|
-
},
|
|
42
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
44
43
|
body: JSON.stringify(coupon),
|
|
45
44
|
})
|
|
46
45
|
|
|
@@ -66,9 +65,7 @@ async function updateCoupon(id: number, coupon: NewCoupon) {
|
|
|
66
65
|
try {
|
|
67
66
|
const response = await fetch(`${baseURL}/commerce/coupons/${id}`, {
|
|
68
67
|
method: 'PATCH',
|
|
69
|
-
headers: {
|
|
70
|
-
'Content-Type': 'application/json',
|
|
71
|
-
},
|
|
68
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
72
69
|
body: JSON.stringify(coupon),
|
|
73
70
|
})
|
|
74
71
|
|
|
@@ -101,6 +98,7 @@ async function deleteCoupon(id: number) {
|
|
|
101
98
|
try {
|
|
102
99
|
const response = await fetch(`${baseURL}/commerce/coupons/${id}`, {
|
|
103
100
|
method: 'DELETE',
|
|
101
|
+
headers: withCsrfHeader(),
|
|
104
102
|
})
|
|
105
103
|
|
|
106
104
|
if (!response.ok) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveApiBaseUrl } from '../api-url'
|
|
2
2
|
import type { Customers } from '../../types/defaults'
|
|
3
|
+
import { withCsrfHeader } from '@stacksjs/browser/composables/csrf'
|
|
3
4
|
import { useStorage } from '@stacksjs/browser/composables/useStorage'
|
|
4
5
|
import { pushToast } from '../toasts'
|
|
5
6
|
|
|
@@ -38,9 +39,7 @@ async function createCustomer(customer: Omit<Customers, 'id'>) {
|
|
|
38
39
|
try {
|
|
39
40
|
const response = await fetch(`${baseURL}/commerce/customers`, {
|
|
40
41
|
method: 'POST',
|
|
41
|
-
headers: {
|
|
42
|
-
'Content-Type': 'application/json',
|
|
43
|
-
},
|
|
42
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
44
43
|
body: JSON.stringify(customer),
|
|
45
44
|
})
|
|
46
45
|
|
|
@@ -66,9 +65,7 @@ async function updateCustomer(customer: Customers) {
|
|
|
66
65
|
try {
|
|
67
66
|
const response = await fetch(`${baseURL}/commerce/customers/${customer.id}`, {
|
|
68
67
|
method: 'PATCH',
|
|
69
|
-
headers: {
|
|
70
|
-
'Content-Type': 'application/json',
|
|
71
|
-
},
|
|
68
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
72
69
|
body: JSON.stringify(customer),
|
|
73
70
|
})
|
|
74
71
|
|
|
@@ -101,6 +98,7 @@ async function deleteCustomer(id: number) {
|
|
|
101
98
|
try {
|
|
102
99
|
const response = await fetch(`${baseURL}/commerce/customers/${id}`, {
|
|
103
100
|
method: 'DELETE',
|
|
101
|
+
headers: withCsrfHeader(),
|
|
104
102
|
})
|
|
105
103
|
|
|
106
104
|
if (!response.ok) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveApiBaseUrl } from '../api-url'
|
|
2
2
|
import type { GiftCards } from '../../types/defaults'
|
|
3
|
+
import { withCsrfHeader } from '@stacksjs/browser/composables/csrf'
|
|
3
4
|
import { useStorage } from '@stacksjs/browser/composables/useStorage'
|
|
4
5
|
import { pushToast } from '../toasts'
|
|
5
6
|
|
|
@@ -35,7 +36,7 @@ async function createGiftCard(giftCard: GiftCards): Promise<GiftCards | null> {
|
|
|
35
36
|
try {
|
|
36
37
|
const response = await fetch(`${baseURL}/commerce/gift-cards`, {
|
|
37
38
|
method: 'POST',
|
|
38
|
-
headers: { 'Content-Type': 'application/json' },
|
|
39
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
39
40
|
body: JSON.stringify(giftCard),
|
|
40
41
|
})
|
|
41
42
|
|
|
@@ -61,7 +62,7 @@ async function updateGiftCard(giftCard: GiftCards): Promise<GiftCards | null> {
|
|
|
61
62
|
try {
|
|
62
63
|
const response = await fetch(`${baseURL}/commerce/gift-cards/${giftCard.id}`, {
|
|
63
64
|
method: 'PATCH',
|
|
64
|
-
headers: { 'Content-Type': 'application/json' },
|
|
65
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
65
66
|
body: JSON.stringify(giftCard),
|
|
66
67
|
})
|
|
67
68
|
|
|
@@ -90,6 +91,7 @@ async function deleteGiftCard(id: number): Promise<boolean> {
|
|
|
90
91
|
try {
|
|
91
92
|
const response = await fetch(`${baseURL}/commerce/gift-cards/${id}`, {
|
|
92
93
|
method: 'DELETE',
|
|
94
|
+
headers: withCsrfHeader(),
|
|
93
95
|
})
|
|
94
96
|
|
|
95
97
|
if (!response.ok) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveApiBaseUrl } from '../api-url'
|
|
2
2
|
import type { Orders } from '../../types/defaults'
|
|
3
|
+
import { withCsrfHeader } from '@stacksjs/browser/composables/csrf'
|
|
3
4
|
import { useStorage } from '@stacksjs/browser/composables/useStorage'
|
|
4
5
|
import { pushToast } from '../toasts'
|
|
5
6
|
|
|
@@ -28,7 +29,7 @@ async function createOrder(order: Orders) {
|
|
|
28
29
|
try {
|
|
29
30
|
const response = await fetch(`${baseURL}/commerce/orders`, {
|
|
30
31
|
method: 'POST',
|
|
31
|
-
headers: { 'Content-Type': 'application/json' },
|
|
32
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
32
33
|
body: JSON.stringify(order),
|
|
33
34
|
})
|
|
34
35
|
|
|
@@ -54,7 +55,7 @@ async function updateOrder(order: Orders) {
|
|
|
54
55
|
try {
|
|
55
56
|
const response = await fetch(`${baseURL}/commerce/orders/${order.id}`, {
|
|
56
57
|
method: 'PATCH',
|
|
57
|
-
headers: { 'Content-Type': 'application/json' },
|
|
58
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
58
59
|
body: JSON.stringify(order),
|
|
59
60
|
})
|
|
60
61
|
|
|
@@ -83,6 +84,7 @@ async function deleteOrder(id: number) {
|
|
|
83
84
|
try {
|
|
84
85
|
const response = await fetch(`${baseURL}/commerce/orders/${id}`, {
|
|
85
86
|
method: 'DELETE',
|
|
87
|
+
headers: withCsrfHeader(),
|
|
86
88
|
})
|
|
87
89
|
|
|
88
90
|
if (!response.ok) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveApiBaseUrl } from '../api-url'
|
|
2
2
|
import type { Payments } from '../../types/defaults'
|
|
3
|
+
import { withCsrfHeader } from '@stacksjs/browser/composables/csrf'
|
|
3
4
|
import { useStorage } from '@stacksjs/browser/composables/useStorage'
|
|
4
5
|
import { pushToast } from '../toasts'
|
|
5
6
|
|
|
@@ -35,7 +36,7 @@ async function createPayment(payment: Payments): Promise<Payments | null> {
|
|
|
35
36
|
try {
|
|
36
37
|
const response = await fetch(`${baseURL}/commerce/payments`, {
|
|
37
38
|
method: 'POST',
|
|
38
|
-
headers: { 'Content-Type': 'application/json' },
|
|
39
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
39
40
|
body: JSON.stringify(payment),
|
|
40
41
|
})
|
|
41
42
|
|
|
@@ -61,7 +62,7 @@ async function updatePayment(payment: Payments): Promise<Payments | null> {
|
|
|
61
62
|
try {
|
|
62
63
|
const response = await fetch(`${baseURL}/commerce/payments/${payment.id}`, {
|
|
63
64
|
method: 'PATCH',
|
|
64
|
-
headers: { 'Content-Type': 'application/json' },
|
|
65
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
65
66
|
body: JSON.stringify(payment),
|
|
66
67
|
})
|
|
67
68
|
|
|
@@ -90,6 +91,7 @@ async function deletePayment(id: number): Promise<boolean> {
|
|
|
90
91
|
try {
|
|
91
92
|
const response = await fetch(`${baseURL}/commerce/payments/${id}`, {
|
|
92
93
|
method: 'DELETE',
|
|
94
|
+
headers: withCsrfHeader(),
|
|
93
95
|
})
|
|
94
96
|
|
|
95
97
|
if (!response.ok) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveApiBaseUrl } from '../../api-url'
|
|
2
2
|
import type { Categories } from '../../../types/defaults'
|
|
3
|
+
import { withCsrfHeader } from '@stacksjs/browser/composables/csrf'
|
|
3
4
|
import { useStorage } from '@stacksjs/browser/composables/useStorage'
|
|
4
5
|
import { pushToast } from '../../toasts'
|
|
5
6
|
|
|
@@ -36,9 +37,7 @@ async function createCategory(category: Categories): Promise<Categories | null>
|
|
|
36
37
|
try {
|
|
37
38
|
const response = await fetch(`${baseURL}/commerce/product-categories`, {
|
|
38
39
|
method: 'POST',
|
|
39
|
-
headers: {
|
|
40
|
-
'Content-Type': 'application/json',
|
|
41
|
-
},
|
|
40
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
42
41
|
body: JSON.stringify(category),
|
|
43
42
|
})
|
|
44
43
|
|
|
@@ -63,9 +62,7 @@ async function updateCategory(category: Categories): Promise<Categories | null>
|
|
|
63
62
|
try {
|
|
64
63
|
const response = await fetch(`${baseURL}/commerce/product-categories/${category.id}`, {
|
|
65
64
|
method: 'PATCH',
|
|
66
|
-
headers: {
|
|
67
|
-
'Content-Type': 'application/json',
|
|
68
|
-
},
|
|
65
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
69
66
|
body: JSON.stringify(category),
|
|
70
67
|
})
|
|
71
68
|
|
|
@@ -93,6 +90,7 @@ async function deleteCategory(id: number): Promise<boolean> {
|
|
|
93
90
|
try {
|
|
94
91
|
const response = await fetch(`${baseURL}/commerce/product-categories/${id}`, {
|
|
95
92
|
method: 'DELETE',
|
|
93
|
+
headers: withCsrfHeader(),
|
|
96
94
|
})
|
|
97
95
|
|
|
98
96
|
if (!response.ok) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveApiBaseUrl } from '../../api-url'
|
|
2
2
|
import type { Manufacturers } from '../../../types/defaults'
|
|
3
|
+
import { withCsrfHeader } from '@stacksjs/browser/composables/csrf'
|
|
3
4
|
import { useStorage } from '@stacksjs/browser/composables/useStorage'
|
|
4
5
|
import { pushToast } from '../../toasts'
|
|
5
6
|
|
|
@@ -36,9 +37,7 @@ async function createManufacturer(manufacturer: Manufacturers): Promise<Manufact
|
|
|
36
37
|
try {
|
|
37
38
|
const response = await fetch(`${baseURL}/commerce/product-manufacturers`, {
|
|
38
39
|
method: 'POST',
|
|
39
|
-
headers: {
|
|
40
|
-
'Content-Type': 'application/json',
|
|
41
|
-
},
|
|
40
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
42
41
|
body: JSON.stringify(manufacturer),
|
|
43
42
|
})
|
|
44
43
|
|
|
@@ -63,9 +62,7 @@ async function updateManufacturer(manufacturer: Manufacturers): Promise<Manufact
|
|
|
63
62
|
try {
|
|
64
63
|
const response = await fetch(`${baseURL}/commerce/product-manufacturers/${manufacturer.id}`, {
|
|
65
64
|
method: 'PATCH',
|
|
66
|
-
headers: {
|
|
67
|
-
'Content-Type': 'application/json',
|
|
68
|
-
},
|
|
65
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
69
66
|
body: JSON.stringify(manufacturer),
|
|
70
67
|
})
|
|
71
68
|
|
|
@@ -93,6 +90,7 @@ async function deleteManufacturer(id: number): Promise<boolean> {
|
|
|
93
90
|
try {
|
|
94
91
|
const response = await fetch(`${baseURL}/commerce/product-manufacturers/${id}`, {
|
|
95
92
|
method: 'DELETE',
|
|
93
|
+
headers: withCsrfHeader(),
|
|
96
94
|
})
|
|
97
95
|
|
|
98
96
|
if (!response.ok) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveApiBaseUrl } from '../../api-url'
|
|
2
2
|
import type { Products } from '../../../types/defaults'
|
|
3
|
+
import { withCsrfHeader } from '@stacksjs/browser/composables/csrf'
|
|
3
4
|
import { useStorage } from '@stacksjs/browser/composables/useStorage'
|
|
4
5
|
import { pushToast } from '../../toasts'
|
|
5
6
|
|
|
@@ -36,9 +37,7 @@ async function createProduct(product: Products): Promise<Products | null> {
|
|
|
36
37
|
try {
|
|
37
38
|
const response = await fetch(`${baseURL}/commerce/products`, {
|
|
38
39
|
method: 'POST',
|
|
39
|
-
headers: {
|
|
40
|
-
'Content-Type': 'application/json',
|
|
41
|
-
},
|
|
40
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
42
41
|
body: JSON.stringify(product),
|
|
43
42
|
})
|
|
44
43
|
|
|
@@ -63,9 +62,7 @@ async function updateProduct(product: Products): Promise<Products | null> {
|
|
|
63
62
|
try {
|
|
64
63
|
const response = await fetch(`${baseURL}/commerce/products/${product.id}`, {
|
|
65
64
|
method: 'PATCH',
|
|
66
|
-
headers: {
|
|
67
|
-
'Content-Type': 'application/json',
|
|
68
|
-
},
|
|
65
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
69
66
|
body: JSON.stringify(product),
|
|
70
67
|
})
|
|
71
68
|
|
|
@@ -93,6 +90,7 @@ async function deleteProduct(id: number): Promise<boolean> {
|
|
|
93
90
|
try {
|
|
94
91
|
const response = await fetch(`${baseURL}/commerce/products/${id}`, {
|
|
95
92
|
method: 'DELETE',
|
|
93
|
+
headers: withCsrfHeader(),
|
|
96
94
|
})
|
|
97
95
|
|
|
98
96
|
if (!response.ok) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveApiBaseUrl } from '../../api-url'
|
|
2
2
|
import type { Reviews } from '../../../types/defaults'
|
|
3
|
+
import { withCsrfHeader } from '@stacksjs/browser/composables/csrf'
|
|
3
4
|
import { useStorage } from '@stacksjs/browser/composables/useStorage'
|
|
4
5
|
import { pushToast } from '../../toasts'
|
|
5
6
|
|
|
@@ -36,9 +37,7 @@ async function createReview(review: Reviews): Promise<Reviews | null> {
|
|
|
36
37
|
try {
|
|
37
38
|
const response = await fetch(`${baseURL}/commerce/products/reviews`, {
|
|
38
39
|
method: 'POST',
|
|
39
|
-
headers: {
|
|
40
|
-
'Content-Type': 'application/json',
|
|
41
|
-
},
|
|
40
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
42
41
|
body: JSON.stringify(review),
|
|
43
42
|
})
|
|
44
43
|
|
|
@@ -63,9 +62,7 @@ async function updateReview(review: Reviews): Promise<Reviews | null> {
|
|
|
63
62
|
try {
|
|
64
63
|
const response = await fetch(`${baseURL}/commerce/products/reviews/${review.id}`, {
|
|
65
64
|
method: 'PATCH',
|
|
66
|
-
headers: {
|
|
67
|
-
'Content-Type': 'application/json',
|
|
68
|
-
},
|
|
65
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
69
66
|
body: JSON.stringify(review),
|
|
70
67
|
})
|
|
71
68
|
|
|
@@ -93,6 +90,7 @@ async function deleteReview(id: number): Promise<boolean> {
|
|
|
93
90
|
try {
|
|
94
91
|
const response = await fetch(`${baseURL}/commerce/products/reviews/${id}`, {
|
|
95
92
|
method: 'DELETE',
|
|
93
|
+
headers: withCsrfHeader(),
|
|
96
94
|
})
|
|
97
95
|
|
|
98
96
|
if (!response.ok) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveApiBaseUrl } from '../../api-url'
|
|
2
2
|
import type { Units } from '../../../types/defaults'
|
|
3
|
+
import { withCsrfHeader } from '@stacksjs/browser/composables/csrf'
|
|
3
4
|
import { useStorage } from '@stacksjs/browser/composables/useStorage'
|
|
4
5
|
import { pushToast } from '../../toasts'
|
|
5
6
|
|
|
@@ -36,9 +37,7 @@ async function createUnit(unit: Units): Promise<Units | null> {
|
|
|
36
37
|
try {
|
|
37
38
|
const response = await fetch(`${baseURL}/commerce/products/units`, {
|
|
38
39
|
method: 'POST',
|
|
39
|
-
headers: {
|
|
40
|
-
'Content-Type': 'application/json',
|
|
41
|
-
},
|
|
40
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
42
41
|
body: JSON.stringify(unit),
|
|
43
42
|
})
|
|
44
43
|
|
|
@@ -63,9 +62,7 @@ async function updateUnit(unit: Units): Promise<Units | null> {
|
|
|
63
62
|
try {
|
|
64
63
|
const response = await fetch(`${baseURL}/commerce/products/units/${unit.id}`, {
|
|
65
64
|
method: 'PATCH',
|
|
66
|
-
headers: {
|
|
67
|
-
'Content-Type': 'application/json',
|
|
68
|
-
},
|
|
65
|
+
headers: withCsrfHeader({ 'Content-Type': 'application/json' }),
|
|
69
66
|
body: JSON.stringify(unit),
|
|
70
67
|
})
|
|
71
68
|
|
|
@@ -93,6 +90,7 @@ async function deleteUnit(id: number): Promise<boolean> {
|
|
|
93
90
|
try {
|
|
94
91
|
const response = await fetch(`${baseURL}/commerce/products/units/${id}`, {
|
|
95
92
|
method: 'DELETE',
|
|
93
|
+
headers: withCsrfHeader(),
|
|
96
94
|
})
|
|
97
95
|
|
|
98
96
|
if (!response.ok) {
|