normalize links
This commit is contained in:
@@ -17,8 +17,6 @@ async function main() {
|
|||||||
link.ctx.safelinkClient.runFetchEvents()
|
link.ctx.safelinkClient.runFetchEvents()
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('here')
|
|
||||||
|
|
||||||
await link.start()
|
await link.start()
|
||||||
httpLogger.info('link service is running')
|
httpLogger.info('link service is running')
|
||||||
process.on('SIGTERM', async () => {
|
process.on('SIGTERM', async () => {
|
||||||
|
|||||||
Vendored
+89
-18
@@ -10,6 +10,9 @@ import type Database from '../db/index.js'
|
|||||||
import {type RulePatternType, type SafelinkRule} from '../db/schema.js'
|
import {type RulePatternType, type SafelinkRule} from '../db/schema.js'
|
||||||
import {redirectLogger} from '../logger.js'
|
import {redirectLogger} from '../logger.js'
|
||||||
|
|
||||||
|
const SAFELINK_MIN_FETCH_INTERVAL = 1_000
|
||||||
|
const SAFELINK_MAX_FETCH_INTERVAL = 10_000
|
||||||
|
|
||||||
export class SafelinkClient {
|
export class SafelinkClient {
|
||||||
private domainCache: LRUCache<string, SafelinkRule | 'ok'>
|
private domainCache: LRUCache<string, SafelinkRule | 'ok'>
|
||||||
private urlCache: LRUCache<string, SafelinkRule | 'ok'>
|
private urlCache: LRUCache<string, SafelinkRule | 'ok'>
|
||||||
@@ -39,37 +42,47 @@ export class SafelinkClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async tryFindRule(link: string): Promise<SafelinkRule | 'ok'> {
|
public async tryFindRule(link: string): Promise<SafelinkRule | 'ok'> {
|
||||||
const u = new URL(link)
|
let url: string
|
||||||
u.search = ''
|
let domain: string
|
||||||
u.hash = ''
|
try {
|
||||||
|
url = SafelinkClient.normalizeUrl(link)
|
||||||
|
domain = SafelinkClient.normalizeDomain(link)
|
||||||
|
} catch (e) {
|
||||||
|
redirectLogger.error(
|
||||||
|
{error: e, inputUrl: link},
|
||||||
|
'failed to normalize looked up link',
|
||||||
|
)
|
||||||
|
|
||||||
const d = new URL(u.href)
|
return 'ok'
|
||||||
d.pathname = ''
|
}
|
||||||
|
|
||||||
const urlRule = this.urlCache.get(u.href)
|
redirectLogger.info(url)
|
||||||
|
redirectLogger.info(domain)
|
||||||
|
|
||||||
|
const urlRule = this.urlCache.get(url)
|
||||||
if (urlRule) {
|
if (urlRule) {
|
||||||
return urlRule
|
return urlRule
|
||||||
}
|
}
|
||||||
|
|
||||||
const domainRule = this.domainCache.get(d.href)
|
const domainRule = this.domainCache.get(domain)
|
||||||
if (domainRule) {
|
if (domainRule) {
|
||||||
return domainRule
|
return domainRule
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const maybeUrlRule = await this.getRule(this.db, u.href, 'url')
|
const maybeUrlRule = await this.getRule(this.db, url, 'url')
|
||||||
this.urlCache.set(u.href, maybeUrlRule)
|
this.urlCache.set(url, maybeUrlRule)
|
||||||
return maybeUrlRule
|
return maybeUrlRule
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.urlCache.set(u.href, 'ok')
|
this.urlCache.set(url, 'ok')
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const maybeDomainRule = await this.getRule(this.db, u.href, 'domain')
|
const maybeDomainRule = await this.getRule(this.db, domain, 'domain')
|
||||||
this.domainCache.set(d.href, maybeDomainRule)
|
this.domainCache.set(domain, maybeDomainRule)
|
||||||
return maybeDomainRule
|
return maybeDomainRule
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.domainCache.set(d.href, 'ok')
|
this.domainCache.set(domain, 'ok')
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'ok'
|
return 'ok'
|
||||||
@@ -89,6 +102,20 @@ export class SafelinkClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async addRule(db: Database, rule: SafelinkRule) {
|
private async addRule(db: Database, rule: SafelinkRule) {
|
||||||
|
try {
|
||||||
|
if (rule.pattern === 'url') {
|
||||||
|
rule.url = SafelinkClient.normalizeUrl(rule.url)
|
||||||
|
} else if (rule.pattern === 'domain') {
|
||||||
|
rule.url = SafelinkClient.normalizeDomain(rule.url)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
redirectLogger.error(
|
||||||
|
{error: e, inputUrl: rule.url},
|
||||||
|
'failed to normalize rule input URL',
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
db.db
|
db.db
|
||||||
.insertInto('safelink_rule')
|
.insertInto('safelink_rule')
|
||||||
.values(rule)
|
.values(rule)
|
||||||
@@ -108,6 +135,20 @@ export class SafelinkClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async removeRule(db: Database, rule: SafelinkRule) {
|
private async removeRule(db: Database, rule: SafelinkRule) {
|
||||||
|
try {
|
||||||
|
if (rule.pattern === 'url') {
|
||||||
|
rule.url = SafelinkClient.normalizeUrl(rule.url)
|
||||||
|
} else if (rule.pattern === 'domain') {
|
||||||
|
rule.url = SafelinkClient.normalizeDomain(rule.url)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
redirectLogger.error(
|
||||||
|
{error: e, inputUrl: rule.url},
|
||||||
|
'failed to normalize rule input URL',
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
await db.db
|
await db.db
|
||||||
.deleteFrom('safelink_rule')
|
.deleteFrom('safelink_rule')
|
||||||
.where('pattern', '=', 'domain')
|
.where('pattern', '=', 'domain')
|
||||||
@@ -133,7 +174,7 @@ export class SafelinkClient {
|
|||||||
agent = await this.ozoneAgent.getAgent()
|
agent = await this.ozoneAgent.getAgent()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
redirectLogger.error({error: err}, 'error getting Ozone agent')
|
redirectLogger.error({error: err}, 'error getting Ozone agent')
|
||||||
setTimeout(() => this.runFetchEvents(), 10_000)
|
setTimeout(() => this.runFetchEvents(), SAFELINK_MAX_FETCH_INTERVAL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,12 +190,16 @@ export class SafelinkClient {
|
|||||||
{error: err},
|
{error: err},
|
||||||
'error fetching safelink events from Ozone',
|
'error fetching safelink events from Ozone',
|
||||||
)
|
)
|
||||||
setTimeout(() => this.runFetchEvents(), 10_000)
|
setTimeout(() => this.runFetchEvents(), SAFELINK_MAX_FETCH_INTERVAL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.data.cursor === this.cursor) {
|
if (res.data.cursor === this.cursor || res.data.events.length === 0) {
|
||||||
setTimeout(() => this.runFetchEvents(), 10_000)
|
redirectLogger.info(
|
||||||
|
{cursor: res.data.cursor},
|
||||||
|
'received same cursor from Ozone',
|
||||||
|
)
|
||||||
|
setTimeout(() => this.runFetchEvents(), SAFELINK_MAX_FETCH_INTERVAL)
|
||||||
} else {
|
} else {
|
||||||
await this.db.transaction(async db => {
|
await this.db.transaction(async db => {
|
||||||
for (const rule of res.data.events) {
|
for (const rule of res.data.events) {
|
||||||
@@ -166,9 +211,13 @@ export class SafelinkClient {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (res.data.cursor) {
|
if (res.data.cursor) {
|
||||||
|
redirectLogger.info(
|
||||||
|
{cursor: res.data.cursor},
|
||||||
|
'received new cursor from Ozone',
|
||||||
|
)
|
||||||
await this.setCursor(res.data.cursor)
|
await this.setCursor(res.data.cursor)
|
||||||
}
|
}
|
||||||
setTimeout(() => this.runFetchEvents(), 1_000)
|
setTimeout(() => this.runFetchEvents(), SAFELINK_MIN_FETCH_INTERVAL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,10 +249,32 @@ export class SafelinkClient {
|
|||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
})
|
})
|
||||||
.execute()
|
.execute()
|
||||||
|
this.cursor = cursor
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
redirectLogger.error({error: err}, 'failed to update safelink cursor')
|
redirectLogger.error({error: err}, 'failed to update safelink cursor')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static normalizeUrl(input: string) {
|
||||||
|
if (!input.startsWith('https://')) {
|
||||||
|
input = `https://${input}`
|
||||||
|
}
|
||||||
|
const u = new URL(input)
|
||||||
|
u.hash = ''
|
||||||
|
let normalized = u.href.replace(/^[^:]+:\/\//, '').toLowerCase()
|
||||||
|
if (normalized.endsWith('/')) {
|
||||||
|
normalized = normalized.substring(0, normalized.length - 1)
|
||||||
|
}
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
private static normalizeDomain(input: string) {
|
||||||
|
if (!input.startsWith('https://')) {
|
||||||
|
input = `https://${input}`
|
||||||
|
}
|
||||||
|
const u = new URL(input)
|
||||||
|
return u.host.toLowerCase()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OzoneAgent {
|
export class OzoneAgent {
|
||||||
|
|||||||
Reference in New Issue
Block a user