Files
odoo-expertos.com/server-upstash-fixed.js
2026-08-28 10:11:11 -03:00

279 lines
9.4 KiB
JavaScript

const express = require('express');
const path = require('path');
const fs = require('fs');
const crypto = require('crypto');
const app = express();
const PORT = process.env.PORT || 8888;
// Upstash Redis Configuration
const UPSTASH_URL = 'https://poetic-goshawk-11700.upstash.io';
const UPSTASH_TOKEN = 'AS20AAIjcDE5YTVjY2VhZDJjOWY0ZDQyOTMzMTlkODFjNjkzZWZmOXAxMA';
// Cache TTL configurations (in seconds)
const CACHE_TTL = {
html: 3600, // 1 hour for HTML pages
css: 86400, // 1 day for CSS
js: 86400, // 1 day for JS
images: 604800, // 1 week for images
api: 1800, // 30 minutes for API responses
default: 3600 // 1 hour default
};
// Helper function to get cache key
function getCacheKey(url) {
return `page_${crypto.createHash('md5').update(url).digest('hex')}`;
}
// Helper function to get file extension
function getFileExtension(filePath) {
return path.extname(filePath).toLowerCase().slice(1) || 'html';
}
// Upstash cache operations
async function getFromCache(key) {
try {
const response = await fetch(`${UPSTASH_URL}/get/${encodeURIComponent(key)}`, {
headers: {
'Authorization': `Bearer ${UPSTASH_TOKEN}`
}
});
if (response.ok) {
const data = await response.json();
return data.result;
}
} catch (error) {
console.error('Cache get error:', error.message);
}
return null;
}
async function setCache(key, value, ttl = CACHE_TTL.default) {
try {
const response = await fetch(`${UPSTASH_URL}/set/${encodeURIComponent(key)}/EX/${ttl}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${UPSTASH_TOKEN}`,
'Content-Type': 'text/plain'
},
body: value
});
return response.ok;
} catch (error) {
console.error('Cache set error:', error.message);
return false;
}
}
// Parse JSON bodies for cache API
app.use(express.json());
// HTTP Cache headers middleware
app.use((req, res, next) => {
const ext = path.extname(req.path).toLowerCase();
// Set browser cache headers based on file type
if (['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.ico'].includes(ext)) {
res.setHeader('Cache-Control', 'public, max-age=604800, immutable'); // 1 week
} else if (['.css', '.js'].includes(ext) && !req.path.includes('animations.js')) {
res.setHeader('Cache-Control', 'public, max-age=86400, must-revalidate'); // 1 day
} else if (ext === '.html' || req.path === '/') {
res.setHeader('Cache-Control', 'public, max-age=3600, must-revalidate'); // 1 hour
} else if (['.woff', '.woff2', '.ttf', '.eot'].includes(ext)) {
res.setHeader('Cache-Control', 'public, max-age=2592000, immutable'); // 30 days
}
// Security headers
res.setHeader('X-Frame-Options', 'SAMEORIGIN');
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('X-Content-Type-Options', 'nosniff');
next();
});
// Cache management API endpoint for stats
app.get('/api/cache/stats', async (req, res) => {
try {
const response = await fetch(`${UPSTASH_URL}/dbsize`, {
headers: {
'Authorization': `Bearer ${UPSTASH_TOKEN}`
}
});
if (response.ok) {
const data = await response.json();
res.json({
success: true,
stats: {
keys: data.result || 0,
connected: true,
backend: 'Upstash Redis'
}
});
} else {
res.json({ success: false, error: 'Failed to get stats' });
}
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Middleware for Upstash caching - simplified version
async function upstashCacheMiddleware(req, res, next) {
// Skip caching for non-GET requests
if (req.method !== 'GET') {
return next();
}
// Skip caching for API endpoints
if (req.path.startsWith('/api/')) {
return next();
}
const cacheKey = getCacheKey(req.url);
const ext = getFileExtension(req.path);
// Try to get from cache
const cached = await getFromCache(cacheKey);
if (cached) {
// Set cache hit header
res.set('X-Cache', 'HIT');
res.set('X-Cache-Key', cacheKey);
res.set('Content-Type', ext === 'html' ? 'text/html' : `text/${ext}`);
res.send(cached);
console.log(`Cache HIT: ${req.path}`);
return;
}
// Cache MISS - continue with normal processing
console.log(`Cache MISS: ${req.path}`);
res.set('X-Cache', 'MISS');
res.set('X-Cache-Key', cacheKey);
next();
}
// Apply Upstash caching middleware for HTML pages only
app.get('/', upstashCacheMiddleware, async (req, res) => {
const indexPath = path.join(__dirname, 'index.html');
fs.readFile(indexPath, 'utf8', async (err, content) => {
if (err) {
res.status(404).send('Page not found');
return;
}
// Cache the content if it was a miss
if (res.get('X-Cache') === 'MISS') {
const cacheKey = getCacheKey(req.url);
await setCache(cacheKey, content, CACHE_TTL.html);
console.log('Homepage cached');
}
res.send(content);
});
});
// Serve static files with caching
app.use(express.static(__dirname, {
etag: true,
lastModified: true,
setHeaders: (res, filePath) => {
if (filePath.endsWith('.html')) {
res.setHeader('X-Powered-By', 'Odoo Expertos with Upstash Cache');
}
}
}));
// Handle directory routes
app.get('*', async (req, res) => {
const requestedPath = path.join(__dirname, req.path);
// Check cache for HTML pages
if (req.path.endsWith('/') || req.path.endsWith('.html')) {
const cacheKey = getCacheKey(req.url);
const cached = await getFromCache(cacheKey);
if (cached) {
res.set('X-Cache', 'HIT');
res.set('Content-Type', 'text/html');
res.send(cached);
console.log(`Cache HIT: ${req.path}`);
return;
}
}
if (fs.existsSync(requestedPath) && fs.statSync(requestedPath).isFile()) {
const content = fs.readFileSync(requestedPath, 'utf8');
// Cache HTML files
if (requestedPath.endsWith('.html')) {
const cacheKey = getCacheKey(req.url);
await setCache(cacheKey, content, CACHE_TTL.html);
res.set('X-Cache', 'MISS');
console.log(`Cached: ${req.path}`);
}
res.sendFile(requestedPath);
return;
}
if (fs.existsSync(requestedPath) && fs.statSync(requestedPath).isDirectory()) {
const indexPath = path.join(requestedPath, 'index.html');
if (fs.existsSync(indexPath)) {
const content = fs.readFileSync(indexPath, 'utf8');
// Cache the directory index
const cacheKey = getCacheKey(req.url);
await setCache(cacheKey, content, CACHE_TTL.html);
res.set('X-Cache', 'MISS');
console.log(`Cached directory index: ${req.path}`);
res.send(content);
} else {
res.status(404).send('Directory index not found');
}
} else {
const notFoundPath = path.join(__dirname, '404.html');
if (fs.existsSync(notFoundPath)) {
res.status(404).sendFile(notFoundPath);
} else {
res.status(404).send('Page not found');
}
}
});
app.listen(PORT, () => {
console.log(`
╔════════════════════════════════════════════════════════════╗
║ ║
║ 🚀 Odoo Expertos with Upstash Redis Cache ║
║ ║
║ Local URL: http://localhost:${PORT}
║ ║
║ ✅ Upstash Redis caching active ║
║ ✅ Global page caching enabled ║
║ ✅ HTTP cache headers configured ║
║ ✅ Performance optimizations active ║
║ ║
║ Cache Stats: http://localhost:${PORT}/api/cache/stats ║
║ ║
║ Press Ctrl+C to stop the server ║
║ ║
╚════════════════════════════════════════════════════════════╝
`);
// Test Upstash connection
getFromCache('test').then(() => {
console.log('✅ Upstash Redis connected successfully');
}).catch(error => {
console.error('⚠️ Upstash connection error:', error.message);
});
// Open browser automatically
const opener = require('opener');
opener(`http://localhost:${PORT}`);
});