diff --git a/backend/src/Squidex.Infrastructure.Amazon/Assets/AmazonS3AssetStore.cs b/backend/src/Squidex.Infrastructure.Amazon/Assets/AmazonS3AssetStore.cs deleted file mode 100644 index 905184347..000000000 --- a/backend/src/Squidex.Infrastructure.Amazon/Assets/AmazonS3AssetStore.cs +++ /dev/null @@ -1,278 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.IO; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Amazon; -using Amazon.S3; -using Amazon.S3.Model; -using Amazon.S3.Transfer; - -namespace Squidex.Assets -{ - public sealed class AmazonS3AssetStore : DisposableObjectBase, IAssetStore, IInitializable - { - private const int BufferSize = 81920; - private readonly AmazonS3Options options; - private TransferUtility transferUtility; - private IAmazonS3 s3Client; - - public AmazonS3AssetStore(AmazonS3Options options) - { - Guard.NotNullOrEmpty(options.Bucket, nameof(options.Bucket)); - Guard.NotNullOrEmpty(options.AccessKey, nameof(options.AccessKey)); - Guard.NotNullOrEmpty(options.SecretKey, nameof(options.SecretKey)); - - this.options = options; - } - - protected override void DisposeObject(bool disposing) - { - if (disposing) - { - s3Client?.Dispose(); - - transferUtility?.Dispose(); - } - } - - public async Task InitializeAsync(CancellationToken ct = default) - { - try - { - var amazonS3Config = new AmazonS3Config { ForcePathStyle = options.ForcePathStyle }; - - if (!string.IsNullOrWhiteSpace(options.ServiceUrl)) - { - amazonS3Config.ServiceURL = options.ServiceUrl; - } - else - { - amazonS3Config.RegionEndpoint = RegionEndpoint.GetBySystemName(options.RegionName); - } - - s3Client = new AmazonS3Client(options.AccessKey, options.SecretKey, amazonS3Config); - - transferUtility = new TransferUtility(s3Client); - - var exists = await s3Client.DoesS3BucketExistAsync(options.Bucket); - - if (!exists) - { - throw new ConfigurationException($"Cannot connect to Amazon S3 bucket '{options.Bucket}'."); - } - } - catch (AmazonS3Exception ex) - { - throw new ConfigurationException($"Cannot connect to Amazon S3 bucket '{options.Bucket}'.", ex); - } - } - - public string? GeneratePublicUrl(string fileName) - { - return null; - } - - public async Task GetSizeAsync(string fileName, CancellationToken ct = default) - { - var key = GetKey(fileName, nameof(fileName)); - - try - { - var request = new GetObjectMetadataRequest - { - BucketName = options.Bucket, - Key = key - }; - - var metadata = await s3Client.GetObjectMetadataAsync(request, ct); - - return metadata.ContentLength; - } - catch (AmazonS3Exception ex) when (ex.StatusCode == HttpStatusCode.NotFound) - { - throw new AssetNotFoundException(fileName, ex); - } - } - - public async Task CopyAsync(string sourceFileName, string targetFileName, CancellationToken ct = default) - { - var sourceKey = GetKey(sourceFileName, nameof(sourceFileName)); - var targetKey = GetKey(targetFileName, nameof(targetFileName)); - - try - { - await EnsureNotExistsAsync(targetKey, targetFileName, ct); - - var request = new CopyObjectRequest - { - SourceBucket = options.Bucket, - SourceKey = sourceKey, - DestinationBucket = options.Bucket, - DestinationKey = targetKey - }; - - await s3Client.CopyObjectAsync(request, ct); - } - catch (AmazonS3Exception ex) when (ex.StatusCode == HttpStatusCode.NotFound) - { - throw new AssetNotFoundException(sourceFileName, ex); - } - catch (AmazonS3Exception ex) when (ex.StatusCode == HttpStatusCode.PreconditionFailed) - { - throw new AssetAlreadyExistsException(targetFileName); - } - } - - public async Task DownloadAsync(string fileName, Stream stream, BytesRange range = default, CancellationToken ct = default) - { - Guard.NotNull(stream, nameof(stream)); - - var key = GetKey(fileName, nameof(fileName)); - - try - { - var request = new GetObjectRequest - { - BucketName = options.Bucket, - Key = key - }; - - if (range.IsDefined) - { - request.ByteRange = new ByteRange(range.ToString()); - } - - using (var response = await s3Client.GetObjectAsync(request, ct)) - { - await response.ResponseStream.CopyToAsync(stream, BufferSize, ct); - } - } - catch (AmazonS3Exception ex) when (ex.StatusCode == HttpStatusCode.NotFound) - { - throw new AssetNotFoundException(fileName, ex); - } - } - - public async Task UploadAsync(string fileName, Stream stream, bool overwrite = false, CancellationToken ct = default) - { - Guard.NotNull(stream, nameof(stream)); - - var key = GetKey(fileName, nameof(fileName)); - - try - { - if (!overwrite) - { - await EnsureNotExistsAsync(key, fileName, ct); - } - - var request = new TransferUtilityUploadRequest - { - BucketName = options.Bucket, - Key = key - }; - - if (!HasContentLength(stream)) - { - var tempFileName = Path.GetTempFileName(); - - var tempStream = new FileStream(tempFileName, - FileMode.Create, - FileAccess.ReadWrite, - FileShare.Delete, 1024 * 16, - FileOptions.Asynchronous | - FileOptions.DeleteOnClose | - FileOptions.SequentialScan); - - using (tempStream) - { - await stream.CopyToAsync(tempStream, ct); - - request.InputStream = tempStream; - - await transferUtility.UploadAsync(request, ct); - } - } - else - { - request.InputStream = new SeekFakerStream(stream); - - request.AutoCloseStream = false; - - await transferUtility.UploadAsync(request, ct); - } - } - catch (AmazonS3Exception ex) when (ex.StatusCode == HttpStatusCode.PreconditionFailed) - { - throw new AssetAlreadyExistsException(fileName); - } - } - - public async Task DeleteAsync(string fileName) - { - var key = GetKey(fileName, nameof(fileName)); - - try - { - var request = new DeleteObjectRequest - { - BucketName = options.Bucket, - Key = key - }; - - await s3Client.DeleteObjectAsync(request); - } - catch (AmazonS3Exception ex) when (ex.StatusCode == HttpStatusCode.NotFound) - { - return; - } - } - - private string GetKey(string fileName, string parameterName) - { - Guard.NotNullOrEmpty(fileName, parameterName); - - if (!string.IsNullOrWhiteSpace(options.BucketFolder)) - { - return $"{options.BucketFolder}/{fileName}"; - } - else - { - return fileName; - } - } - - private async Task EnsureNotExistsAsync(string key, string fileName, CancellationToken ct) - { - try - { - await s3Client.GetObjectAsync(options.Bucket, key, ct); - } - catch - { - return; - } - - throw new AssetAlreadyExistsException(fileName); - } - - private static bool HasContentLength(Stream stream) - { - try - { - return stream.Length > 0; - } - catch - { - return false; - } - } - } -} \ No newline at end of file diff --git a/backend/src/Squidex.Infrastructure.Amazon/Assets/AmazonS3Options.cs b/backend/src/Squidex.Infrastructure.Amazon/Assets/AmazonS3Options.cs deleted file mode 100644 index d538c1dcb..000000000 --- a/backend/src/Squidex.Infrastructure.Amazon/Assets/AmazonS3Options.cs +++ /dev/null @@ -1,26 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -namespace Squidex.Assets -{ - public sealed class AmazonS3Options - { - public string? ServiceUrl { get; set; } - - public string? RegionName { get; set; } - - public string Bucket { get; set; } - - public string? BucketFolder { get; set; } - - public string AccessKey { get; set; } - - public string SecretKey { get; set; } - - public bool ForcePathStyle { get; set; } - } -} \ No newline at end of file diff --git a/backend/src/Squidex.Infrastructure.Amazon/Assets/SeekFakerStream.cs b/backend/src/Squidex.Infrastructure.Amazon/Assets/SeekFakerStream.cs deleted file mode 100644 index 659feb2b9..000000000 --- a/backend/src/Squidex.Infrastructure.Amazon/Assets/SeekFakerStream.cs +++ /dev/null @@ -1,85 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.IO; - -namespace Squidex.Assets -{ - public sealed class SeekFakerStream : Stream - { - private readonly Stream inner; - - public override bool CanRead - { - get { return inner.CanRead; } - } - - public override bool CanSeek - { - get { return true; } - } - - public override bool CanWrite - { - get { return false; } - } - - public override long Length - { - get { return inner.Length; } - } - - public override long Position - { - get { return inner.Position; } - set { throw new NotSupportedException(); } - } - - public SeekFakerStream(Stream inner) - { - Guard.NotNull(inner, nameof(inner)); - - if (!inner.CanRead) - { - throw new ArgumentException("Inner stream must be readable."); - } - - this.inner = inner; - } - - public override int Read(byte[] buffer, int offset, int count) - { - return inner.Read(buffer, offset, count); - } - - public override void Flush() - { - throw new NotSupportedException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - if (offset != 0 || origin != SeekOrigin.Begin) - { - throw new NotSupportedException(); - } - - return 0; - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - } -} diff --git a/backend/src/Squidex.Infrastructure.Amazon/Squidex.Infrastructure.Amazon.csproj b/backend/src/Squidex.Infrastructure.Amazon/Squidex.Infrastructure.Amazon.csproj deleted file mode 100644 index ad0e92c2b..000000000 --- a/backend/src/Squidex.Infrastructure.Amazon/Squidex.Infrastructure.Amazon.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - netcoreapp3.1 - Squidex.Infrastructure - 8.0 - enable - - - - - - - - - - - ..\..\Squidex.ruleset - - - - - \ No newline at end of file diff --git a/backend/src/Squidex.Infrastructure.GoogleCloud/Assets/GoogleCloudAssetStore.cs b/backend/src/Squidex.Infrastructure.GoogleCloud/Assets/GoogleCloudAssetStore.cs deleted file mode 100644 index b67c958ee..000000000 --- a/backend/src/Squidex.Infrastructure.GoogleCloud/Assets/GoogleCloudAssetStore.cs +++ /dev/null @@ -1,141 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.IO; -using System.Net; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; -using Google; -using Google.Cloud.Storage.V1; - -namespace Squidex.Assets -{ - public sealed class GoogleCloudAssetStore : IAssetStore, IInitializable - { - private static readonly UploadObjectOptions IfNotExists = new UploadObjectOptions { IfGenerationMatch = 0 }; - private static readonly CopyObjectOptions IfNotExistsCopy = new CopyObjectOptions { IfGenerationMatch = 0 }; - private readonly string bucketName; - private StorageClient storageClient; - - public GoogleCloudAssetStore(string bucketName) - { - Guard.NotNullOrEmpty(bucketName, nameof(bucketName)); - - this.bucketName = bucketName; - } - - public async Task InitializeAsync(CancellationToken ct = default) - { - try - { - storageClient = await StorageClient.CreateAsync(); - - await storageClient.GetBucketAsync(bucketName, cancellationToken: ct); - } - catch (Exception ex) - { - throw new ConfigurationException($"Cannot connect to google cloud bucket '{bucketName}'.", ex); - } - } - - public string? GeneratePublicUrl(string fileName) - { - return null; - } - - public async Task GetSizeAsync(string fileName, CancellationToken ct = default) - { - Guard.NotNullOrEmpty(fileName, nameof(fileName)); - - try - { - var obj = await storageClient.GetObjectAsync(bucketName, fileName, null, ct); - - if (!obj.Size.HasValue) - { - throw new AssetNotFoundException(fileName); - } - - return (long)obj.Size.Value; - } - catch (GoogleApiException ex) when (ex.HttpStatusCode == HttpStatusCode.NotFound) - { - throw new AssetNotFoundException(fileName, ex); - } - } - - public async Task CopyAsync(string sourceFileName, string targetFileName, CancellationToken ct = default) - { - Guard.NotNullOrEmpty(sourceFileName, nameof(sourceFileName)); - Guard.NotNullOrEmpty(targetFileName, nameof(targetFileName)); - - try - { - await storageClient.CopyObjectAsync(bucketName, sourceFileName, bucketName, targetFileName, IfNotExistsCopy, ct); - } - catch (GoogleApiException ex) when (ex.HttpStatusCode == HttpStatusCode.NotFound) - { - throw new AssetNotFoundException(sourceFileName, ex); - } - catch (GoogleApiException ex) when (ex.HttpStatusCode == HttpStatusCode.PreconditionFailed) - { - throw new AssetAlreadyExistsException(targetFileName); - } - } - - public async Task DownloadAsync(string fileName, Stream stream, BytesRange range = default, CancellationToken ct = default) - { - Guard.NotNullOrEmpty(fileName, nameof(fileName)); - - try - { - var downloadOptions = new DownloadObjectOptions(); - - if (range.IsDefined) - { - downloadOptions.Range = new RangeHeaderValue(range.From, range.To); - } - - await storageClient.DownloadObjectAsync(bucketName, fileName, stream, downloadOptions, ct); - } - catch (GoogleApiException ex) when (ex.HttpStatusCode == HttpStatusCode.NotFound) - { - throw new AssetNotFoundException(fileName, ex); - } - } - - public async Task UploadAsync(string fileName, Stream stream, bool overwrite = false, CancellationToken ct = default) - { - Guard.NotNullOrEmpty(fileName, nameof(fileName)); - - try - { - await storageClient.UploadObjectAsync(bucketName, fileName, "application/octet-stream", stream, overwrite ? null : IfNotExists, ct); - } - catch (GoogleApiException ex) when (ex.HttpStatusCode == HttpStatusCode.PreconditionFailed) - { - throw new AssetAlreadyExistsException(fileName); - } - } - - public async Task DeleteAsync(string fileName) - { - Guard.NotNullOrEmpty(fileName, nameof(fileName)); - - try - { - await storageClient.DeleteObjectAsync(bucketName, fileName); - } - catch (GoogleApiException ex) when (ex.HttpStatusCode == HttpStatusCode.NotFound) - { - return; - } - } - } -} diff --git a/backend/src/Squidex.Infrastructure.GoogleCloud/Squidex.Infrastructure.GoogleCloud.csproj b/backend/src/Squidex.Infrastructure.GoogleCloud/Squidex.Infrastructure.GoogleCloud.csproj deleted file mode 100644 index e3c47f7d8..000000000 --- a/backend/src/Squidex.Infrastructure.GoogleCloud/Squidex.Infrastructure.GoogleCloud.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - netcoreapp3.1 - Squidex.Infrastructure - 8.0 - enable - - - full - True - - - - - - - - - - - - ..\..\Squidex.ruleset - - - - - \ No newline at end of file