#!/usr/bin/env python3 """ Website Refactoring Script - Update existing Astro websites to new PDPA-compliant standard This script migrates existing Astro websites to the new standardized structure with: - PDPA-compliant legal pages - Cookie consent system - Consent logging database - i18n routing (Thai/English) - Umami Analytics integration Usage: python3 refactor_existing_website.py \ --input "./existing-website" \ --output "./refactored-website" \ --languages "th,en" """ import os import sys import shutil import argparse from pathlib import Path from datetime import datetime def main(): parser = argparse.ArgumentParser( description='Refactor existing Astro website to PDPA-compliant standard' ) parser.add_argument('--input', '-i', required=True, help='Input directory (existing website)') parser.add_argument('--output', '-o', required=True, help='Output directory (refactored website)') parser.add_argument('--languages', default='th,en', help='Languages (comma-separated)') parser.add_argument('--umami-id', default='', help='Umami Website ID') parser.add_argument('--umami-domain', default='analytics.example.com', help='Umami domain') parser.add_argument('--admin-password', default='changeme', help='Admin password') parser.add_argument('--skip-backup', action='store_true', help='Skip backup creation') args = parser.parse_args() input_dir = Path(args.input) output_dir = Path(args.output) languages = [lang.strip() for lang in args.languages.split(',')] if not input_dir.exists(): print(f"Error: Input directory '{input_dir}' does not exist") sys.exit(1) print(f"🔄 Refactoring website from: {input_dir}") print(f"📁 Output directory: {output_dir}") print(f"🌐 Languages: {languages}") # Create backup if not args.skip_backup: create_backup(input_dir) # Create new structure output_dir.mkdir(parents=True, exist_ok=True) # Migrate content migrate_content(input_dir, output_dir, languages) # Add new features add_pdpa_features(output_dir, args, languages) # Update configuration update_configs(output_dir, args, languages) print(f"\n✅ Refactoring complete!") print(f"\n📁 Refactored website at: {output_dir}") print("\n📋 Next steps:") print(f" 1. cd {output_dir}") print(" 2. Review changes") print(" 3. npm install") print(" 4. Update .env with your credentials") print(" 5. npm run dev") print("\n⚠️ Important:") print(" - Review Privacy Policy content (update company info)") print(" - Review Terms & Conditions (update service details)") print(" - Change admin password in .env") print(" - Test all features before deployment") def create_backup(input_dir): """Create backup of existing website.""" timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') backup_dir = input_dir.parent / f"{input_dir.name}_backup_{timestamp}" print(f"💾 Creating backup: {backup_dir}") shutil.copytree(input_dir, backup_dir) print(f" ✓ Backup created") def migrate_content(input_dir, output_dir, languages): """Migrate existing content to new structure.""" # Create new folder structure create_new_structure(output_dir) # Copy existing content src_input = input_dir / 'src' if src_input.exists(): # Copy components components_input = src_input / 'components' if components_input.exists(): print("📦 Migrating components...") shutil.copytree( components_input, output_dir / 'src' / 'components' / 'migrated', dirs_exist_ok=True ) print(f" ✓ Components migrated") # Copy layouts layouts_input = src_input / 'layouts' if layouts_input.exists(): print("📐 Migrating layouts...") shutil.copytree( layouts_input, output_dir / 'src' / 'layouts' / 'migrated', dirs_exist_ok=True ) print(f" ✓ Layouts migrated") # Copy content collections (blog, products) content_input = src_input / 'content' if content_input.exists(): print("📝 Migrating content collections...") # Copy blog posts blog_input = content_input / 'blog' if blog_input.exists(): # Organize by language if possible for lang in languages: lang_dir = output_dir / 'src' / 'content' / 'blog' / f'({lang})' lang_dir.mkdir(parents=True, exist_ok=True) # Copy all posts to default language folder for now default_lang = 'en' if 'en' in languages else languages[0] default_dir = output_dir / 'src' / 'content' / 'blog' / f'({default_lang})' for md_file in blog_input.glob('*.md'): shutil.copy2(md_file, default_dir) print(f" ✓ Copied: {md_file.name}") # Copy static assets public_input = input_dir / 'public' if public_input.exists(): print("🖼️ Migrating static assets...") shutil.copytree( public_input, output_dir / 'public', dirs_exist_ok=True ) print(f" ✓ Assets migrated") # Copy images for img_dir in src_input.glob('**/images'): rel_path = img_dir.relative_to(src_input) dest_dir = output_dir / 'src' / 'components' / rel_path dest_dir.mkdir(parents=True, exist_ok=True) for img_file in img_dir.glob('*'): if img_file.suffix.lower() in ['.jpg', '.jpeg', '.png', '.svg', '.webp']: shutil.copy2(img_file, dest_dir) print(f" ✓ Content migration complete") def create_new_structure(output_dir): """Create the new standardized folder structure.""" dirs = [ output_dir / 'public' / 'images', output_dir / 'src' / 'components' / 'common', output_dir / 'src' / 'components' / 'consent', output_dir / 'src' / 'components' / 'ui', output_dir / 'src' / 'components' / 'migrated', output_dir / 'src' / 'layouts', output_dir / 'src' / 'layouts' / 'migrated', output_dir / 'src' / 'pages', output_dir / 'src' / 'pages' / 'api' / 'consent', output_dir / 'src' / 'pages' / 'admin', output_dir / 'src' / 'styles', output_dir / 'src' / 'content' / 'blog', output_dir / 'src' / 'lib', output_dir / 'db', ] for d in dirs: d.mkdir(parents=True, exist_ok=True) print("📁 Created new folder structure") def add_pdpa_features(output_dir, args, languages): """Add PDPA compliance features.""" default_locale = 'en' if 'en' in languages else languages[0] # Create Privacy Policy pages print("📄 Creating Privacy Policy pages...") for lang in languages: lang_dir = output_dir / 'src' / 'pages' / lang lang_dir.mkdir(parents=True, exist_ok=True) privacy_policy = get_privacy_policy_template(lang, args) (lang_dir / 'privacy-policy.astro').write_text(privacy_policy) print(" ✓ Privacy Policy created") # Create Terms & Conditions pages print("📄 Creating Terms & Conditions pages...") for lang in languages: lang_dir = output_dir / 'src' / 'pages' / lang terms = get_terms_template(lang) (lang_dir / 'terms-and-conditions.astro').write_text(terms) print(" ✓ Terms & Conditions created") # Create consent components print("🍪 Creating cookie consent components...") cookie_banner = get_cookie_banner_template() (output_dir / 'src' / 'components' / 'consent' / 'CookieBanner.astro').write_text(cookie_banner) print(" ✓ Cookie banner created") # Create admin dashboard print("🔐 Creating admin dashboard...") admin_page = get_admin_dashboard_template() admin_dir = output_dir / 'src' / 'pages' / 'admin' admin_dir.mkdir(parents=True, exist_ok=True) (admin_dir / 'consent-logs.astro').write_text(admin_page) print(" ✓ Admin dashboard created") # Create database schema print("💾 Creating database schema...") db_config = get_db_config_template() (output_dir / 'db' / 'config.ts').write_text(db_config) db_seed = get_db_seed_template() (output_dir / 'db' / 'seed.ts').write_text(db_seed) print(" ✓ Database schema created") # Create API endpoints print("🔌 Creating API endpoints...") create_api_endpoints(output_dir) print(" ✓ API endpoints created") # Create i18n lib print("🌐 Creating i18n utilities...") i18n_lib = get_i18n_lib_template(languages) (output_dir / 'src' / 'lib' / 'i18n.ts').write_text(i18n_lib) print(" ✓ i18n utilities created") def update_configs(output_dir, args, languages): """Update configuration files.""" default_locale = 'en' if 'en' in languages else languages[0] locales_str = ', '.join([f"'{lang}'" for lang in languages]) # astro.config.mjs print("⚙️ Updating astro.config.mjs...") astro_config = f"""import {{ defineConfig }} from 'astro/config'; import tailwindcss from '@tailwindcss/vite'; import db from '@astrojs/db'; import sitemap from '@astrojs/sitemap'; export default defineConfig({{ site: 'https://example.com', output: 'hybrid', i18n: {{ locales: [{locales_str}], defaultLocale: '{default_locale}', routing: {{ prefixDefaultLocale: false, fallbackType: 'rewrite', }}, fallback: {{ th: 'en', }}, }}, integrations: [ tailwindcss(), db(), sitemap({{ i18n: {{ defaultLocale: '{default_locale}', }}, }}), ], }}); """ (output_dir / 'astro.config.mjs').write_text(astro_config) print(" ✓ astro.config.mjs updated") # package.json (add dependencies) print("📦 Updating package.json...") package_json_path = output_dir / 'package.json' if package_json_path.exists(): import json with open(package_json_path, 'r') as f: package_json = json.load(f) # Add new dependencies new_deps = { "@astrojs/db": "^0.14.0", "@astrojs/sitemap": "^3.2.0", "drizzle-orm": "^0.38.0", "@libsql/client": "^0.14.0", } if 'dependencies' not in package_json: package_json['dependencies'] = {} package_json['dependencies'].update(new_deps) # Add new scripts package_json['scripts']['db:push'] = 'astro db push --remote' package_json['scripts']['db:seed'] = 'astro db seed' with open(package_json_path, 'w') as f: json.dump(package_json, f, indent=2) print(" ✓ package.json updated") # Create .env.example print("🔐 Creating .env.example...") env_example = f"""# Umami Analytics UMAMI_WEBSITE_ID={args.umami_id or 'your-website-id-here'} UMAMI_DOMAIN={args.umami_domain} # Admin ADMIN_PASSWORD={args.admin_password} # Database (optional) # ASTRO_DB_REMOTE_URL=libsql://your-db.turso.io # ASTRO_DB_APP_TOKEN=your-turso-token # Site Configuration SITE_URL=https://example.com SITE_NAME="Website Name" """ (output_dir / '.env.example').write_text(env_example) # Update .gitignore gitignore = """node_modules dist .env .astro *.db *.log .DS_Store """ (output_dir / '.gitignore').write_text(gitignore) print(" ✓ .env.example created") # Create/update Dockerfile print("🐳 Creating Dockerfile...") dockerfile = """FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm install --production COPY --from=builder /app/dist ./dist COPY --from=builder /app/db ./db RUN apk add --no-cache sqlite-libs EXPOSE 80 ENV NODE_ENV=production ENV ASTRO_DB_REMOTE_URL=file:/app/data/consent.db CMD ["sh", "-c", "mkdir -p /app/data && npx astro preview --host 0.0.0.0 --port 80"] """ (output_dir / 'Dockerfile').write_text(dockerfile) print(" ✓ Dockerfile created") # Create MIGRATION.md print("📝 Creating migration guide...") migration_guide = f"""# Migration Guide ## What Changed This website has been refactored to include: - ✅ PDPA-compliant Privacy Policy - ✅ PDPA-compliant Terms & Conditions - ✅ Cookie consent system - ✅ Consent logging database - ✅ i18n routing ({', '.join(languages)}) - ✅ Umami Analytics integration - ✅ Admin dashboard ## New Features ### 1. Privacy Policy Location: `/privacy-policy`, `/th/privacy-policy` - All 14 PDPA Section 36 disclosures - Version tracking - Last updated date ### 2. Cookie Consent - Appears on first visit - Opt-in model (required by PDPA) - Granular choices (essential/analytics/marketing) - Consent logged to database ### 3. Admin Dashboard URL: `/admin/consent-logs` Password: {args.admin_password} (**CHANGE THIS!**) Features: - View all consent records - Filter by date/locale - Delete records (right to be forgotten) ### 4. Database Schema: `db/config.ts` - Stores consent logs - SQLite file (development) - Turso ready (production) ### 5. i18n Routing - Default locale: {default_locale} - URL structure: `/about` ({default_locale}), `/th/about` (Thai) - Fallback: Thai → English ## Migration Steps ### 1. Review Changes ```bash # Check new files ls -la src/pages/ ls -la src/components/consent/ ls -la db/ ``` ### 2. Update Content - [ ] Edit Privacy Policy (add your company info) - [ ] Edit Terms & Conditions (add service details) - [ ] Update .env with your credentials - [ ] Change admin password ### 3. Test Features ```bash npm install npm run dev # Open http://localhost:4321 ``` Checklist: - [ ] Language switcher works - [ ] Cookie consent appears - [ ] Admin dashboard accessible - [ ] Consent logging works ### 4. Deploy ```bash npm run build docker build -t website:latest . # Deploy to Easypanel ``` ## Content Migration ### Blog Posts Your existing blog posts have been copied to: `src/content/blog/(en)/` (or your default language) To add bilingual content: 1. Translate posts 2. Copy to `src/content/blog/(th)/` 3. Update frontmatter with `locale: 'th'` ### Components Migrated components are in: `src/components/migrated/` You can continue using them or migrate to new structure. ## Rollback If you need to rollback: 1. Use the backup created at: `{args.input}_backup_TIMESTAMP` 2. Restore from backup 3. Revert deployment ## Support See documentation: - `SKILL.md` - Complete skill workflow - `SPECIFICATION.md` - Technical details - `IMPLEMENTATION_SUMMARY.md` - Feature summary """ (output_dir / 'MIGRATION.md').write_text(migration_guide) print(" ✓ Migration guide created") def get_privacy_policy_template(lang, args): """Generate Privacy Policy template.""" if lang == 'th': return f"""--- import BaseLayout from '../../layouts/BaseLayout.astro'; const lastUpdated = new Date().toLocaleDateString('th-TH', {{ year: 'numeric', month: 'long', day: 'numeric' }}); const version = '1.0.0'; ---

นโยบายความเป็นส่วนตัว

ฉบับที่ {{version}} | อัปเดตล่าสุด: {{lastUpdated}}

1. ผู้ควบคุมข้อมูล

{args.umami_domain.split('.')[0] if args.umami_domain else 'Website'} เป็นผู้ควบคุมข้อมูลส่วนบุคคลของคุณ
ติดต่อ: [ข้อมูลการติดต่อของคุณ]

2. ข้อมูลที่เก็บรวบรวม

เราเก็บรวบรวมข้อมูลส่วนบุคคลประเภทต่อไปนี้:

  • ข้อมูลการติดต่อ (ชื่อ, อีเมล, เบอร์โทรศัพท์) เมื่อคุณติดต่อเรา
  • ข้อมูลการใช้งาน (จำนวนการดูหน้า, ระยะเวลาเซสชัน) ผ่านการวิเคราะห์
  • การตั้งค่าคุกกี้และบันทึกการยินยอม
  • ข้อมูลทางเทคนิค (ที่อยู่ IP, ประเภทเบราว์เซอร์, ข้อมูลอุปกรณ์)

3. วัตถุประสงค์ในการประมวลผล

เราประมวลผลข้อมูลของคุณเพื่อวัตถุประสงค์ดังนี้:

  • เพื่อให้บริการและบำรุงรักษาบริการของเรา
  • เพื่อสื่อสารกับคุณเกี่ยวกับบริการของเรา
  • เพื่อปรับปรุงเว็บไซต์ผ่านการวิเคราะห์ (ด้วยความยินยอมของคุณ)
  • เพื่อปฏิบัติตามข้อผูกพันทางกฎหมาย

ฐานทางกฎหมาย: ความยินยอม ผลประโยชน์โดยชอบด้วยกฎหมาย ความจำเป็นตามสัญญา

4. ระยะเวลาเก็บรักษาข้อมูล

เราเก็บรักษาข้อมูลส่วนบุคคลตราบเท่าที่จำเป็นเพื่อวัตถุประสงค์ที่ระบุในนโยบายนี้ หรือตามที่กฎหมายกำหนด บันทึกการยินยอมถูกเก็บรักษาไว้ 10 ปีเพื่อปฏิบัติตามข้อกำหนด PDPA

5. การเปิดเผยข้อมูล

เราไม่ขายหรือให้เช่าข้อมูลส่วนบุคคลของคุณ เราอาจแบ่งปันข้อมูลกับ:

  • ผู้ให้บริการที่ประมวลผลข้อมูลในนามของเรา
  • หน่วยงานทางกฎหมายเมื่อ-required by law
  • ผู้ให้บริการการวิเคราะห์ (เฉพาะเมื่อคุณยินยอม)

6. คุกกี้และการติดตาม

เราใช้คุกกี้และเทคโนโลยีที่คล้ายคลึงกัน คุกกี้ที่จำเป็นจะทำงานเสมอ คุกกี้การวิเคราะห์และการตลาดต้องการความยินยอมที่ชัดเจนจากคุณ คุณสามารถจัดการการตั้งค่าได้ผ่านแบนเนอร์การยินยอมคุกกี้ของเรา

7. สิทธิ์ของคุณ (PDPA)

คุณมีสิทธิ์ดังต่อไปนี้ภายใต้ PDPA:

  • สิทธิ์ในการเข้าถึงข้อมูลส่วนบุคคลของคุณ
  • สิทธิ์ในการแก้ไขข้อมูลที่ไม่ถูกต้อง
  • สิทธิ์ในการลบข้อมูล (สิทธิ์ที่จะถูกลืม)
  • สิทธิ์ในการระงับการประมวลผล
  • สิทธิ์ในการพกพาข้อมูล
  • สิทธิ์ในการคัดค้านการประมวลผล
  • สิทธิ์ในการถอนความยินยอมเมื่อใดก็ได้

8. ความปลอดภัยของข้อมูล

เราใช้มาตรการรักษาความปลอดภัยที่เหมาะสมทั้งทางเทคนิคและองค์กร เพื่อปกป้องข้อมูลส่วนบุคคลของคุณจากการเข้าถึง การแก้ไข การเปิดเผย หรือการทำลายโดยไม่ได้รับอนุญาต

9. การโอนข้อมูลข้ามประเทศ

[ถ้ามี] ข้อมูลของคุณอาจถูกโอนไปยังและประมวลผลในประเทศอื่นๆ นอกเหนือจากประเทศไทย เรารับรองว่ามีมาตรการคุ้มครองที่เหมาะสมสำหรับการโอนดังกล่าว

10. การติดต่อและข้อร้องเรียน

สำหรับคำถามใดๆ หรือเพื่อใช้สิทธิ์ของคุณ ติดต่อเราได้ที่: [อีเมลติดต่อของคุณ]

คุณยังมีสิทธิ์ในการยื่นข้อร้องเรียนต่อคณะกรรมการคุ้มครองข้อมูลส่วนบุคคล (PDPC)

11. การอัปเดตนโยบาย

เราอาจอัปเดตนโยบายนี้เป็นครั้งคราว เราจะแจ้งให้คุณทราบถึงการเปลี่ยนแปลงใดๆ โดยการลงประกาศนโยบายใหม่บนหน้านี้และอัปเดตเลขฉบับที่

For the English version of this policy, please see: Privacy Policy

""" else: return f"""--- import BaseLayout from '../../layouts/BaseLayout.astro'; const lastUpdated = new Date().toLocaleDateString('en-US', {{ year: 'numeric', month: 'long', day: 'numeric' }}); const version = '1.0.0'; ---

Privacy Policy

Version {{version}} | Last updated: {{lastUpdated}}

1. Data Controller

{args.umami_domain.split('.')[0] if args.umami_domain else 'Website'} is the data controller responsible for your personal data.
Contact: [Your contact information]

2. Data We Collect

We collect the following types of personal data:

  • Contact information (name, email, phone number) when you contact us
  • Usage data (page views, session duration) via analytics
  • Cookie preferences and consent records
  • Technical data (IP address, browser type, device information)

3. Purpose of Processing

We process your data for the following purposes:

  • To provide and maintain our services
  • To communicate with you about our services
  • To improve our website through analytics (with your consent)
  • To comply with legal obligations

Legal Basis: Consent, legitimate interest, contractual necessity

4. Data Retention

We retain personal data for as long as necessary to fulfill the purposes outlined in this policy, or as required by law. Consent records are retained for 10 years to comply with PDPA requirements.

5. Data Sharing & Disclosure

We do not sell or rent your personal data. We may share data with:

  • Service providers who process data on our behalf
  • Legal authorities when required by law
  • Analytics providers (only with your consent)

6. Cookies & Tracking

We use cookies and similar technologies. Essential cookies are always active. Analytics and marketing cookies require your explicit consent. You can manage your preferences through our cookie consent banner.

7. Your Rights (PDPA)

You have the following rights under PDPA:

  • Right to access your personal data
  • Right to rectification (correction) of inaccurate data
  • Right to erasure (right to be forgotten)
  • Right to restrict processing
  • Right to data portability
  • Right to object to processing
  • Right to withdraw consent at any time

8. Data Security

We implement appropriate technical and organizational measures to protect your personal data against unauthorized access, alteration, disclosure, or destruction.

9. Cross-Border Transfers

[If applicable] Your data may be transferred to and processed in countries other than Thailand. We ensure appropriate safeguards are in place for such transfers.

10. Contact & Complaints

For any questions or to exercise your rights, contact us at: [Your contact email]

You also have the right to lodge a complaint with the Personal Data Protection Committee (PDPC).

11. Policy Updates

We may update this policy from time to time. We will notify you of any changes by posting the new policy on this page and updating the version number.

สำหรับเวอร์ชันภาษาไทย โปรดดูที่: นโยบายความเป็นส่วนตัว

""" def get_terms_template(lang): """Generate Terms & Conditions template.""" if lang == 'th': return """--- import BaseLayout from '../../layouts/BaseLayout.astro'; const lastUpdated = new Date().toLocaleDateString('th-TH', { year: 'numeric', month: 'long', day: 'numeric' }); ---

ข้อกำหนดและเงื่อนไข

อัปเดตล่าสุด: {lastUpdated}

1. การยอมรับเงื่อนไข

ด้วยการเข้าถึงและใช้เว็บไซต์นี้ คุณยอมรับและตกลงที่จะถูกผูกพันด้วยข้อกำหนด และบทบัญญัติของข้อตกลงนี้

2. บริการ

เว็บไซต์ให้บริการ [ระบุบริการของคุณ] รายละเอียดบริการที่สมบูรณ์จะแจ้งให้ทราบแยกต่างหากเมื่อมีการใช้บริการ

3. ทรัพย์สินทางปัญญา

เนื้อหาทั้งหมดบนเว็บไซต์นี้ รวมถึงข้อความ กราฟิก โลโก้ และซอฟต์แวร์ เป็นทรัพย์สินของเว็บไซต์และอยู่ภายใต้การคุ้มครองกฎหมายลิขสิทธิ์ของไทยและสากล

4. หน้าที่ของผู้ใช้

คุณตกลงที่จะใช้เว็บไซต์นี้เพื่อวัตถุประสงค์ที่ถูกต้องตามกฎหมายเท่านั้น และในวิธีที่ไม่ละเมิดสิทธิ์ จำกัด หรือยับยั้งการใช้งานของผู้อื่น

5. การจำกัดความรับผิด

เว็บไซต์จะไม่รับผิดต่อความเสียหายทางอ้อม โดยบังเอิญ เฉพาะเรื่อง หรือเชิงลงโทษ อันเกิดจากการใช้หรือไม่สามารถใช้เว็บไซต์นี้

6. กฎหมายที่ใช้บังคับ

ข้อกำหนดเหล่านี้จะอยู่ภายใต้การตีความและบังคับตามกฎหมายของประเทศไทย โดยไม่คำนึงถึงหลักการขัดกันแห่งกฎหมาย

7. การระงับข้อพิพาท

ข้อพิพาทใดๆ ที่เกิดจากข้อกำหนดนี้จะได้รับการแก้ไขผ่านการเจรจาต่อรองด้วยดี หากไม่สำเร็จ ข้อพิพาทจะถูกยื่นต่อศาลที่มีเขตอำนาจในประเทศไทย

8. การแก้ไข

เราขอสงวนสิทธิ์ในการแก้ไขข้อกำหนดเหล่านี้ได้ตลอดเวลา การใช้เว็บไซต์ต่อไปหลังจากมีการเปลี่ยนแปลงถือเป็นการยอมรับการเปลี่ยนแปลง tersebut

9. ข้อมูลการติดต่อ

สำหรับคำถามเกี่ยวกับข้อกำหนดเหล่านี้ โปรดติดต่อเราที่: [อีเมลติดต่อของคุณ]

For the English version of these terms, please see: Terms and Conditions

""" else: return """--- import BaseLayout from '../../layouts/BaseLayout.astro'; const lastUpdated = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }); ---

Terms and Conditions

Last updated: {lastUpdated}

1. Acceptance of Terms

By accessing and using this website, you accept and agree to be bound by the terms and provision of this agreement.

2. Services

Website provides [describe your services]. Detailed service terms are provided separately upon engagement.

3. Intellectual Property

All content on this website, including text, graphics, logos, and software, is the property of Website and is protected by Thai and international copyright laws.

4. User Obligations

You agree to use this website only for lawful purposes and in a way that does not infringe the rights of, restrict or inhibit anyone else's use of the website.

5. Limitation of Liability

Website shall not be liable for any indirect, incidental, special, consequential or punitive damages resulting from your use of or inability to use this website.

6. Governing Law

These terms shall be governed by and construed in accordance with the laws of Thailand, without regard to its conflict of law provisions.

7. Dispute Resolution

Any disputes arising from these terms shall be resolved through good faith negotiations. If unsuccessful, disputes shall be submitted to the competent courts of Thailand.

8. Modifications

We reserve the right to modify these terms at any time. Continued use of the website following any changes constitutes acceptance of those changes.

9. Contact Information

For questions about these terms, please contact us at: [Your contact email]

สำหรับเวอร์ชันภาษาไทย โปรดดูที่: ข้อกำหนดและเงื่อนไข

""" def get_cookie_banner_template(): """Get cookie banner component template.""" return """--- const siteName = "Website"; --- """ def get_admin_dashboard_template(): """Get admin dashboard template.""" return """--- import { db, ConsentLog, desc } from 'astro:db'; const ADMIN_PASSWORD = Astro.env.ADMIN_PASSWORD || 'changeme'; let logs = []; let isAuthenticated = false; if (Astro.request.method === 'POST') { const formData = await Astro.request.formData(); const password = formData.get('password'); if (password === ADMIN_PASSWORD) { isAuthenticated = true; logs = await db.select().from(ConsentLog).orderBy(desc(ConsentLog.timestamp)).limit(100); } } --- Consent Logs Admin

🔐 Consent Logs Admin

{!isAuthenticated ? (

Admin Login

) : (
Refresh {logs.map(log => ( ))}
DateLocaleSession IDEssentialAnalyticsMarketingPolicy Ver
{new Date(log.timestamp).toLocaleString()} {log.locale} {log.sessionId} {log.essential ? '✓' : '✗'} {log.analytics ? '✓' : '✗'} {log.marketing ? '✓' : '✗'} {log.policyVersion}
)}
""" def get_db_config_template(): """Get database configuration template.""" return """import { defineDb, defineTable, column } from 'astro:db'; const ConsentLog = defineTable({ columns: { id: column.number({ primaryKey: true }), sessionId: column.text({ unique: true }), timestamp: column.date(), locale: column.text(), essential: column.boolean(), analytics: column.boolean(), marketing: column.boolean(), policyVersion: column.text(), ipHash: column.text(), userAgent: column.text(), }, }); export default defineDb({ tables: { ConsentLog }, }); """ def get_db_seed_template(): """Get database seed template.""" return """import { db, ConsentLog } from 'astro:db'; export default async function seed() { await db.insert(ConsentLog).values([ { sessionId: 'dev-session-001', timestamp: new Date(), locale: 'en', essential: true, analytics: true, marketing: false, policyVersion: '1.0.0', ipHash: 'dev1234567890abcd', userAgent: 'Mozilla/5.0 (development)', }, ]); } """ def get_i18n_lib_template(languages): """Get i18n utilities template.""" lang_entries = [] for lang in languages: if lang == 'th': lang_entries.append(" th: { name: 'ไทย', locale: 'th' },") else: lang_entries.append(" en: { name: 'English', locale: 'en' },") return f"""export const languages = {{ {chr(10).join(lang_entries)} }}; export const defaultLocale = 'en'; export function getLanguageFromLocale(locale: string) {{ return languages[locale as keyof typeof languages] || languages.en; }} """ def create_api_endpoints(output_dir): """Create API endpoint files.""" api_dir = output_dir / 'src' / 'pages' / 'api' / 'consent' api_dir.mkdir(parents=True, exist_ok=True) # POST endpoint post_endpoint = """import type { APIRoute } from 'astro'; import { db, ConsentLog } from 'astro:db'; import { createHash } from 'crypto'; export const POST: APIRoute = async ({ request }) => { try { const data = await request.json(); const { sessionId, locale, essential, analytics, marketing, policyVersion } = data; if (!sessionId || !locale) { return new Response( JSON.stringify({ error: 'Missing required fields' }), { status: 400, headers: { 'Content-Type': 'application/json' } } ); } const ip = request.headers.get('x-forwarded-for') || 'unknown'; const ipHash = createHash('sha256').update(ip).digest('hex').substring(0, 16); await db.insert(ConsentLog).values({ sessionId, timestamp: new Date(), locale, essential: essential || false, analytics: analytics || false, marketing: marketing || false, policyVersion, ipHash, userAgent: request.headers.get('user-agent') || '', }); return new Response( JSON.stringify({ success: true, sessionId }), { status: 201, headers: { 'Content-Type': 'application/json' } } ); } catch (error) { console.error('Consent logging error:', error); return new Response( JSON.stringify({ error: 'Failed to log consent' }), { status: 500, headers: { 'Content-Type': 'application/json' } } ); } }; """ (api_dir / 'POST.ts').write_text(post_endpoint) # GET endpoint get_endpoint = """import type { APIRoute } from 'astro'; import { db, ConsentLog, desc } from 'astro:db'; export const GET: APIRoute = async () => { try { const logs = await db.select().from(ConsentLog).orderBy(desc(ConsentLog.timestamp)).limit(100); return new Response( JSON.stringify({ success: true, logs }), { status: 200, headers: { 'Content-Type': 'application/json' } } ); } catch (error) { console.error('Consent fetch error:', error); return new Response( JSON.stringify({ error: 'Failed to fetch consent logs' }), { status: 500, headers: { 'Content-Type': 'application/json' } } ); } }; """ (api_dir / 'GET.ts').write_text(get_endpoint) # DELETE endpoint delete_dir = api_dir / '[sessionId]' delete_dir.mkdir(parents=True, exist_ok=True) delete_endpoint = """import type { APIRoute } from 'astro'; import { db, ConsentLog, eq } from 'astro:db'; export const DELETE: APIRoute = async ({ params }) => { try { const { sessionId } = params; if (!sessionId) { return new Response( JSON.stringify({ error: 'Session ID required' }), { status: 400, headers: { 'Content-Type': 'application/json' } } ); } const result = await db.delete(ConsentLog).where( eq(ConsentLog.sessionId, sessionId) ); return new Response( JSON.stringify({ success: true, deleted: result.changes > 0 }), { status: 200, headers: { 'Content-Type': 'application/json' } } ); } catch (error) { console.error('Consent deletion error:', error); return new Response( JSON.stringify({ error: 'Failed to delete consent' }), { status: 500, headers: { 'Content-Type': 'application/json' } } ); } }; """ (delete_dir / 'DELETE.ts').write_text(delete_endpoint) if __name__ == "__main__": main()