1// SPDX-FileCopyrightText: 2022 Amolith <amolith@secluded.site>
  2//
  3// SPDX-License-Identifier: BSD-2-Clause
  4
  5package main
  6
  7import (
  8	"fmt"
  9	"io"
 10	"log"
 11	"net/http"
 12	"net/url"
 13	"strings"
 14
 15	"github.com/dchest/uniuri"
 16	"github.com/dgraph-io/badger/v3"
 17)
 18
 19func (m model) root(writer http.ResponseWriter, request *http.Request) {
 20	path := request.URL.Path
 21	if path != "/" {
 22		destinationKey := strings.TrimPrefix(path, "/")
 23		err := m.database.View(func(txn *badger.Txn) error {
 24			destinationEntry, err := txn.Get([]byte(destinationKey))
 25			if err == nil {
 26				destinationValue, err := destinationEntry.ValueCopy(nil)
 27				if err != nil {
 28					return err
 29				}
 30				log.Println("Redirecting visitor to \"" + string(destinationValue) + "\"")
 31				http.Redirect(writer, request, string(destinationValue), 302)
 32			}
 33			return err
 34		})
 35		if err != nil {
 36			_, err = io.WriteString(writer, string(err.Error()))
 37			if err != nil {
 38				log.Println(err)
 39			}
 40		}
 41		return
 42	}
 43
 44	cookie, err := request.Cookie("access_token")
 45	if err != nil || cookie.Value != m.AccessToken {
 46		_, err = io.WriteString(writer, unauthenticated())
 47		if err != nil {
 48			log.Println(err)
 49		}
 50		return
 51	}
 52
 53	query := request.URL.Query()
 54
 55	action := query.Get("action")
 56	if len(action) == 0 {
 57		_, err = io.WriteString(writer, m.authenticated())
 58		if err != nil {
 59			log.Println(err)
 60		}
 61	}
 62
 63	destination := query.Get("url")
 64	name := query.Get("name")
 65	oldName := query.Get("oldName")
 66
 67	var message string
 68
 69	if action == "create" {
 70		if len(destination) == 0 {
 71			message = "URL field is required"
 72			http.Redirect(writer, request, "/?message="+message, 302)
 73			return
 74		}
 75
 76		if len(name) == 0 {
 77			name = uniuri.NewLen(4)
 78			for m.nameExists(name) {
 79				name = uniuri.NewLen(4)
 80				log.Println("Generated new name:", name)
 81			}
 82		} else if m.nameExists(name) {
 83			http.Error(writer, "406 Not Acceptable: A shortened URL with this name already exists", 406)
 84			message = "A shortened URL with this name already exists"
 85			http.Redirect(writer, request, "/?message="+message, 302)
 86			return
 87		}
 88
 89		message = url.QueryEscape(m.create(name, destination))
 90		http.Redirect(writer, request, "/?message="+message, 302)
 91		return
 92	}
 93
 94	if action == "edit" {
 95		_, err = io.WriteString(writer, m.edit(name, destination))
 96		if err != nil {
 97			log.Println(err)
 98		}
 99	}
100
101	if action == "update" {
102		if len(destination) == 0 {
103			message = "URL field is required"
104			http.Redirect(writer, request, "/?message="+message, 302)
105			return
106		}
107
108		if len(name) == 0 {
109			message = "Name field is required"
110			http.Redirect(writer, request, "/?message="+message, 302)
111			return
112		}
113
114		if len(oldName) == 0 {
115			message = "oldName field is required"
116			http.Redirect(writer, request, "/?message="+message, 302)
117			return
118		}
119
120		if len(name) != 0 && oldName != name {
121			if m.nameExists(name) {
122				message = "A shortened URL with this name already exists"
123				http.Redirect(writer, request, "/?message="+message, 302)
124				return
125			}
126		}
127
128		message := url.QueryEscape(m.update(name, destination, oldName))
129		http.Redirect(writer, request, "/?message="+message, 302)
130	}
131
132	if action == "delete" {
133		if len(name) == 0 {
134			message = "Name field is required"
135			http.Redirect(writer, request, "/?message="+message, 302)
136		}
137		message := url.QueryEscape(m.delete(name))
138		log.Println(message)
139		http.Redirect(writer, request, "/?message="+message, 302)
140	}
141}
142
143func (m model) genTable() string {
144	var table string
145	err := m.database.View(func(txn *badger.Txn) error {
146		opts := badger.DefaultIteratorOptions
147		opts.PrefetchSize = 10
148		iterator := txn.NewIterator(opts)
149		defer iterator.Close()
150		for iterator.Rewind(); iterator.Valid(); iterator.Next() {
151			item := iterator.Item()
152			k := item.Key()
153			err := item.Value(func(v []byte) error {
154				table = table + fmt.Sprintf(`<tr>
155	<td><p>%s</p></td>
156	<td><p>%s</p></td>
157	<td><a class="button" href="/?action=edit&name=%s&url=%s">Edit</a><a class="button" href="/?action=delete&name=%s">Delete</a></td>
158</tr>`, k, v, k, url.QueryEscape(string(v)), k)
159				return nil
160			})
161			if err != nil {
162				return err
163			}
164		}
165		return nil
166	})
167	if err != nil {
168		return err.Error()
169	}
170	return table
171}