@muhammedaksam/easiarr 0.7.7 → 0.7.8

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.7.7",
3
+ "version": "0.7.8",
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",
@@ -64,11 +64,22 @@ export interface PortainerApiKeyResponse {
64
64
  export class PortainerApiClient {
65
65
  private baseUrl: string
66
66
  private jwtToken: string | null = null
67
+ private apiKey: string | null = null
67
68
 
68
69
  constructor(host: string, port: number) {
69
70
  this.baseUrl = `http://${host}:${port}`
70
71
  }
71
72
 
73
+ /**
74
+ * Set API key for authentication (alternative to JWT login)
75
+ * @param apiKey - The Portainer API key (e.g., ptr_xxx)
76
+ */
77
+ setApiKey(key: string): void {
78
+ if (key) {
79
+ this.apiKey = key
80
+ }
81
+ }
82
+
72
83
  private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
73
84
  const url = `${this.baseUrl}/api${endpoint}`
74
85
  const headers: Record<string, string> = {
@@ -76,8 +87,10 @@ export class PortainerApiClient {
76
87
  ...(options.headers as Record<string, string>),
77
88
  }
78
89
 
79
- // Add JWT token if authenticated
80
- if (this.jwtToken) {
90
+ // Add authentication - prefer API key, fallback to JWT
91
+ if (this.apiKey) {
92
+ headers["X-API-Key"] = this.apiKey
93
+ } else if (this.jwtToken) {
81
94
  headers["Authorization"] = `Bearer ${this.jwtToken}`
82
95
  }
83
96
 
@@ -240,6 +253,40 @@ export class PortainerApiClient {
240
253
  return this.request<PortainerEndpoint[]>("/endpoints")
241
254
  }
242
255
 
256
+ /**
257
+ * Get the local Docker socket environment ID.
258
+ * Finds the first endpoint that uses unix:///var/run/docker.sock or is named "local".
259
+ * @returns The environment ID or null if not found
260
+ */
261
+ async getLocalEnvironmentId(): Promise<number | null> {
262
+ try {
263
+ const endpoints = await this.getEndpoints()
264
+
265
+ // First, try to find one using Docker socket
266
+ const socketEndpoint = endpoints.find(
267
+ (e) => e.URL === "unix:///var/run/docker.sock" || e.URL.includes("docker.sock")
268
+ )
269
+ if (socketEndpoint) {
270
+ return socketEndpoint.Id
271
+ }
272
+
273
+ // Fallback: find one named "local"
274
+ const localEndpoint = endpoints.find((e) => e.Name.toLowerCase() === "local")
275
+ if (localEndpoint) {
276
+ return localEndpoint.Id
277
+ }
278
+
279
+ // Last resort: return the first endpoint if any exist
280
+ if (endpoints.length > 0) {
281
+ return endpoints[0].Id
282
+ }
283
+
284
+ return null
285
+ } catch {
286
+ return null
287
+ }
288
+ }
289
+
243
290
  /**
244
291
  * Get all containers for an endpoint
245
292
  */
@@ -10,7 +10,8 @@ import type { EasiarrConfig, AppCategory } from "./schema"
10
10
  import { APP_CATEGORIES } from "./schema"
11
11
  import { getApp } from "../apps/registry"
12
12
  import { CATEGORY_ORDER } from "../apps/categories"
13
- import { readEnvSync, getLocalIp } from "../utils/env"
13
+ import { readEnvSync, getLocalIp, updateEnv } from "../utils/env"
14
+ import { PortainerApiClient } from "../api/portainer-api"
14
15
 
15
16
  /**
16
17
  * Get the Homepage config directory path
@@ -95,8 +96,27 @@ export async function generateServicesYaml(config: EasiarrConfig): Promise<strin
95
96
  }
96
97
 
97
98
  if (appDef.id === "portainer") {
98
- // Default to environment 1 (local)
99
- service.widget.env = env["PORTAINER_ENV"] ?? "1"
99
+ // Try to auto-detect Portainer environment ID
100
+ // User can override with PORTAINER_ENV in .env file
101
+ if (env["PORTAINER_ENV"]) {
102
+ service.widget.env = env["PORTAINER_ENV"]
103
+ } else {
104
+ // Auto-detect from Portainer API
105
+ const portainerPort = appConfig.port ?? appDef.defaultPort
106
+ const portainerClient = new PortainerApiClient(localIp, portainerPort)
107
+ const apiKey = env["API_KEY_PORTAINER"]
108
+ if (apiKey) {
109
+ portainerClient.setApiKey(apiKey)
110
+ }
111
+ const localEnvId = await portainerClient.getLocalEnvironmentId()
112
+ const envIdStr = localEnvId?.toString() ?? "1"
113
+ service.widget.env = envIdStr
114
+
115
+ // Persist the detected env ID to .env for future use
116
+ if (localEnvId) {
117
+ await updateEnv({ PORTAINER_ENV: envIdStr })
118
+ }
119
+ }
100
120
  }
101
121
 
102
122
  // Add any custom widget fields