This commit is contained in:
		@@ -11,21 +11,17 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Router struct {
 | 
			
		||||
	static fs.FS
 | 
			
		||||
	lib    Library
 | 
			
		||||
	rcol   RecordCollection
 | 
			
		||||
	static  fs.FS
 | 
			
		||||
	lib     Library
 | 
			
		||||
	rcol    RecordCollection
 | 
			
		||||
	ts      *tailscale.LocalClient
 | 
			
		||||
	isAdmin bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AdminRouter struct {
 | 
			
		||||
	static fs.FS
 | 
			
		||||
	lib    Library
 | 
			
		||||
	ts     *tailscale.LocalClient
 | 
			
		||||
}
 | 
			
		||||
type path map[string]func()
 | 
			
		||||
 | 
			
		||||
type handler map[string]func()
 | 
			
		||||
 | 
			
		||||
func (h handler) Handle(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
	if f, ok := h[req.Method]; ok {
 | 
			
		||||
func (h path) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	if f, ok := h[r.Method]; ok {
 | 
			
		||||
		f()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -50,31 +46,33 @@ func writeJSON(w http.ResponseWriter, b any, status int) {
 | 
			
		||||
 | 
			
		||||
func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	switch r.URL.Path {
 | 
			
		||||
	case "/api/records":
 | 
			
		||||
		handler{
 | 
			
		||||
			http.MethodGet: func() { getRecords(router.rcol, w, r) },
 | 
			
		||||
		}.Handle(w, r)
 | 
			
		||||
	case "/api/books":
 | 
			
		||||
		handler{
 | 
			
		||||
			http.MethodGet: func() { getBooks(router.lib, w, r) },
 | 
			
		||||
		}.Handle(w, r)
 | 
			
		||||
	default:
 | 
			
		||||
		static(router.static).ServeHTTP(w, r)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (router *AdminRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	switch r.URL.Path {
 | 
			
		||||
	case "/api/mode":
 | 
			
		||||
		path{
 | 
			
		||||
			http.MethodGet: func() {
 | 
			
		||||
				writeJSON(w, struct{ Admin bool }{Admin: router.isAdmin}, http.StatusOK)
 | 
			
		||||
			},
 | 
			
		||||
		}.ServeHTTP(w, r)
 | 
			
		||||
	case "/api/whoami":
 | 
			
		||||
		handler{
 | 
			
		||||
		if !router.isAdmin {
 | 
			
		||||
			http.NotFoundHandler().ServeHTTP(w, r)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		path{
 | 
			
		||||
			http.MethodGet: func() { getWhoAmI(router.ts, w, r) },
 | 
			
		||||
		}.Handle(w, r)
 | 
			
		||||
		}.ServeHTTP(w, r)
 | 
			
		||||
	case "/api/records":
 | 
			
		||||
		path{
 | 
			
		||||
			http.MethodGet: func() { getRecords(router.rcol, w, r) },
 | 
			
		||||
		}.ServeHTTP(w, r)
 | 
			
		||||
	case "/api/books":
 | 
			
		||||
		handler{
 | 
			
		||||
			http.MethodGet:    func() { getBooks(router.lib, w, r) },
 | 
			
		||||
			http.MethodPost:   func() { addBook(router.lib, w, r) },
 | 
			
		||||
			http.MethodDelete: func() { deleteBook(router.lib, w, r) },
 | 
			
		||||
		}.Handle(w, r)
 | 
			
		||||
		p := path{
 | 
			
		||||
			http.MethodGet: func() { getBooks(router.lib, w, r) },
 | 
			
		||||
		}
 | 
			
		||||
		if router.isAdmin {
 | 
			
		||||
			p[http.MethodPost] = func() { addBook(router.lib, w, r) }
 | 
			
		||||
			p[http.MethodDelete] = func() { deleteBook(router.lib, w, r) }
 | 
			
		||||
		}
 | 
			
		||||
		p.ServeHTTP(w, r)
 | 
			
		||||
	default:
 | 
			
		||||
		static(router.static).ServeHTTP(w, r)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -55,24 +55,26 @@ func main() {
 | 
			
		||||
		c.DiscogsToken, time.Hour*24, c.DiscogsUser, c.DiscogsPersist, c.DiscogsCacheFile,
 | 
			
		||||
	))
 | 
			
		||||
 | 
			
		||||
	frontendRoot := must.Get(frontend.Root())
 | 
			
		||||
	adminRoot := must.Get(frontend.AdminRoot())
 | 
			
		||||
	staticRoot := must.Get(frontend.Root())
 | 
			
		||||
 | 
			
		||||
	servers := make(chan (*http.Server), 3)
 | 
			
		||||
	errGroup := errgroup.Group{}
 | 
			
		||||
	errGroup.Go(func() error {
 | 
			
		||||
		return start(servers)(
 | 
			
		||||
			publicServer(8080, &Router{
 | 
			
		||||
				static: frontendRoot,
 | 
			
		||||
				lib:    lib,
 | 
			
		||||
				rcol:   discogsCache,
 | 
			
		||||
				static:  staticRoot,
 | 
			
		||||
				lib:     lib,
 | 
			
		||||
				rcol:    discogsCache,
 | 
			
		||||
				isAdmin: false,
 | 
			
		||||
			}))
 | 
			
		||||
	})
 | 
			
		||||
	errGroup.Go(func() error {
 | 
			
		||||
		return start(servers)(
 | 
			
		||||
			tailscaleListener("library-admin", &AdminRouter{
 | 
			
		||||
				static: adminRoot,
 | 
			
		||||
				lib:    lib,
 | 
			
		||||
			tailscaleListener("library-admin", &Router{
 | 
			
		||||
				static:  staticRoot,
 | 
			
		||||
				lib:     lib,
 | 
			
		||||
				rcol:    discogsCache,
 | 
			
		||||
				isAdmin: true,
 | 
			
		||||
			}))
 | 
			
		||||
	})
 | 
			
		||||
	errGroup.Go(func() error {
 | 
			
		||||
@@ -143,10 +145,15 @@ func publicServer(port int, handler http.Handler) (*http.Server, net.Listener, e
 | 
			
		||||
	return server, ln, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func tailscaleListener(hostname string, handler *AdminRouter) (*http.Server, net.Listener, error) {
 | 
			
		||||
func tailscaleListener(hostname string, handler *Router) (*http.Server, net.Listener, error) {
 | 
			
		||||
	s := &tsnet.Server{
 | 
			
		||||
		Dir:      ".config/" + hostname,
 | 
			
		||||
		Hostname: hostname,
 | 
			
		||||
		Logf: func(s string, a ...any) { // silence most tsnet logs
 | 
			
		||||
			if strings.HasPrefix(s, "To start this tsnet server") {
 | 
			
		||||
				log.Printf(s, a...)
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	ln, err := s.Listen("tcp", ":80")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user