Bluesky app fork with some witchin' additions 💫 witchsky.app
bluesky fork client
119
fork

Configure Feed

Select the types of activity you want to include in your feed.

offer a json response for grabbing short links (#4671)

authored by

Hailey and committed by
GitHub
d5ca9523 fff3ae8f

+56 -3
+17 -3
bskylink/src/routes/redirect.ts
··· 11 11 '/:linkId', 12 12 handler(async (req, res) => { 13 13 const linkId = req.params.linkId 14 + const contentType = req.accepts(['html', 'json']) 14 15 assert( 15 16 typeof linkId === 'string', 16 17 'express guarantees id parameter is a string', ··· 21 22 .where('id', '=', linkId) 22 23 .executeTakeFirst() 23 24 if (!found) { 24 - // potentially broken or mistyped link— send user to the app 25 - res.setHeader('Location', `https://${ctx.cfg.service.appHostname}`) 25 + // potentially broken or mistyped link 26 26 res.setHeader('Cache-Control', 'no-store') 27 + if (contentType === 'json') { 28 + return res 29 + .status(404) 30 + .json({ 31 + error: 'NotFound', 32 + message: 'Link not found', 33 + }) 34 + .end() 35 + } 36 + // send the user to the app 37 + res.setHeader('Location', `https://${ctx.cfg.service.appHostname}`) 27 38 return res.status(302).end() 28 39 } 29 40 // build url from original url in order to preserve query params ··· 32 43 `https://${ctx.cfg.service.appHostname}`, 33 44 ) 34 45 url.pathname = found.path 46 + res.setHeader('Cache-Control', `max-age=${(7 * DAY) / SECOND}`) 47 + if (contentType === 'json') { 48 + return res.json({url: url.href}).end() 49 + } 35 50 res.setHeader('Location', url.href) 36 - res.setHeader('Cache-Control', `max-age=${(7 * DAY) / SECOND}`) 37 51 return res.status(301).end() 38 52 }), 39 53 )
+39
bskylink/tests/index.ts
··· 56 56 ) 57 57 }) 58 58 59 + it('returns json object with url when requested', async () => { 60 + const link = await getLink('/start/did:example:carol/zzz/') 61 + const [status, json] = await getJsonRedirect(link) 62 + assert.strictEqual(status, 200) 63 + assert(json.url) 64 + const url = new URL(json.url) 65 + assert.strictEqual(url.pathname, '/start/did:example:carol/zzz') 66 + }) 67 + 68 + it('returns 404 for unknown link when requesting json', async () => { 69 + const [status, json] = await getJsonRedirect( 70 + 'https://test.bsky.link/unknown', 71 + ) 72 + assert(json.error) 73 + assert(json.message) 74 + assert.strictEqual(status, 404) 75 + assert.strictEqual(json.error, 'NotFound') 76 + assert.strictEqual(json.message, 'Link not found') 77 + }) 78 + 59 79 async function getRedirect(link: string): Promise<[number, string]> { 60 80 const url = new URL(link) 61 81 const base = new URL(baseUrl) ··· 68 88 'response was not a redirect', 69 89 ) 70 90 return [res.status, res.headers.get('location') ?? ''] 91 + } 92 + 93 + async function getJsonRedirect( 94 + link: string, 95 + ): Promise<[number, {url?: string; error?: string; message?: string}]> { 96 + const url = new URL(link) 97 + const base = new URL(baseUrl) 98 + url.protocol = base.protocol 99 + url.host = base.host 100 + const res = await fetch(url, { 101 + redirect: 'manual', 102 + headers: {accept: 'application/json,text/html'}, 103 + }) 104 + assert( 105 + res.headers.get('content-type')?.startsWith('application/json'), 106 + 'content type was not json', 107 + ) 108 + const json = await res.json() 109 + return [res.status, json] 71 110 } 72 111 73 112 async function getLink(path: string): Promise<string> {