1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  2//
  3// SPDX-License-Identifier: Apache-2.0
  4
  5package ws
  6
  7import (
  8	"database/sql"
  9	"embed"
 10	"fmt"
 11	"io"
 12	"net/http"
 13	"net/url"
 14	"strings"
 15	"sync"
 16	"text/template"
 17
 18	"git.sr.ht/~amolith/willow/project"
 19	"github.com/microcosm-cc/bluemonday"
 20)
 21
 22type Handler struct {
 23	DbConn        *sql.DB
 24	Mutex         *sync.Mutex
 25	Req           *chan struct{}
 26	ManualRefresh *chan struct{}
 27	Res           *chan []project.Project
 28}
 29
 30//go:embed static
 31var fs embed.FS
 32
 33// bmUGC    = bluemonday.UGCPolicy()
 34var bmStrict = bluemonday.StrictPolicy()
 35
 36func (h Handler) RootHandler(w http.ResponseWriter, r *http.Request) {
 37	if !h.isAuthorised(r) {
 38		http.Redirect(w, r, "/login", http.StatusSeeOther)
 39		return
 40	}
 41	*h.Req <- struct{}{}
 42	data := <-*h.Res
 43	tmpl := template.Must(template.ParseFS(fs, "static/home.html"))
 44	if err := tmpl.Execute(w, data); err != nil {
 45		fmt.Println(err)
 46	}
 47}
 48
 49func (h Handler) NewHandler(w http.ResponseWriter, r *http.Request) {
 50	if !h.isAuthorised(r) {
 51		http.Redirect(w, r, "/login", http.StatusSeeOther)
 52		return
 53	}
 54	params := r.URL.Query()
 55	action := bmStrict.Sanitize(params.Get("action"))
 56	if r.Method == http.MethodGet {
 57		if action == "" {
 58			tmpl := template.Must(template.ParseFS(fs, "static/new.html"))
 59			if err := tmpl.Execute(w, nil); err != nil {
 60				fmt.Println(err)
 61			}
 62		} else if action != "delete" {
 63			submittedURL := bmStrict.Sanitize(params.Get("url"))
 64			if submittedURL == "" {
 65				w.WriteHeader(http.StatusBadRequest)
 66				_, err := w.Write([]byte("No URL provided"))
 67				if err != nil {
 68					fmt.Println(err)
 69				}
 70				return
 71			}
 72
 73			forge := bmStrict.Sanitize(params.Get("forge"))
 74			if forge == "" {
 75				w.WriteHeader(http.StatusBadRequest)
 76				_, err := w.Write([]byte("No forge provided"))
 77				if err != nil {
 78					fmt.Println(err)
 79				}
 80				return
 81			}
 82
 83			name := bmStrict.Sanitize(params.Get("name"))
 84			if name == "" {
 85				w.WriteHeader(http.StatusBadRequest)
 86				_, err := w.Write([]byte("No name provided"))
 87				if err != nil {
 88					fmt.Println(err)
 89				}
 90			}
 91
 92			proj := project.Project{
 93				URL:   submittedURL,
 94				Name:  name,
 95				Forge: forge,
 96			}
 97			proj, err := project.GetReleases(h.DbConn, proj)
 98			if err != nil {
 99				w.WriteHeader(http.StatusBadRequest)
100				_, err := w.Write([]byte(fmt.Sprintf("Error getting releases: %s", err)))
101				if err != nil {
102					fmt.Println(err)
103				}
104			}
105			tmpl := template.Must(template.ParseFS(fs, "static/select-release.html"))
106			if err := tmpl.Execute(w, proj); err != nil {
107				fmt.Println(err)
108			}
109		} else if action == "delete" {
110			submittedURL := params.Get("url")
111			if submittedURL == "" {
112				w.WriteHeader(http.StatusBadRequest)
113				_, err := w.Write([]byte("No URL provided"))
114				if err != nil {
115					fmt.Println(err)
116				}
117			}
118
119			project.Untrack(h.DbConn, h.ManualRefresh, submittedURL)
120			http.Redirect(w, r, "/", http.StatusSeeOther)
121		}
122	}
123
124	if r.Method == http.MethodPost {
125		err := r.ParseForm()
126		if err != nil {
127			fmt.Println(err)
128		}
129		nameValue := bmStrict.Sanitize(r.FormValue("name"))
130		urlValue := bmStrict.Sanitize(r.FormValue("url"))
131		forgeValue := bmStrict.Sanitize(r.FormValue("forge"))
132		releaseValue := bmStrict.Sanitize(r.FormValue("release"))
133
134		if nameValue != "" && urlValue != "" && forgeValue != "" && releaseValue != "" {
135			project.Track(h.DbConn, h.ManualRefresh, nameValue, urlValue, forgeValue, releaseValue)
136			http.Redirect(w, r, "/", http.StatusSeeOther)
137			return
138		}
139
140		if nameValue != "" && urlValue != "" && forgeValue != "" && releaseValue == "" {
141			http.Redirect(w, r, "/new?action=yoink&name="+url.QueryEscape(nameValue)+"&url="+url.QueryEscape(urlValue)+"&forge="+url.QueryEscape(forgeValue), http.StatusSeeOther)
142			return
143		}
144
145		if nameValue == "" && urlValue == "" && forgeValue == "" && releaseValue == "" {
146			w.WriteHeader(http.StatusBadRequest)
147			_, err := w.Write([]byte("No data provided"))
148			if err != nil {
149				fmt.Println(err)
150			}
151		}
152	}
153}
154
155func (h Handler) LoginHandler(w http.ResponseWriter, r *http.Request) {
156	// TODO: do this
157}
158
159func (h Handler) isAuthorised(r *http.Request) bool {
160	// TODO: do this
161	return false
162}
163
164func StaticHandler(writer http.ResponseWriter, request *http.Request) {
165	resource := strings.TrimPrefix(request.URL.Path, "/")
166	// if path ends in .css, set content type to text/css
167	if strings.HasSuffix(resource, ".css") {
168		writer.Header().Set("Content-Type", "text/css")
169	} else if strings.HasSuffix(resource, ".js") {
170		writer.Header().Set("Content-Type", "text/javascript")
171	}
172	home, err := fs.ReadFile(resource)
173	if err != nil {
174		fmt.Println(err)
175	}
176	if _, err = io.WriteString(writer, string(home)); err != nil {
177		fmt.Println(err)
178	}
179}