diff --git a/apps/api/src/routes/settings.test.ts b/apps/api/src/routes/settings.test.ts index 1c964fc..1542652 100644 --- a/apps/api/src/routes/settings.test.ts +++ b/apps/api/src/routes/settings.test.ts @@ -26,7 +26,7 @@ describe('settings telegram test', () => { it('returns telegram API error with hint', async () => { vi.stubGlobal( 'fetch', - vi.fn(async () => + vi.fn(async () => Response.json({ ok: false, description: 'Bad Request: chat not found' }), ), ) @@ -39,7 +39,7 @@ describe('settings telegram test', () => { }) it('uses body overrides and falls back to db token', async () => { - const fetchMock = vi.fn(async () => Response.json({ ok: true })) + const fetchMock = vi.fn(async () => Response.json({ ok: true })) vi.stubGlobal('fetch', fetchMock) const res = await app.inject({ @@ -54,9 +54,9 @@ describe('settings telegram test', () => { const body = res.json() as { ok: boolean } expect(body.ok).toBe(true) - const call = fetchMock.mock.calls[0] as [string, RequestInit] | undefined + const call = fetchMock.mock.calls[0] expect(call).toBeDefined() - const sent = JSON.parse(String(call![1].body)) as { + const sent = JSON.parse(String(call![1]?.body)) as { chat_id: string message_thread_id: number } @@ -65,7 +65,7 @@ describe('settings telegram test', () => { }) it('uses body token when provided', async () => { - const fetchMock = vi.fn(async () => Response.json({ ok: true })) + const fetchMock = vi.fn(async () => Response.json({ ok: true })) vi.stubGlobal('fetch', fetchMock) await app.inject({ @@ -77,7 +77,7 @@ describe('settings telegram test', () => { }, }) - const url = String((fetchMock.mock.calls[0] as [string])[0]) + const url = String(fetchMock.mock.calls[0]![0]) expect(url).toContain('botoverride-token/') }) }) @@ -115,7 +115,7 @@ describe('settings cfdm sync', () => { }) it('requests full sync from CFDM', async () => { - const fetchMock = vi.fn(async () => + const fetchMock = vi.fn(async () => Response.json({ ok: true, count: 1, @@ -141,9 +141,9 @@ describe('settings cfdm sync', () => { expect(res.json()).toMatchObject({ ok: true }) expect((res.json() as { count: number }).count).toBeGreaterThanOrEqual(1) - const call = fetchMock.mock.calls[0] as [string, RequestInit] | undefined + const call = fetchMock.mock.calls[0] expect(call?.[0]).toBe('http://cfdm.test/api/v1/integrations/vps-tracker/sync') - expect((call?.[1].headers as Record).Authorization).toBe( + expect((call?.[1]?.headers as Record).Authorization).toBe( 'Bearer shared-token', ) }) @@ -154,7 +154,7 @@ describe('settings cfdm sync', () => { integrationToken: 'shared-token', cfdmApiUrl: 'http://cfdm.test', }) - const fetchMock = vi.fn(async () => + const fetchMock = vi.fn(async () => Response.json({ ok: true, count: 0, bindings: [], fullSync: true }), ) vi.stubGlobal('fetch', fetchMock) @@ -173,7 +173,7 @@ describe('settings cfdm sync', () => { }) vi.stubGlobal( 'fetch', - vi.fn(async () => { + vi.fn(async () => { throw new TypeError('fetch failed') }), ) @@ -211,18 +211,19 @@ describe('settings cfdm sync', () => { id: 'cfdm', name: 'CFDM', url: 'http://192.168.100.67:6363', + icon: 'cloud', }, ], }, }) - const fetchMock = vi.fn(async () => + const fetchMock = vi.fn(async () => Response.json({ ok: true, count: 0, bindings: [], fullSync: true }), ) vi.stubGlobal('fetch', fetchMock) const res = await app.inject({ method: 'POST', url: '/api/settings/cfdm/sync' }) expect(res.statusCode).toBe(200) - const call = fetchMock.mock.calls[0] as [string] | undefined + const call = fetchMock.mock.calls[0] expect(call?.[0]).toBe('https://cfdm.prod.example/api/v1/integrations/vps-tracker/sync') }) }) diff --git a/apps/api/src/services/audit.test.ts b/apps/api/src/services/audit.test.ts index ac6131f..dcb9e8b 100644 --- a/apps/api/src/services/audit.test.ts +++ b/apps/api/src/services/audit.test.ts @@ -46,7 +46,9 @@ describe('audit dual-write', () => { }) it('fire-and-forgets portal ingest with vps action key', async () => { - const fetchMock = vi.fn(async () => new Response(JSON.stringify({ accepted: 1, duplicates: 0 }))) + const fetchMock = vi.fn( + async () => new Response(JSON.stringify({ accepted: 1, duplicates: 0 })), + ) vi.stubGlobal('fetch', fetchMock) auditCreate( @@ -63,15 +65,15 @@ describe('audit dual-write', () => { await vi.waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1)) - const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit] + const [url, init] = fetchMock.mock.calls[0]! expect(url).toBe('http://portal.test/api/v1/ingest/audit') - expect(init.method).toBe('POST') - expect(init.headers).toMatchObject({ + expect(init?.method).toBe('POST') + expect(init?.headers).toMatchObject({ Authorization: 'Bearer test-ingest-secret', 'Content-Type': 'application/json', }) - const body = JSON.parse(String(init.body)) as { + const body = JSON.parse(String(init?.body)) as { events: Array<{ event_id: string source_app: string @@ -94,7 +96,7 @@ describe('audit dual-write', () => { it('does not throw when portal ingest fails', () => { vi.stubGlobal( 'fetch', - vi.fn(async () => { + vi.fn(async () => { throw new Error('network down') }), ) diff --git a/apps/api/src/services/billmanager/sync-job.ts b/apps/api/src/services/billmanager/sync-job.ts index 6d14670..42c8bbe 100644 --- a/apps/api/src/services/billmanager/sync-job.ts +++ b/apps/api/src/services/billmanager/sync-job.ts @@ -3,18 +3,15 @@ */ import type { BillmanagerSyncAccount } from './context.js' -import type { SyncFromBillmanagerOptions, SyncFromBillmanagerResult } from './sync.js' +import type { SyncFromBillmanagerOptions } from './sync.js' import { billmanagerAdapter } from '../providers/billmanager-adapter.js' -import { runAccountSync } from '../providers/sync-job.js' +import { runAccountSync, type RunAccountSyncResult } from '../providers/sync-job.js' -export interface RunBillmanagerAccountSyncResult extends SyncFromBillmanagerResult { - ok: true - logId: string -} +export type RunBillmanagerAccountSyncResult = RunAccountSyncResult export async function runBillmanagerAccountSync( account: BillmanagerSyncAccount, opts: SyncFromBillmanagerOptions = {}, -): Promise { +): Promise { return runAccountSync(billmanagerAdapter, account, opts) } diff --git a/apps/api/src/services/providers/types.ts b/apps/api/src/services/providers/types.ts index babb851..8a0a20f 100644 --- a/apps/api/src/services/providers/types.ts +++ b/apps/api/src/services/providers/types.ts @@ -9,7 +9,12 @@ export interface SyncResult { vpsCount: number paymentsCount: number tariffsCount: number - balance: { balance?: number; currency?: string } | null + balance: { + balance?: number + currency?: string + enoughmoneyto?: string + realbalance?: string + } | null syncSummary: SyncSummary newTariffs: { name: string; price: string; providerId: string }[] } diff --git a/apps/api/src/services/veesp/mappers.ts b/apps/api/src/services/veesp/mappers.ts index 92df922..b7542d9 100644 --- a/apps/api/src/services/veesp/mappers.ts +++ b/apps/api/src/services/veesp/mappers.ts @@ -30,20 +30,10 @@ function roundRate(n: number): number { return Math.round(n * 100) / 100 } -function mapBillingCycle(cycle: string | undefined): { - tariffType: string - dailyRate: number | null - monthlyRate: number | null -} { - const c = String(cycle ?? '').toLowerCase() - return { tariffType: c.includes('day') ? 'daily' : 'monthly', dailyRate: null, monthlyRate: null } -} - function ratesFromTotal( total: number, cycle: string | undefined, ): { tariffType: string; dailyRate: number | null; monthlyRate: number | null } { - const base = mapBillingCycle(cycle) const c = String(cycle ?? '').toLowerCase() if (c.includes('day')) { return { tariffType: 'daily', dailyRate: roundRate(total), monthlyRate: null } @@ -253,7 +243,7 @@ export function mapVpsRecordToVps( currency, dailyRate: rates.dailyRate, monthlyRate: rates.monthlyRate, - createdAt: dateToIso(detail.date_created), + createdAt: dateToIso(serviceDetail?.date_created), paidUntil: dateToIso(detail.next_due ?? service.next_due), notes: label ? `${label} [veesp-${externalId}]` : `veesp-${externalId}`, } diff --git a/apps/api/src/services/veesp/operations.ts b/apps/api/src/services/veesp/operations.ts index dcf28cf..02a94a7 100644 --- a/apps/api/src/services/veesp/operations.ts +++ b/apps/api/src/services/veesp/operations.ts @@ -62,6 +62,7 @@ export interface VeespVmDetail extends VeespVmListItem { } export interface VeespIpItem { + id?: string | number ip?: string address?: string ipaddress?: string @@ -207,7 +208,7 @@ export function isVpsService(service: VeespServiceListItem, vpsCategoryIds?: Set return false } -function unwrapKeyedList>(raw: unknown): T[] { +function unwrapKeyedList(raw: unknown): T[] { if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return [] const out: T[] = [] for (const [key, item] of Object.entries(raw as Record)) { @@ -218,7 +219,7 @@ function unwrapKeyedList>(raw: unknown): T[] { } if (typeof item !== 'object') continue const row = item as Record - out.push({ ...row, id: row.id ?? key } as T) + out.push({ ...row, id: row.id ?? key } as unknown as T) } return out }