45f0f7eefe
* Direct port of embeds to new arch (cherry picked from commit cc3fa1f6cea396dd9222486c633a508bfee1ecd6) * Re-org * Split out ListEmbed and FeedEmbed * Split out ImageEmbed * DRY up a bit * Port over ExternalLinkEmbed * Port over Player and Gif embeds * Migrate ComposerReplyTo * Replace other usages of old post-embeds * Migrate view contexts * Copy pasta VideoEmbed * Copy pasta GifEmbed * Swap in new file location * Clean up * Fix up native * Add back in correct moderation on List and Feed embeds * Format * Prettier * delete old video utils * move bandwidth-estimate.ts * Remove log * Add LazyQuoteEmbed for composer use * Clean up unused things * Remove remaining items * Prettier * Fix imports * Handle nested quotes same as prod * Add back silenced error handling * Fix lint --------- Co-authored-by: Samuel Newman <mozzius@protonmail.com>
53 lines
1.8 KiB
TypeScript
53 lines
1.8 KiB
TypeScript
import assert from 'node:assert'
|
|
|
|
import {DAY, SECOND} from '@atproto/common'
|
|
import escapeHTML from 'escape-html'
|
|
import {type Express} from 'express'
|
|
|
|
import {type AppContext} from '../context.js'
|
|
import {handler} from './util.js'
|
|
|
|
const INTERNAL_IP_REGEX = new RegExp(
|
|
'(^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',
|
|
'i',
|
|
)
|
|
|
|
export default function (ctx: AppContext, app: Express) {
|
|
return app.get(
|
|
'/redirect',
|
|
handler(async (req, res) => {
|
|
let link = req.query.u
|
|
assert(
|
|
typeof link === 'string',
|
|
'express guarantees link query parameter is a string',
|
|
)
|
|
|
|
let url: URL | undefined
|
|
try {
|
|
url = new URL(link)
|
|
} catch {}
|
|
|
|
if (
|
|
!url ||
|
|
(url.protocol !== 'http:' && url.protocol !== 'https:') || // is a http(s) url
|
|
(ctx.cfg.service.hostnames.includes(url.hostname.toLowerCase()) &&
|
|
url.pathname === '/redirect') || // is a redirect loop
|
|
INTERNAL_IP_REGEX.test(url.hostname) // isn't directing to an internal location
|
|
) {
|
|
res.setHeader('Cache-Control', 'no-store')
|
|
res.setHeader('Location', `https://${ctx.cfg.service.appHostname}`)
|
|
return res.status(302).end()
|
|
}
|
|
|
|
res.setHeader('Cache-Control', `max-age=${(7 * DAY) / SECOND}`)
|
|
res.type('html')
|
|
res.status(200)
|
|
|
|
const escaped = escapeHTML(url.href)
|
|
return res.send(
|
|
`<html><head><meta http-equiv="refresh" content="0; URL='${escaped}'" /><style>:root { color-scheme: light dark; }</style></head></html>`,
|
|
)
|
|
}),
|
|
)
|
|
}
|