Style: Unify cart Continue Shopping button with wishlist style
Updated cart dropdown Continue Shopping buttons to use btn-outline class (matching wishlist style) instead of btn-text across all pages. Changes: - shop.html: btn-text → btn-outline - contact.html: btn-text → btn-outline - product.html: btn-text → btn-outline - about.html: btn-text → btn-outline All cart Continue Shopping buttons now have consistent styling with the wishlist Continue Shopping button (purple outline style).
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
# Performance Optimization Report
|
# Performance Optimization Report
|
||||||
|
|
||||||
## SkyArtShop Website Performance Enhancements
|
## SkyArtShop Website Performance Enhancements
|
||||||
|
|
||||||
**Date:** January 14, 2026
|
**Date:** January 14, 2026
|
||||||
@@ -10,6 +11,7 @@
|
|||||||
## Executive Summary
|
## Executive Summary
|
||||||
|
|
||||||
Successfully optimized SkyArtShop for maximum performance without changing any functionality. All optimizations focus on:
|
Successfully optimized SkyArtShop for maximum performance without changing any functionality. All optimizations focus on:
|
||||||
|
|
||||||
- Faster load times
|
- Faster load times
|
||||||
- Reduced memory usage
|
- Reduced memory usage
|
||||||
- Efficient API calls
|
- Efficient API calls
|
||||||
@@ -17,6 +19,7 @@ Successfully optimized SkyArtShop for maximum performance without changing any f
|
|||||||
- Intelligent caching
|
- Intelligent caching
|
||||||
|
|
||||||
**Key Results:**
|
**Key Results:**
|
||||||
|
|
||||||
- ✅ API response times: 7-12ms (excellent performance)
|
- ✅ API response times: 7-12ms (excellent performance)
|
||||||
- ✅ Backend cache providing 0-41% performance gains
|
- ✅ Backend cache providing 0-41% performance gains
|
||||||
- ✅ All 30+ database indexes verified and optimized
|
- ✅ All 30+ database indexes verified and optimized
|
||||||
@@ -29,6 +32,7 @@ Successfully optimized SkyArtShop for maximum performance without changing any f
|
|||||||
### New Indexes Created
|
### New Indexes Created
|
||||||
|
|
||||||
#### Products Table
|
#### Products Table
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Slug lookup optimization (for product detail pages)
|
-- Slug lookup optimization (for product detail pages)
|
||||||
CREATE INDEX idx_products_slug ON products(slug) WHERE isactive = true;
|
CREATE INDEX idx_products_slug ON products(slug) WHERE isactive = true;
|
||||||
@@ -45,6 +49,7 @@ CREATE INDEX idx_products_active_category ON products(isactive, category)
|
|||||||
**Impact:** Product queries now use optimized index scans instead of full table scans.
|
**Impact:** Product queries now use optimized index scans instead of full table scans.
|
||||||
|
|
||||||
#### Blog Posts Table
|
#### Blog Posts Table
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Slug lookup for individual blog posts
|
-- Slug lookup for individual blog posts
|
||||||
CREATE INDEX idx_blogposts_slug ON blogposts(slug) WHERE ispublished = true;
|
CREATE INDEX idx_blogposts_slug ON blogposts(slug) WHERE ispublished = true;
|
||||||
@@ -57,6 +62,7 @@ CREATE INDEX idx_blogposts_published ON blogposts(ispublished, createdat DESC)
|
|||||||
**Impact:** Blog post queries execute 40-60% faster with index-only scans.
|
**Impact:** Blog post queries execute 40-60% faster with index-only scans.
|
||||||
|
|
||||||
#### Portfolio Projects Table
|
#### Portfolio Projects Table
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Composite index for main portfolio query
|
-- Composite index for main portfolio query
|
||||||
CREATE INDEX idx_portfolio_active_display
|
CREATE INDEX idx_portfolio_active_display
|
||||||
@@ -89,6 +95,7 @@ VACUUM ANALYZE;
|
|||||||
### New File: `/website/public/assets/js/api-cache.js`
|
### New File: `/website/public/assets/js/api-cache.js`
|
||||||
|
|
||||||
**Features:**
|
**Features:**
|
||||||
|
|
||||||
- **Intelligent Caching:** Automatic caching of GET requests with configurable TTL
|
- **Intelligent Caching:** Automatic caching of GET requests with configurable TTL
|
||||||
- **Request Deduplication:** Multiple simultaneous requests to same endpoint only fetch once
|
- **Request Deduplication:** Multiple simultaneous requests to same endpoint only fetch once
|
||||||
- **Memory Management:** Automatic cleanup of expired cache entries every 60 seconds
|
- **Memory Management:** Automatic cleanup of expired cache entries every 60 seconds
|
||||||
@@ -155,6 +162,7 @@ const response = await window.apiCache.fetch('/api/products');
|
|||||||
### Script Loading Order
|
### Script Loading Order
|
||||||
|
|
||||||
All pages now load scripts in this optimized order:
|
All pages now load scripts in this optimized order:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="/assets/js/api-cache.js"></script> <!-- Load cache first -->
|
<script src="/assets/js/api-cache.js"></script> <!-- Load cache first -->
|
||||||
<script src="/assets/js/shop-system.js"></script>
|
<script src="/assets/js/shop-system.js"></script>
|
||||||
@@ -169,11 +177,13 @@ All pages now load scripts in this optimized order:
|
|||||||
The backend already had excellent performance optimizations in place:
|
The backend already had excellent performance optimizations in place:
|
||||||
|
|
||||||
### Existing Backend Caching
|
### Existing Backend Caching
|
||||||
|
|
||||||
- **Route-level caching:** Using `cacheMiddleware(ttl)`
|
- **Route-level caching:** Using `cacheMiddleware(ttl)`
|
||||||
- **Query-level caching:** In-memory cache for SELECT queries
|
- **Query-level caching:** In-memory cache for SELECT queries
|
||||||
- **Response optimization:** Field filtering, pagination, ETag generation
|
- **Response optimization:** Field filtering, pagination, ETag generation
|
||||||
|
|
||||||
### Existing Database Optimizations
|
### Existing Database Optimizations
|
||||||
|
|
||||||
- **Connection pooling:** 10-30 connections with keepAlive
|
- **Connection pooling:** 10-30 connections with keepAlive
|
||||||
- **Query timeout:** 30s safeguard
|
- **Query timeout:** 30s safeguard
|
||||||
- **Prepared statements:** Automatic query plan caching
|
- **Prepared statements:** Automatic query plan caching
|
||||||
@@ -182,6 +192,7 @@ The backend already had excellent performance optimizations in place:
|
|||||||
### API Route Performance
|
### API Route Performance
|
||||||
|
|
||||||
All routes use:
|
All routes use:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Cached for 5-30 minutes depending on endpoint
|
// Cached for 5-30 minutes depending on endpoint
|
||||||
cacheMiddleware(300000), // 5 minutes
|
cacheMiddleware(300000), // 5 minutes
|
||||||
@@ -210,6 +221,7 @@ Automated testing of all endpoints with timing:
|
|||||||
### Test Results
|
### Test Results
|
||||||
|
|
||||||
#### API Endpoints (Cold vs Warm)
|
#### API Endpoints (Cold vs Warm)
|
||||||
|
|
||||||
```
|
```
|
||||||
/api/products
|
/api/products
|
||||||
HTTP 200 | Cold: 12ms | Warm: 7ms | Gain: 41%
|
HTTP 200 | Cold: 12ms | Warm: 7ms | Gain: 41%
|
||||||
@@ -233,6 +245,7 @@ Automated testing of all endpoints with timing:
|
|||||||
**Analysis:** Responses are already extremely fast (7-12ms). Backend cache shows up to 41% improvement on complex queries.
|
**Analysis:** Responses are already extremely fast (7-12ms). Backend cache shows up to 41% improvement on complex queries.
|
||||||
|
|
||||||
#### Page Load Times
|
#### Page Load Times
|
||||||
|
|
||||||
```
|
```
|
||||||
/home | HTTP 200 | Load: 8ms
|
/home | HTTP 200 | Load: 8ms
|
||||||
/shop | HTTP 200 | Load: 7ms
|
/shop | HTTP 200 | Load: 7ms
|
||||||
@@ -249,21 +262,26 @@ Automated testing of all endpoints with timing:
|
|||||||
### All Endpoints Verified ✅
|
### All Endpoints Verified ✅
|
||||||
|
|
||||||
**Products API:**
|
**Products API:**
|
||||||
|
|
||||||
- ✅ GET /api/products - Returns all products with images
|
- ✅ GET /api/products - Returns all products with images
|
||||||
- ✅ GET /api/products/featured - Returns 4 featured products
|
- ✅ GET /api/products/featured - Returns 4 featured products
|
||||||
- ✅ GET /api/products/:id - Returns single product by ID/slug
|
- ✅ GET /api/products/:id - Returns single product by ID/slug
|
||||||
- ✅ GET /api/categories - Returns unique categories
|
- ✅ GET /api/categories - Returns unique categories
|
||||||
|
|
||||||
**Portfolio API:**
|
**Portfolio API:**
|
||||||
|
|
||||||
- ✅ GET /api/portfolio/projects - Returns 6 projects with images
|
- ✅ GET /api/portfolio/projects - Returns 6 projects with images
|
||||||
|
|
||||||
**Blog API:**
|
**Blog API:**
|
||||||
|
|
||||||
- ✅ GET /api/blog/posts - Returns 3 published posts
|
- ✅ GET /api/blog/posts - Returns 3 published posts
|
||||||
|
|
||||||
**Pages API:**
|
**Pages API:**
|
||||||
|
|
||||||
- ✅ GET /api/pages - Returns all active custom pages
|
- ✅ GET /api/pages - Returns all active custom pages
|
||||||
|
|
||||||
### Response Format (Consistent)
|
### Response Format (Consistent)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"success": true,
|
"success": true,
|
||||||
@@ -273,7 +291,9 @@ Automated testing of all endpoints with timing:
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Error Handling
|
### Error Handling
|
||||||
|
|
||||||
All pages implement proper error handling:
|
All pages implement proper error handling:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
try {
|
try {
|
||||||
const response = await window.apiCache.fetch('/api/...');
|
const response = await window.apiCache.fetch('/api/...');
|
||||||
@@ -294,12 +314,14 @@ try {
|
|||||||
### Frontend Memory Management
|
### Frontend Memory Management
|
||||||
|
|
||||||
**API Cache Memory:**
|
**API Cache Memory:**
|
||||||
|
|
||||||
- Maximum 500 cached entries (configurable)
|
- Maximum 500 cached entries (configurable)
|
||||||
- Automatic cleanup every 60 seconds
|
- Automatic cleanup every 60 seconds
|
||||||
- Expired entries removed from memory
|
- Expired entries removed from memory
|
||||||
- Memory-efficient crypto-based cache keys (MD5 hash)
|
- Memory-efficient crypto-based cache keys (MD5 hash)
|
||||||
|
|
||||||
**Cache Statistics Available:**
|
**Cache Statistics Available:**
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Get cache stats in browser console
|
// Get cache stats in browser console
|
||||||
window.apiCache.getStats();
|
window.apiCache.getStats();
|
||||||
@@ -307,6 +329,7 @@ window.apiCache.getStats();
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Manual Cache Control:**
|
**Manual Cache Control:**
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Clear specific entry
|
// Clear specific entry
|
||||||
window.apiCache.clear('/api/products');
|
window.apiCache.clear('/api/products');
|
||||||
@@ -318,6 +341,7 @@ window.clearAPICache();
|
|||||||
### Backend Memory Management
|
### Backend Memory Management
|
||||||
|
|
||||||
Already optimized:
|
Already optimized:
|
||||||
|
|
||||||
- Connection pool limits (max 30)
|
- Connection pool limits (max 30)
|
||||||
- Query cache size limits
|
- Query cache size limits
|
||||||
- Automatic connection recycling
|
- Automatic connection recycling
|
||||||
@@ -328,18 +352,21 @@ Already optimized:
|
|||||||
## 8. Load Time Improvements
|
## 8. Load Time Improvements
|
||||||
|
|
||||||
### Before Optimization (Estimated)
|
### Before Optimization (Estimated)
|
||||||
|
|
||||||
- Cold API calls: ~15-20ms
|
- Cold API calls: ~15-20ms
|
||||||
- Repeated API calls: ~15-20ms (no caching)
|
- Repeated API calls: ~15-20ms (no caching)
|
||||||
- Database queries: Full table scans on some queries
|
- Database queries: Full table scans on some queries
|
||||||
- Multiple simultaneous requests: Duplicate network calls
|
- Multiple simultaneous requests: Duplicate network calls
|
||||||
|
|
||||||
### After Optimization
|
### After Optimization
|
||||||
|
|
||||||
- Cold API calls: 7-12ms (backend cache + indexes)
|
- Cold API calls: 7-12ms (backend cache + indexes)
|
||||||
- Repeated API calls: <1ms (frontend cache hit)
|
- Repeated API calls: <1ms (frontend cache hit)
|
||||||
- Database queries: Index-only scans
|
- Database queries: Index-only scans
|
||||||
- Multiple simultaneous requests: Deduplicated (single fetch)
|
- Multiple simultaneous requests: Deduplicated (single fetch)
|
||||||
|
|
||||||
### Improvement Summary
|
### Improvement Summary
|
||||||
|
|
||||||
- **Database queries:** 40-60% faster with new indexes
|
- **Database queries:** 40-60% faster with new indexes
|
||||||
- **API responses:** Already excellent (7-12ms)
|
- **API responses:** Already excellent (7-12ms)
|
||||||
- **Frontend cache hits:** Near-instant (<1ms)
|
- **Frontend cache hits:** Near-instant (<1ms)
|
||||||
@@ -353,6 +380,7 @@ Already optimized:
|
|||||||
### Browser Console Logging
|
### Browser Console Logging
|
||||||
|
|
||||||
Cache activity is logged for monitoring:
|
Cache activity is logged for monitoring:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
[Cache] FETCH: /api/products
|
[Cache] FETCH: /api/products
|
||||||
[Cache] SET: /api/products (TTL: 300000ms)
|
[Cache] SET: /api/products (TTL: 300000ms)
|
||||||
@@ -364,12 +392,14 @@ Cache activity is logged for monitoring:
|
|||||||
### Performance Monitoring
|
### Performance Monitoring
|
||||||
|
|
||||||
Check cache statistics:
|
Check cache statistics:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// In browser console
|
// In browser console
|
||||||
console.log(window.apiCache.getStats());
|
console.log(window.apiCache.getStats());
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
{
|
{
|
||||||
size: 6, // Cached entries
|
size: 6, // Cached entries
|
||||||
@@ -389,11 +419,13 @@ Output:
|
|||||||
### Database Monitoring
|
### Database Monitoring
|
||||||
|
|
||||||
Verify indexes:
|
Verify indexes:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./test-api-performance.sh
|
./test-api-performance.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Check query performance:
|
Check query performance:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- In PostgreSQL
|
-- In PostgreSQL
|
||||||
EXPLAIN ANALYZE SELECT * FROM products WHERE isactive = true;
|
EXPLAIN ANALYZE SELECT * FROM products WHERE isactive = true;
|
||||||
@@ -404,30 +436,35 @@ EXPLAIN ANALYZE SELECT * FROM products WHERE isactive = true;
|
|||||||
## 10. Best Practices Implemented
|
## 10. Best Practices Implemented
|
||||||
|
|
||||||
### ✅ Database
|
### ✅ Database
|
||||||
|
|
||||||
- Partial indexes with WHERE clauses (smaller, faster)
|
- Partial indexes with WHERE clauses (smaller, faster)
|
||||||
- Composite indexes for multi-column queries
|
- Composite indexes for multi-column queries
|
||||||
- Regular ANALYZE for updated statistics
|
- Regular ANALYZE for updated statistics
|
||||||
- VACUUM for space reclamation
|
- VACUUM for space reclamation
|
||||||
|
|
||||||
### ✅ Caching
|
### ✅ Caching
|
||||||
|
|
||||||
- Appropriate TTL per data type
|
- Appropriate TTL per data type
|
||||||
- Automatic cache invalidation
|
- Automatic cache invalidation
|
||||||
- Request deduplication
|
- Request deduplication
|
||||||
- Memory-efficient storage
|
- Memory-efficient storage
|
||||||
|
|
||||||
### ✅ Frontend
|
### ✅ Frontend
|
||||||
|
|
||||||
- Minimal dependencies
|
- Minimal dependencies
|
||||||
- Progressive enhancement
|
- Progressive enhancement
|
||||||
- Error boundaries
|
- Error boundaries
|
||||||
- User-friendly error messages
|
- User-friendly error messages
|
||||||
|
|
||||||
### ✅ Backend
|
### ✅ Backend
|
||||||
|
|
||||||
- Query timeout safeguards
|
- Query timeout safeguards
|
||||||
- Connection pooling
|
- Connection pooling
|
||||||
- Response compression
|
- Response compression
|
||||||
- Security headers (Helmet.js)
|
- Security headers (Helmet.js)
|
||||||
|
|
||||||
### ✅ Testing
|
### ✅ Testing
|
||||||
|
|
||||||
- Automated performance testing
|
- Automated performance testing
|
||||||
- Cold vs warm comparison
|
- Cold vs warm comparison
|
||||||
- Comprehensive endpoint coverage
|
- Comprehensive endpoint coverage
|
||||||
@@ -440,6 +477,7 @@ EXPLAIN ANALYZE SELECT * FROM products WHERE isactive = true;
|
|||||||
### Cache Management
|
### Cache Management
|
||||||
|
|
||||||
**Clear frontend cache:**
|
**Clear frontend cache:**
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// In browser console
|
// In browser console
|
||||||
window.clearAPICache();
|
window.clearAPICache();
|
||||||
@@ -447,6 +485,7 @@ window.clearAPICache();
|
|||||||
|
|
||||||
**Adjust cache TTL:**
|
**Adjust cache TTL:**
|
||||||
Edit `/website/public/assets/js/api-cache.js`:
|
Edit `/website/public/assets/js/api-cache.js`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
this.ttlConfig = {
|
this.ttlConfig = {
|
||||||
'/api/products': 5 * 60 * 1000, // Change to desired ms
|
'/api/products': 5 * 60 * 1000, // Change to desired ms
|
||||||
@@ -457,12 +496,14 @@ this.ttlConfig = {
|
|||||||
### Database Maintenance
|
### Database Maintenance
|
||||||
|
|
||||||
**Add new indexes:**
|
**Add new indexes:**
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- In backend/optimize-database-indexes.sql
|
-- In backend/optimize-database-indexes.sql
|
||||||
CREATE INDEX CONCURRENTLY idx_name ON table(column);
|
CREATE INDEX CONCURRENTLY idx_name ON table(column);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Update statistics:**
|
**Update statistics:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
PGPASSWORD=SkyArt2025Pass psql -h localhost -U skyartapp -d skyartshop -c "ANALYZE;"
|
PGPASSWORD=SkyArt2025Pass psql -h localhost -U skyartapp -d skyartshop -c "ANALYZE;"
|
||||||
```
|
```
|
||||||
@@ -470,12 +511,14 @@ PGPASSWORD=SkyArt2025Pass psql -h localhost -U skyartapp -d skyartshop -c "ANALY
|
|||||||
### Performance Testing
|
### Performance Testing
|
||||||
|
|
||||||
**Run comprehensive test:**
|
**Run comprehensive test:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /media/pts/Website/SkyArtShop
|
cd /media/pts/Website/SkyArtShop
|
||||||
./test-api-performance.sh
|
./test-api-performance.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
**Monitor PM2 logs:**
|
**Monitor PM2 logs:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pm2 logs skyartshop
|
pm2 logs skyartshop
|
||||||
```
|
```
|
||||||
@@ -485,11 +528,13 @@ pm2 logs skyartshop
|
|||||||
## 12. Files Modified
|
## 12. Files Modified
|
||||||
|
|
||||||
### New Files Created
|
### New Files Created
|
||||||
|
|
||||||
- ✅ `backend/optimize-database-indexes.sql` - Database optimization script
|
- ✅ `backend/optimize-database-indexes.sql` - Database optimization script
|
||||||
- ✅ `website/public/assets/js/api-cache.js` - Frontend caching system
|
- ✅ `website/public/assets/js/api-cache.js` - Frontend caching system
|
||||||
- ✅ `test-api-performance.sh` - Automated testing script
|
- ✅ `test-api-performance.sh` - Automated testing script
|
||||||
|
|
||||||
### Modified Files
|
### Modified Files
|
||||||
|
|
||||||
- ✅ `website/public/portfolio.html` - Added api-cache integration
|
- ✅ `website/public/portfolio.html` - Added api-cache integration
|
||||||
- ✅ `website/public/blog.html` - Added api-cache integration
|
- ✅ `website/public/blog.html` - Added api-cache integration
|
||||||
- ✅ `website/public/shop.html` - Added api-cache integration
|
- ✅ `website/public/shop.html` - Added api-cache integration
|
||||||
@@ -519,6 +564,7 @@ pm2 logs skyartshop
|
|||||||
While current performance is excellent, these could provide marginal gains:
|
While current performance is excellent, these could provide marginal gains:
|
||||||
|
|
||||||
### Advanced Optimizations (Optional)
|
### Advanced Optimizations (Optional)
|
||||||
|
|
||||||
1. **Image Lazy Loading:** Already implemented with `loading="lazy"`
|
1. **Image Lazy Loading:** Already implemented with `loading="lazy"`
|
||||||
2. **CDN Integration:** Consider CDN for static assets
|
2. **CDN Integration:** Consider CDN for static assets
|
||||||
3. **Service Worker:** Add offline caching for PWA
|
3. **Service Worker:** Add offline caching for PWA
|
||||||
@@ -526,6 +572,7 @@ While current performance is excellent, these could provide marginal gains:
|
|||||||
5. **WebP Images:** Convert images to WebP format
|
5. **WebP Images:** Convert images to WebP format
|
||||||
|
|
||||||
### Monitoring (Optional)
|
### Monitoring (Optional)
|
||||||
|
|
||||||
1. **Real User Monitoring:** Track actual user load times
|
1. **Real User Monitoring:** Track actual user load times
|
||||||
2. **Error Tracking:** Sentry or similar for production errors
|
2. **Error Tracking:** Sentry or similar for production errors
|
||||||
3. **Analytics:** Track cache hit rates in production
|
3. **Analytics:** Track cache hit rates in production
|
||||||
@@ -544,6 +591,7 @@ While current performance is excellent, these could provide marginal gains:
|
|||||||
**Performance Grade: A+**
|
**Performance Grade: A+**
|
||||||
|
|
||||||
The SkyArtShop website is now highly optimized for:
|
The SkyArtShop website is now highly optimized for:
|
||||||
|
|
||||||
- Fast load times (< 10ms)
|
- Fast load times (< 10ms)
|
||||||
- Low memory usage (automatic cleanup)
|
- Low memory usage (automatic cleanup)
|
||||||
- Efficient API calls (cached + deduplicated)
|
- Efficient API calls (cached + deduplicated)
|
||||||
|
|||||||
@@ -3,19 +3,23 @@
|
|||||||
## What Was Done
|
## What Was Done
|
||||||
|
|
||||||
### 🗄️ Database (Backend)
|
### 🗄️ Database (Backend)
|
||||||
|
|
||||||
- Added 6 new high-performance indexes
|
- Added 6 new high-performance indexes
|
||||||
- Total: **44 active indexes**
|
- Total: **44 active indexes**
|
||||||
- All tables analyzed and optimized
|
- All tables analyzed and optimized
|
||||||
- Vacuum completed for space reclamation
|
- Vacuum completed for space reclamation
|
||||||
|
|
||||||
### 🚀 Frontend API Cache
|
### 🚀 Frontend API Cache
|
||||||
|
|
||||||
- New file: `api-cache.js` (intelligent caching)
|
- New file: `api-cache.js` (intelligent caching)
|
||||||
- Request deduplication (prevents duplicate calls)
|
- Request deduplication (prevents duplicate calls)
|
||||||
- Auto-cleanup every 60 seconds
|
- Auto-cleanup every 60 seconds
|
||||||
- Custom TTL per endpoint (5-30 minutes)
|
- Custom TTL per endpoint (5-30 minutes)
|
||||||
|
|
||||||
### 📄 Pages Updated
|
### 📄 Pages Updated
|
||||||
|
|
||||||
All pages now use optimized API cache:
|
All pages now use optimized API cache:
|
||||||
|
|
||||||
- [home.html](website/public/home.html)
|
- [home.html](website/public/home.html)
|
||||||
- [shop.html](website/public/shop.html)
|
- [shop.html](website/public/shop.html)
|
||||||
- [portfolio.html](website/public/portfolio.html)
|
- [portfolio.html](website/public/portfolio.html)
|
||||||
@@ -35,6 +39,7 @@ Frontend-Backend: ✅ Verified Working
|
|||||||
## Verify It's Working
|
## Verify It's Working
|
||||||
|
|
||||||
### 1. Browser Console (F12)
|
### 1. Browser Console (F12)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// You should see cache messages like:
|
// You should see cache messages like:
|
||||||
[Cache] FETCH: /api/products
|
[Cache] FETCH: /api/products
|
||||||
@@ -46,11 +51,13 @@ window.apiCache.getStats()
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 2. Network Tab (F12 → Network)
|
### 2. Network Tab (F12 → Network)
|
||||||
|
|
||||||
- First page visit: You'll see API calls
|
- First page visit: You'll see API calls
|
||||||
- Navigate to another page and back: Fewer/no API calls
|
- Navigate to another page and back: Fewer/no API calls
|
||||||
- This means cache is working! 🎉
|
- This means cache is working! 🎉
|
||||||
|
|
||||||
### 3. Command Line Test
|
### 3. Command Line Test
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /media/pts/Website/SkyArtShop
|
cd /media/pts/Website/SkyArtShop
|
||||||
./test-api-performance.sh
|
./test-api-performance.sh
|
||||||
@@ -59,12 +66,14 @@ cd /media/pts/Website/SkyArtShop
|
|||||||
## How It Works
|
## How It Works
|
||||||
|
|
||||||
### Before Optimization
|
### Before Optimization
|
||||||
|
|
||||||
```
|
```
|
||||||
User → Page → fetch('/api/products') → Server → Database → Response
|
User → Page → fetch('/api/products') → Server → Database → Response
|
||||||
User → Page → fetch('/api/products') → Server → Database → Response (duplicate!)
|
User → Page → fetch('/api/products') → Server → Database → Response (duplicate!)
|
||||||
```
|
```
|
||||||
|
|
||||||
### After Optimization
|
### After Optimization
|
||||||
|
|
||||||
```
|
```
|
||||||
User → Page → apiCache.fetch('/api/products') → Server → Database → Response → CACHE
|
User → Page → apiCache.fetch('/api/products') → Server → Database → Response → CACHE
|
||||||
User → Page → apiCache.fetch('/api/products') → CACHE (instant!) ⚡
|
User → Page → apiCache.fetch('/api/products') → CACHE (instant!) ⚡
|
||||||
@@ -73,13 +82,16 @@ User → Page → apiCache.fetch('/api/products') → CACHE (instant!) ⚡
|
|||||||
## Cache Control
|
## Cache Control
|
||||||
|
|
||||||
### Clear Cache Manually
|
### Clear Cache Manually
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// In browser console (F12)
|
// In browser console (F12)
|
||||||
window.clearAPICache(); // Clears all cached data
|
window.clearAPICache(); // Clears all cached data
|
||||||
```
|
```
|
||||||
|
|
||||||
### Adjust Cache Time
|
### Adjust Cache Time
|
||||||
|
|
||||||
Edit `website/public/assets/js/api-cache.js`:
|
Edit `website/public/assets/js/api-cache.js`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
this.ttlConfig = {
|
this.ttlConfig = {
|
||||||
'/api/products': 5 * 60 * 1000, // 5 minutes (default)
|
'/api/products': 5 * 60 * 1000, // 5 minutes (default)
|
||||||
@@ -90,6 +102,7 @@ this.ttlConfig = {
|
|||||||
## Monitoring
|
## Monitoring
|
||||||
|
|
||||||
### Check Active Indexes
|
### Check Active Indexes
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
SELECT indexname FROM pg_indexes
|
SELECT indexname FROM pg_indexes
|
||||||
WHERE schemaname = 'public'
|
WHERE schemaname = 'public'
|
||||||
@@ -97,6 +110,7 @@ AND indexname LIKE 'idx_%';
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Watch PM2 Logs
|
### Watch PM2 Logs
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pm2 logs skyartshop
|
pm2 logs skyartshop
|
||||||
```
|
```
|
||||||
@@ -111,6 +125,7 @@ pm2 logs skyartshop
|
|||||||
## What Was NOT Changed
|
## What Was NOT Changed
|
||||||
|
|
||||||
✅ **Zero functionality changes**
|
✅ **Zero functionality changes**
|
||||||
|
|
||||||
- All features work exactly the same
|
- All features work exactly the same
|
||||||
- User experience unchanged
|
- User experience unchanged
|
||||||
- Admin panel unchanged
|
- Admin panel unchanged
|
||||||
@@ -122,16 +137,19 @@ pm2 logs skyartshop
|
|||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Cache not working?
|
### Cache not working?
|
||||||
|
|
||||||
1. Hard refresh: `Ctrl+Shift+R`
|
1. Hard refresh: `Ctrl+Shift+R`
|
||||||
2. Check console for errors
|
2. Check console for errors
|
||||||
3. Verify api-cache.js loads: `curl http://localhost:5000/assets/js/api-cache.js`
|
3. Verify api-cache.js loads: `curl http://localhost:5000/assets/js/api-cache.js`
|
||||||
|
|
||||||
### Slow queries?
|
### Slow queries?
|
||||||
|
|
||||||
1. Run: `./test-api-performance.sh`
|
1. Run: `./test-api-performance.sh`
|
||||||
2. Check if responses are >50ms
|
2. Check if responses are >50ms
|
||||||
3. Review database indexes
|
3. Review database indexes
|
||||||
|
|
||||||
### Pages not loading?
|
### Pages not loading?
|
||||||
|
|
||||||
1. Check PM2: `pm2 status`
|
1. Check PM2: `pm2 status`
|
||||||
2. Check logs: `pm2 logs skyartshop`
|
2. Check logs: `pm2 logs skyartshop`
|
||||||
3. Restart: `pm2 restart skyartshop`
|
3. Restart: `pm2 restart skyartshop`
|
||||||
|
|||||||
@@ -119,7 +119,7 @@
|
|||||||
<a href="/checkout" class="btn-primary-full"
|
<a href="/checkout" class="btn-primary-full"
|
||||||
>Proceed to Checkout</a
|
>Proceed to Checkout</a
|
||||||
>
|
>
|
||||||
<a href="/shop" class="btn-text">Continue Shopping</a>
|
<a href="/shop" class="btn-outline">Continue Shopping</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ class APICache {
|
|||||||
|
|
||||||
// Custom TTL for different endpoints
|
// Custom TTL for different endpoints
|
||||||
this.ttlConfig = {
|
this.ttlConfig = {
|
||||||
'/api/products': 5 * 60 * 1000, // 5 min
|
"/api/products": 5 * 60 * 1000, // 5 min
|
||||||
'/api/products/featured': 10 * 60 * 1000, // 10 min
|
"/api/products/featured": 10 * 60 * 1000, // 10 min
|
||||||
'/api/categories': 30 * 60 * 1000, // 30 min
|
"/api/categories": 30 * 60 * 1000, // 30 min
|
||||||
'/api/portfolio/projects': 10 * 60 * 1000, // 10 min
|
"/api/portfolio/projects": 10 * 60 * 1000, // 10 min
|
||||||
'/api/blog/posts': 5 * 60 * 1000, // 5 min
|
"/api/blog/posts": 5 * 60 * 1000, // 5 min
|
||||||
'/api/pages': 10 * 60 * 1000, // 10 min
|
"/api/pages": 10 * 60 * 1000, // 10 min
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start periodic cleanup
|
// Start periodic cleanup
|
||||||
@@ -79,7 +79,7 @@ class APICache {
|
|||||||
this.cache.set(key, {
|
this.cache.set(key, {
|
||||||
data,
|
data,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
ttl
|
ttl,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`[Cache] SET: ${url} (TTL: ${ttl}ms)`);
|
console.log(`[Cache] SET: ${url} (TTL: ${ttl}ms)`);
|
||||||
@@ -99,7 +99,7 @@ class APICache {
|
|||||||
*/
|
*/
|
||||||
clearAll() {
|
clearAll() {
|
||||||
this.cache.clear();
|
this.cache.clear();
|
||||||
console.log('[Cache] CLEARED ALL');
|
console.log("[Cache] CLEARED ALL");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,7 +107,7 @@ class APICache {
|
|||||||
*/
|
*/
|
||||||
async fetch(url, options = {}) {
|
async fetch(url, options = {}) {
|
||||||
// Only cache GET requests
|
// Only cache GET requests
|
||||||
if (options.method && options.method !== 'GET') {
|
if (options.method && options.method !== "GET") {
|
||||||
return fetch(url, options);
|
return fetch(url, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ class APICache {
|
|||||||
json: async () => cached,
|
json: async () => cached,
|
||||||
ok: true,
|
ok: true,
|
||||||
status: 200,
|
status: 200,
|
||||||
fromCache: true
|
fromCache: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ class APICache {
|
|||||||
// Make new request
|
// Make new request
|
||||||
console.log(`[Cache] FETCH: ${url}`);
|
console.log(`[Cache] FETCH: ${url}`);
|
||||||
const requestPromise = fetch(url, options)
|
const requestPromise = fetch(url, options)
|
||||||
.then(async response => {
|
.then(async (response) => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP ${response.status}`);
|
throw new Error(`HTTP ${response.status}`);
|
||||||
}
|
}
|
||||||
@@ -148,10 +148,10 @@ class APICache {
|
|||||||
json: async () => data,
|
json: async () => data,
|
||||||
ok: true,
|
ok: true,
|
||||||
status: response.status,
|
status: response.status,
|
||||||
fromCache: false
|
fromCache: false,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch((error) => {
|
||||||
// Remove from pending on error
|
// Remove from pending on error
|
||||||
this.pendingRequests.delete(url);
|
this.pendingRequests.delete(url);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -192,8 +192,8 @@ class APICache {
|
|||||||
url: key,
|
url: key,
|
||||||
age: Date.now() - entry.timestamp,
|
age: Date.now() - entry.timestamp,
|
||||||
ttl: entry.ttl,
|
ttl: entry.ttl,
|
||||||
valid: this.isValid(entry)
|
valid: this.isValid(entry),
|
||||||
}))
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,7 +127,7 @@
|
|||||||
<a href="/checkout" class="btn-primary-full"
|
<a href="/checkout" class="btn-primary-full"
|
||||||
>Proceed to Checkout</a
|
>Proceed to Checkout</a
|
||||||
>
|
>
|
||||||
<a href="/shop" class="btn-text">Continue Shopping</a>
|
<a href="/shop" class="btn-outline">Continue Shopping</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -153,7 +153,7 @@
|
|||||||
<a href="/checkout" class="btn-primary-full"
|
<a href="/checkout" class="btn-primary-full"
|
||||||
>Proceed to Checkout</a
|
>Proceed to Checkout</a
|
||||||
>
|
>
|
||||||
<a href="/shop" class="btn-text">Continue Shopping</a>
|
<a href="/shop" class="btn-outline">Continue Shopping</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -259,7 +259,9 @@
|
|||||||
"Fetching product from API:",
|
"Fetching product from API:",
|
||||||
`/api/products/${productId}`
|
`/api/products/${productId}`
|
||||||
);
|
);
|
||||||
const response = await window.apiCache.fetch(`/api/products/${productId}`);
|
const response = await window.apiCache.fetch(
|
||||||
|
`/api/products/${productId}`
|
||||||
|
);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
console.log("API response:", data);
|
console.log("API response:", data);
|
||||||
|
|
||||||
|
|||||||
@@ -684,7 +684,7 @@
|
|||||||
<a href="/checkout" class="btn-primary-full"
|
<a href="/checkout" class="btn-primary-full"
|
||||||
>Proceed to Checkout</a
|
>Proceed to Checkout</a
|
||||||
>
|
>
|
||||||
<a href="/shop" class="btn-text">Continue Shopping</a>
|
<a href="/shop" class="btn-outline">Continue Shopping</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user