Add ability to set 404 image

Fix imagemagick blur arguments
This commit is contained in:
asonix 2023-07-05 19:06:48 -05:00
parent e7e4876908
commit 3487cb0e30
4 changed files with 85 additions and 13 deletions

View file

@ -430,6 +430,21 @@ A secure API key can be generated by any password generator.
"identifier": "/path/to/object"
}
```
- `POST /internal/set_not_found` Set the 404 image that is served from the original and process
endpoints. The image used must already be uploaded and have an alias. The request should look
like this:
```json
{
"alias": "asdf.png"
}
```
On success, the returned json should look like this:
```json
{
"msg": "ok"
}
```
Additionally, all endpoints support setting deadlines, after which the request will cease
processing. To enable deadlines for your requests, you can set the `X-Request-Deadline` header to an

View file

@ -1,6 +1,8 @@
[server]
address = '0.0.0.0:8080'
worker_id = 'pict-rs-1'
api_key = 'api-key'
[tracing.logging]
format = 'normal'
targets = 'warn,tracing_actix_web=info,actix_server=info,actix_web=info'

View file

@ -80,6 +80,8 @@ const MINUTES: u32 = 60;
const HOURS: u32 = 60 * MINUTES;
const DAYS: u32 = 24 * HOURS;
const NOT_FOUND_KEY: &str = "404-alias";
static DO_CONFIG: OnceCell<(Configuration, Operation)> = OnceCell::new();
static CONFIG: Lazy<Configuration> = Lazy::new(|| {
DO_CONFIG
@ -595,6 +597,21 @@ async fn process_details<R: FullRepo, S: Store>(
Ok(HttpResponse::Ok().json(&details))
}
async fn not_found_hash<R: FullRepo>(repo: &R) -> Result<Option<(Alias, R::Bytes)>, Error> {
let Some(not_found) = repo.get(NOT_FOUND_KEY).await? else {
return Ok(None);
};
let alias = String::from_utf8_lossy(not_found.as_ref())
.parse::<Alias>()
.expect("Infallible");
repo.hash(&alias)
.await
.map(|opt| opt.map(|hash| (alias, hash)))
.map_err(Error::from)
}
/// Process files
#[tracing::instrument(name = "Serving processed image", skip(repo, store))]
async fn process<R: FullRepo, S: Store + 'static>(
@ -607,10 +624,15 @@ async fn process<R: FullRepo, S: Store + 'static>(
let (format, alias, thumbnail_path, thumbnail_args) = prepare_process(query, ext.as_str())?;
let path_string = thumbnail_path.to_string_lossy().to_string();
let Some(hash) = repo.hash(&alias).await? else {
// Invalid alias
// TODO: placeholder 404 image
return Ok(HttpResponse::NotFound().finish());
let (hash, alias, not_found) = if let Some(hash) = repo.hash(&alias).await? {
(hash, alias, false)
} else {
let Some((alias, hash)) = not_found_hash(&repo).await? else {
return Ok(HttpResponse::NotFound().finish());
};
(hash, alias, true)
};
let identifier_opt = repo
@ -637,7 +659,7 @@ async fn process<R: FullRepo, S: Store + 'static>(
new_details
};
return ranged_file_resp(&store, identifier, range, details).await;
return ranged_file_resp(&store, identifier, range, details, not_found).await;
}
let original_details = ensure_details(&repo, &store, &alias).await?;
@ -674,6 +696,11 @@ async fn process<R: FullRepo, S: Store + 'static>(
} else {
return Err(UploadError::Range.into());
}
} else if not_found {
(
HttpResponse::NotFound(),
Either::right(once(ready(Ok(bytes)))),
)
} else {
(HttpResponse::Ok(), Either::right(once(ready(Ok(bytes)))))
};
@ -785,15 +812,21 @@ async fn serve<R: FullRepo, S: Store + 'static>(
) -> Result<HttpResponse, Error> {
let alias = alias.into_inner();
let Some(identifier) = repo.identifier_from_alias::<S::Identifier>(&alias).await? else {
// Invalid alias
// TODO: placeholder 404 image
return Ok(HttpResponse::NotFound().finish());
let (hash, alias, not_found) = if let Some(hash) = repo.hash(&alias).await? {
(hash, Serde::into_inner(alias), false)
} else {
let Some((alias, hash)) = not_found_hash(&repo).await? else {
return Ok(HttpResponse::NotFound().finish());
};
(hash, alias, true)
};
let identifier = repo.identifier(hash).await?;
let details = ensure_details(&repo, &store, &alias).await?;
ranged_file_resp(&store, identifier, range, details).await
ranged_file_resp(&store, identifier, range, details, not_found).await
}
#[tracing::instrument(name = "Serving file headers", skip(repo, store))]
@ -855,6 +888,7 @@ async fn ranged_file_resp<S: Store + 'static>(
identifier: S::Identifier,
range: Option<web::Header<Range>>,
details: Details,
not_found: bool,
) -> Result<HttpResponse, Error> {
let (builder, stream) = if let Some(web::Header(range_header)) = range {
//Range header exists - return as ranged
@ -887,7 +921,12 @@ async fn ranged_file_resp<S: Store + 'static>(
.to_stream(&identifier, None, None)
.await?
.map_err(Error::from);
(HttpResponse::Ok(), Either::right(stream))
if not_found {
(HttpResponse::NotFound(), Either::right(stream))
} else {
(HttpResponse::Ok(), Either::right(stream))
}
};
Ok(srv_response(
@ -952,6 +991,21 @@ struct AliasQuery {
alias: Serde<Alias>,
}
#[tracing::instrument(name = "Setting 404 Image", skip(repo))]
async fn set_not_found<R: FullRepo>(
json: web::Json<AliasQuery>,
repo: web::Data<R>,
) -> Result<HttpResponse, Error> {
let alias = json.into_inner().alias;
repo.set(NOT_FOUND_KEY, Vec::from(alias.to_string()).into())
.await?;
Ok(HttpResponse::Created().json(serde_json::json!({
"msg": "ok",
})))
}
#[tracing::instrument(name = "Purging file", skip(repo))]
async fn purge<R: FullRepo>(
query: web::Query<AliasQuery>,
@ -1110,7 +1164,8 @@ fn configure_endpoints<R: FullRepo + 'static, S: Store + 'static>(
.service(web::resource("/variants").route(web::delete().to(clean_variants::<R>)))
.service(web::resource("/purge").route(web::post().to(purge::<R>)))
.service(web::resource("/aliases").route(web::get().to(aliases::<R>)))
.service(web::resource("/identifier").route(web::get().to(identifier::<R, S>))),
.service(web::resource("/identifier").route(web::get().to(identifier::<R, S>)))
.service(web::resource("/set_not_found").route(web::post().to(set_not_found::<R>))),
);
}

View file

@ -295,7 +295,7 @@ impl Processor for Blur {
}
fn command(&self, mut args: Vec<String>) -> Vec<String> {
args.extend(["-gaussian-blur".to_string(), self.0.to_string()]);
args.extend(["-gaussian-blur".to_string(), format!("0x{}", self.0)]);
args
}