Skip to content

PROD : OUT-3763 (1/2) | Fall back to comment-scoped path for stale attachment URLs#1253

Merged
arpandhakal merged 5 commits into
productionfrom
main
May 22, 2026
Merged

PROD : OUT-3763 (1/2) | Fall back to comment-scoped path for stale attachment URLs#1253
arpandhakal merged 5 commits into
productionfrom
main

Conversation

@arpandhakal
Copy link
Copy Markdown
Collaborator

No description provided.

SandipBajracharya and others added 4 commits May 21, 2026 20:14
…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
@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 22, 2026

OUT-3763

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tasks-app Ready Ready Preview, Comment May 22, 2026 10:44am

Request Review

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 22, 2026

Deployment failed with the following error:

Deploying Serverless Functions to multiple regions is restricted to the Pro and Enterprise plans.

Learn More: https://vercel.link/multiple-function-regions

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 22, 2026

Greptile Summary

This PR has two independent changes: it propagates isShared from parent to subtasks created via template, and it introduces a fallback mechanism in signedUrlReplacer.ts to re-sign ~26 comments whose stored attachment URLs point at a pre-move storage path.

  • tasksShared.service.ts: isShared is destructured from parentTask and forwarded to CreateTaskRequestSchema.parse, ensuring subtasks inherit the parent's sharing flag. Clean, minimal change.
  • signedUrlReplacer.ts: signMediaForComments now delegates to the new rewriteCommentMediaSrcs, which scans both <img> and attachment tags. Attachment tags whose paths already include the comment-scoped segment are skipped; stale ones are passed to signCommentMediaPath for fallback signing. However, the fallback is unreachable because Supabase createSignedUrl always returns a signed URL token regardless of object existence, so the comment-scoped path is never attempted.

Confidence Score: 3/5

The isShared propagation is safe to merge on its own, but the stale-URL fallback in signedUrlReplacer.ts will not produce the intended fix — stale attachment downloads will still fail after this lands.

The tasksShared.service.ts change is a clean one-field addition. The signedUrlReplacer.ts change contains a core logic defect: signCommentMediaPath always returns the primary Supabase-signed URL because createSignedUrl populates data.signedUrl for any path string without verifying object existence. The comment-scoped fallback path is never reached, so the 26 affected attachment URLs remain broken post-merge.

src/utils/signedUrlReplacer.ts — specifically signCommentMediaPath and attachmentTagRegex

Important Files Changed

Filename Overview
src/utils/signedUrlReplacer.ts Adds rewriteCommentMediaSrcs and signCommentMediaPath to fall back to comment-scoped paths for ~26 stale attachment URLs; the fallback is broken because Supabase createSignedUrl always returns a URL regardless of file existence, making the comment-scoped path unreachable.
src/app/api/tasks/tasksShared.service.ts Propagates isShared from parent task to subtasks created from templates — a straightforward one-field addition with no apparent issues.

Sequence Diagram

sequenceDiagram
    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[]
Loading

Reviews (1): Last reviewed commit: "Merge branch 'production' into main" | Re-trigger Greptile

Comment on lines +108 to +118
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}`)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

@arpandhakal arpandhakal merged commit 6cc6286 into production May 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants