• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / storage / 26213785588

21 May 2026 08:08AM UTC coverage: 40.286% (-34.7%) from 74.992%
26213785588

Pull #1118

github

web-flow
Merge fcbe53820 into 322ab2ebb
Pull Request #1118: fix: single callback for memory collector

2182 of 5969 branches covered (36.56%)

Branch coverage included in aggregate %.

15 of 15 new or added lines in 2 files covered. (100.0%)

3727 existing lines in 166 files now uncovered.

4335 of 10208 relevant lines covered (42.47%)

35.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

13.64
/src/http/routes/object/getSignedObject.ts
1
import { FastifyInstance } from 'fastify'
2
import { FromSchema } from 'json-schema-to-ts'
3
import { getConfig } from '../../../config'
4
import { SignedToken, verifyJWT } from '../../../internal/auth'
5
import { getJwtSecret } from '../../../internal/database'
6
import { ERRORS } from '../../../internal/errors'
7
import { ROUTE_OPERATIONS } from '../operations'
8

9
const { storageS3Bucket } = getConfig()
2✔
10

11
const getSignedObjectParamsSchema = {
2✔
12
  type: 'object',
13
  properties: {
14
    bucketName: { type: 'string', examples: ['avatars'] },
15
    '*': { type: 'string', examples: ['folder/cat.png'] },
16
  },
17
  required: ['bucketName', '*'],
18
} as const
19

20
const getSignedObjectQSSchema = {
2✔
21
  type: 'object',
22
  properties: {
23
    download: { type: 'string', examples: ['filename.jpg', null] },
24
    token: {
25
      type: 'string',
26
      examples: [
27
        'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJidWNrZXQyL3B1YmxpYy9zYWRjYXQtdXBsb2FkMjMucG5nIiwiaWF0IjoxNjE3NzI2MjczLCJleHAiOjE2MTc3MjcyNzN9.uBQcXzuvXxfw-9WgzWMBfE_nR3VOgpvfZe032sfLSSk',
28
      ],
29
    },
30
  },
31
  required: ['token'],
32
} as const
33

34
interface GetSignedObjectRequestInterface {
35
  Params: FromSchema<typeof getSignedObjectParamsSchema>
36
  Querystring: FromSchema<typeof getSignedObjectQSSchema>
37
  Headers: {
38
    range?: string
39
  }
40
}
41

42
export default async function routes(fastify: FastifyInstance) {
UNCOV
43
  const summary = 'Retrieve an object via a presigned URL'
×
UNCOV
44
  fastify.get<GetSignedObjectRequestInterface>(
×
45
    '/sign/:bucketName/*',
46
    {
47
      // @todo add success response schema here
48
      schema: {
49
        params: getSignedObjectParamsSchema,
50
        querystring: getSignedObjectQSSchema,
51
        summary,
52
        response: { '4xx': { $ref: 'errorSchema#', description: 'Error response' } },
53
        tags: ['object'],
54
      },
55
      config: {
56
        operation: { type: ROUTE_OPERATIONS.GET_SIGNED_OBJECT },
57
      },
58
    },
59
    async (request, response) => {
UNCOV
60
      const { token } = request.query
×
UNCOV
61
      const { download } = request.query
×
62

63
      let payload: SignedToken
UNCOV
64
      const { secret: jwtSecret, jwks } = await getJwtSecret(request.tenantId)
×
65

UNCOV
66
      try {
×
UNCOV
67
        payload = (await verifyJWT(token, jwtSecret, jwks)) as SignedToken
×
68
      } catch (e) {
UNCOV
69
        const err = e as Error
×
UNCOV
70
        throw ERRORS.InvalidJWT(err)
×
71
      }
72

UNCOV
73
      const { url, exp } = payload
×
UNCOV
74
      const path = `${request.params.bucketName}/${request.params['*']}`
×
75

UNCOV
76
      if (url !== path) {
×
UNCOV
77
        throw ERRORS.InvalidSignature()
×
78
      }
79

UNCOV
80
      const s3Key = `${request.tenantId}/${url}`
×
81

UNCOV
82
      const [bucketName, ...objParts] = url.split('/')
×
UNCOV
83
      const obj = await request.storage
×
84
        .asSuperUser()
85
        .from(bucketName)
86
        .findObject(objParts.join('/'), 'id,version,metadata')
87

UNCOV
88
      return request.storage.renderer('asset').render(request, response, {
×
89
        bucket: storageS3Bucket,
90
        key: s3Key,
91
        version: obj.version,
92
        download,
93
        expires: new Date(exp * 1000).toUTCString(),
94
        xRobotsTag: obj.metadata?.['xRobotsTag'] as string | undefined,
95
        signal: request.signals.disconnect.signal,
96
      })
97
    }
98
  )
99
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc