root.go

  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			io.WriteString(writer, string(err.Error()))
 37		}
 38		return
 39	}
 40
 41	cookie, err := request.Cookie("access_token")
 42	if err != nil || cookie.Value != m.AccessToken {
 43		io.WriteString(writer, unauthenticated())
 44		return
 45	}
 46
 47	query := request.URL.Query()
 48
 49	action := query.Get("action")
 50	if len(action) == 0 {
 51		io.WriteString(writer, m.authenticated())
 52	}
 53
 54	destination := query.Get("url")
 55	name := query.Get("name")
 56	oldName := query.Get("oldName")
 57
 58	var message string
 59
 60	if action == "create" {
 61		if len(destination) == 0 {
 62			message = "URL field is required"
 63			http.Redirect(writer, request, "/?message="+message, 302)
 64			return
 65		}
 66
 67		if len(name) == 0 {
 68			name = uniuri.NewLen(4)
 69			for m.nameExists(name) {
 70				name = uniuri.NewLen(4)
 71				log.Println("Generated new name:", name)
 72			}
 73		} else if m.nameExists(name) {
 74			http.Error(writer, "406 Not Acceptable: A shortened URL with this name already exists", 406)
 75			message = "A shortened URL with this name already exists"
 76			http.Redirect(writer, request, "/?message="+message, 302)
 77			return
 78		}
 79
 80		message = url.QueryEscape(m.create(name, destination))
 81		http.Redirect(writer, request, "/?message="+message, 302)
 82		return
 83	}
 84
 85	if action == "edit" {
 86		io.WriteString(writer, m.edit(name, destination))
 87	}
 88
 89	if action == "update" {
 90		if len(destination) == 0 {
 91			message = "URL field is required"
 92			http.Redirect(writer, request, "/?message="+message, 302)
 93			return
 94		}
 95
 96		if len(name) == 0 {
 97			message = "Name field is required"
 98			http.Redirect(writer, request, "/?message="+message, 302)
 99			return
100		}
101
102		if len(oldName) == 0 {
103			message = "oldName field is required"
104			http.Redirect(writer, request, "/?message="+message, 302)
105			return
106		}
107
108		if len(name) != 0 && oldName != name {
109			if m.nameExists(name) {
110				message = "A shortened URL with this name already exists"
111				http.Redirect(writer, request, "/?message="+message, 302)
112				return
113			}
114		}
115
116		message := url.QueryEscape(m.update(name, destination, oldName))
117		http.Redirect(writer, request, "/?message="+message, 302)
118	}
119
120	if action == "delete" {
121		if len(name) == 0 {
122			message = "Name field is required"
123			http.Redirect(writer, request, "/?message="+message, 302)
124		}
125		message := url.QueryEscape(m.delete(name))
126		log.Println(message)
127		http.Redirect(writer, request, "/?message="+message, 302)
128	}
129}
130
131func (m model) genTable() string {
132	var table string
133	err := m.database.View(func(txn *badger.Txn) error {
134		opts := badger.DefaultIteratorOptions
135		opts.PrefetchSize = 10
136		iterator := txn.NewIterator(opts)
137		defer iterator.Close()
138		for iterator.Rewind(); iterator.Valid(); iterator.Next() {
139			item := iterator.Item()
140			k := item.Key()
141			err := item.Value(func(v []byte) error {
142				table = table + fmt.Sprintf(`<tr>
143	<td><p>%s</p></td>
144	<td><p>%s</p></td>
145	<td><a href="/?action=edit&name=%s&url=%s">Edit</a><a href="/?action=delete&name=%s">Delete</a></td>
146</tr>`, k, v, k, url.QueryEscape(string(v)), k)
147				return nil
148			})
149			if err != nil {
150				return err
151			}
152		}
153		return nil
154	})
155	if err != nil {
156		return err.Error()
157	}
158	return table
159}