feat: add image upload for avatar and banner
This commit is contained in:
@@ -14,10 +14,12 @@ postgres = { workspace = true }
|
||||
postgres-search = { workspace = true }
|
||||
postgres-federation = { workspace = true }
|
||||
activitypub = { workspace = true }
|
||||
k-ap = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-ap.git", tag = "v0.1.2" }
|
||||
k-ap = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-ap.git", tag = "v0.1.3" }
|
||||
nats = { workspace = true }
|
||||
event-transport = { workspace = true }
|
||||
auth = { workspace = true }
|
||||
storage = { workspace = true }
|
||||
application = { workspace = true }
|
||||
sqlx = { workspace = true }
|
||||
async-nats = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
|
||||
@@ -11,6 +11,18 @@ pub struct Config {
|
||||
pub host: String,
|
||||
pub cors_origins: String,
|
||||
pub rate_limit: Option<u32>,
|
||||
// Storage
|
||||
pub storage_backend: String,
|
||||
pub storage_path: Option<String>,
|
||||
pub storage_prefix: String,
|
||||
pub s3_endpoint: Option<String>,
|
||||
pub s3_access_key_id: Option<String>,
|
||||
pub s3_secret_access_key: Option<String>,
|
||||
pub s3_bucket: Option<String>,
|
||||
pub s3_region: Option<String>,
|
||||
// Upload limits
|
||||
pub upload_max_bytes: usize,
|
||||
pub upload_allowed_types: Vec<String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -36,6 +48,23 @@ impl Config {
|
||||
rate_limit: std::env::var("RATE_LIMIT")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok()),
|
||||
storage_backend: std::env::var("STORAGE_BACKEND").unwrap_or_else(|_| "local".into()),
|
||||
storage_path: std::env::var("STORAGE_PATH").ok(),
|
||||
storage_prefix: std::env::var("STORAGE_PREFIX").unwrap_or_default(),
|
||||
s3_endpoint: std::env::var("S3_ENDPOINT").ok(),
|
||||
s3_access_key_id: std::env::var("S3_ACCESS_KEY_ID").ok(),
|
||||
s3_secret_access_key: std::env::var("S3_SECRET_ACCESS_KEY").ok(),
|
||||
s3_bucket: std::env::var("S3_BUCKET").ok(),
|
||||
s3_region: std::env::var("S3_REGION").ok(),
|
||||
upload_max_bytes: std::env::var("UPLOAD_MAX_BYTES")
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(5 * 1024 * 1024),
|
||||
upload_allowed_types: std::env::var("UPLOAD_ALLOWED_TYPES")
|
||||
.unwrap_or_else(|_| "image/jpeg,image/png,image/gif,image/webp,image/avif".into())
|
||||
.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ use async_trait::async_trait;
|
||||
use sqlx::PgPool;
|
||||
use std::sync::Arc;
|
||||
|
||||
use application::use_cases::profile::UploadConfig;
|
||||
use storage::{build_store, ObjectStorageAdapter, StorageConfig};
|
||||
|
||||
use activitypub::{ApFederationAdapter, ThoughtsObjectHandler};
|
||||
use k_ap::ActivityPubService;
|
||||
use auth::ApiKeyServiceImpl;
|
||||
use domain::{
|
||||
errors::DomainError,
|
||||
@@ -14,6 +16,7 @@ use domain::{
|
||||
ports::{EventPublisher, OutboxWriter},
|
||||
};
|
||||
use event_transport::EventPublisherAdapter;
|
||||
use k_ap::ActivityPubService;
|
||||
use nats::NatsTransport;
|
||||
use postgres::activitypub::PgActivityPubRepository;
|
||||
use postgres::engagement::PgEngagementRepository;
|
||||
@@ -72,8 +75,7 @@ pub async fn build(cfg: &Config) -> Infrastructure {
|
||||
};
|
||||
|
||||
// 3. ActivityPub federation
|
||||
let connections_repo =
|
||||
Arc::new(PgRemoteActorConnectionRepository::new(pool.clone()));
|
||||
let connections_repo = Arc::new(PgRemoteActorConnectionRepository::new(pool.clone()));
|
||||
let raw_ap_service = Arc::new(
|
||||
ActivityPubService::builder(
|
||||
Arc::new(PostgresFederationRepository::new(pool.clone())),
|
||||
@@ -98,7 +100,27 @@ pub async fn build(cfg: &Config) -> Infrastructure {
|
||||
);
|
||||
let ap_service = Arc::new(ApFederationAdapter::new(raw_ap_service, connections_repo));
|
||||
|
||||
// 4. Application state
|
||||
// 4. Storage adapter
|
||||
let storage_cfg = StorageConfig {
|
||||
backend: cfg.storage_backend.clone(),
|
||||
local_path: cfg.storage_path.clone(),
|
||||
s3_endpoint: cfg.s3_endpoint.clone(),
|
||||
s3_access_key_id: cfg.s3_access_key_id.clone(),
|
||||
s3_secret_access_key: cfg.s3_secret_access_key.clone(),
|
||||
s3_bucket: cfg.s3_bucket.clone(),
|
||||
s3_region: cfg.s3_region.clone(),
|
||||
};
|
||||
let object_store = build_store(&storage_cfg).expect("Failed to build object store");
|
||||
let media_adapter: Arc<dyn domain::ports::MediaStore> = Arc::new(
|
||||
ObjectStorageAdapter::new(object_store, cfg.storage_prefix.clone())
|
||||
.expect("Failed to create storage adapter"),
|
||||
);
|
||||
let upload_config = UploadConfig {
|
||||
max_bytes: cfg.upload_max_bytes,
|
||||
allowed_content_types: cfg.upload_allowed_types.clone(),
|
||||
};
|
||||
|
||||
// 5. Application state
|
||||
let state = AppState {
|
||||
users: Arc::new(postgres::user::PgUserRepository::new(pool.clone())),
|
||||
thoughts: Arc::new(postgres::thought::PgThoughtRepository::new(pool.clone())),
|
||||
@@ -140,6 +162,9 @@ pub async fn build(cfg: &Config) -> Infrastructure {
|
||||
postgres::api_key::PgApiKeyRepository::new(pool.clone()),
|
||||
))),
|
||||
engagement: Arc::new(PgEngagementRepository::new(pool.clone())),
|
||||
media: media_adapter,
|
||||
upload_config,
|
||||
base_url: cfg.base_url.clone(),
|
||||
};
|
||||
|
||||
Infrastructure { state, ap_service }
|
||||
|
||||
Reference in New Issue
Block a user