revamping frontend logging system and reorganising documentation

This commit is contained in:
2025-10-28 21:07:26 +01:00
parent 5c80ba17c5
commit b0cc0385f8
68 changed files with 3481 additions and 624 deletions

View File

@@ -0,0 +1,562 @@
# Alpine.js Page Template - Quick Reference (WITH CENTRALIZED LOGGING)
## ✅ Correct Page Structure
```javascript
// static/admin/js/your-page.js (or vendor/shop)
// 1. ✅ Use centralized logger (ONE LINE!)
const yourPageLog = window.LogConfig.loggers.yourPage;
// OR create custom if not pre-configured
// const yourPageLog = window.LogConfig.createLogger('YOUR-PAGE', window.LogConfig.logLevel);
// 2. Create your Alpine.js component
function yourPageComponent() {
return {
// ✅ CRITICAL: Inherit base layout functionality
...data(),
// ✅ CRITICAL: Set page identifier
currentPage: 'your-page',
// Your page-specific state
items: [],
loading: false,
error: null,
// ✅ CRITICAL: Proper initialization with guard
async init() {
yourPageLog.info('=== YOUR PAGE INITIALIZING ===');
// Prevent multiple initializations
if (window._yourPageInitialized) {
yourPageLog.warn('Page already initialized, skipping...');
return;
}
window._yourPageInitialized = true;
// Load your data
await this.loadData();
yourPageLog.info('=== YOUR PAGE INITIALIZATION COMPLETE ===');
},
// Your methods
async loadData() {
yourPageLog.info('Loading data...');
this.loading = true;
this.error = null;
try {
const startTime = performance.now();
// Log API request
const url = '/your/endpoint';
window.LogConfig.logApiCall('GET', url, null, 'request');
// ✅ CRITICAL: Use lowercase apiClient
const response = await apiClient.get(url);
// Log API response
window.LogConfig.logApiCall('GET', url, response, 'response');
this.items = response.items || [];
// Log performance
const duration = performance.now() - startTime;
window.LogConfig.logPerformance('Load Data', duration);
yourPageLog.info(`Data loaded successfully`, {
count: this.items.length,
duration: `${duration}ms`
});
} catch (error) {
// Use centralized error logging
window.LogConfig.logError(error, 'Load Data');
this.error = error.message;
Utils.showToast('Failed to load data', 'error');
} finally {
this.loading = false;
}
},
// Format date helper (if needed)
formatDate(dateString) {
if (!dateString) return '-';
return Utils.formatDate(dateString);
},
// Your other methods...
};
}
yourPageLog.info('Your page module loaded');
```
---
## 🎯 Checklist for New Pages
### HTML Template
```jinja2
{# app/templates/admin/your-page.html #}
{% extends "admin/base.html" %}
{% block title %}Your Page{% endblock %}
{# ✅ CRITICAL: Link to your Alpine.js component #}
{% block alpine_data %}yourPageComponent(){% endblock %}
{% block content %}
{% endblock %}
{% block extra_scripts %}
{# ✅ CRITICAL: Load your JavaScript file #}
{% endblock %}
```
### JavaScript File Checklist
- [ ] ✅ Use centralized logger (ONE line instead of 15!)
- [ ] ✅ Function name matches `alpine_data` in template
- [ ]`...data(),` at start of return object
- [ ]`currentPage: 'your-page'` set
- [ ] ✅ Initialization guard in `init()`
- [ ] ✅ Use lowercase `apiClient` for API calls
- [ ] ✅ Use `window.LogConfig.logApiCall()` for API logging
- [ ] ✅ Use `window.LogConfig.logPerformance()` for performance
- [ ] ✅ Use `window.LogConfig.logError()` for errors
- [ ] ✅ Module loaded log at end
---
## 📦 Pre-configured Loggers by Frontend
### Admin Frontend
```javascript
window.LogConfig.loggers.vendors // Vendor management
window.LogConfig.loggers.vendorTheme // Theme customization
window.LogConfig.loggers.vendorUsers // Vendor users
window.LogConfig.loggers.products // Product management
window.LogConfig.loggers.inventory // Inventory
window.LogConfig.loggers.orders // Order management
window.LogConfig.loggers.users // User management
window.LogConfig.loggers.audit // Audit logs
window.LogConfig.loggers.dashboard // Dashboard
window.LogConfig.loggers.imports // Import operations
```
### Vendor Frontend
```javascript
window.LogConfig.loggers.dashboard // Vendor dashboard
window.LogConfig.loggers.products // Product management
window.LogConfig.loggers.inventory // Inventory
window.LogConfig.loggers.orders // Order management
window.LogConfig.loggers.theme // Theme customization
window.LogConfig.loggers.settings // Settings
window.LogConfig.loggers.analytics // Analytics
```
### Shop Frontend
```javascript
window.LogConfig.loggers.catalog // Product browsing
window.LogConfig.loggers.product // Product details
window.LogConfig.loggers.search // Search
window.LogConfig.loggers.cart // Shopping cart
window.LogConfig.loggers.checkout // Checkout
window.LogConfig.loggers.account // User account
window.LogConfig.loggers.orders // Order history
window.LogConfig.loggers.wishlist // Wishlist
```
---
## ❌ Common Mistakes to Avoid
### 1. Old Way vs New Way
```javascript
// ❌ OLD WAY - 15 lines of duplicate code
const YOUR_PAGE_LOG_LEVEL = 3;
const yourPageLog = {
error: (...args) => YOUR_PAGE_LOG_LEVEL >= 1 && console.error('❌ [YOUR_PAGE ERROR]', ...args),
warn: (...args) => YOUR_PAGE_LOG_LEVEL >= 2 && console.warn('⚠️ [YOUR_PAGE WARN]', ...args),
info: (...args) => YOUR_PAGE_LOG_LEVEL >= 3 && console.info(' [YOUR_PAGE INFO]', ...args),
debug: (...args) => YOUR_PAGE_LOG_LEVEL >= 4 && console.log('🔍 [YOUR_PAGE DEBUG]', ...args)
};
// ✅ NEW WAY - 1 line!
const yourPageLog = window.LogConfig.loggers.yourPage;
```
### 2. Missing Base Inheritance
```javascript
// ❌ WRONG
function myPage() {
return {
items: [],
// Missing ...data()
};
}
// ✅ CORRECT
function myPage() {
return {
...data(), // Must be first!
items: [],
};
}
```
### 3. Wrong API Client Name
```javascript
// ❌ WRONG - Capital letters
await ApiClient.get('/endpoint');
await API_CLIENT.get('/endpoint');
// ✅ CORRECT - lowercase
await apiClient.get('/endpoint');
```
### 4. Missing Init Guard
```javascript
// ❌ WRONG
async init() {
await this.loadData();
}
// ✅ CORRECT
async init() {
if (window._myPageInitialized) return;
window._myPageInitialized = true;
await this.loadData();
}
```
### 5. Missing currentPage
```javascript
// ❌ WRONG
return {
...data(),
items: [],
// Missing currentPage
};
// ✅ CORRECT
return {
...data(),
currentPage: 'my-page', // Must set this!
items: [],
};
```
---
## 🔧 API Client Pattern
### GET Request
```javascript
try {
const response = await apiClient.get('/endpoint');
this.data = response;
} catch (error) {
console.error('Failed:', error);
Utils.showToast('Failed to load', 'error');
}
```
### POST Request
```javascript
try {
const response = await apiClient.post('/endpoint', {
name: 'value',
// ... data
});
Utils.showToast('Created successfully', 'success');
} catch (error) {
console.error('Failed:', error);
Utils.showToast('Failed to create', 'error');
}
```
### PUT Request
```javascript
try {
const response = await apiClient.put('/endpoint/123', {
name: 'updated value'
});
Utils.showToast('Updated successfully', 'success');
} catch (error) {
console.error('Failed:', error);
Utils.showToast('Failed to update', 'error');
}
```
### DELETE Request
```javascript
try {
await apiClient.delete('/endpoint/123');
Utils.showToast('Deleted successfully', 'success');
await this.reloadData();
} catch (error) {
console.error('Failed:', error);
Utils.showToast('Failed to delete', 'error');
}
```
---
## 🔧 Centralized Logging Patterns
### Basic Logging
```javascript
const log = window.LogConfig.loggers.yourPage;
log.info('Page loaded');
log.warn('Connection slow');
log.error('Failed to load data', error);
log.debug('User data:', userData);
```
### Grouped Logging
```javascript
log.group('Loading Theme Data');
log.info('Fetching vendor...');
log.info('Fetching theme...');
log.info('Fetching presets...');
log.groupEnd();
```
### API Call Logging
```javascript
const url = '/api/vendors';
// Before request
window.LogConfig.logApiCall('GET', url, null, 'request');
// Make request
const data = await apiClient.get(url);
// After response
window.LogConfig.logApiCall('GET', url, data, 'response');
```
### Error Logging
```javascript
try {
await saveTheme();
} catch (error) {
window.LogConfig.logError(error, 'Save Theme');
}
```
### Performance Logging
```javascript
const start = performance.now();
await loadThemeData();
const duration = performance.now() - start;
window.LogConfig.logPerformance('Load Theme Data', duration);
```
### Table Logging
```javascript
log.table([
{ id: 1, name: 'Vendor A', status: 'active' },
{ id: 2, name: 'Vendor B', status: 'inactive' }
]);
```
---
## 📚 Benefits of Centralized Logging
| Aspect | Old Way | New Way |
|--------|---------|---------|
| **Lines of code** | ~15 per file | 1 line per file |
| **Consistency** | Varies by file | Unified across all frontends |
| **Maintenance** | Update each file | Update one shared file |
| **Features** | Basic logging | Advanced (groups, perf, API) |
| **Environment** | Manual config | Auto-detected |
| **Frontend aware** | No | Yes (admin/vendor/shop) |
| **Log levels** | Per file | Per frontend + environment |
---
## 🎨 Common UI Patterns
### Loading State
```javascript
async loadData() {
this.loading = true;
try {
const data = await apiClient.get('/endpoint');
this.items = data;
} finally {
this.loading = false;
}
}
```
### Refresh/Reload
```javascript
async refresh() {
console.info('Refreshing...');
await this.loadData();
Utils.showToast('Refreshed successfully', 'success');
}
```
---
## 📚 Available Utilities
### From `init-alpine.js` (via `...data()`)
- `this.dark` - Dark mode state
- `this.toggleTheme()` - Toggle theme
- `this.isSideMenuOpen` - Side menu state
- `this.toggleSideMenu()` - Toggle side menu
- `this.closeSideMenu()` - Close side menu
- `this.isNotificationsMenuOpen` - Notifications menu state
- `this.toggleNotificationsMenu()` - Toggle notifications
- `this.closeNotificationsMenu()` - Close notifications
- `this.isProfileMenuOpen` - Profile menu state
- `this.toggleProfileMenu()` - Toggle profile menu
- `this.closeProfileMenu()` - Close profile menu
- `this.isPagesMenuOpen` - Pages menu state
- `this.togglePagesMenu()` - Toggle pages menu
### From `Utils` (global)
- `Utils.showToast(message, type, duration)` - Show toast notification
- `Utils.formatDate(dateString)` - Format date for display
- `Utils.confirm(message, title)` - Show confirmation dialog (if available)
### From `apiClient` (global)
- `apiClient.get(url)` - GET request
- `apiClient.post(url, data)` - POST request
- `apiClient.put(url, data)` - PUT request
- `apiClient.delete(url)` - DELETE request
---
## 🎨 Complete Example
```javascript
// static/admin/js/vendor-theme.js
// 1. Use centralized logger
const themeLog = window.LogConfig.loggers.vendorTheme;
// 2. Create component
function adminVendorTheme() {
return {
...data(),
currentPage: 'vendor-theme',
vendor: null,
themeData: {},
loading: true,
async init() {
themeLog.info('Initializing vendor theme editor');
if (window._vendorThemeInitialized) {
themeLog.warn('Already initialized, skipping...');
return;
}
window._vendorThemeInitialized = true;
const startTime = performance.now();
try {
themeLog.group('Loading theme data');
await Promise.all([
this.loadVendor(),
this.loadTheme()
]);
themeLog.groupEnd();
const duration = performance.now() - startTime;
window.LogConfig.logPerformance('Theme Editor Init', duration);
themeLog.info('Theme editor initialized successfully');
} catch (error) {
window.LogConfig.logError(error, 'Theme Editor Init');
Utils.showToast('Failed to initialize', 'error');
} finally {
this.loading = false;
}
},
async loadVendor() {
const url = `/admin/vendors/${this.vendorCode}`;
window.LogConfig.logApiCall('GET', url, null, 'request');
const response = await apiClient.get(url);
this.vendor = response;
window.LogConfig.logApiCall('GET', url, response, 'response');
themeLog.debug('Vendor loaded:', this.vendor);
},
async saveTheme() {
themeLog.info('Saving theme changes');
try {
const url = `/admin/vendor-themes/${this.vendorCode}`;
window.LogConfig.logApiCall('PUT', url, this.themeData, 'request');
const response = await apiClient.put(url, this.themeData);
window.LogConfig.logApiCall('PUT', url, response, 'response');
themeLog.info('Theme saved successfully');
Utils.showToast('Theme saved', 'success');
} catch (error) {
window.LogConfig.logError(error, 'Save Theme');
Utils.showToast('Failed to save theme', 'error');
}
}
};
}
themeLog.info('Vendor theme editor module loaded');
```
---
## 🚀 Quick Start Template Files
Copy these to create a new page:
1. **Copy base file:** `dashboard.js` → rename to `your-page.js`
2. **Update logger:**
```javascript
// Change from:
const dashLog = window.LogConfig.loggers.dashboard;
// To:
const yourPageLog = window.LogConfig.loggers.yourPage;
// Or create custom:
const yourPageLog = window.LogConfig.createLogger('YOUR-PAGE');
```
3. **Replace function name:** `adminDashboard()` → `yourPageComponent()`
4. **Update init flag:** `_dashboardInitialized` → `_yourPageInitialized`
5. **Update page identifier:** `currentPage: 'dashboard'` → `currentPage: 'your-page'`
6. **Replace data loading logic** with your API endpoints
7. **Update HTML template** to use your function name:
```jinja2
{% block alpine_data %}yourPageComponent(){% endblock %}
```
8. **Load your script** in the template:
```jinja2
{% block extra_scripts %}
<script src="{{ url_for('static', path='admin/js/your-page.js') }}"></script>
{% endblock %}
```
Done! ✅

View File

@@ -0,0 +1,410 @@
# Quick Reference: Centralized Logging System
## 🚀 Quick Start
### In Any Page File (Admin/Vendor/Shop):
```javascript
// 1. Use pre-configured logger (RECOMMENDED)
const pageLog = window.LogConfig.loggers.yourPage;
// 2. Or create custom logger
const pageLog = window.LogConfig.createLogger('PAGE-NAME');
// 3. Use it
pageLog.info('Page loaded');
pageLog.debug('Data:', data);
pageLog.warn('Warning message');
pageLog.error('Error occurred', error);
```
---
## 📦 Pre-configured Loggers
### Admin Frontend
```javascript
window.LogConfig.loggers.vendors
window.LogConfig.loggers.vendorTheme
window.LogConfig.loggers.products
window.LogConfig.loggers.orders
window.LogConfig.loggers.users
window.LogConfig.loggers.dashboard
window.LogConfig.loggers.imports
```
### Vendor Frontend
```javascript
window.LogConfig.loggers.dashboard
window.LogConfig.loggers.products
window.LogConfig.loggers.orders
window.LogConfig.loggers.theme
window.LogConfig.loggers.analytics
```
### Shop Frontend
```javascript
window.LogConfig.loggers.catalog
window.LogConfig.loggers.cart
window.LogConfig.loggers.checkout
window.LogConfig.loggers.product
```
---
## 🎨 Basic Logging
```javascript
const log = window.LogConfig.loggers.myPage;
// Simple messages
log.info('Operation started');
log.warn('Slow connection detected');
log.error('Operation failed');
log.debug('Debug data:', { user: 'John', id: 123 });
// With multiple arguments
log.info('User logged in:', username, 'at', timestamp);
```
---
## 🔥 Advanced Features
### API Call Logging
```javascript
const url = '/api/vendors';
// Before request
window.LogConfig.logApiCall('GET', url, null, 'request');
// Make request
const data = await apiClient.get(url);
// After response
window.LogConfig.logApiCall('GET', url, data, 'response');
```
### Performance Logging
```javascript
const start = performance.now();
await expensiveOperation();
const duration = performance.now() - start;
window.LogConfig.logPerformance('Operation Name', duration);
// Output: ⚡ Operation Name took 45ms (fast)
// Output: ⏱️ Operation Name took 250ms (medium)
// Output: 🐌 Operation Name took 750ms (slow)
```
### Error Logging
```javascript
try {
await riskyOperation();
} catch (error) {
window.LogConfig.logError(error, 'Operation Context');
// Automatically logs error message and stack trace
}
```
### Grouped Logging
```javascript
log.group('Loading Data');
log.info('Fetching users...');
log.info('Fetching products...');
log.info('Fetching orders...');
log.groupEnd();
```
### Table Logging
```javascript
const users = [
{ id: 1, name: 'John', status: 'active' },
{ id: 2, name: 'Jane', status: 'inactive' }
];
log.table(users);
```
### Timer Logging
```javascript
log.time('Data Processing');
// ... long operation ...
log.timeEnd('Data Processing');
// Output: ⏱️ [ADMIN:PAGE TIME] Data Processing: 1234.56ms
```
---
## 📋 Complete Page Template
```javascript
// static/admin/js/my-page.js
// 1. Setup logger
const pageLog = window.LogConfig.loggers.myPage;
// 2. Create component
function myPageComponent() {
return {
...data(),
currentPage: 'my-page',
items: [],
loading: false,
async init() {
pageLog.info('Initializing page');
// Prevent double init
if (window._myPageInitialized) return;
window._myPageInitialized = true;
const start = performance.now();
try {
pageLog.group('Loading Data');
await this.loadData();
pageLog.groupEnd();
const duration = performance.now() - start;
window.LogConfig.logPerformance('Page Init', duration);
pageLog.info('Page initialized successfully');
} catch (error) {
window.LogConfig.logError(error, 'Page Init');
}
},
async loadData() {
const url = '/api/my-data';
window.LogConfig.logApiCall('GET', url, null, 'request');
const data = await apiClient.get(url);
window.LogConfig.logApiCall('GET', url, data, 'response');
this.items = data;
}
};
}
pageLog.info('Page module loaded');
```
---
## 🎯 Log Levels
```javascript
LOG_LEVELS = {
ERROR: 1, // Only errors
WARN: 2, // Errors + warnings
INFO: 3, // Errors + warnings + info (default)
DEBUG: 4 // Everything
}
```
**Auto-detected:**
- `localhost` → DEBUG (4)
- Production → Varies by frontend
**Manual override:**
```javascript
const myLog = window.LogConfig.createLogger('MY-PAGE', 4); // Force DEBUG
```
---
## 🔍 Debugging Tips
### Check Current Config
```javascript
console.log(window.LogConfig.frontend); // 'admin' | 'vendor' | 'shop'
console.log(window.LogConfig.environment); // 'development' | 'production'
console.log(window.LogConfig.logLevel); // 1 | 2 | 3 | 4
```
### Test Logger
```javascript
const test = window.LogConfig.createLogger('TEST', 4);
test.info('This is a test');
test.debug('Debug info:', { test: true });
```
### Enable Debug Mode
```javascript
// In browser console
window.LogConfig.loggers.myPage = window.LogConfig.createLogger('MY-PAGE', 4);
// Now reload page to see debug logs
```
---
## ⚠️ Common Mistakes
### ❌ WRONG
```javascript
// Old way (don't do this!)
const LOG_LEVEL = 3;
const log = {
info: (...args) => LOG_LEVEL >= 3 && console.log(...args)
};
// Wrong case
ApiClient.get(url) // Should be apiClient
Logger.info() // Should be window.LogConfig
```
### ✅ CORRECT
```javascript
// New way
const log = window.LogConfig.loggers.myPage;
// Correct case
apiClient.get(url)
window.LogConfig.loggers.myPage.info()
```
---
## 🎨 Console Output Examples
### Development Mode
```
🎛️ Admin Frontend Logging System Initialized
Environment: Development
Log Level: 4 (DEBUG)
[ADMIN:VENDORS INFO] Vendors module loaded
[ADMIN:VENDORS INFO] Initializing vendors page
📤 [ADMIN:API DEBUG] GET /admin/vendors
📥 [ADMIN:API DEBUG] GET /admin/vendors {data...}
⚡ [ADMIN:PERF DEBUG] Load Vendors took 45ms
[ADMIN:VENDORS INFO] Vendors loaded: 25 items
```
### Production Mode (Admin)
```
🎛️ Admin Frontend Logging System Initialized
Environment: Production
Log Level: 2 (WARN)
⚠️ [ADMIN:API WARN] Slow API response: 2.5s
❌ [ADMIN:VENDORS ERROR] Failed to load vendor
```
---
## 📱 Frontend Detection
The system automatically detects which frontend based on URL:
| URL Path | Frontend | Prefix |
|----------|----------|--------|
| `/admin/*` | admin | `ADMIN:` |
| `/vendor/*` | vendor | `VENDOR:` |
| `/shop/*` | shop | `SHOP:` |
---
## 🛠️ Customization
### Add New Pre-configured Logger
Edit `log-config.js`:
```javascript
const adminLoggers = {
// ... existing loggers
myNewPage: createLogger('MY-NEW-PAGE', ACTIVE_LOG_LEVEL)
};
```
### Change Log Level for Frontend
Edit `log-config.js`:
```javascript
const DEFAULT_LOG_LEVELS = {
admin: {
development: LOG_LEVELS.DEBUG,
production: LOG_LEVELS.INFO // Change this
}
};
```
---
## 📚 Full API Reference
```javascript
window.LogConfig = {
// Log levels
LOG_LEVELS: { ERROR: 1, WARN: 2, INFO: 3, DEBUG: 4 },
// Current config
frontend: 'admin' | 'vendor' | 'shop',
environment: 'development' | 'production',
logLevel: 1 | 2 | 3 | 4,
// Default logger
log: { error(), warn(), info(), debug(), group(), groupEnd(), table(), time(), timeEnd() },
// Pre-configured loggers
loggers: { vendors, products, orders, ... },
// Create custom logger
createLogger(prefix, level?),
// Utility functions
logApiCall(method, url, data?, status),
logError(error, context),
logPerformance(operation, duration)
}
```
---
## 💡 Tips & Tricks
1. **Use Pre-configured Loggers**
- Faster to write
- Consistent naming
- Already configured
2. **Log API Calls**
- Easy to track requests
- See request/response data
- Debug API issues
3. **Track Performance**
- Find slow operations
- Optimize bottlenecks
- Monitor page load times
4. **Group Related Logs**
- Cleaner console
- Easier to follow
- Better debugging
5. **Use Debug Level in Development**
- See everything
- Find issues faster
- Learn how code flows
---
## 📖 More Documentation
- [Migration Guide](MIGRATION_GUIDE.md)
- [Alpine.js Template V2](ALPINE_PAGE_TEMPLATE_V2.md)
- [Recommendation Summary](RECOMMENDATION_SUMMARY.md)
---
**Print this page and keep it handy!** 📌
Quick pattern to remember:
```javascript
const log = window.LogConfig.loggers.myPage;
log.info('Hello from my page!');
```
That's it! 🎉

View File

@@ -402,7 +402,7 @@ Value: 123.45.67.89
TTL: 3600
Type: TXT
Name: _letzshop-verify
Name: _wizamart-verify
Value: abc123xyz (verification token from your platform)
TTL: 3600
```

View File

@@ -161,7 +161,7 @@ To prevent domain hijacking, verify the vendor owns the domain:
2. System generates verification token: `abc123xyz`
3. Vendor adds DNS TXT record:
```
Name: _letzshop-verify.customdomain1.com
Name: _wizamart-verify.customdomain1.com
Type: TXT
Value: abc123xyz
```
@@ -177,7 +177,7 @@ def verify_domain(domain_id: int, db: Session):
# Query DNS for TXT record
txt_records = dns.resolver.resolve(
f"_letzshop-verify.{domain.domain}",
f"_wizamart-verify.{domain.domain}",
'TXT'
)

View File

@@ -116,7 +116,7 @@ Name: @
Value: 123.45.67.89 (your server IP)
Type: TXT
Name: _letzshop-verify
Name: _wizamart-verify
Value: abc123xyz
```

View File

@@ -125,7 +125,7 @@ pip install dnspython
import dns.resolver
# Test querying TXT record
answers = dns.resolver.resolve("_letzshop-verify.example.com", "TXT")
answers = dns.resolver.resolve("_wizamart-verify.example.com", "TXT")
for txt in answers:
print(txt.to_text())
```
@@ -259,7 +259,7 @@ TTL: 3600
**Verification TXT Record:**
```
Type: TXT
Name: _letzshop-verify
Name: _wizamart-verify
Value: [token from step 9.1]
TTL: 3600
```
@@ -351,7 +351,7 @@ HAVING COUNT(*) > 1;
**Check DNS propagation:**
```bash
# Check if TXT record exists
dig _letzshop-verify.customdomain1.com TXT
dig _wizamart-verify.customdomain1.com TXT
# Should show verification token
```

View File

@@ -203,7 +203,7 @@ When a vendor wants to use `customdomain1.com`:
Value: 123.45.67.89 (your server IP)
2. Verification TXT Record:
Name: _letzshop-verify
Name: _wizamart-verify
Value: [token from your platform]
3. Wait 5-15 minutes for DNS propagation

View File

@@ -331,7 +331,7 @@ Step 2: Get Instructions
┌────────────────────────────────────┐
│ System returns instructions: │
│ "Add TXT record: │
│ _letzshop-verify.myshop.com │
│ _wizamart-verify.myshop.com │
│ Value: abc123..." │
└────────────────────────────────────┘
@@ -343,7 +343,7 @@ Step 3: Vendor Adds DNS Record
┌────────────────────────────────────┐
│ DNS Provider (GoDaddy/etc) │
│ _letzshop-verify.myshop.com TXT │
│ _wizamart-verify.myshop.com TXT │
│ "abc123..." │
└────────────────────────────────────┘

View File

@@ -356,14 +356,14 @@ GET /api/v1/admin/vendors/domains/1/verification-instructions
},
"txt_record": {
"type": "TXT",
"name": "_letzshop-verify",
"name": "_wizamart-verify",
"value": "abc123xyz...",
"ttl": 3600
}
}
# Step 2: Vendor adds DNS record
# _letzshop-verify.myshop.com TXT "abc123xyz..."
# _wizamart-verify.myshop.com TXT "abc123xyz..."
# Step 3: Verify domain
POST /api/v1/admin/vendors/domains/1/verify

View File

@@ -1,331 +0,0 @@
# Alpine.js Page Template - Quick Reference
## ✅ Correct Page Structure
```javascript
// static/admin/js/your-page.js
// 1. Setup logging (optional but recommended)
const YOUR_PAGE_LOG_LEVEL = 3;
const yourPageLog = {
error: (...args) => YOUR_PAGE_LOG_LEVEL >= 1 && console.error('❌ [YOUR_PAGE ERROR]', ...args),
warn: (...args) => YOUR_PAGE_LOG_LEVEL >= 2 && console.warn('⚠️ [YOUR_PAGE WARN]', ...args),
info: (...args) => YOUR_PAGE_LOG_LEVEL >= 3 && console.info(' [YOUR_PAGE INFO]', ...args),
debug: (...args) => YOUR_PAGE_LOG_LEVEL >= 4 && console.log('🔍 [YOUR_PAGE DEBUG]', ...args)
};
// 2. Create your Alpine.js component
function yourPageComponent() {
return {
// ✅ CRITICAL: Inherit base layout functionality
...data(),
// ✅ CRITICAL: Set page identifier
currentPage: 'your-page',
// Your page-specific state
items: [],
loading: false,
error: null,
// ✅ CRITICAL: Proper initialization with guard
async init() {
yourPageLog.info('=== YOUR PAGE INITIALIZING ===');
// Prevent multiple initializations
if (window._yourPageInitialized) {
yourPageLog.warn('Page already initialized, skipping...');
return;
}
window._yourPageInitialized = true;
// Load your data
await this.loadData();
yourPageLog.info('=== YOUR PAGE INITIALIZATION COMPLETE ===');
},
// Your methods
async loadData() {
yourPageLog.info('Loading data...');
this.loading = true;
this.error = null;
try {
const startTime = Date.now();
// ✅ CRITICAL: Use lowercase apiClient
const response = await apiClient.get('/your/endpoint');
const duration = Date.now() - startTime;
this.items = response.items || [];
yourPageLog.info(`Data loaded in ${duration}ms`, {
count: this.items.length
});
} catch (error) {
yourPageLog.error('Failed to load data:', error);
this.error = error.message;
Utils.showToast('Failed to load data', 'error');
} finally {
this.loading = false;
}
},
// Format date helper (if needed)
formatDate(dateString) {
if (!dateString) return '-';
return Utils.formatDate(dateString);
},
// Your other methods...
};
}
yourPageLog.info('Your page module loaded');
```
---
## 🎯 Checklist for New Pages
### HTML Template
```jinja2
{# app/templates/admin/your-page.html #}
{% extends "admin/base.html" %}
{% block title %}Your Page{% endblock %}
{# ✅ CRITICAL: Link to your Alpine.js component #}
{% block alpine_data %}yourPageComponent(){% endblock %}
{% block content %}
<!-- Your page content -->
{% endblock %}
{% block extra_scripts %}
{# ✅ CRITICAL: Load your JavaScript file #}
<script src="{{ url_for('static', path='admin/js/your-page.js') }}"></script>
{% endblock %}
```
### JavaScript File Checklist
- [ ] ✅ Logging setup (optional)
- [ ] ✅ Function name matches `alpine_data` in template
- [ ]`...data(),` at start of return object
- [ ]`currentPage: 'your-page'` set
- [ ] ✅ Initialization guard in `init()`
- [ ] ✅ Use lowercase `apiClient` for API calls
- [ ] ✅ Use your custom logger (not `Logger`)
- [ ] ✅ Performance tracking with `Date.now()` (optional)
- [ ] ✅ Module loaded log at end
---
## ❌ Common Mistakes to Avoid
### 1. Missing Base Inheritance
```javascript
// ❌ WRONG
function myPage() {
return {
items: [],
// Missing ...data()
};
}
// ✅ CORRECT
function myPage() {
return {
...data(), // Must be first!
items: [],
};
}
```
### 2. Wrong API Client Name
```javascript
// ❌ WRONG - Capital letters
await ApiClient.get('/endpoint');
await API_CLIENT.get('/endpoint');
// ✅ CORRECT - lowercase
await apiClient.get('/endpoint');
```
### 3. Missing Init Guard
```javascript
// ❌ WRONG
async init() {
await this.loadData();
}
// ✅ CORRECT
async init() {
if (window._myPageInitialized) return;
window._myPageInitialized = true;
await this.loadData();
}
```
### 4. Missing currentPage
```javascript
// ❌ WRONG
return {
...data(),
items: [],
// Missing currentPage
};
// ✅ CORRECT
return {
...data(),
currentPage: 'my-page', // Must set this!
items: [],
};
```
---
## 🔧 API Client Pattern
### GET Request
```javascript
try {
const response = await apiClient.get('/endpoint');
this.data = response;
} catch (error) {
console.error('Failed:', error);
Utils.showToast('Failed to load', 'error');
}
```
### POST Request
```javascript
try {
const response = await apiClient.post('/endpoint', {
name: 'value',
// ... data
});
Utils.showToast('Created successfully', 'success');
} catch (error) {
console.error('Failed:', error);
Utils.showToast('Failed to create', 'error');
}
```
### PUT Request
```javascript
try {
const response = await apiClient.put('/endpoint/123', {
name: 'updated value'
});
Utils.showToast('Updated successfully', 'success');
} catch (error) {
console.error('Failed:', error);
Utils.showToast('Failed to update', 'error');
}
```
### DELETE Request
```javascript
try {
await apiClient.delete('/endpoint/123');
Utils.showToast('Deleted successfully', 'success');
await this.reloadData();
} catch (error) {
console.error('Failed:', error);
Utils.showToast('Failed to delete', 'error');
}
```
---
## 🎨 Common UI Patterns
### Loading State
```javascript
async loadData() {
this.loading = true;
try {
const data = await apiClient.get('/endpoint');
this.items = data;
} finally {
this.loading = false;
}
}
```
### Error Handling
```javascript
async loadData() {
this.loading = true;
this.error = null;
try {
const data = await apiClient.get('/endpoint');
this.items = data;
} catch (error) {
this.error = error.message;
Utils.showToast('Failed to load', 'error');
} finally {
this.loading = false;
}
}
```
### Refresh/Reload
```javascript
async refresh() {
console.info('Refreshing...');
await this.loadData();
Utils.showToast('Refreshed successfully', 'success');
}
```
---
## 📚 Available Utilities
### From `init-alpine.js` (via `...data()`)
- `this.dark` - Dark mode state
- `this.toggleTheme()` - Toggle theme
- `this.isSideMenuOpen` - Side menu state
- `this.toggleSideMenu()` - Toggle side menu
- `this.closeSideMenu()` - Close side menu
- `this.isNotificationsMenuOpen` - Notifications menu state
- `this.toggleNotificationsMenu()` - Toggle notifications
- `this.closeNotificationsMenu()` - Close notifications
- `this.isProfileMenuOpen` - Profile menu state
- `this.toggleProfileMenu()` - Toggle profile menu
- `this.closeProfileMenu()` - Close profile menu
- `this.isPagesMenuOpen` - Pages menu state
- `this.togglePagesMenu()` - Toggle pages menu
### From `Utils` (global)
- `Utils.showToast(message, type, duration)` - Show toast notification
- `Utils.formatDate(dateString)` - Format date for display
- `Utils.confirm(message, title)` - Show confirmation dialog (if available)
### From `apiClient` (global)
- `apiClient.get(url)` - GET request
- `apiClient.post(url, data)` - POST request
- `apiClient.put(url, data)` - PUT request
- `apiClient.delete(url)` - DELETE request
---
## 🚀 Quick Start Template Files
Copy these to create a new page:
1. Copy `dashboard.js` → rename to `your-page.js`
2. Replace function name: `adminDashboard()``yourPageComponent()`
3. Update logging prefix: `dashLog``yourPageLog`
4. Update init flag: `_dashboardInitialized``_yourPageInitialized`
5. Update `currentPage`: `'dashboard'``'your-page'`
6. Replace data loading logic with your endpoints
7. Update HTML template to use your function name
Done! ✅

View File

@@ -0,0 +1,235 @@
# Database Quick Reference Card
Quick reference for common database operations. See `DATABASE_SETUP_GUIDE.md` for detailed instructions.
---
## 🚀 Quick Start (New Developer)
```bash
# 1. Clone and setup
git clone <repo>
cd letzshop
python -m venv venv
venv\Scripts\activate # Windows
pip install -r requirements.txt
# 2. Initialize database
alembic upgrade head
python scripts/seed_database.py
# 3. Start server
python -m uvicorn app.main:app --reload
# 4. Login
# http://localhost:8000/admin/login
# Username: admin | Password: admin123
```
---
## 🔄 Reset Database (Clean Slate)
**Windows:**
```powershell
.\scripts\reset_database.ps1
```
**Linux/Mac:**
```bash
./scripts/reset_database.sh
```
**Manual:**
```bash
# 1. Delete migrations
Remove-Item alembic\versions\*.py -Exclude __init__.py
# 2. Delete database
Remove-Item wizamart.db
# 3. Regenerate
alembic revision --autogenerate -m "Initial migration"
alembic upgrade head
python scripts/seed_database.py
```
---
## 📝 Creating Migrations
```bash
# After modifying models in models/database/
# 1. Generate migration
alembic revision --autogenerate -m "Add field to vendor_themes"
# 2. Review the file
# Check: alembic/versions/<id>_add_field_to_vendor_themes.py
# 3. Apply migration
alembic upgrade head
```
---
## 🔍 Checking Status
```bash
# Current migration version
alembic current
# Migration history
alembic history --verbose
# List all tables
python -c "import sqlite3; conn = sqlite3.connect('wizamart.db'); cursor = conn.cursor(); cursor.execute('SELECT name FROM sqlite_master WHERE type=\"table\" ORDER BY name;'); [print(t[0]) for t in cursor.fetchall()]"
# Check specific table
python -c "import sqlite3; conn = sqlite3.connect('wizamart.db'); cursor = conn.cursor(); cursor.execute('PRAGMA table_info(vendor_themes)'); [print(col) for col in cursor.fetchall()]"
```
---
## ⬆️ Upgrading Database
```bash
# Upgrade to latest
alembic upgrade head
# Upgrade one version
alembic upgrade +1
# Upgrade to specific revision
alembic upgrade <revision_id>
```
---
## ⬇️ Rolling Back
```bash
# Downgrade one version
alembic downgrade -1
# Downgrade to specific revision
alembic downgrade <revision_id>
# Downgrade to empty database
alembic downgrade base
```
---
## 🌱 Seeding Data
```bash
# Seed development data
python scripts/seed_database.py
# Manual seed (Python shell)
python
>>> from scripts.seed_database import seed_database
>>> seed_database()
```
---
## 🐛 Common Fixes
### "No such table" Error
```bash
# Check model imported in alembic/env.py
# Then regenerate:
alembic revision --autogenerate -m "Add missing table"
alembic upgrade head
```
### "Database locked"
```bash
# Stop server
taskkill /F /IM python.exe # Windows
pkill python # Linux/Mac
# Try again
alembic upgrade head
```
### "Target database not up to date"
```bash
alembic stamp head
alembic upgrade head
```
### "Multiple head revisions"
```bash
alembic merge heads -m "Merge migrations"
alembic upgrade head
```
---
## 📦 Adding New Models
**Checklist:**
```bash
# 1. Create model file
# models/database/my_model.py
# 2. Import in alembic/env.py
# from models.database.my_model import MyModel
# 3. Generate migration
alembic revision --autogenerate -m "Add MyModel table"
# 4. Review migration file
# alembic/versions/<id>_add_mymodel_table.py
# 5. Apply migration
alembic upgrade head
# 6. Test
python -c "from models.database.my_model import MyModel; print('✓ Import works')"
```
---
## 🔗 Important Files
```
├── alembic/
│ ├── env.py ← Import ALL models here!
│ └── versions/ ← Migration files
├── app/core/
│ └── database.py ← Database connection
├── models/database/ ← SQLAlchemy models
├── scripts/
│ ├── seed_database.py ← Seed script
│ └── reset_database.ps1 ← Reset script
├── alembic.ini ← Alembic config
└── wizamart.db ← SQLite database
```
---
## 🆘 Getting Help
1. Check full guide: `docs/DATABASE_SETUP_GUIDE.md`
2. Check Alembic docs: https://alembic.sqlalchemy.org/
3. Ask team lead
---
## ⚠️ Important Notes
- ✅ Always review migrations before applying
- ✅ Test on local database first
- ✅ Keep migrations in version control
- ✅ Backup before destructive operations
- ❌ Never edit applied migrations
- ❌ Never delete migration files
---
**Last Updated:** 2025-10-27

View File

@@ -0,0 +1,883 @@
# Database Setup & Initialization Guide
## Overview
This guide walks you through setting up the LetzShop database from scratch. Whether you're a new developer joining the team or need to reset your local database, this document covers everything you need to know.
---
## Table of Contents
1. [Prerequisites](#prerequisites)
2. [First-Time Setup](#first-time-setup)
3. [Database Reset (Clean Slate)](#database-reset-clean-slate)
4. [Verifying Your Setup](#verifying-your-setup)
5. [Common Issues & Solutions](#common-issues--solutions)
6. [Database Migrations](#database-migrations)
7. [Seeding Data](#seeding-data)
---
## Prerequisites
### Required Software
- **Python 3.11+**
- **Alembic** (installed via `requirements.txt`)
- **SQLite** (built into Python) or **PostgreSQL** (for production-like setup)
### Environment Setup
```bash
# 1. Clone the repository
git clone <repository-url>
cd letzshop
# 2. Create virtual environment
python -m venv venv
# 3. Activate virtual environment
# Windows:
venv\Scripts\activate
# Linux/Mac:
source venv/bin/activate
# 4. Install dependencies
pip install -r requirements.txt
```
---
## First-Time Setup
### Step 1: Configure Database Connection
Create or update `.env` file in project root:
```env
# Database Configuration
DATABASE_URL=sqlite:///./wizamart.db
# For PostgreSQL (production):
# DATABASE_URL=postgresql://user:password@localhost:5432/letzshop
# Other required settings
SECRET_KEY=your-secret-key-here-change-in-production
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=30
```
---
### Step 2: Initialize Alembic (First Time Only)
**If `alembic/` folder doesn't exist:**
```bash
# Initialize Alembic
alembic init alembic
```
**Configure `alembic.ini`:**
```ini
# alembic.ini
# Find this line and update:
sqlalchemy.url = sqlite:///./wizamart.db
# Or for PostgreSQL:
# sqlalchemy.url = postgresql://user:password@localhost:5432/letzshop
```
**Configure `alembic/env.py`:**
```python
# alembic/env.py
# Add this near the top (after imports)
import sys
from pathlib import Path
# Add project root to Python path
sys.path.append(str(Path(__file__).parents[1]))
# ============================================================================
# CRITICAL: Import ALL database models here
# ============================================================================
from models.database.user import User
from models.database.vendor import Vendor
from models.database.vendor_domain import VendorDomain
from models.database.vendor_theme import VendorTheme
from models.database.customer import Customer
from models.database.team import Team, TeamMember, TeamInvitation
from models.database.product import Product
from models.database.marketplace_product import MarketplaceProduct
from models.database.inventory import Inventory
from models.database.order import Order
from models.database.marketplace_import_job import MarketplaceImportJob
# Import Base
from app.core.database import Base
# Set target metadata
target_metadata = Base.metadata
```
---
### Step 3: Generate Initial Migration
```bash
# Generate migration from your models
alembic revision --autogenerate -m "Initial migration - all tables"
# You should see output listing all detected tables:
# INFO [alembic.autogenerate.compare] Detected added table 'users'
# INFO [alembic.autogenerate.compare] Detected added table 'vendors'
# INFO [alembic.autogenerate.compare] Detected added table 'vendor_themes'
# etc.
```
---
### Step 4: Apply Migration
```bash
# Apply the migration to create all tables
alembic upgrade head
# You should see:
# INFO [alembic.runtime.migration] Running upgrade -> abc123def456, Initial migration - all tables
```
---
### Step 5: Seed Initial Data
Create an admin user and test vendor:
```bash
python scripts/seed_database.py
```
**Or manually:**
```python
# Run Python interactive shell
python
# Then execute:
from app.core.database import SessionLocal
from models.database.user import User
from models.database.vendor import Vendor
from middleware.auth import AuthManager
from datetime import datetime, timezone
db = SessionLocal()
auth_manager = AuthManager()
# Create admin user
admin = User(
username="admin",
email="admin@letzshop.com",
hashed_password=auth_manager.hash_password("admin123"),
role="admin",
is_active=True,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
db.add(admin)
db.flush()
# Create test vendor
vendor = Vendor(
vendor_code="TESTVENDOR",
subdomain="testvendor",
name="Test Vendor",
description="Development test vendor",
owner_user_id=admin.id,
contact_email="contact@testvendor.com",
is_active=True,
is_verified=True,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
db.add(vendor)
db.commit()
print("✅ Admin user and test vendor created!")
db.close()
exit()
```
---
### Step 6: Start the Application
```bash
# Start FastAPI server
python -m uvicorn app.main:app --reload
# Server should start on http://localhost:8000
```
**Test the setup:**
- Admin login: http://localhost:8000/admin/login
- Username: `admin`
- Password: `admin123`
---
## Database Reset (Clean Slate)
Use this when you want to completely reset your database and start fresh.
⚠️ **WARNING:** This will delete ALL data! Make a backup if needed.
---
### Quick Reset Script (Windows PowerShell)
**Create `scripts/reset_database.ps1`:**
```powershell
# scripts/reset_database.ps1
Write-Host "🗑️ Database Reset Script" -ForegroundColor Cyan
Write-Host "This will delete ALL data!" -ForegroundColor Red
$confirm = Read-Host "Type 'YES' to continue"
if ($confirm -ne "YES") {
Write-Host "Aborted." -ForegroundColor Yellow
exit
}
Write-Host ""
Write-Host "Step 1: Backing up current database..." -ForegroundColor Yellow
if (Test-Path wizamart.db) {
$backupName = "wizamart_backup_$(Get-Date -Format 'yyyyMMdd_HHmmss').db"
Copy-Item wizamart.db $backupName
Write-Host "✓ Backup created: $backupName" -ForegroundColor Green
}
Write-Host ""
Write-Host "Step 2: Removing old migrations..." -ForegroundColor Yellow
Remove-Item alembic\versions\*.py -Exclude __init__.py -ErrorAction SilentlyContinue
Remove-Item -Recurse -Force alembic\versions\__pycache__ -ErrorAction SilentlyContinue
# Ensure __init__.py exists
if (-not (Test-Path alembic\versions\__init__.py)) {
New-Item -Path alembic\versions\__init__.py -ItemType File | Out-Null
}
Write-Host "✓ Old migrations removed" -ForegroundColor Green
Write-Host ""
Write-Host "Step 3: Deleting database..." -ForegroundColor Yellow
Remove-Item wizamart.db -ErrorAction SilentlyContinue
Write-Host "✓ Database deleted" -ForegroundColor Green
Write-Host ""
Write-Host "Step 4: Generating new migration..." -ForegroundColor Yellow
alembic revision --autogenerate -m "Initial migration - all tables"
Write-Host "✓ Migration generated" -ForegroundColor Green
Write-Host ""
Write-Host "Step 5: Applying migration..." -ForegroundColor Yellow
alembic upgrade head
Write-Host "✓ Migration applied" -ForegroundColor Green
Write-Host ""
Write-Host "Step 6: Verifying tables..." -ForegroundColor Yellow
python -c @"
import sqlite3
conn = sqlite3.connect('wizamart.db')
cursor = conn.cursor()
cursor.execute('SELECT name FROM sqlite_master WHERE type=\"table\" ORDER BY name;')
tables = [t[0] for t in cursor.fetchall()]
print('\n📊 Tables created:')
for t in tables:
print(f' {t}')
print(f'\n Total: {len(tables)} tables')
if 'vendor_themes' in tables:
print(' vendor_themes table confirmed!')
conn.close()
"@
Write-Host ""
Write-Host "Step 7: Seeding initial data..." -ForegroundColor Yellow
python scripts/seed_database.py
Write-Host ""
Write-Host "✅ Database reset complete!" -ForegroundColor Green
Write-Host ""
Write-Host "Next steps:" -ForegroundColor Cyan
Write-Host " 1. Start server: python -m uvicorn app.main:app --reload" -ForegroundColor White
Write-Host " 2. Login: http://localhost:8000/admin/login" -ForegroundColor White
Write-Host " 3. Username: admin | Password: admin123" -ForegroundColor White
```
**Run it:**
```powershell
# First time only - allow script execution
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# Run the reset
.\scripts\reset_database.ps1
```
---
### Quick Reset Script (Linux/Mac Bash)
**Create `scripts/reset_database.sh`:**
```bash
#!/bin/bash
# scripts/reset_database.sh
echo "🗑️ Database Reset Script"
echo "⚠️ This will delete ALL data!"
read -p "Type 'YES' to continue: " confirm
if [ "$confirm" != "YES" ]; then
echo "Aborted."
exit 1
fi
echo ""
echo "Step 1: Backing up current database..."
if [ -f wizamart.db ]; then
backup_name="wizamart_backup_$(date +%Y%m%d_%H%M%S).db"
cp wizamart.db "$backup_name"
echo "✓ Backup created: $backup_name"
fi
echo ""
echo "Step 2: Removing old migrations..."
find alembic/versions -type f -name "*.py" ! -name "__init__.py" -delete
rm -rf alembic/versions/__pycache__
touch alembic/versions/__init__.py
echo "✓ Old migrations removed"
echo ""
echo "Step 3: Deleting database..."
rm -f wizamart.db
echo "✓ Database deleted"
echo ""
echo "Step 4: Generating new migration..."
alembic revision --autogenerate -m "Initial migration - all tables"
echo "✓ Migration generated"
echo ""
echo "Step 5: Applying migration..."
alembic upgrade head
echo "✓ Migration applied"
echo ""
echo "Step 6: Verifying tables..."
python -c "
import sqlite3
conn = sqlite3.connect('wizamart.db')
cursor = conn.cursor()
cursor.execute('SELECT name FROM sqlite_master WHERE type=\"table\" ORDER BY name;')
tables = [t[0] for t in cursor.fetchall()]
print('\n📊 Tables created:')
for t in tables:
print(f' ✓ {t}')
print(f'\n✅ Total: {len(tables)} tables')
if 'vendor_themes' in tables:
print('✅ vendor_themes table confirmed!')
conn.close()
"
echo ""
echo "Step 7: Seeding initial data..."
python scripts/seed_database.py
echo ""
echo "✅ Database reset complete!"
echo ""
echo "Next steps:"
echo " 1. Start server: python -m uvicorn app.main:app --reload"
echo " 2. Login: http://localhost:8000/admin/login"
echo " 3. Username: admin | Password: admin123"
```
**Run it:**
```bash
chmod +x scripts/reset_database.sh
./scripts/reset_database.sh
```
---
### Manual Reset Steps
If you prefer to run commands manually:
```bash
# 1. Backup (optional)
cp wizamart.db wizamart_backup.db # Windows: copy wizamart.db wizamart_backup.db
# 2. Remove migrations
rm alembic/versions/*.py # Windows: Remove-Item alembic\versions\*.py -Exclude __init__.py
touch alembic/versions/__init__.py # Windows: New-Item -Path alembic\versions\__init__.py
# 3. Delete database
rm wizamart.db # Windows: Remove-Item wizamart.db
# 4. Generate migration
alembic revision --autogenerate -m "Initial migration - all tables"
# 5. Apply migration
alembic upgrade head
# 6. Seed data
python scripts/seed_database.py
```
---
## Verifying Your Setup
### Check Alembic Status
```bash
# Check current migration version
alembic current
# Should show:
# <revision_id> (head)
```
### Verify Tables Exist
**Using Python:**
```python
import sqlite3
conn = sqlite3.connect('wizamart.db')
cursor = conn.cursor()
# List all tables
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;")
tables = cursor.fetchall()
print("📊 Database Tables:")
for table in tables:
print(f"{table[0]}")
# Check specific table structure
cursor.execute("PRAGMA table_info(vendor_themes);")
columns = cursor.fetchall()
print("\n📋 vendor_themes columns:")
for col in columns:
print(f" - {col[1]} ({col[2]})")
conn.close()
```
### Test Database Connection
```bash
# Start Python shell
python
# Test connection
from app.core.database import SessionLocal, engine
from sqlalchemy import text
db = SessionLocal()
result = db.execute(text("SELECT name FROM sqlite_master WHERE type='table';"))
tables = [row[0] for row in result]
print("✅ Database connection successful!")
print(f"📊 Tables found: {len(tables)}")
for table in tables:
print(f" - {table}")
db.close()
exit()
```
### Test Application Startup
```bash
# Start server
python -m uvicorn app.main:app --reload
# Should see:
# INFO: Uvicorn running on http://127.0.0.1:8000
# INFO: Application startup complete.
```
**No errors = successful setup!**
---
## Common Issues & Solutions
### Issue 1: "No such table" Error
**Symptom:**
```
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: vendor_themes
```
**Solution:**
```bash
# 1. Check if model is imported in alembic/env.py
# Open alembic/env.py and verify:
from models.database.vendor_theme import VendorTheme
# 2. Regenerate migration
alembic revision --autogenerate -m "Add missing tables"
# 3. Apply migration
alembic upgrade head
```
---
### Issue 2: "Target database is not up to date"
**Symptom:**
```
ERROR [alembic.util.messaging] Target database is not up to date.
```
**Solution:**
```bash
# Option 1: Stamp to current head
alembic stamp head
# Option 2: Check and upgrade
alembic current # See current version
alembic upgrade head # Upgrade to latest
# Option 3: Full reset (see Database Reset section)
```
---
### Issue 3: Database Locked
**Symptom:**
```
sqlite3.OperationalError: database is locked
```
**Solution:**
```bash
# 1. Stop the FastAPI server (Ctrl+C)
# 2. Kill any Python processes
# Windows:
taskkill /F /IM python.exe
# Linux/Mac:
pkill python
# 3. Close any database browser applications
# 4. Try again
alembic upgrade head
```
---
### Issue 4: Migration File Conflicts
**Symptom:**
```
Multiple head revisions are present
```
**Solution:**
```bash
# Option 1: Merge migrations
alembic merge heads -m "Merge migrations"
alembic upgrade head
# Option 2: Clean reset (recommended for development)
# See Database Reset section above
```
---
### Issue 5: Import Errors in alembic/env.py
**Symptom:**
```
ModuleNotFoundError: No module named 'models'
```
**Solution:**
Check `alembic/env.py` has correct path setup:
```python
# alembic/env.py (top of file)
import sys
from pathlib import Path
# Add project root to Python path
sys.path.append(str(Path(__file__).parents[1]))
```
---
## Database Migrations
### Creating New Migrations
When you add/modify models:
```bash
# 1. Update your model file (e.g., models/database/vendor_theme.py)
# 2. Generate migration
alembic revision --autogenerate -m "Add new field to vendor_themes"
# 3. Review the generated migration
# Check: alembic/versions/<revision>_add_new_field_to_vendor_themes.py
# 4. Apply migration
alembic upgrade head
```
---
### Migration Best Practices
**DO:**
- Review generated migrations before applying
- Use descriptive migration messages
- Test migrations on development database first
- Keep migrations small and focused
- Commit migrations to version control
**DON'T:**
- Edit applied migrations (create new ones instead)
- Delete migration files from version control
- Skip migration review
- Combine unrelated changes in one migration
---
### Rolling Back Migrations
```bash
# Downgrade one version
alembic downgrade -1
# Downgrade to specific revision
alembic downgrade <revision_id>
# Downgrade to base (empty database)
alembic downgrade base
```
---
## Seeding Data
### Development Data Seed
**Create `scripts/seed_database.py`:**
```python
# scripts/seed_database.py
"""
Seed the database with initial development data.
"""
from app.core.database import SessionLocal
from models.database.user import User
from models.database.vendor import Vendor
from app.core.security import get_password_hash
from datetime import datetime, timezone
def seed_database():
"""Seed database with initial data."""
db = SessionLocal()
try:
print("🌱 Seeding database...")
# Check if admin already exists
existing_admin = db.query(User).filter(User.username == "admin").first()
if existing_admin:
print("⚠️ Admin user already exists, skipping...")
return
# Create admin user
admin = User(
username="admin",
email="admin@letzshop.com",
hashed_password=get_password_hash("admin123"),
is_admin=True,
is_active=True,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
db.add(admin)
db.flush()
print(f"✓ Admin user created (ID: {admin.id})")
# Create test vendor
vendor = Vendor(
vendor_code="TESTVENDOR",
subdomain="testvendor",
name="Test Vendor",
description="Development test vendor",
owner_user_id=admin.id,
contact_email="contact@testvendor.com",
is_active=True,
is_verified=True,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
db.add(vendor)
db.flush()
print(f"✓ Test vendor created: {vendor.vendor_code}")
db.commit()
print("\n✅ Database seeded successfully!")
print("\n📝 Login Credentials:")
print(" URL: http://localhost:8000/admin/login")
print(" Username: admin")
print(" Password: admin123")
except Exception as e:
db.rollback()
print(f"❌ Error seeding database: {e}")
raise
finally:
db.close()
if __name__ == "__main__":
seed_database()
```
**Run it:**
```bash
python scripts/seed_database.py
```
---
### Production Data Migration
For production, create separate seed scripts:
```python
# scripts/seed_production.py
# - Create only essential admin user
# - No test data
# - Strong passwords from environment variables
```
---
## Quick Reference
### Essential Commands
```bash
# Check migration status
alembic current
# Generate new migration
alembic revision --autogenerate -m "Description"
# Apply migrations
alembic upgrade head
# Rollback one migration
alembic downgrade -1
# View migration history
alembic history
# Seed database
python scripts/seed_database.py
# Start server
python -m uvicorn app.main:app --reload
```
---
### File Locations
```
project/
├── alembic/
│ ├── versions/ # Migration files here
│ │ └── __init__.py
│ ├── env.py # Alembic configuration (import models here!)
│ └── script.py.mako
├── app/
│ └── core/
│ └── database.py # Database connection
├── models/
│ └── database/ # SQLAlchemy models
│ ├── user.py
│ ├── vendor.py
│ ├── vendor_theme.py
│ └── ...
├── scripts/
│ ├── seed_database.py # Development seed
│ └── reset_database.ps1 # Reset script
├── alembic.ini # Alembic config
├── wizamart.db # SQLite database (gitignored)
└── .env # Environment variables (gitignored)
```
---
## Getting Help
### Internal Resources
- **Architecture Guide:** `docs/PROPER_ARCHITECTURE_GUIDE.md`
- **Exception Handling:** `docs/EXCEPTION_PATTERN_EXPLAINED.md`
- **Admin Feature Guide:** `docs/ADMIN_FEATURE_INTEGRATION_GUIDE.md`
### External Resources
- [Alembic Documentation](https://alembic.sqlalchemy.org/)
- [SQLAlchemy Documentation](https://docs.sqlalchemy.org/)
- [FastAPI Documentation](https://fastapi.tiangolo.com/)
---
## Contributing
When adding new models:
1. ✅ Create the model in `models/database/`
2. ✅ Import it in `alembic/env.py`
3. ✅ Generate migration: `alembic revision --autogenerate -m "Add XYZ model"`
4. ✅ Review the migration file
5. ✅ Test locally: `alembic upgrade head`
6. ✅ Commit both model and migration files
---
**Last Updated:** 2025-10-27
**Maintainer:** Development Team
**Questions?** Contact the team lead or check internal documentation.