mirror of
https://github.com/bettercap/bettercap
synced 2025-08-19 13:09:49 -07:00
new: embedded ui
This commit is contained in:
parent
3ec7b01bed
commit
d8aeecb99f
5 changed files with 70 additions and 141 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "ui"]
|
||||||
|
path = modules/ui/ui
|
||||||
|
url = git@github.com:bettercap/ui.git
|
7
Makefile
7
Makefile
|
@ -5,7 +5,7 @@ GO ?= go
|
||||||
|
|
||||||
all: build
|
all: build
|
||||||
|
|
||||||
build: resources
|
build: resources ui
|
||||||
$(GOFLAGS) $(GO) build -o $(TARGET) .
|
$(GOFLAGS) $(GO) build -o $(TARGET) .
|
||||||
|
|
||||||
build_with_race_detector: resources
|
build_with_race_detector: resources
|
||||||
|
@ -13,6 +13,11 @@ build_with_race_detector: resources
|
||||||
|
|
||||||
resources: network/manuf.go
|
resources: network/manuf.go
|
||||||
|
|
||||||
|
ui: ./modules/ui/ui/dist/ui/index.html
|
||||||
|
|
||||||
|
./modules/ui/ui/dist/ui/index.html:
|
||||||
|
@cd modules/ui/ui && make build
|
||||||
|
|
||||||
network/manuf.go:
|
network/manuf.go:
|
||||||
@python3 ./network/make_manuf.py
|
@python3 ./network/make_manuf.py
|
||||||
|
|
||||||
|
|
4
hooks/post_checkout
Normal file
4
hooks/post_checkout
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Docker hub does a recursive clone, then checks the branch out,
|
||||||
|
# so when a PR adds a submodule (or updates it), it fails.
|
||||||
|
git submodule update --init
|
1
modules/ui/ui
Submodule
1
modules/ui/ui
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 505b5edec3bdad82f63dfac158f0f43a263d098b
|
196
modules/ui/ui.go
196
modules/ui/ui.go
|
@ -2,69 +2,55 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io/fs"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"time"
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/bettercap/bettercap/v2/session"
|
"github.com/bettercap/bettercap/v2/session"
|
||||||
|
|
||||||
"github.com/google/go-github/github"
|
|
||||||
|
|
||||||
"github.com/evilsocket/islazy/fs"
|
|
||||||
"github.com/evilsocket/islazy/tui"
|
|
||||||
"github.com/evilsocket/islazy/zip"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var versionParser = regexp.MustCompile(`name:"ui",version:"([^"]+)"`)
|
var (
|
||||||
|
//go:embed ui/dist/ui
|
||||||
|
web embed.FS
|
||||||
|
)
|
||||||
|
|
||||||
type UIModule struct {
|
type UIModule struct {
|
||||||
session.SessionModule
|
session.SessionModule
|
||||||
client *github.Client
|
|
||||||
tmpFile string
|
|
||||||
basePath string
|
|
||||||
uiPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDefaultInstallBase() string {
|
server *http.Server
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
return filepath.Join(os.Getenv("ALLUSERSPROFILE"), "bettercap")
|
|
||||||
}
|
|
||||||
return "/usr/local/share/bettercap/"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUIModule(s *session.Session) *UIModule {
|
func NewUIModule(s *session.Session) *UIModule {
|
||||||
mod := &UIModule{
|
mod := &UIModule{
|
||||||
SessionModule: session.NewSessionModule("ui", s),
|
SessionModule: session.NewSessionModule("ui", s),
|
||||||
client: github.NewClient(nil),
|
server: &http.Server{},
|
||||||
}
|
}
|
||||||
|
|
||||||
mod.AddParam(session.NewStringParameter("ui.basepath",
|
mod.SessionModule.Requires("api.rest")
|
||||||
getDefaultInstallBase(),
|
|
||||||
"",
|
|
||||||
"UI base installation path."))
|
|
||||||
|
|
||||||
mod.AddParam(session.NewStringParameter("ui.tmpfile",
|
mod.AddHandler(session.NewModuleHandler("ui on", "",
|
||||||
filepath.Join(os.TempDir(), "ui.zip"),
|
"Start the web user interface.",
|
||||||
"",
|
|
||||||
"Temporary file to use while downloading UI updates."))
|
|
||||||
|
|
||||||
mod.AddHandler(session.NewModuleHandler("ui.version", "",
|
|
||||||
"Print the currently installed UI version.",
|
|
||||||
func(args []string) error {
|
|
||||||
return mod.showVersion()
|
|
||||||
}))
|
|
||||||
|
|
||||||
mod.AddHandler(session.NewModuleHandler("ui.update", "",
|
|
||||||
"Download the latest available version of the UI and install it.",
|
|
||||||
func(args []string) error {
|
func(args []string) error {
|
||||||
return mod.Start()
|
return mod.Start()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
mod.AddHandler(session.NewModuleHandler("ui off", "",
|
||||||
|
"Stop the web user interface.",
|
||||||
|
func(args []string) error {
|
||||||
|
return mod.Stop()
|
||||||
|
}))
|
||||||
|
|
||||||
|
mod.AddParam(session.NewStringParameter("ui.address",
|
||||||
|
"127.0.0.1",
|
||||||
|
session.IPv4Validator,
|
||||||
|
"Address to bind the web ui to."))
|
||||||
|
|
||||||
|
mod.AddParam(session.NewIntParameter("ui.port",
|
||||||
|
"8080",
|
||||||
|
"Port to bind the web ui server to."))
|
||||||
|
|
||||||
return mod
|
return mod
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +59,7 @@ func (mod *UIModule) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mod *UIModule) Description() string {
|
func (mod *UIModule) Description() string {
|
||||||
return "A module to manage bettercap's UI updates and installed version."
|
return "Web User Interface."
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mod *UIModule) Author() string {
|
func (mod *UIModule) Author() string {
|
||||||
|
@ -81,118 +67,48 @@ func (mod *UIModule) Author() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mod *UIModule) Configure() (err error) {
|
func (mod *UIModule) Configure() (err error) {
|
||||||
if err, mod.basePath = mod.StringParam("ui.basepath"); err != nil {
|
var ip string
|
||||||
|
var port int
|
||||||
|
|
||||||
|
if mod.Running() {
|
||||||
|
return session.ErrAlreadyStarted(mod.Name())
|
||||||
|
} else if err, ip = mod.StringParam("ui.address"); err != nil {
|
||||||
|
return err
|
||||||
|
} else if err, port = mod.IntParam("ui.port"); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
|
||||||
mod.uiPath = filepath.Join(mod.basePath, "ui")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err, mod.tmpFile = mod.StringParam("ui.tmpfile"); err != nil {
|
mod.server.Addr = fmt.Sprintf("%s:%d", ip, port)
|
||||||
return err
|
|
||||||
}
|
dist, _ := fs.Sub(web, "ui/dist/ui")
|
||||||
|
mod.server.Handler = http.FileServer(http.FS(dist))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mod *UIModule) Stop() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mod *UIModule) download(version, url string) error {
|
|
||||||
if !fs.Exists(mod.basePath) {
|
|
||||||
mod.Warning("creating ui install path %s ...", mod.basePath)
|
|
||||||
if err := os.MkdirAll(mod.basePath, os.ModePerm); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := os.Create(mod.tmpFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer out.Close()
|
|
||||||
defer os.Remove(mod.tmpFile)
|
|
||||||
|
|
||||||
mod.Info("downloading ui %s from %s ...", tui.Bold(version), url)
|
|
||||||
|
|
||||||
resp, err := http.Get(url)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if _, err := io.Copy(out, resp.Body); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if fs.Exists(mod.uiPath) {
|
|
||||||
mod.Warning("removing previously installed UI from %s ...", mod.uiPath)
|
|
||||||
if err := os.RemoveAll(mod.uiPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod.Info("installing to %s ...", mod.uiPath)
|
|
||||||
|
|
||||||
if _, err = zip.Unzip(mod.tmpFile, mod.basePath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
mod.Info("installation complete, you can now run the %s (or https-ui) caplet to start the UI.", tui.Bold("http-ui"))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mod *UIModule) showVersion() error {
|
|
||||||
if err := mod.Configure(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !fs.Exists(mod.uiPath) {
|
|
||||||
return fmt.Errorf("path %s does not exist, ui not installed", mod.uiPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
search := filepath.Join(mod.uiPath, "/main.*.js")
|
|
||||||
matches, err := filepath.Glob(search)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if len(matches) == 0 {
|
|
||||||
return fmt.Errorf("can't find any main.*.js files in %s", mod.uiPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, filename := range matches {
|
|
||||||
if raw, err := ioutil.ReadFile(filename); err != nil {
|
|
||||||
return err
|
|
||||||
} else if m := versionParser.FindStringSubmatch(string(raw)); m != nil {
|
|
||||||
version := m[1]
|
|
||||||
mod.Info("v%s", version)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("can't parse version from %s", search)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mod *UIModule) Start() error {
|
func (mod *UIModule) Start() error {
|
||||||
if err := mod.Configure(); err != nil {
|
if err := mod.Configure(); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err := mod.SetRunning(true, nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
defer mod.SetRunning(false, nil)
|
|
||||||
|
|
||||||
mod.Info("checking latest stable release ...")
|
mod.SetRunning(true, func() {
|
||||||
|
var err error
|
||||||
|
|
||||||
if releases, _, err := mod.client.Repositories.ListReleases(context.Background(), "bettercap", "ui", nil); err == nil {
|
mod.Info("web ui starting on http://%s", mod.server.Addr)
|
||||||
latest := releases[0]
|
err = mod.server.ListenAndServe()
|
||||||
for _, a := range latest.Assets {
|
|
||||||
if *a.Name == "ui.zip" {
|
if err != nil && err != http.ErrServerClosed {
|
||||||
return mod.download(*latest.TagName, *a.BrowserDownloadURL)
|
panic(err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
})
|
||||||
mod.Error("error while fetching latest release info from GitHub: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mod *UIModule) Stop() error {
|
||||||
|
return mod.SetRunning(false, func() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
mod.server.Shutdown(ctx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue