fix(privacy): self-host Inter font, remove Google Fonts
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 19m23s
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 19m23s
- Drops all fonts.googleapis.com / fonts.gstatic.com references - Adds self-hosted woff2 under public/fonts/ - @font-face with font-display: swap - Test asserts no external font refs in generated HTML Closes GDPR contradiction: privacy policy promises 'no third-party tracking' and 'EU-hosted' while every page was leaking visitor IPs to Google (Munich District Court 2022 ruling, €100/visitor damages precedent).
This commit is contained in:
parent
6ce773a071
commit
a374a93937
21 changed files with 143 additions and 23 deletions
|
|
@ -17,6 +17,12 @@
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<link rel="stylesheet" href="/swagger-ui/swagger-ui.css">
|
<link rel="stylesheet" href="/swagger-ui/swagger-ui.css">
|
||||||
<style>
|
<style>
|
||||||
|
/* Self-hosted Inter font (GDPR: no third-party requests to Google) */
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/Inter-Regular.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/Inter-Medium.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap; src: url('/fonts/Inter-SemiBold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/Inter-Bold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 800; font-display: swap; src: url('/fonts/Inter-ExtraBold.woff2') format('woff2'); }
|
||||||
html { box-sizing: border-box; overflow-y: scroll; }
|
html { box-sizing: border-box; overflow-y: scroll; }
|
||||||
*, *:before, *:after { box-sizing: inherit; }
|
*, *:before, *:after { box-sizing: inherit; }
|
||||||
body { margin: 0; background: #1a1a2e; font-family: 'Inter', -apple-system, system-ui, sans-serif; }
|
body { margin: 0; background: #1a1a2e; font-family: 'Inter', -apple-system, system-ui, sans-serif; }
|
||||||
|
|
@ -109,8 +115,6 @@
|
||||||
.skip-link { position: absolute; top: -100%; left: 16px; background: #34d399; color: #0b0d11; padding: 8px 16px; border-radius: 0 0 8px 8px; font-weight: 600; font-size: 0.9rem; z-index: 200; transition: top 0.2s; text-decoration: none; }
|
.skip-link { position: absolute; top: -100%; left: 16px; background: #34d399; color: #0b0d11; padding: 8px 16px; border-radius: 0 0 8px 8px; font-weight: 600; font-size: 0.9rem; z-index: 200; transition: top 0.2s; text-decoration: none; }
|
||||||
.skip-link:focus { top: 0; }
|
.skip-link:focus { top: 0; }
|
||||||
</style>
|
</style>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<a href="#main" class="skip-link">Skip to content</a>
|
<a href="#main" class="skip-link">Skip to content</a>
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,13 @@
|
||||||
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
||||||
<link rel="canonical" href="https://docfast.dev/examples">
|
<link rel="canonical" href="https://docfast.dev/examples">
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
<style>
|
||||||
|
/* Self-hosted Inter font (GDPR: no third-party requests to Google) */
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/Inter-Regular.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/Inter-Medium.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap; src: url('/fonts/Inter-SemiBold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/Inter-Bold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 800; font-display: swap; src: url('/fonts/Inter-ExtraBold.woff2') format('woff2'); }
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
:root {
|
:root {
|
||||||
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
||||||
|
|
|
||||||
BIN
public/fonts/Inter-Bold.woff2
Normal file
BIN
public/fonts/Inter-Bold.woff2
Normal file
Binary file not shown.
BIN
public/fonts/Inter-ExtraBold.woff2
Normal file
BIN
public/fonts/Inter-ExtraBold.woff2
Normal file
Binary file not shown.
BIN
public/fonts/Inter-Medium.woff2
Normal file
BIN
public/fonts/Inter-Medium.woff2
Normal file
Binary file not shown.
BIN
public/fonts/Inter-Regular.woff2
Normal file
BIN
public/fonts/Inter-Regular.woff2
Normal file
Binary file not shown.
BIN
public/fonts/Inter-SemiBold.woff2
Normal file
BIN
public/fonts/Inter-SemiBold.woff2
Normal file
Binary file not shown.
|
|
@ -13,8 +13,13 @@
|
||||||
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
||||||
<link rel="canonical" href="https://docfast.dev/impressum">
|
<link rel="canonical" href="https://docfast.dev/impressum">
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
<style>
|
||||||
|
/* Self-hosted Inter font (GDPR: no third-party requests to Google) */
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/Inter-Regular.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/Inter-Medium.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap; src: url('/fonts/Inter-SemiBold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/Inter-Bold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 800; font-display: swap; src: url('/fonts/Inter-ExtraBold.woff2') format('woff2'); }
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
:root {
|
:root {
|
||||||
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,12 @@
|
||||||
</script>
|
</script>
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<style>
|
<style>
|
||||||
|
/* Self-hosted Inter font (GDPR: no third-party requests to Google) */
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/Inter-Regular.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/Inter-Medium.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap; src: url('/fonts/Inter-SemiBold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/Inter-Bold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 800; font-display: swap; src: url('/fonts/Inter-ExtraBold.woff2') format('woff2'); }
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
:root {
|
:root {
|
||||||
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
||||||
|
|
@ -366,9 +372,6 @@ html, body {
|
||||||
.skip-link { position: absolute; top: -100%; left: 16px; background: var(--accent); color: #0b0d11; padding: 8px 16px; border-radius: 0 0 8px 8px; font-weight: 600; font-size: 0.9rem; z-index: 200; transition: top 0.2s; }
|
.skip-link { position: absolute; top: -100%; left: 16px; background: var(--accent); color: #0b0d11; padding: 8px 16px; border-radius: 0 0 8px 8px; font-weight: 600; font-size: 0.9rem; z-index: 200; transition: top 0.2s; }
|
||||||
.skip-link:focus { top: 0; }
|
.skip-link:focus { top: 0; }
|
||||||
</style>
|
</style>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<a href="#main" class="skip-link">Skip to content</a>
|
<a href="#main" class="skip-link">Skip to content</a>
|
||||||
|
|
@ -595,9 +598,9 @@ html, body {
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<!-- Recovery Modal -->
|
<!-- Recovery Modal -->
|
||||||
<div class="modal-overlay" id="recoverModal" role="dialog" aria-label="Recover API key">
|
<div class="modal-overlay" id="recoverModal" role="dialog" aria-modal="true" aria-label="Recover API key">
|
||||||
<div class="modal">
|
<div class="modal">
|
||||||
<button class="close" id="btn-close-recover">×</button>
|
<button class="close" id="btn-close-recover" aria-label="Close">×</button>
|
||||||
|
|
||||||
<div id="recoverInitial" class="active">
|
<div id="recoverInitial" class="active">
|
||||||
<h2>Recover your API key</h2>
|
<h2>Recover your API key</h2>
|
||||||
|
|
@ -639,9 +642,9 @@ html, body {
|
||||||
|
|
||||||
|
|
||||||
<!-- Email Change Modal -->
|
<!-- Email Change Modal -->
|
||||||
<div class="modal-overlay" id="emailChangeModal" role="dialog" aria-label="Change email">
|
<div class="modal-overlay" id="emailChangeModal" role="dialog" aria-modal="true" aria-label="Change email">
|
||||||
<div class="modal">
|
<div class="modal">
|
||||||
<button class="close" id="btn-close-email-change">×</button>
|
<button class="close" id="btn-close-email-change" aria-label="Close">×</button>
|
||||||
|
|
||||||
<div id="emailChangeInitial" class="active">
|
<div id="emailChangeInitial" class="active">
|
||||||
<h2>Change your email</h2>
|
<h2>Change your email</h2>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,10 @@
|
||||||
<style>
|
<style>
|
||||||
|
/* Self-hosted Inter font (GDPR: no third-party requests to Google) */
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/Inter-Regular.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/Inter-Medium.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap; src: url('/fonts/Inter-SemiBold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/Inter-Bold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 800; font-display: swap; src: url('/fonts/Inter-ExtraBold.woff2') format('woff2'); }
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
:root {
|
:root {
|
||||||
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,13 @@
|
||||||
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
||||||
<link rel="canonical" href="https://docfast.dev/privacy">
|
<link rel="canonical" href="https://docfast.dev/privacy">
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
<style>
|
||||||
|
/* Self-hosted Inter font (GDPR: no third-party requests to Google) */
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/Inter-Regular.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/Inter-Medium.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap; src: url('/fonts/Inter-SemiBold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/Inter-Bold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 800; font-display: swap; src: url('/fonts/Inter-ExtraBold.woff2') format('woff2'); }
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
:root {
|
:root {
|
||||||
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
||||||
<link rel="canonical" href="https://docfast.dev/examples">
|
<link rel="canonical" href="https://docfast.dev/examples">
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
{{> styles_base}}
|
{{> styles_base}}
|
||||||
<style>
|
<style>
|
||||||
.examples-hero { padding: 80px 0 40px; text-align: center; }
|
.examples-hero { padding: 80px 0 40px; text-align: center; }
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
||||||
<link rel="canonical" href="https://docfast.dev/impressum">
|
<link rel="canonical" href="https://docfast.dev/impressum">
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
{{> styles_base}}
|
{{> styles_base}}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,12 @@
|
||||||
</script>
|
</script>
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<style>
|
<style>
|
||||||
|
/* Self-hosted Inter font (GDPR: no third-party requests to Google) */
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/Inter-Regular.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/Inter-Medium.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap; src: url('/fonts/Inter-SemiBold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/Inter-Bold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 800; font-display: swap; src: url('/fonts/Inter-ExtraBold.woff2') format('woff2'); }
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
:root {
|
:root {
|
||||||
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
||||||
|
|
@ -366,9 +372,6 @@ html, body {
|
||||||
.skip-link { position: absolute; top: -100%; left: 16px; background: var(--accent); color: #0b0d11; padding: 8px 16px; border-radius: 0 0 8px 8px; font-weight: 600; font-size: 0.9rem; z-index: 200; transition: top 0.2s; }
|
.skip-link { position: absolute; top: -100%; left: 16px; background: var(--accent); color: #0b0d11; padding: 8px 16px; border-radius: 0 0 8px 8px; font-weight: 600; font-size: 0.9rem; z-index: 200; transition: top 0.2s; }
|
||||||
.skip-link:focus { top: 0; }
|
.skip-link:focus { top: 0; }
|
||||||
</style>
|
</style>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<a href="#main" class="skip-link">Skip to content</a>
|
<a href="#main" class="skip-link">Skip to content</a>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
||||||
<link rel="canonical" href="https://docfast.dev/privacy">
|
<link rel="canonical" href="https://docfast.dev/privacy">
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
{{> styles_base}}
|
{{> styles_base}}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
||||||
<link rel="canonical" href="https://docfast.dev/status">
|
<link rel="canonical" href="https://docfast.dev/status">
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
{{> styles_base}}
|
{{> styles_base}}
|
||||||
<style>
|
<style>
|
||||||
.status-hero { text-align: center; padding: 48px 0 32px; }
|
.status-hero { text-align: center; padding: 48px 0 32px; }
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
||||||
<link rel="canonical" href="https://docfast.dev/terms">
|
<link rel="canonical" href="https://docfast.dev/terms">
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
{{> styles_base}}
|
{{> styles_base}}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,13 @@
|
||||||
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
||||||
<link rel="canonical" href="https://docfast.dev/status">
|
<link rel="canonical" href="https://docfast.dev/status">
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
<style>
|
||||||
|
/* Self-hosted Inter font (GDPR: no third-party requests to Google) */
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/Inter-Regular.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/Inter-Medium.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap; src: url('/fonts/Inter-SemiBold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/Inter-Bold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 800; font-display: swap; src: url('/fonts/Inter-ExtraBold.woff2') format('woff2'); }
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
:root {
|
:root {
|
||||||
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,13 @@
|
||||||
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
<meta name="twitter:image" content="https://docfast.dev/og-image.png">
|
||||||
<link rel="canonical" href="https://docfast.dev/terms">
|
<link rel="canonical" href="https://docfast.dev/terms">
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
<style>
|
||||||
|
/* Self-hosted Inter font (GDPR: no third-party requests to Google) */
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/Inter-Regular.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/Inter-Medium.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap; src: url('/fonts/Inter-SemiBold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/Inter-Bold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 800; font-display: swap; src: url('/fonts/Inter-ExtraBold.woff2') format('woff2'); }
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
:root {
|
:root {
|
||||||
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
--bg: #0b0d11; --bg2: #12151c; --fg: #e4e7ed; --muted: #7a8194;
|
||||||
|
|
|
||||||
82
src/__tests__/no-google-fonts.test.ts
Normal file
82
src/__tests__/no-google-fonts.test.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
import { describe, it, expect, beforeAll } from "vitest";
|
||||||
|
import { execSync } from "node:child_process";
|
||||||
|
import { readFileSync, readdirSync, statSync, existsSync } from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GDPR compliance: DocFast's privacy policy promises "no third-party tracking"
|
||||||
|
* and markets itself as "EU-hosted". Loading Google Fonts from
|
||||||
|
* fonts.googleapis.com / fonts.gstatic.com leaks visitor IPs to Google (US),
|
||||||
|
* which the Munich District Court ruled a GDPR violation in 2022.
|
||||||
|
*
|
||||||
|
* This test enforces that:
|
||||||
|
* 1. No generated HTML page references Google Fonts domains
|
||||||
|
* 2. The inlined 404 HTML in src/index.ts references no Google Fonts
|
||||||
|
* 3. Self-hosted Inter woff2 files exist for all weights we use
|
||||||
|
*/
|
||||||
|
|
||||||
|
const repoRoot = path.resolve(__dirname, "..", "..");
|
||||||
|
const publicDir = path.join(repoRoot, "public");
|
||||||
|
const fontsDir = path.join(publicDir, "fonts");
|
||||||
|
|
||||||
|
function walkHtml(dir: string, acc: string[] = []): string[] {
|
||||||
|
for (const entry of readdirSync(dir)) {
|
||||||
|
const full = path.join(dir, entry);
|
||||||
|
const st = statSync(full);
|
||||||
|
if (st.isDirectory()) {
|
||||||
|
// Skip build-irrelevant directories
|
||||||
|
if (entry === "src" || entry === "partials" || entry === "swagger-ui") continue;
|
||||||
|
walkHtml(full, acc);
|
||||||
|
} else if (entry.endsWith(".html")) {
|
||||||
|
acc.push(full);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("GDPR: no Google Fonts references", () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
// Ensure HTML pages are freshly built before scanning
|
||||||
|
execSync("npm run build:pages", { cwd: repoRoot, stdio: "inherit" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("generated HTML pages contain no fonts.googleapis.com / fonts.gstatic.com", () => {
|
||||||
|
const htmlFiles = walkHtml(publicDir);
|
||||||
|
expect(htmlFiles.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
const offenders: { file: string; match: string }[] = [];
|
||||||
|
for (const file of htmlFiles) {
|
||||||
|
const content = readFileSync(file, "utf-8");
|
||||||
|
if (content.includes("fonts.googleapis.com")) {
|
||||||
|
offenders.push({ file: path.relative(repoRoot, file), match: "fonts.googleapis.com" });
|
||||||
|
}
|
||||||
|
if (content.includes("fonts.gstatic.com")) {
|
||||||
|
offenders.push({ file: path.relative(repoRoot, file), match: "fonts.gstatic.com" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(offenders, `Google Fonts references found:\n${JSON.stringify(offenders, null, 2)}`).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("src/index.ts inlined HTML contains no Google Fonts references", () => {
|
||||||
|
const indexTs = readFileSync(path.join(repoRoot, "src", "index.ts"), "utf-8");
|
||||||
|
expect(indexTs).not.toContain("fonts.googleapis.com");
|
||||||
|
expect(indexTs).not.toContain("fonts.gstatic.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("self-hosted Inter woff2 files exist for all weights we use", () => {
|
||||||
|
const expected = [
|
||||||
|
"Inter-Regular.woff2", // 400
|
||||||
|
"Inter-Medium.woff2", // 500
|
||||||
|
"Inter-SemiBold.woff2", // 600
|
||||||
|
"Inter-Bold.woff2", // 700
|
||||||
|
"Inter-ExtraBold.woff2", // 800
|
||||||
|
];
|
||||||
|
expect(existsSync(fontsDir), `fonts directory missing at ${fontsDir}`).toBe(true);
|
||||||
|
for (const f of expected) {
|
||||||
|
const full = path.join(fontsDir, f);
|
||||||
|
expect(existsSync(full), `missing font file: public/fonts/${f}`).toBe(true);
|
||||||
|
const size = statSync(full).size;
|
||||||
|
expect(size, `public/fonts/${f} is empty`).toBeGreaterThan(1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -195,8 +195,10 @@ app.use((req, res) => {
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>404 - Page Not Found | DocFast</title>
|
<title>404 - Page Not Found | DocFast</title>
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
<style>
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/Inter-Regular.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap; src: url('/fonts/Inter-SemiBold.woff2') format('woff2'); }
|
||||||
|
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/Inter-Bold.woff2') format('woff2'); }
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
body { font-family: 'Inter', sans-serif; background: #0b0d11; color: #e4e7ed; min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; }
|
body { font-family: 'Inter', sans-serif; background: #0b0d11; color: #e4e7ed; min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; }
|
||||||
.container { background: #151922; border: 1px solid #1e2433; border-radius: 16px; padding: 48px; max-width: 520px; width: 100%; text-align: center; }
|
.container { background: #151922; border: 1px solid #1e2433; border-radius: 16px; padding: 48px; max-width: 520px; width: 100%; text-align: center; }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue