- Add ProductDetail page for viewing products locally - Add Dutchie and Details buttons to product cards in Products and StoreDetail pages - Add Last Updated display showing data freshness - Add parallel scrape scripts and routes - Add K8s deployment configurations - Add frontend Dockerfile with nginx 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
101 lines
3.6 KiB
TypeScript
Executable File
101 lines
3.6 KiB
TypeScript
Executable File
import express from 'express';
|
|
import cors from 'cors';
|
|
import path from 'path';
|
|
import dotenv from 'dotenv';
|
|
import { initializeMinio, isMinioEnabled } from './utils/minio';
|
|
import { logger } from './services/logger';
|
|
import { cleanupOrphanedJobs } from './services/proxyTestQueue';
|
|
|
|
dotenv.config();
|
|
|
|
const app = express();
|
|
const PORT = process.env.PORT || 3010;
|
|
|
|
app.use(cors());
|
|
app.use(express.json());
|
|
|
|
// Serve static images when MinIO is not configured
|
|
const LOCAL_IMAGES_PATH = process.env.LOCAL_IMAGES_PATH || '/app/public/images';
|
|
app.use('/images', express.static(LOCAL_IMAGES_PATH));
|
|
|
|
app.get('/health', (req, res) => {
|
|
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
});
|
|
|
|
// Endpoint to check server's outbound IP (for proxy whitelist setup)
|
|
app.get('/outbound-ip', async (req, res) => {
|
|
try {
|
|
const axios = require('axios');
|
|
const response = await axios.get('https://api.ipify.org?format=json', { timeout: 10000 });
|
|
res.json({ outbound_ip: response.data.ip });
|
|
} catch (error: any) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
});
|
|
|
|
import authRoutes from './routes/auth';
|
|
import dashboardRoutes from './routes/dashboard';
|
|
import storesRoutes from './routes/stores';
|
|
import dispensariesRoutes from './routes/dispensaries';
|
|
import changesRoutes from './routes/changes';
|
|
import categoriesRoutes from './routes/categories';
|
|
import productsRoutes from './routes/products';
|
|
import campaignsRoutes from './routes/campaigns';
|
|
import analyticsRoutes from './routes/analytics';
|
|
import settingsRoutes from './routes/settings';
|
|
import proxiesRoutes from './routes/proxies';
|
|
import logsRoutes from './routes/logs';
|
|
import scraperMonitorRoutes from './routes/scraper-monitor';
|
|
import apiTokensRoutes from './routes/api-tokens';
|
|
import apiPermissionsRoutes from './routes/api-permissions';
|
|
import parallelScrapeRoutes from './routes/parallel-scrape';
|
|
import { trackApiUsage, checkRateLimit } from './middleware/apiTokenTracker';
|
|
import { validateWordPressPermissions } from './middleware/wordpressPermissions';
|
|
|
|
// Apply WordPress permissions validation first (sets req.apiToken)
|
|
app.use(validateWordPressPermissions);
|
|
|
|
// Apply API tracking middleware globally
|
|
app.use(trackApiUsage);
|
|
app.use(checkRateLimit);
|
|
|
|
app.use('/api/auth', authRoutes);
|
|
app.use('/api/dashboard', dashboardRoutes);
|
|
app.use('/api/stores', storesRoutes);
|
|
app.use('/api/dispensaries', dispensariesRoutes);
|
|
app.use('/api/changes', changesRoutes);
|
|
app.use('/api/categories', categoriesRoutes);
|
|
app.use('/api/products', productsRoutes);
|
|
app.use('/api/campaigns', campaignsRoutes);
|
|
app.use('/api/analytics', analyticsRoutes);
|
|
app.use('/api/settings', settingsRoutes);
|
|
app.use('/api/proxies', proxiesRoutes);
|
|
app.use('/api/logs', logsRoutes);
|
|
app.use('/api/scraper-monitor', scraperMonitorRoutes);
|
|
app.use('/api/api-tokens', apiTokensRoutes);
|
|
app.use('/api/api-permissions', apiPermissionsRoutes);
|
|
app.use('/api/parallel-scrape', parallelScrapeRoutes);
|
|
|
|
async function startServer() {
|
|
try {
|
|
logger.info('system', 'Starting server...');
|
|
|
|
await initializeMinio();
|
|
logger.info('system', isMinioEnabled() ? 'MinIO storage initialized' : 'Local filesystem storage initialized');
|
|
|
|
// Clean up any orphaned proxy test jobs from previous server runs
|
|
await cleanupOrphanedJobs();
|
|
|
|
app.listen(PORT, () => {
|
|
logger.info('system', `Server running on port ${PORT}`);
|
|
console.log(`🚀 Server running on port ${PORT}`);
|
|
});
|
|
} catch (error) {
|
|
logger.error('system', `Failed to start server: ${error}`);
|
|
console.error('Failed to start server:', error);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
startServer();
|