2022-11-18 21:02:07 +00:00
|
|
|
import { db } from '@/db/postgre.js';
|
2022-10-29 01:13:22 +00:00
|
|
|
import { fetchMeta } from '@/misc/fetch-meta.js';
|
|
|
|
import { Instance } from '@/models/entities/instance.js';
|
|
|
|
import { DAY } from '@/const.js';
|
2022-12-02 14:26:14 +00:00
|
|
|
import { Meta } from '@/models/entities/meta.js';
|
2022-10-29 01:13:22 +00:00
|
|
|
|
|
|
|
// Threshold from last contact after which an instance will be considered
|
|
|
|
// "dead" and should no longer get activities delivered to it.
|
2022-11-09 17:47:28 +00:00
|
|
|
const deadThreshold = 7 * DAY;
|
2022-10-29 01:13:22 +00:00
|
|
|
|
2022-12-01 06:42:09 +00:00
|
|
|
/**
|
|
|
|
* Returns whether a given host matches a wildcard pattern.
|
2022-12-01 17:07:43 +00:00
|
|
|
* @param host punycoded instance host
|
|
|
|
* @param pattern wildcard pattern containing a punycoded instance host
|
2022-12-01 06:42:09 +00:00
|
|
|
* @returns whether the post matches the pattern
|
|
|
|
*/
|
|
|
|
function matchHost(host: Instance['host'], pattern: string): boolean {
|
|
|
|
// Escape all of the regex special characters. Pattern from:
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
|
|
|
|
const escape = (str: string): string => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
2022-12-01 17:07:43 +00:00
|
|
|
const re = new RegExp('^' + pattern.split('*').map(escape).join('.*') + '$');
|
2022-12-01 16:34:11 +00:00
|
|
|
|
2022-12-01 17:07:43 +00:00
|
|
|
return re.test(host);
|
2022-12-01 06:42:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether a specific host (punycoded) should be blocked.
|
|
|
|
*
|
|
|
|
* @param host punycoded instance host
|
2022-12-02 14:26:14 +00:00
|
|
|
* @param meta a Promise contatining the information from the meta table (oprional)
|
2022-12-01 06:42:09 +00:00
|
|
|
* @returns whether the given host should be blocked
|
|
|
|
*/
|
2022-12-02 14:26:14 +00:00
|
|
|
export async function shouldBlockInstance(host: string, meta: Promise<Meta> = fetchMeta()): Promise<boolean> {
|
|
|
|
const { blockedHosts } = await meta;
|
2022-12-01 06:42:09 +00:00
|
|
|
return blockedHosts.some(blockedHost => matchHost(host, blockedHost));
|
|
|
|
}
|
|
|
|
|
2022-10-29 01:13:22 +00:00
|
|
|
/**
|
|
|
|
* Returns the subset of hosts which should be skipped.
|
|
|
|
*
|
|
|
|
* @param hosts array of punycoded instance hosts
|
2022-12-01 06:42:09 +00:00
|
|
|
* @returns array of punycoded instance hosts that should be skipped (subset of hosts parameter)
|
2022-10-29 01:13:22 +00:00
|
|
|
*/
|
2022-12-01 06:42:09 +00:00
|
|
|
export async function skippedInstances(hosts: Array<Instance['host']>): Promise<Array<Instance['host']>> {
|
2022-12-02 14:10:20 +00:00
|
|
|
// Resolve the boolean promises before filtering
|
2022-12-02 14:26:14 +00:00
|
|
|
const meta = fetchMeta();
|
|
|
|
const shouldSkip = await Promise.all(hosts.map(host => shouldBlockInstance(host, meta)));
|
2022-12-02 14:10:20 +00:00
|
|
|
const skipped = hosts.filter((_, i) => shouldSkip[i]);
|
|
|
|
|
2022-10-29 01:13:22 +00:00
|
|
|
// if possible return early and skip accessing the database
|
2022-12-01 06:42:09 +00:00
|
|
|
if (skipped.length === hosts.length) return hosts;
|
2022-10-29 01:13:22 +00:00
|
|
|
|
|
|
|
const deadTime = new Date(Date.now() - deadThreshold);
|
|
|
|
|
|
|
|
return skipped.concat(
|
2022-11-18 21:02:07 +00:00
|
|
|
await db.query(
|
|
|
|
`SELECT host FROM instance WHERE ("isSuspended" OR "latestStatus" = 410 OR "lastCommunicatedAt" < $1::date) AND host = ANY(string_to_array($2, ','))`,
|
|
|
|
[
|
|
|
|
deadTime.toISOString(),
|
2022-10-29 01:13:22 +00:00
|
|
|
// don't check hosts again that we already know are suspended
|
|
|
|
// also avoids adding duplicates to the list
|
2022-12-01 16:34:11 +00:00
|
|
|
hosts.filter(host => !skipped.includes(host) && !host.includes(',')).join(','),
|
2022-11-18 21:02:07 +00:00
|
|
|
],
|
|
|
|
)
|
2022-12-01 06:42:09 +00:00
|
|
|
.then(res => res.map(row => row.host)),
|
2022-10-29 01:13:22 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether a specific host (punycoded) should be skipped.
|
|
|
|
* Convenience wrapper around skippedInstances which should only be used if there is a single host to check.
|
|
|
|
* If you have multiple hosts, consider using skippedInstances instead to do a bulk check.
|
|
|
|
*
|
|
|
|
* @param host punycoded instance host
|
|
|
|
* @returns whether the given host should be skipped
|
|
|
|
*/
|
2022-12-01 06:42:09 +00:00
|
|
|
export async function shouldSkipInstance(host: Instance['host']): Promise<boolean> {
|
2022-10-29 01:13:22 +00:00
|
|
|
const skipped = await skippedInstances([host]);
|
|
|
|
return skipped.length > 0;
|
|
|
|
}
|