fix: make db-reset work in non-interactive mode
- Add FORCE_RESET environment variable to skip confirmation prompt - Update Makefile db-reset target to use FORCE_RESET=true - Handle EOFError gracefully with helpful message - Fix duplicate translation creation in seed script - Check for existing translations before inserting 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
17
Makefile
17
Makefile
@@ -14,6 +14,9 @@ endif
|
||||
PYTHON := python
|
||||
PIP := pip
|
||||
|
||||
# Set PYTHONPATH for scripts
|
||||
export PYTHONPATH := $(shell pwd)
|
||||
|
||||
# =============================================================================
|
||||
# INSTALLATION & SETUP
|
||||
# =============================================================================
|
||||
@@ -141,7 +144,19 @@ db-setup: migrate-up init-prod seed-demo
|
||||
@echo "✅ Database setup complete!"
|
||||
@echo "✨ Run 'make dev' to start development server"
|
||||
|
||||
db-reset: migrate-down migrate-up init-prod seed-demo-reset
|
||||
db-reset:
|
||||
@echo "⚠️ WARNING: This will DELETE ALL existing data!"
|
||||
ifeq ($(DETECTED_OS),Windows)
|
||||
@set SEED_MODE=reset&& set FORCE_RESET=true&& $(PYTHON) -m alembic downgrade -1
|
||||
@set SEED_MODE=reset&& set FORCE_RESET=true&& $(PYTHON) -m alembic upgrade head
|
||||
@set SEED_MODE=reset&& set FORCE_RESET=true&& $(PYTHON) scripts/init_production.py
|
||||
@set SEED_MODE=reset&& set FORCE_RESET=true&& $(PYTHON) scripts/seed_demo.py
|
||||
else
|
||||
$(PYTHON) -m alembic downgrade -1
|
||||
$(PYTHON) -m alembic upgrade head
|
||||
$(PYTHON) scripts/init_production.py
|
||||
SEED_MODE=reset FORCE_RESET=true $(PYTHON) scripts/seed_demo.py
|
||||
endif
|
||||
@echo ""
|
||||
@echo "✅ Database completely reset!"
|
||||
|
||||
|
||||
@@ -21,10 +21,16 @@ Usage:
|
||||
make seed-demo # Normal demo seeding
|
||||
make seed-demo-minimal # Minimal seeding (1 vendor only)
|
||||
make seed-demo-reset # Delete all data and reseed (DANGEROUS!)
|
||||
make db-reset # Full reset (migrate down/up + init + seed reset)
|
||||
|
||||
Environment Variables:
|
||||
SEED_MODE=normal|minimal|reset - Seeding mode (default: normal)
|
||||
FORCE_RESET=true - Skip confirmation in reset mode (for non-interactive use)
|
||||
|
||||
This script is idempotent when run normally.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from datetime import UTC, datetime
|
||||
from decimal import Decimal
|
||||
@@ -51,6 +57,7 @@ from models.database.company import Company
|
||||
from models.database.customer import Customer, CustomerAddress
|
||||
from models.database.marketplace_import_job import MarketplaceImportJob
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
from models.database.marketplace_product_translation import MarketplaceProductTranslation
|
||||
from models.database.order import Order, OrderItem
|
||||
from models.database.product import Product
|
||||
from models.database.user import User
|
||||
@@ -59,6 +66,7 @@ from models.database.vendor_domain import VendorDomain
|
||||
from models.database.vendor_theme import VendorTheme
|
||||
|
||||
SEED_MODE = os.getenv("SEED_MODE", "normal") # normal, minimal, reset
|
||||
FORCE_RESET = os.getenv("FORCE_RESET", "false").lower() in ("true", "1", "yes")
|
||||
|
||||
# =============================================================================
|
||||
# DEMO DATA CONFIGURATION
|
||||
@@ -243,11 +251,20 @@ def reset_all_data(db: Session):
|
||||
print(" This will delete all vendors, customers, orders, etc.")
|
||||
print(" Admin user will be preserved.")
|
||||
|
||||
# Get confirmation
|
||||
response = input("\n Type 'DELETE ALL DATA' to confirm: ")
|
||||
if response != "DELETE ALL DATA":
|
||||
print(" Reset cancelled.")
|
||||
sys.exit(0)
|
||||
# Skip confirmation if FORCE_RESET is set (for non-interactive use)
|
||||
if FORCE_RESET:
|
||||
print_warning("FORCE_RESET enabled - skipping confirmation")
|
||||
else:
|
||||
# Get confirmation interactively
|
||||
try:
|
||||
response = input("\n Type 'DELETE ALL DATA' to confirm: ")
|
||||
if response != "DELETE ALL DATA":
|
||||
print(" Reset cancelled.")
|
||||
sys.exit(0)
|
||||
except EOFError:
|
||||
print_error("No interactive terminal available.")
|
||||
print(" Use FORCE_RESET=true to skip confirmation in non-interactive mode.")
|
||||
sys.exit(1)
|
||||
|
||||
# Delete in correct order (respecting foreign keys)
|
||||
tables_to_clear = [
|
||||
@@ -514,10 +531,10 @@ def create_demo_products(db: Session, vendor: Vendor, count: int) -> list[Produc
|
||||
marketplace_product_id = f"{vendor.vendor_code}-MP-{i:04d}"
|
||||
product_id = f"{vendor.vendor_code}-PROD-{i:03d}"
|
||||
|
||||
# Check if this product already exists
|
||||
# Check if this product already exists (by vendor_sku)
|
||||
existing_product = (
|
||||
db.query(Product)
|
||||
.filter(Product.vendor_id == vendor.id, Product.product_id == product_id)
|
||||
.filter(Product.vendor_id == vendor.id, Product.vendor_sku == product_id)
|
||||
.first()
|
||||
)
|
||||
|
||||
@@ -538,9 +555,7 @@ def create_demo_products(db: Session, vendor: Vendor, count: int) -> list[Produc
|
||||
# Create the MarketplaceProduct (base product data)
|
||||
marketplace_product = MarketplaceProduct(
|
||||
marketplace_product_id=marketplace_product_id,
|
||||
title=f"Sample Product {i} - {vendor.name}",
|
||||
description=f"This is a demo product for testing purposes in {vendor.name}. High quality and affordable.",
|
||||
link=f"https://{vendor.subdomain}.example.com/products/sample-{i}",
|
||||
source_url=f"https://{vendor.subdomain}.example.com/products/sample-{i}",
|
||||
image_link=f"https://{vendor.subdomain}.example.com/images/product-{i}.jpg",
|
||||
price=str(Decimal(f"{(i * 10) % 500 + 9.99}")), # Store as string
|
||||
brand=vendor.name,
|
||||
@@ -557,19 +572,34 @@ def create_demo_products(db: Session, vendor: Vendor, count: int) -> list[Produc
|
||||
db.add(marketplace_product)
|
||||
db.flush() # Flush to get the marketplace_product.id
|
||||
|
||||
# Check if English translation already exists
|
||||
existing_translation = (
|
||||
db.query(MarketplaceProductTranslation)
|
||||
.filter(
|
||||
MarketplaceProductTranslation.marketplace_product_id
|
||||
== marketplace_product.id,
|
||||
MarketplaceProductTranslation.language == "en",
|
||||
)
|
||||
.first()
|
||||
)
|
||||
|
||||
if not existing_translation:
|
||||
# Create English translation
|
||||
translation = MarketplaceProductTranslation(
|
||||
marketplace_product_id=marketplace_product.id,
|
||||
language="en",
|
||||
title=f"Sample Product {i} - {vendor.name}",
|
||||
description=f"This is a demo product for testing purposes in {vendor.name}. High quality and affordable.",
|
||||
)
|
||||
db.add(translation)
|
||||
|
||||
# Create the Product (vendor-specific entry)
|
||||
product = Product(
|
||||
vendor_id=vendor.id,
|
||||
marketplace_product_id=marketplace_product.id,
|
||||
product_id=product_id,
|
||||
vendor_sku=product_id, # Use vendor_sku for vendor's internal product reference
|
||||
price=float(Decimal(f"{(i * 10) % 500 + 9.99}")), # Store as float
|
||||
availability="in stock",
|
||||
condition="new",
|
||||
currency="EUR",
|
||||
is_active=True,
|
||||
is_featured=(i % 5 == 0), # Every 5th product is featured
|
||||
display_order=i,
|
||||
min_quantity=1,
|
||||
created_at=datetime.now(UTC),
|
||||
updated_at=datetime.now(UTC),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user