Refine blog navigation and AI install guide

This commit is contained in:
yoma committed 2026-07-03 21:04:30 +08:00
1 parent e0a6a69578
commit a2cb26bb5d
7 files changed
+117 -233

No files matched your search

@@ -0,0 +1,112 @@
---
import { Icon } from "astro-icon/components";
const promptText =
"请先 Fork EdgeEver 仓库:https://github.com/msh01/edgeever\n\nFork 完成后,请使用 Fork 后的仓库把 EdgeEver 安装部署到 Cloudflare 上。";
---
<section class="not-prose relative left-1/2 my-10 w-[min(92vw,960px)] -translate-x-1/2" data-agent-install-tabs>
<div class="mx-auto flex max-w-xl items-center justify-center gap-14 border-b border-slate-200 text-lg font-semibold">
<button
class="agent-install-tab px-2 pb-4 text-slate-500 transition hover:text-brand-text"
type="button"
data-tab-target="manual"
aria-selected="false">
手动安装
</button>
<button
class="agent-install-tab active px-2 pb-4 text-brand relative transition"
type="button"
data-tab-target="agent"
aria-selected="true">
通过 AI Agent 安装
</button>
</div>
<div class="mt-8 overflow-hidden rounded-[28px] bg-slate-100 px-6 py-8 sm:px-10 md:px-14">
<div class="agent-install-panel" data-tab-panel="agent">
<div class="mx-auto max-w-3xl rounded-2xl bg-white p-6 shadow-sm">
<p class="text-sm font-medium text-slate-400">提示词</p>
<pre class="mt-4 whitespace-pre-wrap break-words font-mono text-base leading-8 text-slate-900">{promptText}</pre>
</div>
<div class="mt-8 flex justify-center">
<button
class="agent-install-copy inline-flex items-center gap-2 rounded-full bg-brand px-8 py-3 text-base font-semibold text-white shadow-[0_14px_30px_-18px_rgb(var(--edgeever-brand-rgb)/0.85)] transition hover:-translate-y-0.5 hover:bg-brand-hover"
type="button"
data-copy-text={promptText}>
<Icon class="h-5 w-5" name="bx:bx-copy" />
<span data-copy-label>复制提示词</span>
</button>
</div>
</div>
<div class="agent-install-panel hidden" data-tab-panel="manual">
<p class="mx-auto max-w-3xl text-center text-lg leading-9 text-slate-600">
手动安装适合熟悉 Cloudflare 与命令行的用户。准备好 Fork 仓库和 Cloudflare 登录态后,按顺序执行下面命令。
</p>
<div class="mx-auto mt-8 max-w-3xl rounded-2xl bg-white p-6 font-mono text-sm leading-7 text-slate-800 shadow-sm">
<pre class="whitespace-pre-wrap">bun install
EDGE_EVER_PASSWORD='&lt;首次登录密码&gt;' bun run deploy:setup
bun run deploy:doctor
bun run deploy</pre>
</div>
</div>
</div>
</section>
<script>
document.querySelectorAll("[data-agent-install-tabs]").forEach((root) => {
const tabs = Array.from(root.querySelectorAll("[data-tab-target]"));
const panels = Array.from(root.querySelectorAll("[data-tab-panel]"));
tabs.forEach((tab) => {
tab.addEventListener("click", () => {
const target = tab.getAttribute("data-tab-target");
tabs.forEach((item) => {
const isActive = item === tab;
item.setAttribute("aria-selected", String(isActive));
item.classList.toggle("active", isActive);
item.classList.toggle("text-brand", isActive);
item.classList.toggle("text-slate-500", !isActive);
});
panels.forEach((panel) => {
panel.classList.toggle("hidden", panel.getAttribute("data-tab-panel") !== target);
});
});
});
root.querySelectorAll("[data-copy-text]").forEach((button) => {
button.addEventListener("click", async () => {
const text = button.getAttribute("data-copy-text") || "";
const label = button.querySelector("[data-copy-label]");
try {
await navigator.clipboard.writeText(text);
if (label) label.textContent = "已复制";
window.setTimeout(() => {
if (label) label.textContent = "复制提示词";
}, 1600);
} catch {
if (label) label.textContent = "复制失败";
}
});
});
});
</script>
<style>
.agent-install-tab.active::after {
position: absolute;
right: 0;
bottom: -1px;
left: 0;
height: 3px;
border-radius: 999px;
background: var(--color-brand);
content: "";
}
</style>
+1 -1
View File
@@ -47,7 +47,7 @@ const heroSlogan = "基于 Cloudflare 全家桶自托管的开源『印象笔记
class="flex gap-2 items-center justify-center px-6 py-3 bg-white/50 backdrop-blur-sm border border-slate-200 hover:border-slate-300 rounded-xl text-slate-600 hover:text-brand-text transition-all text-sm font-medium hover:-translate-y-0.5"
>
<Icon class="w-5 h-5" name="bx:bxs-bot" />
AI 辅助部署
通过AI Agent安装
</a>
</div>
</div>
+2 -2
View File
@@ -10,8 +10,8 @@ const menuitems = [
path: "/#features",
},
{
title: "部署成本",
path: "/pricing",
title: "博客",
path: "/blog",
},
{
title: "关于项目",
-54
View File
@@ -1,54 +0,0 @@
---
import { Tick } from "@/components/ui/icons";
import Link from "@/components/ui/link.astro";
const { plan } = Astro.props;
---
<div class="h-full">
<div
class={`flex flex-col h-full justify-between border border-slate-100 p-8 rounded-3xl glow-card hover-lift relative ${plan.popular ? 'border-brand/20 bg-gradient-to-b from-brand-soft/40 to-white' : 'bg-white'}`}>
{plan.popular && (
<div class="absolute -top-3 right-6 border border-brand-border bg-brand-muted px-3 py-1 rounded-full text-xs font-semibold uppercase tracking-wider text-brand-text shadow-md shadow-brand/10">
推荐方案
</div>
)}
<div>
<div class="text-center pb-6 border-b border-slate-100">
<h4 class="text-base font-semibold text-slate-500 uppercase tracking-wider">{plan.name}</h4>
<p class="mt-4 text-4xl font-extrabold text-slate-900 md:text-5xl">
{
plan.price && typeof plan.price === "object"
? plan.price.monthly
: plan.price
}
</p>
</div>
<ul class="grid mt-8 text-left gap-y-4">
{
plan.features.map((item) => (
<li class="flex items-start gap-3 text-slate-600 text-sm">
<span class="bg-brand-soft rounded-full p-0.5 shrink-0 flex items-center justify-center border border-brand-border/20 mt-0.5">
<Tick class="w-4.5 h-4.5 text-brand" />
</span>
<span class="leading-relaxed">{item}</span>
</li>
))
}
</ul>
</div>
<div class="flex mt-10">
<Link
href={plan.button.link || "#"}
block
style={plan.popular ? "primary" : "outline"}
class={`w-full text-center py-3 rounded-xl font-semibold shadow-sm transition-all hover:scale-[1.01] ${plan.popular ? 'shadow-brand/10' : 'border border-slate-200 hover:bg-slate-50'}`}>
{plan.button.text || "Get Started"}
</Link>
</div>
</div>
</div>
@@ -11,120 +11,3 @@ category: "Deployment"
author: "EdgeEver Team"
tags: [cloudflare, deploy, ai-agent, serverless]
---
如果您习惯使用 AI 编程助手或 Agent(例如 Codex、Claude Code、Antigravity 等),可以让 Agent 按核心仓库的部署协议自动完成 EdgeEver 的 Cloudflare 部署。
这篇文章同步自 `edgeever/docs/agent-deploy-cloudflare.md` 的当前流程。推荐先 Fork 官方仓库到自己的 GitHub 账号,再把 Fork 仓库链接交给 Agent。
---
### 一键部署指令
您可以直接把下面这句话发送给 AI 助手:
```text
这个是我的 EdgeEver Fork 仓库链接:<你的 Fork 仓库 URL>。请把这个项目安装部署到 Cloudflare 上。
```
---
### Agent 应遵守的规则
- 先读取 `AGENTS.md`、`README.md`、`.env.local.example` 和 `docs/agent-deploy-cloudflare.md`。
- 不创建新分支,部署工作在 `main` 上执行。
- 优先使用 `bun run deploy:setup`、`bun run deploy:doctor` 和 `bun run deploy`。
- 不把个人 Worker 名称、D1 database ID、R2 bucket 名称、account ID、API token 或域名硬编码进源码。
- 本地和私有部署值写入 `.env.local`,该文件已被 git ignore。
- 只有 Cloudflare 授权、首次登录密码、自定义域名归属等无法安全推断的信息,才需要询问用户。
---
### 标准部署流程
1. Clone 仓库并进入目录:
```sh
git clone <repo-url>
cd edgeever
```
2. 安装依赖:
```sh
bun install
```
3. 确认 Cloudflare 授权:
```sh
bunx wrangler whoami
```
如果失败,Agent 应先协助完成 Cloudflare 登录或要求提供合适的 API Token。
4. 准备 Cloudflare 资源和 `.env.local`:
```sh
EDGE_EVER_PASSWORD='<first-login-password>' bun run deploy:setup
```
`deploy:setup` 会在需要时复制 `.env.local.example`,复用或创建 D1 数据库,创建 R2 bucket,并根据首次登录密码生成 `EDGE_EVER_AUTH_PASSWORD_HASH`。
5. 检查部署输入:
```sh
bun run deploy:doctor
```
所有 `fail` 结果都应修复后再继续。
6. 部署:
```sh
bun run deploy
```
该命令会构建 Web 应用、应用远端 D1 migrations,并部署 Cloudflare Worker。部署过程中,脚本会把 `EDGE_EVER_AUTH_PASSWORD_HASH` 作为 Worker secret 上传。
7. 验证结果:
```sh
curl -I https://<worker-url>/
curl https://<worker-url>/api/openapi.json
```
然后打开站点,用 `EDGE_EVER_AUTH_USERNAME` 和首次登录密码登录,再在应用内 MCP 设置创建 Token。
### 高级自定义变量与多实例部署
可以在执行 `deploy:setup` 或 `deploy` 前向 `.env.local` 写入自定义变量:
```sh
EDGE_EVER_WORKER_NAME=edgeever
EDGE_EVER_D1_DATABASE_NAME=edgeever
EDGE_EVER_R2_BUCKET_NAME=edgeever-resources
EDGE_EVER_R2_PREVIEW_BUCKET_NAME=edgeever-resources-preview
EDGE_EVER_AUTH_USERNAME=admin
EDGE_EVER_CUSTOM_DOMAIN=notes.example.com
```
多实例可以设置 `EDGE_EVER_INSTANCE=<name>`,并使用作用域变量:
```sh
EDGE_EVER_INSTANCE=PROD
EDGE_EVER_PROD_WORKER_NAME=edgeever-prod
EDGE_EVER_PROD_D1_DATABASE_ID=<特定的数据库-ID>
EDGE_EVER_PROD_R2_BUCKET_NAME=edgeever-prod-resources
EDGE_EVER_PROD_AUTH_PASSWORD_HASH=<特定的密码-HASH>
```
### Agent 何时应该停下来询问
只有以下情况应阻塞并询问用户:
- Cloudflare 授权缺失,且 Agent 无法打开或完成登录。
- 用户必须选择或提供首次登录密码。
- 自定义域名不在当前 Cloudflare 账号中。
- 资源创建因账号限制、权限、计费或命名冲突失败,且无法通过选择新名称解决。
部署完成后,Agent 应报告部署 URL、登录用户名、密码是用户提供还是生成的、在应用哪里创建 EdgeEver MCP Token,以及是否还存在自定义域名或 DNS 步骤。
+2
View File
@@ -1,5 +1,6 @@
---
import { getCollection, render } from "astro:content";
import AgentInstallTabs from "@/components/agent-install-tabs.astro";
import BlogLayout from "@/layouts/BlogLayout.astro";
export async function getStaticPaths() {
@@ -15,5 +16,6 @@ const { Content } = await render(entry);
---
<BlogLayout frontmatter={entry.data}>
{entry.slug === "ai-agent-deploy-cloudflare" && <AgentInstallTabs />}
<Content />
</BlogLayout>
-59
View File
@@ -1,59 +0,0 @@
---
import Layout from "@/layouts/Layout.astro";
import Container from "@/components/container.astro";
import Sectionhead from "@/components/sectionhead.astro";
import PricingCard from "@/components/pricing.astro";
const pricing = [
{
name: "个人自托管",
price: "接近 $0",
popular: true,
features: [
"基于 Cloudflare Workers、D1 和 R2",
"README 估算:短笔记可达 10 万条量级",
"README 估算:200KB 图片约可存放 5 万张",
"Web 端图片压缩可减少 R2 资源占用",
"REST API、OpenAPI、CLI 与 Remote MCP 均可使用",
"适合个人笔记、迁移旧资料和 AI Agent 读取整理"
],
button: {
text: "查看部署仓库",
link: "https://github.com/msh01/edgeever",
},
},
{
name: "超出免费额度后",
price: "按 Cloudflare 计费",
popular: false,
features: [
"费用由你的 Cloudflare 账号计划和用量决定",
"大量图片、附件或高频 API 调用会增加资源消耗",
"多实例可通过 EDGE_EVER_INSTANCE 和作用域变量隔离",
"自定义域名可通过 EDGE_EVER_CUSTOM_DOMAIN 配置",
"部署前可运行 bun run deploy:doctor 检查配置",
"具体单价请以 Cloudflare 官方定价页为准"
],
button: {
text: "查看 Cloudflare 定价",
link: "https://www.cloudflare.com/zh-cn/developer-platform/products/",
},
},
];
---
<Layout title="托管费用">
<Container>
<Sectionhead>
<Fragment slot="title">极低的使用与托管成本</Fragment>
<Fragment slot="desc">
EdgeEver 本身是开源项目。官网不替 Cloudflare 承诺价格,以下只同步核心仓库 README 对个人使用规模的说明;实际账单以您的 Cloudflare 账号计划、资源用量和官方定价为准。
</Fragment>
</Sectionhead>
<div
class="grid md:grid-cols-2 gap-10 mx-auto max-w-4xl mt-16 pb-12">
{pricing.map((item) => <PricingCard plan={item} />)}
</div>
</Container>
</Layout>