Switch remote deploy to vendored source builds
Move remote deployment to a vendored source bundle built on the target host via Docker so redeploys no longer require local cross-compilation or host Go installation. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
255
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/assetserver.go
generated
vendored
Normal file
255
vendor/github.com/wailsapp/wails/v2/pkg/assetserver/assetserver.go
generated
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
"html/template"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
|
||||
)
|
||||
|
||||
const (
|
||||
runtimeJSPath = "/wails/runtime.js"
|
||||
ipcJSPath = "/wails/ipc.js"
|
||||
runtimePath = "/wails/runtime"
|
||||
)
|
||||
|
||||
type RuntimeAssets interface {
|
||||
DesktopIPC() []byte
|
||||
WebsocketIPC() []byte
|
||||
RuntimeDesktopJS() []byte
|
||||
}
|
||||
|
||||
type RuntimeHandler interface {
|
||||
HandleRuntimeCall(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
type AssetServer struct {
|
||||
handler http.Handler
|
||||
runtimeJS []byte
|
||||
ipcJS func(*http.Request) []byte
|
||||
|
||||
logger Logger
|
||||
runtime RuntimeAssets
|
||||
|
||||
servingFromDisk bool
|
||||
appendSpinnerToBody bool
|
||||
|
||||
// Use http based runtime
|
||||
runtimeHandler RuntimeHandler
|
||||
|
||||
// plugin scripts
|
||||
pluginScripts map[string]string
|
||||
|
||||
assetServerWebView
|
||||
}
|
||||
|
||||
func NewAssetServerMainPage(bindingsJSON string, options *options.App, servingFromDisk bool, logger Logger, runtime RuntimeAssets) (*AssetServer, error) {
|
||||
assetOptions, err := BuildAssetServerConfig(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewAssetServer(bindingsJSON, assetOptions, servingFromDisk, logger, runtime)
|
||||
}
|
||||
|
||||
func NewAssetServer(bindingsJSON string, options assetserver.Options, servingFromDisk bool, logger Logger, runtime RuntimeAssets) (*AssetServer, error) {
|
||||
handler, err := NewAssetHandler(options, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewAssetServerWithHandler(handler, bindingsJSON, servingFromDisk, logger, runtime)
|
||||
}
|
||||
|
||||
func NewAssetServerWithHandler(handler http.Handler, bindingsJSON string, servingFromDisk bool, logger Logger, runtime RuntimeAssets) (*AssetServer, error) {
|
||||
|
||||
var buffer bytes.Buffer
|
||||
if bindingsJSON != "" {
|
||||
escapedBindingsJSON := template.JSEscapeString(bindingsJSON)
|
||||
buffer.WriteString(`window.wailsbindings='` + escapedBindingsJSON + `';` + "\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: servingFromDisk,
|
||||
logger: logger,
|
||||
runtime: runtime,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (d *AssetServer) UseRuntimeHandler(handler RuntimeHandler) {
|
||||
d.runtimeHandler = handler
|
||||
}
|
||||
|
||||
func (d *AssetServer) AddPluginScript(pluginName string, script string) {
|
||||
if d.pluginScripts == nil {
|
||||
d.pluginScripts = make(map[string]string)
|
||||
}
|
||||
pluginName = strings.ReplaceAll(pluginName, "/", "_")
|
||||
pluginName = html.EscapeString(pluginName)
|
||||
pluginScriptName := fmt.Sprintf("/plugin_%s_%d.js", pluginName, rand.Intn(100000))
|
||||
d.pluginScripts[pluginScriptName] = script
|
||||
}
|
||||
|
||||
func (d *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
if isWebSocket(req) {
|
||||
// WebSockets are not supported by the AssetServer
|
||||
rw.WriteHeader(http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if d.servingFromDisk {
|
||||
rw.Header().Add(HeaderCacheControl, "no-cache")
|
||||
}
|
||||
|
||||
handler := d.handler
|
||||
if req.Method != http.MethodGet {
|
||||
handler.ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
|
||||
path := req.URL.Path
|
||||
if path == runtimeJSPath {
|
||||
d.writeBlob(rw, path, d.runtimeJS)
|
||||
} else if path == runtimePath && d.runtimeHandler != nil {
|
||||
d.runtimeHandler.HandleRuntimeCall(rw, req)
|
||||
} else if path == ipcJSPath {
|
||||
content := d.runtime.DesktopIPC()
|
||||
if d.ipcJS != nil {
|
||||
content = d.ipcJS(req)
|
||||
}
|
||||
d.writeBlob(rw, path, content)
|
||||
|
||||
} else if script, ok := d.pluginScripts[path]; ok {
|
||||
d.writeBlob(rw, path, []byte(script))
|
||||
} else if d.isRuntimeInjectionMatch(path) {
|
||||
recorder := &bodyRecorder{
|
||||
ResponseWriter: rw,
|
||||
doRecord: func(code int, h http.Header) bool {
|
||||
if code == http.StatusNotFound {
|
||||
return true
|
||||
}
|
||||
|
||||
if code != http.StatusOK {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.Contains(h.Get(HeaderContentType), "text/html")
|
||||
},
|
||||
}
|
||||
|
||||
handler.ServeHTTP(recorder, req)
|
||||
|
||||
body := recorder.Body()
|
||||
if body == nil {
|
||||
// The body has been streamed and not recorded, we are finished
|
||||
return
|
||||
}
|
||||
|
||||
code := recorder.Code()
|
||||
switch code {
|
||||
case http.StatusOK:
|
||||
content, err := d.processIndexHTML(body.Bytes())
|
||||
if err != nil {
|
||||
d.serveError(rw, err, "Unable to processIndexHTML")
|
||||
return
|
||||
}
|
||||
d.writeBlob(rw, indexHTML, content)
|
||||
|
||||
case http.StatusNotFound:
|
||||
d.writeBlob(rw, indexHTML, defaultHTML)
|
||||
|
||||
default:
|
||||
rw.WriteHeader(code)
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
handler.ServeHTTP(rw, req)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Inject plugins
|
||||
for scriptName := range d.pluginScripts {
|
||||
if err := insertScriptInHead(htmlNode, scriptName); 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) {
|
||||
err := serveFile(rw, filename, blob)
|
||||
if 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...)
|
||||
}
|
||||
}
|
||||
|
||||
func (AssetServer) isRuntimeInjectionMatch(path string) bool {
|
||||
if path == "" {
|
||||
path = "/"
|
||||
}
|
||||
|
||||
return strings.HasSuffix(path, "/") ||
|
||||
strings.HasSuffix(path, "/"+indexHTML)
|
||||
}
|
||||
Reference in New Issue
Block a user