ebayDeepScan/server.js

226 lines
6.2 KiB
JavaScript

const express = require('express');
const cors = require('cors');
const path = require('path');
const fs = require('fs');
const db = require('./db');
const scanner = require('./scanner');
const ai = require('./ai');
const app = express();
const PORT = process.env.PORT || 4000;
// Middleware
app.use(cors());
app.use(express.json());
// Serve static files from public directory
app.use(express.static(path.join(__dirname, 'public')));
// Create public directory if it doesn't exist
const publicDir = path.join(__dirname, 'public');
if (!fs.existsSync(publicDir)) {
fs.mkdirSync(publicDir, { recursive: true });
}
// Track scanning state
let isScanning = false;
let lastRunTime = null;
let scanProgress = { current: 0, total: 0, profileName: '' };
// API Routes
// --- PROFILES ---
app.get('/api/profiles', (req, res) => {
try {
let profiles = db.getProfiles();
if (profiles.length === 0) {
// Create a default profile if none exists
db.addProfile('Default Profile', 0.85, null);
profiles = db.getProfiles();
}
res.json(profiles);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.post('/api/profiles', (req, res) => {
try {
const { name, price_ratio, common_keywords } = req.body;
db.addProfile(name, price_ratio || 0.85, common_keywords);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.put('/api/profiles/:id', (req, res) => {
try {
const { name, price_ratio, common_keywords } = req.body;
db.updateProfile(req.params.id, name, price_ratio, common_keywords);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.delete('/api/profiles/:id', (req, res) => {
try {
db.deleteProfile(req.params.id);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// --- KEYWORDS ---
app.get('/api/profiles/:id/keywords', (req, res) => {
try {
const keywords = db.getKeywords(req.params.id);
res.json(keywords);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.post('/api/profiles/:id/keywords', (req, res) => {
try {
const { part_number, keywords, target_price } = req.body;
db.addKeyword(req.params.id, part_number, keywords, target_price);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.put('/api/keywords/:id', (req, res) => {
try {
const { part_number, keywords, target_price } = req.body;
db.updateKeyword(req.params.id, part_number, keywords, target_price);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.post('/api/profiles/:id/keywords/bulk', (req, res) => {
try {
const { items } = req.body; // Array of {part_number, keywords, target_price}
for (const item of items) {
db.addKeyword(req.params.id, item.part_number, item.keywords, item.target_price);
}
res.json({ success: true, count: items.length });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.delete('/api/keywords/:id', (req, res) => {
try {
db.deleteKeyword(req.params.id);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// --- ITEMS ---
// Get scan status
app.get('/api/status', (req, res) => {
res.json({ isScanning, lastRunTime, scanProgress });
});
// Get all "waiting" PASS items for a profile
app.get('/api/items', (req, res) => {
try {
const { profile_id } = req.query;
const items = db.getWaitingPassItems(profile_id);
res.json(items);
} catch (err) {
console.error('Error fetching items:', err);
res.status(500).json({ error: 'Internal server error' });
}
});
// Update item review status
app.put('/api/items/:id/status', (req, res) => {
try {
const { id } = req.params;
const { status } = req.body;
if (!['waiting', 'done', 'skip'].includes(status)) {
return res.status(400).json({ error: 'Invalid status' });
}
db.updateReviewStatus(id, status);
res.json({ success: true, message: `Status updated to ${status}` });
} catch (err) {
console.error(`Error updating status for item ${req.params.id}:`, err);
res.status(500).json({ error: 'Internal server error' });
}
});
// Trigger a new scan
app.post('/api/scan', async (req, res) => {
if (isScanning) {
return res.status(400).json({ error: 'Scan is already running' });
}
const { profile_id } = req.body;
if (!profile_id) {
return res.status(400).json({ error: 'profile_id is required' });
}
isScanning = true;
res.json({ success: true, message: 'Scan started in background' });
try {
console.log(`--- STARTING BACKGROUND SCAN FOR PROFILE ${profile_id} ---`);
scanProgress = { current: 0, total: 0, profileName: '' };
await scanner.runScannerCore(profile_id, (current, total, profileName) => {
scanProgress = { current, total, profileName };
});
console.log('--- BACKGROUND SCAN FINISHED ---');
} catch (err) {
console.error('Error running scan:', err);
} finally {
isScanning = false;
lastRunTime = new Date().toISOString();
scanProgress = { current: 0, total: 0, profileName: '' };
}
});
// Trigger AI on specific item
app.post('/api/items/:id/ai', async (req, res) => {
try {
const { id } = req.params;
const item = db.getItem(id);
if (!item) {
return res.status(404).json({ error: 'Item not found' });
}
// Convert JSON strings back to objects for AI prompt
item.detail_response = item.detail_response ? JSON.parse(item.detail_response) : null;
const suggestion = await ai.getAiSuggestion(item);
db.updateAiSuggestion(id, suggestion);
res.json({ success: true, ai_suggestion: suggestion });
} catch (err) {
console.error(`Error requesting AI for item ${req.params.id}:`, err);
res.status(500).json({ error: 'Internal server error' });
}
});
// Default catch-all to index.html for SPA feel
app.use((req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
// Start server
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});