feat: worker plugin system — domain ports, pipeline executor, built-in plugins
- PluginExecutor + PluginRegistry ports in domain - ExecutePipelineCommand orchestrates job→pipeline→plugin steps - ProcessNextJobCommand polls + executes next queued job - InMemoryPluginRegistry, NoOp/MetadataExtractor/SidecarSync plugins - Worker main rewritten with poll loop, factories module for DI - Deleted template job/runner/jobs remnants
This commit is contained in:
68
crates/worker/src/plugins/metadata_extractor.rs
Normal file
68
crates/worker/src/plugins/metadata_extractor.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use async_trait::async_trait;
|
||||
use domain::{
|
||||
entities::{AssetMetadata, MetadataSource},
|
||||
errors::DomainError,
|
||||
ports::{AssetMetadataRepository, AssetRepository, FileStoragePort, PluginExecutor},
|
||||
value_objects::{MetadataValue, StructuredData, SystemId},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tracing::info;
|
||||
|
||||
pub struct MetadataExtractorPlugin {
|
||||
asset_repo: Arc<dyn AssetRepository>,
|
||||
file_storage: Arc<dyn FileStoragePort>,
|
||||
metadata_repo: Arc<dyn AssetMetadataRepository>,
|
||||
}
|
||||
|
||||
impl MetadataExtractorPlugin {
|
||||
pub fn new(
|
||||
asset_repo: Arc<dyn AssetRepository>,
|
||||
file_storage: Arc<dyn FileStoragePort>,
|
||||
metadata_repo: Arc<dyn AssetMetadataRepository>,
|
||||
) -> Self {
|
||||
Self {
|
||||
asset_repo,
|
||||
file_storage,
|
||||
metadata_repo,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl PluginExecutor for MetadataExtractorPlugin {
|
||||
fn plugin_name(&self) -> &str {
|
||||
"metadata_extractor"
|
||||
}
|
||||
|
||||
async fn execute(
|
||||
&self,
|
||||
asset_id: Option<SystemId>,
|
||||
_payload: &StructuredData,
|
||||
_config: &StructuredData,
|
||||
) -> Result<StructuredData, DomainError> {
|
||||
let asset_id = asset_id.ok_or_else(|| {
|
||||
DomainError::Validation("metadata_extractor requires asset_id".into())
|
||||
})?;
|
||||
|
||||
let asset = self
|
||||
.asset_repo
|
||||
.find_by_id(&asset_id)
|
||||
.await?
|
||||
.ok_or_else(|| DomainError::NotFound(format!("Asset {} not found", asset_id)))?;
|
||||
|
||||
let path = &asset.source_reference.relative_path;
|
||||
let data = self.file_storage.read_file(path).await?;
|
||||
let file_size = data.len() as i64;
|
||||
|
||||
let mut extracted = StructuredData::new();
|
||||
extracted.insert("file_size_bytes", MetadataValue::Integer(file_size));
|
||||
extracted.insert("mime_type", MetadataValue::String(asset.mime_type.clone()));
|
||||
|
||||
let metadata =
|
||||
AssetMetadata::new(asset_id, MetadataSource::ExifExtracted, extracted.clone());
|
||||
self.metadata_repo.save(&metadata).await?;
|
||||
|
||||
info!(asset_id = %asset_id, file_size, "extracted basic metadata");
|
||||
Ok(extracted)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user