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 });
|
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>
|
/// <summary>
|
||||||
/// Initialize cookie date to current date if cookie exists but date is not set
|
/// Initialize cookie date to current date if cookie exists but date is not set
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1146,8 +1146,57 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function restartContainer() {
|
async 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.');
|
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() {
|
function openAddPlaylist() {
|
||||||
|
|||||||
@@ -111,6 +111,8 @@ services:
|
|||||||
- ${DOWNLOAD_PATH:-./downloads}:/app/downloads
|
- ${DOWNLOAD_PATH:-./downloads}:/app/downloads
|
||||||
- ${KEPT_PATH:-./kept}:/app/kept
|
- ${KEPT_PATH:-./kept}:/app/kept
|
||||||
- ${CACHE_PATH:-./cache}:/app/cache
|
- ${CACHE_PATH:-./cache}:/app/cache
|
||||||
|
# Docker socket for self-restart capability (admin UI only)
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
allstarr-network:
|
allstarr-network:
|
||||||
|
|||||||
Reference in New Issue
Block a user