mcpField
M
migrate-to-railcode

migrate-to-railcode

github.com/railcode-hq云与 DevOps179 次安装2 天前更新
代码仓库

关于 migrate-to-railcode

migrate-to-railcode 指导将可用应用移植到 Railcode,同时保留面向用户的行为并考虑平台差异。它帮助开发者盘点依赖、映射服务、分阶段迁移,并在切换用户前验证已部署应用。

主要功能

  1. 01盘点身份验证、数据和托管依赖
  2. 02将项目组件映射到 Railcode 服务
  3. 03指导分阶段移植应用
  4. 04为不支持的组件规划替代方案
  5. 05与原应用并行部署
  6. 06验证迁移行为并报告后续任务

适用场景

  1. 01评估应用是否适合 Railcode
  2. 02移植 Next.js 应用或容器化服务
  3. 03规划队列或 Webhook 的替代实现
  4. 04导入前核对数据所有权映射

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/railcode-hq/railcode-skills --skill migrate-to-railcode

安装后在 Claude Code 中输入/migrate-to-railcode确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
references/
containers.md
databases.md
nextjs-cache-adapter.ts
nextjs-under-api.md
nextjs.md

详细介绍

迁移到 Railcode

迁移是移植,不是重写:项目已经在某处运行,任务是在替换旧托管环境提供的功能(身份验证、数据库、环境变量、定时任务、文件存储)的同时,保留项目为用户提供的功能。

大多数内部工具都适合迁移。通常有一两个部分没有直接对应项,而其中几乎所有部分都能用其他方式重建,用户不会察觉——请参阅另寻他法。目前有一小部分内容完全无法托管——请参阅Railcode 目前无法托管的内容。做出任何承诺前,请先了解这两份清单。

按源项目分类的指南:

源项目指南状态
Next.js(App Router)references/nextjs.md支持 "type": "next",由 @railcode/next 构建。已在实际应用中验证,未覆盖的情况已在指南中列出
容器:通过 Dockerfile 或 docker-compose 堆栈运行任意语言编写的服务端references/containers.md方法和映射基于平台文档中的行为整理;尚无已记录的迁移案例作为依据

对于其他源项目,请遵循以下方法,并使用 create-railcode-app 构建应用。

Railcode 自带的 db 适用于快速、简单的用途。对于大量数据或复杂查询(不支持联接、聚合或事务;读取需分页),它有一定限制,因此需要这些功能的项目应保留原有数据库。references/databases.md介绍了这些限制、如何将数据库连接为数据源、如何通过密钥直接使用数据库、Railcode 尚无连接器支持的数据库、架构迁移的运行位置,以及如何通过 railcode agent-feedback 表达对托管 Postgres 的兴趣。

首先更新

运行 railcode 命令前,请更新技能和 CLI,并确认已发布的内容:

npx skills add Railcode-HQ/railcode-skills
npm install -g railcode@latest
railcode --version
npm view railcode version

如果无法连接 npm,请说明这一点,不要声称本指南是最新的。本版本依据 CLI 0.3.8、@railcode/sdk 0.4.0 和 @railcode/next 0.1.0 编写。

本技能不会重复介绍 SDK、清单或部署流程。这些内容位于 create-railcode-app 中(references/worker-sdk.md、references/cli-workflow.md、references/deployment.md、references/app-patterns.md);以下步骤提到 SDK 接口时,请阅读相应文档。

对于从其他托管环境迁入的人来说,Railcode 是什么

有四项事实会影响每次迁移。请先对照项目逐项确认。

  • 每位查看者都是已登录该组织的成员。 没有匿名访客,也不支持自行注册。Railcode 托管的是内部工具。
  • 后端是 Workers 运行时(nodejs_compat)上的单个 worker 模块,不是 Node 服务端,也不是容器。它会针对每个请求运行一次,且不会在请求之间保留内存中的内容。不支持文件系统、子进程、原生扩展或原始 TCP。
  • 只有在已登录成员的浏览器调用 worker,或应用自身的 cron 触发时,worker 才会运行。 公共互联网无法调用它。
  • 身份验证由平台自动处理。 平台会在任何应用代码运行前为用户完成登录,并向 worker 提供经过验证的 ctx.user。项目自身的登录功能将不再需要,这通常是迁移中删除内容最多的一部分。

作为交换,应用无需设置即可获得:登录、存储(db)、文件存储(files)、LLM 网关(llm)、邮件发送、每个应用独立的密钥、定时任务、连接组织数据库和 SaaS 账号的连接器、托管 Agent、部署历史和日志。

方法

请按以下顺序操作。修改代码前先盘点:大多数迁移失败都是因为先移植了 UI,直到第二天才发现数据层没有对应功能。

1. 清点

阅读项目,并记录以下内容及其文件路径:

内容查找位置
身份验证NextAuth/Auth.js、Clerk、Supabase auth、Passport、会话 Cookie、/login 路由
数据ORM(Prisma、Drizzle、SQLAlchemy、ActiveRecord)、数据库 URL、托管 KV 或 Blob 存储
密钥和配置.env*、process.env.*、Dockerfile 中的 ENV 行、旧主机的控制面板
定时和后台任务vercel.json 中的 cron、队列及其工作进程、服务器中的 setInterval、cron 容器
出站调用服务器代码发起请求的所有主机
入站调用Webhook、公共 API 路由、OAuth 回调,以及所有非由应用自身前端调用的内容
文件上传内容、生成的文档、S3 客户端、挂载卷
进程和二进制文件docker-compose 中的所有服务、所有 apt-get install 命令、代码通过 shell 调用的所有二进制文件
特定主机的软件包@vercel/*、平台 SDK、仅限边缘环境的 API
2. 映射各项内容
项目现状在 Railcode 上的对应方式
自有登录和会话删除身份验证机制。读取 ctx.user(id、email、name、is_admin、roles)。appUsers 列出组织成员
用户表保留属于应用数据的内容(个人资料、偏好、应用级角色),并迁移到 db。Railcode 用户 ID 不同于旧 ID——导入任何以用户为键的数据前,请参阅第 4 步
自有数据库中的逐用户记录使用 db 集合——每个应用一个扁平存储,因此将用户 ID 放入键中(<userId>:<id>),并在服务器代码中强制执行访问控制。适用于少量、简单的数据;对于大型表或复杂查询,请保留数据库
应用读取的数据库(Postgres、BigQuery、Turso)将其连接为数据源:由组织管理员设置的数据连接器。只读。 优先使用已保存的查询(query(name, params));直接 SQL 使用 data(name).runSQL(...),并在清单中配置 adhoc_sql:
应用读取并写入的数据库保留并直接使用:将连接凭据存入 railcode secrets,在 egress: 下添加主机,并使用支持 HTTPS 的客户端(不支持原始 TCP)。或者将应用自己的表迁移到 db。参阅 references/databases.md
Railcode 没有连接器的数据库(MySQL、MongoDB、DynamoDB、Redis 等)同样,将凭据设为密钥,允许访问其主机,并像以前一样通过其 HTTPS API 使用
架构迁移(Prisma、Drizzle、Alembic、Rails)在构建或部署脚本中运行迁移,并在运行该脚本的机器上执行,绝不要在工作进程中运行。参阅 references/databases.md
process.env.SECRET使用 railcode secrets set NAME 设置,并通过 secrets.NAME 读取
Cron 任务在 manifest.yaml 中配置 crons:,每个任务都使用 POST 调用应用自己的某个路由
出站请求在 egress: 下列出各个主机
文件上传、挂载卷使用 files
LLM 提供商密钥和客户端使用 llm,并在清单中设置 llm: true
事务性邮件使用 email,并在清单中设置 email: true
使用自有 OAuth 流程或 API 密钥调用的 SaaS API使用管理员或用户关联的连接器,并通过 connector(name) 调用;或者使用密钥并配置 egress: 主机
队列、WebSocket、Webhook 接收器、原生二进制文件、搜索索引没有直接对应方案。请转到另寻方案

对于每一项没有直接对应方案的内容,移植前请告知用户:项目目前的行为、你提议的重建方案,以及对用户的影响(例如“新的付款会在一分钟内显示,而不是立即显示”)。逐项征得用户同意。用户同意的不同实现是良好的迁移;用户事后才发现的近似替代方案则是一个缺陷。

3. 迁移
  1. 添加 Railcode 文件(railcode.json、manifest.yaml),并确保页面在 railcode dev 下渲染不变。
  2. 替换身份验证。
  3. 逐个替换数据层中的实体。
  4. 将密钥、定时任务和出站网络访问移入清单,并使用 railcode secrets。
  5. 重建另辟蹊径中的各个部分。
  6. 移除旧托管平台的包和配置。

确保每一步都能运行。如果迁移必须等到六步全部完成后才能运行,就无法调试。

railcode dev 会将 db 和 files 存储在本地临时存储中,但 SQL、连接器、电子邮件、 llm 和 Agent 会转发到真实实例。测试会发送电子邮件的路由时,确实会发送电子邮件。

4. 与原应用并行部署

部署到新的应用 slug,并让原应用继续运行。首次部署不会造成任何破坏,用户可以对比两个应用。只有在检查新应用后,才迁移数据并切换用户。

现有数据不会自动迁移。如果数据很重要,就编写一次性导入脚本(读取旧存储并通过临时的仅限管理员访问的路由写入,或通过 CLI 使用 railcode app kv),运行一次,然后删除脚本。

属于用户的记录需要转换所有者信息。旧应用的用户 ID 在 Railcode 中毫无意义,因此用旧 ID 导入记录会导致这些记录无法访问。先建立映射关系——旧 ID → 电子邮件地址 → Railcode 中具有该电子邮件地址的成员(appUsers)——告知用户哪些记录未能匹配,然后再导入,并将每条记录写入对应的 Railcode ID 下。

5. 验证后再报告

以真实用户身份打开已部署的应用,并测试每项已迁移功能;部署成功仅表示包已上传。railcode logs app --app <slug> 会列出每次调用,railcode logs app <invocation-id> 会显示其控制台输出和错误。

按以下顺序报告:哪些功能可用以及如何检查;哪些部分以不同方式重建以及用户体验有何变化;哪些内容未迁移以及原因;用户仍需执行哪些操作(设置密钥、关联连接器、导入数据)。

另辟蹊径

如果项目中的某个部分没有直接对应的实现方式,不要停下,也不要直接舍弃它。要问这部分要实现什么目的,而不是它由什么构成。队列的存在是为了让耗时的工作在用户无需等待的情况下完成。Webhook 的存在是为了让应用获知付款信息。WebSocket 的存在是为了让列表显示最新内容。这些目标通常都可以通过定时任务、存储和轮询的前端来实现。

可用的构建模块:

  • 每分钟最多可触发一次的定时任务,可使用 db、files、llm、email、 secrets、出站网络访问、SQL 和组织连接器。
  • 可用作队列、缓存、锁或游标的存储。
  • 可随意频繁调用工作进程的前端。 浏览器标签页没有子请求额度限制,而且会一直打开,因此可以逐步执行长时间任务。
  • 托管 Agent,适用于耗时、需要执行代码或需要使用 AI 读取文件的工作。
  • 用于连接组织现有数据库和 SaaS 账户的连接器。
  • 可访问你声明的任意主机的出站 HTTP,让托管 API 能够完成本地二进制程序原本执行的工作。

适用的模式:

项目当前的做法可以这样构建对用户的影响
接收 Webhook,以获知发生了某件事(付款、新问题、表单提交)用 cron 轮询提供商的列表或事件 API。在 db 中将上次看到的 ID 或时间戳保存为游标,并确保处理过程具有幂等性最多延迟一分钟
运行作业队列(BullMQ、Celery、Sidekiq)使用 jobs 集合。路由插入带有 status: "queued" 的行;cron 路由获取一小批作业并进行处理,然后将每行标记为已完成。cron 运行至少一次,且可能重叠,因此要用 ctx.invocationId 标记已领取的行,并确保副作用可以安全重复执行作业会在一分钟内启动,而不是立即启动
在一次请求中运行一项长任务(大型导入、对数千行数据生成报告)将任务拆分为多个步骤,并在 db 中保存游标。前端循环调用步骤路由并显示进度,或者由 cron 推进任务。确保每个步骤远低于约 100 次子请求的限额显示进度条,而不是转圈指示器
通过 WebSocket 或 SSE 推送实时更新前端每隔几秒轮询一个开销较低的路由,且仅在标签页可见时轮询。返回一个 updated_at,这样没有变化的轮询只会产生很小的响应更新会在轮询间隔内送达
向浏览器流式传输文本返回流式 Response;SDK 中的 toNdjson() 负责处理分帧无
在自己的数据库中联接和筛选数据根据页面的读取方式设计键(<userId>:<id>、prefix()),存储少量重复数据,使用 query().where() 筛选,并持续分页,直到返回的页面较短。如果数据与其他系统共享,就将其保留在该数据库中,并通过数据连接器读取如果键与页面匹配,则无影响
写入必须保持为关系型的数据库(需要事务、约束,或由其他系统读取)保留该数据库,并在工作器中通过密钥和 HTTPS 驱动程序直接使用(参考文档)。数据连接器为只读,因此只能用于读取。如果数据库没有 HTTPS 接口,就在前面加一层接口,或者在旧主机上保留一个小型写入服务有 HTTPS 驱动程序时无影响
使用 Redis 或进程内存进行缓存使用带有 expires_at 字段的 db 集合,并在读取时检查该字段。内存缓存无法跨请求保留无
使用 Redis 进行速率限制或锁定在 db 中为每个键存储一行,其中包含计数器和窗口起始时间。这不是原子操作,因此只能用于公平控制,不可用于资金处理无
对自己的记录进行全文搜索对于几千条记录,可在工作器或浏览器中加载并筛选。数据量更大时,通过连接器查询源数据库,或通过 egress: 调用托管搜索 API无
调用二进制文件(ffmpeg、ImageMagick、Chromium、pandoc)首先查找适用于工作器包的纯 JavaScript 库;PDF 和电子表格通常可以这样生成。否则,通过 egress: 调用托管 API,或将任务交给可以运行代码的托管 Agent结果可能较慢或为异步返回
自行运行针对 Google、Slack、GitHub 等服务的 OAuth 流程使用连接器。管理员关联组织账户,或者每位用户关联自己的账户;工作器调用 connector(name),永远不会接触令牌用户在 Railcode 中关联一次账户,而不是在应用中关联
接收邮件,或以真实个人或团队的地址发送邮件使用该账户的 Gmail 连接器:组织所有的账户通过 cron 轮询并保存游标,个人账户则由个人托管 Agent 按计划读取其收件箱收到邮件最多延迟一分钟
需要 llm 网关不支持的嵌入、视觉或其他模型功能直接调用相应提供商:将密钥放在 secrets 中,将主机放在 egress: 中无
发送推送或聊天通知使用 email,或使用 Slack 连接器向频道发送消息通知会通过电子邮件或 Slack 送达
多个内部页面中包含一个公开页面(公开表单、状态页)将其拆分。公开页面保留在旧主机上并写入其数据库;Railcode 应用通过数据连接器读取该数据库需要两个部署,而不是一个
使用模板(Jinja、ERB、EJS)在服务器上渲染 HTML使用静态前端,从 /api/* 路由获取 JSON无
在 docker-compose 中运行多个服务使用一个包含多个路由组的工作器。如果两个部分面向不同受众,就将其构建为两个应用无
要做好这项工作,请遵循以下规则:
  • 说明会有哪些变化。 每一行都会有所取舍,通常是延迟。用一句话说明,并在开始构建前征得同意。
  • 优先选择最简单的方案。 前端轮询胜过 cron;cron 胜过 Agent。
  • 设计 cron 方案前先确认其限制:最短间隔为一分钟;每个应用最多五个计划任务;没有调用者(ctx.user 为 null);并且无法启动或读取受管理的 Agent 运行。若五个计划任务不够,一个 cron 路由可以分派多种工作。
  • 不要虚构保证。 如果原方案依赖事务、恰好一次投递,或第三方即时响应,那么轮询方案无法提供这些保证。相关内容见下一节。

如果用户需要的功能不在此表中,请用这些构建模块设计方案,告知用户这是一个新模式,并报告该模式(参见 反馈),以便将其加入。

Railcode 目前无法托管的内容

有些需求无法变通,因为它们需要互联网能够访问应用、持续运行的进程,或平台不提供的运行时。应在盘点阶段识别这些需求,而不是等到移植后才发现。

项目需求不适用的原因
必须得到响应的入站 Webhook — Slack 斜杠命令和交互式按钮、Twilio 语音或 SMS 回复、会不断重试直到收到确认且没有可供轮询的 API 的服务提供商组织外部的任何系统都无法调用该 Worker。仅用于通知的 Webhook 可以替换为轮询(见上文);要求应用作出响应的则不行
面向客户、合作伙伴、移动应用或其他服务器的公共 API 或公共端点原因相同。请求来自已登录成员的浏览器或 Railcode 自有 Agent,而非外部系统
公共或面向客户的页面:营销网站、店面、自助注册、任何供搜索引擎收录的内容所有访问者都必须是已登录的组织成员
自有身份系统:为非组织成员提供登录、向第三方签发自有 API 密钥或令牌身份由组织成员资格决定
真正的实时功能:协同编辑、在线状态、多人游戏、WebSocket 服务端没有推送接口。轮询能实现“看起来是最新的”,但无法做到“逐键实时更新”
长时间运行的进程:守护进程、保持 Socket 连接的机器人、流式消费者(Kafka、Postgres LISTEN)、必须以短于一分钟的间隔触发的工作Worker 按请求运行,然后停止
非 HTTP 协议:通过 TCP 连接的数据库驱动(pg、mysql2、ioredis)、SMTP 服务端、gRPC不支持原始 TCP 入站或出站。数据库可通过数据连接器(读取)或带密钥的 HTTPS 驱动(读取和写入)访问;参见 references/databases.md。只有 Socket 接口且没有其他接入方式的数据库无法访问
容器本身:原样运行的 Python、Go、Ruby、Java 或 PHP 服务、系统包、原生扩展、GPU、本地模型后端是一个 JavaScript 模块。其他语言需要移植,不能直接托管——参见容器指南
大型或多文件后端包:.wasm 模块、超出大小上限的 Worker(10 MB;不支持 worker_routes 的服务为 5 MB)Worker 必须是单个 ESM 模块
自定义域名、原生移动应用、浏览器推送通知应用是位于 <app>.<parent> 的 Web 应用
自有邮件域名:入站邮件服务端、应用拥有的邮箱地址,或通过自定义发件人调用 email.send()email 只能通过平台发件人发送。支持通过个人拥有的 Gmail 帐户发送邮件;参见寻找其他方式

如果项目需要上述某项功能:

  1. 立即直截了当地告知用户。 说清楚该功能,并用一句话说明它目前无法在 Railcode 上运行及原因。不要把需要响应的功能模拟成轮询,并说成两者等效。

  2. 说明仍可实现的部分。 通常,项目其余部分可以迁移,只有这一部分留在原处,并共享一个由 Railcode 通过连接器读取的数据库。如果缺失的部分是项目的核心(例如公共店面、Slack 机器人),就说明项目目前应继续留在现有托管平台上。

  3. 使用 CLI 提交请求。 Railcode 团队会根据这些报告决定接下来要构建什么。每项不同的缺失能力都要提交一份请求,即使项目的部分内容已经迁移:

    railcode agent-feedback --category suggestion \
      --message "Migration blocked on inbound webhooks. The project receives POST /webhooks/slack for slash commands and must reply within 3 seconds; there is no API to poll instead. Request: a way for an app to expose a public, signature-verified endpoint." \
      --context "Source: Express + Postgres in a container, about 40 routes. Everything else mapped to db, secrets and crons. The Slack routes stayed on the old host; the rest was migrated." \
      --agent "<your assistant name>" --model "<your model>"
    

    报告要写得让从未接触过该项目的人也能理解需求:项目是什么(技术栈和大致规模)、它具体需要什么、你改用什么方案,以及迁移是否继续进行。不要包含凭据、客户数据、源代码或任何可识别用户客户的信息。该命令需要已有登录,并会输出 Feedback accepted.;所有选项都在 create-railcode-app 的 “Report Railcode feedback” 部分。如果命令失败,告知用户报告未发送,然后继续处理。

  4. 告知用户你已发送报告,并说明报告内容。

如果平台已有其他实现方式,不要提交请求;请先查看寻找其他实现方式和 create-railcode-app。

机器安全

框架构建很耗资源,而迁移会运行许多次构建。

  • 一次只运行一个构建或开发服务器,并在结束本轮前停止你启动的所有进程。使用 ps 检查,不要想当然。
  • 绝不要将 package.json 的 build 脚本指向会调用框架适配器的程序,前提是该适配器本身会运行 npm run build。这会导致构建链无限延伸。Next.js 指南中有具体规定。
  • 除非用户要求,否则不要启动项目的 docker-compose 堆栈来了解其工作方式。请阅读代码。

反馈

每次迁移后,都使用 railcode agent-feedback 报告具体阻碍(用法见上文和 create-railcode-app),每项发现提交一份报告:源项目使用了什么、什么功能没有等效实现、你采用了什么重建模式,以及指南未能预见的故障。缺失的能力使用 --category suggestion;本技能中错误或不清晰的步骤使用 --category friction。这就是下一份指南的编写依据。

评论

支持 Markdown