holiday-api/main.go

289 lines
7.6 KiB
Go

package main
import (
"embed"
"fmt"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/google/uuid"
"github.com/joho/godotenv"
_ "github.com/lib/pq"
"holiday-api/holiday"
"holiday-api/migration"
"html/template"
"log"
"net/http"
"os"
"strconv"
"strings"
"time"
)
//go:embed db/dev/*.sql
var devMigrations embed.FS
//go:embed db/prod/*.sql
var prodMigrations embed.FS
var isDev = false
func init() {
godotenv.Load()
if strings.Contains(os.Getenv("PROFILE"), "dev") {
isDev = true
}
log.SetPrefix("")
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
}
func main() {
client, err := connectToDb()
if err != nil {
log.Fatalf("couldn't connect to db: %v", err)
}
migrationFolder := prodMigrations
if isDev {
migrationFolder = devMigrations
}
if err := migration.InitializeMigrations(client, migrationFolder); err != nil {
log.Fatalf("couldn't execute migrations: %v", err)
}
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
holidayService := holiday.Service{client}
r.Get("/api/v1/holidays", getHolidays(holidayService))
templates, err := template.New("").ParseFiles("templates/index.gohtml", "templates/search.gohtml", "templates/search_date.gohtml", "templates/docs.gohtml")
if err != nil {
log.Fatalf("couldn't load templates: %v", err)
}
r.Get("/docs", func(w http.ResponseWriter, r *http.Request) {
templates, err := template.New("").ParseFiles("templates/docs.gohtml")
if err != nil {
log.Fatalf("couldn't load templates: %v", err)
}
renderTemplate(w, r, 200, templates, "docs.gohtml", 0)
})
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
renderTemplate(w, r, 200, templates, "index.gohtml", 0)
})
r.Get("/search", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
country := r.FormValue("country")
year := time.Now().Year()
if y := r.FormValue("year"); y != "" {
if yr, err := strconv.ParseInt(y, 10, 64); err == nil {
year = int(yr)
}
}
search := holiday.Search{Year: &year, Country: country}
holidays, _ := holidayService.Find(search, holiday.Paging{PageSize: 100})
holidayResponse := mapHolidays(holidays)
renderTemplate(w, r, 200, templates, "search.gohtml", holidayResponse)
})
r.Get("/search/date", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
country := r.FormValue("country")
date := time.Now()
if y := r.FormValue("date"); y != "" {
date = parseDate(y)
}
search := holiday.Search{Date: &date, Country: country}
holidays, _ := holidayService.Find(search, holiday.Paging{PageSize: 100})
holidayResponse := mapHolidays(holidays)
renderTemplate(w, r, 200, templates, "search_date.gohtml", holidayResponse)
})
r.Mount("/admin", adminDashboard(holidayService))
log.Fatal(http.ListenAndServe(":5281", r))
}
func adminDashboard(service holiday.Service) http.Handler {
r := chi.NewRouter()
credentials := loadCredentials()
templates, err := template.New("").Funcs(template.FuncMap{
"boolcmp": func(value *bool, expected string) bool {
if value == nil {
return expected == "nil"
} else {
return (*value && expected == "true") || (!(*value) && expected == "false")
}
},
"deferint": func(value *int) int { return *value },
}).ParseFiles("templates/admin_dashboard.gohtml", "templates/holiday.gohtml", "templates/error.gohtml")
if err != nil {
log.Fatalf("couldn't load templates: %v", err)
}
r.Use(middleware.BasicAuth("/admin", credentials))
r.Get("/holiday", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
id := r.FormValue("id")
response := HolidaySingleResponse{
Id: nil,
Country: "",
Date: DateResponse{time.Now()},
Name: "",
Description: "",
IsStateHoliday: true,
IsReligiousHoliday: false,
}
if id != "" {
if h, err := service.FindById(uuid.Must(uuid.Parse(id))); err == nil {
response = HolidaySingleResponse{
Id: &h.Id,
Country: h.Country,
Date: DateResponse{h.Date},
Name: h.Name,
Description: h.Description,
IsStateHoliday: h.IsStateHoliday,
IsReligiousHoliday: h.IsReligiousHoliday,
}
}
}
renderTemplate(w, r, 200, templates, "holiday.gohtml", response)
})
r.Post("/holiday/delete", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
var id uuid.UUID
if i := r.FormValue("id"); i != "" {
id = uuid.MustParse(i)
} else {
log.Printf("Failed deleting holiday: missing id", err)
w.WriteHeader(500)
w.Write([]byte("There was an error"))
}
err := service.Delete(id)
if err != nil {
log.Printf("Failed deleting holiday: %v", err)
w.WriteHeader(500)
w.Write([]byte("There was an error"))
} else {
w.Header().Add("Location", "/admin")
w.WriteHeader(http.StatusSeeOther)
w.Write([]byte{})
}
})
r.Post("/holiday", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
var id *uuid.UUID = nil
if i := r.FormValue("id"); i != "" {
temp := uuid.MustParse(i)
id = &temp
}
name := strings.TrimSpace(r.FormValue("name"))
description := strings.TrimSpace(r.FormValue("description"))
isStateHoliday := parseCheckbox(r.FormValue("stateHoliday"))
isReligiousHoliday := parseCheckbox(r.FormValue("religiousHoliday"))
date := parseDate(r.FormValue("date"))
country := r.FormValue("country")
hol := holiday.Holiday{
Country: country,
Date: date,
Name: name,
Description: description,
IsStateHoliday: isStateHoliday,
IsReligiousHoliday: isReligiousHoliday,
}
var err error
if id != nil {
hol.Id = *id
hol, err = service.Update(hol)
} else {
hol, err = service.Create(hol)
}
if err != nil {
log.Printf("Failed updating or creating holiday: %v", err)
w.WriteHeader(500)
w.Write([]byte("There was an error"))
} else {
w.Header().Add("Location", "/admin/holiday?id="+hol.Id.String())
w.WriteHeader(http.StatusSeeOther)
w.Write([]byte{})
}
})
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
country := r.FormValue("country")
if country == "" {
country = "HR"
}
var stateHoliday *bool = nil
if h := r.FormValue("stateHoliday"); h != "" {
if hol, err := strconv.ParseBool(h); err == nil {
stateHoliday = &hol
}
}
var religiousHoliday *bool = nil
if h := r.FormValue("religiousHoliday"); h != "" {
if hol, err := strconv.ParseBool(h); err == nil {
religiousHoliday = &hol
}
}
year := time.Now().Year()
if y := r.FormValue("year"); y != "" {
if yr, err := strconv.ParseInt(y, 10, 64); err == nil {
year = int(yr)
}
}
search := holiday.Search{Year: &year, Country: country, StateHoliday: stateHoliday, ReligiousHoliday: religiousHoliday}
holidays, _ := service.Find(search, holiday.Paging{PageSize: 100})
holidayResponse := mapHolidays(holidays)
response := map[string]any{}
response["holidays"] = holidayResponse
response["search"] = search
renderTemplate(w, r, 200, templates, "admin_dashboard.gohtml", response)
})
return r
}
func parseDate(date string) time.Time {
var year, month, day int
fmt.Sscanf(date, "%d-%d-%d", &year, &month, &day)
return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
}
func parseCheckbox(value string) bool {
if value != "" {
return true
}
return false
}
func loadCredentials() map[string]string {
credentials := envMustExist("AUTH_KEY")
values := strings.Split(credentials, ":")
return map[string]string{values[0]: values[1]}
}