@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.
- package/dist/index.js +29837 -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,284 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* App Manager Screen
|
|
3
|
-
* Add, remove, or configure apps
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { CliRenderer, KeyEvent, RenderContext } from "@opentui/core"
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
BoxRenderable,
|
|
10
|
-
RGBA,
|
|
11
|
-
SelectRenderable,
|
|
12
|
-
SelectRenderableEvents,
|
|
13
|
-
TextRenderable,
|
|
14
|
-
} from "@opentui/core"
|
|
15
|
-
|
|
16
|
-
import type { AppId, EasiarrConfig } from "~/config/schema"
|
|
17
|
-
import type { App } from "~/ui"
|
|
18
|
-
import { getApp } from "~/apps/registry"
|
|
19
|
-
import { saveCompose } from "~/compose"
|
|
20
|
-
import { saveConfig } from "~/config"
|
|
21
|
-
import { ensureDirectoryStructure } from "~/structure/manager"
|
|
22
|
-
import { ApplicationSelector } from "~/ui/components/ApplicationSelector"
|
|
23
|
-
import { createPageLayout } from "~/ui/components/PageLayout"
|
|
24
|
-
import { SecretsEditor } from "./SecretsEditor"
|
|
25
|
-
|
|
26
|
-
export class AppManager {
|
|
27
|
-
private renderer: RenderContext
|
|
28
|
-
private container: BoxRenderable
|
|
29
|
-
private app: App
|
|
30
|
-
private config: EasiarrConfig
|
|
31
|
-
private selector: ApplicationSelector | null = null
|
|
32
|
-
private navMenu: SelectRenderable | null = null
|
|
33
|
-
private keyHandler: ((key: KeyEvent) => void) | null = null
|
|
34
|
-
private activeZone: "selector" | "nav" = "selector"
|
|
35
|
-
private previouslyEnabledApps: Set<AppId>
|
|
36
|
-
private page: BoxRenderable | null = null
|
|
37
|
-
|
|
38
|
-
constructor(renderer: RenderContext, container: BoxRenderable, app: App, config: EasiarrConfig) {
|
|
39
|
-
this.renderer = renderer
|
|
40
|
-
this.container = container
|
|
41
|
-
this.app = app
|
|
42
|
-
this.config = config
|
|
43
|
-
// Track which apps were enabled before user starts editing
|
|
44
|
-
this.previouslyEnabledApps = new Set(config.apps.filter((a) => a.enabled).map((a) => a.id))
|
|
45
|
-
|
|
46
|
-
this.render()
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private render(): void {
|
|
50
|
-
// Clean up previous listeners
|
|
51
|
-
if (this.keyHandler && (this.renderer as CliRenderer).keyInput) {
|
|
52
|
-
;(this.renderer as CliRenderer).keyInput.off("keypress", this.keyHandler)
|
|
53
|
-
this.keyHandler = null
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Clear container
|
|
57
|
-
const children = this.container.getChildren()
|
|
58
|
-
for (const child of children) {
|
|
59
|
-
this.container.remove(child.id)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const { container: page, content } = createPageLayout(this.renderer as CliRenderer, {
|
|
63
|
-
title: "Manage Apps",
|
|
64
|
-
stepInfo: "Toggle apps linked to your configuration",
|
|
65
|
-
footerHint: [
|
|
66
|
-
{ type: "key", key: "←→", value: "Tab" },
|
|
67
|
-
{ type: "key", key: "Enter", value: "Toggle" },
|
|
68
|
-
{ type: "key", key: "s", value: "Save" },
|
|
69
|
-
{ type: "key", key: "q", value: "Back" },
|
|
70
|
-
],
|
|
71
|
-
})
|
|
72
|
-
this.page = page
|
|
73
|
-
|
|
74
|
-
// Selected Apps Set for the selector
|
|
75
|
-
// Selected Apps Set for the selector
|
|
76
|
-
// We create a temporary Set to track changes, then commit to config on "Save".
|
|
77
|
-
// Changes are modified in memory immediately to match QuickSetup behavior.
|
|
78
|
-
|
|
79
|
-
const enabledApps = new Set(this.config.apps.filter((a) => a.enabled).map((a) => a.id))
|
|
80
|
-
|
|
81
|
-
this.selector = new ApplicationSelector(this.renderer as CliRenderer, {
|
|
82
|
-
selectedApps: enabledApps,
|
|
83
|
-
width: "100%",
|
|
84
|
-
flexGrow: 1, // list takes available space
|
|
85
|
-
onToggle: (appId, enabled) => {
|
|
86
|
-
this.toggleApp(appId, enabled)
|
|
87
|
-
},
|
|
88
|
-
})
|
|
89
|
-
content.add(this.selector)
|
|
90
|
-
|
|
91
|
-
// Separator
|
|
92
|
-
content.add(new TextRenderable(this.renderer, { content: " " }))
|
|
93
|
-
|
|
94
|
-
// Nav Menu (Save / Back)
|
|
95
|
-
this.navMenu = new SelectRenderable(this.renderer, {
|
|
96
|
-
id: "app-manager-nav",
|
|
97
|
-
width: "100%",
|
|
98
|
-
height: 4,
|
|
99
|
-
options: [
|
|
100
|
-
{ name: "💾 Save & Apply", description: "Write config and regenerate docker-compose.yml" },
|
|
101
|
-
{ name: "❌ Discard / Back", description: "Return to Main Menu" },
|
|
102
|
-
],
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
this.navMenu.on(SelectRenderableEvents.ITEM_SELECTED, async (index) => {
|
|
106
|
-
if (index === 0) {
|
|
107
|
-
await this.save()
|
|
108
|
-
} else {
|
|
109
|
-
this.app.navigateTo("main")
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
content.add(this.navMenu)
|
|
114
|
-
|
|
115
|
-
this.container.add(page)
|
|
116
|
-
|
|
117
|
-
// Initial Focus
|
|
118
|
-
this.selector.focus()
|
|
119
|
-
this.activeZone = "selector"
|
|
120
|
-
|
|
121
|
-
// Key Handler
|
|
122
|
-
this.keyHandler = (key: KeyEvent) => {
|
|
123
|
-
if (key.name === "q") {
|
|
124
|
-
this.app.navigateTo("main")
|
|
125
|
-
return
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (key.name === "s" && key.ctrl) {
|
|
129
|
-
// Ctrl+S to save
|
|
130
|
-
this.save()
|
|
131
|
-
return
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (key.name === "tab") {
|
|
135
|
-
// Switch zones
|
|
136
|
-
if (this.activeZone === "selector") {
|
|
137
|
-
this.activeZone = "nav"
|
|
138
|
-
this.selector?.blur()
|
|
139
|
-
this.navMenu?.focus()
|
|
140
|
-
} else {
|
|
141
|
-
this.activeZone = "selector"
|
|
142
|
-
this.navMenu?.blur()
|
|
143
|
-
this.selector?.focus()
|
|
144
|
-
}
|
|
145
|
-
return
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Delegate to selector if active
|
|
149
|
-
if (this.activeZone === "selector" && this.selector) {
|
|
150
|
-
const handled = this.selector.handleKey(key)
|
|
151
|
-
if (handled) return
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
;(this.renderer as CliRenderer).keyInput.on("keypress", this.keyHandler)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
private toggleApp(id: AppId, enabled: boolean): void {
|
|
158
|
-
const existingIndex = this.config.apps.findIndex((a) => a.id === id)
|
|
159
|
-
if (existingIndex >= 0) {
|
|
160
|
-
this.config.apps[existingIndex].enabled = enabled
|
|
161
|
-
} else {
|
|
162
|
-
if (enabled) {
|
|
163
|
-
this.config.apps.push({ id, enabled: true })
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Auto-enable Traefik config when traefik app is enabled
|
|
168
|
-
if (id === "traefik") {
|
|
169
|
-
if (enabled && !this.config.traefik?.enabled) {
|
|
170
|
-
this.config.traefik = {
|
|
171
|
-
enabled: true,
|
|
172
|
-
domain: "${CLOUDFLARE_DNS_ZONE}",
|
|
173
|
-
entrypoint: "web",
|
|
174
|
-
middlewares: [],
|
|
175
|
-
}
|
|
176
|
-
} else if (!enabled && this.config.traefik?.enabled) {
|
|
177
|
-
this.config.traefik.enabled = false
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Auto-enable VPN config when gluetun app is enabled
|
|
182
|
-
if (id === "gluetun") {
|
|
183
|
-
if (enabled && !this.config.vpn) {
|
|
184
|
-
this.config.vpn = { mode: "mini" }
|
|
185
|
-
} else if (!enabled && this.config.vpn) {
|
|
186
|
-
this.config.vpn.mode = "none"
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
private async save() {
|
|
192
|
-
await saveConfig(this.config)
|
|
193
|
-
await ensureDirectoryStructure(this.config)
|
|
194
|
-
await saveCompose(this.config)
|
|
195
|
-
|
|
196
|
-
// Check for newly-enabled apps that have secrets
|
|
197
|
-
const currentlyEnabled = new Set(this.config.apps.filter((a) => a.enabled).map((a) => a.id))
|
|
198
|
-
const newlyEnabled = [...currentlyEnabled].filter((id) => !this.previouslyEnabledApps.has(id))
|
|
199
|
-
const appsWithSecrets = newlyEnabled.filter((id) => {
|
|
200
|
-
const appDef = getApp(id)
|
|
201
|
-
return appDef?.secrets && appDef.secrets.length > 0
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
if (appsWithSecrets.length > 0) {
|
|
205
|
-
this.showSecretsPrompt(appsWithSecrets)
|
|
206
|
-
} else {
|
|
207
|
-
this.app.navigateTo("main")
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
private showSecretsPrompt(appsWithSecrets: AppId[]) {
|
|
212
|
-
// Hide the main page
|
|
213
|
-
if (this.page) this.page.visible = false
|
|
214
|
-
if (this.keyHandler) {
|
|
215
|
-
;(this.renderer as CliRenderer).keyInput.off("keypress", this.keyHandler)
|
|
216
|
-
this.keyHandler = null
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Create a prompt overlay
|
|
220
|
-
const overlay = new BoxRenderable(this.renderer, {
|
|
221
|
-
id: "secrets-prompt-overlay",
|
|
222
|
-
width: "100%",
|
|
223
|
-
height: "100%",
|
|
224
|
-
backgroundColor: RGBA.fromHex("#111111"),
|
|
225
|
-
zIndex: 200,
|
|
226
|
-
flexDirection: "column",
|
|
227
|
-
padding: 2,
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
const appNames = appsWithSecrets.map((id) => getApp(id)?.name || id).join(", ")
|
|
231
|
-
overlay.add(
|
|
232
|
-
new TextRenderable(this.renderer, {
|
|
233
|
-
content: "🔑 New Apps Require Configuration",
|
|
234
|
-
fg: "#f1fa8c",
|
|
235
|
-
marginBottom: 1,
|
|
236
|
-
})
|
|
237
|
-
)
|
|
238
|
-
overlay.add(
|
|
239
|
-
new TextRenderable(this.renderer, {
|
|
240
|
-
content: `The following apps need secrets configured: ${appNames}`,
|
|
241
|
-
fg: "#aaaaaa",
|
|
242
|
-
marginBottom: 2,
|
|
243
|
-
})
|
|
244
|
-
)
|
|
245
|
-
|
|
246
|
-
const menu = new SelectRenderable(this.renderer, {
|
|
247
|
-
id: "secrets-prompt-menu",
|
|
248
|
-
width: "100%",
|
|
249
|
-
height: 4,
|
|
250
|
-
options: [
|
|
251
|
-
{ name: "✓ Configure Secrets Now", description: "Open the Secrets Editor" },
|
|
252
|
-
{ name: "✗ Skip for Now", description: "Return to Main Menu" },
|
|
253
|
-
],
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
menu.on(SelectRenderableEvents.ITEM_SELECTED, (index) => {
|
|
257
|
-
this.container.remove("secrets-prompt-overlay")
|
|
258
|
-
if (index === 0) {
|
|
259
|
-
// Show SecretsEditor
|
|
260
|
-
const editor = new SecretsEditor(this.renderer as CliRenderer, {
|
|
261
|
-
id: "secrets-editor-overlay",
|
|
262
|
-
width: "100%",
|
|
263
|
-
height: "100%",
|
|
264
|
-
config: this.config,
|
|
265
|
-
onSave: () => {
|
|
266
|
-
this.container.remove("secrets-editor-overlay")
|
|
267
|
-
this.app.navigateTo("main")
|
|
268
|
-
},
|
|
269
|
-
onCancel: () => {
|
|
270
|
-
this.container.remove("secrets-editor-overlay")
|
|
271
|
-
this.app.navigateTo("main")
|
|
272
|
-
},
|
|
273
|
-
})
|
|
274
|
-
this.container.add(editor)
|
|
275
|
-
} else {
|
|
276
|
-
this.app.navigateTo("main")
|
|
277
|
-
}
|
|
278
|
-
})
|
|
279
|
-
|
|
280
|
-
overlay.add(menu)
|
|
281
|
-
this.container.add(overlay)
|
|
282
|
-
menu.focus()
|
|
283
|
-
}
|
|
284
|
-
}
|