khatru/blossom: implement bud-04 mirror.
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -15,6 +16,10 @@ import (
|
|||||||
"github.com/liamg/magic"
|
"github.com/liamg/magic"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type mirrorRequest struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
func (bs BlossomServer) handleUploadCheck(w http.ResponseWriter, r *http.Request) {
|
func (bs BlossomServer) handleUploadCheck(w http.ResponseWriter, r *http.Request) {
|
||||||
auth, err := readAuthorization(r)
|
auth, err := readAuthorization(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -355,6 +360,92 @@ func (bs BlossomServer) handleReport(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bs BlossomServer) handleMirror(w http.ResponseWriter, r *http.Request) {
|
func (bs BlossomServer) handleMirror(w http.ResponseWriter, r *http.Request) {
|
||||||
|
auth, err := readAuthorization(r)
|
||||||
|
if err != nil {
|
||||||
|
blossomError(w, "invalid \"Authorization\": "+err.Error(), 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if auth == nil {
|
||||||
|
blossomError(w, "missing \"Authorization\" header", 401)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if auth.Tags.FindWithValue("t", "upload") == nil {
|
||||||
|
blossomError(w, "invalid \"Authorization\" event \"t\" tag", 403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req mirrorRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
blossomError(w, "invalid request body: "+err.Error(), 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// download the blob
|
||||||
|
resp, err := http.Get(req.URL)
|
||||||
|
if err != nil {
|
||||||
|
blossomError(w, "failed to download from url: "+err.Error(), 503)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
blossomError(w, "failed to read response body: "+err.Error(), 503)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate sha256
|
||||||
|
hash := sha256.Sum256(body)
|
||||||
|
hhash := hex.EncodeToString(hash[:])
|
||||||
|
|
||||||
|
// verify hash against x tag
|
||||||
|
if auth.Tags.FindWithValue("x", hhash) == nil {
|
||||||
|
blossomError(w, "blob hash does not match any \"x\" tag in authorization event", 403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine content type and extension
|
||||||
|
var ext string
|
||||||
|
contentType := resp.Header.Get("Content-Type")
|
||||||
|
if contentType != "" {
|
||||||
|
ext = getExtension(contentType)
|
||||||
|
} else {
|
||||||
|
// try to detect from url
|
||||||
|
ext = filepath.Ext(req.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// run reject hook if defined
|
||||||
|
if bs.RejectUpload != nil {
|
||||||
|
reject, reason, code := bs.RejectUpload(r.Context(), auth, len(body), ext)
|
||||||
|
if reject {
|
||||||
|
blossomError(w, reason, code)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep track of the blob descriptor
|
||||||
|
bd := BlobDescriptor{
|
||||||
|
URL: bs.ServiceURL + "/" + hhash + ext,
|
||||||
|
SHA256: hhash,
|
||||||
|
Size: len(body),
|
||||||
|
Type: contentType,
|
||||||
|
Uploaded: nostr.Now(),
|
||||||
|
}
|
||||||
|
if err := bs.Store.Keep(r.Context(), bd, auth.PubKey); err != nil {
|
||||||
|
blossomError(w, "failed to save event: "+err.Error(), 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// save actual blob
|
||||||
|
if bs.StoreBlob != nil {
|
||||||
|
if err := bs.StoreBlob(r.Context(), hhash, body); err != nil {
|
||||||
|
blossomError(w, "failed to save: "+err.Error(), 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return response
|
||||||
|
json.NewEncoder(w).Encode(bd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs BlossomServer) handleNegentropy(w http.ResponseWriter, r *http.Request) {
|
func (bs BlossomServer) handleNegentropy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
@@ -44,6 +44,11 @@ func New(rl *khatru.Relay, serviceURL string) *BlossomServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.URL.Path == "/mirror" && r.Method == "PUT" {
|
||||||
|
bs.handleMirror(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(r.URL.Path, "/list/") && r.Method == "GET" {
|
if strings.HasPrefix(r.URL.Path, "/list/") && r.Method == "GET" {
|
||||||
bs.handleList(w, r)
|
bs.handleList(w, r)
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user