mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
66 changed files with 619 additions and 526 deletions
@ -0,0 +1,58 @@ |
|||
FROM microsoft/dotnet:2.2-sdk |
|||
|
|||
# Install runtime dependencies |
|||
RUN apt-get update \ |
|||
&& apt-get install -y --no-install-recommends ca-certificates bzip2 libfontconfig \ |
|||
&& apt-get clean \ |
|||
&& rm -rf /var/lib/apt/lists/* |
|||
|
|||
# Install official PhantomJS release |
|||
RUN set -x \ |
|||
&& apt-get update \ |
|||
&& apt-get install -y --no-install-recommends \ |
|||
&& mkdir /srv/var \ |
|||
&& mkdir /tmp/phantomjs \ |
|||
# Download Phantom JS |
|||
&& curl -L https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 | tar -xj --strip-components=1 -C /tmp/phantomjs \ |
|||
# Copy binaries only |
|||
&& mv /tmp/phantomjs/bin/phantomjs /usr/local/bin \ |
|||
# Create symbol link |
|||
# Clean up |
|||
&& apt-get autoremove -y \ |
|||
&& apt-get clean all \ |
|||
&& rm -rf /tmp/* /var/lib/apt/lists/* |
|||
|
|||
RUN phantomjs --version |
|||
|
|||
# Install Node |
|||
ENV NODE_VERSION 8.9.4 |
|||
ENV NODE_DOWNLOAD_SHA 21fb4690e349f82d708ae766def01d7fec1b085ce1f5ab30d9bda8ee126ca8fc |
|||
RUN curl -SL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz" --output nodejs.tar.gz \ |
|||
&& echo "$NODE_DOWNLOAD_SHA nodejs.tar.gz" | sha256sum -c - \ |
|||
&& tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 \ |
|||
&& rm nodejs.tar.gz \ |
|||
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs |
|||
|
|||
# Install Google Chrome |
|||
|
|||
# See https://crbug.com/795759 |
|||
RUN apt-get update && apt-get install -yq libgconf-2-4 |
|||
|
|||
# Install latest chrome dev package and fonts to support major charsets (Chinese, Japanese, Arabic, Hebrew, Thai and a few others) |
|||
RUN apt-get update && apt-get install -y wget --no-install-recommends \ |
|||
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ |
|||
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \ |
|||
&& apt-get update \ |
|||
&& apt-get install -y google-chrome-unstable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst ttf-freefont \ |
|||
--no-install-recommends \ |
|||
&& rm -rf /var/lib/apt/lists/* \ |
|||
&& apt-get autoremove -y \ |
|||
&& rm -rf /src/*.deb |
|||
|
|||
# It's a good idea to use dumb-init to help prevent zombie chrome processes. |
|||
ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64 /usr/local/bin/dumb-init |
|||
|
|||
RUN chmod +x /usr/local/bin/dumb-init |
|||
|
|||
# Install puppeteer so it's available in the container. |
|||
RUN npm i puppeteer |
|||
@ -1,4 +1,4 @@ |
|||
<?xml version="1.0" encoding="utf-8" ?> |
|||
<Weavers> |
|||
<Freezable/> |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<Freezable /> |
|||
</Weavers> |
|||
@ -0,0 +1,26 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="Freezable" minOccurs="0" maxOccurs="1" type="xs:anyType" /> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -1,27 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace Squidex.Infrastructure.Diagnostics |
|||
{ |
|||
public sealed class HealthCheckResult |
|||
{ |
|||
public bool IsHealthy { get; } |
|||
|
|||
public string Description { get; } |
|||
|
|||
public Dictionary<string, object> Data { get; } |
|||
|
|||
public HealthCheckResult(bool isHealthy, string description = null, Dictionary<string, object> data = null) |
|||
{ |
|||
IsHealthy = isHealthy; |
|||
Data = data; |
|||
Description = description; |
|||
} |
|||
} |
|||
} |
|||
@ -1,16 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Infrastructure.Diagnostics |
|||
{ |
|||
public static class HealthCheckScopes |
|||
{ |
|||
public const string Any = "*"; |
|||
public const string Node = "node"; |
|||
public const string Cluster = "cluster"; |
|||
} |
|||
} |
|||
@ -1,20 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Squidex.Infrastructure.Diagnostics |
|||
{ |
|||
public interface IHealthCheck |
|||
{ |
|||
IEnumerable<string> Scopes { get; } |
|||
|
|||
Task<HealthCheckResult> CheckHealthAsync(CancellationToken cancellationToken = default(CancellationToken)); |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using NJsonSchema; |
|||
using NSwag.SwaggerGeneration.Processors; |
|||
using NSwag.SwaggerGeneration.Processors.Contexts; |
|||
using Squidex.Infrastructure.Tasks; |
|||
|
|||
namespace Squidex.Areas.Api.Config.Swagger |
|||
{ |
|||
public class FixProcessor : IOperationProcessor |
|||
{ |
|||
private static readonly JsonSchema4 StringSchema = new JsonSchema4 { Type = JsonObjectType.String }; |
|||
|
|||
public Task<bool> ProcessAsync(OperationProcessorContext context) |
|||
{ |
|||
foreach (var parameter in context.Parameters.Values) |
|||
{ |
|||
if (parameter.IsRequired && parameter.Schema != null && parameter.Schema.Type == JsonObjectType.String) |
|||
{ |
|||
parameter.Schema = StringSchema; |
|||
} |
|||
} |
|||
|
|||
return TaskHelper.True; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using Microsoft.Extensions.Options; |
|||
using NSwag; |
|||
using NSwag.SwaggerGeneration.Processors.Security; |
|||
using Squidex.Config; |
|||
using Squidex.Pipeline.Swagger; |
|||
|
|||
namespace Squidex.Areas.Api.Config.Swagger |
|||
{ |
|||
public class SecurityProcessor : SecurityDefinitionAppender |
|||
{ |
|||
public SecurityProcessor(IOptions<MyUrlsOptions> urlOptions) |
|||
: base(Constants.SecurityDefinition, CreateOAuthSchema(urlOptions.Value)) |
|||
{ |
|||
} |
|||
|
|||
private static SwaggerSecurityScheme CreateOAuthSchema(MyUrlsOptions urlOptions) |
|||
{ |
|||
var securityScheme = new SwaggerSecurityScheme(); |
|||
|
|||
var tokenUrl = urlOptions.BuildUrl($"{Constants.IdentityServerPrefix}/connect/token", false); |
|||
|
|||
securityScheme.TokenUrl = tokenUrl; |
|||
|
|||
var securityDocs = NSwagHelper.LoadDocs("security"); |
|||
var securityText = securityDocs.Replace("<TOKEN_URL>", tokenUrl); |
|||
|
|||
securityScheme.Description = securityText; |
|||
|
|||
securityScheme.Type = SwaggerSecuritySchemeType.OAuth2; |
|||
securityScheme.Flow = SwaggerOAuth2Flow.Application; |
|||
|
|||
securityScheme.Scopes = new Dictionary<string, string> |
|||
{ |
|||
[Constants.ApiScope] = "Read and write access to the API" |
|||
}; |
|||
|
|||
return securityScheme; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Options; |
|||
using NSwag.SwaggerGeneration.Processors; |
|||
using NSwag.SwaggerGeneration.Processors.Contexts; |
|||
using Squidex.Config; |
|||
using Squidex.Infrastructure.Tasks; |
|||
|
|||
namespace Squidex.Areas.Api.Config.Swagger |
|||
{ |
|||
public sealed class ThemeProcessor : IDocumentProcessor |
|||
{ |
|||
private const string Background = "#3f83df"; |
|||
|
|||
private readonly string url; |
|||
|
|||
public ThemeProcessor(IOptions<MyUrlsOptions> urlOptions) |
|||
{ |
|||
url = urlOptions.Value.BuildUrl("images/logo-white.png", false); |
|||
} |
|||
|
|||
public Task ProcessAsync(DocumentProcessorContext context) |
|||
{ |
|||
context.Document.BasePath = Constants.ApiPrefix; |
|||
|
|||
context.Document.Info.ExtensionData = new Dictionary<string, object> |
|||
{ |
|||
["x-logo"] = new { url, backgroundColor = Background } |
|||
}; |
|||
|
|||
return TaskHelper.Done; |
|||
} |
|||
} |
|||
} |
|||
@ -1,113 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Diagnostics; |
|||
using Squidex.Infrastructure.Json; |
|||
|
|||
namespace Squidex.Pipeline.Diagnostics |
|||
{ |
|||
public sealed class HealthCheckMiddleware |
|||
{ |
|||
private const string Suffix = "HealthCheck"; |
|||
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(2); |
|||
|
|||
private readonly Dictionary<string, IHealthCheck> healthChecks; |
|||
private readonly IJsonSerializer serializer; |
|||
private readonly RequestDelegate next; |
|||
private readonly List<string> scopes; |
|||
|
|||
public HealthCheckMiddleware(IEnumerable<IHealthCheck> healthChecks, IJsonSerializer serializer, RequestDelegate next, string scopes) |
|||
{ |
|||
Guard.NotNull(healthChecks, nameof(healthChecks)); |
|||
Guard.NotNull(serializer, nameof(serializer)); |
|||
|
|||
this.healthChecks = healthChecks.ToDictionary(GetName); |
|||
this.next = next; |
|||
this.serializer = serializer; |
|||
this.scopes = SplitScopes(scopes); |
|||
} |
|||
|
|||
public async Task Invoke(HttpContext context) |
|||
{ |
|||
if (CanServeRequest(context.Request)) |
|||
{ |
|||
using (var cts = new CancellationTokenSource(Timeout)) |
|||
{ |
|||
var matchingChecks = healthChecks.Where(x => CanUseCheck(x.Value)); |
|||
|
|||
var results = await Task.WhenAll(matchingChecks.Select(x => MakeHealthCheckAsync(x.Key, x.Value, cts.Token))); |
|||
|
|||
context.Response.StatusCode = 200; |
|||
context.Response.Headers.Add("Content-Type", "application/json"); |
|||
|
|||
if (results.Any(x => !x.Result.IsHealthy)) |
|||
{ |
|||
context.Response.StatusCode = 503; |
|||
} |
|||
|
|||
var response = results.ToDictionary(x => x.Name, x => x.Result); |
|||
|
|||
var json = serializer.Serialize(new { status = response }); |
|||
|
|||
await context.Response.WriteAsync(json); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
await next(context); |
|||
} |
|||
} |
|||
|
|||
private bool CanUseCheck(IHealthCheck check) |
|||
{ |
|||
return scopes.Count == 0 || check.Scopes.Intersect(scopes).Any(); |
|||
} |
|||
|
|||
private bool CanServeRequest(HttpRequest request) |
|||
{ |
|||
return HttpMethods.IsGet(request.Method) && (request.Path == "/" || string.IsNullOrEmpty(request.Path)); |
|||
} |
|||
|
|||
private static List<string> SplitScopes(string scopes) |
|||
{ |
|||
return scopes.Split(",").Where(x => x != "*").ToList(); |
|||
} |
|||
|
|||
private static string GetName(IHealthCheck check) |
|||
{ |
|||
var name = check.GetType().Name.ToCamelCase(); |
|||
|
|||
if (name.EndsWith(Suffix, StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
name = name.Substring(0, name.Length - Suffix.Length); |
|||
} |
|||
|
|||
return name; |
|||
} |
|||
|
|||
private async Task<(string Name, HealthCheckResult Result)> MakeHealthCheckAsync(string name, IHealthCheck check, CancellationToken ct) |
|||
{ |
|||
try |
|||
{ |
|||
var result = await check.CheckHealthAsync(ct); |
|||
|
|||
return (name, result); |
|||
} |
|||
catch |
|||
{ |
|||
return (name, new HealthCheckResult(false)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<configuration> |
|||
<system.webServer> |
|||
<handlers> |
|||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" /> |
|||
</handlers> |
|||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" /> |
|||
</system.webServer> |
|||
</configuration> |
|||
Loading…
Reference in new issue