BREAKING CHANGE(core): rebrand from smarts3 to smartstorage
- Package renamed from @push.rocks/smarts3 to @push.rocks/smartstorage - Class: Smarts3 → SmartStorage, Interface: ISmarts3Config → ISmartStorageConfig - Method: getS3Descriptor → getStorageDescriptor - Rust binary: rusts3 → ruststorage - Rust types: S3Error→StorageError, S3Action→StorageAction, S3Config→SmartStorageConfig, S3Server→StorageServer - On-disk file extension: ._S3_object → ._storage_object - Default credentials: S3RVER → STORAGE - All internal S3 branding removed; AWS S3 protocol compatibility fully maintained
This commit is contained in:
+33
-33
@@ -18,22 +18,22 @@ use tokio::sync::watch;
|
||||
use tokio_util::io::ReaderStream;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::action::{self, RequestContext, S3Action};
|
||||
use crate::action::{self, RequestContext, StorageAction};
|
||||
use crate::auth::{self, AuthenticatedIdentity};
|
||||
use crate::config::S3Config;
|
||||
use crate::config::SmartStorageConfig;
|
||||
use crate::policy::{self, PolicyDecision, PolicyStore};
|
||||
use crate::s3_error::S3Error;
|
||||
use crate::error::StorageError;
|
||||
use crate::storage::FileStore;
|
||||
use crate::xml_response;
|
||||
|
||||
pub struct S3Server {
|
||||
pub struct StorageServer {
|
||||
store: Arc<FileStore>,
|
||||
shutdown_tx: watch::Sender<bool>,
|
||||
server_handle: tokio::task::JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl S3Server {
|
||||
pub async fn start(config: S3Config) -> Result<Self> {
|
||||
impl StorageServer {
|
||||
pub async fn start(config: SmartStorageConfig) -> Result<Self> {
|
||||
let store = Arc::new(FileStore::new(config.storage.directory.clone().into()));
|
||||
|
||||
// Initialize or reset storage
|
||||
@@ -104,7 +104,7 @@ impl S3Server {
|
||||
});
|
||||
|
||||
if !config.server.silent {
|
||||
tracing::info!("S3 server listening on {}", addr);
|
||||
tracing::info!("Storage server listening on {}", addr);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
@@ -124,7 +124,7 @@ impl S3Server {
|
||||
}
|
||||
}
|
||||
|
||||
impl S3Config {
|
||||
impl SmartStorageConfig {
|
||||
fn address(&self) -> &str {
|
||||
&self.server.address
|
||||
}
|
||||
@@ -192,7 +192,7 @@ fn empty_response(status: StatusCode, request_id: &str) -> Response<BoxBody> {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn s3_error_response(err: &S3Error, request_id: &str) -> Response<BoxBody> {
|
||||
fn storage_error_response(err: &StorageError, request_id: &str) -> Response<BoxBody> {
|
||||
let xml = err.to_xml();
|
||||
Response::builder()
|
||||
.status(err.status)
|
||||
@@ -205,7 +205,7 @@ fn s3_error_response(err: &S3Error, request_id: &str) -> Response<BoxBody> {
|
||||
async fn handle_request(
|
||||
req: Request<Incoming>,
|
||||
store: Arc<FileStore>,
|
||||
config: S3Config,
|
||||
config: SmartStorageConfig,
|
||||
policy_store: Arc<PolicyStore>,
|
||||
) -> Result<Response<BoxBody>, std::convert::Infallible> {
|
||||
let request_id = Uuid::new_v4().to_string();
|
||||
@@ -219,7 +219,7 @@ async fn handle_request(
|
||||
return Ok(resp);
|
||||
}
|
||||
|
||||
// Step 1: Resolve S3 action from request
|
||||
// Step 1: Resolve storage action from request
|
||||
let request_ctx = action::resolve_action(&req);
|
||||
|
||||
// Step 2: Auth + policy pipeline
|
||||
@@ -238,7 +238,7 @@ async fn handle_request(
|
||||
Ok(id) => Some(id),
|
||||
Err(e) => {
|
||||
tracing::warn!("Auth failed: {}", e.message);
|
||||
return Ok(s3_error_response(&e, &request_id));
|
||||
return Ok(storage_error_response(&e, &request_id));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -248,7 +248,7 @@ async fn handle_request(
|
||||
|
||||
// Step 3: Authorization (policy evaluation)
|
||||
if let Err(e) = authorize_request(&request_ctx, identity.as_ref(), &policy_store).await {
|
||||
return Ok(s3_error_response(&e, &request_id));
|
||||
return Ok(storage_error_response(&e, &request_id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,12 +256,12 @@ async fn handle_request(
|
||||
let mut response = match route_request(req, store, &config, &request_id, &policy_store).await {
|
||||
Ok(resp) => resp,
|
||||
Err(err) => {
|
||||
if let Some(s3err) = err.downcast_ref::<S3Error>() {
|
||||
s3_error_response(s3err, &request_id)
|
||||
if let Some(s3err) = err.downcast_ref::<StorageError>() {
|
||||
storage_error_response(s3err, &request_id)
|
||||
} else {
|
||||
tracing::error!("Internal error: {}", err);
|
||||
let s3err = S3Error::internal_error(&err.to_string());
|
||||
s3_error_response(&s3err, &request_id)
|
||||
let s3err = StorageError::internal_error(&err.to_string());
|
||||
storage_error_response(&s3err, &request_id)
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -288,11 +288,11 @@ async fn authorize_request(
|
||||
ctx: &RequestContext,
|
||||
identity: Option<&AuthenticatedIdentity>,
|
||||
policy_store: &PolicyStore,
|
||||
) -> Result<(), S3Error> {
|
||||
) -> Result<(), StorageError> {
|
||||
// ListAllMyBuckets requires authentication (no bucket to apply policy to)
|
||||
if ctx.action == S3Action::ListAllMyBuckets {
|
||||
if ctx.action == StorageAction::ListAllMyBuckets {
|
||||
if identity.is_none() {
|
||||
return Err(S3Error::access_denied());
|
||||
return Err(StorageError::access_denied());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
@@ -302,7 +302,7 @@ async fn authorize_request(
|
||||
if let Some(bucket_policy) = policy_store.get_policy(bucket).await {
|
||||
let decision = policy::evaluate_policy(&bucket_policy, ctx, identity);
|
||||
match decision {
|
||||
PolicyDecision::Deny => return Err(S3Error::access_denied()),
|
||||
PolicyDecision::Deny => return Err(StorageError::access_denied()),
|
||||
PolicyDecision::Allow => return Ok(()),
|
||||
PolicyDecision::NoOpinion => {
|
||||
// Fall through to default behavior
|
||||
@@ -313,7 +313,7 @@ async fn authorize_request(
|
||||
|
||||
// Default: authenticated users get full access, anonymous denied
|
||||
if identity.is_none() {
|
||||
return Err(S3Error::access_denied());
|
||||
return Err(StorageError::access_denied());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -326,7 +326,7 @@ async fn authorize_request(
|
||||
async fn route_request(
|
||||
req: Request<Incoming>,
|
||||
store: Arc<FileStore>,
|
||||
_config: &S3Config,
|
||||
_config: &SmartStorageConfig,
|
||||
request_id: &str,
|
||||
policy_store: &Arc<PolicyStore>,
|
||||
) -> Result<Response<BoxBody>> {
|
||||
@@ -414,8 +414,8 @@ async fn route_request(
|
||||
let upload_id = query.get("uploadId").unwrap().clone();
|
||||
handle_complete_multipart(req, store, &bucket, &key, &upload_id, request_id).await
|
||||
} else {
|
||||
let err = S3Error::invalid_request("Invalid POST request");
|
||||
Ok(s3_error_response(&err, request_id))
|
||||
let err = StorageError::invalid_request("Invalid POST request");
|
||||
Ok(storage_error_response(&err, request_id))
|
||||
}
|
||||
}
|
||||
_ => Ok(empty_response(StatusCode::METHOD_NOT_ALLOWED, request_id)),
|
||||
@@ -467,7 +467,7 @@ async fn handle_head_bucket(
|
||||
if store.bucket_exists(bucket).await {
|
||||
Ok(empty_response(StatusCode::OK, request_id))
|
||||
} else {
|
||||
Err(S3Error::no_such_bucket().into())
|
||||
Err(StorageError::no_such_bucket().into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,7 +682,7 @@ async fn handle_get_bucket_policy(
|
||||
.unwrap();
|
||||
Ok(resp)
|
||||
}
|
||||
None => Err(S3Error::no_such_bucket_policy().into()),
|
||||
None => Err(StorageError::no_such_bucket_policy().into()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,7 +695,7 @@ async fn handle_put_bucket_policy(
|
||||
) -> Result<Response<BoxBody>> {
|
||||
// Verify bucket exists
|
||||
if !store.bucket_exists(bucket).await {
|
||||
return Err(S3Error::no_such_bucket().into());
|
||||
return Err(StorageError::no_such_bucket().into());
|
||||
}
|
||||
|
||||
// Read body
|
||||
@@ -709,7 +709,7 @@ async fn handle_put_bucket_policy(
|
||||
policy_store
|
||||
.put_policy(bucket, validated_policy)
|
||||
.await
|
||||
.map_err(|e| S3Error::internal_error(&e.to_string()))?;
|
||||
.map_err(|e| StorageError::internal_error(&e.to_string()))?;
|
||||
|
||||
Ok(empty_response(StatusCode::NO_CONTENT, request_id))
|
||||
}
|
||||
@@ -722,7 +722,7 @@ async fn handle_delete_bucket_policy(
|
||||
policy_store
|
||||
.delete_policy(bucket)
|
||||
.await
|
||||
.map_err(|e| S3Error::internal_error(&e.to_string()))?;
|
||||
.map_err(|e| StorageError::internal_error(&e.to_string()))?;
|
||||
Ok(empty_response(StatusCode::NO_CONTENT, request_id))
|
||||
}
|
||||
|
||||
@@ -756,7 +756,7 @@ async fn handle_upload_part(
|
||||
.unwrap_or(0);
|
||||
|
||||
if part_number < 1 || part_number > 10000 {
|
||||
return Err(S3Error::invalid_part_number().into());
|
||||
return Err(StorageError::invalid_part_number().into());
|
||||
}
|
||||
|
||||
let body = req.into_body();
|
||||
@@ -925,7 +925,7 @@ fn extract_xml_value<'a>(xml: &'a str, tag: &str) -> Option<String> {
|
||||
// CORS
|
||||
// ============================
|
||||
|
||||
fn build_cors_preflight(config: &S3Config, request_id: &str) -> Response<BoxBody> {
|
||||
fn build_cors_preflight(config: &SmartStorageConfig, request_id: &str) -> Response<BoxBody> {
|
||||
let mut builder = Response::builder()
|
||||
.status(StatusCode::NO_CONTENT)
|
||||
.header("x-amz-request-id", request_id);
|
||||
@@ -949,7 +949,7 @@ fn build_cors_preflight(config: &S3Config, request_id: &str) -> Response<BoxBody
|
||||
builder.body(empty_body()).unwrap()
|
||||
}
|
||||
|
||||
fn add_cors_headers(headers: &mut hyper::HeaderMap, config: &S3Config) {
|
||||
fn add_cors_headers(headers: &mut hyper::HeaderMap, config: &SmartStorageConfig) {
|
||||
if let Some(ref origins) = config.cors.allowed_origins {
|
||||
headers.insert(
|
||||
"access-control-allow-origin",
|
||||
|
||||
Reference in New Issue
Block a user