Files
go-persist/main.go
matst80 1fb2f490b7
Some checks failed
Build and Publish / BuildAndDeploy (push) Failing after 31s
cleanup
2025-05-15 19:55:45 +02:00

218 lines
4.7 KiB
Go

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 {
w.WriteHeader(http.StatusNotFound)
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)
}
}
})
if err := http.ListenAndServe(":8080", mux); err != nil {
panic(err)
}
}