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.
|
- 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
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"net/http"
|
"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/apis"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"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"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/crypto/acme"
|
|
||||||
"golang.org/x/crypto/acme/autocert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewServeCommand creates and returns new command responsible for
|
// NewServeCommand creates and returns new command responsible for
|
||||||
|
@ -33,96 +20,15 @@ func NewServeCommand(app core.App, showStartBanner bool) *cobra.Command {
|
||||||
Use: "serve",
|
Use: "serve",
|
||||||
Short: "Starts the web server (default to 127.0.0.1:8090)",
|
Short: "Starts the web server (default to 127.0.0.1:8090)",
|
||||||
Run: func(command *cobra.Command, args []string) {
|
Run: func(command *cobra.Command, args []string) {
|
||||||
// ensure that the latest migrations are applied before starting the server
|
err := apis.Serve(app, &apis.ServeOptions{
|
||||||
if err := runMigrations(app); err != nil {
|
HttpAddr: httpAddr,
|
||||||
panic(err)
|
HttpsAddr: httpsAddr,
|
||||||
}
|
ShowStartBanner: showStartBanner,
|
||||||
|
AllowedOrigins: allowedOrigins,
|
||||||
|
})
|
||||||
|
|
||||||
// reload app settings in case a new default value was set with a migration
|
if err != http.ErrServerClosed {
|
||||||
// (or if this is the first time the init migration was executed)
|
log.Fatalln(err)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -150,34 +56,3 @@ func NewServeCommand(app core.App, showStartBanner bool) *cobra.Command {
|
||||||
|
|
||||||
return 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