@muhammedaksam/easiarr 0.3.0 → 0.3.2
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/package.json +2 -1
- package/src/api/arr-api.ts +14 -4
- package/src/api/prowlarr-api.ts +14 -2
- package/src/index.ts +9 -0
- package/src/ui/screens/AppConfigurator.ts +22 -0
- package/src/ui/screens/ProwlarrSetup.ts +3 -1
- package/src/utils/debug.ts +48 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@muhammedaksam/easiarr",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "TUI tool for generating docker-compose files for the *arr media ecosystem with 41 apps, TRaSH Guides best practices, VPN routing, and Traefik reverse proxy support",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
],
|
|
36
36
|
"scripts": {
|
|
37
37
|
"dev": "bun run --watch src/index.ts",
|
|
38
|
+
"dev:debug": "bun run dev --debug",
|
|
38
39
|
"build": "bun build src/index.ts --outdir dist --target bun",
|
|
39
40
|
"typecheck": "bun x tsc --noEmit",
|
|
40
41
|
"lint": "eslint src/",
|
package/src/api/arr-api.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Interacts with Radarr, Sonarr, Lidarr, Readarr, Whisparr APIs
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { debugLog } from "../utils/debug"
|
|
7
|
+
|
|
6
8
|
// Types for Root Folder API
|
|
7
9
|
export interface RootFolder {
|
|
8
10
|
id?: number
|
|
@@ -124,16 +126,24 @@ export class ArrApiClient {
|
|
|
124
126
|
...options.headers,
|
|
125
127
|
}
|
|
126
128
|
|
|
129
|
+
debugLog("ArrAPI", `${options.method || "GET"} ${url}`)
|
|
130
|
+
if (options.body) {
|
|
131
|
+
debugLog("ArrAPI", `Request Body: ${options.body}`)
|
|
132
|
+
}
|
|
133
|
+
|
|
127
134
|
const response = await fetch(url, { ...options, headers })
|
|
135
|
+
const text = await response.text()
|
|
136
|
+
|
|
137
|
+
debugLog("ArrAPI", `Response ${response.status} from ${endpoint}`)
|
|
138
|
+
if (text && text.length < 2000) {
|
|
139
|
+
debugLog("ArrAPI", `Response Body: ${text}`)
|
|
140
|
+
}
|
|
128
141
|
|
|
129
142
|
if (!response.ok) {
|
|
130
|
-
throw new Error(`API request failed: ${response.status} ${response.statusText}`)
|
|
143
|
+
throw new Error(`API request failed: ${response.status} ${response.statusText} - ${text}`)
|
|
131
144
|
}
|
|
132
145
|
|
|
133
|
-
// Handle empty responses (DELETE returns empty body)
|
|
134
|
-
const text = await response.text()
|
|
135
146
|
if (!text) return {} as T
|
|
136
|
-
|
|
137
147
|
return JSON.parse(text) as T
|
|
138
148
|
}
|
|
139
149
|
|
package/src/api/prowlarr-api.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Manages Indexer Proxies, Sync Profiles, and FlareSolverr integration
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { debugLog } from "../utils/debug"
|
|
7
|
+
|
|
6
8
|
export interface IndexerProxy {
|
|
7
9
|
id?: number
|
|
8
10
|
name: string
|
|
@@ -55,13 +57,23 @@ export class ProwlarrClient {
|
|
|
55
57
|
...((options.headers as Record<string, string>) || {}),
|
|
56
58
|
}
|
|
57
59
|
|
|
60
|
+
debugLog("Prowlarr", `${options.method || "GET"} ${url}`)
|
|
61
|
+
if (options.body) {
|
|
62
|
+
debugLog("Prowlarr", `Request Body: ${options.body}`)
|
|
63
|
+
}
|
|
64
|
+
|
|
58
65
|
const response = await fetch(url, { ...options, headers })
|
|
66
|
+
const text = await response.text()
|
|
67
|
+
|
|
68
|
+
debugLog("Prowlarr", `Response ${response.status} from ${endpoint}`)
|
|
69
|
+
if (text && text.length < 2000) {
|
|
70
|
+
debugLog("Prowlarr", `Response Body: ${text}`)
|
|
71
|
+
}
|
|
59
72
|
|
|
60
73
|
if (!response.ok) {
|
|
61
|
-
throw new Error(`Prowlarr API request failed: ${response.status} ${response.statusText}`)
|
|
74
|
+
throw new Error(`Prowlarr API request failed: ${response.status} ${response.statusText} - ${text}`)
|
|
62
75
|
}
|
|
63
76
|
|
|
64
|
-
const text = await response.text()
|
|
65
77
|
if (!text) return {} as T
|
|
66
78
|
return JSON.parse(text) as T
|
|
67
79
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,12 +2,21 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Easiarr Entry Point
|
|
4
4
|
* TUI tool for generating docker-compose files for the *arr ecosystem
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* easiarr - Start the TUI
|
|
8
|
+
* easiarr --debug - Start with debug logging enabled
|
|
9
|
+
* easiarr -d - Same as --debug
|
|
5
10
|
*/
|
|
6
11
|
|
|
7
12
|
import { createCliRenderer } from "@opentui/core"
|
|
8
13
|
import { App } from "./ui/App"
|
|
14
|
+
import { initDebug } from "./utils/debug"
|
|
9
15
|
|
|
10
16
|
async function main() {
|
|
17
|
+
// Initialize debug logging if enabled
|
|
18
|
+
initDebug()
|
|
19
|
+
|
|
11
20
|
const renderer = await createCliRenderer({
|
|
12
21
|
consoleOptions: {
|
|
13
22
|
startInDebugMode: false,
|
|
@@ -240,6 +240,28 @@ export class AppConfigurator extends BoxRenderable {
|
|
|
240
240
|
this.updateDisplay()
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
// Setup auth for *arr apps without root folders (e.g., Prowlarr)
|
|
244
|
+
if (this.globalPassword) {
|
|
245
|
+
const arrAppsNeedingAuth = ["prowlarr"]
|
|
246
|
+
for (const appId of arrAppsNeedingAuth) {
|
|
247
|
+
const appConfig = this.config.apps.find((a) => a.id === appId && a.enabled)
|
|
248
|
+
if (!appConfig) continue
|
|
249
|
+
|
|
250
|
+
const apiKey = this.extractApiKey(appId as AppId)
|
|
251
|
+
if (!apiKey) continue
|
|
252
|
+
|
|
253
|
+
const appDef = getApp(appId as AppId)
|
|
254
|
+
const port = appConfig.port || appDef?.defaultPort || 9696
|
|
255
|
+
const client = new ArrApiClient("localhost", port, apiKey)
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
await client.updateHostConfig(this.globalUsername, this.globalPassword, this.overrideExisting)
|
|
259
|
+
} catch {
|
|
260
|
+
// Auth setup for these apps is best-effort
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
243
265
|
// After root folders, prompt for download clients if needed
|
|
244
266
|
if (this.hasQBittorrent || this.hasSABnzbd) {
|
|
245
267
|
if (this.hasQBittorrent) {
|
|
@@ -178,7 +178,8 @@ export class ProwlarrSetup extends BoxRenderable {
|
|
|
178
178
|
const appDef = getApp(app.id)
|
|
179
179
|
const port = app.port || appDef?.defaultPort || 7878
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
// In Docker, use container names for inter-container communication
|
|
182
|
+
await this.prowlarrClient.addArrApp(appType, app.id, port, apiKey, "prowlarr", prowlarrPort)
|
|
182
183
|
|
|
183
184
|
const result = this.results.find((r) => r.name === app.id)
|
|
184
185
|
if (result) {
|
|
@@ -225,6 +226,7 @@ export class ProwlarrSetup extends BoxRenderable {
|
|
|
225
226
|
this.results[0].message = "FlareSolverr not enabled in config"
|
|
226
227
|
} else {
|
|
227
228
|
const fsPort = fsConfig.port || 8191
|
|
229
|
+
// In Docker, use container name for FlareSolverr
|
|
228
230
|
await this.prowlarrClient.configureFlareSolverr(`http://flaresolverr:${fsPort}`)
|
|
229
231
|
this.results[0].status = "success"
|
|
230
232
|
this.results[0].message = "Proxy added with 'flaresolverr' tag"
|
package/src/utils/debug.ts
CHANGED
|
@@ -1,18 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Debug logging utility for Easiarr
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Enable debug logging via:
|
|
5
|
+
* - CLI flag: easiarr --debug
|
|
6
|
+
* - Environment variable: EASIARR_DEBUG=1 bun run dev
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
|
-
import { appendFileSync } from "fs"
|
|
9
|
+
import { appendFileSync, writeFileSync } from "fs"
|
|
9
10
|
import { join } from "path"
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
const
|
|
12
|
+
// Check CLI args for --debug flag
|
|
13
|
+
const hasDebugFlag = process.argv.includes("--debug") || process.argv.includes("-d")
|
|
14
|
+
const hasEnvDebug = process.env.EASIARR_DEBUG === "1" || process.env.EASIARR_DEBUG === "true"
|
|
15
|
+
|
|
16
|
+
export const DEBUG_ENABLED = hasDebugFlag || hasEnvDebug
|
|
17
|
+
|
|
18
|
+
// Save debug log to ~/.easiarr/ like other config files
|
|
19
|
+
const easiarrDir = join(process.env.HOME || "~", ".easiarr")
|
|
20
|
+
const logFile = join(easiarrDir, "debug.log")
|
|
13
21
|
|
|
14
22
|
/**
|
|
15
|
-
*
|
|
23
|
+
* Initialize debug mode - clears old log file
|
|
24
|
+
*/
|
|
25
|
+
export function initDebug(): void {
|
|
26
|
+
if (!DEBUG_ENABLED) return
|
|
27
|
+
try {
|
|
28
|
+
writeFileSync(logFile, `=== Easiarr Debug Log - ${new Date().toISOString()} ===\n`)
|
|
29
|
+
} catch {
|
|
30
|
+
// Ignore
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Log a debug message to debug.log file if debug mode is enabled
|
|
16
36
|
*/
|
|
17
37
|
export function debugLog(category: string, message: string): void {
|
|
18
38
|
if (!DEBUG_ENABLED) return
|
|
@@ -25,3 +45,25 @@ export function debugLog(category: string, message: string): void {
|
|
|
25
45
|
// Ignore logging errors
|
|
26
46
|
}
|
|
27
47
|
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Log API request details for debugging
|
|
51
|
+
*/
|
|
52
|
+
export function debugRequest(method: string, url: string, body?: unknown): void {
|
|
53
|
+
if (!DEBUG_ENABLED) return
|
|
54
|
+
debugLog("API", `${method} ${url}`)
|
|
55
|
+
if (body) {
|
|
56
|
+
debugLog("API", `Body: ${JSON.stringify(body, null, 2)}`)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Log API response details for debugging
|
|
62
|
+
*/
|
|
63
|
+
export function debugResponse(status: number, url: string, body?: string): void {
|
|
64
|
+
if (!DEBUG_ENABLED) return
|
|
65
|
+
debugLog("API", `Response ${status} from ${url}`)
|
|
66
|
+
if (body && body.length < 2000) {
|
|
67
|
+
debugLog("API", `Response Body: ${body}`)
|
|
68
|
+
}
|
|
69
|
+
}
|