Description

EXIF-based photo blog with built-in admin panel, Vercel Postgres as CMS, and Vercel Blob for image storage.

npx boilerapp photo-blog

文档

📷 EXIF Photo Blog

https://github.com/sambecker/exif-photo-blog/assets/169298/4253ea54-558a-4358-8834-89943cfbafb4

Deploy with Vercel

🎬  Demo

https://photos.sambecker.com

✨  Features

  • Built-in auth
  • Photo upload with EXIF extraction
  • Organize photos by tag
  • Infinite scroll
  • Light/dark mode
  • Automatic OG image generation
  • CMD-K menu with photo search
  • AI-generated text descriptions
  • RSS/JSON feeds
  • Support for Fujifilm recipes and film simulations
<img src="/readme/og-image-share.png" alt="OG Image Preview" width=600 />

🛠️  Installation

1. Deploy to Vercel

  1. Click Deploy
  2. Add required storage (Vercel Postgres + Vercel Blob) as part of template installation
  3. Configure environment variable for production domain in project settings
    • NEXT_PUBLIC_DOMAIN (e.g., photos.domain.com—used in absolute urls and seen in navigation if no explicit nav title is set)

2. Setup Auth

  1. Generate auth secret and add to environment variables:
    • AUTH_SECRET
  2. Add admin user to environment variables:
    • ADMIN_EMAIL
    • ADMIN_PASSWORD
  3. Trigger redeploy
    • Visit project on Vercel, navigate to "Deployments" tab, click ••• button next to most recent deployment, and select "Redeploy"

3. Upload your first photo 🎉

  1. Visit /admin
  2. Sign in with credentials supplied in Step 2
  3. Click "Upload Photos"
  4. Add optional title
  5. Click "Create"

🔄  Receiving updates

If you don't plan to change the code, or don't mind making your updates public, consider forking this repo to easily receive future updates. If you've already set up your project on Vercel see these migration instructions.

💻  Local development

  1. Clone code
  2. Run pnpm i to install dependencies
  3. If necessary, install Vercel CLI and authenticate by running vercel login
  4. Run vercel link to connect CLI to your project
  5. Run vercel dev to start dev server with Vercel-managed environment variables

See FAQ for limitations of local development

🎨  Customization

Content

  • NEXT_PUBLIC_META_TITLE (seen in search results and browser tab)
  • NEXT_PUBLIC_META_DESCRIPTION (seen in search results)
  • NEXT_PUBLIC_NAV_TITLE (seen in top-right navigation, defaults to domain when not configured)
  • NEXT_PUBLIC_NAV_CAPTION (seen in top-right navigation, beneath title)
  • NEXT_PUBLIC_PAGE_ABOUT (seen in grid sidebar—accepts rich formatting tags: <b>, <strong>, <i>, <em>, <u>, <br>)
  • NEXT_PUBLIC_DOMAIN_SHARE (seen in share modals where a shorter url may be desirable)

Performance

⚠️ Enabling may result in increased project usage. See FAQ for static optimization troubleshooting hints.

  • NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS = 1 enables static optimization for photo pages (p/[photoId]), i.e., renders pages at build time
  • NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES = 1 enables static optimization for OG images, i.e., renders images at build time
  • NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES = 1 enables static optimization for photo categories (tag/[tag], shot-on/[make]/[model], etc.), i.e., renders pages at build time
  • NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORY_OG_IMAGES = 1 enables static optimization for photo category (tag/[tag], shot-on/[make]/[model], etc.) OG images, i.e., renders images at build time
  • NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS = 1 prevents photo uploads being compressed before storing
  • NEXT_PUBLIC_IMAGE_QUALITY = 1-100 controls the quality of large photos
  • NEXT_PUBLIC_BLUR_DISABLED = 1 prevents image blur data being stored and displayed (potentially useful for limiting Postgres usage)

AI text generation

To auto-generate text descriptions of photo:

  1. Setup OpenAI
    • Create OpenAI account and fund it (see thread if you're having issues)
    • Setup usage limits to avoid unexpected charges (recommended)
    • Set OPENAI_BASE_URL in order to use alternate OpenAI-compatible providers (experimental)
  2. Generate API key and store in environment variable OPENAI_SECRET_KEY (enable Responses API write access if customizing permissions)
  3. Add rate limiting (recommended)
  4. Configure auto-generated fields (optional)
    • Set which text fields auto-generate when uploading a photo by storing a comma-separated list, e.g., AI_TEXT_AUTO_GENERATED_FIELDS = title,semantic
    • Accepted values:
      • all
      • title (default)
      • caption
      • tags (default)
      • semantic (default)
      • none

Location services

To add location meta to entities like albums:

  1. Setup Google Places API
  2. Store API key in GOOGLE_PLACES_API_KEY
  3. Add rate limiting (recommended)

Rate limiting

Create Upstash Redis store from storage tab of Vercel dashboard and link to your project (if required, add environment variable prefix EXIF) in order to enable rate limiting—no further configuration necessary.

Categories

  • NEXT_PUBLIC_CATEGORY_VISIBILITY
    • Comma-separated value controlling which photo sets appear in grid sidebar and CMD-K menu, and in what order. For example, you could move cameras above tags, and hide film simulations, by updating to cameras,tags,lenses,recipes.
    • Accepted values:
      • recents (default)
      • years
      • tags (default)
      • cameras (default)
      • lenses (default)
      • recipes (default)
      • films (default)
      • focal-lengths
  • NEXT_PUBLIC_HIDE_CATEGORIES_ON_MOBILE = 1 prevents categories displaying on mobile grid view
  • NEXT_PUBLIC_HIDE_CATEGORY_IMAGE_HOVERS = 1 prevents images displaying when hovering over category links
  • NEXT_PUBLIC_EXHAUSTIVE_SIDEBAR_CATEGORIES = 1 always shows expanded sidebar content
  • NEXT_PUBLIC_HIDE_TAGS_WITH_ONE_PHOTO = 1 to only show tags with 2 or more photos

Sorting

  • NEXT_PUBLIC_DEFAULT_SORT
    • Sets default sort on grid/full homepages
    • Accepted values:
      • taken-at (default)
      • taken-at-oldest-first
      • uploaded-at
      • uploaded-at-oldest-first
  • NEXT_PUBLIC_NAV_SORT_CONTROL
    • Controls sort UI on grid/full homepages
    • Accepted values:
      • none
      • toggle (default)
      • menu
  • Color-based sorting (experimental)
    • NEXT_PUBLIC_SORT_BY_COLOR = 1 enables color-based sorting (forces nav sort control to "menu," flags photos missing color data in admin dashboard)—color identification benefits greatly from AI being enabled
    • NEXT_PUBLIC_COLOR_SORT_STARTING_HUE controls which colors start first (accepts a hue of 0 to 360, default: 80)
    • NEXT_PUBLIC_COLOR_SORT_CHROMA_CUTOFF controls which colors are considered sufficiently vibrant (accepts a chroma of 0 to 0.37, default: 0.05):
  • NEXT_PUBLIC_PRIORITY_BASED_SORTING = 1 takes priority field into account when sorting photos (⚠️ enabling may have performance consequences)

Display

  • NEXT_PUBLIC_HIDE_KEYBOARD_SHORTCUT_TOOLTIPS = 1 hides keyboard shortcut hints in areas like the main nav, and previous/next photo links
  • NEXT_PUBLIC_HIDE_EXIF_DATA = 1 hides EXIF data in photo details and OG images (potentially useful for portfolios, which don't focus on photography)
  • NEXT_PUBLIC_HIDE_ZOOM_CONTROLS = 1 hides fullscreen photo zoom controls
  • NEXT_PUBLIC_HIDE_TAKEN_AT_TIME = 1 hides taken at time from photo meta
  • NEXT_PUBLIC_HIDE_REPO_LINK = 1 removes footer link to repo

Grid

  • NEXT_PUBLIC_GRID_HOMEPAGE = 1 shows grid layout on homepage
  • NEXT_PUBLIC_GRID_ASPECT_RATIO = 1.5 sets aspect ratio for grid tiles (defaults to 1—setting to 0 removes the constraint)
  • NEXT_PUBLIC_SHOW_LARGE_THUMBNAILS = 1 ensures large thumbnails on photo grid views (if not configured, density is based on aspect ratio)

Design

  • NEXT_PUBLIC_DEFAULT_THEME = light | dark sets preferred initial theme (defaults to system when not configured)
  • NEXT_PUBLIC_MATTE_PHOTOS = 1 constrains the size of each photo, and displays a surrounding border, potentially useful for photos with tall aspect ratios (colors can be customized via NEXT_PUBLIC_MATTE_COLOR + NEXT_PUBLIC_MATTE_COLOR_DARK)

Settings

  • NEXT_PUBLIC_GEO_PRIVACY = 1 disables collection/display of location-based data (⚠️ re-compresses uploaded images in order to remove GPS information)
  • NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS = 1 enables public photo downloads for all visitors (⚠️ may result in increased bandwidth usage)
  • NEXT_PUBLIC_SOCIAL_NETWORKS
    • Comma-separated list of social networks to show in share modal
    • Accepted values:
      • x (default)
      • threads
      • facebook
      • linkedin
      • all
      • none
  • NEXT_PUBLIC_SITE_FEEDS = 1 enables feeds at /feed.json and /rss.xml
  • NEXT_PUBLIC_OG_TEXT_ALIGNMENT = BOTTOM keeps OG image text bottom aligned (default is top)

Scripts & Analytics

  • Web Analytics
    1. Open project on Vercel
    2. Click "Analytics" tab
    3. Follow "Enable Web Analytics" instructions (@vercel/analytics already included)
  • Speed Insights
    1. Open project on Vercel
    2. Click "Speed Insights" tab
    3. Follow "Enable Speed Insights" instructions (@vercel/speed-insights already included)
  • PAGE_SCRIPT_URLS
    • comma-separated list of URLs to be added to the bottom of the body tag via "next/script"
    • urls must begin with 'https'
    • ⚠️ this will invoke arbitrary script execution on every page—use with caution

Debugging

  • DISABLE_DEBUG_OUTPUTS = 1
    • removes build identifier in <head />
    • disables /admin/configuration/export.json

Alternate storage providers

Only one storage adapter—Vercel Blob, Cloudflare R2, AWS S3, or MinIO—can be used at a time. Ideally, this is configured before photos are uploaded (see Issue #34 for migration considerations). If you have multiple adapters, you can set one as preferred by storing aws-s3, cloudflare-r2, minio, or vercel-blob in NEXT_PUBLIC_STORAGE_PREFERENCE. See FAQ regarding unsupported providers.

Cloudflare R2

  1. Setup bucket
    [{
        "AllowedHeaders": ["*"],
        "AllowedMethods": [
          "GET\
    

Prix

Gratuit

FREE

评论 (0)

常见问题

常见问题解答 (FAQ)

有问题?我们有答案。如果您找不到想要的答案,请随时联络我们。

Boilerapp 是一个专门用于分享 Boilerplates、入门套件(Starter Kits)和项目模版的开发者社区平台。我们的目标很简单:为您节省初始配置(Setup)的时间,让您可以专注于真正重要的代码。无论您是在寻找简单的代码库还是完整的 SaaS 项目,都能在这里找到。

还有其他问题?

我们的团队随时为您提供帮助。联络我们,我们将尽快回复。