telegram-ssh-bot 2.0.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -22
  3. package/deploy/.env.example +86 -0
  4. package/dist/config/index.d.ts +77 -0
  5. package/dist/config/index.d.ts.map +1 -0
  6. package/dist/config/index.js +333 -0
  7. package/dist/config/index.js.map +1 -0
  8. package/dist/config/schema.d.ts +6 -0
  9. package/dist/config/schema.d.ts.map +1 -0
  10. package/dist/config/schema.js +50 -0
  11. package/dist/config/schema.js.map +1 -0
  12. package/dist/core/Bot.d.ts +91 -0
  13. package/dist/core/Bot.d.ts.map +1 -0
  14. package/dist/core/Bot.js +263 -0
  15. package/dist/core/Bot.js.map +1 -0
  16. package/dist/core/ConnectionPool.d.ts +125 -0
  17. package/dist/core/ConnectionPool.d.ts.map +1 -0
  18. package/dist/core/ConnectionPool.js +397 -0
  19. package/dist/core/ConnectionPool.js.map +1 -0
  20. package/dist/core/SSHClient.d.ts +112 -0
  21. package/dist/core/SSHClient.d.ts.map +1 -0
  22. package/dist/core/SSHClient.js +367 -0
  23. package/dist/core/SSHClient.js.map +1 -0
  24. package/dist/core/ServerManager.d.ts +80 -0
  25. package/dist/core/ServerManager.d.ts.map +1 -0
  26. package/dist/core/ServerManager.js +207 -0
  27. package/dist/core/ServerManager.js.map +1 -0
  28. package/dist/core/index.d.ts +8 -0
  29. package/dist/core/index.d.ts.map +1 -0
  30. package/dist/core/index.js +8 -0
  31. package/dist/core/index.js.map +1 -0
  32. package/dist/errors/AuthError.d.ts +30 -0
  33. package/dist/errors/AuthError.d.ts.map +1 -0
  34. package/dist/errors/AuthError.js +35 -0
  35. package/dist/errors/AuthError.js.map +1 -0
  36. package/dist/errors/BaseError.d.ts +17 -0
  37. package/dist/errors/BaseError.d.ts.map +1 -0
  38. package/dist/errors/BaseError.js +34 -0
  39. package/dist/errors/BaseError.js.map +1 -0
  40. package/dist/errors/ConfigurationError.d.ts +24 -0
  41. package/dist/errors/ConfigurationError.d.ts.map +1 -0
  42. package/dist/errors/ConfigurationError.js +24 -0
  43. package/dist/errors/ConfigurationError.js.map +1 -0
  44. package/dist/errors/PoolError.d.ts +21 -0
  45. package/dist/errors/PoolError.d.ts.map +1 -0
  46. package/dist/errors/PoolError.js +30 -0
  47. package/dist/errors/PoolError.js.map +1 -0
  48. package/dist/errors/SSHError.d.ts +24 -0
  49. package/dist/errors/SSHError.d.ts.map +1 -0
  50. package/dist/errors/SSHError.js +38 -0
  51. package/dist/errors/SSHError.js.map +1 -0
  52. package/dist/errors/StorageError.d.ts +24 -0
  53. package/dist/errors/StorageError.d.ts.map +1 -0
  54. package/dist/errors/StorageError.js +35 -0
  55. package/dist/errors/StorageError.js.map +1 -0
  56. package/dist/errors/ValidationError.d.ts +29 -0
  57. package/dist/errors/ValidationError.d.ts.map +1 -0
  58. package/dist/errors/ValidationError.js +35 -0
  59. package/dist/errors/ValidationError.js.map +1 -0
  60. package/dist/errors/index.d.ts +11 -0
  61. package/dist/errors/index.d.ts.map +1 -0
  62. package/dist/errors/index.js +18 -0
  63. package/dist/errors/index.js.map +1 -0
  64. package/dist/handlers/BaseHandler.d.ts +50 -0
  65. package/dist/handlers/BaseHandler.d.ts.map +1 -0
  66. package/dist/handlers/BaseHandler.js +87 -0
  67. package/dist/handlers/BaseHandler.js.map +1 -0
  68. package/dist/handlers/CommandHandler.d.ts +23 -0
  69. package/dist/handlers/CommandHandler.d.ts.map +1 -0
  70. package/dist/handlers/CommandHandler.js +99 -0
  71. package/dist/handlers/CommandHandler.js.map +1 -0
  72. package/dist/handlers/HealthHandler.d.ts +25 -0
  73. package/dist/handlers/HealthHandler.d.ts.map +1 -0
  74. package/dist/handlers/HealthHandler.js +51 -0
  75. package/dist/handlers/HealthHandler.js.map +1 -0
  76. package/dist/handlers/HelpHandler.d.ts +32 -0
  77. package/dist/handlers/HelpHandler.d.ts.map +1 -0
  78. package/dist/handlers/HelpHandler.js +76 -0
  79. package/dist/handlers/HelpHandler.js.map +1 -0
  80. package/dist/handlers/ServerHandler.d.ts +72 -0
  81. package/dist/handlers/ServerHandler.d.ts.map +1 -0
  82. package/dist/handlers/ServerHandler.js +272 -0
  83. package/dist/handlers/ServerHandler.js.map +1 -0
  84. package/dist/handlers/index.d.ts +9 -0
  85. package/dist/handlers/index.d.ts.map +1 -0
  86. package/dist/handlers/index.js +9 -0
  87. package/dist/handlers/index.js.map +1 -0
  88. package/dist/index.d.ts +10 -0
  89. package/dist/index.d.ts.map +1 -0
  90. package/dist/index.js +365 -0
  91. package/dist/index.js.map +1 -0
  92. package/dist/middleware/AuthMiddleware.d.ts +28 -0
  93. package/dist/middleware/AuthMiddleware.d.ts.map +1 -0
  94. package/dist/middleware/AuthMiddleware.js +49 -0
  95. package/dist/middleware/AuthMiddleware.js.map +1 -0
  96. package/dist/middleware/RateLimitMiddleware.d.ts +23 -0
  97. package/dist/middleware/RateLimitMiddleware.d.ts.map +1 -0
  98. package/dist/middleware/RateLimitMiddleware.js +34 -0
  99. package/dist/middleware/RateLimitMiddleware.js.map +1 -0
  100. package/dist/middleware/index.d.ts +6 -0
  101. package/dist/middleware/index.d.ts.map +1 -0
  102. package/dist/middleware/index.js +6 -0
  103. package/dist/middleware/index.js.map +1 -0
  104. package/dist/services/BackupService.d.ts +119 -0
  105. package/dist/services/BackupService.d.ts.map +1 -0
  106. package/dist/services/BackupService.js +313 -0
  107. package/dist/services/BackupService.js.map +1 -0
  108. package/dist/services/CryptoService.d.ts +37 -0
  109. package/dist/services/CryptoService.d.ts.map +1 -0
  110. package/dist/services/CryptoService.js +108 -0
  111. package/dist/services/CryptoService.js.map +1 -0
  112. package/dist/services/HealthService.d.ts +126 -0
  113. package/dist/services/HealthService.d.ts.map +1 -0
  114. package/dist/services/HealthService.js +213 -0
  115. package/dist/services/HealthService.js.map +1 -0
  116. package/dist/services/LoggingService.d.ts +115 -0
  117. package/dist/services/LoggingService.d.ts.map +1 -0
  118. package/dist/services/LoggingService.js +334 -0
  119. package/dist/services/LoggingService.js.map +1 -0
  120. package/dist/services/MonitoringService.d.ts +119 -0
  121. package/dist/services/MonitoringService.d.ts.map +1 -0
  122. package/dist/services/MonitoringService.js +267 -0
  123. package/dist/services/MonitoringService.js.map +1 -0
  124. package/dist/services/NotificationService.d.ts +132 -0
  125. package/dist/services/NotificationService.d.ts.map +1 -0
  126. package/dist/services/NotificationService.js +297 -0
  127. package/dist/services/NotificationService.js.map +1 -0
  128. package/dist/services/RateLimiter.d.ts +51 -0
  129. package/dist/services/RateLimiter.d.ts.map +1 -0
  130. package/dist/services/RateLimiter.js +141 -0
  131. package/dist/services/RateLimiter.js.map +1 -0
  132. package/dist/services/ValidationService.d.ts +49 -0
  133. package/dist/services/ValidationService.d.ts.map +1 -0
  134. package/dist/services/ValidationService.js +158 -0
  135. package/dist/services/ValidationService.js.map +1 -0
  136. package/dist/services/index.d.ts +12 -0
  137. package/dist/services/index.d.ts.map +1 -0
  138. package/dist/services/index.js +12 -0
  139. package/dist/services/index.js.map +1 -0
  140. package/dist/types/Bot.d.ts +63 -0
  141. package/dist/types/Bot.d.ts.map +1 -0
  142. package/dist/types/Bot.js +5 -0
  143. package/dist/types/Bot.js.map +1 -0
  144. package/dist/types/Config.d.ts +57 -0
  145. package/dist/types/Config.d.ts.map +1 -0
  146. package/dist/types/Config.js +5 -0
  147. package/dist/types/Config.js.map +1 -0
  148. package/dist/types/Errors.d.ts +37 -0
  149. package/dist/types/Errors.d.ts.map +1 -0
  150. package/dist/types/Errors.js +34 -0
  151. package/dist/types/Errors.js.map +1 -0
  152. package/dist/types/SSH.d.ts +56 -0
  153. package/dist/types/SSH.d.ts.map +1 -0
  154. package/dist/types/SSH.js +6 -0
  155. package/dist/types/SSH.js.map +1 -0
  156. package/dist/types/Server.d.ts +39 -0
  157. package/dist/types/Server.d.ts.map +1 -0
  158. package/dist/types/Server.js +5 -0
  159. package/dist/types/Server.js.map +1 -0
  160. package/dist/types/index.d.ts +10 -0
  161. package/dist/types/index.d.ts.map +1 -0
  162. package/dist/types/index.js +6 -0
  163. package/dist/types/index.js.map +1 -0
  164. package/dist/utils/commandUtils.d.ts +25 -0
  165. package/dist/utils/commandUtils.d.ts.map +1 -0
  166. package/dist/utils/commandUtils.js +94 -0
  167. package/dist/utils/commandUtils.js.map +1 -0
  168. package/dist/utils/fileUtils.d.ts +40 -0
  169. package/dist/utils/fileUtils.d.ts.map +1 -0
  170. package/dist/utils/fileUtils.js +114 -0
  171. package/dist/utils/fileUtils.js.map +1 -0
  172. package/dist/utils/index.d.ts +7 -0
  173. package/dist/utils/index.d.ts.map +1 -0
  174. package/dist/utils/index.js +7 -0
  175. package/dist/utils/index.js.map +1 -0
  176. package/dist/utils/pathUtils.d.ts +40 -0
  177. package/dist/utils/pathUtils.d.ts.map +1 -0
  178. package/dist/utils/pathUtils.js +140 -0
  179. package/dist/utils/pathUtils.js.map +1 -0
  180. package/dist/wizard/InstallationWizard.d.ts +32 -0
  181. package/dist/wizard/InstallationWizard.d.ts.map +1 -0
  182. package/dist/wizard/InstallationWizard.js +96 -0
  183. package/dist/wizard/InstallationWizard.js.map +1 -0
  184. package/dist/wizard/index.d.ts +10 -0
  185. package/dist/wizard/index.d.ts.map +1 -0
  186. package/dist/wizard/index.js +8 -0
  187. package/dist/wizard/index.js.map +1 -0
  188. package/dist/wizard/prompts.d.ts +45 -0
  189. package/dist/wizard/prompts.d.ts.map +1 -0
  190. package/dist/wizard/prompts.js +194 -0
  191. package/dist/wizard/prompts.js.map +1 -0
  192. package/dist/wizard/validators.d.ts +50 -0
  193. package/dist/wizard/validators.d.ts.map +1 -0
  194. package/dist/wizard/validators.js +107 -0
  195. package/dist/wizard/validators.js.map +1 -0
  196. package/package.json +33 -5
  197. package/scripts/build.sh +20 -0
  198. package/scripts/postinstall.js +87 -0
  199. package/scripts/release.sh +22 -0
  200. package/scripts/setup-env.d.ts +28 -0
  201. package/scripts/setup-env.js +293 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 farhanzzg
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -26,29 +26,47 @@ A secure, production-ready Telegram bot for remote SSH server management.
26
26
 
27
27
  ### Prerequisites
28
28
 
29
- - Node.js 18+ (for development)
30
- - Linux (for production deployment)
29
+ - Node.js 18+ (for npm/pnpm installation or development)
30
+ - Linux (for production deployment with systemd)
31
31
 
32
32
  ### Installation
33
33
 
34
- #### From Binary (Recommended)
34
+ #### Option 1: npm/pnpm Global (Recommended)
35
+
36
+ ```bash
37
+ # Install globally with npm
38
+ npm install -g telegram-ssh-bot
39
+
40
+ # Or with pnpm
41
+ pnpm add -g telegram-ssh-bot
42
+
43
+ # The .env file is auto-generated at ~/.config/telegram-ssh-bot/.env
44
+ # Edit with your credentials
45
+ nano ~/.config/telegram-ssh-bot/.env
46
+
47
+ # Start the bot
48
+ telegram-ssh-bot
49
+ ```
50
+
51
+ #### Option 2: Binary Installation
35
52
 
36
53
  ```bash
37
54
  # Download and install
38
55
  ./deploy/install.sh
39
56
 
40
- # Configure
57
+ # The .env file is auto-generated at ~/.config/telegram-ssh-bot/.env
58
+ # Edit with your credentials
41
59
  nano ~/.config/telegram-ssh-bot/.env
42
60
 
43
- # Start
61
+ # Start with systemd
44
62
  ./deploy/manage.sh start
45
63
  ```
46
64
 
47
- #### From Source
65
+ #### Option 3: From Source
48
66
 
49
67
  ```bash
50
68
  # Clone and build
51
- git clone https://github.com/user/telegram-ssh-bot.git
69
+ git clone https://github.com/farhanzzg/telegram-ssh.git
52
70
  cd telegram-ssh-bot
53
71
  npm install
54
72
  npm run build:binary
@@ -59,40 +77,103 @@ npm run build:binary
59
77
 
60
78
  ## Configuration
61
79
 
62
- Create `~/.config/telegram-ssh-bot/.env`:
80
+ The configuration file is automatically generated at `~/.config/telegram-ssh-bot/.env` during installation. The encryption key is also auto-generated for you.
63
81
 
64
82
  ```bash
65
83
  # Required
66
84
  BOT_TOKEN=your_telegram_bot_token
67
85
  BOT_CHAT_ID=your_chat_id
68
86
  BOT_OWNER_IDS=123456789,987654321
69
- ENCRYPTION_KEY=64_char_hex_key
87
+ ENCRYPTION_KEY=auto_generated_64_char_hex_key
70
88
 
71
- # Optional
72
- LOG_LEVEL=info
89
+ # SSH Configuration
90
+ SSH_DEFAULT_PORT=22
91
+ SSH_CONNECTION_TIMEOUT=30000
73
92
  SSH_COMMAND_TIMEOUT=30000
93
+ SSH_DEFAULT_PRIVATE_KEY_PATH=
94
+
95
+ # Rate Limiting
96
+ RATE_LIMIT_WINDOW_MS=60000
97
+ RATE_LIMIT_MAX_REQUESTS=100
98
+
99
+ # Backup
100
+ BACKUP_ENABLED=true
101
+ BACKUP_INTERVAL_MS=3600000
102
+ BACKUP_MAX_COUNT=10
103
+
104
+ # Monitoring
105
+ MONITORING_ENABLED=true
106
+ MONITORING_INTERVAL_MS=300000
107
+
108
+ # Logging (Optional)
109
+ LOG_LEVEL=info
74
110
  ```
75
111
 
76
112
  ### Configuration Options
77
113
 
78
- | Variable | Required | Description |
79
- | --------------------- | -------- | ----------------------------------------------------------------- |
80
- | `BOT_TOKEN` | Yes | Telegram bot token from [@BotFather](https://t.me/botfather) |
81
- | `BOT_CHAT_ID` | Yes | Primary chat ID for notifications |
82
- | `BOT_OWNER_IDS` | Yes | Comma-separated list of authorized user IDs |
83
- | `ENCRYPTION_KEY` | Yes | 64-character hex key for credential encryption |
84
- | `LOG_LEVEL` | No | Logging level: `error`, `warn`, `info`, `debug` (default: `info`) |
85
- | `SSH_COMMAND_TIMEOUT` | No | Command timeout in ms (default: `30000`) |
86
- | `BACKUP_INTERVAL` | No | Backup interval in ms (default: `3600000`) |
87
- | `MAX_CONNECTIONS` | No | Max SSH connections in pool (default: `10`) |
114
+ #### Required Variables
115
+
116
+ | Variable | Description |
117
+ | ---------------- | --------------------------------------------------------------- |
118
+ | `BOT_TOKEN` | Telegram bot token from [@BotFather](https://t.me/botfather) |
119
+ | `BOT_CHAT_ID` | Primary chat ID for notifications |
120
+ | `BOT_OWNER_IDS` | Comma-separated list of authorized user IDs |
121
+ | `ENCRYPTION_KEY` | 64-character hex key for credential encryption (auto-generated) |
88
122
 
89
- ### Generating Encryption Key
123
+ #### SSH Configuration
124
+
125
+ | Variable | Default | Description |
126
+ | ------------------------------ | --------------- | -------------------------------- |
127
+ | `SSH_DEFAULT_PORT` | `22` | Default SSH port for new servers |
128
+ | `SSH_CONNECTION_TIMEOUT` | `30000` | Connection timeout in ms |
129
+ | `SSH_COMMAND_TIMEOUT` | `30000` | Command execution timeout in ms |
130
+ | `SSH_DEFAULT_PRIVATE_KEY_PATH` | `~/.ssh/id_rsa` | Default private key path |
131
+
132
+ #### Rate Limiting
133
+
134
+ | Variable | Default | Description |
135
+ | ------------------------- | ------- | ---------------------------------- |
136
+ | `RATE_LIMIT_WINDOW_MS` | `60000` | Rate limit window in ms (1 minute) |
137
+ | `RATE_LIMIT_MAX_REQUESTS` | `100` | Max requests per window |
138
+
139
+ #### Backup Configuration
140
+
141
+ | Variable | Default | Description |
142
+ | -------------------- | --------- | ------------------------------ |
143
+ | `BACKUP_ENABLED` | `true` | Enable automatic backups |
144
+ | `BACKUP_INTERVAL_MS` | `3600000` | Backup interval in ms (1 hour) |
145
+ | `BACKUP_MAX_COUNT` | `10` | Maximum backup files to keep |
146
+
147
+ #### Monitoring Configuration
148
+
149
+ | Variable | Default | Description |
150
+ | ------------------------ | -------- | ----------------------------------- |
151
+ | `MONITORING_ENABLED` | `true` | Enable health monitoring |
152
+ | `MONITORING_INTERVAL_MS` | `300000` | Health check interval in ms (5 min) |
153
+
154
+ #### Optional Variables
155
+
156
+ | Variable | Default | Description |
157
+ | ------------- | ------- | ----------------------------------------------------- |
158
+ | `LOG_LEVEL` | `info` | Logging level: `error`, `warn`, `info`, `debug` |
159
+ | `BOT_API_URL` | - | Custom Telegram Bot API URL (for proxies/middlewares) |
160
+
161
+ ### Auto-Generated Configuration
162
+
163
+ During installation, the following are automatically generated:
164
+
165
+ - **Configuration file**: `~/.config/telegram-ssh-bot/.env`
166
+ - **Encryption key**: Secure 64-character hex key (32 bytes)
167
+
168
+ If you need to regenerate the encryption key:
90
169
 
91
170
  ```bash
92
171
  # Generate a secure 64-character hex key
93
172
  openssl rand -hex 32
94
173
  ```
95
174
 
175
+ > **Note**: Changing the encryption key will make previously encrypted credentials unreadable. You'll need to re-add your servers after changing the key.
176
+
96
177
  ## Usage
97
178
 
98
179
  ### Bot Commands
@@ -0,0 +1,86 @@
1
+ # =============================================================================
2
+ # Telegram SSH Bot Configuration
3
+ # =============================================================================
4
+ # Copy this file to ~/.config/telegram-ssh-bot/.env and fill in your values
5
+ # =============================================================================
6
+
7
+ # -----------------------------------------------------------------------------
8
+ # Telegram Bot Configuration
9
+ # -----------------------------------------------------------------------------
10
+ # Your Telegram bot token obtained from @BotFather
11
+ BOT_TOKEN=your_telegram_bot_token_here
12
+
13
+ # The default chat ID for notifications (your personal chat or a group)
14
+ # Use @userinfobot to get your chat ID
15
+ BOT_CHAT_ID=your_chat_id_here
16
+
17
+ # Comma-separated list of Telegram user IDs who can control the bot
18
+ # These users will have full access to all bot commands
19
+ BOT_OWNER_IDS=123456789,987654321
20
+
21
+ # -----------------------------------------------------------------------------
22
+ # Security
23
+ # -----------------------------------------------------------------------------
24
+ # Encryption key for sensitive data (64 hex characters = 32 bytes)
25
+ # A secure key will be auto-generated during installation
26
+ # You can generate a new one with: openssl rand -hex 32
27
+ ENCRYPTION_KEY=
28
+
29
+ # -----------------------------------------------------------------------------
30
+ # SSH Configuration
31
+ # -----------------------------------------------------------------------------
32
+ # Default SSH port for new server connections
33
+ SSH_DEFAULT_PORT=22
34
+
35
+ # Timeout for establishing SSH connections (in milliseconds)
36
+ SSH_CONNECTION_TIMEOUT=30000
37
+
38
+ # Timeout for executing SSH commands (in milliseconds)
39
+ SSH_COMMAND_TIMEOUT=30000
40
+
41
+ # Default path to SSH private key (leave empty to use ~/.ssh/id_rsa)
42
+ # Example: /home/user/.ssh/custom_key
43
+ SSH_DEFAULT_PRIVATE_KEY_PATH=
44
+
45
+ # -----------------------------------------------------------------------------
46
+ # Rate Limiting
47
+ # -----------------------------------------------------------------------------
48
+ # Time window for rate limiting (in milliseconds)
49
+ # Default: 60000 (1 minute)
50
+ RATE_LIMIT_WINDOW_MS=60000
51
+
52
+ # Maximum number of requests allowed within the rate limit window
53
+ RATE_LIMIT_MAX_REQUESTS=100
54
+
55
+ # -----------------------------------------------------------------------------
56
+ # Backup Configuration
57
+ # -----------------------------------------------------------------------------
58
+ # Enable automatic backup of server configurations
59
+ BACKUP_ENABLED=true
60
+
61
+ # Interval between automatic backups (in milliseconds)
62
+ # Default: 3600000 (1 hour)
63
+ BACKUP_INTERVAL_MS=3600000
64
+
65
+ # Maximum number of backup files to keep
66
+ # Older backups will be automatically deleted
67
+ BACKUP_MAX_COUNT=10
68
+
69
+ # -----------------------------------------------------------------------------
70
+ # Monitoring Configuration
71
+ # -----------------------------------------------------------------------------
72
+ # Enable automatic server health monitoring
73
+ MONITORING_ENABLED=true
74
+
75
+ # Interval between health checks (in milliseconds)
76
+ # Default: 300000 (5 minutes)
77
+ MONITORING_INTERVAL_MS=300000
78
+
79
+ # -----------------------------------------------------------------------------
80
+ # Logging Configuration (Optional)
81
+ # -----------------------------------------------------------------------------
82
+ # Log level: debug, info, warn, error
83
+ # LOG_LEVEL=info
84
+
85
+ # Log file name (stored in config directory)
86
+ # LOG_FILE=telegram-ssh-bot.log
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Configuration loader module with hot reload support
3
+ */
4
+ import "dotenv/config";
5
+ import { EventEmitter } from "events";
6
+ import type { AppConfig } from "../types/index.js";
7
+ /**
8
+ * Configuration reload event
9
+ */
10
+ export interface ConfigReloadEvent {
11
+ oldConfig: AppConfig;
12
+ newConfig: AppConfig;
13
+ changedKeys: string[];
14
+ timestamp: Date;
15
+ }
16
+ /**
17
+ * Configuration reloader for hot reload support
18
+ */
19
+ export declare class ConfigReloader extends EventEmitter {
20
+ private currentConfig;
21
+ private readonly envPath;
22
+ private watcher;
23
+ private reloadDebounce;
24
+ private reloadPromise;
25
+ constructor(initialConfig: AppConfig, envPath?: string);
26
+ /**
27
+ * Start watching for configuration changes
28
+ */
29
+ startWatching(): void;
30
+ /**
31
+ * Stop watching for configuration changes
32
+ */
33
+ stopWatching(): void;
34
+ /**
35
+ * Get current configuration
36
+ */
37
+ getConfig(): AppConfig;
38
+ /**
39
+ * Schedule a configuration reload (debounced)
40
+ */
41
+ private scheduleReload;
42
+ /**
43
+ * Reload configuration from environment
44
+ * Uses promise-based locking to prevent race conditions
45
+ */
46
+ reload(): Promise<boolean>;
47
+ /**
48
+ * Internal reload implementation
49
+ */
50
+ private doReload;
51
+ /**
52
+ * Load .env file manually
53
+ */
54
+ private loadEnvFile;
55
+ /**
56
+ * Find changed keys between two configurations
57
+ */
58
+ private findChangedKeys;
59
+ }
60
+ /**
61
+ * Load and validate configuration from environment
62
+ */
63
+ export declare function loadConfig(): Promise<AppConfig>;
64
+ /**
65
+ * Validate that owner IDs are configured
66
+ */
67
+ export declare function validateOwnerIds(config: AppConfig): void;
68
+ /**
69
+ * Check if the installation wizard should run
70
+ * Returns true if required environment variables are missing
71
+ */
72
+ export declare function shouldRunWizard(): boolean;
73
+ /**
74
+ * Get list of missing required configuration keys
75
+ */
76
+ export declare function getMissingConfigKeys(): string[];
77
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAItC,OAAO,KAAK,EACV,SAAS,EAIV,MAAM,mBAAmB,CAAC;AAI3B;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,SAAS,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,OAAO,CAAyC;IACxD,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,aAAa,CAAiC;gBAE1C,aAAa,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,MAAM;IAMtD;;OAEG;IACH,aAAa,IAAI,IAAI;IAqBrB;;OAEG;IACH,YAAY,IAAI,IAAI;IAWpB;;OAEG;IACH,SAAS,IAAI,SAAS;IAItB;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;;OAGG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAgBhC;;OAEG;YACW,QAAQ;IAqCtB;;OAEG;YACW,WAAW;IAqBzB;;OAEG;IACH,OAAO,CAAC,eAAe;CAiCxB;AAkFD;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,SAAS,CAAC,CAsFrD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAMxD;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAGzC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAE/C"}
@@ -0,0 +1,333 @@
1
+ /**
2
+ * Configuration loader module with hot reload support
3
+ */
4
+ import "dotenv/config";
5
+ import { EventEmitter } from "events";
6
+ import { existsSync, watch } from "fs";
7
+ import * as path from "path";
8
+ import { InvalidConfigError, MissingConfigError } from "../errors/index.js";
9
+ import { expandTilde } from "../utils/pathUtils.js";
10
+ import { configSchema } from "./schema.js";
11
+ /**
12
+ * Configuration reloader for hot reload support
13
+ */
14
+ export class ConfigReloader extends EventEmitter {
15
+ currentConfig;
16
+ envPath;
17
+ watcher = null;
18
+ reloadDebounce = null;
19
+ reloadPromise = null;
20
+ constructor(initialConfig, envPath) {
21
+ super();
22
+ this.currentConfig = initialConfig;
23
+ this.envPath = envPath ?? path.resolve(process.cwd(), ".env");
24
+ }
25
+ /**
26
+ * Start watching for configuration changes
27
+ */
28
+ startWatching() {
29
+ if (this.watcher) {
30
+ return;
31
+ }
32
+ // Check if .env file exists
33
+ if (!existsSync(this.envPath)) {
34
+ return;
35
+ }
36
+ this.watcher = watch(this.envPath, (eventType) => {
37
+ if (eventType === "change") {
38
+ this.scheduleReload();
39
+ }
40
+ });
41
+ this.watcher.on("error", (error) => {
42
+ this.emit("error", error);
43
+ });
44
+ }
45
+ /**
46
+ * Stop watching for configuration changes
47
+ */
48
+ stopWatching() {
49
+ if (this.watcher) {
50
+ this.watcher.close();
51
+ this.watcher = null;
52
+ }
53
+ if (this.reloadDebounce) {
54
+ clearTimeout(this.reloadDebounce);
55
+ this.reloadDebounce = null;
56
+ }
57
+ }
58
+ /**
59
+ * Get current configuration
60
+ */
61
+ getConfig() {
62
+ return this.currentConfig;
63
+ }
64
+ /**
65
+ * Schedule a configuration reload (debounced)
66
+ */
67
+ scheduleReload() {
68
+ if (this.reloadDebounce) {
69
+ clearTimeout(this.reloadDebounce);
70
+ }
71
+ this.reloadDebounce = setTimeout(() => {
72
+ this.reload();
73
+ }, 1000); // 1 second debounce
74
+ }
75
+ /**
76
+ * Reload configuration from environment
77
+ * Uses promise-based locking to prevent race conditions
78
+ */
79
+ async reload() {
80
+ // If already reloading, return the existing promise
81
+ if (this.reloadPromise) {
82
+ return this.reloadPromise;
83
+ }
84
+ // Create a new reload operation
85
+ this.reloadPromise = this.doReload();
86
+ try {
87
+ return await this.reloadPromise;
88
+ }
89
+ finally {
90
+ this.reloadPromise = null;
91
+ }
92
+ }
93
+ /**
94
+ * Internal reload implementation
95
+ */
96
+ async doReload() {
97
+ try {
98
+ // Re-read .env file
99
+ const result = await this.loadEnvFile();
100
+ if (!result.success) {
101
+ this.emit("error", new Error(`Failed to reload .env: ${result.error}`));
102
+ return false;
103
+ }
104
+ // Load new configuration
105
+ const newConfig = await loadConfig();
106
+ const changedKeys = this.findChangedKeys(this.currentConfig, newConfig);
107
+ if (changedKeys.length === 0) {
108
+ return false;
109
+ }
110
+ const event = {
111
+ oldConfig: this.currentConfig,
112
+ newConfig,
113
+ changedKeys,
114
+ timestamp: new Date(),
115
+ };
116
+ this.currentConfig = newConfig;
117
+ this.emit("reload", event);
118
+ return true;
119
+ }
120
+ catch (error) {
121
+ this.emit("error", error instanceof Error ? error : new Error(String(error)));
122
+ return false;
123
+ }
124
+ }
125
+ /**
126
+ * Load .env file manually
127
+ */
128
+ async loadEnvFile() {
129
+ try {
130
+ if (!existsSync(this.envPath)) {
131
+ return { success: false, error: ".env file not found" };
132
+ }
133
+ // Re-import dotenv to reload
134
+ // Note: This is a simplified approach; in production you might want
135
+ // to manually parse the .env file and update process.env
136
+ const { config } = await import("dotenv");
137
+ config({ path: this.envPath, override: true });
138
+ return { success: true };
139
+ }
140
+ catch (error) {
141
+ return {
142
+ success: false,
143
+ error: error instanceof Error ? error.message : String(error),
144
+ };
145
+ }
146
+ }
147
+ /**
148
+ * Find changed keys between two configurations
149
+ */
150
+ findChangedKeys(old, new_) {
151
+ const changed = [];
152
+ // Compare telegram config
153
+ if (old.telegram.token !== new_.telegram.token)
154
+ changed.push("telegram.token");
155
+ if (old.telegram.chatId !== new_.telegram.chatId)
156
+ changed.push("telegram.chatId");
157
+ if (JSON.stringify(old.telegram.ownerIds) !==
158
+ JSON.stringify(new_.telegram.ownerIds)) {
159
+ changed.push("telegram.ownerIds");
160
+ }
161
+ // Compare security config
162
+ if (old.security.encryptionKey !== new_.security.encryptionKey) {
163
+ changed.push("security.encryptionKey");
164
+ }
165
+ if (old.security.rateLimit.enabled !== new_.security.rateLimit.enabled) {
166
+ changed.push("security.rateLimit.enabled");
167
+ }
168
+ if (old.security.rateLimit.maxRequests !== new_.security.rateLimit.maxRequests) {
169
+ changed.push("security.rateLimit.maxRequests");
170
+ }
171
+ // Compare logging config
172
+ if (old.logging.level !== new_.logging.level)
173
+ changed.push("logging.level");
174
+ return changed;
175
+ }
176
+ }
177
+ /**
178
+ * Required environment variable keys
179
+ */
180
+ const REQUIRED_ENV_KEYS = [
181
+ "BOT_TOKEN",
182
+ "BOT_CHAT_ID",
183
+ "ENCRYPTION_KEY",
184
+ ];
185
+ /**
186
+ * Collect all missing required environment variables
187
+ */
188
+ function getMissingEnvKeys() {
189
+ const missing = [];
190
+ for (const key of REQUIRED_ENV_KEYS) {
191
+ if (!process.env[key]) {
192
+ missing.push(key);
193
+ }
194
+ }
195
+ return missing;
196
+ }
197
+ /**
198
+ * Get required environment variable or throw error
199
+ */
200
+ function getRequiredEnv(key) {
201
+ const value = process.env[key];
202
+ if (!value) {
203
+ throw new MissingConfigError(key);
204
+ }
205
+ return value;
206
+ }
207
+ /**
208
+ * Get optional environment variable with default
209
+ */
210
+ function getOptionalEnv(key, defaultValue) {
211
+ return process.env[key] ?? defaultValue;
212
+ }
213
+ /**
214
+ * Parse comma-separated string to array
215
+ */
216
+ function parseArray(value, defaultValue) {
217
+ if (!value) {
218
+ return defaultValue;
219
+ }
220
+ return value
221
+ .split(",")
222
+ .map((s) => s.trim())
223
+ .filter((s) => s.length > 0);
224
+ }
225
+ /**
226
+ * Parse boolean from string
227
+ */
228
+ function parseBoolean(value, defaultValue) {
229
+ if (value === undefined) {
230
+ return defaultValue;
231
+ }
232
+ return value.toLowerCase() === "true" || value === "1";
233
+ }
234
+ /**
235
+ * Parse number from string
236
+ */
237
+ function parseNumber(value, defaultValue) {
238
+ if (value === undefined) {
239
+ return defaultValue;
240
+ }
241
+ const parsed = parseInt(value, 10);
242
+ return isNaN(parsed) ? defaultValue : parsed;
243
+ }
244
+ /**
245
+ * Load and validate configuration from environment
246
+ */
247
+ export async function loadConfig() {
248
+ // Check for all missing required environment variables upfront
249
+ const missingKeys = getMissingEnvKeys();
250
+ if (missingKeys.length > 0) {
251
+ throw new InvalidConfigError(`Missing required environment variables: ${missingKeys.join(", ")}. ` +
252
+ `Please set these in your .env file or environment.`);
253
+ }
254
+ // Build config from environment variables
255
+ const envConfig = {
256
+ telegram: {
257
+ token: getRequiredEnv("BOT_TOKEN"),
258
+ chatId: getRequiredEnv("BOT_CHAT_ID"),
259
+ ownerIds: parseArray(process.env.BOT_OWNER_IDS, []),
260
+ polling: parseBoolean(process.env.BOT_POLLING, true),
261
+ },
262
+ security: {
263
+ encryptionKey: getRequiredEnv("ENCRYPTION_KEY"),
264
+ rateLimit: {
265
+ enabled: parseBoolean(process.env.RATE_LIMIT_ENABLED, true),
266
+ windowMs: parseNumber(process.env.RATE_LIMIT_WINDOW_MS, 60000),
267
+ maxRequests: parseNumber(process.env.RATE_LIMIT_MAX_REQUESTS, 30),
268
+ skipFailedRequests: parseBoolean(process.env.RATE_LIMIT_SKIP_FAILED, false),
269
+ },
270
+ allowedCommands: parseArray(process.env.ALLOWED_COMMANDS, []),
271
+ blockedCommands: parseArray(process.env.BLOCKED_COMMANDS, []),
272
+ },
273
+ ssh: {
274
+ defaultPrivateKeyPath: expandTilde(getOptionalEnv("SSH_DEFAULT_PRIVATE_KEY_PATH", "~/.ssh/id_rsa")),
275
+ defaultPort: parseNumber(process.env.SSH_DEFAULT_PORT, 22),
276
+ connectionTimeout: parseNumber(process.env.SSH_CONNECTION_TIMEOUT, 30000),
277
+ keepaliveInterval: parseNumber(process.env.SSH_KEEPALIVE_INTERVAL, 10000),
278
+ maxConnections: parseNumber(process.env.SSH_MAX_CONNECTIONS, 5),
279
+ commandTimeout: parseNumber(process.env.SSH_COMMAND_TIMEOUT, 60000),
280
+ },
281
+ logging: {
282
+ level: getOptionalEnv("LOG_LEVEL", "info"),
283
+ format: getOptionalEnv("LOG_FORMAT", "json"),
284
+ file: process.env.LOG_FILE,
285
+ },
286
+ storage: {
287
+ serversFile: getOptionalEnv("STORAGE_SERVERS_FILE", "./conf/servers.json"),
288
+ encryptionEnabled: parseBoolean(process.env.STORAGE_ENCRYPTION_ENABLED, true),
289
+ },
290
+ backup: {
291
+ enabled: parseBoolean(process.env.BACKUP_ENABLED, true),
292
+ intervalMs: parseNumber(process.env.BACKUP_INTERVAL_MS, 3600000),
293
+ maxCount: parseNumber(process.env.BACKUP_MAX_COUNT, 10),
294
+ },
295
+ monitoring: {
296
+ enabled: parseBoolean(process.env.MONITORING_ENABLED, true),
297
+ intervalMs: parseNumber(process.env.MONITORING_INTERVAL_MS, 300000),
298
+ },
299
+ };
300
+ // Validate configuration
301
+ const { error, value } = configSchema.validate(envConfig, {
302
+ abortEarly: false,
303
+ allowUnknown: false,
304
+ });
305
+ if (error) {
306
+ const messages = error.details.map((d) => d.message).join(", ");
307
+ throw new InvalidConfigError(`Configuration validation failed: ${messages}`);
308
+ }
309
+ return value;
310
+ }
311
+ /**
312
+ * Validate that owner IDs are configured
313
+ */
314
+ export function validateOwnerIds(config) {
315
+ if (config.telegram.ownerIds.length === 0) {
316
+ throw new InvalidConfigError("At least one owner ID must be configured via BOT_OWNER_IDS");
317
+ }
318
+ }
319
+ /**
320
+ * Check if the installation wizard should run
321
+ * Returns true if required environment variables are missing
322
+ */
323
+ export function shouldRunWizard() {
324
+ const missingKeys = getMissingEnvKeys();
325
+ return missingKeys.length > 0;
326
+ }
327
+ /**
328
+ * Get list of missing required configuration keys
329
+ */
330
+ export function getMissingConfigKeys() {
331
+ return getMissingEnvKeys();
332
+ }
333
+ //# sourceMappingURL=index.js.map