create-nextblock 0.16.1 → 0.16.2
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/docker-template/.dockerignore +1 -1
- package/libs/db/tsconfig.lib.json +3 -3
- package/libs/editor/tsconfig.lib.json +3 -3
- package/libs/ui/tsconfig.lib.json +3 -3
- package/libs/utils/tsconfig.json +3 -3
- package/package.json +50 -50
- package/project.json +19 -19
- package/templates/nextblock-template/.dockerignore +1 -1
- package/templates/nextblock-template/.swcrc +30 -30
- package/templates/nextblock-template/AGENTS.md +9 -9
- package/templates/nextblock-template/CLAUDE.md +1 -1
- package/templates/nextblock-template/app/(auth-pages)/layout.tsx +9 -9
- package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +27 -27
- package/templates/nextblock-template/app/.well-known/ucp/route.ts +16 -16
- package/templates/nextblock-template/app/ToasterProvider.tsx +26 -26
- package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -7
- package/templates/nextblock-template/app/actions/contactSellerActions.test.ts +280 -280
- package/templates/nextblock-template/app/actions/contactSellerActions.ts +222 -222
- package/templates/nextblock-template/app/actions/email-retry.test.ts +62 -62
- package/templates/nextblock-template/app/actions/email.ts +241 -241
- package/templates/nextblock-template/app/actions/formActions.ts +245 -245
- package/templates/nextblock-template/app/actions/interactions.ts +489 -489
- package/templates/nextblock-template/app/actions/threadActions.ts +166 -166
- package/templates/nextblock-template/app/actions/visibilityActions.ts +210 -210
- package/templates/nextblock-template/app/api/ai/seo/alt-text/route.ts +221 -221
- package/templates/nextblock-template/app/api/ai/seo/metadata/route.ts +186 -186
- package/templates/nextblock-template/app/api/checkout/freemius/sync/route.ts +29 -29
- package/templates/nextblock-template/app/api/checkout/route.ts +162 -162
- package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +3092 -3092
- package/templates/nextblock-template/app/api/mcp/route.ts +415 -415
- package/templates/nextblock-template/app/api/media/record/route.ts +160 -160
- package/templates/nextblock-template/app/api/search/route.ts +43 -43
- package/templates/nextblock-template/app/api/view/route.ts +114 -114
- package/templates/nextblock-template/app/api/visual-editing/block-draft/route.ts +47 -47
- package/templates/nextblock-template/app/api/visual-editing/product-draft/route.ts +47 -47
- package/templates/nextblock-template/app/auth/callback/route.ts +31 -31
- package/templates/nextblock-template/app/cart/page.tsx +7 -7
- package/templates/nextblock-template/app/checkout/UcpCartHydrator.tsx +20 -20
- package/templates/nextblock-template/app/checkout/page.tsx +57 -57
- package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +558 -558
- package/templates/nextblock-template/app/cms/blocks/components/BlockEditorModal.tsx +241 -241
- package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -149
- package/templates/nextblock-template/app/cms/blocks/editors/FormBlockEditor.tsx +304 -304
- package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +406 -406
- package/templates/nextblock-template/app/cms/components/ContactReminderBanner.tsx +75 -75
- package/templates/nextblock-template/app/cms/components/CortexAiActiveContext.tsx +23 -23
- package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -58
- package/templates/nextblock-template/app/cms/components/FeatureImageField.tsx +254 -254
- package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +36 -36
- package/templates/nextblock-template/app/cms/components/PaymentsReminderBanner.tsx +58 -58
- package/templates/nextblock-template/app/cms/components/VisibilityBadge.tsx +62 -62
- package/templates/nextblock-template/app/cms/components/VisibilityControl.tsx +542 -542
- package/templates/nextblock-template/app/cms/coupons/[id]/edit/page.tsx +16 -16
- package/templates/nextblock-template/app/cms/coupons/page.tsx +16 -16
- package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -228
- package/templates/nextblock-template/app/cms/dashboard/components/DashboardComponents.tsx +200 -200
- package/templates/nextblock-template/app/cms/inquiries/actions.ts +66 -66
- package/templates/nextblock-template/app/cms/inquiries/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/interactions/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/layout.tsx +101 -101
- package/templates/nextblock-template/app/cms/media/components/FolderNavigator.tsx +273 -273
- package/templates/nextblock-template/app/cms/media/components/FolderTree.tsx +122 -122
- package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -69
- package/templates/nextblock-template/app/cms/messages/MessagesClient.tsx +661 -661
- package/templates/nextblock-template/app/cms/messages/actions.ts +404 -404
- package/templates/nextblock-template/app/cms/messages/loadInbox.ts +333 -333
- package/templates/nextblock-template/app/cms/messages/page.tsx +87 -87
- package/templates/nextblock-template/app/cms/messages/require-admin.ts +37 -37
- package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +3 -3
- package/templates/nextblock-template/app/cms/pages/components/PageForm.tsx +649 -649
- package/templates/nextblock-template/app/cms/posts/components/PostForm.tsx +618 -618
- package/templates/nextblock-template/app/cms/products/[id]/edit/page.tsx +370 -370
- package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -13
- package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -133
- package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -5
- package/templates/nextblock-template/app/cms/revisions/actions.ts +332 -332
- package/templates/nextblock-template/app/cms/revisions/service.test.ts +498 -498
- package/templates/nextblock-template/app/cms/revisions/service.ts +569 -569
- package/templates/nextblock-template/app/cms/revisions/utils.ts +304 -304
- package/templates/nextblock-template/app/cms/settings/cortex-ai/CortexAiSettingsClient.tsx +948 -948
- package/templates/nextblock-template/app/cms/settings/cortex-ai/McpServerSettingsCard.tsx +628 -628
- package/templates/nextblock-template/app/cms/settings/cortex-ai/mcp-actions.ts +230 -230
- package/templates/nextblock-template/app/cms/settings/cortex-ai/require-admin.ts +34 -34
- package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -331
- package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -494
- package/templates/nextblock-template/app/cms/settings/email/components/EmailForm.tsx +227 -227
- package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -767
- package/templates/nextblock-template/app/cms/settings/extra-translations/actions.ts +276 -276
- package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -93
- package/templates/nextblock-template/app/cms/settings/global-css/components/ThemeEditor.tsx +382 -382
- package/templates/nextblock-template/app/cms/settings/global-css/components/ThemeManager.tsx +267 -267
- package/templates/nextblock-template/app/cms/settings/global-css/page.tsx +40 -40
- package/templates/nextblock-template/app/cms/settings/global-css/theme-actions.ts +259 -259
- package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
- package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -339
- package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
- package/templates/nextblock-template/app/cms/settings/seo/RedirectsCard.tsx +514 -514
- package/templates/nextblock-template/app/cms/settings/seo/RobotsCard.tsx +529 -529
- package/templates/nextblock-template/app/cms/settings/seo/SeoSettingsClient.tsx +57 -57
- package/templates/nextblock-template/app/cms/settings/seo/actions.ts +448 -448
- package/templates/nextblock-template/app/cms/settings/seo/mappers.ts +93 -93
- package/templates/nextblock-template/app/cms/settings/seo/page.tsx +46 -46
- package/templates/nextblock-template/app/cms/settings/seo/require-admin.ts +47 -47
- package/templates/nextblock-template/app/cms/settings/site-scripts/page.tsx +51 -51
- package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -21
- package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -20
- package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +12 -12
- package/templates/nextblock-template/app/layout.tsx +671 -671
- package/templates/nextblock-template/app/lib/seo.ts +319 -319
- package/templates/nextblock-template/app/lib/ucp/protocol.ts +190 -190
- package/templates/nextblock-template/app/lib/ucp/server.test.ts +56 -56
- package/templates/nextblock-template/app/product/[slug]/page.tsx +502 -502
- package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -16
- package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -9
- package/templates/nextblock-template/app/profile/account-links.ts +22 -22
- package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -124
- package/templates/nextblock-template/app/profile/orders/page.tsx +19 -19
- package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -128
- package/templates/nextblock-template/app/profile/password/actions.ts +59 -59
- package/templates/nextblock-template/app/profile/password/page.tsx +27 -27
- package/templates/nextblock-template/app/providers.tsx +96 -96
- package/templates/nextblock-template/app/robots.ts +123 -123
- package/templates/nextblock-template/app/thread/ThreadView.tsx +164 -164
- package/templates/nextblock-template/app/thread/[token]/route.ts +57 -57
- package/templates/nextblock-template/app/thread/layout.tsx +15 -15
- package/templates/nextblock-template/app/thread/page.tsx +98 -98
- package/templates/nextblock-template/app/ucp/v1/carts/[id]/cancel/route.ts +38 -38
- package/templates/nextblock-template/app/ucp/v1/carts/[id]/route.ts +68 -68
- package/templates/nextblock-template/app/ucp/v1/carts/route.ts +35 -35
- package/templates/nextblock-template/app/ucp/v1/catalog/lookup/route.ts +35 -35
- package/templates/nextblock-template/app/ucp/v1/catalog/product/route.ts +35 -35
- package/templates/nextblock-template/app/ucp/v1/catalog/search/route.ts +34 -34
- package/templates/nextblock-template/components/BlockRenderer.tsx +312 -312
- package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -7
- package/templates/nextblock-template/components/CartTranslator.tsx +210 -210
- package/templates/nextblock-template/components/ContactSellerSection.tsx +188 -188
- package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -23
- package/templates/nextblock-template/components/DeferredCartTranslator.tsx +51 -51
- package/templates/nextblock-template/components/DeferredGlobalSearch.tsx +68 -68
- package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -70
- package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -69
- package/templates/nextblock-template/components/FooterNavigation.tsx +32 -32
- package/templates/nextblock-template/components/GlobalSearch.tsx +557 -557
- package/templates/nextblock-template/components/Header.tsx +38 -38
- package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -47
- package/templates/nextblock-template/components/LanguageSwitcher.tsx +2 -2
- package/templates/nextblock-template/components/PostCommentsSection.tsx +378 -378
- package/templates/nextblock-template/components/ProductReviewsSection.tsx +426 -426
- package/templates/nextblock-template/components/SiteScripts.tsx +56 -56
- package/templates/nextblock-template/components/StaffReplies.tsx +102 -102
- package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -12
- package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -12
- package/templates/nextblock-template/components/blocks/PostsGridClient.tsx +48 -48
- package/templates/nextblock-template/components/blocks/TestimonialBlock.tsx +9 -9
- package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -23
- package/templates/nextblock-template/components/blocks/publicRendererLoaders.ts +25 -25
- package/templates/nextblock-template/components/blocks/renderers/ButtonBlockRenderer.tsx +92 -92
- package/templates/nextblock-template/components/blocks/renderers/CartBlockRenderer.tsx +18 -18
- package/templates/nextblock-template/components/blocks/renderers/CheckoutBlockRenderer.tsx +20 -20
- package/templates/nextblock-template/components/blocks/renderers/FeaturedProductBlockRenderer.tsx +25 -25
- package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +385 -385
- package/templates/nextblock-template/components/blocks/renderers/PostsGridBlockRenderer.tsx +24 -24
- package/templates/nextblock-template/components/blocks/renderers/ProductDetailsBlockRenderer.tsx +157 -157
- package/templates/nextblock-template/components/blocks/renderers/ProductGridBlockRenderer.tsx +34 -34
- package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +612 -612
- package/templates/nextblock-template/components/blocks/renderers/TestimonialBlockRenderer.tsx +57 -57
- package/templates/nextblock-template/components/blocks/renderers/inline/AlertWidgetRenderer.tsx +2 -2
- package/templates/nextblock-template/components/blocks/renderers/inline/CtaWidgetRenderer.tsx +2 -2
- package/templates/nextblock-template/components/blocks/types.ts +7 -7
- package/templates/nextblock-template/components/commerce/PaymentReadinessBoundary.tsx +32 -32
- package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
- package/templates/nextblock-template/components/form-message.tsx +32 -32
- package/templates/nextblock-template/components/seo/GenerateMetaButton.tsx +137 -137
- package/templates/nextblock-template/components/seo/PageSeoAuditSection.tsx +244 -244
- package/templates/nextblock-template/components/seo/SeoAuditPanel.tsx +749 -749
- package/templates/nextblock-template/components/seo/SeoIssueList.tsx +195 -195
- package/templates/nextblock-template/components/seo/SeoScoreDial.tsx +144 -144
- package/templates/nextblock-template/components/seo/SocialPreview.tsx +243 -243
- package/templates/nextblock-template/components/seo/SocialPreviewDialog.tsx +110 -110
- package/templates/nextblock-template/components/submit-button.tsx +23 -23
- package/templates/nextblock-template/components/theme-icon.tsx +78 -78
- package/templates/nextblock-template/components/theme-switcher.tsx +85 -85
- package/templates/nextblock-template/context/AuthContext.tsx +23 -23
- package/templates/nextblock-template/context/ThemeCatalogContext.tsx +44 -44
- package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -94
- package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +77 -77
- package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +372 -372
- package/templates/nextblock-template/docs/14-MESSAGES-INBOX.md +309 -309
- package/templates/nextblock-template/docs/README.md +42 -42
- package/templates/nextblock-template/docs/TECHNICAL_SPECIFICATION.md +12506 -12506
- package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -21
- package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -101
- package/templates/nextblock-template/index.d.ts +7 -7
- package/templates/nextblock-template/lib/auth-redirects.ts +46 -46
- package/templates/nextblock-template/lib/blocks/blockColors.test.ts +134 -134
- package/templates/nextblock-template/lib/blocks/blockColors.ts +175 -175
- package/templates/nextblock-template/lib/blocks/blockRegistry.ts +761 -761
- package/templates/nextblock-template/lib/blocks/inlineScriptNonce.ts +20 -20
- package/templates/nextblock-template/lib/cms/contact-reminder.ts +64 -64
- package/templates/nextblock-template/lib/cms/payments-reminder.test.ts +135 -0
- package/templates/nextblock-template/lib/cms/payments-reminder.ts +30 -23
- package/templates/nextblock-template/lib/cms/unread-messages.ts +42 -42
- package/templates/nextblock-template/lib/commerce/seller-contact.ts +162 -162
- package/templates/nextblock-template/lib/config/email-settings.ts +323 -323
- package/templates/nextblock-template/lib/config/email-tls.test.ts +57 -57
- package/templates/nextblock-template/lib/cortex-ai/alt-text-request.ts +86 -86
- package/templates/nextblock-template/lib/cortex-ai/sandbox-headers.ts +60 -60
- package/templates/nextblock-template/lib/email/placeholder-address.test.ts +59 -59
- package/templates/nextblock-template/lib/email/placeholder-address.ts +39 -39
- package/templates/nextblock-template/lib/messages/thread-reference.test.ts +70 -70
- package/templates/nextblock-template/lib/messages/thread-token.test.ts +93 -93
- package/templates/nextblock-template/lib/messages/thread-token.ts +157 -157
- package/templates/nextblock-template/lib/messages/threads.ts +579 -579
- package/templates/nextblock-template/lib/posts/readTime.ts +60 -60
- package/templates/nextblock-template/lib/publishing/viewUrl.ts +26 -26
- package/templates/nextblock-template/lib/search/types.ts +27 -27
- package/templates/nextblock-template/lib/seo/alt-text-write-back.test.ts +154 -154
- package/templates/nextblock-template/lib/seo/alt-text-write-back.ts +109 -109
- package/templates/nextblock-template/lib/seo/block-content.ts +123 -123
- package/templates/nextblock-template/lib/seo/fix-prompts.test.ts +242 -242
- package/templates/nextblock-template/lib/seo/fix-prompts.ts +204 -204
- package/templates/nextblock-template/lib/seo/page-audit-context.tsx +140 -140
- package/templates/nextblock-template/lib/seo/page-document.test.ts +350 -350
- package/templates/nextblock-template/lib/seo/page-document.ts +412 -412
- package/templates/nextblock-template/lib/seo/redirect-store.test.ts +479 -479
- package/templates/nextblock-template/lib/seo/redirect-store.ts +466 -466
- package/templates/nextblock-template/lib/seo/robots-settings-signature.test.ts +102 -102
- package/templates/nextblock-template/lib/seo/robots-settings-signature.ts +41 -41
- package/templates/nextblock-template/lib/seo/robots-txt.test.ts +370 -370
- package/templates/nextblock-template/lib/seo/robots-txt.ts +510 -510
- package/templates/nextblock-template/lib/setup/migrations-bundle.ts +177 -177
- package/templates/nextblock-template/lib/site-scripts/revisions.ts +71 -71
- package/templates/nextblock-template/lib/site-scripts/types.ts +46 -46
- package/templates/nextblock-template/lib/site-url.test.ts +89 -89
- package/templates/nextblock-template/lib/site-url.ts +102 -102
- package/templates/nextblock-template/lib/themes/buildThemeCss.ts +124 -124
- package/templates/nextblock-template/lib/themes/tokenColor.ts +31 -31
- package/templates/nextblock-template/lib/themes/tokens.ts +143 -143
- package/templates/nextblock-template/lib/visual-editing/draft-content.test.ts +105 -105
- package/templates/nextblock-template/lib/visual-editing/draft-route.test.ts +42 -42
- package/templates/nextblock-template/lib/visual-editing/edit-info.test.ts +143 -143
- package/templates/nextblock-template/lib/visual-editing/edit-info.ts +94 -94
- package/templates/nextblock-template/lib/visual-editing/product-drafts.test.ts +81 -81
- package/templates/nextblock-template/lib/zod-config.ts +5 -5
- package/templates/nextblock-template/next-env.d.ts +1 -2
- package/templates/nextblock-template/package.json +1 -1
- package/templates/nextblock-template/postcss.config.js +6 -6
- package/templates/nextblock-template/scripts/backup.js +115 -115
- package/templates/nextblock-template/scripts/restore.js +385 -385
- package/templates/nextblock-template/scripts/validate-editor-block-schema.ts +112 -112
- package/templates/nextblock-template/tailwind.config.js +25 -25
- package/templates/nextblock-template/tools/build-migrate.mjs +102 -102
- package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -282
- package/templates/nextblock-template/tools/deploy-supabase.js +159 -159
- package/templates/nextblock-template/tools/lib/migrate-core.mjs +569 -569
- package/templates/nextblock-template/tools/update.mjs +1303 -1303
- package/tsconfig.base.json +3 -3
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
import { headers } from 'next/headers';
|
|
2
|
-
import { getServiceRoleSupabaseClient } from '@nextblock-cms/db/server';
|
|
3
|
-
|
|
4
|
-
import { getCookieValue } from '../../lib/auth/cookies';
|
|
5
|
-
import {
|
|
6
|
-
THREAD_COOKIE,
|
|
7
|
-
touchThreadToken,
|
|
8
|
-
verifyThreadToken,
|
|
9
|
-
} from '../../lib/messages/thread-token';
|
|
10
|
-
import ThreadView, { type ThreadMessage } from './ThreadView';
|
|
11
|
-
|
|
12
|
-
// A conversation changes whenever either side writes; a cached copy would show the
|
|
13
|
-
// visitor a reply that is no longer the latest, or hide one that just arrived.
|
|
14
|
-
export const dynamic = 'force-dynamic';
|
|
15
|
-
export const fetchCache = 'force-no-store';
|
|
16
|
-
|
|
17
|
-
const MAX_MESSAGES = 200;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* The visitor's half of a private conversation.
|
|
21
|
-
*
|
|
22
|
-
* Authentication is the HttpOnly cookie set by `/thread/[token]`, verified here in
|
|
23
|
-
* application code and then read with the service role — no RLS policy in this schema
|
|
24
|
-
* authenticates an anonymous caller by token, and this page does not invent one.
|
|
25
|
-
*
|
|
26
|
-
* There is exactly one failure page. A visitor whose link expired, was revoked, or never
|
|
27
|
-
* existed sees the same words, because anything else would let someone probe for valid
|
|
28
|
-
* threads.
|
|
29
|
-
*/
|
|
30
|
-
export default async function ThreadPage() {
|
|
31
|
-
const supabase = getServiceRoleSupabaseClient();
|
|
32
|
-
const token = await getCookieValue(THREAD_COOKIE);
|
|
33
|
-
const check = await verifyThreadToken(supabase, token);
|
|
34
|
-
|
|
35
|
-
if (!check.valid) {
|
|
36
|
-
return <ThreadView invalid />;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const thread = check.thread;
|
|
40
|
-
|
|
41
|
-
const [{ data: rows }, botProtection, scriptNonce] = await Promise.all([
|
|
42
|
-
supabase
|
|
43
|
-
.from('thread_messages')
|
|
44
|
-
.select('id, direction, body, author_name, created_at')
|
|
45
|
-
.eq('thread_id', thread.id)
|
|
46
|
-
.order('created_at', { ascending: true })
|
|
47
|
-
.limit(MAX_MESSAGES),
|
|
48
|
-
loadBotProtection(supabase),
|
|
49
|
-
loadNonce(),
|
|
50
|
-
]);
|
|
51
|
-
|
|
52
|
-
// The visitor is looking at it, so it is no longer unread for them.
|
|
53
|
-
if (thread.unread_for_visitor) {
|
|
54
|
-
await supabase
|
|
55
|
-
.from('message_threads')
|
|
56
|
-
.update({ unread_for_visitor: false })
|
|
57
|
-
.eq('id', thread.id);
|
|
58
|
-
}
|
|
59
|
-
await touchThreadToken(supabase, thread.id);
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<ThreadView
|
|
63
|
-
subjectLabel={thread.subject_label}
|
|
64
|
-
closed={thread.status === 'closed'}
|
|
65
|
-
messages={(rows ?? []) as ThreadMessage[]}
|
|
66
|
-
botProtectionProvider={botProtection.provider}
|
|
67
|
-
botProtectionSiteKey={botProtection.siteKey}
|
|
68
|
-
scriptNonce={scriptNonce}
|
|
69
|
-
/>
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async function loadBotProtection(supabase: {
|
|
74
|
-
from: (table: string) => any;
|
|
75
|
-
}): Promise<{ provider: 'none' | 'turnstile' | 'recaptcha'; siteKey: string }> {
|
|
76
|
-
try {
|
|
77
|
-
const { data } = await supabase
|
|
78
|
-
.from('site_settings')
|
|
79
|
-
.select('value')
|
|
80
|
-
.eq('key', 'bot_protection_public')
|
|
81
|
-
.maybeSingle();
|
|
82
|
-
if (data?.value) {
|
|
83
|
-
const value = data.value as Record<string, any>;
|
|
84
|
-
return { provider: value.provider || 'none', siteKey: value.siteKey || '' };
|
|
85
|
-
}
|
|
86
|
-
} catch (error) {
|
|
87
|
-
console.error('[thread] Could not load bot protection settings:', error);
|
|
88
|
-
}
|
|
89
|
-
return { provider: 'none', siteKey: '' };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async function loadNonce(): Promise<string> {
|
|
93
|
-
try {
|
|
94
|
-
return (await headers()).get('x-nonce') || '';
|
|
95
|
-
} catch {
|
|
96
|
-
return '';
|
|
97
|
-
}
|
|
98
|
-
}
|
|
1
|
+
import { headers } from 'next/headers';
|
|
2
|
+
import { getServiceRoleSupabaseClient } from '@nextblock-cms/db/server';
|
|
3
|
+
|
|
4
|
+
import { getCookieValue } from '../../lib/auth/cookies';
|
|
5
|
+
import {
|
|
6
|
+
THREAD_COOKIE,
|
|
7
|
+
touchThreadToken,
|
|
8
|
+
verifyThreadToken,
|
|
9
|
+
} from '../../lib/messages/thread-token';
|
|
10
|
+
import ThreadView, { type ThreadMessage } from './ThreadView';
|
|
11
|
+
|
|
12
|
+
// A conversation changes whenever either side writes; a cached copy would show the
|
|
13
|
+
// visitor a reply that is no longer the latest, or hide one that just arrived.
|
|
14
|
+
export const dynamic = 'force-dynamic';
|
|
15
|
+
export const fetchCache = 'force-no-store';
|
|
16
|
+
|
|
17
|
+
const MAX_MESSAGES = 200;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The visitor's half of a private conversation.
|
|
21
|
+
*
|
|
22
|
+
* Authentication is the HttpOnly cookie set by `/thread/[token]`, verified here in
|
|
23
|
+
* application code and then read with the service role — no RLS policy in this schema
|
|
24
|
+
* authenticates an anonymous caller by token, and this page does not invent one.
|
|
25
|
+
*
|
|
26
|
+
* There is exactly one failure page. A visitor whose link expired, was revoked, or never
|
|
27
|
+
* existed sees the same words, because anything else would let someone probe for valid
|
|
28
|
+
* threads.
|
|
29
|
+
*/
|
|
30
|
+
export default async function ThreadPage() {
|
|
31
|
+
const supabase = getServiceRoleSupabaseClient();
|
|
32
|
+
const token = await getCookieValue(THREAD_COOKIE);
|
|
33
|
+
const check = await verifyThreadToken(supabase, token);
|
|
34
|
+
|
|
35
|
+
if (!check.valid) {
|
|
36
|
+
return <ThreadView invalid />;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const thread = check.thread;
|
|
40
|
+
|
|
41
|
+
const [{ data: rows }, botProtection, scriptNonce] = await Promise.all([
|
|
42
|
+
supabase
|
|
43
|
+
.from('thread_messages')
|
|
44
|
+
.select('id, direction, body, author_name, created_at')
|
|
45
|
+
.eq('thread_id', thread.id)
|
|
46
|
+
.order('created_at', { ascending: true })
|
|
47
|
+
.limit(MAX_MESSAGES),
|
|
48
|
+
loadBotProtection(supabase),
|
|
49
|
+
loadNonce(),
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
// The visitor is looking at it, so it is no longer unread for them.
|
|
53
|
+
if (thread.unread_for_visitor) {
|
|
54
|
+
await supabase
|
|
55
|
+
.from('message_threads')
|
|
56
|
+
.update({ unread_for_visitor: false })
|
|
57
|
+
.eq('id', thread.id);
|
|
58
|
+
}
|
|
59
|
+
await touchThreadToken(supabase, thread.id);
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<ThreadView
|
|
63
|
+
subjectLabel={thread.subject_label}
|
|
64
|
+
closed={thread.status === 'closed'}
|
|
65
|
+
messages={(rows ?? []) as ThreadMessage[]}
|
|
66
|
+
botProtectionProvider={botProtection.provider}
|
|
67
|
+
botProtectionSiteKey={botProtection.siteKey}
|
|
68
|
+
scriptNonce={scriptNonce}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function loadBotProtection(supabase: {
|
|
74
|
+
from: (table: string) => any;
|
|
75
|
+
}): Promise<{ provider: 'none' | 'turnstile' | 'recaptcha'; siteKey: string }> {
|
|
76
|
+
try {
|
|
77
|
+
const { data } = await supabase
|
|
78
|
+
.from('site_settings')
|
|
79
|
+
.select('value')
|
|
80
|
+
.eq('key', 'bot_protection_public')
|
|
81
|
+
.maybeSingle();
|
|
82
|
+
if (data?.value) {
|
|
83
|
+
const value = data.value as Record<string, any>;
|
|
84
|
+
return { provider: value.provider || 'none', siteKey: value.siteKey || '' };
|
|
85
|
+
}
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error('[thread] Could not load bot protection settings:', error);
|
|
88
|
+
}
|
|
89
|
+
return { provider: 'none', siteKey: '' };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function loadNonce(): Promise<string> {
|
|
93
|
+
try {
|
|
94
|
+
return (await headers()).get('x-nonce') || '';
|
|
95
|
+
} catch {
|
|
96
|
+
return '';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import { NextResponse } from 'next/server';
|
|
2
|
-
import {
|
|
3
|
-
buildProtocolError,
|
|
4
|
-
cancelUcpCart,
|
|
5
|
-
ensureEcommerceOnline,
|
|
6
|
-
} from '../../../../../lib/ucp/server';
|
|
7
|
-
|
|
8
|
-
export const dynamic = 'force-dynamic';
|
|
9
|
-
export const runtime = 'nodejs';
|
|
10
|
-
|
|
11
|
-
interface CartCancelRouteContext {
|
|
12
|
-
params: Promise<{ id: string }>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function POST(request: Request, context: CartCancelRouteContext) {
|
|
16
|
-
try {
|
|
17
|
-
const isOnline = await ensureEcommerceOnline();
|
|
18
|
-
if (!isOnline) {
|
|
19
|
-
return NextResponse.json(
|
|
20
|
-
buildProtocolError(
|
|
21
|
-
'ecommerce_unavailable',
|
|
22
|
-
'Ecommerce module license is inactive.'
|
|
23
|
-
),
|
|
24
|
-
{ status: 403 }
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const { id } = await context.params;
|
|
29
|
-
const result = await cancelUcpCart(id, request);
|
|
30
|
-
return NextResponse.json(result.body, { status: result.status });
|
|
31
|
-
} catch (error) {
|
|
32
|
-
console.error('UCP cancel cart error:', error);
|
|
33
|
-
return NextResponse.json(
|
|
34
|
-
buildProtocolError('internal_server_error', 'Cart cancellation failed.'),
|
|
35
|
-
{ status: 500 }
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import {
|
|
3
|
+
buildProtocolError,
|
|
4
|
+
cancelUcpCart,
|
|
5
|
+
ensureEcommerceOnline,
|
|
6
|
+
} from '../../../../../lib/ucp/server';
|
|
7
|
+
|
|
8
|
+
export const dynamic = 'force-dynamic';
|
|
9
|
+
export const runtime = 'nodejs';
|
|
10
|
+
|
|
11
|
+
interface CartCancelRouteContext {
|
|
12
|
+
params: Promise<{ id: string }>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function POST(request: Request, context: CartCancelRouteContext) {
|
|
16
|
+
try {
|
|
17
|
+
const isOnline = await ensureEcommerceOnline();
|
|
18
|
+
if (!isOnline) {
|
|
19
|
+
return NextResponse.json(
|
|
20
|
+
buildProtocolError(
|
|
21
|
+
'ecommerce_unavailable',
|
|
22
|
+
'Ecommerce module license is inactive.'
|
|
23
|
+
),
|
|
24
|
+
{ status: 403 }
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { id } = await context.params;
|
|
29
|
+
const result = await cancelUcpCart(id, request);
|
|
30
|
+
return NextResponse.json(result.body, { status: result.status });
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error('UCP cancel cart error:', error);
|
|
33
|
+
return NextResponse.json(
|
|
34
|
+
buildProtocolError('internal_server_error', 'Cart cancellation failed.'),
|
|
35
|
+
{ status: 500 }
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
import { NextResponse } from 'next/server';
|
|
2
|
-
import {
|
|
3
|
-
buildProtocolError,
|
|
4
|
-
ensureEcommerceOnline,
|
|
5
|
-
getUcpCart,
|
|
6
|
-
parseJsonBody,
|
|
7
|
-
updateUcpCart,
|
|
8
|
-
} from '../../../../lib/ucp/server';
|
|
9
|
-
|
|
10
|
-
export const dynamic = 'force-dynamic';
|
|
11
|
-
export const runtime = 'nodejs';
|
|
12
|
-
|
|
13
|
-
interface CartRouteContext {
|
|
14
|
-
params: Promise<{ id: string }>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export async function GET(request: Request, context: CartRouteContext) {
|
|
18
|
-
try {
|
|
19
|
-
const isOnline = await ensureEcommerceOnline();
|
|
20
|
-
if (!isOnline) {
|
|
21
|
-
return NextResponse.json(
|
|
22
|
-
buildProtocolError(
|
|
23
|
-
'ecommerce_unavailable',
|
|
24
|
-
'Ecommerce module license is inactive.'
|
|
25
|
-
),
|
|
26
|
-
{ status: 403 }
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const { id } = await context.params;
|
|
31
|
-
const result = await getUcpCart(id, request);
|
|
32
|
-
return NextResponse.json(result.body, { status: result.status });
|
|
33
|
-
} catch (error) {
|
|
34
|
-
console.error('UCP get cart error:', error);
|
|
35
|
-
return NextResponse.json(
|
|
36
|
-
buildProtocolError('internal_server_error', 'Cart retrieval failed.'),
|
|
37
|
-
{ status: 500 }
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export async function PUT(request: Request, context: CartRouteContext) {
|
|
43
|
-
try {
|
|
44
|
-
const isOnline = await ensureEcommerceOnline();
|
|
45
|
-
if (!isOnline) {
|
|
46
|
-
return NextResponse.json(
|
|
47
|
-
buildProtocolError(
|
|
48
|
-
'ecommerce_unavailable',
|
|
49
|
-
'Ecommerce module license is inactive.'
|
|
50
|
-
),
|
|
51
|
-
{ status: 403 }
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const { id } = await context.params;
|
|
56
|
-
const body = await parseJsonBody(request);
|
|
57
|
-
const result = await updateUcpCart(id, body, request);
|
|
58
|
-
return NextResponse.json(result.body, { status: result.status });
|
|
59
|
-
} catch (error) {
|
|
60
|
-
console.error('UCP update cart error:', error);
|
|
61
|
-
return NextResponse.json(
|
|
62
|
-
buildProtocolError('internal_server_error', 'Cart update failed.'),
|
|
63
|
-
{ status: 500 }
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export const PATCH = PUT;
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import {
|
|
3
|
+
buildProtocolError,
|
|
4
|
+
ensureEcommerceOnline,
|
|
5
|
+
getUcpCart,
|
|
6
|
+
parseJsonBody,
|
|
7
|
+
updateUcpCart,
|
|
8
|
+
} from '../../../../lib/ucp/server';
|
|
9
|
+
|
|
10
|
+
export const dynamic = 'force-dynamic';
|
|
11
|
+
export const runtime = 'nodejs';
|
|
12
|
+
|
|
13
|
+
interface CartRouteContext {
|
|
14
|
+
params: Promise<{ id: string }>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function GET(request: Request, context: CartRouteContext) {
|
|
18
|
+
try {
|
|
19
|
+
const isOnline = await ensureEcommerceOnline();
|
|
20
|
+
if (!isOnline) {
|
|
21
|
+
return NextResponse.json(
|
|
22
|
+
buildProtocolError(
|
|
23
|
+
'ecommerce_unavailable',
|
|
24
|
+
'Ecommerce module license is inactive.'
|
|
25
|
+
),
|
|
26
|
+
{ status: 403 }
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { id } = await context.params;
|
|
31
|
+
const result = await getUcpCart(id, request);
|
|
32
|
+
return NextResponse.json(result.body, { status: result.status });
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('UCP get cart error:', error);
|
|
35
|
+
return NextResponse.json(
|
|
36
|
+
buildProtocolError('internal_server_error', 'Cart retrieval failed.'),
|
|
37
|
+
{ status: 500 }
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function PUT(request: Request, context: CartRouteContext) {
|
|
43
|
+
try {
|
|
44
|
+
const isOnline = await ensureEcommerceOnline();
|
|
45
|
+
if (!isOnline) {
|
|
46
|
+
return NextResponse.json(
|
|
47
|
+
buildProtocolError(
|
|
48
|
+
'ecommerce_unavailable',
|
|
49
|
+
'Ecommerce module license is inactive.'
|
|
50
|
+
),
|
|
51
|
+
{ status: 403 }
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const { id } = await context.params;
|
|
56
|
+
const body = await parseJsonBody(request);
|
|
57
|
+
const result = await updateUcpCart(id, body, request);
|
|
58
|
+
return NextResponse.json(result.body, { status: result.status });
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error('UCP update cart error:', error);
|
|
61
|
+
return NextResponse.json(
|
|
62
|
+
buildProtocolError('internal_server_error', 'Cart update failed.'),
|
|
63
|
+
{ status: 500 }
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const PATCH = PUT;
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import { NextResponse } from 'next/server';
|
|
2
|
-
import {
|
|
3
|
-
buildProtocolError,
|
|
4
|
-
createUcpCart,
|
|
5
|
-
ensureEcommerceOnline,
|
|
6
|
-
parseJsonBody,
|
|
7
|
-
} from '../../../lib/ucp/server';
|
|
8
|
-
|
|
9
|
-
export const dynamic = 'force-dynamic';
|
|
10
|
-
export const runtime = 'nodejs';
|
|
11
|
-
|
|
12
|
-
export async function POST(request: Request) {
|
|
13
|
-
try {
|
|
14
|
-
const isOnline = await ensureEcommerceOnline();
|
|
15
|
-
if (!isOnline) {
|
|
16
|
-
return NextResponse.json(
|
|
17
|
-
buildProtocolError(
|
|
18
|
-
'ecommerce_unavailable',
|
|
19
|
-
'Ecommerce module license is inactive.'
|
|
20
|
-
),
|
|
21
|
-
{ status: 403 }
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const body = await parseJsonBody(request);
|
|
26
|
-
const result = await createUcpCart(body, request);
|
|
27
|
-
return NextResponse.json(result.body, { status: result.status });
|
|
28
|
-
} catch (error) {
|
|
29
|
-
console.error('UCP create cart error:', error);
|
|
30
|
-
return NextResponse.json(
|
|
31
|
-
buildProtocolError('internal_server_error', 'Cart creation failed.'),
|
|
32
|
-
{ status: 500 }
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import {
|
|
3
|
+
buildProtocolError,
|
|
4
|
+
createUcpCart,
|
|
5
|
+
ensureEcommerceOnline,
|
|
6
|
+
parseJsonBody,
|
|
7
|
+
} from '../../../lib/ucp/server';
|
|
8
|
+
|
|
9
|
+
export const dynamic = 'force-dynamic';
|
|
10
|
+
export const runtime = 'nodejs';
|
|
11
|
+
|
|
12
|
+
export async function POST(request: Request) {
|
|
13
|
+
try {
|
|
14
|
+
const isOnline = await ensureEcommerceOnline();
|
|
15
|
+
if (!isOnline) {
|
|
16
|
+
return NextResponse.json(
|
|
17
|
+
buildProtocolError(
|
|
18
|
+
'ecommerce_unavailable',
|
|
19
|
+
'Ecommerce module license is inactive.'
|
|
20
|
+
),
|
|
21
|
+
{ status: 403 }
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const body = await parseJsonBody(request);
|
|
26
|
+
const result = await createUcpCart(body, request);
|
|
27
|
+
return NextResponse.json(result.body, { status: result.status });
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error('UCP create cart error:', error);
|
|
30
|
+
return NextResponse.json(
|
|
31
|
+
buildProtocolError('internal_server_error', 'Cart creation failed.'),
|
|
32
|
+
{ status: 500 }
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import { NextResponse } from 'next/server';
|
|
2
|
-
import {
|
|
3
|
-
buildProtocolError,
|
|
4
|
-
ensureEcommerceOnline,
|
|
5
|
-
lookupCatalogProducts,
|
|
6
|
-
parseJsonBody,
|
|
7
|
-
} from '../../../../lib/ucp/server';
|
|
8
|
-
|
|
9
|
-
export const dynamic = 'force-dynamic';
|
|
10
|
-
export const runtime = 'nodejs';
|
|
11
|
-
|
|
12
|
-
export async function POST(request: Request) {
|
|
13
|
-
try {
|
|
14
|
-
const isOnline = await ensureEcommerceOnline();
|
|
15
|
-
if (!isOnline) {
|
|
16
|
-
return NextResponse.json(
|
|
17
|
-
buildProtocolError(
|
|
18
|
-
'ecommerce_unavailable',
|
|
19
|
-
'Ecommerce module license is inactive.'
|
|
20
|
-
),
|
|
21
|
-
{ status: 403 }
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const body = await parseJsonBody(request);
|
|
26
|
-
const result = await lookupCatalogProducts(body, request);
|
|
27
|
-
return NextResponse.json(result.body, { status: result.status });
|
|
28
|
-
} catch (error) {
|
|
29
|
-
console.error('UCP catalog lookup error:', error);
|
|
30
|
-
return NextResponse.json(
|
|
31
|
-
buildProtocolError('internal_server_error', 'Catalog lookup failed.'),
|
|
32
|
-
{ status: 500 }
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import {
|
|
3
|
+
buildProtocolError,
|
|
4
|
+
ensureEcommerceOnline,
|
|
5
|
+
lookupCatalogProducts,
|
|
6
|
+
parseJsonBody,
|
|
7
|
+
} from '../../../../lib/ucp/server';
|
|
8
|
+
|
|
9
|
+
export const dynamic = 'force-dynamic';
|
|
10
|
+
export const runtime = 'nodejs';
|
|
11
|
+
|
|
12
|
+
export async function POST(request: Request) {
|
|
13
|
+
try {
|
|
14
|
+
const isOnline = await ensureEcommerceOnline();
|
|
15
|
+
if (!isOnline) {
|
|
16
|
+
return NextResponse.json(
|
|
17
|
+
buildProtocolError(
|
|
18
|
+
'ecommerce_unavailable',
|
|
19
|
+
'Ecommerce module license is inactive.'
|
|
20
|
+
),
|
|
21
|
+
{ status: 403 }
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const body = await parseJsonBody(request);
|
|
26
|
+
const result = await lookupCatalogProducts(body, request);
|
|
27
|
+
return NextResponse.json(result.body, { status: result.status });
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error('UCP catalog lookup error:', error);
|
|
30
|
+
return NextResponse.json(
|
|
31
|
+
buildProtocolError('internal_server_error', 'Catalog lookup failed.'),
|
|
32
|
+
{ status: 500 }
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import { NextResponse } from 'next/server';
|
|
2
|
-
import {
|
|
3
|
-
buildProtocolError,
|
|
4
|
-
ensureEcommerceOnline,
|
|
5
|
-
getCatalogProduct,
|
|
6
|
-
parseJsonBody,
|
|
7
|
-
} from '../../../../lib/ucp/server';
|
|
8
|
-
|
|
9
|
-
export const dynamic = 'force-dynamic';
|
|
10
|
-
export const runtime = 'nodejs';
|
|
11
|
-
|
|
12
|
-
export async function POST(request: Request) {
|
|
13
|
-
try {
|
|
14
|
-
const isOnline = await ensureEcommerceOnline();
|
|
15
|
-
if (!isOnline) {
|
|
16
|
-
return NextResponse.json(
|
|
17
|
-
buildProtocolError(
|
|
18
|
-
'ecommerce_unavailable',
|
|
19
|
-
'Ecommerce module license is inactive.'
|
|
20
|
-
),
|
|
21
|
-
{ status: 403 }
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const body = await parseJsonBody(request);
|
|
26
|
-
const result = await getCatalogProduct(body, request);
|
|
27
|
-
return NextResponse.json(result.body, { status: result.status });
|
|
28
|
-
} catch (error) {
|
|
29
|
-
console.error('UCP catalog product error:', error);
|
|
30
|
-
return NextResponse.json(
|
|
31
|
-
buildProtocolError('internal_server_error', 'Catalog product lookup failed.'),
|
|
32
|
-
{ status: 500 }
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import {
|
|
3
|
+
buildProtocolError,
|
|
4
|
+
ensureEcommerceOnline,
|
|
5
|
+
getCatalogProduct,
|
|
6
|
+
parseJsonBody,
|
|
7
|
+
} from '../../../../lib/ucp/server';
|
|
8
|
+
|
|
9
|
+
export const dynamic = 'force-dynamic';
|
|
10
|
+
export const runtime = 'nodejs';
|
|
11
|
+
|
|
12
|
+
export async function POST(request: Request) {
|
|
13
|
+
try {
|
|
14
|
+
const isOnline = await ensureEcommerceOnline();
|
|
15
|
+
if (!isOnline) {
|
|
16
|
+
return NextResponse.json(
|
|
17
|
+
buildProtocolError(
|
|
18
|
+
'ecommerce_unavailable',
|
|
19
|
+
'Ecommerce module license is inactive.'
|
|
20
|
+
),
|
|
21
|
+
{ status: 403 }
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const body = await parseJsonBody(request);
|
|
26
|
+
const result = await getCatalogProduct(body, request);
|
|
27
|
+
return NextResponse.json(result.body, { status: result.status });
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error('UCP catalog product error:', error);
|
|
30
|
+
return NextResponse.json(
|
|
31
|
+
buildProtocolError('internal_server_error', 'Catalog product lookup failed.'),
|
|
32
|
+
{ status: 500 }
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|