feat: implement merge readiness plan to close gaps between v2 and v1
Some checks failed
lint / lint (push) Has been cancelled
test / unit (push) Has been cancelled
test / integration (push) Has been cancelled
lint / lint (pull_request) Failing after 5m8s
test / unit (pull_request) Successful in 16m18s
test / integration (pull_request) Failing after 16m59s

- Task 1: Fix feed response hydration by adding `to_thought_response` helper and updating feed handlers to return full `ThoughtResponse`.
- Task 2: Wire follower/following REST routes for user feeds.
- Task 3: Add user listing and count endpoints, including `GET /users` and `GET /users/count`.
- Task 4: Implement popular tags feature with `GET /tags/popular`.
- Task 5: Enhance configuration with HOST, CORS_ORIGINS, and optional rate limiting using tower-governor.
This commit is contained in:
2026-05-14 16:28:18 +02:00
parent e6f4a6256f
commit 004bfb427b
30 changed files with 8716 additions and 808 deletions

View File

@@ -1,12 +1,12 @@
use async_trait::async_trait;
use chrono::{Duration, Utc};
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
use domain::{
errors::DomainError,
ports::{AuthService, GeneratedToken, PasswordHasher},
value_objects::{PasswordHash, UserId},
};
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct Claims {
@@ -21,7 +21,10 @@ pub struct JwtAuthService {
impl JwtAuthService {
pub fn new(secret: String, ttl_seconds: i64) -> Self {
Self { secret, ttl_seconds }
Self {
secret,
ttl_seconds,
}
}
}
@@ -51,8 +54,8 @@ impl AuthService for JwtAuthService {
&Validation::default(),
)
.map_err(|_| DomainError::Unauthorized)?;
let uuid = uuid::Uuid::parse_str(&data.claims.sub)
.map_err(|_| DomainError::Unauthorized)?;
let uuid =
uuid::Uuid::parse_str(&data.claims.sub).map_err(|_| DomainError::Unauthorized)?;
Ok(UserId::from_uuid(uuid))
}
}
@@ -62,10 +65,7 @@ pub struct Argon2PasswordHasher;
#[async_trait]
impl PasswordHasher for Argon2PasswordHasher {
async fn hash(&self, plain: &str) -> Result<PasswordHash, DomainError> {
use argon2::{
password_hash::SaltString,
Argon2, PasswordHasher as _,
};
use argon2::{password_hash::SaltString, Argon2, PasswordHasher as _};
use rand::rngs::OsRng;
let salt = SaltString::generate(OsRng);
let hash = Argon2::default()
@@ -77,8 +77,7 @@ impl PasswordHasher for Argon2PasswordHasher {
async fn verify(&self, plain: &str, hash: &PasswordHash) -> Result<bool, DomainError> {
use argon2::{password_hash::PasswordHash as ArgonHash, Argon2, PasswordVerifier};
let parsed = ArgonHash::new(&hash.0)
.map_err(|e| DomainError::Internal(e.to_string()))?;
let parsed = ArgonHash::new(&hash.0).map_err(|e| DomainError::Internal(e.to_string()))?;
Ok(Argon2::default()
.verify_password(plain.as_bytes(), &parsed)
.is_ok())