mirror of
https://github.com/MizuchiLabs/mantrae.git
synced 2025-12-16 20:05:17 -06:00
add more name verifications
This commit is contained in:
@@ -5,8 +5,8 @@ services:
|
||||
image: ghcr.io/mizuchilabs/mantrae:latest
|
||||
container_name: mantrae
|
||||
command:
|
||||
- --url=http://traefik:8080
|
||||
- --username=admin
|
||||
- --url=http://traefik:8080 # if traefik is running on the same host
|
||||
- --username=admin # use if you want to enable basicauth on traefik
|
||||
- --password=admin
|
||||
ports:
|
||||
- 3000:3000
|
||||
@@ -25,7 +25,7 @@ services:
|
||||
- ./traefik:/etc/traefik
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
environment:
|
||||
- CF_DNS_API_TOKEN=xxx
|
||||
- CF_DNS_API_TOKEN=xxx # Set cloudflare token
|
||||
networks:
|
||||
- proxy
|
||||
command:
|
||||
@@ -37,6 +37,7 @@ services:
|
||||
- --entrypoints.web.http.redirections.entryPoint.scheme=https
|
||||
- --entrypoints.websecure.address=:443
|
||||
- --entrypoints.websecure.asDefault=true
|
||||
- --entrypoints.websecure.http.tls.certresolver=letsencrypt # Set the default cert resolver
|
||||
- --serversTransport.insecureSkipVerify=true
|
||||
- --providers.docker=true
|
||||
- --providers.docker.exposedByDefault=false
|
||||
@@ -48,7 +49,7 @@ services:
|
||||
- --certificatesresolvers.letsencrypt.acme.dnschallenge.delaybeforecheck=0
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.dashboard.rule=PathPrefix(`/api`) || PathPrefix(`/dashboard`)
|
||||
- traefik.http.routers.dashboard.rule=Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
|
||||
- traefik.http.routers.dashboard.service=api@internal
|
||||
- traefik.http.routers.dashboard.entrypoints=websecure
|
||||
restart: always
|
||||
|
||||
@@ -351,6 +351,7 @@ func UpdateMiddleware(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
updateName(profile.Dynamic.Middlewares, middlewareName, middleware.Name)
|
||||
|
||||
if err := middleware.Verify(); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
|
||||
@@ -123,7 +123,7 @@ func getRouters[T Routerable](p Profile, endpoint string) map[string]Router {
|
||||
return nil
|
||||
}
|
||||
|
||||
routers := make(map[string]Router)
|
||||
routers := make(map[string]Router, len(routerables))
|
||||
for _, r := range routerables {
|
||||
newRouter := r.ToRouter()
|
||||
routers[newRouter.Name] = *newRouter
|
||||
@@ -216,7 +216,7 @@ func getServices[T Serviceable](p Profile, endpoint string) map[string]Service {
|
||||
return nil
|
||||
}
|
||||
|
||||
services := make(map[string]Service)
|
||||
services := make(map[string]Service, len(serviceables))
|
||||
for _, s := range serviceables {
|
||||
newService := s.ToService()
|
||||
services[newService.Name] = *newService
|
||||
@@ -319,7 +319,7 @@ func getMiddlewares[T Middlewareable](p Profile, endpoint string) map[string]Mid
|
||||
return nil
|
||||
}
|
||||
|
||||
middlewares := make(map[string]Middleware)
|
||||
middlewares := make(map[string]Middleware, len(middlewareables))
|
||||
for _, m := range middlewareables {
|
||||
newMiddleware := m.ToMiddleware()
|
||||
middlewares[newMiddleware.Name] = *newMiddleware
|
||||
@@ -346,7 +346,10 @@ func GetTraefikConfig() {
|
||||
getRouters[HTTPRouter](profile, HTTPRouterAPI),
|
||||
getRouters[TCPRouter](profile, TCPRouterAPI),
|
||||
getRouters[UDPRouter](profile, UDPRouterAPI),
|
||||
profile.Dynamic.Routers,
|
||||
filterByLocalProvider(
|
||||
profile.Dynamic.Routers,
|
||||
func(r Router) string { return r.Provider },
|
||||
),
|
||||
)
|
||||
|
||||
// Retrieve services
|
||||
@@ -354,14 +357,20 @@ func GetTraefikConfig() {
|
||||
getServices[HTTPService](profile, HTTPServiceAPI),
|
||||
getServices[TCPService](profile, TCPServiceAPI),
|
||||
getServices[UDPService](profile, UDPServiceAPI),
|
||||
profile.Dynamic.Services,
|
||||
filterByLocalProvider(
|
||||
profile.Dynamic.Services,
|
||||
func(s Service) string { return s.Provider },
|
||||
),
|
||||
)
|
||||
|
||||
// Fetch middlewares
|
||||
d.Middlewares = merge(
|
||||
getMiddlewares[HTTPMiddleware](profile, HTTPMiddlewaresAPI),
|
||||
getMiddlewares[TCPMiddleware](profile, TCPMiddlewaresAPI),
|
||||
profile.Dynamic.Middlewares,
|
||||
filterByLocalProvider(
|
||||
profile.Dynamic.Middlewares,
|
||||
func(m Middleware) string { return m.Provider },
|
||||
),
|
||||
)
|
||||
|
||||
// Retrieve entrypoints
|
||||
@@ -414,6 +423,17 @@ func Sync() {
|
||||
}
|
||||
}
|
||||
|
||||
// Filter http provider
|
||||
func filterByLocalProvider[T any](items map[string]T, getProvider func(T) string) map[string]T {
|
||||
filteredItems := make(map[string]T)
|
||||
for key, item := range items {
|
||||
if getProvider(item) == "http" {
|
||||
filteredItems[key] = item
|
||||
}
|
||||
}
|
||||
return filteredItems
|
||||
}
|
||||
|
||||
func merge[T any](maps ...map[string]T) map[string]T {
|
||||
merged := make(map[string]T)
|
||||
for _, m := range maps {
|
||||
|
||||
@@ -29,17 +29,22 @@ func (p *Profile) Verify() error {
|
||||
|
||||
func (r *Router) Verify() error {
|
||||
if r.Name == "" {
|
||||
return fmt.Errorf("router name cannot be empty")
|
||||
}
|
||||
if r.Service == "" {
|
||||
return fmt.Errorf("service cannot be empty")
|
||||
}
|
||||
if r.RouterType == "" {
|
||||
return fmt.Errorf("router type cannot be empty")
|
||||
return fmt.Errorf("name cannot be empty")
|
||||
}
|
||||
if r.Rule == "" && r.RouterType != "udp" {
|
||||
return fmt.Errorf("rule cannot be empty")
|
||||
}
|
||||
if r.RouterType == "" {
|
||||
r.RouterType = "http"
|
||||
}
|
||||
if r.Provider == "" {
|
||||
r.Provider = "http"
|
||||
}
|
||||
if r.Service == "" {
|
||||
r.Service = r.Name
|
||||
}
|
||||
r.Name = validateName(r.Name, r.Provider)
|
||||
r.Service = validateName(r.Name, r.Provider)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -48,8 +53,12 @@ func (s *Service) Verify() error {
|
||||
return fmt.Errorf("service name cannot be empty")
|
||||
}
|
||||
if s.ServiceType == "" {
|
||||
return fmt.Errorf("service type cannot be empty")
|
||||
s.ServiceType = "http"
|
||||
}
|
||||
if s.Provider == "" {
|
||||
s.Provider = "http"
|
||||
}
|
||||
s.Name = validateName(s.Name, s.Provider)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -58,11 +67,12 @@ func (m *Middleware) Verify() error {
|
||||
return fmt.Errorf("middleware name cannot be empty")
|
||||
}
|
||||
if m.Provider == "" {
|
||||
return fmt.Errorf("provider cannot be empty")
|
||||
m.Provider = "http"
|
||||
}
|
||||
if m.Type == "" {
|
||||
return fmt.Errorf("type cannot be empty")
|
||||
}
|
||||
m.Name = validateName(m.Name, m.Provider)
|
||||
|
||||
// Hashes the password strings in the middleware
|
||||
if m.BasicAuth != nil {
|
||||
@@ -120,6 +130,19 @@ func (m *Middleware) Verify() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateName(s, p string) string {
|
||||
name := strings.ToLower(s)
|
||||
parts := strings.Split(name, "@")
|
||||
|
||||
if len(parts) > 1 {
|
||||
name = parts[0] + "@" + parts[1]
|
||||
} else {
|
||||
name = parts[0] + "@" + p
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
func isValidURL(u string) bool {
|
||||
// If no scheme is provided, prepend "http://"
|
||||
if !strings.Contains(u, "://") {
|
||||
|
||||
@@ -125,16 +125,22 @@ export async function deleteProfile(name: string): Promise<void> {
|
||||
export async function updateRouter(
|
||||
oldName: string,
|
||||
router: Router,
|
||||
service: Service | undefined
|
||||
service: Service
|
||||
): Promise<void> {
|
||||
if (!get(profile)) return;
|
||||
if (service === undefined) service = newService();
|
||||
service.name = router.service + '@' + router.provider;
|
||||
service.serviceType = router.routerType;
|
||||
await handleRequest(`/routers/${get(profile).name}/${oldName}`, 'PUT', router);
|
||||
const response = await handleRequest(`/services/${get(profile).name}/${oldName}`, 'PUT', service);
|
||||
profile.set(response);
|
||||
toast.success(`Router ${router.name} updated`);
|
||||
|
||||
const resRouter = await handleRequest(`/routers/${get(profile).name}/${oldName}`, 'PUT', router);
|
||||
if (resRouter) {
|
||||
const resService = await handleRequest(
|
||||
`/services/${get(profile).name}/${oldName}`,
|
||||
'PUT',
|
||||
service
|
||||
);
|
||||
if (resService) {
|
||||
profile.set(resService);
|
||||
toast.success(`Router ${router.name} updated`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteRouter(name: string): Promise<void> {
|
||||
|
||||
@@ -16,10 +16,8 @@
|
||||
let service = newService();
|
||||
|
||||
const create = async () => {
|
||||
if (router.service === '' || isNameTaken) return;
|
||||
router.name = router.service + '@' + router.provider;
|
||||
service.name = router.service + '@' + router.provider;
|
||||
service.serviceType = router.routerType;
|
||||
if (router.name === '' || isNameTaken) return;
|
||||
service.name = router.name.split('@')[0] + '@' + router.provider;
|
||||
await updateRouter(router.name, router, service);
|
||||
|
||||
router = newRouter();
|
||||
@@ -59,7 +57,9 @@
|
||||
|
||||
// Check if router name is taken
|
||||
let isNameTaken = false;
|
||||
$: isNameTaken = $routers.some((r) => r.service === router.service);
|
||||
$: isNameTaken = $routers.some(
|
||||
(r) => r.name.split('@')[0].toLowerCase() === router.name.split('@')[0].toLowerCase()
|
||||
);
|
||||
</script>
|
||||
|
||||
<Dialog.Root>
|
||||
@@ -105,7 +105,7 @@
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
bind:value={router.service}
|
||||
bind:value={router.name}
|
||||
class={isNameTaken
|
||||
? 'col-span-3 border-red-400 focus-visible:ring-0 focus-visible:ring-offset-0'
|
||||
: 'col-span-3 focus-visible:ring-0 focus-visible:ring-offset-0'}
|
||||
|
||||
@@ -14,21 +14,15 @@
|
||||
import Service from '../forms/service.svelte';
|
||||
|
||||
export let router: Router;
|
||||
let service = getService(router.name);
|
||||
let service = getService(router.name) ?? newService();
|
||||
let routerCompare = $routers.filter((r) => r.name !== router.name);
|
||||
|
||||
let open = false;
|
||||
const update = () => {
|
||||
if (service === undefined) {
|
||||
service = newService();
|
||||
service.serviceType = router.routerType;
|
||||
}
|
||||
if (router.service === '' || isNameTaken) return;
|
||||
let oldName = router.name;
|
||||
router.name = router.service + '@' + router.provider;
|
||||
service.name = router.service + '@' + router.provider; // Extra check in case router name changed
|
||||
service.serviceType = router.routerType;
|
||||
updateRouter(oldName, router, service);
|
||||
if (router.name === '' || isNameTaken) return;
|
||||
// Extra check in case router name changed
|
||||
service.name = router.name.split('@')[0] + '@' + router.provider;
|
||||
updateRouter(router.service, router, service);
|
||||
open = false;
|
||||
};
|
||||
|
||||
@@ -55,7 +49,9 @@
|
||||
|
||||
// Check if router name is taken unless self
|
||||
let isNameTaken = false;
|
||||
$: isNameTaken = routerCompare.some((r) => r.service === router.service);
|
||||
$: isNameTaken = routerCompare.some(
|
||||
(r) => r.name.split('@')[0].toLowerCase() === router.name.split('@')[0].toLowerCase()
|
||||
);
|
||||
|
||||
const onKeydown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
@@ -104,7 +100,7 @@
|
||||
class={isNameTaken
|
||||
? 'col-span-3 border-red-400 focus-visible:ring-0 focus-visible:ring-offset-0'
|
||||
: 'col-span-3 focus-visible:ring-0 focus-visible:ring-offset-0'}
|
||||
bind:value={router.service}
|
||||
bind:value={router.name}
|
||||
placeholder="Name of the router"
|
||||
on:keydown={onKeydown}
|
||||
required
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
middlewares,
|
||||
routers,
|
||||
services,
|
||||
updateRouter
|
||||
updateRouter,
|
||||
getService
|
||||
} from '$lib/api';
|
||||
import CreateRouter from '$lib/components/modals/createRouter.svelte';
|
||||
import UpdateRouter from '$lib/components/modals/updateRouter.svelte';
|
||||
@@ -84,13 +85,13 @@
|
||||
const toggleEntrypoint = (router: Router, item: Selected<unknown>[] | undefined) => {
|
||||
if (item === undefined) return;
|
||||
router.entrypoints = item.map((i) => i.value) as string[];
|
||||
let service = $profile?.dynamic?.services?.[router.name];
|
||||
let service = getService(router.name);
|
||||
updateRouter(router.name, router, service);
|
||||
};
|
||||
const toggleMiddleware = (router: Router, item: Selected<unknown>[] | undefined) => {
|
||||
if (item === undefined) return;
|
||||
router.middlewares = item.map((i) => i.value) as string[];
|
||||
let service = $profile?.dynamic?.services?.[router.name];
|
||||
let service = getService(router.name);
|
||||
updateRouter(router.name, router, service);
|
||||
};
|
||||
const getSelectedEntrypoints = (router: Router): Selected<unknown>[] => {
|
||||
@@ -213,7 +214,7 @@
|
||||
{#each fRouters as router}
|
||||
<Table.Row>
|
||||
<Table.Cell class={showColumn('name') ? 'font-medium' : 'hidden'}>
|
||||
{router.service}
|
||||
{router.name.split('@')[0]}
|
||||
</Table.Cell>
|
||||
<Table.Cell class={showColumn('provider') ? 'font-medium' : 'hidden'}>
|
||||
<span
|
||||
|
||||
Reference in New Issue
Block a user