2020-06-04 11:28:17 +00:00
|
|
|
const path = require('path')
|
|
|
|
const express = require('express')
|
|
|
|
const StatusIm = require('js-status-chat-name')
|
|
|
|
const links = require('../resources/links.json')
|
|
|
|
const assetLinks = require('../resources/assetlinks.json')
|
|
|
|
const appleSiteAssociation = require('../resources/apple-app-site-association.json')
|
|
|
|
const utils = require('../utils')
|
2018-10-05 05:31:10 +00:00
|
|
|
|
2020-06-04 11:28:17 +00:00
|
|
|
const router = express.Router()
|
2018-06-28 06:47:06 +00:00
|
|
|
|
2020-02-21 11:34:51 +00:00
|
|
|
/* Helper for generating pages */
|
2020-02-28 12:25:35 +00:00
|
|
|
const genPage = (req, res, options) => {
|
2020-06-04 11:28:17 +00:00
|
|
|
/* Protection against XSS attacks */
|
|
|
|
if (!utils.isValidUrl(options.mainTarget)) {
|
|
|
|
handleError(`Input contains HTML: ${options.mainTarget}`)(req, res)
|
|
|
|
return
|
|
|
|
}
|
2020-03-19 16:42:49 +00:00
|
|
|
let qrUrl = genUrl(req, options.path)
|
|
|
|
utils.makeQrCodeDataUri(qrUrl).then(
|
|
|
|
qrUri => res.render('index', { ...options, qrUri }),
|
|
|
|
error => res.render('index', options)
|
2020-02-20 15:40:13 +00:00
|
|
|
)
|
2020-02-21 10:31:12 +00:00
|
|
|
}
|
2018-06-28 06:47:06 +00:00
|
|
|
|
2020-02-21 10:31:12 +00:00
|
|
|
/* Helper for full URLs, can specify optional path */
|
2020-02-28 12:25:35 +00:00
|
|
|
const genUrl = (req, path) => (
|
|
|
|
/* Make button open user profile if on Android */
|
|
|
|
utils.isAndroid(req) ? `status-im:/${path}` :
|
|
|
|
`${req.protocol}://${req.hostname}${path}`
|
2020-02-21 10:31:12 +00:00
|
|
|
)
|
2018-06-28 07:27:15 +00:00
|
|
|
|
2020-02-25 11:27:58 +00:00
|
|
|
/* Helper for returning syntax errors */
|
|
|
|
const handleError = (msg) => (
|
2020-06-04 11:28:17 +00:00
|
|
|
(req, res) => {
|
2020-02-25 11:27:58 +00:00
|
|
|
res.status(400)
|
|
|
|
res.render('index', { error: new Error(msg) })
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2020-04-02 09:27:57 +00:00
|
|
|
/* Helper for redirecting to upper case URLs */
|
2020-06-04 11:28:17 +00:00
|
|
|
const handleRedirect = (req, res) => {
|
|
|
|
/* Protection against XSS attacks */
|
|
|
|
if (!utils.isValidUrl(req.originalUrl)) {
|
|
|
|
handleError(`Input contains HTML: ${req.originalUrl}`)(req, res)
|
|
|
|
return
|
|
|
|
}
|
2020-04-02 09:27:57 +00:00
|
|
|
res.render('index', {
|
|
|
|
title: 'Redirecting form upper case',
|
|
|
|
redirect: {
|
|
|
|
name: req.params[0].toLowerCase(),
|
|
|
|
path: req.originalUrl.toLowerCase(),
|
|
|
|
},
|
|
|
|
})
|
2020-06-04 11:28:17 +00:00
|
|
|
}
|
2020-04-02 09:27:57 +00:00
|
|
|
|
2020-02-21 11:11:54 +00:00
|
|
|
/* Open Website/Dapp in Status */
|
|
|
|
const handleSite = (req, res) => {
|
2020-02-20 15:40:13 +00:00
|
|
|
let { url } = req.params
|
|
|
|
url = url.replace(/https?:\/\//, '')
|
2020-02-28 12:25:35 +00:00
|
|
|
genPage(req, res, {
|
2020-02-20 15:40:13 +00:00
|
|
|
title: `Browse to ${url} in Status`,
|
|
|
|
info: `Browse to ${url} in Status`,
|
2020-03-19 16:42:49 +00:00
|
|
|
mainTarget: url,
|
2020-02-20 15:40:13 +00:00
|
|
|
headerName: `<a href="https://${url}">${url}</a>`,
|
2020-02-28 12:25:35 +00:00
|
|
|
path: `/b/${url}`
|
2020-02-21 10:31:12 +00:00
|
|
|
})
|
2020-02-21 11:11:54 +00:00
|
|
|
}
|
2020-02-21 10:31:12 +00:00
|
|
|
|
2020-02-21 11:36:48 +00:00
|
|
|
/* Open User Profile from Chat Key in Status */
|
|
|
|
const handleChatKey = (req, res) => {
|
2020-07-02 13:35:49 +00:00
|
|
|
let chatKey = req.params[0]
|
|
|
|
let uncompressedKey = chatKey
|
|
|
|
|
2020-02-25 11:27:58 +00:00
|
|
|
try {
|
2020-07-02 13:35:49 +00:00
|
|
|
if (!chatKey.startsWith('0x')) { /* decompress/deserialize key */
|
|
|
|
uncompressedKey = utils.decompressKey(chatKey)
|
|
|
|
} else { /* We accept upper case for hexadecimal public keys */
|
|
|
|
chatKey = chatKey.toLowerCase()
|
|
|
|
}
|
|
|
|
chatName = StatusIm.chatKeyToChatName(uncompressedKey)
|
2020-02-25 11:27:58 +00:00
|
|
|
} catch(error) {
|
2020-07-02 13:35:49 +00:00
|
|
|
console.error(`Failed to parse: "${uncompressedKey}", Error:`, error.message)
|
2020-02-25 11:27:58 +00:00
|
|
|
res.render('index', { title: 'Invalid chat key format!', error })
|
|
|
|
return
|
|
|
|
}
|
2020-02-28 12:25:35 +00:00
|
|
|
genPage(req, res, {
|
2020-02-21 11:36:48 +00:00
|
|
|
title: `Join ${chatName} in Status`,
|
|
|
|
info: `Chat and transact with <span>${chatKey}</span> in Status.`,
|
2020-03-19 16:42:49 +00:00
|
|
|
mainTarget: chatKey,
|
2020-02-21 11:36:48 +00:00
|
|
|
headerName: chatName,
|
2020-04-01 13:13:39 +00:00
|
|
|
path: `/u/${chatKey}`,
|
2020-02-21 11:36:48 +00:00
|
|
|
})
|
|
|
|
}
|
2020-02-21 10:31:12 +00:00
|
|
|
|
2020-02-21 11:11:54 +00:00
|
|
|
/* Open User Profile from ENS Name in Status */
|
|
|
|
const handleEnsName = (req, res) => {
|
2020-02-21 11:34:51 +00:00
|
|
|
let username
|
|
|
|
try {
|
|
|
|
username = utils.normalizeEns(req.params[0])
|
|
|
|
} catch(error) { /* ENS names have the widest regex: .+ */
|
2020-02-21 11:36:48 +00:00
|
|
|
console.error(`Failed to parse: "${req.params[0]}", Error:`, error.message)
|
2020-02-25 11:27:58 +00:00
|
|
|
res.render('index', { title: 'Invalid username format!', error })
|
2020-02-21 11:34:51 +00:00
|
|
|
return
|
|
|
|
}
|
2020-02-28 12:25:35 +00:00
|
|
|
genPage(req, res, {
|
2020-02-21 10:31:12 +00:00
|
|
|
title: `Join @${username} in Status`,
|
|
|
|
info: `Chat and transact with <span>@${username}</span> in Status.`,
|
2020-03-19 16:42:49 +00:00
|
|
|
mainTarget: username,
|
2020-02-21 11:11:54 +00:00
|
|
|
headerName: `@${utils.showSpecialChars(username)}`,
|
2020-03-06 17:01:54 +00:00
|
|
|
path: req.originalUrl,
|
2020-02-21 10:31:12 +00:00
|
|
|
})
|
2020-02-21 11:11:54 +00:00
|
|
|
}
|
2020-02-21 10:31:12 +00:00
|
|
|
|
2020-02-21 11:11:54 +00:00
|
|
|
/* Open Public Channel in Status */
|
|
|
|
const handlePublicChannel = (req, res) => {
|
2020-02-21 10:31:12 +00:00
|
|
|
const chatName = req.params[0]
|
2020-02-28 12:25:35 +00:00
|
|
|
genPage(req, res, {
|
2020-02-21 10:31:12 +00:00
|
|
|
title: `Join #${chatName} in Status`,
|
|
|
|
info: `Join public channel <span>#${chatName}</span> on Status.`,
|
2020-03-19 16:42:49 +00:00
|
|
|
mainTarget: chatName,
|
2020-02-21 10:31:12 +00:00
|
|
|
headerName: `#${chatName}`,
|
2020-02-28 12:25:35 +00:00
|
|
|
path: req.originalUrl,
|
2020-02-21 10:31:12 +00:00
|
|
|
})
|
2020-02-21 11:11:54 +00:00
|
|
|
}
|
2020-02-21 10:31:12 +00:00
|
|
|
|
2020-02-21 11:36:48 +00:00
|
|
|
router.get('/.well-known/assetlinks.json', (req, res) => {
|
|
|
|
res.json(assetLinks)
|
|
|
|
})
|
|
|
|
|
|
|
|
router.get('/.well-known/apple-app-site-association', (req, res) => {
|
|
|
|
res.json(appleSiteAssociation)
|
|
|
|
})
|
|
|
|
|
|
|
|
router.get('/health', (req, res) => res.send('OK'))
|
|
|
|
|
|
|
|
router.get('/b/:url(*)', handleSite)
|
|
|
|
router.get('/browse/:url(*)', handleSite) /* Legacy */
|
|
|
|
|
2020-07-02 13:35:49 +00:00
|
|
|
router.get(/^\/u\/(z[0-9a-zA-Z]{46,49})$/, handleChatKey)
|
|
|
|
router.get(/^\/u\/(z[0-9a-zA-Z]+)$/, handleError('Incorrect length of chat key'))
|
|
|
|
router.get(/^\/u\/(fe701[0-9a-fA-F]{66})$/, handleChatKey)
|
|
|
|
router.get(/^\/u\/(fe701[0-9a-fA-F]+)$/, handleError('Incorrect length of chat key'))
|
|
|
|
router.get(/^\/u\/(f[0-9a-fA-F]{66})$/, handleChatKey)
|
|
|
|
router.get(/^\/u\/(f[0-9a-fA-F]+)$/, handleError('Incorrect length of chat key'))
|
2020-04-01 13:13:39 +00:00
|
|
|
router.get(/^\/u\/(0[xX]04[0-9a-fA-F]{128})$/, handleChatKey)
|
2020-07-02 13:35:49 +00:00
|
|
|
router.get(/^\/u\/(0[xX]04[0-9a-fA-F]+)$/, handleError('Incorrect length of chat key'))
|
2020-02-21 11:36:48 +00:00
|
|
|
router.get(/^\/user\/(0[xX]04[0-9a-fA-F]{128})$/, handleChatKey) /* Legacy */
|
|
|
|
|
2020-06-04 11:28:17 +00:00
|
|
|
router.get(/^\/u\/([^><]*[A-Z]+[^><]*)$/, handleRedirect)
|
|
|
|
router.get(/^\/u\/([^<>]+)$/, handleEnsName)
|
|
|
|
router.get(/^\/user\/([^<>]+)$/, handleEnsName) /* Legacy */
|
2020-02-21 11:36:48 +00:00
|
|
|
|
2020-02-21 11:11:54 +00:00
|
|
|
router.get(/^\/([a-z0-9-]+)$/, handlePublicChannel)
|
2020-04-02 09:27:57 +00:00
|
|
|
router.get(/^\/([a-zA-Z0-9-]+)$/, handleRedirect)
|
2020-02-25 11:27:58 +00:00
|
|
|
router.get(/^\/chat\/public\/([a-z0-9-]+)$/, handlePublicChannel) /* Legacy */
|
2020-04-02 09:27:57 +00:00
|
|
|
router.get(/^\/chat\/public\/([a-zA-Z0-9-]+)$/, handleRedirect)
|
2020-03-09 15:02:21 +00:00
|
|
|
router.get(/^\/([a-zA-Z0-9-]+)$/, (req, res) => res.redirect(req.originalUrl.toLowerCase()))
|
2020-02-21 10:31:12 +00:00
|
|
|
|
2020-02-21 11:11:54 +00:00
|
|
|
/* Catchall for everything else */
|
|
|
|
router.get('*', (req, res, next) => {
|
2020-02-21 10:31:12 +00:00
|
|
|
if (req.query.redirect) {
|
|
|
|
return next()
|
|
|
|
}
|
|
|
|
res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate')
|
|
|
|
res.header('Expires', '-1')
|
|
|
|
res.header('Pragma', 'no-cache')
|
|
|
|
|
2020-02-21 11:36:48 +00:00
|
|
|
let redirect = links.getStatus
|
2020-02-28 12:25:35 +00:00
|
|
|
if (utils.isAndroid(req)) {
|
2020-02-21 11:36:48 +00:00
|
|
|
redirect = links.playStore
|
2020-02-28 12:25:35 +00:00
|
|
|
} else if (utils.isIOS(req)) {
|
2020-02-21 11:36:48 +00:00
|
|
|
redirect = links.appleStore
|
2020-02-21 10:31:12 +00:00
|
|
|
}
|
|
|
|
|
2020-02-21 11:36:48 +00:00
|
|
|
return res.redirect(redirect)
|
2020-02-20 15:40:13 +00:00
|
|
|
})
|
2018-06-28 06:47:06 +00:00
|
|
|
|
2020-02-20 15:40:13 +00:00
|
|
|
module.exports = router
|