@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@muhammedaksam/easiarr",
3
- "version": "0.3.0",
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/",
@@ -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
 
@@ -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
- await this.prowlarrClient.addArrApp(appType, "localhost", port, apiKey, "localhost", prowlarrPort)
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"
@@ -1,18 +1,38 @@
1
1
  /**
2
2
  * Debug logging utility for Easiarr
3
3
  *
4
- * Only logs when EASIARR_DEBUG environment variable is set.
5
- * Usage: EASIARR_DEBUG=1 bun run dev
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
- const DEBUG_ENABLED = process.env.EASIARR_DEBUG === "1" || process.env.EASIARR_DEBUG === "true"
12
- const logFile = join(import.meta.dir, "..", "..", "debug.log")
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
- * Log a debug message to debug.log file if EASIARR_DEBUG is enabled
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
+ }