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 { SystemUpdateService } from '#services/system_update_service'
|
||||||
import { ContainerRegistryService } from '#services/container_registry_service'
|
import { ContainerRegistryService } from '#services/container_registry_service'
|
||||||
import { CheckServiceUpdatesJob } from '#jobs/check_service_updates_job'
|
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 { inject } from '@adonisjs/core'
|
||||||
import type { HttpContext } from '@adonisjs/core/http'
|
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> {
|
private async getHostArch(): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const info = await this.dockerService.docker.info()
|
const info = await this.dockerService.docker.info()
|
||||||
|
|
|
||||||
|
|
@ -31,3 +31,10 @@ export const updateServiceValidator = vine.compile(
|
||||||
target_version: vine.string().trim(),
|
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) {
|
async forceReinstallService(service_name: string) {
|
||||||
return catchInternal(async () => {
|
return catchInternal(async () => {
|
||||||
const response = await this.client.post<{ success: boolean; message: string }>(
|
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
|
// 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);
|
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
|
// 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}`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import { useTransmit } from 'react-adonis-transmit'
|
||||||
import { BROADCAST_CHANNELS } from '../../../constants/broadcast'
|
import { BROADCAST_CHANNELS } from '../../../constants/broadcast'
|
||||||
import { IconArrowUp, IconCheck, IconDownload } from '@tabler/icons-react'
|
import { IconArrowUp, IconCheck, IconDownload } from '@tabler/icons-react'
|
||||||
import UpdateServiceModal from '~/components/UpdateServiceModal'
|
import UpdateServiceModal from '~/components/UpdateServiceModal'
|
||||||
|
import EditServiceUrlModal from '~/components/EditServiceUrlModal'
|
||||||
|
|
||||||
function extractTag(containerImage: string): string {
|
function extractTag(containerImage: string): string {
|
||||||
if (!containerImage) return ''
|
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 AppActions = ({ record }: { record: ServiceSlim }) => {
|
||||||
const ForceReinstallButton = () => (
|
const ForceReinstallButton = () => (
|
||||||
<StyledButton
|
<StyledButton
|
||||||
|
|
@ -258,6 +282,12 @@ export default function SettingsPage(props: { system: { services: ServiceSlim[]
|
||||||
>
|
>
|
||||||
Open
|
Open
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
|
<StyledButton
|
||||||
|
icon={'IconEdit'}
|
||||||
|
onClick={() => handleEditUrl(record)}
|
||||||
|
>
|
||||||
|
Edit URL
|
||||||
|
</StyledButton>
|
||||||
{record.available_update_version && (
|
{record.available_update_version && (
|
||||||
<StyledButton
|
<StyledButton
|
||||||
icon="IconArrowUp"
|
icon="IconArrowUp"
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,7 @@ router
|
||||||
router.post('/services/check-updates', [SystemController, 'checkServiceUpdates'])
|
router.post('/services/check-updates', [SystemController, 'checkServiceUpdates'])
|
||||||
router.get('/services/:name/available-versions', [SystemController, 'getAvailableVersions'])
|
router.get('/services/:name/available-versions', [SystemController, 'getAvailableVersions'])
|
||||||
router.post('/services/update', [SystemController, 'updateService'])
|
router.post('/services/update', [SystemController, 'updateService'])
|
||||||
|
router.post('/services/update-location', [SystemController, 'updateServiceLocation'])
|
||||||
router.post('/subscribe-release-notes', [SystemController, 'subscribeToReleaseNotes'])
|
router.post('/subscribe-release-notes', [SystemController, 'subscribeToReleaseNotes'])
|
||||||
router.get('/latest-version', [SystemController, 'checkLatestVersion'])
|
router.get('/latest-version', [SystemController, 'checkLatestVersion'])
|
||||||
router.post('/update', [SystemController, 'requestSystemUpdate'])
|
router.post('/update', [SystemController, 'requestSystemUpdate'])
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user