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" ?> |
<?xml version="1.0" encoding="utf-8"?> |
||||
<Weavers> |
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
<Freezable/> |
<Freezable /> |
||||
</Weavers> |
</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