startpage/main.go

204 lines
4.4 KiB
Go
Raw Normal View History

2022-05-08 14:32:13 +02:00
package main
import (
"embed"
2022-09-05 14:17:07 +02:00
"encoding/gob"
"encoding/hex"
"encoding/json"
2022-05-08 18:19:41 +02:00
"errors"
2022-05-08 14:32:13 +02:00
"flag"
"fmt"
"html/template"
"io"
"net/http"
"net/url"
2022-09-05 14:17:07 +02:00
"os"
"strings"
2022-09-05 19:36:22 +02:00
"time"
2022-05-08 14:32:13 +02:00
_ "embed"
2022-09-05 14:17:07 +02:00
2022-09-05 19:36:22 +02:00
"git.milar.in/milarin/envvars/v2"
2022-09-05 14:17:07 +02:00
"git.milar.in/milarin/slices"
2022-09-05 14:46:00 +02:00
"github.com/gorilla/mux"
2022-09-05 19:36:22 +02:00
"github.com/srinathgs/mysqlstore"
2022-05-08 14:32:13 +02:00
)
var (
2022-05-08 14:41:53 +02:00
intf = flag.String("i", "", "interface")
2022-05-08 14:32:13 +02:00
port = flag.Uint("p", 80, "port")
)
var (
//go:embed templates/*
TemplateFS embed.FS
//go:embed static/*
StaticFS embed.FS
Templates *template.Template
2022-09-05 14:17:07 +02:00
2022-09-05 19:36:22 +02:00
SessionStore = Must(mysqlstore.NewMySQLStore(
fmt.Sprintf(
"%s:%s@tcp(%s:%d)/%s?parseTime=true&loc=Local",
envvars.String("DB_USER", ""),
envvars.String("DB_PASS", ""),
envvars.String("DB_HOST", ""),
envvars.Uint16("DB_PORT", 3306),
envvars.String("DB_BASE", "startpage"),
),
"sessions",
"/",
int((time.Hour * 24 * 365).Seconds()),
Must(hex.DecodeString(os.Getenv("SESSION_KEY"))),
))
2022-05-08 14:32:13 +02:00
)
func main() {
2022-09-05 14:17:07 +02:00
gob.Register([]Bookmark{})
gob.Register(&Settings{})
2022-05-08 14:32:13 +02:00
2022-09-05 14:17:07 +02:00
flag.Parse()
2022-05-08 14:32:13 +02:00
if tmpl, err := template.New("homepage").ParseFS(TemplateFS, "templates/*"); err == nil {
Templates = tmpl
} else {
panic(err)
}
2022-09-05 14:46:00 +02:00
r := mux.NewRouter()
r.HandleFunc("/", handler)
r.HandleFunc("/customize", customize)
r.HandleFunc("/save-changes", saveChanges)
r.HandleFunc("/search", search)
r.PathPrefix("/static/").Handler(http.FileServer(http.FS(StaticFS)))
if err := http.ListenAndServe(fmt.Sprintf("%s:%d", *intf, *port), r); err != nil {
2022-05-08 14:32:13 +02:00
panic(err)
}
}
func handler(w http.ResponseWriter, r *http.Request) {
2022-09-05 14:17:07 +02:00
session, _ := SessionStore.Get(r, "settings")
2022-05-08 14:32:13 +02:00
data := &TmplData{
text: GetText(r),
2022-09-05 14:17:07 +02:00
Bookmarks: GetValueDefault(session, "bookmarks", DefaultBookmarks()),
Settings: GetValueDefault(session, "settings", DefaultSettings()),
2022-05-08 14:32:13 +02:00
}
if err := Templates.ExecuteTemplate(w, "index.html", data); err != nil {
panic(err)
}
}
2022-05-08 21:43:47 +02:00
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) {
2022-09-05 14:17:07 +02:00
session, _ := SessionStore.Get(r, "settings")
settings := GetValueDefault(session, "settings", DefaultSettings())
2022-05-08 21:43:47 +02:00
if err := r.ParseForm(); err != nil {
2022-05-08 14:32:13 +02:00
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
2022-09-05 14:17:07 +02:00
w.Header().Add("Location", fmt.Sprintf(settings.Search, query))
w.WriteHeader(http.StatusMovedPermanently)
}
2022-05-08 21:43:47 +02:00
}
2022-09-05 14:17:07 +02:00
func customize(w http.ResponseWriter, r *http.Request) {
session, _ := SessionStore.Get(r, "settings")
text := GetText(r)
bookmarks := slices.Map(GetValueDefault(session, "bookmarks", DefaultBookmarks()), func(b Bookmark) BookmarkData {
return BookmarkData{
Bookmark: &b,
text: text,
}
})
data := &CustomizeData{
text: text,
Bookmarks: bookmarks,
Settings: GetValueDefault(session, "settings", DefaultSettings()),
}
if err := Templates.ExecuteTemplate(w, "customize.html", data); err != nil {
panic(err)
}
}
func saveChanges(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
session, _ := SessionStore.Get(r, "settings")
sessionData := &SessionData{}
err := json.NewDecoder(r.Body).Decode(&sessionData)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Println(err)
return
}
Reorder(sessionData.Bookmarks)
session.Values["bookmarks"] = sessionData.Bookmarks
session.Values["settings"] = sessionData.Settings
if err := session.Save(r, w); err != nil {
fmt.Println(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
}
func ParseURI(uri string) (*url.URL, error) {
if !strings.HasPrefix(uri, "http://") && !strings.HasPrefix(uri, "https://") {
uri = "https://" + uri
}
2022-05-08 21:43:47 +02:00
ret, err := url.ParseRequestURI(uri)
2022-05-08 18:38:42 +02:00
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
2022-05-08 14:32:13 +02:00
}