#!/usr/bin/env python3 """ Website Creator - Generate PDPA-compliant Astro websites Creates complete Astro projects with: - Bilingual support (Thai/English) - Umami Analytics integration (auto-create) - GA4 Analytics support (existing or new) - Google Search Console setup - Cookie consent management - Consent logging database (Astro DB) - PDPA-compliant legal pages - Easypanel deployment Usage: python3 create_astro_website.py \ --name "Deal Plus Tech" \ --type "corporate" \ --languages "th,en" \ --output "./dealplustech-website" """ import os import sys import argparse import shutil import subprocess from pathlib import Path from datetime import datetime # ============================================================================ # INTERACTIVE SETUP FUNCTIONS # ============================================================================ def ask_analytics_setup(): """ Interactive analytics setup workflow Returns: dict: Analytics configuration """ print("\n" + "=" * 60) print("šŸ“Š ANALYTICS SETUP") print("=" * 60) config = { 'search_console': None, 'analytics_type': None, # 'umami' or 'ga4' 'umami_auto_create': False, 'umami_website_id': None, 'ga4_property_id': None, 'ga4_credentials_path': None, 'ga4_existing': False } # Step 1: Google Search Console (for all websites) print("\n1ļøāƒ£ Google Search Console Setup") print(" GSC is recommended for all websites for SEO monitoring.") gsc_choice = input("\n Do you want to setup Google Search Console? (y/n): ").strip().lower() if gsc_choice == 'y': print("\n GSC Setup Options:") print(" 1. I'll add it manually later (skip for now)") print(" 2. I have service account credentials file") gsc_method = input("\n Choose option (1-2): ").strip() if gsc_method == '2': gsc_path = input(" Enter path to GSC credentials file: ").strip() if os.path.exists(gsc_path): config['search_console'] = { 'credentials_path': gsc_path, 'setup_later': False } print(" āœ“ GSC credentials loaded") else: print(" ⚠ File not found, will setup later") config['search_console'] = {'setup_later': True} else: config['search_console'] = {'setup_later': True} print(" āœ“ Will setup later") else: print(" ā­ļø Skipping GSC setup") # Step 2: Choose Analytics Type (Umami OR GA4) print("\n2ļøāƒ£ Analytics Platform") print(" Choose ONE analytics platform:") print(" 1. Umami Analytics (recommended for most users)") print(" - Privacy-focused, self-hosted") print(" - Simple setup, auto-created") print(" - Good for most websites") print("\n 2. Google Analytics 4 (for advanced users)") print(" - Full-featured analytics") print(" - Requires Google account") print(" - Good for existing GA4 users") analytics_choice = input("\n Choose analytics (1-2): ").strip() if analytics_choice == '1': # Umami setup config['analytics_type'] = 'umami' print("\n šŸ“ˆ Umami Analytics Setup") # Check if Umami credentials are configured from dotenv import load_dotenv load_dotenv(os.path.join(os.path.dirname(__file__), '../../../.env')) umami_url = os.getenv('UMAMI_URL', '') umami_username = os.getenv('UMAMI_USERNAME', '') umami_password = os.getenv('UMAMI_PASSWORD', '') if umami_url and umami_username and umami_password: print(" āœ“ Umami credentials found in .env") print(" āœ“ Will auto-create Umami website for this project") config['umami_auto_create'] = True else: print(" ⚠ Umami credentials not configured in .env") print(" ā­ļø Skipping Umami setup (can add manually later)") elif analytics_choice == '2': # GA4 setup config['analytics_type'] = 'ga4' print("\n šŸ” Google Analytics 4 Setup") print(" 1. Create new GA4 property (auto-setup)") print(" 2. Use existing GA4 property (manual setup)") ga4_choice = input("\n Choose option (1-2): ").strip() if ga4_choice == '1': print("\n ⚠ Auto-creating GA4 properties requires API setup.") print(" ā­ļø Will provide instructions for manual setup") config['ga4_existing'] = False else: print("\n Please provide your existing GA4 details:") # Check unified .env for GA4 credentials from dotenv import load_dotenv load_dotenv(os.path.join(os.path.dirname(__file__), '../../../.env')) ga4_property_id = os.getenv('GA4_PROPERTY_ID', '') ga4_credentials_path = os.getenv('GA4_CREDENTIALS_PATH', '') if ga4_property_id: print(f" Found GA4 Property ID in .env: {ga4_property_id[:20]}...") use_global = input(" Use this for this project? (y/n): ").strip().lower() if use_global == 'y': config['ga4_property_id'] = ga4_property_id config['ga4_credentials_path'] = ga4_credentials_path print(" āœ“ Using global GA4 credentials") else: config['ga4_property_id'] = input(" Enter GA4 Property ID: ").strip() config['ga4_credentials_path'] = input(" Enter GA4 credentials file path: ").strip() else: config['ga4_property_id'] = input(" Enter GA4 Property ID (G-XXXXXXXXXX): ").strip() config['ga4_credentials_path'] = input(" Enter GA4 credentials file path: ").strip() config['ga4_existing'] = True else: print(" ā­ļø Skipping analytics setup") return config # ============================================================================ # TEMPLATES (abbreviated for brevity) # ============================================================================ ASTRO_CONFIG_TEMPLATE = """import {{ defineConfig }} from 'astro/config'; import tailwindcss from '@tailwindcss/vite'; import db from '@astrojs/db'; import sitemap from '@astrojs/sitemap'; export default defineConfig({{ site: '{site_url}', output: 'hybrid', i18n: {{ locales: [{locales}], defaultLocale: '{default_locale}', routing: {{ prefixDefaultLocale: false, fallbackType: 'rewrite', }}, fallback: {{ th: 'en', }}, }}, integrations: [ tailwindcss(), db(), sitemap({{ i18n: {{ defaultLocale: '{default_locale}', }}, }}), ], }}); """ PACKAGE_JSON_TEMPLATE = """{{ "name": "{name}", "type": "module", "version": "1.0.0", "scripts": {{ "dev": "astro dev", "build": "astro build --remote", "preview": "astro preview", "astro": "astro", "db:push": "astro db push --remote", "db:seed": "astro db seed" }}, "dependencies": {{ "astro": "^5.17.1", "@astrojs/db": "^0.14.0", "@astrojs/sitemap": "^3.2.0", "@tailwindcss/vite": "^4.2.1", "tailwindcss": "^4.2.1", "astro-consent": "^1.0.0", "drizzle-orm": "^0.38.0", "@libsql/client": "^0.14.0" }} }} """ # ... (rest of templates remain the same) # ============================================================================ # MAIN FUNCTION # ============================================================================ def main(): """Main entry point.""" parser = argparse.ArgumentParser(description='Create PDPA-compliant Astro website') parser.add_argument('--name', required=True, help='Website name') parser.add_argument('--type', default='corporate', choices=['corporate', 'portfolio', 'landing', 'blog', 'ecommerce'], help='Website type') parser.add_argument('--languages', default='th,en', help='Languages (comma-separated): th, en') parser.add_argument('--primary-color', default='#2563eb', help='Primary color (hex)') parser.add_argument('--secondary-color', default='#1e40af', help='Secondary color (hex)') parser.add_argument('--features', default='blog,contact', help='Features (comma-separated): blog, products, contact, portfolio') 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 for consent logs') parser.add_argument('--output', '-o', default='.', help='Output directory') parser.add_argument('--no-interactive', action='store_true', help='Skip interactive setup (use defaults)') args = parser.parse_args() # Load unified credentials from dotenv import load_dotenv load_dotenv(os.path.join(os.path.dirname(__file__), '../../../.env')) # Get Umami credentials for auto-setup args.umami_url = os.getenv('UMAMI_URL', '') args.umami_username = os.getenv('UMAMI_USERNAME', '') args.umami_password = os.getenv('UMAMI_PASSWORD', '') args.auto_setup_umami = bool(args.umami_url and args.umami_username and args.umami_password) languages = [lang.strip() for lang in args.languages.split(',')] default_locale = 'en' if 'en' in languages else languages[0] features = [f.strip() for f in args.features.split(',')] print(f"Creating website: {args.name}") print(f"Type: {args.type}") print(f"Languages: {languages}") print(f"Features: {features}") print(f"Output: {args.output}") # Interactive analytics setup (if not in no-interactive mode) analytics_config = None if not args.no_interactive: analytics_config = ask_analytics_setup() # Create project structure create_project(args, languages, default_locale, features) # Save analytics configuration to project if analytics_config: save_analytics_config(args.output, analytics_config) # Auto-setup Umami if credentials provided umami_website_id = args.umami_id if args.auto_setup_umami and (not analytics_config or analytics_config.get('analytics_type') == 'umami'): print("\nšŸ“ˆ Setting up Umami Analytics...") try: from umami_integration import setup_umami_for_website website_domain = args.name.lower().replace(' ', '-') + '.moreminimore.com' success, result = setup_umami_for_website( args.umami_url, args.umami_username, args.umami_password, args.name, website_domain, args.output ) if success: umami_website_id = result['website_id'] print(f" āœ“ Umami website created: {umami_website_id}") else: print(f" ⚠ Umami setup skipped: {result.get('error', 'Unknown error')}") except Exception as e: print(f" ⚠ Umami setup failed: {e}") print(" Continuing without Umami...") print(f"\nāœ… Website created successfully at: {args.output}") # Update .env with Umami ID if auto-setup env_file = os.path.join(args.output, '.env') if os.path.exists(env_file) and umami_website_id: with open(env_file, 'a', encoding='utf-8') as f: f.write(f'\n# Umami Analytics (auto-configured)\n') f.write(f'UMAMI_WEBSITE_ID={umami_website_id}\n') print(f" āœ“ Umami ID added to .env") print("\nNext steps:") print(f" 1. cd {args.output}") print(" 2. npm install") print(" 3. Update .env with your credentials") print(" 4. npm run dev") # Auto-deploy (always on) print("") print("=" * 60) print("šŸš€ AUTO-DEPLOY STARTING") print("=" * 60) print("") # Step 1: Sync to Gitea print("šŸ“¦ Step 1/3: Syncing to Gitea...") git_url = sync_to_gitea(args.output, args.name) # Step 2: Deploy to Easypanel print("") print("šŸš€ Step 2/3: Deploying to Easypanel...") deployment_url = deploy_to_easypanel(args.output, args.name, git_url) # Step 3: Verify and monitor print("") print("šŸ“Š Step 3/3: Monitoring deployment...") monitor_deployment(args.name) # Final output print("") print("=" * 60) print("āœ… COMPLETE!") print("=" * 60) print("") print(f"šŸ“ Website generated: {args.output}") print(f"🌐 Gitea Repository: {git_url.replace('.git', '')}") print(f"šŸš€ Easypanel Deployment: {deployment_url}") print("") print("šŸ“‹ Next steps:") print(f" 1. Website is deploying to: {deployment_url}") print(f" 2. Check status at: https://panelwebsite.moreminimore.com") print(f" 3. Edit Umami config: cd {args.output} && nano .env") print("") def save_analytics_config(output_path: str, config: dict): """Save analytics configuration to project context""" context_dir = os.path.join(output_path, 'context') os.makedirs(context_dir, exist_ok=True) # Save data-services.json data_services = { 'ga4': { 'enabled': config.get('analytics_type') == 'ga4', 'property_id': config.get('ga4_property_id', ''), 'credentials_path': config.get('ga4_credentials_path', '') } if config.get('analytics_type') == 'ga4' else {'enabled': False}, 'gsc': { 'enabled': config.get('search_console') is not None, 'site_url': '', 'credentials_path': config.get('search_console', {}).get('credentials_path', '') }, 'umami': { 'enabled': config.get('analytics_type') == 'umami', 'api_url': os.getenv('UMAMI_URL', ''), 'website_id': config.get('umami_website_id', '') } if config.get('analytics_type') == 'umami' else {'enabled': False}, 'dataforseo': {'enabled': False} } with open(os.path.join(context_dir, 'data-services.json'), 'w', encoding='utf-8') as f: json.dump(data_services, f, indent=2) print(f" āœ“ Analytics config saved to context/data-services.json") # ... (rest of functions remain the same - create_project, sync_to_gitea, etc.) if __name__ == '__main__': main()