2021-05-20 19:11:58 +00:00
|
|
|
export function ensureProtocol (value) {
|
2023-08-24 00:06:26 +00:00
|
|
|
if (!value) return value
|
2023-05-20 00:50:56 +00:00
|
|
|
value = value.trim()
|
2024-06-04 17:10:37 +00:00
|
|
|
let url
|
|
|
|
|
|
|
|
try {
|
|
|
|
url = new URL(value)
|
|
|
|
} catch {
|
|
|
|
try {
|
|
|
|
url = new URL('http://' + value)
|
|
|
|
} catch {
|
|
|
|
return value
|
|
|
|
}
|
2021-05-20 19:11:58 +00:00
|
|
|
}
|
2024-06-04 17:10:37 +00:00
|
|
|
|
|
|
|
// remove trailing slash if new URL() added it
|
|
|
|
if (url.href.endsWith('/') && !value.endsWith('/')) {
|
|
|
|
return url.href.slice(0, -1)
|
|
|
|
}
|
|
|
|
return url.href
|
2021-05-20 19:11:58 +00:00
|
|
|
}
|
2022-11-15 23:51:00 +00:00
|
|
|
|
2023-05-07 13:33:51 +00:00
|
|
|
export function isExternal (url) {
|
2024-04-08 22:54:39 +00:00
|
|
|
return !url.startsWith(process.env.NEXT_PUBLIC_URL + '/') && !url.startsWith('/')
|
2023-05-07 13:33:51 +00:00
|
|
|
}
|
|
|
|
|
2022-11-15 23:51:00 +00:00
|
|
|
export function removeTracking (value) {
|
2023-08-24 00:06:26 +00:00
|
|
|
if (!value) return value
|
2022-11-15 23:51:00 +00:00
|
|
|
const exprs = [
|
|
|
|
// twitter URLs
|
2023-08-31 01:13:43 +00:00
|
|
|
/^(?<url>https?:\/\/(?:twitter|x)\.com\/(?:#!\/)?(?<user>\w+)\/status(?:es)?\/(?<id>\d+))/
|
2022-11-15 23:51:00 +00:00
|
|
|
]
|
|
|
|
for (const expr of exprs) {
|
2023-01-11 20:58:52 +00:00
|
|
|
value = expr.exec(value)?.groups.url ?? value
|
2022-11-15 23:51:00 +00:00
|
|
|
}
|
|
|
|
return value
|
|
|
|
}
|
2023-01-11 18:17:25 +00:00
|
|
|
|
2024-02-17 21:53:36 +00:00
|
|
|
/**
|
|
|
|
* parse links like https://stacker.news/items/123456 as #123456
|
|
|
|
*/
|
2024-06-03 17:05:31 +00:00
|
|
|
|
|
|
|
export function isItemPath (pathname) {
|
|
|
|
if (!pathname) return false
|
|
|
|
|
|
|
|
const [page, id] = pathname.split('/').filter(part => !!part)
|
|
|
|
return page === 'items' && /^[0-9]+$/.test(id)
|
|
|
|
}
|
|
|
|
|
2024-02-17 21:53:36 +00:00
|
|
|
export function parseInternalLinks (href) {
|
|
|
|
const url = new URL(href)
|
2024-04-08 22:54:39 +00:00
|
|
|
const internalURL = process.env.NEXT_PUBLIC_URL
|
2024-02-17 21:53:36 +00:00
|
|
|
const { pathname, searchParams } = url
|
2024-06-03 17:05:31 +00:00
|
|
|
|
2024-02-17 21:53:36 +00:00
|
|
|
// ignore empty parts which exist due to pathname starting with '/'
|
2024-06-03 17:05:31 +00:00
|
|
|
if (isItemPath(pathname) && url.origin === internalURL) {
|
|
|
|
const parts = pathname.split('/').filter(part => !!part)
|
2024-02-17 21:53:36 +00:00
|
|
|
const itemId = parts[1]
|
|
|
|
// check for valid item page due to referral links like /items/123456/r/ekzyis
|
|
|
|
const itemPages = ['edit', 'ots', 'related']
|
|
|
|
const itemPage = itemPages.includes(parts[2]) ? parts[2] : null
|
|
|
|
if (itemPage) {
|
|
|
|
// parse https://stacker.news/items/1/related?commentId=2
|
|
|
|
// as #1/related
|
|
|
|
// and not #2
|
|
|
|
// since commentId will be ignored anyway
|
|
|
|
const linkText = `#${itemId}/${itemPage}`
|
2024-06-04 02:54:42 +00:00
|
|
|
return { itemId, linkText }
|
2024-02-17 21:53:36 +00:00
|
|
|
}
|
|
|
|
const commentId = searchParams.get('commentId')
|
|
|
|
const linkText = `#${commentId || itemId}`
|
2024-06-04 02:54:42 +00:00
|
|
|
return { itemId, commentId, linkText }
|
2024-02-17 21:53:36 +00:00
|
|
|
}
|
2024-06-04 02:54:42 +00:00
|
|
|
|
|
|
|
return {}
|
2024-02-17 21:53:36 +00:00
|
|
|
}
|
|
|
|
|
2024-05-28 13:18:32 +00:00
|
|
|
export function parseEmbedUrl (href) {
|
2024-06-04 17:10:37 +00:00
|
|
|
try {
|
|
|
|
const { hostname, pathname, searchParams } = new URL(href)
|
|
|
|
|
2024-06-05 14:08:27 +00:00
|
|
|
if (hostname.endsWith('youtube.com')) {
|
|
|
|
if (pathname.includes('/watch')) {
|
|
|
|
return {
|
|
|
|
provider: 'youtube',
|
|
|
|
id: searchParams.get('v'),
|
|
|
|
meta: {
|
|
|
|
href,
|
|
|
|
start: searchParams.get('t')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pathname.includes('/shorts')) {
|
|
|
|
const id = pathname.split('/').slice(-1).join()
|
|
|
|
return {
|
|
|
|
provider: 'youtube',
|
|
|
|
id
|
2024-06-04 17:10:37 +00:00
|
|
|
}
|
2024-05-28 13:18:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-04 17:10:37 +00:00
|
|
|
if (hostname.endsWith('youtu.be') && pathname.length > 1) {
|
|
|
|
return {
|
|
|
|
provider: 'youtube',
|
|
|
|
id: pathname.slice(1), // remove leading slash
|
|
|
|
meta: {
|
|
|
|
href,
|
|
|
|
start: searchParams.get('t')
|
|
|
|
}
|
2024-05-28 13:18:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-04 17:10:37 +00:00
|
|
|
if (hostname.endsWith('rumble.com') && pathname.includes('/embed')) {
|
|
|
|
return {
|
|
|
|
provider: 'rumble',
|
|
|
|
id: null, // not required
|
|
|
|
meta: {
|
|
|
|
href
|
|
|
|
}
|
2024-05-28 13:18:32 +00:00
|
|
|
}
|
|
|
|
}
|
2024-06-20 16:28:25 +00:00
|
|
|
|
|
|
|
if (hostname.endsWith('peertube.tv') || hostname.endsWith('bitcointv.com')) {
|
|
|
|
return {
|
|
|
|
provider: 'peertube',
|
|
|
|
id: null,
|
|
|
|
meta: {
|
|
|
|
href: href.replace('/w/', '/videos/embed/')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-06-04 17:10:37 +00:00
|
|
|
} catch {
|
|
|
|
// ignore
|
2024-05-28 13:18:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Important to return empty object as default
|
|
|
|
return {}
|
|
|
|
}
|
|
|
|
|
2024-02-02 21:06:33 +00:00
|
|
|
export function stripTrailingSlash (uri) {
|
2024-02-02 20:44:37 +00:00
|
|
|
return uri.endsWith('/') ? uri.slice(0, -1) : uri
|
|
|
|
}
|
|
|
|
|
2024-02-14 21:09:13 +00:00
|
|
|
export function parseNwcUrl (walletConnectUrl) {
|
|
|
|
if (!walletConnectUrl) return {}
|
|
|
|
|
|
|
|
walletConnectUrl = walletConnectUrl
|
|
|
|
.replace('nostrwalletconnect://', 'http://')
|
|
|
|
.replace('nostr+walletconnect://', 'http://') // makes it possible to parse with URL in the different environments (browser/node/...)
|
|
|
|
|
|
|
|
// XXX There is a bug in parsing since we use the URL constructor for parsing:
|
|
|
|
// A wallet pubkey matching /^[0-9a-fA-F]{64}$/ might not be a valid hostname.
|
|
|
|
// Example: 11111111111 (10 1's) is a valid hostname (gets parsed as IPv4) but 111111111111 (11 1's) is not.
|
|
|
|
// See https://stackoverflow.com/questions/56804936/how-does-only-numbers-in-url-resolve-to-a-domain
|
|
|
|
// However, this seems to only get triggered if a wallet pubkey only contains digits so this is pretty improbable.
|
|
|
|
const url = new URL(walletConnectUrl)
|
|
|
|
const params = {}
|
|
|
|
params.walletPubkey = url.host
|
|
|
|
const secret = url.searchParams.get('secret')
|
|
|
|
const relayUrl = url.searchParams.get('relay')
|
|
|
|
if (secret) {
|
|
|
|
params.secret = secret
|
|
|
|
}
|
|
|
|
if (relayUrl) {
|
|
|
|
params.relayUrl = relayUrl
|
|
|
|
}
|
|
|
|
return params
|
|
|
|
}
|
|
|
|
|
2023-01-05 19:24:09 +00:00
|
|
|
// eslint-disable-next-line
|
2023-01-07 00:53:09 +00:00
|
|
|
export const URL_REGEXP = /^((https?|ftp):\/\/)?(www.)?(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i
|
|
|
|
|
|
|
|
// eslint-disable-next-line
|
2023-12-07 22:21:13 +00:00
|
|
|
export const WS_REGEXP = /^(wss?:\/\/)([0-9]{1,3}(?:\.[0-9]{1,3}){3}|(?=[^\/]{1,254}(?![^\/]))(?:(?=[a-zA-Z0-9-]{1,63}\.)(?:xn--+)?[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*\.)+[a-zA-Z]{2,63})(:([0-9]{1,5}))?(\/[^\s`@#$^&=.?"{}\\]+\/?)*([^\s`@#$^&=?"{}\/\\]+)?(\?[^\s`#$^"{}\\]+)*$/
|
2023-07-13 00:10:01 +00:00
|
|
|
|
|
|
|
export const IMGPROXY_URL_REGEXP = new RegExp(`^${process.env.NEXT_PUBLIC_IMGPROXY_URL}.*$`)
|
2024-05-06 21:39:20 +00:00
|
|
|
export const MEDIA_DOMAIN_REGEXP = new RegExp(`^https?://${process.env.NEXT_PUBLIC_MEDIA_DOMAIN}/.*$`)
|
|
|
|
|
2023-07-13 00:10:01 +00:00
|
|
|
// this regex is not a bullet proof way of checking if a url points to an image. to be sure, fetch the url and check the mimetype
|
|
|
|
export const IMG_URL_REGEXP = /^(https?:\/\/.*\.(?:png|jpg|jpeg|gif))$/
|
2024-05-16 13:41:49 +00:00
|
|
|
|
|
|
|
export const TOR_REGEXP = /\.onion(:[0-9]+)?$/
|