mirror of
https://github.com/LD-Reborn/Berufsschule_HAM.git
synced 2026-05-06 11:32:19 +00:00
Merge pull request #334 from LD-Reborn/331-bug-image-elements-do-not-have-explicit-width-and-height-lighthouse-warning
331 bug image elements do not have explicit width and height lighthouse warning
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+5
-1
@@ -6,6 +6,7 @@ using Berufsschule_HAM.Services;
|
|||||||
using Berufsschule_HAM.Models;
|
using Berufsschule_HAM.Models;
|
||||||
using Berufsschule_HAM.HealthChecks;
|
using Berufsschule_HAM.HealthChecks;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.AspNetCore.ResponseCompression;
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Bind options
|
// Bind options
|
||||||
@@ -50,11 +51,14 @@ builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationSc
|
|||||||
builder.Services.AddResponseCompression(options =>
|
builder.Services.AddResponseCompression(options =>
|
||||||
{
|
{
|
||||||
options.EnableForHttps = true;
|
options.EnableForHttps = true;
|
||||||
|
options.Providers.Add<GzipCompressionProvider>();
|
||||||
|
options.Providers.Add<BrotliCompressionProvider>();
|
||||||
options.MimeTypes =
|
options.MimeTypes =
|
||||||
[
|
[
|
||||||
"text/plain",
|
"text/plain",
|
||||||
"text/css",
|
"text/css",
|
||||||
"application/javascript",
|
"application/javascript",
|
||||||
|
"text/javascript",
|
||||||
"text/html",
|
"text/html",
|
||||||
"application/xml",
|
"application/xml",
|
||||||
"text/xml",
|
"text/xml",
|
||||||
@@ -91,6 +95,7 @@ if (app.Environment.IsDevelopment())
|
|||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.UseResponseCompression();
|
||||||
app.UseStaticFiles(new StaticFileOptions
|
app.UseStaticFiles(new StaticFileOptions
|
||||||
{
|
{
|
||||||
OnPrepareResponse = ctx =>
|
OnPrepareResponse = ctx =>
|
||||||
@@ -118,7 +123,6 @@ app.UseStaticFiles(new StaticFileOptions
|
|||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
app.UseResponseCaching();
|
app.UseResponseCaching();
|
||||||
app.UseResponseCompression();
|
|
||||||
|
|
||||||
string[] supportedCultures = ["de", "en"];
|
string[] supportedCultures = ["de", "en"];
|
||||||
var localizationOptions = new RequestLocalizationOptions()
|
var localizationOptions = new RequestLocalizationOptions()
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
<form id="updateSettings" style="margin-bottom: 4rem !important" method="post" asp-controller="Settings" asp-action="Admin">
|
<form id="updateSettings" style="margin-bottom: 4rem !important" method="post" asp-controller="Settings" asp-action="Admin">
|
||||||
|
<h4 class="fw-bold">@T["General settings"]</h4>
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<h4 class="fw-bold">@T["General settings"]</h4>
|
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label class="form-label" for="updateHashAlgorithm">@T["Default hash algorithm"]</label>
|
<label class="form-label" for="updateHashAlgorithm">@T["Default hash algorithm"]</label>
|
||||||
<select type="text" name="DefaultHashAlgorithm" id="updateHashAlgorithm" class="form-control">
|
<select type="text" name="DefaultHashAlgorithm" id="updateHashAlgorithm" class="form-control">
|
||||||
|
|||||||
@@ -12,14 +12,26 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>@ViewData["Title"] - Berufsschule_HAM</title>
|
<title>@ViewData["Title"] - Berufsschule_HAM</title>
|
||||||
<script type="importmap"></script>
|
<script type="importmap"></script>
|
||||||
@* <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" asp-append-version="true"/> *@
|
<style>
|
||||||
<link rel="stylesheet"
|
@if (Context.Request.Path.Value is not null)
|
||||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
|
{
|
||||||
crossorigin="anonymous">
|
string path = System.IO.Path.Combine("CriticalCSS", Context.Request.Path.Value.Trim('/').Replace("/", ".") + ".css");
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
@Html.Raw(File.ReadAllText(path));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@Html.Raw(File.ReadAllText("CriticalCSS/_Layout.css"));
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link rel="preload" href="~/lib/bootstrap/dist/css/bootstrap.min.css" as="style"/>
|
||||||
|
<link rel="stylesheet" fetchpriority="high"
|
||||||
|
href="~/lib/bootstrap/dist/css/bootstrap.min.css"
|
||||||
|
media="print"
|
||||||
|
onload="this.media='all'">
|
||||||
@* <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" /> *@
|
@* <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" /> *@
|
||||||
<link rel="preload" href="~/css/site.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
<link rel="preload" href="~/css/site.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
||||||
<noscript><link rel="stylesheet" href="~/css/site.css"></noscript>
|
<noscript><link fetchpriority="high" rel="stylesheet" href="~/css/site.css"></noscript>
|
||||||
<link rel="stylesheet" href="~/Berufsschule_HAM.styles.css" asp-append-version="true" />
|
|
||||||
<script>
|
<script>
|
||||||
window.appTranslations = {
|
window.appTranslations = {
|
||||||
selectLocation: '@T["Select location"]',
|
selectLocation: '@T["Select location"]',
|
||||||
@@ -38,9 +50,9 @@
|
|||||||
</script>
|
</script>
|
||||||
<header>
|
<header>
|
||||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar bg border-bottom box-shadow mb-3">
|
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar bg border-bottom box-shadow mb-3">
|
||||||
<a href="#main-content" class="skip-link btn btn-primary">@T["Jump to content"]</a>
|
<a href="#main-content" class="skip-link btn btn-primary" style="position: fixed; left: -10000px; z-index: 1000;">@T["Jump to content"]</a>
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="" asp-area="" asp-controller="Home" asp-action="Index"><img fetchpriority="high" src="/HAM_Banner_xs.png" alt="Logo" style="max-height: 40px; width: 123px;"></a>
|
<a class="" asp-area="" asp-controller="Home" asp-action="Index"><img fetchpriority="high" src="/HAM_Banner_xs.png" alt="Logo" width="123" height="40" style="width: 123px; height: 40px;"></a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
||||||
aria-expanded="false" aria-label="Toggle navigation">
|
aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
|||||||
@@ -46,14 +46,3 @@ button.accept-policy {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
line-height: 60px;
|
line-height: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skip-link {
|
|
||||||
position: fixed;
|
|
||||||
left: -10000px;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
.skip-link:focus {
|
|
||||||
left: 10px;
|
|
||||||
top: 10px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|||||||
+164
@@ -0,0 +1,164 @@
|
|||||||
|
import { generate } from 'critical';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import puppeteer from 'puppeteer';
|
||||||
|
|
||||||
|
const browser = await puppeteer.launch();
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
// Login
|
||||||
|
await page.goto('http://localhost:5275/Home/Login');
|
||||||
|
await page.type('#username', 'admin');
|
||||||
|
await page.type('#password', 'Test1234.');
|
||||||
|
await page.click('button[type=submit]');
|
||||||
|
await page.waitForNavigation();
|
||||||
|
|
||||||
|
// Extract cookies
|
||||||
|
const cookies = await page.cookies();
|
||||||
|
await browser.close();
|
||||||
|
|
||||||
|
async function generateCriticalCSSForViews() {
|
||||||
|
const viewsDir = 'Views';
|
||||||
|
|
||||||
|
// Helper function to get all .cshtml files recursively
|
||||||
|
function getAllCshtmlFiles(dir) {
|
||||||
|
let results = [];
|
||||||
|
const list = fs.readdirSync(dir);
|
||||||
|
|
||||||
|
list.forEach(file => {
|
||||||
|
const filePath = path.join(dir, file);
|
||||||
|
const stat = fs.statSync(filePath);
|
||||||
|
|
||||||
|
if (stat && stat.isDirectory()) {
|
||||||
|
// Recursively get files from subdirectories
|
||||||
|
results = results.concat(getAllCshtmlFiles(filePath));
|
||||||
|
} else if (file.endsWith('.cshtml')) {
|
||||||
|
results.push(filePath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to convert file path to URL path
|
||||||
|
function filePathToUrlPath(filePath) {
|
||||||
|
// Remove 'Views/' prefix
|
||||||
|
let relativePath = filePath.replace(/^Views[\/\\]/, '');
|
||||||
|
|
||||||
|
// Remove .cshtml extension
|
||||||
|
relativePath = relativePath.replace(/\.cshtml$/, '');
|
||||||
|
|
||||||
|
// Convert to URL format (replace \ with / and capitalize first letter)
|
||||||
|
const urlPath = relativePath
|
||||||
|
.split(/[\/\\]/)
|
||||||
|
.map((segment, index) =>
|
||||||
|
index === 0 ? segment : segment.charAt(0).toUpperCase() + segment.slice(1)
|
||||||
|
)
|
||||||
|
.join('/');
|
||||||
|
|
||||||
|
// Handle the case where we have a single file (like Index.cshtml)
|
||||||
|
if (relativePath.includes('/')) {
|
||||||
|
// Convert to URL path format: Views/Home/Index.cshtml -> /Home/Index
|
||||||
|
return '/' + relativePath.replace(/\\/g, '/').replace(/\.cshtml$/, '');
|
||||||
|
} else {
|
||||||
|
// For files directly in Views folder (like Views/Index.cshtml)
|
||||||
|
return '/' + relativePath.replace(/\.cshtml$/, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all .cshtml files
|
||||||
|
const cshtmlFiles = getAllCshtmlFiles(viewsDir);
|
||||||
|
|
||||||
|
// Create CriticalCSS directory if it doesn't exist
|
||||||
|
const criticalCssDir = 'CriticalCSS';
|
||||||
|
if (!fs.existsSync(criticalCssDir)) {
|
||||||
|
fs.mkdirSync(criticalCssDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process each file
|
||||||
|
for (const file of cshtmlFiles) {
|
||||||
|
try {
|
||||||
|
const urlPath = filePathToUrlPath(file);
|
||||||
|
|
||||||
|
// Generate critical CSS
|
||||||
|
await generate({
|
||||||
|
src: `http://localhost:5275${urlPath}`,
|
||||||
|
inline: false,
|
||||||
|
width: 1920,
|
||||||
|
height: 1080,
|
||||||
|
penthouse: {
|
||||||
|
customHeaders: {
|
||||||
|
cookie: cookies.map(c => `${c.name}=${c.value}`).join('; ')
|
||||||
|
},
|
||||||
|
forceInclude: [
|
||||||
|
'[data-bs-theme="dark"]',
|
||||||
|
'.navbar',
|
||||||
|
'.dropdown-menu',
|
||||||
|
'.me-2',
|
||||||
|
'.align-items-center',
|
||||||
|
'.d-flex',
|
||||||
|
'.position-fixed', // print batch
|
||||||
|
'.bottom-0',
|
||||||
|
'.start-0',
|
||||||
|
'.m-4',
|
||||||
|
'.row', // elements
|
||||||
|
'.row>*',
|
||||||
|
'.g-3',
|
||||||
|
'.col-md-3',
|
||||||
|
'.text-center',
|
||||||
|
'.mb-3',
|
||||||
|
'.mb-4',
|
||||||
|
'.mt-3',
|
||||||
|
'.py-4',
|
||||||
|
'.text-center',
|
||||||
|
'h2',
|
||||||
|
'.form-control',
|
||||||
|
'.form-control-sm',
|
||||||
|
'.modal',
|
||||||
|
'.btn',
|
||||||
|
'.btn-sm',
|
||||||
|
'.btn-secondary',
|
||||||
|
'.btn-warning',
|
||||||
|
'.btn-danger',
|
||||||
|
'.rounded-circle', // user icon
|
||||||
|
'table', // table elements (users)
|
||||||
|
'.table>thead',
|
||||||
|
'.user-row > td',
|
||||||
|
'thead',
|
||||||
|
'tbody',
|
||||||
|
'th',
|
||||||
|
'tr',
|
||||||
|
'td',
|
||||||
|
'.table>:not(caption)>*>*',
|
||||||
|
'.table-striped>tbody>tr:nth-of-type(odd)>*',
|
||||||
|
'.gap-2',
|
||||||
|
'.table',
|
||||||
|
'.table-responsive',
|
||||||
|
'.align-middle'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
css: path.join(criticalCssDir, urlPath.replace(/\//g, '.').replace(/^\./, '') + '.css')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Critical CSS generated for: ${urlPath}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error processing ${file}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('All critical CSS files generated!');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the function
|
||||||
|
generateCriticalCSSForViews().catch(console.error);
|
||||||
|
|
||||||
|
|
||||||
|
// How to run this:
|
||||||
|
// install dependencies:
|
||||||
|
// npm i -D critical
|
||||||
|
// npm install puppeteer
|
||||||
|
// run:
|
||||||
|
// node critical.js
|
||||||
@@ -72,3 +72,13 @@ h3.modal-title {
|
|||||||
h4.fw-bold, h4.card-title {
|
h4.fw-bold, h4.card-title {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table button {
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skip-link:focus {
|
||||||
|
left: 10px;
|
||||||
|
top: 10px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user