/
start.go
173 lines (140 loc) Β· 4.39 KB
/
start.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package commands
import (
"context"
"fmt"
"os"
"os/signal"
"strconv"
"syscall"
"time"
"github.com/sevlyar/go-daemon"
"github.com/urfave/cli"
"github.com/photoprism/photoprism/internal/auto"
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/server"
"github.com/photoprism/photoprism/internal/session"
"github.com/photoprism/photoprism/internal/workers"
"github.com/photoprism/photoprism/pkg/clean"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/report"
)
// StartCommand configures the command name, flags, and action.
var StartCommand = cli.Command{
Name: "start",
Aliases: []string{"up"},
Usage: "Starts the Web server",
Flags: startFlags,
Action: startAction,
}
// startFlags specifies the start command parameters.
var startFlags = []cli.Flag{
cli.BoolFlag{
Name: "detach-server, d",
Usage: "detach from the console (daemon mode)",
EnvVar: "PHOTOPRISM_DETACH_SERVER",
},
cli.BoolFlag{
Name: "config, c",
Usage: "show config",
},
}
// startAction starts the Web server and initializes the daemon.
func startAction(ctx *cli.Context) error {
conf, err := InitConfig(ctx)
if err != nil {
return err
}
if ctx.IsSet("config") {
// Create config report.
cols := []string{"Name", "Value"}
rows := [][]string{
{"detach-server", fmt.Sprintf("%t", conf.DetachServer())},
{"http-mode", conf.HttpMode()},
{"http-compression", conf.HttpCompression()},
{"http-cache-public", fmt.Sprintf("%t", conf.HttpCachePublic())},
{"http-cache-maxage", fmt.Sprintf("%d", conf.HttpCacheMaxAge())},
{"http-video-maxage", fmt.Sprintf("%d", conf.HttpVideoMaxAge())},
{"http-host", conf.HttpHost()},
{"http-port", fmt.Sprintf("%d", conf.HttpPort())},
}
// Render config report.
opt := report.Options{Format: report.CliFormat(ctx), NoWrap: true}
result, _ := report.Render(rows, cols, opt)
fmt.Printf("\n%s\n", result)
return nil
}
if conf.HttpPort() < 1 || conf.HttpPort() > 65535 {
log.Fatal("server port must be a number between 1 and 65535")
}
// Pass this context down the chain.
cctx, cancel := context.WithCancel(context.Background())
// Initialize the index database.
conf.InitDb()
// Check if the daemon is running, if not, initialize the daemon.
dctx := new(daemon.Context)
dctx.LogFileName = conf.LogFilename()
dctx.PidFileName = conf.PIDFilename()
dctx.Args = ctx.Args()
if !daemon.WasReborn() && conf.DetachServer() {
conf.Shutdown()
cancel()
if pid, ok := childAlreadyRunning(conf.PIDFilename()); ok {
log.Infof("daemon already running with process id %v\n", pid)
return nil
}
child, contextErr := dctx.Reborn()
if contextErr != nil {
return fmt.Errorf("daemon context error: %w", contextErr)
}
if child != nil {
if writeErr := fs.WriteString(conf.PIDFilename(), strconv.Itoa(child.Pid)); writeErr != nil {
log.Error(writeErr)
return fmt.Errorf("failed writing process id to %s", clean.Log(conf.PIDFilename()))
}
log.Infof("daemon started with process id %v\n", child.Pid)
return nil
}
}
// Show info if read-only mode is enabled.
if conf.ReadOnly() {
log.Infof("config: enabled read-only mode")
}
// Start built-in web server.
go server.Start(cctx, conf)
// Restore albums from YAML files.
if count, restoreErr := photoprism.RestoreAlbums(conf.AlbumsPath(), false); restoreErr != nil {
log.Errorf("restore: %s", restoreErr)
} else if count > 0 {
log.Infof("%d albums restored", count)
}
// Start worker that periodically deletes expired sessions.
session.Cleanup(conf.SessionCacheDuration() * 4)
// Start sync and metadata maintenance background workers.
workers.Start(conf)
// Start auto-indexing background worker.
auto.Start(conf)
// Wait for signal to initiate server shutdown.
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1)
sig := <-quit
// Stop all background activity.
auto.Stop()
workers.Stop()
session.Shutdown()
mutex.CancelAll()
log.Info("shutting down...")
cancel()
if contextErr := dctx.Release(); contextErr != nil {
log.Error(contextErr)
}
// Finally, close the DB connection after a short grace period.
time.Sleep(2 * time.Second)
conf.Shutdown()
// Don't exit with 0 if SIGUSR1 was received to avoid restarts.
if sig == syscall.SIGUSR1 {
os.Exit(1)
return nil
}
return nil
}