Update skills: add website-creator, mql-developer, ecommerce-astro
Changes: - Add FAL_KEY and GEMINI_API_KEY to .env.example - Update picture-it to use ~/.config/opencode/.env (unified creds) - Remove shodh-memory skill (no longer used) - Remove alphaear-* skills (deprecated) - Remove thai-frontend-dev skill (replaced by website-creator) - Remove theme-factory skill - Add mql-developer skill (MQL5 trading) - Add ecommerce-astro skill (Astro e-commerce) - Add website-creator skill (Next.js + Payload CMS) - Update install script for new skills
This commit is contained in:
81
skills/website-creator/templates/consent/api/consent.ts
Normal file
81
skills/website-creator/templates/consent/api/consent.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
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' },
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
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' } }
|
||||
)
|
||||
}
|
||||
}
|
||||
80
skills/website-creator/templates/consent/api/route.ts
Normal file
80
skills/website-creator/templates/consent/api/route.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
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 }
|
||||
* }
|
||||
*/
|
||||
export async function POST(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
|
||||
|
||||
// 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 })
|
||||
}
|
||||
|
||||
// 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({
|
||||
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,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return NextResponse.json({ success: true, doc: consentLog })
|
||||
} catch (error) {
|
||||
console.error('Consent logging error:', error)
|
||||
return NextResponse.json({ error: 'Failed to log consent' }, { 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'
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user