html
This commit is contained in:
parent
b958ff7ca2
commit
6e98701299
14
.idea/webResources.xml
Normal file
14
.idea/webResources.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="WebResourcesPaths">
|
||||
<contentEntries>
|
||||
<entry url="file://$PROJECT_DIR$">
|
||||
<entryData>
|
||||
<resourceRoots>
|
||||
<path value="file://$PROJECT_DIR$/webassets" />
|
||||
</resourceRoots>
|
||||
</entryData>
|
||||
</entry>
|
||||
</contentEntries>
|
||||
</component>
|
||||
</project>
|
2
Makefile
2
Makefile
@ -22,7 +22,7 @@ ids:
|
||||
|
||||
run: build
|
||||
mkdir -p .run-data
|
||||
sudo _build/bunny_backend
|
||||
sudo BUNNY_LIVERELOAD="$(shell pwd)/webassets" CONF_NS="local-host" _build/bunny_backend
|
||||
|
||||
gow:
|
||||
# go install github.com/mitranim/gow@latest
|
||||
|
47
api/handler/apiHandler.go
Normal file
47
api/handler/apiHandler.go
Normal file
@ -0,0 +1,47 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/ginext"
|
||||
bunny "locbunny"
|
||||
"locbunny/logic"
|
||||
"locbunny/models"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type APIHandler struct {
|
||||
app *logic.Application
|
||||
}
|
||||
|
||||
func NewAPIHandler(app *logic.Application) APIHandler {
|
||||
return APIHandler{
|
||||
app: app,
|
||||
}
|
||||
}
|
||||
|
||||
// ListServer swaggerdoc
|
||||
//
|
||||
// @Summary List running server
|
||||
//
|
||||
// @Success 200 {object} handler.ListServer.response
|
||||
// @Failure 400 {object} models.APIError
|
||||
// @Failure 500 {object} models.APIError
|
||||
//
|
||||
// @Router /server [GET]
|
||||
func (h APIHandler) ListServer(pctx ginext.PreContext) ginext.HTTPResponse {
|
||||
type response struct {
|
||||
Server []models.Server `json:"server"`
|
||||
}
|
||||
|
||||
ctx, _, errResp := pctx.Start()
|
||||
if errResp != nil {
|
||||
return *errResp
|
||||
}
|
||||
defer ctx.Cancel()
|
||||
|
||||
srvs, err := h.app.ListServer(ctx, bunny.Conf.VerifyConnTimeoutAPI)
|
||||
if err != nil {
|
||||
return ginext.Error(err)
|
||||
}
|
||||
|
||||
return ginext.JSON(http.StatusOK, response{Server: srvs})
|
||||
}
|
@ -1,11 +1,20 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/ginext"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||
templhtml "html/template"
|
||||
bunny "locbunny"
|
||||
"locbunny/logic"
|
||||
"locbunny/models"
|
||||
"locbunny/webassets"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
templtext "text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WebHandler struct {
|
||||
@ -18,32 +27,151 @@ func NewWebHandler(app *logic.Application) WebHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// ListServer swaggerdoc
|
||||
// ServeIndexHTML swaggerdoc
|
||||
//
|
||||
// @Summary List running server
|
||||
// @Summary (Website)
|
||||
//
|
||||
// @Success 200 {object} handler.ListServer.response
|
||||
// @Failure 400 {object} models.APIError
|
||||
// @Failure 500 {object} models.APIError
|
||||
//
|
||||
// @Router /server [GET]
|
||||
func (h WebHandler) ListServer(pctx ginext.PreContext) ginext.HTTPResponse {
|
||||
type response struct {
|
||||
Server []models.Server `json:"server"`
|
||||
}
|
||||
|
||||
// @Router / [GET]
|
||||
// @Router /index.html [GET]
|
||||
func (h WebHandler) ServeIndexHTML(pctx ginext.PreContext) ginext.HTTPResponse {
|
||||
ctx, _, errResp := pctx.Start()
|
||||
if errResp != nil {
|
||||
return *errResp
|
||||
}
|
||||
defer ctx.Cancel()
|
||||
|
||||
srvs, err := h.app.ListServer(ctx)
|
||||
templ, err := h.app.Assets.Template("index.html", h.buildIndexHTMLTemplate)
|
||||
if err != nil {
|
||||
return ginext.Error(err)
|
||||
}
|
||||
|
||||
langext.SortBy(srvs, func(v models.Server) int { return v.Port })
|
||||
data := map[string]any{}
|
||||
|
||||
return ginext.JSON(http.StatusOK, response{Server: srvs})
|
||||
bin := bytes.Buffer{}
|
||||
err = templ.Execute(&bin, data)
|
||||
if err != nil {
|
||||
return ginext.Error(err)
|
||||
}
|
||||
|
||||
return ginext.Data(http.StatusOK, "text/html", bin.Bytes())
|
||||
}
|
||||
|
||||
// ServeScriptJS swaggerdoc
|
||||
//
|
||||
// @Summary (Website)
|
||||
//
|
||||
// @Router /scripts.script.js [GET]
|
||||
func (h WebHandler) ServeScriptJS(pctx ginext.PreContext) ginext.HTTPResponse {
|
||||
ctx, _, errResp := pctx.Start()
|
||||
if errResp != nil {
|
||||
return *errResp
|
||||
}
|
||||
defer ctx.Cancel()
|
||||
|
||||
templ, err := h.app.Assets.Template("scripts/script.js", h.buildScriptJSTemplate)
|
||||
if err != nil {
|
||||
return ginext.Error(err)
|
||||
}
|
||||
|
||||
data := map[string]any{}
|
||||
|
||||
bin := bytes.Buffer{}
|
||||
err = templ.Execute(&bin, data)
|
||||
if err != nil {
|
||||
return ginext.Error(err)
|
||||
}
|
||||
|
||||
return ginext.Data(http.StatusOK, "text/javascript", bin.Bytes())
|
||||
}
|
||||
|
||||
func (h WebHandler) buildIndexHTMLTemplate(content []byte) (webassets.ITemplate, error) {
|
||||
t := templhtml.New("index.html")
|
||||
|
||||
t.Funcs(h.templateFuncMap())
|
||||
|
||||
_, err := t.Parse(string(content))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (h WebHandler) ServeAssets(pctx ginext.PreContext) ginext.HTTPResponse {
|
||||
type uri struct {
|
||||
FP1 *string `uri:"fp1"`
|
||||
FP2 *string `uri:"fp2"`
|
||||
FP3 *string `uri:"fp3"`
|
||||
}
|
||||
|
||||
var u uri
|
||||
ctx, _, errResp := pctx.URI(&u).Start()
|
||||
if errResp != nil {
|
||||
return *errResp
|
||||
}
|
||||
defer ctx.Cancel()
|
||||
|
||||
assetpath := ""
|
||||
if u.FP1 == nil && u.FP2 == nil && u.FP3 == nil {
|
||||
assetpath = filepath.Join()
|
||||
} else if u.FP2 == nil && u.FP3 == nil {
|
||||
assetpath = filepath.Join(*u.FP1)
|
||||
} else if u.FP3 == nil {
|
||||
assetpath = filepath.Join(*u.FP1, *u.FP2)
|
||||
} else {
|
||||
assetpath = filepath.Join(*u.FP1, *u.FP2, *u.FP3)
|
||||
}
|
||||
|
||||
data, err := h.app.Assets.Read(assetpath)
|
||||
if err != nil {
|
||||
return ginext.JSON(http.StatusNotFound, gin.H{"error": "AssetNotFound", "assetpath": assetpath})
|
||||
}
|
||||
|
||||
mime := bunny.FilenameToMime(assetpath, "text/plain")
|
||||
|
||||
return ginext.Data(http.StatusOK, mime, data)
|
||||
}
|
||||
|
||||
func (h WebHandler) buildScriptJSTemplate(content []byte) (webassets.ITemplate, error) {
|
||||
t := templtext.New("scripts/script.js")
|
||||
|
||||
t.Funcs(h.templateFuncMap())
|
||||
|
||||
_, err := t.Parse(string(content))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (h WebHandler) templateFuncMap() map[string]any {
|
||||
return map[string]any{
|
||||
"listServers": func() []models.Server {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), bunny.Conf.VerifyConnTimeoutHTML+2*time.Second)
|
||||
defer cancel()
|
||||
v, err := h.app.ListServer(ctx, bunny.Conf.VerifyConnTimeoutHTML)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
},
|
||||
"safe_html": func(s string) templhtml.HTML { return templhtml.HTML(s) }, //nolint:gosec
|
||||
"safe_js": func(s string) templhtml.JS { return templhtml.JS(s) }, //nolint:gosec
|
||||
"json": func(obj any) string {
|
||||
v, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(v)
|
||||
},
|
||||
"json_indent": func(obj any) string {
|
||||
v, err := json.MarshalIndent(obj, "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(v)
|
||||
},
|
||||
"mkarr": func(ln int) []int { return make([]int, ln) },
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ type Router struct {
|
||||
app *logic.Application
|
||||
|
||||
commonHandler handler.CommonHandler
|
||||
apiHandler handler.APIHandler
|
||||
webHandler handler.WebHandler
|
||||
}
|
||||
|
||||
@ -21,6 +22,7 @@ func NewRouter(app *logic.Application) *Router {
|
||||
app: app,
|
||||
|
||||
commonHandler: handler.NewCommonHandler(app),
|
||||
apiHandler: handler.NewAPIHandler(app),
|
||||
webHandler: handler.NewWebHandler(app),
|
||||
}
|
||||
}
|
||||
@ -50,9 +52,18 @@ func (r *Router) Init(e *ginext.GinWrapper) {
|
||||
docs.GET("/swagger/*sub").Handle(swagger.Handle)
|
||||
}
|
||||
|
||||
// ================ Website ================
|
||||
|
||||
e.Routes().GET("/").Handle(r.webHandler.ServeIndexHTML)
|
||||
e.Routes().GET("/index.html").Handle(r.webHandler.ServeIndexHTML)
|
||||
e.Routes().GET("/scripts/script.js").Handle(r.webHandler.ServeScriptJS)
|
||||
e.Routes().GET("/:fp1").Handle(r.webHandler.ServeAssets)
|
||||
e.Routes().GET("/:fp1/:fp2").Handle(r.webHandler.ServeAssets)
|
||||
e.Routes().GET("/:fp1/:fp2/:fp3").Handle(r.webHandler.ServeAssets)
|
||||
|
||||
// ================ API ================
|
||||
|
||||
api.GET("/server").Handle(r.webHandler.ListServer)
|
||||
api.GET("/server").Handle(r.apiHandler.ListServer)
|
||||
|
||||
// ================ ================
|
||||
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
bunny "locbunny"
|
||||
"locbunny/api"
|
||||
"locbunny/logic"
|
||||
"locbunny/webassets"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -16,7 +17,9 @@ func main() {
|
||||
|
||||
log.Info().Msg(fmt.Sprintf("Starting with config-namespace <%s>", conf.Namespace))
|
||||
|
||||
app := logic.NewApp()
|
||||
assets := webassets.NewAssets()
|
||||
|
||||
app := logic.NewApp(assets)
|
||||
|
||||
ginengine := ginext.NewEngine(conf.Cors, conf.GinDebug, true, conf.RequestTimeout)
|
||||
|
||||
|
140
config.go
140
config.go
@ -14,92 +14,110 @@ const APILevel = 1
|
||||
var SelfProcessID int
|
||||
|
||||
type Config struct {
|
||||
Namespace string
|
||||
GinDebug bool `env:"GINDEBUG"`
|
||||
ReturnRawErrors bool `env:"RETURNERRORS"`
|
||||
Custom404 bool `env:"CUSTOM404"`
|
||||
LogLevel zerolog.Level `env:"LOGLEVEL"`
|
||||
ServerIP string `env:"IP"`
|
||||
ServerPort int `env:"PORT"`
|
||||
RequestTimeout time.Duration `env:"REQUEST_TIMEOUT"`
|
||||
Cors bool `env:"CORS"`
|
||||
VerifyConnTimeout time.Duration `env:"VERIFY_CONN_TIMEOUT"`
|
||||
Namespace string
|
||||
GinDebug bool `env:"GINDEBUG"`
|
||||
ReturnRawErrors bool `env:"RETURNERRORS"`
|
||||
Custom404 bool `env:"CUSTOM404"`
|
||||
LogLevel zerolog.Level `env:"LOGLEVEL"`
|
||||
ServerIP string `env:"IP"`
|
||||
ServerPort int `env:"PORT"`
|
||||
RequestTimeout time.Duration `env:"REQUEST_TIMEOUT"`
|
||||
Cors bool `env:"CORS"`
|
||||
VerifyConnTimeoutHTML time.Duration `env:"VERIFY_CONN_TIMEOUT_HTML"`
|
||||
VerifyConnTimeoutAPI time.Duration `env:"VERIFY_CONN_TIMEOUT_API"`
|
||||
LiveReload *string `env:"LIVERELOAD"`
|
||||
CacheDuration time.Duration `env:"CACHE_DURATION"`
|
||||
}
|
||||
|
||||
var Conf Config
|
||||
|
||||
var configLocHost = func() Config {
|
||||
return Config{
|
||||
Namespace: "local",
|
||||
GinDebug: true,
|
||||
ServerIP: "0.0.0.0",
|
||||
ServerPort: 80,
|
||||
Custom404: true,
|
||||
ReturnRawErrors: true,
|
||||
RequestTimeout: 16 * time.Second,
|
||||
LogLevel: zerolog.DebugLevel,
|
||||
Cors: true,
|
||||
VerifyConnTimeout: time.Second,
|
||||
Namespace: "local",
|
||||
GinDebug: true,
|
||||
ServerIP: "0.0.0.0",
|
||||
ServerPort: 80,
|
||||
Custom404: true,
|
||||
ReturnRawErrors: true,
|
||||
RequestTimeout: 16 * time.Second,
|
||||
LogLevel: zerolog.DebugLevel,
|
||||
Cors: true,
|
||||
VerifyConnTimeoutAPI: 2 * time.Second,
|
||||
VerifyConnTimeoutHTML: 500 * time.Millisecond,
|
||||
LiveReload: nil,
|
||||
CacheDuration: 8 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
var configLocDocker = func() Config {
|
||||
return Config{
|
||||
Namespace: "local-docker",
|
||||
GinDebug: true,
|
||||
ServerIP: "0.0.0.0",
|
||||
ServerPort: 80,
|
||||
Custom404: true,
|
||||
ReturnRawErrors: true,
|
||||
RequestTimeout: 16 * time.Second,
|
||||
LogLevel: zerolog.DebugLevel,
|
||||
Cors: true,
|
||||
VerifyConnTimeout: time.Second,
|
||||
Namespace: "local-docker",
|
||||
GinDebug: true,
|
||||
ServerIP: "0.0.0.0",
|
||||
ServerPort: 80,
|
||||
Custom404: true,
|
||||
ReturnRawErrors: true,
|
||||
RequestTimeout: 16 * time.Second,
|
||||
LogLevel: zerolog.DebugLevel,
|
||||
Cors: true,
|
||||
VerifyConnTimeoutAPI: 2 * time.Second,
|
||||
VerifyConnTimeoutHTML: 500 * time.Millisecond,
|
||||
LiveReload: nil,
|
||||
CacheDuration: 8 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
var configDev = func() Config {
|
||||
return Config{
|
||||
Namespace: "develop",
|
||||
GinDebug: true,
|
||||
ServerIP: "0.0.0.0",
|
||||
ServerPort: 80,
|
||||
Custom404: false,
|
||||
ReturnRawErrors: false,
|
||||
RequestTimeout: 16 * time.Second,
|
||||
LogLevel: zerolog.DebugLevel,
|
||||
Cors: false,
|
||||
VerifyConnTimeout: time.Second,
|
||||
Namespace: "develop",
|
||||
GinDebug: true,
|
||||
ServerIP: "0.0.0.0",
|
||||
ServerPort: 80,
|
||||
Custom404: false,
|
||||
ReturnRawErrors: false,
|
||||
RequestTimeout: 16 * time.Second,
|
||||
LogLevel: zerolog.DebugLevel,
|
||||
Cors: false,
|
||||
VerifyConnTimeoutAPI: 2 * time.Second,
|
||||
VerifyConnTimeoutHTML: 500 * time.Millisecond,
|
||||
LiveReload: nil,
|
||||
CacheDuration: 8 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
var configStag = func() Config {
|
||||
return Config{
|
||||
Namespace: "staging",
|
||||
GinDebug: true,
|
||||
ServerIP: "0.0.0.0",
|
||||
ServerPort: 80,
|
||||
Custom404: false,
|
||||
ReturnRawErrors: false,
|
||||
RequestTimeout: 16 * time.Second,
|
||||
LogLevel: zerolog.DebugLevel,
|
||||
Cors: false,
|
||||
VerifyConnTimeout: time.Second,
|
||||
Namespace: "staging",
|
||||
GinDebug: true,
|
||||
ServerIP: "0.0.0.0",
|
||||
ServerPort: 80,
|
||||
Custom404: false,
|
||||
ReturnRawErrors: false,
|
||||
RequestTimeout: 16 * time.Second,
|
||||
LogLevel: zerolog.DebugLevel,
|
||||
Cors: false,
|
||||
VerifyConnTimeoutAPI: 2 * time.Second,
|
||||
VerifyConnTimeoutHTML: 500 * time.Millisecond,
|
||||
LiveReload: nil,
|
||||
CacheDuration: 8 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
var configProd = func() Config {
|
||||
return Config{
|
||||
Namespace: "production",
|
||||
GinDebug: false,
|
||||
ServerIP: "0.0.0.0",
|
||||
ServerPort: 80,
|
||||
Custom404: false,
|
||||
ReturnRawErrors: false,
|
||||
RequestTimeout: 16 * time.Second,
|
||||
LogLevel: zerolog.InfoLevel,
|
||||
Cors: false,
|
||||
VerifyConnTimeout: time.Second,
|
||||
Namespace: "production",
|
||||
GinDebug: false,
|
||||
ServerIP: "0.0.0.0",
|
||||
ServerPort: 80,
|
||||
Custom404: false,
|
||||
ReturnRawErrors: false,
|
||||
RequestTimeout: 16 * time.Second,
|
||||
LogLevel: zerolog.InfoLevel,
|
||||
Cors: false,
|
||||
VerifyConnTimeoutAPI: 2 * time.Second,
|
||||
VerifyConnTimeoutHTML: 500 * time.Millisecond,
|
||||
LiveReload: nil,
|
||||
CacheDuration: 8 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,7 +137,7 @@ func InstanceID() string {
|
||||
|
||||
func getConfig(ns string) (Config, bool) {
|
||||
if ns == "" {
|
||||
ns = "local-host"
|
||||
ns = "production"
|
||||
}
|
||||
if cfn, ok := allConfig[ns]; ok {
|
||||
c := cfn()
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"io"
|
||||
bunny "locbunny"
|
||||
"locbunny/models"
|
||||
"locbunny/webassets"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -30,12 +31,19 @@ type Application struct {
|
||||
Port string
|
||||
IsRunning *syncext.AtomicBool
|
||||
|
||||
Gin *ginext.GinWrapper
|
||||
Jobs []Job
|
||||
Gin *ginext.GinWrapper
|
||||
Assets *webassets.Assets
|
||||
Jobs []Job
|
||||
|
||||
cacheLock sync.Mutex
|
||||
serverCacheValue []models.Server
|
||||
serverCacheTime *time.Time
|
||||
}
|
||||
|
||||
func NewApp() *Application {
|
||||
func NewApp(ass *webassets.Assets) *Application {
|
||||
//nolint:exhaustruct
|
||||
return &Application{
|
||||
Assets: ass,
|
||||
stopChan: make(chan bool),
|
||||
IsRunning: syncext.NewAtomicBool(false),
|
||||
}
|
||||
@ -110,18 +118,35 @@ func (app *Application) Run() {
|
||||
app.IsRunning.Set(false)
|
||||
}
|
||||
|
||||
func (app *Application) ListServer(ctx *ginext.AppContext) ([]models.Server, error) {
|
||||
func (app *Application) ListServer(ctx context.Context, timeout time.Duration) ([]models.Server, error) {
|
||||
|
||||
app.cacheLock.Lock()
|
||||
if app.serverCacheTime != nil && app.serverCacheTime.After(time.Now().Add(-bunny.Conf.CacheDuration)) {
|
||||
v := langext.ArrCopy(app.serverCacheValue)
|
||||
log.Debug().Msg(fmt.Sprintf("Return cache values (from %s)", app.serverCacheTime.Format(time.RFC3339Nano)))
|
||||
app.cacheLock.Unlock()
|
||||
return v, nil
|
||||
}
|
||||
app.cacheLock.Unlock()
|
||||
|
||||
socks4, err := netstat.TCPSocks(netstat.NoopFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
socks6, err := netstat.TCP6Socks(netstat.NoopFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sockCount := len(socks4) + len(socks6)
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
@ -135,7 +160,7 @@ func (app *Application) ListServer(ctx *ginext.AppContext) ([]models.Server, err
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
con1, err := app.verifyHTTPConn(socks4[i], "HTTP", "v4")
|
||||
con1, err := app.verifyHTTPConn(socks4[i], "HTTP", "v4", timeout)
|
||||
if err == nil {
|
||||
rchan <- con1
|
||||
return
|
||||
@ -148,7 +173,7 @@ func (app *Application) ListServer(ctx *ginext.AppContext) ([]models.Server, err
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
con2, err := app.verifyHTTPConn(socks4[i], "HTTPS", "v4")
|
||||
con2, err := app.verifyHTTPConn(socks4[i], "HTTPS", "v4", timeout)
|
||||
if err == nil {
|
||||
rchan <- con2
|
||||
return
|
||||
@ -164,7 +189,7 @@ func (app *Application) ListServer(ctx *ginext.AppContext) ([]models.Server, err
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
con1, err := app.verifyHTTPConn(socks6[i], "HTTP", "v6")
|
||||
con1, err := app.verifyHTTPConn(socks6[i], "HTTP", "v6", timeout)
|
||||
if err == nil {
|
||||
rchan <- con1
|
||||
return
|
||||
@ -177,7 +202,7 @@ func (app *Application) ListServer(ctx *ginext.AppContext) ([]models.Server, err
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
con2, err := app.verifyHTTPConn(socks6[i], "HTTPS", "v6")
|
||||
con2, err := app.verifyHTTPConn(socks6[i], "HTTPS", "v6", timeout)
|
||||
if err == nil {
|
||||
rchan <- con2
|
||||
return
|
||||
@ -191,6 +216,10 @@ func (app *Application) ListServer(ctx *ginext.AppContext) ([]models.Server, err
|
||||
close(echan)
|
||||
close(rchan)
|
||||
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
duplicates := make(map[int]bool, sockCount*3)
|
||||
res := make([]models.Server, 0, sockCount*3)
|
||||
for v := range rchan {
|
||||
@ -201,35 +230,44 @@ func (app *Application) ListServer(ctx *ginext.AppContext) ([]models.Server, err
|
||||
}
|
||||
}
|
||||
|
||||
langext.SortBy(res, func(v models.Server) int { return v.Port })
|
||||
|
||||
app.cacheLock.Lock()
|
||||
app.serverCacheValue = langext.ArrCopy(res)
|
||||
app.serverCacheTime = langext.Ptr(time.Now())
|
||||
app.cacheLock.Unlock()
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (app *Application) verifyHTTPConn(sock netstat.SockTabEntry, proto string, ipversion string) (models.Server, error) {
|
||||
func (app *Application) verifyHTTPConn(sock netstat.SockTabEntry, proto string, ipversion string, timeout time.Duration) (models.Server, error) {
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), bunny.Conf.VerifyConnTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
if sock.State != netstat.Listen {
|
||||
log.Debug().Msg(fmt.Sprintf("Failed to verify socket [%s|%s] invalid state: %s", ipversion, strings.ToUpper(proto), sock.State.String()))
|
||||
port := int(sock.LocalAddr.Port)
|
||||
|
||||
if sock.State != netstat.Listen && sock.State != netstat.Established && sock.State != netstat.TimeWait {
|
||||
log.Debug().Msg(fmt.Sprintf("Failed to verify socket [%s|%s|%d] invalid state: %s", strings.ToUpper(proto), ipversion, port, sock.State.String()))
|
||||
return models.Server{}, errors.New("invalid sock-state")
|
||||
}
|
||||
|
||||
if int(sock.LocalAddr.Port) == bunny.Conf.ServerPort && sock.Process != nil && sock.Process.Pid == bunny.SelfProcessID {
|
||||
log.Debug().Msg(fmt.Sprintf("Skip socket [%s|%s] (this is our own server)", ipversion, strings.ToUpper(proto)))
|
||||
if port == bunny.Conf.ServerPort && sock.Process != nil && sock.Process.Pid == bunny.SelfProcessID {
|
||||
log.Debug().Msg(fmt.Sprintf("Skip socket [%s|%s|%d] (this is our own server)", strings.ToUpper(proto), ipversion, port))
|
||||
return models.Server{}, errors.New("skip self")
|
||||
}
|
||||
|
||||
c := http.Client{}
|
||||
url := fmt.Sprintf("%s://localhost:%d", strings.ToLower(proto), sock.LocalAddr.Port)
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
url := fmt.Sprintf("%s://localhost:%d", strings.ToLower(proto), port)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
log.Debug().Msg(fmt.Sprintf("Failed to create [%s|%s] request to %d", ipversion, strings.ToUpper(proto), sock.LocalAddr.Port))
|
||||
log.Debug().Msg(fmt.Sprintf("Failed to create [%s|%s|%d] request to %d (-> %s)", strings.ToUpper(proto), ipversion, port, port, err.Error()))
|
||||
return models.Server{}, err
|
||||
}
|
||||
|
||||
resp1, err := c.Do(req)
|
||||
if err != nil {
|
||||
log.Debug().Msg(fmt.Sprintf("Failed to send [%s|%s] request to %s", ipversion, strings.ToUpper(proto), url))
|
||||
log.Debug().Msg(fmt.Sprintf("Failed to send [%s|%s|%d] request to %s (-> %s)", strings.ToUpper(proto), ipversion, port, url, err.Error()))
|
||||
return models.Server{}, err
|
||||
}
|
||||
|
||||
@ -237,7 +275,7 @@ func (app *Application) verifyHTTPConn(sock netstat.SockTabEntry, proto string,
|
||||
|
||||
resbody, err := io.ReadAll(resp1.Body)
|
||||
if err != nil {
|
||||
log.Debug().Msg(fmt.Sprintf("Failed to read [%s|%s] response from %s", ipversion, strings.ToUpper(proto), url))
|
||||
log.Debug().Msg(fmt.Sprintf("Failed to read [%s|%s|%d] response from %s (-> %s)", strings.ToUpper(proto), ipversion, port, url, err.Error()))
|
||||
return models.Server{}, err
|
||||
}
|
||||
|
||||
@ -252,7 +290,7 @@ func (app *Application) verifyHTTPConn(sock netstat.SockTabEntry, proto string,
|
||||
}
|
||||
|
||||
return models.Server{
|
||||
Port: int(sock.LocalAddr.Port),
|
||||
Port: port,
|
||||
IP: sock.LocalAddr.IP.String(),
|
||||
Protocol: proto,
|
||||
StatusCode: resp1.StatusCode,
|
||||
@ -265,6 +303,6 @@ func (app *Application) verifyHTTPConn(sock netstat.SockTabEntry, proto string,
|
||||
}, nil
|
||||
}
|
||||
|
||||
log.Debug().Msg(fmt.Sprintf("Failed to categorize [%s|%s] response from %s (Content-Type: '%s')", ipversion, strings.ToUpper(proto), url, ct))
|
||||
log.Debug().Msg(fmt.Sprintf("Failed to categorize [%s|%s|%d] response from %s (Content-Type: '%s')", strings.ToUpper(proto), ipversion, port, url, ct))
|
||||
return models.Server{}, errors.New("invalid response-type")
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package swagger
|
||||
|
||||
import (
|
||||
"embed"
|
||||
_ "embed"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/ginext"
|
||||
"net/http"
|
||||
|
@ -8,6 +8,12 @@
|
||||
"host": "localhost",
|
||||
"basePath": "/api/v1/",
|
||||
"paths": {
|
||||
"/": {
|
||||
"get": {
|
||||
"summary": "(Website)",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/api/health": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@ -169,6 +175,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/index.html": {
|
||||
"get": {
|
||||
"summary": "(Website)",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/scripts.script.js": {
|
||||
"get": {
|
||||
"summary": "(Website)",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/server": {
|
||||
"get": {
|
||||
"summary": "List running server",
|
||||
|
@ -83,6 +83,10 @@ info:
|
||||
title: LocalHostBunny
|
||||
version: "1.0"
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
responses: {}
|
||||
summary: (Website)
|
||||
/api/health:
|
||||
get:
|
||||
responses:
|
||||
@ -187,6 +191,14 @@ paths:
|
||||
summary: Return 200 after x seconds
|
||||
tags:
|
||||
- Common
|
||||
/index.html:
|
||||
get:
|
||||
responses: {}
|
||||
summary: (Website)
|
||||
/scripts.script.js:
|
||||
get:
|
||||
responses: {}
|
||||
summary: (Website)
|
||||
/server:
|
||||
get:
|
||||
responses:
|
||||
|
39
utils.go
Normal file
39
utils.go
Normal file
@ -0,0 +1,39 @@
|
||||
package bunny
|
||||
|
||||
import "strings"
|
||||
|
||||
func FilenameToMime(fn string, fallback string) string {
|
||||
lowerFN := strings.ToLower(fn)
|
||||
if strings.HasSuffix(lowerFN, ".html") || strings.HasSuffix(lowerFN, ".htm") {
|
||||
return "text/html"
|
||||
}
|
||||
if strings.HasSuffix(lowerFN, ".css") {
|
||||
return "text/css"
|
||||
}
|
||||
if strings.HasSuffix(lowerFN, ".js") {
|
||||
return "text/javascript"
|
||||
}
|
||||
if strings.HasSuffix(lowerFN, ".json") {
|
||||
return "application/json"
|
||||
}
|
||||
if strings.HasSuffix(lowerFN, ".jpeg") || strings.HasSuffix(lowerFN, ".jpg") {
|
||||
return "image/jpeg"
|
||||
}
|
||||
if strings.HasSuffix(lowerFN, ".png") {
|
||||
return "image/png"
|
||||
}
|
||||
if strings.HasSuffix(lowerFN, ".svg") {
|
||||
return "image/svg+xml"
|
||||
}
|
||||
if strings.HasSuffix(lowerFN, ".gif") {
|
||||
return "image/gif"
|
||||
}
|
||||
if strings.HasSuffix(lowerFN, ".webp") {
|
||||
return "audio/webm"
|
||||
}
|
||||
if strings.HasSuffix(lowerFN, ".bmp") {
|
||||
return "image/bmp"
|
||||
}
|
||||
|
||||
return fallback
|
||||
}
|
20
webassets/css/fonts.css
Normal file
20
webassets/css/fonts.css
Normal file
@ -0,0 +1,20 @@
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceArgon'; font-weight: bold; font-style: normal; src: url('/fonts/MonaspaceArgon-Bold.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceArgon'; font-weight: bold; font-style: italic; src: url('/fonts/MonaspaceArgon-BoldItalic.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceArgon'; font-weight: normal; font-style: italic; src: url('/fonts/MonaspaceArgon-Italic.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceArgon'; font-weight: normal; font-style: normal; src: url('/fonts/MonaspaceArgon-Regular.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceKrypton'; font-weight: bold; font-style: normal; src: url('/fonts/MonaspaceKrypton-Bold.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceKrypton'; font-weight: bold; font-style: italic; src: url('/fonts/MonaspaceKrypton-BoldItalic.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceKrypton'; font-weight: normal; font-style: italic; src: url('/fonts/MonaspaceKrypton-Italic.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceKrypton'; font-weight: normal; font-style: normal; src: url('/fonts/MonaspaceKrypton-Regular.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceNeon'; font-weight: bold; font-style: normal; src: url('/fonts/MonaspaceNeon-Bold.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceNeon'; font-weight: bold; font-style: italic; src: url('/fonts/MonaspaceNeon-BoldItalic.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceNeon'; font-weight: normal; font-style: italic; src: url('/fonts/MonaspaceNeon-Italic.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceNeon'; font-weight: normal; font-style: normal; src: url('/fonts/MonaspaceNeon-Regular.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceRadon'; font-weight: bold; font-style: normal; src: url('/fonts/MonaspaceRadon-Bold.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceRadon'; font-weight: bold; font-style: italic; src: url('/fonts/MonaspaceRadon-BoldItalic.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceRadon'; font-weight: normal; font-style: italic; src: url('/fonts/MonaspaceRadon-Italic.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceRadon'; font-weight: normal; font-style: normal; src: url('/fonts/MonaspaceRadon-Regular.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceXenon'; font-weight: bold; font-style: normal; src: url('/fonts/MonaspaceXenon-Bold.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceXenon'; font-weight: bold; font-style: italic; src: url('/fonts/MonaspaceXenon-BoldItalic.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceXenon'; font-weight: normal; font-style: italic; src: url('/fonts/MonaspaceXenon-Italic.woff') format('woff2') }
|
||||
@font-face { font-display: swap; font-family: 'MonaspaceXenon'; font-weight: normal; font-style: normal; src: url('/fonts/MonaspaceXenon-Regular.woff') format('woff2') }
|
51
webassets/css/reset.css
Normal file
51
webassets/css/reset.css
Normal file
@ -0,0 +1,51 @@
|
||||
|
||||
|
||||
/* https://meyerweb.com/eric/tools/css/reset/ */
|
||||
|
||||
|
||||
/*
|
||||
1. Use a more-intuitive box-sizing model.
|
||||
*/
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/*
|
||||
2. Remove default margin
|
||||
*/
|
||||
* {
|
||||
margin: 0;
|
||||
}
|
||||
/*
|
||||
Typographic tweaks!
|
||||
3. Add accessible line-height
|
||||
4. Improve text rendering
|
||||
*/
|
||||
body {
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
/*
|
||||
5. Improve media defaults
|
||||
*/
|
||||
img, picture, video, canvas, svg {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
/*
|
||||
6. Remove built-in form typography styles
|
||||
*/
|
||||
input, button, textarea, select {
|
||||
font: inherit;
|
||||
}
|
||||
/*
|
||||
7. Avoid text overflows
|
||||
*/
|
||||
p, h1, h2, h3, h4, h5, h6 {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
/*
|
||||
8. Create a root stacking context
|
||||
*/
|
||||
#root, #__next {
|
||||
isolation: isolate;
|
||||
}
|
55
webassets/css/styles.css
Normal file
55
webassets/css/styles.css
Normal file
@ -0,0 +1,55 @@
|
||||
|
||||
* {
|
||||
font-family: 'MonaspaceXenon';
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#maincontent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.server {
|
||||
background-color: #CCC;
|
||||
text-align: center;
|
||||
padding: 2px 0.5rem;
|
||||
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
|
||||
border-radius: 6px;
|
||||
border: 1px solid #888;
|
||||
|
||||
box-shadow: 0 0 4px #888;
|
||||
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.server:hover {
|
||||
box-shadow: 0 0 4px #000;
|
||||
background-color: #AAA;
|
||||
color: #00F;
|
||||
}
|
||||
|
BIN
webassets/favicon.ico
Normal file
BIN
webassets/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
webassets/fonts/MonaspaceArgon-Bold.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-Bold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-BoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-BoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-ExtraBold.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-ExtraBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-ExtraBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-ExtraBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-ExtraLight.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-ExtraLight.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-ExtraLightItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-ExtraLightItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-Italic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-Italic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-Light.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-Light.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-LightItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-LightItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-Medium.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-Medium.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-MediumItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-MediumItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-Regular.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-Regular.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiBold.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiWideBold.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiWideBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiWideBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiWideBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiWideExtraBold.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiWideExtraBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiWideExtraBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiWideExtraBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiWideExtraLight.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiWideExtraLight.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiWideExtraLightItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiWideExtraLightItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiWideItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiWideItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiWideLight.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiWideLight.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiWideLightItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiWideLightItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiWideMedium.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiWideMedium.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiWideMediumItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiWideMediumItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiWideRegular.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiWideRegular.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiWideSemiBold.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiWideSemiBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-SemiWideSemiBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-SemiWideSemiBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-WideBold.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-WideBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-WideBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-WideBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-WideExtraBold.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-WideExtraBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-WideExtraBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-WideExtraBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-WideExtraLight.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-WideExtraLight.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-WideExtraLightItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-WideExtraLightItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-WideItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-WideItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-WideLight.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-WideLight.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-WideLightItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-WideLightItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-WideMedium.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-WideMedium.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-WideMediumItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-WideMediumItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-WideRegular.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-WideRegular.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-WideSemiBold.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-WideSemiBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgon-WideSemiBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceArgon-WideSemiBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgonVarVF[wght,wdth,slnt].woff
Normal file
BIN
webassets/fonts/MonaspaceArgonVarVF[wght,wdth,slnt].woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceArgonVarVF[wght,wdth,slnt].woff2
Normal file
BIN
webassets/fonts/MonaspaceArgonVarVF[wght,wdth,slnt].woff2
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-Bold.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-Bold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-BoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-BoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-ExtraBold.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-ExtraBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-ExtraBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-ExtraBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-ExtraLight.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-ExtraLight.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-ExtraLightItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-ExtraLightItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-Italic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-Italic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-Light.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-Light.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-LightItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-LightItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-Medium.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-Medium.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-MediumItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-MediumItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-Regular.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-Regular.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiBold.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiWideBold.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiWideBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiWideBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiWideBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiWideExtraBold.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiWideExtraBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiWideExtraBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiWideExtraBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiWideExtraLight.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiWideExtraLight.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiWideExtraLightItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiWideExtraLightItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiWideItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiWideItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiWideLight.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiWideLight.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiWideLightItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiWideLightItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiWideMedium.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiWideMedium.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiWideMediumItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiWideMediumItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiWideRegular.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiWideRegular.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiWideSemiBold.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiWideSemiBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-SemiWideSemiBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-SemiWideSemiBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-WideBold.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-WideBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-WideBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-WideBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-WideExtraBold.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-WideExtraBold.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-WideExtraBoldItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-WideExtraBoldItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-WideExtraLight.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-WideExtraLight.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-WideExtraLightItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-WideExtraLightItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-WideItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-WideItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-WideLight.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-WideLight.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-WideLightItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-WideLightItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-WideMedium.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-WideMedium.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-WideMediumItalic.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-WideMediumItalic.woff
Normal file
Binary file not shown.
BIN
webassets/fonts/MonaspaceKrypton-WideRegular.woff
Normal file
BIN
webassets/fonts/MonaspaceKrypton-WideRegular.woff
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user