dinheiro

ref: master

cmd/server.go


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package cmd

import (
	"os"
	"log"
	"path/filepath"
	"strings"
	"net/http"
	"database/sql"
	"html/template"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	_ "github.com/lib/pq"
	goRedis "github.com/go-redis/redis/v8"

	"git.eletrotupi.com/git/dinheiro/config"
	"git.eletrotupi.com/git/dinheiro/db"
	"git.eletrotupi.com/git/dinheiro/keys"
	"git.eletrotupi.com/git/dinheiro/redis"
	"git.eletrotupi.com/git/dinheiro/auth"
)

func FileServer(router chi.Router, path string, root http.FileSystem) {
	if strings.ContainsAny(path, "{}*") {
		// TODO: Should we abort here?
		log.Fatal("FileServer does not permit any URL parameters.")
		os.Exit(1)
	}

	if path != "/" && path[len(path)-1] != '/' {
		router.Get(path, http.RedirectHandler(path + "/", 301).ServeHTTP)
		path += "/"
	}

	path += "*"

	router.Get(path, func(w http.ResponseWriter, r *http.Request) {
		rctx := chi.RouteContext(r.Context())
		pathPrefix := strings.TrimSuffix(rctx.RoutePattern(), "/*")
		fs := http.StripPrefix(pathPrefix, http.FileServer(root))
		fs.ServeHTTP(w, r)
	})
}

func registerStaticRoutes(router chi.Router) {
	// TODO: Deal with cache busting
	workDir, _ := os.Getwd()
	publicDir := http.Dir(filepath.Join(workDir, "public"))

	FileServer(router, "/public", publicDir)
}

func registerRoutes(router chi.Router) {
	registerStaticRoutes(router)

	router.Get("/", func(w http.ResponseWriter, r *http.Request) {
		authCtx, err := auth.ForContext(r.Context())

		parsedTemplate, _ := template.ParseFiles("templates/index.html")
		err = parsedTemplate.Execute(w, authCtx)
		if err != nil {
			log.Println("Error executing template :", err)
			return
		}
	})

	router.Post("/auth", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("authenticated!"))
	})
}

func Server() {
	appConfig := config.LoadConfig()
	router := chi.NewRouter()

	keys.Prepare(appConfig)

	// XXX: We should probably have a struct holding all this together
	redisHost, ok := appConfig.Get("redis", "redis-host")
	if !ok {
		log.Fatalf("Invalid redis::redis-host config")
	}

	ropts, err := goRedis.ParseURL(redisHost)
	if err != nil {
		log.Fatal("Invalid redis host")
	}

	dbConnString, ok := appConfig.Get("database", "connection-string")
	if !ok {
		log.Fatalf("Invalid database::connection-string config")
	}

	dbConn, err := sql.Open("postgres", dbConnString)
	if err != nil {
		log.Fatalf("Failed to open a connection to the database: %v", err)
	}

	rc := goRedis.NewClient(ropts)
	router.Use(config.Middleware(appConfig))
	router.Use(db.Middleware(dbConn))
	router.Use(auth.Middleware())
	router.Use(redis.Middleware(rc))
	router.Use(middleware.RealIP)
	router.Use(middleware.Logger)

	registerRoutes(router)

	addr := ":8555"
	if len(os.Args) > 2 {
		addr = os.Args[2]
	}

	log.Printf("Listening on %s", addr)
	http.ListenAndServe(addr, router)
}