github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/config/profile.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/qri-io/jsonschema"
     8  	"github.com/qri-io/qri/dsref"
     9  )
    10  
    11  // ProfilePod is serializable plain-old-data that configures a qri profile
    12  type ProfilePod struct {
    13  	ID       string `json:"id"`
    14  	PrivKey  string `json:"privkey,omitempty"`
    15  	KeyID    string `json:"keyid"`
    16  	Peername string `json:"peername"`
    17  	// Created timestamp
    18  	Created time.Time `json:"created"`
    19  	// Updated timestamp
    20  	Updated time.Time `json:"updated"`
    21  	// specifies weather this is a user or an organization
    22  	Type string `json:"type"`
    23  	// user's email address
    24  	Email string `json:"email"`
    25  	// user name field. could be first[space]last, but not strictly enforced
    26  	Name string `json:"name"`
    27  	// user-filled description of self
    28  	Description string `json:"description"`
    29  	// url this user wants the world to click
    30  	HomeURL string `json:"homeurl"`
    31  	// color this user likes to use as their theme color
    32  	Color string `json:"color"`
    33  	// Thumb path for user's thumbnail
    34  	Thumb string `json:"thumb"`
    35  	// Profile photo
    36  	Photo string `json:"photo"`
    37  	// Poster photo for users's profile page
    38  	Poster string `json:"poster"`
    39  	// Twitter is a peer's twitter handle
    40  	Twitter string `json:"twitter"`
    41  	// Online indicates if the user is currently connected to the qri network
    42  	// Should not serialize to config.yaml
    43  	Online bool `json:"online,omitempty"`
    44  	// PeerIDs maps this profile to peer Identifiers in the form /[network]/peerID example:
    45  	// /ipfs/QmSyDX5LYTiwQi861F5NAwdHrrnd1iRGsoEvCyzQMUyZ4W
    46  	// where QmSy... is a peer identifier on the IPFS peer-to-peer network
    47  	// Should not serialize to config.yaml
    48  	PeerIDs []string `json:"peerIDs,omitempty"`
    49  	// NetworkAddrs keeps a list of locations for this profile on the network as multiaddr strings
    50  	// Should not serialize to config.yaml
    51  	NetworkAddrs []string `json:"networkAddrs,omitempty"`
    52  }
    53  
    54  // SetArbitrary is an interface implementation of base/fill/struct in order to safely
    55  // consume config files that have definitions beyond those specified in the struct.
    56  // This simply ignores all additional fields at read time.
    57  func (p *ProfilePod) SetArbitrary(key string, val interface{}) error {
    58  	return nil
    59  }
    60  
    61  // DefaultProfile gives a new default profile configuration
    62  func DefaultProfile() *ProfilePod {
    63  	now := time.Now()
    64  	return &ProfilePod{
    65  		Created: now,
    66  		Updated: now,
    67  		Type:    "peer",
    68  	}
    69  }
    70  
    71  // Validate validates all fields of profile returning all errors found.
    72  func (p ProfilePod) Validate() error {
    73  	if err := dsref.EnsureValidUsername(p.Peername); err != nil {
    74  		return err
    75  	}
    76  
    77  	schema := jsonschema.Must(`{
    78      "$schema": "http://json-schema.org/draft-06/schema#",
    79      "title": "Profile",
    80      "description": "Profile of a qri peer",
    81      "type": "object",
    82      "properties": {
    83        "id": {
    84          "description": "Unique identifier for a peername",
    85          "type": "string"
    86        },
    87        "privkey": {
    88          "description": "Private key associated with this peerid",
    89          "type": "string"
    90        },
    91        "peername": {
    92          "description": "Handle name for this peer on qri",
    93          "type": "string",
    94          "not": {
    95            "enum": [
    96              "me",
    97              "status",
    98              "at",
    99              "add",
   100              "history",
   101              "remove",
   102              "export",
   103              "profile",
   104              "list",
   105              "peers",
   106              "connections",
   107              "save",
   108              "connect"
   109            ]
   110          }
   111        },
   112        "created": {
   113          "description": "Datetime the profile was created",
   114          "type": "string",
   115          "format": "date-time"
   116        },
   117        "updated": {
   118          "description": "Datetime the profile was last updated",
   119          "type": "string",
   120          "format": "date-time"
   121        },
   122        "type": {
   123          "description": "The type of peer this profile represents",
   124          "type": "string",
   125          "enum": [
   126            "peer",
   127            "organization"
   128          ]
   129        },
   130        "email": {
   131          "description": "Email associated with this peer",
   132          "type": "string",
   133          "anyOf": [
   134            {
   135              "maxLength": 255,
   136              "format": "email"
   137            },
   138            {
   139              "maxLength": 0
   140            }
   141          ]
   142        },
   143        "name": {
   144          "description": "Name of peer",
   145          "type": "string",
   146          "maxLength": 255
   147        },
   148        "description": {
   149          "description": "Description or bio of peer",
   150          "type": "string",
   151          "maxLength": 255
   152        },
   153        "homeUrl": {
   154          "description": "URL associated with this peer",
   155          "type": "string",
   156          "anyOf": [
   157            {
   158              "maxLength": 255,
   159              "format": "uri"
   160            },
   161            {
   162              "maxLength": 0
   163            }
   164          ]
   165        },
   166        "color": {
   167          "description": "Color scheme peer prefers viewing qri on webapp",
   168          "type": "string",
   169          "anyOf": [
   170            {
   171              "enum": [
   172                "default"
   173              ]
   174            },
   175            {
   176              "maxLength": 0
   177            }
   178          ]
   179        },
   180        "thumb": {
   181          "description": "Location of thumbnail of peer's profile picture, an ipfs hash",
   182          "type": "string"
   183        },
   184        "photo": {
   185          "description": "Location of peer's profile picture, an ipfs hash",
   186          "type": "string"
   187        },
   188        "poster": {
   189          "description": "Location of a peer's profile poster, an ipfs hash",
   190          "type": "string"
   191        },
   192        "twitter": {
   193          "description": "Twitter handle associated with peer",
   194          "type": "string",
   195          "maxLength": 15
   196        }
   197      },
   198      "required": [
   199        "id",
   200        "created",
   201        "updated",
   202        "type",
   203        "peername",
   204        "privkey"
   205      ]
   206    }`)
   207  	return validate(schema, &p)
   208  }
   209  
   210  // Copy makes a deep copy of the ProfilePod struct
   211  func (p *ProfilePod) Copy() *ProfilePod {
   212  	res := &ProfilePod{
   213  		ID:          p.ID,
   214  		KeyID:       p.KeyID,
   215  		PrivKey:     p.PrivKey,
   216  		Peername:    p.Peername,
   217  		Created:     p.Created,
   218  		Updated:     p.Updated,
   219  		Type:        p.Type,
   220  		Email:       p.Email,
   221  		Name:        p.Name,
   222  		Description: p.Description,
   223  		HomeURL:     p.HomeURL,
   224  		Color:       p.Color,
   225  		Thumb:       p.Thumb,
   226  		Photo:       p.Photo,
   227  		Poster:      p.Poster,
   228  		Twitter:     p.Twitter,
   229  	}
   230  	if p.PeerIDs != nil {
   231  		res.PeerIDs = make([]string, len(p.PeerIDs))
   232  		copy(res.PeerIDs, p.PeerIDs)
   233  	}
   234  
   235  	return res
   236  }
   237  
   238  // SetField assigns to the name field of the Profile.
   239  // TODO: Replace this with a generic package.
   240  func (p *ProfilePod) SetField(field, value string) error {
   241  	if field == "id" {
   242  		p.ID = value
   243  	} else if field == "privkey" {
   244  		return fmt.Errorf("Cannot set profile.privkey, read-only")
   245  	} else if field == "peername" {
   246  		p.Peername = value
   247  	} else if field == "created" {
   248  		return fmt.Errorf("Cannot set profile.created, read-only")
   249  	} else if field == "updated" {
   250  		return fmt.Errorf("Cannot set profile.updated, read-only")
   251  	} else if field == "type" {
   252  		p.Type = value
   253  	} else if field == "email" {
   254  		p.Email = value
   255  	} else if field == "name" {
   256  		p.Name = value
   257  	} else if field == "description" {
   258  		p.Description = value
   259  	} else if field == "homeurl" {
   260  		p.HomeURL = value
   261  	} else if field == "color" {
   262  		p.Color = value
   263  	} else if field == "thumb" {
   264  		p.Thumb = value
   265  	} else if field == "photo" {
   266  		return fmt.Errorf("Not implemented: set profile.photo")
   267  	} else if field == "poster" {
   268  		p.Poster = value
   269  	} else if field == "twitter" {
   270  		p.Twitter = value
   271  	} else {
   272  		return fmt.Errorf("Unknown profile field: %s", value)
   273  	}
   274  	return nil
   275  }