package main import ( "database/sql" "encoding/json" "io" "log" "net/http" "os" "path" "strings" "github.com/jmoiron/sqlx" gonanoid "github.com/matoous/go-nanoid/v2" "github.com/mattn/go-sqlite3" ) func badRequest(w http.ResponseWriter) { http.Error(w, "bad request", http.StatusBadRequest) } func getAdminAuth(w http.ResponseWriter, req *http.Request) bool { auth := req.Header.Get("Authorization") if len(auth) == 0 { badRequest(w) return false } if auth == "Bearer "+admin_secret { return true } return false } func (r *cache) getCache(w http.ResponseWriter, req *http.Request, name string) *dbCache { auth := req.Header.Get("Authorization") if len(auth) == 0 { badRequest(w) return nil } var c dbCache err := r.db.Get(&c, "SELECT secret FROM caches WHERE name = ?", name) if err != nil { if err == sql.ErrNoRows { http.Error(w, "cache doesn't exist", http.StatusNotFound) } else { log.Printf("Get(SELECT): %w", err) http.Error(w, "server error", http.StatusInternalServerError) } return nil } if auth == "Bearer "+c.Secret { return &c } else { http.Error(w, "wrong secret", http.StatusUnauthorized) return nil } } type cache struct { db *sqlx.DB } type dbCache struct { Secret string } type responseNewCache struct { Secret string `json:"secret"` } func (r *cache) ServeHTTP(w http.ResponseWriter, req *http.Request) { parts := strings.Split(req.URL.Path[1:], "/") log.Println(parts) if len(parts) != 2 { http.Error(w, "wrong amount of parts", http.StatusBadRequest) return } name := parts[1] if req.Method == "POST" { if !getAdminAuth(w, req) { return } if strings.ContainsAny(name, "/") { badRequest(w) return } secret, err := gonanoid.New() if err != nil { log.Printf("Error generating ID: %w", err) http.Error(w, "server error", http.StatusInternalServerError) return } _, err = r.db.Exec("INSERT INTO caches(name, secret) VALUES(?, ?)", name, secret) if err != nil { if err.(sqlite3.Error).Code == sqlite3.ErrConstraint { http.Error(w, "cache already exists", http.StatusConflict) } else { log.Printf("Error saving cache DB entry: %w", err) http.Error(w, "server error", http.StatusInternalServerError) } return } byt, err := json.Marshal(responseNewCache{Secret: secret}) if err != nil { log.Printf("Marshal: %w", err) http.Error(w, "server error", http.StatusInternalServerError) return } w.Header().Add("Content-Encoding", "application/json") w.Write(byt) } else if req.Method == "PUT" { c := r.getCache(w, req, name) if c == nil { return } file, err := os.Create(path.Join("./files/", name)) if err != nil { log.Printf("os.Create: %w", err) http.Error(w, "server error", http.StatusInternalServerError) return } _, err = io.Copy(file, req.Body) if err != nil { log.Printf("io.Copy: %w", err) http.Error(w, "server error", http.StatusInternalServerError) return } } else if req.Method == "GET" { c := r.getCache(w, req, name) if c == nil { return } file, err := os.Open(path.Join("./files/", name)) if err != nil { log.Printf("os.Open: %w", err) http.Error(w, "server error", http.StatusInternalServerError) return } _, err = io.Copy(w, file) if err != nil { log.Printf("io.Copy: %w", err) http.Error(w, "server error", http.StatusInternalServerError) return } } else { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } }