about summary refs log tree commit diff
path: root/bskylink/src/routes/redirect.ts
diff options
context:
space:
mode:
Diffstat (limited to 'bskylink/src/routes/redirect.ts')
-rw-r--r--bskylink/src/routes/redirect.ts40
1 files changed, 40 insertions, 0 deletions
diff --git a/bskylink/src/routes/redirect.ts b/bskylink/src/routes/redirect.ts
new file mode 100644
index 000000000..7791ea815
--- /dev/null
+++ b/bskylink/src/routes/redirect.ts
@@ -0,0 +1,40 @@
+import assert from 'node:assert'
+
+import {DAY, SECOND} from '@atproto/common'
+import {Express} from 'express'
+
+import {AppContext} from '../context.js'
+import {handler} from './util.js'
+
+export default function (ctx: AppContext, app: Express) {
+  return app.get(
+    '/:linkId',
+    handler(async (req, res) => {
+      const linkId = req.params.linkId
+      assert(
+        typeof linkId === 'string',
+        'express guarantees id parameter is a string',
+      )
+      const found = await ctx.db.db
+        .selectFrom('link')
+        .selectAll()
+        .where('id', '=', linkId)
+        .executeTakeFirst()
+      if (!found) {
+        // potentially broken or mistyped link— send user to the app
+        res.setHeader('Location', `https://${ctx.cfg.service.appHostname}`)
+        res.setHeader('Cache-Control', 'no-store')
+        return res.status(302).end()
+      }
+      // build url from original url in order to preserve query params
+      const url = new URL(
+        req.originalUrl,
+        `https://${ctx.cfg.service.appHostname}`,
+      )
+      url.pathname = found.path
+      res.setHeader('Location', url.href)
+      res.setHeader('Cache-Control', `max-age=${(7 * DAY) / SECOND}`)
+      return res.status(301).end()
+    }),
+  )
+}