Skip to content

Repository files navigation

@maigic/blogtool

Upload markdown local images to S3/R2 storage with auto watermark and link replacement.

中文文档

Features

  • Scan markdown files for local image references (![alt](path) and <img src="path">)
  • Upload images to any S3-compatible storage (Cloudflare R2, AWS S3, MinIO, etc.)
  • Auto watermark with configurable opacity, size, position
  • MD5 hash-based deduplication across files
  • Replace local paths with public URLs in markdown
  • CLI and programmatic API

Install

npm install @maigic/blogtool

Quick Start

1. Initialize config

npx blogtool init

This generates blogtool.config.mjs in the current directory with all available options and comments.

2. Configure storage credentials

Edit blogtool.config.mjs to fill in your S3/R2 credentials, or set environment variables:

export R2_ACCOUNT_ID=your_account_id
export R2_ACCESS_KEY_ID=your_access_key
export R2_SECRET_ACCESS_KEY=your_secret_key
export R2_BUCKET=your_bucket

3. Preview and upload

# Preview what will be processed
npx blogtool --dry-run

# Upload images and replace links
npx blogtool

# Process a single file
npx blogtool -f src/content/blog/my-post.md

# Use a custom config file
npx blogtool -c ./custom.config.mjs

CLI Reference

Usage: blogtool [options] [command]

Upload markdown local images to S3/R2 with watermark and link replacement

Commands:
  init [options]       generate a config file template

Options:
  -c, --config <path>   config file path (default: "blogtool.config.mjs")
  --dry-run             preview without uploading
  -f, --file <path>     process a single markdown file
  -V, --version         output the version number
  -h, --help            display help for command

init

Usage: blogtool init [options]

generate a config file template

Options:
  -o, --output <path>   output file path (default: "blogtool.config.mjs")
  -h, --help            display help for command

Programmatic API

import { processImages } from "@maigic/blogtool";

const result = await processImages({
  storage: {
    endpoint: "https://xxx.r2.cloudflarestorage.com",
    credentials: {
      accessKeyId: "...",
      secretAccessKey: "...",
    },
    bucket: "my-bucket",
    publicDomain: "img.example.com",
  },
  watermark: {
    imagePath: "./logo.svg",
  },
  markdown: {
    dirs: ["./src/content/blog"],
  },
  dryRun: false,
});

console.log(`Uploaded: ${result.uploaded}, Skipped: ${result.skipped}`);

Configuration

storage

Option Type Required Default Description
endpoint string Yes - S3-compatible endpoint URL
region string No "auto" Region
credentials.accessKeyId string Yes - Access key ID
credentials.secretAccessKey string Yes - Secret access key
bucket string Yes - Bucket name
publicDomain string Yes - Public access domain
keyPrefix string No "blog" Key prefix in bucket

Uploaded files are stored as {keyPrefix}/{hash[0:2]}/{hash}{ext}, e.g. blog/4e/4ea7a...png.

watermark

Option Type Required Default Description
imagePath string Yes - Path to watermark image (SVG or PNG)
opacity number No 0.15 Opacity (0-1)
maxSize number No 120 Max watermark size in px
minImageSize number No 400 Skip watermark if image short side is below this
padding number No 16 Padding from edge in px
position string No "bottom-left" bottom-left, bottom-right, top-left, top-right

Watermark size scales dynamically: 8% of the image's short side, capped at maxSize.

For SVG watermarks, paths are extracted and rendered in white with the specified opacity. Background rects are removed automatically.

markdown

Option Type Required Default Description
dirs string[] Yes - Directories to scan for markdown files
filePattern string No "**/*.md" Glob pattern (currently supports recursive flag)

How It Works

  1. Scans markdown files in configured directories
  2. Extracts local image paths (relative or absolute, not http/data URIs)
  3. Reads each image and optionally applies watermark via Sharp
  4. Computes MD5 hash of the processed image buffer
  5. Checks if the file already exists in storage (HEAD request)
  6. Uploads if new, skips if exists
  7. Replaces local paths in markdown with public URLs
  8. Writes updated markdown back to disk

FAQ

Cloudflare R2 Setup

  1. Create an R2 bucket in Cloudflare dashboard
  2. Go to Manage R2 API Tokens and create a token with "Object Read & Write" permission
  3. Copy the endpoint, access key ID, and secret access key
  4. Optionally bind a custom domain to the bucket for public access

Works with AWS S3 / MinIO?

Yes, any S3-compatible storage is supported. Just set the appropriate endpoint and region.

License

MIT

About

Upload markdown local images to S3/R2 storage with auto watermark and link replacement.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages