关于 revamp
revamp 是面向旅行者和旅游经营者的亚美尼亚旅游市场平台,可浏览住宿、餐厅、旅游和地区体验,并根据实时已发布目录生成行程。平台还提供经营者刊登管理与审核流程;目前尚未接入实际付款。
主要功能
- 01搜索并筛选旅游目录
- 02地图与亚美尼亚图集浏览
- 03按实时目录生成逐日行程
- 04经营者管理住宿和旅游项目
- 05审核后发布新项目
适用场景
- 01查找特定地区的住宿、餐厅或旅游
- 02根据已发布项目规划逐日行程
- 03经营者提交和维护住宿或旅游项目
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://mcp.revampvacations.com/mcpclaude mcp add --transport http com-revampvacations-revamp https://mcp.revampvacations.com/mcp{"mcpServers":{"com-revampvacations-revamp":{"type":"http","url":"https://mcp.revampvacations.com/mcp"}}}在 Cursor 中打开{"servers":{"com-revampvacations-revamp":{"type":"http","url":"https://mcp.revampvacations.com/mcp"}}}在 VS Code 中打开{"mcpServers":{"com-revampvacations-revamp":{"type":"http","url":"https://mcp.revampvacations.com/mcp"}}}{"mcpServers":{"com-revampvacations-revamp":{"serverUrl":"https://mcp.revampvacations.com/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
4| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| type | enum | 否 | 限定为一种列表类型。stay | tour | experience | eat | place |
| limit | integer | 否 | 最大结果数(默认值为 10)。 |
| query | string | 否 | 按标题、描述、标签、菜系和城市进行全文匹配。 |
| region | string | 否 | 亚美尼亚的地区或城市,例如 'Yerevan'、'Tavush'、'Dilijan'。 |
| cuisine | string | 否 | 适用于餐厅:菜系或场所类型,例如 'Armenian'、'Fast food'。 |
详细介绍
重塑 Revamp 旅行市场
Revamp Travel 是一个双边 Armenian 旅行市场。旅行者可以通过编辑内容、搜索和交互式 Armenia 地图册,浏览住宿地点、餐厅、旅行团和地区体验。运营商可以注册自己的账户并创建住宿/旅行团列表——也可以从其他网站上的现有列表链接开始,快速预填信息——之后这些列表会经过简短审核,才会在其他地方显示。AI 行程规划器会根据实时发布的目录生成行程。
当前状态
该代码库是一个全栈应用:React SPA 直接连接 Supabase(Postgres + Auth + Row-Level Security),用于管理账户和列表;前端还有一个小型 Express API,负责两项工作——由 Anthropic 提供支持的行程规划器,以及供运营商控制面板使用的链接导入预填辅助功能。这是里程碑 A:真实账户、真实的运营商自有列表、由 RLS 强制执行的所有权,以及审核/发布关卡——但尚未接入真实支付。预订 CTA 会如实显示相应状态:未登录时显示“登录以预订”;已登录时显示禁用的“支付功能即将推出”状态。显示的价格是真实价格(每个列表中的 price_cents),但目前不会收取任何费用——这属于里程碑 B(Stripe),已在 CLAUDE.md 中规划,但尚未构建。
| 区域 | 包含内容 |
|---|---|
| 首页探索 | 主视觉、统一搜索、类别入口、精选列表、地区、地图册预览和结尾 CTA。 |
| 市场目录 | 文本搜索、类别筛选、地区筛选、空状态、响应式卡片,以及桌面/移动端地图视图。 |
| 列表 | 住宿/餐厅共用详情模板,以及专用的 GetYourGuide 风格旅行团详情布局;两者均包含图库、详情、设施、根据身份验证状态显示的预订 CTA、位置地图册和相关记录。 |
账户(/login、/signup) | 通过 Supabase Auth 自助注册为旅行者或运营商。 |
运营商控制面板(/dashboard) | 添加、编辑和删除自己的住宿和旅行团列表,也可以从粘贴的链接开始(尽力从该页面自身的公开元数据中预填标题/描述/照片引用——绝不抓取网页,也绝不自动保存第三方照片)。新列表和重新提交的列表会进入审核,审核通过前对其他人不可见;已上线列表的编辑会立即保存。写入操作直接发送到 Supabase,并由 Row-Level Security 限定为已登录的运营商所有——而非依赖客户端强制执行。餐厅列表仅由编辑团队维护/作为种子数据提供。 |
审核队列(/admin) | 批准待审核列表或退回列表;仅有一个管理员账户(通过 SQL 提升权限,不提供注册切换项)。批准后立即发布;附上备注退回列表后,运营商可以修正并重新提交。 |
AI 行程规划器(/plan) | 按天生成行程,由服务端调用 Anthropic API,并以网站的实时已发布住宿/旅行团/餐厅目录为依据。需要 ANTHROPIC_API_KEY(见下文“环境”)。 |
| 地图 | 基于确定性 SVG 的 Armenia 地图册,包含价格标记和同步选择。无需外部地图密钥。 |
| 品牌 | 实现 Revamp 品牌手册:小写的 revamp. 字标、紧凑标志 re.、#F15822 橙色、#212121 炭灰色、白色、圆角造型和无衬线字体。 |
技术
客户端使用 React 19、TypeScript、Vite 7、Tailwind CSS 4、Wouter、Radix UI/shadcn 组件、Lucide 图标和 Sonner,并直接连接 Supabase(@supabase/supabase-js)来处理身份验证和列表。Express、Zod 和官方 @anthropic-ai/sdk 保留在服务端,用于处理唯一需要浏览器无法持有的密钥的路由——AI 行程规划器。列表不再使用 JSON 文件或进程内存储。
要求
使用 Node.js 22 或更高版本、pnpm 10 或更高版本,以及一个 Supabase 项目(免费套餐即可)。
node --version
pnpm --version
本地设置
pnpm install
cp .env.example .env
# fill in VITE_SUPABASE_URL / VITE_SUPABASE_ANON_KEY to run against real accounts + listings
# fill in ANTHROPIC_API_KEY to enable /plan
pnpm dev
如果未设置 Supabase 变量,应用仍可运行,并使用静态的 shared/listings.ts 种子数据作为只读备用数据进行浏览(ListingsContext 会报告 offline: true)——足以查看界面,但注册、登录和任何写入操作都会失败。请参阅下方的“Supabase 设置”,以启用真实数据。
pnpm dev 会通过 concurrently 同时运行两个进程:Vite(客户端,支持 HMR)和 Express API(通过 tsx watch 运行 server/index.ts,监听 API_PORT,默认值为 3001)——API 进程现在只提供 /api/plan-trip。Vite 会将 /api/* 请求代理到该进程,因此打开 Vite 输出的 URL 即可——无需直接访问 API 自己的端口。
Supabase 设置
-
在 supabase.com 创建项目。从其 API 设置中复制 Project URL 和 anon/public key,填入
.env中的VITE_SUPABASE_URL/VITE_SUPABASE_ANON_KEY。 -
按顺序各运行一次
supabase/migrations/0001_init.sql和supabase/migrations/0002_review_gate_and_admin.sql——将它们粘贴到 SQL Editor 中,或使用 CLI 通过supabase db push/psql运行。两者会创建profiles、listings、审核门控触发器,以及应用所依赖的所有行级安全策略。 -
启动应用(
pnpm dev,或部署应用),并在/signup注册一个真实账户,选择 Operator。该账户将成为用于管理种子目录的“Revamp”主账户。 -
仅在本地,将
SUPABASE_SERVICE_ROLE_KEY(从同一 API 设置页面获取——请妥善保密)、SEED_OPERATOR_EMAIL和SEED_OPERATOR_PASSWORD(第 3 步创建的账户)添加到.env,然后运行:pnpm seed:catalog这会将
shared/listings.ts中的种子住宿、旅行团和餐厅导入真实的listings表,并归属到该运营者账户;这些条目会自动通过审核并立即上线(已审核内容的批量导入不应进入审核队列)。可安全重复运行——系统会按 slug 匹配已有记录并跳过。具体说明为何餐厅导入需要服务角色密钥,请参阅scripts/seed-catalog.ts文件头部的注释。 -
若要审核真实提交,请为自己注册一个单独账户(不要使用主账户),并将其提升为管理员——在
/signup正常注册,在 Supabase 控制面板中找到你的用户 ID(Authentication → Users),然后在 SQL Editor 中运行:update public.profiles set role = 'admin' where id = '<your-user-id>';按设计,用户无法自行注册管理员账户。使用该账户登录并打开
/admin,即可批准待审核条目,或附上说明将其退回。
验证和生产构建
pnpm check
pnpm build
pnpm preview
pnpm build 会将客户端构建到 dist/public,并将 Express 服务打包到 dist/index.js。pnpm preview(Vite 的静态预览)只会提供客户端——构建后如需运行完整应用:
pnpm start
此单一进程会在同一个 PORT(默认值为 3000)上提供已构建的 SPA 和 /api/plan-trip 路由。
项目结构
client/
index.html
public/
brand/revamp-mark.svg
images/ # self-hosted brand illustrations (see Assets below)
src/
components/
ui/
ArmeniaMap.tsx
BookingCta.tsx # auth-aware booking CTA, shared by every listing detail layout
BrandMark.tsx
ListingCard.tsx
RequireRole.tsx # route guard for /dashboard
SearchBar.tsx
SiteFooter.tsx
SiteHeader.tsx # includes the auth-aware account menu
TourCard.tsx
TourDetail.tsx
contexts/
ThemeContext.tsx
AuthContext.tsx # Supabase Auth session/profile state (traveler | operator | admin)
ListingsContext.tsx # live catalog: reads/writes the Supabase `listings` table directly
data/
listings.ts # thin compatibility barrel over shared/listings.ts
lib/
api.ts # fetch wrapper for /api/plan-trip and /api/import-listing
supabase.ts # browser Supabase client
slug.ts # slugify() for new listing titles
tourFacts.ts
pages/
Explore.tsx
Home.tsx
ListingPage.tsx
Dashboard.tsx # operator-only: add/edit/delete own stay & tour listings, link-prefill
AdminReview.tsx # admin-only: approve/reject pending listings
Login.tsx
Signup.tsx
MapPage.tsx
Plan.tsx # AI trip planner
Tours.tsx
NotFound.tsx
App.tsx
index.css
main.tsx
server/
index.ts # Express app: JSON body parsing, API routes, static SPA serving
routes.ts # POST /api/plan-trip and POST /api/import-listing — listings CRUD is gone (Supabase + RLS replaced it)
supabase.ts # anon-key server client: planner catalog digest + verifyUser() for import-listing auth
urlPrefill.ts # best-effort OpenGraph/meta-tag fetch for the link-prefill assist, with an SSRF guard
planner.ts # Anthropic API call + prompt/response handling for the trip planner
supabase/
migrations/
0001_init.sql # profiles + listings schema, triggers, and every RLS policy
0002_review_gate_and_admin.sql # pending/review-note columns, admin role, review-gate trigger
scripts/
seed-catalog.ts # one-time import of shared/listings.ts into the live `listings` table
shared/
listings.ts # canonical Listing type, seed data, and constants (client + server + scripts)
const.ts
CLAUDE.md
brandbook-implementation.md
marketplace-spec.md
ENVIRONMENT.md
产品路由
| URL | 用途 |
|---|---|
/ | 编辑精选首页和统一搜索。 |
/explore | 展示所有条目,支持搜索和筛选。 |
/explore/stay | 住宿分类。 |
/explore/eat | 餐厅分类。 |
/explore/tour | 旅行团分类(专用的 GetYourGuide 风格浏览器)。 |
/map | 以地图为核心的探索页面。 |
/listing/:slug | 单个条目的详情页。 |
/plan | AI 行程规划器。 |
/dashboard | 仅限运营者:添加、编辑和删除自己的住宿和旅行团条目。 |
/admin | 仅限管理员:审核待处理条目、批准条目或附上说明将其退回。 |
/login | 登录。 |
/signup | 注册成为旅行者或运营者。 |
编辑清单
根据操作内容,有两种方式:
- **日常操作/供实际运营人员使用:**以运营人员身份在
/signup注册,然后打开/dashboard并使用表单界面;也可以先使用“从链接预填”,从粘贴的 URL 自身的公开元数据中填入标题/描述。写入内容会直接进入 Supabaselistings表,并通过行级安全(Row-Level Security,supabase/migrations/0001_init.sql、0002_review_gate_and_admin.sql)限定为仅限你自己的账户——客户端无法自行强制执行这一限制。新建或重新提交的清单会设为pending,在管理员于/admin批准之前不会显示在任何其他位置;编辑已发布的清单后会立即保存并显示(包括卡片、筛选条件、地图标记、相关内容以及基于 slug 的详情页,这些内容都通过client/src/contexts/ListingsContext.tsx中的ListingsContext实时读取),无需再次审核。 - 为全新安装植入数据:
shared/listings.ts是一次性数据源,pnpm seed:catalog(scripts/seed-catalog.ts)会将其中内容导入 Supabaselistings表,并归属一个名为 “Revamp” 的内部运营账户——请参阅上文的 Supabase 设置。之后编辑shared/listings.ts只会影响未来运行pnpm seed:catalog时的数据(可以安全地重复运行;现有行会按 slug 匹配并跳过),不会影响已上线的行。餐厅(type: "eat")仅用于种子数据/编辑内容;RLS 完全没有针对type = 'eat'的插入策略,因此任何运营人员——包括内部账户——都无法通过常规应用创建、编辑或删除此类条目(shared/listings.ts中的EDITABLE_LISTING_TYPES仅包含stay和tour)。
无论采用哪种方式:坐标都必须在亚美尼亚境内(纬度 38–42,经度 43–47——由数据库检查约束强制执行),不得编造客户评分、评论、推荐语、预订次数或认证声明。
品牌指南
进行视觉更改前,请先阅读 brandbook-implementation.md。正式品牌系统简约且严格受控。请使用完整的小写 revamp. 字标(末尾带句点)、紧凑的 re. 标识、橙色 #F15822、炭灰色 #212121 和白色 #FFFFFF。指定的品牌字体系列为 Circular Std;除非环境中提供了 Circular Std,否则代码会使用获得许可、可安全用于 Web 的视觉替代字体 Manrope。
资源与可移植性
图片 URL 定义在 shared/listings.ts 中。所有随项目提供的图片都是自托管的品牌插图,位于 client/public/images/ 下(纯 SVG,不会发起外部请求)——该市场没有任何外部图片依赖,图片功能可完全离线/在隔离网络环境中运行。只要有真实摄影图片,就请替换为真实照片:更新 shared/listings.ts 顶部的 assets 映射并重新植入数据,或者通过 /dashboard 为每条清单设置 image URL(仅限 stay/tour)。
环境
目录和浏览体验无需任何密钥即可只读运行(使用静态种子数据作为回退),但账户和实际写入需要 VITE_SUPABASE_URL/VITE_SUPABASE_ANON_KEY。AI 行程规划器需要 ANTHROPIC_API_KEY(仅在服务端使用,不会发送到浏览器)。链接预填助手(POST /api/import-listing)无需新增密钥——它会复用同一个 anon-key Supabase 客户端来验证调用者是否已登录,然后直接获取粘贴的 URL。完整变量列表请参阅 ENVIRONMENT.md,其中还包括仅供种子脚本使用的 Supabase service-role 密钥,以及可选的 ANTHROPIC_MODEL/API_PORT 覆盖项。
Claude Code
Claude Code 应先阅读 CLAUDE.md。该文件介绍了架构、不可妥协的内容/品牌/支付诚信约束、Supabase 数据模型和 RLS 设计、路由行为、组件规范、资源模型、质量门槛以及常见更改方案。
部署说明
服务仍有两个有意保留的职责(参见 CLAUDE.md):调用 Anthropic API 规划行程,以及获取用户粘贴的 URL 的公开元数据,以辅助预填链接(需通过登录检查和 SSRF 防护)。其余所有功能——账户、房源、所有权以及审核/发布关卡——都由 Supabase 负责,并通过行级安全策略和数据库触发器保护,而非依赖应用代码。目前仍没有真正的支付处理或预订确认;那是里程碑 B,是像这次升级一样有意规划的升级,而不是临时加到 BookingCta.tsx 上。要部署为单个 Node 进程:按顺序运行两个 Supabase 迁移(0001_init.sql,然后是 0002_review_gate_and_admin.sql)并运行一次种子脚本,将你自己的账户提升为 admin(参见上文 Supabase 设置),使用 pnpm build 构建,并设置 PORT、VITE_SUPABASE_URL、VITE_SUPABASE_ANON_KEY 和 ANTHROPIC_API_KEY 后运行 pnpm start。
Netlify(无服务器)
此仓库已配置为部署到 Netlify,API 将作为无服务器函数运行,而非持久进程:
netlify.toml— 执行pnpm build构建 → 发布dist/public;函数目录为netlify/functions;在/*上配置prerender边缘函数;将/api/* → /.netlify/functions/api/:splat重写;将/robots.txt+/sitemap.xml重写到函数;为 wouter 配置 SPA 回退(/* → /index.html);并在/dashboard//admin上设置X-Robots-Tag: noindex标头。netlify/functions/api.ts— 使用serverless-http封装共享的 Expressapp(server/app.ts)。API 包括POST /api/plan-trip、POST /api/import-listing、POST /api/sync-ical、动态生成的robots.txt/sitemap.xml,以及GET /api/prerender;账户、房源和审核关卡则由浏览器在 RLS 保护下直接连接 Supabase。netlify/edge-functions/prerender.ts— 用于 SEO/爬虫发现。Deno 边缘函数会为非 JS 爬虫(Googlebot、GPTBot、ClaudeBot、PerplexityBot、Twitterbot,……)提供由server/prerender.ts生成的真实服务器渲染 HTML(通过/api/prerender),而真实访客则收到未经修改的 SPA。浏览器和 JS 爬虫所需的真实页面级<title>/meta/OG/JSON-LD 由client/src/hooks/useDocumentMeta.ts提供。无需新增依赖或环境变量——渲染器使用手写 HTML,robots/sitemap 的来源则根据每个请求推导。- 环境变量(Site configuration → Environment variables):
VITE_SUPABASE_URL、VITE_SUPABASE_ANON_KEY— 构建时(Vite 会将其内联到客户端)和函数运行时(规划器会读取已发布的目录,且/api/import-listing会使用 anon key 验证调用方的 bearer token)都需要这些变量。首次构建前就应设置;值必须是完整的https://<project-ref>.supabase.co,否则客户端会回退到只读的shared/listings.ts种子数据。更改这些值需要重新构建(重新部署),仅保存变量并不足够。ANTHROPIC_API_KEY(以及可选的ANTHROPIC_MODEL)— 仅在运行时使用,供/api/plan-trip使用。- 切勿在 Netlify 上设置
SUPABASE_SERVICE_ROLE_KEY— 它仅用于在本地一次性运行pnpm seed:catalog。
- 将 GitHub 仓库连接到 Netlify 后,每次推送到
main都会持续部署。Supabase 迁移(0001–0005,包括0005为运营人员上传照片创建的listing-photosStorage 存储桶)、pnpm seed:catalog以及管理员权限提升都是单独的一次性步骤(参见 Supabase 设置)。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。