feat: implement URL plugins with table/row actions and prefill support

- Add URL plugin effects (register, export_url, clear_urls) in core
- Add PluginHostnameInfo, HostnameMetadata::Plugin, and plugin registration types
- Implement plugin URL table in web UI with tableAction button and rowAction overflow menus
- Thread urlPluginMetadata (packageId, hostId, interfaceId, internalPort) as prefill to actions
- Add prefill support to PackageActionData so metadata passes through form dialogs
- Add i18n translations for plugin error messages
- Clean up plugin URLs on package uninstall
This commit is contained in:
Aiden McClelland
2026-02-18 17:51:13 -07:00
parent dce975410f
commit 9c3053f103
53 changed files with 792 additions and 278 deletions

View File

@@ -154,11 +154,11 @@ export const addressHostToUrl = (
const effectiveScheme = hostname.ssl ? sslScheme : scheme
let host: string
if (hostname.metadata.kind === 'ipv6') {
host = IPV6_LINK_LOCAL.contains(hostname.host)
? `[${hostname.host}%${hostname.metadata.scopeId}]`
: `[${hostname.host}]`
host = IPV6_LINK_LOCAL.contains(hostname.hostname)
? `[${hostname.hostname}%${hostname.metadata.scopeId}]`
: `[${hostname.hostname}]`
} else {
host = hostname.host
host = hostname.hostname
}
let portStr = ''
if (hostname.port !== null) {
@@ -206,10 +206,10 @@ function filterRec(
(kind.has('ipv4') && h.metadata.kind === 'ipv4') ||
(kind.has('ipv6') && h.metadata.kind === 'ipv6') ||
(kind.has('localhost') &&
['localhost', '127.0.0.1', '::1'].includes(h.host)) ||
['localhost', '127.0.0.1', '::1'].includes(h.hostname)) ||
(kind.has('link-local') &&
h.metadata.kind === 'ipv6' &&
IPV6_LINK_LOCAL.contains(IpAddress.parse(h.host)))),
IPV6_LINK_LOCAL.contains(IpAddress.parse(h.hostname)))),
)
}
@@ -229,13 +229,13 @@ function enabledAddresses(addr: DerivedAddressInfo): HostnameInfo[] {
if (h.port === null) return true
const sa =
h.metadata.kind === 'ipv6'
? `[${h.host}]:${h.port}`
: `${h.host}:${h.port}`
? `[${h.hostname}]:${h.port}`
: `${h.hostname}:${h.port}`
return addr.enabled.includes(sa)
} else {
// Everything else: enabled by default, explicitly disabled via [host, port] tuple
// Everything else: enabled by default, explicitly disabled via [hostname, port] tuple
return !addr.disabled.some(
([host, port]) => host === h.host && port === (h.port ?? 0),
([hostname, port]) => hostname === h.hostname && port === (h.port ?? 0),
)
}
})