Resolve project + safe absolute path for any project-scoped route.
(
c: RouteContext,
adapter: StudioApiAdapter,
pathPrefix: (projectId: string) => string,
opts?: { mustExist?: boolean },
)
| 108 | |
| 109 | /** Resolve project + safe absolute path for any project-scoped route. */ |
| 110 | async function resolveProjectPath( |
| 111 | c: RouteContext, |
| 112 | adapter: StudioApiAdapter, |
| 113 | pathPrefix: (projectId: string) => string, |
| 114 | opts?: { mustExist?: boolean }, |
| 115 | ) { |
| 116 | const id = c.req.param("id"); |
| 117 | const project = await adapter.resolveProject(id); |
| 118 | if (!project) { |
| 119 | return { error: c.json({ error: "not found" }, 404) } as const; |
| 120 | } |
| 121 | |
| 122 | const filePath = decodeURIComponent(c.req.path.replace(pathPrefix(project.id), "")); |
| 123 | if (filePath.includes("\0")) { |
| 124 | return { error: c.json({ error: "forbidden" }, 403) } as const; |
| 125 | } |
| 126 | |
| 127 | const absPath = resolveWithinProject(project.dir, filePath); |
| 128 | if (!absPath) { |
| 129 | return { error: c.json({ error: "forbidden" }, 403) } as const; |
| 130 | } |
| 131 | |
| 132 | if (opts?.mustExist && !existsSync(absPath)) { |
| 133 | return { error: c.json({ error: "not found" }, 404) } as const; |
| 134 | } |
| 135 | |
| 136 | return { project, filePath, absPath } as const; |
| 137 | } |
| 138 | |
| 139 | function resolveProjectFile( |
| 140 | c: RouteContext, |
no test coverage detected