Initial commit: AutonetSellCar platform with deployment system

- Frontend: Next.js 14 with TypeScript
- Backend: FastAPI with SQLAlchemy
- Agent: Carmodoo sync agent
- Deployment: Docker Compose based staging/production setup
- Scripts: Automated deployment with rollback support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
AutonetSellCar Deploy
2025-12-30 13:24:39 +09:00
commit 1f0dcb1ddb
224 changed files with 55119 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
"""
Database migration: Add pdf_path column to car_performance_checks table
Usage:
cd backend
python scripts/add_pdf_path_column.py
"""
import os
import sys
# Add parent directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from sqlalchemy import create_engine, text
from sqlalchemy.exc import OperationalError
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./autonetsellcar.db")
def add_pdf_path_column():
"""Add pdf_path column to car_performance_checks table"""
engine = create_engine(DATABASE_URL)
try:
with engine.connect() as conn:
# Check if column already exists
if "sqlite" in DATABASE_URL:
result = conn.execute(text("PRAGMA table_info(car_performance_checks)"))
columns = [row[1] for row in result.fetchall()]
if "pdf_path" in columns:
print("Column 'pdf_path' already exists. Nothing to do.")
return
else:
# PostgreSQL
result = conn.execute(text("""
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'car_performance_checks'
AND column_name = 'pdf_path'
"""))
if result.fetchone():
print("Column 'pdf_path' already exists. Nothing to do.")
return
# Add the column
print("Adding 'pdf_path' column to car_performance_checks table...")
conn.execute(text(
"ALTER TABLE car_performance_checks ADD COLUMN pdf_path VARCHAR(500)"
))
conn.commit()
print("Column 'pdf_path' added successfully!")
except OperationalError as e:
if "duplicate column" in str(e).lower() or "already exists" in str(e).lower():
print("Column 'pdf_path' already exists. Nothing to do.")
else:
raise
if __name__ == "__main__":
add_pdf_path_column()

View File

@@ -0,0 +1,154 @@
"""
Migration script: Generate PDFs for existing performance check records
This script:
1. Finds all CarPerformanceCheck records that have check_number but no pdf_path
2. Generates PDF for each using Playwright
3. Updates the pdf_path in the database
Usage:
cd backend
python scripts/migrate_performance_check_to_pdf.py
Requirements:
pip install playwright
playwright install chromium
"""
import asyncio
import sys
import os
# Add parent directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.database import Base
from app.models import CarPerformanceCheck
from app.services.pdf_service import capture_performance_check_pdf, PLAYWRIGHT_AVAILABLE
# Database connection
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./autonetsellcar.db")
async def migrate_performance_checks():
"""Migrate existing performance checks to PDF format"""
if not PLAYWRIGHT_AVAILABLE:
print("ERROR: Playwright is not installed. Please run:")
print(" pip install playwright")
print(" playwright install chromium")
return
# Create database session
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()
try:
# Find all performance checks without PDF
records = db.query(CarPerformanceCheck).filter(
CarPerformanceCheck.check_number.isnot(None),
CarPerformanceCheck.check_number != "",
(CarPerformanceCheck.pdf_path.is_(None)) | (CarPerformanceCheck.pdf_path == "")
).all()
total = len(records)
print(f"Found {total} performance check records to migrate")
if total == 0:
print("No records to migrate. Done.")
return
success_count = 0
error_count = 0
for i, record in enumerate(records, 1):
print(f"\n[{i}/{total}] Processing car_id={record.car_id}, check_number={record.check_number}")
try:
pdf_path = await capture_performance_check_pdf(
record.check_number,
record.car_id
)
if pdf_path:
record.pdf_path = pdf_path
db.commit()
print(f" SUCCESS: {pdf_path}")
success_count += 1
else:
print(f" FAILED: PDF generation returned None")
error_count += 1
except Exception as e:
print(f" ERROR: {e}")
error_count += 1
db.rollback()
# Small delay to avoid overwhelming the server
await asyncio.sleep(1)
print(f"\n{'='*50}")
print(f"Migration completed!")
print(f" Total: {total}")
print(f" Success: {success_count}")
print(f" Errors: {error_count}")
finally:
db.close()
async def check_status():
"""Check current migration status"""
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()
try:
total = db.query(CarPerformanceCheck).count()
with_check_num = db.query(CarPerformanceCheck).filter(
CarPerformanceCheck.check_number.isnot(None),
CarPerformanceCheck.check_number != ""
).count()
with_pdf = db.query(CarPerformanceCheck).filter(
CarPerformanceCheck.pdf_path.isnot(None),
CarPerformanceCheck.pdf_path != ""
).count()
print(f"Performance Check Status:")
print(f" Total records: {total}")
print(f" With check_number: {with_check_num}")
print(f" With PDF: {with_pdf}")
print(f" Pending migration: {with_check_num - with_pdf}")
finally:
db.close()
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Migrate performance checks to PDF")
parser.add_argument("--status", action="store_true", help="Show current status only")
parser.add_argument("--dry-run", action="store_true", help="Show what would be done without making changes")
args = parser.parse_args()
if args.status:
asyncio.run(check_status())
else:
print("Starting performance check PDF migration...")
print("This will generate PDFs for all existing performance check records.")
print()
if args.dry_run:
print("DRY RUN - No changes will be made")
asyncio.run(check_status())
else:
confirm = input("Continue? (y/n): ")
if confirm.lower() == 'y':
asyncio.run(migrate_performance_checks())
else:
print("Cancelled.")