From f818fff3a55cb7372c0fbedffeba6851cd39c7e6 Mon Sep 17 00:00:00 2001 From: Local Server Date: Wed, 14 Jan 2026 20:47:32 -0600 Subject: [PATCH] Style: Unify cart Continue Shopping button with wishlist style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- PERFORMANCE_OPTIMIZATION.md | 48 ++++++++++++++++ PERFORMANCE_QUICK_REF.md | 20 ++++++- website/public/about.html | 2 +- website/public/assets/js/api-cache.js | 80 +++++++++++++-------------- website/public/contact.html | 2 +- website/public/product.html | 6 +- website/public/shop.html | 2 +- 7 files changed, 114 insertions(+), 46 deletions(-) diff --git a/PERFORMANCE_OPTIMIZATION.md b/PERFORMANCE_OPTIMIZATION.md index f6dda15..5bebf1b 100644 --- a/PERFORMANCE_OPTIMIZATION.md +++ b/PERFORMANCE_OPTIMIZATION.md @@ -1,4 +1,5 @@ # Performance Optimization Report + ## SkyArtShop Website Performance Enhancements **Date:** January 14, 2026 @@ -10,6 +11,7 @@ ## Executive Summary Successfully optimized SkyArtShop for maximum performance without changing any functionality. All optimizations focus on: + - Faster load times - Reduced memory usage - Efficient API calls @@ -17,6 +19,7 @@ Successfully optimized SkyArtShop for maximum performance without changing any f - Intelligent caching **Key Results:** + - ✅ API response times: 7-12ms (excellent performance) - ✅ Backend cache providing 0-41% performance gains - ✅ All 30+ database indexes verified and optimized @@ -29,6 +32,7 @@ Successfully optimized SkyArtShop for maximum performance without changing any f ### New Indexes Created #### Products Table + ```sql -- Slug lookup optimization (for product detail pages) 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. #### Blog Posts Table + ```sql -- Slug lookup for individual blog posts 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. #### Portfolio Projects Table + ```sql -- Composite index for main portfolio query CREATE INDEX idx_portfolio_active_display @@ -89,6 +95,7 @@ VACUUM ANALYZE; ### New File: `/website/public/assets/js/api-cache.js` **Features:** + - **Intelligent Caching:** Automatic caching of GET requests with configurable TTL - **Request Deduplication:** Multiple simultaneous requests to same endpoint only fetch once - **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 All pages now load scripts in this optimized order: + ```html @@ -169,11 +177,13 @@ All pages now load scripts in this optimized order: The backend already had excellent performance optimizations in place: ### Existing Backend Caching + - **Route-level caching:** Using `cacheMiddleware(ttl)` - **Query-level caching:** In-memory cache for SELECT queries - **Response optimization:** Field filtering, pagination, ETag generation ### Existing Database Optimizations + - **Connection pooling:** 10-30 connections with keepAlive - **Query timeout:** 30s safeguard - **Prepared statements:** Automatic query plan caching @@ -182,6 +192,7 @@ The backend already had excellent performance optimizations in place: ### API Route Performance All routes use: + ```javascript // Cached for 5-30 minutes depending on endpoint cacheMiddleware(300000), // 5 minutes @@ -210,6 +221,7 @@ Automated testing of all endpoints with timing: ### Test Results #### API Endpoints (Cold vs Warm) + ``` /api/products 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. #### Page Load Times + ``` /home | HTTP 200 | Load: 8ms /shop | HTTP 200 | Load: 7ms @@ -249,21 +262,26 @@ Automated testing of all endpoints with timing: ### All Endpoints Verified ✅ **Products API:** + - ✅ GET /api/products - Returns all products with images - ✅ GET /api/products/featured - Returns 4 featured products - ✅ GET /api/products/:id - Returns single product by ID/slug - ✅ GET /api/categories - Returns unique categories **Portfolio API:** + - ✅ GET /api/portfolio/projects - Returns 6 projects with images **Blog API:** + - ✅ GET /api/blog/posts - Returns 3 published posts **Pages API:** + - ✅ GET /api/pages - Returns all active custom pages ### Response Format (Consistent) + ```json { "success": true, @@ -273,7 +291,9 @@ Automated testing of all endpoints with timing: ``` ### Error Handling + All pages implement proper error handling: + ```javascript try { const response = await window.apiCache.fetch('/api/...'); @@ -294,12 +314,14 @@ try { ### Frontend Memory Management **API Cache Memory:** + - Maximum 500 cached entries (configurable) - Automatic cleanup every 60 seconds - Expired entries removed from memory - Memory-efficient crypto-based cache keys (MD5 hash) **Cache Statistics Available:** + ```javascript // Get cache stats in browser console window.apiCache.getStats(); @@ -307,6 +329,7 @@ window.apiCache.getStats(); ``` **Manual Cache Control:** + ```javascript // Clear specific entry window.apiCache.clear('/api/products'); @@ -318,6 +341,7 @@ window.clearAPICache(); ### Backend Memory Management Already optimized: + - Connection pool limits (max 30) - Query cache size limits - Automatic connection recycling @@ -328,18 +352,21 @@ Already optimized: ## 8. Load Time Improvements ### Before Optimization (Estimated) + - Cold API calls: ~15-20ms - Repeated API calls: ~15-20ms (no caching) - Database queries: Full table scans on some queries - Multiple simultaneous requests: Duplicate network calls ### After Optimization + - Cold API calls: 7-12ms (backend cache + indexes) - Repeated API calls: <1ms (frontend cache hit) - Database queries: Index-only scans - Multiple simultaneous requests: Deduplicated (single fetch) ### Improvement Summary + - **Database queries:** 40-60% faster with new indexes - **API responses:** Already excellent (7-12ms) - **Frontend cache hits:** Near-instant (<1ms) @@ -353,6 +380,7 @@ Already optimized: ### Browser Console Logging Cache activity is logged for monitoring: + ```javascript [Cache] FETCH: /api/products [Cache] SET: /api/products (TTL: 300000ms) @@ -364,12 +392,14 @@ Cache activity is logged for monitoring: ### Performance Monitoring Check cache statistics: + ```javascript // In browser console console.log(window.apiCache.getStats()); ``` Output: + ```javascript { size: 6, // Cached entries @@ -389,11 +419,13 @@ Output: ### Database Monitoring Verify indexes: + ```bash ./test-api-performance.sh ``` Check query performance: + ```sql -- In PostgreSQL EXPLAIN ANALYZE SELECT * FROM products WHERE isactive = true; @@ -404,30 +436,35 @@ EXPLAIN ANALYZE SELECT * FROM products WHERE isactive = true; ## 10. Best Practices Implemented ### ✅ Database + - Partial indexes with WHERE clauses (smaller, faster) - Composite indexes for multi-column queries - Regular ANALYZE for updated statistics - VACUUM for space reclamation ### ✅ Caching + - Appropriate TTL per data type - Automatic cache invalidation - Request deduplication - Memory-efficient storage ### ✅ Frontend + - Minimal dependencies - Progressive enhancement - Error boundaries - User-friendly error messages ### ✅ Backend + - Query timeout safeguards - Connection pooling - Response compression - Security headers (Helmet.js) ### ✅ Testing + - Automated performance testing - Cold vs warm comparison - Comprehensive endpoint coverage @@ -440,6 +477,7 @@ EXPLAIN ANALYZE SELECT * FROM products WHERE isactive = true; ### Cache Management **Clear frontend cache:** + ```javascript // In browser console window.clearAPICache(); @@ -447,6 +485,7 @@ window.clearAPICache(); **Adjust cache TTL:** Edit `/website/public/assets/js/api-cache.js`: + ```javascript this.ttlConfig = { '/api/products': 5 * 60 * 1000, // Change to desired ms @@ -457,12 +496,14 @@ this.ttlConfig = { ### Database Maintenance **Add new indexes:** + ```sql -- In backend/optimize-database-indexes.sql CREATE INDEX CONCURRENTLY idx_name ON table(column); ``` **Update statistics:** + ```bash 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 **Run comprehensive test:** + ```bash cd /media/pts/Website/SkyArtShop ./test-api-performance.sh ``` **Monitor PM2 logs:** + ```bash pm2 logs skyartshop ``` @@ -485,11 +528,13 @@ pm2 logs skyartshop ## 12. Files Modified ### New Files Created + - ✅ `backend/optimize-database-indexes.sql` - Database optimization script - ✅ `website/public/assets/js/api-cache.js` - Frontend caching system - ✅ `test-api-performance.sh` - Automated testing script ### Modified Files + - ✅ `website/public/portfolio.html` - Added api-cache integration - ✅ `website/public/blog.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: ### Advanced Optimizations (Optional) + 1. **Image Lazy Loading:** Already implemented with `loading="lazy"` 2. **CDN Integration:** Consider CDN for static assets 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 ### Monitoring (Optional) + 1. **Real User Monitoring:** Track actual user load times 2. **Error Tracking:** Sentry or similar for production errors 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+** The SkyArtShop website is now highly optimized for: + - Fast load times (< 10ms) - Low memory usage (automatic cleanup) - Efficient API calls (cached + deduplicated) diff --git a/PERFORMANCE_QUICK_REF.md b/PERFORMANCE_QUICK_REF.md index 7336b14..f23c256 100644 --- a/PERFORMANCE_QUICK_REF.md +++ b/PERFORMANCE_QUICK_REF.md @@ -3,21 +3,25 @@ ## What Was Done ### 🗄️ Database (Backend) + - Added 6 new high-performance indexes - Total: **44 active indexes** - All tables analyzed and optimized - Vacuum completed for space reclamation ### 🚀 Frontend API Cache + - New file: `api-cache.js` (intelligent caching) - Request deduplication (prevents duplicate calls) - Auto-cleanup every 60 seconds - Custom TTL per endpoint (5-30 minutes) ### 📄 Pages Updated + All pages now use optimized API cache: + - [home.html](website/public/home.html) -- [shop.html](website/public/shop.html) +- [shop.html](website/public/shop.html) - [portfolio.html](website/public/portfolio.html) - [blog.html](website/public/blog.html) - [product.html](website/public/product.html) @@ -35,6 +39,7 @@ Frontend-Backend: ✅ Verified Working ## Verify It's Working ### 1. Browser Console (F12) + ```javascript // You should see cache messages like: [Cache] FETCH: /api/products @@ -46,11 +51,13 @@ window.apiCache.getStats() ``` ### 2. Network Tab (F12 → Network) + - First page visit: You'll see API calls - Navigate to another page and back: Fewer/no API calls - This means cache is working! 🎉 ### 3. Command Line Test + ```bash cd /media/pts/Website/SkyArtShop ./test-api-performance.sh @@ -59,12 +66,14 @@ cd /media/pts/Website/SkyArtShop ## How It Works ### Before Optimization + ``` User → Page → fetch('/api/products') → Server → Database → Response User → Page → fetch('/api/products') → Server → Database → Response (duplicate!) ``` ### After Optimization + ``` User → Page → apiCache.fetch('/api/products') → Server → Database → Response → CACHE User → Page → apiCache.fetch('/api/products') → CACHE (instant!) ⚡ @@ -73,13 +82,16 @@ User → Page → apiCache.fetch('/api/products') → CACHE (instant!) ⚡ ## Cache Control ### Clear Cache Manually + ```javascript // In browser console (F12) window.clearAPICache(); // Clears all cached data ``` ### Adjust Cache Time + Edit `website/public/assets/js/api-cache.js`: + ```javascript this.ttlConfig = { '/api/products': 5 * 60 * 1000, // 5 minutes (default) @@ -90,6 +102,7 @@ this.ttlConfig = { ## Monitoring ### Check Active Indexes + ```sql SELECT indexname FROM pg_indexes WHERE schemaname = 'public' @@ -97,6 +110,7 @@ AND indexname LIKE 'idx_%'; ``` ### Watch PM2 Logs + ```bash pm2 logs skyartshop ``` @@ -111,6 +125,7 @@ pm2 logs skyartshop ## What Was NOT Changed ✅ **Zero functionality changes** + - All features work exactly the same - User experience unchanged - Admin panel unchanged @@ -122,16 +137,19 @@ pm2 logs skyartshop ## Troubleshooting ### Cache not working? + 1. Hard refresh: `Ctrl+Shift+R` 2. Check console for errors 3. Verify api-cache.js loads: `curl http://localhost:5000/assets/js/api-cache.js` ### Slow queries? + 1. Run: `./test-api-performance.sh` 2. Check if responses are >50ms 3. Review database indexes ### Pages not loading? + 1. Check PM2: `pm2 status` 2. Check logs: `pm2 logs skyartshop` 3. Restart: `pm2 restart skyartshop` diff --git a/website/public/about.html b/website/public/about.html index e05ff8f..83a1795 100644 --- a/website/public/about.html +++ b/website/public/about.html @@ -119,7 +119,7 @@ Proceed to Checkout - Continue Shopping + Continue Shopping diff --git a/website/public/assets/js/api-cache.js b/website/public/assets/js/api-cache.js index a65d188..0a4f107 100644 --- a/website/public/assets/js/api-cache.js +++ b/website/public/assets/js/api-cache.js @@ -11,28 +11,28 @@ class APICache { this.cache = new Map(); this.pendingRequests = new Map(); this.defaultTTL = 5 * 60 * 1000; // 5 minutes default - + // Custom TTL for different endpoints this.ttlConfig = { - '/api/products': 5 * 60 * 1000, // 5 min - '/api/products/featured': 10 * 60 * 1000, // 10 min - '/api/categories': 30 * 60 * 1000, // 30 min - '/api/portfolio/projects': 10 * 60 * 1000, // 10 min - '/api/blog/posts': 5 * 60 * 1000, // 5 min - '/api/pages': 10 * 60 * 1000, // 10 min + "/api/products": 5 * 60 * 1000, // 5 min + "/api/products/featured": 10 * 60 * 1000, // 10 min + "/api/categories": 30 * 60 * 1000, // 30 min + "/api/portfolio/projects": 10 * 60 * 1000, // 10 min + "/api/blog/posts": 5 * 60 * 1000, // 5 min + "/api/pages": 10 * 60 * 1000, // 10 min }; - + // Start periodic cleanup this.startCleanup(); } - + /** * Get cache key from URL */ getCacheKey(url) { return url; } - + /** * Get TTL for specific endpoint */ @@ -45,46 +45,46 @@ class APICache { } return this.defaultTTL; } - + /** * Check if cache entry is valid */ isValid(entry) { return entry && Date.now() - entry.timestamp < entry.ttl; } - + /** * Get from cache */ get(url) { const key = this.getCacheKey(url); const entry = this.cache.get(key); - + if (this.isValid(entry)) { console.log(`[Cache] HIT: ${url}`); return entry.data; } - + console.log(`[Cache] MISS: ${url}`); return null; } - + /** * Set cache entry */ set(url, data) { const key = this.getCacheKey(url); const ttl = this.getTTL(url); - + this.cache.set(key, { data, timestamp: Date.now(), - ttl + ttl, }); - + console.log(`[Cache] SET: ${url} (TTL: ${ttl}ms)`); } - + /** * Clear specific cache entry */ @@ -93,24 +93,24 @@ class APICache { this.cache.delete(key); console.log(`[Cache] CLEAR: ${url}`); } - + /** * Clear all cache */ clearAll() { this.cache.clear(); - console.log('[Cache] CLEARED ALL'); + console.log("[Cache] CLEARED ALL"); } - + /** * Fetch with caching and deduplication */ async fetch(url, options = {}) { // Only cache GET requests - if (options.method && options.method !== 'GET') { + if (options.method && options.method !== "GET") { return fetch(url, options); } - + // Check cache first const cached = this.get(url); if (cached) { @@ -118,51 +118,51 @@ class APICache { json: async () => cached, ok: true, status: 200, - fromCache: true + fromCache: true, }; } - + // Check if request is already pending if (this.pendingRequests.has(url)) { console.log(`[Cache] DEDUP: ${url} - Waiting for pending request`); return this.pendingRequests.get(url); } - + // Make new request console.log(`[Cache] FETCH: ${url}`); const requestPromise = fetch(url, options) - .then(async response => { + .then(async (response) => { if (!response.ok) { throw new Error(`HTTP ${response.status}`); } - + const data = await response.json(); - + // Cache successful response this.set(url, data); - + // Remove from pending this.pendingRequests.delete(url); - + return { json: async () => data, ok: true, status: response.status, - fromCache: false + fromCache: false, }; }) - .catch(error => { + .catch((error) => { // Remove from pending on error this.pendingRequests.delete(url); throw error; }); - + // Store as pending this.pendingRequests.set(url, requestPromise); - + return requestPromise; } - + /** * Periodic cleanup of expired entries */ @@ -180,7 +180,7 @@ class APICache { } }, 60000); // Run every minute } - + /** * Get cache statistics */ @@ -192,8 +192,8 @@ class APICache { url: key, age: Date.now() - entry.timestamp, ttl: entry.ttl, - valid: this.isValid(entry) - })) + valid: this.isValid(entry), + })), }; } } diff --git a/website/public/contact.html b/website/public/contact.html index e861ebb..62c4ea1 100644 --- a/website/public/contact.html +++ b/website/public/contact.html @@ -127,7 +127,7 @@ Proceed to Checkout - Continue Shopping + Continue Shopping diff --git a/website/public/product.html b/website/public/product.html index 9daec99..09e8fac 100644 --- a/website/public/product.html +++ b/website/public/product.html @@ -153,7 +153,7 @@ Proceed to Checkout - Continue Shopping + Continue Shopping @@ -259,7 +259,9 @@ "Fetching product from API:", `/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(); console.log("API response:", data); diff --git a/website/public/shop.html b/website/public/shop.html index 003ef23..8e6f89e 100644 --- a/website/public/shop.html +++ b/website/public/shop.html @@ -684,7 +684,7 @@ Proceed to Checkout - Continue Shopping + Continue Shopping