feat: add multi-user support and role-based access control #31

This commit is contained in:
rustmailer
2025-12-26 14:27:04 +08:00
parent 1e2f526a07
commit 4af5176b65
181 changed files with 23745 additions and 3187 deletions
+25 -1
View File
@@ -16,9 +16,10 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use std::{fs, io, path::PathBuf};
use crate::modules::error::BichonResult;
use base64::engine::general_purpose::STANDARD;
use base64::{engine::general_purpose, Engine};
use rand::{rng, Rng};
@@ -310,3 +311,26 @@ pub fn get_total_size(path: &PathBuf) -> io::Result<u64> {
Ok(total_size)
}
const MAX_AVATAR_BYTES: usize = 128 * 1024;
pub fn decode_avatar_bytes(base64_str: &str) -> BichonResult<Vec<u8>> {
let bytes = STANDARD.decode(base64_str).map_err(|e| {
raise_error!(
format!("Invalid avatar base64 encoding: {}", e),
ErrorCode::InvalidParameter
)
})?;
if bytes.len() > MAX_AVATAR_BYTES {
return Err(raise_error!(
format!(
"Avatar image exceeds maximum size ({} KB).",
MAX_AVATAR_BYTES / 1024
),
ErrorCode::InvalidParameter
));
}
Ok(bytes)
}
+11 -12
View File
@@ -16,7 +16,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use dashmap::DashMap;
use governor::{
clock::{QuantaClock, QuantaInstant},
@@ -30,14 +29,14 @@ use std::{
time::Duration,
};
use crate::modules::token::RateLimit;
use crate::modules::users::acl::RateLimit;
pub static RATE_LIMITER_MANAGER: LazyLock<TokenRateLimiter> = LazyLock::new(TokenRateLimiter::new);
pub static RATE_LIMITER_MANAGER: LazyLock<UserRateLimiter> = LazyLock::new(UserRateLimiter::new);
pub struct TokenRateLimiter {
pub struct UserRateLimiter {
limiters: Arc<
DashMap<
String,
u64,
(
Arc<RateLimiter<NotKeyed, InMemoryState, QuantaClock, NoOpMiddleware>>,
RateLimit,
@@ -46,29 +45,29 @@ pub struct TokenRateLimiter {
>,
}
impl TokenRateLimiter {
impl UserRateLimiter {
pub fn new() -> Self {
TokenRateLimiter {
UserRateLimiter {
limiters: Arc::new(DashMap::new()),
}
}
pub async fn check(
&self,
token: &str,
user_id: u64,
limit: RateLimit,
) -> Result<(), NotUntil<QuantaInstant>> {
let limiter = self.get_or_update_limiter(token, limit).await;
let limiter = self.get_or_update_limiter(user_id, limit).await;
limiter.check()
}
async fn get_or_update_limiter(
&self,
token: &str,
user_id: u64,
limit: RateLimit,
) -> Arc<RateLimiter<NotKeyed, InMemoryState, QuantaClock, NoOpMiddleware>> {
self.limiters
.entry(token.to_string())
.entry(user_id)
.and_modify(|(existing_limiter, current_limit)| {
if current_limit.interval != limit.interval || current_limit.quota != limit.quota {
let quota = Quota::with_period(Duration::from_secs(limit.interval))
@@ -100,4 +99,4 @@ impl TokenRateLimiter {
.0
.clone()
}
}
}