fix: service name defs and ollama ui location

This commit is contained in:
Jake Turner 2026-02-01 05:46:23 +00:00
parent 4584844ca6
commit 31c671bdb5
15 changed files with 62 additions and 44 deletions

View File

@ -24,6 +24,7 @@ import type {
} from '../../types/benchmark.js' } from '../../types/benchmark.js'
import { randomUUID, createHmac } from 'node:crypto' import { randomUUID, createHmac } from 'node:crypto'
import { DockerService } from './docker_service.js' import { DockerService } from './docker_service.js'
import { SERVICE_NAMES } from '../../constants/service_names.js'
// HMAC secret for signing submissions to the benchmark repository // HMAC secret for signing submissions to the benchmark repository
// This provides basic protection against casual API abuse. // This provides basic protection against casual API abuse.
@ -421,7 +422,7 @@ export class BenchmarkService {
this._updateStatus('running_ai', 'Running AI benchmark...') this._updateStatus('running_ai', 'Running AI benchmark...')
const ollamaAPIURL = await this.dockerService.getServiceURL(DockerService.OLLAMA_SERVICE_NAME) const ollamaAPIURL = await this.dockerService.getServiceURL(SERVICE_NAMES.OLLAMA)
if (!ollamaAPIURL) { if (!ollamaAPIURL) {
throw new Error('AI Assistant service location could not be determined. Ensure AI Assistant is installed and running.') throw new Error('AI Assistant service location could not be determined. Ensure AI Assistant is installed and running.')
} }

View File

@ -6,18 +6,12 @@ import transmit from '@adonisjs/transmit/services/main'
import { doResumableDownloadWithRetry } from '../utils/downloads.js' import { doResumableDownloadWithRetry } from '../utils/downloads.js'
import { join } from 'path' import { join } from 'path'
import { ZIM_STORAGE_PATH } from '../utils/fs.js' import { ZIM_STORAGE_PATH } from '../utils/fs.js'
import { SERVICE_NAMES } from '../../constants/service_names.js'
@inject() @inject()
export class DockerService { export class DockerService {
public docker: Docker public docker: Docker
private activeInstallations: Set<string> = new Set() private activeInstallations: Set<string> = new Set()
public static KIWIX_SERVICE_NAME = 'nomad_kiwix_serve'
public static OLLAMA_SERVICE_NAME = 'nomad_ollama'
public static QDRANT_SERVICE_NAME = 'nomad_qdrant'
public static CYBERCHEF_SERVICE_NAME = 'nomad_cyberchef'
public static FLATNOTES_SERVICE_NAME = 'nomad_flatnotes'
public static KOLIBRI_SERVICE_NAME = 'nomad_kolibri'
public static BENCHMARK_SERVICE_NAME = 'nomad_benchmark'
public static NOMAD_NETWORK = 'project-nomad_default' public static NOMAD_NETWORK = 'project-nomad_default'
constructor() { constructor() {
@ -441,7 +435,7 @@ export class DockerService {
await new Promise((res) => this.docker.modem.followProgress(pullStream, res)) await new Promise((res) => this.docker.modem.followProgress(pullStream, res))
} }
if (service.service_name === DockerService.KIWIX_SERVICE_NAME) { if (service.service_name === SERVICE_NAMES.KIWIX) {
await this._runPreinstallActions__KiwixServe() await this._runPreinstallActions__KiwixServe()
this._broadcast( this._broadcast(
service.service_name, service.service_name,
@ -556,12 +550,12 @@ export class DockerService {
logger.info(`[DockerService] Kiwix Serve pre-install: Downloading ZIM file to ${filepath}`) logger.info(`[DockerService] Kiwix Serve pre-install: Downloading ZIM file to ${filepath}`)
this._broadcast( this._broadcast(
DockerService.KIWIX_SERVICE_NAME, SERVICE_NAMES.KIWIX,
'preinstall', 'preinstall',
`Running pre-install actions for Kiwix Serve...` `Running pre-install actions for Kiwix Serve...`
) )
this._broadcast( this._broadcast(
DockerService.KIWIX_SERVICE_NAME, SERVICE_NAMES.KIWIX,
'preinstall', 'preinstall',
`Downloading Wikipedia ZIM file from ${WIKIPEDIA_ZIM_URL}. This may take some time...` `Downloading Wikipedia ZIM file from ${WIKIPEDIA_ZIM_URL}. This may take some time...`
) )
@ -579,13 +573,13 @@ export class DockerService {
}) })
this._broadcast( this._broadcast(
DockerService.KIWIX_SERVICE_NAME, SERVICE_NAMES.KIWIX,
'preinstall', 'preinstall',
`Downloaded Wikipedia ZIM file to ${filepath}` `Downloaded Wikipedia ZIM file to ${filepath}`
) )
} catch (error) { } catch (error) {
this._broadcast( this._broadcast(
DockerService.KIWIX_SERVICE_NAME, SERVICE_NAMES.KIWIX,
'preinstall-error', 'preinstall-error',
`Failed to download Wikipedia ZIM file: ${error.message}` `Failed to download Wikipedia ZIM file: ${error.message}`
) )

View File

@ -1,6 +1,5 @@
import { inject } from '@adonisjs/core' import { inject } from '@adonisjs/core'
import { ChatRequest, Ollama } from 'ollama' import { ChatRequest, Ollama } from 'ollama'
import { DockerService } from './docker_service.js'
import { NomadOllamaModel } from '../../types/ollama.js' import { NomadOllamaModel } from '../../types/ollama.js'
import { FALLBACK_RECOMMENDED_OLLAMA_MODELS } from '../../constants/ollama.js' import { FALLBACK_RECOMMENDED_OLLAMA_MODELS } from '../../constants/ollama.js'
import fs from 'node:fs/promises' import fs from 'node:fs/promises'
@ -9,6 +8,7 @@ import logger from '@adonisjs/core/services/logger'
import axios from 'axios' import axios from 'axios'
import { DownloadModelJob } from '#jobs/download_model_job' import { DownloadModelJob } from '#jobs/download_model_job'
import { PassThrough } from 'node:stream' import { PassThrough } from 'node:stream'
import { SERVICE_NAMES } from '../../constants/service_names.js'
const NOMAD_MODELS_API_BASE_URL = 'https://api.projectnomad.us/api/v1/ollama/models' const NOMAD_MODELS_API_BASE_URL = 'https://api.projectnomad.us/api/v1/ollama/models'
const MODELS_CACHE_FILE = path.join(process.cwd(), 'storage', 'ollama-models-cache.json') const MODELS_CACHE_FILE = path.join(process.cwd(), 'storage', 'ollama-models-cache.json')
@ -25,7 +25,7 @@ export class OllamaService {
if (!this.ollamaInitPromise) { if (!this.ollamaInitPromise) {
this.ollamaInitPromise = (async () => { this.ollamaInitPromise = (async () => {
const dockerService = new (await import('./docker_service.js')).DockerService() const dockerService = new (await import('./docker_service.js')).DockerService()
const qdrantUrl = await dockerService.getServiceURL(DockerService.OLLAMA_SERVICE_NAME) const qdrantUrl = await dockerService.getServiceURL(SERVICE_NAMES.OLLAMA)
if (!qdrantUrl) { if (!qdrantUrl) {
throw new Error('Ollama service is not installed or running.') throw new Error('Ollama service is not installed or running.')
} }
@ -56,7 +56,7 @@ export class OllamaService {
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
try { try {
const dockerService = new (await import('./docker_service.js')).DockerService() const dockerService = new (await import('./docker_service.js')).DockerService()
const container = dockerService.docker.getContainer(DockerService.OLLAMA_SERVICE_NAME) const container = dockerService.docker.getContainer(SERVICE_NAMES.OLLAMA)
if (!container) { if (!container) {
logger.warn('[OllamaService] Ollama container is not running. Cannot download model.') logger.warn('[OllamaService] Ollama container is not running. Cannot download model.')
resolve({ resolve({
@ -242,7 +242,7 @@ export class OllamaService {
const dockerService = new (await import('./docker_service.js')).DockerService() const dockerService = new (await import('./docker_service.js')).DockerService()
const ollamAPIURL = await dockerService.getServiceURL(DockerService.OLLAMA_SERVICE_NAME) const ollamAPIURL = await dockerService.getServiceURL(SERVICE_NAMES.OLLAMA)
if (!ollamAPIURL) { if (!ollamAPIURL) {
logger.warn('[OllamaService] Ollama service is not running. Cannot download model.') logger.warn('[OllamaService] Ollama service is not running. Cannot download model.')
return { return {

View File

@ -9,6 +9,7 @@ import { PDFParse } from 'pdf-parse'
import { createWorker } from 'tesseract.js' import { createWorker } from 'tesseract.js'
import { fromBuffer } from 'pdf2pic' import { fromBuffer } from 'pdf2pic'
import { OllamaService } from './ollama_service.js' import { OllamaService } from './ollama_service.js'
import { SERVICE_NAMES } from '../../constants/service_names.js'
@inject() @inject()
export class RagService { export class RagService {
@ -26,7 +27,7 @@ export class RagService {
private async _initializeQdrantClient() { private async _initializeQdrantClient() {
if (!this.qdrantInitPromise) { if (!this.qdrantInitPromise) {
this.qdrantInitPromise = (async () => { this.qdrantInitPromise = (async () => {
const qdrantUrl = await this.dockerService.getServiceURL(DockerService.QDRANT_SERVICE_NAME) const qdrantUrl = await this.dockerService.getServiceURL(SERVICE_NAMES.QDRANT)
if (!qdrantUrl) { if (!qdrantUrl) {
throw new Error('Qdrant service is not installed or running.') throw new Error('Qdrant service is not installed or running.')
} }

View File

@ -26,6 +26,7 @@ import InstalledTier from '#models/installed_tier'
import WikipediaSelection from '#models/wikipedia_selection' import WikipediaSelection from '#models/wikipedia_selection'
import { RunDownloadJob } from '#jobs/run_download_job' import { RunDownloadJob } from '#jobs/run_download_job'
import { DownloadCollectionOperation, DownloadRemoteSuccessCallback } from '../../types/files.js' import { DownloadCollectionOperation, DownloadRemoteSuccessCallback } from '../../types/files.js'
import { SERVICE_NAMES } from '../../constants/service_names.js'
const ZIM_MIME_TYPES = ['application/x-zim', 'application/x-openzim', 'application/octet-stream'] const ZIM_MIME_TYPES = ['application/x-zim', 'application/x-openzim', 'application/octet-stream']
const CATEGORIES_URL = 'https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/master/collections/kiwix-categories.json' const CATEGORIES_URL = 'https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/master/collections/kiwix-categories.json'
@ -243,7 +244,7 @@ export class ZimService implements IZimService {
// Restart KIWIX container to pick up new ZIM file // Restart KIWIX container to pick up new ZIM file
if (restart) { if (restart) {
await this.dockerService await this.dockerService
.affectContainer(DockerService.KIWIX_SERVICE_NAME, 'restart') .affectContainer(SERVICE_NAMES.KIWIX, 'restart')
.catch((error) => { .catch((error) => {
logger.error(`[ZimService] Failed to restart KIWIX container:`, error) // Don't stop the download completion, just log the error. logger.error(`[ZimService] Failed to restart KIWIX container:`, error) // Don't stop the download completion, just log the error.
}) })
@ -434,7 +435,7 @@ export class ZimService implements IZimService {
// Restart Kiwix to reflect the change // Restart Kiwix to reflect the change
await this.dockerService await this.dockerService
.affectContainer(DockerService.KIWIX_SERVICE_NAME, 'restart') .affectContainer(SERVICE_NAMES.KIWIX, 'restart')
.catch((error) => { .catch((error) => {
logger.error(`[ZimService] Failed to restart Kiwix after Wikipedia removal:`, error) logger.error(`[ZimService] Failed to restart Kiwix after Wikipedia removal:`, error)
}) })

View File

@ -0,0 +1,8 @@
export const SERVICE_NAMES = {
KIWIX: 'nomad_kiwix_server',
OLLAMA: 'nomad_ollama',
QDRANT: 'nomad_qdrant',
CYBERCHEF: 'nomad_cyberchef',
FLATNOTES: 'nomad_flatnotes',
KOLIBRI: 'nomad_kolibri',
}

View File

@ -27,6 +27,7 @@ export default class extends BaseSchema {
UPDATE services SET UPDATE services SET
friendly_name = 'AI Assistant', friendly_name = 'AI Assistant',
powered_by = 'Ollama', powered_by = 'Ollama',
ui_location = '/chat',
display_order = 3, display_order = 3,
description = 'Local AI chat that runs entirely on your hardware - no internet required' description = 'Local AI chat that runs entirely on your hardware - no internet required'
WHERE service_name = 'nomad_ollama' WHERE service_name = 'nomad_ollama'

View File

@ -1,8 +1,8 @@
import Service from '#models/service' import Service from '#models/service'
import { DockerService } from '#services/docker_service'
import { BaseSeeder } from '@adonisjs/lucid/seeders' import { BaseSeeder } from '@adonisjs/lucid/seeders'
import { ModelAttributes } from '@adonisjs/lucid/types/model' import { ModelAttributes } from '@adonisjs/lucid/types/model'
import env from '#start/env' import env from '#start/env'
import { SERVICE_NAMES } from '../../constants/service_names.js'
export default class ServiceSeeder extends BaseSeeder { export default class ServiceSeeder extends BaseSeeder {
// Use environment variable with fallback to production default // Use environment variable with fallback to production default
@ -15,7 +15,7 @@ export default class ServiceSeeder extends BaseSeeder {
'created_at' | 'updated_at' | 'metadata' | 'id' 'created_at' | 'updated_at' | 'metadata' | 'id'
>[] = [ >[] = [
{ {
service_name: DockerService.KIWIX_SERVICE_NAME, service_name: SERVICE_NAMES.KIWIX,
friendly_name: 'Information Library', friendly_name: 'Information Library',
powered_by: 'Kiwix', powered_by: 'Kiwix',
display_order: 1, display_order: 1,
@ -39,7 +39,7 @@ export default class ServiceSeeder extends BaseSeeder {
depends_on: null, depends_on: null,
}, },
{ {
service_name: DockerService.QDRANT_SERVICE_NAME, service_name: SERVICE_NAMES.QDRANT,
friendly_name: 'Qdrant Vector Database', friendly_name: 'Qdrant Vector Database',
powered_by: null, powered_by: null,
display_order: 100, // Dependency service, not shown directly display_order: 100, // Dependency service, not shown directly
@ -62,7 +62,7 @@ export default class ServiceSeeder extends BaseSeeder {
depends_on: null, depends_on: null,
}, },
{ {
service_name: DockerService.OLLAMA_SERVICE_NAME, service_name: SERVICE_NAMES.OLLAMA,
friendly_name: 'AI Assistant', friendly_name: 'AI Assistant',
powered_by: 'Ollama', powered_by: 'Ollama',
display_order: 3, display_order: 3,
@ -78,14 +78,14 @@ export default class ServiceSeeder extends BaseSeeder {
}, },
ExposedPorts: { '11434/tcp': {} }, ExposedPorts: { '11434/tcp': {} },
}), }),
ui_location: null, ui_location: '/chat',
installed: false, installed: false,
installation_status: 'idle', installation_status: 'idle',
is_dependency_service: false, is_dependency_service: false,
depends_on: DockerService.QDRANT_SERVICE_NAME, depends_on: SERVICE_NAMES.QDRANT,
}, },
{ {
service_name: DockerService.CYBERCHEF_SERVICE_NAME, service_name: SERVICE_NAMES.CYBERCHEF,
friendly_name: 'Data Tools', friendly_name: 'Data Tools',
powered_by: 'CyberChef', powered_by: 'CyberChef',
display_order: 11, display_order: 11,
@ -107,7 +107,7 @@ export default class ServiceSeeder extends BaseSeeder {
depends_on: null, depends_on: null,
}, },
{ {
service_name: DockerService.FLATNOTES_SERVICE_NAME, service_name: SERVICE_NAMES.FLATNOTES,
friendly_name: 'Notes', friendly_name: 'Notes',
powered_by: 'FlatNotes', powered_by: 'FlatNotes',
display_order: 10, display_order: 10,
@ -131,7 +131,7 @@ export default class ServiceSeeder extends BaseSeeder {
depends_on: null, depends_on: null,
}, },
{ {
service_name: DockerService.KOLIBRI_SERVICE_NAME, service_name: SERVICE_NAMES.KOLIBRI,
friendly_name: 'Education Platform', friendly_name: 'Education Platform',
powered_by: 'Kolibri', powered_by: 'Kolibri',
display_order: 2, display_order: 2,

View File

@ -16,6 +16,12 @@ export function getServiceLink(ui_location: string): string {
// If it's a port number, return a link to the service on that port // If it's a port number, return a link to the service on that port
return `http://${window.location.hostname}:${parsedPort}`; return `http://${window.location.hostname}:${parsedPort}`;
} }
// Otherwise, treat it as a path
const pathPattern = /^\/.+/;
if (pathPattern.test(ui_location)) {
// If it starts with a slash, treat it as a full path
return ui_location;
}
return `/${ui_location}`; return `/${ui_location}`;
} }

View File

@ -18,6 +18,7 @@ import useInternetStatus from '~/hooks/useInternetStatus'
import { useSystemInfo } from '~/hooks/useSystemInfo' import { useSystemInfo } from '~/hooks/useSystemInfo'
import classNames from 'classnames' import classNames from 'classnames'
import { CuratedCategory, CategoryTier, CategoryResource } from '../../../types/downloads' import { CuratedCategory, CategoryTier, CategoryResource } from '../../../types/downloads'
import { SERVICE_NAMES } from '../../../constants/service_names'
// Capability definitions - maps user-friendly categories to services // Capability definitions - maps user-friendly categories to services
interface Capability { interface Capability {
@ -43,7 +44,7 @@ const CORE_CAPABILITIES: Capability[] = [
'WikiHow articles and tutorials', 'WikiHow articles and tutorials',
'Project Gutenberg books and literature', 'Project Gutenberg books and literature',
], ],
services: ['nomad_kiwix_serve'], services: [SERVICE_NAMES.KIWIX],
icon: 'IconBooks', icon: 'IconBooks',
}, },
{ {
@ -57,7 +58,7 @@ const CORE_CAPABILITIES: Capability[] = [
'Interactive exercises and quizzes', 'Interactive exercises and quizzes',
'Progress tracking for learners', 'Progress tracking for learners',
], ],
services: ['nomad_kolibri'], services: [SERVICE_NAMES.KOLIBRI],
icon: 'IconSchool', icon: 'IconSchool',
}, },
{ {
@ -71,7 +72,7 @@ const CORE_CAPABILITIES: Capability[] = [
'Ask questions, get help with writing, brainstorm ideas', 'Ask questions, get help with writing, brainstorm ideas',
'Runs on your own hardware with local AI models', 'Runs on your own hardware with local AI models',
], ],
services: ['nomad_ollama'], services: [SERVICE_NAMES.OLLAMA],
icon: 'IconRobot', icon: 'IconRobot',
}, },
] ]
@ -83,7 +84,7 @@ const ADDITIONAL_TOOLS: Capability[] = [
technicalName: 'FlatNotes', technicalName: 'FlatNotes',
description: 'Simple note-taking app with local storage', description: 'Simple note-taking app with local storage',
features: ['Markdown support', 'All notes stored locally', 'No account required'], features: ['Markdown support', 'All notes stored locally', 'No account required'],
services: ['nomad_flatnotes'], services: [SERVICE_NAMES.FLATNOTES],
icon: 'IconNotes', icon: 'IconNotes',
}, },
{ {
@ -96,7 +97,7 @@ const ADDITIONAL_TOOLS: Capability[] = [
'Encryption and hashing tools', 'Encryption and hashing tools',
'Data format conversion', 'Data format conversion',
], ],
services: ['nomad_cyberchef'], services: [SERVICE_NAMES.CYBERCHEF],
icon: 'IconChefHat', icon: 'IconChefHat',
}, },
] ]
@ -804,10 +805,10 @@ export default function EasySetupWizard(props: { system: { services: ServiceSlim
const renderStep3 = () => { const renderStep3 = () => {
// Check if AI or Information capabilities are selected OR already installed // Check if AI or Information capabilities are selected OR already installed
const isAiSelected = selectedServices.includes('nomad_ollama') || const isAiSelected = selectedServices.includes(SERVICE_NAMES.OLLAMA) ||
installedServices.some((s) => s.service_name === 'nomad_ollama') installedServices.some((s) => s.service_name === SERVICE_NAMES.OLLAMA)
const isInformationSelected = selectedServices.includes('nomad_kiwix') || const isInformationSelected = selectedServices.includes(SERVICE_NAMES.KIWIX) ||
installedServices.some((s) => s.service_name === 'nomad_kiwix') installedServices.some((s) => s.service_name === SERVICE_NAMES.KIWIX)
return ( return (
<div className="space-y-6"> <div className="space-y-6">

View File

@ -12,6 +12,7 @@ import AppLayout from '~/layouts/AppLayout'
import { getServiceLink } from '~/lib/navigation' import { getServiceLink } from '~/lib/navigation'
import { ServiceSlim } from '../../types/services' import { ServiceSlim } from '../../types/services'
import DynamicIcon, { DynamicIconName } from '~/components/DynamicIcon' import DynamicIcon, { DynamicIconName } from '~/components/DynamicIcon'
import { SERVICE_NAMES } from '../../constants/service_names'
// Maps is a Core Capability (display_order: 4) // Maps is a Core Capability (display_order: 4)
const MAPS_ITEM = { const MAPS_ITEM = {
@ -126,7 +127,7 @@ export default function Home(props: {
// Add system items // Add system items
items.push(...SYSTEM_ITEMS) items.push(...SYSTEM_ITEMS)
if (props.system.services.find((s) => s.service_name === 'nomad_ollama' && s.installed)) { if (props.system.services.find((s) => s.service_name === SERVICE_NAMES.OLLAMA && s.installed)) {
items.push(KNOWLEDGE_BASE_ITEM) items.push(KNOWLEDGE_BASE_ITEM)
} }

View File

@ -22,6 +22,7 @@ import { BenchmarkProgress, BenchmarkStatus } from '../../../types/benchmark'
import BenchmarkResult from '#models/benchmark_result' import BenchmarkResult from '#models/benchmark_result'
import api from '~/lib/api' import api from '~/lib/api'
import useServiceInstalledStatus from '~/hooks/useServiceInstalledStatus' import useServiceInstalledStatus from '~/hooks/useServiceInstalledStatus'
import { SERVICE_NAMES } from '../../../constants/service_names'
type BenchmarkProgressWithID = BenchmarkProgress & { benchmark_id: string } type BenchmarkProgressWithID = BenchmarkProgress & { benchmark_id: string }
@ -34,7 +35,7 @@ export default function BenchmarkPage(props: {
}) { }) {
const { subscribe } = useTransmit() const { subscribe } = useTransmit()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const aiInstalled = useServiceInstalledStatus('nomad_ollama') const aiInstalled = useServiceInstalledStatus(SERVICE_NAMES.OLLAMA)
const [progress, setProgress] = useState<BenchmarkProgressWithID | null>(null) const [progress, setProgress] = useState<BenchmarkProgressWithID | null>(null)
const [isRunning, setIsRunning] = useState(props.benchmark.status !== 'idle') const [isRunning, setIsRunning] = useState(props.benchmark.status !== 'idle')
const [showDetails, setShowDetails] = useState(false) const [showDetails, setShowDetails] = useState(false)

View File

@ -10,11 +10,12 @@ import api from '~/lib/api'
import { useModals } from '~/context/ModalContext' import { useModals } from '~/context/ModalContext'
import StyledModal from '~/components/StyledModal' import StyledModal from '~/components/StyledModal'
import { ModelResponse } from 'ollama' import { ModelResponse } from 'ollama'
import { SERVICE_NAMES } from '../../../constants/service_names'
export default function ModelsPage(props: { export default function ModelsPage(props: {
models: { availableModels: NomadOllamaModel[]; installedModels: ModelResponse[] } models: { availableModels: NomadOllamaModel[]; installedModels: ModelResponse[] }
}) { }) {
const { isInstalled } = useServiceInstalledStatus('nomad_ollama') const { isInstalled } = useServiceInstalledStatus(SERVICE_NAMES.OLLAMA)
const { addNotification } = useNotifications() const { addNotification } = useNotifications()
const { openModal, closeAllModals } = useModals() const { openModal, closeAllModals } = useModals()

View File

@ -9,11 +9,12 @@ import StyledModal from '~/components/StyledModal'
import useServiceInstalledStatus from '~/hooks/useServiceInstalledStatus' import useServiceInstalledStatus from '~/hooks/useServiceInstalledStatus'
import Alert from '~/components/Alert' import Alert from '~/components/Alert'
import { FileEntry } from '../../../../types/files' import { FileEntry } from '../../../../types/files'
import { SERVICE_NAMES } from '../../../../constants/service_names'
export default function ZimPage() { export default function ZimPage() {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const { openModal, closeAllModals } = useModals() const { openModal, closeAllModals } = useModals()
const { isInstalled } = useServiceInstalledStatus('nomad_kiwix_serve') const { isInstalled } = useServiceInstalledStatus(SERVICE_NAMES.KIWIX)
const { data, isLoading } = useQuery<FileEntry[]>({ const { data, isLoading } = useQuery<FileEntry[]>({
queryKey: ['zim-files'], queryKey: ['zim-files'],
queryFn: getFiles, queryFn: getFiles,

View File

@ -36,6 +36,7 @@ import {
} from '../../../../types/downloads' } from '../../../../types/downloads'
import useDownloads from '~/hooks/useDownloads' import useDownloads from '~/hooks/useDownloads'
import ActiveDownloads from '~/components/ActiveDownloads' import ActiveDownloads from '~/components/ActiveDownloads'
import { SERVICE_NAMES } from '../../../../constants/service_names'
const CURATED_COLLECTIONS_KEY = 'curated-zim-collections' const CURATED_COLLECTIONS_KEY = 'curated-zim-collections'
const CURATED_CATEGORIES_KEY = 'curated-categories' const CURATED_CATEGORIES_KEY = 'curated-categories'
@ -60,7 +61,7 @@ export default function ZimRemoteExplorer() {
const { openModal, closeAllModals } = useModals() const { openModal, closeAllModals } = useModals()
const { addNotification } = useNotifications() const { addNotification } = useNotifications()
const { isOnline } = useInternetStatus() const { isOnline } = useInternetStatus()
const { isInstalled } = useServiceInstalledStatus('nomad_kiwix_serve') const { isInstalled } = useServiceInstalledStatus(SERVICE_NAMES.KIWIX)
const { debounce } = useDebounce() const { debounce } = useDebounce()
const [query, setQuery] = useState('') const [query, setQuery] = useState('')