From 2d58facb97c8f5e79de78df35c74c9e3cf5e1164 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Nov 2025 18:51:28 +0000 Subject: [PATCH] Add comprehensive architecture documentation for Docker monitoring integration Generated detailed architecture guide covering: - Project structure and technology stack - Monitor types and implementation patterns - Database schema and models - API patterns and endpoints - Job scheduling and polling mechanisms - Frontend components and UI structure - Authentication and configuration flow This documentation supports the Docker monitoring integration with Capture API. --- CHECKMATE_ARCHITECTURE_GUIDE.md | 993 ++++++++++++++++++++++++++++++++ 1 file changed, 993 insertions(+) create mode 100644 CHECKMATE_ARCHITECTURE_GUIDE.md diff --git a/CHECKMATE_ARCHITECTURE_GUIDE.md b/CHECKMATE_ARCHITECTURE_GUIDE.md new file mode 100644 index 000000000..a4493fcd5 --- /dev/null +++ b/CHECKMATE_ARCHITECTURE_GUIDE.md @@ -0,0 +1,993 @@ +# Checkmate Codebase Architecture - Docker Monitoring Integration Guide + +## 1. PROJECT STRUCTURE + +### Directory Layout +``` +Checkmate/ +├── server/ # Backend Node.js/Express +│ ├── src/ +│ │ ├── db/v1|v2/ # Database (MongoDB) +│ │ ├── service/v1|v2/ # Business logic +│ │ ├── routes/v1|v2/ # API routes +│ │ ├── controllers/v1|v2/ # HTTP request handlers +│ │ ├── middleware/v1/ # Express middleware +│ │ ├── config/ # Service initialization +│ │ ├── validation/ # Input validation +│ │ └── utils/ # Helper functions +│ └── package.json # Dependencies (Node >=20) +├── client/ # Frontend React + TypeScript +│ ├── src/ +│ │ ├── Components/v1|v2/ # UI Components +│ │ ├── Pages/v1|v2/ # Page layouts +│ │ ├── Hooks/ # React hooks (data fetching) +│ │ ├── Features/ # Redux slices +│ │ └── Utils/ # Utilities & API client +│ └── package.json # React, Vite, MUI +└── docker/ # Deployment configs +``` + +### Technology Stack +**Backend:** +- Express 4.19 (HTTP server) +- MongoDB 8.3 (database) +- Mongoose (ODM) +- BullMQ 5.41 (job queue) - V2 only +- super-simple-scheduler 1.4 (job scheduling) - V1 +- Dockerode 4.0.6 (Docker API client) +- JWT authentication +- Winston (logging) + +**Frontend:** +- React 18.3 + TypeScript +- Vite (build tool) +- MUI v6 (Material-UI) +- Redux Toolkit (state management) +- Axios (HTTP client) +- React Router (navigation) +- i18next (internationalization) + +**Architectures:** +- V1: Simpler, SuperSimpleQueue-based job scheduling +- V2: TypeScript-based, BullMQ, more structured (in development) + +--- + +## 2. MONITOR TYPES & IMPLEMENTATION + +### Supported Monitor Types +Defined in `/server/src/db/v1/models/Monitor.js` line 46: +```javascript +type: { + enum: ["http", "ping", "pagespeed", "hardware", "docker", "port", "game"], + required: true +} +``` + +### How Monitor Types Are Handled + +#### Request Dispatch (NetworkService) +**Location:** `/server/src/service/v1/infrastructure/networkService.js` (580 lines) + +Entry point: `requestStatus(monitor)` - Routes to type-specific handlers: +```javascript +async requestStatus(monitor) { + switch(monitor.type) { + case "ping": return await this.requestPing(monitor); + case "http": return await this.requestHttp(monitor); + case "pagespeed": return await this.requestPageSpeed(monitor); + case "hardware": return await this.requestHardware(monitor); + case "docker": return await this.requestDocker(monitor); + case "port": return await this.requestPort(monitor); + case "game": return await this.requestGame(monitor); + } +} +``` + +#### DOCKER IMPLEMENTATION (Lines 289-383) +```javascript +async requestDocker(monitor) { + // Uses Dockerode to connect to /var/run/docker.sock + const docker = new this.Docker({ + socketPath: "/var/run/docker.sock", + handleError: true, + }); + + // Container matching (priority-based): + // 1. Exact full ID match (64-char) + // 2. Exact name match (case-insensitive) + // 3. Partial ID match (backwards compatibility) + + // Returns: { status: bool, code: int, message: string, responseTime: ms } +} +``` + +**Docker Monitor Storage:** +- `monitor.url`: Container ID or name (e.g., "my-app" or "a1b2c3d4...") +- `monitor.port`: Not used for Docker +- Returns: Only status and response time (no detailed metrics) + +#### HARDWARE IMPLEMENTATION (Lines 279-287) +```javascript +async requestHardware(monitor) { + return await this.requestHttp(monitor); // Delegates to HTTP! +} +``` +Hardware monitors send HTTP requests to Capture API endpoint with secret authentication. + +### Hardware/Docker Check Data Structure +**Location:** `/server/src/db/v1/models/Check.js` (176 lines) + +Check schema includes: +```javascript +// Common fields +monitorId, teamId, type, status, responseTime, message + +// Hardware/Docker specific (capture data): +cpu: { + physical_core, logical_core, frequency, temperature[], + free_percent, usage_percent +}, +memory: { + total_bytes, available_bytes, used_bytes, usage_percent +}, +disk: [{ + read_speed_bytes, write_speed_bytes, + total_bytes, free_bytes, usage_percent +}], +host: { os, platform, kernel_version }, +errors: [{ metric[], err }], +capture: { version, mode }, +net: [{ name, bytes_sent, bytes_recv, ... }] +``` + +**TTL:** Auto-deletes after 30 days +**Indexes:** monitorId, type, status, updatedAt (optimized for queries) + +--- + +## 3. DATABASE SCHEMA + +### Core Models + +#### Monitor Model +**File:** `/server/src/db/v1/models/Monitor.js` (208 lines) + +```javascript +{ + userId: ObjectId (immutable, required), + teamId: ObjectId (immutable, required), + name: String (required), + description: String, + type: String (enum: http|ping|pagespeed|hardware|docker|port|game), + url: String (required) // container name/ID for docker + port: Number, + interval: Number (ms, default: 60000), + isActive: Boolean (default: true), + status: Boolean, // Current up/down + statusWindow: [Boolean], // Last N checks + statusWindowSize: Number (default: 5), + statusWindowThreshold: Number (default: 60), + + // Hardware/Docker thresholds + thresholds: { + usage_cpu: Number (0-1), + usage_memory: Number (0-1), + usage_disk: Number (0-1), + usage_temperature: Number + }, + + // Notification thresholds + alertThreshold: Number (default: 5), + cpuAlertThreshold, memoryAlertThreshold, diskAlertThreshold, tempAlertThreshold, + + // Relationships + notifications: [ObjectId], // refs to Notification model + + // Advanced + secret: String, // For Capture API auth + ignoreTlsErrors: Boolean, + jsonPath: String, + expectedValue: String, + matchMethod: String (equal|include|regex), + gameId: String, + group: String (max 50 chars), + + // Computed + uptimePercentage: Number, + + timestamps: true // createdAt, updatedAt +} + +// Indexes +MonitorSchema.index({ teamId: 1, type: 1 }); + +// Pre-hooks +pre("findOneAndDelete"): Deletes checks, stats, removes from status pages +pre("deleteMany"): Cascade deletes +pre("save"): Syncs alertThresholds +pre("findOneAndUpdate"): Updates thresholds +``` + +#### Check Model +**File:** `/server/src/db/v1/models/Check.js` + +```javascript +{ + monitorId: ObjectId (ref: Monitor, immutable, indexed), + teamId: ObjectId (ref: Team, immutable, indexed), + type: String (enum: http|ping|pagespeed|hardware|docker|port|game, indexed), + status: Boolean (indexed), + responseTime: Number, + timings: Object, + statusCode: Number, + message: String, + + // Acknowledgment + ack: Boolean (default: false), + ackAt: Date, + + // Hardware fields (see above) + cpu, memory, disk[], host, errors[], capture, net[] + + // PageSpeed only + accessibility, bestPractices, seo, performance, audits, + + // Expiry + expiry: Date (TTL index: 30 days), + timestamps: true +} + +// Indexes +CheckSchema.index({ updatedAt: 1 }); +CheckSchema.index({ monitorId: 1, updatedAt: 1 }); +CheckSchema.index({ monitorId: 1, updatedAt: -1 }); +CheckSchema.index({ teamId: 1, updatedAt: -1 }); +``` + +#### MonitorStats Model +**File:** `/server/src/db/v1/models/MonitorStats.js` + +```javascript +{ + monitorId: ObjectId (ref: Monitor, immutable, indexed), + avgResponseTime: Number, + totalChecks: Number, + totalUpChecks: Number, + totalDownChecks: Number, + uptimePercentage: Number, + lastCheckTimestamp: Number, + lastResponseTime: Number, + timeOfLastFailure: Number, + timestamps: true +} +``` + +### Database Access Layer +**Location:** `/server/src/db/v1/modules/` + +- `MonitorModule.js` (593 lines) - Monitor CRUD + aggregations +- `CheckModule.js` - Check recording +- `MonitorModuleQueries.js` - MongoDB aggregation pipelines + +**Key Methods:** +```javascript +// Creation +Monitor.createMonitor({ body, teamId, userId }) +Monitor.createBulkMonitors(monitors) + +// Retrieval +Monitor.getMonitorById(id) +Monitor.getMonitorsByTeamId() +Monitor.getMonitorStatsById() +Monitor.getHardwareDetailsById() + +// Updates +Monitor.updateMonitor(id, updateData) + +// Deletions +Monitor.deleteMonitor(id) +``` + +--- + +## 4. API PATTERNS + +### REST Endpoint Structure +**Base:** `/api/v1/` + +#### Monitor Routes +**File:** `/server/src/routes/v1/monitorRoute.js` + +``` +GET /monitors getAllMonitors +POST /monitors createMonitor +DELETE /monitors deleteAllMonitors (superadmin) + +GET /monitors/:monitorId getMonitorById +PUT /monitors/:monitorId editMonitor (admin) +DELETE /monitors/:monitorId deleteMonitor (admin) + +GET /monitors/team getMonitorsByTeamId +GET /monitors/team/with-checks getMonitorsWithChecksByTeamId +GET /monitors/team/summary getMonitorsSummaryByTeamId +GET /monitors/team/groups getGroupsByTeamId + +GET /monitors/stats/:monitorId getMonitorStatsById +GET /monitors/uptime/details/:monitorId getUptimeDetailsById +GET /monitors/hardware/details/:monitorId getHardwareDetailsById + +POST /monitors/pause/:monitorId pauseMonitor (admin) +POST /monitors/demo addDemoMonitors +POST /monitors/bulk createBulkMonitors (CSV upload) +POST /monitors/test-email sendTestEmail + +GET /monitors/games getAllGames +GET /monitors/certificate/:monitorId fetchCertificate +``` + +#### Check Routes +**File:** `/server/src/routes/v1/checkRoute.js` + +``` +GET /checks/:monitorId getChecksByMonitorId +GET /checks/team/:teamId getChecksByTeamId +GET /checks/:monitorId/:checkId getCheckById +PUT /checks/:checkId updateCheck (ack) +``` + +### Request/Response Pattern + +**Create Monitor Request:** +```javascript +POST /api/v1/monitors +Authorization: Bearer +Content-Type: application/json + +{ + name: "My Docker Container", + description: "Monitors app container", + type: "docker", + url: "my-app-container", // Container name + interval: 60000, // 1 minute + isActive: true, + secret: "api-key-for-capture", // If using hardware monitoring + notifications: ["notif-id-1"], + thresholds: { + usage_cpu: 0.8, // 80% + usage_memory: 0.9, // 90% + usage_disk: 0.95 + }, + alertThreshold: 5, // Consecutive failures + statusWindowSize: 5, // Last 5 checks + statusWindowThreshold: 60 // % threshold +} + +Response 200: +{ + data: { + _id: "monitor-id", + name: "My Docker Container", + type: "docker", + status: true, + interval: 60000, + ... + }, + msg: "Monitor created successfully" +} +``` + +**Edit Monitor Request:** +```javascript +PUT /api/v1/monitors/:monitorId + +{ + name: "Updated Name", + interval: 120000, + thresholds: { usage_cpu: 0.85 }, + // Partial updates allowed +} +``` + +### Authentication & Authorization + +**JWT Middleware:** `/server/src/middleware/v1/verifyJWT.js` +- Header: `Authorization: Bearer ` +- Validates signature using `jwtSecret` from AppSettings +- Extracts user info: `req.user = { userId, teamId, role, ... }` + +**Role-Based Access:** +```javascript +// isAllowed middleware checks req.user.role +router.post("/monitors", isAllowed(["admin", "superadmin"]), createMonitor); +router.delete("/monitors/:monitorId", isAllowed(["admin", "superadmin"]), deleteMonitor); +``` + +### Validation +**File:** `/server/src/validation/joi.js` + +```javascript +const createMonitorBodyValidation = joi.object({ + name: joi.string().required(), + description: joi.string().required(), + type: joi.string().required(), + url: joi.string().required(), + interval: joi.number(), + port: joi.number(), + thresholds: joi.object().keys({ + usage_cpu: joi.number(), + usage_memory: joi.number(), + usage_disk: joi.number(), + usage_temperature: joi.number(), + }), + notifications: joi.array().items(joi.string()), + secret: joi.string(), + // ... more fields +}); +``` + +--- + +## 5. POLLING/JOBS IMPLEMENTATION + +### Job Queue Architecture + +#### V1: SuperSimpleScheduler (Production) +**Files:** +- `/server/src/service/v1/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js` +- `/server/src/service/v1/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js` + +**Job Lifecycle:** + +1. **Initialization** (on server startup): +```javascript +// Load all monitors from DB +const monitors = await db.monitorModule.getAllMonitors(); +for (const monitor of monitors) { + // Add job with random offset (0-100ms) + this.addJob(monitor._id, monitor); +} + +// Register job template +scheduler.addTemplate("monitor-job", this.helper.getMonitorJob()); +``` + +2. **Job Template** (executed per interval): +```javascript +getMonitorJob = () => { + return async (monitor) => { + // Check maintenance window + if (await this.isInMaintenanceWindow(monitorId, teamId)) { + return; // Skip + } + + // Execute monitor check + const networkResponse = await this.networkService.requestStatus(monitor); + + // Update monitor status + const { monitor: updated, statusChanged } = + await this.statusService.updateStatus(networkResponse); + + // Send notifications if status changed + if (statusChanged) { + await this.notificationService.handleNotifications({ + ...networkResponse, + monitor: updated, + prevStatus, + statusChanged + }); + } + }; +}; +``` + +3. **Job Control:** +```javascript +// Add job +addJob(monitorId, monitor) { + scheduler.addJob({ + id: monitorId.toString(), + template: "monitor-job", + repeat: monitor.interval, // Milliseconds + active: monitor.isActive, + data: monitor.toObject() + }); +} + +// Pause/Resume +pauseJob(monitor) { scheduler.pauseJob(monitor._id); } +resumeJob(monitor) { scheduler.resumeJob(monitor._id); } + +// Update interval +updateJob(monitor) { + scheduler.updateJob(monitor._id, { + repeat: monitor.interval, + data: monitor.toObject() + }); +} + +// Delete +deleteJob(monitor) { scheduler.removeJob(monitor._id); } +``` + +#### V2: BullMQ (TypeScript, Planned) +**Files:** +- `/server/src/service/v2/infrastructure/JobQueue.ts` +- `/server/src/service/v2/infrastructure/JobGenerator.ts` + +**Key Differences:** +- Uses Redis backing store +- TypeScript interfaces (type safety) +- More robust queue management +- Cleanup jobs for orphaned checks + +```typescript +// Cleanup job runs every 24 hours +scheduler.addJob({ + id: "cleanup-orphaned-checks", + template: "cleanup-job", + repeat: 24 * 60 * 60 * 1000, + active: true +}); +``` + +### Monitor Execution Flow + +``` +Monitor Created/Updated + ↓ +MonitorService.createMonitor() + ↓ +jobQueue.addJob(monitor) + ↓ +Job scheduled at interval + ↓ +[Job Execution] + ├─ Check maintenance window + ├─ NetworkService.requestStatus(monitor) + │ └─ Routes to type handler (requestDocker, requestHttp, etc) + ├─ Create Check record (database) + ├─ StatusService.updateMonitorStatus() + │ └─ Updates Monitor.status field + ├─ MonitorStatsService.updateStats() + ├─ If status changed → NotificationService.handleNotifications() + └─ Job completes / reschedules for next interval +``` + +### Status Update Logic +**Location:** `/server/src/service/v1/infrastructure/statusService.js` + +```javascript +async updateStatus(networkResponse) { + // networkResponse = { monitorId, type, status, code, message, responseTime } + + // 1. Create Check record + const check = await Check.create({ + monitorId, teamId, type, status, + responseTime, statusCode: code, message + }); + + // 2. Get previous status + const prevStatus = monitor.status; + + // 3. Apply statusWindow logic (sliding window of checks) + monitor.statusWindow.push(status); + if (monitor.statusWindow.length > monitor.statusWindowSize) { + monitor.statusWindow.shift(); + } + + // 4. Determine if DOWN (majority of window is false) + const downCount = monitor.statusWindow.filter(s => !s).length; + const isDown = (downCount / monitor.statusWindowSize) > + (monitor.statusWindowThreshold / 100); + + monitor.status = isDown ? false : true; + + // 5. Return status change info + return { + monitor: await monitor.save(), + statusChanged: prevStatus !== monitor.status, + prevStatus + }; +} +``` + +--- + +## 6. FRONTEND COMPONENTS + +### Monitor Management Pages + +#### Infrastructure Monitors (Hardware/Docker) +**Location:** `/client/src/Pages/v1/Infrastructure/` + +``` +Infrastructure/ +├── Create/ +│ ├── index.jsx (Main form - 10K lines) +│ ├── Components/ +│ │ ├── MonitorStatusHeader.jsx +│ │ ├── MonitorActionButtons.jsx +│ │ └── CustomAlertsSection.jsx +│ └── hooks/ +│ ├── useInfrastructureMonitorForm.jsx +│ ├── useValidateInfrastructureForm.jsx +│ └── useInfrastructureSubmit.jsx +├── Details/ +├── Monitors/ +│ └── Components/ +│ └── MonitorsTable.jsx +``` + +#### Form Structure (useInfrastructureMonitorForm hook) +```javascript +const [infrastructureMonitor, setInfrastructureMonitor] = useState({ + url: "", // Container name/IP + name: "", // Display name + notifications: [], // Notification IDs + interval: 0.25, // Minutes (0.25 = 15s, 1 = 60s) + + // Threshold toggles + cpu: false, + usage_cpu: "", // Percentage + memory: false, + usage_memory: "", + disk: false, + usage_disk: "", + temperature: false, + usage_temperature: "", + + secret: "", // Capture API secret + statusWindowSize: 5, + statusWindowThreshold: 60 +}); +``` + +**Frequency Options:** +```javascript +[ + { _id: 0.25, name: "15 Seconds" }, + { _id: 0.5, name: "30 Seconds" }, + { _id: 1, name: "1 Minute" }, + { _id: 2, name: "2 Minutes" }, + { _id: 5, name: "5 Minutes" }, + { _id: 10, name: "10 Minutes" } +] +``` + +#### Form Submission (useInfrastructureSubmit hook) +```javascript +// Converts frontend form → backend model +const finalForm = { + url: `http${https ? "s" : ""}://` + infrastructureMonitor.url, + name: infrastructureMonitor.name || infrastructureMonitor.url, + interval: infrastructureMonitor.interval * 60000, // Convert to ms + type: "hardware", // Always "hardware" for infrastructure monitors + + // Thresholds (convert % to decimal 0-1) + thresholds: { + ...(cpu ? { usage_cpu: usage_cpu / 100 } : {}), + ...(memory ? { usage_memory: usage_memory / 100 } : {}), + ...(disk ? { usage_disk: usage_disk / 100 } : {}), + ...(temperature ? { usage_temperature: usage_temperature / 100 } : {}) + }, + + notifications: infrastructureMonitor.notifications +}; + +// API call +isCreate ? + await createMonitor({ monitor: finalForm, redirect: "/infrastructure" }) : + await updateMonitor({ monitor: finalForm, redirect: "/infrastructure" }); +``` + +### Monitor Display Components + +#### Monitor Status Component +**Location:** `/client/src/Components/v2/Monitors/MonitorStatus.tsx` + +Displays: +- Status indicator (up/down) +- Response time +- Last check time +- Incident badges + +#### Monitoring Hooks +**Location:** `/client/src/Hooks/v1/monitorHooks.js` + +```javascript +// Fetch monitors +useFetchMonitorsByTeamId({ types, limit, page, ... }) +useFetchMonitorsWithSummary({ types, monitorUpdateTrigger }) +useFetchMonitorsWithChecks({ types, limit, ... }) + +// Fetch individual monitor +useFetchHardwareMonitorById({ monitorId, updateTrigger }) +useFetchStatsByMonitorId({ monitorId, dateRange, ... }) + +// Manage monitors +useCreateMonitor() // POST /monitors +useUpdateMonitor() // PUT /monitors/:id +useDeleteMonitor() // DELETE /monitors/:id +usePauseMonitor() // POST /monitors/pause/:id +``` + +### Network Service (API Client) +**Location:** `/client/src/Utils/NetworkService.js` (1157 lines) + +```javascript +// Constructor +constructor(store, dispatch, navigate) { + // Axios instance with: + // - Bearer token injection + // - Language header + // - 401 redirect to login + // - Network error handling +} + +// Monitor endpoints +async createMonitor(config) { + return this.axiosInstance.post(`/monitors`, config.monitor); +} + +async updateMonitor(config) { + return this.axiosInstance.put(`/monitors/${config.monitorId}`, config.monitor); +} + +async getMonitorsByTeamId(config) { + // Query params: limit, types[], page, rowsPerPage, filter, field, order + return this.axiosInstance.get(`/monitors/team?...`); +} + +async getHardwareDetailsByMonitorId(config) { + return this.axiosInstance.get(`/monitors/hardware/details/${config.monitorId}`); +} + +async getStatsByMonitorId(config) { + // Query params: sortOrder, limit, dateRange, numToDisplay, normalize + return this.axiosInstance.get(`/monitors/stats/${config.monitorId}?...`); +} +``` + +--- + +## 7. AUTHENTICATION & API KEYS + +### User Authentication (JWT) + +**Registration/Login:** +```javascript +// POST /auth/register +{ + firstName: "John", + lastName: "Doe", + email: "john@example.com", + password: "SecurePass123!" +} + +Response: +{ + user: { _id, email, role, teamId, ... }, + token: "eyJhbGciOiJIUzI1NiIs..." // JWT +} +``` + +**Token Storage:** Redux store (`Features/Auth/authSlice`) + +**Token Injection:** Automatic by NetworkService +```javascript +config.headers.Authorization = `Bearer ${authToken}`; +``` + +### Monitor-Specific Authentication + +#### For Hardware/Docker Monitors via Capture API +Monitors can have a `secret` field for authenticating requests to the Capture API: + +```javascript +{ + type: "hardware", + url: "http://192.168.1.100:3000", // Capture endpoint + secret: "api-key-12345", // Passed to Capture API + thresholds: { ... } +} +``` + +**Implementation:** The secret is stored in the Monitor model and could be: +1. Sent as HTTP header: `Authorization: Bearer ` +2. Sent as query param: `?secret=` +3. Sent in request body for POST requests + +--- + +## 8. CONFIGURATION & USER SETTINGS + +### Global Settings +**Model:** `/server/src/db/v1/models/AppSettings.js` + +```javascript +{ + jwtSecret: String, // For signing JWTs + sslCertificate: String, // SSL cert content + smtpConfig: Object, // Email settings + globalThresholds: { // Default thresholds + cpu: Number, + memory: Number, + disk: Number, + temperature: Number + }, + siteTitle: String, + defaultLanguage: String, + maintenanceMode: Boolean +} +``` + +**Fetching Settings:** +```javascript +// Frontend +useFetchGlobalSettings() + → NetworkService.getGlobalSettings() + → GET /settings + +// Backend +SettingsService.loadSettings() + → Loads from AppSettings collection +``` + +### Monitor Configuration UI Flow + +1. **Create Page:** `/infrastructure/create` + - Fetch global settings for default thresholds + - Form includes: + - Monitor name + - URL/Container name + - Interval (dropdown) + - CPU/Memory/Disk/Temp thresholds (checkboxes + inputs) + - Notification channels (multi-select) + - Secret (if using Capture API) + +2. **Edit Page:** `/infrastructure/configure/:monitorId` + - Load existing monitor data + - Update form fields + - Save changes with PUT request + +3. **Details Page:** `/infrastructure/:monitorId` + - Display monitor stats + - Show uptime graph + - List recent checks + - Status history + +### Team-Based Isolation +**Access Control:** +```javascript +// All queries filtered by teamId +await Monitor.find({ teamId: req.user.teamId }); + +// verifyTeamAccess middleware +await verifyTeamAccess(req.user.teamId, req.params.monitorId); +``` + +--- + +## KEY PATTERNS FOR DOCKER MONITORING INTEGRATION + +### 1. Adding Docker Monitor Type +Already supported! Located in: +- `Monitor.schema` type enum ✓ +- `NetworkService.requestDocker()` ✓ +- `Check.schema` with type ✓ + +### 2. Docker-Specific Data Collection +Currently: Only status + response time +**To Add Detailed Metrics:** +- Extend `Check.schema` with Docker stats fields +- Modify `NetworkService.requestDocker()` to call `container.stats()` +- Stream stats to `Check.docker` field + +### 3. Capture API Integration (Hardware Monitoring) +**Flow:** +``` +Monitor with type="hardware" and url="http://capture-endpoint" + ↓ +NetworkService.requestHardware() + ↓ (delegates to) +NetworkService.requestHttp() + ↓ +Expects JSON response: { cpu, memory, disk, net, ... } + ↓ +Check record stores full payload in hardware fields +``` + +### 4. Database Queries +**Get hardware checks:** +```javascript +Check.find({ + monitorId: id, + type: "hardware", + createdAt: { $gte: startDate, $lte: endDate } +}) +.sort({ createdAt: -1 }) +.limit(100) +``` + +### 5. UI Display +- Stats dashboard: `/infrastructure/:monitorId` +- Uptime details: `/monitor/uptime/details/:monitorId` +- Hardware details: `/monitor/hardware/details/:monitorId` + +--- + +## SERVICE ARCHITECTURE + +### Service Registry Pattern +**Location:** `/server/src/service/v1/system/serviceRegistry.js` + +```javascript +// Singleton pattern +ServiceRegistry.set(serviceName, serviceInstance); +const service = ServiceRegistry.get(serviceName); +``` + +### Service Dependencies +**Initialization:** `/server/src/config/services.js` + +```javascript +// Infrastructure services +new NetworkService({ + axios, got, https, jmespath, GameDig, ping, + logger, http, Docker, // ← Docker client injected + net, stringService, settingsService +}) + +// Business services +new MonitorService({ + db, settingsService, jobQueue, + stringService, emailService, logger +}) +``` + +--- + +## KEY FILES SUMMARY + +| Component | File | Lines | Purpose | +|-----------|------|-------|---------| +| Monitor Model | `server/src/db/v1/models/Monitor.js` | 209 | Schema, hooks | +| Check Model | `server/src/db/v1/models/Check.js` | 177 | Check records, TTL | +| NetworkService | `server/src/service/v1/infrastructure/networkService.js` | 580 | Type handlers | +| MonitorService | `server/src/service/v1/business/monitorService.js` | 277 | CRUD, validation | +| MonitorModule | `server/src/db/v1/modules/monitorModule.js` | 593 | DB queries | +| JobQueue | `server/src/service/v1/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js` | 177 | Job scheduling | +| MonitorController | `server/src/controllers/v1/monitorController.js` | 475 | HTTP handlers | +| MonitorRoute | `server/src/routes/v1/monitorRoute.js` | 61 | Endpoint definitions | +| Frontend Hooks | `client/src/Hooks/v1/monitorHooks.js` | 500+ | Data fetching | +| NetworkService (FE) | `client/src/Utils/NetworkService.js` | 1157 | API client | +| Form Hook | `client/src/Pages/v1/Infrastructure/Create/hooks/useInfrastructureMonitorForm.jsx` | 97 | Form state | + +--- + +## DEPLOYMENT & DOCKER INTEGRATION + +### Docker-Compose Setup +**Files:** `/docker/dev/docker-compose.yaml`, `/docker/prod/docker-compose.yaml` + +**Volumes for Docker Monitoring:** +```yaml +volumes: + - /var/run/docker.sock:/var/run/docker.sock # Required for Dockerode +``` + +### Environment Variables +**Backend (.env):** +``` +MONGODB_URI=mongodb://mongo:27017/checkmate +JWT_SECRET=your-secret-key +NODE_ENV=production|development +PORT=5000 +``` + +**Frontend (.env):** +``` +VITE_APP_API_BASE_URL=http://localhost:5000/api/v1 +``` +