MiddleWare HTTP API

MEMO Middleware provides a secure, flexible, and composable data network for developers, enterprises, and more. Users have the flexibility to choose the underlying storage system that suits them.

The middleware will provide storage unit price query and storage plan purchase services, users choose to recharge and purchase packages, obtain storage space, and then upload and download files, and support users to query the list of uploaded files.

Start the middleware service, and the default listening port is 8080. In the example used in this document, the http listening port is set to 8081 and the baseURL is http://localhost:8081. All the following request URLs should be changed according to the actual situation.

1. Login Verification

1.1 Challenge

Before logging in, users need to get the challenge message according to the address, and users must set the domain through the Origin field.

Request URL: http://localhost:8081/challenge?address={address}

Request Method: GET

Return Parameters: 'challenge message' (defined by EIP-4361)

Request Example:

challenge

Note: When calling the challenge interface, you need to specify the domain name in the Origin field of the headers, and currently only the domain name http://memo.io is supported, otherwise an error will be returned.

Error Code:

HTTP Status Code Error Code Error Description
500 InternalError We encountered an internal error, please try again.

1.2 Login Request(use eth account)

Users can use the eth account to log in. Users must call the challenge interface first before logging in to get message. Then, the user sign the message used private key. See [EIP-191](https://eips.ethereum.org/EIPS/eip -191) for more details on signature methods. Note: After calling the challenge interface to get the challenge message, the login must be completed within 30 seconds, otherwise the login will fail.

Request URL:http://localhost:8081/login

Request Method:POST

Request Parameters(JSON format):

{
"message":
"memo.io wants you to sign in with your Ethereum account:\n0xFD976F1F3dC6413Da5Fed05471eaBB01F4FaaC42\n\n\nURI: http://memo.io\nVersion: 1\nChain ID: 985\nNonce: 12c2ad59e12abbe224cf86741c4bf00a21432fb2673b29a694b72062385f9b5d\nIssued At: 2023-04-23T07:36:10Z",
"signature":
"0xd5c406dd9ca168cc0894788cd262c3e9bd2f5413f87654c8cb685a9d872b9ab151fe80930c8de4da6adeadf38714387bee65c949ba47efa3eb5ee1335b6cc79400"
}
Parameter Value Type Required Description
message request signature message string yes the value returned by 'challenge'
signature signature information string yes the message after signing the text

The way to get the signature information is shown in the following code:

package main

import (
"crypto/ecdsa"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)

func main() {
secretKey := flag.String("sk", "", "the sk to signature")

flag.Parse()

privateKey, err := crypto.HexToECDSA(*secretKey)
if err != nil {
    fmt.Println(err.Error())
    return
}

publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
    log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
}
address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()

// get MEMO-Middleware challenge message
text, err := Challenge(address)
if err != nil {
    log.Fatal(err)
}
fmt.Println("message:\n", text)

// eip191-signature
hash := crypto.Keccak256([]byte(fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(text), text)))
signature, err := crypto.Sign(hash, privateKey)
if err != nil {
    log.Fatal(err)
}
sig := hexutil.Encode(signature)
fmt.Println("login sig:\n", sig)
}

func Challenge(address string) (string, error) {
client := &http.Client{Timeout: time.Minute}
// ip:port should be corresponding to that MEMO-Middleware server is listening
url := "http://localhost:8081/challenge"

req, err := http.NewRequest("GET", url, nil)
if err != nil {
    return "", err
}

params := req.URL.Query()
params.Add("address", address)
req.URL.RawQuery = params.Encode()
req.Header.Set("Origin", "https://memo.io")

res, err := client.Do(req)
if err != nil {
    return "", err
}
defer res.Body.Close()

body, err := ioutil.ReadAll(res.Body)
if err != nil {
    return "", err
}

if res.StatusCode != http.StatusOK {
    return "", fmt.Errorf("respond code[%d]: %s", res.StatusCode, string(body))
}

return string(body), nil
}

Return Parameters(JSON):

{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoxLCJhdWQiOiJtZW1vLmlvIiwiZXhwIjoxNjc3NDkwMTgyLCJpYXQiOjE2Nzc0ODkyODIsImlzcyI6Im1lbW8uaW8iLCJzdWIiOiIweEU3RTlmMTJmOTlhRDE3ZDQ3ODZiOUIxMjQ3QzA5N2U2M2NlYUY4RGIifQ.F0asDvu3LH3ccK6LAztBGF1TTzGw7Stc9gBEzVicuE4",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoyLCJhdWQiOiJtZW1vLmlvIiwiZXhwIjoxNjc4MDk0MDgyLCJpYXQiOjE2Nzc0ODkyODIsImlzcyI6Im1lbW8uaW8iLCJzdWIiOiIweEU3RTlmMTJmOTlhRDE3ZDQ3ODZiOUIxMjQ3QzA5N2U2M2NlYUY4RGIifQ.PDxQ2orOlsES6fvkyR-xWc6M1yBY8RiFTcn8m5AGROc"
}
Parameter Value Type Required Description
accessToken authentication token string yes within 15 minutes, holding the token can be password-free
refreshToken refresh token string yes within 7 days, holding the token can regenerate the access token

Request Example:

login

Error Code:

HTTP Status Code Error Code Error Description
500 InternalError We encountered an internal error, please try again.
401 Authentication There is an empty parameter;Can't parse message;Got wrong chain id; Got wrong domain; Got wrong nonce; Got wrong address; Got wrong signature

1.3 Login Request(use lens account)

Log in with a lens account without calling the interface to get nonce. However, it is necessary to call the challenge interface of lens to obtain the text information to be signed (the format defined by EIP-4361), and use EIP-191 The defined signature method to sign the text information needs to be sent a login request within 30 seconds. When running the middleware service, check whether the account is a Lens account is enabled or disabled.

Request URL: http://localhost:8081/lens/login

Request Method:POST

Request Parameter(JSON Format):

{
"message":"\nmemo.io wants you to sign in with your Ethereum account:\n0x51632235cc673a788E02B30B9F16F7B1D300194C\n\nSign in with ethereum to lens\n\nURI: memo.io\nVersion: 1\nChain ID: 137\nNonce: bcb9b92754e2b900\nIssued At: 2023-03-14T07:26:05.501Z\n ",
"signature":"0x..."
}
Parameter Value Type Required Description
message request signature message string yes call the challenge interface of lens to obtain the text information that needs to be signed
signature signature information string yes the message after signing the text

Call the lens interface to obtain text information as follows:

import(
"context"

"github.com/machinebox/graphql"
)

type Challenge struct {
Challenge struct {
    Text string
} `graphql:"challenge(request: $request)"`
}

type ChallengeRequest struct {
Address string `json:"address"`
}

func ChallengeRequest(address string) (string, error) {
client := graphql.NewClient("https://api.lens.dev")

req := graphql.NewRequest(`
    query Challenge($request:ChallengeRequest!) {
        challenge(request:$request) {
            text
        }
    }`)

req.Var("request", ChallengeRequest{ Address: address })
req.Header.Set("Origin", "memo.io")

var query Challenge
if err := client.Run(context.Background(), req, &query); err != nil {
    return "", err
}

return query.Challenge.Text, nil
}

Signing using the EIP-191 definition can be borrowed from the code example in 1.2.

Returned Parameter(JSON):

{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoxLCJhdWQiOiJtZW1vLmlvIiwiZXhwIjoxNjc3NDkwMTgyLCJpYXQiOjE2Nzc0ODkyODIsImlzcyI6Im1lbW8uaW8iLCJzdWIiOiIweEU3RTlmMTJmOTlhRDE3ZDQ3ODZiOUIxMjQ3QzA5N2U2M2NlYUY4RGIifQ.F0asDvu3LH3ccK6LAztBGF1TTzGw7Stc9gBEzVicuE4",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoyLCJhdWQiOiJtZW1vLmlvIiwiZXhwIjoxNjc4MDk0MDgyLCJpYXQiOjE2Nzc0ODkyODIsImlzcyI6Im1lbW8uaW8iLCJzdWIiOiIweEU3RTlmMTJmOTlhRDE3ZDQ3ODZiOUIxMjQ3QzA5N2U2M2NlYUY4RGIifQ.PDxQ2orOlsES6fvkyR-xWc6M1yBY8RiFTcn8m5AGROc"
}
Parameter Value Type Required Description
accessToken authentication token string yes within 15 minutes, holding the token can be password-free
refreshToken refresh token string yes within 7 days, holding the token can regenerate the access token

Request Example:

lenslogin

Error Code:

HTTP Status Code Error Code Error Description
517 Address The address {address} is not registered on lens
500 InternalError We encountered an internal error, please try again.
401 Authentication There is an empty parameter; Got wrong domain; Got wrong chain id; Got wrong address/signature;

1.4 refresh accessToken

AccessToken is valid for 15 minutes; The validity period of refreshToken is 7 days, when accesToken expires, you need to refresh accessToken according to refreshToken for passwordless authentication login.

Request URL: http://localhost:8081/refresh

Request Method: GET

Request Header Information:

Parameter Value Type Required Description
Authorization "Bearer refreshToken" string yes the refreshToken returned by the above login request

Returned Parameter(JSON):

Parameter Value Type Description
access token authentication token string the authentication token is regenerated after the validity period has elapsed

Request Example:

refresh

Error Code:

HTTP Status Code Error Code Error Description
401 Unauthorized Illegal fresh token

 

2. File Upload

After the user is logged in, they can upload files. MEFS uses object storage, and files are uploaded to a bucket with the same name as the login account by default. When you upload a file, if you have not created a bucket with the same name, the middleware will automatically create a bucket with the same name.

Uploading files is limited by the user's recharge amount and storage space, and users can first query the storage unit price and storage plan to recharge to obtain storage space and balance.

Request URL:

choose upload to mefs:http://localhost:8081/mefs/

choose upload to ipfs:http://localhost:8081/ipfs/

Request Method: POST

Request Header Information:

Parameter Value
Content-Type multipart/form-data
Authorization "Bearer accessToken"

Request Parameter:

Parameter Value Type Required Description
file the file to upload File yes note that select 'File' format

Returned Parameter(JSON format):

Parameter Value Type Required Description
cid the CID of the uploaded file string yes a unique identifier for the file

Returned Example:

Response:Status 200

{
 "cid": "bafybeie2ph7iokrckc5iy6xu7npa4xqst6ez5ewb7j2igeilxjaw6sd2qi"
}

Request Example:

put1

put2

Error Code:

HTTP Status Code Error Code Error Description
401 Authentication Token is Null; Invalid token payload; Invalid token;
500 InternalError We encountered an internal error, please try again.
518 Storage storage not support

 

3. File Download

Users can download the corresponding file according to the CID of the file.

Request URL:

http://ip:port/mefs/$cid

http://ip:port/ipfs/$cid

choose download from mefs: http://localhost:8081/mefs/bafkreifzwcj6vkozz6brwutpxl3hqneran4y5vtvirnbrtw3l2m3jtlgq4

choose download from ipfs: http://localhost:8081/ipfs/bafkreifzwcj6vkozz6brwutpxl3hqneran4y5vtvirnbrtw3l2m3jtlgq4

Request Method: GET

Request Header Information:

none

Request Parameter:

none

Returned Parameter(DataFromReader):

Return file.

Parameter Description Value
code status code 200
contentLength file size
contentType file type
reader io.Reader,the file transfer buffer

Request Example:

get1

get2

Error Code:

HTTP Status Code Error Code Error Description
500 InternalError We encountered an internal error, please try again.
518 Storage storage not support
517 Address address is null

 

4. Delete File

Delete uploaded file (only support mefs).

Request URL:
http://ip:port/mefs/delete

Request Method: GET

Request Header Information:

Parameter Value Type Required Description
Authorization "Bearer accessToken" string yes If it expires, you can get a new valid accessToken by refreshing the accessToken.

Request Parameter:

Parameter Value Type Required Description
mid file mid string yes the unique identifier of the file, the string returned when file is uploaded

Returned Parameter(JSON):

Parameter Type Description
Status string deletion success or fail

Request Example:

delete

 

5. Query File List

Users query the list of files they uploaded.

Request URL:

http://ip:port/mefs/listobjects

http://ip:port/ipfs/listobjects

The file list query of IPFS has not yet been implemented.

http://localhost:8081/mefs/listobjects

http://localhost:8081/ipfs/listobjects

Request Method: GET

A list of files for the login account is displayed.

Request Header Information:

Parameter Value Type Required Description
Authorization "Bearer accessToken" string yes If it expires, you can get a new valid accessToken by refreshing the accessToken.

Request Parameter:

none

Returned Parameter(JSON):

Parameter Type Description
Address string account Ethereum wallet address
Storage string mefs or ipfs
Object struct file list

Each object contains information:

Parameter Type Description
Name string file name
Size int64 file size
Cid string file cid
ModTime time file modification time
UserDefined struct some other information about the file

UserDefined struct information:

Parameter Type Description
encryption string the file encryption method
etag string file ID mode (default cid)

Request Example:

list

Error Code:

HTTP Status Code Error Code Error Description
401 Authentication Token is Null; Invalid token payload; Invalid token;
516 Storage list object error %s
518 Storage storage not support

 

6. Query Account Balance

Users query their storage balances.

Request URL:http://localhost:8081/account/balance

Request Method: GET

Request Header Information:

Parameter Value Type Required Description
Authorization "Bearer accessToken" string yes If it expires, you can get a new valid accessToken by refreshing the accessToken.

Request Parameter:

none

Returned Parameter(JSON):

Parameter Value Type Required Description
Address string yes the Ethereum wallet address where you log in to your account
Balance string yes the smallest unit number of balances is indicated

Request Example:

balance

Error Code:

HTTP Status Code Error Code Error Description
401 Authentication Token is Null; Invalid token payload; Invalid token;
516 Storage make bucket error %s;
518 Storage storage not support
520 Eth rpc error

 

7. Query Storage Space

Users query their storage space, including used, available, free space, and the number of files uploaded.

Request URL:http://localhost:8081/account/getstorage?stype={stype}

Request Method: GET

Request Header Information:

Parameter Value Type Required Description
Authorization "Bearer accessToken" string yes If it expires, you can get a new valid accessToken by refreshing the accessToken.

Request Parameter:

Parameter Value Description
stype storage type supported mefs,ipfs,qiniu now

Returned Parameter(JSON):

Parameter Type Description
Used string space used
Available string space avail
Free string space free
Files string number of files

Request Example:

Error Code:

HTTP Status Code Error Code Error Description
401 Authentication Token is Null; Invalid token payload; Invalid token;

 

8. Query Price

Query the storage unit price of various storage methods.

The server side has not yet been implemented.

 

9. Query Storage Packages

Request URL:

http://localhost:8081/account/pkginfos

Request Method: GET

Request Headers:

Parameter Name Variable Type [Length Limit] Required Description
Authorization "Bearer Login-Generated AccessToken" string Yes If expired, new valid AccessToken can be obtained by refreshing AccessToken

Response Parameters (JSON):

Parameter Name Variable Type [Length Limit] Description
Time Storage Time (in seconds) string the storage duration included in the package
Kind Storage Type int the storage kind, includes MEFS and IPFS now
Buysize Purchased Storage Space string the storage size included in the package
Amount Price string the price of the package
State Status int the state of the package, 1 means valid, 0 means invalid

Request Example:

pkginfos

Error Codes:

HTTP Status Code Error Code Error Description
401 Authentication Token is Null; Invalid token payload; Invalid token;

 

10. Buy a Storage Package

Request URL:    http://localhost:8081/account/buypkg

Request Method: GET

Request Headers:

Parameter Name Variable Type [Length Limit] Required Description
Authorization "Bearer Login-Generated AccessToken" string Yes If expired, new valid AccessToken can be obtained by refreshing AccessToken

Request Parameters:

Parameter Name Variable Type [Length Limit] Required Description
amount Amount string Yes payment amount
pkgid Package ID string Yes package id, can get from 'Query Storage Packages'
chainid Chain ID string Yes the blockchain ID that executed the purchase package

Response Parameters (JSON):

Parameter Name Variable Type [Length Limit] Required Description
Status Status string Yes buying is success or fails

Request Example:

Error Codes:

HTTP Status Code Error Code Error Description
401 Authentication Token is Null; Invalid token payload; Invalid token;

 

11. Get Purchased Storage Packages

Request URL:  http://localhost:8081/account/getbuypkgs

Request Method: GET

Request Headers:

Parameter Name Variable Type [Length Limit] Required Description
Authorization "Bearer Login-Generated AccessToken" string Yes If expired, new valid AccessToken can be obtained by refreshing AccessToken

Request Parameters:

None

Response Parameters (JSON):

Parameter Name Variable Type [Length Limit] Description
Starttime Start Time string the start service time of the purchased package
Endtime End Time string the end service time of the purchased package
Kind Type int 0: MEFS, 1: IPFS
Buysize Purchased Size int the size of the purchased package
Amount Purchase Amount int the payment value of the purchased package
State Status int the package state

Request Example:

Error Codes:

HTTP Status Code Error Code Error Description
401 Authentication Token is Null; Invalid token payload; Invalid token;

MiddleWare HTTP API

  1. Login Verification