mirror of
https://github.com/bettercap/bettercap
synced 2025-08-14 10:46:57 -07:00
new: embedded ui
This commit is contained in:
parent
3ec7b01bed
commit
d8aeecb99f
5 changed files with 70 additions and 141 deletions
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 (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"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 {
|
||||
session.SessionModule
|
||||
client *github.Client
|
||||
tmpFile string
|
||||
basePath string
|
||||
uiPath string
|
||||
}
|
||||
|
||||
func getDefaultInstallBase() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return filepath.Join(os.Getenv("ALLUSERSPROFILE"), "bettercap")
|
||||
}
|
||||
return "/usr/local/share/bettercap/"
|
||||
server *http.Server
|
||||
}
|
||||
|
||||
func NewUIModule(s *session.Session) *UIModule {
|
||||
mod := &UIModule{
|
||||
SessionModule: session.NewSessionModule("ui", s),
|
||||
client: github.NewClient(nil),
|
||||
server: &http.Server{},
|
||||
}
|
||||
|
||||
mod.AddParam(session.NewStringParameter("ui.basepath",
|
||||
getDefaultInstallBase(),
|
||||
"",
|
||||
"UI base installation path."))
|
||||
mod.SessionModule.Requires("api.rest")
|
||||
|
||||
mod.AddParam(session.NewStringParameter("ui.tmpfile",
|
||||
filepath.Join(os.TempDir(), "ui.zip"),
|
||||
"",
|
||||
"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.",
|
||||
mod.AddHandler(session.NewModuleHandler("ui on", "",
|
||||
"Start the web user interface.",
|
||||
func(args []string) error {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -73,7 +59,7 @@ func (mod *UIModule) Name() 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 {
|
||||
|
@ -81,118 +67,48 @@ func (mod *UIModule) Author() string {
|
|||
}
|
||||
|
||||
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
|
||||
} else {
|
||||
mod.uiPath = filepath.Join(mod.basePath, "ui")
|
||||
}
|
||||
|
||||
if err, mod.tmpFile = mod.StringParam("ui.tmpfile"); err != nil {
|
||||
return err
|
||||
}
|
||||
mod.server.Addr = fmt.Sprintf("%s:%d", ip, port)
|
||||
|
||||
dist, _ := fs.Sub(web, "ui/dist/ui")
|
||||
mod.server.Handler = http.FileServer(http.FS(dist))
|
||||
|
||||
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 {
|
||||
if err := mod.Configure(); err != nil {
|
||||
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 {
|
||||
latest := releases[0]
|
||||
for _, a := range latest.Assets {
|
||||
if *a.Name == "ui.zip" {
|
||||
return mod.download(*latest.TagName, *a.BrowserDownloadURL)
|
||||
}
|
||||
mod.Info("web ui starting on http://%s", mod.server.Addr)
|
||||
err = mod.server.ListenAndServe()
|
||||
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
mod.Error("error while fetching latest release info from GitHub: %s", err)
|
||||
}
|
||||
})
|
||||
|
||||
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