package main import ( "encoding/json" "io" "log" "net/http" "net/url" "os" "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" } var apiKey = os.Getenv("API_KEY") func main() { 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: %+v, fileName:%s, method: %s", pth, fileName, r.Method) folder, err := app.GetOrSpawn(pth) if err != nil { if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } } if fileName == "" { authKey := r.Header.Get("Authorization") if authKey != apiKey { w.WriteHeader(http.StatusUnauthorized) return } 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() authKey := r.Header.Get("Authorization") if authKey != apiKey { w.WriteHeader(http.StatusUnauthorized) return } data, err := io.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } folder.Storage.Put(fileName, data) w.WriteHeader(http.StatusCreated) } } w.WriteHeader(http.StatusOK) }) if err := http.ListenAndServe(":8080", mux); err != nil { panic(err) } }