Files
UpSnap/backend/pb/pb.go
2023-08-12 21:05:05 +02:00

254 lines
6.2 KiB
Go

package pb
import (
"io/fs"
"net/http"
"os"
"github.com/labstack/echo/v5"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/models"
"github.com/pocketbase/pocketbase/plugins/migratecmd"
"github.com/seriousm4x/upsnap/cronjobs"
"github.com/seriousm4x/upsnap/logger"
_ "github.com/seriousm4x/upsnap/migrations"
)
var App *pocketbase.PocketBase
var Version = "(untracked)"
func StartPocketBase(distDirFS fs.FS) {
App = pocketbase.New()
App.RootCmd.Short = "UpSnap CLI"
App.RootCmd.Version = Version
// auto migrate db
migratecmd.MustRegister(App, App.RootCmd, migratecmd.Config{
Automigrate: true,
})
// event hooks
App.OnBeforeServe().Add(func(e *core.ServeEvent) error {
// set static website path
e.Router.GET("/*", apis.StaticDirectoryHandler(distDirFS, true))
// add wake route to api
e.Router.AddRoute(echo.Route{
Method: http.MethodGet,
Path: "/api/upsnap/wake/:id",
Handler: HandlerWake,
Middlewares: []echo.MiddlewareFunc{
apis.ActivityLogger(App),
apis.RequireAdminOrRecordAuth("users"),
},
})
// add shutdown route to api
e.Router.AddRoute(echo.Route{
Method: http.MethodGet,
Path: "/api/upsnap/shutdown/:id",
Handler: HandlerShutdown,
Middlewares: []echo.MiddlewareFunc{
apis.ActivityLogger(App),
apis.RequireAdminOrRecordAuth("users"),
},
})
// add network scan route to api
e.Router.AddRoute(echo.Route{
Method: http.MethodGet,
Path: "/api/upsnap/scan",
Handler: HandlerScan,
Middlewares: []echo.MiddlewareFunc{
apis.ActivityLogger(App),
apis.RequireAdminAuth(),
},
})
// import environment and set settings
if err := importSettings(); err != nil {
return err
}
// reset device states and run ping cronjob
if err := resetDeviceStates(); err != nil {
return err
}
// run cronjobs
go cronjobs.RunPing(App)
go cronjobs.RunWakeShutdown(App)
// restart ping cronjobs or wake/shutdown cronjobs on model update
// add event hook before starting server.
// using this outside App.OnBeforeServe() would not work
App.OnModelAfterUpdate().Add(func(e *core.ModelEvent) error {
if e.Model.TableName() == "settings_private" {
for _, job := range cronjobs.CronPing.Entries() {
cronjobs.CronPing.Remove(job.ID)
}
go cronjobs.RunPing(App)
} else if e.Model.TableName() == "devices" {
if err := refreshDeviceList(); err != nil {
logger.Error.Println(err)
return err
}
for _, job := range cronjobs.CronWakeShutdown.Entries() {
cronjobs.CronWakeShutdown.Remove(job.ID)
}
go cronjobs.RunWakeShutdown(App)
}
return nil
})
return nil
})
// refresh the device list on database events
App.OnModelAfterCreate().Add(func(e *core.ModelEvent) error {
if e.Model.TableName() == "_admins" {
if err := setSetupCompleted(); err != nil {
logger.Error.Println(err)
return err
}
} else {
if err := refreshDeviceList(); err != nil {
logger.Error.Println(err)
return err
}
}
return nil
})
App.OnModelAfterDelete().Add(func(e *core.ModelEvent) error {
if e.Model.TableName() == "_admins" {
if err := setSetupCompleted(); err != nil {
logger.Error.Println(err)
return err
}
} else {
if err := refreshDeviceList(); err != nil {
logger.Error.Println(err)
return err
}
}
return nil
})
// start pocketbase
if err := App.Start(); err != nil {
logger.Error.Fatalln(err)
}
}
func importSettings() error {
// get first settingsPrivate record
settingsPrivateRecords, err := App.Dao().FindRecordsByExpr("settings_private")
if err != nil {
return err
}
settingsPrivateCollection, err := App.Dao().FindCollectionByNameOrId("settings_private")
if err != nil {
return err
}
settingsPrivate := models.NewRecord(settingsPrivateCollection)
if len(settingsPrivateRecords) > 0 {
settingsPrivate = settingsPrivateRecords[0]
}
// get first settingsPublic record
settingsPublicRecords, err := App.Dao().FindRecordsByExpr("settings_public")
if err != nil {
return err
}
settingsPublicCollection, err := App.Dao().FindCollectionByNameOrId("settings_public")
if err != nil {
return err
}
settingsPublic := models.NewRecord(settingsPublicCollection)
if len(settingsPublicRecords) > 0 {
settingsPublic = settingsPublicRecords[0]
}
// set ping interval settings. priority:
// 1st: env var
// 2nd: database entry
// 3rd: default values
interval := "@every 3s"
if settingsPrivate.GetString("interval") != "" {
interval = settingsPrivate.GetString("interval")
}
if os.Getenv("UPSNAP_INTERVAL") != "" {
interval = os.Getenv("UPSNAP_INTERVAL")
}
// set private settings
settingsPrivate.Set("interval", interval)
if scanRange := os.Getenv("UPSNAP_SCAN_RANGE"); scanRange != "" {
settingsPrivate.Set("scan_range", scanRange)
}
// set public settings
if websiteTitle := os.Getenv("UPSNAP_WEBSITE_TITLE"); websiteTitle != "" {
settingsPublic.Set("website_title", websiteTitle)
}
// save records
if err := App.Dao().SaveRecord(settingsPrivate); err != nil {
return err
}
if err := App.Dao().SaveRecord(settingsPublic); err != nil {
return err
}
if err := setSetupCompleted(); err != nil {
logger.Error.Println(err)
return err
}
logger.Info.Println("Ping interval set to", interval)
return nil
}
func resetDeviceStates() error {
devices, err := App.Dao().FindRecordsByExpr("devices")
if err != nil {
return err
}
for _, device := range devices {
device.Set("status", "offline")
if err := App.Dao().SaveRecord(device); err != nil {
return err
}
}
cronjobs.Devices = devices
return nil
}
func refreshDeviceList() error {
var err error
if cronjobs.Devices, err = App.Dao().FindRecordsByExpr("devices"); err != nil {
return err
}
return nil
}
func setSetupCompleted() error {
totalAdmins, err := App.Dao().TotalAdmins()
if err != nil {
return err
}
settingsPublicRecords, err := App.Dao().FindRecordsByExpr("settings_public")
if err != nil {
return err
}
if totalAdmins > 0 {
settingsPublicRecords[0].Set("setup_completed", true)
} else {
settingsPublicRecords[0].Set("setup_completed", false)
}
if err := App.Dao().SaveRecord(settingsPublicRecords[0]); err != nil {
return err
}
return nil
}