mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Server: Move process handling and shutdown to separate package #4767
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -2,16 +2,15 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||||
|
"github.com/photoprism/photoprism/internal/server/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StopServer shuts down the server.
|
// StopServer initiates a server restart if the user is authorized.
|
||||||
//
|
//
|
||||||
// POST /api/v1/server/stop
|
// POST /api/v1/server/stop
|
||||||
func StopServer(router *gin.RouterGroup) {
|
func StopServer(router *gin.RouterGroup) {
|
||||||
@@ -25,16 +24,11 @@ func StopServer(router *gin.RouterGroup) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
process, err := os.FindProcess(os.Getpid())
|
// Trigger restart.
|
||||||
if err != nil {
|
//
|
||||||
c.AbortWithStatusJSON(http.StatusInternalServerError, NewResponse(http.StatusInternalServerError, err, ""))
|
// Note that this requires an entrypoint script or other process to
|
||||||
return
|
// spawns a new instance when the server exists with status code 1.
|
||||||
} else {
|
c.JSON(http.StatusOK, conf.Options())
|
||||||
c.JSON(http.StatusOK, conf.Options())
|
process.Restart()
|
||||||
}
|
|
||||||
|
|
||||||
if err = process.Signal(syscall.SIGTERM); err != nil {
|
|
||||||
log.Errorf("server: %s", err)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/photoprism/photoprism/internal/mutex"
|
"github.com/photoprism/photoprism/internal/mutex"
|
||||||
"github.com/photoprism/photoprism/internal/photoprism/backup"
|
"github.com/photoprism/photoprism/internal/photoprism/backup"
|
||||||
"github.com/photoprism/photoprism/internal/server"
|
"github.com/photoprism/photoprism/internal/server"
|
||||||
|
"github.com/photoprism/photoprism/internal/server/process"
|
||||||
"github.com/photoprism/photoprism/internal/workers"
|
"github.com/photoprism/photoprism/internal/workers"
|
||||||
"github.com/photoprism/photoprism/internal/workers/auto"
|
"github.com/photoprism/photoprism/internal/workers/auto"
|
||||||
"github.com/photoprism/photoprism/pkg/clean"
|
"github.com/photoprism/photoprism/pkg/clean"
|
||||||
@@ -144,9 +145,9 @@ func startAction(ctx *cli.Context) error {
|
|||||||
// Start auto-indexing background worker.
|
// Start auto-indexing background worker.
|
||||||
auto.Start(conf)
|
auto.Start(conf)
|
||||||
|
|
||||||
// Wait for signal to initiate server shutdown.
|
// Wait for signal to trigger server shutdown or restart.
|
||||||
signal.Notify(server.Signal, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1)
|
signal.Notify(process.Signal, os.Interrupt, syscall.SIGTERM, syscall.SIGUSR1)
|
||||||
sig := <-server.Signal
|
sig := <-process.Signal
|
||||||
|
|
||||||
// Stop all background activity.
|
// Stop all background activity.
|
||||||
auto.Shutdown()
|
auto.Shutdown()
|
||||||
@@ -165,7 +166,10 @@ func startAction(ctx *cli.Context) error {
|
|||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
conf.Shutdown()
|
conf.Shutdown()
|
||||||
|
|
||||||
// Don't exit with 0 if SIGUSR1 was received to avoid restarts.
|
// Exit with status code 1 if the shutdown was initiated with SIGUSR1 to request a restart.
|
||||||
|
//
|
||||||
|
// Note that this requires an entrypoint script or other process to
|
||||||
|
// spawns a new instance when the server exists with status code 1.
|
||||||
if sig == syscall.SIGUSR1 {
|
if sig == syscall.SIGUSR1 {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
14
internal/server/fail.go
Normal file
14
internal/server/fail.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/photoprism/photoprism/internal/server/process"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fail logs an error and then initiates a server shutdown.
|
||||||
|
func Fail(err string, params ...interface{}) {
|
||||||
|
if err != "" {
|
||||||
|
log.Errorf(err, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
process.Shutdown()
|
||||||
|
}
|
||||||
27
internal/server/process/pid.go
Normal file
27
internal/server/process/pid.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package process
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/photoprism/photoprism/pkg/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ID is the process ID under which the server is running.
|
||||||
|
var ID = os.Getpid()
|
||||||
|
|
||||||
|
// WritePID writes the process ID to a file, if specified.
|
||||||
|
func WritePID(fileName string) error {
|
||||||
|
if fileName == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if pidDir := filepath.Dir(fileName); !fs.Writable(pidDir) {
|
||||||
|
return fmt.Errorf("%s is not writable", pidDir)
|
||||||
|
} else if err := fs.WriteString(fileName, fmt.Sprintf("%d", ID)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
14
internal/server/process/pid_test.go
Normal file
14
internal/server/process/pid_test.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package process
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestID(t *testing.T) {
|
||||||
|
t.Run("Matches", func(t *testing.T) {
|
||||||
|
assert.Equal(t, os.Getpid(), ID)
|
||||||
|
})
|
||||||
|
}
|
||||||
25
internal/server/process/process.go
Normal file
25
internal/server/process/process.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
Package process provides server process information and handling.
|
||||||
|
|
||||||
|
Copyright (c) 2018 - 2025 PhotoPrism UG. All rights reserved.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
|
||||||
|
<https://docs.photoprism.app/license/agpl>
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
The AGPL is supplemented by our Trademark and Brand Guidelines,
|
||||||
|
which describe how our Brand Assets may be used:
|
||||||
|
<https://www.photoprism.app/trademark>
|
||||||
|
|
||||||
|
Feel free to send an email to hello@photoprism.app if you have questions,
|
||||||
|
want to support our work, or just want to say hello.
|
||||||
|
|
||||||
|
Additional information can be found in our Developer Guide:
|
||||||
|
<https://docs.photoprism.app/developer-guide/>
|
||||||
|
*/
|
||||||
|
package process
|
||||||
22
internal/server/process/signal.go
Normal file
22
internal/server/process/signal.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package process
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Signal channel for initiating the server shutdown.
|
||||||
|
var Signal = make(chan os.Signal)
|
||||||
|
|
||||||
|
// Restart gracefully restarts the server.
|
||||||
|
//
|
||||||
|
// Note that this requires an entrypoint script or other process to
|
||||||
|
// spawns a new instance when the server exists with status code 1.
|
||||||
|
func Restart() {
|
||||||
|
Signal <- syscall.SIGUSR1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown gracefully stops the server.
|
||||||
|
func Shutdown() {
|
||||||
|
Signal <- syscall.SIGTERM
|
||||||
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Signal channel for initiating the shutdown.
|
|
||||||
var Signal = make(chan os.Signal)
|
|
||||||
|
|
||||||
// Fail reports an error and shuts down the server.
|
|
||||||
func Fail(err string, params ...interface{}) {
|
|
||||||
if err != "" {
|
|
||||||
log.Errorf(err, params...)
|
|
||||||
}
|
|
||||||
|
|
||||||
Shutdown()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown gracefully stops the server.
|
|
||||||
func Shutdown() {
|
|
||||||
Signal <- syscall.SIGINT
|
|
||||||
}
|
|
||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/photoprism/photoprism/internal/config"
|
"github.com/photoprism/photoprism/internal/config"
|
||||||
|
"github.com/photoprism/photoprism/internal/server/process"
|
||||||
"github.com/photoprism/photoprism/pkg/clean"
|
"github.com/photoprism/photoprism/pkg/clean"
|
||||||
"github.com/photoprism/photoprism/pkg/fs"
|
"github.com/photoprism/photoprism/pkg/fs"
|
||||||
"github.com/photoprism/photoprism/pkg/net/header"
|
"github.com/photoprism/photoprism/pkg/net/header"
|
||||||
@@ -33,6 +34,9 @@ func Start(ctx context.Context, conf *config.Config) {
|
|||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
|
// Log the server process ID for troubleshooting purposes.
|
||||||
|
log.Infof("server: started as pid %d", process.ID)
|
||||||
|
|
||||||
// Set web server mode.
|
// Set web server mode.
|
||||||
if conf.HttpMode() != "" {
|
if conf.HttpMode() != "" {
|
||||||
gin.SetMode(conf.HttpMode())
|
gin.SetMode(conf.HttpMode())
|
||||||
|
|||||||
2
scripts/dist/cmd.sh
vendored
2
scripts/dist/cmd.sh
vendored
@@ -88,7 +88,7 @@ echo "backup path...: ${PHOTOPRISM_BACKUP_PATH:-default}"
|
|||||||
echo "import path...: ${PHOTOPRISM_IMPORT_PATH:-default}"
|
echo "import path...: ${PHOTOPRISM_IMPORT_PATH:-default}"
|
||||||
echo "originals path: ${PHOTOPRISM_ORIGINALS_PATH:-default}"
|
echo "originals path: ${PHOTOPRISM_ORIGINALS_PATH:-default}"
|
||||||
|
|
||||||
# error code of the last executed command
|
# exit status code of the last command executed
|
||||||
ret=0
|
ret=0
|
||||||
|
|
||||||
# change to another user and group on request
|
# change to another user and group on request
|
||||||
|
|||||||
Reference in New Issue
Block a user