Skip to content

Commit

Permalink
add support for avatar url
Browse files Browse the repository at this point in the history
  • Loading branch information
Kirill-K-1 committed Jan 29, 2024
1 parent 50a1e14 commit 21c07cc
Show file tree
Hide file tree
Showing 12 changed files with 64 additions and 4 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ axum = "0"
tower-http = { version = "0", features = ["cors"] }
reqwest = { version = "0", features = ["rustls-tls", "json", "deflate"] }
jsonwebtoken = "9"
url = { version = "2", features = ["serde"] }

# SerDe
serde = { version = "1", features = ["derive"] }
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Default configuration could be found in `./gov-portal-mocker/config/default.json
- `registration`: registration manager configuration
- `secret`: secret to generate registration tokens to be sent to user specified email for verification. Should be set before app start
- `lifetime`: lifetime duration in seconds for which the registration token will be valid to register user by using middleware. Defaults to 10 min
- `userProfileAttributes`: restrictions to user profile attributes for data validation purpose, eg. max user name or bio length
- `mongo`: MongoDB configuration
- `url`: mongo connection url in format `mongodb://host:port`. Should be set before app start
- `db`: database name with users collection. Defaults to `AirDAOGovPortal`
Expand Down
1 change: 1 addition & 0 deletions gov-portal-db/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ axum = { workspace = true }
tower-http = { workspace = true }
reqwest = { workspace = true }
jsonwebtoken = { workspace = true }
url = { workspace = true }

# SerDe
serde = { workspace = true }
Expand Down
3 changes: 2 additions & 1 deletion gov-portal-db/config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"emailMaxLength": 64,
"telegramMaxLength": 32,
"twitterMaxLength": 32,
"bioMaxLength": 250
"bioMaxLength": 250,
"avatarUrlMaxLength": 250
}
},
"mongo": {
Expand Down
3 changes: 3 additions & 0 deletions gov-portal-db/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ pub struct UpdateUserRequest {
pub twitter: Option<String>,
#[serde(default)]
pub bio: Option<String>,
#[serde(default)]
pub avatar: Option<url::Url>,
#[serde(flatten)]
pub session: SessionToken,
}
Expand Down Expand Up @@ -212,6 +214,7 @@ impl UpdateUserRequest {
telegram: self.telegram,
twitter: self.twitter,
bio: self.bio,
avatar: self.avatar,
}
}
}
19 changes: 18 additions & 1 deletion gov-portal-db/src/users_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub struct UserProfileAttributes {
telegram_max_length: usize,
twitter_max_length: usize,
bio_max_length: usize,
avatar_url_max_length: usize,
}

/// User profiles manager which provides read/write access to user profile data stored MongoDB
Expand Down Expand Up @@ -260,6 +261,21 @@ impl UsersManager {
)));
}

if user.avatar.as_ref().is_some_and(|value| {
value.as_str().len()
> self
.registration_config
.user_profile_attributes
.avatar_url_max_length
}) {
return Err(error::Error::InvalidInput(format!(
"Avatar URL too long (max: {})",
self.registration_config
.user_profile_attributes
.avatar_url_max_length
)));
}

Ok(())
}
}
Expand Down Expand Up @@ -292,7 +308,8 @@ mod tests {
"emailMaxLength": 64,
"telegramMaxLength": 32,
"twitterMaxLength": 32,
"bioMaxLength": 250
"bioMaxLength": 250,
"avatarUrlMaxLength": 250
}
}
"#,
Expand Down
1 change: 1 addition & 0 deletions gov-portal-mocker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ sha3 = { workspace = true }
axum = { workspace = true }
tower-http = { workspace = true }
reqwest = { workspace = true }
url = { workspace = true }

# SerDe
serde = { workspace = true }
Expand Down
25 changes: 24 additions & 1 deletion gov-portal-mocker/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ pub enum UpdateUserQuery {
telegram: Option<String>,
twitter: Option<String>,
bio: Option<String>,
avatar: Option<String>,
},
NoJwtToken {},
}
Expand Down Expand Up @@ -212,6 +213,13 @@ async fn index_route(
"{{USER_WALLET}}",
&utils::get_checksum_address(&user.wallet),
)
.replace(
"{{USER_AVATAR_URL}}",
user.avatar
.as_ref()
.map(|url| url.as_str())
.unwrap_or_default(),
)
.replace("{{USER_NAME}}", user.name.as_deref().unwrap_or_default())
.replace("{{USER_ROLE}}", user.role.as_deref().unwrap_or_default())
.replace(
Expand Down Expand Up @@ -249,8 +257,9 @@ async fn update_user_route(
telegram,
twitter,
bio,
avatar,
} => match state
.update_user(&token, name, role, telegram, twitter, bio)
.update_user(&token, name, role, telegram, twitter, bio, avatar)
.and_then(|_| state.get_user(&token))
.await
{
Expand Down Expand Up @@ -282,6 +291,13 @@ async fn update_user_route(
"{{USER_WALLET}}",
&utils::get_checksum_address(&user.wallet),
)
.replace(
"{{USER_AVATAR_URL}}",
user.avatar
.as_ref()
.map(|url| url.as_str())
.unwrap_or_default(),
)
.replace("{{USER_NAME}}", user.name.as_deref().unwrap_or_default())
.replace("{{USER_ROLE}}", user.role.as_deref().unwrap_or_default())
.replace(
Expand Down Expand Up @@ -523,6 +539,7 @@ impl AppState {
serde_json::from_str::<User>(&raw_data).map_err(|_| anyhow::Error::msg(raw_data))
}

#[allow(clippy::too_many_arguments)]
async fn update_user(
&self,
token: &str,
Expand All @@ -531,7 +548,12 @@ impl AppState {
telegram: Option<String>,
twitter: Option<String>,
bio: Option<String>,
avatar: Option<String>,
) -> Result<(), anyhow::Error> {
let avatar = match avatar {
Some(avatar_url) => Some(url::Url::parse(&avatar_url)?),
None => None,
};
let raw_data = self
.client
.post(&[&self.config.user_db.base_url, "/update-user"].concat())
Expand All @@ -542,6 +564,7 @@ impl AppState {
"telegram": telegram,
"twitter": twitter,
"bio": bio,
"avatar": avatar,
}))
.send()
.await?
Expand Down
7 changes: 6 additions & 1 deletion gov-portal-mocker/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@
const telegram = encodeURIComponent(document.getElementById("telegram").value);
const twitter = encodeURIComponent(document.getElementById("twitter").value);
const bio = encodeURIComponent(document.getElementById("bio").value);
window.location.replace(`/update-user?session=${session}&name=${name}&role=${role}&telegram=${telegram}&twitter=${twitter}&bio=${bio}`);
const avatar = encodeURIComponent(document.getElementById("avatar").value);
window.location.replace(`/update-user?session=${session}&name=${name}&role=${role}&telegram=${telegram}&twitter=${twitter}&bio=${bio}&avatar=${avatar}`);
}
}

Expand Down Expand Up @@ -83,6 +84,10 @@ <h1>User Profile</h1>
<label for="wallet">Wallet:</label>
<input type="text" id="wallet" name="wallet" value="{{USER_WALLET}}" maxlength="64" size="64" disabled="true" />
</p>
<p>
<label for="avatar">Avatar URL:</label>
<input type="text" id="avatar" name="avatar" value="{{USER_AVATAR_URL}}" maxlength="64" size="64" />
</p>
<p>
<label for="name">Name:</label>
<input type="text" id="name" name="name" value="{{USER_NAME}}" maxlength="64" size="64" />
Expand Down
1 change: 1 addition & 0 deletions shared/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ k256 = { workspace = true }

# Web
jsonwebtoken = { workspace = true }
url = { workspace = true }

# SerDe
serde = { workspace = true }
Expand Down
2 changes: 2 additions & 0 deletions shared/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ pub struct User {
pub twitter: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bio: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub avatar: Option<url::Url>,
}

impl TryFrom<RawUserRegistrationToken> for User {
Expand Down

0 comments on commit 21c07cc

Please sign in to comment.