Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image Upload from Tribes to Meme Server #894

Merged
merged 12 commits into from
Nov 9, 2023
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ If you would like to enable Relay for invoice creation and keysend payment add t
RELAY_AUTH_KEY=
```

### Enable Meme Image Upload

If you would like to enable Meme image upload for organization's add the meme env keyd and values to the .env file
kevkevinpal marked this conversation as resolved.
Show resolved Hide resolved

```
MEME_URL=
RELAY_NODE_KEY=
```

### For Contributions

Read the contribution doc [here](./Contribution.md)
Expand Down
9 changes: 9 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
var Host string
var JwtKey string
var RelayUrl string
var MemeUrl string
var RelayAuthKey string
var RelayNodeKey string

// these are constants for the store
var InvoiceList = "INVOICELIST"
Expand All @@ -19,15 +21,22 @@ func InitConfig() {
Host = os.Getenv("LN_SERVER_BASE_URL")
JwtKey = os.Getenv("LN_JWT_KEY")
RelayUrl = os.Getenv("RELAY_URL")
MemeUrl = os.Getenv("MEME_URL")
RelayAuthKey = os.Getenv("RELAY_AUTH_KEY")
RelayNodeKey = os.Getenv("RELAY_NODE_KEY")
tobi-bams marked this conversation as resolved.
Show resolved Hide resolved

if Host == "" {
Host = "https://people.sphinx.chat"
}

if MemeUrl == "" {
MemeUrl = "https://memes.sphinx.chat"
}

if JwtKey == "" {
JwtKey = GenerateRandomString()
}

}

func GenerateRandomString() string {
Expand Down
4 changes: 4 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ func TestInitConfig(t *testing.T) {
t.Error("Could not load default host")
}

if MemeUrl != "https://memes.sphinx.chat" {
t.Error("Could not load default meme url")
}

if JwtKey == "" {
t.Error("Could not load random jwtKey")
}
Expand Down
37 changes: 37 additions & 0 deletions db/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,43 @@ type WithdrawBudgetRequest struct {
OrgUuid string `json:"org_uuid"`
}

type MemeChallenge struct {
Id string `json:"id"`
Challenge string `json:"challenge"`
}

type SignerResponse struct {
Sig string `json:"sig"`
}

type RelaySignerResponse struct {
Success bool `json:"success"`
Response SignerResponse `json:"response"`
}

type MemeTokenSuccess struct {
Token string `json:"token"`
}

type Meme struct {
Muid string `json:"muid"`
OwnerPubKey string `json:"owner_pub_key"`
Name string `json:"name"`
Description string `json:"description"`
Price int64 `json:"price"`
Tags StringArray `json:"tags"`
Filename string `json:"filename"`
Ttl int64 `json:"ttl"`
Size int64 `json:"size"`
Mime string `json:"mime"`
Created *time.Time `json:"created"`
Updated *time.Time `json:"updates"`
Width int `json:"width"`
Height int `json:"height"`
Template bool `json:"template"`
Expiry *time.Time `json:"expiry"`
}

func (Person) TableName() string {
return "people"
}
Expand Down
3 changes: 2 additions & 1 deletion frontend/app/src/helpers/__test__/helpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
satToUsd,
calculateTimeLeft,
toCapitalize,
userHasRole
userHasRole,
isInvoiceExpired
} from '../helpers';
import { uiStore } from '../../store/ui';
import crypto from 'crypto';
Expand Down
220 changes: 220 additions & 0 deletions handlers/organizations.go
kevkevinpal marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
package handlers

import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"mime/multipart"
"net/http"
"os"
"path"
"path/filepath"
"time"

"net/url"

"github.com/go-chi/chi"
"github.com/rs/xid"
"github.com/stakwork/sphinx-tribes/auth"
"github.com/stakwork/sphinx-tribes/config"
"github.com/stakwork/sphinx-tribes/db"
"github.com/stakwork/sphinx-tribes/utils"
)
Expand Down Expand Up @@ -526,3 +535,214 @@ func GetInvoicesCount(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(invoiceCount)
}

func MemeImageUpload(w http.ResponseWriter, r *http.Request) {
dirName := "uploads"
file, header, err := r.FormFile("file")
if err != nil {
http.Error(w, "Unable to parse file", http.StatusBadRequest)
return
}
defer file.Close()

// Check if uploads directory exists or create it
CreateUploadsDirectory(dirName)

// Saving the file
dst, err := os.Create(dirName + "/" + header.Filename)

if err != nil {
http.Error(w, "Unable to save file 1", http.StatusInternalServerError)
return
}

defer dst.Close()

_, err = io.Copy(dst, file)
if err != nil {
http.Error(w, "Unable to save file 2", http.StatusInternalServerError)
return
}

challenge := GetMemeChallenge()
signer := SignChallenge(challenge.Challenge)
mErr, mToken := GetMemeToken(challenge.Id, signer.Response.Sig)

if mErr != "" {
msg := "Could not get meme token"
fmt.Println(msg, mErr)
w.WriteHeader(http.StatusNoContent)
json.NewEncoder(w).Encode(msg)
} else {
memeImgUrl := UploadMemeImage(file, mToken.Token, header.Filename)
if memeImgUrl == "" {
msg := "Could not get meme image"
fmt.Println(msg)
w.WriteHeader(http.StatusNoContent)
json.NewEncoder(w).Encode(msg)
} else {
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(memeImgUrl)
}
}
}

func GetMemeChallenge() db.MemeChallenge {
memeChallenge := db.MemeChallenge{}

url := fmt.Sprintf("%s/ask", config.MemeUrl)

client := &http.Client{}
req, err := http.NewRequest(http.MethodGet, url, nil)

req.Header.Set("Content-Type", "application/json")
res, _ := client.Do(req)

if err != nil {
log.Printf("Request Failed: %s", err)
}

defer res.Body.Close()

body, err := io.ReadAll(res.Body)

// Unmarshal result
err = json.Unmarshal(body, &memeChallenge)

if err != nil {
log.Printf("Reading Invoice body failed: %s", err)
}

return memeChallenge
}

func SignChallenge(challenge string) db.RelaySignerResponse {
url := fmt.Sprintf("%s/signer/%s", config.RelayUrl, challenge)

client := &http.Client{}
req, err := http.NewRequest(http.MethodGet, url, nil)

req.Header.Set("x-user-token", config.RelayAuthKey)
req.Header.Set("Content-Type", "application/json")
res, _ := client.Do(req)

if err != nil {
log.Printf("Request Failed: %s", err)
}

defer res.Body.Close()

body, err := io.ReadAll(res.Body)

signerResponse := db.RelaySignerResponse{}

// Unmarshal result
err = json.Unmarshal(body, &signerResponse)

if err != nil {
log.Printf("Reading Challenge body failed: %s", err)
}

return signerResponse
}

func GetMemeToken(id string, sig string) (string, db.MemeTokenSuccess) {
memeUrl := fmt.Sprintf("%s/verify", config.MemeUrl)

formData := url.Values{
"id": {id},
"sig": {sig},
"pubkey": {config.RelayNodeKey},
}

res, err := http.PostForm(memeUrl, formData)

if err != nil {
log.Printf("Request Failed: %s", err)
return "", db.MemeTokenSuccess{}
}

defer res.Body.Close()
body, err := io.ReadAll(res.Body)

if res.StatusCode == 200 {
tokenSuccess := db.MemeTokenSuccess{}

// Unmarshal result
err = json.Unmarshal(body, &tokenSuccess)

if err != nil {
log.Printf("Reading token success body failed: %s", err)
}

return "", tokenSuccess
} else {
var tokenError string

// Unmarshal result
err = json.Unmarshal(body, &tokenError)

if err != nil {
log.Printf("Reading token error body failed: %s %d", err, res.StatusCode)
}

return tokenError, db.MemeTokenSuccess{}
}
}

func UploadMemeImage(file multipart.File, token string, fileName string) string {
url := fmt.Sprintf("%s/public", config.MemeUrl)
filePath := path.Join("./uploads", fileName)
fileW, _ := os.Open(filePath)
defer file.Close()

fileBody := &bytes.Buffer{}
writer := multipart.NewWriter(fileBody)
part, _ := writer.CreateFormFile("file", filepath.Base(filePath))
io.Copy(part, fileW)
writer.Close()

client := &http.Client{}
req, err := http.NewRequest(http.MethodPost, url, fileBody)
req.Header.Set("Authorization", "BEARER "+token)
req.Header.Set("Content-Type", writer.FormDataContentType())
res, err := client.Do(req)

// Delete image from uploads folder
DeleteImageFromUploadsFolder(filePath)

if err != nil {
fmt.Println("meme request Error ===", err)
return ""
}

defer res.Body.Close()
body, err := io.ReadAll(res.Body)

if err == nil {
memeSuccess := db.Meme{}
// Unmarshal result
err = json.Unmarshal(body, &memeSuccess)
if err != nil {
log.Printf("Reading meme error body failed: %s", err)
} else {
return config.MemeUrl + "/public/" + memeSuccess.Muid
}
}

return ""
}

func DeleteImageFromUploadsFolder(filePath string) {
e := os.Remove(filePath)
if e != nil {
log.Printf("Could not delete Image %s %s", filePath, e)
}
}

func CreateUploadsDirectory(dirName string) {
if _, err := os.Open(dirName); os.IsNotExist(err) {
fmt.Println("The directory named", dirName, "does not exist")
os.Mkdir(dirName, 0755)
}
}
1 change: 1 addition & 0 deletions routes/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ func NewRouter() *http.Server {
r.Get("/refresh_jwt", handlers.RefreshToken)
r.Post("/invoices", handlers.GenerateInvoice)
r.Post("/budgetinvoices", handlers.GenerateBudgetInvoice)
r.Post("/meme_upload", handlers.MemeImageUpload)
})

PORT := os.Getenv("PORT")
Expand Down
Loading