Files
opencode-skill/skills/website-creator/scripts/migrate-tina.sh
Kunthawat Greethong 628298183a feat: migrate website-creator from Next.js+Payload to Astro+Tina CMS
Major changes:
- Replace Payload CMS with Tina CMS (self-hosted)
- Add Astro DB for consent logging (PDPA compliant)
- Update Tailwind v3 to v4 (@tailwindcss/vite plugin)
- Add astro-tina-starter template
- Rewrite consent template for Astro (ConsentBanner.astro, Astro DB, Nano Stores)
- Add install-tina-backend.sh for self-hosted Tina per customer
- Rename convert-astro.sh to migrate-tina.sh
- Add AGENTS.md template for generated websites
- Delete all Payload/Next.js files

Technical updates:
- Astro DB using defineDb with eq operators for queries
- Tailwind v4 with @theme block
- Tina CMS local development mode
- Proper Astro API routes for consent

Research-verified with official documentation (April 2026)
2026-04-17 14:52:59 +07:00

443 lines
11 KiB
Bash
Executable File

#!/usr/bin/env bash
#===============================================================================
# migrate-tina.sh - Migrate existing websites to Astro + Tina CMS
#
# Usage: ./migrate-tina.sh [source-path] [target-path]
#
# This script migrates websites to Astro + Tina CMS:
# - Converts content to Tina CMS format
# - Sets up Astro DB for consent logging
# - Adds PDPA-compliant consent system
# - Preserves content and structure
#
# Requirements:
# - node.js 20+
# - npm
# - git
#
#===============================================================================
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
SOURCE_PATH="${1:-}"
TARGET_PATH="${2:-.}"
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
print_usage() {
cat << EOF
Usage: $(basename "$0") [source-path] [target-path]
Migrate existing website to Astro + Tina CMS
Arguments:
source-path Path to existing website project
target-path Path for the migrated Astro + Tina project
Examples:
$(basename "$0") /path/to/existing-site /path/to/migrated-site
Features:
- Detects source website technology (Astro, Next.js, etc.)
- Converts content to Tina CMS format
- Sets up Astro DB for consent logging (PDPA compliant)
- Adds cookie consent banner with Thai law compliance
- Preserves SEO metadata and content structure
EOF
}
detect_source_type() {
log_info "Detecting source website type..."
cd "$SOURCE_PATH"
if [ -f "astro.config.mjs" ] || [ -f "astro.config.ts" ]; then
SOURCE_TYPE="astro"
log_success "Detected: Astro"
elif [ -f "next.config.js" ] || [ -f "next.config.mjs" ] || [ -f "package.json" ] && grep -q "next" package.json 2>/dev/null; then
SOURCE_TYPE="nextjs"
log_success "Detected: Next.js"
elif [ -f "package.json" ] && grep -q "remix" package.json 2>/dev/null; then
SOURCE_TYPE="remix"
log_success "Detected: Remix"
elif [ -d "src/content" ] || [ -d "content/posts" ]; then
SOURCE_TYPE="generic"
log_success "Detected: Generic static site"
else
log_warning "Could not detect source type, assuming generic"
SOURCE_TYPE="generic"
fi
}
analyze_source_content() {
log_info "Analyzing source content..."
cd "$SOURCE_PATH"
local md_count=$(find . -type f \( -name "*.md" -o -name "*.mdx" \) 2>/dev/null | grep -v node_modules | wc -l)
local astro_count=$(find . -type f -name "*.astro" 2>/dev/null | grep -v node_modules | wc -l)
local pages_count=$(find . -type f \( -name "*.tsx" -o -name "*.jsx" \) 2>/dev/null | grep -v node_modules | grep -E "pages/|app/" | wc -l)
echo ""
echo " Analysis Results:"
echo " ─────────────────"
echo " Markdown/MDX files: $md_count"
echo " Astro components: $astro_count"
echo " Pages (tsx/jsx): $pages_count"
echo ""
# List sample content files
if [ $md_count -gt 0 ]; then
echo " Sample content files:"
find . -type f \( -name "*.md" -o -name "*.mdx" \) 2>/dev/null | grep -v node_modules | head -5 | while read -r f; do
echo " - $f"
done
echo ""
fi
}
copy_template() {
log_info "Copying Astro+Tina template..."
local template_dir="$(dirname "$(dirname "$(readlink -f "$0")")")/templates/astro-tina-starter"
if [ ! -d "$template_dir" ]; then
log_error "Template not found: $template_dir"
exit 1
fi
cp -r "$template_dir"/* "$TARGET_PATH/"
cp -r "$template_dir"/.* "$TARGET_PATH/" 2>/dev/null || true
log_success "Template copied to: $TARGET_PATH"
}
migrate_content() {
log_info "Migrating content to Tina format..."
cd "$SOURCE_PATH"
# Detect content directory
local content_dir=""
if [ -d "src/content" ]; then
content_dir="src/content"
elif [ -d "content" ]; then
content_dir="content"
elif [ -d "content/posts" ]; then
content_dir="content/posts"
fi
if [ -z "$content_dir" ]; then
log_warning "No content directory found, creating default structure"
mkdir -p "$TARGET_PATH/src/content"
return
fi
# Create Tina content directory
mkdir -p "$TARGET_PATH/src/content"
# Copy markdown/mdx files
find "$content_dir" -type f \( -name "*.md" -o -name "*.mdx" \) 2>/dev/null | while read -r file; do
local relative_path="${file#$SOURCE_PATH/$content_dir/}"
local target_file="$TARGET_PATH/src/content/$relative_path"
mkdir -p "$(dirname "$target_file")"
cp "$file" "$target_file"
echo " Migrated: $relative_path"
done
log_success "Content migration complete"
}
add_consent_system() {
log_info "Adding PDPA-compliant consent system..."
local consent_template="$(dirname "$(dirname "$(readlink -f "$0")")")/templates/consent"
if [ ! -d "$consent_template" ]; then
log_warning "Consent template not found, skipping"
return
fi
# Copy consent files
cp -r "$consent_template"/* "$TARGET_PATH/src/components/consent/" 2>/dev/null || true
log_success "Consent system added"
}
create_tina_schema() {
log_info "Creating Tina CMS schema..."
cd "$TARGET_PATH"
# Ensure .tina directory exists
mkdir -p .tina
# Create or update schema
cat > .tina/schema.ts << 'EOF'
import { defineSchema, config } from 'tinacms'
// Your content collections
const schema = defineSchema({
collections: [
{
name: 'post',
label: 'Posts',
path: 'src/content/posts',
fields: [
{
type: 'string',
name: 'title',
label: 'Title',
required: true,
},
{
type: 'string',
name: 'slug',
label: 'Slug',
required: true,
},
{
type: 'datetime',
name: 'date',
label: 'Date',
},
{
type: 'string',
name: 'author',
label: 'Author',
},
{
type: 'string',
name: 'image',
label: 'Featured Image',
},
{
type: 'string',
name: 'description',
label: 'Description',
},
{
type: 'rich-text',
name: 'body',
label: 'Body',
isBody: true,
},
],
},
{
name: 'page',
label: 'Pages',
path: 'src/content/pages',
fields: [
{
type: 'string',
name: 'title',
label: 'Title',
required: true,
},
{
type: 'string',
name: 'slug',
label: 'Slug',
required: true,
},
{
type: 'rich-text',
name: 'body',
label: 'Body',
isBody: true,
},
],
},
],
})
export default config({
schema,
// Other config options
})
EOF
log_success "Tina schema created"
}
create_migration_report() {
log_info "Creating migration report..."
cd "$SOURCE_PATH"
local md_count=$(find . -type f \( -name "*.md" -o -name "*.mdx" \) 2>/dev/null | grep -v node_modules | wc -l)
cat > "$TARGET_PATH/MIGRATION_REPORT.md" << EOF
# Migration Report: → Astro + Tina CMS
## Source
- **Original Type:** $SOURCE_TYPE
- **Path:** $SOURCE_PATH
- **Date:** $(date)
## Statistics
- **Content Files Migrated:** $md_count
## What's Included
### ✅ Astro 6.1.7
Modern static site framework with excellent performance.
### ✅ Tina CMS
Self-hosted Git-based CMS for visual content editing.
### ✅ Tailwind CSS 4.x
Latest Tailwind with @tailwindcss/vite plugin.
### ✅ Astro DB
Built-in database for consent logging and dynamic content.
### ✅ PDPA Consent System
Thai Personal Data Protection Act compliant cookie consent:
- Cookie banner with Accept/Reject/Preferences
- Consent logging in Astro DB
- API endpoint for consent management
### ✅ Nano Stores
Lightweight client-side state management.
## Project Structure
\`\`\`
$TARGET_PATH/
├── src/
│ ├── components/
│ │ └── consent/ # PDPA consent system
│ ├── content/
│ │ ├── posts/ # Blog posts (Tina managed)
│ │ └── pages/ # Static pages (Tina managed)
│ ├── layouts/
│ │ └── Layout.astro
│ ├── pages/
│ │ └── index.astro
│ └── styles/
│ └── global.css
├── .tina/
│ └── schema.ts # Tina content schema
├── db/
│ └── config.ts # Astro DB config
├── Dockerfile
└── AGENTS.md # AI agent instructions
\`\`\`
## Next Steps
1. **Install dependencies:**
\`\`\`bash
cd $TARGET_PATH
npm install
\`\`\`
2. **Set up environment:**
\`\`\`bash
cp .env.example .env
# Edit .env with your settings
\`\`\`
3. **Start development:**
\`\`\`bash
npm run dev
\`\`\`
4. **Access Tina Admin:**
- Visit \`http://localhost:4321/admin\` (when in dev mode)
- Or \`http://localhost:4321/___tina\` for direct access
5. **Configure Tina Backend** (for production):
\`\`\`bash
./scripts/install-tina-backend.sh
\`\`\`
## Tina CMS Setup
For production, you'll need to set up the Tina backend:
\`\`\`bash
./scripts/install-tina-backend.sh
\`\`\`
This will install:
- Auth.js for authentication
- Database adapter for content storage
- Git provider for content management
## PDPA Compliance
The consent system logs:
- User consent choices (accept/reject)
- Cookie categories (analytics, marketing, functional)
- Timestamp and user agent
- IP address (for compliance auditing)
Logs are stored in Astro DB and can be exported for compliance reporting.
EOF
log_success "Migration report: $TARGET_PATH/MIGRATION_REPORT.md"
}
main() {
echo "=============================================="
echo " Website → Astro + Tina CMS Migration Tool"
echo "=============================================="
echo ""
if [ "$1" == "-h" ] || [ "$1" == "--help" ]; then
print_usage
exit 0
fi
if [ -z "$SOURCE_PATH" ]; then
print_usage
echo ""
log_error "Please specify source path"
exit 1
fi
if [ ! -d "$SOURCE_PATH" ]; then
log_error "Source path not found: $SOURCE_PATH"
exit 1
fi
if [ ! -d "$TARGET_PATH" ]; then
mkdir -p "$TARGET_PATH"
fi
detect_source_type
analyze_source_content
copy_template
migrate_content
add_consent_system
create_tina_schema
create_migration_report
echo ""
echo "=============================================="
log_success "Migration complete!"
echo "=============================================="
echo ""
echo "Next steps:"
echo " 1. cd $TARGET_PATH"
echo " 2. npm install"
echo " 3. npm run dev"
echo " 4. See MIGRATION_REPORT.md for details"
echo ""
}
main "$@"