github.com/rpdict/ponzu@v0.10.1-0.20190226054626-477f29d6bf5e/cmd/ponzu/main.go (about) 1 // Package main is located in the cmd/ponzu directory and contains the code to build 2 // and operate the command line interface (CLI) to manage Ponzu systems. Here, 3 // you will find the code that is used to create new Ponzu projects, generate 4 // code for content types and other files, build Ponzu binaries and run servers. 5 package main 6 7 import ( 8 "errors" 9 "fmt" 10 "log" 11 "net/http" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "strings" 16 "time" 17 18 _ "github.com/rpdict/ponzu/content" 19 "github.com/rpdict/ponzu/system/admin" 20 "github.com/rpdict/ponzu/system/api" 21 "github.com/rpdict/ponzu/system/api/analytics" 22 "github.com/rpdict/ponzu/system/db" 23 "github.com/rpdict/ponzu/system/tls" 24 25 "github.com/spf13/cobra" 26 ) 27 28 var ( 29 bind string 30 httpsport int 31 port int 32 docsport int 33 https bool 34 devhttps bool 35 docs bool 36 cli bool 37 38 // for ponzu internal / core development 39 gocmd string 40 fork string 41 dev bool 42 43 year = fmt.Sprintf("%d", time.Now().Year()) 44 ) 45 46 var rootCmd = &cobra.Command{ 47 Use: "ponzu", 48 Long: `Ponzu is an open-source HTTP server framework and CMS, released under 49 the BSD-3-Clause license. 50 (c) 2016 - ` + year + ` Boss Sauce Creative, LLC`, 51 } 52 53 var runCmd = &cobra.Command{ 54 Use: "run [flags] <service(,service)>", 55 Short: "starts the 'ponzu' HTTP server for the JSON API and or Admin System.", 56 Long: `Starts the 'ponzu' HTTP server for the JSON API, Admin System, or both. 57 The segments, separated by a comma, describe which services to start, either 58 'admin' (Admin System / CMS backend) or 'api' (JSON API), and, optionally, 59 if the server should utilize TLS encryption - served over HTTPS, which is 60 automatically managed using Let's Encrypt (https://letsencrypt.org) 61 62 Defaults to 'run --port=8080 admin,api' (running Admin & API on port 8080, without TLS) 63 64 Note: 65 Admin and API cannot run on separate processes unless you use a copy of the 66 database, since the first process to open it receives a lock. If you intend 67 to run the Admin and API on separate processes, you must call them with the 68 'ponzu' command independently.`, 69 Example: `$ ponzu run 70 (or) 71 $ ponzu run --port=8080 --https admin,api 72 (or) 73 $ ponzu run admin 74 (or) 75 $ ponzu run --port=8888 api`, 76 RunE: func(cmd *cobra.Command, args []string) error { 77 var addTLS string 78 if https { 79 addTLS = "--https" 80 } else { 81 addTLS = "--https=false" 82 } 83 84 if devhttps { 85 addTLS = "--dev-https" 86 } 87 88 var addDocs string 89 if docs { 90 addDocs = "--docs" 91 } else { 92 addDocs = "--docs=false" 93 } 94 95 var services string 96 if len(args) > 0 { 97 services = args[0] 98 } else { 99 services = "admin,api" 100 } 101 102 name := buildOutputName() 103 buildPathName := strings.Join([]string{".", name}, string(filepath.Separator)) 104 serve := exec.Command(buildPathName, 105 "serve", 106 services, 107 fmt.Sprintf("--bind=%s", bind), 108 fmt.Sprintf("--port=%d", port), 109 fmt.Sprintf("--https-port=%d", httpsport), 110 fmt.Sprintf("--docs-port=%d", docsport), 111 addDocs, 112 addTLS, 113 ) 114 serve.Stderr = os.Stderr 115 serve.Stdout = os.Stdout 116 117 return serve.Run() 118 }, 119 } 120 121 // ErrWrongOrMissingService informs a user that the services to run must be 122 // explicitly specified when serve is called 123 var ErrWrongOrMissingService = errors.New("To execute 'ponzu serve', " + 124 "you must specify which service to run.") 125 126 var serveCmd = &cobra.Command{ 127 Use: "serve [flags] <service,service>", 128 Aliases: []string{"s"}, 129 Short: "run the server (serve is wrapped by the run command)", 130 Hidden: true, 131 RunE: func(cmd *cobra.Command, args []string) error { 132 if len(args) == 0 { 133 return ErrWrongOrMissingService 134 } 135 136 db.Init() 137 defer db.Close() 138 139 analytics.Init() 140 defer analytics.Close() 141 142 services := strings.Split(args[0], ",") 143 144 for _, service := range services { 145 if service == "api" { 146 api.Run() 147 } else if service == "admin" { 148 admin.Run() 149 } else { 150 return ErrWrongOrMissingService 151 } 152 } 153 154 // run docs server if --docs is true 155 if docs { 156 admin.Docs(docsport) 157 } 158 159 // init search index 160 go db.InitSearchIndex() 161 162 // save the https port the system is listening on 163 err := db.PutConfig("https_port", fmt.Sprintf("%d", httpsport)) 164 if err != nil { 165 log.Fatalln("System failed to save config. Please try to run again.", err) 166 } 167 168 // cannot run production HTTPS and development HTTPS together 169 if devhttps { 170 fmt.Println("Enabling self-signed HTTPS... [DEV]") 171 172 go tls.EnableDev() 173 fmt.Println("Server listening on https://localhost:10443 for requests... [DEV]") 174 fmt.Println("----") 175 fmt.Println("If your browser rejects HTTPS requests, try allowing insecure connections on localhost.") 176 fmt.Println("on Chrome, visit chrome://flags/#allow-insecure-localhost") 177 178 } else if https { 179 fmt.Println("Enabling HTTPS...") 180 181 go tls.Enable() 182 fmt.Printf("Server listening on :%s for HTTPS requests...\n", db.ConfigCache("https_port").(string)) 183 } 184 185 // save the https port the system is listening on so internal system can make 186 // HTTP api calls while in dev or production w/o adding more cli flags 187 err = db.PutConfig("http_port", fmt.Sprintf("%d", port)) 188 if err != nil { 189 log.Fatalln("System failed to save config. Please try to run again.", err) 190 } 191 192 // save the bound address the system is listening on so internal system can make 193 // HTTP api calls while in dev or production w/o adding more cli flags 194 if bind == "" { 195 bind = "localhost" 196 } 197 err = db.PutConfig("bind_addr", bind) 198 if err != nil { 199 log.Fatalln("System failed to save config. Please try to run again.", err) 200 } 201 202 fmt.Printf("Server listening at %s:%d for HTTP requests...\n", bind, port) 203 fmt.Println("\nVisit '/admin' to get started.") 204 log.Fatalln(http.ListenAndServe(fmt.Sprintf("%s:%d", bind, port), nil)) 205 return nil 206 }, 207 } 208 209 func init() { 210 for _, cmd := range []*cobra.Command{runCmd, serveCmd} { 211 cmd.Flags().StringVar(&bind, "bind", "localhost", "address for ponzu to bind the HTTP(S) server") 212 cmd.Flags().IntVar(&httpsport, "https-port", 443, "port for ponzu to bind its HTTPS listener") 213 cmd.Flags().IntVar(&port, "port", 8080, "port for ponzu to bind its HTTP listener") 214 cmd.Flags().IntVar(&docsport, "docs-port", 1234, "[dev environment] override the documentation server port") 215 cmd.Flags().BoolVar(&docs, "docs", false, "[dev environment] run HTTP server to view local HTML documentation") 216 cmd.Flags().BoolVar(&https, "https", false, "enable automatic TLS/SSL certificate management") 217 cmd.Flags().BoolVar(&devhttps, "dev-https", false, "[dev environment] enable automatic TLS/SSL certificate management") 218 } 219 220 RegisterCmdlineCommand(serveCmd) 221 RegisterCmdlineCommand(runCmd) 222 223 pflags := rootCmd.PersistentFlags() 224 pflags.StringVar(&gocmd, "gocmd", "go", "custom go command if using beta or new release of Go") 225 } 226 227 func main() { 228 if err := rootCmd.Execute(); err != nil { 229 fmt.Println(err) 230 os.Exit(1) 231 } 232 } 233 234 func execAndWait(command string, arg ...string) error { 235 cmd := exec.Command(command, arg...) 236 cmd.Stderr = os.Stderr 237 cmd.Stdout = os.Stdout 238 239 err := cmd.Start() 240 if err != nil { 241 return err 242 243 } 244 return cmd.Wait() 245 }