diff --git a/.env.example b/.env.example index 1cf90ef9b..c36af831a 100644 --- a/.env.example +++ b/.env.example @@ -10,4 +10,10 @@ NEXTAUTH_SECRET="anything" # Disable analytics NEXT_PUBLIC_DISABLE_ANALYTICS="true" -DEFAULT_COLOR_SCHEME="light" \ No newline at end of file +DEFAULT_COLOR_SCHEME="light" + +# Unraid API Configuration +UNRAID_HOST=192.168.10.20 +UNRAID_API_KEY=your-api-key-here +UNRAID_USE_SSL=false +UNRAID_PORT=80 \ No newline at end of file diff --git a/docker-compose.unraid.yml b/docker-compose.unraid.yml index b4ea4b89b..856fe1402 100644 --- a/docker-compose.unraid.yml +++ b/docker-compose.unraid.yml @@ -1,12 +1,10 @@ version: "3.8" services: - homarr-unraid: + unmarr: image: git.xtrm-lab.org/jazzymc/homarr:latest - container_name: homarr-unraid-ui + container_name: unmarr restart: unless-stopped - ports: - - "7576:7575" environment: # Unraid API Configuration - UNRAID_HOST=192.168.10.20 @@ -17,18 +15,28 @@ services: - TZ=Europe/Sofia - DATABASE_URL=file:/data/db.sqlite - AUTH_TRUST_HOST=true - - NEXTAUTH_URL=http://192.168.10.20:7576 + - NEXTAUTH_URL=https://unmarr.xtrm-lab.org - NEXTAUTH_SECRET=${NEXTAUTH_SECRET:-changeme} volumes: - - /mnt/user/appdata/homarr-unraid/data:/data - - /mnt/user/appdata/homarr-unraid/configs:/app/data/configs + - /mnt/user/appdata/unmarr/data:/data + - /mnt/user/appdata/unmarr/configs:/app/data/configs networks: - - unraid-net + dockerproxy: + ipv4_address: 172.18.0.5 labels: + # Traefik + - "traefik.enable=true" + - "traefik.constraint=valid" + - "traefik.http.routers.unmarr.rule=Host(`unmarr.xtrm-lab.org`)" + - "traefik.http.routers.unmarr.entrypoints=https" + - "traefik.http.routers.unmarr.tls=true" + - "traefik.http.routers.unmarr.tls.certresolver=cloudflare" + - "traefik.http.services.unmarr.loadbalancer.server.port=7575" + # Unraid - "net.unraid.docker.managed=true" - "net.unraid.docker.icon=https://homarr.dev/img/logo.png" - - "net.unraid.docker.webui=http://[IP]:[PORT:7576]" + - "net.unraid.docker.webui=https://unmarr.xtrm-lab.org" networks: - unraid-net: - driver: bridge + dockerproxy: + external: true diff --git a/scripts/build-and-push.sh b/scripts/build-and-push.sh index 20922a0c1..de2745410 100755 --- a/scripts/build-and-push.sh +++ b/scripts/build-and-push.sh @@ -32,14 +32,7 @@ echo "Image: ${REGISTRY}/${IMAGE_NAME}:${TAG}" echo "" echo "To deploy on Unraid:" echo "1. SSH to Unraid: ssh root@192.168.10.20 -p 422" -echo "2. Create directory: mkdir -p /mnt/user/appdata/homarr-unraid/{data,configs}" -echo "3. Pull image: docker pull ${REGISTRY}/${IMAGE_NAME}:${TAG}" -echo "4. Run container:" -echo " docker run -d \\" -echo " --name homarr-unraid-ui \\" -echo " -p 7576:7575 \\" -echo " -e UNRAID_HOST=192.168.10.20 \\" -echo " -e UNRAID_API_KEY=YOUR_API_KEY \\" -echo " -v /mnt/user/appdata/homarr-unraid/data:/data \\" -echo " -v /mnt/user/appdata/homarr-unraid/configs:/app/data/configs \\" -echo " ${REGISTRY}/${IMAGE_NAME}:${TAG}" +echo "2. Create directory: mkdir -p /mnt/user/appdata/unmarr/{data,configs}" +echo "3. Copy docker-compose.unraid.yml to Unraid" +echo "4. Set UNRAID_API_KEY in environment" +echo "5. Run: docker compose -f docker-compose.unraid.yml up -d" diff --git a/src/components/Unraid/Dashboard/ArrayCard.tsx b/src/components/Unraid/Dashboard/ArrayCard.tsx index 6a4f23b95..ffbf3f6a8 100644 --- a/src/components/Unraid/Dashboard/ArrayCard.tsx +++ b/src/components/Unraid/Dashboard/ArrayCard.tsx @@ -21,7 +21,7 @@ import { IconPlayerPlay, IconPlayerStop, IconTemperature, - IconHardDrive, + IconDisc, } from '@tabler/icons-react'; import type { UnraidArray, ArrayDisk, ArrayState } from '~/lib/unraid/types'; @@ -81,7 +81,7 @@ function DiskRow({ disk }: { disk: ArrayDisk }) { - + {disk.name} diff --git a/src/components/Unraid/Dashboard/DockerCard.tsx b/src/components/Unraid/Dashboard/DockerCard.tsx index d28097ef7..d64a883d1 100644 --- a/src/components/Unraid/Dashboard/DockerCard.tsx +++ b/src/components/Unraid/Dashboard/DockerCard.tsx @@ -179,7 +179,7 @@ export function DockerCard({ - + {sortedContainers.length === 0 ? ( @@ -197,7 +197,7 @@ export function DockerCard({ )) )} - + ); } diff --git a/src/components/Unraid/Dashboard/VmsCard.tsx b/src/components/Unraid/Dashboard/VmsCard.tsx index eee9e2a69..e2e227ef1 100644 --- a/src/components/Unraid/Dashboard/VmsCard.tsx +++ b/src/components/Unraid/Dashboard/VmsCard.tsx @@ -248,7 +248,7 @@ export function VmsCard({ - + {sortedVms.length === 0 ? ( @@ -269,7 +269,7 @@ export function VmsCard({ )) )} - + ); } diff --git a/src/lib/unraid/client.ts b/src/lib/unraid/client.ts index 88c6f5a56..84fbf9441 100644 --- a/src/lib/unraid/client.ts +++ b/src/lib/unraid/client.ts @@ -22,6 +22,7 @@ import type { UnraidApiResponse, UnraidArray, UpsDevice, + User, VirtualMachine, } from './types'; @@ -255,6 +256,38 @@ export class UnraidClient { return data.customization; } + // ============================================================================ + // USERS (STUB - Unraid GraphQL API does not expose user management yet) + // ============================================================================ + + async getUsers(): Promise { + // TODO: Implement when Unraid GraphQL API supports user queries + return []; + } + + // ============================================================================ + // SYSLOG (STUB - Unraid GraphQL API does not expose syslog yet) + // ============================================================================ + + async getSyslog(lines = 100): Promise { + // TODO: Implement when Unraid GraphQL API supports syslog queries + return []; + } + + // ============================================================================ + // NOTIFICATION ACTIONS + // ============================================================================ + + async markNotificationRead(id: string): Promise<{ success: boolean }> { + // TODO: Implement when Unraid GraphQL API supports marking notifications read + return { success: true }; + } + + async markAllNotificationsRead(): Promise<{ success: boolean }> { + // TODO: Implement when Unraid GraphQL API supports bulk notification actions + return { success: true }; + } + // ============================================================================ // DASHBOARD (COMPOSITE QUERY) // ============================================================================ diff --git a/src/lib/unraid/types.ts b/src/lib/unraid/types.ts index 4b7600f03..2cd15cb9b 100644 --- a/src/lib/unraid/types.ts +++ b/src/lib/unraid/types.ts @@ -278,6 +278,8 @@ export interface Vms { // SHARES TYPES // ============================================================================ +export type ShareSecurityLevel = 'PUBLIC' | 'SECURE' | 'PRIVATE'; + export interface Share { name: string; comment: string; @@ -292,7 +294,17 @@ export interface Share { splitLevel: number; allocator: 'highwater' | 'fillup' | 'mostfree'; export: string; - security: string; + security: ShareSecurityLevel; +} + +// ============================================================================ +// USER TYPES +// ============================================================================ + +export interface User { + id: string; + name: string; + description?: string; } // ============================================================================ diff --git a/src/pages/unraid/array/index.tsx b/src/pages/unraid/array/index.tsx index 13a5a8b76..fdd72ded3 100644 --- a/src/pages/unraid/array/index.tsx +++ b/src/pages/unraid/array/index.tsx @@ -31,7 +31,7 @@ import { import { notifications } from '@mantine/notifications'; import { IconDatabase, - IconHardDrive, + IconDisc, IconTemperature, IconPlayerPlay, IconPlayerStop, @@ -93,7 +93,7 @@ function DiskDetailsRow({ disk }: { disk: ArrayDisk }) { variant="light" color={disk.spunDown ? 'gray' : getStatusColor(disk.status)} > - +
@@ -530,7 +530,7 @@ export default function ArrayPage() { }> Parity ({array.parities.length}) - }> + }> Data Disks ({array.disks.length}) }> @@ -647,7 +647,7 @@ export default function ArrayPage() { - + {device.name} diff --git a/src/pages/unraid/settings/identification.tsx b/src/pages/unraid/settings/identification.tsx index 5b67cc9a7..cc7df46ae 100644 --- a/src/pages/unraid/settings/identification.tsx +++ b/src/pages/unraid/settings/identification.tsx @@ -59,8 +59,8 @@ export default function IdentificationSettingsPage() { if (vars && !form.isTouched()) { form.setValues({ name: vars.name || '', - description: vars.comment || '', - model: vars.flashProduct || '', + description: vars.description || '', + model: vars.model || '', timezone: vars.timezone || '', }); } @@ -155,7 +155,7 @@ export default function IdentificationSettingsPage() { Linux Kernel - {info?.versions.linux || 'Unknown'} + {info?.os.kernel || 'Unknown'} @@ -166,7 +166,7 @@ export default function IdentificationSettingsPage() { CPU - {info?.cpu.model || 'Unknown'} + {info?.cpu.brand || 'Unknown'} @@ -177,7 +177,7 @@ export default function IdentificationSettingsPage() { Motherboard - {info?.motherboard?.product || 'Unknown'} + {info?.baseboard?.model || 'Unknown'} @@ -207,19 +207,19 @@ export default function IdentificationSettingsPage() { - {/* Flash Drive */} + {/* Server Model */} - Flash Drive + Hardware - Product + Model - {vars?.flashProduct || 'Unknown'} + {vars?.model || 'Unknown'} @@ -227,10 +227,10 @@ export default function IdentificationSettingsPage() { - Vendor + Protocol - {vars?.flashVendor || 'Unknown'} + {vars?.protocol || 'Unknown'} @@ -238,10 +238,10 @@ export default function IdentificationSettingsPage() { - GUID + Port - {vars?.flashGuid || 'Unknown'} + {vars?.port || 'Unknown'} diff --git a/src/pages/unraid/settings/notifications.tsx b/src/pages/unraid/settings/notifications.tsx index ee254d7e5..625398a22 100644 --- a/src/pages/unraid/settings/notifications.tsx +++ b/src/pages/unraid/settings/notifications.tsx @@ -74,8 +74,8 @@ function getImportanceIcon(importance: NotificationImportance) { } } -function formatDate(timestamp: number): string { - return new Date(timestamp * 1000).toLocaleDateString('en-US', { +function formatDate(timestamp: string): string { + return new Date(timestamp).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', diff --git a/src/pages/unraid/tools/diagnostics.tsx b/src/pages/unraid/tools/diagnostics.tsx index e28b188f3..b53c6481f 100644 --- a/src/pages/unraid/tools/diagnostics.tsx +++ b/src/pages/unraid/tools/diagnostics.tsx @@ -289,7 +289,7 @@ export default function DiagnosticsPage() { Linux Kernel - {info?.versions.linux || 'Unknown'} + {info?.os.kernel || 'Unknown'} @@ -309,7 +309,7 @@ export default function DiagnosticsPage() { CPU - {info?.cpu.model || 'Unknown'} + {info?.cpu.brand || 'Unknown'} diff --git a/src/pages/unraid/tools/syslog.tsx b/src/pages/unraid/tools/syslog.tsx index 89cf2dd95..b0d96159d 100644 --- a/src/pages/unraid/tools/syslog.tsx +++ b/src/pages/unraid/tools/syslog.tsx @@ -108,7 +108,7 @@ export default function SyslogPage() { error, refetch, } = api.unraid.syslog.useQuery( - { lines: 500 }, + undefined, { refetchInterval: autoScroll ? 5000 : false, } @@ -116,7 +116,7 @@ export default function SyslogPage() { useEffect(() => { if (syslog) { - setLines(syslog.lines || []); + setLines(syslog || []); } }, [syslog]); diff --git a/src/server/api/routers/unraid/router.ts b/src/server/api/routers/unraid/router.ts index 43a3e0dbd..806c998bf 100644 --- a/src/server/api/routers/unraid/router.ts +++ b/src/server/api/routers/unraid/router.ts @@ -337,6 +337,52 @@ export const unraidRouter = createTRPCRouter({ return client.archiveNotification(input.id); }), + // ============================================================================ + // USERS + // ============================================================================ + + /** + * Get users + */ + users: protectedProcedure.query(async () => { + const client = getUnraidClient(); + return client.getUsers(); + }), + + // ============================================================================ + // SYSLOG + // ============================================================================ + + /** + * Get syslog lines + */ + syslog: protectedProcedure.query(async () => { + const client = getUnraidClient(); + return client.getSyslog(); + }), + + // ============================================================================ + // NOTIFICATION ACTIONS + // ============================================================================ + + /** + * Mark notification as read + */ + markNotificationRead: protectedProcedure + .input(notificationIdSchema) + .mutation(async ({ input }) => { + const client = getUnraidClient(); + return client.markNotificationRead(input.id); + }), + + /** + * Mark all notifications as read + */ + markAllNotificationsRead: protectedProcedure.mutation(async () => { + const client = getUnraidClient(); + return client.markAllNotificationsRead(); + }), + // ============================================================================ // SERVICES // ============================================================================ diff --git a/src/styles/orchis/theme.ts b/src/styles/orchis/theme.ts index fcbb1d273..89aa9e505 100644 --- a/src/styles/orchis/theme.ts +++ b/src/styles/orchis/theme.ts @@ -4,7 +4,7 @@ * https://github.com/vinceliuice/Orchis-theme */ -import { MantineProviderProps, MantineThemeColors, Tuple } from '@mantine/core'; +import { MantineThemeOverride, Tuple } from '@mantine/core'; // ============================================================================ // ORCHIS COLOR PALETTE @@ -159,7 +159,7 @@ const orchisOrange: Tuple = [ // MANTINE THEME CONFIGURATION // ============================================================================ -export const orchisColors: MantineThemeColors = { +export const orchisColors: Record> = { // Override default colors with Orchis palette blue: orchisBlue, gray: orchisGrey, @@ -176,7 +176,7 @@ export const orchisColors: MantineThemeColors = { surface: orchisGrey, }; -export const orchisTheme: MantineProviderProps['theme'] = { +export const orchisTheme: MantineThemeOverride = { // Color configuration colors: orchisColors, primaryColor: 'blue', @@ -192,21 +192,21 @@ export const orchisTheme: MantineProviderProps['theme'] = { // Border radius - Orchis uses 12px as default radius: { - xs: 4, - sm: 6, - md: 12, // Default Orchis corner radius - lg: 18, // Window radius - xl: 24, + xs: '4px', + sm: '6px', + md: '12px', // Default Orchis corner radius + lg: '18px', // Window radius + xl: '24px', }, defaultRadius: 'md', // Spacing - Orchis base is 6px spacing: { - xs: 4, - sm: 6, - md: 12, - lg: 18, - xl: 24, + xs: '4px', + sm: '6px', + md: '12px', + lg: '18px', + xl: '24px', }, // Shadows - Material Design elevation