khatru/blossom: blossom ext updates.

see https://github.com/fiatjaf/khatru/pull/59
This commit is contained in:
Anthony Accioly
2025-09-05 11:17:34 -03:00
committed by fiatjaf
parent 5f8b069f5d
commit 8750197ea7
4 changed files with 47 additions and 27 deletions

View File

@@ -7,7 +7,6 @@ import (
"io" "io"
"mime" "mime"
"net/http" "net/http"
"path/filepath"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@@ -130,13 +129,17 @@ func (bs BlossomServer) handleUpload(w http.ResponseWriter, r *http.Request) {
hash := sha256.Sum256(b) hash := sha256.Sum256(b)
hhash := hex.EncodeToString(hash[:]) hhash := hex.EncodeToString(hash[:])
mimeType := mime.TypeByExtension(ext)
if mimeType == "" {
mimeType = "application/octet-stream"
}
// keep track of the blob descriptor // keep track of the blob descriptor
bd := BlobDescriptor{ bd := BlobDescriptor{
URL: bs.ServiceURL + "/" + hhash + ext, URL: bs.ServiceURL + "/" + hhash + ext,
SHA256: hhash, SHA256: hhash,
Size: len(b), Size: len(b),
Type: mime.TypeByExtension(ext), Type: mimeType,
Uploaded: nostr.Now(), Uploaded: nostr.Now(),
} }
if err := bs.Store.Keep(r.Context(), bd, auth.PubKey); err != nil { if err := bs.Store.Keep(r.Context(), bd, auth.PubKey); err != nil {
@@ -146,7 +149,7 @@ func (bs BlossomServer) handleUpload(w http.ResponseWriter, r *http.Request) {
// save actual blob // save actual blob
if nil != bs.StoreBlob { if nil != bs.StoreBlob {
if err := bs.StoreBlob(r.Context(), hhash, b); err != nil { if err := bs.StoreBlob(r.Context(), hhash, ext, b); err != nil {
blossomError(w, "failed to save: "+err.Error(), 500) blossomError(w, "failed to save: "+err.Error(), 500)
return return
} }
@@ -187,21 +190,27 @@ func (bs BlossomServer) handleGetBlob(w http.ResponseWriter, r *http.Request) {
} }
} }
var ext string
bd, err := bs.Store.Get(r.Context(), hhash)
if err != nil {
// can't find the BlobDescriptor, try to get the extension from the URL
if len(spl) == 2 {
ext = spl[1]
}
} else if bd != nil {
ext = getExtension(bd.Type)
}
if nil != bs.RejectGet { if nil != bs.RejectGet {
reject, reason, code := bs.RejectGet(r.Context(), auth, hhash) reject, reason, code := bs.RejectGet(r.Context(), auth, hhash, ext)
if reject { if reject {
blossomError(w, reason, code) blossomError(w, reason, code)
return return
} }
} }
var ext string
if len(spl) == 2 {
ext = "." + spl[1]
}
if bs.LoadBlob != nil { if bs.LoadBlob != nil {
reader, redirectURL, err := bs.LoadBlob(r.Context(), hhash) reader, redirectURL, err := bs.LoadBlob(r.Context(), hhash, ext)
if err == nil && redirectURL != nil { if err == nil && redirectURL != nil {
// check that the redirectURL contains the hash of the file // check that the redirectURL contains the hash of the file
if ok, _ := regexp.MatchString(`\b`+hhash+`\b`, redirectURL.String()); !ok { if ok, _ := regexp.MatchString(`\b`+hhash+`\b`, redirectURL.String()); !ok {
@@ -329,9 +338,20 @@ func (bs BlossomServer) handleDelete(w http.ResponseWriter, r *http.Request) {
return return
} }
var ext string
bd, err := bs.Store.Get(r.Context(), hhash)
if err != nil {
// can't find the BlobDescriptor, try to get the extension from the URL
if len(spl) == 2 {
ext = spl[1]
}
} else if bd != nil {
ext = getExtension(bd.Type)
}
// should we accept this delete? // should we accept this delete?
if nil != bs.RejectDelete { if nil != bs.RejectDelete {
reject, reason, code := bs.RejectDelete(r.Context(), auth, hhash) reject, reason, code := bs.RejectDelete(r.Context(), auth, hhash, ext)
if reject { if reject {
blossomError(w, reason, code) blossomError(w, reason, code)
return return
@@ -347,7 +367,7 @@ func (bs BlossomServer) handleDelete(w http.ResponseWriter, r *http.Request) {
// we will actually only delete the file if no one else owns it // we will actually only delete the file if no one else owns it
if bd, err := bs.Store.Get(r.Context(), hhash); err == nil && bd == nil { if bd, err := bs.Store.Get(r.Context(), hhash); err == nil && bd == nil {
if nil != bs.DeleteBlob { if nil != bs.DeleteBlob {
if err := bs.DeleteBlob(r.Context(), hhash); err != nil { if err := bs.DeleteBlob(r.Context(), hhash, ext); err != nil {
blossomError(w, "failed to delete blob: "+err.Error(), 500) blossomError(w, "failed to delete blob: "+err.Error(), 500)
return return
} }
@@ -415,7 +435,6 @@ func (bs BlossomServer) handleMirror(w http.ResponseWriter, r *http.Request) {
return return
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
blossomError(w, "failed to read response body: "+err.Error(), 503) blossomError(w, "failed to read response body: "+err.Error(), 503)
@@ -437,9 +456,10 @@ func (bs BlossomServer) handleMirror(w http.ResponseWriter, r *http.Request) {
contentType := resp.Header.Get("Content-Type") contentType := resp.Header.Get("Content-Type")
if contentType != "" { if contentType != "" {
ext = getExtension(contentType) ext = getExtension(contentType)
} else { } else if ft, _ := magic.Lookup(body); ft != nil {
// try to detect from url ext = "." + ft.Extension
ext = filepath.Ext(req.URL) } else if idx := strings.LastIndex(req.URL, "."); idx != -1 {
ext = req.URL[idx:]
} }
// run reject hook if defined // run reject hook if defined
@@ -466,7 +486,7 @@ func (bs BlossomServer) handleMirror(w http.ResponseWriter, r *http.Request) {
// save actual blob // save actual blob
if bs.StoreBlob != nil { if bs.StoreBlob != nil {
if err := bs.StoreBlob(r.Context(), hhash, body); err != nil { if err := bs.StoreBlob(r.Context(), hhash, ext, body); err != nil {
blossomError(w, "failed to save: "+err.Error(), 500) blossomError(w, "failed to save: "+err.Error(), 500)
return return
} }

View File

@@ -15,15 +15,15 @@ type BlossomServer struct {
ServiceURL string ServiceURL string
Store BlobIndex Store BlobIndex
StoreBlob func(ctx context.Context, sha256 string, body []byte) error StoreBlob func(ctx context.Context, sha256 string, ext string, body []byte) error
LoadBlob func(ctx context.Context, sha256 string) (io.ReadSeeker, *url.URL, error) LoadBlob func(ctx context.Context, sha256 string, ext string) (io.ReadSeeker, *url.URL, error)
DeleteBlob func(ctx context.Context, sha256 string) error DeleteBlob func(ctx context.Context, sha256 string, ext string) error
ReceiveReport func(ctx context.Context, reportEvt nostr.Event) error ReceiveReport func(ctx context.Context, reportEvt nostr.Event) error
RejectUpload func(ctx context.Context, auth *nostr.Event, size int, ext string) (bool, string, int) RejectUpload func(ctx context.Context, auth *nostr.Event, size int, ext string) (bool, string, int)
RejectGet func(ctx context.Context, auth *nostr.Event, sha256 string) (bool, string, int) RejectGet func(ctx context.Context, auth *nostr.Event, sha256 string, ext string) (bool, string, int)
RejectList func(ctx context.Context, auth *nostr.Event, pubkey nostr.PubKey) (bool, string, int) RejectList func(ctx context.Context, auth *nostr.Event, pubkey nostr.PubKey) (bool, string, int)
RejectDelete func(ctx context.Context, auth *nostr.Event, sha256 string) (bool, string, int) RejectDelete func(ctx context.Context, auth *nostr.Event, sha256 string, ext string) (bool, string, int)
} }
func New(rl *khatru.Relay, serviceURL string) *BlossomServer { func New(rl *khatru.Relay, serviceURL string) *BlossomServer {

View File

@@ -22,15 +22,15 @@ func main() {
bl.Store = blossom.EventStoreBlobIndexWrapper{Store: blobdb, ServiceURL: bl.ServiceURL} bl.Store = blossom.EventStoreBlobIndexWrapper{Store: blobdb, ServiceURL: bl.ServiceURL}
// implement the required storage functions // implement the required storage functions
bl.StoreBlob = func(ctx context.Context, sha256 string, body []byte) error { bl.StoreBlob = func(ctx context.Context, sha256 string, ext string, body []byte) error {
// store the blob data somewhere // store the blob data somewhere
return nil return nil
} }
bl.LoadBlob = func(ctx context.Context, sha256 string) (io.ReadSeeker, *url.URL, error) { bl.LoadBlob = func(ctx context.Context, sha256 string, ext string) (io.ReadSeeker, *url.URL, error) {
// load and return the blob data, or a redirect URL // load and return the blob data, or a redirect URL
return nil, nil, nil return nil, nil, nil
} }
bl.DeleteBlob = func(ctx context.Context, sha256 string) error { bl.DeleteBlob = func(ctx context.Context, sha256 string, ext string) error {
// delete the blob data // delete the blob data
return nil return nil
} }

View File

@@ -29,11 +29,11 @@ func main() {
} }
bl := blossom.New(relay, "http://localhost:3334") bl := blossom.New(relay, "http://localhost:3334")
bl.Store = blossom.EventStoreBlobIndexWrapper{Store: bdb, ServiceURL: bl.ServiceURL} bl.Store = blossom.EventStoreBlobIndexWrapper{Store: bdb, ServiceURL: bl.ServiceURL}
bl.StoreBlob = func(ctx context.Context, sha256 string, body []byte) error { bl.StoreBlob = func(ctx context.Context, sha256 string, ext string, body []byte) error {
fmt.Println("storing", sha256, len(body)) fmt.Println("storing", sha256, len(body))
return nil return nil
} }
bl.LoadBlob = func(ctx context.Context, sha256 string) (io.ReadSeeker, *url.URL, error) { bl.LoadBlob = func(ctx context.Context, sha256 string, ext string) (io.ReadSeeker, *url.URL, error) {
fmt.Println("loading", sha256) fmt.Println("loading", sha256)
blob := strings.NewReader("aaaaa") blob := strings.NewReader("aaaaa")
return blob, nil, nil return blob, nil, nil