Simplified profile code.

This commit is contained in:
Kleissner
2021-09-28 05:39:38 +02:00
parent 9ef6a88512
commit 608dbf6f47
7 changed files with 203 additions and 375 deletions

View File

@@ -86,27 +86,6 @@ type apiBlockchainBlock struct {
RecordsDecoded []interface{} `json:"recordsdecoded"` // Records decoded. The encoding for each record depends on its type.
}
// apiBlockRecordProfile contains end-user information. Any data is treated as untrusted and unverified by default.
type apiBlockRecordProfile struct {
Fields []apiBlockRecordProfileField `json:"fields"` // All fields
Blobs []apiBlockRecordProfileBlob `json:"blobs"` // Blobs
}
// apiBlockRecordProfileField contains a single information about the end user. The data is always UTF8 text encoded.
// Note that all profile data is arbitrary and shall be considered untrusted and unverified.
// To establish trust, the user must load Certificates into the blockchain that validate certain data.
type apiBlockRecordProfileField struct {
Type uint16 `json:"type"` // See ProfileFieldX constants.
Text string `json:"text"` // The data
}
// apiBlockRecordProfileBlob is similar to apiBlockRecordProfileField but contains binary objects instead of text.
// It can be used for example to store a profile picture on the blockchain.
type apiBlockRecordProfileBlob struct {
Type uint16 `json:"type"` // See ProfileBlobX constants.
Data []byte `json:"data"` // The data
}
// apiFileMetadata contains metadata information.
type apiFileMetadata struct {
Type uint16 `json:"type"` // See core.TagX constants.

View File

@@ -15,68 +15,63 @@ import (
// apiProfileData contains profile metadata stored on the blockchain. Any data is treated as untrusted and unverified by default.
type apiProfileData struct {
Fields []apiBlockRecordProfileField `json:"fields"` // All fields
Blobs []apiBlockRecordProfileBlob `json:"blobs"` // All blobs
Status int `json:"status"` // Status of the operation, only used when this structure is returned from the API. See core.BlockchainStatusX.
Fields []apiBlockRecordProfile `json:"fields"` // All fields
Status int `json:"status"` // Status of the operation, only used when this structure is returned from the API. See core.BlockchainStatusX.
}
// apiBlockRecordProfile provides information about the end user. Note that all profile data is arbitrary and shall be considered untrusted and unverified.
// To establish trust, the user must load Certificates into the blockchain that validate certain data.
type apiBlockRecordProfile struct {
Type uint16 `json:"type"` // See ProfileX constants.
// Depending on the exact type, one of the below fields is used for proper encoding:
Text string `json:"text"` // Text value. UTF-8 encoding.
Blob []byte `json:"blob"` // Binary data
}
/*
apiProfileList lists all users profile fields and blobs.
apiProfileList lists all users profile fields.
Request: GET /profile/list
Response: 200 with JSON structure apiProfileData
*/
func apiProfileList(w http.ResponseWriter, r *http.Request) {
fields, blobs, status := core.UserProfileList()
fields, status := core.UserProfileList()
result := apiProfileData{Status: status}
for n := range fields {
result.Fields = append(result.Fields, apiBlockRecordProfileField{Type: fields[n].Type, Text: fields[n].Text})
}
for n := range blobs {
result.Blobs = append(result.Blobs, apiBlockRecordProfileBlob{Type: blobs[n].Type, Data: blobs[n].Data})
result.Fields = append(result.Fields, blockRecordProfileToAPI(fields[n]))
}
EncodeJSON(w, r, result)
}
/*
apiProfileRead reads a specific users profile field or blob.
For the index see core.ProfileFieldX and core.ProfileBlobX constants.
apiProfileRead reads a specific users profile field. See core.ProfileX for recognized fields.
Request: GET /profile/read?field=[index] or &blob=[index]
Request: GET /profile/read?field=[index]
Response: 200 with JSON structure apiProfileData
*/
func apiProfileRead(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fieldN, err1 := strconv.Atoi(r.Form.Get("field"))
blobN, err2 := strconv.Atoi(r.Form.Get("blob"))
if (err1 != nil && err2 != nil) || (err1 == nil && fieldN < 0) || (err2 == nil && blobN < 0) {
if err1 != nil || fieldN < 0 {
http.Error(w, "", http.StatusBadRequest)
return
}
var result apiProfileData
if err1 == nil {
var text string
if text, result.Status = core.UserProfileReadField(uint16(fieldN)); result.Status == core.BlockchainStatusOK {
result.Fields = append(result.Fields, apiBlockRecordProfileField{Type: uint16(fieldN), Text: text})
}
} else {
var data []byte
if data, result.Status = core.UserProfileReadBlob(uint16(blobN)); result.Status == core.BlockchainStatusOK {
result.Blobs = append(result.Blobs, apiBlockRecordProfileBlob{Type: uint16(blobN), Data: data})
}
var data []byte
if data, result.Status = core.UserProfileReadField(uint16(fieldN)); result.Status == core.BlockchainStatusOK {
result.Fields = append(result.Fields, blockRecordProfileToAPI(core.BlockRecordProfile{Type: uint16(fieldN), Data: data}))
}
EncodeJSON(w, r, result)
}
/*
apiProfileWrite writes profile fields or blobs.
For the index see core.ProfileFieldX and core.ProfileBlobX constants.
apiProfileWrite writes profile fields. See core.ProfileX for recognized fields.
Request: POST /profile/write with JSON structure apiProfileData
Response: 200 with JSON structure apiBlockchainBlockStatus
@@ -87,23 +82,19 @@ func apiProfileWrite(w http.ResponseWriter, r *http.Request) {
return
}
var profile core.BlockRecordProfile
var fields []core.BlockRecordProfile
for n := range input.Fields {
profile.Fields = append(profile.Fields, core.BlockRecordProfileField{Type: input.Fields[n].Type, Text: input.Fields[n].Text})
}
for n := range input.Blobs {
profile.Blobs = append(profile.Blobs, core.BlockRecordProfileBlob{Type: input.Blobs[n].Type, Data: input.Blobs[n].Data})
fields = append(fields, blockRecordProfileFromAPI(input.Fields[n]))
}
newHeight, newVersion, status := core.UserProfileWrite(profile)
newHeight, newVersion, status := core.UserProfileWrite(fields)
EncodeJSON(w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
}
/*
apiProfileDelete deletes profile fields or blobs identified by the types.
For the index see core.ProfileFieldX and core.ProfileBlobX constants.
apiProfileDelete deletes profile fields identified by the types. See core.ProfileX for recognized fields.
Request: POST /profile/delete with JSON structure apiProfileData
Response: 200 with JSON structure apiBlockchainBlockStatus
@@ -114,16 +105,13 @@ func apiProfileDelete(w http.ResponseWriter, r *http.Request) {
return
}
var fields, blobs []uint16
var fields []uint16
for n := range input.Fields {
fields = append(fields, input.Fields[n].Type)
}
for n := range input.Blobs {
blobs = append(blobs, input.Blobs[n].Type)
}
newHeight, newVersion, status := core.UserProfileDelete(fields, blobs)
newHeight, newVersion, status := core.UserProfileDelete(fields)
EncodeJSON(w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
}
@@ -131,22 +119,34 @@ func apiProfileDelete(w http.ResponseWriter, r *http.Request) {
// --- conversion from core to API data ---
func blockRecordProfileToAPI(input core.BlockRecordProfile) (output apiBlockRecordProfile) {
for n := range input.Fields {
output.Fields = append(output.Fields, apiBlockRecordProfileField{Type: input.Fields[n].Type, Text: input.Fields[n].Text})
}
for n := range input.Blobs {
output.Blobs = append(output.Blobs, apiBlockRecordProfileBlob{Type: input.Blobs[n].Type, Data: input.Blobs[n].Data})
output.Type = input.Type
switch input.Type {
case core.ProfileName, core.ProfileEmail, core.ProfileWebsite, core.ProfileTwitter, core.ProfileYouTube, core.ProfileAddress:
output.Text = input.Text()
case core.ProfilePicture:
output.Blob = input.Data
default:
output.Blob = input.Data
}
return output
}
func blockRecordProfileFromAPI(input apiBlockRecordProfile) (output core.BlockRecordProfile) {
for n := range input.Fields {
output.Fields = append(output.Fields, core.BlockRecordProfileField{Type: input.Fields[n].Type, Text: input.Fields[n].Text})
}
for n := range input.Blobs {
output.Blobs = append(output.Blobs, core.BlockRecordProfileBlob{Type: input.Blobs[n].Type, Data: input.Blobs[n].Data})
output.Type = input.Type
switch input.Type {
case core.ProfileName, core.ProfileEmail, core.ProfileWebsite, core.ProfileTwitter, core.ProfileYouTube, core.ProfileAddress:
output.Data = []byte(input.Text)
case core.ProfilePicture:
output.Data = input.Blob
default:
output.Data = input.Blob
}
return output

View File

@@ -37,10 +37,10 @@ These are the functions provided by the API:
/blockchain/self/list/file List all files stored on the blockchain
/blockchain/self/delete/file Delete files from the blockchain
/profile/list List all users profile fields and blobs
/profile/read Read a profile field or blob
/profile/write Write profile fields or blobs
/profile/delete Delete profile fields or blobs
/profile/list List all profile fields
/profile/read Read a profile field
/profile/write Write profile fields
/profile/delete Delete profile fields
/search Submit a search request
/search/result Return search results
@@ -98,6 +98,18 @@ type apiResponsePeerSelf struct {
## Blockchain Functions
Common status codes returned by various endpoints:
```go
const (
BlockchainStatusOK = 0 // No problems in the blockchain detected.
BlockchainStatusBlockNotFound = 1 // Missing block in the blockchain.
BlockchainStatusCorruptBlock = 2 // Error block encoding
BlockchainStatusCorruptBlockRecord = 3 // Error block record encoding
BlockchainStatusDataNotFound = 4 // Requested data not available in the blockchain
)
```
### Blockchain Self Header
This function returns information about the current peer. It is not required that a peer has a blockchain. If no data is shared, there are no blocks. The blockchain does not formally have a header as each block has the same structure.
@@ -167,23 +179,6 @@ The array `RecordsDecoded` will contain any present record of the following:
* Profile records, see `apiBlockRecordProfile`
* File records, see `apiBlockRecordFile`
```go
type apiBlockRecordProfile struct {
Fields []apiBlockRecordProfileField `json:"fields"` // All fields
Blobs []apiBlockRecordProfileBlob `json:"blobs"` // Blobs
}
type apiBlockRecordProfileField struct {
Type uint16 `json:"type"` // See ProfileFieldX constants.
Text string `json:"text"` // The data
}
type apiBlockRecordProfileBlob struct {
Type uint16 `json:"type"` // See ProfileBlobX constants.
Data []byte `json:"data"` // The data
}
```
## File Functions
These functions allow adding, deleting, and listing files stored on the users blockchain. Only metadata is actually stored on the blockchain.
@@ -355,25 +350,26 @@ Example response:
## Profile Functions
User profile data such as the username, email address, and picture are stored on the blockchain. The Profile API distinguishes between text data (= fields) and binary data (= blobs). Text is UTF-8 encoded.
User profile data such as the username, email address, and picture are stored on the blockchain. Profile fields are text (UTF-8) or binary encoded, depending on the type.
Below is the current list of well known profile information. Clients may define additional fields. The purpose of this defined list is to provide a common mapping across different client software. In the Go code they are defined as constants starting with `ProfileField` and `ProfileBlob`.
Note that all profile data is arbitrary and shall be considered untrusted and unverified. To establish trust, the user must load Certificates into the blockchain that validate certain data.
| Field Identifier | Purpose |
|------------------|----------------------------|
| 0 | Username, arbitrary |
| 1 | Email address |
| 2 | Website address |
| 3 | Twitter account with the @ |
| 4 | YouTube channel URL |
| 5 | Physical address |
Below is the list of well known profile information. Clients may define additional fields. The purpose of this defined list is to provide a common mapping across different client software. Undefined types are always mapped into the `blob` field.
| Blob Identifier | Purpose |
|------------------|----------------------------|
| 0 | Profile picture, unspecified size |
| Type | Constant | Encoding | Info |
|------|----------------|----------|-------------------------------|
| 0 | ProfileName | Text | Arbitrary username |
| 1 | ProfileEmail | Text | Email address |
| 2 | ProfileWebsite | Text | Website address |
| 3 | ProfileTwitter | Text | Twitter account without the @ |
| 4 | ProfileYouTube | Text | YouTube channel URL |
| 5 | ProfileAddress | Text | Physical address |
| 6 | ProfilePicture | Blob | Profile picture |
### Profile List
This lists all profile fields.
```
Request: GET /profile/list
Response: 200 with JSON structure apiProfileData
@@ -381,18 +377,16 @@ Response: 200 with JSON structure apiProfileData
```go
type apiProfileData struct {
Fields []apiBlockRecordProfileField `json:"fields"` // All fields
Blobs []apiBlockRecordProfileBlob `json:"blobs"` // All blobs
Status int `json:"status"` // Status of the operation, only used when this structure is returned from the API. See core.BlockchainStatusX.
Fields []apiBlockRecordProfile `json:"fields"` // All fields
Status int `json:"status"` // Status of the operation, only used when this structure is returned from the API. See core.BlockchainStatusX.
}
const (
BlockchainStatusOK = 0 // No problems in the blockchain detected.
BlockchainStatusBlockNotFound = 1 // Missing block in the blockchain.
BlockchainStatusCorruptBlock = 2 // Error block encoding
BlockchainStatusCorruptBlockRecord = 3 // Error block record encoding
BlockchainStatusDataNotFound = 4 // Requested data not available in the blockchain
)
type apiBlockRecordProfile struct {
Type uint16 `json:"type"` // See ProfileX constants.
// Depending on the exact type, one of the below fields is used for proper encoding:
Text string `json:"text"` // Text value. UTF-8 encoding.
Blob []byte `json:"blob"` // Binary data
}
```
Example request: `http://127.0.0.1:112/profile/list`
@@ -403,22 +397,23 @@ Example response:
{
"fields": [{
"type": 0,
"text": "Test Username 2022"
"text": "Test Username 2021",
"blob": null
}, {
"type": 1,
"text": "test@example.com"
"text": "test@example.com",
"blob": null
}],
"blobs": null,
"status": 0
}
```
### Profile Read
This reads a specific users' profile field or blob. For the index see the `ProfileFieldX` and `ProfileBlobX` constants.
This reads a specific profile field. See ProfileX for recognized fields.
```
Request: GET /profile/read?field=[index] or &blob=[index]
Request: GET /profile/read?field=[index]
Response: 200 with JSON structure apiProfileData
```
@@ -430,16 +425,16 @@ Example response:
{
"fields": [{
"type": 0,
"text": "Test Username 2022"
"text": "Test Username 2021",
"blob": null
}],
"blobs": null,
"status": 0
}
```
### Profile Write
This writes profile fields and blobs. It can write multiple fields and blobs at once.
This writes profile fields. It can write multiple fields at once. See ProfileX for recognized fields.
```
Request: POST /profile/write with JSON structure apiProfileData
@@ -469,7 +464,7 @@ Example response:
### Profile Delete
This function allows to delete profile data (both fields and blobs). Only the type number in the fields and blobs are required. Multiple fields and blobs can be deleted at the same time.
This function allows to delete profile fields. Only the type number is required. Multiple fields can be deleted at the same time.
```
Request: POST /profile/delete with JSON structure apiProfileData
@@ -486,16 +481,6 @@ Example POST request to `http://127.0.0.1:112/profile/delete` (deleting the prof
}
```
One practical use case is deleting the profile picture (by specifying blob 0) with the following payload:
```json
{
"blobs": [{
"type": 0
}]
}
```
## Search API
The search API provides a high-level function to search for files in Peernet. Searching is always asynchronous. `/search` returns an UUID which is used to loop over `/search/result` until the search is terminated.