@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.
- package/dist/index.js +29750 -0
- package/package.json +15 -15
- package/src/VersionInfo.ts +0 -12
- package/src/api/arr-api.ts +0 -521
- package/src/api/auto-setup-types.ts +0 -64
- package/src/api/base-api.ts +0 -216
- package/src/api/bazarr-api.ts +0 -297
- package/src/api/cloudflare-api.ts +0 -493
- package/src/api/custom-format-api.ts +0 -224
- package/src/api/grafana-api.ts +0 -230
- package/src/api/heimdall-api.ts +0 -141
- package/src/api/homarr-api.ts +0 -209
- package/src/api/huntarr-api.ts +0 -457
- package/src/api/index.ts +0 -9
- package/src/api/jellyfin-api.ts +0 -389
- package/src/api/jellyseerr-api.ts +0 -559
- package/src/api/maintainerr-api.ts +0 -461
- package/src/api/naming-config.ts +0 -101
- package/src/api/overseerr-api.ts +0 -392
- package/src/api/plex-api.ts +0 -308
- package/src/api/portainer-api.ts +0 -374
- package/src/api/profilarr-api.ts +0 -391
- package/src/api/prowlarr-api.ts +0 -488
- package/src/api/qbittorrent-api.ts +0 -300
- package/src/api/quality-profile-api.ts +0 -272
- package/src/api/tautulli-api.ts +0 -244
- package/src/api/uptime-kuma-api.ts +0 -343
- package/src/apps/categories.ts +0 -14
- package/src/apps/index.ts +0 -2
- package/src/apps/registry.ts +0 -1275
- package/src/compose/caddy-config.ts +0 -130
- package/src/compose/generator.ts +0 -287
- package/src/compose/index.ts +0 -7
- package/src/compose/templates.ts +0 -78
- package/src/compose/traefik-config.ts +0 -187
- package/src/config/bookmarks-generator.ts +0 -160
- package/src/config/defaults.ts +0 -37
- package/src/config/homepage-config.ts +0 -381
- package/src/config/index.ts +0 -6
- package/src/config/manager.ts +0 -150
- package/src/config/recyclarr-config.ts +0 -181
- package/src/config/schema.ts +0 -310
- package/src/config/slskd-config.ts +0 -86
- package/src/config/soularr-config.ts +0 -105
- package/src/config/trash-quality-definitions.ts +0 -213
- package/src/data/lidarr-custom-formats.ts +0 -127
- package/src/data/trash-profiles.ts +0 -333
- package/src/docker/client.ts +0 -312
- package/src/docker/index.ts +0 -18
- package/src/index.ts +0 -77
- package/src/setup/actions/arr-common.ts +0 -307
- package/src/setup/actions/dashboards.ts +0 -79
- package/src/setup/actions/download-clients.ts +0 -54
- package/src/setup/actions/jellyseerr.ts +0 -331
- package/src/setup/actions/media-servers.ts +0 -68
- package/src/setup/actions/monitoring.ts +0 -120
- package/src/setup/actions/prowlarr.ts +0 -137
- package/src/setup/actions/utilities.ts +0 -490
- package/src/setup/index.ts +0 -78
- package/src/setup/types.ts +0 -144
- package/src/structure/manager.ts +0 -129
- package/src/ui/App.ts +0 -111
- package/src/ui/components/ApplicationSelector.ts +0 -275
- package/src/ui/components/CredentialsForm.ts +0 -180
- package/src/ui/components/DownloadClientForm.ts +0 -114
- package/src/ui/components/FileEditor.ts +0 -91
- package/src/ui/components/FooterHint.ts +0 -119
- package/src/ui/components/PageLayout.ts +0 -160
- package/src/ui/components/UpdateNotification.ts +0 -102
- package/src/ui/index.ts +0 -7
- package/src/ui/screens/AdvancedSettings.ts +0 -188
- package/src/ui/screens/ApiKeyViewer.ts +0 -511
- package/src/ui/screens/AppConfigurator.ts +0 -543
- package/src/ui/screens/AppManager.ts +0 -284
- package/src/ui/screens/ContainerControl.ts +0 -581
- package/src/ui/screens/LogsViewer.ts +0 -478
- package/src/ui/screens/MainMenu.ts +0 -299
- package/src/ui/screens/MonitorDashboard.ts +0 -881
- package/src/ui/screens/SecretsEditor.ts +0 -216
- package/src/ui/screens/SettingsScreen.ts +0 -633
- package/src/ui/screens/index.ts +0 -22
- package/src/ui/screens/setup/BaseAppSetupScreen.ts +0 -375
- package/src/ui/screens/setup/CloudflaredSetup.ts +0 -460
- package/src/ui/screens/setup/FullAutoSetup.ts +0 -766
- package/src/ui/screens/setup/HomepageSetup.ts +0 -165
- package/src/ui/screens/setup/JellyfinSetup.ts +0 -328
- package/src/ui/screens/setup/JellyseerrSetup.ts +0 -242
- package/src/ui/screens/setup/ProwlarrSetup.ts +0 -500
- package/src/ui/screens/setup/QBittorrentSetup.ts +0 -221
- package/src/ui/screens/setup/QuickSetup.ts +0 -1169
- package/src/ui/screens/setup/RecyclarrSetup.ts +0 -270
- package/src/ui/screens/setup/TRaSHProfileSetup.ts +0 -381
- package/src/utils/arch.ts +0 -78
- package/src/utils/browser.ts +0 -27
- package/src/utils/categories.ts +0 -61
- package/src/utils/debug.ts +0 -84
- package/src/utils/env.ts +0 -138
- package/src/utils/index.ts +0 -8
- package/src/utils/logs.ts +0 -119
- package/src/utils/migrations/1765626338_rename_env_variables.ts +0 -102
- package/src/utils/migrations/1765707135_rename_easiarr_status.ts +0 -91
- package/src/utils/migrations/1765732722_remove_cloudflare_dns_api_token.ts +0 -45
- package/src/utils/migrations/1769515650_migrate_to_xdg.ts +0 -114
- package/src/utils/migrations.ts +0 -165
- package/src/utils/password.ts +0 -42
- package/src/utils/paths.ts +0 -33
- package/src/utils/unraid.ts +0 -103
- package/src/utils/update-checker.ts +0 -210
- package/src/utils/url-utils.ts +0 -38
package/src/api/portainer-api.ts
DELETED
|
@@ -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
|
-
}
|