diff options
author | Colin Reeder <colin@vpzom.click> | 2020-09-11 09:38:33 -0600 |
---|---|---|
committer | Colin Reeder <colin@vpzom.click> | 2020-09-11 09:43:31 -0600 |
commit | f443d25dbd6227e30b40bc8a75a926166b9f1c50 (patch) | |
tree | 04f76ca8e6eb2e768f0e01457b74dbdb1a311c99 | |
parent | 6637cc73a0aefdab49b63611f9192c2cd756e815 (diff) |
Introduce site admins, allow them to edit site description
-rw-r--r-- | migrations/20200911144521_admin/down.sql | 3 | ||||
-rw-r--r-- | migrations/20200911144521_admin/up.sql | 3 | ||||
-rw-r--r-- | openapi/openapi.json | 25 | ||||
-rw-r--r-- | res/lang/en.ftl | 1 | ||||
-rw-r--r-- | src/main.rs | 22 | ||||
-rw-r--r-- | src/routes/api/mod.rs | 59 |
6 files changed, 103 insertions, 10 deletions
diff --git a/migrations/20200911144521_admin/down.sql b/migrations/20200911144521_admin/down.sql new file mode 100644 index 0000000..1ae08bb --- /dev/null +++ b/migrations/20200911144521_admin/down.sql @@ -0,0 +1,3 @@ +BEGIN; + ALTER TABLE person DROP COLUMN is_site_admin; +COMMIT; diff --git a/migrations/20200911144521_admin/up.sql b/migrations/20200911144521_admin/up.sql new file mode 100644 index 0000000..49293b6 --- /dev/null +++ b/migrations/20200911144521_admin/up.sql @@ -0,0 +1,3 @@ +BEGIN; + ALTER TABLE person ADD COLUMN is_site_admin BOOLEAN NOT NULL DEFAULT (FALSE); +COMMIT; diff --git a/openapi/openapi.json b/openapi/openapi.json index fe7cd68..a5b4eae 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -700,6 +700,27 @@ } } } + }, + "patch": { + "summary": "Modify instance settings", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "description": {"type": "string"} + } + } + } + } + }, + "responses": { + "204": { + "description": "Successfully modified." + } + } } }, "/api/unstable/logins": { @@ -756,9 +777,11 @@ "properties": { "user": { "type": "object", - "required": ["id", "has_unread_notifications"], + "required": ["id", "name", "is_site_admin", "has_unread_notifications"], "properties": { "id": {"type": "integer"}, + "name": {"type": "string"}, + "is_site_admin": {"type": "boolean"}, "has_unread_notifications": {"type": "boolean"} } } diff --git a/res/lang/en.ftl b/res/lang/en.ftl index f1f462a..16fadd4 100644 --- a/res/lang/en.ftl +++ b/res/lang/en.ftl @@ -9,6 +9,7 @@ no_such_community = No such community no_such_local_user_by_name = No local user found by that name no_such_post = No such post no_such_user = No such user +not_admin = You are not a site admin password_incorrect = Incorrect password post_content_conflict = content_markdown and content_text are mutually exclusive post_href_invalid = Specified URL is not valid diff --git a/src/main.rs b/src/main.rs index 01ff110..c654fb5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -395,6 +395,22 @@ pub async fn res_to_error( } } +pub trait ReqParts { + fn headers(&self) -> &hyper::HeaderMap<hyper::header::HeaderValue>; +} + +impl<T> ReqParts for hyper::Request<T> { + fn headers(&self) -> &hyper::HeaderMap<hyper::header::HeaderValue> { + self.headers() + } +} + +impl ReqParts for http::request::Parts { + fn headers(&self) -> &hyper::HeaderMap<hyper::header::HeaderValue> { + &self.headers + } +} + lazy_static::lazy_static! { static ref LANG_MAP: HashMap<unic_langid::LanguageIdentifier, fluent::FluentResource> = { let mut result = HashMap::new(); @@ -433,7 +449,7 @@ impl Translator { } } -pub fn get_lang_for_req(req: &hyper::Request<hyper::Body>) -> Translator { +pub fn get_lang_for_req(req: &impl ReqParts) -> Translator { let default = unic_langid::langid!("en"); let languages = match req .headers() @@ -471,7 +487,7 @@ pub fn get_lang_for_req(req: &hyper::Request<hyper::Body>) -> Translator { } pub async fn authenticate( - req: &hyper::Request<hyper::Body>, + req: &impl ReqParts, db: &tokio_postgres::Client, ) -> Result<Option<UserLocalID>, Error> { use headers::Header; @@ -504,7 +520,7 @@ pub async fn authenticate( } pub async fn require_login( - req: &hyper::Request<hyper::Body>, + req: &impl ReqParts, db: &tokio_postgres::Client, ) -> Result<UserLocalID, Error> { authenticate(req, db).await?.ok_or_else(|| { diff --git a/src/routes/api/mod.rs b/src/routes/api/mod.rs index ca3e7e9..da21cab 100644 --- a/src/routes/api/mod.rs +++ b/src/routes/api/mod.rs @@ -162,7 +162,9 @@ pub fn route_api() -> crate::RouteNode<()> { .with_child("communities", communities::route_communities()) .with_child( "instance", - crate::RouteNode::new().with_handler_async("GET", route_unstable_instance_get), + crate::RouteNode::new() + .with_handler_async("GET", route_unstable_instance_get) + .with_handler_async("PATCH", route_unstable_instance_patch), ) .with_child( "misc", @@ -367,13 +369,15 @@ async fn route_unstable_logins_current_get( let user = crate::require_login(&req, &db).await?; - let has_notifications: bool = { - let row = db.query_one("SELECT EXISTS(SELECT 1 FROM notification WHERE to_user = $1 AND created_at > (SELECT last_checked_notifications FROM person WHERE id=$1))", &[&user]).await?; - row.get(0) - }; + let row = db.query_one("SELECT username, is_site_admin, EXISTS(SELECT 1 FROM notification WHERE to_user = person.id AND created_at > person.last_checked_notifications) FROM person WHERE id=$1", &[&user]).await?; + let username: &str = row.get(0); + let is_site_admin: bool = row.get(1); + let has_notifications: bool = row.get(2); let body = serde_json::to_vec( - &serde_json::json!({"user": {"id": user, "has_unread_notifications": has_notifications}}), + &serde_json::json!({ + "user": {"id": user, "name": username, "is_site_admin": is_site_admin, "has_unread_notifications": has_notifications} + }), )? .into(); @@ -465,6 +469,49 @@ async fn route_unstable_instance_get( .body(serde_json::to_vec(&body)?.into())?) } +async fn route_unstable_instance_patch( + _: (), + ctx: Arc<crate::RouteContext>, + req: hyper::Request<hyper::Body>, +) -> Result<hyper::Response<hyper::Body>, crate::Error> { + #[derive(Deserialize)] + struct InstanceEditBody<'a> { + description: Option<Cow<'a, str>>, + } + + let lang = crate::get_lang_for_req(&req); + + let (req_parts, body) = req.into_parts(); + + let body = hyper::body::to_bytes(body).await?; + let body: InstanceEditBody = serde_json::from_slice(&body)?; + + let db = ctx.db_pool.get().await?; + + let user = crate::require_login(&req_parts, &db).await?; + + let is_site_admin: bool = { + let row = db + .query_one("SELECT is_site_admin FROM person WHERE id=$1", &[&user]) + .await?; + row.get(0) + }; + + if is_site_admin { + if let Some(description) = body.description { + db.execute("UPDATE site SET description=$1", &[&description]) + .await?; + } + + Ok(crate::empty_response()) + } else { + Ok(crate::simple_response( + hyper::StatusCode::FORBIDDEN, + lang.tr("not_admin", None).into_owned(), + )) + } +} + async fn apply_comments_replies<'a, T>( comments: &mut Vec<(T, RespPostCommentInfo<'a>)>, include_your_for: Option<UserLocalID>, |