{"id":31773023,"url":"https://github.com/trackd/filewatchrest","last_synced_at":"2026-05-29T21:31:22.840Z","repository":{"id":315260832,"uuid":"1058762466","full_name":"trackd/FileWatchRest","owner":"trackd","description":"filewatch service to post to a rest api","archived":false,"fork":false,"pushed_at":"2025-10-01T14:45:12.000Z","size":83,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-01T16:34:12.966Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/trackd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-17T14:12:29.000Z","updated_at":"2025-09-26T22:22:14.000Z","dependencies_parsed_at":"2025-09-17T17:03:41.734Z","dependency_job_id":null,"html_url":"https://github.com/trackd/FileWatchRest","commit_stats":null,"previous_names":["trackd/filewatchrest"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/trackd/FileWatchRest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trackd%2FFileWatchRest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trackd%2FFileWatchRest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trackd%2FFileWatchRest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trackd%2FFileWatchRest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trackd","download_url":"https://codeload.github.com/trackd/FileWatchRest/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trackd%2FFileWatchRest/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279002685,"owners_count":26083442,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-10T02:00:06.843Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-10T04:19:49.283Z","updated_at":"2026-05-29T21:31:22.804Z","avatar_url":"https://github.com/trackd.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿\u003ch1 align=\"center\"\u003eFileWatchRest\u003c/h1\u003e  \r\n\u003cdiv align=\"center\"\u003e  \r\n   \u003csub\u003e  \r\n\r\n   FileWatch Windows Service that can take actions on files.  \r\n   \u003c/sub\u003e  \r\n\u003cbr/\u003e\u003cbr/\u003e  \r\n\r\n[![build](https://github.com/trackd/FileWatchRest/actions/workflows/ci.yml/badge.svg)](https://github.com/trackd/FileWatchRest/actions/workflows/ci.yml)\r\n[![codecov](https://codecov.io/github/trackd/FileWatchRest/graph/badge.svg?token=7H2MHCOP0G)](https://codecov.io/github/trackd/FileWatchRest)\r\n[![LICENSE](https://img.shields.io/github/license/trackd/FileWatchRest)](https://github.com/trackd/FileWatchRest/blob/main/LICENSE)\r\n\u003c/div\u003e  \r\n\r\nModern Windows service that watches folders for new or changed files and POSTs file information (and  \r\noptionally file contents) to a configured HTTP REST API  \r\n\r\nKey Features  \r\n  \r\n- **Multi-folder watching** with real-time configuration updates and per-folder actions\r\n- **BackgroundService architecture** with dedicated debouncing and sending services\r\n- **Bearer token authentication** for secure API communication with automatic encryption\r\n- **File content processing** with streaming support for large files\r\n- **Extension-based filtering** and wildcard pattern exclusion\r\n- **Automatic file archiving** to processed folders after successful API calls\r\n- **Debounced detection** with low-latency posting using bounded channels and ArrayPool\u003cT\u003e\r\n- **Robust error handling** with configurable retry logic, circuit breaker, and restart mechanisms\r\n- **Real-time diagnostics** with structured CSV/JSON logging and HTTP metrics endpoints\r\n- **Native AOT ready** for high-performance deployment\r\n\r\nProject Structure  \r\n  \r\nThe codebase is organized into logical folders following modern .NET patterns:\r\n\r\n```md\r\nFileWatchRest/\r\n├── Configuration/     # Configuration management and validation\r\n│   ├── ExternalConfiguration.cs\r\n│   ├── ConfigurationService.cs\r\n│   ├── ExternalConfigurationValidator.cs\r\n│   └── SecureConfigurationHelper.cs (token encryption)\r\n├── Services/          # BackgroundService implementations\r\n│   ├── Worker.cs (orchestration)\r\n│   ├── FileDebounceService.cs (dedicated debouncing)\r\n│   ├── FileSenderService.cs (dedicated sending)\r\n│   ├── FileWatcherManager.cs (watcher lifecycle)\r\n│   ├── HttpResilienceService.cs (retry + circuit breaker)\r\n│   └── DiagnosticsService.cs (metrics + HTTP endpoints)\r\n├── Monitor/           # IOptionsMonitor pattern implementation\r\n│   └── ExternalConfigurationOptionsMonitor.cs\r\n├── Models/            # Data models with System.Text.Json source generation\r\n│   ├── FileNotification.cs\r\n│   ├── UploadMetadata.cs\r\n│   └── JsonContexts.cs\r\n├── Logging/           # Custom structured logging\r\n│   ├── SimpleFileLoggerProvider.cs (CSV/JSON)\r\n│   └── LoggerDelegates.cs (LoggerMessage pattern)\r\n├── Helpers/           # Utility classes\r\n│   └── WildcardPatternMatcher.cs\r\n└── Program.cs         # Application entry point\r\n```\r\n\r\nConfiguration  \r\n  \r\nThe service uses a single JSON configuration file for all settings:\r\n\r\n**Configuration File**: `$env:ProgramData\\FileWatchRest\\FileWatchRest.json`  \r\n\r\nThis file is created automatically with defaults and can be edited while the service is running.  \r\nChanges are detected automatically and applied without restarting the service.  \r\n\r\nExample configuration (typed `Folders` + `Actions`):\r\n\r\n```json\r\n{\r\n  \"Folders\": [\r\n    {\r\n      \"FolderPath\": \"C:\\\\temp\\\\watch\",\r\n      \"ActionName\": \"RestEndpoint1\"\r\n    },\r\n    {\r\n      \"FolderPath\": \"C:\\\\data\\\\incoming\",\r\n      \"ActionName\": \"ObjectScript\"\r\n    }\r\n  ],\r\n  \"Actions\": [\r\n    {\r\n      \"Name\": \"RestEndpoint1\",\r\n      \"ActionType\": \"RestPost\",\r\n      \"ApiEndpoint\": \"https://api.example.com/files\",\r\n      \"BearerToken\": \"your-bearer-token-here\"\r\n    },\r\n    {\r\n      \"Name\": \"ObjectScript\",\r\n      \"ActionType\": \"PowerShellScript\",\r\n      \"ScriptPath\": \"C:\\\\scripts\\\\processObject.ps1\",\r\n      \"Arguments\": [\r\n        \"{FileNotification:json}\"\r\n      ],\r\n      \"IncludeSubdirectories\": true\r\n    }\r\n  ],\r\n  \"ApiEndpoint\": \"https://api.example.com/files\",\r\n  \"BearerToken\": \"your-bearer-token-here-will-be-encrypted-automatically\",\r\n  \"PostFileContents\": true,\r\n  \"MoveProcessedFiles\": true,\r\n  \"ProcessedFolder\": \"processed\",\r\n  \"AllowedExtensions\": [\r\n    \".txt\",\r\n    \".json\",\r\n    \".xml\"\r\n  ],\r\n  \"IncludeSubdirectories\": true,\r\n  \"DebounceMilliseconds\": 1000,\r\n  \"Retries\": 3,\r\n  \"RetryDelayMilliseconds\": 500,\r\n  \"WatcherMaxRestartAttempts\": 3,\r\n  \"WatcherRestartDelayMilliseconds\": 1000,\r\n  \"DiagnosticsUrlPrefix\": \"http://localhost:5005/\",\r\n  \"DiagnosticsBearerToken\": null,\r\n  \"ChannelCapacity\": 1000,\r\n  \"MaxParallelSends\": 4,\r\n  \"FileWatcherInternalBufferSize\": 65536,\r\n  \"WaitForFileReadyMilliseconds\": 0,\r\n  \"DiscardZeroByteFiles\": false,\r\n  \"MaxContentBytes\": 5242880,\r\n  \"StreamingThresholdBytes\": 262144,\r\n  \"EnableCircuitBreaker\": false,\r\n  \"CircuitBreakerFailureThreshold\": 5,\r\n  \"CircuitBreakerOpenDurationMilliseconds\": 30000,\r\n  \"Logging\": {\r\n    \"LogType\": \"Csv\",\r\n    \"FilePathPattern\": \"logs/FileWatchRest_{0:yyyyMMdd_HHmmss}\",\r\n    \"LogLevel\": \"Information\",\r\n    \"RetainedDays\": 14\r\n  }\r\n}\r\n```\r\n\r\nConfig file overrides  \r\n\r\nYou can override the default configuration file path when starting the service or in your environment:\r\n\r\n- **Command-line:** pass `--config \u003cpath\u003e` or `-c \u003cpath\u003e` to specify the configuration file to use.\r\n- **Environment variable:** set `FILEWATCHREST_CONFIG` to a file path and it will be used when no `--config` arg is provided.\r\n\r\nIf neither is provided the service falls back to the default file under `$env:ProgramData\\FileWatchRest\\FileWatchRest.json`.  \r\n\r\n## Additional Configuration Examples\r\n\r\nBelow are a few small example configurations demonstrating common patterns (minimal REST action, reusable PowerShell action, mixed action types, and legacy string-array folders). These are provided here for convenience; full example files are in the `examples/` folder and a single runnable template is in `FileWatchRest.json.example`.  \r\n\r\nExample files (in-repo):\r\n\r\n- `examples/FileWatchRest.example.minimal.json`: minimal REST-only example\r\n- `examples/FileWatchRest.example.powershell.json`: reusable PowerShell action example\r\n- `examples/FileWatchRest.example.mixed.json`: mixed executable + REST example\r\n\r\nUse these as starting points — copy the one you need to `FileWatchRest.json` (or point the service to it with `--config`).  \r\n\r\n### 1) Minimal single REST action (simple)\r\n\r\n```json\r\n{\r\n  \"Folders\": [\r\n    { \r\n      \"FolderPath\": \"C:\\\\temp\\\\watch\", \r\n      \"ActionName\": \"RestDefault\" \r\n    }\r\n  ],\r\n  \"Actions\": [\r\n    {\r\n      \"Name\": \"RestDefault\",\r\n      \"ActionType\": \"RestPost\",\r\n      \"ApiEndpoint\": \"https://api.example.com/files\"\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n### 2) PowerShell script per-folder (reusable actions)\r\n\r\n```json\r\n{\r\n  \"Folders\": [\r\n    { \r\n      \"FolderPath\": \"C:\\\\data\\\\incoming\", \r\n      \"ActionName\": \"ParseAndTransform\" \r\n    },\r\n    { \r\n      \"FolderPath\": \"C:\\\\data\\\\incoming\\\\objects\", \r\n      \"ActionName\": \"ParseAndTransform\" \r\n    }\r\n  ],\r\n  \"Actions\": [\r\n    {\r\n      \"Name\": \"ParseAndTransform\",\r\n      \"ActionType\": \"PowerShellScript\",\r\n      \"ScriptPath\": \"C:\\\\scripts\\\\processObject.ps1\",\r\n      \"Arguments\": [\"{FileNotification:json}\"],\r\n      \"IncludeSubdirectories\": true,\r\n      \"AllowedExtensions\": [\".json\", \".xml\"]\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n### 3) Mixed action types: script, executable, and REST with per-action overrides\r\n\r\n```json\r\n{\r\n  \"Folders\": [\r\n    { \r\n      \"FolderPath\": \"C:\\\\apps\\\\drop\", \r\n      \"ActionName\": \"RunExe\" \r\n    },\r\n    { \r\n      \"FolderPath\": \"C:\\\\invoices\", \r\n      \"ActionName\": \"PostInvoices\" \r\n    }\r\n  ],\r\n  \"Actions\": [\r\n    {\r\n      \"Name\": \"RunExe\",\r\n      \"ActionType\": \"Executable\",\r\n      \"ExecutablePath\": \"C:\\\\tools\\\\processor.exe\",\r\n      \"Arguments\": [\"--input\", \"{FilePath}\"],\r\n      \"MoveProcessedFiles\": true,\r\n      \"ProcessedFolder\": \"processed_exe\"\r\n    },\r\n    {\r\n      \"Name\": \"PostInvoices\",\r\n      \"ActionType\": \"RestPost\",\r\n      \"ApiEndpoint\": \"https://invoices.example.com/upload\",\r\n      \"BearerToken\": \"\u003cencrypted-or-plain-token\u003e\",\r\n      \"PostFileContents\": true,\r\n      \"AllowedExtensions\": [\".pdf\", \".docx\"],\r\n      \"Retries\": 5\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\n### 4) Legacy compatible: string-array `Folders` (migration)\r\n\r\nThe monitor accepts the legacy `Folders: [\"C:\\\\path\"]` string-array format and will migrate it into the typed object format during load. Prefer the typed object form for clarity and reusability.  \r\n\r\nConfiguration Options  \r\n  \r\n**Core File Watching Settings:**  \r\n\r\n- `Folders`: Array of typed folder objects. Each entry must include `FolderPath` and a reference to a named action via `ActionName`. Folders are lightweight mappings that reference reusable `Actions[]` entries which define processing behavior. Example:\r\n\r\n```json\r\n\"Folders\": [\r\n  {\r\n    \"FolderPath\": \"C:\\\\temp\\\\watch\",\r\n    \"ActionName\": \"RestEndpoint1\"\r\n  },\r\n  {\r\n    \"FolderPath\": \"C:\\\\data\\\\incoming\",\r\n    \"ActionName\": \"ObjectScript\"\r\n  }\r\n]\r\n```\r\n\r\n- `Actions`: Array of named `ActionConfig` objects. Each action is a complete, reusable processing configuration (action type, script/executable paths, REST endpoint, bearer token, file handling options, retries, circuit breaker settings, etc.). Example:\r\n\r\n```json\r\n\"Actions\": [\r\n  {\r\n    \"Name\": \"RestEndpoint1\",\r\n    \"ActionType\": \"RestPost\",\r\n    \"ApiEndpoint\": \"https://api.example.com/files\"\r\n  },\r\n  {\r\n    \"Name\": \"ObjectScript\",\r\n    \"ActionType\": \"PowerShellScript\",\r\n    \"ScriptPath\": \"C:\\\\scripts\\\\processObject.ps1\"\r\n  }\r\n]\r\n```\r\n\r\nPrecedence and overrides:\r\n\r\n- Settings on an `ActionConfig` override global settings for any folder mapped to that action.\r\n- Global (root) settings are defaults used when an action does not specify a value.\r\n- `Folders` are intentionally lightweight (path + `ActionName`) and do not carry overrides.\r\n- Arrays and null/empty semantics:\r\n  - If a global collection (e.g., `AllowedExtensions`, `ExcludePatterns`) is null or empty, it means “no filtering” unless the action provides values.\r\n  - If an action provides a collection, it fully defines the behavior for that folder mapping.\r\n  - If an action explicitly provides an empty collection, it disables that filter for that action (e.g., empty `AllowedExtensions` means all files allowed).\r\n- `ApiEndpoint`: HTTP endpoint to POST file notifications to\r\n- `BearerToken`: Bearer token for API authentication. **Automatically encrypted** using\r\n  machine-specific encryption when saved. Plain text tokens are automatically encrypted on first  \r\n  save.  \r\n- `PostFileContents`: If true, reads and includes file contents in the POST\r\n- `ExecutionTimeoutMilliseconds`: Optional per-action timeout in milliseconds. When set, the action's process will be terminated if it runs longer than this duration. Default: 60000 (60s).\r\n- `IgnoreOutput`: Optional boolean. When true the action will not capture or log stdout/stderr (they are not redirected). Use this to avoid buffering or logging large outputs. Default: false.\r\n- `MoveProcessedFiles`: If true, moves files to processed folder after successful POST\r\n- `ProcessedFolder`: Name of subfolder to move processed files to (default: \"processed\"). Files in\r\n  this folder are automatically excluded from monitoring to prevent infinite loops.  \r\n- `AllowedExtensions`: Array of file extensions to watch (empty = all files)\r\n- `ExcludePatterns`: Array of filename patterns to exclude from processing. Supports wildcard\r\n  matching with `*` (any characters) and `?` (single character). Examples: `\"Backup_*\"` (starts with  \r\n  Backup\\_), `\"*_temp\"` (ends with \\_temp), `\"*.bak\"` (backup files). Files matching any exclude  \r\n  pattern are ignored even if they pass extension filtering.  \r\n- `IncludeSubdirectories`: Whether to watch subfolders\r\n- `DebounceMilliseconds`: Wait time to debounce file events\r\n\r\n**Performance and Reliability Settings:**  \r\n\r\n- `Retries`: Number of retry attempts for failed API calls (default: 3)\r\n- `RetryDelayMilliseconds`: Delay between retry attempts (default: 500)\r\n- `WatcherMaxRestartAttempts`: Max attempts to restart a failed file watcher (default: 3)\r\n- `WatcherRestartDelayMilliseconds`: Delay before restarting a watcher (default: 1000)\r\n- `DiagnosticsUrlPrefix`: URL prefix for diagnostics endpoint (default: \"\u003chttp://localhost:5005/\u003e\")\r\n- `DiagnosticsBearerToken`: Optional bearer token required to access diagnostics endpoints. If\r\n  null or empty, diagnostics endpoints are accessible without authentication. No token is generated automatically.  \r\n- `ChannelCapacity`: Internal channel capacity for pending file events (default: 1000)\r\n- `MaxParallelSends`: Number of concurrent HTTP senders (default: 4)\r\n- `FileWatcherInternalBufferSize`: FileSystemWatcher buffer size in bytes (default: 65536)\r\n- `WaitForFileReadyMilliseconds`: Wait time for files to become ready before processing (default: 0)\r\n- `DiscardZeroByteFiles`: If true, files that remain zero bytes after waiting the configured\r\n  `WaitForFileReadyMilliseconds` will be discarded and not posted. Default: false. Use this when  \r\n  producers create zero-length placeholder files that should be ignored.  \r\n- `MaxContentBytes`: Maximum bytes of file content to include in the POST request. Files larger than\r\n  this are sent without inline content.  \r\n- `StreamingThresholdBytes`: Size threshold for switching to streaming uploads. Files larger than\r\n  this use multipart streaming for uploads.  \r\n- `EnableCircuitBreaker`: Enables an optional circuit breaker for HTTP calls. When enabled, the\r\n  circuit breaker trips after a number of failures, temporarily blocking requests to allow the  \r\n  remote service to recover.  \r\n- `CircuitBreakerFailureThreshold`: Number of consecutive failures required to trip the circuit\r\n  breaker (default: 5).  \r\n- `CircuitBreakerOpenDurationMilliseconds`: Time duration in milliseconds to keep the circuit\r\n  breaker open before allowing retries (default: 30000).  \r\n\r\n`System.Security.Cryptography.ProtectedData` with machine-specific encryption. This means:\r\n\r\n- Plain text bearer tokens are automatically encrypted when the configuration is first saved\r\n- Encrypted tokens can only be decrypted on the same machine by the same application\r\n- Configuration files are safe to store in version control (tokens are encrypted)\r\n- No master password or key management required - Windows handles the encryption keys\r\n\r\n**Migration Support**: Existing plain text tokens are automatically detected and encrypted on the  \r\nnext configuration save without requiring user intervention.  \r\n\r\nDevelopment and Testing  \r\n  \r\nRun locally from repository root:\r\n\r\n```powershell\r\n# Build\r\ndotnet build FileWatchRest.sln  \r\n\r\n# Run tests (123 comprehensive tests)\r\ndotnet test FileWatchRest.sln\r\n\r\n# Run with coverage\r\ndotnet test FileWatchRest.sln --collect:\"XPlat Code Coverage\"\r\n\r\n# Run as console for testing\r\ndotnet run --project .\\FileWatchRest\\FileWatchRest.csproj  \r\n```\r\n\r\nPackaging for Deployment  \r\n  \r\nPrepare a deployment package (creates `./output` by default):\r\n\r\n```powershell\r\npwsh -NoProfile -ExecutionPolicy Bypass -File .\\build.ps1 -ProjectPath FileWatchRest -OutputDir .\\output  \r\n```\r\n\r\nThe script automatically creates a deployment package with `install_on_target.ps1`.  \r\n\r\nInstallation on Target Machine  \r\n  \r\n1. Copy the entire `output` folder to the target machine\r\n2. As Administrator, run from inside the `output` folder:\r\n\r\n```powershell\r\npwsh -NoProfile -ExecutionPolicy Bypass .\\install_on_target.ps1  \r\n```\r\n\r\nThis installs files to `$env:ProgramFiles\\FileWatchRest`, creates and starts the Windows service,  \r\nand sets up the configuration directory under `$env:ProgramData\\FileWatchRest`.  \r\n\r\nAPI Payload Format  \r\n  \r\nThe service POSTs JSON data to your configured endpoint:\r\n\r\nBasic Notification (metadata)  \r\n  \r\n```json\r\n{  \r\n  \"Path\": \"C:\\\\temp\\\\watch\\\\example.txt\",  \r\n  \"Content\": null,  \r\n  \"ComputerName\": \"Server1\",\r\n  \"FileSize\": 1024,  \r\n  \"LastWriteTime\": \"2025-09-17T10:30:00\"  \r\n}  \r\n```\r\n\r\nFull Notification (with content)  \r\n  \r\n```json\r\n{  \r\n  \"Path\": \"C:\\\\temp\\\\watch\\\\example.txt\",  \r\n  \"Content\": \"file content here...\",  \r\n  \"ComputerName\": \"Server2\",\r\n  \"FileSize\": 1024,  \r\n  \"LastWriteTime\": \"2025-09-17T10:30:00\"  \r\n}  \r\n```\r\n\r\n📊 Diagnostics Endpoints  \r\n  \r\nThe service provides a built-in HTTP server for real-time diagnostics and monitoring. The server  \r\nruns on the URL specified by `DiagnosticsUrlPrefix` (default: `http://localhost:5005/`).  \r\n\r\nDiagnostics Endpoints  \r\n  \r\n| Endpoint | Description | Response Format |\r\n|----------|-------------|-----------------|\r\n| `GET /` | Complete service status (same as `/status`) | JSON |\r\n| `GET /status` | Full service metrics and diagnostics | JSON |\r\n| `GET /health` | Simple health check | JSON |\r\n| `GET /events` | Recent file processing events (last 500) | JSON |\r\n| `GET /watchers` | Currently active folder watchers | JSON |\r\n| `GET /config` | Current runtime configuration (normalized) | JSON |\r\n| `GET /metrics` | Prometheus-compatible metrics | Text |\r\n| `GET /circuits` | Circuit breaker states per endpoint | JSON |\r\n\r\nExamples  \r\n  \r\nGET /status  \r\n  \r\n```json\r\n{\r\n  \"ActiveWatchers\": [\"C:\\\\temp\\\\watch\", \"C:\\\\data\\\\incoming\"],\r\n  \"RestartAttempts\": {},\r\n  \"RecentEvents\": [\r\n    {\r\n      \"Path\": \"C:\\\\temp\\\\watch\\\\document.txt\",\r\n      \"Timestamp\": \"2025-09-18T14:30:22.123Z\",\r\n      \"PostedSuccess\": true,\r\n      \"StatusCode\": 200\r\n    }\r\n  ],\r\n  \"Timestamp\": \"2025-09-18T14:30:22.456Z\",\r\n  \"EventCount\": 15\r\n}\r\n```\r\n\r\nGET /health  \r\n  \r\n```json\r\n{\r\n  \"status\": \"healthy\",\r\n  \"timestamp\": \"2025-09-18T14:30:22.456Z\"\r\n}\r\n```\r\n\r\nGET /events  \r\n  \r\n```json\r\n[\r\n  {\r\n    \"Path\": \"C:\\\\temp\\\\watch\\\\file1.txt\",\r\n    \"Timestamp\": \"2025-09-18T14:30:22.123Z\",\r\n    \"PostedSuccess\": true,\r\n    \"StatusCode\": 200\r\n  },\r\n  {\r\n    \"Path\": \"C:\\\\temp\\\\watch\\\\file2.txt\", \r\n    \"Timestamp\": \"2025-09-18T14:29:15.456Z\",\r\n    \"PostedSuccess\": false,\r\n    \"StatusCode\": 500\r\n  }\r\n]\r\n```\r\n\r\n**Features:**  \r\n\r\n- **Lightweight**: HttpListener-based, no ASP.NET Core overhead\r\n- **Real-time monitoring**: See file processing events as they happen\r\n- **CORS enabled**: Browser-accessible from any origin  \r\n- **Error tracking**: Monitor failed API calls and retry attempts\r\n- **Service health**: Quick health checks for monitoring systems\r\n\r\n**Usage:**  \r\n\r\n1. Start the FileWatchRest service\r\n2. Open browser to `http://localhost:5005/status`\r\n3. Monitor file processing in real-time\r\n4. Use `/events` for troubleshooting failed file processing\r\n\r\nSecurity and accessing diagnostics  \r\n  \r\nBy default, diagnostics endpoints are **unauthenticated** and accessible without credentials. To secure them, set `DiagnosticsBearerToken` in your configuration:\r\n\r\n```json\r\n{\r\n  \"DiagnosticsBearerToken\": \"your-secret-token-here\"\r\n}\r\n```\r\n\r\nWhen a token is configured, all diagnostics endpoints require a matching Authorization header:\r\n\r\n```bash\r\ncurl -H \"Authorization: Bearer your-secret-token-here\" http://localhost:5005/status\r\n```\r\n\r\n**Note**: If you configure a token, it will be automatically encrypted using Windows machine-specific encryption when the configuration is saved, just like API bearer tokens.  \r\n\r\nLogging  \r\n  \r\nLogging is configured from the same external configuration file used by the service  \r\n(`$env:ProgramData\\\\FileWatchRest\\\\FileWatchRest.json`)  \r\n\r\nconfiguration file. By default the service emits CSV logs and JSON is opt-in. The configuration  \r\nfocuses on a single file name/pattern and an explicit `LogType` selector. The provider automatically  \r\nappends the correct file extension based on the `LogType` value.  \r\nThe service uses modern structured logging with LoggerMessage delegates for zero-allocation logging.  \r\nYou can select CSV, JSON, or both via the `Logging` / `LoggingOptions` settings in the configuration  \r\nfile. By default the service emits CSV logs and JSON is opt-in. The configuration focuses on a single  \r\nfile name/pattern and an explicit `LogType` selector. The provider automatically appends the correct  \r\nfile extension based on the `LogType` value.  \r\n\r\nDefault logging locations (per-run timestamped by default):\r\n\r\n- `$env:ProgramData\\\\FileWatchRest\\\\logs\\\\FileWatchRest_{0:yyyyMMdd_HHmmss}.csv` (structured CSV)\r\n- `$env:ProgramData\\\\FileWatchRest\\\\logs\\\\FileWatchRest_{0:yyyyMMdd_HHmmss}.ndjson` (structured\r\n  JSON)  \r\n\r\nExample `Logging` section (place this in the external configuration `FileWatchRest.json`):\r\n\r\n```json\r\n\"Logging\": {\r\n  \"LogType\": \"Csv\",        // One of: \"Csv\", \"Json\", \"Both\"\r\n  \"FilePathPattern\": \"logs/FileWatchRest_{0:yyyyMMdd_HHmmss}\",\r\n  \"LogLevel\": \"Information\",\r\n  \"RetainedDays\": 14\r\n}\r\n```\r\n\r\nNotes:\r\n\r\n- `LogType` selects which file formats the built-in provider writes. The provider automatically\r\n  appends the appropriate extension (`.csv` for Csv, `.ndjson` for Json).  \r\n- `LogLevel` can be adjusted at runtime via the configuration file; note that changing the log file\r\n  target (FilePathPattern or LogType) typically requires a service restart for the file provider to  \r\n  open new files.  \r\n\r\nTroubleshooting  \r\n  \r\nService Won't Start  \r\n  \r\n- Run the executable directly from command prompt to see console errors\r\n- Check Windows Event Log for startup failures\r\n- Verify configuration file exists and is valid JSON\r\n\r\nFiles Not Being Detected  \r\n  \r\n- Check that folder paths in configuration exist and are accessible\r\n- Verify file extensions match `AllowedExtensions` if specified\r\n- Review logs (JSON/CSV) for watcher errors or restart attempts\r\n- **Note**: Files in folders matching the `ProcessedFolder` configuration value are automatically\r\n  ignored to prevent infinite processing loops  \r\n\r\nAPI Calls Failing  \r\n  \r\n- Verify `ApiEndpoint` is correct and accessible\r\n- Check `BearerToken` if API requires authentication\r\n- Review retry settings in `appsettings.json`\r\n- Check logs (JSON/CSV) for HTTP status codes\r\n\r\nNative AOT Deployment  \r\n  \r\nFor high-performance deployment with Native AOT:\r\n\r\n```powershell\r\npwsh -NoProfile -ExecutionPolicy Bypass -File .\\build.ps1\r\n```\r\n\r\n**Requirements**: Visual C++ build tools must be installed on the build machine for Native AOT  \r\ncompilation.  \r\n\r\nConfiguration Management  \r\n  \r\n- **Single Configuration File**: All settings are now in one place - `FileWatchRest.json`\r\n- Configuration changes are detected automatically - no service restart required\r\n- Invalid JSON will cause service to use previous valid configuration\r\n- Default configuration is created automatically on first run\r\n- Configuration file can be edited manually or through automated deployment scripts\r\n- No need for separate `appsettings.json` modifications\r\n\r\n# Test version bump\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrackd%2Ffilewatchrest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrackd%2Ffilewatchrest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrackd%2Ffilewatchrest/lists"}