khatru: the accioly blossom redirect patch, reworked.
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"mime"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -199,8 +200,21 @@ func (bs BlossomServer) handleGetBlob(w http.ResponseWriter, r *http.Request) {
|
||||
ext = "." + spl[1]
|
||||
}
|
||||
|
||||
if nil != bs.LoadBlob {
|
||||
reader, _ := bs.LoadBlob(r.Context(), hhash)
|
||||
if bs.LoadBlob != nil {
|
||||
reader, redirectURL, err := bs.LoadBlob(r.Context(), hhash)
|
||||
if err == nil && redirectURL != nil {
|
||||
// check that the redirectURL contains the hash of the file
|
||||
if ok, _ := regexp.MatchString(`\b`+hhash+`\b`, redirectURL.String()); !ok {
|
||||
blossomError(w, "redirect url doesn't contain the file hash", 500)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("ETag", hhash)
|
||||
w.Header().Set("Cache-Control", "public, max-age=604800, immutable")
|
||||
http.Redirect(w, r, redirectURL.String(), http.StatusTemporaryRedirect)
|
||||
return
|
||||
}
|
||||
|
||||
if reader != nil {
|
||||
// use unix epoch as the time if we can't find the descriptor
|
||||
// as described in the http.ServeContent documentation
|
||||
@@ -211,7 +225,11 @@ func (bs BlossomServer) handleGetBlob(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
w.Header().Set("ETag", hhash)
|
||||
w.Header().Set("Cache-Control", "public, max-age=604800, immutable")
|
||||
http.ServeContent(w, r, hhash+ext, t, reader)
|
||||
name := hhash
|
||||
if ext != "" {
|
||||
name += ext
|
||||
}
|
||||
http.ServeContent(w, r, name, t, reader)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"fiatjaf.com/nostr"
|
||||
@@ -15,7 +16,7 @@ type BlossomServer struct {
|
||||
Store BlobIndex
|
||||
|
||||
StoreBlob func(ctx context.Context, sha256 string, body []byte) error
|
||||
LoadBlob func(ctx context.Context, sha256 string) (io.ReadSeeker, error)
|
||||
LoadBlob func(ctx context.Context, sha256 string) (io.ReadSeeker, *url.URL, error)
|
||||
DeleteBlob func(ctx context.Context, sha256 string) error
|
||||
ReceiveReport func(ctx context.Context, reportEvt nostr.Event) error
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ func main() {
|
||||
// store the blob data somewhere
|
||||
return nil
|
||||
}
|
||||
bl.LoadBlob = func(ctx context.Context, sha256 string) (io.ReadSeeker, error) {
|
||||
// load and return the blob data
|
||||
return nil, nil
|
||||
bl.LoadBlob = func(ctx context.Context, sha256 string) (io.ReadSeeker, *url.URL, error) {
|
||||
// load and return the blob data, or a redirect URL
|
||||
return nil, nil, nil
|
||||
}
|
||||
bl.DeleteBlob = func(ctx context.Context, sha256 string) error {
|
||||
// delete the blob data
|
||||
@@ -43,10 +43,34 @@ func main() {
|
||||
|
||||
You can integrate any storage backend by implementing the three core functions:
|
||||
|
||||
- `StoreBlob`: Save the blob data
|
||||
- `LoadBlob`: Retrieve the blob data
|
||||
- `StoreBlob`: Persist the blob data
|
||||
- `LoadBlob`: Retrieve the blob data -- or a redirect URL
|
||||
- `DeleteBlob`: Remove the blob data
|
||||
|
||||
## URL Redirection
|
||||
|
||||
Blossom supports redirection to external storage locations when retrieving blobs. This is useful when you want to serve files from a CDN or cloud storage service while keeping Blossom compatibility.
|
||||
|
||||
To implement this, your `LoadBlob` function should return a `*url.URL` as its second argument. If this URL is not `nil`, `khatru` will redirect the client to it.
|
||||
|
||||
Here's an example that redirects to a templated URL:
|
||||
```go
|
||||
import (
|
||||
"net/url"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ...
|
||||
|
||||
bl.LoadBlob = func(ctx context.Context, sha256 string) (io.ReadSeeker, *url.URL, error) {
|
||||
// generate a custom redirect URL
|
||||
redirectURL, _ := url.Parse(fmt.Sprintf("https://my-cdn.com/%s", sha256))
|
||||
return nil, redirectURL, nil
|
||||
}
|
||||
```
|
||||
|
||||
This URL must include the sha256 hash somewhere.
|
||||
|
||||
## Upload Restrictions
|
||||
|
||||
You can implement upload restrictions using the `RejectUpload` hook. Here's an example that limits file size and restricts uploads to whitelisted users:
|
||||
@@ -91,3 +115,32 @@ bl.Store = blossom.EventStoreBlobIndexWrapper{
|
||||
```
|
||||
|
||||
This will store blob metadata as special `kind:24242` events, but you shouldn't have to worry about it as the wrapper handles all the complexity of tracking ownership and managing blob lifecycle. Jut avoid reusing the same datastore that is used for the actual relay events unless you know what you're doing.
|
||||
|
||||
## Upload Restrictions
|
||||
|
||||
You can implement upload restrictions using the `RejectUpload` hook. Here's an example that limits file size and restricts uploads to whitelisted users:
|
||||
|
||||
```go
|
||||
const maxFileSize = 10 * 1024 * 1024 // 10MB
|
||||
|
||||
var allowedUsers = map[string]bool{
|
||||
"pubkey1": true,
|
||||
"pubkey2": true,
|
||||
}
|
||||
|
||||
bl.RejectUpload = func(ctx context.Context, auth *nostr.Event, size int, ext string) (bool, string, int) {
|
||||
// check file size
|
||||
if size > maxFileSize {
|
||||
return true, "file too large", 413
|
||||
}
|
||||
|
||||
// check if user is allowed
|
||||
if auth == nil || !allowedUsers[auth.PubKey] {
|
||||
return true, "unauthorized", 403
|
||||
}
|
||||
|
||||
return false, "", 0
|
||||
}
|
||||
```
|
||||
|
||||
There are other `Reject*` hooks you can also implement, but this is the most important one.
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"fiatjaf.com/nostr/eventstore/badger"
|
||||
@@ -32,10 +33,10 @@ func main() {
|
||||
fmt.Println("storing", sha256, len(body))
|
||||
return nil
|
||||
}
|
||||
bl.LoadBlob = func(ctx context.Context, sha256 string) (io.ReadSeeker, error) {
|
||||
bl.LoadBlob = func(ctx context.Context, sha256 string) (io.ReadSeeker, *url.URL, error) {
|
||||
fmt.Println("loading", sha256)
|
||||
blob := strings.NewReader("aaaaa")
|
||||
return blob, nil
|
||||
return blob, nil, nil
|
||||
}
|
||||
|
||||
fmt.Println("running on :3334")
|
||||
|
||||
Reference in New Issue
Block a user