Skip to content

按 slug 解析项目 API

team slug + project slug 翻译为 project UUID。仅供前端路由 guard 使用: 当 URL /o/<team-slug>/p/<project-slug>/... 进入时,前端会先调本接口拿到 UUID,再 router.replace 把地址规范化为 UUID 形态,避免下游代码把 slug 直接喂给 Supabase 触发 PostgREST 400。

端点

GET /projects/by-slug?team=<teamSlug>&slug=<projectSlug>

认证

仅支持 JWT Bearer Token(与 teams 路由一致)。

Authorization: Bearer <jwt>
X-Server-Mode: cloud | selfhosted

不接受 API Key / agent token——这是 UI 专属接口,agent 调用方应直接持有 UUID。

请求参数

参数位置必填说明
teamquery团队 slug,允许小写字母/数字/中文片段,连字符分隔,长度 1~48
slugquery项目 slug,必须匹配 /^[a-z][a-z0-9-]{1,63}$/,与 DB projects_slug_format 约束一致

响应

200 成功

json
{
  "success": true,
  "data": {
    "id": "uuid",
    "slug": "my-project",
    "name": "我的项目",
    "team_id": "uuid",
    "access": "private"
  }
}

仅返回 5 字段白名单。不暴露 owner_user_id / config / description 等。

200 未找到 / 无权限

json
{ "success": true, "data": null }

为避免泄漏项目存在性,下列情况均返回 data: null(不返回 404):

  • 团队 slug 不存在
  • 当前用户不是该团队成员
  • 项目 slug 不存在或已软删除

调用方需自行处理 data === null 的情况(前端 guard 会 redirect 到 dashboard)。

400 参数非法

json
{ "error": "invalid_request" }

teamslug 缺失、为空、格式不符。

401 未认证

json
{ "error": "unauthorized" }

500 服务端错误

json
{ "error": "internal_error" }

日志带 prefix [projects/by-slug]不记录 slug / team 原文(脱敏)。

行为细节

  • team 鉴权复用 apps/backend/src/services/team-membership.ts 中的 checkTeamMember,与 teams/by-slug 行为一致
  • project 查询使用 service-role 客户端(绕过 RLS),但仅在通过 team membership 校验后才查询;查询条件 (team_id, slug) WHERE deleted_at IS NULL 对应唯一索引 idx_projects_slug_team
  • 错误分支保证幂等:网络层失败返 500,业务层无果返 data: null

源码

  • 路由:apps/backend/src/routes/projects-by-slug.ts
  • slug 校验:apps/backend/src/utils/slug-validation.ts
  • team membership:apps/backend/src/services/team-membership.ts

调用方

  • 前端路由 guard:apps/frontend/src/router/index.ts 全局 beforeEach 中识别非 UUID 形态的 :projectId 路由参数,调用本接口翻译
  • 前端 service:projectService.getProjectBySlug(teamSlug, projectSlug)

相关

AI Workflow Editor