github.com/wader/devd@v0.0.0-20221031103345-441c7e455249/cmd/devd/devd.go (about)

     1  package main
     2  
     3  import (
     4  	"net/http"
     5  	"os"
     6  	"path"
     7  
     8  	"github.com/cortesi/termlog"
     9  	"github.com/mitchellh/go-homedir"
    10  	"github.com/toqueteos/webbrowser"
    11  	"github.com/wader/devd"
    12  	"gopkg.in/alecthomas/kingpin.v2"
    13  )
    14  
    15  func main() {
    16  	address := kingpin.Flag("address", "Address to listen on").
    17  		Short('A').
    18  		Default("127.0.0.1").
    19  		String()
    20  
    21  	allInterfaces := kingpin.Flag("all", "Listen on all addresses").
    22  		Short('a').
    23  		Bool()
    24  
    25  	certFile := kingpin.Flag("cert", "Certificate bundle file - enables TLS").
    26  		Short('c').
    27  		PlaceHolder("PATH").
    28  		ExistingFile()
    29  
    30  	forceColor := kingpin.Flag("color", "Enable colour output, even if devd is not connected to a terminal").
    31  		Short('C').
    32  		Bool()
    33  
    34  	downKbps := kingpin.Flag(
    35  		"down",
    36  		"Throttle downstream from the client to N kilobytes per second",
    37  	).
    38  		PlaceHolder("N").
    39  		Short('d').
    40  		Default("0").
    41  		Uint()
    42  
    43  	notfound := kingpin.Flag("notfound", "Default when a static file is not found").
    44  		PlaceHolder("PATH").
    45  		Short('f').
    46  		Strings()
    47  
    48  	logHeaders := kingpin.Flag("logheaders", "Log headers").
    49  		Short('H').
    50  		Default("false").
    51  		Bool()
    52  
    53  	ignoreLogs := kingpin.Flag(
    54  		"ignore",
    55  		"Disable logging matching requests. Regexes are matched over 'host/path'",
    56  	).
    57  		Short('I').
    58  		PlaceHolder("REGEX").
    59  		Strings()
    60  
    61  	livereloadNaked := kingpin.Flag("livereload", "Enable livereload").
    62  		Short('L').
    63  		Default("false").
    64  		Bool()
    65  
    66  	livereloadRoutes := kingpin.Flag("livewatch", "Enable livereload and watch for static file changes").
    67  		Short('l').
    68  		Default("false").
    69  		Bool()
    70  
    71  	moddMode := kingpin.Flag("modd", "Modd is our parent - synonym for -LCt").
    72  		Short('m').
    73  		Bool()
    74  
    75  	latency := kingpin.Flag("latency", "Add N milliseconds of round-trip latency").
    76  		PlaceHolder("N").
    77  		Short('n').
    78  		Default("0").
    79  		Int()
    80  
    81  	openBrowser := kingpin.Flag("open", "Open browser window on startup").
    82  		Short('o').
    83  		Default("false").
    84  		Bool()
    85  
    86  	port := kingpin.Flag(
    87  		"port",
    88  		"Port to listen on - if not specified, devd will auto-pick a sensible port",
    89  	).
    90  		Short('p').
    91  		Int()
    92  
    93  	credspec := kingpin.Flag(
    94  		"password",
    95  		"HTTP basic password protection",
    96  	).
    97  		PlaceHolder("USER:PASS").
    98  		Short('P').
    99  		String()
   100  
   101  	quiet := kingpin.Flag("quiet", "Silence all logs").
   102  		Short('q').
   103  		Default("false").
   104  		Bool()
   105  
   106  	tls := kingpin.Flag("tls", "Serve TLS with auto-generated self-signed certificate (~/.devd.cert)").
   107  		Short('s').
   108  		Default("false").
   109  		Bool()
   110  
   111  	noTimestamps := kingpin.Flag("notimestamps", "Disable timestamps in output").
   112  		Short('t').
   113  		Default("false").
   114  		Bool()
   115  
   116  	logTime := kingpin.Flag("logtime", "Log timing").
   117  		Short('T').
   118  		Default("false").
   119  		Bool()
   120  
   121  	upKbps := kingpin.Flag(
   122  		"up",
   123  		"Throttle upstream from the client to N kilobytes per second",
   124  	).
   125  		PlaceHolder("N").
   126  		Short('u').
   127  		Default("0").
   128  		Uint()
   129  
   130  	watch := kingpin.Flag("watch", "Watch path to trigger livereload").
   131  		PlaceHolder("PATH").
   132  		Short('w').
   133  		Strings()
   134  
   135  	cors := kingpin.Flag("crossdomain", "Set the CORS headers to allow everything (origin, credentials, headers, methods)").
   136  		Short('X').
   137  		Default("false").
   138  		Bool()
   139  
   140  	excludes := kingpin.Flag("exclude", "Glob pattern for files to exclude from livereload").
   141  		PlaceHolder("PATTERN").
   142  		Short('x').
   143  		Strings()
   144  
   145  	debug := kingpin.Flag("debug", "Debugging for devd development").
   146  		Default("false").
   147  		Bool()
   148  
   149  	routes := kingpin.Arg(
   150  		"route",
   151  		`Routes have the following forms:
   152  			[SUBDOMAIN]/<PATH>=<DIR>
   153  			[SUBDOMAIN]/<PATH>=<URL>
   154  			<DIR>
   155  			<URL>
   156  		`,
   157  	).Required().Strings()
   158  
   159  	kingpin.CommandLine.HelpFlag.Short('h')
   160  	kingpin.Version(devd.Version)
   161  
   162  	kingpin.Parse()
   163  
   164  	if *moddMode {
   165  		*forceColor = true
   166  		*noTimestamps = true
   167  		*livereloadNaked = true
   168  	}
   169  
   170  	realAddr := *address
   171  	if *allInterfaces {
   172  		realAddr = "0.0.0.0"
   173  	}
   174  
   175  	var creds *devd.Credentials
   176  	if *credspec != "" {
   177  		var err error
   178  		creds, err = devd.CredentialsFromSpec(*credspec)
   179  		if err != nil {
   180  			kingpin.Fatalf("%s", err)
   181  			return
   182  		}
   183  	}
   184  
   185  	hdrs := make(http.Header)
   186  	if *cors {
   187  		hdrs.Set("Access-Control-Allow-Credentials", "true")
   188  	}
   189  
   190  	var servingScheme string
   191  	if *tls {
   192  		servingScheme = "https"
   193  	} else {
   194  		servingScheme = "http"
   195  	}
   196  
   197  	dd := devd.Devd{
   198  		// Shaping
   199  		Latency:       *latency,
   200  		DownKbps:      *downKbps,
   201  		UpKbps:        *upKbps,
   202  		ServingScheme: servingScheme,
   203  
   204  		AddHeaders: &hdrs,
   205  
   206  		// Livereload
   207  		LivereloadRoutes: *livereloadRoutes,
   208  		Livereload:       *livereloadNaked,
   209  		WatchPaths:       *watch,
   210  		Excludes:         *excludes,
   211  
   212  		Cors: *cors,
   213  
   214  		Credentials: creds,
   215  	}
   216  
   217  	if err := dd.AddRoutes(*routes, *notfound); err != nil {
   218  		kingpin.Fatalf("%s", err)
   219  	}
   220  
   221  	if err := dd.AddIgnores(*ignoreLogs); err != nil {
   222  		kingpin.Fatalf("%s", err)
   223  	}
   224  
   225  	logger := termlog.NewLog()
   226  	if *quiet {
   227  		logger.Quiet()
   228  	}
   229  	if *debug {
   230  		logger.Enable("debug")
   231  	}
   232  	if *logTime {
   233  		logger.Enable("timer")
   234  	}
   235  	if *logHeaders {
   236  		logger.Enable("headers")
   237  	}
   238  	if *forceColor {
   239  		logger.Color(true)
   240  	}
   241  	if *noTimestamps {
   242  		logger.TimeFmt = ""
   243  	}
   244  
   245  	for _, i := range dd.Routes {
   246  		logger.Say("Route %s -> %s", i.MuxMatch(), i.Endpoint.String())
   247  	}
   248  
   249  	if *tls {
   250  		home, err := homedir.Dir()
   251  		if err != nil {
   252  			kingpin.Fatalf("Could not get user's homedir: %s", err)
   253  		}
   254  		dst := path.Join(home, ".devd.cert")
   255  		if _, err := os.Stat(dst); os.IsNotExist(err) {
   256  			err := devd.GenerateCert(dst)
   257  			if err != nil {
   258  				kingpin.Fatalf("Could not generate cert: %s", err)
   259  			}
   260  		}
   261  		*certFile = dst
   262  	}
   263  
   264  	err := dd.Serve(
   265  		realAddr,
   266  		*port,
   267  		*certFile,
   268  		logger,
   269  		func(url string) {
   270  			if *openBrowser {
   271  				err := webbrowser.Open(url)
   272  				if err != nil {
   273  					kingpin.Fatalf("Failed to open browser: %s", err)
   274  				}
   275  			}
   276  		},
   277  	)
   278  	if err != nil {
   279  		kingpin.Fatalf("%s", err)
   280  	}
   281  }