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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 │
|
||||
└────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user