Author SHA1 Message Date
imagesbyolofsson 85bd49b0c3 Merge pull request 'SEO & AEO: everything from the PDF review (robots, sitemap, 404, meta/OG, JSON-LD, Swedish text, /tjanster, EXIF clean, alt texts, Admin@, Örebro)' (#1) from seo-aeo-2026-10-27 into main
Reviewed-on: #1
2026-10-27 23:45:31 +00:00
Linus OlofssonandClaude Opus 5.5 7207e351d4 Portfolio alt texts: what each photo shows (147/147, Swedish, no names/places/camera), written with Cleura gemma-4 vision and checked by eye
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WvE3C43Uwtv2vtnoFBLDw7
2026-10-27 23:27:17 +01:00
Linus OlofssonandClaude Opus 5.5 03a70efcb6 Verified facts: Örebro / Örebro län (his public Flickr + LinkedIn) and the Facebook page in sameAs and llms.txt
Phone, prices, packages, delivery time and years stay null until Linus confirms them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WvE3C43Uwtv2vtnoFBLDw7
2026-10-27 23:14:55 +01:00
Linus OlofssonandClaude Opus 5.5 15560445ce Contact e-mail: Admin@imagesbyolofsson.se everywhere (Linus 2026-10-27), as on the old contact page
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WvE3C43Uwtv2vtnoFBLDw7
2026-10-27 23:08:40 +01:00
Linus OlofssonandClaude Opus 5.5 1e921dfe37 fix(images): stop publishing EXIF/GPS originals, responsive images, crawlable gallery
- Homepage images: <Picture> from src/assets (AVIF/WebP/JPG srcset, width/
  height, first image eager + fetchpriority high, rest lazy), Swedish alt
  texts. (The duplicate EXIF-laden copies in public/3d-scroll were deleted
  in 30e385e; README links now point at src/assets.)
- Portfolio: originals are no longer referenced (no .src access), so Astro
  does not copy them to dist/. Small/Medium/Large downloads and the lightbox
  srcset are EXIF-free JPGs from getImage. Old -s/-m files are no longer
  imported.
- Alt texts: Swedish fallback from date (EXIF or filename), never camera
  model or "undefined"; portfolio-meta.ts overrides per image.
- Gallery items are <a href> to the large image (lightbox on click);
  download options are buttons; no empty H3, no H4 for the password box.
- Stats beacon unchanged; item keys computed at build time (same format).
- Script moved inside the layout so it is no longer rendered after </html>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WvE3C43Uwtv2vtnoFBLDw7
2026-10-27 19:13:24 +01:00
Linus OlofssonandClaude Opus 5.5 4f1b468f75 feat(server): redirects, real 404, caching and security headers (nginx + Caddy)
- www -> apex 301, old URLs (/my-people, /animales-and-nature,
  /celebrations-with-family, /northen-lights) -> /portfolio 301,
  trailing slash and .html -> clean URL 301.
- try_files $uri $uri.html, otherwise 404.html with status 404.
- /_astro/*: public, max-age=31536000, immutable (path-based map, no
  duplicate Cache-Control headers); HTML must-revalidate.
- HSTS, Referrer-Policy, Permissions-Policy, X-Frame-Options, CSP that
  allows the Meta Pixel and the stats beacon to customer.imagesbyolofsson.se.
- deploy/caddy-site.snippet: the same rules as importable Caddy snippets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WvE3C43Uwtv2vtnoFBLDw7
2026-10-27 19:13:07 +01:00
Linus OlofssonandClaude Opus 5.5 206d31d9c2 feat(content): Swedish copy, new /tjanster page with booking flow and FAQ
- New /tjanster: genres from existing copy, where/price (contact for offer
  while unknown), booking flow via customer portal, delivery in a private
  gallery, originals after payment, FAQ (also emitted as FAQPage).
- Homepage: tagline + text section on who/what/where/how to book.
- About: Swedish, photographer framing instead of "by day/by night",
  Adobe line. Contact: Swedish, plain mailto info@, booking CTA, fixed
  unclosed div. Privacy: Swedish (Google Limited Use line kept in English).
- Unique Swedish title and description per page; scripts moved inside the
  layout (no <script> after </html>).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WvE3C43Uwtv2vtnoFBLDw7
2026-10-27 19:13:07 +01:00
Linus OlofssonandClaude Opus 5.5 f452d6d237 feat(seo): site config, sitemap, robots.txt, llms.txt and a real 404 page
- astro.config: site, trailingSlash "never", build.format "file".
- /sitemap-index.xml + /sitemap-0.xml as prerendered endpoints (no new
  dependency); pages are derived from src/pages/*.astro, 404 excluded.
- robots.txt: all bots allowed, AI crawlers/training bots explicitly
  allowed (Linus's decision), /download/ disallowed, sitemap line.
- /llms.txt generated from business.ts, incl. usage-with-attribution note.
- 404.astro in Swedish with links back, noindex.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WvE3C43Uwtv2vtnoFBLDw7
2026-10-27 19:13:07 +01:00
Linus OlofssonandClaude Opus 5.5 30e385ec7f feat(seo): facts in src/data/business.ts, SEO + JSON-LD components in layout
- business.ts: single source of truth; unknown facts (city, area, prices,
  packages, phone, delivery time, experience, testimonials) are null and are
  skipped everywhere. faq.ts builds answers only from known facts.
- portfolio-meta.ts: data hook for real per-image alt texts.
- SEO.astro: title, description, canonical, Open Graph, Twitter, og:image
  (EXIF-free JPG via getImage, absolute URL).
- SchemaOrg.astro: @graph with WebSite, Person, ProfessionalService
  (sameAs, ReserveAction to the customer portal, offer catalog without
  prices), WebPage, optional FAQPage and BreadcrumbList.
- MainLayout: lang=sv, Swedish nav (adds Tjänster), consent banner and
  footer in Swedish, lyxlimo/karleksguiden line removed, info@ address.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WvE3C43Uwtv2vtnoFBLDw7
2026-10-27 19:13:07 +01:00
Claude (for Linus) ce5676f552 Anonymous portfolio statistics (opens/downloads per image, cookieless) + privacy text
Counts go to customer.imagesbyolofsson.se/api/stats/hit via sendBeacon. No cookie, IP or id stored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 11:41:59 +01:00
Linus OlofssonandClaude Opus 5.5 31d4e540ce feat(tracking): Meta Pixel behind cookie consent
- Meta Pixel (2453598878495749) loads only after the visitor clicks Accept;
  choice stored in localStorage (ibo-consent).
- Decline after a previous Accept clears _fbp/_fbc and reloads.
- "Cookie settings" link in the footer reopens the banner.
- Privacy page: removed "no tracking cookies", added Meta Pixel section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:11:36 +01:00
imagesbyolofssonandClaude Opus 5.5 3f02b323a3 feat(privacy): privacy policy page + footer link
Needed to publish the townhouse-backup Google app (Drive/Photos backup).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 23:46:24 +00:00
imagesbyolofsson e3c511b5d5 chore(portfolio): sync 2026-09-25 2026-09-25 09:20:03 +00:00
imagesbyolofsson ae51f41fdf chore(portfolio): sync 2026-09-20 2026-09-20 19:20:22 +00:00
portfolio-sync d875ae70d5 style(portfolio): mork hover, pilstatus och klickrorelse
- storleksraderna far en mork hinna vid hover i stallet for ljus
- pilen i knappen gar gra medan menyn ar oppen, vit igen nar den stangs
  (bade vid val av storlek och nar man klickar bort menyn)
- knappen trycks ner lite vid klick (active:scale-95)
2026-09-19 16:33:59 +00:00
portfolio-sync 841d831486 style(portfolio): dampad knapp intill EXIF-raden
- neutral-700 i stallet for helvit sa den inte skriker mot bilden
- tunn svart kant + ring/skugga for en sleak kontur
- knappen grupperad med EXIF i en hogergrupp; footern ar justify-between,
  darfor hamnade den tidigare mitt i raden
- menyn forankras till hoger pa desktop sa den inte gar utanfor kortet
2026-09-19 16:27:15 +00:00
portfolio-sync ce93f90250 style(portfolio): nedladdnings-UI i linje med sajtens formsprak
Knappen var kantig medan allt runt omkring ar rundat. Nu:
- rounded-full + hover:scale-105 som sajtens ovriga CTA-knappar
- nedladdningsikon, uppercase tracking-widest
- menyn rundad med backdrop-blur och mjuk in/ut-overgang
- losenordsrutan rundad med samma kort-kansla som lightboxen
- Escape stanger menyn innan lightboxen
2026-09-19 16:21:53 +00:00
portfolio-sync 59383f703f feat(portfolio): nedladdningsmeny med Small/Medium/Large/Original
DOWNLOAD-knapp i lightboxen oppnar en storleksmeny. S/M/L pekar pa filer i
repot; Original gar till /download/original och oppnar en losenordsruta.
Losenordet sparas i sessionStorage (bara fliken), och HEAD-anrop anvands sa
fel losenord ger 401 utan att dra igang en 70 MB-nedladdning.

🔴 Globen filtrerar nu bort -s/-m ur galleriet - utan det visades varje bild
tre ganger. Variantkartan (assetId -> hashade URL:er) genereras i frontmattern.
2026-09-19 16:11:13 +00:00
portfolio-sync 0d3da3d102 feat(portfolio): small/medium/large nedladdningsstorlekar
Genererar -s (800px) och -m (1600px) vid sidan av befintlig 3000px-fil.
En nedladdning fran Immich -> tre storlekar. Befintliga 82 bilder backfillade.

Original (27-70 MB/st, ~4 GB totalt) ligger INTE i repot utan hamtas live
via original-proxyn pa /download/original, skyddad av losenord + kontroll
att assetet faktiskt bar portfolio-taggen.
2026-09-19 16:03:56 +00:00
imagesbyolofsson c5d4d16596 chore(portfolio): sync 2026-09-19 2026-09-19 00:40:02 +00:00
imagesbyolofsson 1b2f26e7eb chore(portfolio): sync 2026-09-18 2026-09-18 23:45:34 +00:00
imagesbyolofsson e78ac3a4c6 Portfolio: multi-select filter (flera ar + manader samtidigt) 2026-09-15 00:49:41 +00:00
imagesbyolofsson d2de8d6c7e Content: new slogan, Sony A7RV gear, email Admin@imagesbyolofsson.se 2026-09-15 00:30:11 +00:00
208 changed files with 2558 additions and 421 deletions
+3 -3
View File
@@ -8,15 +8,15 @@ A modern, responsive photography portfolio website built with Astro, Tailwind CS
<table> <table>
<tr> <tr>
<td align="center"> <td align="center">
<img src="https://raw.githubusercontent.com/Images-by-Olofsson/homepage/main/public/3d-scroll/img1.jpg" width="250" alt="Portfolio Grid"> <img src="https://raw.githubusercontent.com/Images-by-Olofsson/homepage/main/src/assets/images/3d-scroll/img1.jpg" width="250" alt="Portfolio Grid">
<br><em> More </em> <br><em> More </em>
</td> </td>
<td align="center"> <td align="center">
<img src="https://raw.githubusercontent.com/Images-by-Olofsson/homepage/main/public/3d-scroll/img2.jpg" width="250" alt="3D Scroll Effect"> <img src="https://raw.githubusercontent.com/Images-by-Olofsson/homepage/main/src/assets/images/3d-scroll/img2.jpg" width="250" alt="3D Scroll Effect">
<br><em> At </em> <br><em> At </em>
</td> </td>
<td align="center"> <td align="center">
<img src="https://raw.githubusercontent.com/Images-by-Olofsson/homepage/main/public/3d-scroll/img3.jpg" width="250" alt="Hero Section"> <img src="https://raw.githubusercontent.com/Images-by-Olofsson/homepage/main/src/assets/images/3d-scroll/img3.jpg" width="250" alt="Hero Section">
<br><em>Imagesbyolofsson.se</em> <br><em>Imagesbyolofsson.se</em>
</td> </td>
</tr> </tr>
+9
View File
@@ -2,6 +2,15 @@ import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite'; import tailwindcss from '@tailwindcss/vite';
export default defineConfig({ export default defineConfig({
// Kanonisk adress: canonical, og:url, sitemap och JSON-LD byggs från denna.
site: 'https://imagesbyolofsson.se',
// En URL per sida: /about (aldrig /about/). Servern 301:ar /about/ -> /about.
trailingSlash: 'never',
build: {
// about.astro -> dist/about.html (inte dist/about/index.html).
// nginx/Caddy serverar /about via try_files $uri.html.
format: 'file',
},
vite: { vite: {
plugins: [tailwindcss()], plugins: [tailwindcss()],
}, },
+99
View File
@@ -0,0 +1,99 @@
# deploy/caddy-site.snippet
# Caddy-motsvarigheten till nginx.conf för imagesbyolofsson.se (statisk dist/ från `astro build`,
# build.format "file": /about -> about.html, 404 -> 404.html).
#
# Användning i Caddyfile (Caddy >= 2.7):
#
# import /etc/caddy/snippets/caddy-site.snippet
#
# www.imagesbyolofsson.se {
# import ibo_www_redirect
# }
# imagesbyolofsson.se {
# # Befintliga specialregler (t.ex. handle /download/original* -> Immich-proxyn)
# # ska ligga FÖRE importen, så att de vinner över try_files/file_server.
# import ibo_site /srv/imagesbyolofsson/dist
# }
#
# Argumentet är sökvägen till dist-katalogen på servern (justera till den riktiga).
(ibo_www_redirect) {
redir https://imagesbyolofsson.se{uri} permanent
}
(ibo_site) {
root * {args[0]}
encode zstd gzip
# --- Säkerhetsheaders ---
# CSP: tillåter Meta Pixel (laddas först efter samtycke), anonym statistik via sendBeacon
# till customer.imagesbyolofsson.se och Cloudflares egna skript (/cdn-cgi, ev. Web Analytics).
# 'unsafe-inline' behövs för samtyckesskriptet och Astros inlinade skript/stilar.
header {
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "strict-origin-when-cross-origin"
Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=()"
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://connect.facebook.net https://static.cloudflareinsights.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://www.facebook.com https://*.facebook.com; font-src 'self' data:; connect-src 'self' https://customer.imagesbyolofsson.se https://www.facebook.com https://*.facebook.com https://connect.facebook.net https://cloudflareinsights.com; frame-src https://www.facebook.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; object-src 'none'; upgrade-insecure-requests"
-Server
}
# --- Cache-Control (ömsesidigt uteslutande matchers) ---
@ibo_immutable path /_astro/*
header @ibo_immutable Cache-Control "public, max-age=31536000, immutable"
@ibo_media {
path *.jpg *.jpeg *.png *.webp *.avif *.gif *.svg *.ico *.woff *.woff2 *.ttf *.otf
not path /_astro/*
}
header @ibo_media Cache-Control "public, max-age=2592000"
@ibo_textfiles path *.xml *.txt
header @ibo_textfiles Cache-Control "public, max-age=3600"
@ibo_pages {
not path /_astro/*
not path *.jpg *.jpeg *.png *.webp *.avif *.gif *.svg *.ico *.woff *.woff2 *.ttf *.otf *.xml *.txt
}
header @ibo_pages Cache-Control "public, max-age=0, must-revalidate"
# --- 301:or ---
# Gamla indexerade adresser -> portfolion.
@ibo_old path /my-people /my-people/ /animales-and-nature /animales-and-nature/ /celebrations-with-family /celebrations-with-family/ /northen-lights /northen-lights/
redir @ibo_old /portfolio permanent
# /index.html -> /
redir /index.html / permanent
# /about.html -> /about (404-sidan hanteras separat nedan)
@ibo_htmlext {
path_regexp ibo_htmlext ^(/.+)\.html$
not path /404.html
}
redir @ibo_htmlext {re.ibo_htmlext.1}{?query} permanent
# Avslutande snedstreck: /about/ -> /about
@ibo_trailing path_regexp ibo_trailing ^(/.+)/$
redir @ibo_trailing {re.ibo_trailing.1}{?query} permanent
# 404-sidan ska bara synas som svar på okända adresser, inte som egen URL med 200.
@ibo_404page path /404 /404.html
error @ibo_404page 404
# --- Statiska filer ---
try_files {path} {path}.html
file_server
# Riktig 404: 404.html med status 404 (inte startsidan med 200).
handle_errors {
@ibo_notfound expression {err.status_code} == 404
handle @ibo_notfound {
root * {args[0]}
rewrite * /404.html
file_server {
status 404
}
}
}
}
+69 -25
View File
@@ -1,11 +1,20 @@
map $sent_http_content_type $cache_control_strategy { # nginx för imagesbyolofsson.se (Docker-image: dist/ i /usr/share/nginx/html).
default "public, max-age=2592000"; # Bygget använder build.format "file": /about -> about.html, 404 -> 404.html.
~^text/html "no-store, must-revalidate"; # Samma regler som Caddy-varianten i deploy/caddy-site.snippet.
~^text/css "public, max-age=31536000, immutable";
~^application/javascript "public, max-age=31536000, immutable"; # Cache-Control per sökväg (inte per Content-Type, så att /_astro/*.jpg också blir immutable).
~^application/json "no-store, must-revalidate"; map $uri $ibo_cache_control {
~^image/ "public, max-age=2592000"; default "public, max-age=0, must-revalidate";
~^font/ "public, max-age=31536000, immutable"; ~^/_astro/ "public, max-age=31536000, immutable";
~*\.(?:jpe?g|png|webp|avif|gif|svg|ico|woff2?|ttf|otf)$ "public, max-age=2592000";
~*\.(?:xml|txt)$ "public, max-age=3600";
}
# www -> apex (om www-trafik når den här containern).
server {
listen 80;
server_name www.imagesbyolofsson.se;
return 301 https://imagesbyolofsson.se$request_uri;
} }
server { server {
@@ -14,33 +23,68 @@ server {
root /usr/share/nginx/html; root /usr/share/nginx/html;
index index.html; index index.html;
# Relativa Location-headers: bakom Cloudflare/proxy blir det aldrig http://host:80/...
absolute_redirect off;
server_tokens off;
# Säkerhetsheaders. OBS: inga add_header i location-blocken nedan, annars ärvs inte dessa.
# CSP: tillåter Meta Pixel (connect.facebook.net/facebook.com, laddas först efter samtycke),
# den anonyma statistiken (sendBeacon till customer.imagesbyolofsson.se) och Cloudflares
# egna skript (/cdn-cgi, ev. Web Analytics). 'unsafe-inline' behövs för samtyckesskriptet
# och Astros inlinade skript/stilar; byt mot hashar (Astro experimental.csp) i ett senare steg.
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options "nosniff" always; add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=()" always;
add_header Cache-Control $cache_control_strategy always; add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://connect.facebook.net https://static.cloudflareinsights.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://www.facebook.com https://*.facebook.com; font-src 'self' data:; connect-src 'self' https://customer.imagesbyolofsson.se https://www.facebook.com https://*.facebook.com https://connect.facebook.net https://cloudflareinsights.com; frame-src https://www.facebook.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; object-src 'none'; upgrade-insecure-requests" always;
add_header Cache-Control $ibo_cache_control always;
# Gamla indexerade adresser -> närmaste nya sida.
location ~ ^/(?:my-people|animales-and-nature|celebrations-with-family|northen-lights)/?$ {
return 301 /portfolio;
}
# /index.html och /about.html -> rena URL:er (bara externa anrop; try_files nedan
# serverar filerna internt utan att gå hit).
location = /index.html {
return 301 /;
}
location ~ ^(?<ibo_noext>/.+)\.html$ {
return 301 $ibo_noext$is_args$args;
}
# Inga dubbletter med avslutande snedstreck: /about/ -> /about.
location ~ ^(?<ibo_noslash>/.+)/$ {
return 301 $ibo_noslash$is_args$args;
}
# Startsidan serveras direkt (ingen intern omdirigering till /index.html).
location = / {
try_files /index.html =404;
}
# Hashade Astro-filer: cachas ett år (immutable sätts via map ovan).
location ^~ /_astro/ {
access_log off;
try_files $uri =404;
}
# Originalnedladdning (/download/original) proxas till Immich någon annanstans
# (Cloudflare/Caddy). Finns den regeln i den här nginx: lägg den här ovanför.
location / { location / {
try_files $uri $uri/ $uri.html $uri/index.html =404; try_files $uri $uri.html =404;
} }
location /_astro/ { # Riktig 404: 404.html med status 404 (inte startsidan med 200).
access_log off; error_page 404 /404.html;
expires 1y; location = /404.html {
} internal;
location ~* \.(webp|avif|jpg|jpeg|png|gif|svg|ico|woff2?|ttf|otf|eot)$ {
access_log off;
expires 30d;
} }
gzip on; gzip on;
gzip_vary on; gzip_vary on;
gzip_min_length 1024; gzip_min_length 1024;
gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss image/svg+xml; gzip_types text/plain text/css text/xml application/xml application/json application/javascript application/xml+rss image/svg+xml;
error_page 404 /404.html;
location = /404.html {
internal;
}
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 700 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 542 KiB

+41
View File
@@ -0,0 +1,41 @@
# robots.txt – https://imagesbyolofsson.se
#
# Policy (beslut av Linus 2026-10-27): ALLA botar är välkomna, även AI-crawlers
# och träningsbotar. Innehållet får användas av AI-assistenter med hänvisning
# till Images by Olofsson (se /llms.txt). Cloudflare-sidan (AI Crawl Control /
# "Block AI bots" / hanterad robots.txt) ställs in separat så att den inte
# blockerar eller skriver över detta.
User-agent: *
Allow: /
# Lösenordsskyddad nedladdning av originalfiler – inget att indexera.
Disallow: /download/
# AI-crawlers och träningsbotar: uttryckligen tillåtna.
User-agent: GPTBot
User-agent: OAI-SearchBot
User-agent: ChatGPT-User
User-agent: ClaudeBot
User-agent: Claude-User
User-agent: Claude-SearchBot
User-agent: anthropic-ai
User-agent: CCBot
User-agent: Google-Extended
User-agent: Applebot
User-agent: Applebot-Extended
User-agent: Bytespider
User-agent: Amazonbot
User-agent: meta-externalagent
User-agent: meta-externalfetcher
User-agent: FacebookBot
User-agent: PerplexityBot
User-agent: Perplexity-User
User-agent: cohere-ai
User-agent: Diffbot
User-agent: YouBot
User-agent: DuckAssistBot
User-agent: MistralAI-User
Allow: /
Disallow: /download/
Sitemap: https://imagesbyolofsson.se/sitemap-index.xml
+32
View File
@@ -0,0 +1,32 @@
// Engangsjobb: skapa -s (800px) och -m (1600px) for redan synkade portfolio-bilder.
// Kallan ar den befintliga 3000px-filen i repot - ingen ny nedladdning fran Immich.
import fs from "node:fs";
import path from "node:path";
import sharp from "sharp";
const DIR = "src/assets/images/portfolio";
const SIZES = [{ suffix: "-s", dim: 800 }, { suffix: "-m", dim: 1600 }];
const QUALITY = 88;
const files = fs.readdirSync(DIR).filter(
(f) => /^immich-[0-9a-f-]{36}-/i.test(f) && /\.jpg$/i.test(f) && !/-[sm]\.jpg$/i.test(f)
);
console.log(`hittade ${files.length} large-bilder`);
let skapade = 0, hoppade = 0;
for (const f of files) {
const src = path.join(DIR, f);
const base = src.replace(/\.jpg$/i, "");
for (const { suffix, dim } of SIZES) {
const dest = `${base}${suffix}.jpg`;
if (fs.existsSync(dest)) { hoppade++; continue; }
await sharp(src)
.rotate()
.resize(dim, dim, { fit: "inside", withoutEnlargement: true })
.withMetadata()
.jpeg({ quality: QUALITY })
.toFile(dest);
skapade++;
}
}
console.log(`klart: ${skapade} skapade, ${hoppade} fanns redan`);
Binary file not shown.

After

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 937 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1017 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Some files were not shown because too many files have changed in this diff Show More