init commit

This commit is contained in:
Denozordec
2026-03-17 23:19:28 +07:00
commit 221edcda16
58 changed files with 11938 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
import { uid } from './utils'
const COLLECTIONS = {
vps: 'vps',
providers: 'providers',
providerAccounts: 'providerAccounts',
payments: 'payments',
balanceLedger: 'balanceLedger',
settings: 'settings',
}
const API_PATHS = {
[COLLECTIONS.vps]: '/api/vps',
[COLLECTIONS.providers]: '/api/providers',
[COLLECTIONS.providerAccounts]: '/api/provider-accounts',
[COLLECTIONS.payments]: '/api/payments',
[COLLECTIONS.balanceLedger]: '/api/balance-ledger',
[COLLECTIONS.settings]: '/api/settings',
}
const STORAGE_KEY_PREFIX = 'vps-tracker:'
const API_BASE = import.meta.env.VITE_API_URL || ''
function getLocalStorageData() {
const data = {}
for (const name of Object.values(COLLECTIONS)) {
const key = `${STORAGE_KEY_PREFIX}${name}`
const raw = localStorage.getItem(key)
if (raw) {
try {
data[name] = JSON.parse(raw)
} catch {
data[name] = []
}
}
}
return data
}
function clearLocalStorage() {
for (const name of Object.values(COLLECTIONS)) {
localStorage.removeItem(`${STORAGE_KEY_PREFIX}${name}`)
}
}
async function fetchApi(path, options = {}) {
const url = `${API_BASE}${path.startsWith('/') ? path : `/${path}`}`
const res = await fetch(url, {
headers: { 'Content-Type': 'application/json', ...options.headers },
...options,
})
if (!res.ok) {
const err = new Error(res.statusText || 'API error')
err.status = res.status
err.response = res
throw err
}
if (res.status === 204) return null
return res.json()
}
export async function initDataStore() {
const localData = getLocalStorageData()
const hasLocalData = Object.keys(localData).some((k) => {
const arr = localData[k]
return Array.isArray(arr) ? arr.length > 0 : arr && typeof arr === 'object'
})
if (hasLocalData) {
try {
await fetchApi('/api/migrate', {
method: 'POST',
body: JSON.stringify(localData),
})
clearLocalStorage()
} catch (err) {
console.warn('Migration from localStorage failed:', err)
}
}
}
export async function loadDataSet() {
const data = await fetchApi('/api/data')
return {
[COLLECTIONS.vps]: data.vps ?? [],
[COLLECTIONS.providers]: data.providers ?? [],
[COLLECTIONS.providerAccounts]: data.providerAccounts ?? [],
[COLLECTIONS.payments]: data.payments ?? [],
[COLLECTIONS.balanceLedger]: data.balanceLedger ?? [],
[COLLECTIONS.settings]: data.settings ?? [],
activeTariffs: data.activeTariffs ?? [],
tariffSyncOptions: data.tariffSyncOptions ?? [],
}
}
async function fetchCollection(collectionName) {
const path = API_PATHS[collectionName]
if (!path) throw new Error(`Unknown collection: ${collectionName}`)
return fetchApi(path)
}
export async function createRecord(collectionName, record) {
const path = API_PATHS[collectionName]
if (!path) throw new Error(`Unknown collection: ${collectionName}`)
const payload = { ...record, id: record.id || uid() }
await fetchApi(path, {
method: 'POST',
body: JSON.stringify(payload),
})
return fetchCollection(collectionName)
}
export async function updateRecord(collectionName, id, patch) {
const path = API_PATHS[collectionName]
if (!path) throw new Error(`Unknown collection: ${collectionName}`)
await fetchApi(`${path}/${encodeURIComponent(id)}`, {
method: 'PUT',
body: JSON.stringify(patch),
})
return fetchCollection(collectionName)
}
export async function deleteRecord(collectionName, id) {
const path = API_PATHS[collectionName]
if (!path) throw new Error(`Unknown collection: ${collectionName}`)
await fetchApi(`${path}/${encodeURIComponent(id)}`, { method: 'DELETE' })
return fetchCollection(collectionName)
}
export async function syncAccount(accountId) {
return fetchApi(`/api/sync/${encodeURIComponent(accountId)}`, { method: 'POST' })
}
export async function fetchAccountBalance(accountId) {
return fetchApi(`/api/sync/${encodeURIComponent(accountId)}/balance`, { method: 'GET' })
}
export async function testApiConnection(apiBaseUrl, apiCredentials) {
return fetchApi('/api/sync/test-connection', {
method: 'POST',
body: JSON.stringify({ apiBaseUrl, apiCredentials }),
})
}