Skip to content

Commit

Permalink
add network settings page (#234)
Browse files Browse the repository at this point in the history
* add network page

* adjust the layout

* adjust the layout

* add proxy control

* adjust the layout

* add webhook component

* adjust the layout

* adjust the layout

* add interface component

* add network API

* fetch network settings

* save IP mode

* save proxy settings

* save webhook settings

* save webhook settings

* save webhook settings

* fix the warnings
  • Loading branch information
TimothyYe authored Feb 24, 2024
1 parent d5ead7f commit 941e9a0
Show file tree
Hide file tree
Showing 14 changed files with 581 additions and 9 deletions.
12 changes: 8 additions & 4 deletions internal/provider/linode/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ import (

func CreateHTTPClient(conf *settings.Settings) (*http.Client, error) {
transport := &http.Transport{}
transport, err := applyProxy(conf.Socks5Proxy, transport)
if err != nil {
log.Infof("Error connecting to proxy: '%s'", err)
log.Info("Continuing without proxy")
var err error

if conf.UseProxy && conf.Socks5Proxy != "" {
transport, err = applyProxy(conf.Socks5Proxy, transport)
if err != nil {
log.Errorf("Error connecting to proxy: '%s'", err)
log.Info("Continuing without proxy")
}
}

if conf.LoginToken == "" {
Expand Down
73 changes: 73 additions & 0 deletions internal/server/controllers/network.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package controllers

import (
"github.com/TimothyYe/godns/internal/settings"
"github.com/TimothyYe/godns/internal/utils"
"github.com/gofiber/fiber/v2"
log "github.com/sirupsen/logrus"
)

type NetworkSettings struct {
IPMode string `json:"ip_mode"`
IPUrls []string `json:"ip_urls"`
IPV6Urls []string `json:"ipv6_urls"`
UseProxy bool `json:"use_proxy"`
SkipSSLVerify bool `json:"skip_ssl_verify"`
Socks5Proxy string `json:"socks5_proxy"`
Webhook settings.Webhook `json:"webhook,omitempty"`
Resolver string `json:"resolver"`
IPInterface string `json:"ip_interface"`
}

func (c *Controller) GetNetworkSettings(ctx *fiber.Ctx) error {
settings := NetworkSettings{
IPMode: c.config.IPType,
IPUrls: c.config.IPUrls,
IPV6Urls: c.config.IPV6Urls,
UseProxy: c.config.UseProxy,
SkipSSLVerify: c.config.SkipSSLVerify,
Socks5Proxy: c.config.Socks5Proxy,
Webhook: c.config.Webhook,
Resolver: c.config.Resolver,
IPInterface: c.config.IPInterface,
}

return ctx.JSON(settings)
}

func (c *Controller) UpdateNetworkSettings(ctx *fiber.Ctx) error {
var settings NetworkSettings
if err := ctx.BodyParser(&settings); err != nil {
log.Errorf("Failed to parse request body: %s", err.Error())
return ctx.Status(400).SendString(err.Error())
}

if settings.IPMode == utils.IPV4 && len(settings.IPUrls) == 0 {
return ctx.Status(400).SendString("IP URLs cannot be empty")
}

if settings.IPMode == utils.IPV6 && len(settings.IPV6Urls) == 0 {
return ctx.Status(400).SendString("IPv6 URLs cannot be empty")
}

c.config.IPType = settings.IPMode
if settings.IPMode == utils.IPV6 {
c.config.IPV6Urls = settings.IPV6Urls
} else {
c.config.IPUrls = settings.IPUrls
}

c.config.UseProxy = settings.UseProxy
c.config.SkipSSLVerify = settings.SkipSSLVerify
c.config.Socks5Proxy = settings.Socks5Proxy
c.config.Webhook = settings.Webhook
c.config.Resolver = settings.Resolver
c.config.IPInterface = settings.IPInterface

if err := c.config.SaveSettings(c.configPath); err != nil {
log.Errorf("Failed to save settings: %s", err.Error())
return ctx.Status(500).SendString("Failed to save network settings")
}

return ctx.JSON(settings)
}
4 changes: 4 additions & 0 deletions internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ func (s *Server) initRoutes() {
route.Get("/provider/settings", s.controller.GetProviderSettings)
route.Put("/provider", s.controller.UpdateProvider)

// Network related routes
route.Get("/network", s.controller.GetNetworkSettings)
route.Put("/network", s.controller.UpdateNetworkSettings)

// Serve embedded files
s.app.Use("/", filesystem.New(filesystem.Config{
Root: http.FS(embeddedFiles),
Expand Down
55 changes: 55 additions & 0 deletions web/api/network.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { get_api_server } from "./env";

export interface WebHook {
enabled: boolean;
url: string;
request_body: string;
}

export interface NetworkSettings {
ip_mode: string;
ip_urls?: string[];
ipv6_urls?: string[];
use_proxy: boolean;
skip_ssl_verify: boolean;
socks5_proxy: string;
webhook: WebHook;
resolver: string;
ip_interface: string;
}

export async function get_network_settings(credentials: string): Promise<NetworkSettings> {
if (credentials) {
const resp = await fetch(get_api_server() + '/api/v1/network', {
method: 'GET',
headers: {
'Authorization': `Basic ${credentials}`
}
})

if (resp.status === 200) {
return resp.json();
}
}

return {} as NetworkSettings;
}

export async function update_network_settings(credentials: string, settings: NetworkSettings): Promise<boolean> {
if (credentials) {
const resp = await fetch(get_api_server() + '/api/v1/network', {
method: 'PUT',
headers: {
'Authorization': `Basic ${credentials}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(settings)
})

if (resp.status === 200) {
return true;
}
}

return false;
}
3 changes: 0 additions & 3 deletions web/app/domains/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ export default function Domains() {
<main className="flex min-h-screen flex-col items-center justify-start pt-10 max-w-screen-xl">
<ToastContainer />
<div className="flex flex-col items-center w-full bg-base-100 p-10">
{/* <div className="flex flex-row items-start w-full"> */}
{/* <span className="text-2xl font-semibold text-neutral-500 ml-1 mb-1">Provider</span> */}
{/* </div> */}
<ProviderControl />
<div className="divider"></div>
<DomainControl />
Expand Down
129 changes: 129 additions & 0 deletions web/app/network/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
'use client';

import { IpMode } from "@/components/ip-mode";
import { Proxy } from "@/components/proxy";
import { WebHook } from "@/components/webhook";
import { Resolver } from "@/components/resolver";
import { IPInterface } from "@/components/ip-interface";
import { useRouter } from "next/navigation";
import { CommonContext } from "@/components/user";
import { useEffect, useState, useContext } from "react";
import { get_network_settings, NetworkSettings, update_network_settings } from "@/api/network";
import { get_info } from "@/api/info";
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

export default function Network() {
const router = useRouter();
const userStore = useContext(CommonContext);
const { credentials, setCurrentPage, saveVersion } = userStore;
const [settings, setSettings] = useState<NetworkSettings>({} as NetworkSettings);

useEffect(() => {
if (!credentials) {
router.push('/login');
return;
}
setCurrentPage('Network');
get_info(credentials).then((info) => {
saveVersion(info.version);
});

get_network_settings(credentials).then((settings) => {
setSettings(settings);
});
}, [credentials, router, setCurrentPage, saveVersion]);


return (
<main className="flex min-h-screen flex-col items-center justify-between pt-10 max-w-screen-xl">
<ToastContainer />
<div className="p-5">
<div className="flex flex-col max-w-screen-lg gap-5">
<IpMode
IPMode={settings.ip_mode}
IPUrls={settings.ip_urls}
IPV6Urls={settings.ipv6_urls}
onIpModeChagne={(data) => {
setSettings({
...settings,
ip_mode: data.IPMode,
ip_urls: data.IPUrls,
ipv6_urls: data.IPV6Urls
});
}}
/>
<Proxy
EnableProxy={settings.use_proxy}
SkipSSLVerify={settings.skip_ssl_verify}
Socks5Proxy={settings.socks5_proxy}
onProxyChange={(data) => {
setSettings({
...settings,
use_proxy: data.EnableProxy,
skip_ssl_verify: data.SkipSSLVerify,
socks5_proxy: data.Socks5Proxy
});
}}
/>
{
settings.webhook ?
<WebHook
Enabled={settings.webhook.enabled}
Url={settings.webhook.url}
RequestBody={settings.webhook.request_body}
onWebHookChange={(data) => {
setSettings({
...settings,
webhook: {
enabled: data.Enabled,
url: data.Url,
request_body: data.RequestBody
}
});
}}
/> : null
}
<Resolver
Resolver={settings.resolver}
onResolverChange={(data) => {
setSettings({
...settings,
resolver: data.Resolver
});
}}
/>
<IPInterface
IPInterface={settings.ip_interface}
onIPInterfaceChange={(data) => {
setSettings({
...settings,
ip_interface: data.IPInterface
});
}}
/>
<div className="flex justify-center">
<button className="flex btn btn-primary"
onClick={() => {
if (!credentials) {
toast.error('Invalid credentials');
return;
}

update_network_settings(credentials, settings).then((success) => {
if (success) {
toast.success('Network settings updated successfully');
} else {
toast.error('Failed to update network settings');
}
});
}}
>
Save
</button>
</div>
</div>
</div>
</main>
);
}
7 changes: 7 additions & 0 deletions web/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ export const MoonFilledIcon = ({
</svg>
);

export const InterfaceIcon = ({
}) => (
<svg className="w-6 h-6 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M16 4h4m0 0v4m0-4-5 5M8 20H4m0 0v-4m0 4 5-5" />
</svg>
);

export const SunFilledIcon = ({
size = 24,
width,
Expand Down
37 changes: 37 additions & 0 deletions web/components/ip-interface.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { InterfaceIcon } from "./icons";

interface IPInterfaceProps {
IPInterface: string;
onIPInterfaceChange?: (data: IPInterfaceProps) => void;
}

export const IPInterface = (props: IPInterfaceProps) => {
return (
<div className="stats shadow bg-primary-content stats-vertical lg:stats-horizontal">
<div className="stat gap-2">
<div className="stat-title">IP Inerface</div>
<div className="flex flex-col gap-3">
<div className="flex flex-row items-center justify-start gap-2">
<span className="label-text text-slate-500 ">Set the network interface</span>
<div className="flex flex-grow justify-end text-secondary">
<InterfaceIcon />
</div>
</div>
<input
type="text"
className="input input-primary w-full"
placeholder="Input the network interface name: e.g. eth0"
value={props.IPInterface}
onChange={(e) => {
if (props.onIPInterfaceChange) {
props.onIPInterfaceChange({
IPInterface: e.target.value
});
}
}}
/>
</div>
</div>
</div>
)
}
Loading

0 comments on commit 941e9a0

Please sign in to comment.