Embed default assets
This commit is contained in:
parent
83d25e00fe
commit
7204ae2e19
13
README.md
13
README.md
@ -34,16 +34,16 @@ Works with mobile versions too.
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
You need Go 1.13+ to compile the binary. If you have an older version of Go and don't want to install the tarball
|
You need Go 1.16+ to compile the binary. If you have an older version of Go and don't want to install the tarball
|
||||||
manually, you can install newer version of Go into your `GOPATH`:
|
manually, you can install newer version of Go into your `GOPATH`:
|
||||||
|
|
||||||
0. Install Go 1.14
|
0. Install Go 1.17
|
||||||
|
|
||||||
```
|
```
|
||||||
$ go get golang.org/dl/go1.14.2
|
$ go get golang.org/dl/go1.17.1
|
||||||
# Assuming your GOPATH is default (~/go), Go 1.14.2 will be installed in ~/go/bin
|
# Assuming your GOPATH is default (~/go), Go 1.17.1 will be installed in ~/go/bin
|
||||||
$ ~/go/bin/go1.14.2 version
|
$ ~/go/bin/go1.17.1 version
|
||||||
go version go1.14.2 linux/amd64
|
go version go1.17.1 linux/amd64
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Clone this repository:
|
1. Clone this repository:
|
||||||
@ -98,6 +98,7 @@ manually, you can install newer version of Go into your `GOPATH`:
|
|||||||
ipinfo_api_key=""
|
ipinfo_api_key=""
|
||||||
|
|
||||||
# assets directory path, defaults to `assets` in the same directory
|
# assets directory path, defaults to `assets` in the same directory
|
||||||
|
# if the path cannot be found, embedded default assets will be used
|
||||||
assets_path="./assets"
|
assets_path="./assets"
|
||||||
|
|
||||||
# password for logging into statistics page, change this to enable stats page
|
# password for logging into statistics page, change this to enable stats page
|
||||||
|
@ -28,7 +28,7 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configFile string = ""
|
configFile string
|
||||||
loadedConfig *Config = nil
|
loadedConfig *Config = nil
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,7 +40,6 @@ func init() {
|
|||||||
viper.SetDefault("enable_cors", false)
|
viper.SetDefault("enable_cors", false)
|
||||||
viper.SetDefault("statistics_password", "PASSWORD")
|
viper.SetDefault("statistics_password", "PASSWORD")
|
||||||
viper.SetDefault("redact_ip_addresses", false)
|
viper.SetDefault("redact_ip_addresses", false)
|
||||||
viper.SetDefault("assets_path", "./assets")
|
|
||||||
viper.SetDefault("database_type", "postgresql")
|
viper.SetDefault("database_type", "postgresql")
|
||||||
viper.SetDefault("database_hostname", "localhost")
|
viper.SetDefault("database_hostname", "localhost")
|
||||||
viper.SetDefault("database_name", "speedtest")
|
viper.SetDefault("database_name", "speedtest")
|
||||||
|
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
|||||||
module github.com/librespeed/speedtest
|
module github.com/librespeed/speedtest
|
||||||
|
|
||||||
go 1.13
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
package results
|
package results
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"image/draw"
|
"image/draw"
|
||||||
"image/png"
|
"image/png"
|
||||||
"io/ioutil"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -37,6 +36,12 @@ const (
|
|||||||
labelUpload = "Upload"
|
labelUpload = "Upload"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed fonts/NotoSansDisplay-Medium.ttf
|
||||||
|
var fontMediumBytes []byte
|
||||||
|
|
||||||
|
//go:embed fonts/NotoSansDisplay-Light.ttf
|
||||||
|
var fontLightBytes []byte
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ipv4Regex = regexp.MustCompile(`(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)`)
|
ipv4Regex = regexp.MustCompile(`(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)`)
|
||||||
ipv6Regex = regexp.MustCompile(`(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))`)
|
ipv6Regex = regexp.MustCompile(`(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))`)
|
||||||
@ -83,25 +88,17 @@ type IPInfoResponse struct {
|
|||||||
func Initialize(c *config.Config) {
|
func Initialize(c *config.Config) {
|
||||||
// changed to use Noto Sans instead of OpenSans, due to issue:
|
// changed to use Noto Sans instead of OpenSans, due to issue:
|
||||||
// https://github.com/golang/freetype/issues/8
|
// https://github.com/golang/freetype/issues/8
|
||||||
if b, err := ioutil.ReadFile(filepath.Join(c.AssetsPath, "NotoSansDisplay-Light.ttf")); err != nil {
|
fLight, err := freetype.ParseFont(fontLightBytes)
|
||||||
log.Fatalf("Error opening NotoSansDisplay-Light font: %s", err)
|
|
||||||
} else {
|
|
||||||
f, err := freetype.ParseFont(b)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error parsing NotoSansDisplay-Light font: %s", err)
|
log.Fatalf("Error parsing NotoSansDisplay-Light font: %s", err)
|
||||||
}
|
}
|
||||||
fontLight = f
|
fontLight = fLight
|
||||||
}
|
|
||||||
|
|
||||||
if b, err := ioutil.ReadFile(filepath.Join(c.AssetsPath, "NotoSansDisplay-Medium.ttf")); err != nil {
|
fMedium, err := freetype.ParseFont(fontMediumBytes)
|
||||||
log.Fatalf("Error opening NotoSansDisplay-Medium font: %s", err)
|
|
||||||
} else {
|
|
||||||
f, err := freetype.ParseFont(b)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error parsing NotoSansDisplay-Medium font: %s", err)
|
log.Fatalf("Error parsing NotoSansDisplay-Medium font: %s", err)
|
||||||
}
|
}
|
||||||
fontBold = f
|
fontBold = fMedium
|
||||||
}
|
|
||||||
|
|
||||||
pingJitterLabelFace = truetype.NewFace(fontBold, &truetype.Options{
|
pingJitterLabelFace = truetype.NewFace(fontBold, &truetype.Options{
|
||||||
Size: 12,
|
Size: 12,
|
||||||
|
@ -5,13 +5,13 @@ listen_port=8989
|
|||||||
# proxy protocol port, use 0 to disable
|
# proxy protocol port, use 0 to disable
|
||||||
proxyprotocol_port=0
|
proxyprotocol_port=0
|
||||||
# Server location
|
# Server location
|
||||||
server_lat=0
|
server_lat=1
|
||||||
server_lng=0
|
server_lng=1
|
||||||
# ipinfo.io API key, if applicable
|
# ipinfo.io API key, if applicable
|
||||||
ipinfo_api_key=""
|
ipinfo_api_key=""
|
||||||
|
|
||||||
# assets directory path, defaults to `assets` in the same directory
|
# assets directory path, defaults to `assets` in the same directory
|
||||||
assets_path="./assets"
|
assets_path=""
|
||||||
|
|
||||||
# password for logging into statistics page
|
# password for logging into statistics page
|
||||||
statistics_password="PASSWORD"
|
statistics_password="PASSWORD"
|
||||||
|
28
web/web.go
28
web/web.go
@ -1,11 +1,14 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -26,6 +29,9 @@ const (
|
|||||||
chunkSize = 1048576
|
chunkSize = 1048576
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed assets
|
||||||
|
var defaultAssets embed.FS
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// generate random data for download test on start to minimize runtime overhead
|
// generate random data for download test on start to minimize runtime overhead
|
||||||
randomData = getRandomData(chunkSize)
|
randomData = getRandomData(chunkSize)
|
||||||
@ -46,9 +52,21 @@ func ListenAndServe(conf *config.Config) error {
|
|||||||
r.Use(middleware.NoCache)
|
r.Use(middleware.NoCache)
|
||||||
r.Use(middleware.Recoverer)
|
r.Use(middleware.Recoverer)
|
||||||
|
|
||||||
|
var assetFS http.FileSystem
|
||||||
|
if fi, err := os.Stat(conf.AssetsPath); os.IsNotExist(err) || !fi.IsDir() {
|
||||||
|
log.Warnf("Configured asset path %s does not exist or is not a directory, using default assets", conf.AssetsPath)
|
||||||
|
sub, err := fs.Sub(defaultAssets, "assets")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed when processing default assets: %s", err)
|
||||||
|
}
|
||||||
|
assetFS = http.FS(sub)
|
||||||
|
} else {
|
||||||
|
assetFS = justFilesFilesystem{fs: http.Dir(conf.AssetsPath), readDirBatchSize: 2}
|
||||||
|
}
|
||||||
|
|
||||||
addr := net.JoinHostPort(conf.BindAddress, conf.Port)
|
addr := net.JoinHostPort(conf.BindAddress, conf.Port)
|
||||||
log.Infof("Starting backend server on %s", addr)
|
log.Infof("Starting backend server on %s", addr)
|
||||||
r.Get("/*", pages)
|
r.Get("/*", pages(assetFS))
|
||||||
r.HandleFunc("/empty", empty)
|
r.HandleFunc("/empty", empty)
|
||||||
r.HandleFunc("/backend/empty", empty)
|
r.HandleFunc("/backend/empty", empty)
|
||||||
r.Get("/garbage", garbage)
|
r.Get("/garbage", garbage)
|
||||||
@ -96,16 +114,18 @@ func listenProxyProtocol(conf *config.Config, r *chi.Mux) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pages(w http.ResponseWriter, r *http.Request) {
|
func pages(fs http.FileSystem) http.HandlerFunc {
|
||||||
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.RequestURI == "/" {
|
if r.RequestURI == "/" {
|
||||||
r.RequestURI = "/index.html"
|
r.RequestURI = "/index.html"
|
||||||
}
|
}
|
||||||
|
|
||||||
conf := config.LoadedConfig()
|
|
||||||
fs := justFilesFilesystem{fs: http.Dir(conf.AssetsPath), readDirBatchSize: 2}
|
|
||||||
http.FileServer(fs).ServeHTTP(w, r)
|
http.FileServer(fs).ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
func empty(w http.ResponseWriter, r *http.Request) {
|
func empty(w http.ResponseWriter, r *http.Request) {
|
||||||
_, err := io.Copy(ioutil.Discard, r.Body)
|
_, err := io.Copy(ioutil.Discard, r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user