Skip to content

Commit a501f59

Browse files
authored
feat: enable passing extra configurable headers to postgrest client. (#191)
1 parent 7cab3a7 commit a501f59

File tree

5 files changed

+101
-6
lines changed

5 files changed

+101
-6
lines changed

.env.sample

+4-1
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@ LOGFLARE_ENABLED=false
2323
LOGFLARE_API_KEY=api_key
2424
LOGFLARE_SOURCE_TOKEN=source_token
2525

26-
IMGPROXY_URL=http://localhost:50020
26+
IMGPROXY_URL=http://localhost:50020
27+
28+
# specify the extra headers will be persisted and passed into Postgrest client, for instance, "x-foo,x-bar"
29+
POSTGREST_FORWARD_HEADERS=

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ FROM node:18-alpine
1515
RUN npm install -g pm2
1616
WORKDIR /app
1717
COPY migrations migrations
18-
COPY ecosystem.config.js package.json .
18+
COPY ecosystem.config.js package.json ./
1919
COPY --from=0 /app/node_modules node_modules
2020
COPY --from=1 /app/dist dist
2121
EXPOSE 5000

src/plugins/postgrest.ts

+36-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,29 @@ declare module 'fastify' {
1212
}
1313
}
1414

15+
function generatePostgrestForwardHeaders(
16+
forwardHeaders: string | undefined,
17+
request: FastifyRequest,
18+
ignore: string[] = []
19+
): Record<string, string> {
20+
if (!forwardHeaders) {
21+
return {}
22+
}
23+
24+
return forwardHeaders
25+
.split(',')
26+
.map((headerName) => headerName.trim())
27+
.filter((headerName) => headerName in request.headers && !ignore.includes(headerName))
28+
.reduce((extraHeaders, headerName) => {
29+
const headerValue = request.headers[headerName]
30+
if (typeof headerValue !== 'string') {
31+
throw new Error(`header ${headerName} must be string`)
32+
}
33+
extraHeaders[headerName] = headerValue
34+
return extraHeaders
35+
}, {} as Record<string, string>)
36+
}
37+
1538
async function getPostgrestClient(request: FastifyRequest, jwt: string): Promise<PostgrestClient> {
1639
const {
1740
anonKey,
@@ -20,6 +43,7 @@ async function getPostgrestClient(request: FastifyRequest, jwt: string): Promise
2043
postgrestURLScheme,
2144
postgrestURLSuffix,
2245
xForwardedHostRegExp,
46+
postgrestForwardHeaders,
2347
} = getConfig()
2448

2549
let url = postgrestURL
@@ -35,14 +59,22 @@ async function getPostgrestClient(request: FastifyRequest, jwt: string): Promise
3559
url = `${postgrestURLScheme}://${xForwardedHost}${postgrestURLSuffix}`
3660
apiKey = await getAnonKey(request.tenantId)
3761
}
38-
const postgrest = new PostgrestClient(url, {
62+
63+
const pgClientHeaders = {
64+
apiKey,
65+
Authorization: `Bearer ${jwt}`,
66+
}
67+
return new PostgrestClient(url, {
3968
headers: {
40-
apiKey,
41-
Authorization: `Bearer ${jwt}`,
69+
...pgClientHeaders,
70+
...generatePostgrestForwardHeaders(
71+
postgrestForwardHeaders,
72+
request,
73+
Object.keys(pgClientHeaders)
74+
), // extra forwarded headers
4275
},
4376
schema: 'storage',
4477
})
45-
return postgrest
4678
}
4779

4880
export const postgrest = fastifyPlugin(async (fastify) => {
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
'use strict'
2+
import app from '../app'
3+
import dotenv from 'dotenv'
4+
import { PostgrestClient } from '@supabase/postgrest-js'
5+
jest.mock('@supabase/postgrest-js')
6+
7+
dotenv.config({ path: '.env.test' })
8+
9+
beforeEach(() => {
10+
jest.clearAllMocks()
11+
})
12+
13+
describe('Extra headers should be forwarded to postgrest client', () => {
14+
test('it should only preserve the headers that are defined in environment variable', async () => {
15+
process.env.POSTGREST_FORWARD_HEADERS = 'x-foo, x-bar'
16+
const extraHeaders = { 'x-foo': 1, 'x-bar': 2, 'x-none-exist': 3 }
17+
await app().inject({
18+
method: 'GET',
19+
url: `/bucket`,
20+
headers: {
21+
authorization: `Bearer ${process.env.AUTHENTICATED_KEY}`,
22+
...extraHeaders,
23+
},
24+
})
25+
26+
expect(PostgrestClient).toBeCalledWith(process.env.POSTGREST_URL, {
27+
headers: {
28+
Authorization: `Bearer ${process.env.AUTHENTICATED_KEY}`,
29+
apiKey: process.env.ANON_KEY,
30+
'x-foo': '1',
31+
'x-bar': '2',
32+
},
33+
schema: 'storage',
34+
})
35+
})
36+
37+
test('it should not preserve any extra headers when environment variable is not set', async () => {
38+
delete process.env.POSTGREST_FORWARD_HEADERS
39+
const extraHeaders = { 'x-foo': 1, 'x-bar': 2 }
40+
41+
await app().inject({
42+
method: 'GET',
43+
url: `/bucket`,
44+
headers: {
45+
authorization: `Bearer ${process.env.AUTHENTICATED_KEY}`,
46+
...extraHeaders,
47+
},
48+
})
49+
50+
expect(PostgrestClient).toBeCalledWith(process.env.POSTGREST_URL, {
51+
headers: {
52+
Authorization: `Bearer ${process.env.AUTHENTICATED_KEY}`,
53+
apiKey: process.env.ANON_KEY,
54+
},
55+
schema: 'storage',
56+
})
57+
})
58+
})

src/utils/config.ts

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type StorageConfigType = {
3434
max: number
3535
}
3636
}
37+
postgrestForwardHeaders?: string
3738
}
3839

3940
function getOptionalConfigFromEnv(key: string): string | undefined {
@@ -93,5 +94,6 @@ export function getConfig(): StorageConfigType {
9394
max: parseInt(getOptionalConfigFromEnv('IMG_LIMITS_MAX_SIZE') || '5000', 10),
9495
},
9596
},
97+
postgrestForwardHeaders: getOptionalConfigFromEnv('POSTGREST_FORWARD_HEADERS'),
9698
}
9799
}

0 commit comments

Comments
 (0)