This commit is contained in:
Pascal Engélibert 2023-05-02 18:03:21 +02:00
commit 9fe5d3c70e
45 changed files with 5311 additions and 815 deletions

20
common/Cargo.toml Normal file
View file

@ -0,0 +1,20 @@
[package]
name = "webcomment-common"
version = "0.1.0"
authors = ["tuxmain <tuxmain@zettascript.org>"]
license = "AGPL-3.0-only"
repository = "https://git.txmn.tk/tuxmain/webcomment"
description = "Templatable comment web server"
edition = "2021"
[dependencies]
argon2 = "0.4.1"
base64 = "0.21.0"
log = "0.4.17"
percent-encoding = "2.2.0"
rand = "0.8.5"
rand_core = { version = "0.6.4", features = ["std"] }
serde = { version = "1.0.154", features = ["derive", "rc"] }
serde_json = "1.0.94"
sha2 = "0.10.6"
unic-langid = { version = "0.9.1", features = ["macros"] }

2
common/src/api.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod queries;
pub mod resps;

34
common/src/api/queries.rs Normal file
View file

@ -0,0 +1,34 @@
use serde::{Deserialize, Serialize};
/*#[derive(Deserialize)]
pub struct Admin<T> {
pub admin_psw: String,
#[serde(flatten)]
pub query: T,
}*/
#[derive(Deserialize, Serialize)]
pub struct CommentsByTopic {
pub mutation_token: Option<String>,
pub topic: String,
}
#[derive(Deserialize, Serialize)]
pub struct CommentsByTopicAdmin {
pub admin_psw: String,
pub topic: String,
}
#[derive(Deserialize, Serialize)]
pub struct NewComment {
pub author: String,
pub email: String,
pub text: String,
pub topic: String,
}
#[derive(Deserialize, Serialize)]
pub struct RemoveCommentAdmin {
pub admin_psw: String,
pub comment_id: String,
}

90
common/src/api/resps.rs Normal file
View file

@ -0,0 +1,90 @@
use crate::types::*;
use serde::{Deserialize, Serialize};
/*/// Use Ok only when there is no dedicated struct
/// (because serde doesn't allow flattening enums)
#[derive(Debug, Serialize)]
pub enum Result {
Ok,
#[serde(rename = "error")]
Err(Error),
}*/
pub type Result = std::result::Result<Response, Error>;
#[derive(Debug, Deserialize, Serialize)]
pub enum Error {
Antispam {
timeout: Time,
},
BadAdminAuth,
IllegalContent,
Internal,
InvalidRequest,
/// Admin only! Error messages may contain sensitive information.
Message(String),
}
#[derive(Debug, Deserialize, Serialize)]
pub enum Response {
CommentsByTopic(CommentsByTopic),
CommentsByTopicAdmin(CommentsByTopicAdmin),
NewComment(NewComment),
Ok,
}
/*#[derive(Serialize)]
pub struct GenericOk {}*/
#[derive(Debug, Deserialize, Serialize)]
pub struct CommentsByTopic {
pub approved_comments: Vec<ApprovedCommentWithMeta>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct CommentsByTopicAdmin {
pub approved_comments: Vec<ApprovedCommentWithMeta>,
pub pending_comments: Vec<PendingCommentWithMeta>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct OriginalComment {
pub author: String,
pub editable: bool,
pub last_edit_time: Option<Time>,
pub post_time: Time,
pub text: String,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ApprovedCommentWithMeta {
pub email: Option<String>,
pub author: String,
pub editable: bool,
pub id: String,
pub last_edit_time: Option<Time>,
pub post_time: Time,
pub status: CommentStatus,
pub text: String,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct PendingCommentWithMeta {
pub addr: Option<String>,
pub email: Option<String>,
pub author: String,
pub editable: bool,
pub id: String,
pub last_edit_time: Option<Time>,
pub post_time: Time,
pub status: CommentStatus,
pub text: String,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct NewComment {
pub id: String,
pub mutation_token: String,
pub post_time: Time,
}

2
common/src/lib.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod api;
pub mod types;

111
common/src/types.rs Normal file
View file

@ -0,0 +1,111 @@
use base64::Engine;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
pub type Time = u64;
pub const BASE64: base64::engine::general_purpose::GeneralPurpose =
base64::engine::general_purpose::GeneralPurpose::new(
&base64::alphabet::URL_SAFE,
base64::engine::general_purpose::NO_PAD,
);
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct MutationToken(pub [u8; 16]);
impl MutationToken {
pub fn new() -> Self {
Self(rand::random())
}
pub fn to_base64(&self) -> String {
BASE64.encode(self.0)
}
pub fn from_base64(s: &str) -> Result<Self, base64::DecodeError> {
std::panic::catch_unwind(|| {
let mut buf = [0; 16];
BASE64.decode_slice_unchecked(s.as_bytes(), &mut buf)?;
Ok(Self(buf))
})
.map_err(|_| base64::DecodeError::InvalidLength)?
}
}
impl AsRef<[u8]> for MutationToken {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct TopicHash(pub [u8; 32]);
impl TopicHash {
pub fn from_topic(topic: &str) -> Self {
let mut hasher = Sha256::new();
hasher.update(topic.as_bytes());
Self(hasher.finalize().into())
}
}
impl AsRef<[u8]> for TopicHash {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct CommentId(pub [u8; 16]);
impl CommentId {
pub fn new() -> Self {
Self(rand::random())
}
pub fn zero() -> Self {
Self([0; 16])
}
pub fn max() -> Self {
Self([255; 16])
}
pub fn to_base64(&self) -> String {
BASE64.encode(self.0)
}
pub fn from_base64(s: &str) -> Result<Self, base64::DecodeError> {
std::panic::catch_unwind(|| {
let mut buf = [0; 16];
BASE64.decode_slice_unchecked(s.as_bytes(), &mut buf)?;
Ok(Self(buf))
})
.map_err(|_| base64::DecodeError::InvalidLength)?
}
}
impl AsRef<[u8]> for CommentId {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[repr(u8)]
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum CommentStatus {
Pending = 0,
Approved = 1,
ApprovedEdited(Comment) = 2,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Comment {
pub author: String,
pub email: Option<String>,
pub last_edit_time: Option<u64>,
pub mutation_token: MutationToken,
pub post_time: u64,
pub text: String,
pub topic_hash: TopicHash,
}