mirror of
https://github.com/Crosstalk-Solutions/project-nomad.git
synced 2026-04-05 00:06:17 +02:00
feat(System): check internet status on backend and allow custom test url
This commit is contained in:
parent
7029e1ea81
commit
a2206b8c13
|
|
@ -11,6 +11,10 @@ export default class SystemController {
|
||||||
private dockerService: DockerService
|
private dockerService: DockerService
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
|
async getInternetStatus({ }: HttpContext) {
|
||||||
|
return await this.systemService.getInternetStatus();
|
||||||
|
}
|
||||||
|
|
||||||
async getSystemInfo({ }: HttpContext) {
|
async getSystemInfo({ }: HttpContext) {
|
||||||
return await this.systemService.getSystemInfo();
|
return await this.systemService.getSystemInfo();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ import { NomadDiskInfo, NomadDiskInfoRaw, SystemInformationResponse } from '../.
|
||||||
import { readFileSync } from 'fs'
|
import { readFileSync } from 'fs'
|
||||||
import path, { join } from 'path'
|
import path, { join } from 'path'
|
||||||
import { getAllFilesystems, getFile } from '../utils/fs.js'
|
import { getAllFilesystems, getFile } from '../utils/fs.js'
|
||||||
|
import axios from 'axios'
|
||||||
|
import env from '#start/env'
|
||||||
|
|
||||||
@inject()
|
@inject()
|
||||||
export class SystemService {
|
export class SystemService {
|
||||||
|
|
@ -16,6 +18,45 @@ export class SystemService {
|
||||||
|
|
||||||
constructor(private dockerService: DockerService) {}
|
constructor(private dockerService: DockerService) {}
|
||||||
|
|
||||||
|
async getInternetStatus(): Promise<boolean> {
|
||||||
|
const DEFAULT_TEST_URL = 'https://1.1.1.1/cdn-cgi/trace'
|
||||||
|
const MAX_ATTEMPTS = 3
|
||||||
|
|
||||||
|
let testUrl = DEFAULT_TEST_URL
|
||||||
|
let customTestUrl = env.get('INTERNET_STATUS_TEST_URL')?.trim()
|
||||||
|
|
||||||
|
// check that customTestUrl is a valid URL, if provided
|
||||||
|
if (customTestUrl && customTestUrl !== '') {
|
||||||
|
try {
|
||||||
|
new URL(customTestUrl)
|
||||||
|
testUrl = customTestUrl
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(
|
||||||
|
`Invalid INTERNET_STATUS_TEST_URL: ${customTestUrl}. Falling back to default URL.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
||||||
|
try {
|
||||||
|
const res = await axios.get(testUrl, { timeout: 5000 })
|
||||||
|
return res.status === 200
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(
|
||||||
|
`Internet status check attempt ${attempt}/${MAX_ATTEMPTS} failed: ${error instanceof Error ? error.message : error}`
|
||||||
|
)
|
||||||
|
|
||||||
|
if (attempt < MAX_ATTEMPTS) {
|
||||||
|
// delay before next attempt
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.warn('All internet status check attempts failed.')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
async getServices({ installedOnly = true }: { installedOnly?: boolean }): Promise<ServiceSlim[]> {
|
async getServices({ installedOnly = true }: { installedOnly?: boolean }): Promise<ServiceSlim[]> {
|
||||||
const query = Service.query()
|
const query = Service.query()
|
||||||
.orderBy('friendly_name', 'asc')
|
.orderBy('friendly_name', 'asc')
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
// Helper hook to check internet connection status
|
// Helper hook to check internet connection status
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { testInternetConnection } from '~/lib/util';
|
import api from '~/lib/api';
|
||||||
|
|
||||||
const useInternetStatus = () => {
|
const useInternetStatus = () => {
|
||||||
const [isOnline, setIsOnline] = useState<boolean>(false);
|
const [isOnline, setIsOnline] = useState<boolean>(false);
|
||||||
const { data } = useQuery<boolean>({
|
const { data } = useQuery<boolean>({
|
||||||
queryKey: ['internetStatus'],
|
queryKey: ['internetStatus'],
|
||||||
queryFn: testInternetConnection,
|
queryFn: async () => (await api.getInternetStatus()) ?? false,
|
||||||
refetchOnWindowFocus: false, // Don't refetch on window focus
|
refetchOnWindowFocus: false, // Don't refetch on window focus
|
||||||
refetchOnReconnect: false, // Refetch when the browser reconnects
|
refetchOnReconnect: true, // Refetch when the browser reconnects
|
||||||
refetchOnMount: false, // Don't refetch when the component mounts
|
refetchOnMount: false, // Don't refetch when the component mounts
|
||||||
retry: 2, // Retry up to 2 times on failure
|
retry: 0, // Retry already handled in backend
|
||||||
staleTime: 1000 * 60 * 10, // Data is fresh for 10 minutes
|
staleTime: 1000 * 60 * 5, // Data is fresh for 5 minutes
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update the online status when data changes
|
// Update the online status when data changes
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,13 @@ class API {
|
||||||
})()
|
})()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getInternetStatus() {
|
||||||
|
return catchInternal(async () => {
|
||||||
|
const response = await this.client.get<boolean>('/system/internet-status')
|
||||||
|
return response.data
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
|
||||||
async getSystemInfo() {
|
async getSystemInfo() {
|
||||||
return catchInternal(async () => {
|
return catchInternal(async () => {
|
||||||
const response = await this.client.get<SystemInformationResponse>('/system/info')
|
const response = await this.client.get<SystemInformationResponse>('/system/info')
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
export function capitalizeFirstLetter(str?: string | null): string {
|
export function capitalizeFirstLetter(str?: string | null): string {
|
||||||
if (!str) return ''
|
if (!str) return ''
|
||||||
|
|
@ -14,18 +13,6 @@ export function formatBytes(bytes: number, decimals = 2): string {
|
||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function testInternetConnection(): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
const response = await axios.get('https://1.1.1.1/cdn-cgi/trace', {
|
|
||||||
timeout: 5000,
|
|
||||||
})
|
|
||||||
return response.status === 200
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error testing internet connection:', error)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateRandomString(length: number): string {
|
export function generateRandomString(length: number): string {
|
||||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||||||
let result = ''
|
let result = ''
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ export default await Env.create(new URL('../', import.meta.url), {
|
||||||
HOST: Env.schema.string({ format: 'host' }),
|
HOST: Env.schema.string({ format: 'host' }),
|
||||||
URL: Env.schema.string(),
|
URL: Env.schema.string(),
|
||||||
LOG_LEVEL: Env.schema.string(),
|
LOG_LEVEL: Env.schema.string(),
|
||||||
|
INTERNET_STATUS_TEST_URL: Env.schema.string.optional(),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|----------------------------------------------------------
|
|----------------------------------------------------------
|
||||||
|
|
@ -26,7 +27,6 @@ export default await Env.create(new URL('../', import.meta.url), {
|
||||||
*/
|
*/
|
||||||
//SESSION_DRIVER: Env.schema.enum(['cookie', 'memory'] as const),
|
//SESSION_DRIVER: Env.schema.enum(['cookie', 'memory'] as const),
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|----------------------------------------------------------
|
|----------------------------------------------------------
|
||||||
| Variables for configuring the database package
|
| Variables for configuring the database package
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ router
|
||||||
router
|
router
|
||||||
.group(() => {
|
.group(() => {
|
||||||
router.get('/info', [SystemController, 'getSystemInfo'])
|
router.get('/info', [SystemController, 'getSystemInfo'])
|
||||||
|
router.get('/internet-status', [SystemController, 'getInternetStatus'])
|
||||||
router.get('/services', [SystemController, 'getServices'])
|
router.get('/services', [SystemController, 'getServices'])
|
||||||
router.post('/services/affect', [SystemController, 'affectService'])
|
router.post('/services/affect', [SystemController, 'affectService'])
|
||||||
router.post('/services/install', [SystemController, 'installService'])
|
router.post('/services/install', [SystemController, 'installService'])
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user