@muhammedaksam/easiarr 1.4.0 → 1.6.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 (109) hide show
  1. package/dist/index.js +29750 -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,374 +0,0 @@
1
- /**
2
- * Portainer API Client
3
- * Handles Portainer-specific API calls for initialization and management
4
- */
5
-
6
- import type { AutoSetupOptions, AutoSetupResult, IAutoSetupClient } from "./auto-setup-types"
7
- import { debugLog } from "~/utils/debug"
8
- import { ensureMinPasswordLength } from "~/utils/password"
9
- import { BaseApiClient } from "./base-api"
10
-
11
- // Portainer requires minimum 12 character password
12
- export const PORTAINER_MIN_PASSWORD_LENGTH = 12
13
-
14
- export interface PortainerUser {
15
- Id?: number
16
- Username: string
17
- Password?: string
18
- Role?: number
19
- }
20
-
21
- export interface PortainerInitResult {
22
- user: PortainerUser
23
- actualPassword: string
24
- passwordWasPadded: boolean
25
- }
26
-
27
- export interface PortainerStatus {
28
- Version: string
29
- InstanceID: string
30
- }
31
-
32
- export interface PortainerSettings {
33
- AuthenticationMethod: number
34
- LogoURL: string
35
- BlackListedLabels: string[]
36
- InternalAuthSettings: {
37
- RequiredPasswordLength: number
38
- }
39
- }
40
-
41
- export interface PortainerAuthResponse {
42
- jwt: string
43
- }
44
-
45
- export interface PortainerApiKeyResponse {
46
- rawAPIKey: string
47
- apiKey: {
48
- id: number
49
- userId: number
50
- description: string
51
- prefix: string
52
- dateCreated: number
53
- lastUsed: number
54
- digest: string
55
- }
56
- }
57
-
58
- export class PortainerApiClient extends BaseApiClient implements IAutoSetupClient {
59
- protected readonly logPrefix = "PortainerAPI"
60
- private jwtToken: string | null = null
61
- private apiKey: string | null = null
62
-
63
- constructor(host: string, port: number) {
64
- super(host, port)
65
- }
66
-
67
- /**
68
- * Set API key for authentication
69
- */
70
- setApiKey(key: string): void {
71
- if (key) {
72
- this.apiKey = key
73
- }
74
- }
75
-
76
- private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
77
- const url = `${this.baseUrl}/api${endpoint}`
78
- const headers: Record<string, string> = {
79
- "Content-Type": "application/json",
80
- ...(options.headers as Record<string, string>),
81
- }
82
-
83
- if (this.apiKey) {
84
- headers["X-API-Key"] = this.apiKey
85
- } else if (this.jwtToken) {
86
- headers["Authorization"] = `Bearer ${this.jwtToken}`
87
- }
88
-
89
- debugLog("PortainerAPI", `${options.method || "GET"} ${url}`)
90
-
91
- const response = await fetch(url, { ...options, headers })
92
- const text = await response.text()
93
-
94
- if (!response.ok) {
95
- throw new Error(`Portainer API request failed: ${response.status} ${response.statusText}`)
96
- }
97
-
98
- if (!text) return {} as T
99
- return JSON.parse(text) as T
100
- }
101
-
102
- /**
103
- * Check if Portainer needs initialization
104
- */
105
- async needsInitialization(): Promise<boolean> {
106
- try {
107
- const response = await fetch(`${this.baseUrl}/api/users/admin/check`)
108
- return response.status === 404
109
- } catch {
110
- return false
111
- }
112
- }
113
-
114
- /**
115
- * Login to Portainer
116
- */
117
- async login(username: string, password: string): Promise<string> {
118
- const safePassword = ensureMinPasswordLength(password, PORTAINER_MIN_PASSWORD_LENGTH)
119
- const response = await this.request<PortainerAuthResponse>("/auth", {
120
- method: "POST",
121
- body: JSON.stringify({ username, password: safePassword }),
122
- })
123
- this.jwtToken = response.jwt
124
- return response.jwt
125
- }
126
-
127
- /**
128
- * Initialize the admin user
129
- */
130
- async initializeAdmin(username: string, password: string): Promise<PortainerInitResult | null> {
131
- const needsInit = await this.needsInitialization()
132
- if (!needsInit) {
133
- return null
134
- }
135
-
136
- const safePassword = ensureMinPasswordLength(password, PORTAINER_MIN_PASSWORD_LENGTH)
137
- const wasPadded = safePassword !== password
138
-
139
- const user = await this.request<PortainerUser>("/users/admin/init", {
140
- method: "POST",
141
- body: JSON.stringify({ Username: username, Password: safePassword }),
142
- })
143
-
144
- await this.login(username, safePassword)
145
-
146
- return { user, actualPassword: safePassword, passwordWasPadded: wasPadded }
147
- }
148
-
149
- /**
150
- * Generate an API key
151
- */
152
- async generateApiKey(
153
- password: string,
154
- description: string = "easiarr-api-key",
155
- userId: number = 1
156
- ): Promise<string> {
157
- if (!this.jwtToken) {
158
- throw new Error("Must be logged in to generate API key")
159
- }
160
-
161
- const safePassword = ensureMinPasswordLength(password, PORTAINER_MIN_PASSWORD_LENGTH)
162
- const response = await this.request<PortainerApiKeyResponse>(`/users/${userId}/tokens`, {
163
- method: "POST",
164
- body: JSON.stringify({ password: safePassword, description }),
165
- })
166
-
167
- return response.rawAPIKey
168
- }
169
-
170
- /**
171
- * Get Portainer status
172
- */
173
- async getStatus(): Promise<PortainerStatus> {
174
- return this.request<PortainerStatus>("/status")
175
- }
176
-
177
- /**
178
- * Check if Portainer is reachable
179
- */
180
- async isHealthy(): Promise<boolean> {
181
- try {
182
- await this.getStatus()
183
- return true
184
- } catch {
185
- return false
186
- }
187
- }
188
-
189
- // ==========================================
190
- // Container Management Methods
191
- // ==========================================
192
-
193
- async getEndpoints(): Promise<PortainerEndpoint[]> {
194
- return this.request<PortainerEndpoint[]>("/endpoints")
195
- }
196
-
197
- async getLocalEnvironmentId(): Promise<number | null> {
198
- try {
199
- const endpoints = await this.getEndpoints()
200
- const socketEndpoint = endpoints.find(
201
- (e) => e.URL === "unix:///var/run/docker.sock" || e.URL.includes("docker.sock")
202
- )
203
- if (socketEndpoint) return socketEndpoint.Id
204
-
205
- const localEndpoint = endpoints.find((e) => e.Name.toLowerCase() === "local")
206
- if (localEndpoint) return localEndpoint.Id
207
-
208
- if (endpoints.length > 0) return endpoints[0].Id
209
- return null
210
- } catch {
211
- return null
212
- }
213
- }
214
-
215
- async getContainers(endpointId: number = 1): Promise<PortainerContainer[]> {
216
- return this.request<PortainerContainer[]>(
217
- `/endpoints/${endpointId}/docker/containers/json?all=true`
218
- )
219
- }
220
-
221
- async startContainer(containerId: string, endpointId: number = 1): Promise<void> {
222
- await this.request(`/endpoints/${endpointId}/docker/containers/${containerId}/start`, {
223
- method: "POST",
224
- })
225
- }
226
-
227
- async stopContainer(containerId: string, endpointId: number = 1): Promise<void> {
228
- await this.request(`/endpoints/${endpointId}/docker/containers/${containerId}/stop`, {
229
- method: "POST",
230
- })
231
- }
232
-
233
- async restartContainer(containerId: string, endpointId: number = 1): Promise<void> {
234
- await this.request(`/endpoints/${endpointId}/docker/containers/${containerId}/restart`, {
235
- method: "POST",
236
- })
237
- }
238
-
239
- async getContainerLogs(
240
- containerId: string,
241
- endpointId: number = 1,
242
- options: { stdout?: boolean; stderr?: boolean; tail?: number } = {}
243
- ): Promise<string> {
244
- const { stdout = true, stderr = true, tail = 100 } = options
245
- const params = new URLSearchParams({
246
- stdout: String(stdout),
247
- stderr: String(stderr),
248
- tail: String(tail),
249
- })
250
- return this.request<string>(
251
- `/endpoints/${endpointId}/docker/containers/${containerId}/logs?${params}`
252
- )
253
- }
254
-
255
- async getContainerStats(
256
- containerId: string,
257
- endpointId: number = 1
258
- ): Promise<PortainerContainerStats> {
259
- return this.request<PortainerContainerStats>(
260
- `/endpoints/${endpointId}/docker/containers/${containerId}/stats?stream=false`
261
- )
262
- }
263
-
264
- /**
265
- * Check if already configured
266
- */
267
- async isInitialized(): Promise<boolean> {
268
- return !(await this.needsInitialization())
269
- }
270
-
271
- /**
272
- * Run the auto-setup process
273
- */
274
- async setup(options: AutoSetupOptions): Promise<AutoSetupResult> {
275
- const { username, password } = options
276
-
277
- try {
278
- const healthy = await this.isHealthy()
279
- if (!healthy) {
280
- return { success: false, message: "Portainer not reachable" }
281
- }
282
-
283
- const needsInit = await this.needsInitialization()
284
-
285
- let actualPassword = ensureMinPasswordLength(password, PORTAINER_MIN_PASSWORD_LENGTH)
286
- let passwordPadded = actualPassword !== password
287
- let apiKey: string | undefined
288
-
289
- if (needsInit) {
290
- const result = await this.initializeAdmin(username, password)
291
- if (result) {
292
- actualPassword = result.actualPassword
293
- passwordPadded = result.passwordWasPadded
294
- }
295
-
296
- try {
297
- apiKey = await this.generateApiKey(actualPassword)
298
- } catch {
299
- // API key generation may fail
300
- }
301
- } else {
302
- try {
303
- await this.login(username, actualPassword)
304
- } catch {
305
- return { success: false, message: "Login failed - check credentials" }
306
- }
307
- }
308
-
309
- const envId = await this.getLocalEnvironmentId()
310
-
311
- return {
312
- success: true,
313
- message: needsInit
314
- ? passwordPadded
315
- ? `Admin created (password padded to ${PORTAINER_MIN_PASSWORD_LENGTH} chars)`
316
- : "Admin created"
317
- : "Logged in",
318
- data: {
319
- adminCreated: needsInit,
320
- passwordPadded,
321
- apiKey,
322
- environmentId: envId,
323
- },
324
- envUpdates: {
325
- ...(apiKey ? { API_KEY_PORTAINER: apiKey } : {}),
326
- ...(envId ? { PORTAINER_ENV: String(envId) } : {}),
327
- ...(passwordPadded ? { PASSWORD_PORTAINER: actualPassword } : {}),
328
- },
329
- }
330
- } catch (error) {
331
- return { success: false, message: `${error}` }
332
- }
333
- }
334
- }
335
-
336
- // ==========================================
337
- // Additional Type Definitions
338
- // ==========================================
339
-
340
- export interface PortainerEndpoint {
341
- Id: number
342
- Name: string
343
- Type: number
344
- Status: number
345
- URL: string
346
- }
347
-
348
- export interface PortainerContainer {
349
- Id: string
350
- Names: string[]
351
- Image: string
352
- State: string
353
- Status: string
354
- Ports: Array<{
355
- IP?: string
356
- PrivatePort: number
357
- PublicPort?: number
358
- Type: string
359
- }>
360
- Labels: Record<string, string>
361
- Created: number
362
- }
363
-
364
- export interface PortainerContainerStats {
365
- cpu_stats: {
366
- cpu_usage: { total_usage: number }
367
- system_cpu_usage: number
368
- online_cpus: number
369
- }
370
- memory_stats: {
371
- usage: number
372
- limit: number
373
- }
374
- }