@@ -17,6 +17,7 @@ import (
17
17
"io/fs"
18
18
"io/ioutil"
19
19
"log"
20
+ "net"
20
21
"net/http"
21
22
"net/url"
22
23
"os"
@@ -32,12 +33,14 @@ import (
32
33
"tailscale.com/tsnet"
33
34
)
34
35
36
+ const defaultHostname = "go"
37
+
35
38
var (
36
39
verbose = flag .Bool ("verbose" , false , "be verbose" )
37
40
sqlitefile = flag .String ("sqlitedb" , "" , "path of SQLite database to store links" )
38
41
dev = flag .String ("dev-listen" , "" , "if non-empty, listen on this addr and run in dev mode; auto-set sqlitedb if empty and don't use tsnet" )
39
42
snapshot = flag .String ("snapshot" , "" , "file path of snapshot file" )
40
- hostname = flag .String ("hostname" , "go" , "service name" )
43
+ hostname = flag .String ("hostname" , defaultHostname , "service name" )
41
44
)
42
45
43
46
var stats struct {
@@ -52,7 +55,7 @@ var stats struct {
52
55
// that will be loaded on startup.
53
56
var LastSnapshot []byte
54
57
55
- //go:embed static tmpl/*.html
58
+ //go:embed static tmpl/*.html tmpl/*.xml
56
59
var embeddedFS embed.FS
57
60
58
61
// db stores short links.
@@ -106,9 +109,20 @@ func Run() error {
106
109
http .HandleFunc ("/.detail/" , serveDetail )
107
110
http .HandleFunc ("/.export" , serveExport )
108
111
http .HandleFunc ("/.help" , serveHelp )
112
+ http .HandleFunc ("/.opensearch" , serveOpenSearch )
109
113
http .Handle ("/.static/" , http .StripPrefix ("/." , http .FileServer (http .FS (embeddedFS ))))
110
114
111
115
if * dev != "" {
116
+ // override default hostname for dev mode
117
+ if * hostname == defaultHostname {
118
+ if h , p , err := net .SplitHostPort (* dev ); err == nil {
119
+ if h == "" {
120
+ h = "localhost"
121
+ }
122
+ * hostname = fmt .Sprintf ("%s:%s" , h , p )
123
+ }
124
+ }
125
+
112
126
log .Printf ("Running in dev mode on %s ..." , * dev )
113
127
log .Fatal (http .ListenAndServe (* dev , nil ))
114
128
}
@@ -154,6 +168,9 @@ var (
154
168
155
169
// helpTmpl is the template used by the http://go/.help page
156
170
helpTmpl * template.Template
171
+
172
+ // opensearchTmpl is the template used by the http://go/.opensearch page
173
+ opensearchTmpl * template.Template
157
174
)
158
175
159
176
type visitData struct {
@@ -172,6 +189,7 @@ func init() {
172
189
detailTmpl = template .Must (template .ParseFS (embeddedFS , "tmpl/base.html" , "tmpl/detail.html" ))
173
190
successTmpl = template .Must (template .ParseFS (embeddedFS , "tmpl/base.html" , "tmpl/success.html" ))
174
191
helpTmpl = template .Must (template .ParseFS (embeddedFS , "tmpl/base.html" , "tmpl/help.html" ))
192
+ opensearchTmpl = template .Must (template .ParseFS (embeddedFS , "tmpl/opensearch.xml" ))
175
193
}
176
194
177
195
// initStats initializes the in-memory stats counter with counts from db.
@@ -244,6 +262,15 @@ func serveHelp(w http.ResponseWriter, _ *http.Request) {
244
262
helpTmpl .Execute (w , nil )
245
263
}
246
264
265
+ func serveOpenSearch (w http.ResponseWriter , _ * http.Request ) {
266
+ type opensearchData struct {
267
+ Hostname string
268
+ }
269
+
270
+ w .Header ().Set ("Content-Type" , "application/opensearchdescription+xml" )
271
+ opensearchTmpl .Execute (w , opensearchData {Hostname : * hostname })
272
+ }
273
+
247
274
func serveGo (w http.ResponseWriter , r * http.Request ) {
248
275
if r .RequestURI == "/" {
249
276
switch r .Method {
0 commit comments