feat(api, web): интеграция Macloud и VDSina через унифицированный UserAPI
Docker / build (push) Has been cancelled

Один модуль userapi для идентичного API обоих хостеров: синк VPS, баланса, тарифов и пополнений.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-06-29 02:02:03 +07:00
co-authored by Cursor
parent 9be50aa03b
commit f2c4c7a412
18 changed files with 1469 additions and 32 deletions
@@ -0,0 +1,82 @@
import { describe, expect, it } from 'vitest'
import { mapOperationToPayment, mapServerToVps } from './mappers.js'
import type { UserApiOperation, UserApiServerDetail } from './operations.js'
const server: UserApiServerDetail = {
id: 12345,
name: 'Server #12345',
full_name: 'Server 2 RAM / 1 CPU / 40 NVMe #12345',
created: '2022-02-24',
end: '2029-02-20',
status: 'active',
host: 'super.server.host',
ip: { id: 1, ip: '91.84.101.78', type: '4' },
template: { id: 23, name: 'Ubuntu 24.04' },
datacenter: { id: 1, name: 'Amsterdam 1', country: 'nl' },
'server-plan': { id: 1, name: '2 RAM / 1 CPU / 40 NVMe' },
data: {
cpu: { value: 1 },
ram: { value: 2 },
disk: { value: 40 },
traff: { value: 32, for: 'Tb' },
},
}
describe('mapServerToVps', () => {
it('maps macloud server with apiType prefix in notes', () => {
const vps = mapServerToVps(server, 'macloud', 'prov-1', 'acc-1')
expect(vps.externalId).toBe('12345')
expect(vps.ip).toBe('91.84.101.78')
expect(vps.os).toBe('Ubuntu 24.04')
expect(vps.vcpu).toBe(1)
expect(vps.ramGb).toBe(2)
expect(vps.diskGb).toBe(40)
expect(vps.bandwidthTb).toBe(32)
expect(vps.country).toBe('NL')
expect(vps.paidUntil).toBe('2029-02-20')
expect(vps.notes).toContain('macloud-12345')
expect(vps.tariffType).toBe('daily')
})
it('maps vdsina server with vdsina prefix in notes', () => {
const vps = mapServerToVps(server, 'vdsina', 'prov-2', 'acc-2')
expect(vps.notes).toContain('vdsina-12345')
})
it('maps paused status for notpaid', () => {
const vps = mapServerToVps({ ...server, status: 'notpaid' }, 'macloud', 'p', 'a')
expect(vps.status).toBe('paused')
})
})
describe('mapOperationToPayment', () => {
const topup: UserApiOperation = {
id: 4290676,
purse: 'real',
type: 1,
status: 1,
summ: '100',
created: '2025-02-22 12:28:23',
comment: 'Balance replenishment',
}
it('maps paid credit to provider_balance_topup', () => {
const payment = mapOperationToPayment(topup, 'macloud', 'acc-1')
expect(payment).toMatchObject({
externalId: '4290676',
type: 'provider_balance_topup',
amount: 100,
date: '2025-02-22',
note: 'Balance replenishment',
})
})
it('returns null for debit operations', () => {
expect(mapOperationToPayment({ ...topup, type: -1 }, 'vdsina', 'acc-1')).toBeNull()
})
it('returns null for unpaid operations', () => {
expect(mapOperationToPayment({ ...topup, status: 0 }, 'macloud', 'acc-1')).toBeNull()
})
})