diff --git a/.gitignore b/.gitignore index 940794e..3705d46 100644 --- a/.gitignore +++ b/.gitignore @@ -286,3 +286,6 @@ __pycache__/ *.btm.cs *.odx.cs *.xsd.cs + +# Project specific +imagecache diff --git a/EquineExchange.ImageHosting.sln b/EquineExchange.ImageHosting.sln new file mode 100644 index 0000000..8153446 --- /dev/null +++ b/EquineExchange.ImageHosting.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.15 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EquineExchange.ImageHosting", "EquineExchange.ImageHosting\EquineExchange.ImageHosting.csproj", "{6FE1ED76-F199-4376-816B-D756DB2F6DEC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FDA411BA-98F0-4E90-9A30-B4A756CE025B}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + LICENSE.txt = LICENSE.txt + README.md = README.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6FE1ED76-F199-4376-816B-D756DB2F6DEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FE1ED76-F199-4376-816B-D756DB2F6DEC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FE1ED76-F199-4376-816B-D756DB2F6DEC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FE1ED76-F199-4376-816B-D756DB2F6DEC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {626A9894-8506-4CDB-854E-55289639749B} + EndGlobalSection +EndGlobal diff --git a/EquineExchange.ImageHosting/AppSettings.cs b/EquineExchange.ImageHosting/AppSettings.cs new file mode 100644 index 0000000..d6b8a04 --- /dev/null +++ b/EquineExchange.ImageHosting/AppSettings.cs @@ -0,0 +1,23 @@ +namespace EquineExchange.ImageHosting +{ + using Microsoft.Azure; + + public static class AppSettings + { + public static string AzureReader(string settingName) => GetString("app:azureReader." + settingName); + + public static string ImageResizer(string settingName) => GetString("app:imageResizer." + settingName); + + private static string GetString(string settingName, string defaultValue = null) + { + var temp = CloudConfigurationManager.GetSetting(settingName); + + if (string.IsNullOrWhiteSpace(temp)) + { + temp = defaultValue; + } + + return temp; + } + } +} diff --git a/EquineExchange.ImageHosting/AzureReaderSettings.cs b/EquineExchange.ImageHosting/AzureReaderSettings.cs new file mode 100644 index 0000000..a2f48d0 --- /dev/null +++ b/EquineExchange.ImageHosting/AzureReaderSettings.cs @@ -0,0 +1,31 @@ +namespace EquineExchange.ImageHosting +{ + using System; + using System.Collections.Specialized; + using System.Configuration; + + public static class AzureReaderSettings + { + public static string Prefix { get; } = AppSettings.AzureReader("prefix"); + + public static string ConnectionString { get; } = AppSettings.AzureReader("connectionString"); + + public static string RedirectToBlobIfUnmodified { get; } = AppSettings.AzureReader("redirectToBlobIfUnmodified"); + + public static string RequireImageExtension { get; } = AppSettings.AzureReader("requireImageExtension"); + + public static string UntrustedData { get; } = AppSettings.AzureReader("untrustedData"); + + public static NameValueCollection ToNameValueCollection() + { + return new NameValueCollection(StringComparer.OrdinalIgnoreCase) + { + { "prefix", Prefix }, + { "connectionString", ConnectionString }, + { "redirectToBlobIfUnmodified", RedirectToBlobIfUnmodified }, + { "requireImageExtension", RequireImageExtension }, + { "untrustedData", UntrustedData }, + }; + } + } +} diff --git a/EquineExchange.ImageHosting/EquineExchange.ImageHosting.csproj b/EquineExchange.ImageHosting/EquineExchange.ImageHosting.csproj new file mode 100644 index 0000000..90e8713 --- /dev/null +++ b/EquineExchange.ImageHosting/EquineExchange.ImageHosting.csproj @@ -0,0 +1,159 @@ + + + + + + Debug + AnyCPU + + + 2.0 + {6FE1ED76-F199-4376-816B-D756DB2F6DEC} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + EquineExchange.ImageHosting + EquineExchange.ImageHosting + v4.7 + true + + + + + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + true + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + ..\packages\ImageResizer.4.1.10\lib\net45\ImageResizer.dll + + + ..\packages\ImageResizer.Plugins.AzureReader2.4.1.10\lib\net45\ImageResizer.Plugins.AzureReader2.dll + + + ..\packages\ImageResizer.Plugins.DiskCache.4.1.10\lib\net45\ImageResizer.Plugins.DiskCache.dll + + + ..\packages\ImageResizer.Plugins.PrettyGifs.4.1.10\lib\net45\ImageResizer.Plugins.PrettyGifs.dll + + + ..\packages\ImageResizer.Storage.4.1.10\lib\net45\ImageResizer.Storage.dll + + + ..\packages\Microsoft.Azure.KeyVault.Core.2.0.4\lib\net45\Microsoft.Azure.KeyVault.Core.dll + + + ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + + + + ..\packages\Microsoft.Data.Edm.5.8.3\lib\net40\Microsoft.Data.Edm.dll + + + ..\packages\Microsoft.Data.OData.5.8.3\lib\net40\Microsoft.Data.OData.dll + + + ..\packages\Microsoft.Data.Services.Client.5.8.3\lib\net40\Microsoft.Data.Services.Client.dll + + + ..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll + + + ..\packages\WindowsAzure.Storage.8.4.0\lib\net45\Microsoft.WindowsAzure.Storage.dll + + + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + + + + + + ..\packages\System.Spatial.5.8.3\lib\net40\System.Spatial.dll + + + + + + + Designer + + + Web.config + + + Web.config + + + + + + Designer + + + + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + True + True + 55847 + / + http://localhost:55847/ + False + False + + + False + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/EquineExchange.ImageHosting/ImageResizerSettings.cs b/EquineExchange.ImageHosting/ImageResizerSettings.cs new file mode 100644 index 0000000..d852312 --- /dev/null +++ b/EquineExchange.ImageHosting/ImageResizerSettings.cs @@ -0,0 +1,7 @@ +namespace EquineExchange.ImageHosting +{ + public static class ImageResizerSettings + { + public static string License { get; } = AppSettings.ImageResizer("license"); + } +} diff --git a/EquineExchange.ImageHosting/ImageResizingConfig.cs b/EquineExchange.ImageHosting/ImageResizingConfig.cs new file mode 100644 index 0000000..0f00991 --- /dev/null +++ b/EquineExchange.ImageHosting/ImageResizingConfig.cs @@ -0,0 +1,30 @@ +namespace EquineExchange.ImageHosting +{ + using System.Web; + + using ImageResizer.Configuration; + using ImageResizer.Plugins.AzureReader2; + using ImageResizer.Plugins.Basic; + + public class ImageResizingConfig : IHttpModule + { + public void Init(HttpApplication context) + { + SetupImageResizer(Config.Current); + } + + public void Dispose() + { + } + + private static void SetupImageResizer(Config config) + { + if (ImageResizerSettings.License != null) + { + new StaticLicenseProvider(ImageResizerSettings.License).Install(config); + } + + new AzureReader2Plugin(AzureReaderSettings.ToNameValueCollection()).Install(config); + } + } +} diff --git a/EquineExchange.ImageHosting/Properties/AssemblyInfo.cs b/EquineExchange.ImageHosting/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..11ce2f1 --- /dev/null +++ b/EquineExchange.ImageHosting/Properties/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Reflection; + +[assembly: AssemblyTitle("EquineExchange.ImageHosting")] +[assembly: AssemblyCompany("Equine Exchange, LLC")] +[assembly: AssemblyProduct("EquineExchange.ImageHosting")] +[assembly: AssemblyCopyright("Copyright © 2017")] + +[assembly: AssemblyVersion("1.0.0")] +[assembly: AssemblyFileVersion("1.0.0")] +[assembly: AssemblyInformationalVersion("1.0.0")] diff --git a/EquineExchange.ImageHosting/Web.Debug.config b/EquineExchange.ImageHosting/Web.Debug.config new file mode 100644 index 0000000..fae9cfe --- /dev/null +++ b/EquineExchange.ImageHosting/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/EquineExchange.ImageHosting/Web.Release.config b/EquineExchange.ImageHosting/Web.Release.config new file mode 100644 index 0000000..da6e960 --- /dev/null +++ b/EquineExchange.ImageHosting/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/EquineExchange.ImageHosting/Web.config b/EquineExchange.ImageHosting/Web.config new file mode 100644 index 0000000..2e625f5 --- /dev/null +++ b/EquineExchange.ImageHosting/Web.config @@ -0,0 +1,85 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/EquineExchange.ImageHosting/packages.config b/EquineExchange.ImageHosting/packages.config new file mode 100644 index 0000000..6d604c7 --- /dev/null +++ b/EquineExchange.ImageHosting/packages.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/EquineExchange.ImageHosting/robots.txt b/EquineExchange.ImageHosting/robots.txt new file mode 100644 index 0000000..7211b8a --- /dev/null +++ b/EquineExchange.ImageHosting/robots.txt @@ -0,0 +1,2 @@ +User-Agent: * +Disallow: / diff --git a/README.md b/README.md index 10e32e1..696c64c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,50 @@ -# EquineExchange.ImageHosting -A simple image host backed by blob storage and ImageResizer +# EquineExchange.ImageHosting + +EquineExchange.ImageHosting is a simple image host backed by [Azure Blob Storage](https://azure.microsoft.com/en-us/services/storage/blobs/) and [ImageResizer](https://imageresizing.net/). +It's sufficient to be used as-is for development and test environments where user load isn't a concern, but for production use you should put it behind a CDN. + +## Usage + +This site is meant to be used as-is with little to no customization. +The license, [AzureReader2](https://imageresizing.net/docs/v4/plugins/azurereader2), and other basic settings are loaded from `appSettings` so you're able to configure them in the Azure Portal. + +### Settings + +| Name | Default value | Description | +| :----------------------------------------- | :---------------------- | :---------------------------------------------------------------------------------------------- | +| app:imageResizer.license | | The ImageResizer license | +| app:azureReader.connectionString | StorageConnectionString | The name of the connection string or the actual connection string to use | +| app:azureReader.prefix | ~/m | The virtual folder all resize requests should use, anything outside here won't hit blob storage | +| app:azureReader.redirectToBlobIfUnmodified | false | Since this is meant to be the source of a CDN there's no reason to redirect here | +| app:azureReader.requireImageExtension | true | Helps ensure the right content type is served to the client | +| app:azureReader.untrustedData | true | Re-encodes all the images to help limit malicious uploads from possibly harming your users | + +### Example + +```xml + + + + + + + + + + + + + + + +``` + +## ImageResizer + +Under the ImageResizer licensing model [you will need to purchase a license](https://imageresizing.net/pricing) even if you comply with their OSS license. +The reason for needing a license is we've chosen to use the NuGet packages instead of compiling our own assemblies, this means the license checks are still in place and the red dot will show in the bottom right corner. +There's a new OSS license that costs $7/year (you may need to contact them about purchasing it) which is a fair trade off for not having to maintain our own fork without the checks. + +## Copyright and license + +Copyright (c) 2017 Equine Exchange, LLC under [the MIT License](LICENSE).