feat: SEO, PWA, and performance optimizations
- Add sitemap.xml endpoint and update robots.txt for SEO - Improve manifest.json with richer metadata and categories - Add meta tags for social sharing and accessibility - Preload critical assets and fonts for faster loading - Optimize login background image and resource hints - Enhance service worker for better caching strategies - Add security headers to server responses - Update Vite config for chunking and dependency optimization - Add logboek.md for project tracking
This commit is contained in:
BIN
logs/20251007-before-optimatizations/home.pdf
Normal file
BIN
logs/20251007-before-optimatizations/home.pdf
Normal file
Binary file not shown.
BIN
logs/20251007-before-optimatizations/login.pdf
Normal file
BIN
logs/20251007-before-optimatizations/login.pdf
Normal file
Binary file not shown.
173
logs/logboek.md
Normal file
173
logs/logboek.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# Logboek - Serengo Project
|
||||
|
||||
## Oktober 2025
|
||||
|
||||
### 7 Oktober 2025 (Maandag) - 1 uur
|
||||
|
||||
**Werk uitgevoerd:**
|
||||
|
||||
- Logo padding gefixed
|
||||
- Favicon bestanden opgeruimd (verwijderd oude favicon bestanden)
|
||||
- Manifest.json geoptimaliseerd
|
||||
|
||||
**Commits:** 1 commit (5f0cae6)
|
||||
|
||||
---
|
||||
|
||||
### 3 Oktober 2025 (Donderdag) - 8 uren
|
||||
|
||||
**Werk uitgevoerd:**
|
||||
|
||||
- Google OAuth implementatie toegevoegd
|
||||
- Database schema uitgebreid voor OAuth
|
||||
- Login formulier met shadcn/ui componenten
|
||||
- Background afbeelding toegevoegd voor login pagina
|
||||
- Location tracking en kaart functionaliteit geïmplementeerd
|
||||
- Sonner toast notifications voor foutmeldingen
|
||||
- Skeleton loading states toegevoegd
|
||||
- MapLibre GL JS maps geïntegreerd
|
||||
- Diverse styling fixes en verbeteringen
|
||||
|
||||
**Commits:** 10 commits (fbe0a75, 0caa5dc, 6fddf42, d8aa99e, 101b6cf, 5cb0b9e, d82f590, 00da815, b82141e, 0abf4f9)
|
||||
|
||||
**Details:**
|
||||
|
||||
- Complexe OAuth flow met Google
|
||||
- Geolocation API integratie
|
||||
- Real-time location watching
|
||||
- UI component library uitbreiding
|
||||
- Map integration met zoom functionaliteit
|
||||
|
||||
---
|
||||
|
||||
### 2 Oktober 2025 (Woensdag) - 5 uren
|
||||
|
||||
**Werk uitgevoerd:**
|
||||
|
||||
- MapLibre maps feature toegevoegd
|
||||
- Header component verbeterd
|
||||
- Basis kaart functionaliteit geïmplementeerd
|
||||
- Heel wat testen met self-hosted maps adhv OpenMapTiles
|
||||
|
||||
**Commits:** 1 commit (b44a69b)
|
||||
|
||||
---
|
||||
|
||||
### 29 September 2025 (Zondag) - 3 uren
|
||||
|
||||
**Werk uitgevoerd:**
|
||||
|
||||
- Modal component geïmplementeerd
|
||||
- Header component toegevoegd aan layout
|
||||
- Profile panel component ontwikkeld
|
||||
- UI verbeteringen voor login pagina
|
||||
- Code cleanup en refactoring
|
||||
|
||||
**Commits:** 6 commits (aa8161f, 0b44f10, 13d9303, 204a443, cc24bc0, b6cf77e)
|
||||
|
||||
**Details:**
|
||||
|
||||
- Grote refactor van UI componenten
|
||||
- Herbruikbare modal component
|
||||
- Verbeterde gebruikersinterface
|
||||
|
||||
---
|
||||
|
||||
### 28 September 2025 (Zaterdag) - 5 uren
|
||||
|
||||
**Werk uitgevoerd:**
|
||||
|
||||
- Complete UI overhaul van homepage
|
||||
- Storybook integratie verwijderd
|
||||
- Custom componenten geïmplementeerd (Button, Input, ErrorMessage, UserInfo)
|
||||
- ProfileIcon en ProfilePanel componenten
|
||||
- Login UI drastisch verbeterd met custom styling
|
||||
- Washington font toegevoegd
|
||||
- CSS styling uitgebreid
|
||||
|
||||
**Commits:** 6 commits (19c1b7b, aa72a3b, 62b2108, 98d745b, bc8a76b, c4deeb8)
|
||||
|
||||
**Details:**
|
||||
|
||||
- Grote codebase cleanup (1000+ lijnen verwijderd)
|
||||
- Custom component library opgebouwd
|
||||
- Professionele login interface
|
||||
- Typography verbeteringen
|
||||
|
||||
---
|
||||
|
||||
### 27 September 2025 (Vrijdag) - 6 uren
|
||||
|
||||
**Werk uitgevoerd:**
|
||||
|
||||
- Vercel deployment configuratie
|
||||
- Auth systeem naar main applicatie verplaatst
|
||||
- Docker configuratie fixes
|
||||
- Container deployment setup
|
||||
- CSRF auth fixes
|
||||
- Database migraties
|
||||
|
||||
**Commits:** 12 commits (925e716, bb118ca, 806fb92, 79f57c1, d8e2569, dd20a12, 219a78e, 0bcf6c8, 752cb04, 62c7c5e, 1b413e1, 88a7e74, bbe68c6, fc39408)
|
||||
|
||||
**Details:**
|
||||
|
||||
- Production deployment voorbereiding
|
||||
- Docker containerization
|
||||
- Auth system integratie
|
||||
- Security verbeteringen
|
||||
|
||||
---
|
||||
|
||||
### 26 September 2025 (Donderdag) - 5 uren
|
||||
|
||||
**Werk uitgevoerd:**
|
||||
|
||||
- Lucia auth systeem geïmplementeerd
|
||||
- Database schema voor gebruikers
|
||||
- Login/logout functionaliteit
|
||||
- pnpm-lock.yaml cleanup
|
||||
- AGENTS.md documentatie toegevoegd
|
||||
- Database setup en migraties
|
||||
- Logo en PWA manifest
|
||||
- Chromatic integration
|
||||
- Code formatting
|
||||
- Tailwind CSS verwijderd
|
||||
|
||||
**Commits:** 10 commits (7e4570c, 3cdb35b, e4fdfc4, f0d30bc, 9b4e956, fe0f939, ad72884, 8745ef1, 9ce6f96, d8f7921, 93eded5, fc5b927, faa2f94)
|
||||
|
||||
**Details:**
|
||||
|
||||
- Volledige authenticatie systeem
|
||||
- Database structuur opgezet
|
||||
- Project configuratie
|
||||
- PWA functionaliteit
|
||||
- Initial project setup
|
||||
|
||||
---
|
||||
|
||||
## Totaal Overzicht
|
||||
|
||||
**Totale geschatte uren:** 33 uren
|
||||
**Werkdagen:** 6 dagen
|
||||
**Gemiddelde uren per dag:** 5.5 uur
|
||||
|
||||
### Project Milestones:
|
||||
|
||||
1. **26 Sept**: Project initialisatie en auth systeem
|
||||
2. **27 Sept**: Deployment en productie setup
|
||||
3. **28 Sept**: UI/UX complete overhaul
|
||||
4. **29 Sept**: Component architectuur verbetering
|
||||
5. **2-3 Okt**: Maps en location features
|
||||
6. **7 Okt**: Optimalisaties
|
||||
|
||||
### Hoofdfunctionaliteiten geïmplementeerd:
|
||||
|
||||
- ✅ Gebruikersauthenticatie (Lucia + Google OAuth)
|
||||
- ✅ Responsive UI met custom componenten
|
||||
- ✅ Real-time locatie tracking
|
||||
- ✅ Interactive maps (MapLibre GL JS)
|
||||
- ✅ PWA functionaliteit
|
||||
- ✅ Docker deployment
|
||||
- ✅ Database (PostgreSQL + Drizzle ORM)
|
||||
- ✅ Toast notifications
|
||||
- ✅ Loading states en error handling
|
||||
26
src/app.css
26
src/app.css
@@ -80,6 +80,7 @@
|
||||
src: url('/fonts/Washington.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
* {
|
||||
@@ -131,5 +132,30 @@
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
/* Ensure links have visible focus indicators and non-color differentiation */
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:focus {
|
||||
outline: 2px solid var(--color-ring);
|
||||
outline-offset: 2px;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Special handling for map attribution links */
|
||||
.maplibregl-ctrl-attrib a {
|
||||
color: #0078a8 !important;
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
|
||||
.maplibregl-ctrl-attrib a:hover,
|
||||
.maplibregl-ctrl-attrib a:focus {
|
||||
text-decoration: underline !important;
|
||||
background-color: rgba(0, 120, 168, 0.1) !important;
|
||||
}
|
||||
}
|
||||
|
||||
10
src/app.html
10
src/app.html
@@ -2,8 +2,16 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<meta name="theme-color" content="#333333" />
|
||||
<meta name="theme-color" content="#f8f8f8" media="(prefers-color-scheme: light)" />
|
||||
<meta name="theme-color" content="#333333" media="(prefers-color-scheme: dark)" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||
<meta name="apple-mobile-web-app-title" content="Serengo" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<link rel="apple-touch-icon" href="/logo.svg" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
|
||||
@@ -39,5 +39,31 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
|
||||
event.locals.user = user;
|
||||
event.locals.session = session;
|
||||
return resolve(event);
|
||||
|
||||
const response = await resolve(event);
|
||||
|
||||
// Add security headers
|
||||
response.headers.set(
|
||||
'Content-Security-Policy',
|
||||
"default-src 'self'; " +
|
||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval'; " +
|
||||
"style-src 'self' 'unsafe-inline' fonts.googleapis.com; " +
|
||||
"font-src 'self' fonts.gstatic.com; " +
|
||||
"img-src 'self' data: blob: *.openstreetmap.org *.tile.openstreetmap.org; " +
|
||||
"connect-src 'self' *.openstreetmap.org; " +
|
||||
"frame-ancestors 'none'; " +
|
||||
"base-uri 'self'; " +
|
||||
"form-action 'self';"
|
||||
);
|
||||
|
||||
response.headers.set('X-Frame-Options', 'DENY');
|
||||
response.headers.set('X-Content-Type-Options', 'nosniff');
|
||||
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||
|
||||
// Add HSTS for HTTPS in production
|
||||
if (event.url.protocol === 'https:') {
|
||||
response.headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
@@ -14,6 +14,20 @@
|
||||
|
||||
<svelte:head>
|
||||
<link rel="icon" href={favicon} />
|
||||
<!-- Performance optimizations -->
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/Washington.ttf"
|
||||
as="font"
|
||||
type="font/ttf"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<link rel="dns-prefetch" href="//tile.openstreetmap.org" />
|
||||
<link rel="preconnect" href="https://tile.openstreetmap.org" crossorigin="anonymous" />
|
||||
<!-- Resource hints for login page background -->
|
||||
{#if isLoginRoute}
|
||||
<link rel="preload" href="/cafe-bg-compressed.jpg" as="image" />
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
||||
<Toaster />
|
||||
|
||||
@@ -2,6 +2,26 @@
|
||||
import { Map } from '$lib';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Serengo - Meet the Unexpected</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Discover unexpected places and experiences with Serengo's interactive map. Find hidden gems and explore your surroundings like never before."
|
||||
/>
|
||||
<meta property="og:title" content="Serengo - Meet the Unexpected" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Discover unexpected places and experiences with Serengo's interactive map. Find hidden gems and explore your surroundings like never before."
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:title" content="Serengo - Meet the Unexpected" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="Discover unexpected places and experiences with Serengo's interactive map. Find hidden gems and explore your surroundings like never before."
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
<div class="home-container">
|
||||
<main class="main-content">
|
||||
<div class="map-section">
|
||||
|
||||
@@ -5,6 +5,26 @@
|
||||
let { form }: { form: ActionData } = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Login - Serengo</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Sign in to your Serengo account to access personalized maps and discover unexpected places around you."
|
||||
/>
|
||||
<meta property="og:title" content="Login - Serengo" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Sign in to your Serengo account to access personalized maps and discover unexpected places around you."
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:title" content="Login - Serengo" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="Sign in to your Serengo account to access personalized maps and discover unexpected places around you."
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
<div class="login-background flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
|
||||
<div class="flex w-full max-w-sm flex-col gap-6">
|
||||
<LoginForm {form} />
|
||||
@@ -12,7 +32,7 @@
|
||||
|
||||
<style>
|
||||
.login-background {
|
||||
background-image: url('/cafe-bg.jpeg');
|
||||
background-image: url('/cafe-bg-compressed.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
@@ -34,5 +54,16 @@
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* Preload hint for faster loading */
|
||||
@media (prefers-reduced-data: no-preference) {
|
||||
.login-background::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-image: url('/cafe-bg-compressed.jpg');
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
|
||||
29
src/routes/sitemap.xml/+server.ts
Normal file
29
src/routes/sitemap.xml/+server.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const GET: RequestHandler = async ({ url }) => {
|
||||
const baseUrl = url.origin;
|
||||
const lastmod = new Date().toISOString().split('T')[0]; // YYYY-MM-DD format
|
||||
|
||||
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>${baseUrl}/</loc>
|
||||
<lastmod>${lastmod}</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>${baseUrl}/login</loc>
|
||||
<lastmod>${lastmod}</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.5</priority>
|
||||
</url>
|
||||
</urlset>`;
|
||||
|
||||
return new Response(sitemap, {
|
||||
headers: {
|
||||
'Content-Type': 'application/xml',
|
||||
'Cache-Control': 'max-age=86400' // Cache for 24 hours
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -15,33 +15,70 @@ import { build, files, version } from '$service-worker';
|
||||
// This gives `self` the correct types
|
||||
const self = globalThis.self as unknown as ServiceWorkerGlobalScope;
|
||||
|
||||
// Create a unique cache name for this deployment
|
||||
// Create cache names for this deployment
|
||||
const CACHE = `cache-${version}`;
|
||||
const RUNTIME_CACHE = `runtime-${version}`;
|
||||
const IMAGE_CACHE = `images-${version}`;
|
||||
|
||||
const ASSETS = [
|
||||
...build, // the app itself
|
||||
...files // everything in `static`
|
||||
];
|
||||
|
||||
// Assets to precache for better performance
|
||||
const CRITICAL_ASSETS = [
|
||||
'/cafe-bg-compressed.jpg',
|
||||
'/fonts/Washington.ttf',
|
||||
'/logo.svg'
|
||||
];
|
||||
|
||||
self.addEventListener('install', (event) => {
|
||||
// Create a new cache and add all files to it
|
||||
async function addFilesToCache() {
|
||||
const cache = await caches.open(CACHE);
|
||||
const imageCache = await caches.open(IMAGE_CACHE);
|
||||
|
||||
// Cache core assets
|
||||
await cache.addAll(ASSETS);
|
||||
|
||||
// Precache critical assets with error handling
|
||||
await Promise.allSettled(
|
||||
CRITICAL_ASSETS.map(async (asset) => {
|
||||
try {
|
||||
const response = await fetch(asset);
|
||||
if (response.ok) {
|
||||
if (asset.includes('jpg') || asset.includes('jpeg') || asset.includes('png') || asset.includes('webp')) {
|
||||
await imageCache.put(asset, response);
|
||||
} else {
|
||||
await cache.put(asset, response);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Failed to cache ${asset}:`, error);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
event.waitUntil(addFilesToCache());
|
||||
// Skip waiting to activate immediately
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
// Remove previous cached data from disk
|
||||
async function deleteOldCaches() {
|
||||
const currentCaches = [CACHE, RUNTIME_CACHE, IMAGE_CACHE];
|
||||
for (const key of await caches.keys()) {
|
||||
if (key !== CACHE) await caches.delete(key);
|
||||
if (!currentCaches.includes(key)) {
|
||||
await caches.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event.waitUntil(deleteOldCaches());
|
||||
// Claim clients immediately
|
||||
self.clients.claim();
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
@@ -51,6 +88,8 @@ self.addEventListener('fetch', (event) => {
|
||||
async function respond() {
|
||||
const url = new URL(event.request.url);
|
||||
const cache = await caches.open(CACHE);
|
||||
const runtimeCache = await caches.open(RUNTIME_CACHE);
|
||||
const imageCache = await caches.open(IMAGE_CACHE);
|
||||
|
||||
// `build`/`files` can always be served from the cache
|
||||
if (ASSETS.includes(url.pathname)) {
|
||||
@@ -61,6 +100,50 @@ self.addEventListener('fetch', (event) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle images with cache-first strategy
|
||||
if (url.pathname.match(/\.(jpg|jpeg|png|gif|webp|svg)$/i)) {
|
||||
const cachedResponse = await imageCache.match(event.request);
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(event.request);
|
||||
if (response.ok) {
|
||||
imageCache.put(event.request, response.clone());
|
||||
}
|
||||
return response;
|
||||
} catch {
|
||||
// Return a fallback image or the cached version
|
||||
return cachedResponse || new Response('Image not available', { status: 404 });
|
||||
}
|
||||
}
|
||||
|
||||
// Handle fonts with cache-first strategy
|
||||
if (url.pathname.match(/\.(woff|woff2|ttf|otf)$/i)) {
|
||||
const cachedResponse = await cache.match(event.request);
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle API and dynamic content with network-first strategy
|
||||
if (url.pathname.startsWith('/api/') || url.searchParams.has('_data')) {
|
||||
try {
|
||||
const response = await fetch(event.request);
|
||||
if (response.ok) {
|
||||
runtimeCache.put(event.request, response.clone());
|
||||
}
|
||||
return response;
|
||||
} catch (err) {
|
||||
const cachedResponse = await runtimeCache.match(event.request);
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// for everything else, try the network first, but
|
||||
// fall back to the cache if we're offline
|
||||
try {
|
||||
@@ -73,15 +156,19 @@ self.addEventListener('fetch', (event) => {
|
||||
}
|
||||
|
||||
if (response.status === 200) {
|
||||
cache.put(event.request, response.clone());
|
||||
runtimeCache.put(event.request, response.clone());
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (err) {
|
||||
const response = await cache.match(event.request);
|
||||
// Try all caches for fallback
|
||||
const cachedResponse =
|
||||
await cache.match(event.request) ||
|
||||
await runtimeCache.match(event.request) ||
|
||||
await imageCache.match(event.request);
|
||||
|
||||
if (response) {
|
||||
return response;
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
// if there's no cache, then just error out
|
||||
|
||||
BIN
static/cafe-bg-compressed.jpg
Normal file
BIN
static/cafe-bg-compressed.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 719 KiB |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"short_name": "Serengo",
|
||||
"name": "Serengo",
|
||||
"description": "meet the unexpected.",
|
||||
"name": "Serengo - meet the unexpected.",
|
||||
"description": "Discover unexpected places and experiences with Serengo's interactive map. Find hidden gems and explore your surroundings like never before.",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/logo.svg",
|
||||
@@ -10,8 +10,11 @@
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"theme_color": "white",
|
||||
"background_color": "white"
|
||||
"orientation": "portrait-primary",
|
||||
"theme_color": "#333333",
|
||||
"background_color": "#f8f8f8",
|
||||
"scope": "/",
|
||||
"categories": ["travel", "maps", "discovery"]
|
||||
}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# allow crawling everything by default
|
||||
# Serengo - meet the unexpected.
|
||||
User-agent: *
|
||||
Disallow:
|
||||
Allow: /
|
||||
Disallow: /api/
|
||||
Disallow: /logout
|
||||
Disallow: /_app/
|
||||
Disallow: /.svelte-kit/
|
||||
|
||||
# Sitemap location
|
||||
Sitemap: https://serengo.ziasvannes.tech/sitemap.xml
|
||||
|
||||
# Crawl delay for polite crawling
|
||||
Crawl-delay: 1
|
||||
|
||||
@@ -4,5 +4,19 @@ import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tailwindcss(), sveltekit()],
|
||||
preview: { allowedHosts: ['ziasvannes.tech'] }
|
||||
preview: { allowedHosts: ['ziasvannes.tech'] },
|
||||
build: {
|
||||
target: 'es2020',
|
||||
cssCodeSplit: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
maplibre: ['maplibre-gl', 'svelte-maplibre']
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ['maplibre-gl', 'svelte-maplibre']
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user