hello world
This commit is contained in:
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
gotoopie
|
13
LICENSE
Normal file
13
LICENSE
Normal file
@ -0,0 +1,13 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
80
backend/client.go
Normal file
80
backend/client.go
Normal file
@ -0,0 +1,80 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const jsonrpcVersion string = "2.0"
|
||||
const scheme = "https://"
|
||||
|
||||
// Client is an I2PControl client
|
||||
type Client struct {
|
||||
Config *Options
|
||||
MsgCount int
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
// Options is atype alias of I2PControl
|
||||
type Options = I2PControl
|
||||
|
||||
// NewClient creates a new I2PControl session with the configuration options Options
|
||||
func NewClient(opts *Options) *Client {
|
||||
c := new(Client)
|
||||
c.Config = opts
|
||||
return c
|
||||
}
|
||||
|
||||
// Request contains a JSONRPC2.0 I2PControl request
|
||||
type Request struct {
|
||||
ID string `json:"id"`
|
||||
Method string `json:"method"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
}
|
||||
|
||||
// Reply contains a Request reply
|
||||
type Reply struct {
|
||||
ID string `json:"id"`
|
||||
Result map[string]interface{} `json:"result"`
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
}
|
||||
|
||||
// Send sends a JSONRPC2.0 I2PControl Request down the wire and returns a Result
|
||||
func (c *Client) Send(method string, params map[string]interface{}) (*Reply, error) {
|
||||
c.mutex.Lock()
|
||||
c.MsgCount++
|
||||
params["Token"] = c.Config.Token
|
||||
c.mutex.Unlock()
|
||||
return c.buildRequest(Request{
|
||||
ID: strconv.Itoa(c.MsgCount),
|
||||
Method: method,
|
||||
Params: params,
|
||||
JSONRPC: jsonrpcVersion,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Client) buildRequest(request Request) (*Reply, error) {
|
||||
data, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return &Reply{}, err
|
||||
}
|
||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
req, err := http.NewRequest("POST", scheme+c.Config.Address+":"+c.Config.Port, bytes.NewBuffer(data))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
r := new(Reply)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
json.NewDecoder(resp.Body).Decode(&r)
|
||||
return r, nil
|
||||
}
|
462
backend/helpers.go
Normal file
462
backend/helpers.go
Normal file
@ -0,0 +1,462 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const apiVersion float64 = 1 // The version of the I2PControl API used by the client
|
||||
const nonEmptyString = "x"
|
||||
const nonEmptyint = 1
|
||||
|
||||
// Authenticate creates and returns an authentication token used for further communication.
|
||||
func (c *Client) Authenticate() error {
|
||||
reply, err := c.Send("Authenticate", map[string]interface{}{
|
||||
"API": apiVersion,
|
||||
"Password": c.Config.Password,
|
||||
})
|
||||
if err != nil || reply.Result == nil {
|
||||
return fmt.Errorf("Authenticate failed: %w", err)
|
||||
}
|
||||
c.Config.Token = reply.Result["Token"].(string)
|
||||
return err
|
||||
}
|
||||
|
||||
// Echo echoes the value of the echo key, used for debugging and testing.
|
||||
func (c *Client) Echo(echo string) (string, error) {
|
||||
reply, err := c.Send("Echo", map[string]interface{}{
|
||||
"Echo": echo,
|
||||
})
|
||||
if err != nil || reply.Result == nil {
|
||||
return "", fmt.Errorf("Echo failed: %w", err)
|
||||
}
|
||||
return reply.Result["Result"].(string), err
|
||||
}
|
||||
|
||||
// GetRate fetches rateStat from router statManager. Creates stat if not already created.
|
||||
func (c *Client) GetRate(stat string, period int) (int, error) {
|
||||
reply, err := c.Send("GetRate", map[string]interface{}{
|
||||
"Stat": stat,
|
||||
"Period": float64(period),
|
||||
})
|
||||
if err != nil || reply.Result == nil {
|
||||
return 0, fmt.Errorf("GetRate failed: %w", err)
|
||||
}
|
||||
return int(reply.Result["Result"].(float64)), err
|
||||
}
|
||||
|
||||
// I2PControl wrappers
|
||||
|
||||
// SetAddress sets a new listen address for I2PControl (only 127.0.0.1 and 0.0.0.0 are implemented in I2PControl currently).
|
||||
func (c *Client) SetAddress(address string) (bool, bool, error) {
|
||||
reply, err := c.I2PControl(I2PControl{
|
||||
Address: address,
|
||||
})
|
||||
if err != nil {
|
||||
return false, false, fmt.Errorf("SetAddress failed: %w", err)
|
||||
}
|
||||
return reply.RestartNeeded, reply.SettingsSaved, err
|
||||
}
|
||||
|
||||
// SetPassword ets a new password for I2PControl, all Authentication tokens will be revoked.
|
||||
func (c *Client) SetPassword(password string) (bool, bool, error) {
|
||||
reply, err := c.I2PControl(I2PControl{
|
||||
Password: password,
|
||||
})
|
||||
if err != nil {
|
||||
return false, false, fmt.Errorf("SetPassword failed: %w", err)
|
||||
}
|
||||
return reply.RestartNeeded, reply.SettingsSaved, err
|
||||
}
|
||||
|
||||
// SetPort switches which port I2PControl will listen for connections on.
|
||||
func (c *Client) SetPort(port string) (bool, bool, error) {
|
||||
reply, err := c.I2PControl(I2PControl{
|
||||
Port: port,
|
||||
})
|
||||
if err != nil {
|
||||
return false, false, fmt.Errorf("SetPort failed: %w", err)
|
||||
}
|
||||
return reply.RestartNeeded, reply.SettingsSaved, err
|
||||
}
|
||||
|
||||
// RouterInfo wrappers
|
||||
|
||||
// RouterStatus returns what the status of the router is.
|
||||
func (c *Client) RouterStatus() (string, error) {
|
||||
reply, err := c.RouterInfo(RouterInfo{
|
||||
Status: nonEmptyString,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("RouterStatus failed: %w", err)
|
||||
}
|
||||
return reply.Status, err
|
||||
}
|
||||
|
||||
// RouterUptime returns what the uptime of the router is in ms.
|
||||
func (c *Client) RouterUptime() (int, error) {
|
||||
reply, err := c.RouterInfo(RouterInfo{
|
||||
Uptime: nonEmptyint,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("RouterUptime failed: %w", err)
|
||||
}
|
||||
return reply.Uptime, err
|
||||
}
|
||||
|
||||
// RouterVersion returns what version of I2P the router is running.
|
||||
func (c *Client) RouterVersion() (string, error) {
|
||||
reply, err := c.RouterInfo(RouterInfo{
|
||||
Version: nonEmptyString,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("RouterVersion failed: %w", err)
|
||||
}
|
||||
return reply.Version, err
|
||||
}
|
||||
|
||||
// NetBwInbound1s returns the 1 second average inbound bandwidth in Bps.
|
||||
func (c *Client) NetBwInbound1s() (int, error) {
|
||||
reply, err := c.RouterInfo(RouterInfo{
|
||||
NetBwInbound1s: nonEmptyint,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("NetBwInbound1s failed: %w", err)
|
||||
}
|
||||
return reply.NetBwInbound1s, err
|
||||
}
|
||||
|
||||
// NetBwInbound15s returns the 15 second average inbound bandwidth in Bps.
|
||||
func (c *Client) NetBwInbound15s() (int, error) {
|
||||
reply, err := c.RouterInfo(RouterInfo{
|
||||
NetBwInbound15s: nonEmptyint,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("NetBwInbound15s failed: %w", err)
|
||||
}
|
||||
return reply.NetBwInbound15s, err
|
||||
}
|
||||
|
||||
// NetBwOutbound1s returns the 1 second average outbound bandwidth in Bps.
|
||||
func (c *Client) NetBwOutbound1s() (int, error) {
|
||||
reply, err := c.RouterInfo(RouterInfo{
|
||||
NetBwOutbound1s: nonEmptyint,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("NetBwOutbound1s failed: %w", err)
|
||||
}
|
||||
return reply.NetBwOutbound1s, err
|
||||
}
|
||||
|
||||
// NetBwOutbound15s returns the 15 second average outbound bandwidth in Bps
|
||||
func (c *Client) NetBwOutbound15s() (int, error) {
|
||||
reply, err := c.RouterInfo(RouterInfo{
|
||||
NetBwOutbound15s: nonEmptyint,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("NetBwOutbound15s failed: %w", err)
|
||||
}
|
||||
return reply.NetBwOutbound15s, err
|
||||
}
|
||||
|
||||
// NetStatus returns what the current network status is.
|
||||
func (c *Client) NetStatus() (int, error) {
|
||||
reply, err := c.RouterInfo(RouterInfo{
|
||||
NetStatus: nonEmptyint,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("NetStatus failed: %w", err)
|
||||
}
|
||||
return reply.NetStatus, err
|
||||
}
|
||||
|
||||
// NetTunnelsParticipating returns how many tunnels on the I2P net are we participating in.
|
||||
func (c *Client) NetTunnelsParticipating() (int, error) {
|
||||
reply, err := c.RouterInfo(RouterInfo{
|
||||
NetTunnelsParticipating: nonEmptyint,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("NetTunnelsParticipating failed: %w", err)
|
||||
}
|
||||
return reply.NetTunnelsParticipating, err
|
||||
}
|
||||
|
||||
// NetDBActivePeers returns how many tunnels on the I2P net are we participating in.
|
||||
func (c *Client) NetDBActivePeers() (int, error) {
|
||||
reply, err := c.RouterInfo(RouterInfo{
|
||||
NetDBActivePeers: nonEmptyint,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("NetDBActivePeers failed: %w", err)
|
||||
}
|
||||
return reply.NetDBActivePeers, err
|
||||
}
|
||||
|
||||
// NetDBFastPeers returns how many peers are considered 'fast'.
|
||||
func (c *Client) NetDBFastPeers() (int, error) {
|
||||
reply, err := c.RouterInfo(RouterInfo{
|
||||
NetDBFastPeers: nonEmptyint,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("NetDBFastPeers failed: %w", err)
|
||||
}
|
||||
return reply.NetDBFastPeers, err
|
||||
}
|
||||
|
||||
// NetDBHighCapacityPeers returns how many peers are considered 'high capacity'.
|
||||
func (c *Client) NetDBHighCapacityPeers() (int, error) {
|
||||
reply, err := c.RouterInfo(RouterInfo{
|
||||
NetDBHighCapacityPeers: nonEmptyint,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("NetDBHighCapacityPeers failed: %w", err)
|
||||
}
|
||||
return reply.NetDBHighCapacityPeers, err
|
||||
}
|
||||
|
||||
// NetDBIsReseeding returns is the router reseeding hosts to its NetDB?
|
||||
func (c *Client) NetDBIsReseeding() (bool, error) {
|
||||
reply, err := c.RouterInfo(RouterInfo{
|
||||
NetDBIsReseeding: true,
|
||||
})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("NetDBIsReseeding failed: %w", err)
|
||||
}
|
||||
return reply.NetDBIsReseeding, err
|
||||
}
|
||||
|
||||
// NetDBKnownPeers returns how many peers are known to us (listed in our NetDB).
|
||||
func (c *Client) NetDBKnownPeers() (int, error) {
|
||||
reply, err := c.RouterInfo(RouterInfo{
|
||||
NetDBKnownPeers: nonEmptyint,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("NetDBKnownPeers failed: %w", err)
|
||||
}
|
||||
return reply.NetDBKnownPeers, err
|
||||
}
|
||||
|
||||
// RouterManager wrappers
|
||||
|
||||
// BlockingFindUpdates initiates a search for signed updates.
|
||||
func (c *Client) BlockingFindUpdates() (bool, error) {
|
||||
reply, err := c.RouterManager(RouterManager{
|
||||
FindUpdates: true,
|
||||
})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("BlockingFindUpdates failed: %w", err)
|
||||
}
|
||||
return reply.FindUpdates, err
|
||||
}
|
||||
|
||||
// Reseed initiates a router reseed, fetching peers into our NetDB from a remote host.
|
||||
func (c *Client) Reseed() error {
|
||||
_, err := c.RouterManager(RouterManager{
|
||||
Reseed: "",
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Reseed failed: %w", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Restart restarts the router.
|
||||
func (c *Client) Restart() error {
|
||||
_, err := c.RouterManager(RouterManager{
|
||||
Restart: "",
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Restart failed: %w", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// RestartGraceful restarts the router gracefully (waits for participating tunnels to expire).
|
||||
func (c *Client) RestartGraceful() error {
|
||||
_, err := c.RouterManager(RouterManager{
|
||||
RestartGraceful: "",
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("RestartGraceful failed: %w", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Shutdown shuts down the router.
|
||||
func (c *Client) Shutdown() error {
|
||||
_, err := c.RouterManager(RouterManager{
|
||||
Shutdown: "",
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Shutdown failed: %w", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ShutdownGraceful shuts down the router.
|
||||
func (c *Client) ShutdownGraceful() error {
|
||||
_, err := c.RouterManager(RouterManager{
|
||||
ShutdownGraceful: "",
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("ShutdownGraceful failed: %w", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Update initiates a router update from signed sources.
|
||||
func (c *Client) Update() error {
|
||||
_, err := c.RouterManager(RouterManager{
|
||||
Update: "",
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Update failed: %w", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// NetworkSetting wrappers
|
||||
|
||||
// NTCPPort gets/sets the port used for the TCP transport.
|
||||
func (c *Client) NTCPPort(port string) (string, error) {
|
||||
reply, err := c.NetworkSetting(NetworkSetting{
|
||||
NTCPPort: port,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("NTCPPort failed: %w", err)
|
||||
}
|
||||
return reply.NTCPPort, err
|
||||
}
|
||||
|
||||
// NTCPHostname gets/sets hostname is used for the TCP transport.
|
||||
func (c *Client) NTCPHostname(hostname string) (string, error) {
|
||||
reply, err := c.NetworkSetting(NetworkSetting{
|
||||
NTCPHostname: hostname,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("NTCPHostname failed: %w", err)
|
||||
}
|
||||
return reply.NTCPHostname, err
|
||||
}
|
||||
|
||||
// NTCPAutoIP use automatically detected ip for TCP transport.
|
||||
func (c *Client) NTCPAutoIP(setting string) (string, error) {
|
||||
reply, err := c.NetworkSetting(NetworkSetting{
|
||||
NTCPAutoIP: setting,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("NTCPAutoIP failed: %w", err)
|
||||
}
|
||||
return reply.NTCPAutoIP, err
|
||||
}
|
||||
|
||||
// SSUPort what port is used for the UDP transport.
|
||||
func (c *Client) SSUPort(port string) (string, error) {
|
||||
reply, err := c.NetworkSetting(NetworkSetting{
|
||||
SSUPort: port,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("SSUPort failed: %w", err)
|
||||
}
|
||||
return reply.SSUPort, err
|
||||
}
|
||||
|
||||
// SSUHostname what hostname is used for the UDP transport.
|
||||
func (c *Client) SSUHostname(hostname string) (string, error) {
|
||||
reply, err := c.NetworkSetting(NetworkSetting{
|
||||
SSUHostname: hostname,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("SSUHostname failed: %w", err)
|
||||
}
|
||||
return reply.SSUHostname, err
|
||||
}
|
||||
|
||||
// SSUAutoIP which methods should be used for detecting the ip address of the UDP transport.
|
||||
func (c *Client) SSUAutoIP(setting string) (string, error) {
|
||||
reply, err := c.NetworkSetting(NetworkSetting{
|
||||
SSUAutoIP: setting,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("SSUAutoIP failed: %w", err)
|
||||
}
|
||||
return reply.SSUAutoIP, err
|
||||
}
|
||||
|
||||
// SSUDetectedIP what ip has been detected by the UDP transport.
|
||||
func (c *Client) SSUDetectedIP() (string, error) {
|
||||
reply, err := c.NetworkSetting(NetworkSetting{
|
||||
SSUDetectedIP: nonEmptyString,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("SSUDetectedIP failed: %w", err)
|
||||
}
|
||||
return reply.SSUDetectedIP, nil
|
||||
}
|
||||
|
||||
// NetUPNP is UPnP enabled?
|
||||
func (c *Client) NetUPNP() (string, error) {
|
||||
reply, err := c.NetworkSetting(NetworkSetting{
|
||||
NetUPNP: nonEmptyString,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("NetUPNP failed: %w", err)
|
||||
}
|
||||
return reply.SSUDetectedIP, nil
|
||||
}
|
||||
|
||||
// NetBWShare how many percent of bandwidth is usable for participating tunnels.
|
||||
func (c *Client) NetBWShare() (string, error) {
|
||||
reply, err := c.NetworkSetting(NetworkSetting{
|
||||
NetBWShare: nonEmptyString,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("NetBWShare failed: %w", err)
|
||||
}
|
||||
return reply.NetBWShare, nil
|
||||
}
|
||||
|
||||
// NetBWIn how many KB/s of inbound bandwidth is allowed.
|
||||
func (c *Client) NetBWIn() (string, error) {
|
||||
reply, err := c.NetworkSetting(NetworkSetting{
|
||||
NetBWIn: nonEmptyString,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("NetBWIn failed: %w", err)
|
||||
}
|
||||
return reply.NetBWIn, nil
|
||||
}
|
||||
|
||||
// NetBWOut How many KB/s of outbound bandwidth is allowed.
|
||||
func (c *Client) NetBWOut() (string, error) {
|
||||
reply, err := c.NetworkSetting(NetworkSetting{
|
||||
NetBWOut: nonEmptyString,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("NetBWOut failed: %w", err)
|
||||
}
|
||||
return reply.NetBWOut, nil
|
||||
}
|
||||
|
||||
// NetLaptopMode is laptop mode enabled (change router identity and UDP port when IP changes).
|
||||
func (c *Client) NetLaptopMode() (string, error) {
|
||||
reply, err := c.NetworkSetting(NetworkSetting{
|
||||
NetLaptopMode: nonEmptyString,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("NetLaptopMode failed: %w", err)
|
||||
}
|
||||
return reply.NetLaptopMode, nil
|
||||
}
|
||||
|
||||
// AdvancedSettings wrappers
|
||||
|
||||
// GetAll lists key value options for AdvancedSettings
|
||||
func (c *Client) GetAll() (map[string]interface{}, error) {
|
||||
reply, err := c.Send("AdvancedSettings", map[string]interface{}{
|
||||
"getAll": nonEmptyString,
|
||||
})
|
||||
if err != nil || reply.Result == nil {
|
||||
return nil, fmt.Errorf("GetAll failed: %w", err)
|
||||
}
|
||||
return reply.Result, err
|
||||
}
|
55
backend/i2pcontrol.go
Normal file
55
backend/i2pcontrol.go
Normal file
@ -0,0 +1,55 @@
|
||||
package backend
|
||||
|
||||
import "fmt"
|
||||
|
||||
// I2PControl manages I2PControl. Ports, passwords and the like.
|
||||
type I2PControl struct {
|
||||
Address string `json:",omitempty"` // Sets a new listen address for I2PControl (only 127.0.0.1 and 0.0.0.0 are implemented in I2PControl currently).
|
||||
Password string `json:",omitempty"` // Sets a new password for I2PControl, all Authentication tokens will be revoked.
|
||||
Port string `json:",omitempty"` // Switches which port I2PControl will listen for connections on.
|
||||
Token string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// I2PControlReply contains an I2PControl reply.
|
||||
type I2PControlReply struct {
|
||||
*I2PControl
|
||||
SettingsSaved bool // Returns true if any changes were made.
|
||||
RestartNeeded bool // Returns true if any changes requiring a restart to take effect were made.
|
||||
}
|
||||
|
||||
// I2PControl manages I2PControl. Ports, passwords and the like.
|
||||
func (c *Client) I2PControl(i2pcontrol I2PControl) (I2PControlReply, error) {
|
||||
params := make(map[string]interface{})
|
||||
if i2pcontrol.Address != "" {
|
||||
params["i2pcontrol.address"] = i2pcontrol.Address
|
||||
}
|
||||
if i2pcontrol.Password != "" {
|
||||
params["i2pcontrol.password"] = i2pcontrol.Password
|
||||
}
|
||||
if i2pcontrol.Port != "" {
|
||||
params["i2pcontrol.port"] = i2pcontrol.Port
|
||||
}
|
||||
|
||||
reply, err := c.Send("I2PControl", params)
|
||||
result := I2PControlReply{}
|
||||
|
||||
if err != nil || reply.Result == nil {
|
||||
return result, fmt.Errorf("I2PControl failed: %w", err)
|
||||
}
|
||||
if reply.Result["i2pcontrol.address"] != nil {
|
||||
result.Address = reply.Result["Address"].(string)
|
||||
}
|
||||
if reply.Result["i2pcontrol.password"] != nil {
|
||||
result.Address = reply.Result["i2pcontrol.password"].(string)
|
||||
}
|
||||
if reply.Result["i2pcontrol.port"] != nil {
|
||||
result.Address = reply.Result["i2pcontrol.port"].(string)
|
||||
}
|
||||
if reply.Result["SettingsSaved"] == "true" {
|
||||
result.SettingsSaved = true
|
||||
}
|
||||
if reply.Result["RestartNeeded"] == "true" {
|
||||
result.RestartNeeded = true
|
||||
}
|
||||
return result, err
|
||||
}
|
111
backend/networksetting.go
Normal file
111
backend/networksetting.go
Normal file
@ -0,0 +1,111 @@
|
||||
package backend
|
||||
|
||||
import "fmt"
|
||||
|
||||
// NetworkSetting fetches or sets various network related settings. Ports, addresses etc.
|
||||
type NetworkSetting struct {
|
||||
NTCPPort string // What port is used for the TCP transport.
|
||||
NTCPHostname string // What hostname is used for the TCP transport.
|
||||
NTCPAutoIP string // Use automatically detected ip for TCP transport.
|
||||
SSUPort string // What port is used for the UDP transport.
|
||||
SSUHostname string // What hostname is used for the UDP transport.
|
||||
SSUAutoIP string // Which methods should be used for detecting the ip address of the UDP transport.
|
||||
SSUDetectedIP string // What ip has been detected by the UDP transport.
|
||||
NetUPNP string // Is UPnP enabled.
|
||||
NetBWShare string // How many percent of bandwidth is usable for participating tunnels.
|
||||
NetBWIn string // How many KB/s of inbound bandwidth is allowed.
|
||||
NetBWOut string // How many KB/s of outbound bandwidth is allowed.
|
||||
NetLaptopMode string // Is laptop mode enabled (change router identity and UDP port when IP changes).
|
||||
}
|
||||
|
||||
// NetworkSettingReply contains a NetworkSetting reply.
|
||||
type NetworkSettingReply struct {
|
||||
*NetworkSetting
|
||||
SettingsSaved bool // Have the provided settings been saved.
|
||||
RestartNeeded bool // Is a restart needed for the new settings to be used.
|
||||
}
|
||||
|
||||
// NetworkSetting fetches or sets various network related settings. Ports, addresses etc.
|
||||
func (c *Client) NetworkSetting(ns NetworkSetting) (NetworkSettingReply, error) {
|
||||
params := make(map[string]interface{})
|
||||
if ns.NTCPPort != "" {
|
||||
params["i2p.router.net.ntcp.port"] = ns.NTCPPort
|
||||
}
|
||||
if ns.NTCPHostname != "" {
|
||||
params["i2p.router.net.ntcp.hostname"] = ns.NTCPHostname
|
||||
}
|
||||
if ns.NTCPAutoIP != "" {
|
||||
params["i2p.router.net.ntcp.autoip"] = ns.NTCPAutoIP
|
||||
}
|
||||
if ns.SSUPort != "" {
|
||||
params["i2p.router.net.ssu.port"] = ns.SSUPort
|
||||
}
|
||||
if ns.SSUHostname != "" {
|
||||
params["i2p.router.net.ssu.hostname"] = ns.SSUHostname
|
||||
}
|
||||
if ns.SSUAutoIP != "" {
|
||||
params["i2p.router.net.ssu.autoip"] = ns.SSUAutoIP
|
||||
}
|
||||
if ns.SSUDetectedIP != "" {
|
||||
params["i2p.router.net.ssu.detectedip"] = ns.SSUDetectedIP
|
||||
}
|
||||
if ns.NetUPNP != "" {
|
||||
params["i2p.router.net.upnp"] = ns.NetUPNP
|
||||
}
|
||||
if ns.NetBWShare != "" {
|
||||
params["i2p.router.net.bw.share"] = ns.NetBWShare
|
||||
}
|
||||
if ns.NetBWIn != "" {
|
||||
params["i2p.router.net.bw.in"] = ns.NetBWIn
|
||||
}
|
||||
if ns.NetBWOut != "" {
|
||||
params["i2p.router.net.bw.out"] = ns.NetBWOut
|
||||
}
|
||||
if ns.NetLaptopMode != "" {
|
||||
params["i2p.router.net.laptopmode"] = ns.NetLaptopMode
|
||||
}
|
||||
|
||||
reply, err := c.Send("NetworkSetting", params)
|
||||
result := NetworkSettingReply{}
|
||||
|
||||
if err != nil || reply.Result == nil {
|
||||
return result, fmt.Errorf("NetworkSetting failed: %w", err)
|
||||
}
|
||||
if reply.Result["i2p.router.net.ntcp.port"] != "" {
|
||||
result.NTCPPort = reply.Result["i2p.router.net.ntcp.port"].(string)
|
||||
}
|
||||
if reply.Result["i2p.router.net.ntcp.hostname"] != "" {
|
||||
result.NTCPHostname = reply.Result["i2p.router.net.ntcp.hostname"].(string)
|
||||
}
|
||||
if reply.Result["i2p.router.net.ntcp.autoip"] != "" {
|
||||
result.NTCPAutoIP = reply.Result["i2p.router.net.ntcp.autoip"].(string)
|
||||
}
|
||||
if reply.Result["i2p.router.net.ssu.port"] != "" {
|
||||
result.SSUPort = reply.Result["i2p.router.net.ssu.port"].(string)
|
||||
}
|
||||
if reply.Result["i2p.router.net.ssu.hostname"] != "" {
|
||||
result.SSUHostname = reply.Result["i2p.router.net.ssu.hostname"].(string)
|
||||
}
|
||||
if reply.Result["i2p.router.net.ssu.autoip"] != "" {
|
||||
result.SSUAutoIP = reply.Result["i2p.router.net.ssu.autoip"].(string)
|
||||
}
|
||||
if reply.Result["i2p.router.net.ssu.detectedip"] != "" {
|
||||
result.SSUDetectedIP = reply.Result["i2p.router.net.ssu.detectedip"].(string)
|
||||
}
|
||||
if reply.Result["i2p.router.net.upnp"] != "" {
|
||||
result.NetUPNP = reply.Result["i2p.router.net.upnp"].(string)
|
||||
}
|
||||
if reply.Result["i2p.router.net.bw.share"] != "" {
|
||||
result.NetBWShare = reply.Result["i2p.router.net.bw.share"].(string)
|
||||
}
|
||||
if reply.Result["i2p.router.net.bw.in"] != "" {
|
||||
result.NetBWIn = reply.Result["i2p.router.net.bw.in"].(string)
|
||||
}
|
||||
if reply.Result["i2p.router.net.bw.out"] != "" {
|
||||
result.NetBWOut = reply.Result["i2p.router.net.bw.out"].(string)
|
||||
}
|
||||
if reply.Result["i2p.router.net.laptopmode"] != "" {
|
||||
result.NetLaptopMode = reply.Result["i2p.router.net.laptopmode"].(string)
|
||||
}
|
||||
return result, err
|
||||
}
|
127
backend/routerinfo.go
Normal file
127
backend/routerinfo.go
Normal file
@ -0,0 +1,127 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// RouterInfo fetches basic information about the I2P router. Uptime, version etc.
|
||||
type RouterInfo struct {
|
||||
Status string
|
||||
Uptime int
|
||||
Version string
|
||||
|
||||
NetBwInbound1s int
|
||||
NetBwInbound15s int
|
||||
NetBwOutbound1s int
|
||||
NetBwOutbound15s int
|
||||
NetStatus int
|
||||
NetTunnelsParticipating int
|
||||
|
||||
NetDBActivePeers int
|
||||
NetDBFastPeers int
|
||||
NetDBHighCapacityPeers int
|
||||
NetDBIsReseeding bool
|
||||
NetDBKnownPeers int
|
||||
}
|
||||
|
||||
// RouterInfo fetches basic information about the I2P router. Uptime, version etc.
|
||||
func (c *Client) RouterInfo(ri RouterInfo) (RouterInfo, error) {
|
||||
params := make(map[string]interface{})
|
||||
if ri.Status != "" {
|
||||
params["i2p.router.status"] = nil
|
||||
}
|
||||
if ri.Uptime != 0 {
|
||||
params["i2p.router.uptime"] = nil
|
||||
}
|
||||
if ri.Version != "" {
|
||||
params["i2p.router.version"] = nil
|
||||
}
|
||||
|
||||
if ri.NetBwInbound1s != 0 {
|
||||
params["i2p.router.net.bw.inbound.1s"] = nil
|
||||
}
|
||||
if ri.NetBwInbound15s != 0 {
|
||||
params["i2p.router.net.bw.inbound.15s"] = nil
|
||||
}
|
||||
if ri.NetBwOutbound1s != 0 {
|
||||
params["i2p.router.net.bw.outbound.1s"] = nil
|
||||
}
|
||||
if ri.NetBwOutbound15s != 0 {
|
||||
params["i2p.router.net.bw.outbound.15s"] = nil
|
||||
}
|
||||
if ri.NetStatus != 0 {
|
||||
params["i2p.router.net.status"] = nil
|
||||
}
|
||||
if ri.NetTunnelsParticipating != 0 {
|
||||
params["i2p.router.net.tunnels.participating"] = nil
|
||||
}
|
||||
|
||||
if ri.NetDBActivePeers != 0 {
|
||||
params["i2p.router.netdb.activepeers"] = nil
|
||||
}
|
||||
if ri.NetDBFastPeers != 0 {
|
||||
params["i2p.router.netdb.fastpeers"] = nil
|
||||
}
|
||||
if ri.NetDBHighCapacityPeers != 0 {
|
||||
params["i2p.router.netdb.highcapacitypeers"] = nil
|
||||
}
|
||||
if ri.NetDBIsReseeding != false {
|
||||
params["i2p.router.netdb.isreseeding"] = nil
|
||||
}
|
||||
if ri.NetDBKnownPeers != 0 {
|
||||
params["i2p.router.netdb.knownpeers"] = nil
|
||||
}
|
||||
|
||||
reply, err := c.Send("RouterInfo", params)
|
||||
result := RouterInfo{}
|
||||
|
||||
if err != nil || reply.Result == nil {
|
||||
return result, fmt.Errorf("RouterInfo failed: %w", err)
|
||||
}
|
||||
|
||||
if reply.Result["i2p.router.status"] != nil {
|
||||
result.Status = reply.Result["i2p.router.status"].(string)
|
||||
}
|
||||
if reply.Result["i2p.router.uptime"] != nil {
|
||||
result.Uptime = int(reply.Result["i2p.router.uptime"].(float64))
|
||||
}
|
||||
if reply.Result["i2p.router.version"] != nil {
|
||||
result.Version = reply.Result["i2p.router.version"].(string)
|
||||
}
|
||||
if reply.Result["i2p.router.net.bw.inbound.1s"] != nil {
|
||||
result.NetBwInbound1s = int(reply.Result["i2p.router.net.bw.inbound.1s"].(float64))
|
||||
}
|
||||
if reply.Result["i2p.router.net.bw.inbound.15s"] != nil {
|
||||
result.NetBwInbound15s = int(reply.Result["i2p.router.net.bw.inbound.15s"].(float64))
|
||||
}
|
||||
if reply.Result["i2p.router.net.bw.outbound.1s"] != nil {
|
||||
result.NetBwOutbound1s = int(reply.Result["i2p.router.net.bw.outbound.1s"].(float64))
|
||||
}
|
||||
if reply.Result["i2p.router.net.bw.outbound.15s"] != nil {
|
||||
result.NetBwOutbound15s = int(reply.Result["i2p.router.net.bw.outbound.15s"].(float64))
|
||||
}
|
||||
if reply.Result["i2p.router.net.status"] != nil {
|
||||
result.NetStatus = int(reply.Result["i2p.router.net.status"].(float64))
|
||||
}
|
||||
if reply.Result["i2p.router.net.tunnels.participating"] != nil {
|
||||
result.NetTunnelsParticipating = int(reply.Result["i2p.router.net.tunnels.participating"].(float64))
|
||||
}
|
||||
if reply.Result["i2p.router.netdb.activepeers"] != nil {
|
||||
result.NetDBActivePeers = int(reply.Result["i2p.router.netdb.activepeers"].(float64))
|
||||
}
|
||||
if reply.Result["i2p.router.netdb.fastpeers"] != nil {
|
||||
result.NetDBFastPeers = int(reply.Result["i2p.router.netdb.fastpeers"].(float64))
|
||||
}
|
||||
if reply.Result["i2p.router.netdb.highcapacitypeers"] != nil {
|
||||
result.NetDBHighCapacityPeers = int(reply.Result["i2p.router.netdb.highcapacitypeers"].(float64))
|
||||
}
|
||||
if reply.Result["i2p.router.netdb.isreseeding"] != "" {
|
||||
if reply.Result["i2p.router.netdb.isreseeding"] == "true" {
|
||||
result.NetDBIsReseeding = true
|
||||
}
|
||||
}
|
||||
if reply.Result["i2p.router.netdb.knownpeers"] != nil {
|
||||
result.NetDBKnownPeers = int(reply.Result["i2p.router.netdb.knownpeers"].(float64))
|
||||
}
|
||||
return result, err
|
||||
}
|
71
backend/routermanager.go
Normal file
71
backend/routermanager.go
Normal file
@ -0,0 +1,71 @@
|
||||
package backend
|
||||
|
||||
import "fmt"
|
||||
|
||||
// RouterManager manages I2P router restart/shutdown.
|
||||
type RouterManager struct {
|
||||
FindUpdates bool // Blocking. Initiates a search for signed updates.
|
||||
Reseed string // Initiates a router reseed, fetching peers into our NetDB from a remote host.
|
||||
Restart string // Restarts the router.
|
||||
RestartGraceful string // Restarts the router gracefully (waits for participating tunnels to expire).
|
||||
Shutdown string // Shuts down the router.
|
||||
ShutdownGraceful string // Shuts down the router gracefully (waits for participating tunnels to expire).
|
||||
Update string // Initiates a router update from signed sources.
|
||||
}
|
||||
|
||||
// RouterManager manages I2P router restart/shutdown.
|
||||
func (c *Client) RouterManager(rm RouterManager) (RouterManager, error) {
|
||||
params := make(map[string]interface{})
|
||||
if rm.FindUpdates != false {
|
||||
params["FindUpdates"] = nil
|
||||
}
|
||||
if rm.Reseed != "" {
|
||||
params["Reseed"] = nil
|
||||
}
|
||||
if rm.Restart != "" {
|
||||
params["Restart"] = nil
|
||||
}
|
||||
if rm.RestartGraceful != "" {
|
||||
params["RestartGraceful"] = nil
|
||||
}
|
||||
if rm.Shutdown != "" {
|
||||
params["Shutdown"] = nil
|
||||
}
|
||||
if rm.ShutdownGraceful != "" {
|
||||
params["ShutdownGraceful"] = nil
|
||||
}
|
||||
if rm.Update != "" {
|
||||
params["Update"] = nil
|
||||
}
|
||||
|
||||
reply, err := c.Send("RouterManager", params)
|
||||
result := RouterManager{}
|
||||
|
||||
if err != nil || reply.Result == nil {
|
||||
return result, fmt.Errorf("RouterManager failed: %w", err)
|
||||
}
|
||||
if reply.Result["FindUpdates"] != "" {
|
||||
if reply.Result["FindUpdates"] == "true" {
|
||||
result.FindUpdates = true
|
||||
}
|
||||
}
|
||||
if reply.Result["Reseed"] != nil {
|
||||
result.Reseed = reply.Result["Reseed"].(string)
|
||||
}
|
||||
if reply.Result["Restart"] != nil {
|
||||
result.Restart = reply.Result["Restart"].(string)
|
||||
}
|
||||
if reply.Result["RestartGraceful"] != nil {
|
||||
result.RestartGraceful = reply.Result["RestartGraceful"].(string)
|
||||
}
|
||||
if reply.Result["Shutdown"] != nil {
|
||||
result.Shutdown = reply.Result["Shutdown"].(string)
|
||||
}
|
||||
if reply.Result["ShutdownGraceful"] != nil {
|
||||
result.ShutdownGraceful = reply.Result["ShutdownGraceful"].(string)
|
||||
}
|
||||
if reply.Result["Update"] != nil {
|
||||
result.Update = reply.Result["Update"].(string)
|
||||
}
|
||||
return result, err
|
||||
}
|
56
frontend/accordionmenus.go
Normal file
56
frontend/accordionmenus.go
Normal file
@ -0,0 +1,56 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"fyne.io/fyne/theme"
|
||||
"fyne.io/fyne/widget"
|
||||
)
|
||||
|
||||
func renderI2PApplications() *widget.AccordionItem {
|
||||
return widget.NewAccordionItem("I2P Applications",
|
||||
widget.NewVBox(
|
||||
&widget.Button{
|
||||
Text: "I2P Email",
|
||||
Icon: theme.MailComposeIcon(),
|
||||
Alignment: widget.ButtonAlignLeading,
|
||||
IconPlacement: 0,
|
||||
OnTapped: func() { log.Println("Not yet implemented") },
|
||||
},
|
||||
&widget.Button{
|
||||
Text: "Torrent",
|
||||
Icon: theme.DownloadIcon(),
|
||||
Alignment: widget.ButtonAlignLeading,
|
||||
IconPlacement: 0,
|
||||
OnTapped: func() { log.Println("Not yet implemented") },
|
||||
},
|
||||
&widget.Button{
|
||||
Text: "Web Server",
|
||||
Icon: theme.ComputerIcon(),
|
||||
Alignment: widget.ButtonAlignLeading,
|
||||
IconPlacement: 0,
|
||||
OnTapped: func() { log.Println("Not yet implemented") },
|
||||
},
|
||||
&widget.Button{
|
||||
Text: "Address Book",
|
||||
Icon: theme.FileApplicationIcon(),
|
||||
Alignment: widget.ButtonAlignLeading,
|
||||
IconPlacement: 0,
|
||||
OnTapped: func() { log.Println("Not yet implemented") },
|
||||
},
|
||||
&widget.Button{
|
||||
Text: "I2P Network Manager",
|
||||
Icon: theme.MoveDownIcon(),
|
||||
Alignment: widget.ButtonAlignLeading,
|
||||
IconPlacement: 0,
|
||||
OnTapped: func() { log.Println("Not yet implemented") },
|
||||
},
|
||||
&widget.Button{
|
||||
Text: "Plugins",
|
||||
Icon: theme.FileVideoIcon(),
|
||||
Alignment: widget.ButtonAlignLeading,
|
||||
IconPlacement: 0,
|
||||
OnTapped: func() { log.Println("Not yet implemented") },
|
||||
},
|
||||
))
|
||||
}
|
15
frontend/bundled.go
Normal file
15
frontend/bundled.go
Normal file
File diff suppressed because one or more lines are too long
30
frontend/content.go
Normal file
30
frontend/content.go
Normal file
@ -0,0 +1,30 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"fyne.io/fyne"
|
||||
"fyne.io/fyne/canvas"
|
||||
"fyne.io/fyne/layout"
|
||||
"fyne.io/fyne/widget"
|
||||
)
|
||||
|
||||
func (f *frontend) renderContent() *fyne.Container {
|
||||
return fyne.NewContainerWithLayout(
|
||||
layout.NewVBoxLayout(),
|
||||
renderLogoImage(),
|
||||
fyne.NewContainerWithLayout(layout.NewCenterLayout(), f.vbox),
|
||||
f.renderAccordionMenus(),
|
||||
layout.NewSpacer())
|
||||
}
|
||||
|
||||
func renderLogoImage() *canvas.Image {
|
||||
l := canvas.NewImageFromResource(resourceLogoPng)
|
||||
l.FillMode = canvas.ImageFillContain
|
||||
l.SetMinSize(fyne.NewSize(172, 172))
|
||||
return l
|
||||
}
|
||||
|
||||
func (f *frontend) renderAccordionMenus() *widget.AccordionContainer {
|
||||
ac := widget.NewAccordionContainer()
|
||||
ac.Append(renderI2PApplications())
|
||||
return ac
|
||||
}
|
28
frontend/footer.go
Normal file
28
frontend/footer.go
Normal file
@ -0,0 +1,28 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"fyne.io/fyne"
|
||||
"fyne.io/fyne/layout"
|
||||
"fyne.io/fyne/theme"
|
||||
"fyne.io/fyne/widget"
|
||||
)
|
||||
|
||||
func (f *frontend) renderFooter() *fyne.Container {
|
||||
return fyne.NewContainerWithLayout(layout.NewGridLayoutWithColumns(2),
|
||||
&widget.Button{
|
||||
Text: "Router Console",
|
||||
Icon: theme.ViewRefreshIcon(),
|
||||
Alignment: widget.ButtonAlignCenter,
|
||||
IconPlacement: 0,
|
||||
OnTapped: func() { log.Println("Not yet implemented") },
|
||||
},
|
||||
&widget.Button{
|
||||
Text: "Settings",
|
||||
Icon: theme.SettingsIcon(),
|
||||
Alignment: widget.ButtonAlignCenter,
|
||||
IconPlacement: 0,
|
||||
OnTapped: func() { log.Println("Not yet implemented") },
|
||||
})
|
||||
}
|
63
frontend/frontend.go
Normal file
63
frontend/frontend.go
Normal file
@ -0,0 +1,63 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"fyne.io/fyne"
|
||||
"fyne.io/fyne/app"
|
||||
"fyne.io/fyne/layout"
|
||||
"fyne.io/fyne/widget"
|
||||
|
||||
"github.com/kpetku/gotoopie/backend"
|
||||
)
|
||||
|
||||
type frontend struct {
|
||||
client *backend.Client
|
||||
app fyne.App
|
||||
window fyne.Window
|
||||
vbox *widget.Box
|
||||
}
|
||||
|
||||
// Start launches the GUI frontend.
|
||||
func Start() {
|
||||
f := startFrontendAndBackend()
|
||||
err := f.client.Authenticate()
|
||||
if err != nil {
|
||||
log.Printf("Error authenticating: %s", err)
|
||||
}
|
||||
f.startGUI()
|
||||
}
|
||||
|
||||
func startFrontendAndBackend() *frontend {
|
||||
f := new(frontend)
|
||||
f.client = backend.NewClient(&backend.Options{
|
||||
Address: "localhost",
|
||||
Port: "7650",
|
||||
Password: "itoopie",
|
||||
})
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *frontend) startGUI() {
|
||||
f.vbox = widget.NewVBox()
|
||||
f.app = app.New()
|
||||
f.window = f.app.NewWindow("gotoopie")
|
||||
|
||||
f.renderNetworkStatus()
|
||||
f.renderPeers()
|
||||
f.renderOther()
|
||||
f.renderAccordionMenus()
|
||||
|
||||
header := f.renderHeader()
|
||||
content := f.renderContent()
|
||||
footer := f.renderFooter()
|
||||
|
||||
layout := layout.NewBorderLayout(header, footer, nil, nil)
|
||||
f.window.SetContent(fyne.NewContainerWithLayout(layout, header, footer, content))
|
||||
|
||||
fyne.CurrentApp().Settings().SetTheme(newToopieTheme())
|
||||
f.window.SetIcon(resourceLogoPng)
|
||||
f.window.Resize(fyne.NewSize(400, 800))
|
||||
f.window.SetFixedSize(true)
|
||||
f.window.ShowAndRun()
|
||||
}
|
4
frontend/gen.sh
Executable file
4
frontend/gen.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
fyne bundle -package frontend ./resources/curve.png > bundled.go
|
||||
fyne bundle -package frontend -append ./resources/logo.png >> bundled.go
|
||||
|
38
frontend/header.go
Normal file
38
frontend/header.go
Normal file
@ -0,0 +1,38 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"fyne.io/fyne"
|
||||
"fyne.io/fyne/canvas"
|
||||
"fyne.io/fyne/layout"
|
||||
"fyne.io/fyne/theme"
|
||||
"fyne.io/fyne/widget"
|
||||
)
|
||||
|
||||
func (f *frontend) renderHeader() *fyne.Container {
|
||||
top := fyne.NewContainerWithLayout(layout.NewGridLayout(1),
|
||||
renderToolbar(),
|
||||
fyne.NewContainerWithLayout(layout.NewCenterLayout(), renderBackgroundImage(), &widget.Button{
|
||||
Text: "Disconnect",
|
||||
Icon: theme.VisibilityOffIcon(),
|
||||
Alignment: widget.ButtonAlignCenter,
|
||||
IconPlacement: 0,
|
||||
OnTapped: func() { log.Println("Not yet implemented") },
|
||||
}))
|
||||
return top
|
||||
}
|
||||
|
||||
func renderToolbar() *widget.Toolbar {
|
||||
return widget.NewToolbar(
|
||||
widget.NewToolbarSpacer(),
|
||||
widget.NewToolbarAction(theme.HelpIcon(), func() { log.Println("Not yet implemented") }),
|
||||
)
|
||||
}
|
||||
|
||||
func renderBackgroundImage() *canvas.Image {
|
||||
b := canvas.NewImageFromResource(resourceCurvePng)
|
||||
b.FillMode = canvas.ImageFillStretch
|
||||
b.SetMinSize(fyne.NewSize(400, 50))
|
||||
return b
|
||||
}
|
48
frontend/networkstatus.go
Normal file
48
frontend/networkstatus.go
Normal file
@ -0,0 +1,48 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"fyne.io/fyne"
|
||||
"fyne.io/fyne/widget"
|
||||
)
|
||||
|
||||
func (f *frontend) renderNetworkStatus() error {
|
||||
ns, err := f.client.NetStatus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var out string
|
||||
switch ns {
|
||||
case 0:
|
||||
out = "Network OK"
|
||||
case 1:
|
||||
out = "Testing Network"
|
||||
case 2:
|
||||
out = "Firewalled"
|
||||
case 3:
|
||||
out = "Hidden"
|
||||
case 4:
|
||||
out = "Warn: Firewalled and fast"
|
||||
case 5:
|
||||
out = "Warn: Firewalled and floodfill"
|
||||
case 6:
|
||||
out = "Warn: Firewalled with inbound TCP"
|
||||
case 7:
|
||||
out = "Warn: Firewalled with UDP disabled"
|
||||
case 8:
|
||||
out = "Error: I2CP"
|
||||
case 9:
|
||||
out = " Error: Clock Skew"
|
||||
case 10:
|
||||
out = "Error: Private TCP address"
|
||||
case 11:
|
||||
out = "Error: Symmetric NAT"
|
||||
case 12:
|
||||
out = "Error: UDP port in use"
|
||||
case 13:
|
||||
out = "Error: No active peers check connection and firewall"
|
||||
case 14:
|
||||
out = "Error: UDP disabled and TCP unset"
|
||||
}
|
||||
f.vbox.Append(widget.NewLabelWithStyle(out, fyne.TextAlignCenter, fyne.TextStyle{Bold: true}))
|
||||
return nil
|
||||
}
|
40
frontend/other.go
Normal file
40
frontend/other.go
Normal file
@ -0,0 +1,40 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/widget"
|
||||
)
|
||||
|
||||
func (f *frontend) renderOther() error {
|
||||
var u string
|
||||
version, err := f.client.RouterVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uptime, err := f.client.RouterUptime()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hours := uptime / 3600000
|
||||
days := (uptime / 86400000) % 7
|
||||
weeks := uptime / (86400000 * 7)
|
||||
if hours < 48 {
|
||||
u = time.Duration(int64(uptime / 3600000)).String()
|
||||
} else if hours > 48 {
|
||||
u = strconv.Itoa(days) + " days"
|
||||
} else if days > 13 {
|
||||
u = strconv.Itoa(weeks) + " weeks"
|
||||
}
|
||||
status, err := f.client.RouterStatus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.vbox.Append(widget.NewGroup("Other",
|
||||
widget.NewLabel("Version: "+version),
|
||||
widget.NewLabel("Uptime: "+u),
|
||||
widget.NewLabel("Status: "+status),
|
||||
))
|
||||
return nil
|
||||
}
|
36
frontend/peers.go
Normal file
36
frontend/peers.go
Normal file
@ -0,0 +1,36 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"fyne.io/fyne/widget"
|
||||
)
|
||||
|
||||
func (f *frontend) renderPeers() error {
|
||||
knownPeers, err := f.client.NetDBKnownPeers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
activePeers, err := f.client.NetDBActivePeers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fastPeers, err := f.client.NetDBFastPeers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
participating, err := f.client.NetTunnelsParticipating()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.vbox.Append(
|
||||
widget.NewGroup("Peers",
|
||||
widget.NewLabel("Active: "+strconv.Itoa(activePeers)+"/"+strconv.Itoa(knownPeers)),
|
||||
widget.NewLabel("Fast: "+strconv.Itoa(fastPeers)+"/"+strconv.Itoa(knownPeers)),
|
||||
widget.NewLabel("Active: "+strconv.Itoa(knownPeers)),
|
||||
))
|
||||
f.vbox.Append(widget.NewGroup("Tunnels",
|
||||
widget.NewLabel("Participating: "+strconv.Itoa(participating)),
|
||||
))
|
||||
return nil
|
||||
}
|
BIN
frontend/resources/curve.png
Normal file
BIN
frontend/resources/curve.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 MiB |
BIN
frontend/resources/logo.png
Normal file
BIN
frontend/resources/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.3 KiB |
110
frontend/theme.go
Normal file
110
frontend/theme.go
Normal file
@ -0,0 +1,110 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"fyne.io/fyne"
|
||||
"fyne.io/fyne/theme"
|
||||
)
|
||||
|
||||
type toopieTheme struct{}
|
||||
|
||||
func (toopieTheme) BackgroundColor() color.Color {
|
||||
return &color.RGBA{R: 0xf8, G: 0xf9, B: 0xfa, A: 0xff}
|
||||
}
|
||||
|
||||
func (toopieTheme) ButtonColor() color.Color {
|
||||
return &color.RGBA{R: 0x36, G: 0x3a, B: 0x68, A: 0xff}
|
||||
}
|
||||
|
||||
func (toopieTheme) DisabledButtonColor() color.Color {
|
||||
return &color.RGBA{R: 0xe7, G: 0xe7, B: 0xe7, A: 0xff}
|
||||
}
|
||||
|
||||
func (toopieTheme) HyperlinkColor() color.Color {
|
||||
return &color.RGBA{R: 0x1b, G: 0x1d, B: 0x34, A: 0xff}
|
||||
}
|
||||
|
||||
func (toopieTheme) TextColor() color.Color {
|
||||
return &color.RGBA{R: 0x86, G: 0x8e, B: 0x96, A: 0xff}
|
||||
}
|
||||
|
||||
func (toopieTheme) DisabledTextColor() color.Color {
|
||||
return &color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0xff}
|
||||
}
|
||||
|
||||
func (toopieTheme) IconColor() color.Color {
|
||||
return &color.RGBA{R: 0x62, G: 0xa8, B: 0xe6, A: 0xff}
|
||||
}
|
||||
|
||||
func (toopieTheme) DisabledIconColor() color.Color {
|
||||
return &color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0xff}
|
||||
}
|
||||
|
||||
func (toopieTheme) PlaceHolderColor() color.Color {
|
||||
return &color.RGBA{R: 0x88, G: 0x88, B: 0x88, A: 0xff}
|
||||
}
|
||||
|
||||
func (toopieTheme) PrimaryColor() color.Color {
|
||||
return &color.RGBA{R: 0x52, G: 0x6b, B: 0xce, A: 0xff}
|
||||
}
|
||||
|
||||
func (toopieTheme) HoverColor() color.Color {
|
||||
return &color.RGBA{R: 0x62, G: 0x80, B: 0xe6, A: 0xff}
|
||||
}
|
||||
|
||||
func (toopieTheme) FocusColor() color.Color {
|
||||
return &color.RGBA{R: 0x9f, G: 0xa8, B: 0xda, A: 0xff}
|
||||
}
|
||||
|
||||
func (toopieTheme) ScrollBarColor() color.Color {
|
||||
return &color.RGBA{R: 0x0, G: 0x0, B: 0x0, A: 0x99}
|
||||
}
|
||||
|
||||
func (toopieTheme) ShadowColor() color.Color {
|
||||
return &color.RGBA{R: 0x0, G: 0x0, B: 0x0, A: 0x33}
|
||||
}
|
||||
|
||||
func (toopieTheme) TextSize() int {
|
||||
return 12
|
||||
}
|
||||
|
||||
func (toopieTheme) TextFont() fyne.Resource {
|
||||
return theme.DefaultTextFont()
|
||||
}
|
||||
|
||||
func (toopieTheme) TextBoldFont() fyne.Resource {
|
||||
return theme.DefaultTextBoldFont()
|
||||
}
|
||||
|
||||
func (toopieTheme) TextItalicFont() fyne.Resource {
|
||||
return theme.DefaultTextItalicFont()
|
||||
}
|
||||
|
||||
func (toopieTheme) TextBoldItalicFont() fyne.Resource {
|
||||
return theme.DefaultTextBoldItalicFont()
|
||||
}
|
||||
|
||||
func (toopieTheme) TextMonospaceFont() fyne.Resource {
|
||||
return theme.DefaultTextMonospaceFont()
|
||||
}
|
||||
|
||||
func (toopieTheme) Padding() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (toopieTheme) IconInlineSize() int {
|
||||
return 35
|
||||
}
|
||||
|
||||
func (toopieTheme) ScrollBarSize() int {
|
||||
return 10
|
||||
}
|
||||
|
||||
func (toopieTheme) ScrollBarSmallSize() int {
|
||||
return 4
|
||||
}
|
||||
|
||||
func newToopieTheme() fyne.Theme {
|
||||
return &toopieTheme{}
|
||||
}
|
19
main.go
Normal file
19
main.go
Normal file
@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
/* Copyright © 2020 The gootopie authors
|
||||
* This program is free software. It comes without any warranty, to
|
||||
* the extent permitted by applicable law. You can redistribute it
|
||||
* and/or modify it under the terms of the Do What The Fuck You Want
|
||||
* To Public License, Version 2, as published by Sam Hocevar. See
|
||||
* http://www.wtfpl.net/ for more details. */
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/kpetku/gotoopie/frontend"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Printf("Starting frontend")
|
||||
frontend.Start()
|
||||
}
|
Reference in New Issue
Block a user