feat(billmanager): профили ihor, cloudrix, servhost, landvps, datacheap
Docker / build (push) Failing after 19s

Гео из datacentername, specs через vds.order/vds.edit; парсер ДЦ/городов РФ.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Denozordec
2026-07-20 00:30:15 +07:00
co-authored by Cursor
parent 11a0a85ff0
commit b7a4a10333
10 changed files with 658 additions and 1 deletions
+22 -1
View File
@@ -207,7 +207,28 @@ export function parseDatacenterName(dcName: unknown): { country: string; locatio
}
}
if (/ММТС|Adman|Москва/i.test(s)) {
// «Москва DC3», «ДЦ Москва», «Новосибирск Xeon» — город РФ (+ опц. префикс ДЦ / суффикс)
// Без \b: в JS word-boundary не срабатывает на кириллице без флага u.
const RU_CITY_CANON: Record<string, string> = {
москва: 'Москва',
'санкт-петербург': 'Санкт-Петербург',
спб: 'Санкт-Петербург',
новосибирск: 'Новосибирск',
екатеринбург: 'Екатеринбург',
казань: 'Казань',
самара: 'Самара',
краснодар: 'Краснодар',
}
const cityHaystack = s.replace(/^ДЦ\s+/i, '').trim()
const ruCityMatch = cityHaystack.match(
/^(Москва|Санкт-Петербург|СПб|Новосибирск|Екатеринбург|Казань|Самара|Краснодар)(?:\s|$)/i,
)
if (ruCityMatch) {
const key = ruCityMatch[1]!.toLowerCase()
return { country: 'Россия', location: RU_CITY_CANON[key] || ruCityMatch[1]! }
}
if (/ММТС|Adman/i.test(s)) {
return { country: 'Россия', location: s }
}
if (/Европа/i.test(s)) {
@@ -95,6 +95,138 @@ const FIRSTBYTE_VDS_FIXTURE = {
],
}
/** Minimal ihor-style bjson (`datacentername`: «Москва DC3»). */
const IHOR_VDS_FIXTURE = {
func: 'vds',
elem: [
{
id: '2927366',
ip: '185.105.116.43',
domain: 'vds2927366.my-ihor.ru',
expiredate: '2026-08-08',
real_expiredate: '2026-08-08',
datacenter: '1',
datacentername: 'Москва DC3',
ostempl: 'Ubuntu-26.04-amd64',
pricelist: 'KVM SSD Start (1 СPU/768 MB RAM/7 GB SSD)',
pricelist_id: '7818',
intname: 'SSD_Start',
cost: '100.00 RUB / Месяц',
item_cost: '100.0000',
currency_str: 'RUB',
createdate: '2026-03-08',
item_status_orig: '2',
item_status: 'Активен',
},
],
}
/** Minimal Cloudrix-style bjson — promo plan without CPU/RAM in pricelist name. */
const CLOUDRIX_VDS_FIXTURE = {
func: 'vds',
elem: [
{
id: '8226',
ip: '95.85.240.37',
domain: 'msk.shz.su',
expiredate: '2026-08-18',
real_expiredate: '2026-08-18',
datacenter: '1',
datacentername: 'Россия, Москва',
ipv6_subnet: '2a12:bec4:1280:7e::/64',
ostempl: 'Alma Linux 10',
pricelist: 'SPROMO-1',
pricelist_id: '1',
intname: 'spromo',
cost: '160.00 RUB / Месяц',
item_cost: '160.0000',
currency_str: 'RUB',
createdate: '2026-05-18',
item_status_orig: '2',
item_status: 'Активен',
},
],
}
/** Minimal serv.host bjson — named Xeon plan, city+CPU line in datacentername. */
const SERVHOST_VDS_FIXTURE = {
func: 'vds',
elem: [
{
id: '621601',
ip: '178.250.186.111',
domain: 'shatskiy.denis.serv.host',
expiredate: '2027-06-16',
real_expiredate: '2027-06-16',
datacenter: '5',
datacentername: 'Новосибирск Xeon',
ostempl: 'Ubuntu 24.04',
pricelist: 'Тариф «Xeon 2667V2-1»',
pricelist_id: '502',
intname: 'Тариф «Xeon 2667V2-1»',
cost: '127.50 RUB / Месяц',
item_cost: '1530.0000',
currency_str: 'RUB',
createdate: '2026-02-16',
item_status_orig: '2',
item_status: 'Активен',
},
],
}
/** Minimal LandVPS bjson — `ДЦ Москва`, tariff code MSK-0, ipv6_subnet. */
const LANDVPS_VDS_FIXTURE = {
func: 'vds',
elem: [
{
id: '196380',
ip: '77.105.133.124',
domain: '196380.landvps.online',
expiredate: '2026-08-21',
real_expiredate: '2026-08-21',
datacenter: '3',
datacentername: 'ДЦ Москва',
ipv6_subnet: '2a12:b3c0:1:b6::/64',
ostempl: 'Ubuntu 24.04',
pricelist: 'MSK-0',
pricelist_id: '19',
intname: 'RUS-0',
cost: '90.00 RUB / Месяц',
item_cost: '270.0000',
currency_str: 'RUB',
createdate: '2026-04-21',
item_status_orig: '2',
item_status: 'Активен',
},
],
}
/** Minimal Datacheap bjson — country-only DC, NVMe plan without CPU/RAM in name. */
const DATACHEAP_VDS_FIXTURE = {
func: 'vds',
elem: [
{
id: '189678',
ip: '84.252.100.102',
domain: 'dc.msk.rt.shx.su',
expiredate: '2026-08-21',
real_expiredate: '2026-08-21',
datacenter: '8',
datacentername: 'Россия',
ostempl: 'Ubuntu 24.04',
pricelist: 'VPS Start NVMe unlimited (RU)',
pricelist_id: '2489',
intname: 'NVMe unlim',
cost: '203.70 RUB / Месяц',
item_cost: '611.1000',
currency_str: 'RUB',
createdate: '2026-01-21',
item_status_orig: '2',
item_status: 'Активен',
},
],
}
describe('billmanager profiles', () => {
afterEach(() => {
vi.unstubAllGlobals()
@@ -122,6 +254,13 @@ describe('billmanager profiles', () => {
expect(p.map.enrichVds).toBeTypeOf('function')
})
it('resolve: ihor hostname → ihor profile', () => {
const p = resolveBillmanagerProfile('https://bill.my-ihor.ru/')
expect(p.id).toBe('ihor')
expect(p.funcs.listVds).toBe('vds')
expect(p.map.enrichVds).toBeTypeOf('function')
})
it('resolve: unknown hoster → default', () => {
const p = resolveBillmanagerProfile('https://bill.hoster.ru/')
expect(p.id).toBe('default')
@@ -143,6 +282,218 @@ describe('billmanager profiles', () => {
})
})
it('parseDatacenterName: ihor «Москва DC3»', () => {
expect(parseDatacenterName('Москва DC3')).toEqual({
country: 'Россия',
location: 'Москва',
})
})
it('ihor fixture → country/city from datacentername', () => {
const profile = resolveBillmanagerProfile('https://my-ihor.ru/')
const elems = extractList(IHOR_VDS_FIXTURE, profile.extract.listVdsKey)
expect(elems).toHaveLength(1)
const vps = mapVdsWithProfile(
profile,
elemToObject(elems[0]!),
'prov-ihor',
'acc-ihor',
)
expect(vps.externalId).toBe('2927366')
expect(vps.ip).toBe('185.105.116.43')
expect(vps.country).toBe('Россия')
expect(vps.city).toBe('Москва')
expect(vps.datacenter).toBe('Москва DC3')
expect(vps.os).toBe('Ubuntu-26.04-amd64')
expect(vps.paidUntil).toBe('2026-08-08')
expect(vps.status).toBe('active')
})
it('resolve: cloudrix hostname → cloudrix profile', () => {
const p = resolveBillmanagerProfile('https://bill.cloudrix.ru/')
expect(p.id).toBe('cloudrix')
expect(p.funcs.listVds).toBe('vds')
expect(p.map.enrichVds).toBeTypeOf('function')
expect(p.options?.fetchVdsEditForSpecs).toBe(true)
})
it('Cloudrix fixture → geo, ipv6; specs from vds.order by pricelist_id', () => {
const profile = resolveBillmanagerProfile('https://cloudrix.ru/')
const elems = extractList(CLOUDRIX_VDS_FIXTURE, profile.extract.listVdsKey)
expect(elems).toHaveLength(1)
const item = elemToObject(elems[0]!)
let vps = mapVdsWithProfile(profile, item, 'prov-cr', 'acc-cr')
expect(vps.externalId).toBe('8226')
expect(vps.country).toBe('Россия')
expect(vps.city).toBe('Москва')
expect(vps.datacenter).toBe('Россия, Москва')
expect(vps.ipv6).toBe('2a12:bec4:1280:7e::/64')
expect(vps.os).toBe('Alma Linux 10')
expect(vps.vcpu).toBe(0)
expect(vps.ramGb).toBe(0)
expect(vps.diskGb).toBe(0)
vps = enrichMappedVpsFromTariffs(item, vps, [
{
externalId: '1',
name: 'SPROMO-1',
desc: '1 CPU / 1 GB RAM / 15 GB NVMe',
vcpu: 1,
ramGb: 1,
diskGb: 15,
diskType: 'NVMe',
virtualization: 'KVM',
},
])
expect(vps.vcpu).toBe(1)
expect(vps.ramGb).toBe(1)
expect(vps.diskGb).toBe(15)
expect(vps.diskType).toBe('NVMe')
expect(vps.virtualization).toBe('KVM')
})
it('resolve: serv.host → servhost profile', () => {
const p = resolveBillmanagerProfile('https://bill.serv.host/')
expect(p.id).toBe('servhost')
expect(p.funcs.listVds).toBe('vds')
expect(p.options?.fetchVdsEditForSpecs).toBe(true)
})
it('parseDatacenterName: serv.host «Новосибирск Xeon»', () => {
expect(parseDatacenterName('Новосибирск Xeon')).toEqual({
country: 'Россия',
location: 'Новосибирск',
})
})
it('serv.host fixture → geo; specs from vds.order by pricelist_id', () => {
const profile = resolveBillmanagerProfile('https://my.serv.host/')
const elems = extractList(SERVHOST_VDS_FIXTURE, profile.extract.listVdsKey)
expect(elems).toHaveLength(1)
const item = elemToObject(elems[0]!)
let vps = mapVdsWithProfile(profile, item, 'prov-sh', 'acc-sh')
expect(vps.externalId).toBe('621601')
expect(vps.country).toBe('Россия')
expect(vps.city).toBe('Новосибирск')
expect(vps.datacenter).toBe('Новосибирск Xeon')
expect(vps.os).toBe('Ubuntu 24.04')
expect(vps.vcpu).toBe(0)
expect(vps.diskGb).toBe(0)
vps = enrichMappedVpsFromTariffs(item, vps, [
{
externalId: '502',
name: 'Тариф «Xeon 2667V2-1»',
desc: '2 CPU / 2 GB RAM / 40 GB NVMe',
vcpu: 2,
ramGb: 2,
diskGb: 40,
diskType: 'NVMe',
virtualization: 'KVM',
},
])
expect(vps.vcpu).toBe(2)
expect(vps.ramGb).toBe(2)
expect(vps.diskGb).toBe(40)
expect(vps.virtualization).toBe('KVM')
})
it('resolve: landvps.online → landvps profile', () => {
const p = resolveBillmanagerProfile('https://bill.landvps.online/')
expect(p.id).toBe('landvps')
expect(p.funcs.listVds).toBe('vds')
expect(p.options?.fetchVdsEditForSpecs).toBe(true)
})
it('parseDatacenterName: LandVPS «ДЦ Москва»', () => {
expect(parseDatacenterName('ДЦ Москва')).toEqual({
country: 'Россия',
location: 'Москва',
})
})
it('LandVPS fixture → geo, ipv6; specs from vds.order by pricelist_id', () => {
const profile = resolveBillmanagerProfile('https://my.landvps.online/')
const elems = extractList(LANDVPS_VDS_FIXTURE, profile.extract.listVdsKey)
expect(elems).toHaveLength(1)
const item = elemToObject(elems[0]!)
let vps = mapVdsWithProfile(profile, item, 'prov-lv', 'acc-lv')
expect(vps.externalId).toBe('196380')
expect(vps.country).toBe('Россия')
expect(vps.city).toBe('Москва')
expect(vps.datacenter).toBe('ДЦ Москва')
expect(vps.ipv6).toBe('2a12:b3c0:1:b6::/64')
expect(vps.os).toBe('Ubuntu 24.04')
expect(vps.paidUntil).toBe('2026-08-21')
expect(vps.vcpu).toBe(0)
expect(vps.diskGb).toBe(0)
vps = enrichMappedVpsFromTariffs(item, vps, [
{
externalId: '19',
name: 'MSK-0',
desc: '1 CPU / 1 GB RAM / 20 GB NVMe',
vcpu: 1,
ramGb: 1,
diskGb: 20,
diskType: 'NVMe',
virtualization: 'KVM',
},
])
expect(vps.vcpu).toBe(1)
expect(vps.ramGb).toBe(1)
expect(vps.diskGb).toBe(20)
expect(vps.virtualization).toBe('KVM')
})
it('resolve: datacheap.ru → datacheap profile', () => {
const p = resolveBillmanagerProfile('https://bill.datacheap.ru/')
expect(p.id).toBe('datacheap')
expect(p.funcs.listVds).toBe('vds')
expect(p.options?.fetchVdsEditForSpecs).toBe(true)
})
it('parseDatacenterName: Datacheap «Россия»', () => {
expect(parseDatacenterName('Россия')).toEqual({
country: 'Россия',
location: '',
})
})
it('Datacheap fixture → country; specs from vds.order by pricelist_id', () => {
const profile = resolveBillmanagerProfile('https://my.datacheap.ru/')
const elems = extractList(DATACHEAP_VDS_FIXTURE, profile.extract.listVdsKey)
expect(elems).toHaveLength(1)
const item = elemToObject(elems[0]!)
let vps = mapVdsWithProfile(profile, item, 'prov-dc', 'acc-dc')
expect(vps.externalId).toBe('189678')
expect(vps.country).toBe('Россия')
expect(vps.city).toBe('')
expect(vps.datacenter).toBe('Россия')
expect(vps.os).toBe('Ubuntu 24.04')
expect(vps.paidUntil).toBe('2026-08-21')
expect(vps.vcpu).toBe(0)
expect(vps.diskGb).toBe(0)
vps = enrichMappedVpsFromTariffs(item, vps, [
{
externalId: '2489',
name: 'VPS Start NVMe unlimited (RU)',
desc: '1 CPU / 1 GB RAM / 15 GB NVMe',
vcpu: 1,
ramGb: 1,
diskGb: 15,
diskType: 'NVMe',
virtualization: 'KVM',
},
])
expect(vps.vcpu).toBe(1)
expect(vps.ramGb).toBe(1)
expect(vps.diskGb).toBe(15)
expect(vps.diskType).toBe('NVMe')
expect(vps.virtualization).toBe('KVM')
})
it('Waicore fixture elem → MappedVps fields + geo from datacentername', () => {
const profile = resolveBillmanagerProfile('https://my.waicore.com/')
const elems = extractList(WAICORE_VDS_FIXTURE, profile.extract.listVdsKey)
@@ -51,3 +51,8 @@ Sync и operations **не** содержат `if (hoster)` — только пр
|----|-------|-------------------|
| `waicore` | `waicore.com` / keyword | `funcs.listVds = vds.vps`; geo из `datacentername` (`[DE] Город \| …` / `Страна, Город`) |
| `firstbyte` | `firstbyte.ru`, `firstbyte.club`, `1byte.ru` | `enrichVds`: страна/город, daily из `billdaily`/`Ежедневное списание`, `dailyRate=cost/30`; `enrichVdsBatch`: общий баланс ÷ сумма daily → один `paidUntil`; specs из `vds.order` / `vds.edit` |
| `ihor` | `my-ihor.ru`, `ihor.ru` / keyword | `enrichVds`: geo из `datacentername` (`Москва DC3` → Россия / Москва) |
| `cloudrix` | `cloudrix.ru` / keyword | `enrichVds`: geo (`Россия, Москва`) + `ipv6_subnet`; specs через `vds.order` / `vds.edit` (`fetchVdsEditForSpecs`) |
| `servhost` | `serv.host` | `enrichVds`: geo (`Новосибирск Xeon` → Россия / Новосибирск); specs через `vds.order` / `vds.edit` |
| `landvps` | `landvps.online` / keyword | `enrichVds`: geo (`ДЦ Москва` → Россия / Москва) + `ipv6_subnet`; specs через `vds.order` / `vds.edit` |
| `datacheap` | `datacheap.ru` / keyword | `enrichVds`: geo (`Россия`); specs через `vds.order` / `vds.edit` |
@@ -0,0 +1,57 @@
import { parseDatacenterName } from '../parsers.js'
import type { MappedVps } from '../mappers.js'
import type {
BillmanagerMapOverrides,
BillmanagerProfileOverrides,
} from './types.js'
/**
* Geo + IPv6 from Cloudrix list row.
* - `datacentername`: `Россия, Москва` → Россия / Москва
* - `ipv6_subnet`: `2a12:…::/64`
*
* Hardware (vcpu/ram/disk): list has only `pricelist` like SPROMO-1 —
* filled in sync via vds.order match and `vds.edit` (fetchVdsEditForSpecs).
*/
export function enrichCloudrixVds(
item: Record<string, string>,
mapped: MappedVps,
): MappedVps {
const dcRaw = (item.datacentername || item.datacenter || mapped.datacenter || '').trim()
const { country, location } = dcRaw
? parseDatacenterName(dcRaw)
: { country: '', location: '' }
const ipv6 = (item.ipv6_subnet || item.ipv6 || mapped.ipv6 || '').trim()
return {
...mapped,
country: country || mapped.country,
city: location || mapped.city,
datacenter: dcRaw || mapped.datacenter,
ipv6: ipv6 || mapped.ipv6,
// Promo / named plans rarely encode disk type — prefer NVMe when unknown
diskType: mapped.diskGb ? mapped.diskType : mapped.diskType || 'NVMe',
virtualization: mapped.virtualization || 'KVM',
}
}
const cloudrixMap: BillmanagerMapOverrides = {
enrichVds: enrichCloudrixVds,
}
/**
* Cloudrix — standard func=vds; geo from datacentername;
* specs via vds.order + vds.edit (no CPU/RAM in list pricelist name).
*/
export const cloudrixOverrides: BillmanagerProfileOverrides = {
id: 'cloudrix',
match: {
hostnames: ['cloudrix.ru', 'cloudrix.space'],
keywords: ['cloudrix'],
},
map: cloudrixMap,
options: {
fetchVdsEditForSpecs: true,
},
}
@@ -0,0 +1,56 @@
import { parseDatacenterName } from '../parsers.js'
import type { MappedVps } from '../mappers.js'
import type {
BillmanagerMapOverrides,
BillmanagerProfileOverrides,
} from './types.js'
/**
* Geo from Datacheap list row.
* - `datacentername`: `Россия` (country only; city empty)
* - `ipv6_subnet` when present
*
* Hardware: `pricelist` like `VPS Start NVMe unlimited (RU)` has no CPU/RAM —
* filled via vds.order + vds.edit (fetchVdsEditForSpecs).
*/
export function enrichDatacheapVds(
item: Record<string, string>,
mapped: MappedVps,
): MappedVps {
const dcRaw = (item.datacentername || item.datacenter || mapped.datacenter || '').trim()
const { country, location } = dcRaw
? parseDatacenterName(dcRaw)
: { country: '', location: '' }
const ipv6 = (item.ipv6_subnet || item.ipv6 || mapped.ipv6 || '').trim()
return {
...mapped,
country: country || mapped.country,
city: location || mapped.city,
datacenter: dcRaw || mapped.datacenter,
ipv6: ipv6 || mapped.ipv6,
diskType: mapped.diskGb ? mapped.diskType : mapped.diskType || 'NVMe',
virtualization: mapped.virtualization || 'KVM',
}
}
const datacheapMap: BillmanagerMapOverrides = {
enrichVds: enrichDatacheapVds,
}
/**
* Datacheap — standard func=vds; geo from datacentername;
* specs via vds.order + vds.edit.
*/
export const datacheapOverrides: BillmanagerProfileOverrides = {
id: 'datacheap',
match: {
hostnames: ['datacheap.ru', 'datacheap.com'],
keywords: ['datacheap'],
},
map: datacheapMap,
options: {
fetchVdsEditForSpecs: true,
},
}
@@ -0,0 +1,42 @@
import { parseDatacenterName } from '../parsers.js'
import type { MappedVps } from '../mappers.js'
import type {
BillmanagerMapOverrides,
BillmanagerProfileOverrides,
} from './types.js'
/**
* Geo from ihor `datacentername`, e.g. `Москва DC3` → Россия / Москва.
*/
export function enrichIhorVds(
item: Record<string, string>,
mapped: MappedVps,
): MappedVps {
const dcRaw = (item.datacentername || item.datacenter || mapped.datacenter || '').trim()
if (!dcRaw) return mapped
const { country, location } = parseDatacenterName(dcRaw)
return {
...mapped,
country: country || mapped.country,
city: location || mapped.city,
datacenter: dcRaw || mapped.datacenter,
}
}
const ihorMap: BillmanagerMapOverrides = {
enrichVds: enrichIhorVds,
}
/**
* ihor (my-ihor.ru) — standard func=vds.
* Geo from datacentername via enrichVds.
*/
export const ihorOverrides: BillmanagerProfileOverrides = {
id: 'ihor',
match: {
hostnames: ['my-ihor.ru', 'ihor.ru'],
keywords: ['ihor'],
},
map: ihorMap,
}
@@ -11,4 +11,9 @@ export type {
BillmanagerProfileOverrides,
BillmanagerRequestParams,
} from './types.js'
export { enrichCloudrixVds, cloudrixOverrides } from './cloudrix.js'
export { enrichDatacheapVds, datacheapOverrides } from './datacheap.js'
export { enrichIhorVds, ihorOverrides } from './ihor.js'
export { enrichLandvpsVds, landvpsOverrides } from './landvps.js'
export { enrichServhostVds, servhostOverrides } from './servhost.js'
export { waicoreOverrides } from './waicore.js'
@@ -0,0 +1,56 @@
import { parseDatacenterName } from '../parsers.js'
import type { MappedVps } from '../mappers.js'
import type {
BillmanagerMapOverrides,
BillmanagerProfileOverrides,
} from './types.js'
/**
* Geo + IPv6 from LandVPS list row.
* - `datacentername`: `ДЦ Москва` → Россия / Москва
* - `ipv6_subnet`
*
* Hardware: `pricelist` like `MSK-0` has no CPU/RAM —
* filled via vds.order + vds.edit (fetchVdsEditForSpecs).
*/
export function enrichLandvpsVds(
item: Record<string, string>,
mapped: MappedVps,
): MappedVps {
const dcRaw = (item.datacentername || item.datacenter || mapped.datacenter || '').trim()
const { country, location } = dcRaw
? parseDatacenterName(dcRaw)
: { country: '', location: '' }
const ipv6 = (item.ipv6_subnet || item.ipv6 || mapped.ipv6 || '').trim()
return {
...mapped,
country: country || mapped.country,
city: location || mapped.city,
datacenter: dcRaw || mapped.datacenter,
ipv6: ipv6 || mapped.ipv6,
diskType: mapped.diskGb ? mapped.diskType : mapped.diskType || 'NVMe',
virtualization: mapped.virtualization || 'KVM',
}
}
const landvpsMap: BillmanagerMapOverrides = {
enrichVds: enrichLandvpsVds,
}
/**
* LandVPS — standard func=vds; geo from datacentername;
* specs via vds.order + vds.edit.
*/
export const landvpsOverrides: BillmanagerProfileOverrides = {
id: 'landvps',
match: {
hostnames: ['landvps.online', 'landvps.ru', 'landvps.com'],
keywords: ['landvps'],
},
map: landvpsMap,
options: {
fetchVdsEditForSpecs: true,
},
}
@@ -1,6 +1,11 @@
import { DEFAULT_PROFILE } from './default.js'
import { cloudrixOverrides } from './cloudrix.js'
import { datacheapOverrides } from './datacheap.js'
import { firstbyteOverrides } from './firstbyte.js'
import { ihorOverrides } from './ihor.js'
import { landvpsOverrides } from './landvps.js'
import { mergeProfile } from './merge.js'
import { servhostOverrides } from './servhost.js'
import type { BillmanagerProfile, BillmanagerProfileOverrides } from './types.js'
import { waicoreOverrides } from './waicore.js'
@@ -11,6 +16,11 @@ import { waicoreOverrides } from './waicore.js'
export const PROFILE_OVERRIDES: BillmanagerProfileOverrides[] = [
waicoreOverrides,
firstbyteOverrides,
ihorOverrides,
cloudrixOverrides,
servhostOverrides,
landvpsOverrides,
datacheapOverrides,
]
function matchesUrl(url: string, override: BillmanagerProfileOverrides): boolean {
@@ -0,0 +1,54 @@
import { parseDatacenterName } from '../parsers.js'
import type { MappedVps } from '../mappers.js'
import type {
BillmanagerMapOverrides,
BillmanagerProfileOverrides,
} from './types.js'
/**
* Geo from serv.host `datacentername`, e.g. `Новосибирск Xeon` → Россия / Новосибирск.
*
* Hardware: list `pricelist` like `Тариф «Xeon 2667V2-1»` has no CPU/RAM/disk —
* filled via vds.order + vds.edit (fetchVdsEditForSpecs).
*/
export function enrichServhostVds(
item: Record<string, string>,
mapped: MappedVps,
): MappedVps {
const dcRaw = (item.datacentername || item.datacenter || mapped.datacenter || '').trim()
const { country, location } = dcRaw
? parseDatacenterName(dcRaw)
: { country: '', location: '' }
const ipv6 = (item.ipv6_subnet || item.ipv6 || mapped.ipv6 || '').trim()
return {
...mapped,
country: country || mapped.country,
city: location || mapped.city,
datacenter: dcRaw || mapped.datacenter,
ipv6: ipv6 || mapped.ipv6,
diskType: mapped.diskGb ? mapped.diskType : mapped.diskType || 'NVMe',
virtualization: mapped.virtualization || 'KVM',
}
}
const servhostMap: BillmanagerMapOverrides = {
enrichVds: enrichServhostVds,
}
/**
* serv.host — standard func=vds; geo from datacentername;
* specs via vds.order + vds.edit.
*/
export const servhostOverrides: BillmanagerProfileOverrides = {
id: 'servhost',
match: {
hostnames: ['serv.host'],
keywords: ['serv.host'],
},
map: servhostMap,
options: {
fetchVdsEditForSpecs: true,
},
}