helperfuncs.go

  1// SPDX-FileCopyrightText: 2022 Amolith <amolith@secluded.site>
  2//
  3// SPDX-License-Identifier: BSD-2-Clause
  4
  5package main
  6
  7import (
  8	"errors"
  9	"fmt"
 10	"log"
 11	"strings"
 12	"text/template"
 13
 14	"github.com/dgraph-io/badger/v3"
 15)
 16
 17// Create shortens a given URL with an optional name. If a name is provided,
 18// that name will be used. Otherwise, 4-character string will be generated and
 19// used instead.
 20func (m model) create(name string, url string) string {
 21	err := m.database.Update(func(txn *badger.Txn) error {
 22		return txn.Set([]byte(name), []byte(url))
 23	})
 24	if err != nil {
 25		// TODO: return an error instead so it can be handled like an error
 26		return fmt.Sprint(err)
 27	}
 28
 29	log.Println("\"" + url + "\" mapped to \"" + name + "\"")
 30	return fmt.Sprint("URL mapped to ", name, "\n")
 31}
 32
 33// read() accepts a start variable for the first link in a set (implying reverse
 34// iteration), an end variable for the last link in a set (implying forward
 35// iteration), and a count variable for the number of requested entries. The
 36// requested set is returned as JSON.
 37func (m model) read(start string, end string, count int) (map[string]string, error) {
 38	links := make(map[string]string)
 39
 40	if !(count > 0) {
 41		return nil, errors.New("Count parameter is required")
 42	}
 43
 44	err := m.database.View(func(txn *badger.Txn) error {
 45		opts := badger.DefaultIteratorOptions
 46		opts.PrefetchSize = 20
 47		if start != "" && end == "" {
 48			// Start value provided, iterate backwards
 49			opts.Reverse = true
 50		}
 51		iterator := txn.NewIterator(opts)
 52		defer iterator.Close()
 53		if start == "" && end == "" {
 54			iterator.Rewind()
 55		} else if start != "" && end == "" {
 56			// Set position to "start"
 57			iterator.Seek([]byte(start))
 58			// If "start" exists, move to next value
 59			if iterator.Valid() {
 60				iterator.Next()
 61			}
 62		} else if start == "" && end != "" {
 63			// Start value provided, iterate forwards
 64			// Set position to "end"
 65			iterator.Seek([]byte(end))
 66			// If "end" exists, move to next value
 67			if iterator.Valid() {
 68				iterator.Next()
 69			}
 70		} else {
 71			return errors.New("Only provide start OR end parameters, not both")
 72		}
 73		for i := 0; i < count; i++ {
 74			if iterator.Valid() {
 75				err := iterator.Item().Value(func(v []byte) error {
 76					links[string(iterator.Item().Key())] = string(v)
 77					return nil
 78				})
 79				if err != nil {
 80					return err
 81				}
 82				iterator.Next()
 83			}
 84		}
 85		return nil
 86	})
 87	if err != nil {
 88		return nil, err
 89	}
 90
 91	return links, nil
 92}
 93
 94// Update modifies a shortened link
 95func (m model) update(name string, url string, oldName string) string {
 96	m.create(name, url)
 97	if name != oldName {
 98		m.delete(oldName)
 99	}
100	return fmt.Sprint("\"" + url + "\" mapped to \"" + name + "\"")
101}
102
103// Delete removes a shortened URL from the database given its name.
104func (m model) delete(name string) string {
105	err := m.database.Update(func(txn *badger.Txn) error {
106		return txn.Delete([]byte(name))
107	})
108	if err != nil {
109		return fmt.Sprint(err)
110	}
111
112	log.Println("\"" + name + "\" has been deleted")
113	return fmt.Sprint("\"" + name + "\" has been deleted\n")
114}
115
116// nameExists returns true if the provided name is already stored in the
117// database and false if it's not.
118func (m model) nameExists(name string) bool {
119	err := m.database.View(func(txn *badger.Txn) error {
120		_, err := txn.Get([]byte(name))
121		if err != nil {
122			return err
123		}
124		return nil
125	})
126	return err == nil
127}
128
129// unauthenticated serves the unauthenticated home page to the visitor
130func unauthenticated() string {
131	home, err := templates.ReadFile("templates/home_unauthenticated.html")
132	if err != nil {
133		log.Fatalln(err)
134	}
135	return string(home)
136}
137
138// authenticated serves the authenticated dashboard to the user
139func (m model) authenticated() string {
140	dash, err := templates.ReadFile("templates/home_authenticated.html")
141	if err != nil {
142		log.Fatalln(err)
143	}
144	tmpl, err := template.New("authenticated").Parse(string(dash))
145	if err != nil {
146		log.Println(err)
147	}
148	page := new(strings.Builder)
149	err = tmpl.Execute(page, m.genTable())
150	if err != nil {
151		log.Println(err)
152	}
153	return page.String()
154}
155
156type EditFields struct {
157	URL  string
158	Name string
159}
160
161// update serves the editing interface to the user
162func (m model) edit(name string, url string) string {
163	fields := EditFields{url, name}
164	editPage, err := templates.ReadFile("templates/edit.html")
165	if err != nil {
166		log.Fatalln(err)
167	}
168	tmpl, err := template.New("editPage").Parse(string(editPage))
169	if err != nil {
170		log.Println(err)
171	}
172	page := new(strings.Builder)
173	err = tmpl.Execute(page, fields)
174	if err != nil {
175		log.Println(err)
176	}
177	return page.String()
178}