Skip to content

Commit

Permalink
Configurable index template and static files
Browse files Browse the repository at this point in the history
Iterating on riotbib's Pull Request #45, it is now possible to overwrite
the index.html template file during startup. Furthermore, static files
are now also supported, e.g., for stylesheets.
  • Loading branch information
oxzi committed Nov 13, 2023
1 parent cb94598 commit 307b416
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 13 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ Types of changes:
- Add goshy as bash script and NixOS program, [@riotbib](https://github.com/riotbib) in [#27](https://github.com/oxzi/gosh/pull/27).
- Created Store RPC working on Unix domain sockets to allow a `fork`+`exec`ed daemon.
- Configuration through YAML configuration file.
- Configurable index template and static files, partially by [@riotbib](https://github.com/riotbib) in [#45](https://github.com/oxzi/gosh/pull/45).

### Changed
- Dependency version bumps.
- Great structural refactoring.
- `goshd` became `gosh`.
- Made `gosh` a `chroot`ed, privilege dropped, `fork`+`exec`ed daemon.
- OpenBSD installation changed due to structural program changes.
- Extract web template into a more editable file, [@riotbib](https://github.com/riotbib) in [#45](https://github.com/oxzi/gosh/pull/45).
- Bumped required Go version from 1.19 to 1.21.
- Replaced logrus logging with Go's new `log/slog` and do wrapping for child processes.

Expand Down
12 changes: 12 additions & 0 deletions gosh.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ import (
"gopkg.in/yaml.v3"
)

// StaticFileConfig describes a static_files from the YAML and holds its data.
type StaticFileConfig struct {
Path string `yaml:"path"`
Mime string `yaml:"mime"`

data []byte
}

// Config is the struct representation of gosh's YAML configuration file.
//
// For each field's meaning, please consider the gosh.yml file in this
Expand Down Expand Up @@ -40,6 +48,10 @@ type Config struct {

UrlPrefix string `yaml:"url_prefix"`

CustomIndex string `yaml:"custom_index"`

StaticFiles map[string]StaticFileConfig `yaml:"static_files"`

ItemConfig struct {
MaxSize string `yaml:"max_size"`
MaxLifetime time.Duration `yaml:"max_lifetime"`
Expand Down
14 changes: 14 additions & 0 deletions gosh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,20 @@ webserver:
# url_prefix is an optional prefix in URL to be used, e.g., "/gosh"
url_prefix: ""

# custom_index will be used instead of the compiled in index.html template.
# For starters, copy the index.html from the repository somewhere nice.
custom_index: "/path/to/alternative/index.html"

# static_files to be read during startup and returned instead of being passed
# against the store's database. This might be used for custom resources.
static_files:
"/favicon.ico":
path: "/path/to/favicon.ico"
mime: "image/vnd.microsoft.icon"
"/custom.css":
path: "/path/to/custom.css"
mime: "text/css"

# item_config sets restrictions for new items, e.g., their max_size, in bytes
# or suffixed with a unit, and max_lifetime, as a Go duration. Furthermore,
# some MIME types might be dropped by mime_drop or rewritten with mime_map.
Expand Down
46 changes: 44 additions & 2 deletions gosh_webserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"io"
"io/fs"
"log/slog"
"net"
Expand Down Expand Up @@ -95,6 +96,43 @@ func mainWebserver(conf Config) {

storeClient := NewStoreRpcClient(rpcConn, fdConn)

indexTpl := ""
if conf.Webserver.CustomIndex != "" {
f, err := os.Open(conf.Webserver.CustomIndex)
if err != nil {
slog.Error("Failed to open custom index file", slog.Any("error", err))
os.Exit(1)
}

indexTplRaw, err := io.ReadAll(f)
if err != nil {
slog.Error("Failed to read custom index file", slog.Any("error", err))
os.Exit(1)
}
_ = f.Close()

indexTpl = string(indexTplRaw)
}

for k, sfc := range conf.Webserver.StaticFiles {
f, err := os.Open(sfc.Path)
if err != nil {
slog.Error("Failed to open static file",
slog.String("file", sfc.Path), slog.Any("error", err))
os.Exit(1)
}

sfc.data, err = io.ReadAll(f)
if err != nil {
slog.Error("Failed to read static file",
slog.String("file", sfc.Path), slog.Any("error", err))
os.Exit(1)
}
_ = f.Close()

conf.Webserver.StaticFiles[k] = sfc
}

maxFilesize, err := ParseBytesize(conf.Webserver.ItemConfig.MaxSize)
if err != nil {
slog.Error("Failed to parse byte size", slog.Any("error", err))
Expand Down Expand Up @@ -159,11 +197,15 @@ func mainWebserver(conf Config) {

server, err := NewServer(
storeClient,
maxFilesize, conf.Webserver.ItemConfig.MaxLifetime,
maxFilesize,
conf.Webserver.ItemConfig.MaxLifetime,
conf.Webserver.Contact,
mimeDrop,
conf.Webserver.ItemConfig.MimeMap,
conf.Webserver.UrlPrefix)
conf.Webserver.UrlPrefix,
indexTpl,
conf.Webserver.StaticFiles,
)
if err != nil {
slog.Error("Failed to create webserver", slog.Any("error", err))
os.Exit(1)
Expand Down
52 changes: 42 additions & 10 deletions webserver.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bytes"
"context"
"fmt"
"html/template"
Expand All @@ -17,7 +18,7 @@ import (
)

//go:embed index.html
var indexTpl string
var defaultIndexTpl string

const (
msgDeletionKeyWrong = "Error: Deletion key is incorrect."
Expand All @@ -39,6 +40,8 @@ type Server struct {
mimeDrop map[string]struct{}
mimeMap map[string]string
urlPrefix string
indexTpl *template.Template
staticFiles map[string]StaticFileConfig
}

// NewServer creates a new Server with a given database directory, and
Expand All @@ -51,7 +54,19 @@ func NewServer(
mimeDrop map[string]struct{},
mimeMap map[string]string,
urlPrefix string,
indexTplRaw string,
staticFiles map[string]StaticFileConfig,
) (s *Server, err error) {
indexTpl := defaultIndexTpl
if indexTplRaw != "" {
indexTpl = indexTplRaw
}

t, err := template.New("index").Parse(indexTpl)
if err != nil {
return nil, err
}

s = &Server{
store: store,
maxSize: maxSize,
Expand All @@ -60,6 +75,8 @@ func NewServer(
mimeDrop: mimeDrop,
mimeMap: mimeMap,
urlPrefix: urlPrefix,
indexTpl: t,
staticFiles: staticFiles,
}
return
}
Expand Down Expand Up @@ -98,6 +115,8 @@ func (serv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
serv.handleRoot(w, r)
} else if strings.HasPrefix(reqPath, "/del/") {
serv.handleDeletion(w, r)
} else if stc, ok := serv.staticFiles[reqPath]; ok {
serv.handleStaticFile(w, r, stc)
} else {
serv.handleRequest(w, r)
}
Expand All @@ -119,14 +138,6 @@ func (serv *Server) handleRoot(w http.ResponseWriter, r *http.Request) {
}

func (serv *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
t, err := template.New("index").Parse(indexTpl)
if err != nil {
slog.Error("Failed to parse template", slog.Any("error", err))

http.Error(w, msgGenericError, http.StatusBadRequest)
return
}

data := struct {
Expires string
Size string
Expand All @@ -148,11 +159,32 @@ func (serv *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html;charset=UTF-8")
w.WriteHeader(http.StatusOK)

if err := t.Execute(w, data); err != nil {
if err := serv.indexTpl.Execute(w, data); err != nil {
slog.Error("Failed to execute template", slog.Any("error", err))
}
}

func (serv *Server) handleStaticFile(w http.ResponseWriter, r *http.Request, sfc StaticFileConfig) {
if r.Method != http.MethodGet {
slog.Debug("Request with unsupported method", slog.String("method", r.Method))

http.Error(w, msgUnsupportedMethod, http.StatusMethodNotAllowed)
return
}

w.Header().Set("Content-Type", sfc.Mime)
w.WriteHeader(http.StatusOK)

staticReader := bytes.NewReader(sfc.data)
_, err := io.Copy(w, staticReader)
if err != nil {
slog.Error("Failed to write static file back to request", slog.Any("error", err))

http.Error(w, msgGenericError, http.StatusBadRequest)
return
}
}

func (serv *Server) handleUpload(w http.ResponseWriter, r *http.Request) {
item, f, err := NewItemFromRequest(r, serv.maxSize, serv.maxLifetime)
if err == ErrLifetimeTooLong {
Expand Down

0 comments on commit 307b416

Please sign in to comment.