feat: trim platform modules, rename platforms, fix seed output
Some checks failed
CI / ruff (push) Successful in 13s
CI / pytest (push) Successful in 36m1s
CI / validate (push) Failing after 21s
CI / dependency-scanning (push) Successful in 28s
CI / docs (push) Has been skipped
CI / deploy (push) Has been skipped

- Rename platforms: Orion OMS → OMS, Orion → Wizard, Loyalty+ → Loyalty
- Per-platform module assignment: core modules always enabled, optional
  modules selectively enabled per platform instead of enabling all 18
- Rename demo store Orion → WizaTech to avoid confusion with app name
- Fix false "already exist" warnings for customers/products in seed
  (broken post-flush id detection replaced with simple counter)
- Make dev port use API_PORT from .env instead of hardcoded 9999
- Add platforms section with dev URLs to init-prod summary
- Add merchant panel and customer login URLs to seed next steps
- Merge alembic heads (z_store_domain_platform_id + tenancy_001)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 18:18:24 +01:00
parent 3d1586f025
commit 682213fdee
4 changed files with 103 additions and 45 deletions

View File

@@ -44,7 +44,7 @@ setup: install-all migrate-up init-prod
# ============================================================================= # =============================================================================
dev: 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 # DATABASE MIGRATIONS

View File

@@ -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

View File

@@ -176,7 +176,7 @@ def create_default_platforms(db: Session) -> list[Platform]:
platform_defs = [ platform_defs = [
{ {
"code": "oms", "code": "oms",
"name": "Orion OMS", "name": "OMS",
"description": "Order Management System for multi-store e-commerce", "description": "Order Management System for multi-store e-commerce",
"domain": "omsflow.lu", "domain": "omsflow.lu",
"path_prefix": "oms", "path_prefix": "oms",
@@ -187,7 +187,7 @@ def create_default_platforms(db: Session) -> list[Platform]:
}, },
{ {
"code": "main", "code": "main",
"name": "Orion", "name": "Wizard",
"description": "Main marketing site showcasing all Orion platforms", "description": "Main marketing site showcasing all Orion platforms",
"domain": "wizard.lu", "domain": "wizard.lu",
"path_prefix": None, "path_prefix": None,
@@ -198,7 +198,7 @@ def create_default_platforms(db: Session) -> list[Platform]:
}, },
{ {
"code": "loyalty", "code": "loyalty",
"name": "Loyalty+", "name": "Loyalty",
"description": "Customer loyalty program platform for Luxembourg businesses", "description": "Customer loyalty program platform for Luxembourg businesses",
"domain": "rewardflow.lu", "domain": "rewardflow.lu",
"path_prefix": "loyalty", "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: def create_platform_modules(db: Session, platforms: list[Platform]) -> int:
"""Create PlatformModule records for all platforms. """Create PlatformModule records for all platforms.
Enables all discovered modules for each platform so the app works Core modules are enabled for every platform. Optional modules are
out of the box. Admins can disable optional modules later via the API. 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) now = datetime.now(UTC)
records_created = 0 records_created = 0
for platform in platforms: for platform in platforms:
enabled_extras = set(PLATFORM_MODULES.get(platform.code, []))
for code in MODULES: for code in MODULES:
# Check if record already exists
existing = db.execute( existing = db.execute(
select(PlatformModule).where( select(PlatformModule).where(
PlatformModule.platform_id == platform.id, PlatformModule.platform_id == platform.id,
@@ -509,11 +518,12 @@ def create_platform_modules(db: Session, platforms: list[Platform]) -> int:
if existing: if existing:
continue continue
enabled = is_core_module(code) or code in enabled_extras
pm = PlatformModule( pm = PlatformModule(
platform_id=platform.id, platform_id=platform.id,
module_code=code, module_code=code,
is_enabled=True, is_enabled=enabled,
enabled_at=now, enabled_at=now if enabled else None,
config={}, config={},
) )
db.add(pm) # noqa: PERF006 db.add(pm) # noqa: PERF006
@@ -649,16 +659,38 @@ def print_summary(db: Session):
print(f" Admin settings: {setting_count}") print(f" Admin settings: {setting_count}")
print(f" Sub. tiers: {tier_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("\n" + "" * 70)
print("🔐 ADMIN CREDENTIALS") print("🔐 ADMIN CREDENTIALS")
print("" * 70) print("" * 70)
admin_url = f"http://localhost:{port}/admin/login"
print(" Super Admin (all platforms):") print(" Super Admin (all platforms):")
print(" URL: /admin/login") print(f" URL: {admin_url}")
print(f" Username: {settings.admin_username}") print(f" Username: {settings.admin_username}")
print(f" Password: {settings.admin_password}") # noqa: SEC021 print(f" Password: {settings.admin_password}") # noqa: SEC021
print() print()
print(" Loyalty Platform Admin (loyalty only):") print(" Loyalty Platform Admin (loyalty only):")
print(" URL: /admin/login") print(f" URL: {admin_url}")
print(" Username: loyalty_admin") print(" Username: loyalty_admin")
print(" Password: admin123") print(" Password: admin123")
print("" * 70) print("" * 70)
@@ -677,17 +709,16 @@ def print_summary(db: Session):
print(" Change them in production via .env file") print(" Change them in production via .env file")
print("\n🚀 NEXT STEPS:") print("\n🚀 NEXT STEPS:")
print(" 1. Login to admin panel")
if is_production(): if is_production():
print(f" 1. Login to admin panel: {admin_url}")
print(" 2. CHANGE DEFAULT PASSWORD IMMEDIATELY!") # noqa: SEC021 print(" 2. CHANGE DEFAULT PASSWORD IMMEDIATELY!") # noqa: SEC021
print(" 3. Configure admin settings") print(" 3. Configure admin settings")
print(" 4. Create first store") print(" 4. Create first store")
else: else:
print(" 2. Create demo data: make seed-demo") print(" 1. Start development: make dev")
print(" 3. Start development: make dev") print(f" 2. Admin panel: {admin_url}")
print(f" 3. Merchant panel: http://localhost:{port}/merchants/login")
print("\n📝 FOR DEMO DATA (Development only):") print(" 4. Create demo data: make seed-demo")
print(" make seed-demo")
# ============================================================================= # =============================================================================

View File

@@ -144,12 +144,12 @@ DEMO_COMPANIES = [
DEMO_STORES = [ DEMO_STORES = [
{ {
"merchant_index": 0, # WizaCorp "merchant_index": 0, # WizaCorp
"store_code": "ORION", "store_code": "WIZATECH",
"name": "Orion", "name": "WizaTech",
"subdomain": "orion", "subdomain": "wizatech",
"description": "Premium electronics and gadgets marketplace", "description": "Premium electronics and gadgets marketplace",
"theme_preset": "modern", "theme_preset": "modern",
"custom_domain": "orion.shop", "custom_domain": "wizatech.shop",
}, },
{ {
"merchant_index": 0, # WizaCorp "merchant_index": 0, # WizaCorp
@@ -216,7 +216,7 @@ DEMO_TEAM_MEMBERS = [
"password": "password123", # noqa: SEC001 "password": "password123", # noqa: SEC001
"first_name": "Alice", "first_name": "Alice",
"last_name": "Manager", "last_name": "Manager",
"store_codes": ["ORION", "WIZAGADGETS"], # manages two stores "store_codes": ["WIZATECH", "WIZAGADGETS"], # manages two stores
"user_type": "member", "user_type": "member",
}, },
{ {
@@ -287,20 +287,20 @@ THEME_PRESETS = {
# Store content page overrides (demonstrates CMS store override feature) # Store content page overrides (demonstrates CMS store override feature)
# Each store can override platform default pages with custom content # Each store can override platform default pages with custom content
STORE_CONTENT_PAGES = { STORE_CONTENT_PAGES = {
"ORION": [ "WIZATECH": [
{ {
"slug": "about", "slug": "about",
"title": "About Orion", "title": "About WizaTech",
"content": """ "content": """
<div class="prose-content"> <div class="prose-content">
<h2>Welcome to Orion</h2> <h2>Welcome to WizaTech</h2>
<p>Your premier destination for cutting-edge electronics and innovative gadgets.</p> <p>Your premier destination for cutting-edge electronics and innovative gadgets.</p>
<h3>Our Story</h3> <h3>Our Story</h3>
<p>Founded by tech enthusiasts, Orion has been bringing the latest technology to customers since 2020. <p>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.</p> We carefully curate our selection to ensure you get only the best products at competitive prices.</p>
<h3>Why Choose Orion?</h3> <h3>Why Choose WizaTech?</h3>
<ul> <ul>
<li><strong>Expert Selection:</strong> Our team tests and reviews every product</li> <li><strong>Expert Selection:</strong> Our team tests and reviews every product</li>
<li><strong>Best Prices:</strong> We negotiate directly with manufacturers</li> <li><strong>Best Prices:</strong> We negotiate directly with manufacturers</li>
@@ -312,20 +312,20 @@ STORE_CONTENT_PAGES = {
<p>123 Tech Street, Luxembourg City<br>Open Monday-Saturday, 9am-7pm</p> <p>123 Tech Street, Luxembourg City<br>Open Monday-Saturday, 9am-7pm</p>
</div> </div>
""", """,
"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_header": True,
"show_in_footer": True, "show_in_footer": True,
}, },
{ {
"slug": "contact", "slug": "contact",
"title": "Contact Orion", "title": "Contact WizaTech",
"content": """ "content": """
<div class="prose-content"> <div class="prose-content">
<h2>Get in Touch with Orion</h2> <h2>Get in Touch with WizaTech</h2>
<h3>Customer Support</h3> <h3>Customer Support</h3>
<ul> <ul>
<li><strong>Email:</strong> support@orion.lu</li> <li><strong>Email:</strong> support@wizatech.lu</li>
<li><strong>Phone:</strong> +352 123 456 789</li> <li><strong>Phone:</strong> +352 123 456 789</li>
<li><strong>WhatsApp:</strong> +352 123 456 789</li> <li><strong>WhatsApp:</strong> +352 123 456 789</li>
<li><strong>Hours:</strong> Monday-Friday, 9am-6pm CET</li> <li><strong>Hours:</strong> Monday-Friday, 9am-6pm CET</li>
@@ -334,7 +334,7 @@ STORE_CONTENT_PAGES = {
<h3>Technical Support</h3> <h3>Technical Support</h3>
<p>Need help with your gadgets? Our tech experts are here to help!</p> <p>Need help with your gadgets? Our tech experts are here to help!</p>
<ul> <ul>
<li><strong>Email:</strong> tech@orion.lu</li> <li><strong>Email:</strong> tech@wizatech.lu</li>
<li><strong>Live Chat:</strong> Available on our website</li> <li><strong>Live Chat:</strong> Available on our website</li>
</ul> </ul>
@@ -342,7 +342,7 @@ STORE_CONTENT_PAGES = {
<p>123 Tech Street<br>Luxembourg City, L-1234<br>Luxembourg</p> <p>123 Tech Street<br>Luxembourg City, L-1234<br>Luxembourg</p>
</div> </div>
""", """,
"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_header": True,
"show_in_footer": True, "show_in_footer": True,
}, },
@@ -837,6 +837,7 @@ def create_demo_customers(
"""Create demo customers for a store.""" """Create demo customers for a store."""
customers = [] customers = []
new_count = 0
# Use a simple demo password for all customers # Use a simple demo password for all customers
demo_password = "customer123" # noqa: SEC001 demo_password = "customer123" # noqa: SEC001
@@ -869,10 +870,10 @@ def create_demo_customers(
) )
db.add(customer) # noqa: PERF006 db.add(customer) # noqa: PERF006
customers.append(customer) customers.append(customer)
new_count += 1
db.flush() db.flush()
new_count = len([c for c in customers if c.id is None or db.is_modified(c)])
if new_count > 0: if new_count > 0:
print_success(f"Created {new_count} customers for {store.name}") print_success(f"Created {new_count} customers for {store.name}")
else: else:
@@ -885,6 +886,7 @@ def create_demo_products(db: Session, store: Store, count: int) -> list[Product]
"""Create demo products for a store.""" """Create demo products for a store."""
products = [] products = []
new_count = 0
for i in range(1, count + 1): for i in range(1, count + 1):
marketplace_product_id = f"{store.store_code}-MP-{i:04d}" 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", availability="in stock",
condition="new", condition="new",
google_product_category="Electronics > Computers > Laptops", google_product_category="Electronics > Computers > Laptops",
marketplace="Orion", marketplace="OMS",
store_name=store.name, store_name=store.name,
currency="EUR", currency="EUR",
created_at=datetime.now(UTC), 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 db.add(product) # noqa: PERF006
products.append(product) products.append(product)
new_count += 1
db.flush() db.flush()
new_count = len([p for p in products if p.id is None or db.is_modified(p)])
if new_count > 0: if new_count > 0:
print_success(f"Created {new_count} products for {store.name}") print_success(f"Created {new_count} products for {store.name}")
else: else:
@@ -1205,7 +1207,7 @@ def print_summary(db: Session):
print(" All customers:") print(" All customers:")
print(" Email: customer1@{subdomain}.example.com") print(" Email: customer1@{subdomain}.example.com")
print(" Password: customer123") # noqa: SEC021 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()
print("\n🏪 Shop Access (Development):") print("\n🏪 Shop Access (Development):")
@@ -1220,15 +1222,14 @@ def print_summary(db: Session):
print("⚠️ ALL DEMO CREDENTIALS ARE INSECURE - For development only!") print("⚠️ ALL DEMO CREDENTIALS ARE INSECURE - For development only!")
port = settings.api_port
print("\n🚀 NEXT STEPS:") print("\n🚀 NEXT STEPS:")
print(" 1. Start development: make dev") print(" 1. Start development: make dev")
print(" 2. Login as store:") print(f" 2. Admin panel: http://localhost:{port}/admin/login")
print(" • Path-based: http://localhost:8000/store/ORION/login") print(f" 3. Merchant panel: http://localhost:{port}/merchants/login")
print(" • Subdomain: http://orion.localhost:8000/store/login") # noqa: SEC034 print(f" 4. Store panel: http://localhost:{port}/store/WIZATECH/login")
print(" 3. Visit store shop: http://localhost:8000/stores/ORION/shop/") print(f" 5. Storefront: http://localhost:{port}/stores/WIZATECH/shop/")
print(" 4. Admin panel: http://localhost:8000/admin/login") print(f" 6. Customer login: http://localhost:{port}/stores/WIZATECH/shop/account")
print(f" Username: {settings.admin_username}")
print(f" Password: {settings.admin_password}") # noqa: SEC021
# ============================================================================= # =============================================================================