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

supabase / storage / 22858730975

09 Mar 2026 02:38PM UTC coverage: 76.711% (+0.5%) from 76.206%
22858730975

Pull #875

github

web-flow
Merge 6feb42ab3 into 53462ff99
Pull Request #875: feat: support unicode in object names

4212 of 5944 branches covered (70.86%)

Branch coverage included in aggregate %.

377 of 446 new or added lines in 22 files covered. (84.53%)

1 existing line in 1 file now uncovered.

27218 of 35028 relevant lines covered (77.7%)

200.69 hits per line

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

94.56
/src/http/routes/object/uploadSignedObject.ts
1
import fastifyMultipart from '@fastify/multipart'
2✔
2
import { SignedUploadToken, verifyJWT } from '@internal/auth'
2✔
3
import { getJwtSecret } from '@internal/database'
2✔
4
import { ERRORS } from '@internal/errors'
2✔
5
import { FastifyInstance } from 'fastify'
2✔
6
import { FromSchema } from 'json-schema-to-ts'
2✔
7
import { ROUTE_OPERATIONS } from '../operations'
2✔
8
import { doesSignedTokenMatchRequestPath } from '../signed-url'
2✔
9

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

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

2✔
32
const successResponseSchema = {
2✔
33
  type: 'object',
2✔
34
  properties: {
2✔
35
    Key: { type: 'string', examples: ['avatars/folder/cat.png'] },
2✔
36
  },
2✔
37
  required: ['Key'],
2✔
38
}
2✔
39

2✔
40
interface UploadSignedObjectRequestInterface {
2✔
41
  Params: FromSchema<typeof uploadSignedObjectParamsSchema>
2✔
42
  Querystring: FromSchema<typeof uploadSignedObjectQSSchema>
2✔
43
  Headers: {
2✔
44
    range?: string
2✔
45
  }
2✔
46
}
2✔
47

2✔
48
export default async function routes(fastify: FastifyInstance) {
2✔
49
  const summary = 'Uploads an object via a presigned URL'
300✔
50

300✔
51
  fastify.register(fastifyMultipart, {
300✔
52
    limits: {
300✔
53
      fields: 10,
300✔
54
      files: 1,
300✔
55
    },
300✔
56
    throwFileSizeLimit: false,
300✔
57
  })
300✔
58

300✔
59
  fastify.addContentTypeParser(
300✔
60
    ['application/json', 'text/plain'],
300✔
61
    function (request, payload, done) {
300✔
62
      done(null)
×
63
    }
×
64
  )
300✔
65

300✔
66
  fastify.put<UploadSignedObjectRequestInterface>(
300✔
67
    '/upload/sign/:bucketName/*',
300✔
68
    {
300✔
69
      // @todo add success response schema here
300✔
70
      schema: {
300✔
71
        params: uploadSignedObjectParamsSchema,
300✔
72
        querystring: uploadSignedObjectQSSchema,
300✔
73
        summary,
300✔
74
        response: {
300✔
75
          200: { description: 'Successful response', ...successResponseSchema },
300✔
76
          '4xx': { $ref: 'errorSchema#', description: 'Error response' },
300✔
77
        },
300✔
78
        tags: ['object'],
300✔
79
      },
300✔
80
      config: {
300✔
81
        operation: { type: ROUTE_OPERATIONS.UPLOAD_SIGN_OBJECT },
300✔
82
      },
300✔
83
    },
300✔
84
    async (request, response) => {
300✔
85
      // Validate sender
18✔
86
      const { token } = request.query
18✔
87
      const { bucketName } = request.params
18✔
88
      const objectName = request.params['*']
18✔
89

18✔
90
      let payload: SignedUploadToken
18✔
91
      const { secret: jwtSecret, jwks } = await getJwtSecret(request.tenantId)
18✔
92

18✔
93
      try {
18✔
94
        payload = (await verifyJWT(token, jwtSecret, jwks)) as SignedUploadToken
18✔
95
      } catch (e) {
18✔
96
        const err = e as Error
4✔
97
        throw ERRORS.InvalidJWT(err)
4✔
98
      }
4✔
99

14✔
100
      const { owner, upsert, url, exp } = payload
14✔
101

14✔
102
      if (!doesSignedTokenMatchRequestPath(request.raw.url, '/object/upload/sign', url)) {
18✔
103
        throw ERRORS.InvalidSignature()
2✔
104
      }
2✔
105

12✔
106
      if (exp * 1000 < Date.now()) {
18!
NEW
107
        throw ERRORS.ExpiredSignature()
×
NEW
108
      }
×
109

12✔
110
      const { objectMetadata, path } = await request.storage
12✔
111
        .asSuperUser()
12✔
112
        .from(bucketName)
12✔
113
        .uploadFromRequest(request, {
12✔
114
          owner,
12✔
115
          objectName,
12✔
116
          isUpsert: upsert,
12✔
117
          signal: request.signals.body.signal,
12✔
118
        })
12✔
119

10✔
120
      return response.status(objectMetadata?.httpStatusCode ?? 200).send({
18!
121
        Key: path,
18✔
122
      })
18✔
123
    }
18✔
124
  )
300✔
125
}
300✔
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