github.com/mncinnovation/ponzu@v0.11.1-0.20200102001432-9bc41b703131/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/ponzu-cms/ponzu/content"
    19  	"github.com/ponzu-cms/ponzu/system/admin"
    20  	"github.com/ponzu-cms/ponzu/system/api"
    21  	"github.com/ponzu-cms/ponzu/system/api/analytics"
    22  	"github.com/ponzu-cms/ponzu/system/db"
    23  	"github.com/ponzu-cms/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  }