Commit ac335fd0 authored by Alessio Parma's avatar Alessio Parma

Merge branch 'master' of https://gitlab.com/pomma89/thumbnailer

parents 69f7af5b 2b4f92dd
......@@ -10,14 +10,14 @@ check-source-code-format:
script:
- dotnet format --dry-run --check --verbosity diagnostic
build-docker-image:
build-testing-docker-image:
stage: build
image: docker:latest
services:
- docker:dind
variables:
DOCKER_TAG: $CI_REGISTRY_IMAGE:testing
DOCKERFILE_LOCATION: src/PommaLabs.Thumbnailer/Dockerfile
DOCKERFILE_LOCATION: src/PommaLabs.Thumbnailer/Dockerfile.latest
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $DOCKER_TAG -f $DOCKERFILE_LOCATION .
......@@ -43,11 +43,13 @@ publish-on-docker-hub:
- docker:dind
variables:
DOCKER_TAG_PREFIX: $DOCKER_ORGANIZATION/$DOCKER_REPOSITORY
DOCKERFILE_LOCATION_PREFIX: src/PommaLabs.Thumbnailer/Dockerfile
script:
- docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
- docker pull $CI_REGISTRY_IMAGE:testing
- docker tag $CI_REGISTRY_IMAGE:testing $DOCKER_TAG_PREFIX:latest
- docker build -t $DOCKER_TAG_PREFIX:latest -f $DOCKERFILE_LOCATION_PREFIX.latest .
- docker push $DOCKER_TAG_PREFIX:latest
- docker build -t $DOCKER_TAG_PREFIX:azure-appsvc -f $DOCKERFILE_LOCATION_PREFIX.azure-appsvc .
- docker push $DOCKER_TAG_PREFIX:azure-appsvc
only:
- master
except:
......@@ -72,11 +74,12 @@ deploy-on-heroku:
services:
- docker:dind
variables:
DOCKER_TAG_PREFIX: $DOCKER_ORGANIZATION/$DOCKER_REPOSITORY
HEROKU_REGISTRY: registry.heroku.com
script:
- docker login -u _ -p $HEROKU_API_KEY $HEROKU_REGISTRY
- docker pull $DOCKER_ORGANIZATION/$DOCKER_REPOSITORY:latest
- docker tag $DOCKER_ORGANIZATION/$DOCKER_REPOSITORY:latest $HEROKU_REGISTRY/$HEROKU_APP_NAME/web
- docker pull $DOCKER_TAG_PREFIX:latest
- docker tag $DOCKER_TAG_PREFIX:latest $HEROKU_REGISTRY/$HEROKU_APP_NAME/web
- docker push $HEROKU_REGISTRY/$HEROKU_APP_NAME/web
- apk add --no-cache curl
- |-
......
......@@ -5,17 +5,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.0.0] - 2020-05-??
## [2.0.0] - 2020-06-01
### Added
- Added new `fill` parameter to thumbnail generation.
- Implemented a retry policy with exponential backoff on the client. Retry count is configurable.
- Client supports RapidAPI endpoints.
### Changed
- `sidePx` parameter has been replaced with `widthPx` and `heightPx`.
- Image optimization performs first a lossy step, then a lossless one.
### Removed
......
......@@ -5,8 +5,7 @@
<add key="defaultPushSource" value="https://www.myget.org/F/pomma89/api/v3/index.json" />
</config>
<packageSources>
<!-- Add these repositories to the list of available repositories -->
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
<add key="myget/pomma89" value="https://www.myget.org/F/pomma89/api/v3/index.json" />
<!-- Add this repository to the list of available repositories -->
<add key="myget.org/pomma89" value="https://www.myget.org/F/pomma89/api/v3/index.json" />
</packageSources>
</configuration>
\ No newline at end of file
......@@ -10,18 +10,21 @@
Web service which exposes endpoints to generate thumbnails and to optimize images.
Thumbnailer heavily relies on the following command line tools:
Thumbnailer heavily relies on many open source command line tools. Just to name a few:
- [ImageMagick][imagemagick-website], used to generate thumbnails.
- [imagemin-cli][github-imagemin-cli], used to optimize images.
- [GraphicsMagick][graphicsmagick-website], used to generate thumbnails.
- [wkhtmltoimage][wkhtmltopdf-website], used to convert HTML files to images.
- [LibreOffice][libreoffice-website], used to convert Office files to images.
- [pngquant][pngquant-website], used to optimize PNG images and thumbnails.
Thumbnailer is used in production by the following products and companies:
- [Replycare][replycare-website], a startup whose goal is to innovate the relationship between patient and doctor.
- [Sharize][sharize-website], a marketing resources management (MRM) platform developed by [Finsa S.p.A.][finsa-website], an Italian consultancy firm.
- [Sharize][sharize-website], a marketing resources management (MRM) platform developed by [Finsa S.p.A.][finsa-website],
an Italian consultancy firm.
Web service runs on ASP.NET Core and this repository contains a [.NET Standard client][nuget-package] which can be used to interact with it.
Web service runs on ASP.NET Core and this repository contains a [.NET Standard client][nuget-package]
which can be used to interact with it.
## Table of Contents
......@@ -45,13 +48,30 @@ docker run -it -e Security__AllowAnonymousAccess=true -p 8080:8080 pommalabs/thu
Local instance will be listening on port 8080 and will accept unauthenticated requests.
**TODO: Document web service settings.**
### Configuration
Docker image can be configured using the following environment variables:
| Environment variable | Notes | Default value |
| ------------------------------------------------ | ------------------------------------------------ | -------------- |
| `Database__CacheLifetime` | How long each cache entry will live. | 10 minutes |
| `Database__ConnectionString` | Connection string of a SQL database. | |
| `Database__Provider` | Provider (`None`, `PostgreSql`, `SqlServer`). | `None` |
| `Security__AcceptApiKeysViaHeaderParameter` | Accept API keys via header parameter. | `true` |
| `Security__AcceptApiKeysViaQueryStringParameter` | Accept API keys via query string parameter. | `false` |
| `Security__AllowAnonymousAccess` | Allow anonymous users to consume services. | `false` |
| `Security__ApiKeys__X__ExpiresAt` | If not specified, API key will not expire. | |
| `Security__ApiKeys__X__Name` | Descriptive name of API key at index X. | |
| `Security__ApiKeys__X__Value` | Value of API key at index X. | |
| `Security__CommandTimeout` | Timeout for low level commands. | 1 minute |
| `Security__MaxFileDownloadSizeInBytes` | How many bytes are allowed for remote downloads. | 64 MB |
| `Security__MaxFileUploadSizeInBytes` | How many bytes are allowed for uploads. | 32 MB |
| `Website__Enabled` | Expose the public website. | `false` |
| `Website__GoogleAnalyticsPropertyId` | Google Analytics property ID. | |
## Usage
```
TODO: Document a simple call.
```
Sample web service calls can be found on [Thumbnailer RapidAPI page][rapidapi-endpoints].
**Image optimization** supports following content types:
......@@ -65,16 +85,18 @@ TODO: Document a simple call.
Anyway, you can use the [test files][thumbnailer-samples] as a quick reference
to find out what the web service really supports.
Inside each "format" folder you will find a copy of the latest thumbnails, that is,
for each file you will find the most recent copy of its thumbnail.
Some notable formats which are **not** supported:
for each file you will find the most recent copy of its thumbnail.
However, not every content type for which a thumbnail could be "theoretically" generated is supported.
Some notable formats which are **not** supported are the following:
- PSD files represent a security issue since GraphicsMagick support is not complete.
As a matter of fact, [their support in GraphicsMagick has been disabled][graphicsmagick-psd-disabled].
## API
Please check OpenAPI docs on your instance (just visit the `/swagger` endpoint)
or head to the [demo instance Swagger endpoint](https://thumbnailer.pommalabs.xyz/swagger/index.html).
Please check OpenAPI docs on your instance (just visit the `/swagger` endpoint)
or head to the [demo instance Swagger endpoint](https://thumbnailer.pommalabs.xyz/swagger/index.html).
Each endpoint has been documented using the OpenAPI specification.
### RapidAPI
......@@ -100,22 +122,23 @@ MIT © 2019-2020 [Alessio Parma][personal-website]
[docker-repository]: https://hub.docker.com/repository/docker/pommalabs/thumbnailer
[docker-stars-badge]: https://img.shields.io/docker/stars/pommalabs/thumbnailer?style=flat-square
[finsa-website]: https://www.finsa.it/
[github-imagemin-cli]: https://github.com/imagemin/imagemin-cli
[github-standard-readme]: https://github.com/RichardLitt/standard-readme
[github-standard-readme-badge]: https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square
[gitlab-pipeline-status-badge]: https://gitlab.com/pommalabs/thumbnailer/badges/master/pipeline.svg?style=flat-square
[gitlab-pipelines]: https://gitlab.com/pommalabs/thumbnailer/pipelines
[gitlab-pomma89]: https://gitlab.com/pomma89
[graphicsmagick-psd-disabled]: http://www.graphicsmagick.org/NEWS.html#may-30-2016
[imagemagick-website]: https://imagemagick.org/
[graphicsmagick-website]: http://www.graphicsmagick.org/
[libreoffice-website]: https://www.libreoffice.org/
[nuget-downloads-badge]: https://img.shields.io/nuget/dt/PommaLabs.Thumbnailer.Client?style=flat-square
[nuget-package]: https://www.nuget.org/packages/PommaLabs.Thumbnailer.Client/
[nuget-version-badge]: https://img.shields.io/nuget/v/PommaLabs.Thumbnailer.Client?style=flat-square
[paypal-donations]: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ELJWKEYS9QGKA
[paypal-donations-badge]: https://img.shields.io/badge/Donate-PayPal-green.svg?style=flat-square
[personal-website]: https://pommalabs.xyz/
[pngquant-website]: https://pngquant.org/
[rapidapi-endpoints]: https://rapidapi.com/pommalabs-pommalabs-default/api/thumbnailer1/endpoints
[replycare-website]: https://www.replycare.com/
[sharize-website]: https://www.sharize.io/
[thumbnailer-samples]: https://gitlab.com/pommalabs/thumbnailer/-/tree/master/tests/PommaLabs.Thumbnailer.Tester/samples
[wkhtmltopdf-website]: https://wkhtmltopdf.org/
\ No newline at end of file
[wkhtmltopdf-website]: https://wkhtmltopdf.org/
......@@ -46,8 +46,8 @@ public sealed class ConcreteThumbnailerClient : IThumbnailerClient
{
private const string ApiKeyHeaderName = "X-Api-Key";
private const string OptimizationModeParamName = "mode";
private static readonly HashSet<int> s_retriableStatusCodes = new HashSet<int> { 424 };
private const string RapidApiKeyHeaderName = "X-RapidAPI-Key";
private static readonly HashSet<int> s_retriableStatusCodes = new HashSet<int> { 424, 429 };
private readonly IOptions<ThumbnailerClientConfiguration> _clientConfiguration;
private readonly AsyncRetryPolicy _retryPolicy;
......@@ -75,6 +75,8 @@ public sealed class ConcreteThumbnailerClient : IThumbnailerClient
.WaitAndRetryAsync(clientConfiguration.Value.RetryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
private bool IsRapidApi => _clientConfiguration.Value.BaseUri.Host.EndsWith(".rapidapi.com", StringComparison.OrdinalIgnoreCase);
/// <inheritdoc/>
public async Task<byte[]> GetThumbnailAsync(
Uri fileUri,
......@@ -84,10 +86,12 @@ public sealed class ConcreteThumbnailerClient : IThumbnailerClient
{
_validator.ValidateFileUri(fileUri);
var isRapidApi = IsRapidApi;
return await EvaluateResponseAsync(OperationType.ThumbnailGeneration, async () => await Url
.Combine(_clientConfiguration.Value.BaseUri.AbsoluteUri, "api/v1/thumbnail")
.Combine(_clientConfiguration.Value.BaseUri.AbsoluteUri, $"{(isRapidApi ? string.Empty : "api/")}v1/thumbnail")
.WithTimeout(_clientConfiguration.Value.Timeout)
.WithHeader(ApiKeyHeaderName, _clientConfiguration.Value.ApiKey)
.WithHeader(isRapidApi ? RapidApiKeyHeaderName : ApiKeyHeaderName, _clientConfiguration.Value.ApiKey)
.SetQueryParam(nameof(fileUri), fileUri.AbsoluteUri)
.SetQueryParam(nameof(widthPx), widthPx.ToString(CultureInfo.InvariantCulture))
.SetQueryParam(nameof(heightPx), heightPx.ToString(CultureInfo.InvariantCulture))
......@@ -110,11 +114,12 @@ public sealed class ConcreteThumbnailerClient : IThumbnailerClient
_validator.ValidateContentTypeForThumbnailGeneration(contentType, @throw: true);
fileName = AdjustFileName(contentType, fileName);
var isRapidApi = IsRapidApi;
return await EvaluateResponseAsync(OperationType.ThumbnailGeneration, async () => await Url
.Combine(_clientConfiguration.Value.BaseUri.AbsoluteUri, "api/v1/thumbnail")
.Combine(_clientConfiguration.Value.BaseUri.AbsoluteUri, $"{(isRapidApi ? string.Empty : "api/")}v1/thumbnail")
.WithTimeout(_clientConfiguration.Value.Timeout)
.WithHeader(ApiKeyHeaderName, _clientConfiguration.Value.ApiKey)
.WithHeader(isRapidApi ? RapidApiKeyHeaderName : ApiKeyHeaderName, _clientConfiguration.Value.ApiKey)
.SetQueryParam(nameof(widthPx), widthPx.ToString(CultureInfo.InvariantCulture))
.SetQueryParam(nameof(heightPx), heightPx.ToString(CultureInfo.InvariantCulture))
.SetQueryParam(nameof(shavePx), shavePx.ToString(CultureInfo.InvariantCulture))
......@@ -145,10 +150,12 @@ public Task<bool> IsThumbnailGenerationSupportedAsync(string contentType)
{
_validator.ValidateFileUri(fileUri);
var isRapidApi = IsRapidApi;
return await EvaluateResponseAsync(OperationType.ImageOptimization, async () => await Url
.Combine(_clientConfiguration.Value.BaseUri.AbsoluteUri, "api/v1/optimize")
.Combine(_clientConfiguration.Value.BaseUri.AbsoluteUri, $"{(isRapidApi ? string.Empty : "api/")}v1/optimize")
.WithTimeout(_clientConfiguration.Value.Timeout)
.WithHeader(ApiKeyHeaderName, _clientConfiguration.Value.ApiKey)
.WithHeader(isRapidApi ? RapidApiKeyHeaderName : ApiKeyHeaderName, _clientConfiguration.Value.ApiKey)
.SetQueryParam(nameof(fileUri), fileUri.AbsoluteUri)
.SetQueryParam(OptimizationModeParamName, mode.ToString())
.GetBytesAsync(cancellationToken)
......@@ -167,11 +174,12 @@ public Task<bool> IsThumbnailGenerationSupportedAsync(string contentType)
_validator.ValidateContentTypeForImageOptimization(contentType, @throw: true);
fileName = AdjustFileName(contentType, fileName);
var isRapidApi = IsRapidApi;
return await EvaluateResponseAsync(OperationType.ImageOptimization, async () => await Url
.Combine(_clientConfiguration.Value.BaseUri.AbsoluteUri, "api/v1/optimize")
.Combine(_clientConfiguration.Value.BaseUri.AbsoluteUri, $"{(isRapidApi ? string.Empty : "api/")}v1/optimize")
.WithTimeout(_clientConfiguration.Value.Timeout)
.WithHeader(ApiKeyHeaderName, _clientConfiguration.Value.ApiKey)
.WithHeader(isRapidApi ? RapidApiKeyHeaderName : ApiKeyHeaderName, _clientConfiguration.Value.ApiKey)
.SetQueryParam(OptimizationModeParamName, mode.ToString())
.PostMultipartAsync(mp => mp.AddFile("file", new MemoryStream(contents), fileName, contentType), cancellationToken)
.ReceiveBytes()
......
# https://gitlab.com/pommalabs/thumbnailer/-/tree/base-image
FROM pommalabs/thumbnailer:azure-appsvc-base AS base
HEALTHCHECK --interval=30s --timeout=5s \
CMD curl -f http://localhost:$PORT/health || exit 1
FROM pommalabs/dotnet:3-sdk AS build
COPY ["NuGet.config", ""]
COPY ["src/PommaLabs.Thumbnailer/PommaLabs.Thumbnailer.csproj", "src/PommaLabs.Thumbnailer/"]
COPY ["src/PommaLabs.Thumbnailer.Client/PommaLabs.Thumbnailer.Client.csproj", "src/PommaLabs.Thumbnailer.Client/"]
RUN dotnet restore "src/PommaLabs.Thumbnailer/PommaLabs.Thumbnailer.csproj"
COPY . .
WORKDIR /opt/sln/src/PommaLabs.Thumbnailer
RUN dotnet build "PommaLabs.Thumbnailer.csproj" -c Release -o /opt/app/build
FROM build AS publish
RUN dotnet publish "PommaLabs.Thumbnailer.csproj" -c Release -o /opt/app/publish
FROM base AS final
COPY --from=publish /opt/app/publish .
CMD ["azure_appsvc_init.sh", "PommaLabs.Thumbnailer.dll"]
# https://gitlab.com/pommalabs/thumbnailer/-/tree/base-image
FROM pommalabs/thumbnailer:base AS base
FROM pommalabs/thumbnailer:latest-base AS base
HEALTHCHECK --interval=30s --timeout=5s \
CMD curl -f http://localhost:$PORT/health || exit 1
FROM pommalabs/dotnet:3-sdk AS build
COPY ["NuGet.config", ""]
......
// File name: CacheConfiguration.cs
//
// Author(s): Alessio Parma <[email protected]>
//
// The MIT License (MIT)
//
// Copyright (c) 2019-2020 Alessio Parma <[email protected]>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
namespace PommaLabs.Thumbnailer.Models.Configurations
{
/// <summary>
/// Internal cache configuration.
/// </summary>
public sealed class CacheConfiguration
{
/// <summary>
/// How long each cache entry will live. Defaults to 10 minutes.
/// </summary>
public TimeSpan Lifetime { get; set; } = TimeSpan.FromMinutes(10);
}
}
......@@ -39,7 +39,12 @@ public sealed class DatabaseConfiguration
private string? _connectionString;
/// <summary>
/// Connection string.
/// How long each cache entry will live. Defaults to 10 minutes.
/// </summary>
public TimeSpan CacheLifetime { get; set; } = TimeSpan.FromMinutes(10);
/// <summary>
/// Connection string of a SQL database.
/// </summary>
public string ConnectionString
{
......
......@@ -49,7 +49,7 @@ public sealed class SecurityConfiguration
public bool AcceptApiKeysViaQueryStringParameter { get; set; } = false;
/// <summary>
/// Allows anonymous users to consume services. Defaults to false, because anonymous users
/// Allow anonymous users to consume services. Defaults to false, because anonymous users
/// can overload the web service, unless a rate limiting process is active.
/// </summary>
public bool AllowAnonymousAccess { get; set; } = false;
......
......@@ -169,7 +169,7 @@ public TempFileDTO(string contentType, string? extension)
/// cached and it is returned to the caller.
/// </summary>
/// <param name="cache">Cache.</param>
/// <param name="cacheConfiguration">Cache configuration.</param>
/// <param name="databaseConfiguration">Database configuration.</param>
/// <param name="file">
/// File which might be deleted if a file with the same hash exists in the cache.
/// </param>
......@@ -179,7 +179,7 @@ public TempFileDTO(string contentType, string? extension)
/// </returns>
/// <remarks>This method is used to avoid flooding the server with the same files.</remarks>
public static async Task<TempFileDTO> CheckCacheAsync(
ICache cache, IOptions<CacheConfiguration> cacheConfiguration,
ICache cache, IOptions<DatabaseConfiguration> databaseConfiguration,
TempFileDTO file, CancellationToken cancellationToken)
{
if (file._path == null || !file._path.IsFile)
......@@ -197,7 +197,7 @@ public TempFileDTO(string contentType, string? extension)
partition: nameof(TempFileDTO),
key: new CacheKey(hash),
valueGetter: () => Task.FromResult(file),
lifetime: cacheConfiguration.Value.Lifetime,
lifetime: databaseConfiguration.Value.CacheLifetime,
cancellationToken: cancellationToken,
continueOnCapturedContext: false).ConfigureAwait(false);
......
......@@ -22,10 +22,10 @@
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.3" PrivateAssets="All" />
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.76" PrivateAssets="All" />
<PackageReference Include="NWebsec.AspNetCore.Middleware" Version="3.0.0" />
<PackageReference Include="PommaLabs.KVLite.AspNetCore" Version="10.0.1" />
<PackageReference Include="PommaLabs.KVLite.Memory" Version="10.0.1" />
<PackageReference Include="PommaLabs.KVLite.PostgreSql" Version="10.0.1" />
<PackageReference Include="PommaLabs.KVLite.SqlServer" Version="10.0.1" />
<PackageReference Include="PommaLabs.KVLite.AspNetCore" Version="10.0.2" />
<PackageReference Include="PommaLabs.KVLite.Memory" Version="10.0.2" />
<PackageReference Include="PommaLabs.KVLite.PostgreSql" Version="10.0.2" />
<PackageReference Include="PommaLabs.KVLite.SqlServer" Version="10.0.2" />
<PackageReference Include="reCAPTCHA.AspNetCore" Version="3.0.3" />
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
<PackageReference Include="Serilog.Filters.Expressions" Version="2.1.0" />
......
......@@ -39,22 +39,22 @@ namespace PommaLabs.Thumbnailer.Services.Background
public sealed class CleanupTempApiKeysBackgroundService : BackgroundService
{
private readonly IApiKeyStore _apiKeyStore;
private readonly IOptions<CacheConfiguration> _cacheConfiguration;
private readonly IOptions<DatabaseConfiguration> _databaseConfiguration;
private readonly ILogger<CleanupTempApiKeysBackgroundService> _logger;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="logger">Logger.</param>
/// <param name="cacheConfiguration">Cache configuration.</param>
/// <param name="databaseConfiguration">Database configuration.</param>
/// <param name="apiKeyStore">API key store.</param>
public CleanupTempApiKeysBackgroundService(
ILogger<CleanupTempApiKeysBackgroundService> logger,
IOptions<CacheConfiguration> cacheConfiguration,
IOptions<DatabaseConfiguration> databaseConfiguration,
IApiKeyStore apiKeyStore)
{
_logger = logger;
_cacheConfiguration = cacheConfiguration;
_databaseConfiguration = databaseConfiguration;
_apiKeyStore = apiKeyStore;
}
......@@ -76,7 +76,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
_logger.LogWarning(ex, "An error occurred while deleting expired temporary API keys");
}
var delaySeconds = (int)_cacheConfiguration.Value.Lifetime.TotalSeconds / 10;
var delaySeconds = (int)_databaseConfiguration.Value.CacheLifetime.TotalSeconds / 10;
_logger.LogDebug("Temporary API keys cleanup service will run again in {DelaySeconds} seconds", delaySeconds);
await Task.Delay(delaySeconds * 1000, stoppingToken).ConfigureAwait(false);
......
......@@ -40,8 +40,8 @@ namespace PommaLabs.Thumbnailer.Services.Background
/// </summary>
public sealed class CleanupTempFilesBackgroundService : BackgroundService
{
private readonly IOptions<CacheConfiguration> _cacheConfiguration;
private readonly IClock _clock;
private readonly IOptions<DatabaseConfiguration> _databaseConfiguration;
private readonly ILogger<CleanupTempFilesBackgroundService> _logger;
private readonly ITempFileStore _tempFileStore;
......@@ -49,17 +49,17 @@ public sealed class CleanupTempFilesBackgroundService : BackgroundService
/// Constructor.
/// </summary>
/// <param name="logger">Logger.</param>
/// <param name="cacheConfiguration">Cache configuration.</param>
/// <param name="databaseConfiguration">Database configuration.</param>
/// <param name="tempFileStore">Temporary file store.</param>
/// <param name="clock">Clock.</param>
public CleanupTempFilesBackgroundService(
ILogger<CleanupTempFilesBackgroundService> logger,
IOptions<CacheConfiguration> cacheConfiguration,
IOptions<DatabaseConfiguration> databaseConfiguration,
ITempFileStore tempFileStore,
IClock clock)
{
_logger = logger;
_cacheConfiguration = cacheConfiguration;
_databaseConfiguration = databaseConfiguration;
_tempFileStore = tempFileStore;
_clock = clock;
}
......@@ -81,7 +81,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
_logger.LogWarning(ex, "An error occurred while deleting old temporary files");
}
var delaySeconds = (int)_cacheConfiguration.Value.Lifetime.TotalSeconds / 10;
var delaySeconds = (int)_databaseConfiguration.Value.CacheLifetime.TotalSeconds / 10;
_logger.LogDebug("Temporary files cleanup service will run again in {DelaySeconds} seconds", delaySeconds);
await Task.Delay(delaySeconds * 1000, stoppingToken).ConfigureAwait(false);
......@@ -92,7 +92,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
private async Task DeleteOldTempFilesAsync(CancellationToken stoppingToken)
{
var threshold = _clock.UtcNow.AddSeconds(_cacheConfiguration.Value.Lifetime.TotalSeconds * -2);
var threshold = _clock.UtcNow.AddSeconds(_databaseConfiguration.Value.CacheLifetime.TotalSeconds * -2);
var evaluatedFileCount = 0;
var deletedFileCount = 0;
......
......@@ -37,7 +37,7 @@ namespace PommaLabs.Thumbnailer.Services.Managers.Download
public sealed class CachingDownloadManager : IDownloadManager
{
private readonly ICache _cache;
private readonly IOptions<CacheConfiguration> _cacheConfiguration;
private readonly IOptions<DatabaseConfiguration> _databaseConfiguration;
private readonly IDownloadManager _downloadManager;
/// <summary>
......@@ -45,15 +45,15 @@ public sealed class CachingDownloadManager : IDownloadManager
/// </summary>
/// <param name="downloadManager">Download manager.</param>
/// <param name="cache">Cache.</param>
/// <param name="cacheConfiguration">Cache configuration.</param>
/// <param name="databaseConfiguration">Database configuration.</param>
public CachingDownloadManager(
IDownloadManager downloadManager,
ICache cache,
IOptions<CacheConfiguration> cacheConfiguration)
IOptions<DatabaseConfiguration> databaseConfiguration)
{
_downloadManager = downloadManager;
_cache = cache;
_cacheConfiguration = cacheConfiguration;
_databaseConfiguration = databaseConfiguration;
}
/// <inheritdoc/>
......@@ -65,9 +65,9 @@ public async Task<TempFileDTO> DownloadFileAsync(Uri fileUri, CancellationToken
valueGetter: async () =>
{
var file = await _downloadManager.DownloadFileAsync(fileUri, cancellationToken).ConfigureAwait(false);
return await TempFileDTO.CheckCacheAsync(_cache, _cacheConfiguration, file, cancellationToken).ConfigureAwait(false);
return await TempFileDTO.CheckCacheAsync(_cache, _databaseConfiguration, file, cancellationToken).ConfigureAwait(false);
},
interval: _cacheConfiguration.Value.Lifetime,
interval: _databaseConfiguration.Value.CacheLifetime,
cancellationToken: cancellationToken,
continueOnCapturedContext: false).ConfigureAwait(false);
}
......
......@@ -37,7 +37,7 @@ namespace PommaLabs.Thumbnailer.Services.Managers.Optimization
public sealed class CachingOptimizationManager : IOptimizationManager
{
private readonly ICache _cache;
private readonly IOptions<CacheConfiguration> _cacheConfiguration;
private readonly IOptions<DatabaseConfiguration> _databaseConfiguration;
private readonly IOptimizationManager _optimizationManager;
/// <summary>
......@@ -45,15 +45,15 @@ public sealed class CachingOptimizationManager : IOptimizationManager
/// </summary>
/// <param name="optimizationManager">Optimization manager.</param>
/// <param name="cache">Cache.</param>
/// <param name="cacheConfiguration">Cache configuration.</param>
/// <param name="databaseConfiguration">Database configuration.</param>
public CachingOptimizationManager(
IOptimizationManager optimizationManager,
ICache cache,
IOptions<CacheConfiguration> cacheConfiguration)
IOptions<DatabaseConfiguration> databaseConfiguration)
{
_optimizationManager = optimizationManager;
_cache = cache;
_cacheConfiguration = cacheConfiguration;
_databaseConfiguration = databaseConfiguration;
}
/// <inheritdoc/>
......@@ -64,7 +64,7 @@ public sealed class CachingOptimizationManager : IOptimizationManager
return await _cache.GetOrAddSlidingAsync(
partition: nameof(IOptimizationManager),
valueExpression: () => _optimizationManager.OptimizeImageAsync(file, mode, cancellationToken),
interval: _cacheConfiguration.Value.Lifetime,
interval: _databaseConfiguration.Value.CacheLifetime,
cancellationToken: cancellationToken,
continueOnCapturedContext: false).ConfigureAwait(false);
}
......
......@@ -36,7 +36,7 @@ namespace PommaLabs.Thumbnailer.Services.Managers.Thumbnail
public sealed class CachingThumbnailManager : IThumbnailManager
{
private readonly ICache _cache;
private readonly IOptions<CacheConfiguration> _cacheConfiguration;
private readonly IOptions<DatabaseConfiguration> _databaseConfiguration;
private readonly IThumbnailManager _thumbnailManager;
/// <summary>
......@@ -44,15 +44,15 @@ public sealed class CachingThumbnailManager : IThumbnailManager
/// </summary>
/// <param name="thumbnailManager">Thumbnail manager.</param>
/// <param name="cache">Cache.</param>
/// <param name="cacheConfiguration">Cache configuration.</param>
/// <param name="databaseConfiguration">Database configuration.</param>
public CachingThumbnailManager(
IThumbnailManager thumbnailManager,
ICache cache,
IOptions<CacheConfiguration> cacheConfiguration)
IOptions<DatabaseConfiguration> databaseConfiguration)
{
_thumbnailManager = thumbnailManager;
_cache = cache;
_cacheConfiguration = cacheConfiguration;
_databaseConfiguration = databaseConfiguration;
}
/// <inheritdoc/>
......@@ -63,7 +63,7 @@ public sealed class CachingThumbnailManager : IThumbnailManager
return await _cache.GetOrAddSlidingAsync(
partition: nameof(IThumbnailManager),
valueExpression: () => _thumbnailManager.GenerateThumbnailAsync(file, widthPx, heightPx, shavePx, fill, cancellationToken),
interval: _cacheConfiguration.Value.Lifetime,
interval: _databaseConfiguration.Value.CacheLifetime,
cancellationToken: cancellationToken,
continueOnCapturedContext: false).ConfigureAwait(false);
}
......
......@@ -38,7 +38,7 @@ namespace PommaLabs.Thumbnailer.Services.Stores.TempFiles
public sealed class CachingTempFileStore : ITempFileStore
{
private readonly ICache _cache;
private readonly IOptions<CacheConfiguration> _cacheConfiguration;
private readonly IOptions<DatabaseConfiguration> _databaseConfiguration;
private readonly ITempFileStore _tempFileStore;
/// <summary>
......@@ -46,15 +46,15 @@ public sealed class CachingTempFileStore : ITempFileStore
/// </summary>
/// <param name="tempFileStore">Temporary file store.</param>
/// <param name="cache">Cache.</param>
/// <param name="cacheConfiguration">Cache configuration.</param>
/// <param name="databaseConfiguration">Database configuration.</param>
public CachingTempFileStore(
ITempFileStore tempFileStore,
ICache cache,
IOptions<CacheConfiguration> cacheConfiguration)
IOptions<DatabaseConfiguration> databaseConfiguration)
{
_tempFileStore = tempFileStore;
_cache = cache;
_cacheConfiguration = cacheConfiguration;
_databaseConfiguration = databaseConfiguration;
}
/// <inheritdoc/>
......@@ -76,14 +76,14 @@ public Task<IEnumerable<TempFileDTO>> GetTempFilesAsync(CancellationToken cancel
public async Task<TempFileDTO> HandleFileUploadAsync(IFormFile formFile, CancellationToken cancellationToken)
{
var file = await _tempFileStore.HandleFileUploadAsync(formFile, cancellationToken).ConfigureAwait(false);
return await TempFileDTO.CheckCacheAsync(_cache, _cacheConfiguration, file, cancellationToken).ConfigureAwait(false);
return await TempFileDTO.CheckCacheAsync(_cache, _databaseConfiguration, file, cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc/>