refac
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import categorySyncer from './category-syncer.js';
|
||||
import pictureSyncer from './picture-syncer.js';
|
||||
import categoryProductsSyncer from './category-products-syncer.js';
|
||||
import { startServer } from './server.js';
|
||||
import categorySyncer from './syncers/category-syncer.js';
|
||||
import pictureSyncer from './syncers/picture-syncer.js';
|
||||
import categoryProductsSyncer from './syncers/category-products-syncer.js';
|
||||
import { startServer } from './server/server.js';
|
||||
|
||||
categorySyncer.on('synced', async ({ tree, unprunedTree, changed }) => {
|
||||
if (changed) {
|
||||
18
src/server/routes/categories.js
Normal file
18
src/server/routes/categories.js
Normal file
@@ -0,0 +1,18 @@
|
||||
export function registerCategories(app, cache) {
|
||||
app.get('/api/categories', async (req, res) => {
|
||||
try {
|
||||
// Check if client has cached version
|
||||
if (req.headers['if-none-match'] === cache.categories.etag) {
|
||||
return res.status(304).end(); // Not Modified
|
||||
}
|
||||
|
||||
// Set cache headers with ETag
|
||||
res.set('Cache-Control', 'public, max-age=60, must-revalidate');
|
||||
res.set('ETag', cache.categories.etag);
|
||||
|
||||
res.json(JSON.parse(cache.categories.data));
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: 'Failed to load category tree' });
|
||||
}
|
||||
});
|
||||
}
|
||||
17
src/server/routes/images.js
Normal file
17
src/server/routes/images.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import path from 'path';
|
||||
|
||||
export function registerImages(app, cacheDir) {
|
||||
app.get('/img/cat/:id.avif', (req, res) => {
|
||||
const { id } = req.params;
|
||||
const imagePath = path.join(cacheDir, 'img', 'categories', `${id}.avif`);
|
||||
|
||||
// Cache images for 1 year (immutable content)
|
||||
res.set('Cache-Control', 'public, max-age=31536000, immutable');
|
||||
|
||||
res.sendFile(path.resolve(imagePath), (err) => {
|
||||
if (err) {
|
||||
res.status(404).send('Image not found');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
20
src/server/routes/index.js
Normal file
20
src/server/routes/index.js
Normal file
@@ -0,0 +1,20 @@
|
||||
export function registerIndex(app, cache) {
|
||||
app.get('/', async (req, res) => {
|
||||
try {
|
||||
// Check if client has cached version
|
||||
if (req.headers['if-none-match'] === cache.html.etag) {
|
||||
return res.status(304).end(); // Not Modified
|
||||
}
|
||||
|
||||
// Set cache headers with ETag
|
||||
res.set('Cache-Control', 'public, max-age=300, must-revalidate');
|
||||
res.set('ETag', cache.html.etag);
|
||||
res.set('Content-Type', 'text/html');
|
||||
|
||||
res.send(cache.html.data);
|
||||
} catch (err) {
|
||||
console.error('Error serving index.html:', err);
|
||||
res.status(500).send('Error loading page');
|
||||
}
|
||||
});
|
||||
}
|
||||
32
src/server/routes/products.js
Normal file
32
src/server/routes/products.js
Normal file
@@ -0,0 +1,32 @@
|
||||
export function registerProducts(app, cache, updateProductCache) {
|
||||
app.get('/api/categories/:id/products', async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
|
||||
// Lazy load if not in cache
|
||||
if (!cache.products.has(id)) {
|
||||
await updateProductCache(id);
|
||||
}
|
||||
|
||||
const cached = cache.products.get(id);
|
||||
|
||||
if (!cached) {
|
||||
return res.status(404).json({ error: 'Category products not found' });
|
||||
}
|
||||
|
||||
// Check if client has cached version
|
||||
if (req.headers['if-none-match'] === cached.etag) {
|
||||
return res.status(304).end(); // Not Modified
|
||||
}
|
||||
|
||||
// Set cache headers with ETag
|
||||
res.set('Cache-Control', 'public, max-age=60, must-revalidate');
|
||||
res.set('ETag', cached.etag);
|
||||
|
||||
res.json(JSON.parse(cached.data));
|
||||
} catch (err) {
|
||||
console.error(`Error serving products for category ${req.params.id}:`, err);
|
||||
res.status(500).json({ error: 'Failed to load products' });
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -4,6 +4,11 @@ import { Server } from 'socket.io';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import fs from 'fs/promises';
|
||||
import { registerCategories } from './routes/categories.js';
|
||||
import { registerProducts } from './routes/products.js';
|
||||
import { registerImages } from './routes/images.js';
|
||||
import { registerIndex } from './routes/index.js';
|
||||
import { registerConnection } from './socket/connection.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -61,7 +66,7 @@ export function startServer(categorySyncer, categoryProductsSyncer) {
|
||||
// Function to calculate ETag for HTML
|
||||
async function updateHtmlCache() {
|
||||
try {
|
||||
const htmlPath = path.join(__dirname, 'index.html');
|
||||
const htmlPath = path.join(__dirname, '../../index.html');
|
||||
const data = await fs.readFile(htmlPath, 'utf-8');
|
||||
const crypto = await import('crypto');
|
||||
cache.html.etag = crypto.createHash('md5').update(data).digest('hex');
|
||||
@@ -93,98 +98,14 @@ export function startServer(categorySyncer, categoryProductsSyncer) {
|
||||
});
|
||||
}
|
||||
|
||||
// Socket.io connection
|
||||
io.on('connection', (socket) => {
|
||||
console.log('🔌 Client connected');
|
||||
socket.on('disconnect', () => {
|
||||
console.log('🔌 Client disconnected');
|
||||
});
|
||||
});
|
||||
// Register socket connection handler
|
||||
registerConnection(io);
|
||||
|
||||
// Serve category tree JSON (with ETag for conditional caching)
|
||||
app.get('/api/categories', async (req, res) => {
|
||||
try {
|
||||
// Check if client has cached version
|
||||
if (req.headers['if-none-match'] === cache.categories.etag) {
|
||||
return res.status(304).end(); // Not Modified
|
||||
}
|
||||
|
||||
// Set cache headers with ETag
|
||||
res.set('Cache-Control', 'public, max-age=60, must-revalidate');
|
||||
res.set('ETag', cache.categories.etag);
|
||||
|
||||
res.json(JSON.parse(cache.categories.data));
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: 'Failed to load category tree' });
|
||||
}
|
||||
});
|
||||
|
||||
// Serve category products JSON (with ETag)
|
||||
app.get('/api/categories/:id/products', async (req, res) => {
|
||||
try {
|
||||
const id = parseInt(req.params.id);
|
||||
|
||||
// Lazy load if not in cache
|
||||
if (!cache.products.has(id)) {
|
||||
await updateProductCache(id);
|
||||
}
|
||||
|
||||
const cached = cache.products.get(id);
|
||||
|
||||
if (!cached) {
|
||||
return res.status(404).json({ error: 'Category products not found' });
|
||||
}
|
||||
|
||||
// Check if client has cached version
|
||||
if (req.headers['if-none-match'] === cached.etag) {
|
||||
return res.status(304).end(); // Not Modified
|
||||
}
|
||||
|
||||
// Set cache headers with ETag
|
||||
res.set('Cache-Control', 'public, max-age=60, must-revalidate');
|
||||
res.set('ETag', cached.etag);
|
||||
|
||||
res.json(JSON.parse(cached.data));
|
||||
} catch (err) {
|
||||
console.error(`Error serving products for category ${req.params.id}:`, err);
|
||||
res.status(500).json({ error: 'Failed to load products' });
|
||||
}
|
||||
});
|
||||
|
||||
// Serve category images (long cache - images rarely change)
|
||||
app.get('/img/cat/:id.avif', (req, res) => {
|
||||
const { id } = req.params;
|
||||
const imagePath = path.join(CACHE_DIR, 'img', 'categories', `${id}.avif`);
|
||||
|
||||
// Cache images for 1 year (immutable content)
|
||||
res.set('Cache-Control', 'public, max-age=31536000, immutable');
|
||||
|
||||
res.sendFile(path.resolve(imagePath), (err) => {
|
||||
if (err) {
|
||||
res.status(404).send('Image not found');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Serve index.html (with ETag for conditional caching)
|
||||
app.get('/', async (req, res) => {
|
||||
try {
|
||||
// Check if client has cached version
|
||||
if (req.headers['if-none-match'] === cache.html.etag) {
|
||||
return res.status(304).end(); // Not Modified
|
||||
}
|
||||
|
||||
// Set cache headers with ETag
|
||||
res.set('Cache-Control', 'public, max-age=300, must-revalidate');
|
||||
res.set('ETag', cache.html.etag);
|
||||
res.set('Content-Type', 'text/html');
|
||||
|
||||
res.send(cache.html.data);
|
||||
} catch (err) {
|
||||
console.error('Error serving index.html:', err);
|
||||
res.status(500).send('Error loading page');
|
||||
}
|
||||
});
|
||||
// Register routes
|
||||
registerCategories(app, cache);
|
||||
registerProducts(app, cache, updateProductCache);
|
||||
registerImages(app, CACHE_DIR);
|
||||
registerIndex(app, cache);
|
||||
|
||||
httpServer.listen(PORT, HOST, () => {
|
||||
console.log(`🌐 Server running on http://${HOST}:${PORT}`);
|
||||
8
src/server/socket/connection.js
Normal file
8
src/server/socket/connection.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export function registerConnection(io) {
|
||||
io.on('connection', (socket) => {
|
||||
console.log('🔌 Client connected');
|
||||
socket.on('disconnect', () => {
|
||||
console.log('🔌 Client disconnected');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { EventEmitter } from 'events';
|
||||
import { createConnection } from './database.js';
|
||||
import { createConnection } from '../utils/database.js';
|
||||
|
||||
class CategoryProductsSyncer extends EventEmitter {
|
||||
constructor() {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { createConnection } from './database.js';
|
||||
import { createConnection } from '../utils/database.js';
|
||||
|
||||
class CategorySyncer extends EventEmitter {
|
||||
constructor() {
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import sharp from 'sharp';
|
||||
import { createConnection } from './database.js';
|
||||
import { createConnection } from '../utils/database.js';
|
||||
|
||||
class PictureSyncer {
|
||||
constructor() {
|
||||
Reference in New Issue
Block a user