Skip to content

Commit 3fc8d2e

Browse files
SonarQube Cloud Issues
1 parent 7cc6684 commit 3fc8d2e

13 files changed

Lines changed: 287 additions & 194 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ Architecture documentée en temps réel. **Compodoc** génère une documentation
110110

111111
🚦 **Toggle Maintenance :**
112112
Besoin de geler l'application en production pour une maintenance ? Un workflow **GitHub Actions** dédié agit comme un interrupteur (`toggle-maintenance.yml`). Depuis l'onglet `Actions` de **GitHub** un simple clic permet d'activer (🔴) ou de désactiver (🟢) la page de maintenance statique (**Erreur 503**). Pour cela le script se connecte en **SSH** pour piloter le fichier `maintenance.enable` intercepté par le `.htaccess`.
113-
✨ Un script ping le serveur et recharge automatiquement la page de l'utilisateur dès lors que le site revient en ligne (sans qu'ils n'aient besoin de rafraîchir manuellement !).
113+
Un script ping le serveur et recharge automatiquement la page de l'utilisateur dès lors que le site revient en ligne (sans qu'ils n'aient besoin de rafraîchir manuellement !). ✨✨✨
114+
115+
![Toggle Maintenance Mode](https://github.com/EmmanuelLefevre/MarkdownImg/blob/main/toggle_maintenance_mode.png)
114116

115117
## 📚 DOCUMENTATION
116118

public/maintenance.html

Lines changed: 136 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,147 @@
11
<!-- eslint-disable @angular-eslint/template/no-inline-styles -->
22

3-
<!DOCTYPE html>
3+
<!doctype html>
44
<html lang="fr">
5-
<head>
6-
<meta charset="utf-8" />
7-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
8-
9-
<base href="/" />
10-
11-
<title>AngularTemplate - En Maintenance</title>
12-
<meta name="description" content="Site web en maintenance, on revient vite." />
13-
<meta name="keywords" content="Angular 21, boilerplate, template, starter kit, PNPM, ESBuild, Vite, Vitest, Husky, lint-staged, ESLint, StyleLint, HTMLHint, SecretLint, Prettier, SonarQube Cloud, Snyk, CodeQL, GitLeaks, GitHub Actions, CI/CD, I18n, SEO, PWA, clean architecture, a11y, Compodoc, Font Awesome, Angular Material, Corepack, GitHub Pages, GitHub Packages, SEMVER,Changelog, design system, mobile-first, TypeScript, RXJS, Signals, NodeJS, Angular Schematics" />
14-
<meta name="robots" content="noindex, follow" />
15-
16-
<link rel="preconnect" href="https://fonts.googleapis.com" />
17-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
18-
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100&family=Roboto+Condensed:wght@700&family=Roboto:wght@400;500;600;700&display=swap" rel="stylesheet" />
19-
20-
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
21-
<link rel="stylesheet" href="/maintenance.css" />
22-
23-
<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>
24-
</head>
25-
<body>
26-
27-
<main class="error-view">
28-
29-
<h1 class="error-view__title" id="h1">
30-
Une erreur est survenue.
31-
</h1>
32-
33-
<h3 class="error-view__code" id="h3-wrapper">
34-
<span class="error-view__label" id="h3">Erreur</span>
35-
<span class="error-view__code-value">503</span>
36-
</h3>
37-
38-
<div class="error-view__content">
39-
<section class="maintenance-error">
40-
<div class="maintenance-error__content">
41-
42-
<div class="maintenance-error__animation-wrapper">
43-
<lottie-player
44-
src="/assets/animations/maintenance-error-under-construction.json"
45-
background="transparent"
46-
speed="1"
47-
style="width: 100%; height: 100%;"
48-
loop
49-
autoplay
50-
class="maintenance-error__lottie"
51-
aria-label="Une équipe technique s'affairant autour d'un écran en maintenance" />
5+
<head>
6+
<meta charset="utf-8" />
7+
<meta
8+
name="viewport"
9+
content="width=device-width, initial-scale=1.0" />
10+
11+
<base href="/" />
12+
13+
<title>AngularTemplate - En Maintenance</title>
14+
<meta
15+
name="description"
16+
content="Site web en maintenance, on revient vite." />
17+
<meta
18+
name="keywords"
19+
content="Angular 21, boilerplate, template, starter kit, PNPM, ESBuild, Vite, Vitest, Husky, lint-staged, ESLint, StyleLint, HTMLHint, SecretLint, Prettier, SonarQube Cloud, Snyk, CodeQL, GitLeaks, GitHub Actions, CI/CD, I18n, SEO, PWA, clean architecture, a11y, Compodoc, Font Awesome, Angular Material, Corepack, GitHub Pages, GitHub Packages, SEMVER,Changelog, design system, mobile-first, TypeScript, RXJS, Signals, NodeJS, Angular Schematics" />
20+
<meta
21+
name="robots"
22+
content="noindex, follow" />
23+
24+
<link
25+
rel="preconnect"
26+
href="https://fonts.googleapis.com" />
27+
<link
28+
rel="preconnect"
29+
href="https://fonts.gstatic.com"
30+
crossorigin />
31+
<link
32+
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100&family=Roboto+Condensed:wght@700&family=Roboto:wght@400;500;600;700&display=swap"
33+
rel="stylesheet" />
34+
35+
<link
36+
rel="icon"
37+
type="image/x-icon"
38+
href="/favicon.ico" />
39+
<link
40+
rel="stylesheet"
41+
href="/maintenance.css" />
42+
43+
<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>
44+
</head>
45+
<body>
46+
<main class="error-view">
47+
<h1
48+
class="error-view__title"
49+
id="h1">
50+
Une erreur est survenue.
51+
</h1>
52+
53+
<h3
54+
class="error-view__code"
55+
id="h3-wrapper">
56+
<span
57+
class="error-view__label"
58+
id="h3"
59+
>Erreur</span
60+
>
61+
<span class="error-view__code-value">503</span>
62+
</h3>
63+
64+
<div class="error-view__content">
65+
<section class="maintenance-error">
66+
<div class="maintenance-error__content">
67+
<div class="maintenance-error__animation-wrapper">
68+
<lottie-player
69+
src="/assets/animations/maintenance-error-under-construction.json"
70+
background="transparent"
71+
speed="1"
72+
style="width: 100%; height: 100%"
73+
loop
74+
autoplay
75+
class="maintenance-error__lottie"
76+
aria-label="Une équipe technique s'affairant autour d'un écran en maintenance" />
77+
</div>
78+
79+
<h4
80+
class="maintenance-error__title"
81+
id="h4">
82+
Casques obligatoires !
83+
</h4>
84+
85+
<h5
86+
class="maintenance-error__subtitle"
87+
id="h5">
88+
On laisse sécher le code et on revient vite...
89+
</h5>
5290
</div>
91+
</section>
92+
</div>
93+
</main>
94+
95+
<script>
96+
const translations = {
97+
fr: {
98+
title: 'Une erreur est survenue.',
99+
label: 'Erreur',
100+
subtitle: 'Casques obligatoires !',
101+
message: 'On laisse sécher le code et on revient vite...',
102+
ariaLabel: "Une équipe technique s'affairant autour d'un écran en maintenance"
103+
},
104+
en: {
105+
title: 'An error has occurred.',
106+
label: 'Error',
107+
subtitle: 'Helmets are needed !',
108+
message: "We'll let the code dry and we'll be back soon...",
109+
ariaLabel: 'A technical team working on a screen undergoing maintenance'
110+
}
111+
};
53112

54-
<h4 class="maintenance-error__title" id="h4">
55-
Casques obligatoires !
56-
</h4>
57-
58-
<h5 class="maintenance-error__subtitle" id="h5">
59-
On laisse sécher le code et on revient vite...
60-
</h5>
61-
62-
</div>
63-
</section>
64-
</div>
65-
66-
</main>
67-
68-
<script>
69-
const translations = {
70-
fr: {
71-
title: "Une erreur est survenue.",
72-
label: "Erreur",
73-
subtitle: "Casques obligatoires !",
74-
message: "On laisse sécher le code et on revient vite...",
75-
ariaLabel: "Une équipe technique s'affairant autour d'un écran en maintenance"
76-
},
77-
en: {
78-
title: "An error has occurred.",
79-
label: "Error",
80-
subtitle: "Helmets are needed !",
81-
message: "We'll let the code dry and we'll be back soon...",
82-
ariaLabel: "A technical team working on a screen undergoing maintenance"
83-
}
84-
};
85-
86-
const userLang = navigator.language || navigator.userLanguage;
87-
const lang = userLang.startsWith('en') ? 'en' : 'fr';
88-
89-
document.getElementById('h1').innerText = translations[lang].title;
90-
document.getElementById('h3').innerText = translations[lang].label;
91-
document.getElementById('h4').innerText = translations[lang].subtitle;
92-
document.getElementById('h5').innerText = translations[lang].message;
93-
94-
document.getElementById('lottie').setAttribute('aria-label', translations[lang].ariaLabel);
95-
96-
if (lang === 'en') {
97-
document.getElementById('h3-wrapper').style.flexDirection = 'row-reverse';
98-
}
113+
const userLang = navigator.language || navigator.userLanguage;
114+
const lang = userLang.startsWith('en') ? 'en' : 'fr';
99115

100-
document.documentElement.lang = lang;
101-
</script>
116+
document.getElementById('h1').innerText = translations[lang].title;
117+
document.getElementById('h3').innerText = translations[lang].label;
118+
document.getElementById('h4').innerText = translations[lang].subtitle;
119+
document.getElementById('h5').innerText = translations[lang].message;
102120

103-
<script>
104-
setInterval(async () => {
105-
try {
106-
const response = await fetch(window.location.href, {
107-
method: 'HEAD',
108-
cache: 'no-store'
109-
});
121+
document.getElementById('lottie').setAttribute('aria-label', translations[lang].ariaLabel);
110122

111-
if (response.ok) {
112-
window.location.reload();
113-
}
123+
if (lang === 'en') {
124+
document.getElementById('h3-wrapper').style.flexDirection = 'row-reverse';
114125
}
115-
catch (error) {
116-
console.info("Website still under maintenance...");
117-
}
118-
}, 30000);
119-
</script>
120126

121-
</body>
127+
document.documentElement.lang = lang;
128+
</script>
129+
130+
<script>
131+
setInterval(async () => {
132+
try {
133+
const response = await fetch(window.location.href, {
134+
method: 'HEAD',
135+
cache: 'no-store'
136+
});
137+
138+
if (response.ok) {
139+
window.location.reload();
140+
}
141+
} catch (error) {
142+
console.info('Website still under maintenance...');
143+
}
144+
}, 30000);
145+
</script>
146+
</body>
122147
</html>

sonar-project.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ sonar.sources=src
66
sonar.tests=src
77
sonar.test.inclusions=**/*.spec.ts
88
sonar.javascript.lcov.reportPaths=coverage/lcov.info
9+
sonar.cpd.exclusions=\
10+
src/app/shared/error-handler/error-views/maintenance-error/*.ts,\
11+
src/app/shared/error-handler/error-views/timeout-error/*.ts,\
12+
src/app/shared/error-handler/error-views/unknown-error/*.ts
913
sonar.coverage.exclusions=\
1014
src/_environments/environment.prod.ts,\
1115
src/_environments/environment.ts,\

src/app/app.routes.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,30 @@ describe('App Routes', () => {
8686
expect(TestBed.inject(Router).url).toBe('/login');
8787
});
8888

89+
it('should redirect to home (via root) if adminGuard fails due to missing token', async() => {
90+
// --- ARRANGE ---
91+
AUTH_SERVICE_MOCK.isAdmin.mockReturnValue(false);
92+
localStorage.clear();
93+
94+
// --- ACT ---
95+
await harness.navigateByUrl('/admin/dashboard');
96+
97+
// --- ASSERT ---
98+
expect(TestBed.inject(Router).url).toBe('/home');
99+
});
100+
101+
it('should allow /admin/dashboard if adminGuard passes', async() => {
102+
// --- ARRANGE ---
103+
AUTH_SERVICE_MOCK.isAdmin.mockReturnValue(true);
104+
AUTH_SERVICE_MOCK.currentUser.set({ roles: ['ADMIN'] });
105+
106+
// --- ACT ---
107+
await harness.navigateByUrl('/admin/dashboard');
108+
109+
// --- ASSERT ---
110+
expect(TestBed.inject(Router).url).toBe('/admin/dashboard');
111+
});
112+
89113
it('should navigate to unfound-error for unknown routes (wildcard)', async() => {
90114
// --- ACT ---
91115
await harness.navigateByUrl('/path/that/does/not/exist');

0 commit comments

Comments
 (0)