mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-02 10:52:22 +00:00
fix: prefill verified Hestia panel endpoint
This commit is contained in:
@@ -460,11 +460,21 @@ def candidate(vmid, record, footprint):
|
|||||||
{'kind': 'working_directory',
|
{'kind': 'working_directory',
|
||||||
'value': record.get('process_root', record['root'])}])
|
'value': record.get('process_root', record['root'])}])
|
||||||
ports = sorted(set(record.get('listening_ports') or []))
|
ports = sorted(set(record.get('listening_ports') or []))
|
||||||
|
# Hestia's control panel is a narrow, verified exception to generic port
|
||||||
|
# inference: its own executable serves the panel on fixed HTTPS port 8083.
|
||||||
|
# Keep this declaration on the exact runtime signature so an unrelated
|
||||||
|
# application merely listening on 8083 never receives an HTTPS guess.
|
||||||
|
hestia_panel = (record['name'] == 'Hestia'
|
||||||
|
and record['path'] == _HESTIA_NGINX
|
||||||
|
and record['root'] == _HESTIA_ROOT
|
||||||
|
and _HESTIA_PANEL_PORT in ports)
|
||||||
evidence.extend({'kind': 'tcp_listener', 'value': str(port)} for port in ports)
|
evidence.extend({'kind': 'tcp_listener', 'value': str(port)} for port in ports)
|
||||||
return {'slug': identity, 'discovery_id': identity, 'source': 'native_metadata',
|
return {'slug': identity, 'discovery_id': identity, 'source': 'native_metadata',
|
||||||
'name': record['name'], 'installed_version': version, 'evidence': evidence,
|
'name': record['name'], 'installed_version': version, 'evidence': evidence,
|
||||||
'installation_path': record['path'], 'default_ports': [],
|
'installation_path': record['path'],
|
||||||
|
'default_ports': [_HESTIA_PANEL_PORT] if hestia_panel else [],
|
||||||
'listening_ports': ports,
|
'listening_ports': ports,
|
||||||
|
**({'scheme': 'https'} if hestia_panel else {}),
|
||||||
'metadata_issue': metadata_issue,
|
'metadata_issue': metadata_issue,
|
||||||
'tracking_suggestion': ({'installed_via': 'manual', 'installed_version': version,
|
'tracking_suggestion': ({'installed_via': 'manual', 'installed_version': version,
|
||||||
'detected_version': version, 'detector_verified': True}
|
'detected_version': version, 'detector_verified': True}
|
||||||
|
|||||||
@@ -153,9 +153,18 @@ class DiscoveryFixtures(unittest.TestCase):
|
|||||||
self.assertEqual(item['name'], 'Hestia')
|
self.assertEqual(item['name'], 'Hestia')
|
||||||
self.assertEqual(item['installation_path'], hestia)
|
self.assertEqual(item['installation_path'], hestia)
|
||||||
self.assertEqual(item['listening_ports'], [8083])
|
self.assertEqual(item['listening_ports'], [8083])
|
||||||
|
self.assertEqual(item['default_ports'], [8083])
|
||||||
|
self.assertEqual(item['scheme'], 'https')
|
||||||
self.assertIn({'kind': 'working_directory', 'value': '/'}, item['evidence'])
|
self.assertIn({'kind': 'working_directory', 'value': '/'}, item['evidence'])
|
||||||
self.assertIn('metadata_limit', result['issues'])
|
self.assertIn('metadata_limit', result['issues'])
|
||||||
|
|
||||||
|
unrelated = native.candidate(101, {
|
||||||
|
'name': 'other-panel', 'path': '/opt/other-panel/server', 'root': '/opt/other-panel',
|
||||||
|
'process': '/opt/other-panel/server', 'listening_ports': [8083],
|
||||||
|
}, 'FILE\n')
|
||||||
|
self.assertEqual(unrelated['default_ports'], [])
|
||||||
|
self.assertNotIn('scheme', unrelated)
|
||||||
|
|
||||||
self.calls.clear()
|
self.calls.clear()
|
||||||
result = native.scan(101, self.runner(
|
result = native.scan(101, self.runner(
|
||||||
services='END\n', processes='LIMIT\n', ports='END\n'))
|
services='END\n', processes='LIMIT\n', ports='END\n'))
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const candidate = {
|
|||||||
evidence: [{kind: 'installation_file', value: '/opt/handmade/server'}], default_ports: [],
|
evidence: [{kind: 'installation_file', value: '/opt/handmade/server'}], default_ports: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
function fixture() {
|
function fixture({nativeCandidate = candidate} = {}) {
|
||||||
let cursor = 0, tree, props, effects = [], states = [], refs = [], effectStates = []
|
let cursor = 0, tree, props, effects = [], states = [], refs = [], effectStates = []
|
||||||
const calls = [], cachedWrites = []
|
const calls = [], cachedWrites = []
|
||||||
let native = {vmid: 101, status: 'not_scanned', candidates: [], stale: false}
|
let native = {vmid: 101, status: 'not_scanned', candidates: [], stale: false}
|
||||||
@@ -49,7 +49,7 @@ function fixture() {
|
|||||||
if (url.endsWith('/native-discovery')) {
|
if (url.endsWith('/native-discovery')) {
|
||||||
if (options.method === 'POST') {
|
if (options.method === 'POST') {
|
||||||
if (scanGate) await scanGate
|
if (scanGate) await scanGate
|
||||||
native = {...native, status: scanStatus, candidates: [candidate]}
|
native = {...native, status: scanStatus, candidates: [nativeCandidate]}
|
||||||
}
|
}
|
||||||
return structuredClone(native)
|
return structuredClone(native)
|
||||||
}
|
}
|
||||||
@@ -123,6 +123,16 @@ async function main() {
|
|||||||
assert.equal(payload.repo, '')
|
assert.equal(payload.repo, '')
|
||||||
assert.equal(payload.upstream_type, '')
|
assert.equal(payload.upstream_type, '')
|
||||||
|
|
||||||
|
const hestia = fixture({nativeCandidate: {
|
||||||
|
...candidate, name: 'Hestia', default_ports: [8083], scheme: 'https',
|
||||||
|
}})
|
||||||
|
hestia.render(); await hestia.settle()
|
||||||
|
await hestia.button('searchApplications').props.onClick(); hestia.render()
|
||||||
|
await hestia.button('registerButton').props.onClick(); hestia.render()
|
||||||
|
await hestia.button('saveButton').props.onClick(); hestia.render()
|
||||||
|
const hestiaPayload = JSON.parse(hestia.calls.find(c => /\/apps$/.test(c.url) && c.method === 'POST').body)
|
||||||
|
assert.deepEqual(hestiaPayload.ports, [{port: 8083, scheme: 'https', web_path: ''}],
|
||||||
|
'a verified Hestia panel pre-fills only its fixed HTTPS endpoint')
|
||||||
const partial = fixture()
|
const partial = fixture()
|
||||||
partial.render(); await partial.settle(); partial.setScanStatus('partial')
|
partial.render(); await partial.settle(); partial.setScanStatus('partial')
|
||||||
await partial.button('searchApplications').props.onClick(); partial.render()
|
await partial.button('searchApplications').props.onClick(); partial.render()
|
||||||
|
|||||||
Reference in New Issue
Block a user