Skip to content

Commit

Permalink
Merge pull request #911 from serlo/staging
Browse files Browse the repository at this point in the history
Deployment
  • Loading branch information
hugotiburtino authored Nov 4, 2024
2 parents b743da9 + 47e4e02 commit 7b15d02
Show file tree
Hide file tree
Showing 31 changed files with 243 additions and 112 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
6 changes: 6 additions & 0 deletions __tests__/__utils__/expect-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,9 @@ export function expectSentryEvent({
export function expectNoSentryError() {
expect(globalThis.sentryEvents).toHaveLength(0)
}

export function expectIsPlaceholderResponse(response: Response) {
expect(response.status).toBe(200)
expect(response.headers.get('content-type')).toBe('image/png')
expect(response.headers.get('content-length')).toBe('135')
}
78 changes: 78 additions & 0 deletions __tests__/asset-proxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { http } from 'msw'

import {
currentTestEnvironment,
expectIsPlaceholderResponse,
} from './__utils__'

beforeEach(() => {
globalThis.server.use(
http.get('https://whatever.org/image', () => {
return new Response('', {
headers: {
'content-type': 'image/png',
'Set-Cookie':
'sessionId=abc123; Expires=Wed, 09 Nov 2024 07:28:00 GMT; Path=/; Domain=whatever.org; Secure; HttpOnly; SameSite=None',
},
})
}),
http.get('https://whatever.org/notimage', () => {
return new Response('', {
headers: { 'content-type': 'application/json' },
})
}),
)
})

test('request to https://asset-proxy.serlo.org/image?url=* gets asset from url query parameter', async () => {
const env = currentTestEnvironment()
const response = await env.fetch({
subdomain: 'asset-proxy',
pathname: '/image?url=https://whatever.org/image',
})
expect(response.status).toBe(200)
expect(response.headers.get('content-type')).toBe('image/png')
expect(response.headers.get('Set-Cookie')).toBeNull()
expect(response.headers.get('cache-control')).toBe(
'public, max-age=31536000, immutable',
)
})

describe('returns placeholder', () => {
test('when url parameter is empty', async () => {
const response = await requestAsset('')

expectIsPlaceholderResponse(response)
})

test('when url is invalid', async () => {
const response = await requestAsset('42')

expectIsPlaceholderResponse(response)
})

test('when url query parameter is missing', async () => {
const response = await currentTestEnvironment().fetch({
subdomain: 'asset-proxy',
pathname: '/image',
})

expectIsPlaceholderResponse(response)
})

test('when response is not image', async () => {
const response = await requestAsset('https://whatever.org/notimage')

expectIsPlaceholderResponse(response)
})
})

async function requestAsset(
url: string,
env = currentTestEnvironment(),
): Promise<Response> {
return await env.fetch({
subdomain: 'asset-proxy',
pathname: '/image?url=' + encodeURIComponent(url),
})
}
7 changes: 1 addition & 6 deletions __tests__/embed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
localTestEnvironment,
expectSentryEvent,
expectNoSentryError,
expectIsPlaceholderResponse,
} from './__utils__'

describe('embed.serlo.org/thumbnail?url=...', () => {
Expand Down Expand Up @@ -651,12 +652,6 @@ describe('embed.serlo.org/thumbnail?url=...', () => {
})
})

function expectIsPlaceholderResponse(response: Response) {
expect(response.status).toBe(200)
expect(response.headers.get('content-type')).toBe('image/png')
expect(response.headers.get('content-length')).toBe('135')
}

async function requestThumbnail(
url: string,
env = currentTestEnvironment(),
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"dependencies": {
"fp-ts": "^2.16.9",
"io-ts": "^2.2.21",
"jose": "^5.9.4",
"jose": "^5.9.6",
"toucan-js": "^4.0.0"
},
"devDependencies": {
Expand All @@ -42,21 +42,21 @@
"@sentry/types": "^8.35.0",
"@testing-library/jest-dom": "^6.5.0",
"@types/iarna__toml": "^2.0.5",
"@types/jest": "^29.5.13",
"@typescript-eslint/eslint-plugin": "^8.9.0",
"@types/jest": "^29.5.14",
"@typescript-eslint/eslint-plugin": "^8.12.0",
"@typescript-eslint/parser": "^8.11.0",
"cross-env": "^7.0.3",
"depcheck": "^1.4.7",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-deprecation": "^3.0.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-react": "^7.37.1",
"eslint-plugin-react": "^7.37.2",
"jest": "^29.7.0",
"msw": "^2.4.3",
"npm-run-all": "^4.1.5",
"prettier": "^3.3.3",
"prettier-plugin-packagejson": "^2.5.2",
"prettier-plugin-packagejson": "^2.5.3",
"prettier-plugin-sh": "^0.14.0",
"ts-jest": "^29.2.5",
"ts-unused-exports": "^10.1.0",
Expand Down
33 changes: 33 additions & 0 deletions src/asset-proxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Url, getPlaceholder, isImageResponse } from './utils'

export async function assetProxy(request: Request): Promise<Response | null> {
const url = Url.fromRequest(request)

if (url.subdomain !== 'asset-proxy') return null
if (url.pathname !== '/image') return null

const urlParam = url.searchParams.get('url')

if (!urlParam) return getPlaceholder()

let assetUrl: Url

try {
assetUrl = new Url(urlParam)
} catch {
return getPlaceholder()
}

const originalResponse = await fetch(assetUrl, {
cf: { cacheTtl: 24 * 60 * 60 * 30 },
})

if (originalResponse.ok && isImageResponse(originalResponse)) {
const response = new Response(originalResponse.body, originalResponse)
response.headers.delete('set-cookie')
response.headers.set('cache-control', 'public, max-age=31536000, immutable')
return response
}

return getPlaceholder()
}
30 changes: 8 additions & 22 deletions src/embed.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import * as t from 'io-ts'

import { SentryFactory, SentryReporter, responseToContext, Url } from './utils'
import {
SentryFactory,
SentryReporter,
responseToContext,
Url,
getPlaceholder,
isImageResponse,
} from './utils'

export async function embed(
request: Request,
Expand Down Expand Up @@ -271,24 +278,3 @@ async function getWikimediaThumbnail(url: URL) {

return getPlaceholder()
}

function getPlaceholder() {
const placeholderBase64 =
'iVBORw0KGgoAAAANSUhEUgAAAwAAAAGwAQMAAAAkGpCRAAAAA1BMVEXv9/t0VvapAAAAP0lEQVR42u3BMQEAAADCIPuntsUuYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQOqOwAAHrgHqAAAAAAElFTkSuQmCC'
const placeholder = Uint8Array.from(atob(placeholderBase64), (c) =>
c.charCodeAt(0),
)
return new Response(placeholder, {
status: 200,
statusText: 'OK',
headers: {
'Content-Type': 'image/png',
'Content-Length': placeholder.length.toString(),
},
})
}

function isImageResponse(res: Response): boolean {
const contentType = res.headers.get('content-type') ?? ''
return res.status === 200 && contentType.startsWith('image/')
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { api } from './api'
import { assetProxy } from './asset-proxy'
import { semanticFileNames } from './assets'
import { auth } from './auth'
import { cloudflareWorkerDev } from './cloudflare-worker-dev'
Expand Down Expand Up @@ -38,6 +39,7 @@ export default {
(await semanticFileNames(request)) ||
(await api(request, env)) ||
(await frontendProxy(request, sentryFactory, env)) ||
(await assetProxy(request)) ||
(await fetch(request))
)
} catch (e) {
Expand Down
20 changes: 20 additions & 0 deletions src/utils/image.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export function getPlaceholder() {
const placeholderBase64 =
'iVBORw0KGgoAAAANSUhEUgAAAwAAAAGwAQMAAAAkGpCRAAAAA1BMVEXv9/t0VvapAAAAP0lEQVR42u3BMQEAAADCIPuntsUuYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQOqOwAAHrgHqAAAAAAElFTkSuQmCC'
const placeholder = Uint8Array.from(atob(placeholderBase64), (c) =>
c.charCodeAt(0),
)
return new Response(placeholder, {
status: 200,
statusText: 'OK',
headers: {
'Content-Type': 'image/png',
'Content-Length': placeholder.length.toString(),
},
})
}

export function isImageResponse(res: Response): boolean {
const contentType = res.headers.get('content-type') ?? ''
return res.status === 200 && contentType.startsWith('image/')
}
1 change: 1 addition & 0 deletions src/utils/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './cf-environment'
export * from './url'
export * from './ui'
export * from './cache'
export * from './image'
Loading

0 comments on commit 7b15d02

Please sign in to comment.