feat: Add image sources management and update server middleware
Some checks failed
Deploy to BeePC / deploy (push) Has been cancelled
Some checks failed
Deploy to BeePC / deploy (push) Has been cancelled
This commit is contained in:
167
routes/images.js
167
routes/images.js
@@ -1,9 +1,58 @@
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const router = express.Router();
|
||||
const database = require('../lib/database');
|
||||
const storage = require('../lib/storage');
|
||||
const imageFetcher = require('../lib/image-fetcher');
|
||||
|
||||
const CONFIG_PATH = path.join(__dirname, '..', 'image-sources.json');
|
||||
|
||||
function readSourcesConfig() {
|
||||
const defaultConfig = { sources: [], fetchInterval: 2.5 };
|
||||
try {
|
||||
if (!fs.existsSync(CONFIG_PATH)) {
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(CONFIG_PATH, 'utf8');
|
||||
const parsed = JSON.parse(content);
|
||||
return {
|
||||
sources: Array.isArray(parsed.sources) ? parsed.sources : [],
|
||||
fetchInterval: typeof parsed.fetchInterval === 'number' ? parsed.fetchInterval : 2.5,
|
||||
comments: parsed.comments || undefined
|
||||
};
|
||||
} catch (err) {
|
||||
console.warn('Failed to read image-sources.json:', err.message);
|
||||
return defaultConfig;
|
||||
}
|
||||
}
|
||||
|
||||
function writeSourcesConfig(config) {
|
||||
const payload = {
|
||||
...config,
|
||||
comments: config.comments || {
|
||||
sources: 'Array of image sources to fetch from',
|
||||
name: 'Human-readable name for the source',
|
||||
url: 'Full URL to the image',
|
||||
tags: 'Array of tags to apply to fetched images (useful for ML training)',
|
||||
enabled: 'Set to true to include this source in fetching',
|
||||
fetchInterval: 'Minutes between fetch cycles. Examples: 0.033 = 2 seconds, 0.05 = 3 seconds, 0.083 = 5 seconds, 1 = 1 minute, 2.5 = 2.5 minutes (default)'
|
||||
}
|
||||
};
|
||||
|
||||
fs.writeFileSync(CONFIG_PATH, JSON.stringify(payload, null, 2));
|
||||
}
|
||||
|
||||
function restartFetcherFromConfig(config) {
|
||||
imageFetcher.stopFetcher();
|
||||
const enabledSources = (config.sources || []).filter((source) => source.enabled);
|
||||
const interval = config.fetchInterval || 2.5;
|
||||
if (enabledSources.length > 0) {
|
||||
imageFetcher.startFetcher(enabledSources, interval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/images - Get all images with optional filtering
|
||||
*/
|
||||
@@ -271,4 +320,122 @@ router.get('/fetcher/status', (req, res) => {
|
||||
res.json({ success: true, status });
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/sources - Get image sources configuration
|
||||
*/
|
||||
router.get('/sources', (req, res) => {
|
||||
const config = readSourcesConfig();
|
||||
res.json({ success: true, config });
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/sources - Add a new image source
|
||||
*/
|
||||
router.post('/sources', (req, res) => {
|
||||
try {
|
||||
const { name, url, tags = [], enabled = true } = req.body;
|
||||
|
||||
if (!name || !url) {
|
||||
return res.status(400).json({ success: false, error: 'name and url are required' });
|
||||
}
|
||||
|
||||
const config = readSourcesConfig();
|
||||
const newSource = {
|
||||
name: String(name).trim(),
|
||||
url: String(url).trim(),
|
||||
tags: Array.isArray(tags) ? tags.map(tag => String(tag).trim()).filter(Boolean) : [],
|
||||
enabled: Boolean(enabled)
|
||||
};
|
||||
|
||||
config.sources = [...(config.sources || []), newSource];
|
||||
writeSourcesConfig(config);
|
||||
restartFetcherFromConfig(config);
|
||||
|
||||
res.status(201).json({ success: true, config });
|
||||
} catch (err) {
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* PUT /api/sources/config - Update fetch interval
|
||||
*/
|
||||
router.put('/sources/config', (req, res) => {
|
||||
try {
|
||||
const { fetchInterval } = req.body;
|
||||
const intervalValue = Number(fetchInterval);
|
||||
|
||||
if (!Number.isFinite(intervalValue) || intervalValue <= 0) {
|
||||
return res.status(400).json({ success: false, error: 'fetchInterval must be a positive number' });
|
||||
}
|
||||
|
||||
const config = readSourcesConfig();
|
||||
config.fetchInterval = intervalValue;
|
||||
writeSourcesConfig(config);
|
||||
restartFetcherFromConfig(config);
|
||||
|
||||
res.json({ success: true, config });
|
||||
} catch (err) {
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* PUT /api/sources/:index - Update an existing source
|
||||
*/
|
||||
router.put('/sources/:index', (req, res) => {
|
||||
try {
|
||||
const index = Number(req.params.index);
|
||||
if (!Number.isInteger(index)) {
|
||||
return res.status(400).json({ success: false, error: 'Invalid source index' });
|
||||
}
|
||||
|
||||
const config = readSourcesConfig();
|
||||
if (!config.sources || !config.sources[index]) {
|
||||
return res.status(404).json({ success: false, error: 'Source not found' });
|
||||
}
|
||||
|
||||
const current = config.sources[index];
|
||||
const updated = {
|
||||
...current,
|
||||
name: req.body.name !== undefined ? String(req.body.name).trim() : current.name,
|
||||
url: req.body.url !== undefined ? String(req.body.url).trim() : current.url,
|
||||
tags: Array.isArray(req.body.tags)
|
||||
? req.body.tags.map(tag => String(tag).trim()).filter(Boolean)
|
||||
: current.tags,
|
||||
enabled: req.body.enabled !== undefined ? Boolean(req.body.enabled) : current.enabled
|
||||
};
|
||||
|
||||
config.sources[index] = updated;
|
||||
writeSourcesConfig(config);
|
||||
restartFetcherFromConfig(config);
|
||||
|
||||
res.json({ success: true, config });
|
||||
} catch (err) {
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* DELETE /api/sources/:index - Remove a source
|
||||
*/
|
||||
router.delete('/sources/:index', (req, res) => {
|
||||
try {
|
||||
const index = Number(req.params.index);
|
||||
const config = readSourcesConfig();
|
||||
|
||||
if (!Number.isInteger(index) || !config.sources || !config.sources[index]) {
|
||||
return res.status(404).json({ success: false, error: 'Source not found' });
|
||||
}
|
||||
|
||||
config.sources.splice(index, 1);
|
||||
writeSourcesConfig(config);
|
||||
restartFetcherFromConfig(config);
|
||||
|
||||
res.json({ success: true, config });
|
||||
} catch (err) {
|
||||
res.status(500).json({ success: false, error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user