- Add PostgreSQL and SQLite database adapters with factory pattern - Add migration script for SQLite to PostgreSQL - Add production Dockerfiles and docker-compose configs - Add deployment documentation and scripts - Add BIA sync dashboard and matching service - Add data completeness configuration and components - Add new dashboard components (BusinessImportanceComparison, ComplexityDynamics, etc.) - Update various services and routes - Remove deprecated management-parameters.json and taxonomy files
159 lines
4.7 KiB
TypeScript
159 lines
4.7 KiB
TypeScript
/**
|
|
* Cache management routes
|
|
*
|
|
* Provides endpoints for cache status and manual sync triggers.
|
|
*/
|
|
|
|
import { Router, Request, Response } from 'express';
|
|
import { cacheStore } from '../services/cacheStore.js';
|
|
import { syncEngine } from '../services/syncEngine.js';
|
|
import { logger } from '../services/logger.js';
|
|
import { OBJECT_TYPES } from '../generated/jira-schema.js';
|
|
import type { CMDBObjectTypeName } from '../generated/jira-types.js';
|
|
|
|
const router = Router();
|
|
|
|
// Get cache status
|
|
router.get('/status', async (req: Request, res: Response) => {
|
|
try {
|
|
const cacheStats = await cacheStore.getStats();
|
|
const syncStatus = await syncEngine.getStatus();
|
|
|
|
// Compare cache count with Jira count for ApplicationComponent
|
|
let jiraComparison: { jiraCount?: number; cacheCount: number; difference?: number } | undefined;
|
|
if (cacheStats.objectsByType['ApplicationComponent'] !== undefined) {
|
|
try {
|
|
const { jiraAssetsClient } = await import('../services/jiraAssetsClient.js');
|
|
const { OBJECT_TYPES } = await import('../generated/jira-schema.js');
|
|
const typeDef = OBJECT_TYPES['ApplicationComponent'];
|
|
if (typeDef) {
|
|
const searchResult = await jiraAssetsClient.searchObjects(`objectType = "${typeDef.name}"`, 1, 1);
|
|
const jiraCount = searchResult.totalCount;
|
|
const cacheCount = cacheStats.objectsByType['ApplicationComponent'] || 0;
|
|
jiraComparison = {
|
|
jiraCount,
|
|
cacheCount,
|
|
difference: jiraCount - cacheCount,
|
|
};
|
|
}
|
|
} catch (err) {
|
|
logger.debug('Could not fetch Jira count for comparison', err);
|
|
}
|
|
}
|
|
|
|
res.json({
|
|
cache: cacheStats,
|
|
sync: syncStatus,
|
|
supportedTypes: Object.keys(OBJECT_TYPES),
|
|
jiraComparison,
|
|
});
|
|
} catch (error) {
|
|
logger.error('Failed to get cache status', error);
|
|
res.status(500).json({ error: 'Failed to get cache status' });
|
|
}
|
|
});
|
|
|
|
// Trigger full sync
|
|
router.post('/sync', async (req: Request, res: Response) => {
|
|
try {
|
|
logger.info('Manual full sync triggered');
|
|
|
|
// Don't wait for completion - return immediately
|
|
syncEngine.fullSync().catch(err => {
|
|
logger.error('Full sync failed', err);
|
|
});
|
|
|
|
res.json({
|
|
status: 'started',
|
|
message: 'Full sync started in background',
|
|
});
|
|
} catch (error) {
|
|
logger.error('Failed to trigger full sync', error);
|
|
res.status(500).json({ error: 'Failed to trigger sync' });
|
|
}
|
|
});
|
|
|
|
// Trigger sync for a specific object type
|
|
router.post('/sync/:objectType', async (req: Request, res: Response) => {
|
|
try {
|
|
const { objectType } = req.params;
|
|
|
|
// Validate object type
|
|
if (!OBJECT_TYPES[objectType]) {
|
|
res.status(400).json({
|
|
error: `Unknown object type: ${objectType}`,
|
|
supportedTypes: Object.keys(OBJECT_TYPES),
|
|
});
|
|
return;
|
|
}
|
|
|
|
logger.info(`Manual sync triggered for ${objectType}`);
|
|
|
|
const result = await syncEngine.syncType(objectType as CMDBObjectTypeName);
|
|
|
|
res.json({
|
|
status: 'completed',
|
|
objectType,
|
|
stats: result,
|
|
});
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : 'Failed to sync object type';
|
|
logger.error(`Failed to sync object type ${req.params.objectType}`, error);
|
|
|
|
// Return 409 (Conflict) if sync is already in progress, otherwise 500
|
|
const statusCode = errorMessage.includes('already in progress') ? 409 : 500;
|
|
res.status(statusCode).json({
|
|
error: errorMessage,
|
|
objectType: req.params.objectType,
|
|
});
|
|
}
|
|
});
|
|
|
|
// Clear cache for a specific type
|
|
router.delete('/clear/:objectType', async (req: Request, res: Response) => {
|
|
try {
|
|
const { objectType } = req.params;
|
|
|
|
if (!OBJECT_TYPES[objectType]) {
|
|
res.status(400).json({
|
|
error: `Unknown object type: ${objectType}`,
|
|
supportedTypes: Object.keys(OBJECT_TYPES),
|
|
});
|
|
return;
|
|
}
|
|
|
|
logger.info(`Clearing cache for ${objectType}`);
|
|
|
|
const deleted = await cacheStore.clearObjectType(objectType as CMDBObjectTypeName);
|
|
|
|
res.json({
|
|
status: 'cleared',
|
|
objectType,
|
|
deletedCount: deleted,
|
|
});
|
|
} catch (error) {
|
|
logger.error(`Failed to clear cache for ${req.params.objectType}`, error);
|
|
res.status(500).json({ error: 'Failed to clear cache' });
|
|
}
|
|
});
|
|
|
|
// Clear entire cache
|
|
router.delete('/clear', async (req: Request, res: Response) => {
|
|
try {
|
|
logger.info('Clearing entire cache');
|
|
|
|
await cacheStore.clearAll();
|
|
|
|
res.json({
|
|
status: 'cleared',
|
|
message: 'Entire cache cleared',
|
|
});
|
|
} catch (error) {
|
|
logger.error('Failed to clear cache', error);
|
|
res.status(500).json({ error: 'Failed to clear cache' });
|
|
}
|
|
});
|
|
|
|
export default router;
|
|
|