strimertul/main.go

181 lines
4.8 KiB
Go

package main
import (
"context"
"embed"
"fmt"
corelog "log"
"log/slog"
_ "net/http/pprof"
"os"
"runtime/debug"
"git.sr.ht/~ashkeel/strimertul/log"
"git.sr.ht/~ashkeel/strimertul/utils"
"github.com/apenwarr/fixconsole"
"github.com/urfave/cli/v2"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
const devVersionMarker = "v0.0.0-UNKNOWN"
var appVersion = devVersionMarker
const (
crashReportURL = "https://crash.strimertul.stream/upload"
)
//go:embed frontend/dist
var frontend embed.FS
func main() {
if err := fixconsole.FixConsoleIfNeeded(); err != nil {
corelog.Fatal(err)
}
var panicLog *os.File
app := &cli.App{
Name: "strimertul",
Usage: "the small broadcasting suite for Twitch",
Version: appVersion,
Action: cliMain,
Flags: []cli.Flag{
&cli.StringFlag{Name: "log-level", Usage: "logging level (debug,info,warn,error)", Value: "info"},
&cli.StringFlag{Name: "driver", Usage: "specify database driver", Value: "auto"},
&cli.StringFlag{Name: "database-dir", Aliases: []string{"db-dir"}, Usage: "specify database directory", Value: "data"},
&cli.StringFlag{Name: "backup-dir", Aliases: []string{"b-dir"}, Usage: "specify backup directory", Value: "backups"},
&cli.IntFlag{Name: "backup-interval", Aliases: []string{"b-i"}, Usage: "specify backup interval (in minutes, 0 to disable)", Value: 60},
&cli.IntFlag{Name: "max-backups", Aliases: []string{"b-max"}, Usage: "maximum number of backups to keep, older ones will be deleted, set to 0 to keep all", Value: 20},
},
Commands: []*cli.Command{
{
Name: "import",
Usage: "import database from JSON file",
ArgsUsage: "[-f input.json]",
Flags: []cli.Flag{
&cli.StringFlag{Name: "file", Aliases: []string{"f"}, Usage: "file to open", DefaultText: "STDIN"},
},
Action: cliImport,
},
{
Name: "export",
Usage: "export database as JSON file",
ArgsUsage: "[-f output.json]",
Flags: []cli.Flag{
&cli.StringFlag{Name: "file", Aliases: []string{"f"}, Usage: "file to save to", DefaultText: "STDOUT"},
},
Action: cliExport,
},
{
Name: "restore",
Usage: "restore database from backup",
ArgsUsage: "[-f backup.db]",
Flags: []cli.Flag{
&cli.StringFlag{Name: "file", Aliases: []string{"f"}, Usage: "backup to open", DefaultText: "STDOUT"},
},
Action: cliRestore,
},
},
Before: func(ctx *cli.Context) error {
// Initialize logger with global flags
level := slog.LevelInfo
if err := level.UnmarshalText([]byte(ctx.String("log-level"))); err != nil {
return cli.Exit(fmt.Sprintf("Invalid log level: %s", err), 1)
}
log.Init(level)
// Create file for panics
var err error
panicLog, err = os.OpenFile(log.PanicFilename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o666)
if err != nil {
slog.Warn("Could not create panic log", log.Error(err))
} else {
utils.RedirectStderr(panicLog)
}
// For development builds, force crash dumps
if isDev() {
debug.SetTraceback("crash")
}
return nil
},
After: func(_ *cli.Context) error {
if panicLog != nil {
utils.Close(panicLog)
}
return nil
},
}
if err := app.Run(os.Args); err != nil {
corelog.Fatal(err)
}
}
func cliMain(ctx *cli.Context) error {
// Create an instance of the app structure
app := NewApp(ctx)
// Create application with options
err := wails.Run(&options.App{
Title: "strimertul",
Width: 1024,
Height: 768,
MinWidth: 480,
MinHeight: 300,
AssetServer: &assetserver.Options{
Assets: frontend,
},
SingleInstanceLock: &options.SingleInstanceLock{
UniqueId: "d1272ae3-765a-4768-97cb-3203b788e7c5",
OnSecondInstanceLaunch: app.onSecondInstanceLaunch,
},
EnableDefaultContextMenu: true,
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
OnStartup: app.startup,
OnShutdown: app.stop,
OnBeforeClose: func(ctx context.Context) (prevent bool) {
dialog, err := runtime.MessageDialog(ctx, runtime.MessageDialogOptions{
Type: runtime.QuestionDialog,
Title: "Quit?",
Message: "Are you sure you want to quit?",
})
if err != nil {
return false
}
return dialog != "Yes"
},
Bind: []any{
app,
},
})
if err != nil {
return cli.Exit(fmt.Errorf("%s: %w", "App exited unexpectedly", err), 1)
}
return nil
}
func warnOnError(err error, text string, fields ...any) {
if err != nil {
fields = append(fields, log.Error(err))
slog.Warn(text, fields...)
}
}
func fatalError(err error, text string) error {
return cli.Exit(fmt.Errorf("%s: %w", text, err), 1)
}
// isDev checks if the running code is a development version
func isDev() bool {
return appVersion == devVersionMarker
}