mirror of
https://github.com/perstarkse/minne.git
synced 2026-03-28 12:21:56 +01:00
mvp file to ingress fn
This commit is contained in:
33
data/55340b32-e38e-48ed-b472-902c3144fe70/changes.md
Normal file
33
data/55340b32-e38e-48ed-b472-902c3144fe70/changes.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
Your proposed structure for the API sounds solid and modular, making it easier to manage files and their relationships with other data. Here’s a breakdown of how this can work and how it could be used with an iOS shortcut or a custom application:
|
||||||
|
|
||||||
|
API Structure
|
||||||
|
File Management Endpoints:
|
||||||
|
|
||||||
|
POST /file: Accepts a file upload and returns a unique identifier (ID) for that file.
|
||||||
|
PUT /file/{id}: Updates the metadata of the file identified by {id}.
|
||||||
|
DELETE /file/{id}: Deletes the file and its associated metadata from the database.
|
||||||
|
Data Ingress Endpoint:
|
||||||
|
|
||||||
|
POST /ingress: Accepts a JSON body containing references (IDs) to files and other necessary data, linking them in the database as needed.
|
||||||
|
Using with iOS Shortcuts
|
||||||
|
You can create shortcuts to interact with your API endpoints without the need for a full-fledged application. Here's how:
|
||||||
|
|
||||||
|
File Upload Shortcut:
|
||||||
|
|
||||||
|
Use the "Get File" action to select a file from the device.
|
||||||
|
Use the "Post" action to send a multipart/form-data request to the /file endpoint.
|
||||||
|
Parse the response to get the returned file ID.
|
||||||
|
Data Ingress Shortcut:
|
||||||
|
|
||||||
|
Use the "Ask for Input" action to gather the necessary fields for the ingress (like instructions, category, etc.) and the file ID(s).
|
||||||
|
Use another "Post" action to send this data to the /ingress endpoint as JSON.
|
||||||
|
Developing a CLI Tool
|
||||||
|
A CLI tool could also be developed for easier interaction with your API. This tool could:
|
||||||
|
|
||||||
|
Upload Files: Handle file uploads and return file IDs.
|
||||||
|
Link Data: Accept user input for instructions, category, and linked file IDs, then submit this data to the /ingress endpoint.
|
||||||
|
Additional Considerations
|
||||||
|
Error Handling: Ensure that both the upload and ingress endpoints handle errors gracefully and provide meaningful messages.
|
||||||
|
Documentation: Create clear API documentation to guide users in constructing requests correctly, whether they are using a shortcut, CLI, or custom application.
|
||||||
|
Authentication: Consider adding authentication to your API endpoints for security, especially if sensitive data is being handled.
|
||||||
|
This approach gives you the flexibility to support various clients, streamline interactions, and keep the server-side implementation clean and manageable. Would you like more specifics on any part of this setup?
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use uuid::Uuid;
|
||||||
|
use crate::redis::client::RedisClient;
|
||||||
|
|
||||||
use super::file_info::FileInfo;
|
use super::file_info::FileInfo;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
@@ -48,7 +53,8 @@ pub enum IngressContentError {
|
|||||||
impl IngressContent {
|
impl IngressContent {
|
||||||
/// Create a new `IngressContent` from `IngressInput`.
|
/// Create a new `IngressContent` from `IngressInput`.
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
input: IngressInput
|
input: IngressInput,
|
||||||
|
redis_client: &RedisClient, // Add RedisClient as a parameter
|
||||||
) -> Result<IngressContent, IngressContentError> {
|
) -> Result<IngressContent, IngressContentError> {
|
||||||
let content = if let Some(input_content) = input.content {
|
let content = if let Some(input_content) = input.content {
|
||||||
// Check if the content is a URL
|
// Check if the content is a URL
|
||||||
@@ -63,15 +69,26 @@ impl IngressContent {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if there is files
|
// Fetch file information if file UUIDs are provided
|
||||||
// Use FileInfo.get(id) with all the ids in files vec
|
let files = if let Some(file_uuids) = input.files {
|
||||||
// Return a vec<FileInfo> as file_info_list
|
let mut file_info_list = Vec::new();
|
||||||
|
for uuid_str in file_uuids {
|
||||||
|
let uuid = Uuid::parse_str(&uuid_str).map_err(|_| IngressContentError::UnsupportedMime("Invalid UUID".into()))?;
|
||||||
|
match FileInfo::get(uuid, redis_client).await {
|
||||||
|
Ok(file_info) => file_info_list.push(file_info),
|
||||||
|
Err(_) => info!("No file with that uuid"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(file_info_list)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
Ok(IngressContent {
|
Ok(IngressContent {
|
||||||
content,
|
content,
|
||||||
instructions: input.instructions,
|
instructions: input.instructions,
|
||||||
category: input.category,
|
category: input.category,
|
||||||
files: None,
|
files,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use crate::models::ingress::IngressContent;
|
|||||||
|
|
||||||
use super::{RabbitMQCommon, RabbitMQConfig, RabbitMQError};
|
use super::{RabbitMQCommon, RabbitMQConfig, RabbitMQError};
|
||||||
use tracing::{info, error};
|
use tracing::{info, error};
|
||||||
use tokio::fs;
|
|
||||||
|
|
||||||
/// Struct to consume messages from RabbitMQ.
|
/// Struct to consume messages from RabbitMQ.
|
||||||
pub struct RabbitMQConsumer {
|
pub struct RabbitMQConsumer {
|
||||||
@@ -16,6 +15,16 @@ pub struct RabbitMQConsumer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RabbitMQConsumer {
|
impl RabbitMQConsumer {
|
||||||
|
//// Creates a new 'RabbitMQConsumer' instance which sets up a rabbitmq client,
|
||||||
|
//// declares a exchange if needed, declares and binds a queue and initializes the consumer
|
||||||
|
////
|
||||||
|
//// # Arguments
|
||||||
|
////
|
||||||
|
//// * 'config' - RabbitMQConfig
|
||||||
|
////
|
||||||
|
//// # Returns
|
||||||
|
////
|
||||||
|
//// * 'Result<Self, RabbitMQError>' - The created client or an error.
|
||||||
pub async fn new(config: &RabbitMQConfig) -> Result<Self, RabbitMQError> {
|
pub async fn new(config: &RabbitMQConfig) -> Result<Self, RabbitMQError> {
|
||||||
let common = RabbitMQCommon::new(config).await?;
|
let common = RabbitMQCommon::new(config).await?;
|
||||||
|
|
||||||
@@ -99,7 +108,7 @@ impl RabbitMQConsumer {
|
|||||||
loop {
|
loop {
|
||||||
match self.consume().await {
|
match self.consume().await {
|
||||||
Ok((ingress, delivery)) => {
|
Ok((ingress, delivery)) => {
|
||||||
info!("Received ingress object: {:?}", ingress);
|
// info!("Received ingress object: {:?}", ingress);
|
||||||
|
|
||||||
// Process the ingress object
|
// Process the ingress object
|
||||||
self.handle_ingress_content(&ingress).await;
|
self.handle_ingress_content(&ingress).await;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// === Contents of ./routes/file.rs ===
|
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::Path,
|
extract::Path,
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
@@ -9,13 +7,12 @@ use axum::{
|
|||||||
use axum_typed_multipart::{TypedMultipart, FieldData, TryFromMultipart};
|
use axum_typed_multipart::{TypedMultipart, FieldData, TryFromMultipart};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
use tracing::{error, info};
|
use tracing::info;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
models::file_info::{FileError, FileInfo},
|
models::file_info::{FileError, FileInfo},
|
||||||
redis::client::RedisClient,
|
redis::client::RedisClient,
|
||||||
rabbitmq::publisher::RabbitMQProducer,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, TryFromMultipart)]
|
#[derive(Debug, TryFromMultipart)]
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||||||
use axum::{http::StatusCode, response::IntoResponse, Extension, Json};
|
use axum::{http::StatusCode, response::IntoResponse, Extension, Json};
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|
||||||
use crate::{models::ingress::{IngressContent, IngressInput}, rabbitmq::publisher::RabbitMQProducer};
|
use crate::{models::ingress::{IngressContent, IngressInput}, rabbitmq::publisher::RabbitMQProducer, redis::client::RedisClient};
|
||||||
|
|
||||||
pub async fn ingress_handler(
|
pub async fn ingress_handler(
|
||||||
Extension(producer): Extension<Arc<RabbitMQProducer>>,
|
Extension(producer): Extension<Arc<RabbitMQProducer>>,
|
||||||
@@ -11,7 +11,8 @@ pub async fn ingress_handler(
|
|||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
info!("Recieved input: {:?}", input);
|
info!("Recieved input: {:?}", input);
|
||||||
|
|
||||||
if let Ok(content) = IngressContent::new(input).await {
|
let redis_client = RedisClient::new("redis://127.0.0.1/");
|
||||||
|
if let Ok(content) = IngressContent::new(input, &redis_client).await {
|
||||||
|
|
||||||
// Publish content to RabbitMQ (or other system)
|
// Publish content to RabbitMQ (or other system)
|
||||||
match producer.publish(&content).await {
|
match producer.publish(&content).await {
|
||||||
|
|||||||
Reference in New Issue
Block a user