mirror of
https://github.com/Crosstalk-Solutions/project-nomad.git
synced 2026-03-28 03:29:25 +01:00
initial win 11 repo setup, user setting for URL for reverse proxy use, testing to commence
This commit is contained in:
parent
f004c002a7
commit
b9c8b5b76c
|
|
@ -3,7 +3,7 @@ import { SystemService } from '#services/system_service'
|
|||
import { SystemUpdateService } from '#services/system_update_service'
|
||||
import { ContainerRegistryService } from '#services/container_registry_service'
|
||||
import { CheckServiceUpdatesJob } from '#jobs/check_service_updates_job'
|
||||
import { affectServiceValidator, checkLatestVersionValidator, installServiceValidator, subscribeToReleaseNotesValidator, updateServiceValidator } from '#validators/system';
|
||||
import { affectServiceValidator, checkLatestVersionValidator, installServiceValidator, subscribeToReleaseNotesValidator, updateServiceLocationValidator, updateServiceValidator } from '#validators/system';
|
||||
import { inject } from '@adonisjs/core'
|
||||
import type { HttpContext } from '@adonisjs/core/http'
|
||||
|
||||
|
|
@ -162,6 +162,18 @@ export default class SystemController {
|
|||
}
|
||||
}
|
||||
|
||||
async updateServiceLocation({ request, response }: HttpContext) {
|
||||
const payload = await request.validateUsing(updateServiceLocationValidator)
|
||||
const Service = (await import('#models/service')).default
|
||||
const service = await Service.findBy('service_name', payload.service_name)
|
||||
if (!service) {
|
||||
return response.status(404).send({ error: 'Service not found' })
|
||||
}
|
||||
service.ui_location = payload.ui_location
|
||||
await service.save()
|
||||
return response.send({ success: true, message: 'Service location updated successfully' })
|
||||
}
|
||||
|
||||
private async getHostArch(): Promise<string> {
|
||||
try {
|
||||
const info = await this.dockerService.docker.info()
|
||||
|
|
|
|||
|
|
@ -31,3 +31,10 @@ export const updateServiceValidator = vine.compile(
|
|||
target_version: vine.string().trim(),
|
||||
})
|
||||
)
|
||||
|
||||
export const updateServiceLocationValidator = vine.compile(
|
||||
vine.object({
|
||||
service_name: vine.string().trim(),
|
||||
ui_location: vine.string().trim(),
|
||||
})
|
||||
)
|
||||
|
|
|
|||
44
admin/inertia/components/EditServiceUrlModal.tsx
Normal file
44
admin/inertia/components/EditServiceUrlModal.tsx
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { useState } from 'react'
|
||||
import { ServiceSlim } from '../../types/services'
|
||||
import StyledModal from './StyledModal'
|
||||
import Input from './inputs/Input'
|
||||
import { IconLink } from '@tabler/icons-react'
|
||||
|
||||
interface EditServiceUrlModalProps {
|
||||
record: ServiceSlim
|
||||
onCancel: () => void
|
||||
onSave: (uiLocation: string) => void
|
||||
}
|
||||
|
||||
export default function EditServiceUrlModal({ record, onCancel, onSave }: EditServiceUrlModalProps) {
|
||||
const [uiLocation, setUiLocation] = useState(record.ui_location || '')
|
||||
|
||||
return (
|
||||
<StyledModal
|
||||
title="Edit Service URL"
|
||||
onConfirm={() => onSave(uiLocation)}
|
||||
onCancel={onCancel}
|
||||
open={true}
|
||||
confirmText="Save"
|
||||
cancelText="Cancel"
|
||||
confirmVariant="primary"
|
||||
icon={<IconLink className="h-12 w-12 text-desert-green" />}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<p className="text-text-primary">
|
||||
Set the URL for <strong>{record.friendly_name || record.service_name}</strong>.
|
||||
</p>
|
||||
<p className="text-sm text-text-muted">
|
||||
Enter a full URL (e.g. <code className="bg-surface-secondary px-1 rounded">https://myservice.example.com</code>) to support reverse proxy setups, or a bare port number (e.g. <code className="bg-surface-secondary px-1 rounded">8080</code>) for direct access.
|
||||
</p>
|
||||
<Input
|
||||
name="ui_location"
|
||||
label="Service URL or Port"
|
||||
value={uiLocation}
|
||||
onChange={(e) => setUiLocation(e.target.value)}
|
||||
placeholder="https://myservice.example.com or 8080"
|
||||
/>
|
||||
</div>
|
||||
</StyledModal>
|
||||
)
|
||||
}
|
||||
|
|
@ -191,6 +191,16 @@ class API {
|
|||
})()
|
||||
}
|
||||
|
||||
async updateServiceLocation(serviceName: string, uiLocation: string) {
|
||||
return catchInternal(async () => {
|
||||
const response = await this.client.post<{ success: boolean; message: string }>(
|
||||
'/system/services/update-location',
|
||||
{ service_name: serviceName, ui_location: uiLocation }
|
||||
)
|
||||
return response.data
|
||||
})()
|
||||
}
|
||||
|
||||
async forceReinstallService(service_name: string) {
|
||||
return catchInternal(async () => {
|
||||
const response = await this.client.post<{ success: boolean; message: string }>(
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ export function getServiceLink(ui_location: string): string {
|
|||
// If it fails, it means it's not a valid URL
|
||||
}
|
||||
|
||||
// Check if the ui location is a port number
|
||||
// Check if the ui location is a port number (strict: must be only digits)
|
||||
const parsedPort = parseInt(ui_location, 10);
|
||||
if (!isNaN(parsedPort)) {
|
||||
if (!isNaN(parsedPort) && String(parsedPort) === ui_location.trim()) {
|
||||
// If it's a port number, return a link to the service on that port
|
||||
return `http://${window.location.hostname}:${parsedPort}`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { useTransmit } from 'react-adonis-transmit'
|
|||
import { BROADCAST_CHANNELS } from '../../../constants/broadcast'
|
||||
import { IconArrowUp, IconCheck, IconDownload } from '@tabler/icons-react'
|
||||
import UpdateServiceModal from '~/components/UpdateServiceModal'
|
||||
import EditServiceUrlModal from '~/components/EditServiceUrlModal'
|
||||
|
||||
function extractTag(containerImage: string): string {
|
||||
if (!containerImage) return ''
|
||||
|
|
@ -199,6 +200,29 @@ export default function SettingsPage(props: { system: { services: ServiceSlim[]
|
|||
)
|
||||
}
|
||||
|
||||
function handleEditUrl(record: ServiceSlim) {
|
||||
openModal(
|
||||
<EditServiceUrlModal
|
||||
record={record}
|
||||
onCancel={closeAllModals}
|
||||
onSave={async (uiLocation: string) => {
|
||||
closeAllModals()
|
||||
try {
|
||||
const response = await api.updateServiceLocation(record.service_name, uiLocation)
|
||||
if (!response?.success) {
|
||||
throw new Error(response?.message || 'Update failed')
|
||||
}
|
||||
window.location.reload()
|
||||
} catch (error: any) {
|
||||
console.error(`Error updating service URL for ${record.service_name}:`, error)
|
||||
showError(`Failed to update service URL: ${error.message || 'Unknown error'}`)
|
||||
}
|
||||
}}
|
||||
/>,
|
||||
`${record.service_name}-edit-url-modal`
|
||||
)
|
||||
}
|
||||
|
||||
const AppActions = ({ record }: { record: ServiceSlim }) => {
|
||||
const ForceReinstallButton = () => (
|
||||
<StyledButton
|
||||
|
|
@ -258,6 +282,12 @@ export default function SettingsPage(props: { system: { services: ServiceSlim[]
|
|||
>
|
||||
Open
|
||||
</StyledButton>
|
||||
<StyledButton
|
||||
icon={'IconEdit'}
|
||||
onClick={() => handleEditUrl(record)}
|
||||
>
|
||||
Edit URL
|
||||
</StyledButton>
|
||||
{record.available_update_version && (
|
||||
<StyledButton
|
||||
icon="IconArrowUp"
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ router
|
|||
router.post('/services/check-updates', [SystemController, 'checkServiceUpdates'])
|
||||
router.get('/services/:name/available-versions', [SystemController, 'getAvailableVersions'])
|
||||
router.post('/services/update', [SystemController, 'updateService'])
|
||||
router.post('/services/update-location', [SystemController, 'updateServiceLocation'])
|
||||
router.post('/subscribe-release-notes', [SystemController, 'subscribeToReleaseNotes'])
|
||||
router.get('/latest-version', [SystemController, 'checkLatestVersion'])
|
||||
router.post('/update', [SystemController, 'requestSystemUpdate'])
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user