mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 22:55:48 +01:00
[v2] Add support for external frontend dev servers (#1290)
* [v2] Consolidate AssetServers * [v2] Support starturl for webview on linux and darwin * [v2] Add support for frontend DevServer * [v2] Activate frontend DevServer in svelte template * [website] Add bleeding edge guide for PRs * DoNotMerge: Bump Version for testing Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
This commit is contained in:
parent
4cedfdc091
commit
65fb42d9e7
29 changed files with 705 additions and 729 deletions
|
|
@ -5,9 +5,11 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
|
@ -32,8 +34,6 @@ import (
|
|||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
)
|
||||
|
||||
const defaultDevServerURL = "http://localhost:34115"
|
||||
|
||||
func LogGreen(message string, args ...interface{}) {
|
||||
text := fmt.Sprintf(message, args...)
|
||||
println(colour.Green(text))
|
||||
|
|
@ -71,9 +71,11 @@ type devFlags struct {
|
|||
loglevel string
|
||||
forceBuild bool
|
||||
debounceMS int
|
||||
devServerURL string
|
||||
devServer string
|
||||
appargs string
|
||||
saveConfig bool
|
||||
|
||||
frontendDevServerURL string
|
||||
}
|
||||
|
||||
// AddSubcommand adds the `dev` command for the Wails application
|
||||
|
|
@ -95,7 +97,8 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||
command.StringFlag("loglevel", "Loglevel to use - Trace, Debug, Info, Warning, Error", &flags.loglevel)
|
||||
command.BoolFlag("f", "Force build application", &flags.forceBuild)
|
||||
command.IntFlag("debounce", "The amount of time to wait to trigger a reload on change", &flags.debounceMS)
|
||||
command.StringFlag("devserverurl", "The url of the dev server to use", &flags.devServerURL)
|
||||
command.StringFlag("devserver", "The address of the wails dev server", &flags.devServer)
|
||||
command.StringFlag("frontenddevserverurl", "The url of the external frontend dev server to use", &flags.frontendDevServerURL)
|
||||
command.StringFlag("appargs", "arguments to pass to the underlying app (quoted and space searated)", &flags.appargs)
|
||||
command.BoolFlag("save", "Save given flags as defaults", &flags.saveConfig)
|
||||
|
||||
|
|
@ -172,13 +175,14 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||
defer closer(&devCommandWaitGroup)
|
||||
}
|
||||
|
||||
devServerURL, err := url.Parse("http://" + flags.devServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// open browser
|
||||
if flags.openBrowser {
|
||||
url := defaultDevServerURL
|
||||
if flags.devServerURL != "" {
|
||||
url = flags.devServerURL
|
||||
}
|
||||
err = browser.OpenURL(url)
|
||||
err = browser.OpenURL(devServerURL.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -186,6 +190,10 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||
|
||||
// create the project files watcher
|
||||
watcher, err := initialiseWatcher(cwd, logger.Fatal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func(watcher *fsnotify.Watcher) {
|
||||
err := watcher.Close()
|
||||
if err != nil {
|
||||
|
|
@ -194,11 +202,14 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||
}(watcher)
|
||||
|
||||
LogGreen("Watching (sub)/directory: %s", cwd)
|
||||
LogGreen("Using Dev Server URL: %s", flags.devServerURL)
|
||||
LogGreen("Using DevServer URL: %s", devServerURL)
|
||||
if flags.frontendDevServerURL != "" {
|
||||
LogGreen("Using Frontend DevServer URL: %s", flags.frontendDevServerURL)
|
||||
}
|
||||
LogGreen("Using reload debounce setting of %d milliseconds", flags.debounceMS)
|
||||
|
||||
// Watch for changes and trigger restartApp()
|
||||
doWatcherLoop(buildOptions, debugBinaryProcess, flags, watcher, exitCodeChannel, quitChannel)
|
||||
doWatcherLoop(buildOptions, debugBinaryProcess, flags, watcher, exitCodeChannel, quitChannel, devServerURL)
|
||||
|
||||
// Kill the current program if running
|
||||
if debugBinaryProcess != nil {
|
||||
|
|
@ -261,7 +272,6 @@ func runCommand(dir string, exitOnError bool, command string, args ...string) er
|
|||
// defaultDevFlags generates devFlags with default options
|
||||
func defaultDevFlags() devFlags {
|
||||
return devFlags{
|
||||
devServerURL: defaultDevServerURL,
|
||||
compilerCommand: "go",
|
||||
verbosity: 1,
|
||||
extensions: "go",
|
||||
|
|
@ -319,12 +329,12 @@ func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, e
|
|||
projectConfig.ReloadDirectories = filepath.ToSlash(flags.reloadDirs)
|
||||
}
|
||||
|
||||
if flags.devServerURL == defaultDevServerURL && projectConfig.DevServerURL != defaultDevServerURL && projectConfig.DevServerURL != "" {
|
||||
flags.devServerURL = projectConfig.DevServerURL
|
||||
if flags.devServer == "" && projectConfig.DevServer != "" {
|
||||
flags.devServer = projectConfig.DevServer
|
||||
}
|
||||
|
||||
if flags.devServerURL != projectConfig.DevServerURL {
|
||||
projectConfig.DevServerURL = flags.devServerURL
|
||||
if flags.frontendDevServerURL == "" && projectConfig.FrontendDevServerURL != "" {
|
||||
flags.frontendDevServerURL = projectConfig.FrontendDevServerURL
|
||||
}
|
||||
|
||||
if flags.wailsjsdir == "" && projectConfig.WailsJSDir != "" {
|
||||
|
|
@ -466,7 +476,8 @@ func restartApp(buildOptions *build.Options, debugBinaryProcess *process.Process
|
|||
// Set environment variables accordingly
|
||||
os.Setenv("loglevel", flags.loglevel)
|
||||
os.Setenv("assetdir", flags.assetDir)
|
||||
os.Setenv("devserverurl", flags.devServerURL)
|
||||
os.Setenv("devserver", flags.devServer)
|
||||
os.Setenv("frontenddevserverurl", flags.frontendDevServerURL)
|
||||
|
||||
// Start up new binary with correct args
|
||||
newProcess := process.NewProcess(appBinary, args...)
|
||||
|
|
@ -486,7 +497,7 @@ func restartApp(buildOptions *build.Options, debugBinaryProcess *process.Process
|
|||
}
|
||||
|
||||
// doWatcherLoop is the main watch loop that runs while dev is active
|
||||
func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Process, flags devFlags, watcher *fsnotify.Watcher, exitCodeChannel chan int, quitChannel chan os.Signal) {
|
||||
func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Process, flags devFlags, watcher *fsnotify.Watcher, exitCodeChannel chan int, quitChannel chan os.Signal, devServerURL *url.URL) {
|
||||
// Main Loop
|
||||
var (
|
||||
err error
|
||||
|
|
@ -513,6 +524,9 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc
|
|||
reload := false
|
||||
assetDir := ""
|
||||
changedPaths := map[string]struct{}{}
|
||||
|
||||
assetDirURL := joinPath(devServerURL, "/wails/assetdir")
|
||||
reloadURL := joinPath(devServerURL, "/wails/reload")
|
||||
for quit == false {
|
||||
//reload := false
|
||||
select {
|
||||
|
|
@ -584,9 +598,14 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc
|
|||
debugBinaryProcess = newBinaryProcess
|
||||
}
|
||||
}
|
||||
|
||||
if flags.frontendDevServerURL != "" {
|
||||
// If we are using an external dev server all the reload of the frontend part can be skipped
|
||||
continue
|
||||
}
|
||||
if len(changedPaths) != 0 {
|
||||
if assetDir == "" {
|
||||
resp, err := http.Get("http://localhost:34115/wails/assetdir")
|
||||
resp, err := http.Get(assetDirURL)
|
||||
if err != nil {
|
||||
LogRed("Error during retrieving assetdir: %s", err.Error())
|
||||
} else {
|
||||
|
|
@ -615,7 +634,7 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc
|
|||
}
|
||||
if reload {
|
||||
reload = false
|
||||
_, err = http.Get("http://localhost:34115/wails/reload")
|
||||
_, err = http.Get(reloadURL)
|
||||
if err != nil {
|
||||
LogRed("Error during refresh: %s", err.Error())
|
||||
}
|
||||
|
|
@ -626,3 +645,9 @@ func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Proc
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func joinPath(url *url.URL, subPath string) string {
|
||||
u := *url
|
||||
u.Path = path.Join(u.Path, subPath)
|
||||
return u.String()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
"outputfilename": "{{.BinaryName}}",
|
||||
"frontend:install": "npm install",
|
||||
"frontend:build": "npm run build",
|
||||
"frontend:dev:watcher": "npm run build:watch",
|
||||
"frontend:dev:watcher": "npm run dev",
|
||||
"frontend:dev:serverUrl": "http://localhost:3000",
|
||||
"wailsjsdir": "./frontend",
|
||||
"debounceMS": 2000,
|
||||
"debounceMS": 1000,
|
||||
"author": {
|
||||
"name": "{{.AuthorName}}",
|
||||
"email": "{{.AuthorEmail}}"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
package internal
|
||||
|
||||
var Version = "v2.0.0-beta.34"
|
||||
var Version = "v2.0.0-beta.34.frontenddevserver" // Just for testing purposes do not merge
|
||||
|
|
|
|||
25
v2/go.mod
25
v2/go.mod
|
|
@ -4,21 +4,17 @@ go 1.17
|
|||
|
||||
require (
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/fatih/structtag v1.2.0
|
||||
github.com/flytam/filenamify v1.0.0
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/gabriel-vasile/mimetype v1.3.1
|
||||
github.com/go-git/go-billy/v5 v5.2.0 // indirect
|
||||
github.com/go-git/go-git/v5 v5.3.0
|
||||
github.com/gofiber/fiber/v2 v2.17.0
|
||||
github.com/gofiber/websocket/v2 v2.0.8
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.1.2 // indirect
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/imdario/mergo v0.3.12
|
||||
github.com/jackmordaunt/icns v1.0.0
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e
|
||||
github.com/labstack/echo/v4 v4.7.2
|
||||
github.com/leaanthony/clir v1.0.4
|
||||
github.com/leaanthony/debme v1.2.1
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1
|
||||
|
|
@ -33,48 +29,43 @@ require (
|
|||
github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/tc-hib/winres v0.1.5
|
||||
github.com/tdewolff/minify v2.3.6+incompatible
|
||||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
||||
github.com/tdewolff/test v1.0.6 // indirect
|
||||
github.com/tidwall/sjson v1.1.7
|
||||
github.com/wzshiming/ctc v1.2.3
|
||||
github.com/ztrue/tracerr v0.3.0
|
||||
golang.org/x/mod v0.4.1
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
|
||||
golang.org/x/tools v0.1.0
|
||||
nhooyr.io/websocket v1.8.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.4.16 // indirect
|
||||
github.com/andybalholm/brotli v1.0.2 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/labstack/gommon v0.3.1 // indirect
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.7 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/tidwall/gjson v1.8.0 // indirect
|
||||
github.com/tidwall/match v1.0.3 // indirect
|
||||
github.com/tidwall/pretty v1.1.0 // indirect
|
||||
github.com/tkrajina/go-reflector v0.5.5 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.28.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
||||
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
|
|
|||
113
v2/go.sum
113
v2/go.sum
|
|
@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/
|
|||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E=
|
||||
github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
|
|
@ -17,10 +15,6 @@ 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/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab h1:9e2joQGp642wHGFP5m86SDptAavrdGBe8/x9DGEEAaI=
|
||||
github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab/go.mod h1:smsv/h4PBEBaU0XDTY5UwJTpZv69fQ0FfcLJr21mA6Y=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/flytam/filenamify v1.0.0 h1:ewx6BY2dj7U6h2zGPJmt33q/BjkSf/YsY/woQvnUNIs=
|
||||
github.com/flytam/filenamify v1.0.0/go.mod h1:Dzf9kVycwcsBlr2ATg6uxjqiFgKGH+5SKFuhdeP5zu8=
|
||||
|
|
@ -28,10 +22,6 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo
|
|||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gabriel-vasile/mimetype v1.3.1 h1:qevA6c2MtE1RorlScnixeG0VA1H4xrXyhyX3oWBynNQ=
|
||||
github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
|
|
@ -47,41 +37,15 @@ github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4
|
|||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
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/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/gofiber/fiber/v2 v2.17.0 h1:qP3PkGUbBB0i9iQh5E057XI1yO5CZigUxZhyUFYAFoM=
|
||||
github.com/gofiber/fiber/v2 v2.17.0/go.mod h1:iftruuHGkRYGEXVISmdD7HTYWyfS2Bh+Dkfq4n/1Owg=
|
||||
github.com/gofiber/websocket/v2 v2.0.8 h1:Hb4y6IxYZVMO0segROODXJiXVgVD3a6i7wnfot8kM6k=
|
||||
github.com/gofiber/websocket/v2 v2.0.8/go.mod h1:fv8HSGQX09sauNv9g5Xq8GeGAaahLFYQKKb4ZdT0x2w=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/jackmordaunt/icns v1.0.0 h1:RYSxplerf/l/DUd09AHtITwckkv/mqjVv4DjYdPmAMQ=
|
||||
|
|
@ -91,15 +55,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i
|
|||
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/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.12.2 h1:2KCfW3I9M7nSc5wOqXAlW2v2U6v+w6cbjvbfp+OykW8=
|
||||
github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
|
|
@ -109,6 +66,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/labstack/echo/v4 v4.7.2 h1:Kv2/p8OaQ+M6Ex4eGimg9b9e6icoxA42JSlOR3msKtI=
|
||||
github.com/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
|
||||
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
|
||||
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU=
|
||||
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
||||
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
|
||||
|
|
@ -127,22 +88,18 @@ github.com/leaanthony/typescriptify-golang-structs v0.1.7 h1:yoznzWzyxkO/iWdlpq+
|
|||
github.com/leaanthony/typescriptify-golang-structs v0.1.7/go.mod h1:cWtOkiVhMF77e6phAXUcfNwYmMwCJ67Sij24lfvi9Js=
|
||||
github.com/leaanthony/winicon v1.0.0 h1:ZNt5U5dY71oEoKZ97UVwJRT4e+5xo5o/ieKuHuk8NqQ=
|
||||
github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e h1:9MlwzLdW7QSDrhDjFlsEYmxpFyIoXmYRon3dt0io31k=
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
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-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
|
|
@ -157,26 +114,17 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f h1:PgA+Olipyj258EIEYnpFFONrrCcAIWNUNoFhUfMqAGY=
|
||||
github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f/go.mod h1:lHhJedqxCoHN+zMtwGNTXWmF0u9Jt363FYRhV6g0CdY=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tc-hib/winres v0.1.5 h1:2dA5yfjdoEA3UyRaOC92HNMt3jap66pLzoW4MjpC/0M=
|
||||
github.com/tc-hib/winres v0.1.5/go.mod h1:pe6dOR40VOrGz8PkzreVKNvEKnlE8t4yR8A8naL+t7A=
|
||||
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
|
||||
github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs=
|
||||
github.com/tdewolff/parse v2.3.4+incompatible h1:x05/cnGwIMf4ceLuDMBOdQ1qGniMoxpP46ghf0Qzh38=
|
||||
github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ=
|
||||
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
|
||||
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tidwall/gjson v1.8.0 h1:Qt+orfosKn0rbNTZqHYDqBrmm3UDA4KRkv70fDzG+PQ=
|
||||
github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
|
||||
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
|
||||
|
|
@ -187,19 +135,10 @@ github.com/tidwall/sjson v1.1.7 h1:sgVPwu/yygHJ2m1pJDLgGM/h+1F5odx5Q9ljG3imRm8=
|
|||
github.com/tidwall/sjson v1.1.7/go.mod h1:w/yG+ezBeTdUxiKs5NcPicO9diP38nk96QBAbIIGeFs=
|
||||
github.com/tkrajina/go-reflector v0.5.5 h1:gwoQFNye30Kk7NrExj8zm3zFtrGPqOkzFMLuQZg1DtQ=
|
||||
github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
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/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
|
||||
github.com/valyala/fasthttp v1.26.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA=
|
||||
github.com/valyala/fasthttp v1.28.0 h1:ruVmTmZaBR5i67NqnjvvH5gEv0zwHfWtbjoyW98iho4=
|
||||
github.com/valyala/fasthttp v1.28.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/wzshiming/ctc v1.2.3 h1:q+hW3IQNsjIlOFBTGZZZeIXTElFM4grF4spW/errh/c=
|
||||
github.com/wzshiming/ctc v1.2.3/go.mod h1:2tVAtIY7SUyraSk0JxvwmONNPFL4ARavPuEsg5+KA28=
|
||||
github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae h1:tpXvBXC3hpQBDCc9OojJZCQMVRAbT3TTdUMP8WguXkY=
|
||||
|
|
@ -214,8 +153,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI=
|
||||
golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
|
|
@ -224,13 +163,12 @@ golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
|
|||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
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-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
|
||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
@ -240,7 +178,6 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
@ -249,18 +186,21 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
|
|
@ -270,9 +210,6 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
|||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
@ -284,10 +221,8 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
|||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
|
||||
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
|
|
@ -63,16 +63,21 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
|||
|
||||
// Check for CLI Flags
|
||||
var assetdirFlag *string
|
||||
var devServerURLFlag *string
|
||||
var devServerFlag *string
|
||||
var frontendDevServerURLFlag *string
|
||||
var loglevelFlag *string
|
||||
|
||||
assetdir := os.Getenv("assetdir")
|
||||
if assetdir == "" {
|
||||
assetdirFlag = flag.String("assetdir", "", "Directory to serve assets")
|
||||
}
|
||||
devServerURL := os.Getenv("devserverurl")
|
||||
if devServerURL == "" {
|
||||
devServerURLFlag = flag.String("devserverurl", "", "URL of development server")
|
||||
devServer := os.Getenv("devserver")
|
||||
if devServer == "" {
|
||||
devServerFlag = flag.String("devserver", "", "Address to bind the wails dev server to")
|
||||
}
|
||||
frontendDevServerURL := os.Getenv("frontenddevserverurl")
|
||||
if frontendDevServerURL == "" {
|
||||
frontendDevServerURLFlag = flag.String("frontenddevserverurl", "", "URL of the external frontend dev server")
|
||||
}
|
||||
|
||||
loglevel := os.Getenv("loglevel")
|
||||
|
|
@ -86,8 +91,11 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
|||
if assetdirFlag != nil {
|
||||
assetdir = *assetdirFlag
|
||||
}
|
||||
if devServerURLFlag != nil {
|
||||
devServerURL = *devServerURLFlag
|
||||
if devServerFlag != nil {
|
||||
devServer = *devServerFlag
|
||||
}
|
||||
if frontendDevServerURLFlag != nil {
|
||||
frontendDevServerURL = *frontendDevServerURLFlag
|
||||
}
|
||||
if loglevelFlag != nil {
|
||||
loglevel = *loglevelFlag
|
||||
|
|
@ -102,7 +110,15 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if assetdir != "" {
|
||||
if frontendDevServerURL != "" {
|
||||
if devServer == "" {
|
||||
return nil, fmt.Errorf("Unable to use FrontendDevServerUrl without a DevServer address")
|
||||
}
|
||||
ctx = context.WithValue(ctx, "starturl", "http://"+devServer)
|
||||
ctx = context.WithValue(ctx, "frontenddevserverurl", frontendDevServerURL)
|
||||
|
||||
myLogger.Info("Serving assets from frontend DevServer URL: %s", frontendDevServerURL)
|
||||
} else if assetdir != "" {
|
||||
// Let's override the assets to serve from on disk, if needed
|
||||
absdir, err := filepath.Abs(assetdir)
|
||||
if err != nil {
|
||||
|
|
@ -115,8 +131,8 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
|||
ctx = context.WithValue(ctx, "assetdir", assetdir)
|
||||
}
|
||||
|
||||
if devServerURL != "" {
|
||||
ctx = context.WithValue(ctx, "devserverurl", devServerURL)
|
||||
if devServer != "" {
|
||||
ctx = context.WithValue(ctx, "devserver", devServer)
|
||||
}
|
||||
|
||||
if loglevel != "" {
|
||||
|
|
|
|||
124
v2/internal/frontend/assetserver/assethandler.go
Normal file
124
v2/internal/frontend/assetserver/assethandler.go
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
package assetserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
iofs "io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
//go:embed defaultindex.html
|
||||
var defaultHTML []byte
|
||||
|
||||
type assetHandler struct {
|
||||
fs iofs.FS
|
||||
|
||||
logger *logger.Logger
|
||||
|
||||
servingFromDisk bool
|
||||
}
|
||||
|
||||
func NewAsssetHandler(ctx context.Context, options *options.App) (http.Handler, error) {
|
||||
vfs := options.Assets
|
||||
if vfs != nil {
|
||||
if _, err := vfs.Open("."); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subDir, err := fs.FindPathToFile(vfs, "index.html")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vfs, err = iofs.Sub(vfs, path.Clean(subDir))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
result := &assetHandler{
|
||||
fs: vfs,
|
||||
|
||||
// Check if we have been given a directory to serve assets from.
|
||||
// If so, this means we are in dev mode and are serving assets off disk.
|
||||
// We indicate this through the `servingFromDisk` flag to ensure requests
|
||||
// aren't cached in dev mode.
|
||||
servingFromDisk: ctx.Value("assetdir") != nil,
|
||||
}
|
||||
|
||||
if _logger := ctx.Value("logger"); _logger != nil {
|
||||
result.logger = _logger.(*logger.Logger)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (d *assetHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
if d.fs == nil {
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
filename := strings.TrimPrefix(req.URL.Path, "/")
|
||||
if d.logger != nil {
|
||||
d.logger.Debug("[AssetHandler] Loading file '%s'", filename)
|
||||
}
|
||||
|
||||
var content []byte
|
||||
var err error
|
||||
switch filename {
|
||||
case "", "index.html":
|
||||
content, err = d.loadFile("index.html")
|
||||
if err != nil {
|
||||
err = nil
|
||||
content = defaultHTML
|
||||
}
|
||||
|
||||
default:
|
||||
content, err = d.loadFile(filename)
|
||||
}
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
mimeType := GetMimetype(filename, content)
|
||||
rw.Header().Set(HeaderContentType, mimeType)
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
_, err = rw.Write(content)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
if d.logger != nil {
|
||||
d.logger.Error("[AssetHandler] Unable to load file '%s': %s", filename, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loadFile will try to load the file from disk. If there is an error
|
||||
// it will retry until eventually it will give up and error.
|
||||
func (d *assetHandler) loadFile(filename string) ([]byte, error) {
|
||||
if !d.servingFromDisk {
|
||||
return iofs.ReadFile(d.fs, filename)
|
||||
}
|
||||
var result []byte
|
||||
var err error
|
||||
for tries := 0; tries < 50; tries++ {
|
||||
result, err = iofs.ReadFile(d.fs, filename)
|
||||
if err != nil {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
190
v2/internal/frontend/assetserver/assetserver.go
Normal file
190
v2/internal/frontend/assetserver/assetserver.go
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
package assetserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/runtime"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
const (
|
||||
runtimeJSPath = "/wails/runtime.js"
|
||||
ipcJSPath = "/wails/ipc.js"
|
||||
)
|
||||
|
||||
type AssetServer struct {
|
||||
handler http.Handler
|
||||
runtimeJS []byte
|
||||
ipcJS func(*http.Request) []byte
|
||||
|
||||
logger *logger.Logger
|
||||
|
||||
servingFromDisk bool
|
||||
appendSpinnerToBody bool
|
||||
}
|
||||
|
||||
func NewAssetServer(ctx context.Context, options *options.App, bindingsJSON string) (*AssetServer, error) {
|
||||
handler, err := NewAsssetHandler(ctx, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewAssetServerWithHandler(ctx, handler, bindingsJSON)
|
||||
}
|
||||
|
||||
func NewAssetServerWithHandler(ctx context.Context, handler http.Handler, bindingsJSON string) (*AssetServer, error) {
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString(`window.wailsbindings='` + bindingsJSON + `';` + "\n")
|
||||
buffer.Write(runtime.RuntimeDesktopJS)
|
||||
|
||||
result := &AssetServer{
|
||||
handler: handler,
|
||||
runtimeJS: buffer.Bytes(),
|
||||
|
||||
// Check if we have been given a directory to serve assets from.
|
||||
// If so, this means we are in dev mode and are serving assets off disk.
|
||||
// We indicate this through the `servingFromDisk` flag to ensure requests
|
||||
// aren't cached in dev mode.
|
||||
servingFromDisk: ctx.Value("assetdir") != nil,
|
||||
}
|
||||
|
||||
if _logger := ctx.Value("logger"); _logger != nil {
|
||||
result.logger = _logger.(*logger.Logger)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (d *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
header := rw.Header()
|
||||
if d.servingFromDisk {
|
||||
header.Add(HeaderCacheControl, "no-cache")
|
||||
}
|
||||
|
||||
path := req.URL.Path
|
||||
switch path {
|
||||
case "", "/", "/index.html":
|
||||
recorder := httptest.NewRecorder()
|
||||
d.handler.ServeHTTP(recorder, req)
|
||||
for k, v := range recorder.HeaderMap {
|
||||
header[k] = v
|
||||
}
|
||||
|
||||
if recorder.Code != http.StatusOK {
|
||||
rw.WriteHeader(recorder.Code)
|
||||
return
|
||||
}
|
||||
|
||||
content, err := d.processIndexHTML(recorder.Body.Bytes())
|
||||
if err != nil {
|
||||
d.serveError(rw, err, "Unable to processIndexHTML")
|
||||
return
|
||||
}
|
||||
|
||||
d.writeBlob(rw, "/index.html", content)
|
||||
|
||||
case runtimeJSPath:
|
||||
d.writeBlob(rw, path, d.runtimeJS)
|
||||
|
||||
case ipcJSPath:
|
||||
content := runtime.DesktopIPC
|
||||
if d.ipcJS != nil {
|
||||
content = d.ipcJS(req)
|
||||
}
|
||||
d.writeBlob(rw, path, content)
|
||||
|
||||
default:
|
||||
d.handler.ServeHTTP(rw, req)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *AssetServer) Load(filename string) ([]byte, string, error) {
|
||||
// This will be removed as soon as AssetsHandler have been fully introduced.
|
||||
if !strings.HasPrefix(filename, "/") {
|
||||
filename = "/" + filename
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "wails://wails"+filename, nil)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
rw := httptest.NewRecorder()
|
||||
d.ServeHTTP(rw, req)
|
||||
|
||||
content := rw.Body.Bytes()
|
||||
mimeType := rw.HeaderMap.Get(HeaderContentType)
|
||||
if mimeType == "" {
|
||||
mimeType = GetMimetype(filename, content)
|
||||
}
|
||||
return content, mimeType, nil
|
||||
}
|
||||
|
||||
func (d *AssetServer) processIndexHTML(indexHTML []byte) ([]byte, error) {
|
||||
htmlNode, err := getHTMLNode(indexHTML)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if d.appendSpinnerToBody {
|
||||
err = appendSpinnerToBody(htmlNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := insertScriptInHead(htmlNode, runtimeJSPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := insertScriptInHead(htmlNode, ipcJSPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
err = html.Render(&buffer, htmlNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (d *AssetServer) writeBlob(rw http.ResponseWriter, filename string, blob []byte) {
|
||||
header := rw.Header()
|
||||
header.Set(HeaderContentLength, fmt.Sprintf("%d", len(blob)))
|
||||
if mimeType := header.Get(HeaderContentType); mimeType == "" {
|
||||
mimeType = GetMimetype(filename, blob)
|
||||
header.Set(HeaderContentType, mimeType)
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
if _, err := rw.Write(blob); err != nil {
|
||||
d.serveError(rw, err, "Unable to write content %s", filename)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *AssetServer) serveError(rw http.ResponseWriter, err error, msg string, args ...interface{}) {
|
||||
args = append(args, err)
|
||||
d.logError(msg+": %s", args...)
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
func (d *AssetServer) logDebug(message string, args ...interface{}) {
|
||||
if d.logger != nil {
|
||||
d.logger.Debug("[AssetServer] "+message, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *AssetServer) logError(message string, args ...interface{}) {
|
||||
if d.logger != nil {
|
||||
d.logger.Error("[AssetServer] "+message, args...)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,114 +4,30 @@
|
|||
package assetserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/runtime"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
The assetserver for dev serves assets from disk.
|
||||
It injects a websocket based IPC script into `index.html`.
|
||||
|
||||
*/
|
||||
|
||||
type BrowserAssetServer struct {
|
||||
assets fs.FS
|
||||
runtimeJS []byte
|
||||
logger *logger.Logger
|
||||
}
|
||||
|
||||
func NewBrowserAssetServer(ctx context.Context, assets fs.FS, bindingsJSON string) (*BrowserAssetServer, error) {
|
||||
result := &BrowserAssetServer{}
|
||||
_logger := ctx.Value("logger")
|
||||
if _logger != nil {
|
||||
result.logger = _logger.(*logger.Logger)
|
||||
}
|
||||
|
||||
var err error
|
||||
result.assets, err = prepareAssetsForServing(assets)
|
||||
func NewBrowserAssetServer(ctx context.Context, handler http.Handler, bindingsJSON string) (*AssetServer, error) {
|
||||
result, err := NewAssetServerWithHandler(ctx, handler, bindingsJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString(`window.wailsbindings='` + bindingsJSON + `';` + "\n")
|
||||
buffer.Write(runtime.RuntimeDesktopJS)
|
||||
result.runtimeJS = buffer.Bytes()
|
||||
result.appendSpinnerToBody = true
|
||||
result.ipcJS = func(req *http.Request) []byte {
|
||||
if strings.Contains(req.UserAgent(), WailsUserAgentValue) {
|
||||
return runtime.DesktopIPC
|
||||
}
|
||||
return runtime.WebsocketIPC
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (d *BrowserAssetServer) LogDebug(message string, args ...interface{}) {
|
||||
if d.logger != nil {
|
||||
d.logger.Debug("[BrowserAssetServer] "+message, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *BrowserAssetServer) processIndexHTML() ([]byte, error) {
|
||||
indexHTML, err := fs.ReadFile(a.assets, "index.html")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
htmlNode, err := getHTMLNode(indexHTML)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = appendSpinnerToBody(htmlNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wailsOptions, err := extractOptions(indexHTML)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if wailsOptions.disableIPCInjection == false {
|
||||
err := insertScriptInHead(htmlNode, "/wails/ipc.js")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if wailsOptions.disableRuntimeInjection == false {
|
||||
err := insertScriptInHead(htmlNode, "/wails/runtime.js")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
err = html.Render(&buffer, htmlNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (a *BrowserAssetServer) Load(filename string) ([]byte, string, error) {
|
||||
var content []byte
|
||||
var err error
|
||||
switch filename {
|
||||
case "/":
|
||||
content, err = a.processIndexHTML()
|
||||
case "/wails/runtime.js":
|
||||
content = a.runtimeJS
|
||||
case "/wails/ipc.js":
|
||||
content = runtime.WebsocketIPC
|
||||
default:
|
||||
filename = strings.TrimPrefix(filename, "/")
|
||||
a.LogDebug("Loading file: %s", filename)
|
||||
content, err = fs.ReadFile(a.assets, filename)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
mimeType := GetMimetype(filename, content)
|
||||
return content, mimeType, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
package assetserver
|
||||
|
||||
import (
|
||||
iofs "io/fs"
|
||||
"path"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
)
|
||||
|
||||
func prepareAssetsForServing(assets iofs.FS) (iofs.FS, error) {
|
||||
if _, err := assets.Open("."); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subDir, err := fs.FindPathToFile(assets, "index.html")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
assets, err = iofs.Sub(assets, path.Clean(subDir))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return assets, nil
|
||||
}
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
package assetserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
_ "embed"
|
||||
"io/fs"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/runtime"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
)
|
||||
|
||||
//go:embed defaultindex.html
|
||||
var defaultHTML []byte
|
||||
|
||||
type DesktopAssetServer struct {
|
||||
assets fs.FS
|
||||
runtimeJS []byte
|
||||
logger *logger.Logger
|
||||
servingFromDisk bool
|
||||
}
|
||||
|
||||
func NewDesktopAssetServer(ctx context.Context, assets fs.FS, bindingsJSON string, servingFromDisk bool) (*DesktopAssetServer, error) {
|
||||
result := &DesktopAssetServer{
|
||||
servingFromDisk: servingFromDisk,
|
||||
}
|
||||
|
||||
_logger := ctx.Value("logger")
|
||||
if _logger != nil {
|
||||
result.logger = _logger.(*logger.Logger)
|
||||
}
|
||||
|
||||
var err error
|
||||
result.assets, err = prepareAssetsForServing(assets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString(`window.wailsbindings='` + bindingsJSON + `';` + "\n")
|
||||
buffer.Write(runtime.RuntimeDesktopJS)
|
||||
result.runtimeJS = buffer.Bytes()
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (d *DesktopAssetServer) LogDebug(message string, args ...interface{}) {
|
||||
if d.logger != nil {
|
||||
d.logger.Debug("[DesktopAssetServer] "+message, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// loadFile will try to load the file from disk. If there is an error
|
||||
// it will retry until eventually it will give up and error.
|
||||
func (d *DesktopAssetServer) loadFile(filename string) ([]byte, error) {
|
||||
if !d.servingFromDisk {
|
||||
return fs.ReadFile(d.assets, filename)
|
||||
}
|
||||
var result []byte
|
||||
var err error
|
||||
for tries := 0; tries < 50; tries++ {
|
||||
result, err = fs.ReadFile(d.assets, filename)
|
||||
if err != nil {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (d *DesktopAssetServer) processIndexHTML() ([]byte, error) {
|
||||
indexHTML, err := d.loadFile("index.html")
|
||||
if err != nil {
|
||||
indexHTML = defaultHTML
|
||||
}
|
||||
wailsOptions, err := extractOptions(indexHTML)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
if wailsOptions.disableRuntimeInjection == false {
|
||||
indexHTML, err = injectHTML(string(indexHTML), `<script src="/wails/runtime.js"></script>`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if wailsOptions.disableIPCInjection == false {
|
||||
indexHTML, err = injectHTML(string(indexHTML), `<script src="/wails/ipc.js"></script>`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return indexHTML, nil
|
||||
}
|
||||
|
||||
func (d *DesktopAssetServer) Load(filename string) ([]byte, string, error) {
|
||||
var content []byte
|
||||
var err error
|
||||
switch filename {
|
||||
case "/":
|
||||
content, err = d.processIndexHTML()
|
||||
case "/wails/runtime.js":
|
||||
content = d.runtimeJS
|
||||
case "/wails/ipc.js":
|
||||
content = runtime.DesktopIPC
|
||||
default:
|
||||
filename = strings.TrimPrefix(filename, "/")
|
||||
d.LogDebug("Loading file: %s", filename)
|
||||
content, err = d.loadFile(filename)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
mimeType := GetMimetype(filename, content)
|
||||
return content, mimeType, nil
|
||||
}
|
||||
|
|
@ -3,91 +3,19 @@ package assetserver
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/net/html"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type optionType string
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
const (
|
||||
noAutoInject optionType = "noautoinject"
|
||||
noAutoInjectRuntime optionType = "noautoinjectruntime"
|
||||
noAutoInjectIPC optionType = "noautoinjectipc"
|
||||
HeaderContentType = "Content-Type"
|
||||
HeaderContentLength = "Content-Length"
|
||||
HeaderUserAgent = "User-Agent"
|
||||
HeaderCacheControl = "Cache-Control"
|
||||
|
||||
WailsUserAgentValue = "wails.io"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
disableRuntimeInjection bool
|
||||
disableIPCInjection bool
|
||||
}
|
||||
|
||||
func newOptions(optionString string) *Options {
|
||||
var result = &Options{}
|
||||
optionString = strings.ToLower(optionString)
|
||||
options := strings.Split(optionString, ",")
|
||||
for _, option := range options {
|
||||
switch optionType(strings.TrimSpace(option)) {
|
||||
case noAutoInject:
|
||||
result.disableRuntimeInjection = true
|
||||
result.disableIPCInjection = true
|
||||
case noAutoInjectIPC:
|
||||
result.disableIPCInjection = true
|
||||
case noAutoInjectRuntime:
|
||||
result.disableRuntimeInjection = true
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func injectHTML(input string, html string) ([]byte, error) {
|
||||
splits := strings.Split(input, "</head>")
|
||||
if len(splits) != 2 {
|
||||
return nil, fmt.Errorf("unable to locate a </head> tag in your html")
|
||||
}
|
||||
|
||||
var result bytes.Buffer
|
||||
result.WriteString(splits[0])
|
||||
result.WriteString(html)
|
||||
result.WriteString("</head>")
|
||||
result.WriteString(splits[1])
|
||||
return result.Bytes(), nil
|
||||
}
|
||||
|
||||
func extractOptions(htmldata []byte) (*Options, error) {
|
||||
doc, err := html.Parse(bytes.NewReader(htmldata))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var extractor func(*html.Node) *Options
|
||||
extractor = func(node *html.Node) *Options {
|
||||
if node.Type == html.ElementNode && node.Data == "meta" {
|
||||
isWailsOptionsTag := false
|
||||
wailsOptions := ""
|
||||
for _, attr := range node.Attr {
|
||||
if isWailsOptionsTag && attr.Key == "content" {
|
||||
wailsOptions = attr.Val
|
||||
}
|
||||
if attr.Val == "wails-options" {
|
||||
isWailsOptionsTag = true
|
||||
}
|
||||
}
|
||||
return newOptions(wailsOptions)
|
||||
}
|
||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||
result := extractor(child)
|
||||
if result != nil {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
result := extractor(doc)
|
||||
if result == nil {
|
||||
result = &Options{}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func createScriptNode(scriptName string) *html.Node {
|
||||
return &html.Node{
|
||||
Type: html.ElementNode,
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
package assetserver
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const realHTML = `<html>
|
||||
|
||||
<head>
|
||||
<title>test3</title>
|
||||
<meta name="wails-options" content="noautoinject">
|
||||
<link rel="stylesheet" href="/main.css">
|
||||
</head>
|
||||
|
||||
<body data-wails-drag>
|
||||
<div class="logo"></div>
|
||||
<div class="result" id="result">Please enter your name below <EFBFBD></div>
|
||||
<div class="input-box" id="input" data-wails-no-drag>
|
||||
<input class="input" id="name" type="text" autocomplete="off">
|
||||
<button class="btn" onclick="greet()">Greet</button>
|
||||
</div>
|
||||
|
||||
<script src="/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
`
|
||||
|
||||
func genMeta(content string) []byte {
|
||||
return []byte("<html><head><meta name=\"wails-options\" content=\"" + content + "\"></head><body></body></html>")
|
||||
}
|
||||
|
||||
func genOptions(runtime bool, bindings bool) *Options {
|
||||
return &Options{
|
||||
disableRuntimeInjection: runtime,
|
||||
disableIPCInjection: bindings,
|
||||
}
|
||||
}
|
||||
|
||||
func Test_extractOptions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
htmldata []byte
|
||||
want *Options
|
||||
wantError bool
|
||||
}{
|
||||
{"empty", []byte(""), &Options{}, false},
|
||||
{"bad data", []byte("<"), &Options{}, false},
|
||||
{"bad options", genMeta("noauto"), genOptions(false, false), false},
|
||||
{"realhtml", []byte(realHTML), genOptions(true, true), false},
|
||||
{"noautoinject", genMeta("noautoinject"), genOptions(true, true), false},
|
||||
{"noautoinjectipc", genMeta("noautoinjectipc"), genOptions(false, true), false},
|
||||
{"noautoinjectruntime", genMeta("noautoinjectruntime"), genOptions(true, false), false},
|
||||
{"spaces", genMeta(" noautoinjectruntime "), genOptions(true, false), false},
|
||||
{"multiple", genMeta("noautoinjectruntime,noautoinjectipc"), genOptions(true, true), false},
|
||||
{"multiple spaces", genMeta(" noautoinjectruntime, noautoinjectipc "), genOptions(true, true), false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := extractOptions(tt.htmldata)
|
||||
if !tt.wantError && err != nil {
|
||||
t.Errorf("did not want error but got it")
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("extractOptions() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ func (r RequestRespone) String() string {
|
|||
return fmt.Sprintf("Body: '%s', StatusCode: %d", string(r.Body), r.StatusCode)
|
||||
}
|
||||
|
||||
func ProcessRequest(uri string, assets *assetserver.DesktopAssetServer, expectedScheme string, expectedHosts ...string) (RequestRespone, error) {
|
||||
func ProcessRequest(uri string, assets *assetserver.AssetServer, expectedScheme string, expectedHosts ...string) (RequestRespone, error) {
|
||||
// Translate URI to file
|
||||
file, err := translateUriToFile(uri, expectedScheme, expectedHosts...)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#define WindowStartsFullscreen 3
|
||||
|
||||
WailsContext* Create(const char* title, int width, int height, int frameless, int resizable, int fullscreen, int fullSizeContent, int hideTitleBar, int titlebarAppearsTransparent, int hideTitle, int useToolbar, int hideToolbarSeparator, int webviewIsTransparent, int alwaysOnTop, int hideWindowOnClose, const char *appearance, int windowIsTranslucent, int debug, int windowStartState, int startsHidden, int minWidth, int minHeight, int maxWidth, int maxHeight);
|
||||
void Run(void*);
|
||||
void Run(void*, const char* url);
|
||||
|
||||
void SetTitle(void* ctx, const char *title);
|
||||
void Center(void* ctx);
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ void AppendSeparator(void* inMenu) {
|
|||
|
||||
|
||||
|
||||
void Run(void *inctx) {
|
||||
void Run(void *inctx, const char* url) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSApplication *app = [NSApplication sharedApplication];
|
||||
AppDelegate* delegate = [AppDelegate new];
|
||||
|
|
@ -341,7 +341,10 @@ void Run(void *inctx) {
|
|||
delegate.startHidden = ctx.startHidden;
|
||||
delegate.startFullscreen = ctx.startFullscreen;
|
||||
|
||||
[ctx loadRequest:@"wails://wails/"];
|
||||
NSString *_url = safeInit(url);
|
||||
[ctx loadRequest:_url];
|
||||
[_url release];
|
||||
|
||||
[app setMainMenu:ctx.applicationMenu];
|
||||
[app run];
|
||||
[ctx release];
|
||||
|
|
|
|||
|
|
@ -211,6 +211,7 @@
|
|||
// Webview stuff here!
|
||||
WKWebViewConfiguration *config = [WKWebViewConfiguration new];
|
||||
config.suppressesIncrementalRendering = true;
|
||||
config.applicationNameForUserAgent = @"wails.io";
|
||||
[config setURLSchemeHandler:self forURLScheme:@"wails"];
|
||||
|
||||
// [config.preferences setValue:[NSNumber numberWithBool:true] forKey:@"developerExtrasEnabled"];
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ type Frontend struct {
|
|||
debug bool
|
||||
|
||||
// Assets
|
||||
assets *assetserver.DesktopAssetServer
|
||||
assets *assetserver.AssetServer
|
||||
startURL string
|
||||
|
||||
// main window handle
|
||||
mainWindow *Window
|
||||
|
|
@ -65,29 +66,37 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
|||
bindings: appBindings,
|
||||
dispatcher: dispatcher,
|
||||
ctx: ctx,
|
||||
startURL: "wails://wails/",
|
||||
}
|
||||
|
||||
// Check if we have been given a directory to serve assets from.
|
||||
// If so, this means we are in dev mode and are serving assets off disk.
|
||||
// We indicate this through the `servingFromDisk` flag to ensure requests
|
||||
// aren't cached by WebView2 in dev mode
|
||||
_assetdir := ctx.Value("assetdir")
|
||||
if _assetdir != nil {
|
||||
result.servingFromDisk = true
|
||||
}
|
||||
_starturl, _ := ctx.Value("starturl").(string)
|
||||
if _starturl != "" {
|
||||
result.startURL = _starturl
|
||||
} else {
|
||||
// Check if we have been given a directory to serve assets from.
|
||||
// If so, this means we are in dev mode and are serving assets off disk.
|
||||
// We indicate this through the `servingFromDisk` flag to ensure requests
|
||||
// aren't cached by WebView2 in dev mode
|
||||
_assetdir := ctx.Value("assetdir")
|
||||
if _assetdir != nil {
|
||||
result.servingFromDisk = true
|
||||
}
|
||||
|
||||
bindingsJSON, err := appBindings.ToJSON()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
bindingsJSON, err := appBindings.ToJSON()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
assets, err := assetserver.NewAssetServer(ctx, appoptions, bindingsJSON)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
result.assets = assets
|
||||
|
||||
go result.startRequestProcessor()
|
||||
}
|
||||
assets, err := assetserver.NewDesktopAssetServer(ctx, appoptions.Assets, bindingsJSON, result.servingFromDisk)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
result.assets = assets
|
||||
|
||||
go result.startMessageProcessor()
|
||||
go result.startRequestProcessor()
|
||||
go result.startCallbackProcessor()
|
||||
|
||||
return result
|
||||
|
|
@ -134,7 +143,7 @@ func (f *Frontend) Run(ctx context.Context) error {
|
|||
f.frontendOptions.OnStartup(f.ctx)
|
||||
}
|
||||
}()
|
||||
mainWindow.Run()
|
||||
mainWindow.Run(f.startURL)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -119,8 +119,10 @@ func (w *Window) Center() {
|
|||
C.Center(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) Run() {
|
||||
C.Run(w.context)
|
||||
func (w *Window) Run(url string) {
|
||||
_url := C.CString(url)
|
||||
C.Run(w.context, _url)
|
||||
C.free(unsafe.Pointer(_url))
|
||||
}
|
||||
|
||||
func (w *Window) Quit() {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ type Frontend struct {
|
|||
debug bool
|
||||
|
||||
// Assets
|
||||
assets *assetserver.DesktopAssetServer
|
||||
assets *assetserver.AssetServer
|
||||
startURL string
|
||||
|
||||
// main window handle
|
||||
|
|
@ -60,41 +60,38 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
|||
bindings: appBindings,
|
||||
dispatcher: dispatcher,
|
||||
ctx: ctx,
|
||||
startURL: "file://wails/",
|
||||
startURL: "wails://wails/",
|
||||
}
|
||||
|
||||
bindingsJSON, err := appBindings.ToJSON()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_starturl, _ := ctx.Value("starturl").(string)
|
||||
if _starturl != "" {
|
||||
result.startURL = _starturl
|
||||
} else {
|
||||
// Check if we have been given a directory to serve assets from.
|
||||
// If so, this means we are in dev mode and are serving assets off disk.
|
||||
// We indicate this through the `servingFromDisk` flag to ensure requests
|
||||
// aren't cached by webkit.
|
||||
|
||||
_devServerURL := ctx.Value("devserverurl")
|
||||
if _devServerURL != nil {
|
||||
devServerURL := _devServerURL.(string)
|
||||
if len(devServerURL) > 0 && devServerURL != "http://localhost:34115" {
|
||||
result.startURL = devServerURL
|
||||
return result
|
||||
_assetdir := ctx.Value("assetdir")
|
||||
if _assetdir != nil {
|
||||
result.servingFromDisk = true
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have been given a directory to serve assets from.
|
||||
// If so, this means we are in dev mode and are serving assets off disk.
|
||||
// We indicate this through the `servingFromDisk` flag to ensure requests
|
||||
// aren't cached by webkit.
|
||||
bindingsJSON, err := appBindings.ToJSON()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
_assetdir := ctx.Value("assetdir")
|
||||
if _assetdir != nil {
|
||||
result.servingFromDisk = true
|
||||
}
|
||||
assets, err := assetserver.NewAssetServer(ctx, appoptions, bindingsJSON)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
result.assets = assets
|
||||
|
||||
assets, err := assetserver.NewDesktopAssetServer(ctx, appoptions.Assets, bindingsJSON, result.servingFromDisk)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
go result.startRequestProcessor()
|
||||
}
|
||||
result.assets = assets
|
||||
|
||||
go result.startMessageProcessor()
|
||||
go result.startRequestProcessor()
|
||||
|
||||
C.gtk_init(nil, nil)
|
||||
|
||||
|
|
@ -127,7 +124,7 @@ func (f *Frontend) Run(ctx context.Context) error {
|
|||
}
|
||||
}()
|
||||
|
||||
f.mainWindow.Run()
|
||||
f.mainWindow.Run(f.startURL)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -299,7 +296,7 @@ func (f *Frontend) processRequest(request unsafe.Pointer) {
|
|||
uri := C.webkit_uri_scheme_request_get_uri(req)
|
||||
goURI := C.GoString(uri)
|
||||
|
||||
res, err := common.ProcessRequest(goURI, f.assets, "wails", "", "null")
|
||||
res, err := common.ProcessRequest(goURI, f.assets, "wails", "wails")
|
||||
if err != nil {
|
||||
f.logger.Error("Error processing request '%s': %s (HttpResponse=%s)", goURI, err, res)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,6 +200,9 @@ GtkWidget* setupWebview(void* contentManager, GtkWindow* window, int hideWindowO
|
|||
} else {
|
||||
g_signal_connect(GTK_WIDGET(window), "delete-event", G_CALLBACK(close_button_pressed), NULL);
|
||||
}
|
||||
|
||||
WebKitSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview));
|
||||
webkit_settings_set_user_agent_with_application_details(settings, "wails.io", "");
|
||||
return webview;
|
||||
}
|
||||
|
||||
|
|
@ -209,8 +212,8 @@ void devtoolsEnabled(void* webview, int enabled) {
|
|||
webkit_settings_set_enable_developer_extras(settings, genabled);
|
||||
}
|
||||
|
||||
void loadIndex(void* webview) {
|
||||
webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), "wails:///");
|
||||
void loadIndex(void* webview, char* url) {
|
||||
webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), url);
|
||||
}
|
||||
|
||||
typedef struct DragOptions {
|
||||
|
|
@ -568,11 +571,12 @@ void SetWindowIcon(GtkWindow* window, const guchar* buf, gsize len) {
|
|||
*/
|
||||
import "C"
|
||||
import (
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func gtkBool(input bool) C.gboolean {
|
||||
|
|
@ -788,12 +792,14 @@ func (w *Window) SetWindowIcon(icon []byte) {
|
|||
C.SetWindowIcon(w.asGTKWindow(), (*C.guchar)(&icon[0]), (C.gsize)(len(icon)))
|
||||
}
|
||||
|
||||
func (w *Window) Run() {
|
||||
func (w *Window) Run(url string) {
|
||||
if w.menubar != nil {
|
||||
C.gtk_box_pack_start(C.GTKBOX(unsafe.Pointer(w.vbox)), w.menubar, 0, 0, 0)
|
||||
}
|
||||
C.gtk_box_pack_start(C.GTKBOX(unsafe.Pointer(w.vbox)), C.GTKWIDGET(w.webview), 1, 1, 0)
|
||||
C.loadIndex(w.webview)
|
||||
_url := C.CString(url)
|
||||
C.loadIndex(w.webview, _url)
|
||||
defer C.free(unsafe.Pointer(_url))
|
||||
C.gtk_widget_show_all(w.asGTKWidget())
|
||||
w.Center()
|
||||
switch w.appoptions.WindowStartState {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/internal/system/operatingsystem"
|
||||
"log"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
|
@ -22,6 +21,7 @@ import (
|
|||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/system/operatingsystem"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ type Frontend struct {
|
|||
debug bool
|
||||
|
||||
// Assets
|
||||
assets *assetserver.DesktopAssetServer
|
||||
assets *assetserver.AssetServer
|
||||
startURL string
|
||||
|
||||
// main window handle
|
||||
|
|
@ -66,18 +66,10 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
|||
versionInfo: versionInfo,
|
||||
}
|
||||
|
||||
bindingsJSON, err := appBindings.ToJSON()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
_devServerURL := ctx.Value("devserverurl")
|
||||
if _devServerURL != nil {
|
||||
devServerURL := _devServerURL.(string)
|
||||
if len(devServerURL) > 0 && devServerURL != "http://localhost:34115" {
|
||||
result.startURL = devServerURL
|
||||
return result
|
||||
}
|
||||
_starturl, _ := ctx.Value("starturl").(string)
|
||||
if _starturl != "" {
|
||||
result.startURL = _starturl
|
||||
return result
|
||||
}
|
||||
|
||||
// Check if we have been given a directory to serve assets from.
|
||||
|
|
@ -90,7 +82,12 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
|||
result.servingFromDisk = true
|
||||
}
|
||||
|
||||
assets, err := assetserver.NewDesktopAssetServer(ctx, appoptions.Assets, bindingsJSON, result.servingFromDisk)
|
||||
bindingsJSON, err := appBindings.ToJSON()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
assets, err := assetserver.NewAssetServer(ctx, appoptions, bindingsJSON)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
@ -361,6 +358,15 @@ func (f *Frontend) Notify(name string, data ...interface{}) {
|
|||
}
|
||||
|
||||
func (f *Frontend) processRequest(req *edge.ICoreWebView2WebResourceRequest, args *edge.ICoreWebView2WebResourceRequestedEventArgs) {
|
||||
// Setting the UserAgent on the CoreWebView2Settings clears the whole default UserAgent of the Edge browser, but
|
||||
// we want to just append our ApplicationIdentifier. So we adjust the UserAgent for every request.
|
||||
if reqHeaders, err := req.GetHeaders(); err == nil {
|
||||
useragent, _ := reqHeaders.GetHeader(assetserver.HeaderUserAgent)
|
||||
useragent = strings.Join([]string{useragent, assetserver.WailsUserAgentValue}, " ")
|
||||
reqHeaders.SetHeader(assetserver.HeaderUserAgent, useragent)
|
||||
reqHeaders.Release()
|
||||
}
|
||||
|
||||
//Get the request
|
||||
uri, _ := req.GetUri()
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,16 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/websocket/v2"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
|
||||
|
|
@ -24,16 +26,16 @@ import (
|
|||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
type DevWebServer struct {
|
||||
server *fiber.App
|
||||
server *echo.Echo
|
||||
ctx context.Context
|
||||
appoptions *options.App
|
||||
logger *logger.Logger
|
||||
appBindings *binding.Bindings
|
||||
dispatcher frontend.Dispatcher
|
||||
assetServer *assetserver.BrowserAssetServer
|
||||
socketMutex sync.Mutex
|
||||
websocketClients map[*websocket.Conn]*sync.Mutex
|
||||
menuManager *menumanager.Manager
|
||||
|
|
@ -41,95 +43,73 @@ type DevWebServer struct {
|
|||
|
||||
// Desktop frontend
|
||||
desktopFrontend frontend.Frontend
|
||||
}
|
||||
|
||||
func (d *DevWebServer) WindowReload() {
|
||||
d.broadcast("reload")
|
||||
devServerAddr string
|
||||
}
|
||||
|
||||
func (d *DevWebServer) Run(ctx context.Context) error {
|
||||
d.ctx = ctx
|
||||
|
||||
assetdir, _ := ctx.Value("assetdir").(string)
|
||||
d.server.Get("/wails/assetdir", func(fctx *fiber.Ctx) error {
|
||||
return fctx.SendString(assetdir)
|
||||
})
|
||||
d.server.GET("/wails/reload", d.handleReload)
|
||||
d.server.GET("/wails/ipc", d.handleIPCWebSocket)
|
||||
|
||||
d.server.Get("/wails/reload", func(fctx *fiber.Ctx) error {
|
||||
d.WindowReload()
|
||||
d.desktopFrontend.WindowReload()
|
||||
var assetHandler http.Handler
|
||||
_fronendDevServerURL, _ := ctx.Value("frontenddevserverurl").(string)
|
||||
if _fronendDevServerURL == "" {
|
||||
assetdir, _ := ctx.Value("assetdir").(string)
|
||||
d.server.GET("/wails/assetdir", func(c echo.Context) error {
|
||||
return c.String(http.StatusOK, assetdir)
|
||||
})
|
||||
|
||||
var err error
|
||||
assetHandler, err = assetserver.NewAsssetHandler(ctx, d.appoptions)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
externalURL, err := url.Parse(_fronendDevServerURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
waitCb := func() { d.LogDebug("Waiting for frontend DevServer '%s' to be ready", externalURL) }
|
||||
if !checkPortIsOpen(externalURL.Host, time.Minute, waitCb) {
|
||||
d.logger.Error("Timeout waiting for frontend DevServer")
|
||||
}
|
||||
|
||||
assetHandler = httputil.NewSingleHostReverseProxy(externalURL)
|
||||
}
|
||||
|
||||
// Setup internal dev server
|
||||
bindingsJSON, err := d.appBindings.ToJSON()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
assetServer, err := assetserver.NewBrowserAssetServer(ctx, assetHandler, bindingsJSON)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
d.server.GET("/*", func(c echo.Context) error {
|
||||
assetServer.ServeHTTP(c.Response(), c.Request())
|
||||
return nil
|
||||
})
|
||||
|
||||
d.server.Get("/wails/ipc", websocket.New(func(c *websocket.Conn) {
|
||||
d.newWebsocketSession(c)
|
||||
locker := d.websocketClients[c]
|
||||
// websocket.Conn bindings https://pkg.go.dev/github.com/fasthttp/websocket?tab=doc#pkg-index
|
||||
var (
|
||||
mt int
|
||||
msg []byte
|
||||
err error
|
||||
)
|
||||
|
||||
for {
|
||||
if mt, msg, err = c.ReadMessage(); err != nil {
|
||||
break
|
||||
}
|
||||
// We do not support drag in browsers
|
||||
if string(msg) == "drag" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Notify the other browsers of "EventEmit"
|
||||
if len(msg) > 2 && strings.HasPrefix(string(msg), "EE") {
|
||||
d.notifyExcludingSender(msg, c)
|
||||
}
|
||||
|
||||
// Send the message to dispatch to the frontend
|
||||
result, err := d.dispatcher.ProcessMessage(string(msg), d)
|
||||
if err != nil {
|
||||
d.logger.Error(err.Error())
|
||||
}
|
||||
if result != "" {
|
||||
locker.Lock()
|
||||
if err = c.WriteMessage(mt, []byte(result)); err != nil {
|
||||
locker.Unlock()
|
||||
break
|
||||
}
|
||||
locker.Unlock()
|
||||
}
|
||||
|
||||
}
|
||||
}))
|
||||
|
||||
_devServerURL := ctx.Value("devserverurl")
|
||||
if _devServerURL == "http://localhost:34115" {
|
||||
// Setup internal dev server
|
||||
bindingsJSON, err := d.appBindings.ToJSON()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
d.assetServer, err = assetserver.NewBrowserAssetServer(ctx, d.appoptions.Assets, bindingsJSON)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
d.server.Get("*", d.loadAsset)
|
||||
|
||||
if devServerAddr := d.devServerAddr; devServerAddr != "" {
|
||||
// Start server
|
||||
go func(server *fiber.App, log *logger.Logger) {
|
||||
err := server.Listen("localhost:34115")
|
||||
go func(server *echo.Echo, log *logger.Logger) {
|
||||
err := server.Start(devServerAddr)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
}
|
||||
d.LogDebug("Shutdown completed")
|
||||
}(d.server, d.logger)
|
||||
|
||||
d.LogDebug("Serving application at http://localhost:34115")
|
||||
d.LogDebug("Serving DevServer at http://%s", devServerAddr)
|
||||
|
||||
defer func() {
|
||||
err := d.server.Shutdown()
|
||||
err := d.server.Shutdown(context.Background())
|
||||
if err != nil {
|
||||
d.logger.Error(err.Error())
|
||||
}
|
||||
|
|
@ -137,7 +117,7 @@ func (d *DevWebServer) Run(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// Launch desktop app
|
||||
err := d.desktopFrontend.Run(ctx)
|
||||
err = d.desktopFrontend.Run(ctx)
|
||||
d.LogDebug("Starting shutdown")
|
||||
|
||||
return err
|
||||
|
|
@ -167,6 +147,11 @@ func (d *DevWebServer) MessageDialog(dialogOptions frontend.MessageDialogOptions
|
|||
return d.desktopFrontend.MessageDialog(dialogOptions)
|
||||
}
|
||||
|
||||
func (d *DevWebServer) WindowReload() {
|
||||
d.broadcast("reload")
|
||||
d.desktopFrontend.WindowReload()
|
||||
}
|
||||
|
||||
func (d *DevWebServer) WindowSetTitle(title string) {
|
||||
d.desktopFrontend.WindowSetTitle(title)
|
||||
}
|
||||
|
|
@ -256,28 +241,57 @@ func (d *DevWebServer) Notify(name string, data ...interface{}) {
|
|||
d.notify(name, data...)
|
||||
}
|
||||
|
||||
func (d *DevWebServer) loadAsset(ctx *fiber.Ctx) error {
|
||||
data, mimetype, err := d.assetServer.Load(ctx.Path())
|
||||
if err != nil {
|
||||
_, ok := err.(*fs.PathError)
|
||||
if !ok {
|
||||
return err
|
||||
func (d *DevWebServer) handleReload(c echo.Context) error {
|
||||
d.WindowReload()
|
||||
return c.NoContent(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (d *DevWebServer) handleIPCWebSocket(c echo.Context) error {
|
||||
websocket.Handler(func(c *websocket.Conn) {
|
||||
d.LogDebug(fmt.Sprintf("Websocket client %p connected", c))
|
||||
d.socketMutex.Lock()
|
||||
d.websocketClients[c] = &sync.Mutex{}
|
||||
locker := d.websocketClients[c]
|
||||
d.socketMutex.Unlock()
|
||||
|
||||
defer func() {
|
||||
d.socketMutex.Lock()
|
||||
delete(d.websocketClients, c)
|
||||
d.socketMutex.Unlock()
|
||||
d.LogDebug(fmt.Sprintf("Websocket client %p disconnected", c))
|
||||
}()
|
||||
|
||||
var msg string
|
||||
defer c.Close()
|
||||
for {
|
||||
if err := websocket.Message.Receive(c, &msg); err != nil {
|
||||
break
|
||||
}
|
||||
// We do not support drag in browsers
|
||||
if msg == "drag" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Notify the other browsers of "EventEmit"
|
||||
if len(msg) > 2 && strings.HasPrefix(string(msg), "EE") {
|
||||
d.notifyExcludingSender([]byte(msg), c)
|
||||
}
|
||||
|
||||
// Send the message to dispatch to the frontend
|
||||
result, err := d.dispatcher.ProcessMessage(string(msg), d)
|
||||
if err != nil {
|
||||
d.logger.Error(err.Error())
|
||||
}
|
||||
if result != "" {
|
||||
locker.Lock()
|
||||
if err = websocket.Message.Send(c, result); err != nil {
|
||||
locker.Unlock()
|
||||
break
|
||||
}
|
||||
locker.Unlock()
|
||||
}
|
||||
}
|
||||
err := ctx.SendStatus(404)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
err = ctx.SendStatus(200)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Set("Content-Type", mimetype)
|
||||
err = ctx.Send(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}).ServeHTTP(c.Response(), c.Request())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -285,20 +299,6 @@ func (d *DevWebServer) LogDebug(message string, args ...interface{}) {
|
|||
d.logger.Debug("[DevWebServer] "+message, args...)
|
||||
}
|
||||
|
||||
func (d *DevWebServer) newWebsocketSession(c *websocket.Conn) {
|
||||
d.socketMutex.Lock()
|
||||
defer d.socketMutex.Unlock()
|
||||
c.SetCloseHandler(func(code int, text string) error {
|
||||
d.socketMutex.Lock()
|
||||
defer d.socketMutex.Unlock()
|
||||
delete(d.websocketClients, c)
|
||||
d.LogDebug(fmt.Sprintf("Websocket client %p disconnected", c))
|
||||
return nil
|
||||
})
|
||||
d.websocketClients[c] = &sync.Mutex{}
|
||||
d.LogDebug(fmt.Sprintf("Websocket client %p connected", c))
|
||||
}
|
||||
|
||||
type EventNotify struct {
|
||||
Name string `json:"name"`
|
||||
Data []interface{} `json:"data"`
|
||||
|
|
@ -314,7 +314,7 @@ func (d *DevWebServer) broadcast(message string) {
|
|||
return
|
||||
}
|
||||
locker.Lock()
|
||||
err := client.WriteMessage(websocket.TextMessage, []byte(message))
|
||||
err := websocket.Message.Send(client, message)
|
||||
if err != nil {
|
||||
locker.Unlock()
|
||||
d.logger.Error(err.Error())
|
||||
|
|
@ -348,7 +348,7 @@ func (d *DevWebServer) broadcastExcludingSender(message string, sender *websocke
|
|||
return
|
||||
}
|
||||
locker.Lock()
|
||||
err := client.WriteMessage(websocket.TextMessage, []byte(message))
|
||||
err := websocket.Message.Send(client, message)
|
||||
if err != nil {
|
||||
locker.Unlock()
|
||||
d.logger.Error(err.Error())
|
||||
|
|
@ -374,19 +374,38 @@ func (d *DevWebServer) notifyExcludingSender(eventMessage []byte, sender *websoc
|
|||
|
||||
func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher, menuManager *menumanager.Manager, desktopFrontend frontend.Frontend) *DevWebServer {
|
||||
result := &DevWebServer{
|
||||
ctx: ctx,
|
||||
desktopFrontend: desktopFrontend,
|
||||
appoptions: appoptions,
|
||||
logger: myLogger,
|
||||
appBindings: appBindings,
|
||||
dispatcher: dispatcher,
|
||||
server: fiber.New(fiber.Config{
|
||||
|
||||
ReadTimeout: time.Second * 5,
|
||||
DisableStartupMessage: true,
|
||||
}),
|
||||
ctx: ctx,
|
||||
desktopFrontend: desktopFrontend,
|
||||
appoptions: appoptions,
|
||||
logger: myLogger,
|
||||
appBindings: appBindings,
|
||||
dispatcher: dispatcher,
|
||||
server: echo.New(),
|
||||
menuManager: menuManager,
|
||||
websocketClients: make(map[*websocket.Conn]*sync.Mutex),
|
||||
}
|
||||
|
||||
result.devServerAddr, _ = ctx.Value("devserver").(string)
|
||||
result.server.HideBanner = true
|
||||
result.server.HidePort = true
|
||||
return result
|
||||
}
|
||||
|
||||
func checkPortIsOpen(host string, timeout time.Duration, waitCB func()) (ret bool) {
|
||||
if timeout == 0 {
|
||||
timeout = time.Minute
|
||||
}
|
||||
|
||||
deadline := time.Now().Add(timeout)
|
||||
for time.Now().Before(deadline) {
|
||||
conn, _ := net.DialTimeout("tcp", host, 2*time.Second)
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
waitCB()
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ function handleDisconnect() {
|
|||
|
||||
function _connect() {
|
||||
if (websocket == null) {
|
||||
websocket = new WebSocket('ws://' + window.location.hostname + ':34115/wails/ipc');
|
||||
websocket = new WebSocket('ws://' + window.location.host + '/wails/ipc');
|
||||
websocket.onopen = handleConnect;
|
||||
websocket.onerror = function (e) {
|
||||
e.stopImmediatePropagation();
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -23,6 +23,8 @@ type Project struct {
|
|||
// Commands used in `wails dev`
|
||||
DevCommand string `json:"frontend:dev"`
|
||||
DevWatcherCommand string `json:"frontend:dev:watcher"`
|
||||
// The url of the external wails dev server. If this is set, this server is used for the frontend. Default ""
|
||||
FrontendDevServerURL string `json:"frontend:dev:serverUrl"`
|
||||
|
||||
// Directory to generate the API Module
|
||||
WailsJSDir string `json:"wailsjsdir"`
|
||||
|
|
@ -71,8 +73,8 @@ type Project struct {
|
|||
// The debounce time for hot-reload of the built-in dev server. Default 100
|
||||
DebounceMS int `json:"debounceMS"`
|
||||
|
||||
// The url to use to server assets. Default "https://localhost:34115"
|
||||
DevServerURL string `json:"devserverurl"`
|
||||
// The address to bind the wails dev server to. Default "localhost:34115"
|
||||
DevServer string `json:"devServer"`
|
||||
|
||||
// Arguments that are forwared to the application in dev mode
|
||||
AppArgs string `json:"appargs"`
|
||||
|
|
@ -162,6 +164,10 @@ func Load(projectPath string) (*Project, error) {
|
|||
result.Info.Comments = &v
|
||||
}
|
||||
|
||||
if result.DevServer == "" {
|
||||
result.DevServer = "localhost:34115"
|
||||
}
|
||||
|
||||
// Return our project data
|
||||
return &result, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,20 @@ To revert back to a stable version, run:
|
|||
If you want to test a branch, follow the instructions above, but ensure you switch the branch you want to test before installing:
|
||||
|
||||
- `git clone https://github.com/wailsapp/wails`
|
||||
- `cd wails`
|
||||
- `git checkout -b branch-to-test --track origin/branch-to-test`
|
||||
- `cd wails/v2/cmd/wails`
|
||||
- `cd v2/cmd/wails`
|
||||
- `go install`
|
||||
|
||||
## Testing a PR
|
||||
|
||||
If you want to test a PR, follow the instructions above, but ensure you fetch the PR and switch the branch before installing.
|
||||
Please replace `[IDofThePR]` with the ID of the PR shown on github.com:
|
||||
|
||||
- `git clone https://github.com/wailsapp/wails`
|
||||
- `cd wails`
|
||||
- `git fetch -u origin pull/[IDofThePR]/head:test/pr-[IDofThePR]`
|
||||
- `git checkout test/pr-[IDofThePR]`
|
||||
- `git reset --hard HEAD`
|
||||
- `cd v2/cmd/wails`
|
||||
- `go install`
|
||||
|
|
|
|||
|
|
@ -169,11 +169,12 @@ Your system is ready for Wails development!
|
|||
| -noreload | Disable automatic reload when assets change | |
|
||||
| -v | Verbosity level (0 - silent, 1 - standard, 2 - verbose) | 1 |
|
||||
| -wailsjsdir | The directory to generate the generated Wails JS modules | Value in `wails.json` |
|
||||
| -debounce | The time to wait for reload after an asset change is detected | 100 (milliseconds) |
|
||||
| -devserverurl "url" | Use 3rd party dev server url, EG Vite | "http://localhost:34115" |
|
||||
| -debounce | The time to wait for reload after an asset change is detected | 100 (milliseconds) |
|
||||
| -devserver "host:port" | The address to bind the wails dev server to | "localhost:34115" |
|
||||
| -frontenddevserverurl "url" | Use 3rd party dev server url to serve assets, EG Vite | "" |
|
||||
| -appargs "args" | Arguments passed to the application in shell style | |
|
||||
| -platform "platform" | Platform/Arch to target | `runtime.GOOS` |
|
||||
| -save | Saves the given `assetdir`, `reloaddirs`, `wailsjsdir`, `debounce` and `devserverurl` flags in
|
||||
| -save | Saves the given `assetdir`, `reloaddirs`, `wailsjsdir`, `debounce`, `devserver` and `frontenddevserverurl` flags in
|
||||
`wails.json` to become the defaults for subsequent invocations. | |
|
||||
|
||||
Example:
|
||||
|
|
|
|||
|
|
@ -14,12 +14,13 @@ The project config resides in the `wails.json` file in the project directory. Th
|
|||
"frontend:install": "[The command to install node dependencies, run in the frontend directory - often `npm install`]",
|
||||
"frontend:build": "[The command to build the assets, run in the frontend directory - often `npm run build`]",
|
||||
"frontend:dev": "[This command is the dev equivalent of frontend:build. If not specified falls back to frontend:build]",
|
||||
"frontend:dev:watcher": "[This command is run in a separate process on `wails dev`. Useful for 3rd party watchers]",
|
||||
"frontend:dev:watcher": "[This command is run in a separate process on `wails dev`. Useful for 3rd party watchers or starting 3d party dev servers]",
|
||||
"frontend:dev:serverUrl": "[URL to a 3rd party dev server to be used to serve assets, EG Vite",
|
||||
"wailsjsdir": "[Relative path to the directory that the auto-generated JS modules will be created]",
|
||||
"version": "[Project config version]",
|
||||
"outputfilename": "[The name of the binary]",
|
||||
"debounceMS": 100, // The default time the dev server waits to reload when it detects a vhange in assets
|
||||
"devserverurl": "[URL to the dev server serving local assets. Default: http://localhost:34115]",
|
||||
"devServer": "[Address to bind the wails dev sever to. Default: http://localhost:34115]",
|
||||
"appargs": "[Arguments passed to the application in shell style when in dev mode]",
|
||||
"runNonNativeBuildHooks": false, // Defines if build hooks should be run though they are defined for an OS other than the host OS.
|
||||
"postBuildHooks": {
|
||||
|
|
@ -40,5 +41,5 @@ The project config resides in the `wails.json` file in the project directory. Th
|
|||
|
||||
This file is read by the Wails CLI when running `wails build` or `wails dev`.
|
||||
|
||||
The `assetdir`, `reloaddirs`, `wailsjsdir`, `debounceMS` and `devserverurl` flags in `wails build/dev` will update the project config
|
||||
The `assetdir`, `reloaddirs`, `wailsjsdir`, `debounceMS`, `devserver` and `frontenddevserverurl` flags in `wails build/dev` will update the project config
|
||||
and thus become defaults for subsequent runs.
|
||||
Loading…
Add table
Add a link
Reference in a new issue