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.

add to blink (#7788)

authored by

Hailey and committed by
GitHub
61a14043 3fc4c32a

+83 -34
bskylink/src/routes/create.ts bskylink/src/routes/createShortLink.ts
+5 -3
bskylink/src/routes/index.ts
··· 1 1 import {Express} from 'express' 2 2 3 3 import {AppContext} from '../context.js' 4 - import {default as create} from './create.js' 4 + import {default as createShortLink} from './createShortLink.js' 5 5 import {default as health} from './health.js' 6 6 import {default as redirect} from './redirect.js' 7 + import {default as shortLink} from './shortLink.js' 7 8 import {default as siteAssociation} from './siteAssociation.js' 8 9 9 10 export * from './util.js' ··· 11 12 export default function (ctx: AppContext, app: Express) { 12 13 app = health(ctx, app) // GET /_health 13 14 app = siteAssociation(ctx, app) // GET /.well-known/apple-app-site-association 14 - app = create(ctx, app) // POST /link 15 - app = redirect(ctx, app) // GET /:linkId (should go last due to permissive matching) 15 + app = redirect(ctx, app) // GET /redirect 16 + app = createShortLink(ctx, app) // POST /link 17 + app = shortLink(ctx, app) // GET /:linkId (should go last due to permissive matching) 16 18 return app 17 19 }
+24 -31
bskylink/src/routes/redirect.ts
··· 6 6 import {AppContext} from '../context.js' 7 7 import {handler} from './util.js' 8 8 9 + const INTERNAL_IP_REGEX = new RegExp( 10 + '(^127.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$)|(^10.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$)|(^172.1[6-9]{1}[0-9]{0,1}.[0-9]{1,3}.[0-9]{1,3}$)|(^172.2[0-9]{1}[0-9]{0,1}.[0-9]{1,3}.[0-9]{1,3}$)|(^172.3[0-1]{1}[0-9]{0,1}.[0-9]{1,3}.[0-9]{1,3}$)|(^192.168.[0-9]{1,3}.[0-9]{1,3}$)|^localhost', 11 + 'i', 12 + ) 13 + 9 14 export default function (ctx: AppContext, app: Express) { 10 15 return app.get( 11 - '/:linkId', 16 + '/redirect', 12 17 handler(async (req, res) => { 13 - const linkId = req.params.linkId 14 - const contentType = req.accepts(['html', 'json']) 18 + let link = req.query.u 15 19 assert( 16 - typeof linkId === 'string', 17 - 'express guarantees id parameter is a string', 20 + typeof link === 'string', 21 + 'express guarantees link query parameter is a string', 18 22 ) 19 - const found = await ctx.db.db 20 - .selectFrom('link') 21 - .selectAll() 22 - .where('id', '=', linkId) 23 - .executeTakeFirst() 24 - if (!found) { 25 - // potentially broken or mistyped link 23 + link = decodeURIComponent(link) 24 + 25 + let url: URL | undefined 26 + try { 27 + url = new URL(link) 28 + } catch {} 29 + 30 + if ( 31 + !url || 32 + (url.protocol !== 'http:' && url.protocol !== 'https:') || // is a http(s) url 33 + (ctx.cfg.service.hostnames.includes(url.hostname.toLowerCase()) && 34 + url.pathname === '/redirect') || // is a redirect loop 35 + INTERNAL_IP_REGEX.test(url.hostname) // isn't directing to an internal location 36 + ) { 26 37 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 38 res.setHeader('Location', `https://${ctx.cfg.service.appHostname}`) 38 39 return res.status(302).end() 39 40 } 40 - // build url from original url in order to preserve query params 41 - const url = new URL( 42 - req.originalUrl, 43 - `https://${ctx.cfg.service.appHostname}`, 44 - ) 45 - url.pathname = found.path 41 + 46 42 res.setHeader('Cache-Control', `max-age=${(7 * DAY) / SECOND}`) 47 - if (contentType === 'json') { 48 - return res.json({url: url.href}).end() 49 - } 50 43 res.setHeader('Location', url.href) 51 44 return res.status(301).end() 52 45 }),
+54
bskylink/src/routes/shortLink.ts
··· 1 + import assert from 'node:assert' 2 + 3 + import {DAY, SECOND} from '@atproto/common' 4 + import {Express} from 'express' 5 + 6 + import {AppContext} from '../context.js' 7 + import {handler} from './util.js' 8 + 9 + export default function (ctx: AppContext, app: Express) { 10 + return app.get( 11 + '/:linkId', 12 + handler(async (req, res) => { 13 + const linkId = req.params.linkId 14 + const contentType = req.accepts(['html', 'json']) 15 + assert( 16 + typeof linkId === 'string', 17 + 'express guarantees id parameter is a string', 18 + ) 19 + const found = await ctx.db.db 20 + .selectFrom('link') 21 + .selectAll() 22 + .where('id', '=', linkId) 23 + .executeTakeFirst() 24 + if (!found) { 25 + // potentially broken or mistyped link 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}`) 38 + return res.status(302).end() 39 + } 40 + // build url from original url in order to preserve query params 41 + const url = new URL( 42 + req.originalUrl, 43 + `https://${ctx.cfg.service.appHostname}`, 44 + ) 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 + } 50 + res.setHeader('Location', url.href) 51 + return res.status(301).end() 52 + }), 53 + ) 54 + }