diff --git a/Makefile b/Makefile index 02b53904..087b6b13 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ setup: install-all migrate-up init-prod # ============================================================================= dev: - $(PYTHON) -m uvicorn main:app --reload --host 0.0.0.0 --port 9999 + $(PYTHON) -m uvicorn main:app --reload --host 0.0.0.0 --port $(or $(API_PORT),8000) # ============================================================================= # DATABASE MIGRATIONS diff --git a/alembic/versions/a44f4956cfb1_merge_heads.py b/alembic/versions/a44f4956cfb1_merge_heads.py new file mode 100644 index 00000000..39094c60 --- /dev/null +++ b/alembic/versions/a44f4956cfb1_merge_heads.py @@ -0,0 +1,26 @@ +"""merge heads + +Revision ID: a44f4956cfb1 +Revises: z_store_domain_platform_id, tenancy_001 +Create Date: 2026-02-17 16:10:36.287976 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'a44f4956cfb1' +down_revision: Union[str, None] = ('z_store_domain_platform_id', 'tenancy_001') +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + pass + + +def downgrade() -> None: + pass diff --git a/scripts/seed/init_production.py b/scripts/seed/init_production.py index 6bba8f92..3b372435 100644 --- a/scripts/seed/init_production.py +++ b/scripts/seed/init_production.py @@ -176,7 +176,7 @@ def create_default_platforms(db: Session) -> list[Platform]: platform_defs = [ { "code": "oms", - "name": "Orion OMS", + "name": "OMS", "description": "Order Management System for multi-store e-commerce", "domain": "omsflow.lu", "path_prefix": "oms", @@ -187,7 +187,7 @@ def create_default_platforms(db: Session) -> list[Platform]: }, { "code": "main", - "name": "Orion", + "name": "Wizard", "description": "Main marketing site showcasing all Orion platforms", "domain": "wizard.lu", "path_prefix": None, @@ -198,7 +198,7 @@ def create_default_platforms(db: Session) -> list[Platform]: }, { "code": "loyalty", - "name": "Loyalty+", + "name": "Loyalty", "description": "Customer loyalty program platform for Luxembourg businesses", "domain": "rewardflow.lu", "path_prefix": "loyalty", @@ -488,17 +488,26 @@ def create_subscription_tiers(db: Session, platform: Platform) -> int: def create_platform_modules(db: Session, platforms: list[Platform]) -> int: """Create PlatformModule records for all platforms. - Enables all discovered modules for each platform so the app works - out of the box. Admins can disable optional modules later via the API. + Core modules are enabled for every platform. Optional modules are + selectively enabled per platform. All other modules are created but + disabled (available to toggle on later via the admin API). """ - from app.modules.registry import MODULES + from app.modules.registry import MODULES, is_core_module + + # Optional modules enabled per platform (core modules always enabled) + PLATFORM_MODULES = { + "oms": ["inventory", "catalog", "orders", "marketplace", "analytics", "cart", "checkout"], + "main": ["analytics", "monitoring", "dev-tools"], + "loyalty": ["loyalty"], + } now = datetime.now(UTC) records_created = 0 for platform in platforms: + enabled_extras = set(PLATFORM_MODULES.get(platform.code, [])) + for code in MODULES: - # Check if record already exists existing = db.execute( select(PlatformModule).where( PlatformModule.platform_id == platform.id, @@ -509,11 +518,12 @@ def create_platform_modules(db: Session, platforms: list[Platform]) -> int: if existing: continue + enabled = is_core_module(code) or code in enabled_extras pm = PlatformModule( platform_id=platform.id, module_code=code, - is_enabled=True, - enabled_at=now, + is_enabled=enabled, + enabled_at=now if enabled else None, config={}, ) db.add(pm) # noqa: PERF006 @@ -649,16 +659,38 @@ def print_summary(db: Session): print(f" Admin settings: {setting_count}") print(f" Sub. tiers: {tier_count}") + # Show platforms + platforms = db.query(Platform).order_by(Platform.code).all() + enabled_counts = {} + for pm in db.query(PlatformModule).filter(PlatformModule.is_enabled.is_(True)).all(): + enabled_counts[pm.platform_id] = enabled_counts.get(pm.platform_id, 0) + 1 + + port = settings.api_port + print("\n" + "─" * 70) + print("🌐 PLATFORMS") + print("─" * 70) + for p in platforms: + n_enabled = enabled_counts.get(p.id, 0) + if p.code == "main": + dev_url = f"http://localhost:{port}/" + else: + dev_url = f"http://localhost:{port}/platforms/{p.code}/" + print(f" {p.name} ({p.code})") + print(f" Domain: {p.domain}") + print(f" Dev URL: {dev_url}") + print(f" Modules: {n_enabled} enabled") + print("\n" + "─" * 70) print("šŸ” ADMIN CREDENTIALS") print("─" * 70) + admin_url = f"http://localhost:{port}/admin/login" print(" Super Admin (all platforms):") - print(" URL: /admin/login") + print(f" URL: {admin_url}") print(f" Username: {settings.admin_username}") print(f" Password: {settings.admin_password}") # noqa: SEC021 print() print(" Loyalty Platform Admin (loyalty only):") - print(" URL: /admin/login") + print(f" URL: {admin_url}") print(" Username: loyalty_admin") print(" Password: admin123") print("─" * 70) @@ -677,17 +709,16 @@ def print_summary(db: Session): print(" Change them in production via .env file") print("\nšŸš€ NEXT STEPS:") - print(" 1. Login to admin panel") if is_production(): + print(f" 1. Login to admin panel: {admin_url}") print(" 2. CHANGE DEFAULT PASSWORD IMMEDIATELY!") # noqa: SEC021 print(" 3. Configure admin settings") print(" 4. Create first store") else: - print(" 2. Create demo data: make seed-demo") - print(" 3. Start development: make dev") - - print("\nšŸ“ FOR DEMO DATA (Development only):") - print(" make seed-demo") + print(" 1. Start development: make dev") + print(f" 2. Admin panel: {admin_url}") + print(f" 3. Merchant panel: http://localhost:{port}/merchants/login") + print(" 4. Create demo data: make seed-demo") # ============================================================================= diff --git a/scripts/seed/seed_demo.py b/scripts/seed/seed_demo.py index 6f19f9ae..ceef9c67 100644 --- a/scripts/seed/seed_demo.py +++ b/scripts/seed/seed_demo.py @@ -144,12 +144,12 @@ DEMO_COMPANIES = [ DEMO_STORES = [ { "merchant_index": 0, # WizaCorp - "store_code": "ORION", - "name": "Orion", - "subdomain": "orion", + "store_code": "WIZATECH", + "name": "WizaTech", + "subdomain": "wizatech", "description": "Premium electronics and gadgets marketplace", "theme_preset": "modern", - "custom_domain": "orion.shop", + "custom_domain": "wizatech.shop", }, { "merchant_index": 0, # WizaCorp @@ -216,7 +216,7 @@ DEMO_TEAM_MEMBERS = [ "password": "password123", # noqa: SEC001 "first_name": "Alice", "last_name": "Manager", - "store_codes": ["ORION", "WIZAGADGETS"], # manages two stores + "store_codes": ["WIZATECH", "WIZAGADGETS"], # manages two stores "user_type": "member", }, { @@ -287,20 +287,20 @@ THEME_PRESETS = { # Store content page overrides (demonstrates CMS store override feature) # Each store can override platform default pages with custom content STORE_CONTENT_PAGES = { - "ORION": [ + "WIZATECH": [ { "slug": "about", - "title": "About Orion", + "title": "About WizaTech", "content": """
-

Welcome to Orion

+

Welcome to WizaTech

Your premier destination for cutting-edge electronics and innovative gadgets.

Our Story

-

Founded by tech enthusiasts, Orion has been bringing the latest technology to customers since 2020. +

Founded by tech enthusiasts, WizaTech has been bringing the latest technology to customers since 2020. We carefully curate our selection to ensure you get only the best products at competitive prices.

-

Why Choose Orion?

+

Why Choose WizaTech?

""", - "meta_description": "Orion - Your trusted source for premium electronics and gadgets in Luxembourg", + "meta_description": "WizaTech - Your trusted source for premium electronics and gadgets in Luxembourg", "show_in_header": True, "show_in_footer": True, }, { "slug": "contact", - "title": "Contact Orion", + "title": "Contact WizaTech", "content": """
-

Get in Touch with Orion

+

Get in Touch with WizaTech

Customer Support

""", - "meta_description": "Contact Orion customer support for electronics and gadget inquiries", + "meta_description": "Contact WizaTech customer support for electronics and gadget inquiries", "show_in_header": True, "show_in_footer": True, }, @@ -837,6 +837,7 @@ def create_demo_customers( """Create demo customers for a store.""" customers = [] + new_count = 0 # Use a simple demo password for all customers demo_password = "customer123" # noqa: SEC001 @@ -869,10 +870,10 @@ def create_demo_customers( ) db.add(customer) # noqa: PERF006 customers.append(customer) + new_count += 1 db.flush() - new_count = len([c for c in customers if c.id is None or db.is_modified(c)]) if new_count > 0: print_success(f"Created {new_count} customers for {store.name}") else: @@ -885,6 +886,7 @@ def create_demo_products(db: Session, store: Store, count: int) -> list[Product] """Create demo products for a store.""" products = [] + new_count = 0 for i in range(1, count + 1): marketplace_product_id = f"{store.store_code}-MP-{i:04d}" @@ -922,7 +924,7 @@ def create_demo_products(db: Session, store: Store, count: int) -> list[Product] availability="in stock", condition="new", google_product_category="Electronics > Computers > Laptops", - marketplace="Orion", + marketplace="OMS", store_name=store.name, currency="EUR", created_at=datetime.now(UTC), @@ -964,10 +966,10 @@ def create_demo_products(db: Session, store: Store, count: int) -> list[Product] ) db.add(product) # noqa: PERF006 products.append(product) + new_count += 1 db.flush() - new_count = len([p for p in products if p.id is None or db.is_modified(p)]) if new_count > 0: print_success(f"Created {new_count} products for {store.name}") else: @@ -1205,7 +1207,7 @@ def print_summary(db: Session): print(" All customers:") print(" Email: customer1@{subdomain}.example.com") print(" Password: customer123") # noqa: SEC021 - print(" (Replace {subdomain} with store subdomain, e.g., orion)") + print(" (Replace {subdomain} with store subdomain, e.g., wizatech)") print() print("\nšŸŖ Shop Access (Development):") @@ -1220,15 +1222,14 @@ def print_summary(db: Session): print("āš ļø ALL DEMO CREDENTIALS ARE INSECURE - For development only!") + port = settings.api_port print("\nšŸš€ NEXT STEPS:") print(" 1. Start development: make dev") - print(" 2. Login as store:") - print(" • Path-based: http://localhost:8000/store/ORION/login") - print(" • Subdomain: http://orion.localhost:8000/store/login") # noqa: SEC034 - print(" 3. Visit store shop: http://localhost:8000/stores/ORION/shop/") - print(" 4. Admin panel: http://localhost:8000/admin/login") - print(f" Username: {settings.admin_username}") - print(f" Password: {settings.admin_password}") # noqa: SEC021 + print(f" 2. Admin panel: http://localhost:{port}/admin/login") + print(f" 3. Merchant panel: http://localhost:{port}/merchants/login") + print(f" 4. Store panel: http://localhost:{port}/store/WIZATECH/login") + print(f" 5. Storefront: http://localhost:{port}/stores/WIZATECH/shop/") + print(f" 6. Customer login: http://localhost:{port}/stores/WIZATECH/shop/account") # =============================================================================