mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	feat(server): add metrics endpoint and functionality
This commit is contained in:
		
							
								
								
									
										267
									
								
								apps/server/src/etapi/metrics.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								apps/server/src/etapi/metrics.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,267 @@ | |||||||
|  | import type { Router } from "express"; | ||||||
|  | import eu from "./etapi_utils.js"; | ||||||
|  | import sql from "../services/sql.js"; | ||||||
|  | import appInfo from "../services/app_info.js"; | ||||||
|  |  | ||||||
|  | interface MetricsData { | ||||||
|  |     version: { | ||||||
|  |         app: string; | ||||||
|  |         db: number; | ||||||
|  |         node: string; | ||||||
|  |         sync: number; | ||||||
|  |         buildDate: string; | ||||||
|  |         buildRevision: string; | ||||||
|  |     }; | ||||||
|  |     database: { | ||||||
|  |         totalNotes: number; | ||||||
|  |         deletedNotes: number; | ||||||
|  |         activeNotes: number; | ||||||
|  |         protectedNotes: number; | ||||||
|  |         totalAttachments: number; | ||||||
|  |         deletedAttachments: number; | ||||||
|  |         activeAttachments: number; | ||||||
|  |         totalRevisions: number; | ||||||
|  |         totalBranches: number; | ||||||
|  |         totalAttributes: number; | ||||||
|  |         totalBlobs: number; | ||||||
|  |         totalEtapiTokens: number; | ||||||
|  |         totalRecentNotes: number; | ||||||
|  |         totalEmbeddings: number; | ||||||
|  |         totalEmbeddingProviders: number; | ||||||
|  |     }; | ||||||
|  |     noteTypes: Record<string, number>; | ||||||
|  |     attachmentTypes: Record<string, number>; | ||||||
|  |     statistics: { | ||||||
|  |         oldestNote: string | null; | ||||||
|  |         newestNote: string | null; | ||||||
|  |         lastModified: string | null; | ||||||
|  |         databaseSizeBytes: number | null; | ||||||
|  |     }; | ||||||
|  |     timestamp: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Converts metrics data to Prometheus text format | ||||||
|  |  */ | ||||||
|  | function formatPrometheusMetrics(data: MetricsData): string { | ||||||
|  |     const lines: string[] = []; | ||||||
|  |     const timestamp = Math.floor(new Date(data.timestamp).getTime() / 1000); | ||||||
|  |  | ||||||
|  |     // Helper function to add a metric | ||||||
|  |     const addMetric = (name: string, value: number | null, help: string, type: string = 'gauge', labels: Record<string, string> = {}) => { | ||||||
|  |         if (value === null) return; | ||||||
|  |  | ||||||
|  |         lines.push(`# HELP ${name} ${help}`); | ||||||
|  |         lines.push(`# TYPE ${name} ${type}`); | ||||||
|  |  | ||||||
|  |         const labelStr = Object.entries(labels).length > 0 | ||||||
|  |             ? `{${Object.entries(labels).map(([k, v]) => `${k}="${v}"`).join(',')}}` | ||||||
|  |             : ''; | ||||||
|  |  | ||||||
|  |         lines.push(`${name}${labelStr} ${value} ${timestamp}`); | ||||||
|  |         lines.push(''); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // Version info | ||||||
|  |     addMetric('trilium_info', 1, 'Trilium instance information', 'gauge', { | ||||||
|  |         version: data.version.app, | ||||||
|  |         db_version: data.version.db.toString(), | ||||||
|  |         node_version: data.version.node, | ||||||
|  |         sync_version: data.version.sync.toString(), | ||||||
|  |         build_date: data.version.buildDate, | ||||||
|  |         build_revision: data.version.buildRevision | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     // Database metrics | ||||||
|  |     addMetric('trilium_notes_total', data.database.totalNotes, 'Total number of notes including deleted'); | ||||||
|  |     addMetric('trilium_notes_deleted', data.database.deletedNotes, 'Number of deleted notes'); | ||||||
|  |     addMetric('trilium_notes_active', data.database.activeNotes, 'Number of active notes'); | ||||||
|  |     addMetric('trilium_notes_protected', data.database.protectedNotes, 'Number of protected notes'); | ||||||
|  |  | ||||||
|  |     addMetric('trilium_attachments_total', data.database.totalAttachments, 'Total number of attachments including deleted'); | ||||||
|  |     addMetric('trilium_attachments_deleted', data.database.deletedAttachments, 'Number of deleted attachments'); | ||||||
|  |     addMetric('trilium_attachments_active', data.database.activeAttachments, 'Number of active attachments'); | ||||||
|  |  | ||||||
|  |     addMetric('trilium_revisions_total', data.database.totalRevisions, 'Total number of note revisions'); | ||||||
|  |     addMetric('trilium_branches_total', data.database.totalBranches, 'Number of active branches'); | ||||||
|  |     addMetric('trilium_attributes_total', data.database.totalAttributes, 'Number of active attributes'); | ||||||
|  |     addMetric('trilium_blobs_total', data.database.totalBlobs, 'Total number of blob records'); | ||||||
|  |     addMetric('trilium_etapi_tokens_total', data.database.totalEtapiTokens, 'Number of active ETAPI tokens'); | ||||||
|  |     addMetric('trilium_recent_notes_total', data.database.totalRecentNotes, 'Number of recent notes tracked'); | ||||||
|  |     addMetric('trilium_embeddings_total', data.database.totalEmbeddings, 'Number of note embeddings'); | ||||||
|  |     addMetric('trilium_embedding_providers_total', data.database.totalEmbeddingProviders, 'Number of embedding providers'); | ||||||
|  |  | ||||||
|  |     // Note types | ||||||
|  |     for (const [type, count] of Object.entries(data.noteTypes)) { | ||||||
|  |         addMetric('trilium_notes_by_type', count, 'Number of notes by type', 'gauge', { type }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Attachment types | ||||||
|  |     for (const [mime, count] of Object.entries(data.attachmentTypes)) { | ||||||
|  |         addMetric('trilium_attachments_by_type', count, 'Number of attachments by MIME type', 'gauge', { mime_type: mime }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Statistics | ||||||
|  |     if (data.statistics.databaseSizeBytes !== null) { | ||||||
|  |         addMetric('trilium_database_size_bytes', data.statistics.databaseSizeBytes, 'Database size in bytes'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (data.statistics.oldestNote) { | ||||||
|  |         const oldestTimestamp = Math.floor(new Date(data.statistics.oldestNote).getTime() / 1000); | ||||||
|  |         addMetric('trilium_oldest_note_timestamp', oldestTimestamp, 'Timestamp of the oldest note'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (data.statistics.newestNote) { | ||||||
|  |         const newestTimestamp = Math.floor(new Date(data.statistics.newestNote).getTime() / 1000); | ||||||
|  |         addMetric('trilium_newest_note_timestamp', newestTimestamp, 'Timestamp of the newest note'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (data.statistics.lastModified) { | ||||||
|  |         const lastModifiedTimestamp = Math.floor(new Date(data.statistics.lastModified).getTime() / 1000); | ||||||
|  |         addMetric('trilium_last_modified_timestamp', lastModifiedTimestamp, 'Timestamp of the last modification'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return lines.join('\n'); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Collects comprehensive metrics about the Trilium instance | ||||||
|  |  */ | ||||||
|  | function collectMetrics(): MetricsData { | ||||||
|  |     // Version information | ||||||
|  |     const version = { | ||||||
|  |         app: appInfo.appVersion, | ||||||
|  |         db: appInfo.dbVersion, | ||||||
|  |         node: appInfo.nodeVersion, | ||||||
|  |         sync: appInfo.syncVersion, | ||||||
|  |         buildDate: appInfo.buildDate, | ||||||
|  |         buildRevision: appInfo.buildRevision | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // Database counts | ||||||
|  |     const totalNotes = sql.getValue<number>("SELECT COUNT(*) FROM notes"); | ||||||
|  |     const deletedNotes = sql.getValue<number>("SELECT COUNT(*) FROM notes WHERE isDeleted = 1"); | ||||||
|  |     const activeNotes = totalNotes - deletedNotes; | ||||||
|  |     const protectedNotes = sql.getValue<number>("SELECT COUNT(*) FROM notes WHERE isProtected = 1 AND isDeleted = 0"); | ||||||
|  |  | ||||||
|  |     const totalAttachments = sql.getValue<number>("SELECT COUNT(*) FROM attachments"); | ||||||
|  |     const deletedAttachments = sql.getValue<number>("SELECT COUNT(*) FROM attachments WHERE isDeleted = 1"); | ||||||
|  |     const activeAttachments = totalAttachments - deletedAttachments; | ||||||
|  |  | ||||||
|  |     const totalRevisions = sql.getValue<number>("SELECT COUNT(*) FROM revisions"); | ||||||
|  |     const totalBranches = sql.getValue<number>("SELECT COUNT(*) FROM branches WHERE isDeleted = 0"); | ||||||
|  |     const totalAttributes = sql.getValue<number>("SELECT COUNT(*) FROM attributes WHERE isDeleted = 0"); | ||||||
|  |     const totalBlobs = sql.getValue<number>("SELECT COUNT(*) FROM blobs"); | ||||||
|  |     const totalEtapiTokens = sql.getValue<number>("SELECT COUNT(*) FROM etapi_tokens WHERE isDeleted = 0"); | ||||||
|  |     const totalRecentNotes = sql.getValue<number>("SELECT COUNT(*) FROM recent_notes"); | ||||||
|  |  | ||||||
|  |     // Embedding-related metrics (these tables might not exist in older versions) | ||||||
|  |     let totalEmbeddings = 0; | ||||||
|  |     let totalEmbeddingProviders = 0; | ||||||
|  |     try { | ||||||
|  |         totalEmbeddings = sql.getValue<number>("SELECT COUNT(*) FROM note_embeddings"); | ||||||
|  |         totalEmbeddingProviders = sql.getValue<number>("SELECT COUNT(*) FROM embedding_providers"); | ||||||
|  |     } catch (e) { | ||||||
|  |         // Tables don't exist, keep defaults | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const database = { | ||||||
|  |         totalNotes, | ||||||
|  |         deletedNotes, | ||||||
|  |         activeNotes, | ||||||
|  |         protectedNotes, | ||||||
|  |         totalAttachments, | ||||||
|  |         deletedAttachments, | ||||||
|  |         activeAttachments, | ||||||
|  |         totalRevisions, | ||||||
|  |         totalBranches, | ||||||
|  |         totalAttributes, | ||||||
|  |         totalBlobs, | ||||||
|  |         totalEtapiTokens, | ||||||
|  |         totalRecentNotes, | ||||||
|  |         totalEmbeddings, | ||||||
|  |         totalEmbeddingProviders | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // Note types breakdown | ||||||
|  |     const noteTypesRows = sql.getRows<{ type: string; count: number }>( | ||||||
|  |         "SELECT type, COUNT(*) as count FROM notes WHERE isDeleted = 0 GROUP BY type ORDER BY count DESC" | ||||||
|  |     ); | ||||||
|  |     const noteTypes: Record<string, number> = {}; | ||||||
|  |     for (const row of noteTypesRows) { | ||||||
|  |         noteTypes[row.type] = row.count; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Attachment types breakdown | ||||||
|  |     const attachmentTypesRows = sql.getRows<{ mime: string; count: number }>( | ||||||
|  |         "SELECT mime, COUNT(*) as count FROM attachments WHERE isDeleted = 0 GROUP BY mime ORDER BY count DESC" | ||||||
|  |     ); | ||||||
|  |     const attachmentTypes: Record<string, number> = {}; | ||||||
|  |     for (const row of attachmentTypesRows) { | ||||||
|  |         attachmentTypes[row.mime] = row.count; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Statistics | ||||||
|  |     const oldestNote = sql.getValue<string | null>( | ||||||
|  |         "SELECT utcDateCreated FROM notes WHERE isDeleted = 0 ORDER BY utcDateCreated ASC LIMIT 1" | ||||||
|  |     ); | ||||||
|  |     const newestNote = sql.getValue<string | null>( | ||||||
|  |         "SELECT utcDateCreated FROM notes WHERE isDeleted = 0 ORDER BY utcDateCreated DESC LIMIT 1" | ||||||
|  |     ); | ||||||
|  |     const lastModified = sql.getValue<string | null>( | ||||||
|  |         "SELECT utcDateModified FROM notes WHERE isDeleted = 0 ORDER BY utcDateModified DESC LIMIT 1" | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     // Database size (this might not work on all systems) | ||||||
|  |     let databaseSizeBytes: number | null = null; | ||||||
|  |     try { | ||||||
|  |         const sizeResult = sql.getValue<number>("SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size()"); | ||||||
|  |         databaseSizeBytes = sizeResult; | ||||||
|  |     } catch (e) { | ||||||
|  |         // Pragma might not be available | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const statistics = { | ||||||
|  |         oldestNote, | ||||||
|  |         newestNote, | ||||||
|  |         lastModified, | ||||||
|  |         databaseSizeBytes | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |         version, | ||||||
|  |         database, | ||||||
|  |         noteTypes, | ||||||
|  |         attachmentTypes, | ||||||
|  |         statistics, | ||||||
|  |         timestamp: new Date().toISOString() | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function register(router: Router) { | ||||||
|  |     eu.route(router, "get", "/etapi/metrics", (req, res, next) => { | ||||||
|  |         try { | ||||||
|  |             const metrics = collectMetrics(); | ||||||
|  |             const format = (req.query.format as string)?.toLowerCase() || 'prometheus'; | ||||||
|  |  | ||||||
|  |             if (format === 'json') { | ||||||
|  |                 res.status(200).json(metrics); | ||||||
|  |             } else if (format === 'prometheus') { | ||||||
|  |                 const prometheusText = formatPrometheusMetrics(metrics); | ||||||
|  |                 res.status(200) | ||||||
|  |                    .set('Content-Type', 'text/plain; version=0.0.4; charset=utf-8') | ||||||
|  |                    .send(prometheusText); | ||||||
|  |             } else { | ||||||
|  |                 throw new eu.EtapiError(400, "INVALID_FORMAT", "Supported formats: 'prometheus' (default), 'json'"); | ||||||
|  |             } | ||||||
|  |         } catch (error: any) { | ||||||
|  |             throw new eu.EtapiError(500, "METRICS_ERROR", `Failed to collect metrics: ${error.message}`); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |     register, | ||||||
|  |     collectMetrics, | ||||||
|  |     formatPrometheusMetrics | ||||||
|  | }; | ||||||
							
								
								
									
										168
									
								
								apps/server/src/routes/api/metrics.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								apps/server/src/routes/api/metrics.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,168 @@ | |||||||
|  | import etapiMetrics from "../../etapi/metrics.js"; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @swagger | ||||||
|  |  * /api/metrics: | ||||||
|  |  *   get: | ||||||
|  |  *     summary: Get Trilium instance metrics | ||||||
|  |  *     operationId: metrics | ||||||
|  |  *     parameters: | ||||||
|  |  *       - in: query | ||||||
|  |  *         name: format | ||||||
|  |  *         schema: | ||||||
|  |  *           type: string | ||||||
|  |  *           enum: [prometheus, json] | ||||||
|  |  *           default: prometheus | ||||||
|  |  *         description: Response format - 'prometheus' (default) for Prometheus text format, 'json' for JSON | ||||||
|  |  *     responses: | ||||||
|  |  *       '200': | ||||||
|  |  *         description: Instance metrics | ||||||
|  |  *         content: | ||||||
|  |  *           text/plain: | ||||||
|  |  *             schema: | ||||||
|  |  *               type: string | ||||||
|  |  *               example: | | ||||||
|  |  *                 # HELP trilium_info Trilium instance information | ||||||
|  |  *                 # TYPE trilium_info gauge | ||||||
|  |  *                 trilium_info{version="0.91.6",db_version="231",node_version="v18.17.0"} 1 1701432000 | ||||||
|  |  * | ||||||
|  |  *                 # HELP trilium_notes_total Total number of notes including deleted | ||||||
|  |  *                 # TYPE trilium_notes_total gauge | ||||||
|  |  *                 trilium_notes_total 1234 1701432000 | ||||||
|  |  *           application/json: | ||||||
|  |  *             schema: | ||||||
|  |  *               type: object | ||||||
|  |  *               properties: | ||||||
|  |  *                 version: | ||||||
|  |  *                   type: object | ||||||
|  |  *                   properties: | ||||||
|  |  *                     app: | ||||||
|  |  *                       type: string | ||||||
|  |  *                       example: "0.91.6" | ||||||
|  |  *                     db: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 231 | ||||||
|  |  *                     node: | ||||||
|  |  *                       type: string | ||||||
|  |  *                       example: "v18.17.0" | ||||||
|  |  *                     sync: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 35 | ||||||
|  |  *                     buildDate: | ||||||
|  |  *                       type: string | ||||||
|  |  *                       example: "2024-09-07T18:36:34Z" | ||||||
|  |  *                     buildRevision: | ||||||
|  |  *                       type: string | ||||||
|  |  *                       example: "7c0d6930fa8f20d269dcfbcbc8f636a25f6bb9a7" | ||||||
|  |  *                 database: | ||||||
|  |  *                   type: object | ||||||
|  |  *                   properties: | ||||||
|  |  *                     totalNotes: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 1234 | ||||||
|  |  *                     deletedNotes: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 56 | ||||||
|  |  *                     activeNotes: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 1178 | ||||||
|  |  *                     protectedNotes: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 23 | ||||||
|  |  *                     totalAttachments: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 89 | ||||||
|  |  *                     deletedAttachments: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 5 | ||||||
|  |  *                     activeAttachments: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 84 | ||||||
|  |  *                     totalRevisions: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 567 | ||||||
|  |  *                     totalBranches: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 1200 | ||||||
|  |  *                     totalAttributes: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 345 | ||||||
|  |  *                     totalBlobs: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 678 | ||||||
|  |  *                     totalEtapiTokens: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 3 | ||||||
|  |  *                     totalRecentNotes: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 50 | ||||||
|  |  *                     totalEmbeddings: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 123 | ||||||
|  |  *                     totalEmbeddingProviders: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       example: 2 | ||||||
|  |  *                 noteTypes: | ||||||
|  |  *                   type: object | ||||||
|  |  *                   additionalProperties: | ||||||
|  |  *                     type: integer | ||||||
|  |  *                   example: | ||||||
|  |  *                     text: 800 | ||||||
|  |  *                     code: 200 | ||||||
|  |  *                     image: 100 | ||||||
|  |  *                     file: 50 | ||||||
|  |  *                 attachmentTypes: | ||||||
|  |  *                   type: object | ||||||
|  |  *                   additionalProperties: | ||||||
|  |  *                     type: integer | ||||||
|  |  *                   example: | ||||||
|  |  *                     "image/png": 45 | ||||||
|  |  *                     "image/jpeg": 30 | ||||||
|  |  *                     "application/pdf": 14 | ||||||
|  |  *                 statistics: | ||||||
|  |  *                   type: object | ||||||
|  |  *                   properties: | ||||||
|  |  *                     oldestNote: | ||||||
|  |  *                       type: string | ||||||
|  |  *                       nullable: true | ||||||
|  |  *                       example: "2020-01-01T00:00:00.000Z" | ||||||
|  |  *                     newestNote: | ||||||
|  |  *                       type: string | ||||||
|  |  *                       nullable: true | ||||||
|  |  *                       example: "2024-12-01T12:00:00.000Z" | ||||||
|  |  *                     lastModified: | ||||||
|  |  *                       type: string | ||||||
|  |  *                       nullable: true | ||||||
|  |  *                       example: "2024-12-01T11:30:00.000Z" | ||||||
|  |  *                     databaseSizeBytes: | ||||||
|  |  *                       type: integer | ||||||
|  |  *                       nullable: true | ||||||
|  |  *                       example: 52428800 | ||||||
|  |  *                 timestamp: | ||||||
|  |  *                   type: string | ||||||
|  |  *                   example: "2024-12-01T12:00:00.000Z" | ||||||
|  |  *       '400': | ||||||
|  |  *         description: Invalid format parameter | ||||||
|  |  *       '500': | ||||||
|  |  *         description: Error collecting metrics | ||||||
|  |  *     security: | ||||||
|  |  *       - session: [] | ||||||
|  |  */ | ||||||
|  | function getMetrics(req: any, res: any): any { | ||||||
|  |     const format = (req.query?.format as string)?.toLowerCase() || 'prometheus'; | ||||||
|  |  | ||||||
|  |     if (format === 'json') { | ||||||
|  |         return etapiMetrics.collectMetrics(); | ||||||
|  |     } else if (format === 'prometheus') { | ||||||
|  |         const metrics = etapiMetrics.collectMetrics(); | ||||||
|  |         const prometheusText = etapiMetrics.formatPrometheusMetrics(metrics); | ||||||
|  |         res.set('Content-Type', 'text/plain; version=0.0.4; charset=utf-8'); | ||||||
|  |         return prometheusText; | ||||||
|  |     } else { | ||||||
|  |         throw new Error("Supported formats: 'prometheus' (default), 'json'"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |     getMetrics | ||||||
|  | }; | ||||||
| @@ -52,6 +52,7 @@ import fontsRoute from "./api/fonts.js"; | |||||||
| import etapiTokensApiRoutes from "./api/etapi_tokens.js"; | import etapiTokensApiRoutes from "./api/etapi_tokens.js"; | ||||||
| import relationMapApiRoute from "./api/relation-map.js"; | import relationMapApiRoute from "./api/relation-map.js"; | ||||||
| import otherRoute from "./api/other.js"; | import otherRoute from "./api/other.js"; | ||||||
|  | import metricsRoute from "./api/metrics.js"; | ||||||
| import shareRoutes from "../share/routes.js"; | import shareRoutes from "../share/routes.js"; | ||||||
| import embeddingsRoute from "./api/embeddings.js"; | import embeddingsRoute from "./api/embeddings.js"; | ||||||
| import ollamaRoute from "./api/ollama.js"; | import ollamaRoute from "./api/ollama.js"; | ||||||
| @@ -68,6 +69,7 @@ import etapiNoteRoutes from "../etapi/notes.js"; | |||||||
| import etapiSpecialNoteRoutes from "../etapi/special_notes.js"; | import etapiSpecialNoteRoutes from "../etapi/special_notes.js"; | ||||||
| import etapiSpecRoute from "../etapi/spec.js"; | import etapiSpecRoute from "../etapi/spec.js"; | ||||||
| import etapiBackupRoute from "../etapi/backup.js"; | import etapiBackupRoute from "../etapi/backup.js"; | ||||||
|  | import etapiMetricsRoute from "../etapi/metrics.js"; | ||||||
| import apiDocsRoute from "./api_docs.js"; | import apiDocsRoute from "./api_docs.js"; | ||||||
| import { apiResultHandler, apiRoute, asyncApiRoute, asyncRoute, route, router, uploadMiddlewareWithErrorHandling } from "./route_api.js"; | import { apiResultHandler, apiRoute, asyncApiRoute, asyncRoute, route, router, uploadMiddlewareWithErrorHandling } from "./route_api.js"; | ||||||
|  |  | ||||||
| @@ -236,6 +238,7 @@ function register(app: express.Application) { | |||||||
|  |  | ||||||
|     apiRoute(PST, "/api/recent-notes", recentNotesRoute.addRecentNote); |     apiRoute(PST, "/api/recent-notes", recentNotesRoute.addRecentNote); | ||||||
|     apiRoute(GET, "/api/app-info", appInfoRoute.getAppInfo); |     apiRoute(GET, "/api/app-info", appInfoRoute.getAppInfo); | ||||||
|  |     apiRoute(GET, "/api/metrics", metricsRoute.getMetrics); | ||||||
|  |  | ||||||
|     // docker health check |     // docker health check | ||||||
|     route(GET, "/api/health-check", [], () => ({ status: "ok" }), apiResultHandler); |     route(GET, "/api/health-check", [], () => ({ status: "ok" }), apiResultHandler); | ||||||
| @@ -363,6 +366,7 @@ function register(app: express.Application) { | |||||||
|     etapiSpecialNoteRoutes.register(router); |     etapiSpecialNoteRoutes.register(router); | ||||||
|     etapiSpecRoute.register(router); |     etapiSpecRoute.register(router); | ||||||
|     etapiBackupRoute.register(router); |     etapiBackupRoute.register(router); | ||||||
|  |     etapiMetricsRoute.register(router); | ||||||
|  |  | ||||||
|     // LLM Chat API |     // LLM Chat API | ||||||
|     asyncApiRoute(PST, "/api/llm/chat", llmRoute.createSession); |     asyncApiRoute(PST, "/api/llm/chat", llmRoute.createSession); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user