summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Reeder <colin@vpzom.click>2020-09-11 09:38:33 -0600
committerColin Reeder <colin@vpzom.click>2020-09-11 09:43:31 -0600
commitf443d25dbd6227e30b40bc8a75a926166b9f1c50 (patch)
tree04f76ca8e6eb2e768f0e01457b74dbdb1a311c99
parent6637cc73a0aefdab49b63611f9192c2cd756e815 (diff)
Introduce site admins, allow them to edit site description
-rw-r--r--migrations/20200911144521_admin/down.sql3
-rw-r--r--migrations/20200911144521_admin/up.sql3
-rw-r--r--openapi/openapi.json25
-rw-r--r--res/lang/en.ftl1
-rw-r--r--src/main.rs22
-rw-r--r--src/routes/api/mod.rs59
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>,