commit
7833c49c4a
@ -0,0 +1,39 @@ |
|||||||
|
build/bin |
||||||
|
build |
||||||
|
node_modules |
||||||
|
frontend/dist |
||||||
|
.idea |
||||||
|
|
||||||
|
.DS_Store |
||||||
|
.thumbs.db |
||||||
|
node_modules |
||||||
|
|
||||||
|
# Quasar core related directories |
||||||
|
.quasar |
||||||
|
/dist |
||||||
|
/quasar.config.*.temporary.compiled* |
||||||
|
|
||||||
|
# Cordova related directories and files |
||||||
|
/src-cordova/node_modules |
||||||
|
/src-cordova/platforms |
||||||
|
/src-cordova/plugins |
||||||
|
/src-cordova/www |
||||||
|
|
||||||
|
# Capacitor related directories and files |
||||||
|
/src-capacitor/www |
||||||
|
/src-capacitor/node_modules |
||||||
|
|
||||||
|
# Log files |
||||||
|
npm-debug.log* |
||||||
|
yarn-debug.log* |
||||||
|
yarn-error.log* |
||||||
|
|
||||||
|
# Editor directories and files |
||||||
|
.idea |
||||||
|
*.suo |
||||||
|
*.ntvs* |
||||||
|
*.njsproj |
||||||
|
*.sln |
||||||
|
|
||||||
|
# local .env files |
||||||
|
.env.local* |
@ -0,0 +1,180 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"encoding/hex" |
||||||
|
"eruhs/core" |
||||||
|
"eruhs/core/parser" |
||||||
|
"eruhs/describer" |
||||||
|
"fmt" |
||||||
|
"github.com/wailsapp/wails/v2/pkg/runtime" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
// App struct
|
||||||
|
type App struct { |
||||||
|
ctx context.Context |
||||||
|
capture *core.Capture |
||||||
|
} |
||||||
|
|
||||||
|
// NewApp creates a new App application struct
|
||||||
|
func NewApp() *App { |
||||||
|
return &App{} |
||||||
|
} |
||||||
|
|
||||||
|
// startup is called when the app starts. The context is saved
|
||||||
|
// so we can call the runtime methods
|
||||||
|
func (a *App) startup(ctx context.Context) { |
||||||
|
a.ctx = ctx |
||||||
|
} |
||||||
|
|
||||||
|
func (a *App) GetNetworkDeviceList() ([]core.Device, error) { |
||||||
|
devices, err := core.GetAllDevice() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
return devices, nil |
||||||
|
} |
||||||
|
|
||||||
|
var globalPackCnt = 0 |
||||||
|
var startTime = time.Now().UnixMilli() |
||||||
|
var prevSendTime = time.Now().UnixMilli() |
||||||
|
var batchSendBuffer []UIDataPackView |
||||||
|
var packetMap = map[int]*core.DataPack{} |
||||||
|
|
||||||
|
func (a *App) StartCapture(device string) { |
||||||
|
if a.capture == nil { |
||||||
|
a.capture = core.NewCapture(packCallback(a.ctx), errCallback(a.ctx)) |
||||||
|
} |
||||||
|
|
||||||
|
globalPackCnt = 0 |
||||||
|
startTime = time.Now().UnixMilli() |
||||||
|
prevSendTime = time.Now().UnixMilli() |
||||||
|
batchSendBuffer = nil |
||||||
|
batchSendBuffer = make([]UIDataPackView, 0, 1000) |
||||||
|
runtime.LogPrint(a.ctx, "StartCapture") |
||||||
|
|
||||||
|
a.capture.StartCaptureOnDevice(device) |
||||||
|
} |
||||||
|
|
||||||
|
func (a *App) PauseCapture() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
func (a *App) StopCapture() { |
||||||
|
a.capture.Stop() |
||||||
|
} |
||||||
|
|
||||||
|
func (a *App) GetDetail(id int) map[int]LayerDescribe { |
||||||
|
pack := packetMap[id] |
||||||
|
if pack == nil { |
||||||
|
return map[int]LayerDescribe{} |
||||||
|
} |
||||||
|
|
||||||
|
result := map[int]LayerDescribe{} |
||||||
|
for _, layer := range pack.LayerStack { |
||||||
|
switch layer.LayerType() { |
||||||
|
case parser.LayerTypeEthernet: |
||||||
|
eth := layer.(*parser.Ethernet) |
||||||
|
result[0] = LayerDescribe{ |
||||||
|
HeaderRaw: hex.Dump(eth.Header), |
||||||
|
LayerName: "ETH", |
||||||
|
Describe: (&describer.EthernetDescriber{Eth: eth}).GetDetailDescribe(), |
||||||
|
} |
||||||
|
case parser.LayerTypeARP: |
||||||
|
arp := layer.(*parser.ARP) |
||||||
|
result[1] = LayerDescribe{ |
||||||
|
HeaderRaw: hex.Dump(arp.Header), |
||||||
|
LayerName: "ARP", |
||||||
|
Describe: (&describer.ARPDescriber{ARP: arp}).GetDetailDescribe(), |
||||||
|
} |
||||||
|
case parser.LayerTypeIPv4: |
||||||
|
ipv4 := layer.(*parser.IPv4) |
||||||
|
result[1] = LayerDescribe{ |
||||||
|
HeaderRaw: hex.Dump(ipv4.Header), |
||||||
|
LayerName: "IPv4", |
||||||
|
Describe: (&describer.IPv4Describer{IPv4: ipv4}).GetDetailDescribe(), |
||||||
|
} |
||||||
|
case parser.LayerTypeTCP: |
||||||
|
tcp := layer.(*parser.TCP) |
||||||
|
result[2] = LayerDescribe{ |
||||||
|
HeaderRaw: hex.Dump(tcp.Header), |
||||||
|
ApplicationPayloadRaw: hex.Dump(tcp.Payload), |
||||||
|
LayerName: "TCP", |
||||||
|
Describe: (&describer.TCPDescriber{TCP: tcp}).GetDetailDescribe(), |
||||||
|
} |
||||||
|
case parser.LayerTypeUDP: |
||||||
|
udp := layer.(*parser.UDP) |
||||||
|
result[2] = LayerDescribe{ |
||||||
|
HeaderRaw: hex.Dump(udp.Header), |
||||||
|
ApplicationPayloadRaw: hex.Dump(udp.Payload), |
||||||
|
LayerName: "UDP", |
||||||
|
Describe: (&describer.UDPDescriber{UDP: udp}).GetDetailDescribe(), |
||||||
|
} |
||||||
|
default: |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
func errCallback(ctx context.Context) func(err error) { |
||||||
|
return func(err error) { |
||||||
|
fmt.Println(err) |
||||||
|
runtime.EventsEmit(ctx, "onErrOccurrence", err.Error()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func packCallback(ctx context.Context) func(pack *core.DataPack) { |
||||||
|
return func(pack *core.DataPack) { |
||||||
|
globalPackCnt++ |
||||||
|
packetMap[globalPackCnt] = pack |
||||||
|
dataPackView := UIDataPackView{ |
||||||
|
Id: globalPackCnt, |
||||||
|
Time: time.Duration(time.Now().UnixMilli() - startTime), |
||||||
|
Source: "", |
||||||
|
Dest: "", |
||||||
|
Proto: "", |
||||||
|
Length: pack.TotalLength, |
||||||
|
} |
||||||
|
|
||||||
|
for _, layer := range pack.LayerStack { |
||||||
|
switch layer.LayerType() { |
||||||
|
case parser.LayerTypeEthernet: |
||||||
|
eth := layer.(*parser.Ethernet) |
||||||
|
dataPackView.Source = eth.Source.String() |
||||||
|
dataPackView.Dest = eth.Destination.String() |
||||||
|
dataPackView.Proto = "ETH" |
||||||
|
case parser.LayerTypeARP: |
||||||
|
dataPackView.Proto = "ARP" |
||||||
|
case parser.LayerTypeIPv4: |
||||||
|
ipv4 := layer.(*parser.IPv4) |
||||||
|
dataPackView.Source = ipv4.SrcIP.String() |
||||||
|
dataPackView.Dest = ipv4.DstIP.String() |
||||||
|
dataPackView.Proto = "IPv4" |
||||||
|
case parser.LayerTypeTCP: |
||||||
|
tcp := layer.(*parser.TCP) |
||||||
|
dataPackView.Source = fmt.Sprintf("%s:%d", dataPackView.Source, tcp.SrcPort) |
||||||
|
dataPackView.Dest = fmt.Sprintf("%s:%d", dataPackView.Dest, tcp.DstPort) |
||||||
|
dataPackView.Proto = "TCP" |
||||||
|
case parser.LayerTypeUDP: |
||||||
|
udp := layer.(*parser.UDP) |
||||||
|
dataPackView.Source = fmt.Sprintf("%s:%d", dataPackView.Source, udp.SrcPort) |
||||||
|
dataPackView.Dest = fmt.Sprintf("%s:%d", dataPackView.Dest, udp.DstPort) |
||||||
|
dataPackView.Proto = "UDP" |
||||||
|
default: |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (time.Now().UnixMilli() - prevSendTime) > int64(0.2*1000) { |
||||||
|
runtime.EventsEmit(ctx, "onNewDataPacketBatch", batchSendBuffer) |
||||||
|
batchSendBuffer = nil |
||||||
|
batchSendBuffer = make([]UIDataPackView, 0, 1000) |
||||||
|
prevSendTime = time.Now().UnixMilli() |
||||||
|
} else { |
||||||
|
batchSendBuffer = append(batchSendBuffer, dataPackView) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,86 @@ |
|||||||
|
package core |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"eruhs/core/parser" |
||||||
|
"github.com/dustin/go-humanize" |
||||||
|
"github.com/google/gopacket" |
||||||
|
"github.com/google/gopacket/pcap" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
type DataPack struct { |
||||||
|
TotalLength int |
||||||
|
LayerStack []parser.Layer |
||||||
|
} |
||||||
|
|
||||||
|
func (d *DataPack) AddLayer(layer parser.Layer) { |
||||||
|
d.LayerStack = append(d.LayerStack, layer) |
||||||
|
} |
||||||
|
|
||||||
|
type Capture struct { |
||||||
|
handle *pcap.Handle |
||||||
|
started bool |
||||||
|
stopFunc func() |
||||||
|
callback func(pack *DataPack) |
||||||
|
errCallback func(err error) |
||||||
|
} |
||||||
|
|
||||||
|
func NewCapture(callback func(pack *DataPack), errCallback func(err error)) *Capture { |
||||||
|
return &Capture{ |
||||||
|
callback: callback, |
||||||
|
errCallback: errCallback, |
||||||
|
started: false, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// StartCaptureOnDevice 传入设备ID,启动抓包
|
||||||
|
func (c *Capture) StartCaptureOnDevice(device string) { |
||||||
|
if c.started { |
||||||
|
return |
||||||
|
} else { |
||||||
|
c.started = true |
||||||
|
} |
||||||
|
|
||||||
|
handle, err := pcap.OpenLive(device, 4*humanize.KByte, false, 1*time.Second) |
||||||
|
if err != nil { |
||||||
|
c.errCallback(err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
c.handle = handle |
||||||
|
ctx, cancel := context.WithCancel(context.Background()) |
||||||
|
c.stopFunc = cancel |
||||||
|
|
||||||
|
// 开启一个goroutine协程,实时读取数据包,并在抓取到数据包后调用callback,继续下一步的解析操作
|
||||||
|
go func(ctx context.Context) { |
||||||
|
defer c.handle.Close() |
||||||
|
packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) |
||||||
|
for packet := range packetSource.Packets() { |
||||||
|
select { |
||||||
|
case <-ctx.Done(): |
||||||
|
return |
||||||
|
default: |
||||||
|
parsedPack, err := c.handlePacket(packet) |
||||||
|
if err != nil { |
||||||
|
if parsedPack != nil { |
||||||
|
c.callback(parsedPack) |
||||||
|
} else { |
||||||
|
c.errCallback(err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
c.callback(parsedPack) |
||||||
|
} |
||||||
|
} |
||||||
|
}(ctx) |
||||||
|
} |
||||||
|
|
||||||
|
func (c *Capture) Pause() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
func (c *Capture) Stop() { |
||||||
|
c.stopFunc() |
||||||
|
c.started = false |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
package core |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"eruhs/core/parser" |
||||||
|
"fmt" |
||||||
|
_ "net/http/pprof" |
||||||
|
"testing" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
func TestCapture_StartCaptureOnDevice(t *testing.T) { |
||||||
|
cnt := 0 |
||||||
|
capture := NewCapture(func(pack *DataPack) { |
||||||
|
cnt++ |
||||||
|
for _, layer := range pack.LayerStack { |
||||||
|
switch layer.(type) { |
||||||
|
case *parser.Ethernet: |
||||||
|
fmt.Printf("%+v", layer.(*parser.Ethernet)) |
||||||
|
case *parser.IPv4: |
||||||
|
fmt.Printf("%+v", layer.(*parser.IPv4)) |
||||||
|
case *parser.ARP: |
||||||
|
fmt.Printf("%+v", layer.(*parser.ARP)) |
||||||
|
case *parser.TCP: |
||||||
|
fmt.Printf("%+v", layer.(*parser.TCP)) |
||||||
|
case *parser.UDP: |
||||||
|
fmt.Printf("%+v", layer.(*parser.UDP)) |
||||||
|
default: |
||||||
|
j, err := json.MarshalIndent(layer, "", " ") |
||||||
|
if err != nil { |
||||||
|
fmt.Println(err) |
||||||
|
} |
||||||
|
fmt.Println(j) |
||||||
|
} |
||||||
|
} |
||||||
|
fmt.Println("----------------") |
||||||
|
//if cnt == 16 {
|
||||||
|
// t.SkipNow()
|
||||||
|
//}
|
||||||
|
}, func(err error) { |
||||||
|
t.Error(err) |
||||||
|
}) |
||||||
|
|
||||||
|
go func() { |
||||||
|
time.Sleep(3 * time.Second) |
||||||
|
capture.Stop() |
||||||
|
}() |
||||||
|
|
||||||
|
capture.StartCaptureOnDevice("\\Device\\NPF_{AA5A8BD6-43B3-489B-9858-0ECB96EAB05E}") |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
package core |
||||||
|
|
||||||
|
import "github.com/google/gopacket/pcap" |
||||||
|
|
||||||
|
type Device struct { |
||||||
|
Name string `json:"Name"` |
||||||
|
Description string `json:"Description"` |
||||||
|
Flags uint32 `json:"Flags"` |
||||||
|
Addresses []string `json:"Addresses"` |
||||||
|
} |
||||||
|
|
||||||
|
func GetAllDevice() ([]Device, error) { |
||||||
|
netInterfaces, err := pcap.FindAllDevs() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
devices := make([]Device, 0, len(netInterfaces)) |
||||||
|
for _, netInterface := range netInterfaces { |
||||||
|
addr := make([]string, 0, len(netInterface.Addresses)) |
||||||
|
for _, address := range netInterface.Addresses { |
||||||
|
addr = append(addr, address.IP.String()) |
||||||
|
} |
||||||
|
|
||||||
|
devices = append(devices, Device{ |
||||||
|
Name: netInterface.Name, |
||||||
|
Description: netInterface.Description, |
||||||
|
Flags: netInterface.Flags, |
||||||
|
Addresses: addr, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
return devices, nil |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package core |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"testing" |
||||||
|
) |
||||||
|
|
||||||
|
func TestGetDevice(t *testing.T) { |
||||||
|
devices, err := GetAllDevice() |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
for _, device := range devices { |
||||||
|
println("--------") |
||||||
|
j, _ := json.MarshalIndent(device, "", " ") |
||||||
|
//fmt.Printf("%+v\n", device)
|
||||||
|
println(string(j)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestName(t *testing.T) { |
||||||
|
a := []int{1, 2, 3, 4, 5} |
||||||
|
fmt.Printf("%+v\n", a) |
||||||
|
as(a) |
||||||
|
fmt.Printf("%+v\n", a) |
||||||
|
} |
||||||
|
|
||||||
|
func as(a []int) { |
||||||
|
a[1] = 500 |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
package core |
||||||
|
|
||||||
|
import ( |
||||||
|
"eruhs/core/parser" |
||||||
|
"github.com/google/gopacket" |
||||||
|
) |
||||||
|
|
||||||
|
func (c *Capture) handlePacket(packet gopacket.Packet) (*DataPack, error) { |
||||||
|
parsedPack := DataPack{} |
||||||
|
data := packet.Data() |
||||||
|
parsedPack.TotalLength = len(data) |
||||||
|
eth, err := parser.DecodeEthernet(data) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
parsedPack.AddLayer(eth) |
||||||
|
|
||||||
|
switch eth.Type { |
||||||
|
case parser.EthernetTypeARP: |
||||||
|
return c.handleARP(eth.Payload, parsedPack) |
||||||
|
case parser.EthernetTypeIPv4: |
||||||
|
return c.handleIPv4(eth.Payload, parsedPack) |
||||||
|
//case custom.EthernetTypeIPv6:
|
||||||
|
default: |
||||||
|
} |
||||||
|
|
||||||
|
return &parsedPack, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (c *Capture) handleARP(data []byte, parsedPack DataPack) (*DataPack, error) { |
||||||
|
arp, err := parser.DecodeARP(data) |
||||||
|
if err != nil { |
||||||
|
return &parsedPack, err |
||||||
|
} |
||||||
|
|
||||||
|
parsedPack.AddLayer(arp) |
||||||
|
return &parsedPack, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (c *Capture) handleIPv4(data []byte, parsedPack DataPack) (*DataPack, error) { |
||||||
|
ipv4, err := parser.DecodeIPV4(data) |
||||||
|
if err != nil { |
||||||
|
return &parsedPack, err |
||||||
|
} |
||||||
|
|
||||||
|
parsedPack.AddLayer(ipv4) |
||||||
|
|
||||||
|
switch ipv4.Protocol { |
||||||
|
case parser.IPv4ProtoTCP: |
||||||
|
return c.handleTCP(ipv4.Payload, parsedPack) |
||||||
|
case parser.IPv4ProtoUDP: |
||||||
|
return c.handleUDP(ipv4.Payload, parsedPack) |
||||||
|
default: |
||||||
|
// unsupported protocol
|
||||||
|
} |
||||||
|
|
||||||
|
return &parsedPack, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (c *Capture) handleTCP(data []byte, parsedPack DataPack) (*DataPack, error) { |
||||||
|
tcp, err := parser.DecodeTCP(data) |
||||||
|
if err != nil { |
||||||
|
return &parsedPack, err |
||||||
|
} |
||||||
|
|
||||||
|
parsedPack.AddLayer(tcp) |
||||||
|
return &parsedPack, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (c *Capture) handleUDP(data []byte, parsedPack DataPack) (*DataPack, error) { |
||||||
|
udp, err := parser.DecodeUDP(data) |
||||||
|
if err != nil { |
||||||
|
return &parsedPack, err |
||||||
|
} |
||||||
|
|
||||||
|
parsedPack.AddLayer(udp) |
||||||
|
return &parsedPack, nil |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
package parser |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/binary" |
||||||
|
"fmt" |
||||||
|
"net" |
||||||
|
) |
||||||
|
|
||||||
|
// ARP ref: https://zh.wikipedia.org/zh-cn/%E5%9C%B0%E5%9D%80%E8%A7%A3%E6%9E%90%E5%8D%8F%E8%AE%AE#%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F
|
||||||
|
type ARP struct { |
||||||
|
BaseLayer |
||||||
|
HardwareType uint16 // 硬件类型
|
||||||
|
ProtoType uint16 // 协议类型
|
||||||
|
HwAddressSize uint8 // 硬件地址长度
|
||||||
|
ProtoAddrSize uint8 // 协议地址长度
|
||||||
|
Opcode uint16 // 操作码
|
||||||
|
SrcHwAddress net.HardwareAddr // 源硬件地址
|
||||||
|
SrcProtoAddress net.IP // 源协议地址
|
||||||
|
DstHwAddress net.HardwareAddr // 目标硬件地址
|
||||||
|
DstProtoAddress net.IP // 目标协议地址
|
||||||
|
} |
||||||
|
|
||||||
|
func (A *ARP) LayerType() int { |
||||||
|
return LayerTypeARP |
||||||
|
} |
||||||
|
|
||||||
|
// DecodeARP ref: https://zh.wikipedia.org/zh-cn/%E5%9C%B0%E5%9D%80%E8%A7%A3%E6%9E%90%E5%8D%8F%E8%AE%AE#%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F
|
||||||
|
func DecodeARP(data []byte) (*ARP, error) { |
||||||
|
// 至少要有8bytes
|
||||||
|
if len(data) < 8 { |
||||||
|
return nil, fmt.Errorf("[arp] 数据包太短: %d bytes", len(data)) |
||||||
|
} |
||||||
|
|
||||||
|
arp := ARP{} |
||||||
|
arp.HardwareType = binary.BigEndian.Uint16(data[0:2]) |
||||||
|
arp.ProtoType = binary.BigEndian.Uint16(data[2:4]) |
||||||
|
arp.HwAddressSize = data[4] |
||||||
|
arp.ProtoAddrSize = data[5] |
||||||
|
arp.Opcode = binary.BigEndian.Uint16(data[6:8]) |
||||||
|
arpLen := 8 + 2*arp.HwAddressSize + 2*arp.ProtoAddrSize |
||||||
|
if len(data) < int(arpLen) { |
||||||
|
return nil, fmt.Errorf("[arp] 实际长度小于声明长度:实际:%d,声明:%d", len(data), arpLen) |
||||||
|
} |
||||||
|
|
||||||
|
var offset uint8 = 8 |
||||||
|
|
||||||
|
arp.SrcHwAddress = data[offset : offset+arp.HwAddressSize] |
||||||
|
offset += arp.HwAddressSize |
||||||
|
|
||||||
|
arp.SrcProtoAddress = data[offset : offset+arp.ProtoAddrSize] |
||||||
|
offset += arp.ProtoAddrSize |
||||||
|
|
||||||
|
arp.DstHwAddress = data[offset : offset+arp.HwAddressSize] |
||||||
|
offset += arp.HwAddressSize |
||||||
|
|
||||||
|
arp.DstProtoAddress = data[offset : offset+arp.ProtoAddrSize] |
||||||
|
|
||||||
|
arp.Header = data[:arpLen] |
||||||
|
arp.Payload = data[arpLen:] |
||||||
|
return &arp, nil |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package parser |
||||||
|
|
||||||
|
const ( |
||||||
|
LayerTypeEthernet = iota |
||||||
|
LayerTypeARP |
||||||
|
LayerTypeIPv4 |
||||||
|
LayerTypeIPv6 |
||||||
|
LayerTypeICMP |
||||||
|
LayerTypeTCP |
||||||
|
LayerTypeUDP |
||||||
|
) |
||||||
|
|
||||||
|
type Layer interface { |
||||||
|
LayerType() int |
||||||
|
} |
||||||
|
|
||||||
|
type BaseLayer struct { |
||||||
|
Header []byte |
||||||
|
Payload []byte |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
package parser |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/binary" |
||||||
|
"errors" |
||||||
|
"net" |
||||||
|
) |
||||||
|
|
||||||
|
// 取值参考自https://en.wikipedia.org/wiki/EtherType,只取常用的ipv4/6和arp类型
|
||||||
|
const ( |
||||||
|
EthernetTypeIPv4 = 0x0800 |
||||||
|
EthernetTypeARP = 0x0806 |
||||||
|
EthernetTypeIPv6 = 0x86DD |
||||||
|
) |
||||||
|
|
||||||
|
type Ethernet struct { |
||||||
|
BaseLayer |
||||||
|
Source net.HardwareAddr // 源mac
|
||||||
|
Destination net.HardwareAddr // 目的mac
|
||||||
|
Type uint16 // 网络层协议类型
|
||||||
|
} |
||||||
|
|
||||||
|
func (e *Ethernet) LayerType() int { |
||||||
|
return LayerTypeEthernet |
||||||
|
} |
||||||
|
|
||||||
|
func (e *Ethernet) GetSourceMac() string { |
||||||
|
return e.Source.String() |
||||||
|
} |
||||||
|
|
||||||
|
func (e *Ethernet) GetDestinationMac() string { |
||||||
|
return e.Destination.String() |
||||||
|
} |
||||||
|
|
||||||
|
func DecodeEthernet(data []byte) (*Ethernet, error) { |
||||||
|
// 帧数据大小小于14,不符合以太网帧格式要求
|
||||||
|
if len(data) < 14 { |
||||||
|
return nil, errors.New("[ethernet] 数据包太小,不符合以太网帧格式要求") |
||||||
|
} |
||||||
|
|
||||||
|
eth := &Ethernet{} |
||||||
|
eth.Destination = data[:6] // 0~5 byte: 目标mac
|
||||||
|
eth.Source = data[6:12] // 6~11 byte: 源mac
|
||||||
|
eth.Type = binary.BigEndian.Uint16(data[12:14]) // 12~13 byte: 类型字段,大端序,uint16
|
||||||
|
|
||||||
|
eth.Header = data[:14] |
||||||
|
eth.Payload = data[14:] |
||||||
|
return eth, nil |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
package parser |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/binary" |
||||||
|
"errors" |
||||||
|
) |
||||||
|
|
||||||
|
type ICMP struct { |
||||||
|
BaseLayer |
||||||
|
TypeCode uint16 |
||||||
|
Checksum uint16 |
||||||
|
Id uint16 |
||||||
|
Seq uint16 |
||||||
|
} |
||||||
|
|
||||||
|
func (I *ICMP) LayerType() int { |
||||||
|
return LayerTypeICMP |
||||||
|
} |
||||||
|
|
||||||
|
func DecodeICMP(data []byte) (*ICMP, error) { |
||||||
|
icmp := ICMP{} |
||||||
|
if len(data) < 8 { |
||||||
|
return nil, errors.New("ICMP数据包头部少于8字节") |
||||||
|
} |
||||||
|
|
||||||
|
icmp.TypeCode = binary.BigEndian.Uint16(data[0:2]) |
||||||
|
icmp.Checksum = binary.BigEndian.Uint16(data[2:4]) |
||||||
|
icmp.Id = binary.BigEndian.Uint16(data[4:6]) |
||||||
|
icmp.Seq = binary.BigEndian.Uint16(data[6:8]) |
||||||
|
icmp.BaseLayer = BaseLayer{data[:8], data[8:]} |
||||||
|
|
||||||
|
return &icmp, nil |
||||||
|
} |
@ -0,0 +1,136 @@ |
|||||||
|
package parser |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/binary" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"net" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
IPv4ProtoICMP = 1 //互联网控制消息协议
|
||||||
|
IPv4ProtoIGMP = 2 //互联网组管理协议
|
||||||
|
IPv4ProtoTCP = 6 //传输控制协议
|
||||||
|
IPv4ProtoUDP = 17 //用户数据报协议
|
||||||
|
IPv4ProtoENCAP = 41 //IPv6封装
|
||||||
|
IPv4ProtoOSPF = 89 //开放式最短路径优先
|
||||||
|
IPv4ProtoSCTP = 132 //流控制传输协议
|
||||||
|
) |
||||||
|
|
||||||
|
type IPv4 struct { |
||||||
|
BaseLayer |
||||||
|
Version uint8 // 版本
|
||||||
|
IHL uint8 // 首部长度
|
||||||
|
DS uint8 // 区分服务
|
||||||
|
TotalLength uint16 // 总长
|
||||||
|
Id uint16 // 标识符
|
||||||
|
Flags uint8 // 标志
|
||||||
|
FragOffset uint16 // 分片偏移
|
||||||
|
TTL uint8 // 存活时间
|
||||||
|
Protocol uint8 // 协议
|
||||||
|
Checksum uint16 // 首部校验和
|
||||||
|
SrcIP net.IP // 源地址
|
||||||
|
DstIP net.IP // 目的地址
|
||||||
|
Options []IPv4Option // 选项字段
|
||||||
|
Padding []byte // 填充
|
||||||
|
} |
||||||
|
|
||||||
|
type IPv4Option struct { |
||||||
|
OptionType uint8 |
||||||
|
OptionLength uint8 |
||||||
|
OptionData []byte |
||||||
|
} |
||||||
|
|
||||||
|
func (I *IPv4) LayerType() int { |
||||||
|
return LayerTypeIPv4 |
||||||
|
} |
||||||
|
|
||||||
|
// DecodeIPV4 ref: https://zh.wikipedia.org/wiki/IPv4#%E6%8A%A5%E6%96%87%E7%BB%93%E6%9E%84
|
||||||
|
func DecodeIPV4(data []byte) (*IPv4, error) { |
||||||
|
// 至少要有20bytes
|
||||||
|
if len(data) < 20 { |
||||||
|
return nil, fmt.Errorf("[ipv4] 无效的长度:%d, 至少需要20", len(data)) |
||||||
|
} |
||||||
|
|
||||||
|
ipv4 := IPv4{} |
||||||
|
ipv4.Version = data[0] >> 4 |
||||||
|
ipv4.IHL = uint8(data[0]) & 0x0F |
||||||
|
ipv4.DS = data[1] |
||||||
|
ipv4.TotalLength = binary.BigEndian.Uint16(data[2:4]) |
||||||
|
ipv4.Id = binary.BigEndian.Uint16(data[4:6]) |
||||||
|
flagsFrags := binary.BigEndian.Uint16(data[6:8]) |
||||||
|
ipv4.Flags = uint8(flagsFrags >> 13) |
||||||
|
ipv4.FragOffset = flagsFrags & 0x1FFF |
||||||
|
ipv4.TTL = data[8] |
||||||
|
ipv4.Protocol = data[9] |
||||||
|
ipv4.Checksum = binary.BigEndian.Uint16(data[10:12]) |
||||||
|
ipv4.SrcIP = data[12:16] |
||||||
|
ipv4.DstIP = data[16:20] |
||||||
|
|
||||||
|
if ipv4.TotalLength == 0 { |
||||||
|
ipv4.TotalLength = uint16(len(data)) |
||||||
|
} |
||||||
|
|
||||||
|
// 检查数据
|
||||||
|
if ipv4.TotalLength < 20 { |
||||||
|
return nil, fmt.Errorf("[IPv4] 数据包长度太小 (%d < 20)", ipv4.TotalLength) |
||||||
|
} else if ipv4.IHL < 5 { |
||||||
|
return nil, fmt.Errorf("[IPv4] 标头长度太小 (%d < 5)", ipv4.IHL) |
||||||
|
} else if int(ipv4.IHL*4) > int(ipv4.TotalLength) { |
||||||
|
return nil, fmt.Errorf("[IPv4] 标头长度 > IP 总长度 (%d > %d)", ipv4.IHL, ipv4.TotalLength) |
||||||
|
} |
||||||
|
|
||||||
|
// 检查数据实际长度是否大于声明长度,大于(有padding)则除去padding拿到实际长度的payload
|
||||||
|
if cmp := len(data) - int(ipv4.TotalLength); cmp > 0 { |
||||||
|
data = data[:ipv4.TotalLength] |
||||||
|
} else if cmp < 0 { |
||||||
|
// 如果声明的头部长度比整体数据还大,异常
|
||||||
|
if int(ipv4.IHL)*4 > len(data) { |
||||||
|
return nil, errors.New("[ipv4] 数据包标头长度大于总数据包长度,标头长度无效或某些数据丢失") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ipv4.Header = data[:ipv4.IHL*4] |
||||||
|
ipv4.Payload = data[ipv4.IHL*4:] |
||||||
|
|
||||||
|
// 20bytes固定头部往后到IHL都是选项字段了
|
||||||
|
data = data[20 : ipv4.IHL*4] |
||||||
|
|
||||||
|
// 解析可选字段,如果data[20 : ipv4.IHL*4]之间还有数据,说明选项字段有内容
|
||||||
|
for len(data) > 0 { |
||||||
|
if ipv4.Options == nil { |
||||||
|
// Pre-allocate to avoid growing the slice too much.
|
||||||
|
ipv4.Options = make([]IPv4Option, 0, 4) |
||||||
|
} |
||||||
|
opt := IPv4Option{OptionType: data[0]} |
||||||
|
switch opt.OptionType { |
||||||
|
case 0: // option字段结束,EOL标志
|
||||||
|
opt.OptionLength = 1 |
||||||
|
ipv4.Options = append(ipv4.Options, opt) |
||||||
|
ipv4.Padding = data[1:] |
||||||
|
goto FINISH |
||||||
|
case 1: // 1byte填充
|
||||||
|
opt.OptionLength = 1 |
||||||
|
data = data[1:] |
||||||
|
ipv4.Options = append(ipv4.Options, opt) |
||||||
|
default: |
||||||
|
// 字段未结束且不是1byte填充字段长度不足2byte
|
||||||
|
if len(data) < 2 { |
||||||
|
return nil, fmt.Errorf("[IPv4] 选项长度 %d 小于2", len(data)) |
||||||
|
} |
||||||
|
opt.OptionLength = data[1] |
||||||
|
if len(data) < int(opt.OptionLength) { |
||||||
|
return nil, fmt.Errorf("[IPv4] 可选字段长度超过剩余 IP 标头大小,选项类型:%v, 长度:%v", opt.OptionType, opt.OptionLength) |
||||||
|
} |
||||||
|
if opt.OptionLength <= 2 { |
||||||
|
return nil, fmt.Errorf("[IPv4] 可选字段长度小于2,选项类型:%v 长度:%d", opt.OptionType, opt.OptionLength) |
||||||
|
} |
||||||
|
opt.OptionData = data[2:opt.OptionLength] |
||||||
|
data = data[opt.OptionLength:] |
||||||
|
ipv4.Options = append(ipv4.Options, opt) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
FINISH: |
||||||
|
return &ipv4, nil |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package parser |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/google/gopacket/layers" |
||||||
|
"net" |
||||||
|
) |
||||||
|
|
||||||
|
type IPv6 struct { |
||||||
|
layers.BaseLayer |
||||||
|
Version uint8 |
||||||
|
TrafficClass uint8 |
||||||
|
FlowLabel uint32 |
||||||
|
Length uint16 |
||||||
|
NextHeader uint8 |
||||||
|
HopLimit uint8 |
||||||
|
SrcIP net.IP |
||||||
|
DstIP net.IP |
||||||
|
HopByHop *layers.IPv6HopByHop |
||||||
|
hbh layers.IPv6HopByHop |
||||||
|
} |
@ -0,0 +1,116 @@ |
|||||||
|
package parser |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/binary" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
TCPOptionKindEndList = 0 // 选项表结束(1字节)
|
||||||
|
TCPOptionKindNop = 1 // 无操作(1字节)
|
||||||
|
TCPOptionKindMSS = 2 // 最大报文段长度(4字节,Maximum Segment Size,MSS)
|
||||||
|
TCPOptionKindWindowScale = 3 // 窗口扩大因子(3字节,wscale)
|
||||||
|
TCPOptionKindSACKOK = 4 // sackOK—发送端支持并同意使用SACK选项。
|
||||||
|
TCPOptionKindSACK = 5 // SACK实际工作的选项。
|
||||||
|
TCPOptionKindTimestamps = 8 // 时间戳(10字节,TCP Timestamps Option,TSopt)
|
||||||
|
) |
||||||
|
|
||||||
|
// TCP ref: https://zh.wikipedia.org/wiki/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE#%E7%8A%B6%E6%80%81%E7%BC%96%E7%A0%81
|
||||||
|
type TCP struct { |
||||||
|
BaseLayer |
||||||
|
SrcPort, DstPort uint16 // 源、目的端口
|
||||||
|
Seq uint32 // 序列号
|
||||||
|
Ack uint32 // 确认号
|
||||||
|
DataOffset uint8 // 数据偏移量(起始)
|
||||||
|
NS, CWR, ECE, URG, ACK, PSH, RST, SYN, FIN bool // 标志符
|
||||||
|
Window uint16 // 窗口
|
||||||
|
Checksum uint16 // 校验和
|
||||||
|
Urgent uint16 // 紧急指针
|
||||||
|
Options []TCPOption // 可选字段
|
||||||
|
Padding []byte // 填充
|
||||||
|
} |
||||||
|
|
||||||
|
type TCPOption struct { |
||||||
|
OptionType uint8 |
||||||
|
OptionLength uint8 |
||||||
|
OptionData []byte |
||||||
|
} |
||||||
|
|
||||||
|
func (T *TCP) LayerType() int { |
||||||
|
return LayerTypeTCP |
||||||
|
} |
||||||
|
|
||||||
|
// DecodeTCP ref: https://zh.wikipedia.org/wiki/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE#%E7%8A%B6%E6%80%81%E7%BC%96%E7%A0%81
|
||||||
|
// 解码TCP包,参考自wiki
|
||||||
|
func DecodeTCP(data []byte) (*TCP, error) { |
||||||
|
if len(data) < 20 { |
||||||
|
return nil, fmt.Errorf("[TCP] 无效标头。长度 %d bytes,至少需要20 bytes", len(data)) |
||||||
|
} |
||||||
|
|
||||||
|
tcp := TCP{} |
||||||
|
tcp.SrcPort = binary.BigEndian.Uint16(data[0:2]) |
||||||
|
tcp.DstPort = binary.BigEndian.Uint16(data[2:4]) |
||||||
|
tcp.Seq = binary.BigEndian.Uint32(data[4:8]) |
||||||
|
tcp.Ack = binary.BigEndian.Uint32(data[8:12]) |
||||||
|
tcp.DataOffset = data[12] >> 4 |
||||||
|
|
||||||
|
{ // 位运算取相得应的标志位
|
||||||
|
tcp.NS = data[12]&0x01 != 0 |
||||||
|
tcp.CWR = data[13]&0x80 != 0 |
||||||
|
tcp.ECE = data[13]&0x40 != 0 |
||||||
|
tcp.URG = data[13]&0x20 != 0 |
||||||
|
tcp.ACK = data[13]&0x10 != 0 |
||||||
|
tcp.PSH = data[13]&0x08 != 0 |
||||||
|
tcp.RST = data[13]&0x04 != 0 |
||||||
|
tcp.SYN = data[13]&0x02 != 0 |
||||||
|
tcp.FIN = data[13]&0x01 != 0 |
||||||
|
} |
||||||
|
|
||||||
|
tcp.Window = binary.BigEndian.Uint16(data[14:16]) |
||||||
|
tcp.Checksum = binary.BigEndian.Uint16(data[16:18]) |
||||||
|
tcp.Urgent = binary.BigEndian.Uint16(data[18:20]) |
||||||
|
|
||||||
|
dataStart := int(tcp.DataOffset) * 4 |
||||||
|
if dataStart > len(data) { |
||||||
|
return nil, errors.New("[TCP] 数据偏移大于数据包长度") |
||||||
|
} |
||||||
|
|
||||||
|
tcp.Header = data[:dataStart] |
||||||
|
tcp.Payload = data[dataStart:] |
||||||
|
|
||||||
|
// 20bytes固定头部往后到dataStart都是选项字段了
|
||||||
|
data = data[20:dataStart] |
||||||
|
|
||||||
|
// 解析可选字段
|
||||||
|
for len(data) > 0 { |
||||||
|
tcp.Options = append(tcp.Options, TCPOption{OptionType: data[0]}) |
||||||
|
opt := &tcp.Options[len(tcp.Options)-1] |
||||||
|
switch opt.OptionType { |
||||||
|
case TCPOptionKindEndList: // 选项表结束
|
||||||
|
opt.OptionLength = 1 |
||||||
|
opt.OptionData = []byte{0} |
||||||
|
tcp.Padding = data[1:] |
||||||
|
goto FINISH |
||||||
|
case TCPOptionKindNop: // 无操作(1字节),用于选项字段之间的字边界对齐。
|
||||||
|
opt.OptionLength = 1 |
||||||
|
opt.OptionData = []byte{0} |
||||||
|
data = data[opt.OptionLength:] |
||||||
|
default: |
||||||
|
if len(data) < 2 { |
||||||
|
return nil, fmt.Errorf("[TCP] 可选字段长度无效") |
||||||
|
} |
||||||
|
opt.OptionLength = data[1] |
||||||
|
if opt.OptionLength < 2 { |
||||||
|
return nil, fmt.Errorf("[TCP] 可选字段长度无效,%d < 2", opt.OptionLength) |
||||||
|
} else if int(opt.OptionLength) > len(data) { |
||||||
|
return nil, fmt.Errorf("[TCP] 无效可选字段长度: %d, 超出剩余的 %d 个字节", opt.OptionLength, len(data)) |
||||||
|
} |
||||||
|
opt.OptionData = data[2:opt.OptionLength] |
||||||
|
data = data[opt.OptionLength:] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
FINISH: |
||||||
|
return &tcp, nil |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
package parser |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/binary" |
||||||
|
"fmt" |
||||||
|
) |
||||||
|
|
||||||
|
type UDP struct { |
||||||
|
BaseLayer |
||||||
|
SrcPort, DstPort uint16 // 源端口(可选)、目标端口
|
||||||
|
Length uint16 // 数据包总长(header+payload)
|
||||||
|
Checksum uint16 // 校验和(ipv4下可选)
|
||||||
|
} |
||||||
|
|
||||||
|
func (U *UDP) LayerType() int { |
||||||
|
return LayerTypeUDP |
||||||
|
} |
||||||
|
|
||||||
|
func DecodeUDP(data []byte) (*UDP, error) { |
||||||
|
if len(data) < 8 { |
||||||
|
return nil, fmt.Errorf("[UDP] 无效数据包长度: %d bytes, 至少需要8 bytes", len(data)) |
||||||
|
} |
||||||
|
|
||||||
|
udp := UDP{} |
||||||
|
udp.SrcPort = binary.BigEndian.Uint16(data[0:2]) |
||||||
|
udp.DstPort = binary.BigEndian.Uint16(data[2:4]) |
||||||
|
udp.Length = binary.BigEndian.Uint16(data[4:6]) |
||||||
|
udp.Checksum = binary.BigEndian.Uint16(data[6:8]) |
||||||
|
udp.Header = data[:8] |
||||||
|
|
||||||
|
switch { |
||||||
|
case udp.Length >= 8: |
||||||
|
hlen := int(udp.Length) |
||||||
|
if hlen > len(data) { |
||||||
|
hlen = len(data) |
||||||
|
} |
||||||
|
udp.Payload = data[8:hlen] |
||||||
|
case udp.Length == 0: // IPv6 Jumbogram, 剩下的所有数据都认为是payload
|
||||||
|
udp.Payload = data[8:] |
||||||
|
default: |
||||||
|
return nil, fmt.Errorf("[UDP] 数据包太小:%d bytes", udp.Length) |
||||||
|
} |
||||||
|
|
||||||
|
return &udp, nil |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
package describer |
||||||
|
|
||||||
|
import ( |
||||||
|
"eruhs/core/parser" |
||||||
|
"fmt" |
||||||
|
) |
||||||
|
|
||||||
|
type ARPDescriber struct { |
||||||
|
ARP *parser.ARP |
||||||
|
} |
||||||
|
|
||||||
|
func (a *ARPDescriber) GetHeader() []byte { |
||||||
|
return a.ARP.Header |
||||||
|
} |
||||||
|
|
||||||
|
func (a *ARPDescriber) GetDetailDescribe() Describe { |
||||||
|
return Describe{ |
||||||
|
1: {"硬件类型", fmt.Sprintf("%d", a.ARP.HardwareType)}, |
||||||
|
2: {"协议类型", fmt.Sprintf("%d", a.ARP.ProtoType)}, |
||||||
|
3: {"硬件地址长度", fmt.Sprintf("%d", a.ARP.HwAddressSize)}, |
||||||
|
4: {"协议地址长度", fmt.Sprintf("%d", a.ARP.ProtoAddrSize)}, |
||||||
|
5: {"操作码", fmt.Sprintf("%d", a.ARP.Opcode)}, |
||||||
|
6: {"源硬件地址", a.ARP.SrcHwAddress.String()}, |
||||||
|
7: {"源协议地址", a.ARP.SrcProtoAddress.String()}, |
||||||
|
8: {"目标硬件地址", a.ARP.DstHwAddress.String()}, |
||||||
|
9: {"目标协议地址", a.ARP.DstProtoAddress.String()}, |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
package describer |
||||||
|
|
||||||
|
type DescribeItem struct { |
||||||
|
Name string |
||||||
|
Describe string |
||||||
|
} |
||||||
|
|
||||||
|
type Describe map[int]DescribeItem |
||||||
|
|
||||||
|
type LayerDescriber interface { |
||||||
|
GetHeader() []byte |
||||||
|
GetDetailDescribe() Describe |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
package describer |
||||||
|
|
||||||
|
import ( |
||||||
|
"eruhs/core/parser" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
type EthernetDescriber struct { |
||||||
|
Eth *parser.Ethernet |
||||||
|
} |
||||||
|
|
||||||
|
func (e *EthernetDescriber) GetHeader() []byte { |
||||||
|
return e.Eth.Header |
||||||
|
} |
||||||
|
|
||||||
|
func (e *EthernetDescriber) GetDetailDescribe() Describe { |
||||||
|
protoName := "ETH" |
||||||
|
switch e.Eth.Type { |
||||||
|
case parser.EthernetTypeIPv4: |
||||||
|
protoName = "IPv4" |
||||||
|
case parser.EthernetTypeIPv6: |
||||||
|
protoName = "IPv6" |
||||||
|
case parser.LayerTypeARP: |
||||||
|
protoName = "ARP" |
||||||
|
default: |
||||||
|
} |
||||||
|
|
||||||
|
return Describe{ |
||||||
|
1: {"源地址", strings.ToUpper(e.Eth.Source.String())}, |
||||||
|
2: {"目的地址", strings.ToUpper(e.Eth.Destination.String())}, |
||||||
|
3: {"网络层协议类型", protoName}, |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
package describer |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/hex" |
||||||
|
"eruhs/core/parser" |
||||||
|
"fmt" |
||||||
|
) |
||||||
|
|
||||||
|
type IPv4Describer struct { |
||||||
|
IPv4 *parser.IPv4 |
||||||
|
} |
||||||
|
|
||||||
|
func (i *IPv4Describer) GetHeader() []byte { |
||||||
|
return i.IPv4.Header |
||||||
|
} |
||||||
|
|
||||||
|
func (i *IPv4Describer) GetDetailDescribe() Describe { |
||||||
|
optionDescribe := make([]map[string]string, 0) |
||||||
|
for _, option := range i.IPv4.Options { |
||||||
|
optionDescribe = append(optionDescribe, map[string]string{ |
||||||
|
"类型": fmt.Sprintf("%d", option.OptionType), |
||||||
|
"长度": fmt.Sprintf("%d", option.OptionLength), |
||||||
|
"数据": hex.EncodeToString(option.OptionData), |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
protoName := "" |
||||||
|
switch i.IPv4.Protocol { |
||||||
|
case parser.IPv4ProtoICMP: |
||||||
|
protoName = "ICMP" |
||||||
|
case parser.IPv4ProtoIGMP: |
||||||
|
protoName = "IGMP" |
||||||
|
case parser.IPv4ProtoTCP: |
||||||
|
protoName = "TCP" |
||||||
|
case parser.IPv4ProtoUDP: |
||||||
|
protoName = "UDP" |
||||||
|
case parser.IPv4ProtoENCAP: |
||||||
|
protoName = "ENCAP" |
||||||
|
case parser.IPv4ProtoOSPF: |
||||||
|
protoName = "OSPF" |
||||||
|
case parser.IPv4ProtoSCTP: |
||||||
|
protoName = "SCTP" |
||||||
|
} |
||||||
|
|
||||||
|
return Describe{ |
||||||
|
1: {"版本", fmt.Sprintf("%d", i.IPv4.Version)}, |
||||||
|
2: {"首部长度", fmt.Sprintf("%d", i.IPv4.IHL)}, |
||||||
|
3: {"区分服务", fmt.Sprintf("%d", i.IPv4.DS)}, |
||||||
|
4: {"总长", fmt.Sprintf("%d", i.IPv4.TotalLength)}, |
||||||
|
5: {"标识符", fmt.Sprintf("%d", i.IPv4.Id)}, |
||||||
|
6: {"标志", fmt.Sprintf("%d", i.IPv4.Flags)}, |
||||||
|
7: {"分片偏移", fmt.Sprintf("%d", i.IPv4.FragOffset)}, |
||||||
|
8: {"存活时间", fmt.Sprintf("%d", i.IPv4.TTL)}, |
||||||
|
9: {"应用层协议", protoName}, |
||||||
|
10: {"首部校验和", fmt.Sprintf("%d", i.IPv4.Checksum)}, |
||||||
|
11: {"源地址", i.IPv4.SrcIP.String()}, |
||||||
|
12: {"目的地址", i.IPv4.DstIP.String()}, |
||||||
|
13: {"选项字段", fmt.Sprintf("%+v", optionDescribe)}, |
||||||
|
14: {"填充", fmt.Sprintf("%d Byte", len(i.IPv4.Padding))}, |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
package describer |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/hex" |
||||||
|
"eruhs/core/parser" |
||||||
|
"fmt" |
||||||
|
) |
||||||
|
|
||||||
|
type TCPDescriber struct { |
||||||
|
TCP *parser.TCP |
||||||
|
} |
||||||
|
|
||||||
|
func (t *TCPDescriber) GetHeader() []byte { |
||||||
|
return t.TCP.Header |
||||||
|
} |
||||||
|
|
||||||
|
func (t *TCPDescriber) GetDetailDescribe() Describe { |
||||||
|
tcpFlags := make([]string, 0) |
||||||
|
if t.TCP.NS { |
||||||
|
tcpFlags = append(tcpFlags, "NS") |
||||||
|
} |
||||||
|
if t.TCP.CWR { |
||||||
|
tcpFlags = append(tcpFlags, "CWR") |
||||||
|
} |
||||||
|
if t.TCP.ECE { |
||||||
|
tcpFlags = append(tcpFlags, "ECE") |
||||||
|
} |
||||||
|
if t.TCP.URG { |
||||||
|
tcpFlags = append(tcpFlags, "URG") |
||||||
|
} |
||||||
|
if t.TCP.ACK { |
||||||
|
tcpFlags = append(tcpFlags, "ACK") |
||||||
|
} |
||||||
|
if t.TCP.PSH { |
||||||
|
tcpFlags = append(tcpFlags, "PSH") |
||||||
|
} |
||||||
|
if t.TCP.RST { |
||||||
|
tcpFlags = append(tcpFlags, "RST") |
||||||
|
} |
||||||
|
if t.TCP.SYN { |
||||||
|
tcpFlags = append(tcpFlags, "SYN") |
||||||
|
} |
||||||
|
if t.TCP.FIN { |
||||||
|
tcpFlags = append(tcpFlags, "FIN") |
||||||
|
} |
||||||
|
|
||||||
|
optionDescribe := make([]map[string]string, 0) |
||||||
|
for _, option := range t.TCP.Options { |
||||||
|
optionDescribe = append(optionDescribe, map[string]string{ |
||||||
|
"类型": fmt.Sprintf("%d", option.OptionType), |
||||||
|
"长度": fmt.Sprintf("%d", option.OptionLength), |
||||||
|
"数据": hex.EncodeToString(option.OptionData), |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
return Describe{ |
||||||
|
1: {"源端口号", fmt.Sprintf("%d", t.TCP.SrcPort)}, |
||||||
|
2: {"目的端口号", fmt.Sprintf("%d", t.TCP.DstPort)}, |
||||||
|
3: {"序列号", fmt.Sprintf("%d", t.TCP.Seq)}, |
||||||
|
4: {"确认号", fmt.Sprintf("%d", t.TCP.Ack)}, |
||||||
|
5: {"数据偏移", fmt.Sprintf("%d", t.TCP.DataOffset)}, |
||||||
|
6: {"标志符", fmt.Sprintf("%v", tcpFlags)}, |
||||||
|
7: {"窗口", fmt.Sprintf("%d", t.TCP.Window)}, |
||||||
|
8: {"校验和", fmt.Sprintf("%d", t.TCP.Checksum)}, |
||||||
|
9: {"紧急指针", fmt.Sprintf("%d", t.TCP.Urgent)}, |
||||||
|
10: {"可选字段", fmt.Sprintf("%+v", optionDescribe)}, |
||||||
|
11: {"填充", fmt.Sprintf("%d", len(t.TCP.Padding))}, |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package describer |
||||||
|
|
||||||
|
import ( |
||||||
|
"eruhs/core/parser" |
||||||
|
"fmt" |
||||||
|
) |
||||||
|
|
||||||
|
type UDPDescriber struct { |
||||||
|
UDP *parser.UDP |
||||||
|
} |
||||||
|
|
||||||
|
func (u *UDPDescriber) GetHeader() []byte { |
||||||
|
return u.UDP.Header |
||||||
|
} |
||||||
|
|
||||||
|
func (u *UDPDescriber) GetDetailDescribe() Describe { |
||||||
|
return Describe{ |
||||||
|
1: {"源端口(0为无)", fmt.Sprintf("%d", u.UDP.SrcPort)}, |
||||||
|
2: {"目标端口", fmt.Sprintf("%d", u.UDP.DstPort)}, |
||||||
|
3: {"数据包总长", fmt.Sprintf("%d", u.UDP.Length)}, |
||||||
|
4: {"校验和(0为无)", fmt.Sprintf("%d", u.UDP.Checksum)}, |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
root = true |
||||||
|
|
||||||
|
[*] |
||||||
|
charset = utf-8 |
||||||
|
indent_style = space |
||||||
|
indent_size = 2 |
||||||
|
end_of_line = lf |
||||||
|
insert_final_newline = true |
||||||
|
trim_trailing_whitespace = true |
@ -0,0 +1,7 @@ |
|||||||
|
/dist |
||||||
|
/src-capacitor |
||||||
|
/src-cordova |
||||||
|
/.quasar |
||||||
|
/node_modules |
||||||
|
.eslintrc.cjs |
||||||
|
/quasar.config.*.temporary.compiled* |
@ -0,0 +1,66 @@ |
|||||||
|
module.exports = { |
||||||
|
// https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy |
||||||
|
// This option interrupts the configuration hierarchy at this file |
||||||
|
// Remove this if you have an higher level ESLint config file (it usually happens into a monorepos) |
||||||
|
root: true, |
||||||
|
|
||||||
|
parserOptions: { |
||||||
|
ecmaVersion: 2021, // Allows for the parsing of modern ECMAScript features |
||||||
|
}, |
||||||
|
|
||||||
|
env: { |
||||||
|
node: true, |
||||||
|
browser: true |
||||||
|
}, |
||||||
|
|
||||||
|
// Rules order is important, please avoid shuffling them |
||||||
|
extends: [ |
||||||
|
// Base ESLint recommended rules |
||||||
|
// 'eslint:recommended', |
||||||
|
|
||||||
|
// Uncomment any of the lines below to choose desired strictness, |
||||||
|
// but leave only one uncommented! |
||||||
|
// See https://eslint.vuejs.org/rules/#available-rules |
||||||
|
'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention) |
||||||
|
// 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability) |
||||||
|
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead) |
||||||
|
|
||||||
|
// https://github.com/prettier/eslint-config-prettier#installation |
||||||
|
// usage with Prettier, provided by 'eslint-config-prettier'. |
||||||
|
'prettier' |
||||||
|
], |
||||||
|
|
||||||
|
plugins: [ |
||||||
|
// https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files |
||||||
|
// required to lint *.vue files |
||||||
|
'vue', |
||||||
|
|
||||||
|
// https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674 |
||||||
|
// Prettier has not been included as plugin to avoid performance impact |
||||||
|
// add it as an extension for your IDE |
||||||
|
|
||||||
|
], |
||||||
|
|
||||||
|
globals: { |
||||||
|
ga: 'readonly', // Google Analytics |
||||||
|
cordova: 'readonly', |
||||||
|
__statics: 'readonly', |
||||||
|
__QUASAR_SSR__: 'readonly', |
||||||
|
__QUASAR_SSR_SERVER__: 'readonly', |
||||||
|
__QUASAR_SSR_CLIENT__: 'readonly', |
||||||
|
__QUASAR_SSR_PWA__: 'readonly', |
||||||
|
process: 'readonly', |
||||||
|
Capacitor: 'readonly', |
||||||
|
chrome: 'readonly' |
||||||
|
}, |
||||||
|
|
||||||
|
// add your custom rules here |
||||||
|
rules: { |
||||||
|
'vue/no-unused-components': 'off', |
||||||
|
'vue/no-unused-vars': 'off', |
||||||
|
'prefer-promise-reject-errors': 'off', |
||||||
|
|
||||||
|
// allow debugger during development only |
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
.DS_Store |
||||||
|
.thumbs.db |
||||||
|
node_modules |
||||||
|
|
||||||
|
# Quasar core related directories |
||||||
|
.quasar |
||||||
|
/dist |
||||||
|
/quasar.config.*.temporary.compiled* |
||||||
|
|
||||||
|
# Cordova related directories and files |
||||||
|
/src-cordova/node_modules |
||||||
|
/src-cordova/platforms |
||||||
|
/src-cordova/plugins |
||||||
|
/src-cordova/www |
||||||
|
|
||||||
|
# Capacitor related directories and files |
||||||
|
/src-capacitor/www |
||||||
|
/src-capacitor/node_modules |
||||||
|
|
||||||
|
# Log files |
||||||
|
npm-debug.log* |
||||||
|
yarn-debug.log* |
||||||
|
yarn-error.log* |
||||||
|
|
||||||
|
# Editor directories and files |
||||||
|
.idea |
||||||
|
*.suo |
||||||
|
*.ntvs* |
||||||
|
*.njsproj |
||||||
|
*.sln |
||||||
|
|
||||||
|
# local .env files |
||||||
|
.env.local* |
@ -0,0 +1,5 @@ |
|||||||
|
# pnpm-related options |
||||||
|
shamefully-hoist=true |
||||||
|
strict-peer-dependencies=false |
||||||
|
# to get the latest compatible packages when creating the project https://github.com/pnpm/pnpm/issues/6463 |
||||||
|
resolution-mode=highest |
@ -0,0 +1,3 @@ |
|||||||
|
# eruhs-ui |
||||||
|
|
||||||
|
undefined |
@ -0,0 +1,21 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title><%= productName %></title> |
||||||
|
|
||||||
|
<meta charset="utf-8"> |
||||||
|
<meta name="description" content="<%= productDescription %>"> |
||||||
|
<meta name="format-detection" content="telephone=no"> |
||||||
|
<meta name="msapplication-tap-highlight" content="no"> |
||||||
|
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>"> |
||||||
|
|
||||||
|
<link rel="icon" type="image/png" sizes="128x128" href="icons/favicon-128x128.png"> |
||||||
|
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png"> |
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png"> |
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png"> |
||||||
|
<link rel="icon" type="image/ico" href="favicon.ico"> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<!-- quasar:entry-point --> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,39 @@ |
|||||||
|
{ |
||||||
|
"compilerOptions": { |
||||||
|
"baseUrl": ".", |
||||||
|
"paths": { |
||||||
|
"src/*": [ |
||||||
|
"src/*" |
||||||
|
], |
||||||
|
"app/*": [ |
||||||
|
"*" |
||||||
|
], |
||||||
|
"components/*": [ |
||||||
|
"src/components/*" |
||||||
|
], |
||||||
|
"layouts/*": [ |
||||||
|
"src/layouts/*" |
||||||
|
], |
||||||
|
"pages/*": [ |
||||||
|
"src/pages/*" |
||||||
|
], |
||||||
|
"assets/*": [ |
||||||
|
"src/assets/*" |
||||||
|
], |
||||||
|
"boot/*": [ |
||||||
|
"src/boot/*" |
||||||
|
], |
||||||
|
"stores/*": [ |
||||||
|
"src/stores/*" |
||||||
|
], |
||||||
|
"vue$": [ |
||||||
|
"node_modules/vue/dist/vue.runtime.esm-bundler.js" |
||||||
|
] |
||||||
|
} |
||||||
|
}, |
||||||
|
"exclude": [ |
||||||
|
"dist", |
||||||
|
".quasar", |
||||||
|
"node_modules" |
||||||
|
] |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,37 @@ |
|||||||
|
{ |
||||||
|
"name": "eruhs-ui", |
||||||
|
"version": "0.0.1", |
||||||
|
"description": "eruhs ui", |
||||||
|
"productName": "eruhs", |
||||||
|
"author": "1@2.com", |
||||||
|
"private": true, |
||||||
|
"scripts": { |
||||||
|
"lint": "eslint --ext .js,.vue ./", |
||||||
|
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore", |
||||||
|
"test": "echo \"No test specified\" && exit 0", |
||||||
|
"dev": "quasar dev", |
||||||
|
"build": "quasar build" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"@quasar/extras": "^1.16.4", |
||||||
|
"quasar": "^2.8.0", |
||||||
|
"vue": "^3.4.18", |
||||||
|
"vue-router": "^4.0.12" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"@quasar/app-vite": "^1.8.0", |
||||||
|
"autoprefixer": "^10.4.18", |
||||||
|
"eslint": "^8.11.0", |
||||||
|
"eslint-config-prettier": "^8.1.0", |
||||||
|
"eslint-plugin-vue": "^9.0.0", |
||||||
|
"postcss": "^8.4.35", |
||||||
|
"prettier": "^2.5.1", |
||||||
|
"tailwindcss": "^3.4.1", |
||||||
|
"vite-plugin-checker": "^0.6.4" |
||||||
|
}, |
||||||
|
"engines": { |
||||||
|
"node": "^20 || ^18 || ^16", |
||||||
|
"npm": ">= 6.13.4", |
||||||
|
"yarn": ">= 1.21.1" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
7d942fd325b75f919c368510ac080ec8 |
@ -0,0 +1,27 @@ |
|||||||
|
/* eslint-disable */ |
||||||
|
// https://github.com/michael-ciniawsky/postcss-load-config |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
plugins: [ |
||||||
|
// https://github.com/postcss/autoprefixer |
||||||
|
require('autoprefixer')({ |
||||||
|
overrideBrowserslist: [ |
||||||
|
'last 4 Chrome versions', |
||||||
|
'last 4 Firefox versions', |
||||||
|
'last 4 Edge versions', |
||||||
|
'last 4 Safari versions', |
||||||
|
'last 4 Android versions', |
||||||
|
'last 4 ChromeAndroid versions', |
||||||
|
'last 4 FirefoxAndroid versions', |
||||||
|
'last 4 iOS versions' |
||||||
|
] |
||||||
|
}) |
||||||
|
|
||||||
|
// https://github.com/elchininet/postcss-rtlcss |
||||||
|
// If you want to support RTL css, then |
||||||
|
// 1. yarn/npm install postcss-rtlcss |
||||||
|
// 2. optionally set quasar.config.js > framework > lang to an RTL language |
||||||
|
// 3. uncomment the following line: |
||||||
|
// require('postcss-rtlcss') |
||||||
|
] |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
module.exports = { |
||||||
|
plugins: { |
||||||
|
tailwindcss: {}, |
||||||
|
autoprefixer: {}, |
||||||
|
}, |
||||||
|
} |
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 859 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 9.4 KiB |
@ -0,0 +1,209 @@ |
|||||||
|
/* eslint-env node */ |
||||||
|
|
||||||
|
/* |
||||||
|
* This file runs in a Node context (it's NOT transpiled by Babel), so use only |
||||||
|
* the ES6 features that are supported by your Node version. https://node.green/
|
||||||
|
*/ |
||||||
|
|
||||||
|
// Configuration for your app
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
|
||||||
|
|
||||||
|
|
||||||
|
const { configure } = require('quasar/wrappers'); |
||||||
|
|
||||||
|
|
||||||
|
module.exports = configure(function (/* ctx */) { |
||||||
|
return { |
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/prefetch-feature
|
||||||
|
// preFetch: true,
|
||||||
|
|
||||||
|
// app boot file (/src/boot)
|
||||||
|
// --> boot files are part of "main.js"
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/boot-files
|
||||||
|
boot: [ |
||||||
|
|
||||||
|
|
||||||
|
], |
||||||
|
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
||||||
|
css: [ |
||||||
|
'app.scss' |
||||||
|
], |
||||||
|
|
||||||
|
// https://github.com/quasarframework/quasar/tree/dev/extras
|
||||||
|
extras: [ |
||||||
|
// 'ionicons-v4',
|
||||||
|
// 'mdi-v7',
|
||||||
|
// 'fontawesome-v6',
|
||||||
|
// 'eva-icons',
|
||||||
|
// 'themify',
|
||||||
|
// 'line-awesome',
|
||||||
|
// 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
|
||||||
|
|
||||||
|
'roboto-font', // optional, you are not bound to it
|
||||||
|
'material-icons', // optional, you are not bound to it
|
||||||
|
], |
||||||
|
|
||||||
|
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
|
||||||
|
build: { |
||||||
|
target: { |
||||||
|
browser: [ 'es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1' ], |
||||||
|
node: 'node20' |
||||||
|
}, |
||||||
|
|
||||||
|
vueRouterMode: 'hash', // available values: 'hash', 'history'
|
||||||
|
// vueRouterBase,
|
||||||
|
// vueDevtools,
|
||||||
|
// vueOptionsAPI: false,
|
||||||
|
|
||||||
|
// rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
|
||||||
|
|
||||||
|
// publicPath: '/',
|
||||||
|
// analyze: true,
|
||||||
|
// env: {},
|
||||||
|
// rawDefine: {}
|
||||||
|
// ignorePublicFolder: true,
|
||||||
|
// minify: false,
|
||||||
|
// polyfillModulePreload: true,
|
||||||
|
// distDir
|
||||||
|
|
||||||
|
// extendViteConf (viteConf) {},
|
||||||
|
// viteVuePluginOptions: {},
|
||||||
|
|
||||||
|
vitePlugins: [ |
||||||
|
['vite-plugin-checker', { |
||||||
|
eslint: { |
||||||
|
lintCommand: 'eslint "./**/*.{js,mjs,cjs,vue}"' |
||||||
|
} |
||||||
|
}, { server: false }] |
||||||
|
] |
||||||
|
}, |
||||||
|
|
||||||
|
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
|
||||||
|
devServer: { |
||||||
|
// https: true
|
||||||
|
open: true, // opens browser window automatically
|
||||||
|
}, |
||||||
|
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
|
||||||
|
framework: { |
||||||
|
config: {}, |
||||||
|
|
||||||
|
// iconSet: 'material-icons', // Quasar icon set
|
||||||
|
// lang: 'en-US', // Quasar language pack
|
||||||
|
|
||||||
|
// For special cases outside of where the auto-import strategy can have an impact
|
||||||
|
// (like functional components as one of the examples),
|
||||||
|
// you can manually specify Quasar components/directives to be available everywhere:
|
||||||
|
//
|
||||||
|
// components: [],
|
||||||
|
// directives: [],
|
||||||
|
|
||||||
|
// Quasar plugins
|
||||||
|
plugins: [ |
||||||
|
'Dialog' |
||||||
|
] |
||||||
|
}, |
||||||
|
|
||||||
|
// animations: 'all', // --- includes all animations
|
||||||
|
// https://v2.quasar.dev/options/animations
|
||||||
|
animations: [], |
||||||
|
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#property-sourcefiles
|
||||||
|
// sourceFiles: {
|
||||||
|
// rootComponent: 'src/App.vue',
|
||||||
|
// router: 'src/router/index',
|
||||||
|
// store: 'src/store/index',
|
||||||
|
// registerServiceWorker: 'src-pwa/register-service-worker',
|
||||||
|
// serviceWorker: 'src-pwa/custom-service-worker',
|
||||||
|
// pwaManifestFile: 'src-pwa/manifest.json',
|
||||||
|
// electronMain: 'src-electron/electron-main',
|
||||||
|
// electronPreload: 'src-electron/electron-preload'
|
||||||
|
// },
|
||||||
|
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/developing-ssr/configuring-ssr
|
||||||
|
ssr: { |
||||||
|
// ssrPwaHtmlFilename: 'offline.html', // do NOT use index.html as name!
|
||||||
|
// will mess up SSR
|
||||||
|
|
||||||
|
// extendSSRWebserverConf (esbuildConf) {},
|
||||||
|
// extendPackageJson (json) {},
|
||||||
|
|
||||||
|
pwa: false, |
||||||
|
|
||||||
|
// manualStoreHydration: true,
|
||||||
|
// manualPostHydrationTrigger: true,
|
||||||
|
|
||||||
|
prodPort: 3000, // The default port that the production server should use
|
||||||
|
// (gets superseded if process.env.PORT is specified at runtime)
|
||||||
|
|
||||||
|
middlewares: [ |
||||||
|
'render' // keep this as last one
|
||||||
|
] |
||||||
|
}, |
||||||
|
|
||||||
|
// https://v2.quasar.dev/quasar-cli-vite/developing-pwa/configuring-pwa
|
||||||
|
pwa: { |
||||||
|
workboxMode: 'generateSW', // or 'injectManifest'
|
||||||
|
injectPwaMetaTags: true, |
||||||
|
swFilename: 'sw.js', |
||||||
|
manifestFilename: 'manifest.json', |
||||||
|
useCredentialsForManifestTag: false, |
||||||
|
// useFilenameHashes: true,
|
||||||
|
// extendGenerateSWOptions (cfg) {}
|
||||||
|
// extendInjectManifestOptions (cfg) {},
|
||||||
|
// extendManifestJson (json) {}
|
||||||
|
// extendPWACustomSWConf (esbuildConf) {}
|
||||||
|
}, |
||||||
|
|
||||||
|
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-cordova-apps/configuring-cordova
|
||||||
|
cordova: { |
||||||
|
// noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
|
||||||
|
}, |
||||||
|
|
||||||
|
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-capacitor-apps/configuring-capacitor
|
||||||
|
capacitor: { |
||||||
|
hideSplashscreen: true |
||||||
|
}, |
||||||
|
|
||||||
|
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-electron-apps/configuring-electron
|
||||||
|
electron: { |
||||||
|
// extendElectronMainConf (esbuildConf)
|
||||||
|
// extendElectronPreloadConf (esbuildConf)
|
||||||
|
|
||||||
|
// specify the debugging port to use for the Electron app when running in development mode
|
||||||
|
inspectPort: 5858, |
||||||
|
|
||||||
|
bundler: 'packager', // 'packager' or 'builder'
|
||||||
|
|
||||||
|
packager: { |
||||||
|
// https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
|
||||||
|
|
||||||
|
// OS X / Mac App Store
|
||||||
|
// appBundleId: '',
|
||||||
|
// appCategoryType: '',
|
||||||
|
// osxSign: '',
|
||||||
|
// protocol: 'myapp://path',
|
||||||
|
|
||||||
|
// Windows only
|
||||||
|
// win32metadata: { ... }
|
||||||
|
}, |
||||||
|
|
||||||
|
builder: { |
||||||
|
// https://www.electron.build/configuration/configuration
|
||||||
|
|
||||||
|
appId: 'eruhs-ui' |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex
|
||||||
|
bex: { |
||||||
|
contentScripts: [ |
||||||
|
'my-content-script' |
||||||
|
], |
||||||
|
|
||||||
|
// extendBexScriptsConf (esbuildConf) {}
|
||||||
|
// extendBexManifestJson (json) {}
|
||||||
|
} |
||||||
|
} |
||||||
|
}); |
@ -0,0 +1,11 @@ |
|||||||
|
<template> |
||||||
|
<router-view /> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import { defineComponent } from 'vue' |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: 'App' |
||||||
|
}) |
||||||
|
</script> |
After Width: | Height: | Size: 4.4 KiB |
@ -0,0 +1,107 @@ |
|||||||
|
<script setup> |
||||||
|
import {ref} from "vue"; |
||||||
|
import {EventsOn} from "app/wailsjs/runtime"; |
||||||
|
import {GetDetail, StartCapture, StopCapture} from "app/wailsjs/go/main/App"; |
||||||
|
import {useQuasar} from "quasar"; |
||||||
|
|
||||||
|
const columns = [ |
||||||
|
{name: 'id', label: '序号', field: 'id', align: 'left'}, |
||||||
|
{name: 'time', label: '时间', field: 'time', align: 'left'}, |
||||||
|
{name: 'source', label: '来源', field: 'source', align: 'left'}, |
||||||
|
{name: 'dest', label: '目的地', field: 'dest', align: 'left'}, |
||||||
|
{name: 'proto', label: '协议', field: 'proto', align: 'left'}, |
||||||
|
{name: 'length', label: '包长度', field: 'length', align: 'left'}, |
||||||
|
] |
||||||
|
|
||||||
|
const packets = ref([]) |
||||||
|
|
||||||
|
const networkDevice = ref({Name: "", Description: "", Flags: 0, Addresses: [""]}) |
||||||
|
|
||||||
|
const pagination = ref({ |
||||||
|
rowsPerPage: 10000 |
||||||
|
}) |
||||||
|
|
||||||
|
const colorScheme = { |
||||||
|
"TCP": '#c8e4fb', |
||||||
|
"UDP": '#d0d4ed', |
||||||
|
"ARP": '#d3ebd3', |
||||||
|
"ETH": '#dacfed' |
||||||
|
} |
||||||
|
|
||||||
|
const detailDialogStatus = ref(false) |
||||||
|
|
||||||
|
EventsOn("onNewDataPacket", data => { |
||||||
|
packets.value.push(data) |
||||||
|
}) |
||||||
|
|
||||||
|
EventsOn("onNewDataPacketBatch", data => { |
||||||
|
packets.value.push(...data) |
||||||
|
}) |
||||||
|
|
||||||
|
EventsOn("onErrOccurrence", err => { |
||||||
|
console.warn(err) |
||||||
|
}) |
||||||
|
|
||||||
|
const packetDetailView = ref() |
||||||
|
|
||||||
|
function startCapture(device) { |
||||||
|
networkDevice.value = device |
||||||
|
packets.value.splice(0, packets.value.length) |
||||||
|
|
||||||
|
StartCapture(networkDevice.value.Name) |
||||||
|
.then(() => console.log("capture started")) |
||||||
|
.catch(e => console.warn(e)) |
||||||
|
} |
||||||
|
|
||||||
|
function stopCapture() { |
||||||
|
// packets.value.splice(0, packets.value.length) |
||||||
|
StopCapture() |
||||||
|
.then(() => console.log("capture stopped")) |
||||||
|
.catch(e => console.warn(e)) |
||||||
|
} |
||||||
|
|
||||||
|
function showDetail(id) { |
||||||
|
GetDetail(id) |
||||||
|
.then(data => { |
||||||
|
console.log(data) |
||||||
|
packetDetailView.value = data |
||||||
|
detailDialogStatus.value = true |
||||||
|
}) |
||||||
|
.catch(e => console.warn(e)) |
||||||
|
} |
||||||
|
|
||||||
|
defineExpose({ |
||||||
|
startCapture, |
||||||
|
stopCapture |
||||||
|
}) |
||||||
|
|
||||||
|
function formattedText(text) { |
||||||
|
if (text === '[]') { |
||||||
|
return '无' |
||||||
|
} |
||||||
|
|
||||||
|
return text |
||||||
|
} |
||||||
|
|
||||||
|
const quasar = useQuasar() |
||||||
|
|
||||||
|
function viewRowHeader(name, text) { |
||||||
|
console.log(text) |
||||||
|
quasar.dialog({ |
||||||
|
title: `${name}层原始头部数据`, |
||||||
|
message: text.replaceAll('\n', '<br>').replaceAll(' ', ' '), |
||||||
|
style: 'font-family: Consolas, Cascadia Code, Cascadia Mono, monospace', |
||||||
|
fullWidth: true, |
||||||
|
ok: true, |
||||||
|
html: true, |
||||||
|
}) |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<div></div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<style lang="sass"> |
||||||
|
|
||||||
|
</style> |
@ -0,0 +1,12 @@ |
|||||||
|
// app global css in SCSS form |
||||||
|
@tailwind base; |
||||||
|
@tailwind components; |
||||||
|
@tailwind utilities; |
||||||
|
// |
||||||
|
//* { |
||||||
|
// user-select: none; |
||||||
|
//} |
||||||
|
|
||||||
|
body, #app, #q-app { |
||||||
|
height: 100vh; |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
// Quasar SCSS (& Sass) Variables |
||||||
|
// -------------------------------------------------- |
||||||
|
// To customize the look and feel of this app, you can override |
||||||
|
// the Sass/SCSS variables found in Quasar's source Sass/SCSS files. |
||||||
|
|
||||||
|
// Check documentation for full list of Quasar variables |
||||||
|
|
||||||
|
// Your own variables (that are declared here) and Quasar's own |
||||||
|
// ones will be available out of the box in your .vue/.scss/.sass files |
||||||
|
|
||||||
|
// It's highly recommended to change the default colors |
||||||
|
// to match your app's branding. |
||||||
|
// Tip: Use the "Theme Builder" on Quasar's documentation website. |
||||||
|
|
||||||
|
$primary : #1976D2; |
||||||
|
$secondary : #26A69A; |
||||||
|
$accent : #9C27B0; |
||||||
|
|
||||||
|
$dark : #1D1D1D; |
||||||
|
$dark-page : #121212; |
||||||
|
|
||||||
|
$positive : #21BA45; |
||||||
|
$negative : #C10015; |
||||||
|
$info : #31CCEC; |
||||||
|
$warning : #F2C037; |
@ -0,0 +1,323 @@ |
|||||||
|
<script setup> |
||||||
|
import {ref, watch} from "vue"; |
||||||
|
import {GetNetworkDeviceList} from "app/wailsjs/go/main/App"; |
||||||
|
import {useQuasar} from "quasar"; |
||||||
|
|
||||||
|
const dialogOpen = ref(false) |
||||||
|
|
||||||
|
const quasar = useQuasar() |
||||||
|
|
||||||
|
let started = ref(false) |
||||||
|
|
||||||
|
const selectedDevice = ref('') |
||||||
|
const selectedNetworkDevice = ref({Name: "", Description: "", Flags: 0, Addresses: [""]}) |
||||||
|
const deviceMap = new Map() |
||||||
|
const devices = ref([{ |
||||||
|
Name: "", |
||||||
|
Description: "", |
||||||
|
Flags: 0, |
||||||
|
Addresses: [""] |
||||||
|
}]) |
||||||
|
|
||||||
|
watch(selectedDevice, (n, old) => { |
||||||
|
console.log("new value", n) |
||||||
|
console.log('device', deviceMap.get(n)) |
||||||
|
selectedNetworkDevice.value = deviceMap.get(n) |
||||||
|
console.log('selectedNetworkDevice.value', selectedNetworkDevice.value) |
||||||
|
}) |
||||||
|
|
||||||
|
import {EventsOn} from "app/wailsjs/runtime"; |
||||||
|
import {GetDetail, StartCapture, StopCapture} from "app/wailsjs/go/main/App"; |
||||||
|
|
||||||
|
const columns = [ |
||||||
|
{name: 'id', label: '序号', field: 'id', align: 'left'}, |
||||||
|
{name: 'time', label: '时间', field: 'time', align: 'left'}, |
||||||
|
{name: 'source', label: '来源', field: 'source', align: 'left'}, |
||||||
|
{name: 'dest', label: '目的地', field: 'dest', align: 'left'}, |
||||||
|
{name: 'proto', label: '协议', field: 'proto', align: 'left'}, |
||||||
|
{name: 'length', label: '包长度', field: 'length', align: 'left'}, |
||||||
|
] |
||||||
|
|
||||||
|
const packets = ref([]) |
||||||
|
|
||||||
|
GetNetworkDeviceList() |
||||||
|
.then(deviceList => { |
||||||
|
devices.value = deviceList; |
||||||
|
for (const device of deviceList) { |
||||||
|
deviceMap.set(device.Name, device) |
||||||
|
} |
||||||
|
|
||||||
|
if (selectedDevice.value === '') { |
||||||
|
selectedDevice.value = deviceList?.[0].Name; |
||||||
|
} |
||||||
|
|
||||||
|
dialogOpen.value = !dialogOpen.value; |
||||||
|
}) |
||||||
|
.catch(e => quasar.dialog({ |
||||||
|
title: '出错了...', |
||||||
|
message: e, |
||||||
|
ok: true |
||||||
|
})) |
||||||
|
|
||||||
|
const pagination = ref({ |
||||||
|
rowsPerPage: 10000 |
||||||
|
}) |
||||||
|
|
||||||
|
const colorScheme = { |
||||||
|
"TCP": '#c8e4fb', |
||||||
|
"UDP": '#d0d4ed', |
||||||
|
"ARP": '#d3ebd3', |
||||||
|
"ETH": '#dacfed' |
||||||
|
} |
||||||
|
|
||||||
|
const detailDialogStatus = ref(false) |
||||||
|
|
||||||
|
EventsOn("onNewDataPacket", data => { |
||||||
|
packets.value.push(data) |
||||||
|
}) |
||||||
|
|
||||||
|
EventsOn("onNewDataPacketBatch", data => { |
||||||
|
packets.value.push(...data) |
||||||
|
}) |
||||||
|
|
||||||
|
EventsOn("onErrOccurrence", err => { |
||||||
|
console.warn(err) |
||||||
|
}) |
||||||
|
|
||||||
|
const packetDetailView = ref() |
||||||
|
|
||||||
|
function startCapture() { |
||||||
|
if (started.value) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
packets.value.splice(0, packets.value.length) |
||||||
|
|
||||||
|
StartCapture(selectedNetworkDevice.value.Name) |
||||||
|
.then(() => { |
||||||
|
console.log("capture started"); |
||||||
|
started.value = true; |
||||||
|
}) |
||||||
|
.catch(e => { |
||||||
|
console.warn(e); |
||||||
|
started.value = false; |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
function stopCapture() { |
||||||
|
// packets.value.splice(0, packets.value.length) |
||||||
|
StopCapture() |
||||||
|
.then(() => { |
||||||
|
console.log("capture stopped"); |
||||||
|
started.value = false; |
||||||
|
}) |
||||||
|
.catch(e => console.warn(e)) |
||||||
|
} |
||||||
|
|
||||||
|
function showDetail(id) { |
||||||
|
GetDetail(id) |
||||||
|
.then(data => { |
||||||
|
console.log(data) |
||||||
|
packetDetailView.value = data |
||||||
|
detailDialogStatus.value = true |
||||||
|
}) |
||||||
|
.catch(e => console.warn(e)) |
||||||
|
} |
||||||
|
|
||||||
|
function formattedText(text) { |
||||||
|
if (text === '[]') { |
||||||
|
return '无' |
||||||
|
} |
||||||
|
|
||||||
|
return text |
||||||
|
} |
||||||
|
|
||||||
|
function viewRowHeader(name, text) { |
||||||
|
console.log(text) |
||||||
|
quasar.dialog({ |
||||||
|
title: `${name}层原始头部数据`, |
||||||
|
message: text.replaceAll('\n', '<br>').replaceAll(' ', ' '), |
||||||
|
style: 'font-family: Consolas, Cascadia Code, Cascadia Mono, monospace', |
||||||
|
fullWidth: true, |
||||||
|
ok: true, |
||||||
|
html: true, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
<q-layout class="h-full select-none" view="hHh lpR fFf"> |
||||||
|
<q-header class="bg-primary text-white"> |
||||||
|
<q-toolbar> |
||||||
|
<q-toolbar-title> |
||||||
|
eruhs |
||||||
|
</q-toolbar-title> |
||||||
|
<div class="flex flex-row gap-3"> |
||||||
|
<q-btn :outline=!started :unelevated=started |
||||||
|
:icon="started ? 'stop_circle' : 'play_arrow'" |
||||||
|
:color="started ? 'red' : 'white' " |
||||||
|
@click="started ? stopCapture() : startCapture()"> |
||||||
|
<span class="ml-1.5">{{ !started ? 'Start' : 'Stop' }}</span> |
||||||
|
</q-btn> |
||||||
|
</div> |
||||||
|
</q-toolbar> |
||||||
|
</q-header> |
||||||
|
|
||||||
|
<q-drawer show-if-above side="left" bordered> |
||||||
|
|
||||||
|
<q-card-section class="z-10 sticky top-0 bg-white" > |
||||||
|
<div class="text-h6 ">选择侦听网卡</div> |
||||||
|
</q-card-section> |
||||||
|
|
||||||
|
<q-list dense> |
||||||
|
<div class="" v-for="device in devices" :key="device"> |
||||||
|
<q-item :disable="started" tag="label" v-ripple> |
||||||
|
<q-item-section avatar> |
||||||
|
<q-radio v-model="selectedDevice" :val="device.Name" color="primary"></q-radio> |
||||||
|
</q-item-section> |
||||||
|
<q-item-section> |
||||||
|
<q-item-label>{{ device.Description }}</q-item-label> |
||||||
|
<q-item-label caption> |
||||||
|
设备ID: {{ device.Name }} <br> |
||||||
|
地址:<span v-for="(addr, idx) in device.Addresses" :key="addr"> {{ idx === 0 ? '' : ', ' }}{{ |
||||||
|
addr |
||||||
|
}}</span> <br> |
||||||
|
Flag: {{ device.Flags }} |
||||||
|
</q-item-label> |
||||||
|
</q-item-section> |
||||||
|
</q-item> |
||||||
|
</div> |
||||||
|
</q-list> |
||||||
|
</q-drawer> |
||||||
|
|
||||||
|
<q-page-container class="h-full"> |
||||||
|
<div class="text-center h-full"> |
||||||
|
<div class="h-full"> |
||||||
|
<q-table |
||||||
|
class="sticky-header-table h-full" |
||||||
|
dense flat |
||||||
|
:title="`网卡${selectedNetworkDevice.Description}上的包信息`" |
||||||
|
:columns="columns" |
||||||
|
:rows="packets" |
||||||
|
row-key="id" |
||||||
|
virtual-scroll |
||||||
|
v-model:pagination="pagination" |
||||||
|
:rows-per-page-options="[0]" |
||||||
|
> |
||||||
|
<template v-slot:body="props"> |
||||||
|
<q-tr :props="props" @click="showDetail(props.row.id)" |
||||||
|
:style="`background-color: ${colorScheme[props.row.proto]}`"> |
||||||
|
<q-td key="id" :props="props">{{ props.row.id }}</q-td> |
||||||
|
<q-td key="time" :props="props">{{ props.row.time / 1000.0 }}s</q-td> |
||||||
|
<q-td key="source" :props="props">{{ props.row.source }}</q-td> |
||||||
|
<q-td key="dest" :props="props">{{ props.row.dest }}</q-td> |
||||||
|
<q-td key="proto" :props="props">{{ props.row.proto }}</q-td> |
||||||
|
<q-td key="length" :props="props">{{ props.row.length }}</q-td> |
||||||
|
</q-tr> |
||||||
|
</template> |
||||||
|
</q-table> |
||||||
|
|
||||||
|
<q-dialog v-model="detailDialogStatus" persistent maximized transition-show="slide-up" transition-hide="slide-down"> |
||||||
|
<q-card class="bg-primary text-white select-none"> |
||||||
|
<q-bar class="sticky top-0 z-10"> |
||||||
|
<q-space/> |
||||||
|
<q-btn dense flat icon="close" v-close-popup> |
||||||
|
<q-tooltip class="bg-white text-primary">关闭</q-tooltip> |
||||||
|
</q-btn> |
||||||
|
</q-bar> |
||||||
|
|
||||||
|
<div class="p-4" > |
||||||
|
<div class="text-4xl">数据包详情</div> |
||||||
|
<div v-for="layer in packetDetailView" :key="layer"> |
||||||
|
<br> |
||||||
|
<div class="my-2 h-0.5 bg-white"></div> |
||||||
|
<div class="flex flex-row"> |
||||||
|
<span class="text-3xl">{{ layer.name }}层</span> |
||||||
|
<q-space></q-space> |
||||||
|
<q-btn outline @click="viewRowHeader(layer.name, layer.header)">查看原始头部</q-btn> |
||||||
|
</div> |
||||||
|
<br> |
||||||
|
<table class="describe-table"> |
||||||
|
<tr v-for="(desc, item) in layer.describe" :key="item"> |
||||||
|
<td class="text-center">{{ item }}</td> |
||||||
|
<td class="min-w-28">{{ desc.Name }}</td> |
||||||
|
<td class="select-text">{{ formattedText(desc.Describe) }}<br></td> |
||||||
|
</tr> |
||||||
|
</table> |
||||||
|
<div v-if="layer.payload !== ''"> |
||||||
|
<br> |
||||||
|
<div class="my-2 h-0.5 bg-white"></div> |
||||||
|
<div class="flex flex-row"> |
||||||
|
<span class="text-3xl">上层数据</span> |
||||||
|
</div> |
||||||
|
<br> |
||||||
|
<!-- <pre>--> |
||||||
|
<!-- <code>--> |
||||||
|
<!-- {{ layer.payload }}--> |
||||||
|
<!-- </code>--> |
||||||
|
<!-- </pre>--> |
||||||
|
<p style="font-family: Consolas, Cascadia Code, Cascadia Mono, monospace" |
||||||
|
class="select-text text-base" |
||||||
|
v-for="line in layer.payload.split('\n')" :key="line">{{ line.replaceAll(' ', ' ') }}</p> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</q-card> |
||||||
|
</q-dialog> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</q-page-container> |
||||||
|
|
||||||
|
</q-layout> |
||||||
|
</template> |
||||||
|
|
||||||
|
<style lang="sass"> |
||||||
|
.describe-table |
||||||
|
td:first-child |
||||||
|
padding: 0 |
||||||
|
|
||||||
|
th, td |
||||||
|
padding: 0.25rem 0.5rem |
||||||
|
|
||||||
|
.sticky-header-table |
||||||
|
user-select: none |
||||||
|
transition: all 0.2s linear 0.2s |
||||||
|
|
||||||
|
th, td |
||||||
|
font-size: 1rem !important |
||||||
|
|
||||||
|
th |
||||||
|
font-weight: bold |
||||||
|
|
||||||
|
tr th |
||||||
|
background-color: white |
||||||
|
// |
||||||
|
//thead tr:first-child th:first-child |
||||||
|
// background-color: white |
||||||
|
// |
||||||
|
//tbody tr:nth-child(odd) |
||||||
|
// background-color: rgb(240, 240, 240) |
||||||
|
// |
||||||
|
//tbody tr:nth-child(even) |
||||||
|
// background-color: white |
||||||
|
|
||||||
|
th:first-child, |
||||||
|
td:first-child |
||||||
|
position: sticky |
||||||
|
left: 0 |
||||||
|
z-index: 1 |
||||||
|
|
||||||
|
th:first-child |
||||||
|
z-index: 3 !important |
||||||
|
|
||||||
|
thead tr th |
||||||
|
position: sticky |
||||||
|
z-index: 2 |
||||||
|
|
||||||
|
thead tr:first-child |
||||||
|
height: 48px !important |
||||||
|
|
||||||
|
thead tr:first-child th |
||||||
|
top: 0 |
||||||
|
</style> |
@ -0,0 +1,30 @@ |
|||||||
|
import { route } from 'quasar/wrappers' |
||||||
|
import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router' |
||||||
|
import routes from './routes' |
||||||
|
|
||||||
|
/* |
||||||
|
* If not building with SSR mode, you can |
||||||
|
* directly export the Router instantiation; |
||||||
|
* |
||||||
|
* The function below can be async too; either use |
||||||
|
* async/await or return a Promise which resolves |
||||||
|
* with the Router instance. |
||||||
|
*/ |
||||||
|
|
||||||
|
export default route(function (/* { store, ssrContext } */) { |
||||||
|
const createHistory = process.env.SERVER |
||||||
|
? createMemoryHistory |
||||||
|
: (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory) |
||||||
|
|
||||||
|
const Router = createRouter({ |
||||||
|
scrollBehavior: () => ({ left: 0, top: 0 }), |
||||||
|
routes, |
||||||
|
|
||||||
|
// Leave this as is and make changes in quasar.conf.js instead!
|
||||||
|
// quasar.conf.js -> build -> vueRouterMode
|
||||||
|
// quasar.conf.js -> build -> publicPath
|
||||||
|
history: createHistory(process.env.VUE_ROUTER_BASE) |
||||||
|
}) |
||||||
|
|
||||||
|
return Router |
||||||
|
}) |
@ -0,0 +1,8 @@ |
|||||||
|
const routes = [ |
||||||
|
{ |
||||||
|
path: '/', |
||||||
|
component: () => import('pages/IndexPage.vue'), |
||||||
|
}, |
||||||
|
] |
||||||
|
|
||||||
|
export default routes |
@ -0,0 +1,11 @@ |
|||||||
|
/** @type {import('tailwindcss').Config} */ |
||||||
|
export default { |
||||||
|
content: [ |
||||||
|
"./index.html", |
||||||
|
"./src/**/*.{vue,js,ts,jsx,tsx}", |
||||||
|
], |
||||||
|
theme: { |
||||||
|
extend: {}, |
||||||
|
}, |
||||||
|
plugins: [], |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
import {main} from '../models'; |
||||||
|
import {core} from '../models'; |
||||||
|
|
||||||
|
export function GetDetail(arg1:number):Promise<{[key: number]: main.LayerDescribe}>; |
||||||
|
|
||||||
|
export function GetNetworkDeviceList():Promise<Array<core.Device>>; |
||||||
|
|
||||||
|
export function PauseCapture():Promise<void>; |
||||||
|
|
||||||
|
export function StartCapture(arg1:string):Promise<void>; |
||||||
|
|
||||||
|
export function StopCapture():Promise<void>; |
@ -0,0 +1,23 @@ |
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export function GetDetail(arg1) { |
||||||
|
return window['go']['main']['App']['GetDetail'](arg1); |
||||||
|
} |
||||||
|
|
||||||
|
export function GetNetworkDeviceList() { |
||||||
|
return window['go']['main']['App']['GetNetworkDeviceList'](); |
||||||
|
} |
||||||
|
|
||||||
|
export function PauseCapture() { |
||||||
|
return window['go']['main']['App']['PauseCapture'](); |
||||||
|
} |
||||||
|
|
||||||
|
export function StartCapture(arg1) { |
||||||
|
return window['go']['main']['App']['StartCapture'](arg1); |
||||||
|
} |
||||||
|
|
||||||
|
export function StopCapture() { |
||||||
|
return window['go']['main']['App']['StopCapture'](); |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
export namespace core { |
||||||
|
|
||||||
|
export class Device { |
||||||
|
Name: string; |
||||||
|
Description: string; |
||||||
|
Flags: number; |
||||||
|
Addresses: string[]; |
||||||
|
|
||||||
|
static createFrom(source: any = {}) { |
||||||
|
return new Device(source); |
||||||
|
} |
||||||
|
|
||||||
|
constructor(source: any = {}) { |
||||||
|
if ('string' === typeof source) source = JSON.parse(source); |
||||||
|
this.Name = source["Name"]; |
||||||
|
this.Description = source["Description"]; |
||||||
|
this.Flags = source["Flags"]; |
||||||
|
this.Addresses = source["Addresses"]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,24 @@ |
|||||||
|
{ |
||||||
|
"name": "@wailsapp/runtime", |
||||||
|
"version": "2.0.0", |
||||||
|
"description": "Wails Javascript runtime library", |
||||||
|
"main": "runtime.js", |
||||||
|
"types": "runtime.d.ts", |
||||||
|
"scripts": { |
||||||
|
}, |
||||||
|
"repository": { |
||||||
|
"type": "git", |
||||||
|
"url": "git+https://github.com/wailsapp/wails.git" |
||||||
|
}, |
||||||
|
"keywords": [ |
||||||
|
"Wails", |
||||||
|
"Javascript", |
||||||
|
"Go" |
||||||
|
], |
||||||
|
"author": "Lea Anthony <lea.anthony@gmail.com>", |
||||||
|
"license": "MIT", |
||||||
|
"bugs": { |
||||||
|
"url": "https://github.com/wailsapp/wails/issues" |
||||||
|
}, |
||||||
|
"homepage": "https://github.com/wailsapp/wails#readme" |
||||||
|
} |
@ -0,0 +1,235 @@ |
|||||||
|
/* |
||||||
|
_ __ _ __ |
||||||
|
| | / /___ _(_) /____ |
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ ) |
||||||
|
|__/|__/\__,_/_/_/____/ |
||||||
|
The electron alternative for Go |
||||||
|
(c) Lea Anthony 2019-present |
||||||
|
*/ |
||||||
|
|
||||||
|
export interface Position { |
||||||
|
x: number; |
||||||
|
y: number; |
||||||
|
} |
||||||
|
|
||||||
|
export interface Size { |
||||||
|
w: number; |
||||||
|
h: number; |
||||||
|
} |
||||||
|
|
||||||
|
export interface Screen { |
||||||
|
isCurrent: boolean; |
||||||
|
isPrimary: boolean; |
||||||
|
width : number |
||||||
|
height : number |
||||||
|
} |
||||||
|
|
||||||
|
// Environment information such as platform, buildtype, ...
|
||||||
|
export interface EnvironmentInfo { |
||||||
|
buildType: string; |
||||||
|
platform: string; |
||||||
|
arch: string; |
||||||
|
} |
||||||
|
|
||||||
|
// [EventsEmit](https://wails.io/docs/reference/runtime/events#eventsemit)
|
||||||
|
// emits the given event. Optional data may be passed with the event.
|
||||||
|
// This will trigger any event listeners.
|
||||||
|
export function EventsEmit(eventName: string, ...data: any): void; |
||||||
|
|
||||||
|
// [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name.
|
||||||
|
export function EventsOn(eventName: string, callback: (...data: any) => void): () => void; |
||||||
|
|
||||||
|
// [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple)
|
||||||
|
// sets up a listener for the given event name, but will only trigger a given number times.
|
||||||
|
export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void; |
||||||
|
|
||||||
|
// [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce)
|
||||||
|
// sets up a listener for the given event name, but will only trigger once.
|
||||||
|
export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void; |
||||||
|
|
||||||
|
// [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff)
|
||||||
|
// unregisters the listener for the given event name.
|
||||||
|
export function EventsOff(eventName: string, ...additionalEventNames: string[]): void; |
||||||
|
|
||||||
|
// [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall)
|
||||||
|
// unregisters all listeners.
|
||||||
|
export function EventsOffAll(): void; |
||||||
|
|
||||||
|
// [LogPrint](https://wails.io/docs/reference/runtime/log#logprint)
|
||||||
|
// logs the given message as a raw message
|
||||||
|
export function LogPrint(message: string): void; |
||||||
|
|
||||||
|
// [LogTrace](https://wails.io/docs/reference/runtime/log#logtrace)
|
||||||
|
// logs the given message at the `trace` log level.
|
||||||
|
export function LogTrace(message: string): void; |
||||||
|
|
||||||
|
// [LogDebug](https://wails.io/docs/reference/runtime/log#logdebug)
|
||||||
|
// logs the given message at the `debug` log level.
|
||||||
|
export function LogDebug(message: string): void; |
||||||
|
|
||||||
|
// [LogError](https://wails.io/docs/reference/runtime/log#logerror)
|
||||||
|
// logs the given message at the `error` log level.
|
||||||
|
export function LogError(message: string): void; |
||||||
|
|
||||||
|
// [LogFatal](https://wails.io/docs/reference/runtime/log#logfatal)
|
||||||
|
// logs the given message at the `fatal` log level.
|
||||||
|
// The application will quit after calling this method.
|
||||||
|
export function LogFatal(message: string): void; |
||||||
|
|
||||||
|
// [LogInfo](https://wails.io/docs/reference/runtime/log#loginfo)
|
||||||
|
// logs the given message at the `info` log level.
|
||||||
|
export function LogInfo(message: string): void; |
||||||
|
|
||||||
|
// [LogWarning](https://wails.io/docs/reference/runtime/log#logwarning)
|
||||||
|
// logs the given message at the `warning` log level.
|
||||||
|
export function LogWarning(message: string): void; |
||||||
|
|
||||||
|
// [WindowReload](https://wails.io/docs/reference/runtime/window#windowreload)
|
||||||
|
// Forces a reload by the main application as well as connected browsers.
|
||||||
|
export function WindowReload(): void; |
||||||
|
|
||||||
|
// [WindowReloadApp](https://wails.io/docs/reference/runtime/window#windowreloadapp)
|
||||||
|
// Reloads the application frontend.
|
||||||
|
export function WindowReloadApp(): void; |
||||||
|
|
||||||
|
// [WindowSetAlwaysOnTop](https://wails.io/docs/reference/runtime/window#windowsetalwaysontop)
|
||||||
|
// Sets the window AlwaysOnTop or not on top.
|
||||||
|
export function WindowSetAlwaysOnTop(b: boolean): void; |
||||||
|
|
||||||
|
// [WindowSetSystemDefaultTheme](https://wails.io/docs/next/reference/runtime/window#windowsetsystemdefaulttheme)
|
||||||
|
// *Windows only*
|
||||||
|
// Sets window theme to system default (dark/light).
|
||||||
|
export function WindowSetSystemDefaultTheme(): void; |
||||||
|
|
||||||
|
// [WindowSetLightTheme](https://wails.io/docs/next/reference/runtime/window#windowsetlighttheme)
|
||||||
|
// *Windows only*
|
||||||
|
// Sets window to light theme.
|
||||||
|
export function WindowSetLightTheme(): void; |
||||||
|
|
||||||
|
// [WindowSetDarkTheme](https://wails.io/docs/next/reference/runtime/window#windowsetdarktheme)
|
||||||
|
// *Windows only*
|
||||||
|
// Sets window to dark theme.
|
||||||
|
export function WindowSetDarkTheme(): void; |
||||||
|
|
||||||
|
// [WindowCenter](https://wails.io/docs/reference/runtime/window#windowcenter)
|
||||||
|
// Centers the window on the monitor the window is currently on.
|
||||||
|
export function WindowCenter(): void; |
||||||
|
|
||||||
|
// [WindowSetTitle](https://wails.io/docs/reference/runtime/window#windowsettitle)
|
||||||
|
// Sets the text in the window title bar.
|
||||||
|
export function WindowSetTitle(title: string): void; |
||||||
|
|
||||||
|
// [WindowFullscreen](https://wails.io/docs/reference/runtime/window#windowfullscreen)
|
||||||
|
// Makes the window full screen.
|
||||||
|
export function WindowFullscreen(): void; |
||||||
|
|
||||||
|
// [WindowUnfullscreen](https://wails.io/docs/reference/runtime/window#windowunfullscreen)
|
||||||
|
// Restores the previous window dimensions and position prior to full screen.
|
||||||
|
export function WindowUnfullscreen(): void; |
||||||
|
|
||||||
|
// [WindowIsFullscreen](https://wails.io/docs/reference/runtime/window#windowisfullscreen)
|
||||||
|
// Returns the state of the window, i.e. whether the window is in full screen mode or not.
|
||||||
|
export function WindowIsFullscreen(): Promise<boolean>; |
||||||
|
|
||||||
|
// [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize)
|
||||||
|
// Sets the width and height of the window.
|
||||||
|
export function WindowSetSize(width: number, height: number): Promise<Size>; |
||||||
|
|
||||||
|
// [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize)
|
||||||
|
// Gets the width and height of the window.
|
||||||
|
export function WindowGetSize(): Promise<Size>; |
||||||
|
|
||||||
|
// [WindowSetMaxSize](https://wails.io/docs/reference/runtime/window#windowsetmaxsize)
|
||||||
|
// Sets the maximum window size. Will resize the window if the window is currently larger than the given dimensions.
|
||||||
|
// Setting a size of 0,0 will disable this constraint.
|
||||||
|
export function WindowSetMaxSize(width: number, height: number): void; |
||||||
|
|
||||||
|
// [WindowSetMinSize](https://wails.io/docs/reference/runtime/window#windowsetminsize)
|
||||||
|
// Sets the minimum window size. Will resize the window if the window is currently smaller than the given dimensions.
|
||||||
|
// Setting a size of 0,0 will disable this constraint.
|
||||||
|
export function WindowSetMinSize(width: number, height: number): void; |
||||||
|
|
||||||
|
// [WindowSetPosition](https://wails.io/docs/reference/runtime/window#windowsetposition)
|
||||||
|
// Sets the window position relative to the monitor the window is currently on.
|
||||||
|
export function WindowSetPosition(x: number, y: number): void; |
||||||
|
|
||||||
|
// [WindowGetPosition](https://wails.io/docs/reference/runtime/window#windowgetposition)
|
||||||
|
// Gets the window position relative to the monitor the window is currently on.
|
||||||
|
export function WindowGetPosition(): Promise<Position>; |
||||||
|
|
||||||
|
// [WindowHide](https://wails.io/docs/reference/runtime/window#windowhide)
|
||||||
|
// Hides the window.
|
||||||
|
export function WindowHide(): void; |
||||||
|
|
||||||
|
// [WindowShow](https://wails.io/docs/reference/runtime/window#windowshow)
|
||||||
|
// Shows the window, if it is currently hidden.
|
||||||
|
export function WindowShow(): void; |
||||||
|
|
||||||
|
// [WindowMaximise](https://wails.io/docs/reference/runtime/window#windowmaximise)
|
||||||
|
// Maximises the window to fill the screen.
|
||||||
|
export function WindowMaximise(): void; |
||||||
|
|
||||||
|
// [WindowToggleMaximise](https://wails.io/docs/reference/runtime/window#windowtogglemaximise)
|
||||||
|
// Toggles between Maximised and UnMaximised.
|
||||||
|
export function WindowToggleMaximise(): void; |
||||||
|
|
||||||
|
// [WindowUnmaximise](https://wails.io/docs/reference/runtime/window#windowunmaximise)
|
||||||
|
// Restores the window to the dimensions and position prior to maximising.
|
||||||
|
export function WindowUnmaximise(): void; |
||||||
|
|
||||||
|
// [WindowIsMaximised](https://wails.io/docs/reference/runtime/window#windowismaximised)
|
||||||
|
// Returns the state of the window, i.e. whether the window is maximised or not.
|
||||||
|
export function WindowIsMaximised(): Promise<boolean>; |
||||||
|
|
||||||
|
// [WindowMinimise](https://wails.io/docs/reference/runtime/window#windowminimise)
|
||||||
|
// Minimises the window.
|
||||||
|
export function WindowMinimise(): void; |
||||||
|
|
||||||
|
// [WindowUnminimise](https://wails.io/docs/reference/runtime/window#windowunminimise)
|
||||||
|
// Restores the window to the dimensions and position prior to minimising.
|
||||||
|
export function WindowUnminimise(): void; |
||||||
|
|
||||||
|
// [WindowIsMinimised](https://wails.io/docs/reference/runtime/window#windowisminimised)
|
||||||
|
// Returns the state of the window, i.e. whether the window is minimised or not.
|
||||||
|
export function WindowIsMinimised(): Promise<boolean>; |
||||||
|
|
||||||
|
// [WindowIsNormal](https://wails.io/docs/reference/runtime/window#windowisnormal)
|
||||||
|
// Returns the state of the window, i.e. whether the window is normal or not.
|
||||||
|
export function WindowIsNormal(): Promise<boolean>; |
||||||
|
|
||||||
|
// [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour)
|
||||||
|
// Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels.
|
||||||
|
export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void; |
||||||
|
|
||||||
|
// [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall)
|
||||||
|
// Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system.
|
||||||
|
export function ScreenGetAll(): Promise<Screen[]>; |
||||||
|
|
||||||
|
// [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl)
|
||||||
|
// Opens the given URL in the system browser.
|
||||||
|
export function BrowserOpenURL(url: string): void; |
||||||
|
|
||||||
|
// [Environment](https://wails.io/docs/reference/runtime/intro#environment)
|
||||||
|
// Returns information about the environment
|
||||||
|
export function Environment(): Promise<EnvironmentInfo>; |
||||||
|
|
||||||
|
// [Quit](https://wails.io/docs/reference/runtime/intro#quit)
|
||||||
|
// Quits the application.
|
||||||
|
export function Quit(): void; |
||||||
|
|
||||||
|
// [Hide](https://wails.io/docs/reference/runtime/intro#hide)
|
||||||
|
// Hides the application.
|
||||||
|
export function Hide(): void; |
||||||
|
|
||||||
|
// [Show](https://wails.io/docs/reference/runtime/intro#show)
|
||||||
|
// Shows the application.
|
||||||
|
export function Show(): void; |
||||||
|
|
||||||
|
// [ClipboardGetText](https://wails.io/docs/reference/runtime/clipboard#clipboardgettext)
|
||||||
|
// Returns the current text stored on clipboard
|
||||||
|
export function ClipboardGetText(): Promise<string>; |
||||||
|
|
||||||
|
// [ClipboardSetText](https://wails.io/docs/reference/runtime/clipboard#clipboardsettext)
|
||||||
|
// Sets a text on the clipboard
|
||||||
|
export function ClipboardSetText(text: string): Promise<boolean>; |
@ -0,0 +1,202 @@ |
|||||||
|
/* |
||||||
|
_ __ _ __ |
||||||
|
| | / /___ _(_) /____ |
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ ) |
||||||
|
|__/|__/\__,_/_/_/____/ |
||||||
|
The electron alternative for Go |
||||||
|
(c) Lea Anthony 2019-present |
||||||
|
*/ |
||||||
|
|
||||||
|
export function LogPrint(message) { |
||||||
|
window.runtime.LogPrint(message); |
||||||
|
} |
||||||
|
|
||||||
|
export function LogTrace(message) { |
||||||
|
window.runtime.LogTrace(message); |
||||||
|
} |
||||||
|
|
||||||
|
export function LogDebug(message) { |
||||||
|
window.runtime.LogDebug(message); |
||||||
|
} |
||||||
|
|
||||||
|
export function LogInfo(message) { |
||||||
|
window.runtime.LogInfo(message); |
||||||
|
} |
||||||
|
|
||||||
|
export function LogWarning(message) { |
||||||
|
window.runtime.LogWarning(message); |
||||||
|
} |
||||||
|
|
||||||
|
export function LogError(message) { |
||||||
|
window.runtime.LogError(message); |
||||||
|
} |
||||||
|
|
||||||
|
export function LogFatal(message) { |
||||||
|
window.runtime.LogFatal(message); |
||||||
|
} |
||||||
|
|
||||||
|
export function EventsOnMultiple(eventName, callback, maxCallbacks) { |
||||||
|
return window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks); |
||||||
|
} |
||||||
|
|
||||||
|
export function EventsOn(eventName, callback) { |
||||||
|
return EventsOnMultiple(eventName, callback, -1); |
||||||
|
} |
||||||
|
|
||||||
|
export function EventsOff(eventName, ...additionalEventNames) { |
||||||
|
return window.runtime.EventsOff(eventName, ...additionalEventNames); |
||||||
|
} |
||||||
|
|
||||||
|
export function EventsOnce(eventName, callback) { |
||||||
|
return EventsOnMultiple(eventName, callback, 1); |
||||||
|
} |
||||||
|
|
||||||
|
export function EventsEmit(eventName) { |
||||||
|
let args = [eventName].slice.call(arguments); |
||||||
|
return window.runtime.EventsEmit.apply(null, args); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowReload() { |
||||||
|
window.runtime.WindowReload(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowReloadApp() { |
||||||
|
window.runtime.WindowReloadApp(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowSetAlwaysOnTop(b) { |
||||||
|
window.runtime.WindowSetAlwaysOnTop(b); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowSetSystemDefaultTheme() { |
||||||
|
window.runtime.WindowSetSystemDefaultTheme(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowSetLightTheme() { |
||||||
|
window.runtime.WindowSetLightTheme(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowSetDarkTheme() { |
||||||
|
window.runtime.WindowSetDarkTheme(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowCenter() { |
||||||
|
window.runtime.WindowCenter(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowSetTitle(title) { |
||||||
|
window.runtime.WindowSetTitle(title); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowFullscreen() { |
||||||
|
window.runtime.WindowFullscreen(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowUnfullscreen() { |
||||||
|
window.runtime.WindowUnfullscreen(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowIsFullscreen() { |
||||||
|
return window.runtime.WindowIsFullscreen(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowGetSize() { |
||||||
|
return window.runtime.WindowGetSize(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowSetSize(width, height) { |
||||||
|
window.runtime.WindowSetSize(width, height); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowSetMaxSize(width, height) { |
||||||
|
window.runtime.WindowSetMaxSize(width, height); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowSetMinSize(width, height) { |
||||||
|
window.runtime.WindowSetMinSize(width, height); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowSetPosition(x, y) { |
||||||
|
window.runtime.WindowSetPosition(x, y); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowGetPosition() { |
||||||
|
return window.runtime.WindowGetPosition(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowHide() { |
||||||
|
window.runtime.WindowHide(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowShow() { |
||||||
|
window.runtime.WindowShow(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowMaximise() { |
||||||
|
window.runtime.WindowMaximise(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowToggleMaximise() { |
||||||
|
window.runtime.WindowToggleMaximise(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowUnmaximise() { |
||||||
|
window.runtime.WindowUnmaximise(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowIsMaximised() { |
||||||
|
return window.runtime.WindowIsMaximised(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowMinimise() { |
||||||
|
window.runtime.WindowMinimise(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowUnminimise() { |
||||||
|
window.runtime.WindowUnminimise(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowSetBackgroundColour(R, G, B, A) { |
||||||
|
window.runtime.WindowSetBackgroundColour(R, G, B, A); |
||||||
|
} |
||||||
|
|
||||||
|
export function ScreenGetAll() { |
||||||
|
return window.runtime.ScreenGetAll(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowIsMinimised() { |
||||||
|
return window.runtime.WindowIsMinimised(); |
||||||
|
} |
||||||
|
|
||||||
|
export function WindowIsNormal() { |
||||||
|
return window.runtime.WindowIsNormal(); |
||||||
|
} |
||||||
|
|
||||||
|
export function BrowserOpenURL(url) { |
||||||
|
window.runtime.BrowserOpenURL(url); |
||||||
|
} |
||||||
|
|
||||||
|
export function Environment() { |
||||||
|
return window.runtime.Environment(); |
||||||
|
} |
||||||
|
|
||||||
|
export function Quit() { |
||||||
|
window.runtime.Quit(); |
||||||
|
} |
||||||
|
|
||||||
|
export function Hide() { |
||||||
|
window.runtime.Hide(); |
||||||
|
} |
||||||
|
|
||||||
|
export function Show() { |
||||||
|
window.runtime.Show(); |
||||||
|
} |
||||||
|
|
||||||
|
export function ClipboardGetText() { |
||||||
|
return window.runtime.ClipboardGetText(); |
||||||
|
} |
||||||
|
|
||||||
|
export function ClipboardSetText(text) { |
||||||
|
return window.runtime.ClipboardSetText(text); |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,41 @@ |
|||||||
|
module eruhs |
||||||
|
|
||||||
|
go 1.21 |
||||||
|
|
||||||
|
toolchain go1.22.0 |
||||||
|
|
||||||
|
require ( |
||||||
|
github.com/dustin/go-humanize v1.0.1 |
||||||
|
github.com/google/gopacket v1.1.19 |
||||||
|
github.com/wailsapp/wails/v2 v2.8.0 |
||||||
|
) |
||||||
|
|
||||||
|
require ( |
||||||
|
github.com/bep/debounce v1.2.1 // indirect |
||||||
|
github.com/go-ole/go-ole v1.2.6 // indirect |
||||||
|
github.com/godbus/dbus/v5 v5.1.0 // indirect |
||||||
|
github.com/google/uuid v1.3.0 // indirect |
||||||
|
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect |
||||||
|
github.com/labstack/echo/v4 v4.10.2 // indirect |
||||||
|
github.com/labstack/gommon v0.4.0 // indirect |
||||||
|
github.com/leaanthony/go-ansi-parser v1.6.0 // indirect |
||||||
|
github.com/leaanthony/gosod v1.0.3 // indirect |
||||||
|
github.com/leaanthony/slicer v1.6.0 // indirect |
||||||
|
github.com/leaanthony/u v1.1.0 // indirect |
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect |
||||||
|
github.com/mattn/go-isatty v0.0.19 // indirect |
||||||
|
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect |
||||||
|
github.com/pkg/errors v0.9.1 // indirect |
||||||
|
github.com/rivo/uniseg v0.4.4 // indirect |
||||||
|
github.com/samber/lo v1.38.1 // indirect |
||||||
|
github.com/tkrajina/go-reflector v0.5.6 // indirect |
||||||
|
github.com/valyala/bytebufferpool v1.0.0 // indirect |
||||||
|
github.com/valyala/fasttemplate v1.2.2 // indirect |
||||||
|
github.com/wailsapp/go-webview2 v1.0.10 // indirect |
||||||
|
github.com/wailsapp/mimetype v1.4.1 // indirect |
||||||
|
golang.org/x/crypto v0.18.0 // indirect |
||||||
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect |
||||||
|
golang.org/x/net v0.20.0 // indirect |
||||||
|
golang.org/x/sys v0.16.0 // indirect |
||||||
|
golang.org/x/text v0.14.0 // indirect |
||||||
|
) |
@ -0,0 +1,110 @@ |
|||||||
|
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= |
||||||
|
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= |
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= |
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= |
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= |
||||||
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= |
||||||
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= |
||||||
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= |
||||||
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= |
||||||
|
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= |
||||||
|
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= |
||||||
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= |
||||||
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= |
||||||
|
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= |
||||||
|
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= |
||||||
|
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M= |
||||||
|
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= |
||||||
|
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= |
||||||
|
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= |
||||||
|
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc= |
||||||
|
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= |
||||||
|
github.com/leaanthony/go-ansi-parser v1.6.0 h1:T8TuMhFB6TUMIUm0oRrSbgJudTFw9csT3ZK09w0t4Pg= |
||||||
|
github.com/leaanthony/go-ansi-parser v1.6.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= |
||||||
|
github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ= |
||||||
|
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4= |
||||||
|
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= |
||||||
|
github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js= |
||||||
|
github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8= |
||||||
|
github.com/leaanthony/u v1.1.0 h1:2n0d2BwPVXSUq5yhe8lJPHdxevE2qK5G99PMStMZMaI= |
||||||
|
github.com/leaanthony/u v1.1.0/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= |
||||||
|
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= |
||||||
|
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= |
||||||
|
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= |
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= |
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= |
||||||
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= |
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= |
||||||
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= |
||||||
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= |
||||||
|
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= |
||||||
|
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= |
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= |
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= |
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= |
||||||
|
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= |
||||||
|
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= |
||||||
|
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= |
||||||
|
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= |
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= |
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= |
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= |
||||||
|
github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE= |
||||||
|
github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= |
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= |
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= |
||||||
|
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= |
||||||
|
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= |
||||||
|
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= |
||||||
|
github.com/wailsapp/go-webview2 v1.0.10 h1:PP5Hug6pnQEAhfRzLCoOh2jJaPdrqeRgJKZhyYyDV/w= |
||||||
|
github.com/wailsapp/go-webview2 v1.0.10/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= |
||||||
|
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= |
||||||
|
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= |
||||||
|
github.com/wailsapp/wails/v2 v2.8.0 h1:b2NNn99uGPiN6P5bDsnPwOJZWtAOUhNLv7Vl+YxMTr4= |
||||||
|
github.com/wailsapp/wails/v2 v2.8.0/go.mod h1:EFUGWkUX3KofO4fmKR/GmsLy3HhPH7NbyOEaMt8lBF0= |
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= |
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |
||||||
|
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= |
||||||
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= |
||||||
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= |
||||||
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= |
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= |
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= |
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||||
|
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= |
||||||
|
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= |
||||||
|
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= |
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||||
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||||
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||||
|
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= |
||||||
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= |
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= |
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= |
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= |
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= |
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
@ -0,0 +1,37 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"embed" |
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2" |
||||||
|
"github.com/wailsapp/wails/v2/pkg/options" |
||||||
|
"github.com/wailsapp/wails/v2/pkg/options/assetserver" |
||||||
|
_ "net/http/pprof" |
||||||
|
) |
||||||
|
|
||||||
|
//go:embed all:frontend/dist/spa
|
||||||
|
var assets embed.FS |
||||||
|
|
||||||
|
func main() { |
||||||
|
// Create an instance of the app structure
|
||||||
|
app := NewApp() |
||||||
|
|
||||||
|
// Create application with options
|
||||||
|
err := wails.Run(&options.App{ |
||||||
|
Title: "eruhs", |
||||||
|
Width: 1480, |
||||||
|
Height: 940, |
||||||
|
AssetServer: &assetserver.Options{ |
||||||
|
Assets: assets, |
||||||
|
}, |
||||||
|
BackgroundColour: &options.RGBA{R: 255, G: 255, B: 255, A: 1}, |
||||||
|
OnStartup: app.startup, |
||||||
|
Bind: []interface{}{ |
||||||
|
app, &UIDataPackView{}, |
||||||
|
}, |
||||||
|
}) |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
println("Error:", err.Error()) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"eruhs/describer" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
type UIDataPackView struct { |
||||||
|
Id int `json:"id"` |
||||||
|
Time time.Duration `json:"time"` |
||||||
|
Source string `json:"source"` |
||||||
|
Dest string `json:"dest"` |
||||||
|
Proto string `json:"proto"` |
||||||
|
Length int `json:"length"` |
||||||
|
} |
||||||
|
|
||||||
|
type LayerDescribe struct { |
||||||
|
HeaderRaw string `json:"header"` |
||||||
|
ApplicationPayloadRaw string `json:"payload"` |
||||||
|
LayerName string `json:"name"` |
||||||
|
Describe describer.Describe `json:"describe"` |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
{ |
||||||
|
"$schema": "https://wails.io/schemas/config.v2.json", |
||||||
|
"name": "eruhs", |
||||||
|
"outputfilename": "eruhs", |
||||||
|
"frontend:install": "npm install", |
||||||
|
"frontend:build": "npm run build", |
||||||
|
"frontend:dev:watcher": "npm run dev", |
||||||
|
"frontend:dev:serverUrl": "http://127.0.0.1:9000", |
||||||
|
"author": { |
||||||
|
"name": "lensfrex", |
||||||
|
"email": "lensferno@outlook.com" |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue