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 }