added apis.Serve helper
This commit is contained in:
		
							parent
							
								
									060ed8013e
								
							
						
					
					
						commit
						3358d8476b
					
				|  | @ -1,4 +1,9 @@ | |||
| ## (WIP) v0.15.1 | ||||
| ## (WIP) | ||||
| 
 | ||||
| - Added `apis.Serve(app, options)` helper to allow starting the API server programmatically. | ||||
| 
 | ||||
| 
 | ||||
| ## v0.15.1 | ||||
| 
 | ||||
| - Fixed `Ctrl + S` in the `editor` field not propagating the quick save shortcut to the parent form. | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,165 @@ | |||
| package apis | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/fatih/color" | ||||
| 	"github.com/labstack/echo/v5/middleware" | ||||
| 	"github.com/pocketbase/dbx" | ||||
| 	"github.com/pocketbase/pocketbase/core" | ||||
| 	"github.com/pocketbase/pocketbase/migrations" | ||||
| 	"github.com/pocketbase/pocketbase/migrations/logs" | ||||
| 	"github.com/pocketbase/pocketbase/tools/migrate" | ||||
| 	"golang.org/x/crypto/acme" | ||||
| 	"golang.org/x/crypto/acme/autocert" | ||||
| ) | ||||
| 
 | ||||
| // ServeOptions defines an optional struct for apis.Serve().
 | ||||
| type ServeOptions struct { | ||||
| 	ShowStartBanner bool | ||||
| 	HttpAddr        string | ||||
| 	HttpsAddr       string | ||||
| 	AllowedOrigins  []string // optional list of CORS origins (default to "*")
 | ||||
| 	BeforeServeFunc func(server *http.Server) error | ||||
| } | ||||
| 
 | ||||
| // Serve starts a new app web server.
 | ||||
| func Serve(app core.App, options *ServeOptions) error { | ||||
| 	if options == nil { | ||||
| 		options = &ServeOptions{} | ||||
| 	} | ||||
| 
 | ||||
| 	if len(options.AllowedOrigins) == 0 { | ||||
| 		options.AllowedOrigins = []string{"*"} | ||||
| 	} | ||||
| 
 | ||||
| 	// ensure that the latest migrations are applied before starting the server
 | ||||
| 	if err := runMigrations(app); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// reload app settings in case a new default value was set with a migration
 | ||||
| 	// (or if this is the first time the init migration was executed)
 | ||||
| 	if err := app.RefreshSettings(); err != nil { | ||||
| 		color.Yellow("=====================================") | ||||
| 		color.Yellow("WARNING: Settings load error! \n%v", err) | ||||
| 		color.Yellow("Fallback to the application defaults.") | ||||
| 		color.Yellow("=====================================") | ||||
| 	} | ||||
| 
 | ||||
| 	router, err := InitApi(app) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// configure cors
 | ||||
| 	router.Use(middleware.CORSWithConfig(middleware.CORSConfig{ | ||||
| 		Skipper:      middleware.DefaultSkipper, | ||||
| 		AllowOrigins: options.AllowedOrigins, | ||||
| 		AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete}, | ||||
| 	})) | ||||
| 
 | ||||
| 	// start http server
 | ||||
| 	// ---
 | ||||
| 	mainAddr := options.HttpAddr | ||||
| 	if options.HttpsAddr != "" { | ||||
| 		mainAddr = options.HttpsAddr | ||||
| 	} | ||||
| 
 | ||||
| 	mainHost, _, _ := net.SplitHostPort(mainAddr) | ||||
| 
 | ||||
| 	certManager := autocert.Manager{ | ||||
| 		Prompt:     autocert.AcceptTOS, | ||||
| 		Cache:      autocert.DirCache(filepath.Join(app.DataDir(), ".autocert_cache")), | ||||
| 		HostPolicy: autocert.HostWhitelist(mainHost, "www."+mainHost), | ||||
| 	} | ||||
| 
 | ||||
| 	serverConfig := &http.Server{ | ||||
| 		TLSConfig: &tls.Config{ | ||||
| 			GetCertificate: certManager.GetCertificate, | ||||
| 			NextProtos:     []string{acme.ALPNProto}, | ||||
| 		}, | ||||
| 		ReadTimeout:       5 * time.Minute, | ||||
| 		ReadHeaderTimeout: 30 * time.Second, | ||||
| 		// WriteTimeout: 60 * time.Second, // breaks sse!
 | ||||
| 		Handler: router, | ||||
| 		Addr:    mainAddr, | ||||
| 	} | ||||
| 
 | ||||
| 	if options.BeforeServeFunc != nil { | ||||
| 		if err := options.BeforeServeFunc(serverConfig); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if options.ShowStartBanner { | ||||
| 		schema := "http" | ||||
| 		if options.HttpsAddr != "" { | ||||
| 			schema = "https" | ||||
| 		} | ||||
| 
 | ||||
| 		date := new(strings.Builder) | ||||
| 		log.New(date, "", log.LstdFlags).Print() | ||||
| 
 | ||||
| 		bold := color.New(color.Bold).Add(color.FgGreen) | ||||
| 		bold.Printf( | ||||
| 			"%s Server started at %s\n", | ||||
| 			strings.TrimSpace(date.String()), | ||||
| 			color.CyanString("%s://%s", schema, serverConfig.Addr), | ||||
| 		) | ||||
| 
 | ||||
| 		regular := color.New() | ||||
| 		regular.Printf(" ➜ REST API: %s\n", color.CyanString("%s://%s/api/", schema, serverConfig.Addr)) | ||||
| 		regular.Printf(" ➜ Admin UI: %s\n", color.CyanString("%s://%s/_/", schema, serverConfig.Addr)) | ||||
| 	} | ||||
| 
 | ||||
| 	// start HTTPS server
 | ||||
| 	if options.HttpsAddr != "" { | ||||
| 		// if httpAddr is set, start an HTTP server to redirect the traffic to the HTTPS version
 | ||||
| 		if options.HttpAddr != "" { | ||||
| 			go http.ListenAndServe(options.HttpAddr, certManager.HTTPHandler(nil)) | ||||
| 		} | ||||
| 
 | ||||
| 		return serverConfig.ListenAndServeTLS("", "") | ||||
| 	} | ||||
| 
 | ||||
| 	// OR start HTTP server
 | ||||
| 	return serverConfig.ListenAndServe() | ||||
| } | ||||
| 
 | ||||
| type migrationsConnection struct { | ||||
| 	DB             *dbx.DB | ||||
| 	MigrationsList migrate.MigrationsList | ||||
| } | ||||
| 
 | ||||
| func runMigrations(app core.App) error { | ||||
| 	connections := []migrationsConnection{ | ||||
| 		{ | ||||
| 			DB:             app.DB(), | ||||
| 			MigrationsList: migrations.AppMigrations, | ||||
| 		}, | ||||
| 		{ | ||||
| 			DB:             app.LogsDB(), | ||||
| 			MigrationsList: logs.LogsMigrations, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, c := range connections { | ||||
| 		runner, err := migrate.NewRunner(c.DB, c.MigrationsList) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		if _, err := runner.Up(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										141
									
								
								cmd/serve.go
								
								
								
								
							
							
						
						
									
										141
									
								
								cmd/serve.go
								
								
								
								
							|  | @ -1,25 +1,12 @@ | |||
| package cmd | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/fatih/color" | ||||
| 	"github.com/labstack/echo/v5/middleware" | ||||
| 	"github.com/pocketbase/dbx" | ||||
| 	"github.com/pocketbase/pocketbase/apis" | ||||
| 	"github.com/pocketbase/pocketbase/core" | ||||
| 	"github.com/pocketbase/pocketbase/migrations" | ||||
| 	"github.com/pocketbase/pocketbase/migrations/logs" | ||||
| 	"github.com/pocketbase/pocketbase/tools/migrate" | ||||
| 	"github.com/spf13/cobra" | ||||
| 	"golang.org/x/crypto/acme" | ||||
| 	"golang.org/x/crypto/acme/autocert" | ||||
| ) | ||||
| 
 | ||||
| // NewServeCommand creates and returns new command responsible for
 | ||||
|  | @ -33,96 +20,15 @@ func NewServeCommand(app core.App, showStartBanner bool) *cobra.Command { | |||
| 		Use:   "serve", | ||||
| 		Short: "Starts the web server (default to 127.0.0.1:8090)", | ||||
| 		Run: func(command *cobra.Command, args []string) { | ||||
| 			// ensure that the latest migrations are applied before starting the server
 | ||||
| 			if err := runMigrations(app); err != nil { | ||||
| 				panic(err) | ||||
| 			} | ||||
| 			err := apis.Serve(app, &apis.ServeOptions{ | ||||
| 				HttpAddr:        httpAddr, | ||||
| 				HttpsAddr:       httpsAddr, | ||||
| 				ShowStartBanner: showStartBanner, | ||||
| 				AllowedOrigins:  allowedOrigins, | ||||
| 			}) | ||||
| 
 | ||||
| 			// reload app settings in case a new default value was set with a migration
 | ||||
| 			// (or if this is the first time the init migration was executed)
 | ||||
| 			if err := app.RefreshSettings(); err != nil { | ||||
| 				color.Yellow("=====================================") | ||||
| 				color.Yellow("WARNING: Settings load error! \n%v", err) | ||||
| 				color.Yellow("Fallback to the application defaults.") | ||||
| 				color.Yellow("=====================================") | ||||
| 			} | ||||
| 
 | ||||
| 			router, err := apis.InitApi(app) | ||||
| 			if err != nil { | ||||
| 				panic(err) | ||||
| 			} | ||||
| 
 | ||||
| 			// configure cors
 | ||||
| 			router.Use(middleware.CORSWithConfig(middleware.CORSConfig{ | ||||
| 				Skipper:      middleware.DefaultSkipper, | ||||
| 				AllowOrigins: allowedOrigins, | ||||
| 				AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete}, | ||||
| 			})) | ||||
| 
 | ||||
| 			// start http server
 | ||||
| 			// ---
 | ||||
| 			mainAddr := httpAddr | ||||
| 			if httpsAddr != "" { | ||||
| 				mainAddr = httpsAddr | ||||
| 			} | ||||
| 
 | ||||
| 			mainHost, _, _ := net.SplitHostPort(mainAddr) | ||||
| 
 | ||||
| 			certManager := autocert.Manager{ | ||||
| 				Prompt:     autocert.AcceptTOS, | ||||
| 				Cache:      autocert.DirCache(filepath.Join(app.DataDir(), ".autocert_cache")), | ||||
| 				HostPolicy: autocert.HostWhitelist(mainHost, "www."+mainHost), | ||||
| 			} | ||||
| 
 | ||||
| 			serverConfig := &http.Server{ | ||||
| 				TLSConfig: &tls.Config{ | ||||
| 					GetCertificate: certManager.GetCertificate, | ||||
| 					NextProtos:     []string{acme.ALPNProto}, | ||||
| 				}, | ||||
| 				ReadTimeout:       5 * time.Minute, | ||||
| 				ReadHeaderTimeout: 30 * time.Second, | ||||
| 				// WriteTimeout: 60 * time.Second, // breaks sse!
 | ||||
| 				Handler: router, | ||||
| 				Addr:    mainAddr, | ||||
| 			} | ||||
| 
 | ||||
| 			if showStartBanner { | ||||
| 				schema := "http" | ||||
| 				if httpsAddr != "" { | ||||
| 					schema = "https" | ||||
| 				} | ||||
| 
 | ||||
| 				date := new(strings.Builder) | ||||
| 				log.New(date, "", log.LstdFlags).Print() | ||||
| 
 | ||||
| 				bold := color.New(color.Bold).Add(color.FgGreen) | ||||
| 				bold.Printf( | ||||
| 					"%s Server started at %s\n", | ||||
| 					strings.TrimSpace(date.String()), | ||||
| 					color.CyanString("%s://%s", schema, serverConfig.Addr), | ||||
| 				) | ||||
| 
 | ||||
| 				regular := color.New() | ||||
| 				regular.Printf(" ➜ REST API: %s\n", color.CyanString("%s://%s/api/", schema, serverConfig.Addr)) | ||||
| 				regular.Printf(" ➜ Admin UI: %s\n", color.CyanString("%s://%s/_/", schema, serverConfig.Addr)) | ||||
| 			} | ||||
| 
 | ||||
| 			var serveErr error | ||||
| 			if httpsAddr != "" { | ||||
| 				// if httpAddr is set, start an HTTP server to redirect the traffic to the HTTPS version
 | ||||
| 				if httpAddr != "" { | ||||
| 					go http.ListenAndServe(httpAddr, certManager.HTTPHandler(nil)) | ||||
| 				} | ||||
| 
 | ||||
| 				// start HTTPS server
 | ||||
| 				serveErr = serverConfig.ListenAndServeTLS("", "") | ||||
| 			} else { | ||||
| 				// start HTTP server
 | ||||
| 				serveErr = serverConfig.ListenAndServe() | ||||
| 			} | ||||
| 
 | ||||
| 			if serveErr != http.ErrServerClosed { | ||||
| 				log.Fatalln(serveErr) | ||||
| 			if err != http.ErrServerClosed { | ||||
| 				log.Fatalln(err) | ||||
| 			} | ||||
| 		}, | ||||
| 	} | ||||
|  | @ -150,34 +56,3 @@ func NewServeCommand(app core.App, showStartBanner bool) *cobra.Command { | |||
| 
 | ||||
| 	return command | ||||
| } | ||||
| 
 | ||||
| type migrationsConnection struct { | ||||
| 	DB             *dbx.DB | ||||
| 	MigrationsList migrate.MigrationsList | ||||
| } | ||||
| 
 | ||||
| func runMigrations(app core.App) error { | ||||
| 	connections := []migrationsConnection{ | ||||
| 		{ | ||||
| 			DB:             app.DB(), | ||||
| 			MigrationsList: migrations.AppMigrations, | ||||
| 		}, | ||||
| 		{ | ||||
| 			DB:             app.LogsDB(), | ||||
| 			MigrationsList: logs.LogsMigrations, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, c := range connections { | ||||
| 		runner, err := migrate.NewRunner(c.DB, c.MigrationsList) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		if _, err := runner.Up(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue