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

supabase / storage / 18384716116

09 Oct 2025 05:59PM UTC coverage: 76.329% (-0.04%) from 76.366%
18384716116

Pull #776

github

web-flow
Merge a019e9b8a into 3562ba0f4
Pull Request #776: fix: add X-Robots-Tag header to prevent robot indexing and following

1746 of 2518 branches covered (69.34%)

Branch coverage included in aggregate %.

179 of 253 new or added lines in 13 files covered. (70.75%)

6 existing lines in 2 files now uncovered.

21348 of 27738 relevant lines covered (76.96%)

100.82 hits per line

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

96.0
/src/http/routes/object/getObject.ts
1
import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'
1✔
2
import { FromSchema } from 'json-schema-to-ts'
1✔
3
import { IncomingMessage, Server, ServerResponse } from 'http'
1✔
4
import { getConfig } from '../../../config'
1✔
5
import { AuthenticatedRangeRequest } from '../../types'
1✔
6
import { ROUTE_OPERATIONS } from '../operations'
1✔
7
import { ERRORS } from '@internal/errors'
1✔
8
import { Obj } from '@storage/schemas'
1✔
9

1✔
10
const { storageS3Bucket } = getConfig()
1✔
11

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

1✔
21
const getObjectQuerySchema = {
1✔
22
  type: 'object',
1✔
23
  properties: {
1✔
24
    download: { type: 'string', examples: ['filename.jpg', null] },
1✔
25
  },
1✔
26
} as const
1✔
27

1✔
28
interface getObjectRequestInterface extends AuthenticatedRangeRequest {
1✔
29
  Params: FromSchema<typeof getObjectParamsSchema>
1✔
30
  Querystring: FromSchema<typeof getObjectQuerySchema>
1✔
31
}
1✔
32

1✔
33
async function requestHandler(
14✔
34
  request: FastifyRequest<getObjectRequestInterface, Server, IncomingMessage>,
14✔
35
  response: FastifyReply<
14✔
36
    Server,
14✔
37
    IncomingMessage,
14✔
38
    ServerResponse,
14✔
39
    getObjectRequestInterface,
14✔
40
    unknown
14✔
41
  >
14✔
42
) {
14✔
43
  const { bucketName } = request.params
14✔
44
  const { download } = request.query
14✔
45
  const objectName = request.params['*']
14✔
46

14✔
47
  // send the object from s3
14✔
48
  const s3Key = request.storage.location.getKeyLocation({
14✔
49
    tenantId: request.tenantId,
14✔
50
    bucketId: bucketName,
14✔
51
    objectName,
14✔
52
  })
14✔
53
  const bucket = await request.storage.asSuperUser().findBucket(bucketName, 'id,public', {
14✔
54
    dontErrorOnEmpty: true,
14✔
55
  })
14✔
56

14✔
57
  // The request is not authenticated
14✔
58
  if (!request.isAuthenticated) {
14✔
59
    // The bucket must be public to access its content
1✔
60
    if (!bucket?.public) {
1✔
61
      throw ERRORS.NoSuchBucket(bucketName)
1✔
62
    }
1✔
63
  }
1✔
64

13✔
65
  // The request is authenticated
13✔
66
  if (!bucket) {
14✔
67
    throw ERRORS.NoSuchBucket(bucketName)
1✔
68
  }
1✔
69

12✔
70
  let obj: Obj | undefined
12✔
71

12✔
72
  if (bucket.public) {
14!
73
    // request is authenticated but we still use the superUser as we don't need to check RLS
×
NEW
74
    obj = await request.storage
×
NEW
75
      .asSuperUser()
×
NEW
76
      .from(bucketName)
×
NEW
77
      .findObject(objectName, 'id, version, metadata')
×
78
  } else {
14✔
79
    // request is authenticated use RLS
12✔
80
    obj = await request.storage.from(bucketName).findObject(objectName, 'id, version, metadata')
12✔
81
  }
9✔
82

9✔
83
  return request.storage.renderer('asset').render(request, response, {
9✔
84
    bucket: storageS3Bucket,
9✔
85
    key: s3Key,
9✔
86
    version: obj.version,
9✔
87
    download,
9✔
88
    xRobotsTag: obj.metadata?.['xRobotsTag'] as string | undefined,
14✔
89
    signal: request.signals.disconnect.signal,
14✔
90
  })
14✔
91
}
14✔
92

1✔
93
export default async function routes(fastify: FastifyInstance) {
1✔
94
  const summary = 'Retrieve an object'
128✔
95
  fastify.get<getObjectRequestInterface>(
128✔
96
    '/authenticated/:bucketName/*',
128✔
97
    {
128✔
98
      exposeHeadRoute: false,
128✔
99
      // @todo add success response schema here
128✔
100
      schema: {
128✔
101
        params: getObjectParamsSchema,
128✔
102
        querystring: getObjectQuerySchema,
128✔
103
        headers: { $ref: 'authSchema#' },
128✔
104
        summary,
128✔
105
        response: { '4xx': { $ref: 'errorSchema#', description: 'Error response' } },
128✔
106
        tags: ['object'],
128✔
107
      },
128✔
108
      config: {
128✔
109
        operation: { type: ROUTE_OPERATIONS.GET_AUTH_OBJECT },
128✔
110
      },
128✔
111
    },
128✔
112
    async (request, response) => {
128✔
113
      return requestHandler(request, response)
11✔
114
    }
11✔
115
  )
128✔
116

128✔
117
  fastify.get<getObjectRequestInterface>(
128✔
118
    '/:bucketName/*',
128✔
119
    {
128✔
120
      exposeHeadRoute: false,
128✔
121
      // @todo add success response schema here
128✔
122
      schema: {
128✔
123
        params: getObjectParamsSchema,
128✔
124
        summary: 'Get object',
128✔
125
        description: 'Serve objects',
128✔
126
        tags: ['object'],
128✔
127
        response: { '4xx': { $ref: 'errorSchema#' } },
128✔
128
      },
128✔
129
      config: {
128✔
130
        operation: { type: ROUTE_OPERATIONS.GET_AUTH_OBJECT },
128✔
131
        allowInvalidJwt: true,
128✔
132
      },
128✔
133
    },
128✔
134
    async (request, response) => {
128✔
135
      return requestHandler(request, response)
3✔
136
    }
3✔
137
  )
128✔
138
}
128✔
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