mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-03-19 14:22:10 +01:00
128 lines
3.1 KiB
Go
128 lines
3.1 KiB
Go
|
package nip96
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"context"
|
||
|
"encoding/base64"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"mime/multipart"
|
||
|
"net/http"
|
||
|
"strconv"
|
||
|
|
||
|
"github.com/nbd-wtf/go-nostr"
|
||
|
)
|
||
|
|
||
|
// Upload uploads a file to the provided req.Host.
|
||
|
func Upload(ctx context.Context, req UploadRequest) (*UploadResponse, error) {
|
||
|
if err := req.Validate(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
client := http.DefaultClient
|
||
|
if req.HTTPClient != nil {
|
||
|
client = req.HTTPClient
|
||
|
}
|
||
|
|
||
|
var requestBody bytes.Buffer
|
||
|
writer := multipart.NewWriter(&requestBody)
|
||
|
{
|
||
|
// Add the file
|
||
|
fileWriter, err := writer.CreateFormFile("file", req.Filename)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("multipartWriter.CreateFormFile: %w", err)
|
||
|
}
|
||
|
if _, err := io.Copy(fileWriter, req.File); err != nil {
|
||
|
return nil, fmt.Errorf("io.Copy: %w", err)
|
||
|
}
|
||
|
|
||
|
// Add the other fields
|
||
|
writer.WriteField("caption", req.Caption)
|
||
|
writer.WriteField("alt", req.Alt)
|
||
|
writer.WriteField("media_type", req.MediaType)
|
||
|
writer.WriteField("content_type", req.ContentType)
|
||
|
writer.WriteField("no_transform", fmt.Sprintf("%t", req.NoTransform))
|
||
|
if req.Expiration == 0 {
|
||
|
writer.WriteField("expiration", "")
|
||
|
} else {
|
||
|
writer.WriteField("expiration", strconv.FormatInt(int64(req.Expiration), 10))
|
||
|
}
|
||
|
|
||
|
if err := writer.Close(); err != nil {
|
||
|
return nil, fmt.Errorf("multipartWriter.Close: %w", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uploadReq, err := http.NewRequest("POST", req.Host, &requestBody)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("http.NewRequest: %w", err)
|
||
|
}
|
||
|
uploadReq.Header.Set("Content-Type", writer.FormDataContentType())
|
||
|
|
||
|
if req.SK != "" {
|
||
|
auth, err := generateAuthHeader(req.SK, req.Host)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("generateAuthHeader: %w", err)
|
||
|
}
|
||
|
uploadReq.Header.Set("Authorization", auth)
|
||
|
}
|
||
|
|
||
|
resp, err := client.Do(uploadReq)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("httpclient.Do: %w", err)
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
switch resp.StatusCode {
|
||
|
case http.StatusRequestEntityTooLarge:
|
||
|
return nil, fmt.Errorf("File is too large")
|
||
|
|
||
|
case http.StatusBadRequest:
|
||
|
return nil, fmt.Errorf("Bad request")
|
||
|
|
||
|
case http.StatusForbidden:
|
||
|
return nil, fmt.Errorf("Unauthorized")
|
||
|
|
||
|
case http.StatusPaymentRequired:
|
||
|
return nil, fmt.Errorf("Payment required")
|
||
|
|
||
|
case http.StatusOK, http.StatusCreated, http.StatusAccepted:
|
||
|
var uploadResp UploadResponse
|
||
|
if err := json.NewDecoder(resp.Body).Decode(&uploadResp); err != nil {
|
||
|
return nil, fmt.Errorf("Error decoding JSON: %w", err)
|
||
|
}
|
||
|
return &uploadResp, nil
|
||
|
|
||
|
default:
|
||
|
return nil, fmt.Errorf("Unexpected error %v", resp.Status)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func generateAuthHeader(sk, host string) (string, error) {
|
||
|
pk, err := nostr.GetPublicKey(sk)
|
||
|
if err != nil {
|
||
|
return "", fmt.Errorf("nostr.GetPublicKey: %w", err)
|
||
|
}
|
||
|
|
||
|
event := nostr.Event{
|
||
|
Kind: 27235,
|
||
|
PubKey: pk,
|
||
|
CreatedAt: nostr.Now(),
|
||
|
Tags: nostr.Tags{
|
||
|
nostr.Tag{"u", host},
|
||
|
nostr.Tag{"method", "POST"},
|
||
|
},
|
||
|
}
|
||
|
event.Sign(sk)
|
||
|
|
||
|
b, err := json.Marshal(event)
|
||
|
if err != nil {
|
||
|
return "", fmt.Errorf("json.Marshal: %w", err)
|
||
|
}
|
||
|
|
||
|
payload := base64.StdEncoding.EncodeToString(b)
|
||
|
|
||
|
return fmt.Sprintf("Nostr %s", payload), nil
|
||
|
}
|