@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
|
@@ -1,490 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility App Setup Actions
|
|
3
|
-
*
|
|
4
|
-
* Setup functions for Portainer, Cloudflare, Maintainerr, Bazarr,
|
|
5
|
-
* Huntarr, Slskd, Soularr, Recyclarr, Profilarr, Overseerr, and other utilities.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { existsSync } from "fs"
|
|
9
|
-
import { mkdir, writeFile } from "fs/promises"
|
|
10
|
-
import { dirname } from "path"
|
|
11
|
-
|
|
12
|
-
import { BazarrApiClient } from "~/api/bazarr-api"
|
|
13
|
-
import { CloudflareApi, setupCloudflaredTunnel } from "~/api/cloudflare-api"
|
|
14
|
-
import { HuntarrClient } from "~/api/huntarr-api"
|
|
15
|
-
import { MaintainerrClient } from "~/api/maintainerr-api"
|
|
16
|
-
import { OverseerrClient } from "~/api/overseerr-api"
|
|
17
|
-
import { PortainerApiClient } from "~/api/portainer-api"
|
|
18
|
-
import { ProfilarrApiClient } from "~/api/profilarr-api"
|
|
19
|
-
import { getApp } from "~/apps/registry"
|
|
20
|
-
import { saveRecyclarrConfig } from "~/config/recyclarr-config"
|
|
21
|
-
import { generateSlskdConfig, getSlskdConfigPath } from "~/config/slskd-config"
|
|
22
|
-
import { generateSoularrConfig, getSoularrConfigPath } from "~/config/soularr-config"
|
|
23
|
-
import { getEnabledAppConfig, SetupContext, SetupResult } from "~/setup/types"
|
|
24
|
-
import { debugLog } from "~/utils/debug"
|
|
25
|
-
import { getApplicationUrl } from "~/utils/url-utils"
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Setup Portainer container management
|
|
29
|
-
*/
|
|
30
|
-
export async function setupPortainer(ctx: SetupContext): Promise<SetupResult> {
|
|
31
|
-
const portainerConfig = getEnabledAppConfig(ctx, "portainer")
|
|
32
|
-
if (!portainerConfig) {
|
|
33
|
-
return { success: false, message: "Not enabled" }
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (!ctx.globalPassword) {
|
|
37
|
-
return { success: false, message: "No PASSWORD_GLOBAL set" }
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const port = portainerConfig.port || 9000
|
|
41
|
-
const client = new PortainerApiClient("localhost", port)
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
const result = await client.setup({
|
|
45
|
-
username: ctx.globalUsername,
|
|
46
|
-
password: ctx.globalPassword,
|
|
47
|
-
env: ctx.env,
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
return result
|
|
51
|
-
} catch (e) {
|
|
52
|
-
return { success: false, message: String(e) }
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Setup Cloudflare Tunnel
|
|
58
|
-
*/
|
|
59
|
-
export async function setupCloudflare(ctx: SetupContext): Promise<SetupResult> {
|
|
60
|
-
const cloudflaredConfig = getEnabledAppConfig(ctx, "cloudflared")
|
|
61
|
-
if (!cloudflaredConfig) {
|
|
62
|
-
return { success: false, message: "Not enabled" }
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const apiToken = ctx.env["CLOUDFLARE_API_TOKEN"]
|
|
66
|
-
if (!apiToken) {
|
|
67
|
-
return { success: false, message: "No CLOUDFLARE_API_TOKEN in .env" }
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const domain = ctx.env["CLOUDFLARE_DNS_ZONE"] || ctx.config.traefik?.domain
|
|
71
|
-
if (!domain) {
|
|
72
|
-
return { success: false, message: "No domain configured" }
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
// Create/update tunnel
|
|
77
|
-
const result = await setupCloudflaredTunnel(apiToken, domain, "easiarr")
|
|
78
|
-
|
|
79
|
-
// Prepare env updates
|
|
80
|
-
const envUpdates: Record<string, string> = {
|
|
81
|
-
CLOUDFLARE_TUNNEL_TOKEN: result.tunnelToken,
|
|
82
|
-
CLOUDFLARE_TUNNEL_ID: result.tunnelId,
|
|
83
|
-
CLOUDFLARE_ACCOUNT_ID: result.accountId,
|
|
84
|
-
CLOUDFLARE_DNS_ZONE: domain,
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Optional: Set up Cloudflare Access if email is available
|
|
88
|
-
const accessEmail = ctx.env["CLOUDFLARE_ACCESS_EMAIL"] || ctx.env["EMAIL_GLOBAL"]
|
|
89
|
-
if (accessEmail) {
|
|
90
|
-
try {
|
|
91
|
-
const api = new CloudflareApi(apiToken)
|
|
92
|
-
await api.setupAccessProtection(domain, [accessEmail], "easiarr")
|
|
93
|
-
return { success: true, message: `Tunnel + Access for ${accessEmail}`, envUpdates }
|
|
94
|
-
} catch {
|
|
95
|
-
return { success: true, message: "Tunnel created (Access failed)", envUpdates }
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return { success: true, message: "Tunnel created", envUpdates }
|
|
100
|
-
} catch (e) {
|
|
101
|
-
return { success: false, message: String(e) }
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Setup Overseerr request management
|
|
107
|
-
*/
|
|
108
|
-
export async function setupOverseerr(ctx: SetupContext): Promise<SetupResult> {
|
|
109
|
-
const overseerrConfig = getEnabledAppConfig(ctx, "overseerr")
|
|
110
|
-
if (!overseerrConfig) {
|
|
111
|
-
return { success: false, message: "Not enabled" }
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Overseerr requires Plex
|
|
115
|
-
const plexConfig = getEnabledAppConfig(ctx, "plex")
|
|
116
|
-
if (!plexConfig) {
|
|
117
|
-
return { success: false, message: "Plex not enabled" }
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const plexToken = ctx.env["API_KEY_PLEX"]
|
|
121
|
-
if (!plexToken) {
|
|
122
|
-
return { success: false, message: "No API_KEY_PLEX in .env" }
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const port = overseerrConfig.port || 5055
|
|
126
|
-
const client = new OverseerrClient("localhost", port)
|
|
127
|
-
|
|
128
|
-
try {
|
|
129
|
-
const result = await client.setup({
|
|
130
|
-
username: ctx.globalUsername,
|
|
131
|
-
password: ctx.globalPassword,
|
|
132
|
-
env: ctx.env,
|
|
133
|
-
plexToken,
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
if (result.success) {
|
|
137
|
-
// Set Overseerr's applicationUrl
|
|
138
|
-
try {
|
|
139
|
-
const overseerrUrl = getApplicationUrl("overseerr", port, ctx.config)
|
|
140
|
-
await client.setApplicationUrl(overseerrUrl)
|
|
141
|
-
debugLog("OverseerrSetup", `applicationUrl set to ${overseerrUrl}`)
|
|
142
|
-
} catch {
|
|
143
|
-
debugLog("OverseerrSetup", "Failed to set applicationUrl")
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return result
|
|
148
|
-
} catch (e) {
|
|
149
|
-
return { success: false, message: String(e) }
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Setup Maintainerr for media maintenance
|
|
155
|
-
*/
|
|
156
|
-
export async function setupMaintainerr(ctx: SetupContext): Promise<SetupResult> {
|
|
157
|
-
const maintainerrConfig = getEnabledAppConfig(ctx, "maintainerr")
|
|
158
|
-
if (!maintainerrConfig) {
|
|
159
|
-
return { success: false, message: "Not enabled" }
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Requires Plex
|
|
163
|
-
const plexConfig = getEnabledAppConfig(ctx, "plex")
|
|
164
|
-
if (!plexConfig) {
|
|
165
|
-
return { success: false, message: "Plex not enabled" }
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const port = maintainerrConfig.port || 6246
|
|
169
|
-
const client = new MaintainerrClient("localhost", port)
|
|
170
|
-
|
|
171
|
-
try {
|
|
172
|
-
const result = await client.setup({
|
|
173
|
-
username: ctx.globalUsername,
|
|
174
|
-
password: ctx.globalPassword,
|
|
175
|
-
env: ctx.env,
|
|
176
|
-
plexToken: ctx.env["API_KEY_PLEX"],
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
if (result.success) {
|
|
180
|
-
// Configure Radarr connection if enabled
|
|
181
|
-
const radarrConfig = getEnabledAppConfig(ctx, "radarr")
|
|
182
|
-
if (radarrConfig && ctx.env["API_KEY_RADARR"]) {
|
|
183
|
-
try {
|
|
184
|
-
const radarrDef = getApp("radarr")
|
|
185
|
-
const radarrPort = radarrConfig.port || radarrDef?.defaultPort || 7878
|
|
186
|
-
await client.configureRadarr("radarr", radarrPort, ctx.env["API_KEY_RADARR"])
|
|
187
|
-
} catch {
|
|
188
|
-
/* connection failed */
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Configure Sonarr connection if enabled
|
|
193
|
-
const sonarrConfig = getEnabledAppConfig(ctx, "sonarr")
|
|
194
|
-
if (sonarrConfig && ctx.env["API_KEY_SONARR"]) {
|
|
195
|
-
try {
|
|
196
|
-
const sonarrDef = getApp("sonarr")
|
|
197
|
-
const sonarrPort = sonarrConfig.port || sonarrDef?.defaultPort || 8989
|
|
198
|
-
await client.configureSonarr("sonarr", sonarrPort, ctx.env["API_KEY_SONARR"])
|
|
199
|
-
} catch {
|
|
200
|
-
/* connection failed */
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Configure Overseerr connection if enabled
|
|
205
|
-
const overseerrConfig = getEnabledAppConfig(ctx, "overseerr")
|
|
206
|
-
if (overseerrConfig && ctx.env["API_KEY_OVERSEERR"]) {
|
|
207
|
-
try {
|
|
208
|
-
const overseerrDef = getApp("overseerr")
|
|
209
|
-
const overseerrPort = overseerrConfig.port || overseerrDef?.defaultPort || 5055
|
|
210
|
-
await client.configureOverseerr("overseerr", overseerrPort, ctx.env["API_KEY_OVERSEERR"])
|
|
211
|
-
} catch {
|
|
212
|
-
/* connection failed */
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Configure Tautulli connection if enabled
|
|
217
|
-
const tautulliConfig = getEnabledAppConfig(ctx, "tautulli")
|
|
218
|
-
if (tautulliConfig && ctx.env["API_KEY_TAUTULLI"]) {
|
|
219
|
-
try {
|
|
220
|
-
const tautulliDef = getApp("tautulli")
|
|
221
|
-
const tautulliPort = tautulliConfig.port || tautulliDef?.defaultPort || 8181
|
|
222
|
-
await client.configureTautulli("tautulli", tautulliPort, ctx.env["API_KEY_TAUTULLI"])
|
|
223
|
-
} catch {
|
|
224
|
-
/* connection failed */
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return result
|
|
230
|
-
} catch (e) {
|
|
231
|
-
return { success: false, message: String(e) }
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Setup Bazarr subtitle management
|
|
237
|
-
*/
|
|
238
|
-
export async function setupBazarr(ctx: SetupContext): Promise<SetupResult> {
|
|
239
|
-
const bazarrConfig = getEnabledAppConfig(ctx, "bazarr")
|
|
240
|
-
if (!bazarrConfig) {
|
|
241
|
-
return { success: false, message: "Not enabled" }
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const port = bazarrConfig.port || 6767
|
|
245
|
-
const client = new BazarrApiClient("localhost", port)
|
|
246
|
-
|
|
247
|
-
// Get and set API key if available
|
|
248
|
-
const existingApiKey = ctx.env["API_KEY_BAZARR"]
|
|
249
|
-
if (existingApiKey) {
|
|
250
|
-
client.setApiKey(existingApiKey)
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
try {
|
|
254
|
-
const result = await client.setup({
|
|
255
|
-
username: ctx.globalUsername,
|
|
256
|
-
password: ctx.globalPassword,
|
|
257
|
-
env: ctx.env,
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
if (result.success) {
|
|
261
|
-
// Configure Radarr/Sonarr connections
|
|
262
|
-
let configured = 0
|
|
263
|
-
const radarrConfig = getEnabledAppConfig(ctx, "radarr")
|
|
264
|
-
if (radarrConfig && ctx.env["API_KEY_RADARR"]) {
|
|
265
|
-
try {
|
|
266
|
-
await client.configureRadarr(
|
|
267
|
-
"radarr",
|
|
268
|
-
radarrConfig.port || 7878,
|
|
269
|
-
ctx.env["API_KEY_RADARR"]
|
|
270
|
-
)
|
|
271
|
-
configured++
|
|
272
|
-
} catch {
|
|
273
|
-
/* connection failed */
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const sonarrConfig = getEnabledAppConfig(ctx, "sonarr")
|
|
278
|
-
if (sonarrConfig && ctx.env["API_KEY_SONARR"]) {
|
|
279
|
-
try {
|
|
280
|
-
await client.configureSonarr(
|
|
281
|
-
"sonarr",
|
|
282
|
-
sonarrConfig.port || 8989,
|
|
283
|
-
ctx.env["API_KEY_SONARR"]
|
|
284
|
-
)
|
|
285
|
-
configured++
|
|
286
|
-
} catch {
|
|
287
|
-
/* connection failed */
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return {
|
|
292
|
-
...result,
|
|
293
|
-
message: configured > 0 ? `${configured} apps connected` : result.message,
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return result
|
|
298
|
-
} catch (e) {
|
|
299
|
-
return { success: false, message: String(e) }
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Setup Huntarr for automatic searches
|
|
305
|
-
*/
|
|
306
|
-
export async function setupHuntarr(ctx: SetupContext): Promise<SetupResult> {
|
|
307
|
-
const huntarrConfig = getEnabledAppConfig(ctx, "huntarr")
|
|
308
|
-
if (!huntarrConfig) {
|
|
309
|
-
return { success: false, message: "Not enabled" }
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const port = huntarrConfig.port || 9705
|
|
313
|
-
const client = new HuntarrClient("localhost", port)
|
|
314
|
-
|
|
315
|
-
// Check if reachable
|
|
316
|
-
const healthy = await client.isHealthy()
|
|
317
|
-
if (!healthy) {
|
|
318
|
-
return { success: false, message: "Not reachable yet" }
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
try {
|
|
322
|
-
// Authenticate (creates user if needed, otherwise logs in)
|
|
323
|
-
const authenticated = await client.authenticate(ctx.globalUsername, ctx.globalPassword)
|
|
324
|
-
if (!authenticated) {
|
|
325
|
-
return { success: false, message: "Auth failed" }
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Add enabled *arr apps to Huntarr
|
|
329
|
-
const result = await client.setupEasiarrApps(ctx.config.apps, ctx.env)
|
|
330
|
-
return { success: true, message: `${result.added} *arr apps added` }
|
|
331
|
-
} catch (e) {
|
|
332
|
-
return { success: false, message: String(e) }
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Setup Slskd for Soulseek integration
|
|
338
|
-
*/
|
|
339
|
-
export async function setupSlskd(ctx: SetupContext): Promise<SetupResult> {
|
|
340
|
-
const slskdConfig = getEnabledAppConfig(ctx, "slskd")
|
|
341
|
-
if (!slskdConfig) {
|
|
342
|
-
return { success: false, message: "Not enabled" }
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
try {
|
|
346
|
-
// Generate slskd.yml config with auto-generated API key
|
|
347
|
-
const { yaml, apiKey } = generateSlskdConfig(ctx.config)
|
|
348
|
-
const configPath = getSlskdConfigPath(ctx.config.rootDir)
|
|
349
|
-
|
|
350
|
-
// Ensure config directory exists
|
|
351
|
-
const configDir = dirname(configPath)
|
|
352
|
-
if (!existsSync(configDir)) {
|
|
353
|
-
await mkdir(configDir, { recursive: true })
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Create slskd download directories
|
|
357
|
-
const slskdDownloadsDir = `${ctx.config.rootDir}/data/slskd_downloads`
|
|
358
|
-
await mkdir(`${slskdDownloadsDir}/incomplete`, { recursive: true })
|
|
359
|
-
await mkdir(`${slskdDownloadsDir}/complete`, { recursive: true })
|
|
360
|
-
|
|
361
|
-
// Write slskd.yml
|
|
362
|
-
await writeFile(configPath, yaml)
|
|
363
|
-
|
|
364
|
-
return {
|
|
365
|
-
success: true,
|
|
366
|
-
message: "Config generated, API key saved",
|
|
367
|
-
envUpdates: { API_KEY_SLSKD: apiKey },
|
|
368
|
-
}
|
|
369
|
-
} catch (e) {
|
|
370
|
-
return { success: false, message: String(e) }
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Setup Soularr for Lidarr/Slskd integration
|
|
376
|
-
*/
|
|
377
|
-
export async function setupSoularr(ctx: SetupContext): Promise<SetupResult> {
|
|
378
|
-
const soularrConfig = getEnabledAppConfig(ctx, "soularr")
|
|
379
|
-
if (!soularrConfig) {
|
|
380
|
-
return { success: false, message: "Not enabled" }
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// Check dependencies
|
|
384
|
-
const lidarrConfig = getEnabledAppConfig(ctx, "lidarr")
|
|
385
|
-
const slskdConfig = getEnabledAppConfig(ctx, "slskd")
|
|
386
|
-
|
|
387
|
-
if (!lidarrConfig || !slskdConfig) {
|
|
388
|
-
return { success: false, message: "Requires Lidarr & Slskd" }
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
try {
|
|
392
|
-
// Generate soularr config.ini
|
|
393
|
-
const configContent = generateSoularrConfig(ctx.config)
|
|
394
|
-
const configPath = getSoularrConfigPath(ctx.config.rootDir)
|
|
395
|
-
|
|
396
|
-
// Ensure directory exists
|
|
397
|
-
const configDir = dirname(configPath)
|
|
398
|
-
if (!existsSync(configDir)) {
|
|
399
|
-
await mkdir(configDir, { recursive: true })
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// Write config file
|
|
403
|
-
await writeFile(configPath, configContent)
|
|
404
|
-
|
|
405
|
-
return { success: true, message: "Config generated" }
|
|
406
|
-
} catch (e) {
|
|
407
|
-
return { success: false, message: String(e) }
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Setup Recyclarr for TRaSH guide syncing
|
|
413
|
-
*/
|
|
414
|
-
export async function setupRecyclarr(ctx: SetupContext): Promise<SetupResult> {
|
|
415
|
-
const recyclarrConfig = getEnabledAppConfig(ctx, "recyclarr")
|
|
416
|
-
if (!recyclarrConfig) {
|
|
417
|
-
return { success: false, message: "Not enabled" }
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// Check if we have at least one *arr app with API key
|
|
421
|
-
const radarrApiKey = ctx.env["API_KEY_RADARR"]
|
|
422
|
-
const sonarrApiKey = ctx.env["API_KEY_SONARR"]
|
|
423
|
-
|
|
424
|
-
if (!radarrApiKey && !sonarrApiKey) {
|
|
425
|
-
return { success: false, message: "No Radarr/Sonarr API keys" }
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
try {
|
|
429
|
-
await saveRecyclarrConfig(ctx.config)
|
|
430
|
-
return { success: true, message: "Config generated" }
|
|
431
|
-
} catch (e) {
|
|
432
|
-
return { success: false, message: String(e) }
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Setup Profilarr for TRaSH profile management
|
|
438
|
-
*/
|
|
439
|
-
export async function setupProfilarr(ctx: SetupContext): Promise<SetupResult> {
|
|
440
|
-
const profilarrConfig = getEnabledAppConfig(ctx, "profilarr")
|
|
441
|
-
if (!profilarrConfig) {
|
|
442
|
-
return { success: false, message: "Not enabled" }
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
const port = profilarrConfig.port || 6868
|
|
446
|
-
const client = new ProfilarrApiClient("localhost", port)
|
|
447
|
-
|
|
448
|
-
// Check if reachable
|
|
449
|
-
const healthy = await client.isHealthy()
|
|
450
|
-
if (!healthy) {
|
|
451
|
-
return { success: false, message: "Not reachable yet" }
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
try {
|
|
455
|
-
// Run the auto-setup process
|
|
456
|
-
const result = await client.setup({
|
|
457
|
-
username: ctx.globalUsername,
|
|
458
|
-
password: ctx.globalPassword,
|
|
459
|
-
env: ctx.env,
|
|
460
|
-
})
|
|
461
|
-
|
|
462
|
-
if (result.success) {
|
|
463
|
-
// Configure Radarr if enabled
|
|
464
|
-
const radarrConfig = getEnabledAppConfig(ctx, "radarr")
|
|
465
|
-
if (radarrConfig && ctx.env["API_KEY_RADARR"]) {
|
|
466
|
-
try {
|
|
467
|
-
const radarrPort = radarrConfig.port || 7878
|
|
468
|
-
await client.configureRadarr("radarr", radarrPort, ctx.env["API_KEY_RADARR"])
|
|
469
|
-
} catch {
|
|
470
|
-
/* connection failed */
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// Configure Sonarr if enabled
|
|
475
|
-
const sonarrConfig = getEnabledAppConfig(ctx, "sonarr")
|
|
476
|
-
if (sonarrConfig && ctx.env["API_KEY_SONARR"]) {
|
|
477
|
-
try {
|
|
478
|
-
const sonarrPort = sonarrConfig.port || 8989
|
|
479
|
-
await client.configureSonarr("sonarr", sonarrPort, ctx.env["API_KEY_SONARR"])
|
|
480
|
-
} catch {
|
|
481
|
-
/* connection failed */
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
return result
|
|
487
|
-
} catch (e) {
|
|
488
|
-
return { success: false, message: String(e) }
|
|
489
|
-
}
|
|
490
|
-
}
|
package/src/setup/index.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Setup Actions
|
|
3
|
-
*
|
|
4
|
-
* Reusable setup functions for all easiarr apps.
|
|
5
|
-
* Import from here rather than individual action files.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* import { runJellyseerrFullSetup, createSetupContext } from "~/setup"
|
|
10
|
-
*
|
|
11
|
-
* const ctx = createSetupContext(config, env)
|
|
12
|
-
* const result = await runJellyseerrFullSetup(ctx)
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
// Core types
|
|
17
|
-
export type { SetupContext, SetupResult, SetupStep, SetupStepStatus } from "./types"
|
|
18
|
-
|
|
19
|
-
// Core functions
|
|
20
|
-
export { reportStep, createSetupContext, isAppEnabled, getEnabledAppConfig } from "./types"
|
|
21
|
-
|
|
22
|
-
// Arr common actions (root folders, naming, quality, auth, external URLs)
|
|
23
|
-
export {
|
|
24
|
-
setupArrRootFolders,
|
|
25
|
-
setupArrNaming,
|
|
26
|
-
setupArrQuality,
|
|
27
|
-
setupArrAuthentication,
|
|
28
|
-
setupBazarrAuthentication,
|
|
29
|
-
setupArrExternalUrls,
|
|
30
|
-
} from "./actions/arr-common"
|
|
31
|
-
|
|
32
|
-
// Jellyseerr actions
|
|
33
|
-
export {
|
|
34
|
-
checkJellyseerrPrerequisites,
|
|
35
|
-
createJellyseerrClient,
|
|
36
|
-
runJellyseerrWizard,
|
|
37
|
-
configureJellyseerrRadarr,
|
|
38
|
-
configureJellyseerrSonarr,
|
|
39
|
-
setJellyseerrExternalUrl,
|
|
40
|
-
setJellyseerrJellyfinUrl,
|
|
41
|
-
syncJellyseerrLibraries,
|
|
42
|
-
runJellyseerrFullSetup,
|
|
43
|
-
type JellyseerrSetupOptions,
|
|
44
|
-
} from "./actions/jellyseerr"
|
|
45
|
-
|
|
46
|
-
// Prowlarr actions
|
|
47
|
-
export {
|
|
48
|
-
createProwlarrClient,
|
|
49
|
-
setupProwlarrApps,
|
|
50
|
-
setupFlareSolverr,
|
|
51
|
-
runProwlarrFullSetup,
|
|
52
|
-
} from "./actions/prowlarr"
|
|
53
|
-
|
|
54
|
-
// Download client actions
|
|
55
|
-
export { setupQBittorrent } from "./actions/download-clients"
|
|
56
|
-
|
|
57
|
-
// Media server actions
|
|
58
|
-
export { setupJellyfin, setupPlex } from "./actions/media-servers"
|
|
59
|
-
|
|
60
|
-
// Monitoring actions
|
|
61
|
-
export { setupUptimeKuma, setupGrafana, setupTautulli } from "./actions/monitoring"
|
|
62
|
-
|
|
63
|
-
// Dashboard actions
|
|
64
|
-
export { setupHomarr, setupHeimdall } from "./actions/dashboards"
|
|
65
|
-
|
|
66
|
-
// Utility actions
|
|
67
|
-
export {
|
|
68
|
-
setupPortainer,
|
|
69
|
-
setupCloudflare,
|
|
70
|
-
setupOverseerr,
|
|
71
|
-
setupMaintainerr,
|
|
72
|
-
setupBazarr,
|
|
73
|
-
setupHuntarr,
|
|
74
|
-
setupSlskd,
|
|
75
|
-
setupSoularr,
|
|
76
|
-
setupRecyclarr,
|
|
77
|
-
setupProfilarr,
|
|
78
|
-
} from "./actions/utilities"
|
package/src/setup/types.ts
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Setup Types
|
|
3
|
-
*
|
|
4
|
-
* Core types shared by all setup actions.
|
|
5
|
-
* These enable code reuse between FullAutoSetup and individual app screens.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { EasiarrConfig } from "~/config/schema"
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Context passed to all setup actions.
|
|
12
|
-
* Contains everything needed to configure an app.
|
|
13
|
-
*/
|
|
14
|
-
export interface SetupContext {
|
|
15
|
-
/** The current easiarr configuration */
|
|
16
|
-
config: EasiarrConfig
|
|
17
|
-
|
|
18
|
-
/** Environment variables from .env file */
|
|
19
|
-
env: Record<string, string>
|
|
20
|
-
|
|
21
|
-
/** Global username for authentication (from USERNAME_GLOBAL) */
|
|
22
|
-
globalUsername: string
|
|
23
|
-
|
|
24
|
-
/** Global password for authentication (from PASSWORD_GLOBAL) */
|
|
25
|
-
globalPassword: string
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Optional callback when a step starts.
|
|
29
|
-
* Used by UI to show "running" status.
|
|
30
|
-
*/
|
|
31
|
-
onStepStart?: (stepName: string) => void
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Optional callback when a step completes.
|
|
35
|
-
* Used by UI to show success/error/skipped status.
|
|
36
|
-
*/
|
|
37
|
-
onStepComplete?: (step: SetupStep) => void
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Status of a single step in a setup action.
|
|
42
|
-
*/
|
|
43
|
-
export type SetupStepStatus = "pending" | "running" | "success" | "error" | "skipped"
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Represents a single step in a multi-step setup action.
|
|
47
|
-
*/
|
|
48
|
-
export interface SetupStep {
|
|
49
|
-
/** Human-readable name of the step */
|
|
50
|
-
name: string
|
|
51
|
-
|
|
52
|
-
/** Current status of the step */
|
|
53
|
-
status: SetupStepStatus
|
|
54
|
-
|
|
55
|
-
/** Optional message (e.g., error details, success info) */
|
|
56
|
-
message?: string
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Result returned from a setup action.
|
|
61
|
-
*/
|
|
62
|
-
export interface SetupResult {
|
|
63
|
-
/** Whether the action succeeded */
|
|
64
|
-
success: boolean
|
|
65
|
-
|
|
66
|
-
/** Human-readable message describing the result */
|
|
67
|
-
message?: string
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Environment variable updates to persist to .env file.
|
|
71
|
-
* Caller is responsible for persisting these.
|
|
72
|
-
*/
|
|
73
|
-
envUpdates?: Record<string, string>
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Additional data returned by the action.
|
|
77
|
-
* Type depends on the specific action.
|
|
78
|
-
*/
|
|
79
|
-
data?: unknown
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Helper to report step progress during multi-step actions.
|
|
84
|
-
*
|
|
85
|
-
* @example
|
|
86
|
-
* ```ts
|
|
87
|
-
* reportStep(ctx, "Configure Radarr", "running")
|
|
88
|
-
* const result = await configureRadarr(ctx)
|
|
89
|
-
* reportStep(ctx, "Configure Radarr", result.success ? "success" : "error", result.message)
|
|
90
|
-
* ```
|
|
91
|
-
*/
|
|
92
|
-
export function reportStep(
|
|
93
|
-
ctx: SetupContext,
|
|
94
|
-
name: string,
|
|
95
|
-
status: SetupStepStatus,
|
|
96
|
-
message?: string
|
|
97
|
-
): void {
|
|
98
|
-
if (status === "running") {
|
|
99
|
-
ctx.onStepStart?.(name)
|
|
100
|
-
}
|
|
101
|
-
ctx.onStepComplete?.({ name, status, message })
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Creates a SetupContext from common sources.
|
|
106
|
-
*
|
|
107
|
-
* @param config - The easiarr configuration
|
|
108
|
-
* @param env - Environment variables (typically from readEnvSync())
|
|
109
|
-
* @param callbacks - Optional progress callbacks
|
|
110
|
-
*/
|
|
111
|
-
export function createSetupContext(
|
|
112
|
-
config: EasiarrConfig,
|
|
113
|
-
env: Record<string, string>,
|
|
114
|
-
callbacks?: {
|
|
115
|
-
onStepStart?: (stepName: string) => void
|
|
116
|
-
onStepComplete?: (step: SetupStep) => void
|
|
117
|
-
}
|
|
118
|
-
): SetupContext {
|
|
119
|
-
return {
|
|
120
|
-
config,
|
|
121
|
-
env,
|
|
122
|
-
globalUsername: env["USERNAME_GLOBAL"] || "admin",
|
|
123
|
-
globalPassword: env["PASSWORD_GLOBAL"] || "",
|
|
124
|
-
onStepStart: callbacks?.onStepStart,
|
|
125
|
-
onStepComplete: callbacks?.onStepComplete,
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Helper to check if an app is enabled in the config.
|
|
131
|
-
*/
|
|
132
|
-
export function isAppEnabled(ctx: SetupContext, appId: string): boolean {
|
|
133
|
-
return ctx.config.apps.some((a) => a.id === appId && a.enabled)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Helper to get app config if enabled.
|
|
138
|
-
*/
|
|
139
|
-
export function getEnabledAppConfig(
|
|
140
|
-
ctx: SetupContext,
|
|
141
|
-
appId: string
|
|
142
|
-
): EasiarrConfig["apps"][number] | undefined {
|
|
143
|
-
return ctx.config.apps.find((a) => a.id === appId && a.enabled)
|
|
144
|
-
}
|