feat: trim platform modules, rename platforms, fix seed output
- 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:
2
Makefile
2
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
|
||||
|
||||
26
alembic/versions/a44f4956cfb1_merge_heads.py
Normal file
26
alembic/versions/a44f4956cfb1_merge_heads.py
Normal 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
|
||||
@@ -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")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
||||
@@ -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": """
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<h3>Why Choose Orion?</h3>
|
||||
<h3>Why Choose WizaTech?</h3>
|
||||
<ul>
|
||||
<li><strong>Expert Selection:</strong> Our team tests and reviews every product</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>
|
||||
</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_footer": True,
|
||||
},
|
||||
{
|
||||
"slug": "contact",
|
||||
"title": "Contact Orion",
|
||||
"title": "Contact WizaTech",
|
||||
"content": """
|
||||
<div class="prose-content">
|
||||
<h2>Get in Touch with Orion</h2>
|
||||
<h2>Get in Touch with WizaTech</h2>
|
||||
|
||||
<h3>Customer Support</h3>
|
||||
<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>WhatsApp:</strong> +352 123 456 789</li>
|
||||
<li><strong>Hours:</strong> Monday-Friday, 9am-6pm CET</li>
|
||||
@@ -334,7 +334,7 @@ STORE_CONTENT_PAGES = {
|
||||
<h3>Technical Support</h3>
|
||||
<p>Need help with your gadgets? Our tech experts are here to help!</p>
|
||||
<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>
|
||||
</ul>
|
||||
|
||||
@@ -342,7 +342,7 @@ STORE_CONTENT_PAGES = {
|
||||
<p>123 Tech Street<br>Luxembourg City, L-1234<br>Luxembourg</p>
|
||||
</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_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")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user