package main import ( "embed" "errors" "flag" "fmt" "html/template" "io" "net/http" "net/url" "strings" _ "embed" ) var ( intf = flag.String("i", "", "interface") port = flag.Uint("p", 80, "port") ) var ( //go:embed templates/* TemplateFS embed.FS //go:embed static/* StaticFS embed.FS Templates *template.Template ) func main() { flag.Parse() ParseBookmarks() if tmpl, err := template.New("homepage").ParseFS(TemplateFS, "templates/*"); err == nil { Templates = tmpl } else { panic(err) } http.HandleFunc("/", handler) http.HandleFunc("/search", search) http.HandleFunc("/style.css", style) http.HandleFunc("/icons.ttf", ProvideFile("static/icons.ttf", "font/ttf")) http.HandleFunc("/transmission.png", ProvideFile("static/transmission.png", "image/png")) http.HandleFunc("/pihole.svg", ProvideFile("static/pihole.svg", "image/svg+xml")) http.HandleFunc("/gitea.png", ProvideFile("static/gitea.png", "image/png")) if err := http.ListenAndServe(fmt.Sprintf("%s:%d", *intf, *port), nil); err != nil { panic(err) } } func handler(w http.ResponseWriter, r *http.Request) { data := &TmplData{ text: GetText(r), Bookmarks: Bookmarks, Settings: DefaultSettings, } if err := Templates.ExecuteTemplate(w, "index.html", data); err != nil { panic(err) } } func style(w http.ResponseWriter, r *http.Request) { file, err := TemplateFS.Open("templates/style.css") if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } defer file.Close() w.Header().Add("Content-Type", "text/css") if err := Templates.ExecuteTemplate(w, "style.css", DefaultSettings); err != nil { panic(err) } } func ProvideFile(path, contentType string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { file, err := StaticFS.Open(path) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } defer file.Close() w.Header().Add("Content-Type", contentType) io.Copy(w, file) } } func search(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { fmt.Println(err) w.WriteHeader(http.StatusInternalServerError) return } query := r.Form.Get("query") if uri, err := ParseURI(query); err == nil { // url w.Header().Add("Location", uri.String()) w.WriteHeader(http.StatusMovedPermanently) } else { // search string w.Header().Add("Location", fmt.Sprintf("https://duckduckgo.com/?q=%s", query)) w.WriteHeader(http.StatusMovedPermanently) } } func ParseURI(uri string) (*url.URL, error) { if !strings.HasPrefix(uri, "http://") && !strings.HasPrefix(uri, "https://") { uri = "https://" + uri } ret, err := url.ParseRequestURI(uri) if err != nil { return nil, err } splits := strings.Split(ret.Hostname(), ".") if len(splits) <= 1 { return nil, errors.New("hostname doesn't have a TLD") } if _, ok := TLDs[splits[len(splits)-1]]; !ok { return nil, errors.New("invalid top level domain") } return ret, nil }