diff options
| -rw-r--r-- | cmd/gopherproxy/main.go | 3 | ||||
| -rw-r--r-- | gopherproxy.go | 97 | ||||
| -rw-r--r-- | startpage.txt | 1 | ||||
| -rw-r--r-- | template.go | 229 |
4 files changed, 187 insertions, 143 deletions
diff --git a/cmd/gopherproxy/main.go b/cmd/gopherproxy/main.go index 66248ac..e84e978 100644 --- a/cmd/gopherproxy/main.go +++ b/cmd/gopherproxy/main.go | |||
| @@ -13,7 +13,6 @@ var ( | |||
| 13 | bind = flag.String("bind", "0.0.0.0:8000", "[int]:port to bind to") | 13 | bind = flag.String("bind", "0.0.0.0:8000", "[int]:port to bind to") |
| 14 | robotsfile = flag.String("robots-file", "robots.txt", "robots.txt file") | 14 | robotsfile = flag.String("robots-file", "robots.txt", "robots.txt file") |
| 15 | robotsdebug = flag.Bool("robots-debug", false, "print output about ignored robots.txt") | 15 | robotsdebug = flag.Bool("robots-debug", false, "print output about ignored robots.txt") |
| 16 | uri = flag.String("uri", "gopher/floodgap.com", "<gopher|gemini>/<host>:[port] to proxy to") | ||
| 17 | vipsconcurrency = flag.Int("vips-concurrency", 1, "Concurrency level of libvips") | 16 | vipsconcurrency = flag.Int("vips-concurrency", 1, "Concurrency level of libvips") |
| 18 | ) | 17 | ) |
| 19 | 18 | ||
| @@ -21,5 +20,5 @@ func main() { | |||
| 21 | flag.Parse() | 20 | flag.Parse() |
| 22 | 21 | ||
| 23 | // Use a config struct | 22 | // Use a config struct |
| 24 | log.Fatal(gopherproxy.ListenAndServe(*bind, *robotsfile, *robotsdebug, *vipsconcurrency, *uri)) | 23 | log.Fatal(gopherproxy.ListenAndServe(*bind, *robotsfile, *robotsdebug, *vipsconcurrency)) |
| 25 | } | 24 | } |
diff --git a/gopherproxy.go b/gopherproxy.go index 751cebf..2a6358c 100644 --- a/gopherproxy.go +++ b/gopherproxy.go | |||
| @@ -177,9 +177,19 @@ func parseGeminiDocument(body *bytes.Buffer, uri string, hostport string) (items | |||
| 177 | return | 177 | return |
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | func DefaultHandler(tpl *template.Template, uri string) http.HandlerFunc { | 180 | func DefaultHandler(tpl *template.Template, startpagetext string, assetList AssetList) http.HandlerFunc { |
| 181 | return func(w http.ResponseWriter, req *http.Request) { | 181 | return func(w http.ResponseWriter, req *http.Request) { |
| 182 | http.Redirect(w, req, "/"+uri, http.StatusFound) | 182 | if err := tpl.Execute(w, struct { |
| 183 | Title string | ||
| 184 | URI string | ||
| 185 | Assets AssetList | ||
| 186 | RawText string | ||
| 187 | Lines []Item | ||
| 188 | Error bool | ||
| 189 | Protocol string | ||
| 190 | }{"Gopher/Gemini proxy", "", assetList, startpagetext, nil, false, "startpage"}); err != nil { | ||
| 191 | log.Println("Template error: " + err.Error()) | ||
| 192 | } | ||
| 183 | } | 193 | } |
| 184 | } | 194 | } |
| 185 | 195 | ||
| @@ -188,7 +198,7 @@ func DefaultHandler(tpl *template.Template, uri string) http.HandlerFunc { | |||
| 188 | // to the request path and renders the content using the provided template. | 198 | // to the request path and renders the content using the provided template. |
| 189 | // The optional robots parameters points to a robotstxt.RobotsData struct | 199 | // The optional robots parameters points to a robotstxt.RobotsData struct |
| 190 | // to test user agents against a configurable robotst.txt file. | 200 | // to test user agents against a configurable robotst.txt file. |
| 191 | func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, assetList AssetList, robotsdebug bool, uri string) http.HandlerFunc { | 201 | func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, assetList AssetList, robotsdebug bool) http.HandlerFunc { |
| 192 | return func(w http.ResponseWriter, req *http.Request) { | 202 | return func(w http.ResponseWriter, req *http.Request) { |
| 193 | agent := req.UserAgent() | 203 | agent := req.UserAgent() |
| 194 | path := strings.TrimPrefix(req.URL.Path, "/gopher/") | 204 | path := strings.TrimPrefix(req.URL.Path, "/gopher/") |
| @@ -201,7 +211,7 @@ func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 201 | hostport := parts[0] | 211 | hostport := parts[0] |
| 202 | 212 | ||
| 203 | if len(hostport) == 0 { | 213 | if len(hostport) == 0 { |
| 204 | http.Redirect(w, req, "/"+uri, http.StatusFound) | 214 | http.Redirect(w, req, "/", http.StatusFound) |
| 205 | return | 215 | return |
| 206 | } | 216 | } |
| 207 | 217 | ||
| @@ -215,7 +225,7 @@ func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 215 | 225 | ||
| 216 | uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) | 226 | uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) |
| 217 | if err != nil { | 227 | if err != nil { |
| 218 | tpl.Execute(w, struct { | 228 | if e := tpl.Execute(w, struct { |
| 219 | Title string | 229 | Title string |
| 220 | URI string | 230 | URI string |
| 221 | Assets AssetList | 231 | Assets AssetList |
| @@ -223,7 +233,10 @@ func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 223 | Lines []Item | 233 | Lines []Item |
| 224 | Error bool | 234 | Error bool |
| 225 | Protocol string | 235 | Protocol string |
| 226 | }{title, hostport, assetList, fmt.Sprintf("Error: %s", err), nil, true, "gopher"}) | 236 | }{title, hostport, assetList, fmt.Sprintf("Error: %s", err), nil, true, "gopher"}); e != nil { |
| 237 | log.Println("Template error: " + e.Error()) | ||
| 238 | log.Println(err.Error()) | ||
| 239 | } | ||
| 227 | return | 240 | return |
| 228 | } | 241 | } |
| 229 | 242 | ||
| @@ -241,7 +254,7 @@ func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 241 | ) | 254 | ) |
| 242 | 255 | ||
| 243 | if err != nil { | 256 | if err != nil { |
| 244 | tpl.Execute(w, struct { | 257 | if e := tpl.Execute(w, struct { |
| 245 | Title string | 258 | Title string |
| 246 | URI string | 259 | URI string |
| 247 | Assets AssetList | 260 | Assets AssetList |
| @@ -249,7 +262,9 @@ func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 249 | Lines []Item | 262 | Lines []Item |
| 250 | Error bool | 263 | Error bool |
| 251 | Protocol string | 264 | Protocol string |
| 252 | }{title, fmt.Sprintf("%s/%s", hostport, uri), assetList, fmt.Sprintf("Error: %s", err), nil, true, "gopher"}) | 265 | }{title, fmt.Sprintf("%s/%s", hostport, uri), assetList, fmt.Sprintf("Error: %s", err), nil, true, "gopher"}); e != nil { |
| 266 | log.Println("Template error: " + e.Error()) | ||
| 267 | } | ||
| 253 | return | 268 | return |
| 254 | } | 269 | } |
| 255 | 270 | ||
| @@ -259,7 +274,7 @@ func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 259 | } else if strings.HasPrefix(parts[1], "0") && !strings.HasSuffix(uri, ".xml") && !strings.HasSuffix(uri, ".asc") { | 274 | } else if strings.HasPrefix(parts[1], "0") && !strings.HasSuffix(uri, ".xml") && !strings.HasSuffix(uri, ".asc") { |
| 260 | buf := new(bytes.Buffer) | 275 | buf := new(bytes.Buffer) |
| 261 | buf.ReadFrom(res.Body) | 276 | buf.ReadFrom(res.Body) |
| 262 | tpl.Execute(w, struct { | 277 | if err := tpl.Execute(w, struct { |
| 263 | Title string | 278 | Title string |
| 264 | URI string | 279 | URI string |
| 265 | Assets AssetList | 280 | Assets AssetList |
| @@ -267,7 +282,9 @@ func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 267 | Lines []Item | 282 | Lines []Item |
| 268 | Error bool | 283 | Error bool |
| 269 | Protocol string | 284 | Protocol string |
| 270 | }{title, fmt.Sprintf("%s/%s", hostport, uri), assetList, buf.String(), nil, false, "gopher"}) | 285 | }{title, fmt.Sprintf("%s/%s", hostport, uri), assetList, buf.String(), nil, false, "gopher"}); err != nil { |
| 286 | log.Println("Template error: " + err.Error()) | ||
| 287 | } | ||
| 271 | } else if strings.HasPrefix(parts[1], "T") { | 288 | } else if strings.HasPrefix(parts[1], "T") { |
| 272 | _, _, err = vips.NewTransform(). | 289 | _, _, err = vips.NewTransform(). |
| 273 | Load(res.Body). | 290 | Load(res.Body). |
| @@ -281,7 +298,7 @@ func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 281 | } | 298 | } |
| 282 | } else { | 299 | } else { |
| 283 | if err := renderGopherDirectory(w, tpl, assetList, uri, hostport, res.Dir); err != nil { | 300 | if err := renderGopherDirectory(w, tpl, assetList, uri, hostport, res.Dir); err != nil { |
| 284 | tpl.Execute(w, struct { | 301 | if e := tpl.Execute(w, struct { |
| 285 | Title string | 302 | Title string |
| 286 | URI string | 303 | URI string |
| 287 | Assets AssetList | 304 | Assets AssetList |
| @@ -289,13 +306,16 @@ func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 289 | Lines []Item | 306 | Lines []Item |
| 290 | Error bool | 307 | Error bool |
| 291 | Protocol string | 308 | Protocol string |
| 292 | }{title, fmt.Sprintf("%s/%s", hostport, uri), assetList, fmt.Sprintf("Error: %s", err), nil, true, "gopher"}) | 309 | }{title, fmt.Sprintf("%s/%s", hostport, uri), assetList, fmt.Sprintf("Error: %s", err), nil, true, "gopher"}); e != nil { |
| 310 | log.Println("Template error: " + e.Error()) | ||
| 311 | log.Println(e.Error()) | ||
| 312 | } | ||
| 293 | } | 313 | } |
| 294 | } | 314 | } |
| 295 | } | 315 | } |
| 296 | } | 316 | } |
| 297 | 317 | ||
| 298 | func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, assetList AssetList, robotsdebug bool, uri string) http.HandlerFunc { | 318 | func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, assetList AssetList, robotsdebug bool) http.HandlerFunc { |
| 299 | return func(w http.ResponseWriter, req *http.Request) { | 319 | return func(w http.ResponseWriter, req *http.Request) { |
| 300 | agent := req.UserAgent() | 320 | agent := req.UserAgent() |
| 301 | path := strings.TrimPrefix(req.URL.Path, "/gemini/") | 321 | path := strings.TrimPrefix(req.URL.Path, "/gemini/") |
| @@ -308,7 +328,7 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 308 | hostport := parts[0] | 328 | hostport := parts[0] |
| 309 | 329 | ||
| 310 | if len(hostport) == 0 { | 330 | if len(hostport) == 0 { |
| 311 | http.Redirect(w, req, "/"+uri, http.StatusFound) | 331 | http.Redirect(w, req, "/", http.StatusFound) |
| 312 | return | 332 | return |
| 313 | } | 333 | } |
| 314 | 334 | ||
| @@ -322,7 +342,7 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 322 | 342 | ||
| 323 | uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) | 343 | uri, err := url.QueryUnescape(strings.Join(parts[1:], "/")) |
| 324 | if err != nil { | 344 | if err != nil { |
| 325 | tpl.Execute(w, struct { | 345 | if e := tpl.Execute(w, struct { |
| 326 | Title string | 346 | Title string |
| 327 | URI string | 347 | URI string |
| 328 | Assets AssetList | 348 | Assets AssetList |
| @@ -330,7 +350,10 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 330 | Lines []Item | 350 | Lines []Item |
| 331 | Error bool | 351 | Error bool |
| 332 | Protocol string | 352 | Protocol string |
| 333 | }{title, hostport, assetList, fmt.Sprintf("Error: %s", err), nil, true, "gemini"}) | 353 | }{title, hostport, assetList, fmt.Sprintf("Error: %s", err), nil, true, "gemini"}); e != nil { |
| 354 | log.Println("Template error: " + e.Error()) | ||
| 355 | log.Println(err.Error()) | ||
| 356 | } | ||
| 334 | return | 357 | return |
| 335 | } | 358 | } |
| 336 | 359 | ||
| @@ -348,7 +371,7 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 348 | ) | 371 | ) |
| 349 | 372 | ||
| 350 | if err != nil { | 373 | if err != nil { |
| 351 | tpl.Execute(w, struct { | 374 | if e := tpl.Execute(w, struct { |
| 352 | Title string | 375 | Title string |
| 353 | URI string | 376 | URI string |
| 354 | Assets AssetList | 377 | Assets AssetList |
| @@ -356,7 +379,10 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 356 | Lines []Item | 379 | Lines []Item |
| 357 | Error bool | 380 | Error bool |
| 358 | Protocol string | 381 | Protocol string |
| 359 | }{title, fmt.Sprintf("%s/%s", hostport, uri), assetList, fmt.Sprintf("Error: %s", err), nil, true, "gemini"}) | 382 | }{title, fmt.Sprintf("%s/%s", hostport, uri), assetList, fmt.Sprintf("Error: %s", err), nil, true, "gemini"}); e != nil { |
| 383 | log.Println("Template error: " + e.Error()) | ||
| 384 | log.Println(err.Error()) | ||
| 385 | } | ||
| 360 | return | 386 | return |
| 361 | } | 387 | } |
| 362 | 388 | ||
| @@ -367,7 +393,7 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 367 | uri, | 393 | uri, |
| 368 | )) | 394 | )) |
| 369 | if err != nil { | 395 | if err != nil { |
| 370 | tpl.Execute(w, struct { | 396 | if e := tpl.Execute(w, struct { |
| 371 | Title string | 397 | Title string |
| 372 | URI string | 398 | URI string |
| 373 | Assets AssetList | 399 | Assets AssetList |
| @@ -375,7 +401,10 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 375 | Lines []Item | 401 | Lines []Item |
| 376 | Error bool | 402 | Error bool |
| 377 | Protocol string | 403 | Protocol string |
| 378 | }{title, fmt.Sprintf("%s/%s", hostport, uri), assetList, fmt.Sprintf("Error: %s", err), nil, true, "gemini"}) | 404 | }{title, fmt.Sprintf("%s/%s", hostport, uri), assetList, fmt.Sprintf("Error: %s", err), nil, true, "gemini"}); e != nil { |
| 405 | log.Println("Template error: " + e.Error()) | ||
| 406 | log.Println(err.Error()) | ||
| 407 | } | ||
| 379 | return | 408 | return |
| 380 | } | 409 | } |
| 381 | 410 | ||
| @@ -384,7 +413,7 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 384 | } | 413 | } |
| 385 | 414 | ||
| 386 | if int(res.Header.Status/10) != 2 { | 415 | if int(res.Header.Status/10) != 2 { |
| 387 | tpl.Execute(w, struct { | 416 | if err := tpl.Execute(w, struct { |
| 388 | Title string | 417 | Title string |
| 389 | URI string | 418 | URI string |
| 390 | Assets AssetList | 419 | Assets AssetList |
| @@ -392,7 +421,9 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 392 | Lines []Item | 421 | Lines []Item |
| 393 | Error bool | 422 | Error bool |
| 394 | Protocol string | 423 | Protocol string |
| 395 | }{title, fmt.Sprintf("%s/%s", hostport, uri), assetList, fmt.Sprintf("Error %d: %s", res.Header.Status, res.Header.Meta), nil, true, "gemini"}) | 424 | }{title, fmt.Sprintf("%s/%s", hostport, uri), assetList, fmt.Sprintf("Error %d: %s", res.Header.Status, res.Header.Meta), nil, true, "gemini"}); err != nil { |
| 425 | log.Println("Template error: " + err.Error()) | ||
| 426 | } | ||
| 396 | return | 427 | return |
| 397 | } | 428 | } |
| 398 | 429 | ||
| @@ -423,7 +454,7 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 423 | rawText = buf.String() | 454 | rawText = buf.String() |
| 424 | } | 455 | } |
| 425 | 456 | ||
| 426 | tpl.Execute(w, struct { | 457 | if err := tpl.Execute(w, struct { |
| 427 | Title string | 458 | Title string |
| 428 | URI string | 459 | URI string |
| 429 | Assets AssetList | 460 | Assets AssetList |
| @@ -431,7 +462,9 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass | |||
| 431 | Lines []Item | 462 | Lines []Item |
| 432 | Error bool | 463 | Error bool |
| 433 | Protocol string | 464 | Protocol string |
| 434 | }{title, fmt.Sprintf("%s/%s", hostport, uri), assetList, rawText, items, false, "gemini"}) | 465 | }{title, fmt.Sprintf("%s/%s", hostport, uri), assetList, rawText, items, false, "gemini"}); err != nil { |
| 466 | log.Println("Template error: " + err.Error()) | ||
| 467 | } | ||
| 435 | } else { | 468 | } else { |
| 436 | io.Copy(w, res.Body) | 469 | io.Copy(w, res.Body) |
| 437 | } | 470 | } |
| @@ -506,7 +539,7 @@ func FontHandler(woff2 bool, fontdata []byte) http.HandlerFunc { | |||
| 506 | // specified by the request. The robots argument is a pointer to | 539 | // specified by the request. The robots argument is a pointer to |
| 507 | // a robotstxt.RobotsData struct for testing user agents against | 540 | // a robotstxt.RobotsData struct for testing user agents against |
| 508 | // a configurable robots.txt file. | 541 | // a configurable robots.txt file. |
| 509 | func ListenAndServe(bind, robotsfile string, robotsdebug bool, vipsconcurrency int, uri string) error { | 542 | func ListenAndServe(bind, robotsfile string, robotsdebug bool, vipsconcurrency int) error { |
| 510 | var ( | 543 | var ( |
| 511 | tpl *template.Template | 544 | tpl *template.Template |
| 512 | robotsdata *robotstxt.RobotsData | 545 | robotsdata *robotstxt.RobotsData |
| @@ -567,6 +600,12 @@ func ListenAndServe(bind, robotsfile string, robotsdebug bool, vipsconcurrency i | |||
| 567 | favicondata = []byte{} | 600 | favicondata = []byte{} |
| 568 | } | 601 | } |
| 569 | 602 | ||
| 603 | startpagedata, err := ioutil.ReadFile("startpage.txt") | ||
| 604 | if err != nil { | ||
| 605 | startpagedata = []byte{} | ||
| 606 | } | ||
| 607 | startpagetext := string(startpagedata) | ||
| 608 | |||
| 570 | tpldata, err := ioutil.ReadFile(".template") | 609 | tpldata, err := ioutil.ReadFile(".template") |
| 571 | if err == nil { | 610 | if err == nil { |
| 572 | tpltext = string(tpldata) | 611 | tpltext = string(tpldata) |
| @@ -622,9 +661,11 @@ func ListenAndServe(bind, robotsfile string, robotsdebug bool, vipsconcurrency i | |||
| 622 | ConcurrencyLevel: vipsconcurrency, | 661 | ConcurrencyLevel: vipsconcurrency, |
| 623 | }) | 662 | }) |
| 624 | 663 | ||
| 625 | http.Handle("/", gziphandler.GzipHandler(DefaultHandler(tpl, uri))) | 664 | assets := AssetList{styleAsset, jsAsset, fontwAsset, fontw2Asset, propfontwAsset, propfontw2Asset} |
| 626 | http.Handle("/gopher/", gziphandler.GzipHandler(GopherHandler(tpl, robotsdata, AssetList{styleAsset, jsAsset, fontwAsset, fontw2Asset, propfontwAsset, propfontw2Asset}, robotsdebug, uri))) | 665 | |
| 627 | http.Handle("/gemini/", gziphandler.GzipHandler(GeminiHandler(tpl, robotsdata, AssetList{styleAsset, jsAsset, fontwAsset, fontw2Asset, propfontwAsset, propfontw2Asset}, robotsdebug, uri))) | 666 | http.Handle("/", gziphandler.GzipHandler(DefaultHandler(tpl, startpagetext, assets))) |
| 667 | http.Handle("/gopher/", gziphandler.GzipHandler(GopherHandler(tpl, robotsdata, assets, robotsdebug))) | ||
| 668 | http.Handle("/gemini/", gziphandler.GzipHandler(GeminiHandler(tpl, robotsdata, assets, robotsdebug))) | ||
| 628 | http.Handle("/robots.txt", gziphandler.GzipHandler(RobotsTxtHandler(robotstxtdata))) | 669 | http.Handle("/robots.txt", gziphandler.GzipHandler(RobotsTxtHandler(robotstxtdata))) |
| 629 | http.Handle("/favicon.ico", gziphandler.GzipHandler(FaviconHandler(favicondata))) | 670 | http.Handle("/favicon.ico", gziphandler.GzipHandler(FaviconHandler(favicondata))) |
| 630 | http.Handle(styleAsset, gziphandler.GzipHandler(StyleHandler(styledata))) | 671 | http.Handle(styleAsset, gziphandler.GzipHandler(StyleHandler(styledata))) |
diff --git a/startpage.txt b/startpage.txt new file mode 100644 index 0000000..b411de6 --- /dev/null +++ b/startpage.txt | |||
| @@ -0,0 +1 @@ | |||
| >>>> Gopher/Gemini proxy <<<< | |||
diff --git a/template.go b/template.go index 3bd8950..7ddc460 100644 --- a/template.go +++ b/template.go | |||
| @@ -2,118 +2,121 @@ package gopherproxy | |||
| 2 | 2 | ||
| 3 | var tpltext = `<!doctype html> | 3 | var tpltext = `<!doctype html> |
| 4 | <html> | 4 | <html> |
| 5 | <head> | 5 | <head> |
| 6 | <meta charset="utf-8"> | 6 | <meta charset="utf-8"> |
| 7 | <meta name="viewport" content="width=device-width, initial-scale=1" /> | 7 | <meta name="viewport" content="width=device-width, initial-scale=1" /> |
| 8 | <title>{{ .Title }} - {{ .Protocol | title }} proxy</title> | 8 | <title>{{ .Title }} - {{ .Protocol | title }} proxy</title> |
| 9 | <link rel="stylesheet" href="{{ .Assets.Style }}" /> | 9 | <link rel="stylesheet" href="{{ .Assets.Style }}" /> |
| 10 | <style> | 10 | <style> |
| 11 | @font-face { | 11 | @font-face { |
| 12 | font-family: 'Iosevka Term SS03'; | 12 | font-family: 'Iosevka Term SS03'; |
| 13 | font-style: normal; | 13 | font-style: normal; |
| 14 | font-weight: normal; | 14 | font-weight: normal; |
| 15 | src: url('{{ .Assets.FontW2 }}') format('woff2'), | 15 | src: url('{{ .Assets.FontW2 }}') format('woff2'), |
| 16 | url('{{ .Assets.FontW }}') format('woff'); | 16 | url('{{ .Assets.FontW }}') format('woff'); |
| 17 | } | 17 | } |
| 18 | @font-face { | 18 | @font-face { |
| 19 | font-family: 'Iosevka Aile'; | 19 | font-family: 'Iosevka Aile'; |
| 20 | font-style: normal; | 20 | font-style: normal; |
| 21 | font-weight: normal; | 21 | font-weight: normal; |
| 22 | src: url('{{ .Assets.PropFontW2 }}') format('woff2'), | 22 | src: url('{{ .Assets.PropFontW2 }}') format('woff2'), |
| 23 | url('{{ .Assets.PropFontW }}') format('woff'); | 23 | url('{{ .Assets.PropFontW }}') format('woff'); |
| 24 | } | 24 | } |
| 25 | </style> | 25 | </style> |
| 26 | </head> | 26 | </head> |
| 27 | <body class="{{ if not .Lines }}is-plain{{ end }}"> | 27 | <body class="{{ if not .Lines }}is-plain{{ end }}"> |
| 28 | <header class="header header-base"> | 28 | <header class="header header-base"> |
| 29 | <div class="location"> | 29 | <div class="location"> |
| 30 | {{- $page := . -}} | 30 | <a class="location__prefix">{{ .Protocol }}://</a><a class="location__prefix location__prefix--mobile">://</a> |
| 31 | {{- $href := "" -}} | 31 | |
| 32 | {{- $uriParts := split .URI "/" -}} | 32 | {{- if .URI -}} |
| 33 | {{- $uriLast := $uriParts | last -}} | 33 | {{- $page := . -}} |
| 34 | {{- $uriParts = $uriParts | pop -}} | 34 | {{- $href := printf "/%s" .Protocol -}} |
| 35 | {{- if eq $uriLast "" -}} | 35 | {{- $uriParts := split .URI "/" -}} |
| 36 | {{- $uriLast = $uriParts | last -}} | 36 | |
| 37 | {{- $uriParts = $uriParts | pop -}} | 37 | {{- $uriLast := $uriParts | last -}} |
| 38 | {{- end -}} | 38 | {{- $uriParts = $uriParts | pop -}} |
| 39 | {{- if eq $uriLast "" -}} | ||
| 40 | {{- $uriLast = $uriParts | last -}} | ||
| 41 | {{- $uriParts = $uriParts | pop -}} | ||
| 42 | {{- end -}} | ||
| 39 | 43 | ||
| 40 | <a class="location__prefix">{{ .Protocol }}://</a><a class="location__prefix location__prefix--mobile">://</a> | 44 | {{- range $i, $part := $uriParts -}} |
| 41 | {{- $href = printf "%s/%s" $href .Protocol -}} | 45 | {{- if and (eq $page.Protocol "gopher") (eq $i 1) -}} |
| 42 | {{- range $i, $part := $uriParts -}} | 46 | {{- $href = printf "%s/1" $href -}} |
| 43 | {{- if and (eq $page.Protocol "gopher") (eq $i 1) -}} | 47 | {{- $part = $part | trimLeftChar -}} |
| 44 | {{- $href = printf "%s/1" $href -}} | 48 | {{- if not (eq $part "") -}} |
| 45 | {{- $part = $part | trimLeftChar -}} | 49 | {{- $href = printf "%s/%s" $href $part -}} |
| 46 | {{- if not (eq $part "") -}} | 50 | <span class="location__slash">/</span><a href="{{ $href }}/" class="location__uripart">{{ $part }}</a> |
| 47 | {{- $href = printf "%s/%s" $href $part -}} | 51 | {{- end -}} |
| 48 | <span class="location__slash">/</span><a href="{{ $href }}/" class="location__uripart">{{ $part }}</a> | 52 | {{- else -}} |
| 49 | {{- end -}} | 53 | {{- $href = printf "%s/%s" $href . -}} |
| 50 | {{- else -}} | 54 | {{- if ne $i 0 -}} |
| 51 | {{- $href = printf "%s/%s" $href . -}} | 55 | <span class="location__slash">/</span> |
| 52 | {{- if ne $i 0 -}} | 56 | {{- end -}} |
| 53 | <span class="location__slash">/</span> | 57 | <a href="{{ $href }}/" class="location__uripart">{{ . }}</a> |
| 54 | {{- end -}} | 58 | {{- end -}} |
| 55 | <a href="{{ $href }}/" class="location__uripart">{{ . }}</a> | 59 | {{- end -}} |
| 56 | {{- end -}} | 60 | {{- if ne (len $uriParts) 0 -}} |
| 57 | {{- end -}} | 61 | <span class="location__slash">/</span> |
| 58 | {{- if ne (len $uriParts) 0 -}} | 62 | {{- end -}} |
| 59 | <span class="location__slash">/</span> | 63 | {{- if and (eq $page.Protocol "gopher") (eq (len $uriParts) 1) -}} |
| 60 | {{- end -}} | 64 | {{- $uriLast = $uriLast | trimLeftChar -}} |
| 61 | {{- if and (eq $page.Protocol "gopher") (eq (len $uriParts) 1) -}} | 65 | {{- end -}} |
| 62 | {{- $uriLast = $uriLast | trimLeftChar -}} | 66 | <span class="location__uripart">{{ $uriLast }}</span> |
| 63 | {{- end -}} | 67 | {{- end -}} |
| 64 | <span class="location__uripart">{{ $uriLast }}</span> | 68 | </div> |
| 65 | </div> | 69 | <div class="actions"> |
| 66 | <div class="actions"> | 70 | {{- if and (not .Lines) (not .Error) (eq .Protocol "gopher") -}} |
| 67 | {{- if and (not .Lines) (not .Error) (eq .Protocol "gopher") -}} | 71 | <div class="action"><a href="/{{ .URI | replace "^(.*?)/0" "$1/9" }}">View raw</a></div> |
| 68 | <div class="action"><a href="/{{ .URI | replace "^(.*?)/0" "$1/9" }}">View raw</a></div> | 72 | {{- end -}} |
| 69 | {{- end -}} | 73 | <div class="action"><button class="settings-btn">Settings</button></div> |
| 70 | <div class="action"><button class="settings-btn">Settings</button></div> | 74 | </div> |
| 71 | </div> | 75 | </header> |
| 72 | </header> | 76 | <main class="wrap"> |
| 73 | <main class="wrap"> | 77 | <pre class="content content--has-monospace-font{{ if .Lines }} content--has-type-annotations{{ end }}"> |
| 74 | <pre class="content content--has-monospace-font{{ if .Lines }} content--has-type-annotations{{ end }}"> | 78 | {{- if .Lines -}} |
| 75 | {{- if .Lines -}} | 79 | {{- $content := "" -}} |
| 76 | {{- $content := "" -}} | 80 | {{- range .Lines -}} |
| 77 | {{- range .Lines -}} | 81 | {{- if ne $content "" -}} |
| 78 | {{- if ne $content "" -}} | 82 | {{- $content = printf "%s\n" $content -}} |
| 79 | {{- $content = printf "%s\n" $content -}} | 83 | {{- end -}} |
| 80 | {{- end -}} | 84 | {{- if .Link -}} |
| 81 | {{- if .Link -}} | 85 | {{- $content = printf "%s%s" $content (printf "<span class=\"type-annotation\">%s </span><a class=\"link link--%s\" href=\"%s\">%s</a>" .Type .Type .Link (.Text | HTMLEscape)) -}} |
| 82 | {{- $content = printf "%s%s" $content (printf "<span class=\"type-annotation\">%s </span><a class=\"link link--%s\" href=\"%s\">%s</a>" .Type .Type .Link (.Text | HTMLEscape)) -}} | 86 | {{- else -}} |
| 83 | {{- else -}} | 87 | {{- $content = printf "%s%s" $content (printf "<span class=\"type-annotation\"> </span>%s" (.Text | HTMLEscape)) -}} |
| 84 | {{- $content = printf "%s%s" $content (printf "<span class=\"type-annotation\"> </span>%s" (.Text | HTMLEscape)) -}} | 88 | {{- end -}} |
| 85 | {{- end -}} | 89 | {{- end -}} |
| 86 | {{- end -}} | 90 | {{- $content | safeHtml -}} |
| 87 | {{- $content | safeHtml -}} | 91 | {{- else -}} |
| 88 | {{- else -}} | 92 | {{- .RawText -}} |
| 89 | {{- .RawText -}} | 93 | {{- end -}} |
| 90 | {{- end -}} | 94 | </pre> |
| 91 | </pre> | 95 | </main> |
| 92 | </main> | 96 | <aside class="modal modal--settings"> |
| 93 | <aside class="modal modal--settings"> | 97 | <div class="modal__content"> |
| 94 | <div class="modal__content"> | 98 | <header class="modal__head header-base"> |
| 95 | <header class="modal__head header-base"> | 99 | <h1 class="modal__title">Settings</h1> |
| 96 | <h1 class="modal__title">Settings</h1> | 100 | <button class="modal__close-btn">Close</button> |
| 97 | <button class="modal__close-btn">Close</button> | 101 | </header> |
| 98 | </header> | 102 | <div class="setting setting--word-wrap"> |
| 99 | <div class="setting setting--word-wrap"> | 103 | <strong class="setting__label">Wrap wide content</strong> |
| 100 | <strong class="setting__label">Wrap wide content</strong> | 104 | <button class="setting__value">[N/A]</button> |
| 101 | <button class="setting__value">[N/A]</button> | 105 | </div> |
| 102 | </div> | 106 | <div class="setting setting--monospace-font"> |
| 103 | <div class="setting setting--monospace-font"> | 107 | <strong class="setting__label">Monospace font</strong> |
| 104 | <strong class="setting__label">Monospace font</strong> | 108 | <button class="setting__value">[N/A]</button> |
| 105 | <button class="setting__value">[N/A]</button> | 109 | </div> |
| 106 | </div> | 110 | <div class="setting setting--image-previews"> |
| 107 | <div class="setting setting--image-previews"> | 111 | <strong class="setting__label">Image thumbnails</strong> |
| 108 | <strong class="setting__label">Image thumbnails</strong> | 112 | <button class="setting__value">[N/A]</button> |
| 109 | <button class="setting__value">[N/A]</button> | 113 | </div> |
| 110 | </div> | 114 | <div class="setting setting--clickable-plain-links"> |
| 111 | <div class="setting setting--clickable-plain-links"> | 115 | <strong class="setting__label">Clickable links in text files</strong> |
| 112 | <strong class="setting__label">Clickable links in text files</strong> | 116 | <button class="setting__value">[N/A]</button> |
| 113 | <button class="setting__value">[N/A]</button> | 117 | </div> |
| 114 | </div> | 118 | </div> |
| 115 | </div> | 119 | </aside> |
| 116 | </aside> | 120 | <script src="{{ .Assets.JS }}"></script> |
| 117 | <script src="{{ .Assets.JS }}"></script> | 121 | </body> |
| 118 | </body> | ||
| 119 | </html>` | 122 | </html>` |
