Merge pull request #1012 from fudiwei/main

This commit is contained in:
RHQYZ
2025-10-28 17:09:54 +08:00
committed by GitHub
8 changed files with 281 additions and 4 deletions
+12
View File
@@ -0,0 +1,12 @@
//go:build !windows
// +build !windows
package cmd
import (
"github.com/pocketbase/pocketbase"
)
func Serve(app *pocketbase.PocketBase) error {
return app.Start()
}
+57
View File
@@ -0,0 +1,57 @@
//go:build windows
// +build windows
package cmd
import (
"fmt"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/core"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/eventlog"
)
type winscHandler struct {
pb *pocketbase.PocketBase
elog *eventlog.Log
}
func (h *winscHandler) Execute(args []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (bool, uint32) {
go func() {
if err := h.pb.Start(); err != nil {
h.elog.Error(999, fmt.Sprintf("Start failed: %v", err))
}
}()
s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
for {
select {
case c := <-r:
switch c.Cmd {
case svc.Interrogate:
s <- c.CurrentStatus
case svc.Stop, svc.Shutdown:
event := new(core.TerminateEvent)
event.App = h.pb
h.pb.OnTerminate().Trigger(event, func(e *core.TerminateEvent) error {
return e.App.ResetBootstrapState()
})
s <- svc.Status{State: svc.Stopped}
return false, 0
default:
h.elog.Warning(998, fmt.Sprintf("unexpected control request: %v", c.Cmd))
}
}
}
}
func Serve(app *pocketbase.PocketBase) error {
if isWinsc, _ := svc.IsWindowsService(); isWinsc {
elog, _ := eventlog.Open(winscName)
defer elog.Close()
return svc.Run(winscName, &winscHandler{pb: app, elog: elog})
}
return app.Start()
}
+18
View File
@@ -0,0 +1,18 @@
//go:build !windows
// +build !windows
package cmd
import (
"github.com/pocketbase/pocketbase/core"
"github.com/spf13/cobra"
)
func NewWinscCommand(app core.App) *cobra.Command {
command := &cobra.Command{
Use: "winsc",
Short: "Install/Uninstall Windows service (Not supported on non-Windows OS)",
}
return command
}
+189
View File
@@ -0,0 +1,189 @@
//go:build windows
// +build windows
package cmd
import (
"fmt"
"log/slog"
"os"
"time"
"github.com/pocketbase/pocketbase/core"
"github.com/spf13/cobra"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/eventlog"
"golang.org/x/sys/windows/svc/mgr"
)
const winscName = "certimate"
func NewWinscCommand(app core.App) *cobra.Command {
command := &cobra.Command{
Use: "winsc",
Short: "Install/Uninstall Windows service",
}
command.AddCommand(winscInstallCommand(app))
command.AddCommand(winscUninstallCommand(app))
command.AddCommand(winscStartCommand(app))
command.AddCommand(winscStopCommand(app))
return command
}
func winscInstallCommand(app core.App) *cobra.Command {
command := &cobra.Command{
Use: "install [args...]",
Example: "winsc install",
Run: func(cmd *cobra.Command, args []string) {
srvPath, err := os.Executable()
if err != nil {
srvPath = os.Args[0]
}
srvArgs := []string{"serve"}
srvArgs = append(srvArgs, args...)
manager, err := mgr.Connect()
if err != nil {
slog.Error(fmt.Sprintf("failed to connect to service manager: %v", err))
return
}
defer manager.Disconnect()
config := mgr.Config{
DisplayName: "Certimate",
Description: "https://github.com/certimate-go/certimate",
StartType: mgr.StartAutomatic,
}
service, err := manager.CreateService(winscName, srvPath, config, srvArgs...)
if err != nil {
slog.Error(fmt.Sprintf("failed to create service: %v", err))
return
}
defer service.Close()
eventlog.InstallAsEventCreate(winscName, eventlog.Error|eventlog.Warning|eventlog.Info)
slog.Info(fmt.Sprintf("service '%s' installed", winscName))
if err := service.Start(); err != nil {
slog.Warn(fmt.Sprintf("failed to start service: %v", err))
}
slog.Info(fmt.Sprintf("service '%s' started", winscName))
},
DisableFlagParsing: true,
}
return command
}
func winscUninstallCommand(app core.App) *cobra.Command {
command := &cobra.Command{
Use: "uninstall",
Example: "winsc uninstall",
Run: func(cmd *cobra.Command, args []string) {
manager, err := mgr.Connect()
if err != nil {
slog.Error(fmt.Sprintf("failed to connect to service manager: %v", err))
return
}
defer manager.Disconnect()
service, err := manager.OpenService(winscName)
if err != nil {
slog.Error(fmt.Sprintf("failed to open service: %v", err))
return
}
defer service.Close()
status, err := service.Query()
if err == nil && status.State != svc.Stopped {
_, err = service.Control(svc.Stop)
if err != nil {
slog.Warn(fmt.Sprintf("failed to stop service: %v", err))
}
time.Sleep(3 * time.Second)
slog.Info(fmt.Sprintf("service '%s' stopped", winscName))
}
if err = service.Delete(); err != nil {
slog.Error(fmt.Sprintf("failed to delete service: %v", err))
return
}
eventlog.Remove(winscName)
slog.Info(fmt.Sprintf("service '%s' uninstalled", winscName))
},
}
return command
}
func winscStartCommand(app core.App) *cobra.Command {
command := &cobra.Command{
Use: "start",
Example: "winsc start",
Run: func(cmd *cobra.Command, args []string) {
manager, err := mgr.Connect()
if err != nil {
slog.Error(fmt.Sprintf("failed to connect to service manager: %v", err))
return
}
defer manager.Disconnect()
service, err := manager.OpenService(winscName)
if err != nil {
slog.Error(fmt.Sprintf("failed to open service: %v", err))
return
}
defer service.Close()
if err := service.Start(); err != nil {
slog.Error(fmt.Sprintf("failed to start service: %v", err))
return
}
slog.Info(fmt.Sprintf("service '%s' started", winscName))
},
}
return command
}
func winscStopCommand(app core.App) *cobra.Command {
command := &cobra.Command{
Use: "stop",
Example: "winsc stop",
Run: func(cmd *cobra.Command, args []string) {
manager, err := mgr.Connect()
if err != nil {
slog.Error(fmt.Sprintf("failed to connect to service manager: %v", err))
return
}
defer manager.Disconnect()
service, err := manager.OpenService(winscName)
if err != nil {
slog.Error(fmt.Sprintf("failed to open service: %v", err))
return
}
defer service.Close()
status, err := service.Query()
if err == nil && status.State != svc.Stopped {
_, err = service.Control(svc.Stop)
if err != nil {
slog.Warn(fmt.Sprintf("failed to stop service: %v", err))
}
time.Sleep(3 * time.Second)
slog.Info(fmt.Sprintf("service '%s' stopped", winscName))
}
},
}
return command
}
+1 -1
View File
@@ -218,7 +218,7 @@ require (
golang.org/x/net v0.46.0 // indirect
golang.org/x/oauth2 v0.32.0 // indirect
golang.org/x/sync v0.17.0
golang.org/x/sys v0.37.0 // indirect
golang.org/x/sys v0.37.0
golang.org/x/term v0.36.0 // indirect
golang.org/x/text v0.30.0 // indirect
golang.org/x/time v0.14.0 // indirect
+1 -1
View File
@@ -206,7 +206,7 @@ func (wd *workflowDispatcher) tryExecuteAsync(task *taskInfo) {
defer func() {
if r := recover(); r != nil {
wd.syslog.Error(fmt.Sprintf("workflow dispatcher panic: %v", r), slog.String("workflowId", task.WorkflowId), slog.String("runId", task.RunId))
slog.Default().Error(fmt.Sprintf("workflow dispatcher panic: %v, stack trace: %s", r, string(debug.Stack())), slog.String("workflowId", task.WorkflowId), slog.String("runId", task.RunId))
slog.Error(fmt.Sprintf("workflow dispatcher panic: %v, stack trace: %s", r, string(debug.Stack())), slog.String("workflowId", task.WorkflowId), slog.String("runId", task.RunId))
if workflowRun != nil {
workflowRun.Status = domain.WorkflowRunStatusTypeFailed
+1 -1
View File
@@ -60,7 +60,7 @@ func (we *workflowEngine) Invoke(ctx context.Context, execution WorkflowExecutio
if r := recover(); r != nil {
we.fireOnErrorHooks(ctx, fmt.Errorf("workflow engine panic: %v", r))
we.syslog.Error(fmt.Sprintf("workflow engine panic: %v", r), slog.String("workflowId", execution.WorkflowId), slog.String("runId", execution.RunId))
slog.Default().Error(fmt.Sprintf("workflow engine panic: %v, stack trace: %s", r, string(debug.Stack())), slog.String("workflowId", execution.WorkflowId), slog.String("runId", execution.RunId))
slog.Error(fmt.Sprintf("workflow engine panic: %v, stack trace: %s", r, string(debug.Stack())), slog.String("workflowId", execution.WorkflowId), slog.String("runId", execution.RunId))
}
}()
+2 -1
View File
@@ -44,6 +44,7 @@ func main() {
})
app.RootCmd.AddCommand(cmd.NewInternalCommand(app))
app.RootCmd.AddCommand(cmd.NewWinscCommand(app))
app.OnServe().BindFunc(func(e *core.ServeEvent) error {
scheduler.Register()
@@ -72,7 +73,7 @@ func main() {
return e.Next()
})
if err := app.Start(); err != nil {
if err := cmd.Serve(app); err != nil {
slog.Error("[CERTIMATE] Start failed.", slog.Any("error", err))
}
}