Dedicate module to config parsing, include config as request context

This commit is contained in:
nemunaire 2020-04-19 08:49:30 +02:00
parent 762966908c
commit 1263b87d1f
13 changed files with 290 additions and 82 deletions

View File

@ -12,6 +12,7 @@ import (
"github.com/julienschmidt/httprouter"
"git.happydns.org/happydns/config"
"git.happydns.org/happydns/model"
"git.happydns.org/happydns/storage"
)
@ -59,7 +60,7 @@ func (r APIErrorResponse) WriteResponse(w http.ResponseWriter) {
http.Error(w, fmt.Sprintf("{\"errmsg\":%q}", r.err.Error()), r.status)
}
func apiHandler(f func(httprouter.Params, io.Reader) Response) func(http.ResponseWriter, *http.Request, httprouter.Params) {
func apiHandler(f func(*config.Options, httprouter.Params, io.Reader) Response) func(http.ResponseWriter, *http.Request, httprouter.Params) {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
r.RemoteAddr = addr
@ -72,11 +73,12 @@ func apiHandler(f func(httprouter.Params, io.Reader) Response) func(http.Respons
return
}
f(ps, r.Body).WriteResponse(w)
opts := r.Context().Value("opts").(*config.Options)
f(opts, ps, r.Body).WriteResponse(w)
}
}
func apiAuthHandler(f func(*happydns.User, httprouter.Params, io.Reader) Response) func(http.ResponseWriter, *http.Request, httprouter.Params) {
func apiAuthHandler(f func(*config.Options, *happydns.User, httprouter.Params, io.Reader) Response) func(http.ResponseWriter, *http.Request, httprouter.Params) {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
r.RemoteAddr = addr
@ -110,7 +112,8 @@ func apiAuthHandler(f func(*happydns.User, httprouter.Params, io.Reader) Respons
status: http.StatusUnauthorized,
}.WriteResponse(w)
} else {
f(std, ps, r.Body).WriteResponse(w)
opts := r.Context().Value("opts").(*config.Options)
f(opts, std, ps, r.Body).WriteResponse(w)
}
}
}

View File

@ -4,8 +4,6 @@ import (
"github.com/julienschmidt/httprouter"
)
var DefaultNameServer = "127.0.0.1:53"
var router = httprouter.New()
func Router() *httprouter.Router {

View File

@ -5,6 +5,7 @@ import (
"github.com/julienschmidt/httprouter"
"git.happydns.org/happydns/config"
"git.happydns.org/happydns/model"
)
@ -13,7 +14,7 @@ func init() {
//router.POST("/api/services", apiHandler(newService))
}
func listServices(_ httprouter.Params, _ io.Reader) Response {
func listServices(_ *config.Options, _ httprouter.Params, _ io.Reader) Response {
if services, err := happydns.GetServices(); err != nil {
return APIErrorResponse{
err: err,

View File

@ -8,6 +8,7 @@ import (
"github.com/julienschmidt/httprouter"
"git.happydns.org/happydns/config"
"git.happydns.org/happydns/model"
"git.happydns.org/happydns/storage"
)
@ -16,12 +17,12 @@ var AuthFunc = checkAuth
func init() {
router.GET("/api/users/auth", apiAuthHandler(validateAuthToken))
router.POST("/api/users/auth", apiHandler(func(ps httprouter.Params, b io.Reader) Response {
router.POST("/api/users/auth", apiHandler(func(_ *config.Options, ps httprouter.Params, b io.Reader) Response {
return AuthFunc(ps, b)
}))
}
func validateAuthToken(u *happydns.User, _ httprouter.Params, _ io.Reader) Response {
func validateAuthToken(_ *config.Options, u *happydns.User, _ httprouter.Params, _ io.Reader) Response {
return APIResponse{
response: u,
}

View File

@ -7,6 +7,7 @@ import (
"github.com/julienschmidt/httprouter"
"git.happydns.org/happydns/config"
"git.happydns.org/happydns/model"
"git.happydns.org/happydns/storage"
)
@ -33,7 +34,7 @@ type UploadedUser struct {
Password string
}
func registerUser(p httprouter.Params, body io.Reader) Response {
func registerUser(opts *config.Options, p httprouter.Params, body io.Reader) Response {
var uu UploadedUser
err := json.NewDecoder(body).Decode(&uu)
if err != nil {

View File

@ -4,13 +4,15 @@ import (
"io"
"github.com/julienschmidt/httprouter"
"git.happydns.org/happydns/config"
)
func init() {
router.GET("/api/version", apiHandler(showVersion))
}
func showVersion(_ httprouter.Params, _ io.Reader) Response {
func showVersion(_ *config.Options, _ httprouter.Params, _ io.Reader) Response {
return APIResponse{
response: map[string]interface{}{"version": 0.1},
}

View File

@ -13,6 +13,7 @@ import (
"github.com/julienschmidt/httprouter"
"github.com/miekg/dns"
"git.happydns.org/happydns/config"
"git.happydns.org/happydns/model"
"git.happydns.org/happydns/storage"
)
@ -27,7 +28,7 @@ func init() {
router.DELETE("/api/zones/:zone/rr", apiAuthHandler(zoneHandler(delRR)))
}
func getZones(u *happydns.User, p httprouter.Params, body io.Reader) Response {
func getZones(_ *config.Options, u *happydns.User, p httprouter.Params, body io.Reader) Response {
if zones, err := storage.MainStore.GetZones(u); err != nil {
return APIErrorResponse{
err: err,
@ -39,7 +40,7 @@ func getZones(u *happydns.User, p httprouter.Params, body io.Reader) Response {
}
}
func addZone(u *happydns.User, p httprouter.Params, body io.Reader) Response {
func addZone(_ *config.Options, u *happydns.User, p httprouter.Params, body io.Reader) Response {
var uz happydns.Zone
err := json.NewDecoder(body).Decode(&uz)
if err != nil {
@ -77,7 +78,7 @@ func addZone(u *happydns.User, p httprouter.Params, body io.Reader) Response {
}
}
func delZone(zone *happydns.Zone, body io.Reader) Response {
func delZone(_ *config.Options, zone *happydns.Zone, body io.Reader) Response {
if err := storage.MainStore.DeleteZone(zone); err != nil {
return APIErrorResponse{
err: err,
@ -89,28 +90,28 @@ func delZone(zone *happydns.Zone, body io.Reader) Response {
}
}
func zoneHandler(f func(*happydns.Zone, io.Reader) Response) func(*happydns.User, httprouter.Params, io.Reader) Response {
return func(u *happydns.User, ps httprouter.Params, body io.Reader) Response {
func zoneHandler(f func(*config.Options, *happydns.Zone, io.Reader) Response) func(*config.Options, *happydns.User, httprouter.Params, io.Reader) Response {
return func(opts *config.Options, u *happydns.User, ps httprouter.Params, body io.Reader) Response {
if zone, err := storage.MainStore.GetZoneByDN(u, ps.ByName("zone")); err != nil {
return APIErrorResponse{
status: http.StatusNotFound,
err: errors.New("Domain not found"),
}
} else {
return f(zone, body)
return f(opts, zone, body)
}
}
}
func getZone(zone *happydns.Zone, body io.Reader) Response {
func getZone(_ *config.Options, zone *happydns.Zone, body io.Reader) Response {
return APIResponse{
response: zone,
}
}
func normalizeNSServer(srv string) string {
func normalizeNSServer(opts *config.Options, srv string) string {
if srv == "" {
return DefaultNameServer
return opts.DefaultNameServer
} else if strings.Index(srv, ":") > -1 {
return srv
} else {
@ -118,9 +119,9 @@ func normalizeNSServer(srv string) string {
}
}
func axfrZone(zone *happydns.Zone, body io.Reader) Response {
func axfrZone(opts *config.Options, zone *happydns.Zone, body io.Reader) Response {
d := net.Dialer{}
con, err := d.Dial("tcp", normalizeNSServer(zone.Server))
con, err := d.Dial("tcp", normalizeNSServer(opts, zone.Server))
if err != nil {
return APIErrorResponse{
status: http.StatusInternalServerError,
@ -136,7 +137,7 @@ func axfrZone(zone *happydns.Zone, body io.Reader) Response {
dnscon := &dns.Conn{Conn: con}
transfer := &dns.Transfer{Conn: dnscon, TsigSecret: map[string]string{zone.KeyName: zone.Base64KeyBlob()}}
c, err := transfer.In(m, normalizeNSServer(zone.Server))
c, err := transfer.In(m, normalizeNSServer(opts, zone.Server))
if err != nil {
return APIErrorResponse{
@ -173,7 +174,7 @@ type uploadedRR struct {
RR string `json:"string"`
}
func addRR(zone *happydns.Zone, body io.Reader) Response {
func addRR(opts *config.Options, zone *happydns.Zone, body io.Reader) Response {
var urr uploadedRR
err := json.NewDecoder(body).Decode(&urr)
if err != nil {
@ -201,7 +202,7 @@ func addRR(zone *happydns.Zone, body io.Reader) Response {
c.TsigSecret = map[string]string{zone.KeyName: zone.Base64KeyBlob()}
m.SetTsig(zone.KeyName, zone.KeyAlgo, 300, time.Now().Unix())
in, rtt, err := c.Exchange(m, normalizeNSServer(zone.Server))
in, rtt, err := c.Exchange(m, normalizeNSServer(opts, zone.Server))
if err != nil {
return APIErrorResponse{
status: http.StatusInternalServerError,
@ -218,7 +219,7 @@ func addRR(zone *happydns.Zone, body io.Reader) Response {
}
}
func delRR(zone *happydns.Zone, body io.Reader) Response {
func delRR(opts *config.Options, zone *happydns.Zone, body io.Reader) Response {
var urr uploadedRR
err := json.NewDecoder(body).Decode(&urr)
if err != nil {
@ -246,7 +247,7 @@ func delRR(zone *happydns.Zone, body io.Reader) Response {
c.TsigSecret = map[string]string{zone.KeyName: zone.Base64KeyBlob()}
m.SetTsig(zone.KeyName, zone.KeyAlgo, 300, time.Now().Unix())
in, rtt, err := c.Exchange(m, normalizeNSServer(zone.Server))
in, rtt, err := c.Exchange(m, normalizeNSServer(opts, zone.Server))
if err != nil {
return APIErrorResponse{
status: http.StatusInternalServerError,

23
config/cli.go Normal file
View File

@ -0,0 +1,23 @@
package config // import "happydns.org/config"
import (
"flag"
)
func (o *Options) parseCLI() error {
flag.StringVar(&o.DevProxy, "dev", o.DevProxy, "Proxify traffic to this host for static assets")
flag.StringVar(&o.Bind, "bind", ":8081", "Bind port/socket")
flag.StringVar(&o.DSN, "dsn", o.DSN, "DSN to connect to the MySQL server")
flag.StringVar(&o.BaseURL, "baseurl", o.BaseURL, "URL prepended to each URL")
flag.StringVar(&o.DefaultNameServer, "defaultns", o.DefaultNameServer, "Adress to the default name server")
flag.Parse()
for _, conf := range flag.Args() {
err := o.parseFile(conf)
if err != nil {
return err
}
}
return nil
}

109
config/config.go Normal file
View File

@ -0,0 +1,109 @@
package config // import "happydns.org/config"
import (
"fmt"
"log"
"os"
"path"
"strings"
"git.happydns.org/happydns/storage/mysql"
)
type Options struct {
Bind string
BaseURL string
DevProxy string
DSN string
DefaultNameServer string
}
func ConsolidateConfig() (opts *Options, err error) {
// Define defaults options
opts = &Options{
Bind: ":8081",
BaseURL: "/",
DSN: database.DSNGenerator(),
DefaultNameServer: "127.0.0.1:53",
}
// Establish a list of possible configuration file locations
configLocations := []string{
"happydns.conf",
}
if home, err := os.UserConfigDir(); err == nil {
configLocations = append(configLocations, path.Join(home, "happydns", "happydns.conf"))
}
configLocations = append(configLocations, path.Join("etc", "happydns.conf"))
// If config file exists, read configuration from it
for _, filename := range configLocations {
if _, e := os.Stat(filename); !os.IsNotExist(e) {
log.Printf("Loading configuration from %s\n", filename)
err = opts.parseFile(filename)
if err != nil {
return
}
break
}
}
// Then, overwrite that by what is present in the environment
err = opts.parseEnvironmentVariables()
if err != nil {
return
}
// Finaly, command line takes precedence
err = opts.parseCLI()
if err != nil {
return
}
// Sanitize options
if opts.BaseURL != "/" {
opts.BaseURL = path.Clean(opts.BaseURL)
} else {
opts.BaseURL = ""
}
return
}
func (o *Options) parseLine(line string) (err error) {
fields := strings.SplitN(line, "=", 2)
key := strings.TrimSpace(fields[0])
value := strings.TrimSpace(fields[1])
key = strings.TrimPrefix(key, "HAPPYDNS_")
key = strings.Replace(key, "_", "", -1)
key = strings.ToUpper(key)
switch key {
case "DEVPROXY":
err = parseString(&o.DevProxy, value)
}
return
}
func parseString(store *string, value string) error {
*store = value
return nil
}
func parseBool(store *bool, value string) error {
value = strings.ToLower(value)
if value == "1" || value == "yes" || value == "true" || value == "on" {
*store = true
} else if value == "" || value == "0" || value == "no" || value == "false" || value == "off" {
*store = false
} else {
return fmt.Errorf("%s is not a valid bool value", value)
}
return nil
}

19
config/env.go Normal file
View File

@ -0,0 +1,19 @@
package config // import "happydns.org/config"
import (
"fmt"
"os"
"strings"
)
func (o *Options) parseEnvironmentVariables() (err error) {
for _, line := range os.Environ() {
if strings.HasPrefix(line, "HAPPYDNS_") {
err := o.parseLine(line)
if err != nil {
return fmt.Errorf("error in environment (%q): %w", line, err)
}
}
}
return
}

31
config/file.go Normal file
View File

@ -0,0 +1,31 @@
package config // import "happydns.org/config"
import (
"bufio"
"fmt"
"os"
"strings"
)
func (o *Options) parseFile(filename string) error {
fp, err := os.Open(filename)
if err != nil {
return err
}
defer fp.Close()
scanner := bufio.NewScanner(fp)
n := 0
for scanner.Scan() {
n += 1
line := strings.TrimSpace(scanner.Text())
if len(line) > 0 && !strings.HasPrefix(line, "#") && strings.Index(line, "=") > 0 {
err := o.parseLine(line)
if err != nil {
return fmt.Errorf("%v:%d: error in configuration: %w", filename, n, err)
}
}
}
return nil
}

49
main.go
View File

@ -1,14 +1,14 @@
package main
import (
"flag"
"context"
"log"
"net/http"
"net/url"
"path"
"strings"
"git.happydns.org/happydns/api"
"git.happydns.org/happydns/config"
"git.happydns.org/happydns/storage"
"git.happydns.org/happydns/storage/mysql"
)
@ -33,20 +33,22 @@ func (r ResponseWriterPrefix) Write(z []byte) (int, error) {
return r.real.Write(z)
}
func StripPrefix(prefix string, h http.Handler) http.Handler {
if prefix == "" {
return h
}
func StripPrefix(opts *config.Options, h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if prefix != "/" && r.URL.Path == "/" {
http.Redirect(w, r, prefix+"/", http.StatusFound)
} else if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) {
// Add in the context's request options
ctx := r.Context()
ctx = context.WithValue(ctx, "opts", opts)
r = r.WithContext(ctx)
if opts.BaseURL != "" && r.URL.Path == "/" {
http.Redirect(w, r, opts.BaseURL+"/", http.StatusFound)
} else if p := strings.TrimPrefix(r.URL.Path, opts.BaseURL); len(p) < len(r.URL.Path) {
r2 := new(http.Request)
*r2 = *r
r2.URL = new(url.URL)
*r2.URL = *r.URL
r2.URL.Path = p
h.ServeHTTP(ResponseWriterPrefix{w, prefix}, r2)
h.ServeHTTP(ResponseWriterPrefix{w, opts.BaseURL}, r2)
} else {
h.ServeHTTP(w, r)
}
@ -54,26 +56,17 @@ func StripPrefix(prefix string, h http.Handler) http.Handler {
}
func main() {
// Read parameters from command line
flag.StringVar(&DevProxy, "dev", DevProxy, "Proxify traffic to this host for static assets")
var bind = flag.String("bind", ":8081", "Bind port/socket")
var dsn = flag.String("dsn", database.DSNGenerator(), "DSN to connect to the MySQL server")
var baseURL = flag.String("baseurl", "/", "URL prepended to each URL")
flag.StringVar(&api.DefaultNameServer, "defaultns", api.DefaultNameServer, "Adress to the default name server")
flag.Parse()
var err error
// Sanitize options
if *baseURL != "/" {
tmp := path.Clean(*baseURL)
baseURL = &tmp
} else {
tmp := ""
baseURL = &tmp
// Load and parse options
var opts *config.Options
if opts, err = config.ConsolidateConfig(); err != nil {
log.Fatal(err)
}
// Initialize contents
log.Println("Opening database...")
if store, err := database.NewMySQLStorage(*dsn); err != nil {
if store, err := database.NewMySQLStorage(opts.DSN); err != nil {
log.Fatal("Cannot open the database: ", err)
} else {
storage.MainStore = store
@ -81,13 +74,13 @@ func main() {
defer storage.MainStore.Close()
log.Println("Do database migrations...")
if err := storage.MainStore.DoMigration(); err != nil {
if err = storage.MainStore.DoMigration(); err != nil {
log.Fatal("Cannot migrate database: ", err)
}
// Serve content
log.Println("Ready, listening on", *bind)
if err := http.ListenAndServe(*bind, StripPrefix(*baseURL, api.Router())); err != nil {
log.Println("Ready, listening on", opts.Bind)
if err = http.ListenAndServe(opts.Bind, StripPrefix(opts, api.Router())); err != nil {
log.Fatal("Unable to listen and serve: ", err)
}
}

View File

@ -8,6 +8,7 @@ import (
"path"
"git.happydns.org/happydns/api"
"git.happydns.org/happydns/config"
"github.com/julienschmidt/httprouter"
)
@ -17,22 +18,25 @@ import (
//go:generate go fmt bindata.go
const StaticDir string = "htdocs/dist"
var DevProxy string
func init() {
api.Router().GET("/", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if DevProxy == "" {
opts := r.Context().Value("opts").(*config.Options)
if opts.DevProxy == "" {
if data, err := Asset("htdocs/dist/index.html"); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
w.Write(data)
}
} else {
fwd_request(w, r, DevProxy)
fwd_request(w, r, opts.DevProxy)
}
})
api.Router().GET("/join", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if DevProxy == "" {
opts := r.Context().Value("opts").(*config.Options)
if opts.DevProxy == "" {
if data, err := Asset("htdocs/dist/index.html"); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
@ -40,11 +44,13 @@ func init() {
}
} else {
r.URL.Path = "/"
fwd_request(w, r, DevProxy)
fwd_request(w, r, opts.DevProxy)
}
})
api.Router().GET("/login", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if DevProxy == "" {
opts := r.Context().Value("opts").(*config.Options)
if opts.DevProxy == "" {
if data, err := Asset("htdocs/dist/index.html"); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
@ -52,11 +58,13 @@ func init() {
}
} else {
r.URL.Path = "/"
fwd_request(w, r, DevProxy)
fwd_request(w, r, opts.DevProxy)
}
})
api.Router().GET("/services/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if DevProxy == "" {
opts := r.Context().Value("opts").(*config.Options)
if opts.DevProxy == "" {
if data, err := Asset("htdocs/dist/index.html"); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
@ -64,11 +72,13 @@ func init() {
}
} else {
r.URL.Path = "/"
fwd_request(w, r, DevProxy)
fwd_request(w, r, opts.DevProxy)
}
})
api.Router().GET("/zones/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if DevProxy == "" {
opts := r.Context().Value("opts").(*config.Options)
if opts.DevProxy == "" {
if data, err := Asset("htdocs/dist/index.html"); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
@ -76,12 +86,14 @@ func init() {
}
} else {
r.URL.Path = "/"
fwd_request(w, r, DevProxy)
fwd_request(w, r, opts.DevProxy)
}
})
api.Router().GET("/css/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if DevProxy == "" {
opts := r.Context().Value("opts").(*config.Options)
if opts.DevProxy == "" {
if data, err := Asset(path.Join(StaticDir, r.URL.Path)); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
@ -89,33 +101,39 @@ func init() {
w.Write(data)
}
} else {
fwd_request(w, r, DevProxy)
fwd_request(w, r, opts.DevProxy)
}
})
api.Router().GET("/fonts/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if DevProxy == "" {
opts := r.Context().Value("opts").(*config.Options)
if opts.DevProxy == "" {
if data, err := Asset(path.Join(StaticDir, r.URL.Path)); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
w.Write(data)
}
} else {
fwd_request(w, r, DevProxy)
fwd_request(w, r, opts.DevProxy)
}
})
api.Router().GET("/img/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if DevProxy == "" {
opts := r.Context().Value("opts").(*config.Options)
if opts.DevProxy == "" {
if data, err := Asset(path.Join(StaticDir, r.URL.Path)); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
w.Write(data)
}
} else {
fwd_request(w, r, DevProxy)
fwd_request(w, r, opts.DevProxy)
}
})
api.Router().GET("/js/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if DevProxy == "" {
opts := r.Context().Value("opts").(*config.Options)
if opts.DevProxy == "" {
if data, err := Asset(path.Join(StaticDir, r.URL.Path)); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
@ -123,23 +141,27 @@ func init() {
w.Write(data)
}
} else {
fwd_request(w, r, DevProxy)
fwd_request(w, r, opts.DevProxy)
}
})
api.Router().GET("/favicon.ico", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if DevProxy == "" {
opts := r.Context().Value("opts").(*config.Options)
if opts.DevProxy == "" {
if data, err := Asset(path.Join(StaticDir, r.URL.Path)); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
w.Write(data)
}
} else {
fwd_request(w, r, DevProxy)
fwd_request(w, r, opts.DevProxy)
}
})
api.Router().GET("/manifest.json", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if DevProxy == "" {
opts := r.Context().Value("opts").(*config.Options)
if opts.DevProxy == "" {
if data, err := Asset(path.Join(StaticDir, r.URL.Path)); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
@ -147,22 +169,26 @@ func init() {
w.Write(data)
}
} else {
fwd_request(w, r, DevProxy)
fwd_request(w, r, opts.DevProxy)
}
})
api.Router().GET("/robots.txt", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if DevProxy == "" {
opts := r.Context().Value("opts").(*config.Options)
if opts.DevProxy == "" {
if data, err := Asset(path.Join(StaticDir, r.URL.Path)); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
w.Write(data)
}
} else {
fwd_request(w, r, DevProxy)
fwd_request(w, r, opts.DevProxy)
}
})
api.Router().GET("/service-worker.js", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if DevProxy == "" {
opts := r.Context().Value("opts").(*config.Options)
if opts.DevProxy == "" {
if data, err := Asset(path.Join(StaticDir, r.URL.Path)); err != nil {
fmt.Fprintf(w, "{\"errmsg\":%q}", err)
} else {
@ -170,7 +196,7 @@ func init() {
w.Write(data)
}
} else {
fwd_request(w, r, DevProxy)
fwd_request(w, r, opts.DevProxy)
}
})
}