includio-cms 0.27.0 → 0.33.0
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/API.md +58 -14
- package/CHANGELOG.md +59 -0
- package/DOCS.md +1 -1
- package/ROADMAP.md +1 -0
- package/dist/admin/api/handler.js +4 -0
- package/dist/admin/api/integrations.d.ts +13 -0
- package/dist/admin/api/integrations.js +61 -0
- package/dist/admin/api/test-email.d.ts +9 -0
- package/dist/admin/api/test-email.js +39 -0
- package/dist/admin/auth-client.d.ts +543 -543
- package/dist/admin/client/index.d.ts +10 -0
- package/dist/admin/client/index.js +12 -0
- package/dist/admin/client/maintenance/maintenance-page.svelte +210 -0
- package/dist/admin/client/shop/coupon-schema.d.ts +1 -1
- package/dist/admin/client/shop/restore-order-cell.svelte +29 -0
- package/dist/admin/client/shop/restore-order-cell.svelte.d.ts +8 -0
- package/dist/admin/client/shop/shop-order-detail-page.svelte +156 -1
- package/dist/admin/client/shop/shop-orders-list-page.svelte +113 -53
- package/dist/admin/components/layout/app-sidebar.svelte +2 -0
- package/dist/admin/components/layout/nav-custom.svelte +26 -0
- package/dist/admin/components/layout/nav-custom.svelte.d.ts +3 -0
- package/dist/admin/components/layout/page-header.svelte +13 -3
- package/dist/admin/components/layout/page-header.svelte.d.ts +13 -3
- package/dist/admin/remote/admin.remote.d.ts +7 -0
- package/dist/admin/remote/admin.remote.js +10 -0
- package/dist/admin/remote/entry.remote.d.ts +2 -2
- package/dist/admin/remote/index.d.ts +1 -0
- package/dist/admin/remote/index.js +1 -0
- package/dist/admin/remote/invite.d.ts +1 -1
- package/dist/admin/remote/shop.remote.d.ts +125 -40
- package/dist/admin/remote/shop.remote.js +59 -10
- package/dist/admin/types.d.ts +15 -0
- package/dist/admin/utils/csv-export.d.ts +45 -0
- package/dist/admin/utils/csv-export.js +61 -0
- package/dist/cli/scaffold/admin.js +1 -1
- package/dist/components/ui/input/input.svelte.d.ts +1 -1
- package/dist/components/ui/input-group/input-group-input.svelte.d.ts +1 -1
- package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +1 -1
- package/dist/core/cms.d.ts +44 -2
- package/dist/core/cms.js +64 -0
- package/dist/core/index.d.ts +2 -4
- package/dist/core/index.js +1 -4
- package/dist/core/server/index.d.ts +4 -1
- package/dist/core/server/index.js +4 -1
- package/dist/db-postgres/schema/shop/index.d.ts +1 -0
- package/dist/db-postgres/schema/shop/index.js +1 -0
- package/dist/db-postgres/schema/shop/invoice.d.ts +254 -0
- package/dist/db-postgres/schema/shop/invoice.js +27 -0
- package/dist/db-postgres/schema/shop/order.d.ts +104 -0
- package/dist/db-postgres/schema/shop/order.js +8 -0
- package/dist/shop/adapters/fakturownia/client.d.ts +33 -0
- package/dist/shop/adapters/fakturownia/client.js +87 -0
- package/dist/shop/adapters/fakturownia/index.d.ts +27 -0
- package/dist/shop/adapters/fakturownia/index.js +47 -0
- package/dist/shop/adapters/fakturownia/payload.d.ts +35 -0
- package/dist/shop/adapters/fakturownia/payload.js +45 -0
- package/dist/shop/adapters/payu/index.js +11 -0
- package/dist/shop/client/index.d.ts +7 -0
- package/dist/shop/http/checkout-handler.js +11 -0
- package/dist/shop/index.d.ts +4 -1
- package/dist/shop/index.js +3 -0
- package/dist/shop/nip.d.ts +12 -0
- package/dist/shop/nip.js +23 -0
- package/dist/shop/server/coupons.d.ts +10 -0
- package/dist/shop/server/coupons.js +19 -0
- package/dist/shop/server/email.d.ts +7 -3
- package/dist/shop/server/email.js +86 -112
- package/dist/shop/server/emailTemplateRegistry.d.ts +47 -0
- package/dist/shop/server/emailTemplateRegistry.js +288 -0
- package/dist/shop/server/invoices.d.ts +64 -0
- package/dist/shop/server/invoices.js +237 -0
- package/dist/shop/server/orders.d.ts +64 -1
- package/dist/shop/server/orders.js +155 -15
- package/dist/shop/templates/_partials/footer.en.html +4 -0
- package/dist/shop/templates/_partials/footer.pl.html +4 -0
- package/dist/shop/templates/_partials/header.en.html +4 -0
- package/dist/shop/templates/_partials/header.pl.html +4 -0
- package/dist/shop/templates/_partials/items.en.html +14 -0
- package/dist/shop/templates/_partials/items.pl.html +14 -0
- package/dist/shop/templates/_partials/tracking.en.html +7 -0
- package/dist/shop/templates/_partials/tracking.pl.html +7 -0
- package/dist/shop/templates/awaiting-payment.en.html +6 -0
- package/dist/shop/templates/awaiting-payment.pl.html +6 -0
- package/dist/shop/templates/cancelled.en.html +6 -0
- package/dist/shop/templates/cancelled.pl.html +6 -0
- package/dist/shop/templates/low-stock.en.html +14 -0
- package/dist/shop/templates/low-stock.pl.html +14 -0
- package/dist/shop/templates/order-completed.en.html +6 -0
- package/dist/shop/templates/order-completed.pl.html +6 -0
- package/dist/shop/templates/order-received.en.html +7 -0
- package/dist/shop/templates/order-received.pl.html +7 -0
- package/dist/shop/templates/payment-received.en.html +7 -0
- package/dist/shop/templates/payment-received.pl.html +7 -0
- package/dist/shop/templates/payment-rejected.en.html +6 -0
- package/dist/shop/templates/payment-rejected.pl.html +6 -0
- package/dist/shop/templates/preparing.en.html +7 -0
- package/dist/shop/templates/preparing.pl.html +7 -0
- package/dist/shop/templates/refunded.en.html +6 -0
- package/dist/shop/templates/refunded.pl.html +6 -0
- package/dist/shop/templates/shipped.en.html +7 -0
- package/dist/shop/templates/shipped.pl.html +7 -0
- package/dist/shop/types.d.ts +130 -1
- package/dist/sveltekit/index.d.ts +0 -1
- package/dist/sveltekit/index.js +0 -1
- package/dist/sveltekit/server/index.d.ts +1 -0
- package/dist/sveltekit/server/index.js +1 -0
- package/dist/types/adapters/email.d.ts +13 -0
- package/dist/types/cms.d.ts +30 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/updates/0.28.0/index.d.ts +2 -0
- package/dist/updates/0.28.0/index.js +38 -0
- package/dist/updates/0.34.0/index.d.ts +2 -0
- package/dist/updates/0.34.0/index.js +17 -0
- package/dist/updates/index.js +5 -1
- package/package.json +7 -2
package/API.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# includio-cms — Public API v0.
|
|
1
|
+
# includio-cms — Public API v0.33.0
|
|
2
2
|
|
|
3
3
|
> Auto-generated by `scripts/generate-api-md.ts`. Do not edit by hand.
|
|
4
4
|
|
|
5
|
-
Entry points: **
|
|
5
|
+
Entry points: **19** · Stable: **489** · Experimental: **4**
|
|
6
6
|
|
|
7
7
|
Tags:
|
|
8
8
|
- `@public` — frozen for v1.0; semver-protected.
|
|
@@ -13,23 +13,29 @@ Tags:
|
|
|
13
13
|
|
|
14
14
|
### `includio-cms`
|
|
15
15
|
|
|
16
|
-
- `
|
|
17
|
-
- `
|
|
18
|
-
- `getCMS(): CMS` — Returns the singleton CMS instance. Must be called after `includioCMS()`
|
|
19
|
-
- `interface ResolvedMedia` — Resolved media file plus its image styles + blur placeholder. Returned by
|
|
16
|
+
- `interface EmailAdapter` — Contract for email adapters. Used by `better-auth` reset-password flow and form submission
|
|
17
|
+
- `getMailer(): EmailAdapter | null` — Returns the configured email adapter from the active CMS singleton, or
|
|
20
18
|
- `type ResolvedSeo = { [K in keyof SeoFieldData]: K extends 'ogImage' ? string | MediaFile | undefined : string | unde...` — Normalized SEO data — localized values flattened by `language`. A stable
|
|
21
|
-
- `resolveMediaWithStyles(mediaIds: string[], styles?: ImageFieldStyle[]): Promise<Record<string, ResolvedMedia>>` — Resolve media files by IDs and generate image styles. Useful for plugins or
|
|
22
19
|
- `resolveSeo(entry: { seo?: Record<string, unknown> } | Record<string,..., language?: string): ResolvedSeo` — Resolve `entry.seo` into flat fields, flattening localized objects by
|
|
20
|
+
- `sendMail(options: SendMailOptions): Promise<void>` — Send an email through the configured CMS email adapter. Thin convenience
|
|
21
|
+
- `interface SendMailOptions`
|
|
23
22
|
|
|
24
23
|
### `includio-cms/core`
|
|
25
24
|
|
|
25
|
+
- `interface EmailAdapter` — Contract for email adapters. Used by `better-auth` reset-password flow and form submission
|
|
26
|
+
- `getMailer(): EmailAdapter | null` — Returns the configured email adapter from the active CMS singleton, or
|
|
27
|
+
- `type ResolvedSeo = { [K in keyof SeoFieldData]: K extends 'ogImage' ? string | MediaFile | undefined : string | unde...` — Normalized SEO data — localized values flattened by `language`. A stable
|
|
28
|
+
- `resolveSeo(entry: { seo?: Record<string, unknown> } | Record<string,..., language?: string): ResolvedSeo` — Resolve `entry.seo` into flat fields, flattening localized objects by
|
|
29
|
+
- `sendMail(options: SendMailOptions): Promise<void>` — Send an email through the configured CMS email adapter. Thin convenience
|
|
30
|
+
- `interface SendMailOptions`
|
|
31
|
+
|
|
32
|
+
### `includio-cms/core/server`
|
|
33
|
+
|
|
26
34
|
- `createEntityAPI(cms: CMS, opts?: EntityAPIOptions): <inferred>` — Creates a high-level Entity API (CRUD + publish/archive) bound to a CMS
|
|
27
35
|
- `getAuth(): <inferred>` — Returns the underlying `better-auth` instance from the initialized CMS.
|
|
28
36
|
- `getCMS(): CMS` — Returns the singleton CMS instance. Must be called after `includioCMS()`
|
|
29
37
|
- `interface ResolvedMedia` — Resolved media file plus its image styles + blur placeholder. Returned by
|
|
30
|
-
- `type ResolvedSeo = { [K in keyof SeoFieldData]: K extends 'ogImage' ? string | MediaFile | undefined : string | unde...` — Normalized SEO data — localized values flattened by `language`. A stable
|
|
31
38
|
- `resolveMediaWithStyles(mediaIds: string[], styles?: ImageFieldStyle[]): Promise<Record<string, ResolvedMedia>>` — Resolve media files by IDs and generate image styles. Useful for plugins or
|
|
32
|
-
- `resolveSeo(entry: { seo?: Record<string, unknown> } | Record<string,..., language?: string): ResolvedSeo` — Resolve `entry.seo` into flat fields, flattening localized objects by
|
|
33
39
|
|
|
34
40
|
### `includio-cms/types`
|
|
35
41
|
|
|
@@ -37,6 +43,8 @@ Tags:
|
|
|
37
43
|
- `interface ApiKeyConfig`
|
|
38
44
|
- `interface ArrayField`
|
|
39
45
|
- `interface AuthConfig`
|
|
46
|
+
- `interface AuthRateLimitConfig`
|
|
47
|
+
- `interface AuthRateLimitRule`
|
|
40
48
|
- `interface BaseField`
|
|
41
49
|
- `interface BlocksField`
|
|
42
50
|
- `interface BooleanField`
|
|
@@ -134,19 +142,28 @@ Tags:
|
|
|
134
142
|
|
|
135
143
|
- `const AcceptInvitePage: LegacyComponentType`
|
|
136
144
|
- `const AdminAfterLoginLayout: LegacyComponentType`
|
|
145
|
+
- `interface AdminConfig` — Optional admin UI configuration — extension points for the panel sidebar
|
|
137
146
|
- `const AdminLayout: LegacyComponentType`
|
|
147
|
+
- `interface AdminNavItem` — One additional sidebar entry rendered below built-in nav sections
|
|
138
148
|
- `const Badge: LegacyComponentType`
|
|
149
|
+
- `interface Breadcrumb`
|
|
150
|
+
- `class Breadcrumbs`
|
|
151
|
+
- `buildCsv(opts: { headers: string[]; rows: (string | number | null ...): string` — Build a CSV string from `headers` and `rows`. Values are escaped per
|
|
139
152
|
- `buildCustomFieldsMap(...plugins: PluginConfig[]): Map<string, CustomFieldDefinition>` — Build a Map<fieldType, CustomFieldDefinition> from plugin configs.
|
|
140
153
|
- `buildIconSetMap(...plugins: IconSetPlugin[]): Map<string, IconSetPlugin>` — Build a Map<slug, IconSetPlugin> from icon-set plugin instances.
|
|
141
154
|
- `const Button: LegacyComponentType`
|
|
142
155
|
- `Card`
|
|
143
156
|
- `const CollectionPage: LegacyComponentType`
|
|
157
|
+
- `type ColumnDef = DisplayColumnDef<TData, TValue> | GroupColumnDef<TData, TValue> | AccessorColumnDef<TData, TValue>`
|
|
144
158
|
- `const DashboardPage: LegacyComponentType`
|
|
159
|
+
- `const DataTable: LegacyComponentType`
|
|
145
160
|
- `Dialog`
|
|
161
|
+
- `downloadCsv(opts: { filename: string; csv: string }): void` — Trigger a client-side CSV download. Wraps the given `csv` string in a
|
|
146
162
|
- `const EntryPage: LegacyComponentType`
|
|
147
163
|
- `const FileMiniature: LegacyComponentType`
|
|
148
164
|
- `const FormPage: LegacyComponentType`
|
|
149
165
|
- `const FormSubmissionPage: LegacyComponentType`
|
|
166
|
+
- `getBreadcrumbs`
|
|
150
167
|
- `getContentLanguage`
|
|
151
168
|
- `getCustomFields(): Map<string, CustomFieldDefinition>`
|
|
152
169
|
- `getIconSets(): Map<string, IconSetPlugin>`
|
|
@@ -158,9 +175,12 @@ Tags:
|
|
|
158
175
|
- `const MaintenancePage: LegacyComponentType`
|
|
159
176
|
- `const MediaPage: LegacyComponentType`
|
|
160
177
|
- `const MediaSelector: LegacyComponentType`
|
|
178
|
+
- `const PageHeader: LegacyComponentType`
|
|
179
|
+
- `interface PaginationState`
|
|
161
180
|
- `const ResetPasswordPage: LegacyComponentType`
|
|
162
181
|
- `resolveIconSet(slug?: string): IconSetPlugin | null` — Resolve a single icon set: explicit `slug` (from {@link IconField.set}) takes
|
|
163
182
|
- `const Separator: LegacyComponentType`
|
|
183
|
+
- `setBreadcrumbs`
|
|
164
184
|
- `const ShippingMethodEditPage: LegacyComponentType`
|
|
165
185
|
- `const ShippingMethodNewPage: LegacyComponentType`
|
|
166
186
|
- `const ShippingMethodsListPage: LegacyComponentType`
|
|
@@ -171,6 +191,10 @@ Tags:
|
|
|
171
191
|
- `const ShopOrdersListPage: LegacyComponentType`
|
|
172
192
|
- `const ShopProductsListPage: LegacyComponentType`
|
|
173
193
|
- `const Skeleton: LegacyComponentType`
|
|
194
|
+
- `type SortingState = ColumnSort[]`
|
|
195
|
+
- `const StateDisplay: LegacyComponentType`
|
|
196
|
+
- `const TablePagination: LegacyComponentType`
|
|
197
|
+
- `const TableToolbar: LegacyComponentType`
|
|
174
198
|
- `Tabs`
|
|
175
199
|
- `Tooltip`
|
|
176
200
|
- `useField(form: SuperForm<Record<string, unknown>>, path: FormPathLeaves<Record<string, unknown>>): <inferred>` — Simplified wrapper around sveltekit-superforms' formFieldProxy.
|
|
@@ -195,6 +219,7 @@ Tags:
|
|
|
195
219
|
- `const deleteFormSubmissions: <inferred>`
|
|
196
220
|
- `const deleteMediaFile: <inferred>`
|
|
197
221
|
- `const deleteMediaTag: <inferred>`
|
|
222
|
+
- `const deleteOrderCmd: <inferred>`
|
|
198
223
|
- `const deleteShippingMethodCmd: <inferred>`
|
|
199
224
|
- `const deleteShopDataForEntry: <inferred>`
|
|
200
225
|
- `const exportFormSubmissions: <inferred>`
|
|
@@ -202,6 +227,7 @@ Tags:
|
|
|
202
227
|
- `const findMediaReferences: <inferred>`
|
|
203
228
|
- `const generateAltText: <inferred>`
|
|
204
229
|
- `const generateBalanceLinkForOrder: <inferred>`
|
|
230
|
+
- `const getAdminExtraNavItems: <inferred>`
|
|
205
231
|
- `const getAltOverview: <inferred>`
|
|
206
232
|
- `const getCollection: <inferred>`
|
|
207
233
|
- `const getCollections: <inferred>`
|
|
@@ -223,6 +249,7 @@ Tags:
|
|
|
223
249
|
- `const getMediaTags: <inferred>`
|
|
224
250
|
- `const getMediaTagsWithCounts: <inferred>`
|
|
225
251
|
- `const getOrderForAdmin: <inferred>`
|
|
252
|
+
- `const getOrderInvoiceAdmin: <inferred>`
|
|
226
253
|
- `const getOrderRefundsAdmin: <inferred>`
|
|
227
254
|
- `const getRawEntries: <inferred>`
|
|
228
255
|
- `const getRawEntry: <inferred>`
|
|
@@ -236,6 +263,7 @@ Tags:
|
|
|
236
263
|
- `const getSingles: <inferred>`
|
|
237
264
|
- `const getSubmissionsOverview: <inferred>`
|
|
238
265
|
- `const isAIAvailable: <inferred>`
|
|
266
|
+
- `const issueInvoiceCmd: <inferred>`
|
|
239
267
|
- `const listCouponsAdmin: <inferred>`
|
|
240
268
|
- `const listOrdersAdmin: <inferred>`
|
|
241
269
|
- `const listShippingMethodsAdmin: <inferred>`
|
|
@@ -248,6 +276,7 @@ Tags:
|
|
|
248
276
|
- `const reorderEntriesCommand: <inferred>`
|
|
249
277
|
- `const reorderShippingMethodsCmd: <inferred>`
|
|
250
278
|
- `const resendOrderEmailCmd: <inferred>`
|
|
279
|
+
- `const restoreOrderCmd: <inferred>`
|
|
251
280
|
- `const searchEntries: <inferred>`
|
|
252
281
|
- `const searchLinkableEntries: <inferred>`
|
|
253
282
|
- `const setFocalPoint: <inferred>`
|
|
@@ -273,10 +302,6 @@ Tags:
|
|
|
273
302
|
- `defineObject(config: Omit<ObjectField, 'type'>): ObjectField` — Defines a reusable object field (nested record). Use inline inside `fields[]`
|
|
274
303
|
- `defineSingle(config: SingleInput): SingleConfig` — Defines a singleton (single-entry content type, e.g. site settings, homepage).
|
|
275
304
|
- `enableHybridEditing(): <inferred>` — Call in a layout/component script to enable data-hybrid-path rendering for all descendant HybridTarget/Image/Video.
|
|
276
|
-
- `extractBlocks(doc: StructuredContentDoc, type?: string): SCNode[]` — Extract block-level nodes, optionally filtered by type.
|
|
277
|
-
- `extractInlineBlocks(doc: StructuredContentDoc, blockType?: string): SCInlineBlockAttrs[]` — Extract inline block nodes, optionally filtered by blockType.
|
|
278
|
-
- `extractMediaRefs(doc: StructuredContentDoc): MediaRef[]` — Extract media references from figure/video/image nodes.
|
|
279
|
-
- `extractText(doc: StructuredContentDoc): string` — Extract all plain text from doc (useful for excerpts, search indexing).
|
|
280
305
|
- `getCustomFields(): Map<string, CustomFieldDefinition>`
|
|
281
306
|
- `getLink(link: string | { url: string | Record<string, string> } |..., language?: string): string` — Resolves a link value to a URL string.
|
|
282
307
|
- `getRemotes(): typeof remotes`
|
|
@@ -302,6 +327,10 @@ Tags:
|
|
|
302
327
|
- `const createConsentLog: <inferred>`
|
|
303
328
|
- `const createFormSubmission: <inferred>`
|
|
304
329
|
- `createRestApiHandler(): <inferred>` — REST API handler factory. Returns `{ GET, POST, PUT, DELETE }`
|
|
330
|
+
- `extractBlocks(doc: StructuredContentDoc, type?: string): SCNode[]` — Extract block-level nodes, optionally filtered by type.
|
|
331
|
+
- `extractInlineBlocks(doc: StructuredContentDoc, blockType?: string): SCInlineBlockAttrs[]` — Extract inline block nodes, optionally filtered by blockType.
|
|
332
|
+
- `extractMediaRefs(doc: StructuredContentDoc): MediaRef[]` — Extract media references from figure/video/image nodes.
|
|
333
|
+
- `extractText(doc: StructuredContentDoc): string` — Extract all plain text from doc (useful for excerpts, search indexing).
|
|
305
334
|
- `generateApiKey(): string` — Generate a cryptographically random API key (32 bytes, base64url-encoded).
|
|
306
335
|
- `getPreviewEntry(event: RequestEvent, options: { language: string }): Promise<Entry | null>` — Resolves the preview entry from a `?preview=<versionId>` query param.
|
|
307
336
|
- `includioCMS(cmsConfig: CMSConfig): Handle[]` — SvelteKit `Handle[]` array that initializes the CMS, generates runtime
|
|
@@ -337,6 +366,8 @@ Tags:
|
|
|
337
366
|
- `const shopCouponRedemptionsTable: <inferred>`
|
|
338
367
|
- `const shopCouponsTable: <inferred>`
|
|
339
368
|
- `type ShopCouponType = 'percent' | 'fixed'`
|
|
369
|
+
- `const shopInvoicesTable: <inferred>`
|
|
370
|
+
- `type ShopInvoiceStatus = 'pending' | 'issued' | 'sent' | 'failed'`
|
|
340
371
|
- `const shopOrderItemsTable: <inferred>`
|
|
341
372
|
- `const shopOrdersTable: <inferred>`
|
|
342
373
|
- `const shopOrderStatusHistoryTable: <inferred>`
|
|
@@ -377,6 +408,8 @@ Tags:
|
|
|
377
408
|
- `const shopCouponRedemptionsTable: <inferred>`
|
|
378
409
|
- `const shopCouponsTable: <inferred>`
|
|
379
410
|
- `type ShopCouponType = 'percent' | 'fixed'`
|
|
411
|
+
- `const shopInvoicesTable: <inferred>`
|
|
412
|
+
- `type ShopInvoiceStatus = 'pending' | 'issued' | 'sent' | 'failed'`
|
|
380
413
|
- `const shopOrderItemsTable: <inferred>`
|
|
381
414
|
- `const shopOrdersTable: <inferred>`
|
|
382
415
|
- `const shopOrderStatusHistoryTable: <inferred>`
|
|
@@ -411,7 +444,9 @@ Tags:
|
|
|
411
444
|
- `interface CouponRef`
|
|
412
445
|
- `type Currency = 'PLN'`
|
|
413
446
|
- `defineShop(config: ShopConfig): ResolvedShopConfig`
|
|
414
|
-
- `type DepositAmount =
|
|
447
|
+
- `type DepositAmount = { type: 'percent'; value: number } | { type: 'amount'; value: number }` — Deposit amount specifier. `percent` charges `floor(base * value / 100)` of
|
|
448
|
+
- `fakturowniaAdapter(opts: FakturowniaAdapterOptions): InvoicingAdapter` — Invoicing adapter backed by Fakturownia (fakturownia.pl). Issues a paid VAT
|
|
449
|
+
- `interface FakturowniaAdapterOptions`
|
|
415
450
|
- `filterUpcoming(variants: T[], config: VariantExpiryConfig | null, now?: Date): T[]` — Return the subset of `variants` that have not yet expired. Order is
|
|
416
451
|
- `type GeowidgetConfigPreset = 'parcelcollect' | 'parcelsend' | 'parcelcollect247' | string`
|
|
417
452
|
- `type I18nText = { [lang: string]: string; }`
|
|
@@ -421,8 +456,17 @@ Tags:
|
|
|
421
456
|
- `interface InpostSenderAddress`
|
|
422
457
|
- `interpolateTemplate(template: string, vars: Record<string, unknown>, locale: string): string` — String interpolation engine used by `defineShop({ variantLabel.template })`
|
|
423
458
|
- `class InvalidVariantAttributesError`
|
|
459
|
+
- `interface InvoiceBuyer`
|
|
460
|
+
- `interface InvoiceContext`
|
|
461
|
+
- `interface InvoiceCreateResult`
|
|
462
|
+
- `type InvoiceIssuePolicy = 'b2bAndOnRequest' | 'always'` — When an invoice should be issued automatically after an order is fully paid.
|
|
463
|
+
- `interface InvoiceLineItem`
|
|
464
|
+
- `interface InvoicePayload`
|
|
465
|
+
- `interface InvoicingAdapter` — Adapter that issues invoices with an external provider (e.g. Fakturownia).
|
|
466
|
+
- `isValidNip(nip: string): boolean` — Validate a Polish NIP (tax id) — 10 digits with a weighted checksum.
|
|
424
467
|
- `isVariantExpired(variant: VariantLike, config: VariantExpiryConfig | null, now: Date = new Date(Date.now())): boolean` — Check whether a variant has expired under the given config. Fail-open by
|
|
425
468
|
- `manualAdapter(opts: ManualPaymentAdapterOptions = {}): PaymentAdapter` — Manual payment adapter — order goes to awaitingPayment, admin marks as paid later
|
|
469
|
+
- `type Order = typeof shopOrdersTable.$inferSelect` — Public row type for a single shop order — mirrors the Drizzle `shop_orders`
|
|
426
470
|
- `interface OrderRef`
|
|
427
471
|
- `type OrderStatus = | 'new' | 'awaitingPayment' | 'paid' | 'preparing' | 'sent' | 'done' | 'cancelled' | 'paymentReje...`
|
|
428
472
|
- `interface PartialPayment` — Persisted partial-payment summary on `order.partialPayment` when the order
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,65 @@
|
|
|
3
3
|
All notable changes to includio-cms are documented here.
|
|
4
4
|
Generated from `src/lib/updates/` — do not edit manually.
|
|
5
5
|
|
|
6
|
+
## 0.34.0 — 2026-06-03
|
|
7
|
+
|
|
8
|
+
Soft-delete zamówień (admin-only) — administrator może ukryć zamówienie z listy bez utraty danych. Odwracalne (kosz + przywracanie), bezpieczne księgowo: nie kasuje wiersza, faktur ani historii. Dozwolone tylko dla „bezpiecznych" statusów bez płatności (`new`, `awaitingPayment`, `cancelled`, `paymentRejected`) i zablokowane dla zamówień z wystawioną fakturą. Cała funkcja widoczna wyłącznie dla roli `admin`. Additive only — bez breaking changes.
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `shop_orders` dostaje kolumny `deleted_at` (timestamptz, NULL = widoczne) i `deleted_by` (text). Lista zamówień domyślnie ukrywa miękko usunięte; `listOrders`/`countOrders` (`$lib/shop/server/orders.ts`) przyjmują `deleted: "exclude" | "only" | "include"` (domyślnie `exclude`).
|
|
12
|
+
- `softDeleteOrder(orderId, deletedBy)` / `restoreOrder(orderId)` (`$lib/shop/server/orders.ts`, `@public`) — miękkie usuwanie/przywracanie (idempotentne). Soft-delete zwalnia natychmiast aktywną rezerwację stocku (nie czeka na TTL). Guard: `decideOrderDeletion(status, invoice)` + `DELETABLE_ORDER_STATUSES` / `isOrderDeletable(status)` (czyste, testowalne) — rzuca `OrderNotDeletableError` dla niedozwolonego statusu lub faktury `issued`/`sent`.
|
|
13
|
+
- Auto-restore: zmiana statusu miękko usuniętego zamówienia na nieusuwalny (np. spóźniony webhook płatności na ukrytym `awaitingPayment` → `paid`) automatycznie zdejmuje je z kosza — opłacone zamówienia nigdy nie zostają ukryte.
|
|
14
|
+
- Customer-facing `getOrderByNumber` nie zwraca miękko usuniętego zamówienia (nie resolvuje się na storefroncie); admin `getOrderById` celowo ignoruje `deletedAt` (podgląd/przywracanie z kosza).
|
|
15
|
+
- `deleteOrderCmd` / `restoreOrderCmd` (commands) + `listOrdersAdmin({ deleted })` (`includio-cms/admin/remote`) — chronione `requireRole("admin")` (kosz i usuwanie tylko dla admina; normalna lista nadal `requireAuth`).
|
|
16
|
+
- Admin UI: `shop-order-detail-page` zyskuje sekcję „Usuń zamówienie" (tylko admin + status usuwalny) z dialogiem potwierdzenia; `shop-orders-list-page` — przełącznik „Kosz" (tylko admin) z akcją „Przywróć" w wierszu.
|
|
17
|
+
|
|
18
|
+
### Migration
|
|
19
|
+
|
|
20
|
+
```sql
|
|
21
|
+
ALTER TABLE shop_orders ADD COLUMN IF NOT EXISTS deleted_at timestamptz;
|
|
22
|
+
ALTER TABLE shop_orders ADD COLUMN IF NOT EXISTS deleted_by text;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 0.28.0 — 2026-06-01
|
|
26
|
+
|
|
27
|
+
Fakturowanie — integracja sklepu z Fakturownią: automatyczne wystawianie i wysyłka faktury po opłaceniu zamówienia. Nowy generyczny `InvoicingAdapter` (spójny z payment/carrier) + `fakturowniaAdapter()` jako pierwsza implementacja. Faktura wystawiana fire-and-forget gdy zamówienie jest w pełni opłacone (`!balanceOwed` — zaliczki czekają na dopłatę balansu), wysyłana przez Fakturownię (`send_by_email`). Trigger konfigurowalny per-adapter (`issueWhen`: `b2bAndOnRequest` domyślnie / `always`). Checkout zbiera dane B2B (NIP z twardą walidacją sumy kontrolnej, nazwa firmy, adres do faktury, opt-in „chcę fakturę"). Idempotencja przez `shop_invoices` (unikat per zamówienie); ręczny retry w adminie. Additive only — żadnych breaking zmian.
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- `defineShop({ invoicing })` (`includio-cms/shop`) — nowy opcjonalny slot adaptera fakturowania, analogiczny do `payment` / `carriers`. `InvoicingAdapter { id, issueWhen?, createInvoice(payload, ctx), send?(externalId, ctx) }` + typy payloadu (`InvoicePayload`, `InvoiceBuyer`, `InvoiceLineItem`, `InvoiceCreateResult`, `InvoiceContext`, `InvoiceIssuePolicy`) wyeksportowane jako `@public` z `includio-cms/shop`.
|
|
31
|
+
- `fakturowniaAdapter({ domain, apiToken, kind?, issueWhen?, sendEmail? })` (`includio-cms/shop`, `@public`) — adapter dla Fakturowni (fakturownia.pl). `createInvoice` mapuje zamówienie na fakturę VAT oznaczoną jako opłacona (`paid_date` = data potwierdzenia płatności), `send` woła `send_by_email` (domyślnie włączone). Numeracja i dane sprzedawcy zarządzane po stronie konta Fakturowni. Sekret `apiToken` przekazywany przez konsumenta z `$env/dynamic/private` (jak `STRIPE_SECRET_KEY`).
|
|
32
|
+
- Auto-faktura po opłaceniu — `maybeIssueInvoiceForOrder(orderId)` (`$lib/shop/server/invoices.ts`) wołane fire-and-forget z `updateOrderStatus` (przy `paid`) oraz z `markBalancePaid` (po dopłacie balansu). Fail-open: błąd providera nie blokuje webhooka płatności. Guard wystawia fakturę tylko gdy zamówienie w pełni opłacone (`status ∈ {paid, preparing, sent, done}` && `!balanceOwed`) — deposit czeka na dopłatę.
|
|
33
|
+
- Konfigurowalny trigger `issueWhen`: `b2bAndOnRequest` (domyślny — faktura gdy podano NIP lub zaznaczono „chcę fakturę") albo `always` (każde opłacone zamówienie, np. dla klienta wymagającego faktur również dla osób fizycznych).
|
|
34
|
+
- `shop_invoices` (nowa tabela) — jedna faktura per zamówienie (`order_id` UNIQUE → idempotencja). Pola `status` (`pending`/`issued`/`sent`/`failed`), `external_id`, `number`, `pdf_url`, `attempts`, `next_retry_at`, `last_error`, `raw`. Kolumny `attempts`/`next_retry_at` zarezerwowane pod przyszły automatyczny retry (obecnie tylko ręczny).
|
|
35
|
+
- Checkout B2B — `shop_orders` dostaje kolumny `customer_nip`, `customer_company_name`, `billing_address` (jsonb), `invoice_requested` (boolean). `createOrderFromCart` / `checkout-handler` przyjmują i zapisują te pola; `CheckoutInput` (`includio-cms/shop/client`) rozszerzony. Storefront form (pola NIP/firma/checkbox/adres) = warstwa konsumenta.
|
|
36
|
+
- `isValidNip(nip)` (`includio-cms/shop`, `@public`) — twarda walidacja polskiego NIP (10 cyfr + suma kontrolna, wagi `[6,5,7,2,3,4,5,6,7]`). Checkout odrzuca nieprawidłowy NIP (400) zanim trafi do bazy — Fakturownia odrzuciłaby błędny NIP, a faktura idzie dalej do KSeF, więc walidujemy u źródła.
|
|
37
|
+
- `getOrderInvoiceAdmin` (query) + `issueInvoiceCmd` (command) — admin remote functions (`includio-cms/admin/remote`). `shop-order-detail-page.svelte` zyskuje sekcję „Faktura": numer, link do PDF, status, przycisk „Wystaw fakturę" / „Wystaw ponownie" (ręczny retry, `force`). Auth-protected (`requireAuth`).
|
|
38
|
+
|
|
39
|
+
### Migration
|
|
40
|
+
|
|
41
|
+
```sql
|
|
42
|
+
CREATE TABLE IF NOT EXISTS shop_invoices (
|
|
43
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
44
|
+
order_id uuid NOT NULL UNIQUE REFERENCES shop_orders(id) ON DELETE CASCADE,
|
|
45
|
+
provider text NOT NULL,
|
|
46
|
+
external_id text,
|
|
47
|
+
number text,
|
|
48
|
+
pdf_url text,
|
|
49
|
+
kind text NOT NULL DEFAULT 'vat',
|
|
50
|
+
status text NOT NULL DEFAULT 'pending',
|
|
51
|
+
attempts integer NOT NULL DEFAULT 0,
|
|
52
|
+
next_retry_at timestamptz,
|
|
53
|
+
last_error text,
|
|
54
|
+
raw jsonb,
|
|
55
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
56
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
ALTER TABLE shop_orders ADD COLUMN IF NOT EXISTS customer_nip text;
|
|
60
|
+
ALTER TABLE shop_orders ADD COLUMN IF NOT EXISTS customer_company_name text;
|
|
61
|
+
ALTER TABLE shop_orders ADD COLUMN IF NOT EXISTS billing_address jsonb;
|
|
62
|
+
ALTER TABLE shop_orders ADD COLUMN IF NOT EXISTS invoice_requested boolean NOT NULL DEFAULT false;
|
|
63
|
+
```
|
|
64
|
+
|
|
6
65
|
## 0.27.0 — 2026-05-29
|
|
7
66
|
|
|
8
67
|
Shop platformowy — Fazy 1 + 2 + 3 + 4 + 5: schema-driven `variantAttributes` (typed `defineShop` schema, Zod walidacja, GIN index helper, ts-gen typed `variant.attributes`) + admin renderer per attribute type + `variantLabel.template` interpolacja z admin auto-prefill nazwy wariantu + `variantExpiry` opt-in (storefront filter, cart/checkout guard, admin "Zakończony" badge) + `paymentPolicy` A-lite (deposit + balance + signed token + refund per kind + admin balance link + per-kind refund dialog). Faza 6 envet pilot (consumer-side end-to-end QA: szkolenia variants + deposit flow + balance link — nie wymaga zmian w core). Post-Faza-5 hot-fixy uwzględnione w tym wydaniu (single release, nie patch): datetime walidator z offsetem, auto-detect deposit kind przy admin manual mark-paid, admin paymentPolicy UI + preserve-on-undefined w upsert. Additive only — żadnych breaking zmian. Plan: `/Users/patryk/.claude/plans/implementacja-ariacms-0-27-partitioned-kahn.md`.
|
package/DOCS.md
CHANGED
package/ROADMAP.md
CHANGED
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
- [x] Faza 6 — envet integration (pilot consumer) — szkolenia variants + deposit flow + balance link end-to-end QA. <!-- files: envet repo src/lib/cms/cms.config.ts, src/routes/api/shop/, src/routes/(website)/{szkolenia,koszyk}/, src/lib/components/{CartIcon,AddToCartModal,CoursePage}.svelte, cli/seed.ts -->
|
|
31
31
|
- [x] Faza 7 — Release artifacts (changelog, ROADMAP cleanup, API.md, build) — 3 post-Faza-5 hotfixy w 0.27.0 (single release). <!-- files: src/lib/updates/0.27.0/index.ts, ROADMAP.md, CHANGELOG.md, API.md, MIGRATION-... -->
|
|
32
32
|
- [x] `[feature]` `[P1]` Slug first-class + SEO v1.0 freeze (0.26.0) — `resolveSeo` public (`includio-cms/core`), `seoFieldDescriptor` SSOT (Zod+TS drift-guard), admin URL un-hardcode (`slugPath.ts` pure resolver, no `seo`-field requirement), `SlugField`/`SeoField`/`SeoFieldData` `@public` v1.0-frozen <!-- files: src/lib/core/fields/slugPath.ts, src/lib/core/fields/seoFieldDescriptor.ts, src/lib/core/fields/resolveSeo.ts, src/lib/types/fields.ts, src/lib/admin/client/collection/collection-entries.svelte, src/lib/updates/0.26.0/ -->
|
|
33
|
+
- [x] `[feature]` `[P2]` Soft-delete zamówień admin-only (0.34.0) — ukrycie zamówienia z listy bez utraty danych (`deleted_at`/`deleted_by`), kosz + przywracanie, guard `decideOrderDeletion` (safe-statuses + faktura), auto-restore przy płatności, zwolnienie rezerwacji stocku, `requireRole('admin')` <!-- files: src/lib/shop/server/orders.ts, src/lib/db-postgres/schema/shop/order.ts, src/lib/admin/remote/shop.remote.ts, src/lib/admin/client/shop/{shop-order-detail-page,shop-orders-list-page,restore-order-cell}.svelte, src/lib/updates/0.34.0/ -->
|
|
33
34
|
|
|
34
35
|
## v1.x — Post-v1.0 deferred
|
|
35
36
|
|
|
@@ -11,6 +11,8 @@ import * as transcodeVideosHandlers from './transcode-videos.js';
|
|
|
11
11
|
import * as uploadLimitHandlers from './upload-limit.js';
|
|
12
12
|
import * as systemInfoHandlers from './system-info.js';
|
|
13
13
|
import * as maintenanceStatusHandlers from './maintenance-status.js';
|
|
14
|
+
import * as integrationsHandlers from './integrations.js';
|
|
15
|
+
import * as testEmailHandlers from './test-email.js';
|
|
14
16
|
import { requireAuth } from '../remote/middleware/auth.js';
|
|
15
17
|
import { getCMS } from '../../core/cms.js';
|
|
16
18
|
import { lookup } from 'mrmime';
|
|
@@ -28,6 +30,8 @@ export function createAdminApiHandler(options) {
|
|
|
28
30
|
'upload-limit': uploadLimitHandlers,
|
|
29
31
|
'system-info': systemInfoHandlers,
|
|
30
32
|
'maintenance-status': maintenanceStatusHandlers,
|
|
33
|
+
integrations: integrationsHandlers,
|
|
34
|
+
'test-email': testEmailHandlers,
|
|
31
35
|
...options?.extraRoutes
|
|
32
36
|
};
|
|
33
37
|
const privateMediaGet = async (event) => {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type RequestHandler } from '@sveltejs/kit';
|
|
2
|
+
/**
|
|
3
|
+
* Integration diagnostics for the admin maintenance panel.
|
|
4
|
+
*
|
|
5
|
+
* `GET` — report which integrations are configured and whether each supports a
|
|
6
|
+
* connectivity ping. Returns NO secrets (only ids, labels, booleans
|
|
7
|
+
* and the admin notification e-mail used as a UI placeholder).
|
|
8
|
+
* `POST` — run a single adapter's non-invasive `healthCheck()` and return the
|
|
9
|
+
* `{ ok, message }` result. Errors are mapped to `message` text — the
|
|
10
|
+
* underlying secret/token is never serialised to the client.
|
|
11
|
+
*/
|
|
12
|
+
export declare const GET: RequestHandler;
|
|
13
|
+
export declare const POST: RequestHandler;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { json } from '@sveltejs/kit';
|
|
2
|
+
import { requireRole } from '../remote/middleware/auth.js';
|
|
3
|
+
import { getCMS } from '../../core/cms.js';
|
|
4
|
+
/**
|
|
5
|
+
* Integration diagnostics for the admin maintenance panel.
|
|
6
|
+
*
|
|
7
|
+
* `GET` — report which integrations are configured and whether each supports a
|
|
8
|
+
* connectivity ping. Returns NO secrets (only ids, labels, booleans
|
|
9
|
+
* and the admin notification e-mail used as a UI placeholder).
|
|
10
|
+
* `POST` — run a single adapter's non-invasive `healthCheck()` and return the
|
|
11
|
+
* `{ ok, message }` result. Errors are mapped to `message` text — the
|
|
12
|
+
* underlying secret/token is never serialised to the client.
|
|
13
|
+
*/
|
|
14
|
+
export const GET = async () => {
|
|
15
|
+
requireRole('admin');
|
|
16
|
+
const cms = getCMS();
|
|
17
|
+
const shop = cms.shopConfig;
|
|
18
|
+
return json({
|
|
19
|
+
email: {
|
|
20
|
+
configured: !!cms.emailAdapter,
|
|
21
|
+
adminEmail: shop?.adminEmail ?? null
|
|
22
|
+
},
|
|
23
|
+
payment: (shop?.payment ?? []).map((p) => ({
|
|
24
|
+
id: p.id,
|
|
25
|
+
label: p.label,
|
|
26
|
+
canPing: typeof p.healthCheck === 'function'
|
|
27
|
+
})),
|
|
28
|
+
invoicing: shop?.invoicing
|
|
29
|
+
? {
|
|
30
|
+
id: shop.invoicing.id,
|
|
31
|
+
canPing: typeof shop.invoicing.healthCheck === 'function'
|
|
32
|
+
}
|
|
33
|
+
: null
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
export const POST = async ({ request }) => {
|
|
37
|
+
requireRole('admin');
|
|
38
|
+
const body = (await request.json().catch(() => null));
|
|
39
|
+
if (!body || (body.kind !== 'payment' && body.kind !== 'invoicing')) {
|
|
40
|
+
return json({ error: 'Invalid body: expected { kind: "payment" | "invoicing", id? }' }, {
|
|
41
|
+
status: 400
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const shop = getCMS().shopConfig;
|
|
45
|
+
const adapter = body.kind === 'payment'
|
|
46
|
+
? (shop?.payment ?? []).find((p) => p.id === body.id) ?? shop?.payment?.[0]
|
|
47
|
+
: shop?.invoicing ?? null;
|
|
48
|
+
if (!adapter) {
|
|
49
|
+
return json({ error: 'Integration not configured' }, { status: 404 });
|
|
50
|
+
}
|
|
51
|
+
if (typeof adapter.healthCheck !== 'function') {
|
|
52
|
+
return json({ ok: false, message: 'Health-check not supported by this adapter' });
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const result = await adapter.healthCheck();
|
|
56
|
+
return json(result);
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
return json({ ok: false, message: e instanceof Error ? e.message : String(e) });
|
|
60
|
+
}
|
|
61
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type RequestHandler } from '@sveltejs/kit';
|
|
2
|
+
/**
|
|
3
|
+
* Send a real test e-mail through the configured email adapter, to verify
|
|
4
|
+
* end-to-end SMTP delivery from the admin maintenance panel.
|
|
5
|
+
*
|
|
6
|
+
* Body: `{ to?: string }`. When `to` is empty, falls back to the shop's
|
|
7
|
+
* `adminEmail`. Returns `{ ok }` or `{ ok: false, message }`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const POST: RequestHandler;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { json } from '@sveltejs/kit';
|
|
2
|
+
import { requireRole } from '../remote/middleware/auth.js';
|
|
3
|
+
import { getCMS } from '../../core/cms.js';
|
|
4
|
+
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
5
|
+
/**
|
|
6
|
+
* Send a real test e-mail through the configured email adapter, to verify
|
|
7
|
+
* end-to-end SMTP delivery from the admin maintenance panel.
|
|
8
|
+
*
|
|
9
|
+
* Body: `{ to?: string }`. When `to` is empty, falls back to the shop's
|
|
10
|
+
* `adminEmail`. Returns `{ ok }` or `{ ok: false, message }`.
|
|
11
|
+
*/
|
|
12
|
+
export const POST = async ({ request }) => {
|
|
13
|
+
requireRole('admin');
|
|
14
|
+
const cms = getCMS();
|
|
15
|
+
const mailer = cms.emailAdapter;
|
|
16
|
+
if (!mailer) {
|
|
17
|
+
return json({ ok: false, message: 'Brak skonfigurowanego adaptera e-mail' }, { status: 400 });
|
|
18
|
+
}
|
|
19
|
+
const body = (await request.json().catch(() => null));
|
|
20
|
+
const to = (body?.to?.trim() || cms.shopConfig?.adminEmail || '').trim();
|
|
21
|
+
if (!to) {
|
|
22
|
+
return json({ ok: false, message: 'Brak odbiorcy — podaj adres lub ustaw ADMIN_EMAIL' }, { status: 400 });
|
|
23
|
+
}
|
|
24
|
+
if (!EMAIL_RE.test(to)) {
|
|
25
|
+
return json({ ok: false, message: `Nieprawidłowy adres e-mail: ${to}` }, { status: 400 });
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
await mailer.sendMail({
|
|
29
|
+
to,
|
|
30
|
+
subject: 'AriaCMS — test połączenia e-mail',
|
|
31
|
+
html: '<p>To jest testowa wiadomość wysłana z panelu Konserwacja. Jeśli ją widzisz, integracja e-mail działa poprawnie.</p>',
|
|
32
|
+
text: 'To jest testowa wiadomość wysłana z panelu Konserwacja. Jeśli ją widzisz, integracja e-mail działa poprawnie.'
|
|
33
|
+
});
|
|
34
|
+
return json({ ok: true });
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
return json({ ok: false, message: e instanceof Error ? e.message : String(e) });
|
|
38
|
+
}
|
|
39
|
+
};
|