diff --git a/.opencode/package-lock.json b/.opencode/package-lock.json index 6aa38cb..a427e44 100644 --- a/.opencode/package-lock.json +++ b/.opencode/package-lock.json @@ -5,21 +5,100 @@ "packages": { "": { "dependencies": { - "@opencode-ai/plugin": "1.3.15" + "@opencode-ai/plugin": "1.4.7" } }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@opencode-ai/plugin": { - "version": "1.3.15", - "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.3.15.tgz", - "integrity": "sha512-jZJbuvUXc5Limz8pacQl+ffATjjKGlq+xaA4wTUeW+/spwOf7Yr5Ryyvan8eNlYM8wy6h5SLfznl1rlFpjYC8w==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.4.7.tgz", + "integrity": "sha512-RbzMl7ILvQDHpZNvqzi6RCYaGcB3eBwNIMRZww467drLvMd1eOwr4/qAurrvYDsIIEctE6cKsrLuSGIKCW/Fxg==", "license": "MIT", "dependencies": { - "@opencode-ai/sdk": "1.3.15", + "@opencode-ai/sdk": "1.4.7", + "effect": "4.0.0-beta.48", "zod": "4.1.8" }, "peerDependencies": { - "@opentui/core": ">=0.1.96", - "@opentui/solid": ">=0.1.96" + "@opentui/core": ">=0.1.99", + "@opentui/solid": ">=0.1.99" }, "peerDependenciesMeta": { "@opentui/core": { @@ -31,14 +110,20 @@ } }, "node_modules/@opencode-ai/sdk": { - "version": "1.3.15", - "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.3.15.tgz", - "integrity": "sha512-Uk59C7wsK20wpdr277yx7Xz7TqG5jGqlZUpSW3wDH/7a2K2iBg0lXc2wskHuCXLRXMhXpPZtb4a3SOpPENkkbg==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.4.7.tgz", + "integrity": "sha512-onEtaooQyoDP5gTShQeQSf0Sd8V7949G9pPNyIyRXnVtFqyDIhUDLGtL/a/+EIW9x5s+Y6lDy/3oVoGMvQ0rQQ==", "license": "MIT", "dependencies": { "cross-spawn": "7.0.6" } }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -53,12 +138,135 @@ "node": ">= 8" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/effect": { + "version": "4.0.0-beta.48", + "resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-beta.48.tgz", + "integrity": "sha512-MMAM/ZabuNdNmgXiin+BAanQXK7qM8mlt7nfXDoJ/Gn9V8i89JlCq+2N0AiWmqFLXjGLA0u3FjiOjSOYQk5uMw==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "fast-check": "^4.6.0", + "find-my-way-ts": "^0.1.6", + "ini": "^6.0.0", + "kubernetes-types": "^1.30.0", + "msgpackr": "^1.11.9", + "multipasta": "^0.2.7", + "toml": "^4.1.1", + "uuid": "^13.0.0", + "yaml": "^2.8.3" + } + }, + "node_modules/fast-check": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.6.0.tgz", + "integrity": "sha512-h7H6Dm0Fy+H4ciQYFxFjXnXkzR2kr9Fb22c0UBpHnm59K2zpr2t13aPTHlltFiNT6zuxp6HMPAVVvgur4BLdpA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^8.0.0" + }, + "engines": { + "node": ">=12.17.0" + } + }, + "node_modules/find-my-way-ts": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/find-my-way-ts/-/find-my-way-ts-0.1.6.tgz", + "integrity": "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==", + "license": "MIT" + }, + "node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/kubernetes-types": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/kubernetes-types/-/kubernetes-types-1.30.0.tgz", + "integrity": "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==", + "license": "Apache-2.0" + }, + "node_modules/msgpackr": { + "version": "1.11.9", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.9.tgz", + "integrity": "sha512-FkoAAyyA6HM8wL882EcEyFZ9s7hVADSwG9xrVx3dxxNQAtgADTrJoEWivID82Iv1zWDsv/OtbrrcZAzGzOMdNw==", + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/multipasta": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/multipasta/-/multipasta-0.2.7.tgz", + "integrity": "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA==", + "license": "MIT" + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -68,6 +276,22 @@ "node": ">=8" } }, + "node_modules/pure-rand": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.0.tgz", + "integrity": "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -89,6 +313,28 @@ "node": ">=8" } }, + "node_modules/toml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/toml/-/toml-4.1.1.tgz", + "integrity": "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -104,6 +350,21 @@ "node": ">= 8" } }, + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/zod": { "version": "4.1.8", "license": "MIT", diff --git a/skills/website-creator/references/payload-nextjs-notes.md b/skills/website-creator/references/payload-nextjs-notes.md index 55c9404..27ef959 100644 --- a/skills/website-creator/references/payload-nextjs-notes.md +++ b/skills/website-creator/references/payload-nextjs-notes.md @@ -3,7 +3,7 @@ ## PostgreSQL Connection Issues ### Wrong port -- Docker container `astro-starter-db-1` exposes PostgreSQL on port **5555** (not 5432) +- Docker container `payload-db-1` exposes MongoDB on port **27017** (default) - Fix: Use `localhost:5555` in DATABASE_URL for local development ### Wrong database name diff --git a/skills/website-creator/references/sitemap-template.md b/skills/website-creator/references/sitemap-template.md index 375ce01..1f04e82 100644 --- a/skills/website-creator/references/sitemap-template.md +++ b/skills/website-creator/references/sitemap-template.md @@ -160,39 +160,59 @@ src/ │ └── team/ │ ├── member-1.md │ └── ... -├── layouts/ -│ ├── BaseLayout.astro -│ ├── PageLayout.astro -│ ├── BlogLayout.astro -│ └── AuthLayout.astro +├── app/ +│ ├── (frontend)/ +│ │ ├── layout.tsx +│ │ ├── page.tsx +│ │ ├── about/ +│ │ │ └── page.tsx +│ │ ├── services/ +│ │ │ ├── page.tsx +│ │ │ └── [slug]/ +│ │ │ └── page.tsx +│ │ ├── blog/ +│ │ │ ├── page.tsx +│ │ │ └── [slug]/ +│ │ │ └── page.tsx +│ │ ├── contact/ +│ │ │ └── page.tsx +│ │ ├── privacy-policy/ +│ │ │ └── page.tsx +│ │ ├── terms-of-service/ +│ │ │ └── page.tsx +│ │ ├── login/ +│ │ │ └── page.tsx +│ │ ├── register/ +│ │ │ └── page.tsx +│ │ └── account/ +│ │ ├── page.tsx +│ │ ├── profile/ +│ │ │ └── page.tsx +│ │ ├── orders/ +│ │ │ └── page.tsx +│ │ └── settings/ +│ │ └── page.tsx +│ └── (payload)/ +│ ├── admin/ +│ │ └── [[...segments]]/ +│ │ └── page.tsx +│ └── api/ +│ └── ... ├── components/ -│ ├── Navigation.astro -│ ├── Footer.astro -│ ├── Hero.astro -│ ├── ServiceCard.astro -│ ├── BlogCard.astro -│ ├── ContactForm.astro -│ ├── CookieConsent.astro +│ ├── Navigation.tsx +│ ├── Footer.tsx +│ ├── Hero.tsx +│ ├── ServiceCard.tsx +│ ├── BlogCard.tsx +│ ├── ContactForm.tsx +│ ├── CookieConsent.tsx │ └── ... -└── pages/ - ├── index.astro - ├── about.astro - ├── services/ - │ ├── index.astro - │ └── [slug].astro - ├── blog/ - │ ├── index.astro - │ └── [slug].astro - ├── contact.astro - ├── privacy-policy.astro - ├── terms-of-service.astro - ├── login.astro - ├── register.astro - └── account/ - ├── index.astro - ├── profile.astro - ├── orders.astro - └── settings.astro +└── collections/ + ├── Posts.ts + ├── Pages.ts + ├── Media.ts + ├── Users.ts + └── ConsentLogs.ts ``` --- @@ -230,7 +250,7 @@ src/ - ติดต่อเรา - ติดต่อ - นโยบายความเป็นส่วนตัว - {EMAIL} - สมัครสมาชิก - เงื่อนไขการให้บริการ -Copyright (c) {YEAR} {SITE_NAME} | Built with Astro +Copyright (c) {YEAR} {SITE_NAME} | Built with Next.js + Payload CMS ``` --- diff --git a/skills/website-creator/templates/consent/CookieConsent.astro b/skills/website-creator/templates/consent/CookieConsent.astro deleted file mode 100644 index e316b59..0000000 --- a/skills/website-creator/templates/consent/CookieConsent.astro +++ /dev/null @@ -1,462 +0,0 @@ ---- -// CookieConsent.astro - PDPA Cookie Consent Banner -// ทำงานจริง: ถ้า reject จะไม่ load tracking scripts - -interface Props { - position?: 'bottom' | 'top'; - theme?: 'light' | 'dark'; -} - -const { position = 'bottom', theme = 'light' } = Astro.props; - -// Consent states -const CONSENT_TYPES = { - ESSENTIAL: 'essential', - ANALYTICS: 'analytics', - MARKETING: 'marketing', - FUNCTIONAL: 'functional', -} as const; ---- - -
- - - - diff --git a/skills/website-creator/templates/consent/api/consent.ts b/skills/website-creator/templates/consent/api/consent.ts deleted file mode 100644 index 2418503..0000000 --- a/skills/website-creator/templates/consent/api/consent.ts +++ /dev/null @@ -1,81 +0,0 @@ -import type { APIRoute } from 'astro' - -// POST /api/consent - บันทึก consent -export const POST: APIRoute = async ({ request }) => { - try { - const body = await request.json() - const { session_id, essential, analytics, marketing, functional } = body - - if (!session_id) { - return new Response(JSON.stringify({ error: 'session_id is required' }), { - status: 400, - headers: { 'Content-Type': 'application/json' }, - }) - } - - // Get client info - const ipAddress = request.headers.get('x-forwarded-for')?.split(',')[0] || 'unknown' - const userAgent = request.headers.get('user-agent') || 'unknown' - - // Build consent record - const consentTypes = [] - if (essential) consentTypes.push('essential') - if (analytics) consentTypes.push('analytics') - if (marketing) consentTypes.push('marketing') - if (functional) consentTypes.push('functional') - - // In Payload CMS, you would save this to the consent-logs collection - // For now, return success (Payload integration happens at build time) - const record = { - sessionId: session_id, - consentType: consentTypes.length === 4 ? 'accept_all' : consentTypes.join(','), - granted: analytics || marketing || functional, - ipAddress, - userAgent, - metadata: { essential, analytics, marketing, functional }, - createdAt: new Date().toISOString(), - } - - // Log for debugging (remove in production) - console.log('[Consent API] New consent record:', JSON.stringify(record)) - - return new Response(JSON.stringify({ success: true, record }), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }) - } catch (error) { - console.error('[Consent API] Error:', error) - return new Response(JSON.stringify({ error: 'Internal server error' }), { - status: 500, - headers: { 'Content-Type': 'application/json' }, - }) - } -} - -// GET /api/consent - ตรวจสอบ consent ของ session -export const GET: APIRoute = async ({ request }) => { - try { - const url = new URL(request.url) - const sessionId = url.searchParams.get('session_id') - - if (!sessionId) { - return new Response(JSON.stringify({ error: 'session_id is required' }), { - status: 400, - headers: { 'Content-Type': 'application/json' }, - }) - } - - // In Payload CMS, query the consent-logs collection - // For now, return not found (Payload integration happens at build time) - return new Response(JSON.stringify({ error: 'Not implemented in template' }), { - status: 501, - headers: { 'Content-Type': 'application/json' }, - }) - } catch (error) { - console.error('[Consent API] Error:', error) - return new Response(JSON.stringify({ error: 'Internal server error' }), { - status: 500, - headers: { 'Content-Type': 'application/json' }, - }) - } -} diff --git a/skills/website-creator/templates/consent/api/right-to-be-forgotten.ts b/skills/website-creator/templates/consent/api/right-to-be-forgotten.ts deleted file mode 100644 index 6afda00..0000000 --- a/skills/website-creator/templates/consent/api/right-to-be-forgotten.ts +++ /dev/null @@ -1,86 +0,0 @@ -import type { APIRoute } from 'astro' - -// Right to be Forgotten API - PDPA Article 17 -// DELETE /api/consent?session_id=xxx - ลบข้อมูลของ session นี้ - -export const DELETE: APIRoute = async ({ request }) => { - try { - const url = new URL(request.url) - const sessionId = url.searchParams.get('session_id') - - if (!sessionId) { - return new Response( - JSON.stringify({ error: 'session_id is required' }), - { status: 400, headers: { 'Content-Type': 'application/json' } } - ) - } - - // In Payload CMS, you would: - // 1. Find all consent-logs with this sessionId - // 2. Delete them - // 3. Also delete any user data associated with this session - - // Example Payload query (for reference): - // await payload.delete({ - // collection: 'consent-logs', - // where: { sessionId: { equals: sessionId } }, - // }) - - console.log(`[Right to be Forgotten] Deleting data for session: ${sessionId}`) - - return new Response( - JSON.stringify({ - success: true, - message: 'ข้อมูลของคุณถูกลบแล้ว', - deletedAt: new Date().toISOString(), - }), - { status: 200, headers: { 'Content-Type': 'application/json' } } - ) - } catch (error) { - console.error('[Right to be Forgotten] Error:', error) - return new Response( - JSON.stringify({ error: 'Internal server error' }), - { status: 500, headers: { 'Content-Type': 'application/json' } } - ) - } -} - -// GET /api/consent/export - ขอ export ข้อมูลของตัวเอง (PDPA Article 31) -export const GET: APIRoute = async ({ request }) => { - try { - const url = new URL(request.url) - const sessionId = url.searchParams.get('session_id') - - if (!sessionId) { - return new Response( - JSON.stringify({ error: 'session_id is required' }), - { status: 400, headers: { 'Content-Type': 'application/json' } } - ) - } - - // In Payload CMS, query consent-logs for this session - // Return the data as JSON for the user to review - - // Example Payload query (for reference): - // const logs = await payload.find({ - // collection: 'consent-logs', - // where: { sessionId: { equals: sessionId } }, - // }) - - return new Response( - JSON.stringify({ - success: true, - message: 'ข้อมูลของคุณ', - data: [], // Replace with actual Payload query result - requestedAt: new Date().toISOString(), - }), - { status: 200, headers: { 'Content-Type': 'application/json' } } - ) - } catch (error) { - console.error('[Consent Export] Error:', error) - return new Response( - JSON.stringify({ error: 'Internal server error' }), - { status: 500, headers: { 'Content-Type': 'application/json' } } - ) - } -} diff --git a/skills/website-creator/templates/consent/api/route.ts b/skills/website-creator/templates/consent/api/route.ts index 8fdf065..01f01f1 100644 --- a/skills/website-creator/templates/consent/api/route.ts +++ b/skills/website-creator/templates/consent/api/route.ts @@ -3,78 +3,37 @@ import { getPayload } from 'payload' import config from '@/payload.config' /** - * POST /api/consent - Record consent action - * - * Request body: - * { - * action: 'accept' | 'reject' | 'update', - * purpose: 'analytics' | 'marketing' | 'functional' | 'all', - * analytics: boolean, - * marketing: boolean, - * functional: boolean, - * previousConsent?: { analytics: boolean, marketing: boolean, functional: boolean } - * } + * DELETE /api/consent - Right to be forgotten (GDPR/PDPA) + * + * Deletes all consent records for a given session or user */ -export async function POST(request: NextRequest) { +export async function DELETE(request: NextRequest) { try { const payloadConfig = await config const payload = await getPayload({ config: payloadConfig }) - const body = await request.json() - const { action, purpose, analytics, marketing, functional, previousConsent } = body + const { searchParams } = new URL(request.url) + const sessionId = searchParams.get('sessionId') - // Validate required fields - if (!action || !['accept', 'reject', 'update'].includes(action)) { - return NextResponse.json({ error: 'Invalid action' }, { status: 400 }) - } - if (!purpose || !['analytics', 'marketing', 'functional', 'all'].includes(purpose)) { - return NextResponse.json({ error: 'Invalid purpose' }, { status: 400 }) + if (!sessionId) { + return NextResponse.json({ error: 'sessionId is required' }, { status: 400 }) } - // Get IP and User Agent - const ip = request.headers.get('x-forwarded-for')?.split(',')[0] - || request.headers.get('x-real-ip') - || 'unknown' - const userAgent = request.headers.get('user-agent') || 'unknown' - - // Create consent log - const consentLog = await payload.create({ + // Find and delete all consent logs for this session + const result = await payload.delete({ collection: 'consent-logs', - data: { - action, - purpose, - analytics: analytics ?? false, - marketing: marketing ?? false, - functional: functional ?? false, - userAgent, - ip, - timestamp: new Date().toISOString(), - previousConsent: previousConsent || null, - newConsent: { - analytics: analytics ?? false, - marketing: marketing ?? false, - functional: functional ?? false, - }, + where: { + sessionId: { equals: sessionId }, }, }) - return NextResponse.json({ success: true, doc: consentLog }) + return NextResponse.json({ + success: true, + deleted: result.deletedDocs?.length || 0, + message: 'All consent records for this session have been deleted' + }) } catch (error) { - console.error('Consent logging error:', error) - return NextResponse.json({ error: 'Failed to log consent' }, { status: 500 }) + console.error('Right to be forgotten error:', error) + return NextResponse.json({ error: 'Failed to delete consent records' }, { status: 500 }) } -} - -/** - * GET /api/consent - Get current consent status (from cookie or localStorage) - * This endpoint is mainly for verification, actual consent is stored client-side - */ -export async function GET(request: NextRequest) { - // Consent is stored client-side in localStorage - // This endpoint is for compliance verification - return NextResponse.json({ - message: 'Consent is stored client-side', - purposes: ['analytics', 'marketing', 'functional'], - note: 'Use POST to update consent preferences' - }) -} +} \ No newline at end of file