Total Pageviews

Showing posts with label nextjs. Show all posts
Showing posts with label nextjs. Show all posts

Monday, 29 June 2026

搭建基于nextjs的静态博客程序ddm

首先fork此项目https://github.com/ddadaal/ddadaal.me ,我fork后,得到的项目地址是https://github.com/brightmann/ddm

访问 https://github.com/brightmann/ddm/tree/master/contents ,在此处新建源帖(即add file),我新建了源帖20260629-fh/index.md,内容为:

---
id: fh
date: 2026-06-29 14:07
title: 战马
lang: cn
tags:
  - misc1
  - misc2
  - misc3
---

此处写正文或html codes.

( 详见https://github.com/brightmann/ddm/blob/master/contents/20260629-fh/index.md?plain=1) 

注意:  index.md所在的目录的名称必须是20260629-fh这样的格式,不能是fh这样的格式,否则发帖会失败。

然后,访问vercel.com/new ,导入项目https://github.com/brightmann/ddm ,

  Build Command栏填入pnpm build

 Install Command栏填入pnpm install

然后点击底部的deploy按钮,等待部署完成。部署完成后,我得到网址https://ddm-sandy.vercel.app/  

项目地址:

 https://github.com/ddadaal/ddadaal.me 

 https://github.com/brightmann/ddm

演示博客: 

 https://ddm-sandy.vercel.app/  

 https://ddm-sandy.vercel.app/articles 支持分页

 https://ddm-sandy.vercel.app/articles/fh 能显示视频

( https://github.com/brightmann/ddm/new/master?filename=.github%2Fworkflows%2Fdeno.yml&workflow_template=ci%2Fdeno ,这个还没细究)


Saturday, 27 June 2026

Next-Whois

 

Better Whois Lookup Tool With Modern UI. Support Domain/IPv4/IPv6/ASN/CIDR Whois Lookup And Powerful Features. Support RDAP protocol.

who.zmh.me

A fast, modern WHOIS/RDAP lookup tool built with Next.js.

English · 简体中文 · 繁體中文 · Русский · 日本語 · Deutsch · Français · 한국어

Deploy to Vercel Deploy to Netlify

Banner

Features

  • WHOIS & RDAP - Domain, IPv4, IPv6, ASN, CIDR lookup with RDAP-first, WHOIS fallback
  • Dynamic OG Images - Satori-based Open Graph image generation via /api/og
  • Responsive UI - Shadcn UI + Tailwind CSS, works across mobile, tablet, and desktop. PWA support.
  • Dark / Light Theme - System detection with manual toggle
  • History & Shortcuts - Local history with search, filter, and keyboard shortcuts
  • EPP Status Codes - Human-readable status descriptions with ICANN references
  • Registrar & NS Branding - Auto-detected icons for major registrars and nameserver providers
  • Domain Metrics - Moz DA/PA/Spam Score integration (optional)
  • Redis Caching - Server-side result caching with Cache-Control headers
  • Open API - /api/lookup for programmatic access, /api/og for image generation
  • i18n - English, Chinese (Simplified/Traditional), German, Russian, Japanese, French, Korean
  • API Documentation - Built-in /docs page with interactive examples

Community Forks

See great work from brilliant builders extending this project:

Deploy

Platforms (Recommended)

Vercel / Netlify / Zeabur

Docker

docker run -d -p 3000:3000 programzmh/next-whois-ui

Source Code

git clone https://github.com/zmh-program/next-whois-ui
cd next-whois-ui
pnpm install
pnpm dev

Environment Variables

Variable Description Default
NEXT_PUBLIC_SITE_TITLE Site title Next Whois
NEXT_PUBLIC_SITE_DESCRIPTION Site description
NEXT_PUBLIC_SITE_KEYWORDS Site keywords
NEXT_PUBLIC_HISTORY_LIMIT Max history items (-1 = unlimited) -1
NEXT_PUBLIC_MAX_WHOIS_FOLLOW Max domain WHOIS follow depth 0
MOZ_ACCESS_ID Moz API Access ID
MOZ_SECRET_KEY Moz API Secret Key
REDIS_HOST Redis host (empty = cache disabled)
REDIS_PORT Redis port 6379
REDIS_PASSWORD Redis password
REDIS_DB Redis database index 0
REDIS_CACHE_TTL Cache TTL in seconds 3600

API

See the built-in API Documentation page, or:

GET /api/lookup?query=google.com — WHOIS/RDAP lookup (parallel, merged results, includes raw responses)

GET /api/og?query=google.com — Dynamic OG image generation

Tech Stack

  • Next.js 14 (Pages Router, Edge Runtime for OG)
  • Whoiser + RDAP client
  • Satori (via next/og) for image generation

from  https://github.com/zmh-program/next-whois

示例: https://who.zmh.me/en/briten.top

Friday, 26 June 2026

搭建基于nextjs的静态博客程序inb

 

 首先fork此项目https://github.com/imyuanli/next-blog. 我fork后,得到的项目地址是

https://github.com/brightmann/inb .

 删除仓库里的package-lock.json文件。编辑package.json文件,在第3行version的下一行加上:

 "engines": { "node": "20.x" },

 命令就用默认的npm install和npm run build

然后点击deploy,等待部署完成。 部署完成后,我得到的博客地址是https://inb-bm.vercel.app/ ,https://inb-bm.vercel.app/blog

访问https://github.com/brightmann/inb/tree/master/posts,在这里新建源帖(即add file),我新建了源帖love-net.mdx,内容为:

---
title: 情网
summary: "这是一篇文章"
date: 2026-06-26 20:35:00
tags: [misc1, misc2, misc3]
draft: false
---

此处写正文或html codes 

( 详见https://github.com/brightmann/inb/blob/master/posts/love-net.mdx?plain=1)

 约等2分钟,博客网站就会更新。

项目地址: 

 https://github.com/imyuanli/next-blog

 https://github.com/brightmann/inb

 https://github.com/imyuanli/next-blog/issues/31

演示博客: 

 https://inb-bm.vercel.app/blog,支持分页

 https://inb-bm.vercel.app/blog/love-net ,能显示视频

( https://inb-tx3p.vercel.app/blog,我之前部署的。对应的仓库是https://github.com/briteming/inb .

 https://inb-tx3p.vercel.app/blog/fh)

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

在vercel.com/new的console里,提示:2026-10-1日开始, vercel.com不再支持node 20.x版。如果在2026-10-1日或之后的时间部署本程序,可能会失败。到时再问问程序作者。

Thursday, 25 June 2026

搭建基于nextjs的静态博客程序nlb

 首先fork此项目https://github.com/PrinOrange/nextjs-lexical-blog. 我fork后,得到的项目地址是https://github.com/brightmann/nextjs-lexical-blog/.

访问 https://github.com/brightmann/nextjs-lexical-blog/tree/main/data/posts,在这里新建源帖(即add file),我新建了源帖2026-06-20-fh.md ,内容为:

---
title: "战马"
time: "2026-06-20 09:40:00"
tags: ["misc1", "misc2", "misc3"]
summary: "这是一篇文章"
---

此处写正文或html codes.

( 详见https://github.com/brightmann/nextjs-lexical-blog/blob/main/data/posts/2026-06-20-fh.md?plain=1)

然后,访问https://app.netlify.com/,导入项目https://github.com/brightmann/nextjs-lexical-blog/ . Project name的值可以自己设置(我设置为nlb-ym),这个值就是平台给你的二级域名。

我点击deploy按钮后,得到的网址是 https://nlb-ym.netlify.app/

项目地址: https://github.com/PrinOrange/nextjs-lexical-blog.

 https://github.com/brightmann/nextjs-lexical-blog/

演示博客: https://nlb-ym.netlify.app/

 https://nlb-ym.netlify.app/posts ,支持分页。

我在vercel.com/new部署失败,于是转到 https://app.netlify.com/部署。

 https://github.com/PrinOrange/nextjs-lexical-blog/issues/11

我在本地的windows的git-bash程序里,build成功。运行pnmp dev, 然后访问http://127.0.0.1:3000/,即可看到网站效果。如图:


 如果你在linux vps里部署它,则另外安装nginx, 用nginx反向代理127.0.0.1:3000

 

 

 

Tuesday, 16 June 2026

stream.new

 

 The repo for https://stream.new



An open-source example application that allows users to record and upload videos using Mux

View Demo · Report a Bug · Request Features

Table of Contents

  1. About The Project
  2. Getting Started
  3. Videos to Test in Development

About The Project

Components:

Mux:

  • Direct uploads - this is an API for uploading video files from a client to create Mux Assets
  • Webhook signature verification - webhook signature verification to make sure Mux webhooks are coming from a trusted source
  • HLS.js - for doing HLS video playback of videos
  • Mux Data - for tracking video quality metrics.

Slackbot moderator. This examples allows you to configure a SLACK_WEBHOOK_ASSET_READY. When a new Mux asset is ready, an Incoming Webhook for slack will be sent. This is an example of how you might integrate a Slack channel that can be used to moderate content. The Slack message contains the asset ID, playback ID and a storyboard of thumbnails from the video.

Slackbot message

NextJS:

  • SWR — dynamically changing the refreshInterval depending on if the client should be polling for updates or not
  • /pages/api routes — a couple endpoints for making authenticated requests to the Mux API.
  • Dynamic routes using getStaticPaths and fallback: true, as well as dynamic API routes.

This app was created with the NextJS with-mux-video example as a starting point.

Getting Started

Step 1. Create an account in Mux

All you need to set this up is a Mux account. You can sign up for free and pricing is pay-as-you-go. There are no upfront charges, you get billed monthly only for what you use.

Without entering a credit card on your Mux account all videos are in “test mode” which means they are watermarked and clipped to 10 seconds. If you enter a credit card all limitations are lifted and you get $20 of free credit. The free credit should be plenty for you to test out and play around with everything before you are charged.

Step 2. Set up environment variables

Copy the .env.local.example file in this directory to .env.local (which will be ignored by Git):

cp .env.local.example .env.local

Then, go to the settings page in your Mux dashboard, get a new API Access Token that allows for "Full Access" against Mux Video and set each variable in .env.local:

  • MUX_TOKEN_ID should be the TOKEN ID of your new token
  • MUX_TOKEN_SECRET should be TOKEN SECRET
  • MUX_WEBHOOK_SIGNATURE_SECRET (optional) - the webhook signing secret if you set up webhooks (see below)
  • SLACK_WEBHOOK_ASSET_READY (optional) - the slack webhook URL that will be used for the Slackbot moderator feature (see below)
  • SLACK_MODERATOR_PASSWORD (optional) - this is the password when you want to take actions from the Slackbot moderator feature (see below)
  • NEXT_PUBLIC_MUX_ENV_KEY (optional) - this is the mux environment key for Mux Data integration

Step 3. Run locally

npm install
npm run dev

Open http://localhost:3000 to see the app.

Step 4. Deploy on Vercel

You can deploy this app to the cloud with Vercel (Documentation).

To deploy on Vercel, you need to set the environment variables using Vercel CLI (Documentation).

Install the Vercel CLI, log in to your account from the CLI, and run the following commands to add the environment variables. Replace the values with the corresponding strings in .env.local:

vercel secrets add stream_new_token_id <MUX_TOKEN_ID>
vercel secrets add stream_new_token_secret <MUX_TOKEN_SECRET>

Then push the project to GitHub/GitLab/Bitbucket and import to Vercel to deploy.

Step 5 (optional) Slackbot Moderator

Slackbot message

This application uses a slackbot to send message to a slack channel every time a new asset is ready for playback. This requires a few steps for setup.

First, login to your Mux dashboard and in the left sidebar navigation find Settings > Webhooks. Create a new webhook and makes sure you are creating a webhook for the environment that matches the access token that you are using.

Mux Webhook Create

For local development you may want to use a tool like ngrok to receive webhooks on localhost. The route for the webhook handler is /api/webhooks/mux (defined in this NextJS app under ./pages/api/webhooks/mux).

Create a Slack 'Incoming Webhook'. Configure the channel you want to post to, the icon, etc.

Slack Incoming Webhook

When you're done with this, you should have a slack webhook URL that looks something like https://hooks.slack.com/services/....

Set the optional environment variables either directly in the vercel UI or by updating vercel.json and setting them as secrets for your organization. The optional environment variables are:

  • MUX_WEBHOOK_SIGNATURE_SECRET - This is a security mechanism that checks the webhook signature header when the request hits your server so that your server can verify that the webhook came from Mux. Read more about webhook signature verification. Note that in ./pages/api/webhooks/mux the code will only verify the signature if you have set a signature secret variable, so this step is optional.
  • SLACK_WEBHOOK_ASSET_READY - This is the https://hooks.slack.com/services/.... URL when you created the Slack Incoming Webhook.
  • SLACK_MODERATOR_PASSWORD - This is the password that will be used to authorize deleting assets from the slack moderator (The button with the red text "DELETE (cannot be undone)")
  • NEXT_PUBLIC_MUX_ENV_KEY - This is the env key to use with Mux Data. Note this is different than your API key and this environment key can be found on your environment page in the Mux dashboard

After all of this is set up the flow will be:

  1. Asset is uploaded
  2. Mux sends a webhook to your server (NextJS API function)
  3. (optional) Your server verifies the webhook signature
  4. If the webhook matches video.asset.ready then your server will post a message to your slack channel that has the Mux Asset ID, the Mux Playback ID, and a thumbnail of the video.

Step 6 (optional) Add AI-powered content moderation and summarization using @mux/ai

stream.new can automatically moderate and summarize content with the help of @mux/ai.

This integration provides:

  • Automatic moderation using both OpenAI and Hive AI providers to detect inappropriate content
  • Auto-deletion of content that exceeds moderation thresholds
  • AI-generated summaries with titles, descriptions, and tags
  • Custom Q&A to answer specific questions about video content

Setup

  1. Set the following environment variables:

    • MUX_TOKEN_ID and MUX_TOKEN_SECRET - Your Mux API credentials
    • OPENAI_API_KEY - Your OpenAI API key (for moderation and summarization)
    • HIVE_API_KEY - Your Hive AI API key (for moderation)
    • AUTO_DELETE_ENABLED=1 - Enable automatic deletion of flagged content (optional)
  2. Configure the webhook endpoint in your Mux dashboard to point to /api/webhooks/mux-ai

  3. Subscribe to these webhook events:

    • video.asset.ready - triggers moderation workflow
    • video.asset.track.ready - triggers summarization when subtitles are generated

How it works

When a video is uploaded:

  1. The video.asset.ready webhook triggers moderation using both OpenAI and Hive AI
  2. If either service detects content exceeding thresholds (default: 0.9), the playback ID is automatically deleted
  3. Moderation results are posted to Slack with visual indicators (🚨 for flagged, ✅ for clean)
  4. When generated subtitles are ready, the video.asset.track.ready webhook triggers AI summarization
  5. Summary results with title, description, tags, and custom Q&A answers are posted to Slack

Hidden playback features via query params:

  • time: will start the video at a specific timestamp in seconds, for example ?time=10 will start at 10 seconds like this
  • color: a hex value without the # character will theme the Mux Player with the primaryColor. It's important to omit the # for example ?color=f97316 like this

Videos to test in development:

When developing, if you make any changes to the video player, make sure it works and looks good with videos of various dimensions:

Horizontal

Vertical

Super vertical

Also be sure to check: Safari, Mobile Safari, Chrome, Firefox because they all behave a little differently.

from  https://github.com/muxinc/stream.new

(mux.com) 

可以上传视频,示例:

 https://stream.new/v/aNs01uKyOx8xdSq86xxENDgy6A90000pGp015FDWeHYECWQ

Sunday, 14 June 2026

launch-react-nextjs-tips

 

NextJS React Mastery Tutorial - Top 8 Patterns That Cover 95% of Use Cases

A progressive React tutorial designed for vibe coders and developers who want to master React fundamentals through practical examples.

📹 Full YouTube Guide: Youtube link

🚀 X Post: X link

💻 Launch Full Stack Product: Github Repo

☕️ Buy me a coffee: Cafe Latte

🤖️ Discord: Invite link

🎯 Why This Tutorial Exists

Many developers using AI coding tools like Cursor, Lovable, and v0 get stuck because they don't understand the fundamental React concepts. This tutorial teaches the 8 essential patterns that power 95% of React applications.

📚 What You'll Learn

🔥 Top 8 React Patterns

  1. useState + useEffect - State management & side effects
  2. Component Composition & Props - Reusable UI components
  3. Conditional Rendering - Loading states, error states, feature flags
  4. List Rendering & Keys - Displaying arrays efficiently
  5. Event Handling - User interactions (clicks, forms, keyboard)
  6. Form Handling & Validation - User input collection
  7. Context API - Global state management

🎬 TCustom Hooks - Reusable logic extractionutorial Structure (Perfect for 40-minute video)

Part 1: Foundation (12 minutes)

  • Pattern 1: useState/useEffect in Counter widget
  • Pattern 2: Side effects in Clock widget
  • Pattern 3: Props & composition in Button component

Part 2: Data Handling (12 minutes)

  • Pattern 4: Conditional rendering in UserProfile widget
  • Pattern 5: List rendering in TodoList widget
  • Pattern 6: Event handling in ContactForm widget

Part 3: Advanced Patterns (12 minutes)

  • Pattern 7: Context API in ThemeToggle widget
  • Pattern 8: Custom hooks in NotesWidget

Part 4: Integration (4 minutes)

  • Demo: All patterns working together
  • Next Steps: Building real applications

🚀 Getting Started

1. Install Dependencies

npm install

2. Start Development Server

npm run dev

3. Open Your Browser

Navigate to http://localhost:3000

from  https://github.com/ShenSeanChen/launch-react-nextjs-tips

Wednesday, 10 June 2026

搭建基于nextjs的静态博客程序amytis

 首先fork此项目https://github.com/hutusi/amytis,我fork后,得到的项目地址是

 https://github.com/brightmann/amytis

在此处 https://github.com/brightmann/amytis/tree/main/content/posts创建源帖,比如fh.mdx ,内容为:

---
title: "战马"
date: "2026-06-09T23:44:00"
excerpt: "这是一篇文章"
category: "Test"
tags: ["misc1", "misc2", "misc3"]
author: "ym"
---

此处写正文或html codes

(详见 https://github.com/brightmann/amytis/blob/main/content/posts/fh.mdx?plain=1)

然后,访问vercel.com/new, 导入项目amytis (点击import),在跳到的页面中,输入如下命令,如图:


 注意:在Build Command那栏,输入的命令应是bun run build, 不是bun build!然后,点击

deploy按钮,等待部署完成。 部署完成后,我得到的网址是

https://amytis-prh7.vercel.app/  

 https://amytis-prh7.vercel.app/posts/ 支持分页
https://amytis-prh7.vercel.app/posts/fh/ 能显示视频。

项目地址: 

 https://github.com/hutusi/amytis

  https://github.com/brightmann/amytis

演示博客: https://amytis-prh7.vercel.app/ 



 

Tuesday, 9 June 2026

ISR in Next.js App Router: Why Your Pages Aren't Updating

 

When building content-driven sites using Next.js App Router, many developers encounter a common issue: API data has been updated, yet page content remains unchanged for an extended period.

This is typically not due to incorrect cache configuration, nor solely because ISR is "unreliable." More often, it stems from misunderstandings about how ISR actually operates and the trade-offs it intentionally makes. This article will explain ISR's actual behavior within App Router—and the problems it can and cannot solve—by examining a real-world troubleshooting case.

Where the Problem Began

Why isn't the homepage content updating after API data refreshes?

While maintaining a content-driven website recently, I encountered an issue during testing: the homepage failed to update after new data was added to the database. Initially, I suspected Cloudflare caching the API response. Postman requests confirmed the data had updated. I then investigated caching on both frontend and backend deployment platforms but found no issues. After recompiling and redeploying the frontend code, the homepage data updated correctly. This narrowed the fault to Next.js's caching mechanism.

A Common Yet Dangerous Equation

API is dynamic ≠ Page will update

Typically, we instinctively assume that changes in API data directly trigger page content updates, following the sequence: API data update -> Page request fetches new data -> Page content updates. However, in certain scenarios, this rendering process breaks down. A commonly overlooked point is whether the page is regenerated—meaning there's a disconnect between data changes and page generation.

We often mistakenly treat Next.js's App Router data fetching as a reactive system that automatically propagates data changes. The official documentation describes it thus:

Caching is a technique for storing the result of data fetching and other computations so that future requests for the same data can be served faster, without doing the work again. While revalidation allows you to update cache entries without having to rebuild your entire application.

This indicates that Next.js's rendered page results can be reused.

What Problem Does ISR Solve?

Before ISR, content-focused sites faced a dilemma:

  • Pure SSG: Stable pages but delayed updates, leaving users seeing outdated content
  • Pure SSR: Timely content delivery but high server load and response times

Unlike e-commerce sites demanding real-time updates, content-driven sites typically require less immediate data freshness—yet they cannot remain static forever. ISR emerged precisely to bridge this gap. ISR was designed to strike a balance between SSG and SSR, enabling pages to leverage the performance benefits of static generation while maintaining content updates within acceptable timeframes.

ISR's design assumptions:

  • Delay is tolerable
  • Inconsistencies are temporary

ISR's core objective is trading time for stability—exchanging predictable latency for deployment and caching benefits. It is not designed for frequently changing data and inherently does not pursue real-time updates.

How ISR Actually Works

When exactly does a page update? It's not "auto-refreshing every N seconds," but rather triggering an update upon the "next visit" after exceeding N seconds.

Contrary to first impressions, ISR isn't a background refresh timer. When the revalidate cycle arrives, pre-generated pages don't update immediately—they wait for the next user visit to trigger the update.

Example:

  • A page set to revalidate: 60 means content can update every 60 seconds
  • User A visits the page at 12:00:00; the page is generated and cached
  • User B visits the page at 12:00:30; the content displayed remains what User A saw
  • User C visits the page at 12:01:05; the revalidate cycle expires, triggering page regeneration in the background, but User C still receives the stale response
  • User D visits the page at 12:01:10, and the page content updates to the latest version, triggered by User C's visit

From the above example, it's clear that page updates in ISR are passively triggered—checking for regeneration only occurs when a user visits. This means delayed visibility is a deliberate design choice, not a flaw.

Advantages and Trade-offs of ISR Compared to Other Rendering Strategies

This is not an evolutionary path, but a trade-off

ISR vs. SSG:

  • Solves the problem of long-term static content by enabling periodic page updates, though not real-time
  • Retains the caching benefits of static generation: fast response times and low server load

ISR vs. SSR:

  • Reduces server load; stable, cacheable content lowers costs
  • Sacrifices real-time content; introduces latency and inconsistency, cannot guarantee freshness on every visit

ISR offers predictable behavior and controllable costs, but isn't suitable for scenarios requiring strong consistency or real-time updates. ISR isn't an upgrade over SSR/SSG—it's a distinct choice

dynamic vs revalidate

dynamic is not a "bug switch," but a solution for different scenarios

In Next.js App Router, dynamic and revalidate are distinct rendering strategies: dynamic determines "whether to render each time," while revalidate determines "whether to reuse old pages."

When a site's content must reflect the latest state on every visit, or when content is highly context-dependent, delayed visibility is unacceptable. dynamic re-renders on every visit, making it ideal for this scenario. Conversely, when page content is relatively stable, revalidate offers a suitable solution. It trades time for stability by allowing some delay.

If I notice a site's homepage isn't updating promptly, I might choose dynamic to ensure fresh content on every visit. This seems like a solution, but it merely shifts the problem to a different scenario. dynamic and revalidate aren't mutually exclusive choices—they represent different rendering strategies for distinct requirements.

Page-Level Revalidation vs. API-Level Revalidation

Granularity depends on consistency requirements, not flexibility

Consider a news site homepage with multiple sections: Today's News, Yesterday's News, etc. When these sections fetch data from separate APIs and use API-level revalidation, this scenario may occur: Today's News remains unchanged while Yesterday's News updates. If spanning multiple days, this could even create a disjointed scenario where Yesterday's News appears more recent than Today's News—directly undermining the site's credibility.

For pages aggregating multiple APIs, API-level revalidate struggles to guarantee content consistency. As the final delivery point, the aggregated page should shoulder consistency responsibility, making page-level revalidate more advantageous. Similarly, if a page's content originates from multiple interfaces but lacks strict suppression requirements between modules, API-level revalidate may be considered for higher performance. Ultimately, the choice of revalidate granularity should be based on the page's consistency requirements, not flexibility.

ISR Behavior in Low-Traffic Content Sites

Why does the website seem broken?

As mentioned earlier, ISR can be configured to revalidate, triggering page updates on the user's next visit. When site traffic is very low, page refresh frequency drastically decreases, potentially leading to pages remaining unchanged for extended periods. When users visit such pages, they may see content that is days or even weeks old, creating the impression that the website is "broken."

This is actually an intended outcome of ISR design. When User A visits a page, they see content generated long ago, and their visit triggers a page re-generation. However, due to the potentially days-long intervals between visits on low-traffic sites, by the time User B visits next, the page content remains the version last updated by User A. Consequently, User B still sees outdated information.

For low-traffic content sites, traffic volume may dictate content refresh frequency. As long as this aligns with the original design intent and is acceptable, this "lazy updating" behavior is reasonable and does not indicate a "broken" website.

Does ISR Lead to Out-of-Sync Pages?

This is not an issue requiring excessive concern

While ISR implementation may indeed encounter scenarios of content asynchrony, this represents a design trade-off that should be understood and accepted—not an issue warranting undue alarm. ISR's architecture inherently permits transient inconsistencies. Rather than fixating on "whether inconsistencies occur," the critical question is whether such inconsistencies are predictable. During design, clearly define the content site's actual consistency requirements. Analyze when inconsistencies might occur and whether their potential impact is acceptable—rather than blindly pursuing timeliness or uniformity. When content inconsistencies are controllable and predictable, "page asynchrony" ceases to be an excessive concern. It becomes a known factor in the design.

Time-Driven Content ISR Strategy

Take the homepage I maintain as an example. It aggregates "Future Content" and "Latest Content" modules—time-driven content where "Future Content" manages content beyond today, while "Latest Content" manages content from today and earlier.

This introduces a trade-off between consistency and flexibility. Opting for API-level revalidate would make a module's caching strategy independent of the page, turning the homepage into an "assembled snapshot." For my homepage, this unnatural state is unacceptable. Furthermore, the homepage has a stable structure with changes occurring only at the content layer. Therefore, page-level ISR is more suitable as the overall invalidation strategy. A page-level strategy allows my pages to update and expire synchronously as a whole, avoiding a state where "part is new and part is old." This aligns with my business semantics.

Data updates on my site occur concentrated at a specific time point within a day. There is only one critical change window per day, with data remaining largely stable at other times. Setting revalidate to one day could easily result in users accessing the page before the critical window, followed by no refreshes for the entire subsequent day. Given the site's low traffic, revalidate effectively functions as "the maximum number of visit opportunities I'm willing to let this page miss daily." Considering the homepage doesn't require minute-level real-time updates, yet I also don't want it displaying stale data for half a day or an entire day, I set revalidate to 1 hour. This ensures that even with low traffic, there are 24 opportunities daily to trigger a page refresh. As long as one user visits after the critical window, they will likely see the updated data.

Why not use a smaller value, like 5 minutes? Because based on the project's actual circumstances, it's unnecessary. Setting revalidate to 5 minutes would make the page behave almost like dynamic rendering, while retaining the complexity of ISR without significant benefit. Most importantly, the business can tolerate the current threshold of latency. For me, ISR's revalidate isn't a caching parameter, but a way to model the business rhythm.

When to Transition from ISR to On-Demand Revalidate

The decision to transition to on-demand revalidate doesn't hinge on technical maturity, but on whether the "content update -> user visibility" chain begins impacting user trust.

My current site updates only during a single daily critical window, with low traffic and users likely in exploratory or occasional visit phases. Their expectations lean toward "stability" rather than "timeliness." Transitioning becomes necessary if:

  • Users start treating my site as a "near-real-time information source"
  • Data update frequency increases/becomes irregular
  • Users frequently report outdated content

This indicates my site's content has shifted from time-driven to event-driven, with users demanding greater real-time relevance. At this point, transitioning from ISR to an on-demand revalidate mechanism becomes necessary. In other words, when users begin perceiving "uncertainty in update timing," it's time to reconsider rendering strategies. On-demand revalidate represents a distinct approach for the site's new phase—not merely a patch for ISR.

Conclusion

ISR isn't unreliable—it's just placed under the wrong expectations.

ISR is valid only when site content can tolerate delays and brief inconsistencies, and when access frequency is unstable. Software engineering has no silver bullets, and ISR isn't omnipotent. The key isn't whether to use ISR, but whether you're willing to accept its inherent delays and inconsistencies. Understand ISR's design trade-offs and choose the appropriate rendering strategy based on business needs.


References

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

Isr,  incremental static regeneration

增量静态再生,它是SSR和SSG的结合。当一个ISR路由被访问时,页面会像SSR一样生成并存储在缓存中,然后提供给客户端。之后任何访问都会直接从缓存中获取渲染后的内容。你可以设置TTL或者无限期地保留缓存,直到你手动重新验证该路由,导致下一次访问该路由时重新以SSR方式渲染并重新缓存。

这对于例如博客文章来说非常棒。你可以无限期地缓存博客文章,当管理员编辑博客文章时,你重新验证特定的博客文章路由。这意味着更少的数据库调用,更少的服务器运行时,这转化为更少的支付给托管平台的费用... 

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

 https://juejin.cn/post/7453286740078526516

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

 

ISR(Incremental Static Regeneration)是一种Web前端开发中的静态网页生成技术。它是Next.js框架引入的一项功能,旨在改进静态站点的性能和用户体验。在了解ISR之前,我们需要先了解几个相关的概念。

静态网页生成(Static Site Generation)

静态网页生成是一种生成静态HTML文件的技术,它将数据与页面内容结合,一次性生成所有页面。这种方法的优势在于提高了网站的加载速度和性能,因为访问者可以直接获取静态HTML而无需等待服务器处理。

服务器端渲染(Server-Side Rendering)

服务器端渲染是在每次请求时动态生成HTML页面的过程。这意味着服务器负责渲染页面的内容,并将最终的HTML发送给客户端。这种方法的好处是可以在每次请求时动态地生成页面,以确保始终提供最新的数据。

预渲染(Pre-rendering)

预渲染是一种在构建时生成页面的技术,它不像静态网页生成那样在每次请求时生成。这样可以减轻服务器的负担,但在某些情况下可能会导致页面内容过时。

ISR 的工作原理

ISR 结合了静态网页生成和服务器端渲染的优点。它的核心思想是在构建时生成静态页面,然后在运行时根据需要进行更新。这意味着你可以预先生成大部分静态页面,然后根据用户的请求,在需要时再重新生成某些页面。

在 Next.js 中,ISR 是通过在页面组件中使用 getStaticProps 函数实现的。这个函数在构建时运行,用于获取页面所需的数据。通过在 getStaticProps 中使用 revalidate 选项,可以指定页面的重新生成时间,即多长时间后页面内容将被更新。如果用户在这段时间内访问该页面,他们将获得旧的静态页面,而同时,Next.js 会在后台异步重新生成新的页面内容,并在下一次请求时提供更新后的页面。

ISR 的优势

  1. 提高性能: ISR 允许大部分页面在构建时生成,从而提高了网站的加载速度。用户可以立即获取静态页面,而不必等待服务器响应。
  2. 实时数据 与传统的静态网页生成不同,ISR 可以在需要时动态更新页面内容。这意味着你可以在不牺牲性能的情况下获取实时数据。
  3. 降低服务器负担: 由于大部分页面在构建时生成,服务器无需在每次请求时都动态生成页面,从而降低了服务器的负担。

举例说明

假设我们有一个博客网站,每篇博客文章都是一个页面。我们希望实现ISR,以便在用户访问每篇文章时,能够提供最新的内容。

  1. 创建博客文章页面

```jsx // pages/blog/[slug].js

import { useRouter } from 'next/router'; import { getStaticProps } from 'next';

const BlogPost = ({ postData }) => { const router = useRouter();

if (router.isFallback) {
   return <div>Loading...</div>;
 }

 return (
   <div>
     <h1>{postData.title}</h1>
     <p>{postData.content}</p>
   </div>
 );

};

export async function getStaticPaths() { // Fetch the list of blog post slugs const paths = getBlogPostSlugs();

return {
   paths,
   fallback: true, // Set to true to enable ISR
 };

}

export async function getStaticProps({ params }) { // Fetch necessary data for the blog post using the slug const postData = getBlogPostData(params.slug);

return {
   props: {
     postData,
   },
   revalidate: 60, // Regenerate page every 60 seconds (1 minute)
 };

}

export default BlogPost; ```

  1. 启用 ISR

在上述代码中,我们通过设置 fallback: true 启用了 ISR。这意味着如果用户访问的页面在构建时不存在(例如,新发布的博客文章),Next.js 会在后台重新生成页面并提供更新后的内容。同时,我们通过 revalidate 选项指定了页面重新生成的时间间隔。

这样,用户可以立即获取现有的静态页面,并在稍后获取更新的内容,而不必等待重新生成。

总体来说,ISR 是一项强大的技术,能够在静态网页生成和服务器端渲染之间找到平衡,提供高性能和实时数据更新的优势。在构建现代Web应用程序时,特别是对于内容频繁变化的页面,使用ISR可以显著改善用户体验。

SSG 是预先生成网页的过程,这意味着在构建阶段,我们会生成所有可能的页面并保存下来。然后,当用户请求一个页面时,我们只需从存储中获取预生成的页面并将其发送给用户。这种方式的好处是,因为页面已经预生成,所以响应速度非常快。然而,对于内容经常变化的网站来说,SSG 不是一个理想的选择,因为每次内容更新,都需要重新构建整个网站。

SSR 则是在用户请求一个页面时,服务器会生成并返回该页面。这意味着每个请求都会触发一个生成页面的过程。这种方式的好处是,能够处理内容频繁更新的情况。然而,因为每个请求都需要生成页面,所以可能会增加服务器的负担,并且可能导致响应时间变慢。

ISR 试图将 SSG 和 SSR 的优点结合起来。在 ISR 中,页面在构建阶段会被预生成,但是可以设置一个 fallback 参数来处理那些未在构建阶段生成的页面。当用户请求一个未预生成的页面时,Next.js 会在后台生成该页面,并返回一个 “fallback” 页面给用户。一旦页面生成,Next.js 会保存它,以便下次请求时可以立即返回。

另外,ISR 还引入了一个 revalidate 参数。这个参数定义了在多长时间后,页面应该被重新生成。例如,如果 revalidate 设置为 1,那么每隔 1 秒,Next.js 会检查页面是否需要更新。如果需要,Next.js 会在后台重新生成页面,并在下次请求时返回新的页面。

让我们以一个博客网站为例来说明 ISR 的使用。假设我们有一个博客网站,其中包含数千篇博客文章。每篇文章都有自己的页面,而且每天都会有新的文章发布。

如果我们使用传统的 SSG,那么每次新文章发布时,我们都需要重新构建整个网站,这将花费大量的时间和计算资源。如果我们使用 SSR,那么每次用户请求一篇文章时,我们都需要在服务器上生成那个页面,这可能会使服务器的负担加重,并可能导致响应时间变慢。

Monday, 8 June 2026

fumadocs

 

The beautiful & flexible React.js docs framework.

 

banner

The framework for building documentation websites in any React.js frameworks.

Officially Supported:

  • Next.js
  • Vite: Tanstack Start, Waku, React Router

📘 Learn More: Documentation.

from  https://github.com/fuma-nama/fumadocs

Thursday, 4 June 2026

next-on-pages

 CLI to build and develop Next.js apps for Cloudflare Pages 

 https://www.npmjs.com/package/@cloudflare/next-on-pages

 

The next-on-pages package is deprecated, if you want to deploy a Next.js application on Cloudflare, please use the OpenNext Cloudflare adapter instead. If you have a Next.js application that already uses next-on-pages the OpenNext getting started guide for existing applications presents instructions on how to migrate to the OpenNext adapter.

⚠️ 🚧 ⚠️

⚡▲ @cloudflare/next-on-pages ▲⚡

Build, develop, and deploy Next.js apps for Cloudflare Pages.

Note

The best way to run Next.js apps on Cloudflare is to use @opennextjs/cloudflare. If you're coming from Vercel, you can migrate your Next.js app easily using Diverce, which can also help you migrate from @cloudflare/next-on-pages

@cloudflare/next-on-pages is a CLI tool that you can use to build and develop Next.js applications so that they can run on the Cloudflare Pages platform.

Alongside the @cloudflare/next-on-pages there is an additional package eslint-plugin-next-on-pages implementing an Eslint plugin which aim is to aid developers at using the @cloudflare/next-on-pages more efficiently and improve their overall developer experience when working with it.

You can see the packages contents (with their documentation) in their respective package directories:

Additionally there is also the next-dev submodule which is implemented as a separate package in this repository but included as a submodule of the main @cloudflare/next-on-pages package, you can see the submodule's content here:

Contributing

If you want to contribute to this project (both to the main package and the eslint one) please refer to the Contributing document.

References

Extra references you might be interested in:

  • Blog post

    The original blog post introducing @cloudflare/next-on-pages (24/10/2022), it goes into details on the inspiration for this package and provides some details on how it works.

  • Cloudflare Next.js Guide

    Cloudflare guide on how to create and deploy a Next.js application. The application can be either static (and deployed as simple static assets) or dynamic using the edge runtime (using @cloudflare/next-on-pages).

  • Caching and Data Revalidation

    Documentation on how the caching and data revalidation for applications built using @cloudflare/next-on-pages works.

  • Technical Documentation

    Explanations and insights into how @cloudflare/next-on-pages works, design decisions behind different aspects, and how it handles different Next.js features.

    from  https://github.com/cloudflare/next-on-pages

Sunday, 24 May 2026

MoeMail

 A cute temporary email service built with NextJS + Cloudflare technology stack 🎉 | 一个基于 NextJS + Cloudflare 技术栈构建的可爱临时邮箱服务

moemail.app

A cute temporary email service built with NextJS + Cloudflare technology stack 🎉

English | 简体中文

MoeMail - OpenAPI‑first temp email, hosted & ready | Product Hunt

Live DemoDocumentationFeaturesTech StackLocal RunDeploymentEmail Domain ConfigPermission SystemSystem SettingsSending EmailsWebhook IntegrationOpenAPICLI ToolEnvironment VariablesGithub OAuth ConfigGoogle OAuth ConfigContributionLicenseCommunitySupport

Live Demo

https://moemail.app

Home

Mailbox

Profile

Documentation

Full Documentation: https://docs.moemail.app

The documentation site contains detailed usage guides, API documentation, deployment tutorials, and other complete information.

Features

  • 🔒 Privacy Protection: Protect your real email address from spam and unnecessary subscriptions
  • Real-time Receipt: Automatic polling, receive email notifications instantly
  • ⏱️ Flexible Validity: Supports 1 hour, 24 hours, 3 days, or permanent validity
  • 🎨 Theme Switching: Supports light and dark modes
  • 📱 Responsive Design: Perfectly adapted for desktop and mobile devices
  • 🔄 Auto Cleanup: Automatically cleans up expired mailboxes and emails
  • 📱 PWA Support: Support PWA installation
  • 💸 Free Self-hosting: Built on Cloudflare, capable of free self-hosting without any cost
  • 🎉 Cute UI: Simple and cute UI interface
  • 📤 Sending Function: Support sending emails using temporary addresses, based on Resend service
  • 🔔 Webhook Notification: Support receiving new email notifications via webhook
  • 🛡️ Permission System: Role-based access control system
  • 🔑 OpenAPI: Support accessing OpenAPI via API Key
  • 🤖 Agent-first CLI: CLI tool designed for AI agents to automate email workflows
  • 🌍 Multi-language Support: Supports Chinese and English interfaces, freely switchable

Tech Stack

Local Run

Prerequisites

  • Node.js 18+
  • Pnpm
  • Wrangler CLI
  • Cloudflare Account

Installation

  1. Clone the repository:
git clone https://github.com/beilunyang/moemail.git
cd moemail
  1. Install dependencies:
pnpm install
  1. Setup Wrangler:
cp wrangler.example.json wrangler.json
cp wrangler.email.example.json wrangler.email.json
cp wrangler.cleanup.example.json wrangler.cleanup.json

Set Cloudflare D1 database name and database ID.

  1. Setup Environment Variables:
cp .env.example .env.local

Set AUTH_GITHUB_ID, AUTH_GITHUB_SECRET, AUTH_SECRET.

  1. Create local database schema:
pnpm db:migrate-local

Development

  1. Start development server:
pnpm dev
  1. Test Email Worker: Currently cannot run and test locally, please use Wrangler to deploy the email worker and test.
pnpm deploy:email
  1. Test Cleanup Worker:
pnpm dev:cleanup
pnpm test:cleanup
  1. Generate Mock Data (Mailboxes and Messages):
pnpm generate-test-data

Deployment

Video Tutorial

https://www.youtube.com/watch?v=Vcw3nqsq2-E

Local Wrangler Deployment

  1. Create .env file
cp .env.example .env
  1. Set Environment Variables in the .env file.

  2. Run deployment script

pnpm dlx tsx ./scripts/deploy/index.ts

Github Actions Deployment

This project supports automated deployment using GitHub Actions. It supports the following triggers:

  1. Auto Trigger: Automatically triggers deployment flow when a new tag is pushed.
  2. Manual Trigger: Manually trigger in the GitHub Actions page.

Deployment Steps

  1. Add the following Secrets in GitHub repository settings:

    • CLOUDFLARE_API_TOKEN: Cloudflare API Token
    • CLOUDFLARE_ACCOUNT_ID: Cloudflare Account ID
    • AUTH_GITHUB_ID: GitHub OAuth App ID
    • AUTH_GITHUB_SECRET: GitHub OAuth App Secret
    • AUTH_SECRET: NextAuth Secret, used to encrypt session, please set a random string
    • CUSTOM_DOMAIN: Custom domain for the website (Optional, if empty, uses Cloudflare Pages default domain)
    • PROJECT_NAME: Pages project name (Optional, if empty, defaults to moemail)
    • DATABASE_NAME: D1 database name (Optional, if empty, defaults to moemail-db)
    • KV_NAMESPACE_NAME: Cloudflare KV namespace name, used for site settings (Optional, if empty, defaults to moemail-kv)
  2. Choose trigger method:

    Method 1: Push Tag Trigger

    # Create a new tag
    git tag v1.0.0
    
    # Push tag to remote repository
    git push origin v1.0.0

    Method 2: Manual Trigger

    • Go to the Actions page of the repository
    • Select "Deploy" workflow
    • Click "Run workflow"
  3. Deployment progress can be viewed in the Actions tab of the repository.

Notes

  • Ensure all Secrets are set correctly.
  • When using tag trigger, the tag must start with v (e.g., v1.0.0).

Deploy to Cloudflare Workers

Email Domain Configuration

In the MoeMail User Profile page, you can configure the site's email domains. Supports multiple domain configurations, separated by commas. Email Domain Configuration

Cloudflare Email Routing Configuration

To make email domains effective, you also need to configure email routing in the Cloudflare console to forward received emails to the Email Worker.

  1. Login to Cloudflare Console
  2. Select your domain
  3. Click "Email" -> "Email Routing" in the left menu
  4. If it shows "Email Routing is currently disabled", please click "Enable Email Routing" Enable Email Routing
  5. After clicking, it will prompt you to add Email Routing DNS records, click "Add records and enable" Add DNS Records
  6. Configure Routing Rules:
    • Catch-all address: Enable "Catch-all"
    • Edit Catch-all address
    • Action: Select "Send to Worker"
    • Destination: Select the "email-receiver-worker" you just deployed
    • Save Configure Routing Rules

Notes

  • Ensure domain DNS is hosted on Cloudflare.
  • Email Worker must be successfully deployed.
  • If Catch-All status is unavailable (stuck loading), please click Destination addresses next to Routing rules, and bind an email address there.

Permission System

The project uses a Role-Based Access Control (RBAC) system.

Role Configuration

New user default roles are configured by the Emperor in the site settings in the User Profile:

  • Duke: New users get temporary email, Webhook config permissions, and API Key management permissions.
  • Knight: New users get temporary email and Webhook config permissions.
  • Civilian: New users have no permissions, need to wait for Emperor to promote to Knight or Duke.

Role Levels

The system includes four role levels:

  1. Emperor

    • Website Owner
    • Has all permissions
    • Only one Emperor per site
  2. Duke

    • Super User
    • Can use temporary email features
    • Can configure Webhook
    • Can create API Key to call OpenAPI
    • Can be demoted to Knight or Civilian by Emperor
  3. Knight

    • Advanced User
    • Can use temporary email features
    • Can configure Webhook
    • Can be demoted to Civilian or promoted to Duke by Emperor
  4. Civilian

    • Regular User
    • No permissions
    • Can be promoted to Knight or Duke by Emperor

Role Upgrade

  1. Become Emperor

    • The first user to visit /api/roles/init-emperor interface will become the Emperor (Website Owner).
    • Once an Emperor exists, no other user can be promoted to Emperor.
  2. Role Changes

    • The Emperor can set other users as Duke, Knight, or Civilian in the User Profile page.

Permission Details

  • Email Management: Create and manage temporary emails
  • Webhook Management: Configure Webhooks for email notifications
  • API Key Management: Create and manage API access keys
  • User Management: Promote/Demote user roles
  • System Settings: Manage global system settings

System Settings

System settings are stored in Cloudflare KV, including:

  • DEFAULT_ROLE: Default role for new users, values: CIVILIAN, KNIGHT, DUKE
  • EMAIL_DOMAINS: Supported email domains, comma-separated
  • ADMIN_CONTACT: Administrator contact info
  • MAX_EMAILS: Maximum number of emails per user

Emperor role can configure these in the User Profile page.

Sending Emails

MoeMail supports sending emails using temporary addresses, based on Resend service.

Features

  • 📨 Send from Temp Email: Use created temporary emails as sender
  • 🎯 Role Limits: Different roles have different daily sending limits
  • 💌 HTML Support: Supports rich text email format

Role Sending Limits

Role Daily Limit Description
Emperor Unlimited Admin has no limits
Duke 5/day Default 5 emails per day
Knight 2/day Default 2 emails per day
Civilian Forbidden No sending permission

💡 Tip: The Emperor can customize the daily limits for Dukes and Knights in the Mail Service Configuration.

Configure Sending Service

  1. Get Resend API Key

    • Register at Resend
    • Create API Key in console
    • Copy API Key for later use
  2. Configure Service

    • Login as Emperor
    • Go to User Profile
    • In "Resend Service Configuration":
      • Enable Sending Service switch
      • Enter Resend API Key
      • Set daily limits for Duke and Knight (Optional)
    • Save configuration
  3. Verify Configuration

    • After saving, authorized users will see a "Send Email" button in the email list
    • Click to open dialog and test

How to Send

  1. Create Temp Email

    • Create a new temporary email in Mailbox page
  2. Send Email

    • Find the email in the list
    • Click "Send Email" button next to it
    • Fill in:
      • Recipient address
      • Subject
      • Content (supports HTML)
    • Click "Send"
  3. View History

    • Sent emails are saved in the message list of the corresponding mailbox
    • View all sent/received emails in mailbox detail page

Notes

  • 📋 Resend Limits: Please note Resend's sending limits and pricing
  • 🔐 Domain Verification: Using custom domains requires verification in Resend
  • 🚫 Anti-Spam: Please follow email sending standards, avoid spamming
  • 📊 Quota Monitoring: System counts daily usage, stops sending when limit reached
  • 🔄 Quota Reset: Daily quota resets at 00:00

Webhook Integration

When a new email is received, the system sends a POST request to the configured and enabled Webhook URL.

Request Header

Content-Type: application/json
X-Webhook-Event: new_message

Request Body

{
  "emailId": "email-uuid",
  "messageId": "message-uuid",
  "fromAddress": "sender@example.com",
  "subject": "Email Subject",
  "content": "Email Text Content",
  "html": "Email HTML Content",
  "receivedAt": "2024-01-01T12:00:00.000Z",
  "toAddress": "your-email@moemail.app"
}

Configuration

  1. Click avatar to enter User Profile
  2. Enable Webhook
  3. Set notification URL
  4. Click Test button
  5. Save to receive notifications

Testing

The project provides a simple test server:

pnpm webhook-test-server

The test server listens on port 3001 (http://localhost:3001) and prints received Webhook details.

For external testing, use Cloudflare Tunnel:

pnpx cloudflared tunnel --url http://localhost:3001

Notes

  • Webhook must respond within 10 seconds
  • Non-2xx response triggers retry

OpenAPI

The project provides OpenAPI interfaces, accessible via API Key. API Keys can be created in User Profile (Requires Duke or Emperor role).

Using API Key

Add API Key to request header:

X-API-Key: YOUR_API_KEY

API Endpoints

Get System Config

GET /api/config

Response:

{
  "defaultRole": "CIVILIAN",
  "emailDomains": "moemail.app,example.com",
  "adminContact": "admin@example.com",
  "maxEmails": "10"
}

Generate Temp Email

POST /api/emails/generate
Content-Type: application/json

{
  "name": "test",
  "expiryTime": 3600000,
  "domain": "moemail.app"
}

Params:

  • name: Prefix (optional)
  • expiryTime: Validity in ms. 3600000(1h), 86400000(24h), 604800000(7d), 0(Permanent)
  • domain: From config

Response:

{
  "id": "email-uuid-123",
  "email": "test@moemail.app"
}

Get Email List

GET /api/emails?cursor=xxx

Get Messages for Email

GET /api/emails/{emailId}?cursor=xxx

Delete Email

DELETE /api/emails/{emailId}

Get Single Message

GET /api/emails/{emailId}/{messageId}

Create Email Share Link

POST /api/emails/{emailId}/share
Content-Type: application/json

{
  "expiresIn": 86400000
}

Get Email Share Links

GET /api/emails/{emailId}/share

Delete Email Share Link

DELETE /api/emails/{emailId}/share/{shareId}

Create Message Share Link

POST /api/emails/{emailId}/messages/{messageId}/share
Content-Type: application/json

{
  "expiresIn": 86400000
}

Get Message Share Links

GET /api/emails/{emailId}/messages/{messageId}/share

Delete Message Share Link

DELETE /api/emails/{emailId}/messages/{messageId}/share/{shareId}

CLI Tool

MoeMail provides an agent-first CLI tool for AI agents and automation workflows.

Install

npm i -g @moemail/cli

Quick Start

# Configure API endpoint and key
moemail config set api-url https://moemail.app
moemail config set api-key YOUR_API_KEY

# Create temporary email
moemail create --domain moemail.app --expiry 1h --json

# Wait for new messages (polling)
moemail wait --email-id <id> --timeout 120 --json

# Read message content
moemail read --email-id <id> --message-id <id> --json

# Delete email
moemail delete --email-id <id>

Agent Workflow

A typical AI agent verification flow in 3 tool calls:

# 1. Create mailbox
EMAIL=$(moemail create --domain moemail.app --expiry 1h --json)
EMAIL_ID=$(echo $EMAIL | jq -r '.id')
ADDRESS=$(echo $EMAIL | jq -r '.address')

# 2. Wait for verification email
MSG=$(moemail wait --email-id $EMAIL_ID --timeout 120 --json)
MSG_ID=$(echo $MSG | jq -r '.messageId')

# 3. Read content, extract verification code
CONTENT=$(moemail read --email-id $EMAIL_ID --message-id $MSG_ID --json)

AI Agent Skill

Install the built-in skill so AI agents (Claude Code, Codex, etc.) automatically know how to use MoeMail:

# Auto-detect installed agent platforms and install
moemail skill install

# Or specify a platform
moemail skill install --platform claude
moemail skill install --platform codex

For full documentation, see packages/cli/README.md.

Environment Variables

Authentication

  • AUTH_GITHUB_ID: GitHub OAuth App ID
  • AUTH_GITHUB_SECRET: GitHub OAuth App Secret
  • AUTH_GOOGLE_ID: Google OAuth App ID
  • AUTH_GOOGLE_SECRET: Google OAuth App Secret
  • AUTH_SECRET: NextAuth Secret

Cloudflare

  • CLOUDFLARE_API_TOKEN: Cloudflare API Token
  • CLOUDFLARE_ACCOUNT_ID: Cloudflare Account ID
  • DATABASE_NAME: D1 Database Name
  • DATABASE_ID: D1 Database ID (Optional, auto-fetched if empty)
  • KV_NAMESPACE_NAME: KV Name
  • KV_NAMESPACE_ID: KV ID (Optional, auto-fetched if empty)
  • CUSTOM_DOMAIN: Custom domain
  • PROJECT_NAME: Pages Project Name

Github OAuth App Configuration

  1. Login Github Developer create new OAuth App
  2. Generate Client ID and Client Secret
  3. Configure:
    • Application name: <your-app-name>
    • Homepage URL: https://<your-domain>
    • Authorization callback URL: https://<your-domain>/api/auth/callback/github

Google OAuth App Configuration

  1. Visit Google Cloud Console create project
  2. Configure OAuth consent screen
  3. Create OAuth Client ID
    • Type: Web application
    • Authorized Javascript origins: https://<your-domain>
    • Authorized redirect URIs: https://<your-domain>/api/auth/callback/google
  4. Get Client ID and Client Secret
  5. Configure env vars AUTH_GOOGLE_ID and AUTH_GOOGLE_SECRET 

from https://github.com/beilunyang/moemail