@@ -24,25 +24,60 @@ import (
24
24
"time"
25
25
)
26
26
27
- // Start starts the Shifu server for given directory.
27
+ // ServerOptions are the options for the Shifu Server.
28
+ type ServerOptions struct {
29
+ // FuncMap will be merged with the default Shifu template function map.
30
+ FuncMap template.FuncMap
31
+ }
32
+
33
+ // Server is the Shifu server.
34
+ type Server struct {
35
+ // Content is the CMS content.
36
+ Content * cms.CMS
37
+
38
+ // Router is the router for the server.
39
+ // It has a CORS and gzip middleware, as well as a route for robots.txt, the static directory, and any unmatched path (/*) by default.
40
+ Router chi.Router
41
+
42
+ dir string
43
+ funcMap template.FuncMap
44
+ }
45
+
46
+ // NewServer creates a new Shifu server for given directory.
28
47
// The second argument is an optional template.FuncMap that will be merged with Shifu's funcmap.
29
- func Start (dir string , funcMap template.FuncMap ) error {
30
- slog .Info ("Starting Shifu" , "version" , version , "directory" , dir )
31
- funcMap = cms .Merge (funcMap )
32
- ctx , cancel := context .WithCancel (context .Background ())
48
+ func NewServer (dir string , options ServerOptions ) * Server {
49
+ return & Server {
50
+ dir : dir ,
51
+ funcMap : options .FuncMap ,
52
+ }
53
+ }
33
54
34
- if err := cfg .Watch (ctx , dir , funcMap ); err != nil {
35
- cancel ()
55
+ // Start starts the Shifu server.
56
+ // The context.CancelFunc is optional and will be called on server shutdown or error if set.
57
+ func (server * Server ) Start (cancel context.CancelFunc ) error {
58
+ slog .Info ("Starting Shifu" , "version" , version , "directory" , server .dir )
59
+ server .funcMap = cms .Merge (server .funcMap )
60
+ ctx , cancelServer := context .WithCancel (context .Background ())
61
+ stop := func () {
62
+ cancelServer ()
63
+
64
+ if cancel != nil {
65
+ cancel ()
66
+ }
67
+ }
68
+
69
+ if err := cfg .Watch (ctx , server .dir , server .funcMap ); err != nil {
70
+ stop ()
36
71
return err
37
72
}
38
73
39
- if err := sass .Watch (ctx , dir ); err != nil {
40
- cancel ()
74
+ if err := sass .Watch (ctx , server . dir ); err != nil {
75
+ stop ()
41
76
return err
42
77
}
43
78
44
- if err := js .Watch (ctx , dir ); err != nil {
45
- cancel ()
79
+ if err := js .Watch (ctx , server . dir ); err != nil {
80
+ stop ()
46
81
return err
47
82
}
48
83
@@ -51,45 +86,45 @@ func Start(dir string, funcMap template.FuncMap) error {
51
86
52
87
switch strings .ToLower (config .Provider ) {
53
88
case "fs" :
54
- provider = source .NewFS (dir , config .UpdateSeconds )
89
+ provider = source .NewFS (server . dir , config .UpdateSeconds )
55
90
break
56
91
case "git" :
57
- provider = source .NewGit (dir , config .Repository , config .UpdateSeconds )
92
+ provider = source .NewGit (server . dir , config .Repository , config .UpdateSeconds )
58
93
break
59
94
default :
60
- cancel ()
95
+ stop ()
61
96
return errors .New ("content provider not found" )
62
97
}
63
98
64
99
sm := sitemap .New ()
65
- content : = cms .NewCMS (cms.Options {
100
+ server . Content = cms .NewCMS (cms.Options {
66
101
Ctx : ctx ,
67
- BaseDir : dir ,
102
+ BaseDir : server . dir ,
68
103
HotReload : cfg .Get ().Dev ,
69
- FuncMap : funcMap ,
104
+ FuncMap : server . funcMap ,
70
105
Source : provider ,
71
106
Sitemap : sm ,
72
107
})
73
108
analytics .Init ()
74
- router := setupRouter (dir , content , sm )
75
- <- startServer (router , cancel )
109
+ server . Router = server . setupRouter (server . dir , server . Content , sm )
110
+ <- server . startServer (server . Router , stop )
76
111
return nil
77
112
}
78
113
79
- func setupRouter (dir string , cms * cms.CMS , sm * sitemap.Sitemap ) chi.Router {
114
+ func ( server * Server ) setupRouter (dir string , cms * cms.CMS , sm * sitemap.Sitemap ) chi.Router {
80
115
router := chi .NewRouter ()
81
116
router .Use (
82
117
middleware .Cors (),
83
118
middleware .Gzip (),
84
119
)
85
120
sm .Serve (router )
86
- serveRobotsTxt (router )
87
- serveStaticDir (router , dir )
121
+ server . serveRobotsTxt (router )
122
+ server . serveStaticDir (router , dir )
88
123
router .Handle ("/*" , http .HandlerFunc (cms .Serve ))
89
124
return router
90
125
}
91
126
92
- func serveRobotsTxt (router chi.Router ) {
127
+ func ( server * Server ) serveRobotsTxt (router chi.Router ) {
93
128
robotsTxt := fmt .Sprintf ("User-agent: *\n Disallow:\n \n Sitemap: %s/sitemap.xml\n " , cfg .Get ().Server .Hostname )
94
129
router .Handle ("/robots.txt" , http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
95
130
w .Header ().Add ("Content-Type" , "text/plain" )
@@ -101,18 +136,18 @@ func serveRobotsTxt(router chi.Router) {
101
136
}))
102
137
}
103
138
104
- func serveStaticDir (router chi.Router , dir string ) {
139
+ func ( server * Server ) serveStaticDir (router chi.Router , dir string ) {
105
140
fs := http .StripPrefix ("/static/" , http .FileServer (http .Dir (filepath .Join (dir , "static" ))))
106
141
router .Handle ("/static/*" , gzhttp .GzipHandler (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
107
142
fs .ServeHTTP (w , r )
108
143
})))
109
144
}
110
145
111
- func startServer (handler http.Handler , cancel context.CancelFunc ) chan struct {} {
146
+ func ( server * Server ) startServer (handler http.Handler , cancel context.CancelFunc ) chan struct {} {
112
147
slog .Info ("Starting server..." )
113
148
config := cfg .Get ()
114
149
addr := fmt .Sprintf ("%s:%d" , config .Server .Host , config .Server .Port )
115
- server := & http.Server {
150
+ httpServer := & http.Server {
116
151
Handler : handler ,
117
152
Addr : addr ,
118
153
WriteTimeout : time .Second * time .Duration (config .Server .WriteTimeout ),
@@ -127,7 +162,7 @@ func startServer(handler http.Handler, cancel context.CancelFunc) chan struct{}
127
162
cancel ()
128
163
ctx , cancel := context .WithTimeout (context .Background (), time .Second * time .Duration (config .Server .ShutdownTimeout ))
129
164
130
- if err := server .Shutdown (ctx ); err != nil {
165
+ if err := httpServer .Shutdown (ctx ); err != nil {
131
166
slog .Error ("Error shutting down server gracefully" , "error" , err )
132
167
panic (err )
133
168
}
@@ -139,12 +174,12 @@ func startServer(handler http.Handler, cancel context.CancelFunc) chan struct{}
139
174
140
175
go func () {
141
176
if config .Server .TLSCertFile != "" && config .Server .TLSKeyFile != "" {
142
- if err := server .ListenAndServeTLS (config .Server .TLSCertFile , config .Server .TLSKeyFile ); err != nil {
177
+ if err := httpServer .ListenAndServeTLS (config .Server .TLSCertFile , config .Server .TLSKeyFile ); err != nil {
143
178
slog .Error ("Error starting server" , "error" , err )
144
179
panic (err )
145
180
}
146
181
} else {
147
- if err := server .ListenAndServe (); err != nil && err != http .ErrServerClosed {
182
+ if err := httpServer .ListenAndServe (); err != nil && err != http .ErrServerClosed {
148
183
slog .Error ("Error starting server" , "error" , err )
149
184
panic (err )
150
185
}
0 commit comments