feat(blocking): определять и показывать хостер в матрице блокировок
Docker / build (push) Failing after 26s
Docker / build (push) Failing after 26s
Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -54,6 +54,7 @@ export type CensorcheckRunDto = {
|
||||
createdAt: string
|
||||
completedAt: string
|
||||
observedSourceIp: string | null
|
||||
detectedHoster: string | null
|
||||
vps: CensorcheckVpsInfo | null
|
||||
results?: CensorcheckResultDto[]
|
||||
}
|
||||
@@ -80,6 +81,7 @@ export type CensorcheckInsertRun = {
|
||||
censorcheckVersion: string | null
|
||||
summary: CensorcheckSummary
|
||||
observedSourceIp: string | null
|
||||
detectedHoster: string | null
|
||||
results: CensorcheckInsertResult[]
|
||||
}
|
||||
|
||||
@@ -175,6 +177,7 @@ function toRunDto(row: RunRow, includeResults = false): CensorcheckRunDto {
|
||||
createdAt: row.createdAt,
|
||||
completedAt: row.completedAt,
|
||||
observedSourceIp: row.observedSourceIp ?? null,
|
||||
detectedHoster: row.detectedHoster ?? null,
|
||||
vps: hydrateVps(row.matchedVpsId ?? null),
|
||||
}
|
||||
if (includeResults) {
|
||||
@@ -248,6 +251,7 @@ export const censorcheckRepository = {
|
||||
createdAt: now,
|
||||
completedAt: now,
|
||||
observedSourceIp: input.observedSourceIp,
|
||||
detectedHoster: input.detectedHoster,
|
||||
})
|
||||
.run()
|
||||
for (const result of input.results) {
|
||||
@@ -304,6 +308,7 @@ export const censorcheckRepository = {
|
||||
like(schema.censorcheckRuns.probePublicIp, pattern),
|
||||
like(schema.censorcheckRuns.claimedPublicIp, pattern),
|
||||
like(schema.censorcheckRuns.runId, pattern),
|
||||
like(schema.censorcheckRuns.detectedHoster, pattern),
|
||||
)!,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -297,7 +297,8 @@ const CORE_TABLE_MIGRATIONS: string[] = [
|
||||
summaryJson TEXT NOT NULL DEFAULT '{}',
|
||||
createdAt TEXT NOT NULL,
|
||||
completedAt TEXT NOT NULL,
|
||||
observedSourceIp TEXT
|
||||
observedSourceIp TEXT,
|
||||
detectedHoster TEXT
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS censorcheck_results (
|
||||
id TEXT PRIMARY KEY,
|
||||
@@ -366,6 +367,7 @@ const COLUMN_MIGRATIONS: string[] = [
|
||||
`ALTER TABLE sync_log ADD COLUMN summary TEXT`,
|
||||
`ALTER TABLE active_tariffs ADD COLUMN spaceId TEXT`,
|
||||
`ALTER TABLE tariff_sync_options ADD COLUMN spaceId TEXT`,
|
||||
`ALTER TABLE censorcheck_runs ADD COLUMN detectedHoster TEXT`,
|
||||
]
|
||||
|
||||
const SPACE_BACKFILL_TABLES = [
|
||||
|
||||
@@ -384,6 +384,7 @@ export const censorcheckRuns = sqliteTable(
|
||||
createdAt: text('createdAt').notNull(),
|
||||
completedAt: text('completedAt').notNull(),
|
||||
observedSourceIp: text('observedSourceIp'),
|
||||
detectedHoster: text('detectedHoster'),
|
||||
},
|
||||
(t) => ({
|
||||
runIdUniq: uniqueIndex('censorcheck_runs_runId').on(t.runId),
|
||||
|
||||
@@ -299,7 +299,8 @@ CREATE TABLE IF NOT EXISTS censorcheck_runs (
|
||||
summaryJson TEXT NOT NULL DEFAULT '{}',
|
||||
createdAt TEXT NOT NULL,
|
||||
completedAt TEXT NOT NULL,
|
||||
observedSourceIp TEXT
|
||||
observedSourceIp TEXT,
|
||||
detectedHoster TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS censorcheck_results (
|
||||
|
||||
@@ -77,6 +77,7 @@ export const censorcheckIngestBodySchema = z.object({
|
||||
runId: z.string().trim().min(8).max(80),
|
||||
probe: z.object({
|
||||
publicIp: z.string().trim().min(1).max(64),
|
||||
hoster: z.string().trim().max(160).optional(),
|
||||
}),
|
||||
censorcheck: z
|
||||
.object({
|
||||
@@ -112,3 +113,56 @@ export function emptyCensorcheckSummary(): CensorcheckSummary {
|
||||
error: 0,
|
||||
}
|
||||
}
|
||||
|
||||
const HOSTER_ALIASES: ReadonlyArray<{ test: RegExp; name: string }> = [
|
||||
{ test: /digitalocean|digital ocean|\bas14061\b/i, name: 'DigitalOcean' },
|
||||
{ test: /hetzner|\bas24940\b/i, name: 'Hetzner' },
|
||||
{ test: /\bovh|\bas16276\b/i, name: 'OVH' },
|
||||
{ test: /linode|akamai|\bas63949\b/i, name: 'Linode' },
|
||||
{ test: /\bvultr|\bas20473\b/i, name: 'Vultr' },
|
||||
{ test: /amazon|aws|\bec2\b|\bas16509\b|\bas14618\b/i, name: 'AWS' },
|
||||
{ test: /google cloud|\bgcp\b|\bas15169\b/i, name: 'Google Cloud' },
|
||||
{ test: /microsoft|azure|\bas8075\b/i, name: 'Azure' },
|
||||
{ test: /oracle cloud|\bas31898\b/i, name: 'Oracle Cloud' },
|
||||
{ test: /selectel|\bas49505\b/i, name: 'Selectel' },
|
||||
{ test: /timeweb|\bas197695\b/i, name: 'Timeweb' },
|
||||
{ test: /vdsina/i, name: 'VDSina' },
|
||||
{ test: /4vps/i, name: '4VPS' },
|
||||
{ test: /macloud/i, name: 'Macloud' },
|
||||
{ test: /veesp/i, name: 'Veesp' },
|
||||
{ test: /ruvds|ru-vds|ru vds/i, name: 'RUVDS' },
|
||||
{ test: /\baeza\b/i, name: 'Aeza' },
|
||||
{ test: /firstvds|firstdedic/i, name: 'FirstVDS' },
|
||||
{ test: /yandex cloud|\bas200350\b/i, name: 'Yandex Cloud' },
|
||||
{ test: /cloud\.ru|sbercloud|\bas208677\b/i, name: 'Cloud.ru' },
|
||||
{ test: /\bbeget\b/i, name: 'Beget' },
|
||||
{ test: /fornex/i, name: 'Fornex' },
|
||||
{ test: /adminvps/i, name: 'AdminVPS' },
|
||||
{ test: /\bihor\b/i, name: 'Ihor' },
|
||||
{ test: /netcup/i, name: 'Netcup' },
|
||||
{ test: /contabo/i, name: 'Contabo' },
|
||||
{ test: /leaseweb/i, name: 'Leaseweb' },
|
||||
]
|
||||
|
||||
function tidyOrgName(value: string): string {
|
||||
const stripped = value
|
||||
.replace(/^as\d+\s+/i, '')
|
||||
.replace(/\s*[,;]?\s*\b(llc|ltd|inc|gmbh|corp|limited|jsc|ooo|ооо|ао)\b\.?$/i, '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim()
|
||||
if (!stripped) return value.slice(0, 80)
|
||||
const words = stripped.split(' ')
|
||||
if (stripped.length > 42 && words.length > 3) return words.slice(0, 3).join(' ')
|
||||
return stripped.slice(0, 80)
|
||||
}
|
||||
|
||||
/** Нормализует ASN/org/PTR/cloud-id в короткое имя хостера. */
|
||||
export function canonicalizeHoster(raw: string | null | undefined): string | null {
|
||||
const value = raw?.replace(/\s+/g, ' ').trim()
|
||||
if (!value) return null
|
||||
const sliced = value.slice(0, 160)
|
||||
for (const alias of HOSTER_ALIASES) {
|
||||
if (alias.test.test(sliced)) return alias.name
|
||||
}
|
||||
return tidyOrgName(sliced)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user