fixed up data-service
This commit is contained in:
parent
68d977f9e0
commit
6b69bcbcaa
8 changed files with 135 additions and 535 deletions
|
|
@ -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 };
|
||||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
Loading…
Add table
Add a link
Reference in a new issue