@muhammedaksam/easiarr 1.4.0 → 1.4.1

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 (109) hide show
  1. package/dist/index.js +29837 -0
  2. package/package.json +15 -15
  3. package/src/VersionInfo.ts +0 -12
  4. package/src/api/arr-api.ts +0 -521
  5. package/src/api/auto-setup-types.ts +0 -64
  6. package/src/api/base-api.ts +0 -216
  7. package/src/api/bazarr-api.ts +0 -297
  8. package/src/api/cloudflare-api.ts +0 -493
  9. package/src/api/custom-format-api.ts +0 -224
  10. package/src/api/grafana-api.ts +0 -230
  11. package/src/api/heimdall-api.ts +0 -141
  12. package/src/api/homarr-api.ts +0 -209
  13. package/src/api/huntarr-api.ts +0 -457
  14. package/src/api/index.ts +0 -9
  15. package/src/api/jellyfin-api.ts +0 -389
  16. package/src/api/jellyseerr-api.ts +0 -559
  17. package/src/api/maintainerr-api.ts +0 -461
  18. package/src/api/naming-config.ts +0 -101
  19. package/src/api/overseerr-api.ts +0 -392
  20. package/src/api/plex-api.ts +0 -308
  21. package/src/api/portainer-api.ts +0 -374
  22. package/src/api/profilarr-api.ts +0 -391
  23. package/src/api/prowlarr-api.ts +0 -488
  24. package/src/api/qbittorrent-api.ts +0 -300
  25. package/src/api/quality-profile-api.ts +0 -272
  26. package/src/api/tautulli-api.ts +0 -244
  27. package/src/api/uptime-kuma-api.ts +0 -343
  28. package/src/apps/categories.ts +0 -14
  29. package/src/apps/index.ts +0 -2
  30. package/src/apps/registry.ts +0 -1275
  31. package/src/compose/caddy-config.ts +0 -130
  32. package/src/compose/generator.ts +0 -287
  33. package/src/compose/index.ts +0 -7
  34. package/src/compose/templates.ts +0 -78
  35. package/src/compose/traefik-config.ts +0 -187
  36. package/src/config/bookmarks-generator.ts +0 -160
  37. package/src/config/defaults.ts +0 -37
  38. package/src/config/homepage-config.ts +0 -381
  39. package/src/config/index.ts +0 -6
  40. package/src/config/manager.ts +0 -150
  41. package/src/config/recyclarr-config.ts +0 -181
  42. package/src/config/schema.ts +0 -310
  43. package/src/config/slskd-config.ts +0 -86
  44. package/src/config/soularr-config.ts +0 -105
  45. package/src/config/trash-quality-definitions.ts +0 -213
  46. package/src/data/lidarr-custom-formats.ts +0 -127
  47. package/src/data/trash-profiles.ts +0 -333
  48. package/src/docker/client.ts +0 -312
  49. package/src/docker/index.ts +0 -18
  50. package/src/index.ts +0 -77
  51. package/src/setup/actions/arr-common.ts +0 -307
  52. package/src/setup/actions/dashboards.ts +0 -79
  53. package/src/setup/actions/download-clients.ts +0 -54
  54. package/src/setup/actions/jellyseerr.ts +0 -331
  55. package/src/setup/actions/media-servers.ts +0 -68
  56. package/src/setup/actions/monitoring.ts +0 -120
  57. package/src/setup/actions/prowlarr.ts +0 -137
  58. package/src/setup/actions/utilities.ts +0 -490
  59. package/src/setup/index.ts +0 -78
  60. package/src/setup/types.ts +0 -144
  61. package/src/structure/manager.ts +0 -129
  62. package/src/ui/App.ts +0 -111
  63. package/src/ui/components/ApplicationSelector.ts +0 -275
  64. package/src/ui/components/CredentialsForm.ts +0 -180
  65. package/src/ui/components/DownloadClientForm.ts +0 -114
  66. package/src/ui/components/FileEditor.ts +0 -91
  67. package/src/ui/components/FooterHint.ts +0 -119
  68. package/src/ui/components/PageLayout.ts +0 -160
  69. package/src/ui/components/UpdateNotification.ts +0 -102
  70. package/src/ui/index.ts +0 -7
  71. package/src/ui/screens/AdvancedSettings.ts +0 -188
  72. package/src/ui/screens/ApiKeyViewer.ts +0 -511
  73. package/src/ui/screens/AppConfigurator.ts +0 -543
  74. package/src/ui/screens/AppManager.ts +0 -284
  75. package/src/ui/screens/ContainerControl.ts +0 -581
  76. package/src/ui/screens/LogsViewer.ts +0 -478
  77. package/src/ui/screens/MainMenu.ts +0 -299
  78. package/src/ui/screens/MonitorDashboard.ts +0 -881
  79. package/src/ui/screens/SecretsEditor.ts +0 -216
  80. package/src/ui/screens/SettingsScreen.ts +0 -633
  81. package/src/ui/screens/index.ts +0 -22
  82. package/src/ui/screens/setup/BaseAppSetupScreen.ts +0 -375
  83. package/src/ui/screens/setup/CloudflaredSetup.ts +0 -460
  84. package/src/ui/screens/setup/FullAutoSetup.ts +0 -766
  85. package/src/ui/screens/setup/HomepageSetup.ts +0 -165
  86. package/src/ui/screens/setup/JellyfinSetup.ts +0 -328
  87. package/src/ui/screens/setup/JellyseerrSetup.ts +0 -242
  88. package/src/ui/screens/setup/ProwlarrSetup.ts +0 -500
  89. package/src/ui/screens/setup/QBittorrentSetup.ts +0 -221
  90. package/src/ui/screens/setup/QuickSetup.ts +0 -1169
  91. package/src/ui/screens/setup/RecyclarrSetup.ts +0 -270
  92. package/src/ui/screens/setup/TRaSHProfileSetup.ts +0 -381
  93. package/src/utils/arch.ts +0 -78
  94. package/src/utils/browser.ts +0 -27
  95. package/src/utils/categories.ts +0 -61
  96. package/src/utils/debug.ts +0 -84
  97. package/src/utils/env.ts +0 -138
  98. package/src/utils/index.ts +0 -8
  99. package/src/utils/logs.ts +0 -119
  100. package/src/utils/migrations/1765626338_rename_env_variables.ts +0 -102
  101. package/src/utils/migrations/1765707135_rename_easiarr_status.ts +0 -91
  102. package/src/utils/migrations/1765732722_remove_cloudflare_dns_api_token.ts +0 -45
  103. package/src/utils/migrations/1769515650_migrate_to_xdg.ts +0 -114
  104. package/src/utils/migrations.ts +0 -165
  105. package/src/utils/password.ts +0 -42
  106. package/src/utils/paths.ts +0 -33
  107. package/src/utils/unraid.ts +0 -103
  108. package/src/utils/update-checker.ts +0 -210
  109. package/src/utils/url-utils.ts +0 -38
@@ -1,457 +0,0 @@
1
- /**
2
- * Huntarr API Client
3
- * Provides health check, version info, and auto-setup for Huntarr
4
- * Configures Sonarr, Radarr, Lidarr, Readarr, Whisparr instances automatically
5
- */
6
-
7
- import type { AutoSetupOptions, AutoSetupResult, IAutoSetupClient } from "./auto-setup-types"
8
- import type { AppConfig } from "~/config/schema"
9
- import { BaseApiClient } from "./base-api"
10
-
11
- interface HuntarrVersion {
12
- version: string
13
- }
14
-
15
- interface TestConnectionResult {
16
- success: boolean
17
- message?: string
18
- version?: string
19
- }
20
-
21
- interface HuntarrInstance {
22
- name: string
23
- api_url: string
24
- api_key: string
25
- enabled?: boolean
26
- [key: string]: unknown
27
- }
28
-
29
- interface HuntarrAppSettings {
30
- instances?: HuntarrInstance[]
31
- [key: string]: unknown
32
- }
33
-
34
- const HUNTARR_APP_TYPES = ["sonarr", "radarr", "lidarr", "readarr", "whisparr"] as const
35
- type HuntarrAppType = (typeof HUNTARR_APP_TYPES)[number]
36
-
37
- export class HuntarrClient extends BaseApiClient implements IAutoSetupClient {
38
- protected readonly logPrefix = "HuntarrApi"
39
- private sessionCookie: string | null = null
40
-
41
- constructor(host: string, port: number = 9705) {
42
- super(host, port)
43
- }
44
-
45
- private getHeaders(): Record<string, string> {
46
- const headers: Record<string, string> = {
47
- "Content-Type": "application/json",
48
- Accept: "application/json",
49
- }
50
- if (this.sessionCookie) {
51
- headers["Cookie"] = `huntarr_session=${this.sessionCookie}`
52
- }
53
- return headers
54
- }
55
-
56
- async userExists(): Promise<boolean> {
57
- try {
58
- const response = await fetch(`${this.baseUrl}/api/setup/status`, {
59
- method: "GET",
60
- signal: AbortSignal.timeout(5000),
61
- })
62
- const data = await response.json()
63
- return data.user_exists === true
64
- } catch {
65
- return false
66
- }
67
- }
68
-
69
- async createUser(username: string, password: string): Promise<boolean> {
70
- try {
71
- const response = await fetch(`${this.baseUrl}/setup`, {
72
- method: "POST",
73
- headers: { "Content-Type": "application/json" },
74
- body: JSON.stringify({
75
- username,
76
- password,
77
- confirm_password: password,
78
- }),
79
- signal: AbortSignal.timeout(10000),
80
- })
81
-
82
- if (response.ok) {
83
- const setCookie = response.headers.get("set-cookie")
84
- if (setCookie) {
85
- const match = setCookie.match(/huntarr_session=([^;]+)/)
86
- if (match) {
87
- this.sessionCookie = match[1]
88
- }
89
- }
90
- await this.completeSetup(username)
91
- await this.enableLocalAccessBypass()
92
- return true
93
- }
94
- } catch {
95
- // Ignore
96
- }
97
- return false
98
- }
99
-
100
- async enableLocalAccessBypass(): Promise<boolean> {
101
- try {
102
- const response = await fetch(`${this.baseUrl}/api/settings/general`, {
103
- method: "POST",
104
- headers: this.getHeaders(),
105
- body: JSON.stringify({ local_access_bypass: true }),
106
- signal: AbortSignal.timeout(10000),
107
- })
108
- return response.ok
109
- } catch {
110
- return false
111
- }
112
- }
113
-
114
- async completeSetup(username: string): Promise<boolean> {
115
- try {
116
- const progress = {
117
- current_step: 6,
118
- completed_steps: [1, 2, 3, 4, 5],
119
- account_created: true,
120
- two_factor_enabled: false,
121
- plex_setup_done: false,
122
- auth_mode_selected: false,
123
- recovery_key_generated: true,
124
- username,
125
- timestamp: new Date().toISOString(),
126
- }
127
-
128
- const saveResponse = await fetch(`${this.baseUrl}/api/setup/progress`, {
129
- method: "POST",
130
- headers: this.getHeaders(),
131
- body: JSON.stringify({ progress }),
132
- signal: AbortSignal.timeout(5000),
133
- })
134
-
135
- if (!saveResponse.ok) return false
136
-
137
- const clearResponse = await fetch(`${this.baseUrl}/api/setup/clear`, {
138
- method: "POST",
139
- headers: this.getHeaders(),
140
- signal: AbortSignal.timeout(5000),
141
- })
142
-
143
- return clearResponse.ok
144
- } catch {
145
- return false
146
- }
147
- }
148
-
149
- async login(username: string, password: string): Promise<boolean> {
150
- try {
151
- const response = await fetch(`${this.baseUrl}/login`, {
152
- method: "POST",
153
- headers: { "Content-Type": "application/json" },
154
- body: JSON.stringify({ username, password }),
155
- signal: AbortSignal.timeout(10000),
156
- })
157
-
158
- if (response.ok) {
159
- const setCookie = response.headers.get("set-cookie")
160
- if (setCookie) {
161
- const match = setCookie.match(/huntarr_session=([^;]+)/)
162
- if (match) {
163
- this.sessionCookie = match[1]
164
- return true
165
- }
166
- }
167
- const data = await response.json()
168
- return data.success === true
169
- }
170
- } catch {
171
- // Ignore
172
- }
173
- return false
174
- }
175
-
176
- async authenticate(username: string, password: string): Promise<boolean> {
177
- const exists = await this.userExists()
178
-
179
- if (!exists) {
180
- return await this.createUser(username, password)
181
- }
182
-
183
- const loggedIn = await this.login(username, password)
184
- if (loggedIn) {
185
- await this.enableLocalAccessBypass()
186
- }
187
- return loggedIn
188
- }
189
-
190
- async isHealthy(): Promise<boolean> {
191
- try {
192
- const response = await fetch(`${this.baseUrl}/health`, {
193
- method: "GET",
194
- signal: AbortSignal.timeout(5000),
195
- })
196
- return response.ok
197
- } catch {
198
- return false
199
- }
200
- }
201
-
202
- async isInitialized(): Promise<boolean> {
203
- return this.isHealthy()
204
- }
205
-
206
- async getVersion(): Promise<string | null> {
207
- try {
208
- const response = await fetch(`${this.baseUrl}/api/version`, {
209
- method: "GET",
210
- signal: AbortSignal.timeout(5000),
211
- })
212
-
213
- if (response.ok) {
214
- const data = (await response.json()) as HuntarrVersion
215
- return data.version
216
- }
217
- } catch {
218
- // Ignore
219
- }
220
- return null
221
- }
222
-
223
- async testConnection(
224
- appType: HuntarrAppType,
225
- apiUrl: string,
226
- apiKey: string
227
- ): Promise<TestConnectionResult> {
228
- try {
229
- const response = await fetch(`${this.baseUrl}/api/${appType}/test-connection`, {
230
- method: "POST",
231
- headers: { "Content-Type": "application/json" },
232
- body: JSON.stringify({
233
- api_url: apiUrl,
234
- api_key: apiKey,
235
- api_timeout: 30,
236
- }),
237
- signal: AbortSignal.timeout(35000),
238
- })
239
-
240
- return (await response.json()) as TestConnectionResult
241
- } catch (error) {
242
- return { success: false, message: `${error}` }
243
- }
244
- }
245
-
246
- async getSettings(appType: HuntarrAppType): Promise<HuntarrAppSettings | null> {
247
- try {
248
- const response = await fetch(`${this.baseUrl}/api/settings/${appType}`, {
249
- method: "GET",
250
- headers: this.getHeaders(),
251
- signal: AbortSignal.timeout(10000),
252
- })
253
-
254
- if (response.ok) {
255
- return (await response.json()) as HuntarrAppSettings
256
- }
257
- } catch {
258
- // Ignore
259
- }
260
- return null
261
- }
262
-
263
- async saveSettings(appType: HuntarrAppType, settings: HuntarrAppSettings): Promise<boolean> {
264
- try {
265
- const response = await fetch(`${this.baseUrl}/api/settings/${appType}`, {
266
- method: "POST",
267
- headers: this.getHeaders(),
268
- body: JSON.stringify(settings),
269
- signal: AbortSignal.timeout(10000),
270
- })
271
- return response.ok
272
- } catch {
273
- return false
274
- }
275
- }
276
-
277
- async addArrInstance(
278
- appType: HuntarrAppType,
279
- name: string,
280
- apiUrl: string,
281
- apiKey: string
282
- ): Promise<boolean> {
283
- try {
284
- const settings = await this.getSettings(appType)
285
-
286
- if (!settings) {
287
- return await this.saveSettings(appType, {
288
- instances: [{ name, api_url: apiUrl, api_key: apiKey, enabled: true }],
289
- })
290
- }
291
-
292
- if (!settings.instances) settings.instances = []
293
-
294
- const existingByUrl = settings.instances.find((i) => i.api_url === apiUrl && i.api_key)
295
- if (existingByUrl) return true
296
-
297
- const emptyInstance = settings.instances.find((i) => !i.api_url || !i.api_key)
298
- if (emptyInstance) {
299
- emptyInstance.name = name
300
- emptyInstance.api_url = apiUrl
301
- emptyInstance.api_key = apiKey
302
- emptyInstance.enabled = true
303
- } else {
304
- settings.instances.push({
305
- name,
306
- api_url: apiUrl,
307
- api_key: apiKey,
308
- enabled: true,
309
- })
310
- }
311
-
312
- return await this.saveSettings(appType, settings)
313
- } catch {
314
- return false
315
- }
316
- }
317
-
318
- async setup(options: AutoSetupOptions): Promise<AutoSetupResult> {
319
- const { env } = options
320
- const results: Array<{ app: string; success: boolean; message?: string }> = []
321
-
322
- try {
323
- const healthy = await this.isHealthy()
324
- if (!healthy) {
325
- return { success: false, message: "Huntarr not reachable" }
326
- }
327
-
328
- const version = await this.getVersion()
329
-
330
- const { getApp } = await import("../apps/registry")
331
-
332
- for (const appType of HUNTARR_APP_TYPES) {
333
- const apiKeyEnvName = `API_KEY_${appType.toUpperCase()}`
334
- const apiKey = env[apiKeyEnvName]
335
-
336
- if (!apiKey) continue
337
-
338
- const appDef = getApp(appType)
339
- const port = appDef?.defaultPort ?? 8989
340
- const apiUrl = `http://${appType}:${port}`
341
-
342
- const result = await this.testConnection(appType, apiUrl, apiKey)
343
- results.push({
344
- app: appType,
345
- success: result.success,
346
- message: result.message,
347
- })
348
- }
349
-
350
- const successCount = results.filter((r) => r.success).length
351
- const failCount = results.filter((r) => !r.success).length
352
-
353
- if (results.length === 0) {
354
- return {
355
- success: true,
356
- message: "No *arr apps configured with API keys",
357
- data: { version },
358
- }
359
- }
360
-
361
- if (failCount === 0) {
362
- return {
363
- success: true,
364
- message: `All ${successCount} *arr connections verified`,
365
- data: { version, results },
366
- }
367
- }
368
-
369
- return {
370
- success: successCount > 0,
371
- message: `${successCount} succeeded, ${failCount} failed.`,
372
- data: { version, results },
373
- }
374
- } catch (error) {
375
- return { success: false, message: `${error}` }
376
- }
377
- }
378
-
379
- async setupEasiarrApps(
380
- apps: AppConfig[],
381
- env: Record<string, string>
382
- ): Promise<{
383
- added: number
384
- skipped: number
385
- results: Array<{ app: string; success: boolean; message?: string }>
386
- }> {
387
- const results: Array<{ app: string; success: boolean; message?: string }> = []
388
- const { getApp } = await import("../apps/registry")
389
-
390
- for (const appConfig of apps) {
391
- if (!appConfig.enabled) continue
392
- if (!HUNTARR_APP_TYPES.includes(appConfig.id as HuntarrAppType)) continue
393
-
394
- const appType = appConfig.id as HuntarrAppType
395
- const apiKey = env[`API_KEY_${appType.toUpperCase()}`]
396
-
397
- if (!apiKey) continue
398
-
399
- const appDef = getApp(appType)
400
- const port = appDef?.defaultPort ?? 8989
401
- const apiUrl = `http://${appType}:${port}`
402
- const instanceName = appDef?.name ?? appType
403
-
404
- const added = await this.addArrInstance(appType, instanceName, apiUrl, apiKey)
405
- results.push({
406
- app: appType,
407
- success: added,
408
- message: added ? "Added to Huntarr" : "Failed to add",
409
- })
410
- }
411
-
412
- const prowlarrConfig = apps.find((a) => a.id === "prowlarr" && a.enabled)
413
- if (prowlarrConfig) {
414
- const prowlarrApiKey = env["API_KEY_PROWLARR"]
415
- if (prowlarrApiKey) {
416
- const prowlarrDef = getApp("prowlarr")
417
- const prowlarrPort = prowlarrConfig.port || prowlarrDef?.defaultPort || 9696
418
- const prowlarrUrl = `http://prowlarr:${prowlarrPort}`
419
-
420
- const configured = await this.configureProwlarr(prowlarrUrl, prowlarrApiKey)
421
- results.push({
422
- app: "prowlarr",
423
- success: configured,
424
- message: configured ? "Configured in Huntarr" : "Failed to configure",
425
- })
426
- }
427
- }
428
-
429
- return {
430
- added: results.filter((r) => r.success).length,
431
- skipped: results.filter((r) => !r.success).length,
432
- results,
433
- }
434
- }
435
-
436
- async configureProwlarr(apiUrl: string, apiKey: string): Promise<boolean> {
437
- try {
438
- const settings = {
439
- api_url: apiUrl,
440
- api_key: apiKey,
441
- name: "Prowlarr",
442
- enabled: true,
443
- }
444
-
445
- const response = await fetch(`${this.baseUrl}/api/settings/prowlarr`, {
446
- method: "POST",
447
- headers: this.getHeaders(),
448
- body: JSON.stringify(settings),
449
- signal: AbortSignal.timeout(10000),
450
- })
451
-
452
- return response.ok
453
- } catch {
454
- return false
455
- }
456
- }
457
- }
package/src/api/index.ts DELETED
@@ -1,9 +0,0 @@
1
- export * from "./arr-api"
2
- export * from "./bazarr-api"
3
- export * from "./cloudflare-api"
4
- export * from "./custom-format-api"
5
- export * from "./jellyfin-api"
6
- export * from "./jellyseerr-api"
7
- export * from "./prowlarr-api"
8
- export * from "./qbittorrent-api"
9
- export * from "./quality-profile-api"