PROD : OUT-3763 (1/2) | Fall back to comment-scoped path for stale attachment URLs#1253
Conversation
…of caller identity When creating subtasks from a template, the parent task's isShared flag was not being passed through, so subtasks created by automations (which authenticate as internal users) would not inherit the share-with-client setting from the parent task.
…URLs A past race left ~26 comments with content pointing at the pre-move task-scoped path while the file lives at the comment-scoped path. On read, retry signing against the comment-scoped path when the stored path fails, so the broken comments render until the backfill rewrites their content. Attachment tags are only rewritten when their path lacks the comment-scoped segment — downloads use the path, not the token, so healthy attachment tags stay untouched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Move both regexes inside rewriteCommentMediaSrcs so each invocation owns its own RegExp instance. Module-level /g regexes share lastIndex, and signMediaForComments runs the rewrite concurrently via Promise.all — a sibling resuming after an await could leave lastIndex past the end of another comment's matches, silently dropping unsigned images. - replace → replaceAll so a file embedded more than once in the same comment has all occurrences rewritten, not just the first. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OUT-3763 (1/2) | Fall back to comment-scoped path for stale attachment URLs
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Deployment failed with the following error: Learn More: https://vercel.link/multiple-function-regions |
Greptile SummaryThis PR has two independent changes: it propagates
Confidence Score: 3/5The The src/utils/signedUrlReplacer.ts — specifically Important Files Changed
Sequence DiagramsequenceDiagram
participant C as Caller
participant S as signMediaForComments
participant R as rewriteCommentMediaSrcs
participant P as signCommentMediaPath
participant G as getSignedUrl (Supabase)
C->>S: comments[]
loop for each comment
S->>R: htmlString, comment.id
note over R: Scan img tags (all signed)
R->>P: filePath, commentId
P->>G: createSignedUrl(filePath)
G-->>P: signedUrl (always populated)
P-->>R: return primary (fallback never reached)
R->>R: push replacement
note over R: Scan attachment tags (skip if path has /comments/id/)
R->>P: stale filePath, commentId
P->>G: createSignedUrl(stale filePath)
G-->>P: signedUrl (always populated — file may not exist)
P-->>R: return primary (comment-scoped fallback unreachable)
R->>R: push stale-path replacement
R->>R: replaceAll srcs in htmlString
R-->>S: rewritten htmlString
end
S-->>C: updated comments[]
Reviews (1): Last reviewed commit: "Merge branch 'production' into main" | Re-trigger Greptile |
| async function signCommentMediaPath(filePath: string, commentId: string): Promise<string | undefined> { | ||
| const primary = await getSignedUrl(filePath) | ||
| if (primary) return primary | ||
|
|
||
| const segments = filePath.split('/') | ||
| const fileName = segments.pop() | ||
| const prefix = segments.join('/') | ||
| if (!fileName || !prefix) return undefined | ||
|
|
||
| return await getSignedUrl(`${prefix}/comments/${commentId}/${fileName}`) | ||
| } |
There was a problem hiding this comment.
Fallback is unreachable —
getSignedUrl always returns a URL
Supabase's createSignedUrl generates a signed token for any path string without checking whether the object exists in the bucket. data?.signedUrl will always be populated (i.e. primary is always truthy), so if (primary) return primary fires on every call and the comment-scoped fallback path is never reached. The ~26 stale attachments will still be signed with the wrong pre-move path, producing broken download URLs at the client.
| // Regexes are created per-call: /g state is mutable and signMediaForComments | ||
| // runs this concurrently via Promise.all — module-level instances would race. | ||
| const imgTagRegex = /<img\s+[^>]*src="([^"]+)"[^>]*>/g | ||
| const attachmentTagRegex = /<\s*[a-zA-Z]+\s+[^>]*data-type="attachment"[^>]*src="([^"]+)"[^>]*>/g |
There was a problem hiding this comment.
attachmentTagRegex only matches when src appears after data-type="attachment"
The pattern data-type="attachment"[^>]*src="([^"]+)" requires data-type to precede src in the attribute list. Any tag serialized with src first (e.g. <div src="…" data-type="attachment">) will be matched by the outer pattern but the capture group will miss the URL, leaving that attachment unprocessed. If the rich-text editor emits attributes in a different order for some or all of the 26 affected comments, those attachments are silently skipped.
No description provided.