dashboard / erock/git-pr / feat: static assets #3 rss

accepted · opened on 2024-07-19T14:53:23Z by erock
Help
checkout latest patchset:
ssh pr.pico.sh print pr-3 | git am -3
checkout any patchset in a patch request:
ssh pr.pico.sh print ps-X | git am -3
add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh pr add 3
add review to patch request:
git format-patch main --stdout | ssh pr.pico.sh pr add --review 3
accept PR:
ssh pr.pico.sh pr accept 3
close PR:
ssh pr.pico.sh pr close 3

Logs

erock created pr with ps-3 on 2024-07-19T14:53:23Z
erock added ps-4 on 2024-07-19T14:55:48Z
jolheiser reviewed pr with ps-5 on 2024-07-19T15:42:29Z
jolheiser changed status on 2024-07-19T15:42:29Z {"status":"reviewed"}
erock added ps-9 on 2024-07-19T17:43:47Z
jolheiser changed status on 2024-07-19T18:27:53Z {"status":"accepted"}

Patchsets

ps-3 by erock on 2024-07-19T14:53:23Z
Range Diff ↕ rd-4
1: 3b99dc0 < -: ------- feat: static assets
4: d8792d5 ! 1: 66cafc6 feat: static assets folder
2: 8919af5 ! 2: 5e76ed3 fix(cli): access control for removing patchsets
3: 7346122 < -: ------- chore: create patch for smol
ps-4 by erock on 2024-07-19T14:55:48Z
Range Diff ↕ rd-5
1: 66cafc6 = 1: cc56ea1 feat: static assets folder
2: 5e76ed3 < -: ------- fix(cli): access control for removing patchsets
-: ------- > 2: ef749a4 review: typo and future enhancement comment
ps-5 by jolheiser on 2024-07-19T15:42:29Z
Range Diff ↕ rd-9
1: cc56ea1 = 1: 0467f9e feat: static assets folder
2: ef749a4 = 2: da1730f review: typo and future enhancement comment
-: ------- > 3: c038404 refactor: per-file override for static folder
ps-9 by erock on 2024-07-19T17:43:47Z

Range-diff rd-4

title
feat: static assets
description
Patch removed
old #1
3b99dc0
new #0
(none)
title
feat: static assets folder
description
Patch changed
old #4
d8792d5
new #1
66cafc6
title
fix(cli): access control for removing patchsets
description
Patch changed
old #2
8919af5
new #2
5e76ed3
title
chore: create patch for smol
description
Patch removed
old #3
7346122
new #0
(none)
Back to top
1: 3b99dc0 < -: ------- feat: static assets
4: d8792d5 ! 1: 66cafc6 feat: static assets folder
web.go web.go
 	"io"
 	"io/fs"
 	"log/slog"
+	"mime"
 	"net/http"
 	"os"
 	"path/filepath"
 		logger := web.Logger
 
 		file := r.PathValue("file")
+		logger.Info("serving file", "file", file, "fs", staticfs)
 		reader, err := staticfs.Open(file)
 		if err != nil {
 			logger.Error(err.Error())
 			http.Error(w, "file not found", 404)
 			return
 		}
-		contentType := http.DetectContentType(contents)
+		contentType := mime.TypeByExtension(filepath.Ext(file))
+		if contentType == "" {
+			contentType = http.DetectContentType(contents)
+		}
 		w.Header().Add("Content-Type", contentType)
 
 		_, err = w.Write(contents)
 	}
 }
 
-type StaticFs struct {
-	Dir string
-}
+func getFileSystem(logger *slog.Logger, ffs embed.FS, datadir string, dirName string) (fs.FS, error) {
+	dir := filepath.Join(datadir, dirName)
+	_, err := os.Stat(dir)
+	if err == nil {
+		logger.Info("found folder in data_dir", "dir", dir)
+		return os.DirFS(dir), nil
+	}
 
-func (fs *StaticFs) Open(name string) (os.File, error) {
-	fp, err := os.Open(filepath.Join(fs.Dir, name))
-	return *fp, err
+	logger.Info("using embeded folder", "dir", dir)
+	fsys, err := fs.Sub(ffs, dirName)
+	if err != nil {
+		return nil, err
+	}
+
+	return fsys, nil
 }
 
 func StartWebServer(cfg *GitCfg) {
 	dbpath := filepath.Join(cfg.DataDir, "pr.db")
 	dbh, err := Open(dbpath, cfg.Logger)
 	if err != nil {
-		panic(fmt.Sprintf("cannot find database file, check folder and perms: %s", dbpath))
+		panic(fmt.Sprintf("cannot find database file, check folder and perms: %s: %s", dbpath, err))
 	}
 
 	be := &Backend{
 	http.HandleFunc("GET /", ctxMdw(ctx, repoListHandler))
 	http.HandleFunc("GET /syntax.css", ctxMdw(ctx, chromaStyleHandler))
 	http.HandleFunc("GET /rss", ctxMdw(ctx, rssHandler))
-	dir := filepath.Join(cfg.DataDir, "static")
-	var filesys fs.FS = staticFS
-	_, err = os.Stat(dir)
-	if err == nil {
-		cfg.Logger.Info("detected static folder, using instead of the default one")
-		filesys = os.DirFS(dir)
+	filesys, err := getFileSystem(cfg.Logger, staticFS, cfg.DataDir, "static")
+	if err != nil {
+		panic(err)
 	}
-	http.HandleFunc("GET /static/{file}", serveFile(filesys))
+	http.HandleFunc("GET /static/{file}", ctxMdw(ctx, serveFile(filesys)))
 
 	cfg.Logger.Info("starting web server", "addr", addr)
 	err = http.ListenAndServe(addr, nil)
web.go web.go
 	"embed"
 	"fmt"
 	"html/template"
+	"io"
+	"io/fs"
 	"log/slog"
+	"mime"
 	"net/http"
+	"os"
 	"path/filepath"
 	"slices"
 	"strconv"
 //go:embed tmpl/*
 var tmplFS embed.FS
 
+//go:embed static/*
+var staticFS embed.FS
+
 type WebCtx struct {
 	Pr        *PrCmd
 	Backend   *Backend
 	}
 }
 
+func serveFile(staticfs fs.FS) func(w http.ResponseWriter, r *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		web, err := getWebCtx(r)
+		if err != nil {
+			w.WriteHeader(http.StatusUnprocessableEntity)
+			return
+		}
+		logger := web.Logger
+
+		file := r.PathValue("file")
+		logger.Info("serving file", "file", file, "fs", staticfs)
+		reader, err := staticfs.Open(file)
+		if err != nil {
+			logger.Error(err.Error())
+			http.Error(w, "file not found", 404)
+			return
+		}
+		contents, err := io.ReadAll(reader)
+		if err != nil {
+			logger.Error(err.Error())
+			http.Error(w, "file not found", 404)
+			return
+		}
+		contentType := mime.TypeByExtension(filepath.Ext(file))
+		if contentType == "" {
+			contentType = http.DetectContentType(contents)
+		}
+		w.Header().Add("Content-Type", contentType)
+
+		_, err = w.Write(contents)
+		if err != nil {
+			logger.Error(err.Error())
+			http.Error(w, "server error", 500)
+			return
+		}
+	}
+}
+
+func getFileSystem(logger *slog.Logger, ffs embed.FS, datadir string, dirName string) (fs.FS, error) {
+	dir := filepath.Join(datadir, dirName)
+	_, err := os.Stat(dir)
+	if err == nil {
+		logger.Info("found folder in data_dir", "dir", dir)
+		return os.DirFS(dir), nil
+	}
+
+	logger.Info("using embeded folder", "dir", dir)
+	fsys, err := fs.Sub(ffs, dirName)
+	if err != nil {
+		return nil, err
+	}
+
+	return fsys, nil
+}
+
 func StartWebServer(cfg *GitCfg) {
 	addr := fmt.Sprintf("%s:%s", cfg.Host, cfg.WebPort)
 
 	dbpath := filepath.Join(cfg.DataDir, "pr.db")
 	dbh, err := Open(dbpath, cfg.Logger)
 	if err != nil {
-		panic(fmt.Sprintf("cannot find database file, check folder and perms: %s", dbpath))
+		panic(fmt.Sprintf("cannot find database file, check folder and perms: %s: %s", dbpath, err))
 	}
 
 	be := &Backend{
 	http.HandleFunc("GET /", ctxMdw(ctx, repoListHandler))
 	http.HandleFunc("GET /syntax.css", ctxMdw(ctx, chromaStyleHandler))
 	http.HandleFunc("GET /rss", ctxMdw(ctx, rssHandler))
+	filesys, err := getFileSystem(cfg.Logger, staticFS, cfg.DataDir, "static")
+	if err != nil {
+		panic(err)
+	}
+	http.HandleFunc("GET /static/{file}", ctxMdw(ctx, serveFile(filesys)))
 
 	cfg.Logger.Info("starting web server", "addr", addr)
 	err = http.ListenAndServe(addr, nil)