mirror of
https://github.com/MizuchiLabs/mantrae.git
synced 2026-01-07 06:50:09 -06:00
fix conversion
This commit is contained in:
@@ -81,10 +81,8 @@ func UpsertMiddleware(a *config.App) http.HandlerFunc {
|
||||
existingConfig.Config.TCPMiddlewares = make(map[string]*runtime.TCPMiddlewareInfo)
|
||||
}
|
||||
|
||||
// Ensure name has @http suffix
|
||||
if !strings.HasSuffix(params.Name, "@http") {
|
||||
params.Name = fmt.Sprintf("%s@http", strings.Split(params.Name, "@")[0])
|
||||
}
|
||||
// Ensure name has no @
|
||||
params.Name = strings.Split(params.Name, "@")[0]
|
||||
|
||||
// Update configuration based on type
|
||||
switch params.Protocol {
|
||||
@@ -141,11 +139,6 @@ func DeleteMiddleware(a *config.App) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure name has @http suffix for consistency
|
||||
if !strings.HasSuffix(mwName, "@http") {
|
||||
mwName = fmt.Sprintf("%s@http", strings.Split(mwName, "@")[0])
|
||||
}
|
||||
|
||||
existingConfig, err := q.GetLocalTraefikConfig(r.Context(), profileID)
|
||||
if err != nil {
|
||||
http.Error(
|
||||
|
||||
@@ -81,20 +81,36 @@ func UpsertRouter(a *config.App) http.HandlerFunc {
|
||||
existingConfig.Config.UDPServices = make(map[string]*runtime.UDPServiceInfo)
|
||||
}
|
||||
|
||||
// Ensure name has @http suffix
|
||||
if !strings.HasSuffix(params.Name, "@http") {
|
||||
params.Name = fmt.Sprintf("%s@http", strings.Split(params.Name, "@")[0])
|
||||
}
|
||||
// Ensure name has no @
|
||||
params.Name = strings.Split(params.Name, "@")[0]
|
||||
|
||||
// Update configuration based on type
|
||||
switch params.Protocol {
|
||||
case "http":
|
||||
if !strings.HasSuffix(params.Router.Service, "@http") {
|
||||
params.Router.Service = fmt.Sprintf(
|
||||
"%s@http",
|
||||
strings.Split(params.Router.Service, "@")[0],
|
||||
)
|
||||
}
|
||||
existingConfig.Config.Routers[params.Name] = params.Router
|
||||
existingConfig.Config.Services[params.Name] = params.Service
|
||||
case "tcp":
|
||||
if !strings.HasSuffix(params.TCPRouter.Service, "@http") {
|
||||
params.TCPRouter.Service = fmt.Sprintf(
|
||||
"%s@http",
|
||||
strings.Split(params.TCPRouter.Service, "@")[0],
|
||||
)
|
||||
}
|
||||
existingConfig.Config.TCPRouters[params.Name] = params.TCPRouter
|
||||
existingConfig.Config.TCPServices[params.Name] = params.TCPService
|
||||
case "udp":
|
||||
if !strings.HasSuffix(params.UDPRouter.Service, "@http") {
|
||||
params.UDPRouter.Service = fmt.Sprintf(
|
||||
"%s@http",
|
||||
strings.Split(params.UDPRouter.Service, "@")[0],
|
||||
)
|
||||
}
|
||||
existingConfig.Config.UDPRouters[params.Name] = params.UDPRouter
|
||||
existingConfig.Config.UDPServices[params.Name] = params.UDPService
|
||||
default:
|
||||
@@ -142,12 +158,6 @@ func DeleteRouter(a *config.App) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure name has @http suffix
|
||||
if !strings.HasSuffix(routerName, "@http") {
|
||||
http.Error(w, "Invalid router provider", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
existingConfig, err := q.GetLocalTraefikConfig(r.Context(), profileID)
|
||||
if err != nil {
|
||||
http.Error(
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/MizuchiLabs/mantrae/internal/config"
|
||||
"github.com/MizuchiLabs/mantrae/internal/db"
|
||||
"github.com/MizuchiLabs/mantrae/internal/source"
|
||||
"github.com/MizuchiLabs/mantrae/internal/traefik"
|
||||
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
||||
)
|
||||
|
||||
@@ -57,81 +58,31 @@ func PublishTraefikConfig(a *config.App) http.HandlerFunc {
|
||||
UDPServices: make(map[string]*runtime.UDPServiceInfo),
|
||||
}
|
||||
|
||||
localT, err := q.GetLocalTraefikConfig(r.Context(), profile.ID)
|
||||
local, err := q.GetLocalTraefikConfig(r.Context(), profile.ID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
agentT, err := q.GetAgentTraefikConfigs(r.Context(), profile.ID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// Merge configurations from each agent
|
||||
for _, a := range agentT {
|
||||
if a.Config == nil {
|
||||
continue
|
||||
}
|
||||
if a.Config.Routers != nil {
|
||||
for k, v := range a.Config.Routers {
|
||||
mergedConfig.Routers[k] = v
|
||||
}
|
||||
}
|
||||
if a.Config.Services != nil {
|
||||
for k, v := range a.Config.Services {
|
||||
mergedConfig.Services[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if localT.Config == nil {
|
||||
if local.Config == nil {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
// Overlay local config to ensure it takes precedence
|
||||
if localT.Config.Routers != nil {
|
||||
for k, v := range localT.Config.Routers {
|
||||
mergedConfig.Routers[k] = v
|
||||
}
|
||||
agents, err := q.GetAgentTraefikConfigs(r.Context(), profile.ID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if localT.Config.Middlewares != nil {
|
||||
for k, v := range localT.Config.Middlewares {
|
||||
mergedConfig.Middlewares[k] = v
|
||||
}
|
||||
}
|
||||
if localT.Config.Services != nil {
|
||||
for k, v := range localT.Config.Services {
|
||||
mergedConfig.Services[k] = v
|
||||
}
|
||||
}
|
||||
if localT.Config.TCPRouters != nil {
|
||||
for k, v := range localT.Config.TCPRouters {
|
||||
mergedConfig.TCPRouters[k] = v
|
||||
}
|
||||
}
|
||||
if localT.Config.TCPMiddlewares != nil {
|
||||
for k, v := range localT.Config.TCPMiddlewares {
|
||||
mergedConfig.TCPMiddlewares[k] = v
|
||||
}
|
||||
}
|
||||
if localT.Config.TCPServices != nil {
|
||||
for k, v := range localT.Config.TCPServices {
|
||||
mergedConfig.TCPServices[k] = v
|
||||
}
|
||||
}
|
||||
if localT.Config.UDPRouters != nil {
|
||||
for k, v := range localT.Config.UDPRouters {
|
||||
mergedConfig.UDPRouters[k] = v
|
||||
}
|
||||
}
|
||||
if localT.Config.UDPServices != nil {
|
||||
for k, v := range localT.Config.UDPServices {
|
||||
mergedConfig.UDPServices[k] = v
|
||||
}
|
||||
// Merge configurations (prefer local)
|
||||
for _, agent := range agents {
|
||||
mergedConfig = traefik.MergeConfigs(mergedConfig, agent.Config)
|
||||
}
|
||||
mergedConfig = traefik.MergeConfigs(mergedConfig, local.Config)
|
||||
|
||||
// Convert to dynamic
|
||||
dynamic := traefik.ConvertToDynamicConfig(mergedConfig)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(mergedConfig)
|
||||
json.NewEncoder(w).Encode(dynamic)
|
||||
}
|
||||
}
|
||||
|
||||
161
internal/traefik/convert.go
Normal file
161
internal/traefik/convert.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package traefik
|
||||
|
||||
import (
|
||||
"github.com/MizuchiLabs/mantrae/internal/db"
|
||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
||||
)
|
||||
|
||||
func ConvertToDynamicConfig(rc *db.TraefikConfiguration) *dynamic.Configuration {
|
||||
dc := &dynamic.Configuration{}
|
||||
|
||||
// Only create HTTP config if there are HTTP components
|
||||
if len(rc.Routers) > 0 || len(rc.Middlewares) > 0 || len(rc.Services) > 0 {
|
||||
dc.HTTP = &dynamic.HTTPConfiguration{
|
||||
Routers: make(map[string]*dynamic.Router),
|
||||
Middlewares: make(map[string]*dynamic.Middleware),
|
||||
Services: make(map[string]*dynamic.Service),
|
||||
}
|
||||
|
||||
for name, router := range rc.Routers {
|
||||
dc.HTTP.Routers[name] = router.Router
|
||||
}
|
||||
for name, service := range rc.Services {
|
||||
dc.HTTP.Services[name] = service.Service
|
||||
}
|
||||
for name, middleware := range rc.Middlewares {
|
||||
dc.HTTP.Middlewares[name] = middleware.Middleware
|
||||
}
|
||||
}
|
||||
|
||||
// Only create TCP config if there are TCP components
|
||||
if len(rc.TCPRouters) > 0 || len(rc.TCPMiddlewares) > 0 || len(rc.TCPServices) > 0 {
|
||||
dc.TCP = &dynamic.TCPConfiguration{
|
||||
Routers: make(map[string]*dynamic.TCPRouter),
|
||||
Middlewares: make(map[string]*dynamic.TCPMiddleware),
|
||||
Services: make(map[string]*dynamic.TCPService),
|
||||
}
|
||||
|
||||
for name, router := range rc.TCPRouters {
|
||||
dc.TCP.Routers[name] = router.TCPRouter
|
||||
}
|
||||
for name, service := range rc.TCPServices {
|
||||
dc.TCP.Services[name] = service.TCPService
|
||||
}
|
||||
for name, middleware := range rc.TCPMiddlewares {
|
||||
dc.TCP.Middlewares[name] = middleware.TCPMiddleware
|
||||
}
|
||||
}
|
||||
|
||||
// Only create UDP config if there are UDP components
|
||||
if len(rc.UDPRouters) > 0 || len(rc.UDPServices) > 0 {
|
||||
dc.UDP = &dynamic.UDPConfiguration{
|
||||
Routers: make(map[string]*dynamic.UDPRouter),
|
||||
Services: make(map[string]*dynamic.UDPService),
|
||||
}
|
||||
|
||||
for name, router := range rc.UDPRouters {
|
||||
dc.UDP.Routers[name] = router.UDPRouter
|
||||
}
|
||||
for name, service := range rc.UDPServices {
|
||||
dc.UDP.Services[name] = service.UDPService
|
||||
}
|
||||
}
|
||||
|
||||
return dc
|
||||
}
|
||||
|
||||
func MergeConfigs(base, overlay *db.TraefikConfiguration) *db.TraefikConfiguration {
|
||||
if overlay == nil {
|
||||
return base
|
||||
}
|
||||
|
||||
// Merge HTTP components
|
||||
mergeRouters(base.Routers, overlay.Routers)
|
||||
mergeMiddlewares(base.Middlewares, overlay.Middlewares)
|
||||
mergeServices(base.Services, overlay.Services)
|
||||
|
||||
// Merge TCP components
|
||||
mergeTCPRouters(base.TCPRouters, overlay.TCPRouters)
|
||||
mergeTCPMiddlewares(base.TCPMiddlewares, overlay.TCPMiddlewares)
|
||||
mergeTCPServices(base.TCPServices, overlay.TCPServices)
|
||||
|
||||
// Merge UDP components
|
||||
mergeUDPRouters(base.UDPRouters, overlay.UDPRouters)
|
||||
mergeUDPServices(base.UDPServices, overlay.UDPServices)
|
||||
|
||||
return base
|
||||
}
|
||||
|
||||
// Merge helper functions for each type
|
||||
func mergeRouters(base, overlay map[string]*runtime.RouterInfo) {
|
||||
if overlay == nil {
|
||||
return
|
||||
}
|
||||
for k, v := range overlay {
|
||||
base[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func mergeMiddlewares(base, overlay map[string]*runtime.MiddlewareInfo) {
|
||||
if overlay == nil {
|
||||
return
|
||||
}
|
||||
for k, v := range overlay {
|
||||
base[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func mergeServices(base, overlay map[string]*db.ServiceInfo) {
|
||||
if overlay == nil {
|
||||
return
|
||||
}
|
||||
for k, v := range overlay {
|
||||
base[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func mergeTCPRouters(base, overlay map[string]*runtime.TCPRouterInfo) {
|
||||
if overlay == nil {
|
||||
return
|
||||
}
|
||||
for k, v := range overlay {
|
||||
base[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func mergeTCPMiddlewares(base, overlay map[string]*runtime.TCPMiddlewareInfo) {
|
||||
if overlay == nil {
|
||||
return
|
||||
}
|
||||
for k, v := range overlay {
|
||||
base[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func mergeTCPServices(base, overlay map[string]*runtime.TCPServiceInfo) {
|
||||
if overlay == nil {
|
||||
return
|
||||
}
|
||||
for k, v := range overlay {
|
||||
base[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func mergeUDPRouters(base, overlay map[string]*runtime.UDPRouterInfo) {
|
||||
if overlay == nil {
|
||||
return
|
||||
}
|
||||
for k, v := range overlay {
|
||||
base[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func mergeUDPServices(base, overlay map[string]*runtime.UDPServiceInfo) {
|
||||
if overlay == nil {
|
||||
return
|
||||
}
|
||||
for k, v := range overlay {
|
||||
base[k] = v
|
||||
}
|
||||
}
|
||||
@@ -634,15 +634,10 @@ async function fetchTraefikConfig(src: TraefikSource) {
|
||||
const newServices = flattenServiceData(res);
|
||||
const newMiddlewares = flattenMiddlewareData(res);
|
||||
const newMerge = newRouters.map((router) => {
|
||||
const routerProvider = router.name.split('@')[1];
|
||||
let serviceName = router.service; // api@internal
|
||||
|
||||
// Most of time the service name doesn't include the provider
|
||||
if (!router.service?.includes('@')) {
|
||||
serviceName = router.service + '@' + routerProvider;
|
||||
let service = newServices.find((service) => service.name === router.service);
|
||||
if (!service) {
|
||||
service = newServices.find((service) => service.name === router.name);
|
||||
}
|
||||
const service = newServices.find((service) => service.name === serviceName);
|
||||
|
||||
return { router, service: service || ({} as Service) };
|
||||
});
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import type { RouterDNSProvider } from '$lib/types';
|
||||
import { source } from '$lib/stores/source';
|
||||
|
||||
interface Props {
|
||||
router: Router;
|
||||
@@ -27,9 +28,7 @@
|
||||
);
|
||||
let rdpName = $derived(routerDNS ? routerDNS.providerName : '');
|
||||
let routerProvider = $derived(router.name ? router.name?.split('@')[1] : 'http');
|
||||
let isHttpProvider = $derived(routerProvider === 'http' || !routerProvider);
|
||||
let isHttpType = $derived(router.protocol === 'http');
|
||||
let disabled = $derived(routerProvider !== 'http' && mode === 'edit');
|
||||
let certResolvers = $derived([
|
||||
...new Set(
|
||||
$routers.filter((item) => item.tls?.certResolver).map((item) => item.tls?.certResolver)
|
||||
@@ -69,13 +68,12 @@
|
||||
</Card.Header>
|
||||
<Card.Content class="flex flex-col gap-2">
|
||||
<!-- Provider Type Toggles -->
|
||||
{#if isHttpProvider}
|
||||
{#if source.isLocal()}
|
||||
<div class="flex items-center justify-end gap-1 font-mono text-sm">
|
||||
<Toggle
|
||||
size="sm"
|
||||
pressed={router.protocol === 'http'}
|
||||
onPressedChange={() => (router.protocol = 'http')}
|
||||
{disabled}
|
||||
class="font-bold data-[state=on]:bg-green-300 dark:data-[state=on]:text-black"
|
||||
>
|
||||
HTTP
|
||||
@@ -84,7 +82,6 @@
|
||||
size="sm"
|
||||
pressed={router.protocol === 'tcp'}
|
||||
onPressedChange={() => (router.protocol = 'tcp')}
|
||||
{disabled}
|
||||
class="font-bold data-[state=on]:bg-blue-300 dark:data-[state=on]:text-black"
|
||||
>
|
||||
TCP
|
||||
@@ -93,7 +90,6 @@
|
||||
size="sm"
|
||||
pressed={router.protocol === 'udp'}
|
||||
onPressedChange={() => (router.protocol = 'udp')}
|
||||
{disabled}
|
||||
class="font-bold data-[state=on]:bg-red-300 dark:data-[state=on]:text-black"
|
||||
>
|
||||
UDP
|
||||
@@ -112,68 +108,64 @@
|
||||
oninput={() => (router.name = router.name?.split('@')[0])}
|
||||
class="col-span-3"
|
||||
required
|
||||
disabled={disabled || mode === 'edit'}
|
||||
disabled={!source.isLocal()}
|
||||
/>
|
||||
<!-- Icon based on provider -->
|
||||
{#if routerProvider !== ''}
|
||||
<span
|
||||
class="pointer-events-none absolute inset-y-0 right-3 flex items-center text-gray-400"
|
||||
>
|
||||
{#if isHttpProvider}
|
||||
<img src={logo} alt="HTTP" width="20" />
|
||||
{/if}
|
||||
{#if routerProvider === 'internal' || routerProvider === 'file'}
|
||||
<iconify-icon icon="devicon:traefikproxy" height="20"></iconify-icon>
|
||||
{/if}
|
||||
{#if routerProvider?.includes('docker')}
|
||||
<iconify-icon icon="logos:docker-icon" height="20"></iconify-icon>
|
||||
{/if}
|
||||
{#if routerProvider?.includes('kubernetes')}
|
||||
<iconify-icon icon="logos:kubernetes" height="20"></iconify-icon>
|
||||
{/if}
|
||||
{#if routerProvider === 'consul'}
|
||||
<iconify-icon icon="logos:consul" height="20"></iconify-icon>
|
||||
{/if}
|
||||
{#if routerProvider === 'nomad'}
|
||||
<iconify-icon icon="logos:nomad-icon" height="20"></iconify-icon>
|
||||
{/if}
|
||||
{#if routerProvider === 'kv'}
|
||||
<iconify-icon icon="logos:redis" height="20"></iconify-icon>
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
<span
|
||||
class="pointer-events-none absolute inset-y-0 right-3 flex items-center text-gray-400"
|
||||
>
|
||||
{#if routerProvider === ''}
|
||||
<img src={logo} alt="HTTP" width="20" />
|
||||
{/if}
|
||||
{#if routerProvider === 'internal' || routerProvider === 'file'}
|
||||
<iconify-icon icon="devicon:traefikproxy" height="20"></iconify-icon>
|
||||
{/if}
|
||||
{#if routerProvider?.includes('docker')}
|
||||
<iconify-icon icon="logos:docker-icon" height="20"></iconify-icon>
|
||||
{/if}
|
||||
{#if routerProvider?.includes('kubernetes')}
|
||||
<iconify-icon icon="logos:kubernetes" height="20"></iconify-icon>
|
||||
{/if}
|
||||
{#if routerProvider === 'consul'}
|
||||
<iconify-icon icon="logos:consul" height="20"></iconify-icon>
|
||||
{/if}
|
||||
{#if routerProvider === 'nomad'}
|
||||
<iconify-icon icon="logos:nomad-icon" height="20"></iconify-icon>
|
||||
{/if}
|
||||
{#if routerProvider === 'kv'}
|
||||
<iconify-icon icon="logos:redis" height="20"></iconify-icon>
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Entrypoints -->
|
||||
{#if isHttpProvider}
|
||||
<div class="grid grid-cols-4 items-center gap-1">
|
||||
<Label class="mr-2 text-right">Entrypoints</Label>
|
||||
<Select.Root type="multiple" bind:value={router.entryPoints} {disabled}>
|
||||
<Select.Trigger class="col-span-3">
|
||||
{router.entryPoints?.length ? router.entryPoints.join(', ') : 'Select entrypoints'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each $entrypoints as ep}
|
||||
<Select.Item value={ep.name}>
|
||||
<div class="flex items-center gap-2">
|
||||
{ep.name}
|
||||
{#if ep.http?.tls}
|
||||
<Lock size="1rem" class="text-green-400" />
|
||||
{/if}
|
||||
</div>
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="grid grid-cols-4 items-center gap-1">
|
||||
<Label class="mr-2 text-right">Entrypoints</Label>
|
||||
<Select.Root type="multiple" bind:value={router.entryPoints} disabled={!source.isLocal()}>
|
||||
<Select.Trigger class="col-span-3">
|
||||
{router.entryPoints?.length ? router.entryPoints.join(', ') : 'Select entrypoints'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each $entrypoints as ep}
|
||||
<Select.Item value={ep.name}>
|
||||
<div class="flex items-center gap-2">
|
||||
{ep.name}
|
||||
{#if ep.http?.tls}
|
||||
<Lock size="1rem" class="text-green-400" />
|
||||
{/if}
|
||||
</div>
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<!-- Middlewares -->
|
||||
{#if router.protocol !== 'udp'}
|
||||
<div class="grid grid-cols-4 items-center gap-1">
|
||||
<Label class="mr-2 text-right">Middlewares</Label>
|
||||
<Select.Root type="multiple" bind:value={router.middlewares} {disabled}>
|
||||
<Select.Root type="multiple" bind:value={router.middlewares} disabled={!source.isLocal()}>
|
||||
<Select.Trigger class="col-span-3">
|
||||
{router.middlewares?.length ? router.middlewares.join(', ') : 'Select middlewares'}
|
||||
</Select.Trigger>
|
||||
@@ -196,14 +188,15 @@
|
||||
<Input
|
||||
bind:value={router.tls.certResolver}
|
||||
placeholder="Certificate resolver"
|
||||
{disabled}
|
||||
disabled={!source.isLocal()}
|
||||
/>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{#each certResolvers as resolver}
|
||||
{#if resolver !== router.tls.certResolver}
|
||||
<Badge
|
||||
onclick={() => !disabled && router.tls && (router.tls.certResolver = resolver)}
|
||||
class={disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'}
|
||||
onclick={() =>
|
||||
!source.isLocal() && router.tls && (router.tls.certResolver = resolver)}
|
||||
class={!source.isLocal() ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'}
|
||||
>
|
||||
{resolver}
|
||||
</Badge>
|
||||
@@ -239,7 +232,11 @@
|
||||
|
||||
<!-- Rule -->
|
||||
{#if router.protocol === 'http' || router.protocol === 'tcp'}
|
||||
<RuleEditor bind:rule={router.rule} bind:type={router.protocol} {disabled} />
|
||||
<RuleEditor
|
||||
bind:rule={router.rule}
|
||||
bind:type={router.protocol}
|
||||
disabled={!source.isLocal()}
|
||||
/>
|
||||
{/if}
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
@@ -6,17 +6,13 @@
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import { type Router, type Service } from '$lib/types/router';
|
||||
import { Plus, Trash } from 'lucide-svelte';
|
||||
import { source } from '$lib/stores/source';
|
||||
|
||||
interface Props {
|
||||
service: Service;
|
||||
router: Router;
|
||||
mode: 'create' | 'edit';
|
||||
}
|
||||
|
||||
let { service = $bindable(), router = $bindable(), mode }: Props = $props();
|
||||
|
||||
let routerProvider = $derived(router.name ? router.name?.split('@')[1] : 'http');
|
||||
let disabled = $derived(routerProvider !== 'http' && mode === 'edit');
|
||||
let { service = $bindable(), router = $bindable() }: Props = $props();
|
||||
|
||||
let servers = $state(getServers());
|
||||
let passHostHeader = $state(service.loadBalancer?.passHostHeader ?? true);
|
||||
@@ -79,7 +75,7 @@
|
||||
class="col-span-3"
|
||||
bind:checked={passHostHeader}
|
||||
onCheckedChange={update}
|
||||
{disabled}
|
||||
disabled={!source.isLocal()}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -94,9 +90,9 @@
|
||||
bind:value={servers[i]}
|
||||
placeholder={router.protocol === 'http' ? 'http://127.0.0.1:8080' : '127.0.0.1:8080'}
|
||||
oninput={update}
|
||||
{disabled}
|
||||
disabled={!source.isLocal()}
|
||||
/>
|
||||
{#if !disabled}
|
||||
{#if !source.isLocal()}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
@@ -110,7 +106,7 @@
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{#if !disabled}
|
||||
{#if !source.isLocal()}
|
||||
<Button type="button" variant="outline" onclick={addItem} class="w-full">
|
||||
<Plus />
|
||||
Add Server
|
||||
|
||||
@@ -26,9 +26,6 @@
|
||||
const update = async () => {
|
||||
try {
|
||||
// Ensure proper name formatting and synchronization
|
||||
if (!router.name.includes('@')) {
|
||||
router.name = `${router.name}@http`;
|
||||
}
|
||||
if (service.loadBalancer?.servers?.length === 0) {
|
||||
toast.error('At least one server is required');
|
||||
return;
|
||||
@@ -81,7 +78,7 @@
|
||||
<RouterForm bind:router {mode} />
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="service">
|
||||
<ServiceForm bind:service bind:router {mode} />
|
||||
<ServiceForm bind:service bind:router />
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
|
||||
|
||||
@@ -64,13 +64,9 @@
|
||||
}
|
||||
|
||||
const deleteRouter = async (router: Router) => {
|
||||
try {
|
||||
let routerProvider = router.name.split('@')[1];
|
||||
if (routerProvider !== 'http') {
|
||||
toast.error('Router not managed by Mantrae!');
|
||||
return;
|
||||
}
|
||||
if (!source.isLocal()) return;
|
||||
|
||||
try {
|
||||
await api.deleteRouter(router);
|
||||
toast.success('Router deleted');
|
||||
} catch (err: unknown) {
|
||||
|
||||
Reference in New Issue
Block a user