Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion protobuf/generated/rust/qaul.net.router_net_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,12 @@ pub struct UserIdTable {
/// User information table
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct UserInfoTable {
/// user info
/// legacy user info (unsigned)
#[prost(message, repeated, tag = "1")]
pub info: ::prost::alloc::vec::Vec<UserInfo>,
/// signed user profiles (preferred when available)
#[prost(message, repeated, tag = "2")]
pub signed_profiles: ::prost::alloc::vec::Vec<SignedUserProfile>,
}
/// User info structure for sending to the neighbours
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
Expand All @@ -90,6 +93,41 @@ pub struct UserInfo {
#[prost(string, tag = "3")]
pub name: ::prost::alloc::string::String,
}
/// Extended user profile, signed by the user's own key
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct UserProfile {
/// user id (38 byte PeerId)
#[prost(bytes = "vec", tag = "1")]
pub id: ::prost::alloc::vec::Vec<u8>,
/// protobuf-encoded Ed25519 public key
#[prost(bytes = "vec", tag = "2")]
pub key: ::prost::alloc::vec::Vec<u8>,
/// display name
#[prost(string, tag = "3")]
pub name: ::prost::alloc::string::String,
/// small avatar image (max ~32KB)
#[prost(bytes = "vec", tag = "4")]
pub avatar: ::prost::alloc::vec::Vec<u8>,
/// bio / status text
#[prost(string, tag = "5")]
pub bio: ::prost::alloc::string::String,
/// monotonically increasing version counter
#[prost(uint64, tag = "6")]
pub version: u64,
/// timestamp in milliseconds since epoch
#[prost(uint64, tag = "7")]
pub updated_at: u64,
}
/// Self-signed user profile wrapper
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct SignedUserProfile {
/// protobuf-encoded UserProfile bytes
#[prost(bytes = "vec", tag = "1")]
pub profile: ::prost::alloc::vec::Vec<u8>,
/// Ed25519 signature over profile bytes, signed by the user's own key
#[prost(bytes = "vec", tag = "2")]
pub signature: ::prost::alloc::vec::Vec<u8>,
}
/// List of feed ID's
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct FeedIdsTable {
Expand Down
32 changes: 31 additions & 1 deletion protobuf/generated/rust/qaul.rpc.user_accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/// user account rpc message container
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct UserAccounts {
#[prost(oneof = "user_accounts::Message", tags = "1, 2, 3, 4, 5, 6")]
#[prost(oneof = "user_accounts::Message", tags = "1, 2, 3, 4, 5, 6, 7, 8")]
pub message: ::core::option::Option<user_accounts::Message>,
}
/// Nested message and enum types in `UserAccounts`.
Expand All @@ -21,6 +21,10 @@ pub mod user_accounts {
SetPasswordRequest(super::SetPasswordRequest),
#[prost(message, tag = "6")]
SetPasswordResponse(super::SetPasswordResponse),
#[prost(message, tag = "7")]
UpdateProfileRequest(super::UpdateProfileRequest),
#[prost(message, tag = "8")]
UpdateProfileResponse(super::UpdateProfileResponse),
}
}
/// create a new user on this node
Expand Down Expand Up @@ -53,6 +57,32 @@ pub struct DefaultUserAccount {
#[prost(message, optional, tag = "2")]
pub my_user_account: ::core::option::Option<MyUserAccount>,
}
/// Request to update the local user's profile
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct UpdateProfileRequest {
/// new display name (empty = no change)
#[prost(string, tag = "1")]
pub name: ::prost::alloc::string::String,
/// new avatar image bytes (empty = no change)
#[prost(bytes = "vec", tag = "2")]
pub avatar: ::prost::alloc::vec::Vec<u8>,
/// new bio / status text (empty = no change)
#[prost(string, tag = "3")]
pub bio: ::prost::alloc::string::String,
}
/// Response after profile update
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct UpdateProfileResponse {
/// whether the update succeeded
#[prost(bool, tag = "1")]
pub success: bool,
/// error message if failed
#[prost(string, tag = "2")]
pub error_message: ::prost::alloc::string::String,
/// the new profile version after update
#[prost(uint64, tag = "3")]
pub new_version: u64,
}
/// Information about my user
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct MyUserAccount {
Expand Down
12 changes: 12 additions & 0 deletions protobuf/generated/rust/qaul.rpc.users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,18 @@ pub struct UserEntry {
/// RoutingTableConnection connections = 11;
#[prost(message, repeated, tag = "11")]
pub connections: ::prost::alloc::vec::Vec<RoutingTableConnection>,
/// bio / status text
#[prost(string, tag = "12")]
pub bio: ::prost::alloc::string::String,
/// avatar bytes (small image)
#[prost(bytes = "vec", tag = "13")]
pub avatar: ::prost::alloc::vec::Vec<u8>,
/// profile version number
#[prost(uint64, tag = "14")]
pub profile_version: u64,
/// profile last updated timestamp (ms since epoch)
#[prost(uint64, tag = "15")]
pub profile_updated_at: u64,
}
/// Routing table connection entry.
/// This message contains a connection to a specific user.
Expand Down
22 changes: 22 additions & 0 deletions protobuf/proto_definitions/node/user_accounts.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ message UserAccounts {
MyUserAccount my_user_account = 4;
SetPasswordRequest set_password_request = 5;
SetPasswordResponse set_password_response = 6;
UpdateProfileRequest update_profile_request = 7;
UpdateProfileResponse update_profile_response = 8;
}
}

Expand All @@ -37,6 +39,26 @@ message DefaultUserAccount {
MyUserAccount my_user_account = 2;
}

// Request to update the local user's profile
message UpdateProfileRequest {
// new display name (empty = no change)
string name = 1;
// new avatar image bytes (empty = no change)
bytes avatar = 2;
// new bio / status text (empty = no change)
string bio = 3;
}

// Response after profile update
message UpdateProfileResponse {
// whether the update succeeded
bool success = 1;
// error message if failed
string error_message = 2;
// the new profile version after update
uint64 new_version = 3;
}

// Information about my user
message MyUserAccount {
string name = 1;
Expand Down
30 changes: 29 additions & 1 deletion protobuf/proto_definitions/router/router_net_info.proto
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ message UserIdTable {

// User information table
message UserInfoTable {
// user info
// legacy user info (unsigned)
repeated UserInfo info = 1;
// signed user profiles (preferred when available)
repeated SignedUserProfile signed_profiles = 2;
}

// User info structure for sending to the neighbours
Expand All @@ -88,6 +90,32 @@ message UserInfo {
string name = 3;
}

// Extended user profile, signed by the user's own key
message UserProfile {
// user id (38 byte PeerId)
bytes id = 1;
// protobuf-encoded Ed25519 public key
bytes key = 2;
// display name
string name = 3;
// small avatar image (max ~32KB)
bytes avatar = 4;
// bio / status text
string bio = 5;
// monotonically increasing version counter
uint64 version = 6;
// timestamp in milliseconds since epoch
uint64 updated_at = 7;
}

// Self-signed user profile wrapper
message SignedUserProfile {
// protobuf-encoded UserProfile bytes
bytes profile = 1;
// Ed25519 signature over profile bytes, signed by the user's own key
bytes signature = 2;
}

// List of feed ID's
message FeedIdsTable {
// feed id
Expand Down
8 changes: 8 additions & 0 deletions protobuf/proto_definitions/router/users.proto
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ message UserEntry {
// routing connection entries
// RoutingTableConnection connections = 11;
repeated RoutingTableConnection connections = 11;
// bio / status text
string bio = 12;
// avatar bytes (small image)
bytes avatar = 13;
// profile version number
uint64 profile_version = 14;
// profile last updated timestamp (ms since epoch)
uint64 profile_updated_at = 15;
}

// Connection modules
Expand Down
4 changes: 4 additions & 0 deletions rust/clients/cli/src/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ impl Users {
verified,
blocked,
connections: vec![],
bio: String::new(),
avatar: Vec::new(),
profile_version: 0,
profile_updated_at: 0,
})),
};

Expand Down
4 changes: 4 additions & 0 deletions rust/clients/qauld-ctl/src/commands/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ fn send_user_update(
verified,
blocked,
connections: vec![],
bio: String::new(),
avatar: Vec::new(),
profile_version: 0,
profile_updated_at: 0,
})),
};

Expand Down
103 changes: 101 additions & 2 deletions rust/libqaul/src/node/user_accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,23 @@ impl UserAccounts {
}
Configuration::save();

// add it to users list
crate::router::users::Users::add(id, keys_ed25519.public(), name.clone(), false, false);
let mut initial_user = router::users::User {
id,
key: keys_ed25519.public(),
name: name.clone(),
verified: false,
blocked: false,
bio: String::new(),
avatar: Vec::new(),
version: 1,
updated_at: crate::utilities::timestamp::Timestamp::get_timestamp(),
signed_profile_bytes: Vec::new(),
signed_profile_signature: Vec::new(),
};
let signed = router::users::Users::create_signed_profile(&initial_user, &keys_ed25519);
initial_user.signed_profile_bytes = signed.profile;
initial_user.signed_profile_signature = signed.signature;
crate::router::users::Users::add(initial_user);

// add user to routing table / connections table
crate::router::connections::ConnectionTable::add_local_user(id);
Expand Down Expand Up @@ -296,6 +311,12 @@ impl UserAccounts {
name: user.name.clone(),
verified: false,
blocked: false,
bio: String::new(),
avatar: Vec::new(),
version: 0,
updated_at: 0,
signed_profile_bytes: Vec::new(),
signed_profile_signature: Vec::new(),
});
}

Expand Down Expand Up @@ -458,6 +479,63 @@ impl UserAccounts {
}
}
}
Some(proto::user_accounts::Message::UpdateProfileRequest(update_req)) => {
let user_peer_id = match PeerId::from_bytes(&user_id) {
Ok(id) => id,
Err(_) => {
Self::send_update_profile_response(false, "invalid user id".to_string(), 0, request_id);
return;
}
};

let account = match Self::get_by_id(user_peer_id) {
Some(a) => a,
None => {
Self::send_update_profile_response(false, "user account not found".to_string(), 0, request_id);
return;
}
};

let id_bytes = user_peer_id.to_bytes();
let q8id = id_bytes[6..14].to_vec();

let updated_user = match router::users::Users::get_user_snapshot(&q8id) {
Some(user) => {
let new_name = if update_req.name.is_empty() { user.name.clone() } else { update_req.name.clone() };
let new_bio = if update_req.bio.is_empty() { user.bio.clone() } else { update_req.bio.clone() };
let new_avatar = if update_req.avatar.is_empty() { user.avatar.clone() } else { update_req.avatar.clone() };
let new_version = user.version + 1;
let new_updated_at = crate::utilities::timestamp::Timestamp::get_timestamp();

router::users::User {
id: user.id,
key: user.key,
name: new_name,
verified: user.verified,
blocked: user.blocked,
bio: new_bio,
avatar: new_avatar,
version: new_version,
updated_at: new_updated_at,
signed_profile_bytes: Vec::new(),
signed_profile_signature: Vec::new(),
}
}
None => {
Self::send_update_profile_response(false, "user not found in users table".to_string(), 0, request_id);
return;
}
};

let signed = router::users::Users::create_signed_profile(&updated_user, &account.keys);
let new_version = updated_user.version;
let mut user_to_store = updated_user;
user_to_store.signed_profile_bytes = signed.profile;
user_to_store.signed_profile_signature = signed.signature;
router::users::Users::add(user_to_store);

Self::send_update_profile_response(true, String::new(), new_version, request_id);
}
_ => {}
}
}
Expand All @@ -467,6 +545,27 @@ impl UserAccounts {
}
}

/// send update profile response to client
fn send_update_profile_response(success: bool, error_message: String, new_version: u64, request_id: String) {
let proto_message = proto::UserAccounts {
message: Some(proto::user_accounts::Message::UpdateProfileResponse(
proto::UpdateProfileResponse {
success,
error_message,
new_version,
},
)),
};
let mut buf = Vec::with_capacity(proto_message.encoded_len());
proto_message.encode(&mut buf).expect("Vec<u8> provides capacity as needed");
Rpc::send_message(
buf,
crate::rpc::proto::Modules::Useraccounts.into(),
request_id,
Vec::new(),
);
}

/// send password operation response ot client
fn send_password_response(success: bool, message: String, request_id: String) {
let proto_message = proto::UserAccounts {
Expand Down
Loading
Loading