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 }