From 1b24607e0235ce5e0f1f911ed4cf66bc4741630d Mon Sep 17 00:00:00 2001 From: Linus Olofsson Date: Fri, 6 Nov 2026 04:08:04 +0100 Subject: [PATCH] Portfolio without downloads; Om mig, Kontakt and Posters come from the portal on the same domain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Linus 2026-11-06: everything public lives on imagesbyolofsson.se (Cleura), the customer subdomain is only for ordering and the logged-in portal; photos are sold as posters instead of being downloadable. - Portfolio: download menu (S/M/L), the original password dialog and /download/original are gone; the lightbox has "Köp som poster" -> /posters instead. - about.astro and contact.astro removed: Caddy on Cleura sends /about, /contact and /posters to the portal, so the About text and collaborations are edited once in admin. They stay in sitemap-0.xml (PORTAL_PAGES); the sitemap index lists /posters/sitemap.xml. - Menu and 404 page link Posters; privacy, robots.txt and llms.txt no longer mention password downloads. Co-Authored-By: Claude Opus 5.5 --- public/robots.txt | 3 - src/layouts/MainLayout.astro | 1 + src/lib/pages.ts | 10 +- src/pages/404.astro | 1 + src/pages/about.astro | 137 ---------------------------- src/pages/contact.astro | 141 ---------------------------- src/pages/llms.txt.ts | 3 +- src/pages/portfolio.astro | 162 ++------------------------------- src/pages/privacy.astro | 1 - src/pages/sitemap-index.xml.ts | 1 + 10 files changed, 23 insertions(+), 437 deletions(-) delete mode 100644 src/pages/about.astro delete mode 100644 src/pages/contact.astro diff --git a/public/robots.txt b/public/robots.txt index 20c0216..81b2f88 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -8,8 +8,6 @@ User-agent: * Allow: / -# Lösenordsskyddad nedladdning av originalfiler – inget att indexera. -Disallow: /download/ # AI-crawlers och träningsbotar: uttryckligen tillåtna. User-agent: GPTBot @@ -36,6 +34,5 @@ User-agent: YouBot User-agent: DuckAssistBot User-agent: MistralAI-User Allow: / -Disallow: /download/ Sitemap: https://imagesbyolofsson.se/sitemap-index.xml diff --git a/src/layouts/MainLayout.astro b/src/layouts/MainLayout.astro index 0132447..82329ec 100644 --- a/src/layouts/MainLayout.astro +++ b/src/layouts/MainLayout.astro @@ -45,6 +45,7 @@ const ogHeight = Number(og.attributes.height) || undefined; const nav = [ { href: '/portfolio', label: 'Portfolio' }, + { href: '/posters', label: 'Posters' }, { href: '/tjanster', label: 'Tjänster' }, { href: '/about', label: 'Om mig' }, { href: '/contact', label: 'Kontakt' }, diff --git a/src/lib/pages.ts b/src/lib/pages.ts index 77379c8..18c264f 100644 --- a/src/lib/pages.ts +++ b/src/lib/pages.ts @@ -3,10 +3,16 @@ // Nya sidor hamnar automatiskt i sitemap. const pageFiles = import.meta.glob('../pages/*.astro'); +// Sidor på samma domän som kundportalen (samma Cleura-server) visar, så att Om mig-texten, +// samarbeten och posters redigeras på ett ställe i admin (Linus 2026-11-06). Caddy skickar dem +// till portalen; enskilda posters listas i portalens egen /posters/sitemap.xml. +export const PORTAL_PAGES = ['/about', '/contact', '/posters'] as const; + export function sitePaths(): string[] { - return Object.keys(pageFiles) + const own = Object.keys(pageFiles) .map((file) => file.replace(/^\.\.\/pages\//, '').replace(/\.astro$/, '')) .filter((name) => name !== '404' && !name.startsWith('_') && !name.startsWith('[')) - .map((name) => (name === 'index' ? '/' : `/${name}`)) + .map((name) => (name === 'index' ? '/' : `/${name}`)); + return [...new Set([...own, ...PORTAL_PAGES])] .sort((a, b) => (a === '/' ? -1 : b === '/' ? 1 : a.localeCompare(b))); } diff --git a/src/pages/404.astro b/src/pages/404.astro index 484f2fc..ee65fc2 100644 --- a/src/pages/404.astro +++ b/src/pages/404.astro @@ -6,6 +6,7 @@ import WaveBackground from '../components/WaveBackground.astro'; const links = [ { href: '/', label: 'Startsidan' }, { href: '/portfolio', label: 'Portfolio' }, + { href: '/posters', label: 'Posters' }, { href: '/tjanster', label: 'Tjänster och bokning' }, { href: '/contact', label: 'Kontakt' }, ]; diff --git a/src/pages/about.astro b/src/pages/about.astro deleted file mode 100644 index ee17719..0000000 --- a/src/pages/about.astro +++ /dev/null @@ -1,137 +0,0 @@ ---- -// src/pages/about.astro -import MainLayout from '../layouts/MainLayout.astro'; -import { Image } from 'astro:assets'; -import profileImg from '../assets/images/about/profile.jpg'; -import WaveBackground from '../components/WaveBackground.astro'; -import { business, basedIn } from '../data/business'; ---- - - - - - -
- -
- -
- -
-
-
- Porträtt av fotografen Linus Olofsson -
-
- -
-

- Levande ögonblick,
- fångade i hög upplösning. -

- -
-

- Hej, jag heter {business.person.name} och är fotografen bakom {business.brand}. -

-

- Jag är baserad i {basedIn()}. Allt började med en enkel nyfikenhet på hur ljuset formar sättet vi ser världen på. Med tiden blev nyfikenheten en besatthet av att fånga detaljerna som de flesta missar – texturer, strukturer, kontraster och ögonblick som passerar obemärkt förbi. -

-

- Jag hittar skönheten i det lilla: en sten, ett träd, en tom gata, en stilla skogsstig eller uttrycket hos en främling som går förbi. -

-

- Oavsett om det är de invecklade mönstren i en makrobild, naturens stora tystnad eller stadens energi är målet alltid detsamma – att skapa bilder som förmedlar känslor och berättar en historia utan ord. -

-

{business.recognition}

-
- -
-

Linus Olofsson

-
- - -
- -
-
-
-

Vad har jag i kameraväskan?

- -
- -
-
- -
-

Kamerahus

-

{business.camera}

-

En pålitlig följeslagare som har varit med mig på otaliga äventyr.

-
- -
-
- -
-

Objektiv

-
    - {business.lenses.map((lens) =>
  • {lens}
  • )} -
  • och ett par extra batterier
  • -
-
- -
-
- -
-

Redigering

-

{business.editing}

-

Här får råfilerna liv. Jag fokuserar på naturliga toner och kontrast.

-
- -
-
- -
-
- - \ No newline at end of file diff --git a/src/pages/contact.astro b/src/pages/contact.astro deleted file mode 100644 index dc7dc39..0000000 --- a/src/pages/contact.astro +++ /dev/null @@ -1,141 +0,0 @@ ---- -// src/pages/contact.astro -import MainLayout from '../layouts/MainLayout.astro'; -import WaveBackground from '../components/WaveBackground.astro'; -import { business } from '../data/business'; ---- - - -
- -
- -
- -
- -
-
-

Kontakta mig

-

- Har du frågor om någon av mina bilder, vill du samarbeta eller bara säga hej? - Hör av dig via sociala medier eller mejla mig direkt. -

-

- Vill du boka en fotografering? Skicka en förfrågan i kundportalen så återkommer jag med en offert. - Läs mer om hur det går till under Tjänster. -

- Skicka en bokningsförfrågan -
- -
-

Du hittar mig också på

- -
- - - - - - - - - - - - - -
-
-
- -
-
- -
-
-

Jag ser fram emot att höra av dig

-

Hör gärna av dig med frågor, förfrågningar eller bara för att prata.

-
- -
-
- - - -
-
-

Mejla mig på

- - {business.email} - -
- -
-

Kopierad!

-
-
- -
-

Eller skriv till mig på Instagram

- - {business.social.instagram.handle} - -
-
-
- -
-
- -
- - diff --git a/src/pages/llms.txt.ts b/src/pages/llms.txt.ts index 05fdb88..45a9c62 100644 --- a/src/pages/llms.txt.ts +++ b/src/pages/llms.txt.ts @@ -40,7 +40,8 @@ export const GET: APIRoute = ({ site }) => { `- [Tjänster och vanliga frågor](${url('/tjanster')}): vad du kan boka, hur bokningen går till och svar på vanliga frågor`, `- [Portfolio](${url('/portfolio')}): urval av fotografier`, `- [Om mig](${url('/about')}): ${business.person.name}, bakgrund och utrustning`, - `- [Kontakt](${url('/contact')}): e-post, Instagram och bokning`, + `- [Posters](${url('/posters')}): fotografier som posters, välj storlek och material`, + `- [Kontakt och samarbeten](${url('/contact')}): samarbeten, reklam och sponsring`, '', '## Vanliga frågor', '', diff --git a/src/pages/portfolio.astro b/src/pages/portfolio.astro index 15e5df6..1527bdb 100644 --- a/src/pages/portfolio.astro +++ b/src/pages/portfolio.astro @@ -90,23 +90,20 @@ const galleryItems = await Promise.all(Object.entries(imageFiles).map(async ([re ].filter(Boolean).join(' '); const alt = meta.alt?.trim() || fallbackAlt.charAt(0).toUpperCase() + fallbackAlt.slice(1); - // Nedladdning/lightbox: nya, EXIF-fria JPG:er i 800/1600/3000 px - // (Astro hoppar över bredder större än originalet). + // Lightbox: EXIF-fria JPG:er i 800/1600/3000 px (Astro hoppar över bredder större än originalet). + // Ingen nedladdning längre (Linus 2026-11-06): bilderna säljs som posters i stället. const jpg = await getImage({ src: file.default, format: 'jpg', quality: 88, width: 800, widths: [800, 1600, 3000] }); const variants = [...jpg.srcSet.values].sort((a, b) => widthOf(a.descriptor ?? '') - widthOf(b.descriptor ?? '')); - const small = variants[0]?.url ?? jpg.src; const large = variants[variants.length - 1]?.url ?? jpg.src; - const medium = variants.find((v) => widthOf(v.descriptor ?? '') === 1600)?.url ?? large; - const assetId = key.match(/^immich-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})-/i)?.[1]?.toLowerCase() ?? null; const statKey = ('portfolio/' + key.replace(/[^\w\-.]/g, '_')).slice(0, 150); return { img: file.default, key, alt, title: meta.title ?? null, year, month, monthIndex, fullDate, model: exifData?.Model, fStop, shutterSpeed, iso, focalLength, - dlS: small, dlM: medium, dlL: large, srcset: jpg.srcSet.attribute, - assetId, statKey, + large, srcset: jpg.srcSet.attribute, + statKey, }; })); @@ -157,17 +154,13 @@ const uniqueMonths = [...new Map(allMonths.map(item => [item.name, item])).value
-
- - -
+
+ Köp som poster +
Brännvidd--
Bländare--
@@ -249,21 +233,6 @@ const uniqueMonths = [...new Map(allMonths.map(item => [item.name, item])).value
- - @@ -276,7 +245,7 @@ const uniqueMonths = [...new Map(allMonths.map(item => [item.name, item])).value function statsItem(item?: HTMLElement): string { return item?.dataset.stat || 'portfolio/okand'; } - function stat(kind: 'view' | 'open' | 'download', item: string) { + function stat(kind: 'view' | 'open', item: string) { try { const body = new Blob([JSON.stringify({ kind, item })], { type: 'text/plain' }); navigator.sendBeacon?.(STATS_URL, body); @@ -348,115 +317,6 @@ const uniqueMonths = [...new Map(allMonths.map(item => [item.name, item])).value document.body.style.overflow = 'hidden'; } - // --- NEDLADDNING (Small/Medium/Large ur repot, Original via proxy) --- - const dlBtn = document.getElementById('lb-download'); - const dlMenu = document.getElementById('lb-sizes'); - const pwModal = document.getElementById('pw-modal'); - const pwInput = document.getElementById('pw-input') as HTMLInputElement; - const pwError = document.getElementById('pw-error'); - - // Small/Medium/Large är EXIF-fria JPG:er som Astro tar fram vid bygget - // (data-dl-s/m/l). Original hämtas från Immich via proxyn och kräver lösenord. - function urlForSize(size: string): string | null { - const item = visibleItems[currentImageIndex]; - if (!item) return null; - if (size === 's') return item.dataset.dlS || null; - if (size === 'm') return item.dataset.dlM || null; - if (size === 'l') return item.dataset.dlL || null; - if (size === 'orig') return item.dataset.asset ? `/download/original?asset=${item.dataset.asset}` : null; - return null; - } - - function fileNameFor(size: string): string { - const item = visibleItems[currentImageIndex]; - const key = (item?.dataset.key || 'bild').replace(/[^\w\-]/g, '_'); - return `imagesbyolofsson-${key}-${size}.jpg`; - } - - function startDownload(url: string, filnamn?: string) { - const a = document.createElement('a'); - a.href = url; - a.download = filnamn || ''; - document.body.appendChild(a); - a.click(); - a.remove(); - } - - const dlArrow = document.getElementById('lb-dl-arrow'); - - function visaMeny(visa: boolean) { - if (!dlMenu) return; - // Pilen går grå medan menyn är öppen och tillbaka till vanligt när den stängs - // (både vid val av storlek och när man klickar bort menyn). - dlArrow?.classList.toggle('text-gray-500', visa); - dlBtn?.setAttribute('aria-expanded', visa ? 'true' : 'false'); - if (visa) { - dlMenu.classList.remove('hidden'); - requestAnimationFrame(() => dlMenu.classList.remove('opacity-0', 'translate-y-2')); - } else { - dlMenu.classList.add('opacity-0', 'translate-y-2'); - setTimeout(() => dlMenu.classList.add('hidden'), 180); - } - } - - dlBtn?.addEventListener('click', (e) => { - e.stopPropagation(); - visaMeny(dlMenu?.classList.contains('hidden') ?? true); - }); - - document.addEventListener('click', (e) => { - if (!dlMenu || dlMenu.classList.contains('hidden')) return; - if (!dlMenu.contains(e.target as Node) && !dlBtn?.contains(e.target as Node)) visaMeny(false); - }); - - document.querySelectorAll('.dl-opt').forEach((opt) => { - opt.addEventListener('click', () => { - const size = (opt as HTMLElement).dataset.size || 'l'; - visaMeny(false); - const url = urlForSize(size); - if (!url) { alert('Den storleken saknas för den här bilden.'); return; } - stat('download', statsItem(visibleItems[currentImageIndex])); - if (size === 'orig') { - // Lösenordet sparas bara för fliken, aldrig i localStorage. - const sparat = sessionStorage.getItem('dlpw'); - if (sparat) { startDownload(`${url}&pw=${encodeURIComponent(sparat)}`); return; } - pwError?.classList.add('hidden'); - if (pwInput) pwInput.value = ''; - pwModal?.classList.remove('hidden'); - setTimeout(() => pwInput?.focus(), 50); - } else { - startDownload(url, fileNameFor(size)); - } - }); - }); - - async function forsokOriginal() { - const pw = pwInput?.value || ''; - if (!pw) return; - const url = urlForSize('orig'); - if (!url) return; - // HEAD först: då får vi 401 utan att dra igång en 70 MB-nedladdning. - try { - const r = await fetch(`${url}&pw=${encodeURIComponent(pw)}`, { method: 'HEAD' }); - if (r.status === 401) { pwError?.classList.remove('hidden'); return; } - if (!r.ok) { alert('Kunde inte hämta originalet just nu.'); return; } - } catch { /* nätverksfel -> försök ändå nedan */ } - sessionStorage.setItem('dlpw', pw); - pwModal?.classList.add('hidden'); - startDownload(`${url}&pw=${encodeURIComponent(pw)}`); - } - - document.getElementById('pw-ok')?.addEventListener('click', forsokOriginal); - pwInput?.addEventListener('keydown', (e) => { if ((e as KeyboardEvent).key === 'Enter') forsokOriginal(); }); - document.getElementById('pw-cancel')?.addEventListener('click', () => pwModal?.classList.add('hidden')); - - document.addEventListener('keydown', (e) => { - if ((e as KeyboardEvent).key === 'Escape' && !dlMenu?.classList.contains('hidden')) { - e.stopImmediatePropagation(); - visaMeny(false); - } - }, true); - function closeLightbox() { lightbox?.classList.add('opacity-0'); setTimeout(() => { @@ -468,8 +328,6 @@ const uniqueMonths = [...new Map(allMonths.map(item => [item.name, item])).value function updateLightboxContent() { const item = visibleItems[currentImageIndex]; if (!item) return; - visaMeny(false); - // srcset först så att webbläsaren väljer 800/1600/3000 px efter skärmen. lbImg.srcset = item.dataset.srcset || ''; lbImg.src = item.dataset.src || ''; diff --git a/src/pages/privacy.astro b/src/pages/privacy.astro index 916f5e5..af72057 100644 --- a/src/pages/privacy.astro +++ b/src/pages/privacy.astro @@ -37,7 +37,6 @@ const link = 'text-white underline hover:text-gray-300';