refactor: rename Wizamart to Orion across entire codebase

Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart
with Orion/orion/ORION across 184 files. This includes database
identifiers, email addresses, domain references, R2 bucket names,
DNS prefixes, encryption salt, Celery app name, config defaults,
Docker configs, CI configs, documentation, seed data, and templates.

Renames homepage-wizamart.html template to homepage-orion.html.
Fixes duplicate file_pattern key in api.yaml architecture rule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 16:46:56 +01:00
parent 34ee7bb7ad
commit e9253fbd84
184 changed files with 1227 additions and 1228 deletions

View File

@@ -95,7 +95,7 @@ from models.database.base import TimestampMixin
class StoreDomain(Base, TimestampMixin):
"""
Custom domain mapping for stores.
Allows stores to use their own domains (e.g., myshop.com)
instead of subdomains (store1.platform.com).
"""
@@ -103,11 +103,11 @@ class StoreDomain(Base, TimestampMixin):
# Primary Key
id = Column(Integer, primary_key=True, index=True)
# Foreign Keys
store_id = Column(
Integer,
ForeignKey("stores.id", ondelete="CASCADE"),
Integer,
ForeignKey("stores.id", ondelete="CASCADE"),
nullable=False,
index=True
)
@@ -116,12 +116,12 @@ class StoreDomain(Base, TimestampMixin):
domain = Column(String(255), nullable=False, unique=True, index=True)
is_primary = Column(Boolean, default=False, nullable=False)
is_active = Column(Boolean, default=True, nullable=False)
# Verification
is_verified = Column(Boolean, default=False, nullable=False)
verification_token = Column(String(100), unique=True, nullable=True)
verified_at = Column(DateTime(timezone=True), nullable=True)
# SSL Status
ssl_status = Column(String(50), default="pending")
ssl_verified_at = Column(DateTime(timezone=True), nullable=True)
@@ -171,9 +171,9 @@ class StoreDomain(Base, TimestampMixin):
class Store(Base, TimestampMixin):
__tablename__ = "stores"
# ... existing fields ...
# Add relationship
domains = relationship(
"StoreDomain",
@@ -181,7 +181,7 @@ class Store(Base, TimestampMixin):
cascade="all, delete-orphan",
order_by="StoreDomain.is_primary.desc()"
)
# Helper method
@property
def primary_domain(self):
@@ -270,10 +270,10 @@ class StoreDomainCreate(BaseModel):
"""Validate and normalize domain."""
# Remove protocol if present
domain = v.replace("https://", "").replace("http://", "")
# Remove trailing slash
domain = domain.rstrip("/")
# Convert to lowercase
domain = domain.lower().strip()
@@ -331,7 +331,7 @@ class StoreDomainResponse(BaseModel):
class StoreDomainListResponse(BaseModel):
"""Schema for paginated store domain list."""
domains: List[StoreDomainResponse]
total: int
@@ -535,7 +535,7 @@ class DNSVerificationException(ExternalServiceException):
| 409 | `ConflictException` | Resource conflicts |
| 422 | `ValidationException` | Input validation errors |
| 429 | `RateLimitException` | Rate limiting |
| 500 | `WizamartException` | Generic errors |
| 500 | `OrionException` | Generic errors |
| 502 | `ExternalServiceException` | Third-party failures |
### Step 2: Update Exception Exports
@@ -561,7 +561,7 @@ from .store_domain import (
__all__ = [
# ... existing exports ...
# Store Domain
"StoreDomainNotFoundException",
"StoreDomainAlreadyExistsException",
@@ -1133,7 +1133,7 @@ def delete_store_domain(
Delete a custom domain (Admin only).
**Warning:** This is permanent and cannot be undone.
**Raises:**
- 404: Domain not found
"""
@@ -1230,7 +1230,7 @@ async def admin_store_domains_page(
Manage custom domains for {{ store_code }}
</p>
</div>
<button
<button
onclick="openAddDomainModal()"
class="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700">
Add Domain
@@ -1248,32 +1248,32 @@ async def admin_store_domains_page(
<div class="flex items-center justify-center min-h-screen px-4">
<div class="bg-white dark:bg-gray-800 rounded-lg max-w-md w-full p-6">
<h2 class="text-2xl font-semibold mb-4">Add Custom Domain</h2>
<form id="addDomainForm">
<div class="mb-4">
<label class="block text-sm font-medium mb-2">Domain</label>
<input
type="text"
<input
type="text"
name="domain"
placeholder="myshop.com"
class="w-full px-3 py-2 border rounded-lg"
required>
</div>
<div class="mb-4">
<label class="flex items-center">
<input type="checkbox" name="is_primary" class="mr-2">
<span class="text-sm">Set as primary domain</span>
</label>
</div>
<div class="flex gap-2">
<button
<button
type="submit"
class="flex-1 bg-purple-600 text-white py-2 rounded-lg hover:bg-purple-700">
Add Domain
</button>
<button
<button
type="button"
onclick="closeAddDomainModal()"
class="flex-1 bg-gray-300 text-gray-700 py-2 rounded-lg hover:bg-gray-400">
@@ -1305,7 +1305,7 @@ async function loadStoreId() {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
if (response.ok) {
const store = await response.json();
storeId = store.id;
@@ -1317,14 +1317,14 @@ async function loadStoreId() {
async function loadDomains() {
if (!storeId) return;
try {
const response = await fetch(`/api/v1/admin/stores/${storeId}/domains`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
if (response.ok) {
const data = await response.json();
renderDomains(data.domains);
@@ -1336,12 +1336,12 @@ async function loadDomains() {
function renderDomains(domains) {
const container = document.getElementById('domainsList');
if (domains.length === 0) {
container.innerHTML = '<p class="text-gray-500">No domains added yet.</p>';
return;
}
container.innerHTML = domains.map(domain => `
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
<div class="flex items-center justify-between">
@@ -1358,13 +1358,13 @@ function renderDomains(domains) {
</div>
<div class="flex gap-2">
${!domain.is_verified ? `
<button
<button
onclick="verifyDomain(${domain.id})"
class="px-3 py-1 bg-green-600 text-white text-sm rounded hover:bg-green-700">
Verify
</button>
` : ''}
<button
<button
onclick="deleteDomain(${domain.id})"
class="px-3 py-1 bg-red-600 text-white text-sm rounded hover:bg-red-700">
Delete
@@ -1386,13 +1386,13 @@ function closeAddDomainModal() {
document.getElementById('addDomainForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const data = {
domain: formData.get('domain'),
is_primary: formData.get('is_primary') === 'on'
};
try {
const response = await fetch(`/api/v1/admin/stores/${storeId}/domains`, {
method: 'POST',
@@ -1402,7 +1402,7 @@ document.getElementById('addDomainForm').addEventListener('submit', async (e) =>
},
body: JSON.stringify(data)
});
if (response.ok) {
closeAddDomainModal();
await loadDomains();
@@ -1425,7 +1425,7 @@ async function verifyDomain(domainId) {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
if (response.ok) {
await loadDomains();
alert('Domain verified successfully!');
@@ -1441,7 +1441,7 @@ async function verifyDomain(domainId) {
async function deleteDomain(domainId) {
if (!confirm('Are you sure you want to delete this domain?')) return;
try {
const response = await fetch(`/api/v1/admin/stores/domains/${domainId}`, {
method: 'DELETE',
@@ -1449,7 +1449,7 @@ async function deleteDomain(domainId) {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
if (response.ok) {
await loadDomains();
alert('Domain deleted successfully!');
@@ -1497,9 +1497,9 @@ def test_add_domain_success(db_session, test_store, service):
domain="test.com",
is_primary=True
)
domain = service.add_domain(db_session, test_store.id, domain_data)
assert domain.domain == "test.com"
assert domain.is_primary is True
assert domain.is_verified is False
@@ -1509,7 +1509,7 @@ def test_add_domain_success(db_session, test_store, service):
def test_add_domain_already_exists(db_session, test_store, existing_domain, service):
"""Test adding duplicate domain raises exception."""
domain_data = StoreDomainCreate(domain=existing_domain.domain)
with pytest.raises(StoreDomainAlreadyExistsException):
service.add_domain(db_session, test_store.id, domain_data)
@@ -1520,7 +1520,7 @@ def test_add_domain_max_limit_reached(db_session, test_store, service):
for i in range(service.max_domains_per_store):
domain_data = StoreDomainCreate(domain=f"test{i}.com")
service.add_domain(db_session, test_store.id, domain_data)
# Try adding one more
domain_data = StoreDomainCreate(domain="overflow.com")
with pytest.raises(MaxDomainsReachedException):
@@ -1549,7 +1549,7 @@ def test_add_domain_endpoint(client, admin_headers, test_store):
json={"domain": "newshop.com", "is_primary": False},
headers=admin_headers
)
assert response.status_code == 201
data = response.json()
assert data["domain"] == "newshop.com"
@@ -1563,7 +1563,7 @@ def test_add_domain_invalid_format(client, admin_headers, test_store):
json={"domain": "admin.example.com"}, # Reserved subdomain
headers=admin_headers
)
assert response.status_code == 422
assert "reserved" in response.json()["message"].lower()
@@ -1574,7 +1574,7 @@ def test_list_domains_endpoint(client, admin_headers, test_store, test_domain):
f"/api/v1/admin/stores/{test_store.id}/domains",
headers=admin_headers
)
assert response.status_code == 200
data = response.json()
assert data["total"] >= 1
@@ -1587,7 +1587,7 @@ def test_delete_domain_endpoint(client, admin_headers, test_domain):
f"/api/v1/admin/stores/domains/{test_domain.id}",
headers=admin_headers
)
assert response.status_code == 200
assert "deleted successfully" in response.json()["message"]
@@ -1598,7 +1598,7 @@ def test_verify_domain_not_found(client, admin_headers):
"/api/v1/admin/stores/domains/99999/verify",
headers=admin_headers
)
assert response.status_code == 404
assert response.json()["error_code"] == "STORE_DOMAIN_NOT_FOUND"
```
@@ -1794,6 +1794,6 @@ For questions or issues:
---
**Last Updated:** 2025-01-15
**Version:** 1.0
**Last Updated:** 2025-01-15
**Version:** 1.0
**Maintainer:** Development Team

View File

@@ -4,7 +4,7 @@ Guide for developing backend features, services, and API endpoints.
## Overview
The Wizamart backend is built with FastAPI and follows a service-oriented architecture pattern. This guide covers backend development practices, patterns, and technical references.
The Orion backend is built with FastAPI and follows a service-oriented architecture pattern. This guide covers backend development practices, patterns, and technical references.
## Backend Structure

View File

@@ -55,7 +55,7 @@ def get_product(
│ "sub": "user_id", │
│ "username": "john.doe", │
│ "store_id": 123, ← Store context in token │
│ "store_code": "WIZAMART", ← Store code in token │
│ "store_code": "ORION", ← Store code in token │
│ "store_role": "Owner" ← Store role in token │
│ } │
└─────────────────────────────────────────────────────────────────┘
@@ -436,14 +436,14 @@ def test_store_in_token():
token_data = auth_manager.create_access_token(
user=user,
store_id=123,
store_code="WIZAMART",
store_code="ORION",
store_role="Owner",
)
# Verify token contains store data
payload = jwt.decode(token_data["access_token"], secret_key)
assert payload["store_id"] == 123
assert payload["store_code"] == "WIZAMART"
assert payload["store_code"] == "ORION"
assert payload["store_role"] == "Owner"
def test_api_endpoint_uses_token_store():
@@ -517,7 +517,7 @@ The architecture enforces a strict layered pattern for where exceptions should b
┌────────────────────────────────────────────────────────────────────────────┐
│ GLOBAL EXCEPTION HANDLER - app/exceptions/handler.py │
│ │
│ ✅ Catches all WizamartException subclasses │
│ ✅ Catches all OrionException subclasses │
│ ✅ Converts to appropriate HTTP responses │
│ ✅ Provides consistent error formatting │
└────────────────────────────────────────────────────────────────────────────┘

View File

@@ -607,7 +607,7 @@ HTTP 403 Forbidden
"message": "You don't have permission to perform this action",
"details": {
"required_permission": "products.delete",
"store_code": "wizamart"
"store_code": "orion"
}
}
```
@@ -622,7 +622,7 @@ HTTP 403 Forbidden
"message": "This operation requires store owner privileges",
"details": {
"operation": "team management",
"store_code": "wizamart"
"store_code": "orion"
}
}
```