关于 encore-code-review
面向 Encore.ts 项目的代码审查技能,可检查常见的架构、安全和可靠性问题,并按严重程度与位置报告问题、警告及良好实践。
主要功能
- 01检查基础设施声明位置
- 02审查导入方式与服务调用模式
- 03标记错误处理和 SQL 风险
- 04检查端点暴露与订阅处理器
- 05按严重程度和位置报告结果
适用场景
- 01审查 Encore.ts 拉取请求
- 02查找不安全的 SQL 拼接
- 03检查服务边界和导入方式
- 04审计订阅幂等性与密钥访问
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ encoredev/ skills --skill encore-code-review安装后在 Claude Code 中输入/encore-code-review确认已加载。
详细介绍
Encore 代码审查
说明
审查 Encore.ts 代码时,请检查以下常见问题:
严重问题
1. 在函数内部定义基础设施
// WRONG: Infrastructure declared inside function
async function setup() {
const db = new SQLDatabase("mydb", { migrations: "./migrations" });
const topic = new Topic<Event>("events", { deliveryGuarantee: "at-least-once" });
}
// CORRECT: Package level declaration
const db = new SQLDatabase("mydb", { migrations: "./migrations" });
const topic = new Topic<Event>("events", { deliveryGuarantee: "at-least-once" });
2. 使用 require() 而非 import
// WRONG
const { api } = require("encore.dev/api");
// CORRECT
import { api } from "encore.dev/api";
3. 服务导入模式错误
// WRONG: Direct import from another service
import { getUser } from "../user/api";
// CORRECT: Use ~encore/clients
import { user } from "~encore/clients";
const result = await user.getUser({ id });
4. 缺少错误处理
// WRONG: Returning null for not found
const user = await db.queryRow`SELECT * FROM users WHERE id = ${id}`;
if (!user) return null;
// CORRECT: Throw APIError
import { APIError } from "encore.dev/api";
const user = await db.queryRow`SELECT * FROM users WHERE id = ${id}`;
if (!user) {
throw APIError.notFound("user not found");
}
5. SQL 注入风险
// WRONG: String concatenation
await db.query(`SELECT * FROM users WHERE email = '${email}'`);
// CORRECT: Template literal with automatic escaping
await db.queryRow`SELECT * FROM users WHERE email = ${email}`;
警告问题
6. 缺少类型注解
// WEAK: No explicit types
export const getUser = api(
{ method: "GET", path: "/users/:id", expose: true },
async ({ id }) => {
return await findUser(id);
}
);
// BETTER: Explicit request/response types
interface GetUserRequest { id: string; }
interface User { id: string; email: string; name: string; }
export const getUser = api(
{ method: "GET", path: "/users/:id", expose: true },
async ({ id }: GetUserRequest): Promise<User> => {
return await findUser(id);
}
);
7. 暴露内部端点
// CHECK: Should this cron endpoint be exposed?
export const cleanupJob = api(
{ expose: true }, // Probably should be false
async () => { /* ... */ }
);
8. 订阅处理程序不具备幂等性
// RISKY: Not idempotent (pubsub has at-least-once delivery)
const _ = new Subscription(orderCreated, "process-order", {
handler: async (event) => {
await chargeCustomer(event.orderId); // Could charge twice!
},
});
// SAFER: Check before processing
const _ = new Subscription(orderCreated, "process-order", {
handler: async (event) => {
const order = await getOrder(event.orderId);
if (order.status !== "pending") return; // Already processed
await chargeCustomer(event.orderId);
},
});
9. 在模块级别调用密钥
// WRONG: Secret accessed at startup
const stripeKey = secret("StripeKey");
const client = new Stripe(stripeKey()); // Called during import
// CORRECT: Access inside functions
const stripeKey = secret("StripeKey");
async function charge() {
const client = new Stripe(stripeKey()); // Called at runtime
}
审查清单
- 所有基础设施都定义在包级别
- 使用 ES6 导入,而非 require()
- 跨服务调用使用
~encore/clients - 使用 APIError 进行恰当的错误处理
- SQL 使用模板字符串
- 已定义请求和响应类型
- 内部端点设置了
expose: false - 订阅处理程序具备幂等性
- 在函数内部访问密钥,而非在导入时访问
- 迁移文件遵循命名约定(001_name.up.sql)
输出格式
审查时,请按以下格式报告问题:
[CRITICAL] [file:line] Description of issue
[WARNING] [file:line] Description of concern
[GOOD] Notable good practice observed
评论
支持 Markdown