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}