all repos — artbound-go @ main

The official administration panel for ArtBound, by EarthBound Café.

artbound.go (view raw)

  1package main
  2
  3import (
  4	"bytes"
  5	"embed"
  6	"encoding/json"
  7	"html/template"
  8	"log"
  9	"net/http"
 10	"net/url"
 11	"os"
 12	"time"
 13
 14	"github.com/BiRabittoh/artbound-go/cache"
 15	"github.com/joho/godotenv"
 16)
 17
 18const templatesDirectory = "templates/"
 19
 20var (
 21	//go:embed templates/index.html templates/help.html
 22	templates embed.FS
 23	//go:embed all:static
 24	static        embed.FS
 25	indexTemplate = template.Must(template.ParseFS(templates, templatesDirectory+"index.html"))
 26	helpTemplate  = template.Must(template.ParseFS(templates, templatesDirectory+"help.html"))
 27)
 28
 29type TemplateData struct {
 30	Emoji        EmojiDict
 31	LastUpdated  string
 32	CurrentMonth string
 33}
 34
 35func indexHandler(db *cache.DB) http.HandlerFunc {
 36	return func(w http.ResponseWriter, r *http.Request) {
 37		if r.Method != http.MethodGet {
 38			http.Error(w, "Please use GET.", http.StatusMethodNotAllowed)
 39			return
 40		}
 41
 42		lastUpdated := db.LastUpdated.Format("02/01/2006 15:04")
 43		currentMonth := time.Now().Format("2006-01")
 44		templateData := &TemplateData{defaultEmojis, lastUpdated, currentMonth}
 45		buf := &bytes.Buffer{}
 46		err := indexTemplate.Execute(buf, templateData)
 47		if err != nil {
 48			http.Error(w, err.Error(), http.StatusInternalServerError)
 49			return
 50		}
 51		buf.WriteTo(w)
 52	}
 53}
 54
 55func helpHandler(w http.ResponseWriter, r *http.Request) {
 56	if r.Method != http.MethodGet {
 57		http.Error(w, "Please use GET.", http.StatusMethodNotAllowed)
 58		return
 59	}
 60
 61	buf := &bytes.Buffer{}
 62	err := helpTemplate.Execute(buf, defaultEmojis)
 63	if err != nil {
 64		http.Error(w, err.Error(), http.StatusInternalServerError)
 65		return
 66	}
 67	buf.WriteTo(w)
 68}
 69
 70func clearHandler(db *cache.DB) http.HandlerFunc {
 71	return func(w http.ResponseWriter, r *http.Request) {
 72		if r.Method != http.MethodPost {
 73			http.Error(w, "Please use POST.", http.StatusMethodNotAllowed)
 74			return
 75		}
 76		err := db.Clear()
 77		if err != nil {
 78			log.Println("Error:", err)
 79			http.Error(w, "Could not delete cache.", http.StatusInternalServerError)
 80		}
 81		http.Error(w, "Done.", http.StatusOK)
 82	}
 83}
 84
 85func updateHandler(db *cache.DB) http.HandlerFunc {
 86	return func(w http.ResponseWriter, r *http.Request) {
 87		if r.Method != http.MethodPost {
 88			http.Error(w, "Please use POST.", http.StatusMethodNotAllowed)
 89			return
 90		}
 91		p := db.UpdateCall()
 92		w.Header().Set("Content-Type", "application/json")
 93		w.WriteHeader(http.StatusCreated)
 94		json.NewEncoder(w).Encode(p)
 95	}
 96}
 97
 98func getHandler(db *cache.DB) http.HandlerFunc {
 99	return func(w http.ResponseWriter, r *http.Request) {
100		u, err := url.Parse(r.URL.String())
101		if err != nil {
102			log.Println("Could not parse URL.")
103			http.Error(w, err.Error(), http.StatusInternalServerError)
104			return
105		}
106
107		month := u.Query().Get("month")
108		if month == "" {
109			http.Error(w, "\"month\" parameter is required.", http.StatusBadRequest)
110			return
111		}
112		entries, err := db.GetEntries(month)
113		if err != nil {
114			http.Error(w, "Could not get entries.", http.StatusInternalServerError)
115			return
116		}
117
118		w.Header().Set("Content-Type", "application/json")
119		w.WriteHeader(http.StatusCreated)
120
121		if len(entries) == 0 {
122			w.Write([]byte("[]"))
123			return
124		}
125		json.NewEncoder(w).Encode(entries)
126	}
127}
128
129func main() {
130	err := godotenv.Load()
131	if err != nil {
132		log.Println("No .env file provided.")
133	}
134
135	port := os.Getenv("PORT")
136	if port == "" {
137		port = "3000"
138	}
139
140	spreadsheetId := os.Getenv("SPREADSHEET_ID")
141	if spreadsheetId == "" {
142		log.Fatal("Please fill out SPREADSHEET_ID in .env")
143	}
144
145	spreadsheetRange := os.Getenv("SPREADSHEET_RANGE")
146	if spreadsheetRange == "" {
147		log.Fatal("Please fill out SPREADSHEET_RANGE in .env")
148	}
149
150	cacheRemotePath := "/" + cache.CachePath + "/"
151	cacheFS := http.FileServer(http.Dir(cache.CachePath))
152
153	fs := http.FileServer(http.FS(static))
154	db := cache.InitDB(spreadsheetId, spreadsheetRange)
155
156	r := http.NewServeMux()
157	r.HandleFunc("/", indexHandler(db))
158	r.HandleFunc("/clear", clearHandler(db))
159	r.HandleFunc("/update", updateHandler(db))
160	r.HandleFunc("/get", getHandler(db))
161	r.HandleFunc("/help", helpHandler)
162	r.Handle("/static/", fs)
163	r.Handle(cacheRemotePath, http.StripPrefix(cacheRemotePath, cacheFS))
164
165	log.Println("Serving on port", port)
166	err = http.ListenAndServe(":"+port, r)
167	if err != nil {
168		log.Fatal(err)
169	}
170}