removed basically all the panics
This commit is contained in:
parent
e4d6348fc3
commit
465266c36d
@ -58,40 +58,40 @@ func CreateBlankConfig() *Config {
|
|||||||
return &conf
|
return &conf
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfig(pathlike string) *VinegarHttpServlet {
|
func LoadConfig(pathlike string) (*VinegarHttpServlet, error) {
|
||||||
contents, exists := vinegarUtil.GetDiskContent(pathlike)
|
contents, err := vinegarUtil.GetDiskContent(pathlike)
|
||||||
if exists {
|
if err != nil {
|
||||||
conf := Config{}
|
|
||||||
err := json.Unmarshal(*contents, &conf)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
servlet := NewServlet(conf.ListeningAddress)
|
|
||||||
for _, v := range conf.Routes {
|
|
||||||
constructor, err := getConstructorFunction(v.ConfigType)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
//we don't have to invoke servlet.AddRoute because the static routes already do that for us
|
|
||||||
constructor(servlet, v.UrlPattern, v.FileLocation, v.UseBuiltinCache)
|
|
||||||
|
|
||||||
}
|
|
||||||
return servlet
|
|
||||||
} else {
|
|
||||||
CreateBlankConfig()
|
CreateBlankConfig()
|
||||||
panic("Could not find config file at" + pathlike)
|
return nil, err
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conf := Config{}
|
||||||
|
err = json.Unmarshal(*contents, &conf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
servlet := NewServlet(conf.ListeningAddress)
|
||||||
|
for _, v := range conf.Routes {
|
||||||
|
constructor, err := getConstructorFunction(v.ConfigType)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
//we don't have to invoke servlet.AddRoute because the static routes already do that for us
|
||||||
|
constructor(servlet, v.UrlPattern, v.FileLocation, v.UseBuiltinCache)
|
||||||
|
|
||||||
|
}
|
||||||
|
return servlet, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ConfigEntry) toRoute(serv *VinegarHttpServlet) {
|
func (e ConfigEntry) toRoute(serv *VinegarHttpServlet) error {
|
||||||
constructor, err := getConstructorFunction(e.ConfigType)
|
constructor, err := getConstructorFunction(e.ConfigType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
constructor(serv, e.UrlPattern, e.FileLocation, e.UseBuiltinCache)
|
constructor(serv, e.UrlPattern, e.FileLocation, e.UseBuiltinCache)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getConstructorFunction(t ConfigType) (RouteConstructor, error) {
|
func getConstructorFunction(t ConfigType) (RouteConstructor, error) {
|
||||||
@ -106,13 +106,14 @@ func getConstructorFunction(t ConfigType) (RouteConstructor, error) {
|
|||||||
return nil, errors.New(fmt.Sprintf("could not determine constructor for invalid ConfigType: %s", t))
|
return nil, errors.New(fmt.Sprintf("could not determine constructor for invalid ConfigType: %s", t))
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateBlankConfig() {
|
func GenerateBlankConfig() error {
|
||||||
fileName := "servlet.json.template.tmpl"
|
fileName := "servlet.json.template.tmpl"
|
||||||
conf := CreateBlankConfig()
|
conf := CreateBlankConfig()
|
||||||
content, err := json.Marshal(&conf)
|
content, err := json.Marshal(&conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("Generating a blank configuration file at ")
|
fmt.Println("Generating a blank configuration file at ")
|
||||||
ioutil.WriteFile(fileName, content, 0755)
|
ioutil.WriteFile(fileName, content, 0755)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package servlet
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ func (api *ApiRoute) RegisterHttpMethodHandler(method HttpMethod, handler Vinega
|
|||||||
|
|
||||||
func getHttpMethod(req *http.Request) HttpMethod {
|
func getHttpMethod(req *http.Request) HttpMethod {
|
||||||
switch req.Method {
|
switch req.Method {
|
||||||
case "GET":
|
case http.MethodGet:
|
||||||
return GET
|
return GET
|
||||||
case "POST":
|
case "POST":
|
||||||
return POST
|
return POST
|
||||||
@ -79,10 +80,10 @@ func getHttpMethod(req *http.Request) HttpMethod {
|
|||||||
|
|
||||||
func SendApiError(w http.ResponseWriter, httpCode int, messageCode int, message string) {
|
func SendApiError(w http.ResponseWriter, httpCode int, messageCode int, message string) {
|
||||||
respMessage := fmt.Sprintf("{\"code\":%d, \"message\":\"%s\"}", messageCode, message)
|
respMessage := fmt.Sprintf("{\"code\":%d, \"message\":\"%s\"}", messageCode, message)
|
||||||
w.WriteHeader(httpCode)
|
log.Printf("[ERROR]\t%s\n", respMessage)
|
||||||
|
|
||||||
_, err := w.Write([]byte(respMessage))
|
_, err := w.Write([]byte(respMessage))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Println(err)
|
||||||
}
|
}
|
||||||
|
w.WriteHeader(httpCode)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import (
|
|||||||
"geniuscartel.xyz/vinegar/vinegarUtil"
|
"geniuscartel.xyz/vinegar/vinegarUtil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@ -83,10 +82,9 @@ func (s *VinegarHttpServlet) ServeHTTP(w http.ResponseWriter, req *http.Request)
|
|||||||
s.SendError(w, req, 404, "Couldn't find your content.", errors.New("failed to match route for ["+path+"]"))
|
s.SendError(w, req, 404, "Couldn't find your content.", errors.New("failed to match route for ["+path+"]"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *VinegarHttpServlet) Start() {
|
func (s *VinegarHttpServlet) Start() error {
|
||||||
if len(s.Routes) < 1 {
|
if len(s.Routes) < 1 {
|
||||||
log.Fatal("No routes found for server. Nothing to listen and serve.")
|
log.Fatal("No routes found for server. Nothing to listen and serve.")
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Listening on [%s]\n", s.Port)
|
log.Printf("Listening on [%s]\n", s.Port)
|
||||||
@ -94,9 +92,9 @@ func (s *VinegarHttpServlet) Start() {
|
|||||||
err := http.ListenAndServe(s.Port, s)
|
err := http.ListenAndServe(s.Port, s)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *VinegarWebRoute) Announce() {
|
func (r *VinegarWebRoute) Announce() {
|
||||||
@ -105,28 +103,36 @@ func (r *VinegarWebRoute) Announce() {
|
|||||||
|
|
||||||
func (s *VinegarHttpServlet) SendError(w http.ResponseWriter, req *http.Request, code int, msg string, aErr error) {
|
func (s *VinegarHttpServlet) SendError(w http.ResponseWriter, req *http.Request, code int, msg string, aErr error) {
|
||||||
fmt.Printf("[%d][%s]. Rendering template for code %d with message: %s\n", code, req.URL.Path, code, msg)
|
fmt.Printf("[%d][%s]. Rendering template for code %d with message: %s\n", code, req.URL.Path, code, msg)
|
||||||
fmt.Println(aErr)
|
out, _ := json.Marshal(aErr)
|
||||||
|
fmt.Println(string(out))
|
||||||
tmpl, exists := s.ErrorRoutes[code]
|
tmpl, exists := s.ErrorRoutes[code]
|
||||||
if exists {
|
if exists {
|
||||||
tmpl.TemplateManager.AddMixin("code", strconv.Itoa(code))
|
err := tmpl.TemplateManager.AddMixin("code", strconv.Itoa(code))
|
||||||
tmpl.TemplateManager.AddMixin("msg", msg)
|
|
||||||
w.WriteHeader(code)
|
|
||||||
_, err := w.Write([]byte(tmpl.TemplateManager.RenderTemplate(fmt.Sprintf("%d.html", code))))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
writeGeneric(w, code, msg)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return
|
err = tmpl.TemplateManager.AddMixin("msg", msg)
|
||||||
} else {
|
|
||||||
w.WriteHeader(code)
|
|
||||||
errorPayload := ErrorResponse{Code: code, Message: msg}
|
|
||||||
genericError, sErr := json.Marshal(errorPayload)
|
|
||||||
if sErr != nil {
|
|
||||||
panic(sErr)
|
|
||||||
}
|
|
||||||
_, err := w.Write(genericError)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
writeGeneric(w, code, msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(code)
|
||||||
|
bitties, err := tmpl.TemplateManager.RenderTemplate(fmt.Sprintf("%d.html", code))
|
||||||
|
_, err = w.Write([]byte(bitties))
|
||||||
|
if err != nil {
|
||||||
|
writeGeneric(w, code, msg)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
writeGeneric(w, code, msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeGeneric(w http.ResponseWriter, code int, msg string) {
|
||||||
|
w.WriteHeader(code)
|
||||||
|
errorPayload := ErrorResponse{Code: code, Message: msg}
|
||||||
|
genericError, _ := json.Marshal(errorPayload)
|
||||||
|
w.Write(genericError)
|
||||||
}
|
}
|
||||||
|
|||||||
24
servlet/server_test.go
Normal file
24
servlet/server_test.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package servlet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewServlet(t *testing.T) {
|
||||||
|
|
||||||
|
port := ":8080"
|
||||||
|
|
||||||
|
srv := NewServlet(port)
|
||||||
|
|
||||||
|
if srv.Port != port {
|
||||||
|
t.Errorf("Expected port %s, got %s", port, srv.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
if srv.Routes != nil {
|
||||||
|
t.Error("Expected Routes to be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if srv.ErrorRoutes == nil {
|
||||||
|
t.Error("Expected ErrorRoutes to be initialized")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ package servlet
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
util "geniuscartel.xyz/vinegar/vinegarUtil"
|
util "geniuscartel.xyz/vinegar/vinegarUtil"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -45,7 +46,7 @@ type (
|
|||||||
//
|
//
|
||||||
// This function signature allows encapsulating the creation of different types of FileRoutes. It is used to define
|
// This function signature allows encapsulating the creation of different types of FileRoutes. It is used to define
|
||||||
// constructor functions for each file type, like NewTextRoute or NewImageRoute.
|
// constructor functions for each file type, like NewTextRoute or NewImageRoute.
|
||||||
RouteConstructor func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) *FileRoute
|
RouteConstructor func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) (*FileRoute, error)
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewTextRoute creates a new FileRoute for serving text files.
|
// NewTextRoute creates a new FileRoute for serving text files.
|
||||||
@ -66,10 +67,10 @@ type (
|
|||||||
//
|
//
|
||||||
// A FileRoute instance configured for serving text files, added to
|
// A FileRoute instance configured for serving text files, added to
|
||||||
// the provided VinegarHttpServlet.
|
// the provided VinegarHttpServlet.
|
||||||
var NewTextRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) *FileRoute {
|
var NewTextRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) (*FileRoute, error) {
|
||||||
fileRoot := filepath.Clean(pathlike)
|
fileRoot := filepath.Clean(pathlike)
|
||||||
if strings.Contains(fileRoot, "../") {
|
if strings.Contains(fileRoot, "../") {
|
||||||
panic("Traversing the directory is not allowed, use an absolute filepath instead")
|
return nil, errors.New("Traversing the directory is not allowed, use an absolute filepath instead")
|
||||||
}
|
}
|
||||||
defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
|
defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
|
||||||
route := FileRoute{srv: servlet, fileRoot: fileRoot, UseCache: useCache}
|
route := FileRoute{srv: servlet, fileRoot: fileRoot, UseCache: useCache}
|
||||||
@ -79,13 +80,13 @@ var NewTextRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPattern
|
|||||||
|
|
||||||
servlet.AddRoute(route.VinegarRoute)
|
servlet.AddRoute(route.VinegarRoute)
|
||||||
|
|
||||||
return &route
|
return &route, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var NewImageRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) *FileRoute {
|
var NewImageRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) (*FileRoute, error) {
|
||||||
fileRoot := filepath.Clean(pathlike)
|
fileRoot := filepath.Clean(pathlike)
|
||||||
if strings.Contains(fileRoot, "../") {
|
if strings.Contains(fileRoot, "../") {
|
||||||
panic("Traversing the directory is not allowed, use an absolute filepath instead")
|
return nil, errors.New("Traversing the directory is not allowed, use an absolute filepath instead")
|
||||||
}
|
}
|
||||||
defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
|
defaultPrune := strings.Replace(urlPattern, ".*", "", -1)
|
||||||
route := FileRoute{srv: servlet, fileRoot: fileRoot, UseCache: useCache}
|
route := FileRoute{srv: servlet, fileRoot: fileRoot, UseCache: useCache}
|
||||||
@ -93,17 +94,20 @@ var NewImageRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPatter
|
|||||||
route.VinegarRoute = rootRoute //i *kinda* don't like this pattern
|
route.VinegarRoute = rootRoute //i *kinda* don't like this pattern
|
||||||
|
|
||||||
servlet.AddRoute(route.VinegarRoute)
|
servlet.AddRoute(route.VinegarRoute)
|
||||||
return &route
|
return &route, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var NewSingleFileRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) *FileRoute {
|
var NewSingleFileRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlPattern string, pathlike string, useCache bool) (*FileRoute, error) {
|
||||||
route := FileRoute{
|
route := FileRoute{
|
||||||
srv: servlet,
|
srv: servlet,
|
||||||
fileRoot: pathlike,
|
fileRoot: pathlike,
|
||||||
UseCache: useCache,
|
UseCache: useCache,
|
||||||
}
|
}
|
||||||
singleFileServletHandler := createSingleFileServletFunction(&route)
|
singleFileServletHandler := createSingleFileServletFunction(&route)
|
||||||
sfCache := util.NewSingleFileCache(pathlike)
|
sfCache, err := util.NewSingleFileCache(pathlike)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
parentRoute := NewServletRoute(urlPattern, singleFileServletHandler)
|
parentRoute := NewServletRoute(urlPattern, singleFileServletHandler)
|
||||||
parentRoute.Handler = singleFileServletHandler
|
parentRoute.Handler = singleFileServletHandler
|
||||||
@ -113,7 +117,7 @@ var NewSingleFileRoute RouteConstructor = func(servlet *VinegarHttpServlet, urlP
|
|||||||
|
|
||||||
servlet.AddRoute(route.VinegarRoute)
|
servlet.AddRoute(route.VinegarRoute)
|
||||||
|
|
||||||
return &route
|
return &route, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// createSingleFileServletFunction creates a handler function for serving a single file.
|
// createSingleFileServletFunction creates a handler function for serving a single file.
|
||||||
@ -167,7 +171,7 @@ func createSingleFileServletFunction(route *FileRoute) VinegarHandlerFunction {
|
|||||||
w.Header().Add(ContentTypeHeaderKey, cache.MimeType)
|
w.Header().Add(ContentTypeHeaderKey, cache.MimeType)
|
||||||
_, err := w.Write(content)
|
_, err := w.Write(content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Println(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,20 +193,23 @@ func createCompressibleFileServletFunction(route *FileRoute, basePattern string,
|
|||||||
resourcePath := path.Join(pathRoot, filePath)
|
resourcePath := path.Join(pathRoot, filePath)
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
content, fileExists := util.GetDiskContent(resourcePath)
|
content, err := util.GetDiskContent(resourcePath)
|
||||||
if fileExists {
|
if err != nil {
|
||||||
if route.UseCache {
|
|
||||||
route.VinegarRoute.Cache.Put(stub, resourcePath)
|
|
||||||
cachedContent, _ = route.VinegarRoute.Cache.Get(stub)
|
|
||||||
} else {
|
|
||||||
w.Header().Add(ContentTypeHeaderKey, util.GuessMimetype(stub))
|
|
||||||
w.Write(*content)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
route.srv.SendError(w, req, 404, "Couldn't find your content.", errors.New("could not find valid file at ["+resourcePath+"]"))
|
route.srv.SendError(w, req, 404, "Couldn't find your content.", errors.New("could not find valid file at ["+resourcePath+"]"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if route.UseCache {
|
||||||
|
err := route.VinegarRoute.Cache.Put(stub, resourcePath)
|
||||||
|
if err != nil {
|
||||||
|
route.srv.SendError(w, req, 500, "Internal Server Error", err)
|
||||||
|
}
|
||||||
|
cachedContent, _ = route.VinegarRoute.Cache.Get(stub)
|
||||||
|
} else {
|
||||||
|
w.Header().Add(ContentTypeHeaderKey, util.GuessMimetype(stub))
|
||||||
|
w.Write(*content)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
w.Header().Add(ContentTypeHeaderKey, cachedContent.MimeType)
|
w.Header().Add(ContentTypeHeaderKey, cachedContent.MimeType)
|
||||||
var err error = nil
|
var err error = nil
|
||||||
@ -214,7 +221,7 @@ func createCompressibleFileServletFunction(route *FileRoute, basePattern string,
|
|||||||
_, err = w.Write(cachedContent.Content)
|
_, err = w.Write(cachedContent.Content)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -241,7 +248,7 @@ func createUncompressedFileServletFunction(route *FileRoute, basePattern string,
|
|||||||
w.Header().Add(ContentTypeHeaderKey, util.GuessMimetype(stub))
|
w.Header().Add(ContentTypeHeaderKey, util.GuessMimetype(stub))
|
||||||
_, err := w.Write(entry.Content)
|
_, err := w.Write(entry.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Println(err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@ -31,17 +31,17 @@ func NewTemplateManager(templatePath string, componentPath string) *TemplateMana
|
|||||||
return &tm
|
return &tm
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *TemplateManager) loadAssets() {
|
func (tm *TemplateManager) loadAssets() error {
|
||||||
templateFiles, err := os.ReadDir(tm.templatePath)
|
templateFiles, err := os.ReadDir(tm.templatePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
for _, templateFile := range templateFiles {
|
for _, templateFile := range templateFiles {
|
||||||
if !templateFile.IsDir() {
|
if !templateFile.IsDir() {
|
||||||
filePath := path.Join(tm.templatePath, templateFile.Name())
|
filePath := path.Join(tm.templatePath, templateFile.Name())
|
||||||
dataContent, exists := util.GetDiskContent(filePath)
|
dataContent, err := util.GetDiskContent(filePath)
|
||||||
if !exists {
|
if err != nil {
|
||||||
panic("Could not read file contents for " + filePath)
|
return err
|
||||||
}
|
}
|
||||||
tmpl := template.New(templateFile.Name())
|
tmpl := template.New(templateFile.Name())
|
||||||
tmpl.Parse(string(*dataContent))
|
tmpl.Parse(string(*dataContent))
|
||||||
@ -52,44 +52,43 @@ func (tm *TemplateManager) loadAssets() {
|
|||||||
|
|
||||||
componentFiles, err := os.ReadDir(tm.componentPath)
|
componentFiles, err := os.ReadDir(tm.componentPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, componentFile := range componentFiles {
|
for _, componentFile := range componentFiles {
|
||||||
if !componentFile.IsDir() {
|
if !componentFile.IsDir() {
|
||||||
filePath := path.Join(tm.componentPath, componentFile.Name())
|
filePath := path.Join(tm.componentPath, componentFile.Name())
|
||||||
contentBytes, exists := util.GetDiskContent(filePath)
|
contentBytes, err := util.GetDiskContent(filePath)
|
||||||
if !exists {
|
if err != nil {
|
||||||
panic("could not find file for " + filePath)
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
content := string(*contentBytes)
|
content := string(*contentBytes)
|
||||||
|
|
||||||
tm.components[componentFile.Name()] = template.HTML(content)
|
tm.components[componentFile.Name()] = template.HTML(content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *TemplateManager) RenderTemplate(key string) string {
|
func (tm *TemplateManager) RenderTemplate(key string) (string, error) {
|
||||||
tmpl, exists := tm.templates[key]
|
tmpl, exists := tm.templates[key]
|
||||||
if !exists {
|
if !exists {
|
||||||
panic("Could not load template for key: " + key)
|
return "", errors.New("Could not load template for key: " + key)
|
||||||
}
|
}
|
||||||
buff := strings.Builder{}
|
buff := strings.Builder{}
|
||||||
err := tmpl.Execute(&buff, tm)
|
err := tmpl.Execute(&buff, tm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return "", err
|
||||||
}
|
}
|
||||||
return buff.String()
|
return buff.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *TemplateManager) GetComponent(key string) template.HTML {
|
func (tm *TemplateManager) GetComponent(key string) (template.HTML, error) {
|
||||||
content, exists := tm.components[key]
|
content, exists := tm.components[key]
|
||||||
if !exists {
|
if !exists {
|
||||||
panic("Could not load data for key: " + key)
|
return "", errors.New("Could not load data for key: " + key)
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
return content
|
return content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *TemplateManager) GetMixin(key string) template.HTML {
|
func (tm *TemplateManager) GetMixin(key string) template.HTML {
|
||||||
@ -107,50 +106,54 @@ func (tm *TemplateManager) AddMixin(key string, value string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tm *TemplateManager) AddMixinFromFile(key string, pathlike string) error {
|
func (tm *TemplateManager) AddMixinFromFile(key string, pathlike string) error {
|
||||||
content, exists := util.GetDiskContent(pathlike)
|
content, err := util.GetDiskContent(pathlike)
|
||||||
if exists {
|
if err != nil {
|
||||||
tm.AddMixin(key, string(*content))
|
return err
|
||||||
return nil
|
|
||||||
} else {
|
} else {
|
||||||
return errors.New("mixin source file not found at " + pathlike)
|
err2 := tm.AddMixin(key, string(*content))
|
||||||
|
if err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *TemplateManager) RenderAllToFile(pathlike string) {
|
func (tm *TemplateManager) RenderAllToFile(pathlike string) error {
|
||||||
for _, v := range tm.Templates {
|
for _, v := range tm.Templates {
|
||||||
path := path.Join(pathlike, v)
|
p := path.Join(pathlike, v)
|
||||||
|
|
||||||
tmpl := tm.templates[v]
|
tmpl := tm.templates[v]
|
||||||
buff := strings.Builder{}
|
buff := strings.Builder{}
|
||||||
err := tmpl.Execute(&buff, tm)
|
err := tmpl.Execute(&buff, tm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ioutil.WriteFile(path, []byte(buff.String()), 0755)
|
err = ioutil.WriteFile(p, []byte(buff.String()), 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RenderTemplate(w http.ResponseWriter, pathlike string, data any) {
|
func RenderTemplate(w http.ResponseWriter, pathlike string, data any) error {
|
||||||
templateHelper := template.New(pathlike)
|
templateHelper := template.New(pathlike)
|
||||||
f, err := os.OpenFile(pathlike, os.O_RDONLY, 0777)
|
f, err := os.OpenFile(pathlike, os.O_RDONLY, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
content, err := ioutil.ReadAll(f)
|
content, err := ioutil.ReadAll(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
templ, err := templateHelper.Parse(string(content))
|
templ, err := templateHelper.Parse(string(content))
|
||||||
err = templ.Execute(w, data)
|
err = templ.Execute(w, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,36 +7,36 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetDiskContent(filePath string) (*[]byte, bool) {
|
func GetDiskContent(filePath string) (*[]byte, error) {
|
||||||
_, ferr := os.Stat(filePath)
|
_, ferr := os.Stat(filePath)
|
||||||
if os.IsNotExist(ferr) {
|
if os.IsNotExist(ferr) {
|
||||||
return nil, false
|
return nil, ferr
|
||||||
} else {
|
} else {
|
||||||
f, err := os.OpenFile(filePath, os.O_RDONLY, 0755)
|
f, err := os.OpenFile(filePath, os.O_RDONLY, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
content, err := ioutil.ReadAll(f)
|
content, err := ioutil.ReadAll(f)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
return &content, true
|
return &content, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GZipBytes(uncompressed *[]byte) *[]byte {
|
func GZipBytes(uncompressed *[]byte) (*[]byte, error) {
|
||||||
buff := bytes.Buffer{}
|
buff := bytes.Buffer{}
|
||||||
gzip := gzip2.NewWriter(&buff)
|
gzip := gzip2.NewWriter(&buff)
|
||||||
_, err := gzip.Write(*uncompressed)
|
_, err := gzip.Write(*uncompressed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
err = gzip.Flush()
|
err = gzip.Flush()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
compressed := buff.Bytes()
|
compressed := buff.Bytes()
|
||||||
return &compressed
|
return &compressed, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
package vinegarUtil
|
package vinegarUtil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
@ -50,17 +49,23 @@ func NewLRU(size int64) *Lru {
|
|||||||
return &Lru{&cLru, size, 0}
|
return &Lru{&cLru, size, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSingleFileCache(pathlike string) *SingleFileCache {
|
func NewSingleFileCache(pathlike string) (*SingleFileCache, error) {
|
||||||
entry := newLRUEntry(pathlike)
|
entry, err := newLRUEntry(pathlike)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
sfc := SingleFileCache{path: pathlike, entry: entry}
|
sfc := SingleFileCache{path: pathlike, entry: entry}
|
||||||
return &sfc
|
return &sfc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Lru) Get(key string) (*LruEntry, bool) {
|
func (l *Lru) Get(key string) (*LruEntry, bool) {
|
||||||
entry, exists := (*l.entries)[key]
|
entry, exists := (*l.entries)[key]
|
||||||
if exists && entry.expires.Before(time.Now()) {
|
if exists && entry.expires.Before(time.Now()) {
|
||||||
fmt.Println("Cache miss due to expired content")
|
fmt.Println("Cache miss due to expired content")
|
||||||
entry.Reload()
|
err := entry.Reload()
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
return entry, true
|
return entry, true
|
||||||
} else {
|
} else {
|
||||||
if exists {
|
if exists {
|
||||||
@ -73,7 +78,10 @@ func (l *Lru) Get(key string) (*LruEntry, bool) {
|
|||||||
func (l *Lru) GetFresh(key string) (*LruEntry, bool) {
|
func (l *Lru) GetFresh(key string) (*LruEntry, bool) {
|
||||||
entry, exists := l.Get(key)
|
entry, exists := l.Get(key)
|
||||||
if exists {
|
if exists {
|
||||||
entry.Reload()
|
err := entry.Reload()
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return l.Get(key)
|
return l.Get(key)
|
||||||
}
|
}
|
||||||
@ -84,11 +92,13 @@ func (l *Lru) Put(key string, pathlike string) error {
|
|||||||
var size int64
|
var size int64
|
||||||
|
|
||||||
if exists {
|
if exists {
|
||||||
content, fExists := GetDiskContent(pathlike)
|
content, err := GetDiskContent(pathlike)
|
||||||
if !fExists {
|
if err != nil {
|
||||||
return errors.New("attempted to refresh cache with file that no longer exists on disk")
|
return err
|
||||||
}
|
}
|
||||||
zippedContent := *GZipBytes(content)
|
zippedBytes, err := GZipBytes(content)
|
||||||
|
zippedContent := *zippedBytes
|
||||||
|
|
||||||
size = int64(len(*content)) + int64(len(zippedContent))
|
size = int64(len(*content)) + int64(len(zippedContent))
|
||||||
l.ensureVacancy(size)
|
l.ensureVacancy(size)
|
||||||
|
|
||||||
@ -99,8 +109,11 @@ func (l *Lru) Put(key string, pathlike string) error {
|
|||||||
entry.mostRecentAccess = time.Now()
|
entry.mostRecentAccess = time.Now()
|
||||||
entry.totalSize = size
|
entry.totalSize = size
|
||||||
} else {
|
} else {
|
||||||
entry = newLRUEntry(pathlike)
|
nEntry, err := newLRUEntry(pathlike)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
entry = nEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
(*l.entries)[key] = entry
|
(*l.entries)[key] = entry
|
||||||
@ -143,9 +156,7 @@ func (l *Lru) getLeastRecentlyUsedKey() string {
|
|||||||
benchmark = entry
|
benchmark = entry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if leastUsedKey == "" {
|
|
||||||
panic("Invalid key for LRU: [" + leastUsedKey + "]")
|
|
||||||
}
|
|
||||||
return leastUsedKey
|
return leastUsedKey
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,13 +168,23 @@ func (l *Lru) recalcSize() {
|
|||||||
l.currentSize = total
|
l.currentSize = total
|
||||||
}
|
}
|
||||||
|
|
||||||
func (le *LruEntry) Reload() {
|
func (le *LruEntry) Reload() error {
|
||||||
content, _ := GetDiskContent(le.path)
|
|
||||||
|
content, err := GetDiskContent(le.path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
zippedBytes, err := GZipBytes(content)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
le.Content = *content
|
le.Content = *content
|
||||||
le.CompressedContent = *GZipBytes(content)
|
le.CompressedContent = *zippedBytes
|
||||||
le.created = time.Now()
|
le.created = time.Now()
|
||||||
le.mostRecentAccess = le.created
|
le.mostRecentAccess = le.created
|
||||||
le.expires = le.created.Add(time.Minute * DefaultCacheTimeInMinutes)
|
le.expires = le.created.Add(time.Minute * DefaultCacheTimeInMinutes)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfc *SingleFileCache) Get(key string) (*LruEntry, bool) {
|
func (sfc *SingleFileCache) Get(key string) (*LruEntry, bool) {
|
||||||
@ -171,14 +192,20 @@ func (sfc *SingleFileCache) Get(key string) (*LruEntry, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sfc *SingleFileCache) GetFresh(key string) (*LruEntry, bool) {
|
func (sfc *SingleFileCache) GetFresh(key string) (*LruEntry, bool) {
|
||||||
sfc.entry.Reload()
|
err := sfc.entry.Reload()
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
return sfc.entry, true
|
return sfc.entry, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put THIS WILL DELETE ANYTHING YOU HAVE STORED IN THIS CACHE.
|
// Put THIS WILL DELETE ANYTHING YOU HAVE STORED IN THIS CACHE.
|
||||||
func (sfc *SingleFileCache) Put(key string, pathlike string) error {
|
func (sfc *SingleFileCache) Put(key string, pathlike string) error {
|
||||||
//there's a bug in this. we don't return an error from newLRUEntry, so if the file disappears, the server will die
|
//there's a bug in this. we don't return an error from newLRUEntry, so if the file disappears, the server will die
|
||||||
entry := newLRUEntry(pathlike)
|
entry, err := newLRUEntry(pathlike)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
sfc.entry = entry
|
sfc.entry = entry
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -187,13 +214,16 @@ func (sfc *SingleFileCache) Remove(key string) {
|
|||||||
//i'm actually not sure what to do here. why would you ever remove from a single-file cache?
|
//i'm actually not sure what to do here. why would you ever remove from a single-file cache?
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLRUEntry(pathlike string) *LruEntry {
|
func newLRUEntry(pathlike string) (*LruEntry, error) {
|
||||||
bits, exists := GetDiskContent(pathlike)
|
bits, err := GetDiskContent(pathlike)
|
||||||
if !exists {
|
if err != nil {
|
||||||
panic("Could not load single file for single file path: " + pathlike)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
compressedBits := GZipBytes(bits)
|
compressedBits, err := GZipBytes(bits)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
size := int64(len(*bits)) + int64(len(*compressedBits))
|
size := int64(len(*bits)) + int64(len(*compressedBits))
|
||||||
|
|
||||||
@ -207,7 +237,7 @@ func newLRUEntry(pathlike string) *LruEntry {
|
|||||||
totalSize: size,
|
totalSize: size,
|
||||||
MimeType: GuessMimetype(pathlike),
|
MimeType: GuessMimetype(pathlike),
|
||||||
}
|
}
|
||||||
return &entry
|
return &entry, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GuessMimetype(filePath string) string {
|
func GuessMimetype(filePath string) string {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user