Browse Source

Fixes and simplified the angular integration. (#822)

* Fixes and simplified the angular integration.

* More fixes.
pull/823/head
Sebastian Stehle 4 years ago
committed by GitHub
parent
commit
4c31931851
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      backend/src/Squidex.Web/Pipeline/CachingManager.cs
  2. 56
      backend/src/Squidex/Areas/Frontend/Middlewares/IndexExtensions.cs
  3. 57
      backend/src/Squidex/Areas/Frontend/Middlewares/IndexMiddleware.cs
  4. 61
      backend/src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs
  5. 85
      backend/src/Squidex/Areas/Frontend/Startup.cs
  6. 4
      backend/src/Squidex/Areas/IdentityServer/Config/DynamicApplicationStore.cs
  7. 7
      backend/src/Squidex/Areas/IdentityServer/Controllers/Setup/SetupController.cs
  8. 75
      backend/src/Squidex/Areas/IdentityServer/Views/Setup/Webpack.cshtml
  9. 11
      backend/src/Squidex/Areas/IdentityServer/Views/_Layout.cshtml
  10. 13
      backend/src/Squidex/Squidex.csproj
  11. 1
      backend/src/Squidex/Startup.cs
  12. 15
      backend/src/Squidex/web.config
  13. 2
      backend/src/Squidex/wwwroot/client-callback-popup.html
  14. 2
      backend/src/Squidex/wwwroot/client-callback-silent.html
  15. 12
      frontend/.eslintrc.js
  16. 18
      frontend/app-config/helpers.js
  17. 275
      frontend/app-config/webpack.config.js
  18. 7
      frontend/app/index.html
  19. 4
      frontend/app/shared/services/auth.service.ts
  20. 193
      frontend/package-lock.json
  21. 5
      frontend/package.json

2
backend/src/Squidex.Web/Pipeline/CachingManager.cs

@ -146,7 +146,7 @@ namespace Squidex.Web.Pipeline
if (headers.Count > 0)
{
response.Headers.Add(HeaderNames.Vary, new StringValues(headers.ToArray()));
response.Headers[HeaderNames.Vary] = new StringValues(headers.ToArray());
}
}

56
backend/src/Squidex/Areas/Frontend/Middlewares/IndexExtensions.cs

@ -9,9 +9,11 @@ using System.Collections.Concurrent;
using System.Globalization;
using System.Net;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Squidex.Areas.Api.Controllers.UI;
using Squidex.Domain.Apps.Entities.History;
using Squidex.Infrastructure.Json;
using Squidex.Web;
namespace Squidex.Areas.Frontend.Middlewares
@ -19,64 +21,52 @@ namespace Squidex.Areas.Frontend.Middlewares
public static class IndexExtensions
{
private static readonly ConcurrentDictionary<string, string> Texts = new ConcurrentDictionary<string, string>();
public static bool IsIndex(this HttpContext context)
{
var path = context.Request.Path.Value;
return path == "/" || path?.EndsWith("/index.html", StringComparison.OrdinalIgnoreCase) == true;
}
public static bool IsHtmlPath(this HttpContext context)
private static readonly JsonSerializer JsonSerializer = JsonSerializer.CreateDefault(new JsonSerializerSettings
{
return context.Request.Path.Value?.EndsWith(".html", StringComparison.OrdinalIgnoreCase) == true;
}
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
public static bool IsNotModified(this HttpResponse response)
public static bool IsIndex(this HttpContext context)
{
return response.StatusCode == (int)HttpStatusCode.NotModified;
return context.Request.Path.StartsWithSegments("/index.html", StringComparison.OrdinalIgnoreCase);
}
public static string AdjustBase(this string html, HttpContext httpContext)
public static string AddOptions(this string html, HttpContext httpContext)
{
if (httpContext.Request.PathBase != null)
var scripts = new List<string>
{
html = html.Replace("<base href=\"/\">", $"<base href=\"{httpContext.Request.PathBase}/\">", StringComparison.OrdinalIgnoreCase);
}
$"var texts = {GetText(CultureInfo.CurrentUICulture.Name)};"
};
return html;
}
public static string AddOptions(this string html, HttpContext httpContext)
{
var uiOptions = httpContext.RequestServices.GetService<IOptions<MyUIOptions>>()?.Value;
if (uiOptions != null)
{
var json = JObject.FromObject(uiOptions, JsonSerializer);
var values = httpContext.RequestServices.GetService<ExposedValues>();
if (values != null)
{
uiOptions.More["info"] = values.ToString();
json["more"] ??= new JObject();
json["more"]!["info"] = values.ToString();
}
var notifo = httpContext.RequestServices!.GetRequiredService<IOptions<NotifoOptions>>();
var notifo = httpContext.RequestServices!.GetService<IOptions<NotifoOptions>>();
if (notifo.Value.IsConfigured())
if (notifo?.Value.IsConfigured() == true)
{
uiOptions.More["notifoApi"] = notifo.Value.ApiUrl;
json["more"] ??= new JObject();
json["more"]!["notifoApi"] = notifo.Value.ApiUrl;
}
uiOptions.More["culture"] = CultureInfo.CurrentUICulture.Name;
var jsonSerializer = httpContext.RequestServices.GetRequiredService<IJsonSerializer>();
var jsonOptions = jsonSerializer.Serialize(uiOptions, true);
var texts = GetText(CultureInfo.CurrentUICulture.Name);
html = html.Replace("<body>", $"<body>\n<script>\nvar options = {jsonOptions};\nvar texts = {texts};</script>", StringComparison.OrdinalIgnoreCase);
scripts.Add($"var options = {json.ToString(Formatting.Indented)};");
}
html = html.Replace("<body>", $"<body>\n<script>{string.Join(Environment.NewLine, scripts)}</script>", StringComparison.OrdinalIgnoreCase);
return html;
}

57
backend/src/Squidex/Areas/Frontend/Middlewares/IndexMiddleware.cs

@ -1,57 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Text;
namespace Squidex.Areas.Frontend.Middlewares
{
public sealed class IndexMiddleware
{
private readonly RequestDelegate next;
public IndexMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task InvokeAsync(HttpContext context)
{
if (context.IsHtmlPath() && !context.Response.IsNotModified())
{
var responseBuffer = new MemoryStream();
var responseBody = context.Response.Body;
context.Response.Body = responseBuffer;
await next(context);
if (!context.Response.IsNotModified())
{
context.Response.Body = responseBody;
var html = Encoding.UTF8.GetString(responseBuffer.ToArray());
html = html.AdjustBase(context);
if (context.IsIndex())
{
html = html.AddOptions(context);
}
context.Response.ContentLength = Encoding.UTF8.GetByteCount(html);
context.Response.Body = responseBody;
await context.Response.WriteAsync(html, context.RequestAborted);
}
}
else
{
await next(context);
}
}
}
}

61
backend/src/Squidex/Areas/Frontend/Middlewares/WebpackMiddleware.cs

@ -1,61 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Areas.Frontend.Middlewares
{
public sealed class WebpackMiddleware
{
private const string WebpackUrl = "https://localhost:3000/index.html";
private readonly RequestDelegate next;
public WebpackMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task InvokeAsync(HttpContext context)
{
if (context.IsIndex() && !context.Response.IsNotModified())
{
try
{
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true
};
using (var client = new HttpClient(handler))
{
var result = await client.GetAsync(WebpackUrl, context.RequestAborted);
context.Response.StatusCode = (int)result.StatusCode;
if (result.IsSuccessStatusCode)
{
var html = await result.Content.ReadAsStringAsync(context.RequestAborted);
html = html.AdjustBase(context);
await context.Response.WriteAsync(html, context.RequestAborted);
}
}
}
catch
{
context.Request.Path = "/identity-server/webpack";
await next(context);
}
}
else
{
await next(context);
}
}
}
}

85
backend/src/Squidex/Areas/Frontend/Startup.cs

@ -5,8 +5,10 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Microsoft.Extensions.FileProviders;
using Microsoft.Net.Http.Headers;
using Squidex.Areas.Frontend.Middlewares;
using Squidex.Hosting.Web;
using Squidex.Pipeline.Squid;
using Squidex.Web.Pipeline;
@ -18,74 +20,73 @@ namespace Squidex.Areas.Frontend
{
var environment = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>();
app.Map("/squid.svg", builder => builder.UseMiddleware<SquidMiddleware>());
var fileProvider = environment.WebRootFileProvider;
app.UseMiddleware<NotifoMiddleware>();
var indexFile =
environment.IsProduction() ?
new PathString("/build/index.html") :
new PathString("/index.html");
app.Use((context, next) =>
if (environment.IsProduction())
{
if (context.Request.Path == "/client-callback-popup")
{
context.Request.Path = new PathString("/client-callback-popup.html");
}
else if (context.Request.Path == "/client-callback-silent")
{
context.Request.Path = new PathString("/client-callback-silent.html");
}
else if (!Path.HasExtension(context.Request.Path.Value))
fileProvider = new CompositeFileProvider(fileProvider,
new PhysicalFileProvider(Path.Combine(environment.WebRootPath, "build")));
app.Use((context, next) =>
{
context.Request.Path = indexFile;
}
if (!Path.HasExtension(context.Request.Path.Value))
{
context.Request.Path = new PathString("/index.html");
}
return next();
return next();
});
}
app.Map("/squid.svg", builder =>
{
builder.UseMiddleware<SquidMiddleware>();
});
app.UseWhen(x => x.Request.Path.StartsWithSegments(indexFile, StringComparison.Ordinal), builder =>
app.UseMiddleware<NotifoMiddleware>();
app.UseWhen(x => x.IsIndex(), builder =>
{
builder.UseMiddleware<SetupMiddleware>();
});
app.UseMiddleware<IndexMiddleware>();
app.UseHtmlTransform(new HtmlTransformOptions
{
Transform = (html, context) =>
{
if (context.IsIndex())
{
html = html.AddOptions(context);
}
app.ConfigureDev();
return new ValueTask<string>(html);
}
});
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = context =>
{
var response = context.Context.Response;
var responseHeaders = response.GetTypedHeaders();
if (!string.Equals(response.ContentType, "text/html", StringComparison.OrdinalIgnoreCase))
if (!string.IsNullOrWhiteSpace(context.Context.Request.QueryString.ToString()))
{
responseHeaders.CacheControl = new CacheControlHeaderValue
{
MaxAge = TimeSpan.FromDays(60)
};
response.Headers[HeaderNames.CacheControl] = "max-age=5184000";
}
else
else if (string.Equals(response.ContentType, "text/html", StringComparison.OrdinalIgnoreCase))
{
responseHeaders.CacheControl = new CacheControlHeaderValue
{
NoCache = true
};
response.Headers[HeaderNames.CacheControl] = "no-cache";
}
}
},
FileProvider = fileProvider
});
}
public static void ConfigureDev(this IApplicationBuilder app)
{
var environment = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>();
if (environment.IsDevelopment())
{
app.UseMiddleware<WebpackMiddleware>();
app.UseSpa(builder =>
{
builder.UseProxyToSpaDevelopmentServer("https://localhost:3000");
});
}
}
}

4
backend/src/Squidex/Areas/IdentityServer/Config/DynamicApplicationStore.cs

@ -155,8 +155,8 @@ namespace Squidex.Areas.IdentityServer.Config
RedirectUris =
{
new Uri(urlGenerator.BuildUrl("login;")),
new Uri(urlGenerator.BuildUrl("client-callback-silent", false)),
new Uri(urlGenerator.BuildUrl("client-callback-popup", false))
new Uri(urlGenerator.BuildUrl("client-callback-silent.html", false)),
new Uri(urlGenerator.BuildUrl("client-callback-popup.html", false))
},
PostLogoutRedirectUris =
{

7
backend/src/Squidex/Areas/IdentityServer/Controllers/Setup/SetupController.cs

@ -43,13 +43,6 @@ namespace Squidex.Areas.IdentityServer.Controllers.Setup
this.userService = userService;
}
[HttpGet]
[Route("webpack/")]
public IActionResult Webpack()
{
return View();
}
[HttpGet]
[Route("setup/")]
public async Task<IActionResult> Setup()

75
backend/src/Squidex/Areas/IdentityServer/Views/Setup/Webpack.cshtml

@ -1,75 +0,0 @@
@{
ViewBag.Title = T.Get("setup.webpack.title");
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<base href="/">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@ViewBag.Title - @T.Get("common.product")</title>
<link href="@Url.RootContentUrl("~/styles/bootstrap.min.css")" rel="stylesheet">
<style>
body {
background: #f5f6f9;
padding-left: 0;
padding-top: 3.75rem;
}
.profile-container {
margin: 0 auto;
max-width: 40rem;
min-width: 10rem;
padding: 1rem 2rem;
}
.profile-logo {
height: 1.75rem;
position: absolute;
padding-left: 0;
padding-right: 0;
right: 1rem;
top: 1rem;
}
.profile-footer {
font-size: .8rem;
font-weight: normal;
margin-top: 2rem;
}
.card-body {
text-align: center;
}
</style>
</head>
<body class="profile">
<div class="profile-container">
<img class="profile-logo" alt="@T.Get("common.product")S" title="@T.Get("common.product")" src="@Url.RootContentUrl("~/images/logo.svg")" />
<div class="profile-card card">
<div class="profile-card-body card-body">
<img class="splash-image" src="@Url.RootContentUrl("~/squid.svg?title=OH%20DAMN&text=Please%20run%20webpack")" />
<h1 class="splash-h1">@T.Get("setup.webpack.headline")</h1>
<p>@T.Get("setup.webpack.text")</p>
<a href="https://docs.squidex.io/01-getting-started/contributing-and-developing/developing" target="_blank">@T.Get("common.documentation")</a>
</div>
</div>
<div class="profile-footer text-center mt-4 mb-2">
<small class="text-muted">
@T.Get("setup.madeBy")<br />@T.Get("setup.madeByCopyright")
</small>
</div>
</div>
</body>
</html>

11
backend/src/Squidex/Areas/IdentityServer/Views/_Layout.cshtml

@ -8,9 +8,7 @@
<title>@ViewBag.Title - @T.Get("common.product")</title>
<environment names="Production">
<link rel="stylesheet" asp-append-version="true" href="@Url.RootContentUrl("~/build/app.css")" />
</environment>
<link rel="stylesheet" asp-append-version="true" href="@Url.RootContentUrl("~/app.css")" />
@if (IsSectionDefined("header"))
{
@ -19,7 +17,7 @@
</head>
<body class="profile">
<div class="profile-container">
<img class="profile-logo" alt="@T.Get("common.product")S" title="@T.Get("common.product")" src="@Url.RootContentUrl("~/images/logo.svg")" />
<img class="profile-logo" alt="@T.Get("common.product")" title="@T.Get("common.product")" src="@Url.RootContentUrl("~/images/logo.svg")" />
<div class="profile-card card">
<div class="profile-card-body card-body">
@ -33,10 +31,5 @@
</small>
</div>
</div>
<environment names="Development">
<script type="text/javascript" src="https://localhost:3000/shims.js"></script>
<script type="text/javascript" src="https://localhost:3000/app.js"></script>
</environment>
</body>
</html>

13
backend/src/Squidex/Squidex.csproj

@ -48,6 +48,7 @@
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.1" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.RulesetToEditorconfigConverter" Version="3.3.3" />
<PackageReference Include="Microsoft.Data.Edm" Version="5.8.5" />
@ -79,18 +80,23 @@
<PackageReference Include="Squidex.Assets.S3" Version="2.6.0" />
<PackageReference Include="Squidex.Caching.Orleans" Version="1.8.0" />
<PackageReference Include="Squidex.ClientLibrary" Version="7.7.0" />
<PackageReference Include="Squidex.Hosting" Version="2.4.0" />
<PackageReference Include="Squidex.Hosting" Version="2.8.0" />
<PackageReference Include="Squidex.OpenIddict.MongoDb" Version="4.0.1-dev" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
<PackageReference Include="System.Linq" Version="4.3.0" />
<PackageReference Include="System.Runtime" Version="4.3.1" />
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="6.0.0" GeneratePathProperty="true" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<Target Name="CopySystemSecurityCryptographyPkcs" AfterTargets="Build">
<Copy SourceFiles="$(PkgSystem_Security_Cryptography_Pkcs)\lib\net6.0\System.Security.Cryptography.Pkcs.dll" DestinationFolder="$(OutDir)" />
</Target>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
@ -126,6 +132,7 @@
<ItemGroup>
<Folder Include="Areas\Frontend\Resources\" />
<Folder Include="wwwroot\build\" />
</ItemGroup>
<ItemGroup>
@ -156,6 +163,4 @@
<PropertyGroup>
<NoWarn>$(NoWarn);CS1591;1591;1573;1572;NU1605;IDE0060</NoWarn>
</PropertyGroup>
<ProjectExtensions><VisualStudio><UserProperties appsettings_1json__JsonSchema="https://json.schemastore.org/bamboo-spec.json" /></VisualStudio></ProjectExtensions>
</Project>

1
backend/src/Squidex/Startup.cs

@ -90,7 +90,6 @@ namespace Squidex
app.UseSquidexLocalCache();
app.UseSquidexCors();
app.ConfigureDev();
app.ConfigureApi();
app.ConfigurePortal();
app.ConfigureOrleansDashboard();

15
backend/src/Squidex/web.config

@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="bin\Debug\net5.0\Squidex.exe" arguments="" stdoutLogEnabled="false" hostingModel="InProcess" />
</system.webServer>
</location>
<system.webServer>
<handlers>
<remove name="aspNetCore" />
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\bin\Debug\net6.0\Squidex.dll" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
</system.webServer>
</configuration>

2
backend/src/Squidex/wwwroot/client-callback-popup.html

@ -4,7 +4,7 @@
<base href="/">
</head>
<body>
<script src="./scripts/oidc-client.min.js"></script>
<script src="scripts/oidc-client.min.js"></script>
<script>
Oidc.Log.logger = console;
Oidc.Log.logLevel = Oidc.Log.INFO;

2
backend/src/Squidex/wwwroot/client-callback-silent.html

@ -4,7 +4,7 @@
<base href="/">
</head>
<body>
<script src="./scripts/oidc-client.min.js"></script>
<script src="scripts/oidc-client.min.js"></script>
<script>
Oidc.Log.logger = console;
Oidc.Log.logLevel = Oidc.Log.INFO;

12
frontend/.eslintrc.js

@ -73,20 +73,16 @@ module.exports = {
"error",
"always"
],
"import/extensions": [
"error",
"never"
],
"import/extensions": "off",
"import/no-extraneous-dependencies": "off",
"import/no-useless-path-segments": "off",
"import/prefer-default-export": "off",
"arrow-body-style": "off",
"arrow-parens": "off",
"class-methods-use-this": "off",
"default-case": "off",
"function-paren-newline": "off",
"implicit-arrow-linebreak": "off",
"import/extensions": "off",
"import/no-extraneous-dependencies": "off",
"import/no-useless-path-segments": "off",
"import/prefer-default-export": "off",
"linebreak-style": "off",
"max-classes-per-file": "off",
"max-len": "off",

18
frontend/app-config/helpers.js

@ -0,0 +1,18 @@
const path = require('path');
const appRoot = path.resolve(__dirname, '..');
function root() {
var newArgs = Array.prototype.slice.call(arguments, 0);
return path.join.apply(path, [appRoot].concat(newArgs));
}
function isDevServer() {
return path.basename(require.main.filename) === 'webpack-dev-server.js';
}
module.exports = {
root,
isDevServer,
};

275
frontend/app-config/webpack.config.js

@ -1,19 +1,7 @@
/* eslint-disable no-useless-escape */
/* eslint-disable global-require */
const webpack = require('webpack');
const path = require('path');
const appRoot = path.resolve(__dirname, '..');
function root() {
// eslint-disable-next-line prefer-rest-params
const newArgs = Array.prototype.slice.call(arguments, 0);
// eslint-disable-next-line prefer-spread
return path.join.apply(path, [appRoot].concat(newArgs));
}
const helpers = require('./helpers');
const plugins = {
// https://github.com/webpack-contrib/mini-css-extract-plugin
MiniCssExtractPlugin: require('mini-css-extract-plugin'),
@ -46,7 +34,7 @@ const plugins = {
module.exports = function calculateConfig(env) {
const isProduction = env && env.production;
const isAnalyzing = isProduction && env.analyze;
const isDevServer = env.WEBPACK_SERVE;
const isDevServer = helpers.isDevServer();
const isTestCoverage = env && env.coverage;
const isTests = env && env.target === 'tests';
@ -59,28 +47,49 @@ module.exports = function calculateConfig(env) {
mode: isProduction ? 'production' : 'development',
/*
* Source map for Karma from the help of karma-sourcemap-loader & karma-webpack.
*
* See: https://webpack.js.org/configuration/devtool/
*/
* Source map for Karma from the help of karma-sourcemap-loader & karma-webpack.
*
* See: https://webpack.js.org/configuration/devtool/
*/
devtool: isProduction ? false : 'inline-source-map',
output: {
/*
* The output directory as absolute path (required).
*
* See: https://webpack.js.org/configuration/output/#output-path
*/
path: helpers.root('/build/'),
/*
* The filename of non-entry chunks as relative path inside the output.path directory.
*
* See: https://webpack.js.org/configuration/output/#output-chunkfilename
*/
chunkFilename: '[id].[fullhash].chunk.js',
/*
* The filename for assets.
*/
assetModuleFilename: 'assets/[hash][ext][query]',
},
/*
* Options affecting the resolving of modules.
*
* See: https://webpack.js.org/configuration/resolve/
*/
* Options affecting the resolving of modules.
*
* See: https://webpack.js.org/configuration/resolve/
*/
resolve: {
/*
* An array of extensions that should be used to resolve modules.
*
* See: https://webpack.js.org/configuration/resolve/#resolve-extensions
*/
* An array of extensions that should be used to resolve modules.
*
* See: https://webpack.js.org/configuration/resolve/#resolve-extensions
*/
extensions: ['.ts', '.js', '.mjs', '.css', '.scss'],
modules: [
root('app'),
root('app', 'theme'),
root('node_modules'),
helpers.root('app'),
helpers.root('app', 'theme'),
helpers.root('node_modules'),
],
plugins: [
@ -91,21 +100,17 @@ module.exports = function calculateConfig(env) {
},
/*
* Options affecting the normal modules.
*
* See: https://webpack.js.org/configuration/module/
*/
* Options affecting the normal modules.
*
* See: https://webpack.js.org/configuration/module/
*/
module: {
/*
* An array of Rules which are matched to requests when modules are created.
*
* See: https://webpack.js.org/configuration/module/#module-rules
*/
* An array of Rules which are matched to requests when modules are created.
*
* See: https://webpack.js.org/configuration/module/#module-rules
*/
rules: [{
test: /\.mjs$/,
type: 'javascript/auto',
include: [/node_modules/],
}, {
// Mark files inside `@angular/core` as using SystemJS style dynamic imports.
test: /[\/\\]@angular[\/\\]core[\/\\].+\.js$/,
parser: { system: true },
@ -152,15 +157,35 @@ module.exports = function calculateConfig(env) {
loader: 'sass-loader',
options: {
additionalData: `
@import '_vars';
@import '_mixins';
`,
@import '_vars';
@import '_mixins';
`,
sassOptions: {
includePaths: [root('app', 'theme')],
includePaths: [helpers.root('app', 'theme')],
},
},
}],
exclude: root('app', 'theme'),
exclude: helpers.root('app', 'theme'),
}, {
test: /\.scss$/,
/*
* Extract the content from a bundle to a file.
*
* See: https://github.com/webpack-contrib/extract-text-webpack-plugin
*/
use: [
plugins.MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
}, {
loader: 'postcss-loader',
}, {
loader: 'sass-loader',
}],
/*
* Do not include component styles.
*/
include: helpers.root('app', 'theme'),
}],
},
@ -174,11 +199,11 @@ module.exports = function calculateConfig(env) {
}),
/*
* Always replace the context for the System.import in angular/core to prevent warnings.
*/
* Always replace the context for the System.import in angular/core to prevent warnings.
*/
new webpack.ContextReplacementPlugin(
/\@angular(\\|\/)core(\\|\/)/,
root('./app', '$_lazy_route_resources'),
helpers.root('./app', '$_lazy_route_resources'),
{},
),
@ -192,10 +217,10 @@ module.exports = function calculateConfig(env) {
}),
/*
* Puts each bundle into a file and appends the hash of the file to the path.
*
* See: https://github.com/webpack-contrib/mini-css-extract-plugin
*/
* Puts each bundle into a file and appends the hash of the file to the path.
*
* See: https://github.com/webpack-contrib/mini-css-extract-plugin
*/
new plugins.MiniCssExtractPlugin({
filename: '[name].css',
}),
@ -204,11 +229,11 @@ module.exports = function calculateConfig(env) {
options: {
htmlLoader: {
/*
* Define the root for images, so that we can use absolute urls.
*
* See: https://github.com/webpack/html-loader#Advanced_Options
*/
root: root('app', 'images'),
* Define the root for images, so that we can use absolute urls.
*
* See: https://github.com/webpack/html-loader#Advanced_Options
*/
root: helpers.root('app', 'images'),
},
context: '/',
},
@ -219,10 +244,10 @@ module.exports = function calculateConfig(env) {
}),
/*
* Detect circular dependencies in app.
*
* See: https://github.com/aackerman/circular-dependency-plugin
*/
* Detect circular dependencies in app.
*
* See: https://github.com/aackerman/circular-dependency-plugin
*/
new plugins.CircularDependencyPlugin({
exclude: /([\\\/]node_modules[\\\/])|(ngfactory\.js$)/,
// Add errors to webpack instead of warnings
@ -230,8 +255,8 @@ module.exports = function calculateConfig(env) {
}),
/*
* Copy lazy loaded libraries to output.
*/
* Copy lazy loaded libraries to output.
*/
new plugins.CopyPlugin({
patterns: [
{ from: './node_modules/simplemde/dist', to: 'dependencies/simplemde' },
@ -279,70 +304,30 @@ module.exports = function calculateConfig(env) {
headers: {
'Access-Control-Allow-Origin': '*',
},
historyApiFallback: true,
},
};
if (!isTests) {
/*
* The entry point for the bundle. Our Angular app.
*
* See: https://webpack.js.org/configuration/entry-context/
*/
* The entry point for the bundle. Our Angular app.
*
* See: https://webpack.js.org/configuration/entry-context/
*/
config.entry = {
shims: './app/shims.ts',
style: './app/style.js',
app: './app/app.ts',
app: './app/app.ts',
};
if (isProduction) {
config.output = {
/*
* The output directory as absolute path (required).
*
* See: https://webpack.js.org/configuration/output/#output-path
*/
path: root('/build/'),
publicPath: './build/',
/*
* Specifies the name of each output file on disk.
*
* See: https://webpack.js.org/configuration/output/#output-filename
*/
filename: '[name].js',
/*
* The filename of non-entry chunks as relative path inside the output.path directory.
*
* See: https://webpack.js.org/configuration/output/#output-chunkfilename
*/
chunkFilename: '[id].[fullhash].chunk.js',
/*
* The filename for assets.
*/
assetModuleFilename: 'assets/[hash][ext][query]',
};
} else {
config.output = {
filename: '[name].js',
/*
* Set the public path, because we are running the website from another port (5000).
*/
publicPath: 'https://localhost:3000/',
};
}
config.plugins.push(
new plugins.HtmlWebpackPlugin({
filename: 'index.html',
hash: true,
chunks: ['shims', 'app'],
chunksSortMode: 'manual',
template: root('app', 'index.html'),
template: helpers.root('app', 'index.html'),
}),
);
@ -352,10 +337,17 @@ module.exports = function calculateConfig(env) {
hash: true,
chunks: ['style'],
chunksSortMode: 'none',
template: root('app', '_theme.html'),
template: helpers.root('app', '_theme.html'),
}),
);
/*
* Specifies the name of each output file on disk.
*
* See: https://webpack.js.org/configuration/output/#output-filename
*/
config.output.filename = '[name].js';
if (isProduction) {
config.plugins.push(
new plugins.ESLintPlugin({
@ -398,6 +390,23 @@ module.exports = function calculateConfig(env) {
},
}],
});
config.module.rules.push({
test: /\.m?js$/,
use: [{
loader: 'babel-loader',
options: {
plugins: ['@angular/compiler-cli/linker/babel'],
compact: false,
cacheDirectory: true,
},
}],
});
} else {
config.module.rules.push({
test: /\.mjs$/,
type: 'javascript/auto',
include: [/node_modules/],
});
}
if (isTestCoverage) {
@ -432,48 +441,6 @@ module.exports = function calculateConfig(env) {
});
}
if (isProduction) {
config.module.rules.push({
test: /\.scss$/,
/*
* Extract the content from a bundle to a file.
*
* See: https://github.com/webpack-contrib/extract-text-webpack-plugin
*/
use: [
plugins.MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
}, {
loader: 'postcss-loader',
}, {
loader: 'sass-loader',
}],
/*
* Do not include component styles.
*/
include: root('app', 'theme'),
});
} else {
config.module.rules.push({
test: /\.scss$/,
use: [{
loader: 'style-loader',
}, {
loader: 'css-loader',
}, {
loader: 'postcss-loader',
}, {
loader: 'sass-loader',
options: {
sourceMap: true,
},
}],
// Do not include component styles.
include: root('app', 'theme'),
});
}
if (isAnalyzing) {
config.plugins.push(new plugins.BundleAnalyzerPlugin());
}

7
frontend/app/index.html

@ -20,9 +20,9 @@
</style>
</noscript>
<link rel="stylesheet" type="text/css" href="./scripts/outdatedbrowser/outdatedbrowser.min.css">
<link rel="stylesheet" type="text/css" href="scripts/outdatedbrowser/outdatedbrowser.min.css">
<script src="./scripts/outdatedbrowser/outdatedbrowser.min.js"></script>
<script src="scripts/outdatedbrowser/outdatedbrowser.min.js"></script>
<script>
function addLoadEvent(callback) {
var oldOnload = window.onload;
@ -42,7 +42,8 @@
addLoadEvent(function () {
outdatedBrowser({
lowerThan: 'object-fit',
languagePath: './scripts/outdatedbrowser/lang/en.html'
language: 'en',
languagePath: 'scripts/outdatedbrowser/lang/en.html'
});
});
</script>

4
frontend/app/shared/services/auth.service.ts

@ -102,8 +102,8 @@ export class AuthService {
response_type: 'code',
redirect_uri: apiUrl.buildUrl('login;'),
post_logout_redirect_uri: apiUrl.buildUrl('logout'),
silent_redirect_uri: apiUrl.buildUrl('client-callback-silent'),
popup_redirect_uri: apiUrl.buildUrl('client-callback-popup'),
silent_redirect_uri: apiUrl.buildUrl('client-callback-silent.html'),
popup_redirect_uri: apiUrl.buildUrl('client-callback-popup.html'),
authority: apiUrl.buildUrl('identity-server/'),
userStore: new WebStorageStateStore({ store: window.localStorage || window.sessionStorage }),
automaticSilentRenew: true,

193
frontend/package-lock.json

@ -66,7 +66,7 @@
"@angular-devkit/build-optimizer": "0.1301.2",
"@angular/cli": "^13.1.2",
"@angular/compiler": "13.1.1",
"@angular/compiler-cli": "13.1.1",
"@angular/compiler-cli": "^13.1.1",
"@jsdevtools/coverage-istanbul-loader": "^3.0.5",
"@ngtools/webpack": "13.1.2",
"@types/codemirror": "0.0.108",
@ -84,6 +84,7 @@
"@types/ws": "8.2.2",
"@typescript-eslint/eslint-plugin": "5.8.1",
"@typescript-eslint/parser": "5.8.1",
"babel-loader": "^8.2.3",
"browserslist": "4.19.1",
"caniuse-lite": "1.0.30001294",
"circular-dependency-plugin": "5.2.2",
@ -3079,6 +3080,69 @@
"ast-types-flow": "0.0.7"
}
},
"node_modules/babel-loader": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz",
"integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==",
"dev": true,
"dependencies": {
"find-cache-dir": "^3.3.1",
"loader-utils": "^1.4.0",
"make-dir": "^3.1.0",
"schema-utils": "^2.6.5"
},
"engines": {
"node": ">= 8.9"
},
"peerDependencies": {
"@babel/core": "^7.0.0",
"webpack": ">=2"
}
},
"node_modules/babel-loader/node_modules/json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"dependencies": {
"minimist": "^1.2.0"
},
"bin": {
"json5": "lib/cli.js"
}
},
"node_modules/babel-loader/node_modules/loader-utils": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
"dev": true,
"dependencies": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^1.0.1"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/babel-loader/node_modules/schema-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
"integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.5",
"ajv": "^6.12.4",
"ajv-keywords": "^3.5.2"
},
"engines": {
"node": ">= 8.9.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/babel-polyfill": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
@ -3851,6 +3915,12 @@
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
"dev": true
},
"node_modules/component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
@ -6360,6 +6430,23 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"node_modules/find-cache-dir": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
"integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
"dev": true,
"dependencies": {
"commondir": "^1.0.1",
"make-dir": "^3.0.2",
"pkg-dir": "^4.1.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/avajs/find-cache-dir?sponsor=1"
}
},
"node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
@ -10409,6 +10496,18 @@
"pkcs7": "bin/cli.js"
}
},
"node_modules/pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
"dev": true,
"dependencies": {
"find-up": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/portfinder": {
"version": "1.0.28",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
@ -14609,18 +14708,6 @@
"node": ">=8"
}
},
"node_modules/webpack-cli/node_modules/pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
"dev": true,
"dependencies": {
"find-up": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/webpack-cli/node_modules/resolve-cwd": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
@ -17412,6 +17499,51 @@
"ast-types-flow": "0.0.7"
}
},
"babel-loader": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz",
"integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==",
"dev": true,
"requires": {
"find-cache-dir": "^3.3.1",
"loader-utils": "^1.4.0",
"make-dir": "^3.1.0",
"schema-utils": "^2.6.5"
},
"dependencies": {
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
},
"loader-utils": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^1.0.1"
}
},
"schema-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
"integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.5",
"ajv": "^6.12.4",
"ajv-keywords": "^3.5.2"
}
}
}
},
"babel-polyfill": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
@ -18030,6 +18162,12 @@
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
"commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
"dev": true
},
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
@ -19957,6 +20095,17 @@
}
}
},
"find-cache-dir": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
"integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
"dev": true,
"requires": {
"commondir": "^1.0.1",
"make-dir": "^3.0.2",
"pkg-dir": "^4.1.0"
}
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
@ -22970,6 +23119,15 @@
"@babel/runtime": "^7.5.5"
}
},
"pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
"dev": true,
"requires": {
"find-up": "^4.0.0"
}
},
"portfinder": {
"version": "1.0.28",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
@ -26012,15 +26170,6 @@
"path-key": "^3.0.0"
}
},
"pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
"dev": true,
"requires": {
"find-up": "^4.0.0"
}
},
"resolve-cwd": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",

5
frontend/package.json

@ -5,7 +5,7 @@
"license": "MIT",
"repository": "https://github.com/SebastianStehle/Squidex",
"scripts": {
"start": "webpack serve --config app-config/webpack.config.js --port 3000 --hot --server-type https --https-pfx ../dev/squidex-dev.pfx --https-passphrase password --env WEBPACK_SERVE",
"start": "webpack serve --config app-config/webpack.config.js --port 3000 --hot --server-type https --server-options-pfx ../dev/squidex-dev.pfx --server-options-passphrase password --env WEBPACK_SERVE",
"test": "karma start",
"test:coverage": "karma start karma.coverage.conf.js",
"test:clean": "rimraf _test-output",
@ -74,7 +74,7 @@
"@angular-devkit/build-optimizer": "0.1301.2",
"@angular/cli": "^13.1.2",
"@angular/compiler": "13.1.1",
"@angular/compiler-cli": "13.1.1",
"@angular/compiler-cli": "^13.1.1",
"@jsdevtools/coverage-istanbul-loader": "^3.0.5",
"@ngtools/webpack": "13.1.2",
"@types/codemirror": "0.0.108",
@ -92,6 +92,7 @@
"@types/ws": "8.2.2",
"@typescript-eslint/eslint-plugin": "5.8.1",
"@typescript-eslint/parser": "5.8.1",
"babel-loader": "^8.2.3",
"browserslist": "4.19.1",
"caniuse-lite": "1.0.30001294",
"circular-dependency-plugin": "5.2.2",

Loading…
Cancel
Save