From 0a5b383526bf917a13c09ab8a7ac4787b68a5333 Mon Sep 17 00:00:00 2001
From: Josh Patra
Date: Wed, 25 Mar 2026 16:11:27 -0400
Subject: [PATCH] v1.4.3: fixed .env restarting from Admin UI, re-release of
prev ver
---
README.md | 4 +-
.../RuntimeEnvConfigurationTests.cs | 88 +++++++
allstarr/AppVersion.cs | 2 +-
allstarr/Controllers/ConfigController.cs | 10 +-
allstarr/Program.cs | 1 +
allstarr/Services/Admin/AdminHelperService.cs | 5 +-
.../Common/RuntimeEnvConfiguration.cs | 235 ++++++++++++++++++
allstarr/wwwroot/index.html | 4 +-
allstarr/wwwroot/js/api.js | 2 +-
allstarr/wwwroot/js/operations.js | 8 +-
10 files changed, 341 insertions(+), 18 deletions(-)
create mode 100644 allstarr.Tests/RuntimeEnvConfigurationTests.cs
create mode 100644 allstarr/Services/Common/RuntimeEnvConfiguration.cs
diff --git a/README.md b/README.md
index 0d54c91..55c82ab 100644
--- a/README.md
+++ b/README.md
@@ -65,13 +65,13 @@ Allstarr includes a web UI for easy configuration and playlist management, acces
- `37i9dQZF1DXcBWIGoYBM5M` (just the ID)
- `spotify:playlist:37i9dQZF1DXcBWIGoYBM5M` (Spotify URI)
- `https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M` (full URL)
-4. **Restart** to apply changes (should be a banner)
+4. **Restart Allstarr** to apply changes (should be a banner)
Then, proceeed to **Active Playlists**, which shows you which Spotify playlists are currently being monitored and filled with tracks, and lets you do a bunch of useful operations on them.
### Configuration Persistence
-The web UI updates your `.env` file directly. Changes persist across container restarts, but require a restart to take effect. In development mode, the `.env` file is in your project root. In Docker, it's at `/app/.env`.
+The web UI updates your `.env` file directly. Allstarr reloads that file on startup, so a normal container restart is enough for UI changes to take effect. In development mode, the `.env` file is in your project root. In Docker, it's at `/app/.env`.
There's an environment variable to modify this.
diff --git a/allstarr.Tests/RuntimeEnvConfigurationTests.cs b/allstarr.Tests/RuntimeEnvConfigurationTests.cs
new file mode 100644
index 0000000..0db1154
--- /dev/null
+++ b/allstarr.Tests/RuntimeEnvConfigurationTests.cs
@@ -0,0 +1,88 @@
+using allstarr.Services.Common;
+using Microsoft.Extensions.Configuration;
+
+namespace allstarr.Tests;
+
+public sealed class RuntimeEnvConfigurationTests : IDisposable
+{
+ private readonly string _envFilePath = Path.Combine(
+ Path.GetTempPath(),
+ $"allstarr-runtime-{Guid.NewGuid():N}.env");
+
+ [Fact]
+ public void MapEnvVarToConfiguration_MapsFlatKeyToNestedConfigKey()
+ {
+ var mappings = RuntimeEnvConfiguration
+ .MapEnvVarToConfiguration("SPOTIFY_IMPORT_MATCHING_INTERVAL_HOURS", "7")
+ .ToList();
+
+ var mapping = Assert.Single(mappings);
+ Assert.Equal("SpotifyImport:MatchingIntervalHours", mapping.Key);
+ Assert.Equal("7", mapping.Value);
+ }
+
+ [Fact]
+ public void MapEnvVarToConfiguration_MapsSharedBackendKeysToBothSections()
+ {
+ var mappings = RuntimeEnvConfiguration
+ .MapEnvVarToConfiguration("MUSIC_SERVICE", "Qobuz")
+ .OrderBy(x => x.Key, StringComparer.Ordinal)
+ .ToList();
+
+ Assert.Equal(2, mappings.Count);
+ Assert.Equal("Jellyfin:MusicService", mappings[0].Key);
+ Assert.Equal("Qobuz", mappings[0].Value);
+ Assert.Equal("Subsonic:MusicService", mappings[1].Key);
+ Assert.Equal("Qobuz", mappings[1].Value);
+ }
+
+ [Fact]
+ public void MapEnvVarToConfiguration_IgnoresComposeOnlyMountKeys()
+ {
+ var mappings = RuntimeEnvConfiguration
+ .MapEnvVarToConfiguration("DOWNLOAD_PATH", "./downloads")
+ .ToList();
+
+ Assert.Empty(mappings);
+ }
+
+ [Fact]
+ public void LoadDotEnvOverrides_StripsQuotesAndSupportsDoubleUnderscoreKeys()
+ {
+ File.WriteAllText(
+ _envFilePath,
+ """
+ SPOTIFY_API_SESSION_COOKIE="secret-cookie"
+ Admin__EnableEnvExport=true
+ """);
+
+ var overrides = RuntimeEnvConfiguration.LoadDotEnvOverrides(_envFilePath);
+
+ Assert.Equal("secret-cookie", overrides["SpotifyApi:SessionCookie"]);
+ Assert.Equal("true", overrides["Admin:EnableEnvExport"]);
+ }
+
+ [Fact]
+ public void AddDotEnvOverrides_OverridesEarlierConfigurationValues()
+ {
+ File.WriteAllText(_envFilePath, "SPOTIFY_IMPORT_MATCHING_INTERVAL_HOURS=7\n");
+
+ var configuration = new ConfigurationManager();
+ configuration.AddInMemoryCollection(new Dictionary
+ {
+ ["SpotifyImport:MatchingIntervalHours"] = "24"
+ });
+
+ RuntimeEnvConfiguration.AddDotEnvOverrides(configuration, _envFilePath);
+
+ Assert.Equal(7, configuration.GetValue("SpotifyImport:MatchingIntervalHours"));
+ }
+
+ public void Dispose()
+ {
+ if (File.Exists(_envFilePath))
+ {
+ File.Delete(_envFilePath);
+ }
+ }
+}
diff --git a/allstarr/AppVersion.cs b/allstarr/AppVersion.cs
index bce89d9..ed18df0 100644
--- a/allstarr/AppVersion.cs
+++ b/allstarr/AppVersion.cs
@@ -9,5 +9,5 @@ public static class AppVersion
///
/// Current application version.
///
- public const string Version = "1.4.1";
+ public const string Version = "1.4.3";
}
diff --git a/allstarr/Controllers/ConfigController.cs b/allstarr/Controllers/ConfigController.cs
index 43dcf5c..48d6530 100644
--- a/allstarr/Controllers/ConfigController.cs
+++ b/allstarr/Controllers/ConfigController.cs
@@ -580,7 +580,7 @@ public class ConfigController : ControllerBase
return Ok(new
{
- message = "Configuration updated. Restart container to apply changes.",
+ message = "Configuration updated. Restart Allstarr to apply changes.",
updatedKeys = appliedUpdates,
requiresRestart = true,
envFilePath = _helperService.GetEnvFilePath()
@@ -696,7 +696,7 @@ public class ConfigController : ControllerBase
_logger.LogWarning("Docker socket not available at {Path}", socketPath);
return StatusCode(503, new {
error = "Docker socket not available",
- message = "Please restart manually: docker-compose restart allstarr"
+ message = "Please restart manually: docker restart allstarr"
});
}
@@ -749,7 +749,7 @@ public class ConfigController : ControllerBase
_logger.LogError("Failed to restart container: {StatusCode} - {Body}", response.StatusCode, errorBody);
return StatusCode((int)response.StatusCode, new {
error = "Failed to restart container",
- message = "Please restart manually: docker-compose restart allstarr"
+ message = "Please restart manually: docker restart allstarr"
});
}
}
@@ -758,7 +758,7 @@ public class ConfigController : ControllerBase
_logger.LogError(ex, "Error restarting container");
return StatusCode(500, new {
error = "Failed to restart container",
- message = "Please restart manually: docker-compose restart allstarr"
+ message = "Please restart manually: docker restart allstarr"
});
}
}
@@ -890,7 +890,7 @@ public class ConfigController : ControllerBase
return Ok(new
{
success = true,
- message = ".env file imported successfully. Restart the application for changes to take effect."
+ message = ".env file imported successfully. Restart Allstarr for changes to take effect."
});
}
catch (Exception ex)
diff --git a/allstarr/Program.cs b/allstarr/Program.cs
index 7725a8c..9a341ee 100644
--- a/allstarr/Program.cs
+++ b/allstarr/Program.cs
@@ -16,6 +16,7 @@ using Microsoft.Extensions.Http;
using System.Net;
var builder = WebApplication.CreateBuilder(args);
+RuntimeEnvConfiguration.AddDotEnvOverrides(builder.Configuration, builder.Environment, Console.Out);
// Discover SquidWTF API and streaming endpoints from uptime feeds.
var squidWtfEndpointCatalog = await SquidWtfEndpointDiscovery.DiscoverAsync();
diff --git a/allstarr/Services/Admin/AdminHelperService.cs b/allstarr/Services/Admin/AdminHelperService.cs
index 465849f..ba0726d 100644
--- a/allstarr/Services/Admin/AdminHelperService.cs
+++ b/allstarr/Services/Admin/AdminHelperService.cs
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using allstarr.Models.Settings;
using allstarr.Models.Spotify;
+using allstarr.Services.Common;
namespace allstarr.Services.Admin;
@@ -20,9 +21,7 @@ public class AdminHelperService
{
_logger = logger;
_jellyfinSettings = jellyfinSettings.Value;
- _envFilePath = environment.IsDevelopment()
- ? Path.Combine(environment.ContentRootPath, "..", ".env")
- : "/app/.env";
+ _envFilePath = RuntimeEnvConfiguration.ResolveEnvFilePath(environment);
}
public string GetJellyfinAuthHeader()
diff --git a/allstarr/Services/Common/RuntimeEnvConfiguration.cs b/allstarr/Services/Common/RuntimeEnvConfiguration.cs
new file mode 100644
index 0000000..218d017
--- /dev/null
+++ b/allstarr/Services/Common/RuntimeEnvConfiguration.cs
@@ -0,0 +1,235 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+
+namespace allstarr.Services.Common;
+
+///
+/// Loads supported flat .env keys into ASP.NET configuration so Docker/admin UI
+/// updates stored in /app/.env take effect on the next application startup.
+///
+public static class RuntimeEnvConfiguration
+{
+ private static readonly IReadOnlyDictionary ExactKeyMappings =
+ new Dictionary(StringComparer.OrdinalIgnoreCase)
+ {
+ ["BACKEND_TYPE"] = ["Backend:Type"],
+ ["ADMIN_BIND_ANY_IP"] = ["Admin:BindAnyIp"],
+ ["ADMIN_TRUSTED_SUBNETS"] = ["Admin:TrustedSubnets"],
+ ["ADMIN_ENABLE_ENV_EXPORT"] = ["Admin:EnableEnvExport"],
+
+ ["CORS_ALLOWED_ORIGINS"] = ["Cors:AllowedOrigins"],
+ ["CORS_ALLOWED_METHODS"] = ["Cors:AllowedMethods"],
+ ["CORS_ALLOWED_HEADERS"] = ["Cors:AllowedHeaders"],
+ ["CORS_ALLOW_CREDENTIALS"] = ["Cors:AllowCredentials"],
+
+ ["SUBSONIC_URL"] = ["Subsonic:Url"],
+ ["JELLYFIN_URL"] = ["Jellyfin:Url"],
+ ["JELLYFIN_API_KEY"] = ["Jellyfin:ApiKey"],
+ ["JELLYFIN_USER_ID"] = ["Jellyfin:UserId"],
+ ["JELLYFIN_CLIENT_USERNAME"] = ["Jellyfin:ClientUsername"],
+ ["JELLYFIN_LIBRARY_ID"] = ["Jellyfin:LibraryId"],
+
+ ["LIBRARY_DOWNLOAD_PATH"] = ["Library:DownloadPath"],
+ ["LIBRARY_KEPT_PATH"] = ["Library:KeptPath"],
+
+ ["REDIS_ENABLED"] = ["Redis:Enabled"],
+ ["REDIS_CONNECTION_STRING"] = ["Redis:ConnectionString"],
+
+ ["SPOTIFY_IMPORT_ENABLED"] = ["SpotifyImport:Enabled"],
+ ["SPOTIFY_IMPORT_SYNC_START_HOUR"] = ["SpotifyImport:SyncStartHour"],
+ ["SPOTIFY_IMPORT_SYNC_START_MINUTE"] = ["SpotifyImport:SyncStartMinute"],
+ ["SPOTIFY_IMPORT_SYNC_WINDOW_HOURS"] = ["SpotifyImport:SyncWindowHours"],
+ ["SPOTIFY_IMPORT_MATCHING_INTERVAL_HOURS"] = ["SpotifyImport:MatchingIntervalHours"],
+ ["SPOTIFY_IMPORT_PLAYLISTS"] = ["SpotifyImport:Playlists"],
+ ["SPOTIFY_IMPORT_PLAYLIST_IDS"] = ["SpotifyImport:PlaylistIds"],
+ ["SPOTIFY_IMPORT_PLAYLIST_NAMES"] = ["SpotifyImport:PlaylistNames"],
+ ["SPOTIFY_IMPORT_PLAYLIST_LOCAL_TRACKS_POSITIONS"] = ["SpotifyImport:PlaylistLocalTracksPositions"],
+
+ ["SPOTIFY_API_ENABLED"] = ["SpotifyApi:Enabled"],
+ ["SPOTIFY_API_SESSION_COOKIE"] = ["SpotifyApi:SessionCookie"],
+ ["SPOTIFY_API_SESSION_COOKIE_SET_DATE"] = ["SpotifyApi:SessionCookieSetDate"],
+ ["SPOTIFY_API_CACHE_DURATION_MINUTES"] = ["SpotifyApi:CacheDurationMinutes"],
+ ["SPOTIFY_API_RATE_LIMIT_DELAY_MS"] = ["SpotifyApi:RateLimitDelayMs"],
+ ["SPOTIFY_API_PREFER_ISRC_MATCHING"] = ["SpotifyApi:PreferIsrcMatching"],
+ ["SPOTIFY_LYRICS_API_URL"] = ["SpotifyApi:LyricsApiUrl"],
+
+ ["SCROBBLING_ENABLED"] = ["Scrobbling:Enabled"],
+ ["SCROBBLING_LOCAL_TRACKS_ENABLED"] = ["Scrobbling:LocalTracksEnabled"],
+ ["SCROBBLING_SYNTHETIC_LOCAL_PLAYED_SIGNAL_ENABLED"] = ["Scrobbling:SyntheticLocalPlayedSignalEnabled"],
+ ["SCROBBLING_LASTFM_ENABLED"] = ["Scrobbling:LastFm:Enabled"],
+ ["SCROBBLING_LASTFM_API_KEY"] = ["Scrobbling:LastFm:ApiKey"],
+ ["SCROBBLING_LASTFM_SHARED_SECRET"] = ["Scrobbling:LastFm:SharedSecret"],
+ ["SCROBBLING_LASTFM_SESSION_KEY"] = ["Scrobbling:LastFm:SessionKey"],
+ ["SCROBBLING_LASTFM_USERNAME"] = ["Scrobbling:LastFm:Username"],
+ ["SCROBBLING_LASTFM_PASSWORD"] = ["Scrobbling:LastFm:Password"],
+ ["SCROBBLING_LISTENBRAINZ_ENABLED"] = ["Scrobbling:ListenBrainz:Enabled"],
+ ["SCROBBLING_LISTENBRAINZ_USER_TOKEN"] = ["Scrobbling:ListenBrainz:UserToken"],
+
+ ["DEBUG_LOG_ALL_REQUESTS"] = ["Debug:LogAllRequests"],
+ ["DEBUG_REDACT_SENSITIVE_REQUEST_VALUES"] = ["Debug:RedactSensitiveRequestValues"],
+
+ ["DEEZER_ARL"] = ["Deezer:Arl"],
+ ["DEEZER_ARL_FALLBACK"] = ["Deezer:ArlFallback"],
+ ["DEEZER_QUALITY"] = ["Deezer:Quality"],
+ ["DEEZER_MIN_REQUEST_INTERVAL_MS"] = ["Deezer:MinRequestIntervalMs"],
+
+ ["QOBUZ_USER_AUTH_TOKEN"] = ["Qobuz:UserAuthToken"],
+ ["QOBUZ_USER_ID"] = ["Qobuz:UserId"],
+ ["QOBUZ_QUALITY"] = ["Qobuz:Quality"],
+ ["QOBUZ_MIN_REQUEST_INTERVAL_MS"] = ["Qobuz:MinRequestIntervalMs"],
+
+ ["SQUIDWTF_QUALITY"] = ["SquidWTF:Quality"],
+ ["SQUIDWTF_MIN_REQUEST_INTERVAL_MS"] = ["SquidWTF:MinRequestIntervalMs"],
+
+ ["MUSICBRAINZ_ENABLED"] = ["MusicBrainz:Enabled"],
+ ["MUSICBRAINZ_USERNAME"] = ["MusicBrainz:Username"],
+ ["MUSICBRAINZ_PASSWORD"] = ["MusicBrainz:Password"],
+
+ ["CACHE_SEARCH_RESULTS_MINUTES"] = ["Cache:SearchResultsMinutes"],
+ ["CACHE_PLAYLIST_IMAGES_HOURS"] = ["Cache:PlaylistImagesHours"],
+ ["CACHE_SPOTIFY_PLAYLIST_ITEMS_HOURS"] = ["Cache:SpotifyPlaylistItemsHours"],
+ ["CACHE_SPOTIFY_MATCHED_TRACKS_DAYS"] = ["Cache:SpotifyMatchedTracksDays"],
+ ["CACHE_LYRICS_DAYS"] = ["Cache:LyricsDays"],
+ ["CACHE_GENRE_DAYS"] = ["Cache:GenreDays"],
+ ["CACHE_METADATA_DAYS"] = ["Cache:MetadataDays"],
+ ["CACHE_ODESLI_LOOKUP_DAYS"] = ["Cache:OdesliLookupDays"],
+ ["CACHE_PROXY_IMAGES_DAYS"] = ["Cache:ProxyImagesDays"],
+ ["CACHE_TRANSCODE_MINUTES"] = ["Cache:TranscodeCacheMinutes"]
+ };
+
+ private static readonly IReadOnlyDictionary SharedBackendKeyMappings =
+ new Dictionary(StringComparer.OrdinalIgnoreCase)
+ {
+ ["MUSIC_SERVICE"] = ["Subsonic:MusicService", "Jellyfin:MusicService"],
+ ["EXPLICIT_FILTER"] = ["Subsonic:ExplicitFilter", "Jellyfin:ExplicitFilter"],
+ ["DOWNLOAD_MODE"] = ["Subsonic:DownloadMode", "Jellyfin:DownloadMode"],
+ ["STORAGE_MODE"] = ["Subsonic:StorageMode", "Jellyfin:StorageMode"],
+ ["CACHE_DURATION_HOURS"] = ["Subsonic:CacheDurationHours", "Jellyfin:CacheDurationHours"],
+ ["ENABLE_EXTERNAL_PLAYLISTS"] = ["Subsonic:EnableExternalPlaylists", "Jellyfin:EnableExternalPlaylists"],
+ ["PLAYLISTS_DIRECTORY"] = ["Subsonic:PlaylistsDirectory", "Jellyfin:PlaylistsDirectory"]
+ };
+
+ private static readonly HashSet IgnoredComposeOnlyKeys = new(StringComparer.OrdinalIgnoreCase)
+ {
+ "DOWNLOAD_PATH",
+ "KEPT_PATH",
+ "CACHE_PATH",
+ "REDIS_DATA_PATH"
+ };
+
+ public static string ResolveEnvFilePath(IHostEnvironment environment)
+ {
+ return environment.IsDevelopment()
+ ? Path.GetFullPath(Path.Combine(environment.ContentRootPath, "..", ".env"))
+ : "/app/.env";
+ }
+
+ public static void AddDotEnvOverrides(
+ ConfigurationManager configuration,
+ IHostEnvironment environment,
+ TextWriter? logWriter = null)
+ {
+ AddDotEnvOverrides(configuration, ResolveEnvFilePath(environment), logWriter);
+ }
+
+ public static void AddDotEnvOverrides(
+ ConfigurationManager configuration,
+ string envFilePath,
+ TextWriter? logWriter = null)
+ {
+ var overrides = LoadDotEnvOverrides(envFilePath);
+ if (overrides.Count == 0)
+ {
+ if (File.Exists(envFilePath))
+ {
+ logWriter?.WriteLine($"No supported runtime overrides found in {envFilePath}");
+ }
+
+ return;
+ }
+
+ configuration.AddInMemoryCollection(overrides);
+ logWriter?.WriteLine($"Loaded {overrides.Count} runtime override(s) from {envFilePath}");
+ }
+
+ public static Dictionary LoadDotEnvOverrides(string envFilePath)
+ {
+ var overrides = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ if (!File.Exists(envFilePath))
+ {
+ return overrides;
+ }
+
+ foreach (var line in File.ReadLines(envFilePath))
+ {
+ if (string.IsNullOrWhiteSpace(line) || line.TrimStart().StartsWith('#'))
+ {
+ continue;
+ }
+
+ var separatorIndex = line.IndexOf('=');
+ if (separatorIndex <= 0)
+ {
+ continue;
+ }
+
+ var envKey = line[..separatorIndex].Trim();
+ var envValue = StripQuotes(line[(separatorIndex + 1)..].Trim());
+
+ foreach (var mapping in MapEnvVarToConfiguration(envKey, envValue))
+ {
+ overrides[mapping.Key] = mapping.Value;
+ }
+ }
+
+ return overrides;
+ }
+
+ public static IEnumerable> MapEnvVarToConfiguration(string envKey, string? envValue)
+ {
+ if (string.IsNullOrWhiteSpace(envKey) || IgnoredComposeOnlyKeys.Contains(envKey))
+ {
+ yield break;
+ }
+
+ if (envKey.Contains("__", StringComparison.Ordinal))
+ {
+ yield return new KeyValuePair(envKey.Replace("__", ":"), envValue);
+ yield break;
+ }
+
+ if (SharedBackendKeyMappings.TryGetValue(envKey, out var sharedKeys))
+ {
+ foreach (var sharedKey in sharedKeys)
+ {
+ yield return new KeyValuePair(sharedKey, envValue);
+ }
+
+ yield break;
+ }
+
+ if (ExactKeyMappings.TryGetValue(envKey, out var configKeys))
+ {
+ foreach (var configKey in configKeys)
+ {
+ yield return new KeyValuePair(configKey, envValue);
+ }
+ }
+ }
+
+ private static string StripQuotes(string? value)
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ return value ?? string.Empty;
+ }
+
+ if (value.StartsWith('"') && value.EndsWith('"') && value.Length >= 2)
+ {
+ return value[1..^1];
+ }
+
+ return value;
+ }
+}
diff --git a/allstarr/wwwroot/index.html b/allstarr/wwwroot/index.html
index c2a3722..b405d6e 100644
--- a/allstarr/wwwroot/index.html
+++ b/allstarr/wwwroot/index.html
@@ -12,7 +12,7 @@
⚠️ Configuration changed. Restart required to apply changes.
-
+
@@ -858,7 +858,7 @@
-
+
diff --git a/allstarr/wwwroot/js/api.js b/allstarr/wwwroot/js/api.js
index 8fbb495..67c0ec4 100644
--- a/allstarr/wwwroot/js/api.js
+++ b/allstarr/wwwroot/js/api.js
@@ -274,7 +274,7 @@ export async function restartContainer() {
return requestJson(
"/api/admin/restart",
{ method: "POST" },
- "Failed to restart container",
+ "Failed to restart Allstarr",
);
}
diff --git a/allstarr/wwwroot/js/operations.js b/allstarr/wwwroot/js/operations.js
index 3d3f926..9de2407 100644
--- a/allstarr/wwwroot/js/operations.js
+++ b/allstarr/wwwroot/js/operations.js
@@ -270,7 +270,7 @@ async function importEnv(event) {
const result = await runAction({
confirmMessage:
- "Import this .env file? This will replace your current configuration.\n\nA backup will be created automatically.\n\nYou will need to restart the container for changes to take effect.",
+ "Import this .env file? This will replace your current configuration.\n\nA backup will be created automatically.\n\nYou will need to restart Allstarr for changes to take effect.",
task: () => API.importEnv(file),
success: (data) => data.message,
error: (err) => err.message || "Failed to import .env file",
@@ -283,7 +283,7 @@ async function importEnv(event) {
async function restartContainer() {
if (
!confirm(
- "Restart the container to apply configuration changes?\n\nThe dashboard will be temporarily unavailable.",
+ "Restart Allstarr to reload /app/.env and apply configuration changes?\n\nThe dashboard will be temporarily unavailable.",
)
) {
return;
@@ -291,7 +291,7 @@ async function restartContainer() {
const result = await runAction({
task: () => API.restartContainer(),
- error: "Failed to restart container",
+ error: "Failed to restart Allstarr",
});
if (!result) {
@@ -301,7 +301,7 @@ async function restartContainer() {
document.getElementById("restart-overlay")?.classList.add("active");
const statusEl = document.getElementById("restart-status");
if (statusEl) {
- statusEl.textContent = "Stopping container...";
+ statusEl.textContent = "Restarting Allstarr...";
}
setTimeout(() => {