Skip to content
47 changes: 47 additions & 0 deletions crates/evops-core/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# `evops-core`

## Overview

Acts as the application's source of truth, containing all core business logic and service integrations. This crate is reused by both `evops-rest` and `evops-grpc` to ensure consistent behavior across API boundaries. Orchestrates database operations (via `evops_db`), file storage (MinIO via `evops_storage`), and ML services (via `evops_ml_client`)

### Core Mechanism

1. **State Initialization**
During `AppState` construction the application is wiring together:
- Database connections
- Storage clients
- ML service integrations

2. **API Layer Integration**
- REST: All endpoints inject AppState via axum's State mechanism
- gRPC: Service handlers hold shared references to AppState

3. **Business Logic Execution**
Both REST controllers and gRPC services directly call `AppState` methods like `create_event()`

### Key Files

- `lib.rs`:
Initializes application state and dependencies
- Key components:
- Sets up API routes
- `AppState`: Shared application state (thread-safe via `Arc`)
- `State`: Internal struct holding:
- `evops_db::Database`
- `evops_storage::Storage`
- `evops_ml_client::MlClient`

- `services.rs`
Aggregates service modules (event, language, tag, user)

### Service Modules

- `event.rs`
Handles event operations and image management

- `user.rs`
Manages user accounts

- `language.rs`
Currently placeholder implementation
Future use: Manage multilingual content
126 changes: 126 additions & 0 deletions crates/evops-grpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# `evops-grpc`

## Overview

Provides gRPC API endpoints using `tonic` framework. Handles protocol buffer serialization, gRPC-web compatibility, and service implementations. Organized into:
- Protocol buffer definitions (`pb`)
- Service implementations (`services`)
- gRPC-specific utilities (`headers`)

### Service Example

Event Service (`services/event.rs`)
- Creating an event:
```rust
async fn create(
&self,
request: Request<EventServiceCreateRequest>,
) -> Result<Response<EventServiceCreateResponse>, Status> {
let request_data = request.into_inner();

let form = {
request_data
.form
.ok_or(ApiError::InvalidArgument({
"EventServiceCreateRequest.form must not be null.".to_owned()
}))?
.try_into()?
};
let event_id = self.state.create_event(form).await?;

let response_data = EventServiceCreateResponse {
event_id: event_id.to_string(),
};
Ok(Response::new(response_data))
}
```

- Streaming image download:
```rust
async fn push_image(
&self,
request: Request<Streaming<EventServicePushImageRequest>>,
) -> Result<Response<EventServicePushImageResponse>, Status> {
use crate::pb::event_service_push_image_request::Message;

let mut request_stream = request.into_inner();

let err_msg_not_null = "EventServicePushImageRequest.message must not be null.";

let event_id = {
let err_msg_1 = "The first message must contain metadata.";
let message = {
request_stream
.next()
.await
.ok_or(ApiError::InvalidArgument(err_msg_1.to_owned()))??
.message
.ok_or(ApiError::InvalidArgument(err_msg_not_null.to_owned()))?
};
let metadata = match message {
Message::Metadata(data) => data,
Message::Chunk(_) => {
return Err(ApiError::InvalidArgument(err_msg_1.to_owned()).into());
}
};

evops_models::EventId::new({
metadata
.event_id
.parse::<Uuid>()
.map_err(|e| ApiError::InvalidArgument(e.to_string()))?
})
};
let image = {
let mut buffer = BytesMut::new();
while let Some(message) = request_stream.message().await? {
let message = {
message
.message
.ok_or(ApiError::InvalidArgument(err_msg_not_null.to_owned()))?
};
let chunk = match message {
Message::Metadata(_) => {
let err_msg = "Expected chunk, found metadata. Only the first message needs to contain metadata.";
return Err(ApiError::InvalidArgument(err_msg.to_owned()).into());
}
Message::Chunk(c) => c,
};
buffer.put(&*chunk);
}
let buffer = buffer.freeze();

evops_models::EventImage::try_from(buffer)?
};

let image_id = self.state.push_event_image(event_id, image).await?;
let response_data = EventServicePushImageResponse {
image_id: image_id.to_string(),
};

Ok(Response::new(response_data))
}
```

### Key Files

- `lib.rs`: Main entry point
- Creates gRPC router with `axum`
- Registers all services
- Sets up gRPC reflection service for debugging
- Configures CORS with gRPC-specific headers

- `pb.rs`: PContains auto-generated Protobuf bindings
- Contains all gRPC service and message definitions
- Includes file descriptor set for reflection
- Manual validation implementations

- `pb/impls.rs`: Protobuf ↔ Domain types
- Handles validation and type conversions
- Key responsibilities
- String to UUID conversion
- Field validation
- Model to gRPC response transformations

- `services.rs`: Service Registry
Aggregates all service implementations
24 changes: 24 additions & 0 deletions crates/evops-ml-client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# `evops-ml-client`

## Overview

Provides gRPC client implementation for communicating with Machine Learning services. Handles connection management, request serialization, and response parsing.

### Key Files

- `lib.rs`: Core Client Setup
Main entry point that establishes gRPC connection to ML server.
Key Responsibilities:
- Creates `MlClient` struct wrapping gRPC channel
- Implements connection retry logic with 5-second intervals
- Initializes gRPC client stub

- `logic.rs`: Business Logic
Contains domain-specific operations and data transformations.
1. Converts `EventDescription` to protobuf format
2. Executes gRPC call to ML service

- `pb.rs`: Protobuf Bindings
Auto-generated file (via `tonic-build`) containing:
- gRPC client stubs (`MlServiceClient`)
- Protobuf message definitions
33 changes: 33 additions & 0 deletions crates/evops-rest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# `evops-rest`

## Overview

Provides REST API endpoints using `axum` crate. Automatically generates OpenAPI documentation using `aide` crate. Organized into logical service groups (events, tags, users, etc.).

### Key Files

- `lib.rs`: Main entry point
- Sets up API routes
- Configures OpenAPI generation

- `routes/`: Endpoint implementations
- Each file handles related operations
- Uses file-system routing (nested routes like `/tags/{tag-id}`)

- `docs.rs`: OpenAPI configuration
- Defines API tags (e.g., `"TagService"`, `"UserService"`)
- Configures OpenAPI 3 schema generation

- `types.rs`: Request/response data structures

- `error.rs`: Error handling
Standardizes API errors:
- `400` Bad Request
- `404` Not Found
- `409` Conflict
- `422` Validation Error
- `500` Server Error

### Adding New Endpoint

1.
20 changes: 20 additions & 0 deletions crates/evops-storage/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# `evops-storage`

## Overview

Provides object storage operations for event images using MinIO. Handles image uploads, streaming downloads, and deletions.

### Key Files

- `lib.rs`: Client Initialization
Sets up the storage client and ensures required buckets exist.
- Creates MinIO/S3 client with authentication
- Checks storage connection
- Auto-creates `event-images` bucket if missing

- `logic.rs`: Business Logic
Implements CRUD operations for event images. Uses WebP format for storage.
Key methods:
- `upload_event_image` - Encodes image as WebP and uploads to storage
- `stream_event_image` - Streams image bytes chunk-by-chunk
- `delete_event_image` - Removes image from storage
27 changes: 27 additions & 0 deletions crates/evops/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# `evops`

## Overview

The main executable crate serving as the backend entry point. Handles:
- Configuration loading
- Logging initialization
- Server startup (REST + gRPC)
- Graceful shutdown
- Dependency integration

### Key Files

- `config.rs`
Loads configuration from environment variables:
- Validates URL formats
- Uses `eyre` for error handling

- `main.rs`
Entry point with async runtime setup

### Dependencies

1. `evops-db`: Database operations (PostgreSQL)
2. `evops-storage`: File storage (MinIO)
3. `evops-ml-client`: Machine learning service integration
4. `evops-models`: Domain data structures