feat: Auto-migrations on startup, worker exit location, proxy improvements
- Add auto-migration system that runs SQL files from migrations/ on server startup - Track applied migrations in schema_migrations table - Show proxy exit location in Workers dashboard - Add "Cleanup Stale" button to remove old workers - Add remove button for individual workers - Include proxy location (city, state, country) in worker heartbeats - Update Proxy interface with location fields - Re-enable bulk proxy import without ON CONFLICT 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import { Router } from 'express';
|
||||
import { authMiddleware, requireRole } from '../auth/middleware';
|
||||
import { pool } from '../db/pool';
|
||||
import { testProxy, addProxy, addProxiesFromList } from '../services/proxy';
|
||||
import { createProxyTestJob, getProxyTestJob, getActiveProxyTestJob, cancelProxyTestJob } from '../services/proxyTestQueue';
|
||||
import { createProxyTestJob, getProxyTestJob, getActiveProxyTestJob, cancelProxyTestJob, ProxyTestMode } from '../services/proxyTestQueue';
|
||||
|
||||
const router = Router();
|
||||
router.use(authMiddleware);
|
||||
@@ -11,9 +11,10 @@ router.use(authMiddleware);
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
const result = await pool.query(`
|
||||
SELECT id, host, port, protocol, active, is_anonymous,
|
||||
SELECT id, host, port, protocol, username, password, active, is_anonymous,
|
||||
last_tested_at, test_result, response_time_ms, created_at,
|
||||
city, state, country, country_code, location_updated_at
|
||||
city, state, country, country_code, location_updated_at,
|
||||
COALESCE(max_connections, 1) as max_connections
|
||||
FROM proxies
|
||||
ORDER BY created_at DESC
|
||||
`);
|
||||
@@ -166,13 +167,39 @@ router.post('/:id/test', requireRole('superadmin', 'admin'), async (req, res) =>
|
||||
});
|
||||
|
||||
// Start proxy test job
|
||||
// Query params: mode=all|failed|inactive, concurrency=10
|
||||
router.post('/test-all', requireRole('superadmin', 'admin'), async (req, res) => {
|
||||
try {
|
||||
const jobId = await createProxyTestJob();
|
||||
res.json({ jobId, message: 'Proxy test job started' });
|
||||
} catch (error) {
|
||||
const mode = (req.query.mode as ProxyTestMode) || 'all';
|
||||
const concurrency = parseInt(req.query.concurrency as string) || 10;
|
||||
|
||||
// Validate mode
|
||||
if (!['all', 'failed', 'inactive'].includes(mode)) {
|
||||
return res.status(400).json({ error: 'Invalid mode. Use: all, failed, or inactive' });
|
||||
}
|
||||
|
||||
// Validate concurrency (1-50)
|
||||
if (concurrency < 1 || concurrency > 50) {
|
||||
return res.status(400).json({ error: 'Concurrency must be between 1 and 50' });
|
||||
}
|
||||
|
||||
const jobId = await createProxyTestJob(mode, concurrency);
|
||||
res.json({ jobId, mode, concurrency, message: `Proxy test job started (mode: ${mode}, concurrency: ${concurrency})` });
|
||||
} catch (error: any) {
|
||||
console.error('Error starting proxy test job:', error);
|
||||
res.status(500).json({ error: 'Failed to start proxy test job' });
|
||||
res.status(500).json({ error: error.message || 'Failed to start proxy test job' });
|
||||
}
|
||||
});
|
||||
|
||||
// Convenience endpoint: Test only failed proxies
|
||||
router.post('/test-failed', requireRole('superadmin', 'admin'), async (req, res) => {
|
||||
try {
|
||||
const concurrency = parseInt(req.query.concurrency as string) || 10;
|
||||
const jobId = await createProxyTestJob('failed', concurrency);
|
||||
res.json({ jobId, mode: 'failed', concurrency, message: 'Retesting failed proxies...' });
|
||||
} catch (error: any) {
|
||||
console.error('Error starting failed proxy test:', error);
|
||||
res.status(500).json({ error: error.message || 'Failed to start proxy test job' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -197,8 +224,8 @@ router.post('/test-job/:jobId/cancel', requireRole('superadmin', 'admin'), async
|
||||
router.put('/:id', requireRole('superadmin', 'admin'), async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { host, port, protocol, username, password, active } = req.body;
|
||||
|
||||
const { host, port, protocol, username, password, active, max_connections } = req.body;
|
||||
|
||||
const result = await pool.query(`
|
||||
UPDATE proxies
|
||||
SET host = COALESCE($1, host),
|
||||
@@ -207,10 +234,11 @@ router.put('/:id', requireRole('superadmin', 'admin'), async (req, res) => {
|
||||
username = COALESCE($4, username),
|
||||
password = COALESCE($5, password),
|
||||
active = COALESCE($6, active),
|
||||
max_connections = COALESCE($7, max_connections),
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $7
|
||||
WHERE id = $8
|
||||
RETURNING *
|
||||
`, [host, port, protocol, username, password, active, id]);
|
||||
`, [host, port, protocol, username, password, active, max_connections, id]);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
return res.status(404).json({ error: 'Proxy not found' });
|
||||
|
||||
Reference in New Issue
Block a user