Create API endpoint for managing client configurations
This commit is contained in:
parent
01494f1770
commit
fc82c83421
8 changed files with 139 additions and 2 deletions
|
@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Created API endpoint for adding aliases.
|
||||
- Populate `alsoKnownAs` property on actor object with declared aliases.
|
||||
- Support account migration from Mastodon.
|
||||
- Created API endpoint for managing client configurations.
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
|
@ -780,6 +780,29 @@ paths:
|
|||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Notification'
|
||||
/api/v1/settings/client_config:
|
||||
post:
|
||||
summary: Update client configuration.
|
||||
security:
|
||||
- tokenAuth: []
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
description: |
|
||||
Client configuration.
|
||||
Should contain a single key identifying type of client.
|
||||
type: object
|
||||
example: {"mitra-web":{"theme":"dark"}}
|
||||
responses:
|
||||
200:
|
||||
description: Successful operation.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CredentialAccount'
|
||||
400:
|
||||
description: Invalid request data.
|
||||
/api/v1/settings/change_password:
|
||||
post:
|
||||
summary: Set or change user's password.
|
||||
|
@ -1504,6 +1527,10 @@ components:
|
|||
role:
|
||||
description: The role assigned to the currently authorized user.
|
||||
$ref: '#/components/schemas/Role'
|
||||
client_config:
|
||||
description: Client configurations.
|
||||
type: object
|
||||
example: {"mitra-web":{"theme":"dark"}}
|
||||
ActivityParameters:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE user_account ADD COLUMN client_config JSONB NOT NULL DEFAULT '{}';
|
|
@ -58,6 +58,7 @@ CREATE TABLE user_account (
|
|||
private_key TEXT NOT NULL,
|
||||
invite_code VARCHAR(100) UNIQUE REFERENCES user_invite_code (code) ON DELETE SET NULL,
|
||||
user_role SMALLINT NOT NULL,
|
||||
client_config JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use serde_json::{Value as JsonValue};
|
||||
use uuid::Uuid;
|
||||
|
||||
use mitra_utils::{
|
||||
|
@ -17,6 +18,8 @@ use crate::profiles::{
|
|||
};
|
||||
|
||||
use super::types::{
|
||||
ClientConfig,
|
||||
DbClientConfig,
|
||||
DbInviteCode,
|
||||
DbUser,
|
||||
Role,
|
||||
|
@ -189,6 +192,26 @@ pub async fn set_user_role(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_client_config(
|
||||
db_client: &impl DatabaseClient,
|
||||
user_id: &Uuid,
|
||||
client_name: &str,
|
||||
client_config_value: &JsonValue,
|
||||
) -> Result<ClientConfig, DatabaseError> {
|
||||
let maybe_row = db_client.query_opt(
|
||||
"
|
||||
UPDATE user_account
|
||||
SET client_config = jsonb_set(client_config, ARRAY[$1], $2, true)
|
||||
WHERE id = $3
|
||||
RETURNING client_config
|
||||
",
|
||||
&[&client_name, &client_config_value, &user_id],
|
||||
).await?;
|
||||
let row = maybe_row.ok_or(DatabaseError::NotFound("user"))?;
|
||||
let client_config: DbClientConfig = row.try_get("client_config")?;
|
||||
Ok(client_config.into_inner())
|
||||
}
|
||||
|
||||
pub async fn get_user_by_id(
|
||||
db_client: &impl DatabaseClient,
|
||||
user_id: &Uuid,
|
||||
|
@ -308,6 +331,7 @@ pub async fn get_user_count(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
use serial_test::serial;
|
||||
use crate::database::test_utils::create_test_database;
|
||||
use crate::users::types::Role;
|
||||
|
@ -362,4 +386,25 @@ mod tests {
|
|||
let user = get_user_by_id(db_client, &user.id).await.unwrap();
|
||||
assert_eq!(user.role, Role::ReadOnlyUser);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_update_client_config() {
|
||||
let db_client = &mut create_test_database().await;
|
||||
let user_data = UserCreateData::default();
|
||||
let user = create_user(db_client, user_data).await.unwrap();
|
||||
assert_eq!(user.client_config.is_empty(), true);
|
||||
let client_name = "test";
|
||||
let client_config_value = json!({"a": 1});
|
||||
let client_config = update_client_config(
|
||||
db_client,
|
||||
&user.id,
|
||||
client_name,
|
||||
&client_config_value,
|
||||
).await.unwrap();
|
||||
assert_eq!(
|
||||
client_config.get(client_name).unwrap(),
|
||||
&client_config_value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use postgres_types::FromSql;
|
||||
use serde::Deserialize;
|
||||
use serde_json::{Value as JsonValue};
|
||||
use uuid::Uuid;
|
||||
|
||||
use mitra_utils::{
|
||||
|
@ -9,6 +13,7 @@ use mitra_utils::{
|
|||
|
||||
use crate::database::{
|
||||
int_enum::{int_enum_from_sql, int_enum_to_sql},
|
||||
json_macro::json_from_sql,
|
||||
DatabaseTypeError,
|
||||
};
|
||||
use crate::profiles::types::DbActorProfile;
|
||||
|
@ -100,6 +105,20 @@ impl TryFrom<i16> for Role {
|
|||
int_enum_from_sql!(Role);
|
||||
int_enum_to_sql!(Role);
|
||||
|
||||
pub type ClientConfig = HashMap<String, JsonValue>;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DbClientConfig(ClientConfig);
|
||||
|
||||
impl DbClientConfig {
|
||||
pub fn into_inner(self) -> ClientConfig {
|
||||
let Self(client_config) = self;
|
||||
client_config
|
||||
}
|
||||
}
|
||||
|
||||
json_from_sql!(DbClientConfig);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(FromSql)]
|
||||
#[postgres(name = "user_account")]
|
||||
|
@ -110,6 +129,7 @@ pub struct DbUser {
|
|||
private_key: String,
|
||||
invite_code: Option<String>,
|
||||
user_role: Role,
|
||||
client_config: DbClientConfig,
|
||||
created_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
|
@ -122,6 +142,7 @@ pub struct User {
|
|||
pub password_hash: Option<String>,
|
||||
pub private_key: String,
|
||||
pub role: Role,
|
||||
pub client_config: ClientConfig,
|
||||
pub profile: DbActorProfile,
|
||||
}
|
||||
|
||||
|
@ -137,6 +158,7 @@ impl User {
|
|||
password_hash: db_user.password_hash,
|
||||
private_key: db_user.private_key,
|
||||
role: db_user.user_role,
|
||||
client_config: db_user.client_config.into_inner(),
|
||||
profile: db_profile,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,9 @@ use mitra_models::{
|
|||
},
|
||||
subscriptions::types::Subscription,
|
||||
users::types::{
|
||||
Role,
|
||||
ClientConfig,
|
||||
Permission,
|
||||
Role,
|
||||
User,
|
||||
},
|
||||
};
|
||||
|
@ -126,6 +127,7 @@ pub struct Account {
|
|||
// CredentialAccount attributes
|
||||
pub source: Option<Source>,
|
||||
pub role: Option<ApiRole>,
|
||||
pub client_config: Option<ClientConfig>,
|
||||
}
|
||||
|
||||
impl Account {
|
||||
|
@ -220,6 +222,7 @@ impl Account {
|
|||
statuses_count: profile.post_count,
|
||||
source: None,
|
||||
role: None,
|
||||
client_config: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,6 +251,7 @@ impl Account {
|
|||
);
|
||||
account.source = Some(source);
|
||||
account.role = Some(role);
|
||||
account.client_config = Some(user.client_config);
|
||||
account
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,11 @@ use mitra_models::{
|
|||
update_profile,
|
||||
},
|
||||
profiles::types::ProfileUpdateData,
|
||||
users::queries::set_user_password,
|
||||
users::queries::{
|
||||
set_user_password,
|
||||
update_client_config,
|
||||
},
|
||||
users::types::ClientConfig,
|
||||
};
|
||||
use mitra_utils::passwords::hash_password;
|
||||
|
||||
|
@ -48,6 +52,37 @@ use super::types::{
|
|||
PasswordChangeRequest,
|
||||
};
|
||||
|
||||
// Similar to Pleroma settings store
|
||||
// https://docs-develop.pleroma.social/backend/development/API/differences_in_mastoapi_responses/#pleroma-settings-store
|
||||
#[post("/client_config")]
|
||||
async fn client_config_view(
|
||||
auth: BearerAuth,
|
||||
connection_info: ConnectionInfo,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<DbPool>,
|
||||
request_data: web::Json<ClientConfig>,
|
||||
) -> Result<HttpResponse, MastodonError> {
|
||||
let db_client = &**get_database_client(&db_pool).await?;
|
||||
let mut current_user = get_current_user(db_client, auth.token()).await?;
|
||||
if request_data.len() != 1 {
|
||||
return Err(ValidationError("can't update more than one config").into());
|
||||
};
|
||||
let (client_name, client_config_value) =
|
||||
request_data.iter().next().expect("hashmap entry should exist");
|
||||
current_user.client_config = update_client_config(
|
||||
db_client,
|
||||
¤t_user.id,
|
||||
client_name,
|
||||
client_config_value,
|
||||
).await?;
|
||||
let account = Account::from_user(
|
||||
&get_request_base_url(connection_info),
|
||||
&config.instance_url(),
|
||||
current_user,
|
||||
);
|
||||
Ok(HttpResponse::Ok().json(account))
|
||||
}
|
||||
|
||||
#[post("/change_password")]
|
||||
async fn change_password_view(
|
||||
auth: BearerAuth,
|
||||
|
@ -231,6 +266,7 @@ async fn move_followers(
|
|||
|
||||
pub fn settings_api_scope() -> Scope {
|
||||
web::scope("/api/v1/settings")
|
||||
.service(client_config_view)
|
||||
.service(change_password_view)
|
||||
.service(add_alias_view)
|
||||
.service(export_followers_view)
|
||||
|
|
Loading…
Reference in a new issue