mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Add container restart capability from admin UI
- Added POST /api/admin/restart endpoint using Docker socket - Mounts Docker socket read-only in docker-compose.yml - Admin UI now has working 'Restart Container' button - Auto-reloads page after container comes back up - Falls back to manual restart instructions if socket unavailable
This commit is contained in:
@@ -502,6 +502,92 @@ public class AdminController : ControllerBase
|
||||
return Ok(new { message = "Cache cleared", filesDeleted = clearedFiles });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restart the allstarr container to apply configuration changes
|
||||
/// </summary>
|
||||
[HttpPost("restart")]
|
||||
public async Task<IActionResult> RestartContainer()
|
||||
{
|
||||
_logger.LogInformation("Container restart requested from admin UI");
|
||||
|
||||
try
|
||||
{
|
||||
// Use Docker socket to restart the container
|
||||
var socketPath = "/var/run/docker.sock";
|
||||
|
||||
if (!System.IO.File.Exists(socketPath))
|
||||
{
|
||||
_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"
|
||||
});
|
||||
}
|
||||
|
||||
// Get container ID from hostname (Docker sets hostname to container ID by default)
|
||||
// Or use the well-known container name
|
||||
var containerId = Environment.MachineName;
|
||||
var containerName = "allstarr";
|
||||
|
||||
_logger.LogInformation("Attempting to restart container {ContainerId} / {ContainerName}", containerId, containerName);
|
||||
|
||||
// Create Unix socket HTTP client
|
||||
var handler = new SocketsHttpHandler
|
||||
{
|
||||
ConnectCallback = async (context, cancellationToken) =>
|
||||
{
|
||||
var socket = new System.Net.Sockets.Socket(
|
||||
System.Net.Sockets.AddressFamily.Unix,
|
||||
System.Net.Sockets.SocketType.Stream,
|
||||
System.Net.Sockets.ProtocolType.Unspecified);
|
||||
|
||||
var endpoint = new System.Net.Sockets.UnixDomainSocketEndPoint(socketPath);
|
||||
await socket.ConnectAsync(endpoint, cancellationToken);
|
||||
|
||||
return new System.Net.Sockets.NetworkStream(socket, ownsSocket: true);
|
||||
}
|
||||
};
|
||||
|
||||
using var dockerClient = new HttpClient(handler)
|
||||
{
|
||||
BaseAddress = new Uri("http://localhost")
|
||||
};
|
||||
|
||||
// Try to restart by container name first, then by ID
|
||||
var response = await dockerClient.PostAsync($"/containers/{containerName}/restart?t=5", null);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
// Try by container ID
|
||||
response = await dockerClient.PostAsync($"/containers/{containerId}/restart?t=5", null);
|
||||
}
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
_logger.LogInformation("Container restart initiated successfully");
|
||||
return Ok(new { message = "Restarting container...", success = true });
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorBody = await response.Content.ReadAsStringAsync();
|
||||
_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"
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error restarting container");
|
||||
return StatusCode(500, new {
|
||||
error = "Failed to restart container",
|
||||
details = ex.Message,
|
||||
message = "Please restart manually: docker-compose restart allstarr"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize cookie date to current date if cookie exists but date is not set
|
||||
/// </summary>
|
||||
|
||||
@@ -1146,8 +1146,57 @@
|
||||
}
|
||||
}
|
||||
|
||||
function restartContainer() {
|
||||
alert('To apply configuration changes, please restart the container manually via Dockge or docker-compose:\n\ndocker-compose restart allstarr\n\nor use Dockge UI to restart the stack.');
|
||||
async function restartContainer() {
|
||||
if (!confirm('Restart the container to apply configuration changes?\n\nThe dashboard will be temporarily unavailable.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
showToast('Restarting container...', 'success');
|
||||
const res = await fetch('/api/admin/restart', { method: 'POST' });
|
||||
const data = await res.json();
|
||||
|
||||
if (res.ok) {
|
||||
showToast('Container restarting... Page will reload shortly.', 'success');
|
||||
// Wait a bit then start checking if the server is back
|
||||
setTimeout(() => {
|
||||
checkServerAndReload();
|
||||
}, 3000);
|
||||
} else {
|
||||
showToast(data.message || data.error || 'Failed to restart', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('Failed to restart container', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function checkServerAndReload() {
|
||||
let attempts = 0;
|
||||
const maxAttempts = 30; // Try for 30 seconds
|
||||
|
||||
const checkHealth = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/admin/status', {
|
||||
method: 'GET',
|
||||
cache: 'no-store'
|
||||
});
|
||||
if (res.ok) {
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// Server still restarting
|
||||
}
|
||||
|
||||
attempts++;
|
||||
if (attempts < maxAttempts) {
|
||||
setTimeout(checkHealth, 1000);
|
||||
} else {
|
||||
showToast('Server may still be restarting. Please refresh manually.', 'warning');
|
||||
}
|
||||
};
|
||||
|
||||
checkHealth();
|
||||
}
|
||||
|
||||
function openAddPlaylist() {
|
||||
|
||||
Reference in New Issue
Block a user