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}