2022-05-08 14:32:13 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"embed"
|
2022-09-05 14:17:07 +02:00
|
|
|
"encoding/json"
|
2022-05-08 18:19:41 +02:00
|
|
|
"errors"
|
2022-05-08 14:32:13 +02:00
|
|
|
"fmt"
|
|
|
|
"html/template"
|
|
|
|
"net/http"
|
2022-05-08 18:17:50 +02:00
|
|
|
"net/url"
|
|
|
|
"strings"
|
2022-09-06 12:36:21 +02:00
|
|
|
"time"
|
2022-05-08 14:32:13 +02:00
|
|
|
|
2022-09-06 11:22:28 +02:00
|
|
|
"git.milar.in/milarin/advsql"
|
|
|
|
"git.milar.in/milarin/channel"
|
2022-09-05 19:36:22 +02:00
|
|
|
"git.milar.in/milarin/envvars/v2"
|
2022-09-05 14:46:00 +02:00
|
|
|
"github.com/gorilla/mux"
|
2022-05-08 14:32:13 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
//go:embed templates/*
|
|
|
|
TemplateFS embed.FS
|
|
|
|
|
|
|
|
//go:embed static/*
|
|
|
|
StaticFS embed.FS
|
|
|
|
|
|
|
|
Templates *template.Template
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
2022-09-06 11:22:28 +02:00
|
|
|
advsql.InitMysqlDatabase(
|
|
|
|
Database,
|
|
|
|
envvars.String("DB_HOST", ""),
|
|
|
|
envvars.Uint16("DB_PORT", 3306),
|
|
|
|
envvars.String("DB_USER", ""),
|
|
|
|
envvars.String("DB_PASS", ""),
|
|
|
|
envvars.String("DB_BASE", "startpage"),
|
|
|
|
)
|
|
|
|
defer Database.Close()
|
2022-05-08 14:32:13 +02:00
|
|
|
|
2022-09-06 12:00:43 +02:00
|
|
|
if tmpl, err := template.New("homepage").Funcs(tmplFuncs).ParseFS(TemplateFS, "templates/*"); err == nil {
|
2022-05-08 14:32:13 +02:00
|
|
|
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)))
|
|
|
|
|
2022-09-06 12:36:21 +02:00
|
|
|
go func() {
|
|
|
|
for range time.Tick(time.Hour) {
|
|
|
|
if err := DeleteSessionsByExpirationDateBefore(time.Now()); err != nil {
|
|
|
|
fmt.Println("could not delete expired sessions", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2022-09-06 11:22:28 +02:00
|
|
|
if err := http.ListenAndServe(fmt.Sprintf("%s:%d", envvars.String("HTTP_INTF", ""), envvars.Uint16("HTTP_PORT", 80)), r); err != nil {
|
2022-05-08 14:32:13 +02:00
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func handler(w http.ResponseWriter, r *http.Request) {
|
2022-09-06 11:22:28 +02:00
|
|
|
session, err := GetSession(w, r)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2022-09-05 14:17:07 +02:00
|
|
|
|
2022-05-08 14:32:13 +02:00
|
|
|
data := &TmplData{
|
|
|
|
text: GetText(r),
|
2023-05-17 19:11:37 +02:00
|
|
|
Bookmarks: channel.ToSliceDeref(GetVisibleBookmarksBySessionIdOrdered(session.ID)),
|
2022-09-06 11:22:28 +02:00
|
|
|
Settings: GetSettingsBySessionID(session.ID),
|
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 search(w http.ResponseWriter, r *http.Request) {
|
2022-09-06 11:22:28 +02:00
|
|
|
session, err := GetSession(w, r)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
settings := GetSettingsBySessionID(session.ID)
|
2022-09-05 14:17:07 +02:00
|
|
|
|
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")
|
2022-05-08 18:17:50 +02:00
|
|
|
|
|
|
|
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))
|
2022-05-08 18:17:50 +02:00
|
|
|
w.WriteHeader(http.StatusMovedPermanently)
|
|
|
|
}
|
2022-05-08 21:43:47 +02:00
|
|
|
|
2022-05-08 18:17:50 +02:00
|
|
|
}
|
|
|
|
|
2022-09-05 14:17:07 +02:00
|
|
|
func customize(w http.ResponseWriter, r *http.Request) {
|
2022-09-06 11:22:28 +02:00
|
|
|
session, err := GetSession(w, r)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2022-09-05 14:17:07 +02:00
|
|
|
|
|
|
|
text := GetText(r)
|
|
|
|
|
|
|
|
data := &CustomizeData{
|
2022-09-06 11:22:28 +02:00
|
|
|
text: text,
|
|
|
|
Settings: GetSettingsBySessionID(session.ID),
|
|
|
|
Bookmarks: channel.ToSliceDeref(channel.MapSuccessive(GetBookmarksBySessionIdOrdered(session.ID), func(b *Bookmark) *BookmarkData {
|
|
|
|
return &BookmarkData{
|
|
|
|
Bookmark: b,
|
|
|
|
text: text,
|
|
|
|
}
|
|
|
|
})),
|
2022-09-05 14:17:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-09-06 11:22:28 +02:00
|
|
|
session, err := GetSession(w, r)
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2022-09-05 14:17:07 +02:00
|
|
|
|
|
|
|
sessionData := &SessionData{}
|
2022-09-06 11:22:28 +02:00
|
|
|
err = json.NewDecoder(r.Body).Decode(&sessionData)
|
2022-09-05 14:17:07 +02:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
2022-09-06 11:22:28 +02:00
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
2022-09-05 14:17:07 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-11-23 09:58:06 +01:00
|
|
|
for _, bookmark := range sessionData.Bookmarks {
|
|
|
|
uri, err := url.Parse(bookmark.Link)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if uri.Scheme == "" {
|
|
|
|
uri.Scheme = "https"
|
|
|
|
}
|
|
|
|
bookmark.Link = uri.String()
|
|
|
|
}
|
|
|
|
|
2022-09-05 14:17:07 +02:00
|
|
|
Reorder(sessionData.Bookmarks)
|
2022-09-06 11:22:28 +02:00
|
|
|
if err := DeleteBookmarksBySessionID(session.ID); err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
2023-05-17 19:11:37 +02:00
|
|
|
|
2022-09-06 11:22:28 +02:00
|
|
|
for _, bookmark := range sessionData.Bookmarks {
|
|
|
|
bookmark.SessionID = session.ID
|
|
|
|
if err := InsertBookmark(&bookmark); err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2022-09-05 14:17:07 +02:00
|
|
|
|
2022-09-06 11:22:28 +02:00
|
|
|
sessionData.Settings.SessionID = session.ID
|
|
|
|
if err := UpdateSettings(sessionData.Settings); err != nil {
|
2022-09-05 14:17:07 +02:00
|
|
|
fmt.Println(err)
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-08 18:17:50 +02:00
|
|
|
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
|
|
|
|
2022-05-08 18:17:50 +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
|
|
|
}
|