206 lines
4.5 KiB
Go
206 lines
4.5 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"git.tornberg.me/go-persist/pkg/storage"
|
|
)
|
|
|
|
type Folder struct {
|
|
Id string
|
|
parent *Folder
|
|
Storage storage.Storage
|
|
Children map[string]*Folder
|
|
}
|
|
|
|
func (f *Folder) GetId() string {
|
|
return f.Id
|
|
}
|
|
func (f *Folder) GetPathIds() []string {
|
|
p := f.parent
|
|
ids := make([]string, 0)
|
|
ids = append(ids, f.GetId())
|
|
for p != nil {
|
|
ids = append(ids, p.GetId())
|
|
p = p.parent
|
|
}
|
|
return ids
|
|
}
|
|
|
|
type App struct {
|
|
Root *Folder
|
|
spawnStorage func(path []string) (storage.Storage, error)
|
|
}
|
|
|
|
func (app *App) GetFolder(pth []string) (*Folder, bool) {
|
|
current := app.Root
|
|
for _, id := range pth {
|
|
if child, exists := current.Children[id]; exists {
|
|
current = child
|
|
} else {
|
|
return nil, false
|
|
}
|
|
}
|
|
return current, true
|
|
}
|
|
|
|
func (app *App) GetOrSpawn(pth []string) (*Folder, error) {
|
|
current := app.Root
|
|
level := []string{}
|
|
for _, id := range pth {
|
|
if child, exists := current.Children[id]; exists {
|
|
current = child
|
|
level = append(level, id)
|
|
} else {
|
|
level = append(level, id)
|
|
s, err := app.spawnStorage(level)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
child := &Folder{
|
|
Id: id,
|
|
parent: current,
|
|
Storage: s,
|
|
Children: make(map[string]*Folder),
|
|
}
|
|
current.Children[id] = child
|
|
current = child
|
|
}
|
|
}
|
|
return current, nil
|
|
}
|
|
|
|
func (app *App) AddFolder(id string, parent *Folder) (*Folder, error) {
|
|
strg, err := app.spawnStorage(parent.GetPathIds())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
folder := &Folder{
|
|
Id: id,
|
|
parent: parent,
|
|
Storage: strg,
|
|
Children: make(map[string]*Folder),
|
|
}
|
|
if parent != nil {
|
|
parent.Children[id] = folder
|
|
}
|
|
return folder, nil
|
|
}
|
|
|
|
func GetPathAndFileFromUrl(u *url.URL) ([]string, string) {
|
|
if u == nil {
|
|
return []string{}, ""
|
|
}
|
|
parts := strings.Split(u.Path, "/")[1:]
|
|
fileName := parts[len(parts)-1]
|
|
return parts[:len(parts)-1], fileName
|
|
}
|
|
|
|
func esimateMimeType(filename string) string {
|
|
if strings.HasSuffix(filename, ".jpg") {
|
|
return "image/jpeg"
|
|
}
|
|
if strings.HasSuffix(filename, ".png") {
|
|
return "image/png"
|
|
}
|
|
if strings.HasSuffix(filename, ".gif") {
|
|
return "image/gif"
|
|
}
|
|
if strings.HasSuffix(filename, ".bmp") {
|
|
return "image/bmp"
|
|
}
|
|
if strings.HasSuffix(filename, ".webp") {
|
|
return "image/webp"
|
|
}
|
|
if strings.HasSuffix(filename, ".json") {
|
|
return "application/json"
|
|
}
|
|
if strings.HasSuffix(filename, ".html") || strings.HasSuffix(filename, ".htm") {
|
|
return "text/html"
|
|
}
|
|
if strings.HasSuffix(filename, ".css") {
|
|
return "text/css"
|
|
}
|
|
if strings.HasSuffix(filename, ".txt") {
|
|
return "text/plain"
|
|
}
|
|
return "application/octet-stream"
|
|
}
|
|
|
|
func main() {
|
|
// Initialize the application
|
|
rootDir, err := storage.NewDiskStorage([]string{})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
app := &App{
|
|
Root: &Folder{
|
|
Id: "data",
|
|
Storage: rootDir,
|
|
Children: make(map[string]*Folder),
|
|
},
|
|
spawnStorage: func(path []string) (storage.Storage, error) {
|
|
if path[0] == "tmp" {
|
|
return storage.NewMemoryStorage(path)
|
|
}
|
|
return storage.NewDiskStorage(path)
|
|
},
|
|
}
|
|
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
pth, fileName := GetPathAndFileFromUrl(r.URL)
|
|
log.Printf("Request path parts: %+v, fileName:%s", pth, fileName)
|
|
folder, err := app.GetOrSpawn(pth)
|
|
if err != nil {
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
w.Write([]byte(err.Error()))
|
|
return
|
|
}
|
|
}
|
|
log.Printf("Retrieved folder: %+v, exists: %v", folder)
|
|
if fileName == "" {
|
|
content, err := folder.Storage.List("")
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
w.Write([]byte(err.Error()))
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(content)
|
|
} else {
|
|
if r.Method == "GET" {
|
|
content, err := folder.Storage.Get(fileName)
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", esimateMimeType(fileName))
|
|
w.Header().Set("Cache-Control", "public; max-age=60")
|
|
w.Header().Set("Content-Disposition", "attachment; filename=\""+fileName+"\"")
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(content)
|
|
} else {
|
|
defer r.Body.Close()
|
|
data, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
w.Write([]byte(err.Error()))
|
|
}
|
|
folder.Storage.Put(fileName, data)
|
|
w.WriteHeader(http.StatusCreated)
|
|
}
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
if err := http.ListenAndServe(":8080", mux); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|