143 lines
4.1 KiB
Go
143 lines
4.1 KiB
Go
/*
|
|
Timelinize
|
|
Copyright (c) 2013 Matthew Holt
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published
|
|
by the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package tlzapp
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"os"
|
|
"runtime"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
type handler interface {
|
|
ServeHTTP(w http.ResponseWriter, r *http.Request) error
|
|
}
|
|
|
|
// handlerFunc is like http.HandlerFunc, except these handlers return an error.
|
|
type handlerFunc func(http.ResponseWriter, *http.Request) error
|
|
|
|
func (h handlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
|
|
return h(w, r)
|
|
}
|
|
|
|
// userHomeDir returns the best guess of the user's home directory,
|
|
// or "." (current directory) if the home directory is unknown
|
|
// (to make it safe to use in filepath.Join()). The implementation of
|
|
// this function is slightly more nuanced than that of os.UserHomeDir()
|
|
// but I do not know if it is more correct/robust; the main difference
|
|
// being that this always returns a valid path even if it is the current
|
|
// directory (".").
|
|
func userHomeDir() string {
|
|
if home := os.Getenv("HOME"); home != "" {
|
|
return home
|
|
}
|
|
if runtime.GOOS == osWindows {
|
|
if home := os.Getenv("USERPROFILE"); home != "" {
|
|
return home
|
|
}
|
|
drive := os.Getenv("HOMEDRIVE")
|
|
path := os.Getenv("HOMEPATH")
|
|
if drive != "" && path != "" {
|
|
return drive + path
|
|
}
|
|
}
|
|
if runtime.GOOS == "plan9" {
|
|
if home := os.Getenv("home"); home != "" {
|
|
return home
|
|
}
|
|
}
|
|
if runtime.GOOS == "android" {
|
|
return "/sdcard"
|
|
}
|
|
if runtime.GOOS == "ios" {
|
|
return "/"
|
|
}
|
|
return "."
|
|
}
|
|
|
|
// fileSelectorRoot describes a starting point for the file selector.
|
|
// Typically drives, mount points, or user home folders.
|
|
type fileSelectorRoot struct {
|
|
Label string `json:"label"`
|
|
Path string `json:"path"`
|
|
Type string `json:"type"`
|
|
}
|
|
|
|
// localFile represents serializable information about a local file.
|
|
type localFile struct {
|
|
FullName string `json:"full_name"`
|
|
Name string `json:"name"`
|
|
Size int64 `json:"size"`
|
|
Mode os.FileMode `json:"mode"`
|
|
ModTime time.Time `json:"mod_time"`
|
|
IsDir bool `json:"is_dir"`
|
|
}
|
|
|
|
type fileListing struct {
|
|
Dir string `json:"dir"` // absolute path of directory
|
|
Selected string `json:"selected,omitempty"` // if file was given, which one is selected
|
|
Up string `json:"up,omitempty"` // directory above this one
|
|
Files []localFile `json:"files"`
|
|
}
|
|
|
|
// wrapErrorHandler turns a handlerFunc (a handler that returns an error)
|
|
// into a standard http.HandlerFunc by handling any returned error.
|
|
func wrapErrorHandler(h handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if err := h.ServeHTTP(w, r); err != nil {
|
|
handleError(w, r, err)
|
|
}
|
|
})
|
|
}
|
|
|
|
// httpWrap wraps a standard http.Handler with a handlerFunc signature.
|
|
func httpWrap(h http.Handler) handlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) error {
|
|
h.ServeHTTP(w, r)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func jsonEncodeErr(err error) error {
|
|
return Error{
|
|
Err: err,
|
|
HTTPStatus: http.StatusInternalServerError,
|
|
Log: "Encoding JSON response",
|
|
Message: "Our program has a bug. It wasn't able to respond with data in JSON format.",
|
|
}
|
|
}
|
|
|
|
func jsonResponse(w http.ResponseWriter, v any, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if v != nil {
|
|
respBytes, err := json.Marshal(v)
|
|
if err != nil {
|
|
return jsonEncodeErr(err)
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
|
|
_, err = w.Write(respBytes)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|