fixed up data-service

This commit is contained in:
Boki 2025-06-18 20:11:05 -04:00
parent 68d977f9e0
commit 6b69bcbcaa
8 changed files with 135 additions and 535 deletions

View file

@ -1,322 +1,22 @@
/**
* Exchange Routes - Simple API endpoints for exchange management
*/
import { Hono } from 'hono';
import { getLogger } from '@stock-bot/logger';
import type { MasterExchange } from '@stock-bot/mongodb-client';
import { connectMongoDB, getDatabase } from '@stock-bot/mongodb-client';
import { queueManager } from '../index';
const logger = getLogger('exchange-routes');
const exchange = new Hono();
export const exchangeRoutes = new Hono();
// Get all master exchanges
exchangeRoutes.get('/api/exchanges', async c => {
// Get all exchanges
exchange.get('/', async c => {
try {
await connectMongoDB();
const db = getDatabase();
const collection = db.collection<MasterExchange>('masterExchanges');
const exchanges = await collection.find({}).toArray();
// TODO: Implement exchange listing from database
return c.json({
data: exchanges,
count: exchanges.length,
status: 'success',
data: [],
message: 'Exchange endpoints will be implemented with database integration'
});
} catch (error) {
logger.error('Error getting all exchanges', { error });
return c.json({ error: 'Internal server error' }, 500);
logger.error('Failed to get exchanges', { error });
return c.json({ status: 'error', message: 'Failed to get exchanges' }, 500);
}
});
// Get master exchange details
exchangeRoutes.get('/api/exchanges/:masterExchangeId', async c => {
try {
const masterExchangeId = c.req.param('masterExchangeId');
await connectMongoDB();
const db = getDatabase();
const collection = db.collection<MasterExchange>('masterExchanges');
const exchange = await collection.findOne({ masterExchangeId });
if (!exchange) {
return c.json({ error: 'Exchange not found' }, 404);
}
return c.json({
status: 'success',
data: exchange,
});
} catch (error) {
logger.error('Error getting exchange details', { error });
return c.json({ error: 'Internal server error' }, 500);
}
});
// Get source mapping
exchangeRoutes.get('/api/exchanges/mapping/:sourceName/:sourceId', async c => {
try {
const sourceName = c.req.param('sourceName');
const sourceId = c.req.param('sourceId');
await connectMongoDB();
const db = getDatabase();
const collection = db.collection<MasterExchange>('masterExchanges');
const result = await collection.findOne(
{ [`sourceMappings.${sourceName}.id`]: sourceId },
{ projection: { masterExchangeId: 1 } }
);
if (!result) {
return c.json({ error: 'Mapping not found' }, 404);
}
return c.json({
status: 'success',
data: { masterExchangeId: result.masterExchangeId, sourceName, sourceId },
});
} catch (error) {
logger.error('Error getting exchange mapping', { error });
return c.json({ error: 'Internal server error' }, 500);
}
});
// Get all exchanges from a specific source
exchangeRoutes.get('/api/exchanges/source/:sourceName', async c => {
try {
const sourceName = c.req.param('sourceName');
await connectMongoDB();
const db = getDatabase();
const collection = db.collection<MasterExchange>('masterExchanges');
const results = await collection
.find(
{ [`sourceMappings.${sourceName}`]: { $exists: true } },
{
projection: {
masterExchangeId: 1,
[`sourceMappings.${sourceName}`]: 1,
},
}
)
.toArray();
const exchanges = results.map(result => ({
masterExchangeId: result.masterExchangeId,
sourceMapping: result.sourceMappings[sourceName],
}));
return c.json({
data: exchanges,
count: exchanges.length,
status: 'success',
sourceName,
});
} catch (error) {
logger.error('Error getting source exchanges', { error });
return c.json({ error: 'Internal server error' }, 500);
}
});
// Add source mapping to an exchange
exchangeRoutes.post('/api/exchanges/:masterExchangeId/mappings', async c => {
try {
const masterExchangeId = c.req.param('masterExchangeId');
const { sourceName, sourceData } = await c.req.json();
if (!sourceName || !sourceData || !sourceData.id) {
return c.json({ error: 'sourceName and sourceData with id are required' }, 400);
}
await connectMongoDB();
const db = getDatabase();
const collection = db.collection<MasterExchange>('masterExchanges');
const result = await collection.updateOne(
{ masterExchangeId },
{
$set: {
[`sourceMappings.${sourceName}`]: sourceData,
updatedAt: new Date(),
},
}
);
if (result.matchedCount === 0) {
return c.json({ error: 'Exchange not found' }, 404);
}
return c.json({
status: 'success',
message: 'Source mapping added successfully',
data: { masterExchangeId, sourceName, sourceData },
});
} catch (error) {
logger.error('Error adding source mapping', { error });
return c.json({ error: 'Internal server error' }, 500);
}
});
// Trigger exchange sync
exchangeRoutes.post('/api/exchanges/sync', async c => {
try {
const job = await queueManager.add('exchange-sync', {
type: 'exchange-sync',
provider: 'exchange-sync',
operation: 'sync-ib-exchanges',
payload: {},
priority: 2,
});
return c.json({
status: 'success',
message: 'IB exchange sync job queued',
jobId: job.id,
operation: 'sync-ib-exchanges',
});
} catch (error) {
logger.error('Error triggering exchange sync', { error });
return c.json({ error: 'Internal server error' }, 500);
}
});
// Update exchange (shortName and active status)
exchangeRoutes.patch('/api/exchanges/:id', async c => {
try {
const id = c.req.param('id');
const updates = await c.req.json();
// Validate the updates - only allow shortName and active
const sanitizedUpdates: Record<string, unknown> = {};
if ('shortName' in updates && typeof updates.shortName === 'string') {
sanitizedUpdates.shortName = updates.shortName;
}
if ('active' in updates && typeof updates.active === 'boolean') {
sanitizedUpdates.active = updates.active;
}
if (Object.keys(sanitizedUpdates).length === 0) {
return c.json({ error: 'No valid fields to update' }, 400);
}
await connectMongoDB();
const db = getDatabase();
const collection = db.collection<MasterExchange>('masterExchanges');
// Update using MongoDB _id (ObjectId)
const result = await collection.updateOne(
{ _id: new (await import('mongodb')).ObjectId(id) },
{
$set: {
...sanitizedUpdates,
updated_at: new Date(),
},
}
);
if (result.matchedCount === 0) {
return c.json({ error: 'Exchange not found' }, 404);
}
return c.json({
status: 'success',
message: 'Exchange updated successfully',
data: { id, updates: sanitizedUpdates },
});
} catch (error) {
logger.error('Error updating exchange', { error });
return c.json({ error: 'Internal server error' }, 500);
}
});
// Add source to exchange
exchangeRoutes.post('/api/exchanges/:id/sources', async c => {
try {
const id = c.req.param('id');
const { source, source_code, mapping } = await c.req.json();
if (!source || !source_code || !mapping || !mapping.id) {
return c.json({ error: 'source, source_code, and mapping with id are required' }, 400);
}
// Create the storage key using source and source_code
const storageKey = `${source}_${source_code}`;
// Add lastUpdated to the mapping
const sourceData = {
...mapping,
lastUpdated: new Date(),
};
await connectMongoDB();
const db = getDatabase();
const collection = db.collection<MasterExchange>('masterExchanges');
// Update using MongoDB _id (ObjectId)
const result = await collection.updateOne(
{ _id: new (await import('mongodb')).ObjectId(id) },
{
$set: {
[`sourceMappings.${storageKey}`]: sourceData,
updated_at: new Date(),
},
}
);
if (result.matchedCount === 0) {
return c.json({ error: 'Exchange not found' }, 404);
}
return c.json({
status: 'success',
message: 'Source mapping added successfully',
data: { id, storageKey, mapping: sourceData },
});
} catch (error) {
logger.error('Error adding source mapping', { error });
return c.json({ error: 'Internal server error' }, 500);
}
});
// Remove source from exchange
exchangeRoutes.delete('/api/exchanges/:id/sources/:sourceName', async c => {
try {
const id = c.req.param('id');
const sourceName = c.req.param('sourceName');
await connectMongoDB();
const db = getDatabase();
const collection = db.collection<MasterExchange>('masterExchanges');
// Remove the source mapping using MongoDB _id (ObjectId)
const result = await collection.updateOne(
{ _id: new (await import('mongodb')).ObjectId(id) },
{
$unset: {
[`sourceMappings.${sourceName}`]: '',
},
$set: {
updated_at: new Date(),
},
}
);
if (result.matchedCount === 0) {
return c.json({ error: 'Exchange not found' }, 404);
}
return c.json({
status: 'success',
message: 'Source mapping removed successfully',
data: { id, sourceName },
});
} catch (error) {
logger.error('Error removing source mapping', { error });
return c.json({ error: 'Internal server error' }, 500);
}
});
export { exchange as exchangeRoutes };

View file

@ -1,20 +1,14 @@
/**
* Health check routes
*/
import { Hono } from 'hono';
import { queueManager } from '../index';
export const healthRoutes = new Hono();
const health = new Hono();
// Health check endpoint
healthRoutes.get('/health', c => {
health.get('/', c => {
return c.json({
service: 'data-service',
status: 'healthy',
service: 'data-service',
timestamp: new Date().toISOString(),
queue: {
status: 'running',
name: queueManager.getQueueName(),
},
});
});
export { health as healthRoutes };

View file

@ -1,72 +1,27 @@
/**
* Queue management routes
*/
import { Hono } from 'hono';
import { getLogger } from '@stock-bot/logger';
import { queueManager } from '../index';
const logger = getLogger('queue-routes');
const queue = new Hono();
export const queueRoutes = new Hono();
// Queue management endpoints
queueRoutes.get('/api/queue/status', async c => {
// Queue status endpoint
queue.get('/status', async c => {
try {
const stats = await queueManager.getStats();
return c.json({ status: 'success', data: stats });
// TODO: Implement queue management
return c.json({
status: 'success',
data: {
active: 0,
waiting: 0,
completed: 0,
failed: 0
},
message: 'Queue management will be implemented'
});
} catch (error) {
logger.error('Failed to get queue status', { error });
return c.json({ status: 'error', message: 'Failed to get queue status' }, 500);
}
});
queueRoutes.post('/api/queue/job', async c => {
try {
const { name, data, options } = await c.req.json();
const job = await queueManager.add(name, data, options);
return c.json({ status: 'success', jobId: job.id });
} catch (error) {
logger.error('Failed to add job', { error });
return c.json({ status: 'error', message: 'Failed to add job' }, 500);
}
});
// Provider registry endpoints
queueRoutes.get('/api/providers', async c => {
try {
const { providerRegistry } = await import('@stock-bot/queue');
const configs = providerRegistry.getProviderConfigs();
return c.json({ status: 'success', providers: configs });
} catch (error) {
logger.error('Failed to get providers', { error });
return c.json({ status: 'error', message: 'Failed to get providers' }, 500);
}
});
// Add new endpoint to see scheduled jobs
queueRoutes.get('/api/scheduled-jobs', async c => {
try {
const { providerRegistry } = await import('@stock-bot/queue');
const jobs = providerRegistry.getAllScheduledJobs();
return c.json({
status: 'success',
count: jobs.length,
jobs,
});
} catch (error) {
logger.error('Failed to get scheduled jobs info', { error });
return c.json({ status: 'error', message: 'Failed to get scheduled jobs' }, 500);
}
});
queueRoutes.post('/api/queue/clean', async c => {
try {
const { grace = 60000 } = await c.req.json(); // Default 1 minute
await queueManager.clean(grace);
const stats = await queueManager.getStats();
return c.json({ status: 'success', message: 'Queue cleaned', queueStats: stats });
} catch (error) {
logger.error('Failed to clean queue', { error });
return c.json({ status: 'error', message: 'Failed to clean queue' }, 500);
}
});
export { queue as queueRoutes };