Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions @tailwind-shared/theme-variants.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
--header-background-color: #1c1b1b;
--header-gradient-start: rgba(28, 27, 27, 0);
--header-gradient-end: rgba(28, 27, 27, 0.7);
--ui-border-muted: hsl(240 5% 20%);
--color-border: #383735;
--color-alpha: #ff8c2f;
--color-beta: #1c1b1b;
Expand All @@ -28,7 +27,6 @@
--header-background-color: #f2f2f2;
--header-gradient-start: rgba(242, 242, 242, 0);
--header-gradient-end: rgba(242, 242, 242, 0.7);
--ui-border-muted: hsl(240 5.9% 90%);
--color-border: #e0e0e0;
--color-alpha: #ff8c2f;
--color-beta: #f2f2f2;
Expand All @@ -43,7 +41,6 @@
--header-background-color: #1c1b1b;
--header-gradient-start: rgba(28, 27, 27, 0);
--header-gradient-end: rgba(28, 27, 27, 0.7);
--ui-border-muted: hsl(240 5% 25%);
--color-border: #383735;
--color-alpha: #ff8c2f;
--color-beta: #383735;
Expand All @@ -58,7 +55,6 @@
--header-background-color: #f2f2f2;
--header-gradient-start: rgba(242, 242, 242, 0);
--header-gradient-end: rgba(242, 242, 242, 0.7);
--ui-border-muted: hsl(210 40% 80%);
--color-border: #5a8bb8;
--color-alpha: #ff8c2f;
--color-beta: #e7f2f8;
Expand All @@ -68,7 +64,6 @@

/* Dark Mode Overrides */
.dark {
--ui-border-muted: hsl(240 5% 20%);
--color-border: #383735;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Menu="ManagementAccess:99"
Title="Unraid API Status"
Icon="icon-u-globe"
Tag="globe"
---
<!-- API Status Manager -->
<unraid-api-status-manager></unraid-api-status-manager>

<!-- end unraid-api section -->
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Menu="ManagementAccess:100"
Title="Unraid API"
Title="Unraid API Settings"
Icon="icon-u-globe"
Tag="globe"
---
Expand Down Expand Up @@ -596,8 +596,10 @@ $(function() {
_(Unraid API extra origins)_:
_(Connect Remote Access)_:
_(GraphQL API Developer Sandbox)_:
_(OIDC Configuration)_:

</div>

<!-- start unraid-api section -->
<unraid-connect-settings></unraid-connect-settings>
<!-- end unraid-api section -->

Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ function response_complete($httpcode, $result, $cli_success_msg='') {
'start',
'restart',
'stop',
'status',
'report',
'wanip'
];
Expand Down Expand Up @@ -68,7 +69,12 @@ function response_complete($httpcode, $result, $cli_success_msg='') {
response_complete(200, array('result' => $output), $output);
break;
case 'restart':
exec('unraid-api restart 2>/dev/null', $output, $retval);
exec('/etc/rc.d/rc.unraid-api restart 2>&1', $output, $retval);
$output = implode(PHP_EOL, $output);
response_complete(200, array('success' => ($retval === 0), 'result' => $output, 'error' => ($retval !== 0 ? $output : null)), $output);
break;
case 'status':
exec('unraid-api status 2>&1', $output, $retval);
$output = implode(PHP_EOL, $output);
response_complete(200, array('result' => $output), $output);
break;
Expand Down
2 changes: 2 additions & 0 deletions web/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ declare module 'vue' {
ApiKeyCreate: typeof import('./src/components/ApiKey/ApiKeyCreate.vue')['default']
ApiKeyManager: typeof import('./src/components/ApiKey/ApiKeyManager.vue')['default']
'ApiKeyPage.standalone': typeof import('./src/components/ApiKeyPage.standalone.vue')['default']
ApiStatus: typeof import('./src/components/ApiStatus/ApiStatus.vue')['default']
'ApiStatus.standalone': typeof import('./src/components/ApiStatus/ApiStatus.standalone.vue')['default']
'Auth.standalone': typeof import('./src/components/Auth.standalone.vue')['default']
Avatar: typeof import('./src/components/Brand/Avatar.vue')['default']
Beta: typeof import('./src/components/UserProfile/Beta.vue')['default']
Expand Down
5 changes: 5 additions & 0 deletions web/src/components/ApiStatus/ApiStatus.standalone.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script lang="ts">
import ApiStatus from '@/components/ApiStatus/ApiStatus.vue';

export default ApiStatus;
</script>
139 changes: 139 additions & 0 deletions web/src/components/ApiStatus/ApiStatus.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<script lang="ts" setup>
import { onMounted, ref } from 'vue';

import { WebguiUnraidApiCommand } from '~/composables/services/webgui';
import { useServerStore } from '~/store/server';

const serverStore = useServerStore();

const apiStatus = ref<string>('');
const isRunning = ref<boolean>(false);
const isLoading = ref<boolean>(false);
const isRestarting = ref<boolean>(false);
const statusMessage = ref<string>('');
const messageType = ref<'success' | 'error' | 'info' | ''>('');

const checkStatus = async () => {
isLoading.value = true;
statusMessage.value = '';
try {
const response = await WebguiUnraidApiCommand({
csrf_token: serverStore.csrf,
command: 'status',
});

if (response?.result) {
apiStatus.value = response.result;
isRunning.value =
response.result.includes('running') ||
response.result.includes('active') ||
response.result.includes('status : online');
}
} catch (error) {
console.error('Failed to get API status:', error);
apiStatus.value = 'Error fetching status';
isRunning.value = false;
statusMessage.value = 'Failed to fetch API status';
messageType.value = 'error';
} finally {
isLoading.value = false;
}
};

const restartApi = async () => {
const confirmed = window.confirm(
'Are you sure you want to restart the Unraid API service? This will temporarily interrupt API connections.'
);

if (!confirmed) return;

isRestarting.value = true;
statusMessage.value = 'Restarting API service...';
messageType.value = 'info';

try {
const response = await WebguiUnraidApiCommand({
csrf_token: serverStore.csrf,
command: 'restart',
});

if (response?.success) {
statusMessage.value = 'API service restart initiated. Please wait a few seconds.';
messageType.value = 'success';
setTimeout(() => {
checkStatus();
}, 3000);
} else {
statusMessage.value = response?.error || 'Failed to restart API service';
messageType.value = 'error';
}
} catch (error) {
console.error('Failed to restart API:', error);
statusMessage.value = 'Failed to restart API service';
messageType.value = 'error';
} finally {
isRestarting.value = false;
}
};

onMounted(() => {
checkStatus();
});
</script>

<template>
<div class="bg-muted border-muted my-4 rounded-lg border p-6">
<div class="mb-4">
<h3 class="mb-2 text-lg font-semibold">API Service Status</h3>
<div class="flex items-center gap-2 text-sm">
<span class="font-medium">Status:</span>
<span :class="['font-semibold', isRunning ? 'text-green-500' : 'text-orange-500']">
{{ isLoading ? 'Loading...' : isRunning ? 'Running' : 'Not Running' }}
</span>
</div>
</div>

<div class="my-4">
<pre
class="max-h-52 overflow-y-auto rounded bg-black p-4 font-mono text-xs break-words whitespace-pre-wrap text-white"
>{{ apiStatus }}</pre
>
</div>

<div
v-if="statusMessage"
:class="[
'my-4 rounded px-4 py-3 text-sm',
messageType === 'success' && 'bg-green-500 text-white',
messageType === 'error' && 'bg-red-500 text-white',
messageType === 'info' && 'bg-blue-500 text-white',
]"
>
{{ statusMessage }}
</div>

<div class="mt-4 flex gap-4">
<button
@click="checkStatus"
:disabled="isLoading"
class="bg-secondary hover:bg-secondary/80 text-secondary-foreground rounded px-4 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-60"
>
{{ isLoading ? 'Refreshing...' : 'Refresh Status' }}
</button>
<button
@click="restartApi"
:disabled="isRestarting"
class="bg-destructive hover:bg-destructive/90 text-destructive-foreground rounded px-4 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-60"
>
{{ isRestarting ? 'Restarting...' : 'Restart API' }}
</button>
</div>

<div class="border-muted mt-6 border-t pt-4">
<p class="text-muted-foreground text-sm">
View the current status of the Unraid API service and restart if needed. Use this to debug API
connection issues.
</p>
</div>
</div>
</template>
5 changes: 5 additions & 0 deletions web/src/components/Wrapper/component-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,9 @@ export const componentMappings: ComponentMapping[] = [
selector: 'unraid-test-theme-switcher',
appId: 'test-theme-switcher',
},
{
component: defineAsyncComponent(() => import('../ApiStatus/ApiStatus.standalone.vue')),
selector: 'unraid-api-status-manager',
appId: 'api-status-manager',
},
];
2 changes: 1 addition & 1 deletion web/src/composables/services/webgui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const WebguiState = request.url('/plugins/dynamix.my.servers/data/server-
*/
export interface WebguiUnraidApiCommandPayload {
csrf_token: string;
command: 'report' | 'restart' | 'start';
command: 'report' | 'restart' | 'start' | 'status';
param1?: '-v' | '-vv';
}
export const WebguiUnraidApiCommand = async (payload: WebguiUnraidApiCommandPayload) => {
Expand Down
Loading