elestio 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,215 +1,215 @@
1
- import { apiRequest } from '../api.js';
2
- import { doAction } from './actions.js';
3
- import { log, colors, outputJson } from '../utils.js';
4
-
5
- // ── Local Backups ──
6
-
7
- async function templateAction(vmID, action, param1 = '', param2 = '', param3 = '') {
8
- const response = await apiRequest('/api/servers/templateAction', 'POST', {
9
- vmID: String(vmID), action, param1, param2, param3
10
- });
11
- if (response.status !== 'OK' && !response.data) throw new Error(response.message || `Action "${action}" failed`);
12
- return response;
13
- }
14
-
15
- export async function listLocalBackups(vmID, json = false) {
16
- let result;
17
- try { result = await templateAction(vmID, 'scriptBackupsList'); }
18
- catch { log('info', 'No local backups found'); return []; }
19
-
20
- const backups = result.data?.backups || result.backups || [];
21
- if (json) { outputJson(backups); return backups; }
22
- if (backups.length === 0) { log('info', 'No local backups found'); return []; }
23
-
24
- console.log(`\n${colors.bold}Local Backups${colors.reset}\n`);
25
- backups.forEach(b => console.log(` ${b}`));
26
- console.log('');
27
- return backups;
28
- }
29
-
30
- export async function takeLocalBackup(vmID) {
31
- log('info', `Taking local backup on ${vmID}...`);
32
- const result = await templateAction(vmID, 'scriptBackup');
33
- log('success', 'Local backup initiated');
34
- return result;
35
- }
36
-
37
- export async function restoreLocalBackup(vmID, backupPath) {
38
- if (!backupPath) throw new Error('Backup path required');
39
- log('info', `Restoring local backup ${backupPath}...`);
40
- const result = await templateAction(vmID, 'scriptRestore', backupPath);
41
- log('success', 'Local backup restore initiated');
42
- return result;
43
- }
44
-
45
- export async function deleteLocalBackup(vmID, backupPath) {
46
- if (!backupPath) throw new Error('Backup path required');
47
- log('info', `Deleting local backup ${backupPath}...`);
48
- const result = await templateAction(vmID, 'scriptBackupDelete', backupPath);
49
- log('success', 'Local backup deleted');
50
- return result;
51
- }
52
-
53
- // ── Remote Backups ──
54
-
55
- export async function listRemoteBackups(vmID, json = false) {
56
- const response = await apiRequest('/api/backups/GetBackupList', 'POST', { serverID: String(vmID) });
57
- if (response.status !== 'OK') { log('info', 'No remote backups found'); return []; }
58
-
59
- const backups = response.data?.backups || [];
60
- if (json) { outputJson(backups); return backups; }
61
- if (backups.length === 0) { log('info', 'No remote backups found'); return []; }
62
-
63
- console.log(`\n${colors.bold}Remote Backups${colors.reset}\n`);
64
- backups.forEach(b => console.log(` ${b.snapshotName || b.name || b}`));
65
- console.log('');
66
- return backups;
67
- }
68
-
69
- export async function takeRemoteBackup(vmID) {
70
- log('info', `Taking remote backup for ${vmID}...`);
71
- const response = await apiRequest('/api/backups/StartManualBackup', 'POST', { serverID: String(vmID) });
72
- if (response.status !== 'OK') throw new Error(response.message || 'Failed to start remote backup');
73
- log('success', 'Remote backup initiated');
74
- return response;
75
- }
76
-
77
- export async function restoreRemoteBackup(vmID, snapshotName) {
78
- if (!snapshotName) throw new Error('Snapshot name required');
79
- log('info', `Restoring remote backup ${snapshotName}...`);
80
- const response = await apiRequest('/api/backups/RestoreBackup', 'POST', { serverID: String(vmID), snapshotName });
81
- if (response.status !== 'OK') throw new Error(response.message || 'Failed to restore');
82
- log('success', 'Remote backup restore initiated');
83
- return response;
84
- }
85
-
86
- export async function setupAutoBackups(vmID, backupPath = '/backup/', backupHour = '03:00') {
87
- log('info', `Setting up auto backups for ${vmID}...`);
88
- const response = await apiRequest('/api/backups/SetupAutoBackups', 'POST', { serverID: String(vmID), backupPath, backupHour });
89
- if (response.status !== 'OK') throw new Error(response.message || 'Failed to setup');
90
- log('success', `Auto backups configured at ${backupHour}`);
91
- return response;
92
- }
93
-
94
- export async function disableAutoBackups(vmID) {
95
- log('info', `Disabling auto backups for ${vmID}...`);
96
- const response = await apiRequest('/api/backups/DisableAutoBackups', 'POST', { serverID: String(vmID) });
97
- if (response.status !== 'OK') throw new Error(response.message || 'Failed to disable');
98
- log('success', 'Auto backups disabled');
99
- return response;
100
- }
101
-
102
- // ── Snapshots ──
103
-
104
- export async function listSnapshots(vmID, json = false) {
105
- const result = await doAction(vmID, 'listSnapshot');
106
- const snapshots = result.data?.snapshots || result.snapshots || [];
107
- if (json) { outputJson(snapshots); return snapshots; }
108
- if (snapshots.length === 0) { log('info', 'No snapshots found'); return []; }
109
-
110
- console.log(`\n${colors.bold}Snapshots${colors.reset}\n`);
111
- snapshots.forEach(s => console.log(` ${s.id || s.orderID || 'N/A'}: ${s.description || s.name || s.id || s} ${s.created ? `(${s.created})` : ''}`));
112
- console.log('');
113
- return snapshots;
114
- }
115
-
116
- export async function takeSnapshot(vmID) {
117
- log('info', `Taking snapshot of ${vmID}...`);
118
- const result = await doAction(vmID, 'takeSnapshot');
119
- log('success', 'Snapshot initiated');
120
- return result;
121
- }
122
-
123
- export async function restoreSnapshot(vmID, snapshotOrderID) {
124
- if (snapshotOrderID === undefined) throw new Error('Snapshot order ID required (0 = most recent)');
125
- log('info', `Restoring snapshot ${snapshotOrderID}...`);
126
- const result = await doAction(vmID, 'restoreSnapshot', { snapshotOrderID: String(snapshotOrderID) });
127
- log('success', 'Snapshot restore initiated');
128
- return result;
129
- }
130
-
131
- export async function deleteSnapshot(vmID, snapshotID) {
132
- if (!snapshotID) throw new Error('Snapshot ID required');
133
- log('info', `Deleting snapshot ${snapshotID}...`);
134
- const result = await doAction(vmID, 'deleteSnapshot', { snapshotID: String(snapshotID) });
135
- log('success', 'Snapshot deleted');
136
- return result;
137
- }
138
-
139
- export async function enableAutoSnapshots(vmID) {
140
- log('info', `Enabling auto snapshots on ${vmID}...`);
141
- const result = await doAction(vmID, 'enableBackup');
142
- log('success', 'Auto snapshots enabled');
143
- return result;
144
- }
145
-
146
- export async function disableAutoSnapshots(vmID) {
147
- log('info', `Disabling auto snapshots on ${vmID}...`);
148
- const result = await doAction(vmID, 'disableBackup');
149
- log('success', 'Auto snapshots disabled');
150
- return result;
151
- }
152
-
153
- // ── S3 External Backups ──
154
-
155
- export async function verifyS3Config(vmID, config) {
156
- const { apiKey, secretKey, bucketName, endPoint, prefix = '', providerType = 's3' } = config;
157
- if (!apiKey || !secretKey || !bucketName || !endPoint) throw new Error('Required: --key, --secret, --bucket, --endpoint');
158
-
159
- log('info', `Verifying S3 configuration for ${vmID}...`);
160
- const result = await doAction(vmID, 'verifyExternalBackupConfig', { apiKey, secretKey, bucketName, endPoint, prefix, providerType });
161
- log('success', 'S3 configuration verified');
162
- return result;
163
- }
164
-
165
- export async function enableS3Backup(vmID, config) {
166
- const { apiKey, secretKey, bucketName, endPoint, prefix = '', providerType = 's3' } = config;
167
- if (!apiKey || !secretKey || !bucketName || !endPoint) throw new Error('Required: --key, --secret, --bucket, --endpoint');
168
-
169
- log('info', `Enabling S3 backup for ${vmID}...`);
170
- const result = await doAction(vmID, 'enableExternalBackup', { apiKey, secretKey, bucketName, endPoint, prefix, providerType });
171
- log('success', 'S3 backup enabled');
172
- return result;
173
- }
174
-
175
- export async function disableS3Backup(vmID) {
176
- log('info', `Disabling S3 backup for ${vmID}...`);
177
- const result = await doAction(vmID, 'disableExternalBackup');
178
- log('success', 'S3 backup disabled');
179
- return result;
180
- }
181
-
182
- export async function takeS3Backup(vmID) {
183
- log('info', `Taking S3 backup for ${vmID}...`);
184
- const result = await doAction(vmID, 'takeExternalBackup');
185
- log('success', 'S3 backup initiated');
186
- return result;
187
- }
188
-
189
- export async function listS3Backups(vmID, json = false) {
190
- const result = await doAction(vmID, 'listExternalBackup');
191
- const backups = result.data?.backups || result.backups || [];
192
- if (json) { outputJson(backups); return backups; }
193
- if (backups.length === 0) { log('info', 'No S3 backups found'); return []; }
194
-
195
- console.log(`\n${colors.bold}S3 Backups${colors.reset}\n`);
196
- backups.forEach(b => console.log(` ${b.key || b.name || b}`));
197
- console.log('');
198
- return backups;
199
- }
200
-
201
- export async function restoreS3Backup(vmID, restoreKey) {
202
- if (!restoreKey) throw new Error('Restore key required');
203
- log('info', `Restoring S3 backup ${restoreKey}...`);
204
- const result = await doAction(vmID, 'restoreExternalBackup', { restoreKey });
205
- log('success', 'S3 backup restore initiated');
206
- return result;
207
- }
208
-
209
- export async function deleteS3Backup(vmID, deleteKey) {
210
- if (!deleteKey) throw new Error('Delete key required');
211
- log('info', `Deleting S3 backup ${deleteKey}...`);
212
- const result = await doAction(vmID, 'deleteExternalBackup', { deleteKey });
213
- log('success', 'S3 backup deleted');
214
- return result;
215
- }
1
+ import { apiRequest } from '../api.js';
2
+ import { doAction } from './actions.js';
3
+ import { log, colors, outputJson } from '../utils.js';
4
+
5
+ // ── Local Backups ──
6
+
7
+ async function templateAction(vmID, action, param1 = '', param2 = '', param3 = '') {
8
+ const response = await apiRequest('/api/servers/templateAction', 'POST', {
9
+ vmID: String(vmID), action, param1, param2, param3
10
+ });
11
+ if (response.status !== 'OK' && !response.data) throw new Error(response.message || `Action "${action}" failed`);
12
+ return response;
13
+ }
14
+
15
+ export async function listLocalBackups(vmID, json = false) {
16
+ let result;
17
+ try { result = await templateAction(vmID, 'scriptBackupsList'); }
18
+ catch { log('info', 'No local backups found'); return []; }
19
+
20
+ const backups = result.data?.backups || result.backups || [];
21
+ if (json) { outputJson(backups); return backups; }
22
+ if (backups.length === 0) { log('info', 'No local backups found'); return []; }
23
+
24
+ console.log(`\n${colors.bold}Local Backups${colors.reset}\n`);
25
+ backups.forEach(b => console.log(` ${b}`));
26
+ console.log('');
27
+ return backups;
28
+ }
29
+
30
+ export async function takeLocalBackup(vmID) {
31
+ log('info', `Taking local backup on ${vmID}...`);
32
+ const result = await templateAction(vmID, 'scriptBackup');
33
+ log('success', 'Local backup initiated');
34
+ return result;
35
+ }
36
+
37
+ export async function restoreLocalBackup(vmID, backupPath) {
38
+ if (!backupPath) throw new Error('Backup path required');
39
+ log('info', `Restoring local backup ${backupPath}...`);
40
+ const result = await templateAction(vmID, 'scriptRestore', backupPath);
41
+ log('success', 'Local backup restore initiated');
42
+ return result;
43
+ }
44
+
45
+ export async function deleteLocalBackup(vmID, backupPath) {
46
+ if (!backupPath) throw new Error('Backup path required');
47
+ log('info', `Deleting local backup ${backupPath}...`);
48
+ const result = await templateAction(vmID, 'scriptBackupDelete', backupPath);
49
+ log('success', 'Local backup deleted');
50
+ return result;
51
+ }
52
+
53
+ // ── Remote Backups ──
54
+
55
+ export async function listRemoteBackups(vmID, json = false) {
56
+ const response = await apiRequest('/api/backups/GetBackupList', 'POST', { serverID: String(vmID) });
57
+ if (response.status !== 'OK') { log('info', 'No remote backups found'); return []; }
58
+
59
+ const backups = response.data?.backups || [];
60
+ if (json) { outputJson(backups); return backups; }
61
+ if (backups.length === 0) { log('info', 'No remote backups found'); return []; }
62
+
63
+ console.log(`\n${colors.bold}Remote Backups${colors.reset}\n`);
64
+ backups.forEach(b => console.log(` ${b.snapshotName || b.name || b}`));
65
+ console.log('');
66
+ return backups;
67
+ }
68
+
69
+ export async function takeRemoteBackup(vmID) {
70
+ log('info', `Taking remote backup for ${vmID}...`);
71
+ const response = await apiRequest('/api/backups/StartManualBackup', 'POST', { serverID: String(vmID) });
72
+ if (response.status !== 'OK') throw new Error(response.message || 'Failed to start remote backup');
73
+ log('success', 'Remote backup initiated');
74
+ return response;
75
+ }
76
+
77
+ export async function restoreRemoteBackup(vmID, snapshotName) {
78
+ if (!snapshotName) throw new Error('Snapshot name required');
79
+ log('info', `Restoring remote backup ${snapshotName}...`);
80
+ const response = await apiRequest('/api/backups/RestoreBackup', 'POST', { serverID: String(vmID), snapshotName });
81
+ if (response.status !== 'OK') throw new Error(response.message || 'Failed to restore');
82
+ log('success', 'Remote backup restore initiated');
83
+ return response;
84
+ }
85
+
86
+ export async function setupAutoBackups(vmID, backupPath = '/backup/', backupHour = '03:00') {
87
+ log('info', `Setting up auto backups for ${vmID}...`);
88
+ const response = await apiRequest('/api/backups/SetupAutoBackups', 'POST', { serverID: String(vmID), backupPath, backupHour });
89
+ if (response.status !== 'OK') throw new Error(response.message || 'Failed to setup');
90
+ log('success', `Auto backups configured at ${backupHour}`);
91
+ return response;
92
+ }
93
+
94
+ export async function disableAutoBackups(vmID) {
95
+ log('info', `Disabling auto backups for ${vmID}...`);
96
+ const response = await apiRequest('/api/backups/DisableAutoBackups', 'POST', { serverID: String(vmID) });
97
+ if (response.status !== 'OK') throw new Error(response.message || 'Failed to disable');
98
+ log('success', 'Auto backups disabled');
99
+ return response;
100
+ }
101
+
102
+ // ── Snapshots ──
103
+
104
+ export async function listSnapshots(vmID, json = false) {
105
+ const result = await doAction(vmID, 'listSnapshot');
106
+ const snapshots = result.data?.snapshots || result.snapshots || [];
107
+ if (json) { outputJson(snapshots); return snapshots; }
108
+ if (snapshots.length === 0) { log('info', 'No snapshots found'); return []; }
109
+
110
+ console.log(`\n${colors.bold}Snapshots${colors.reset}\n`);
111
+ snapshots.forEach(s => console.log(` ${s.id || s.orderID || 'N/A'}: ${s.description || s.name || s.id || s} ${s.created ? `(${s.created})` : ''}`));
112
+ console.log('');
113
+ return snapshots;
114
+ }
115
+
116
+ export async function takeSnapshot(vmID) {
117
+ log('info', `Taking snapshot of ${vmID}...`);
118
+ const result = await doAction(vmID, 'takeSnapshot');
119
+ log('success', 'Snapshot initiated');
120
+ return result;
121
+ }
122
+
123
+ export async function restoreSnapshot(vmID, snapshotOrderID) {
124
+ if (snapshotOrderID === undefined) throw new Error('Snapshot order ID required (0 = most recent)');
125
+ log('info', `Restoring snapshot ${snapshotOrderID}...`);
126
+ const result = await doAction(vmID, 'restoreSnapshot', { snapshotOrderID: String(snapshotOrderID) });
127
+ log('success', 'Snapshot restore initiated');
128
+ return result;
129
+ }
130
+
131
+ export async function deleteSnapshot(vmID, snapshotID) {
132
+ if (!snapshotID) throw new Error('Snapshot ID required');
133
+ log('info', `Deleting snapshot ${snapshotID}...`);
134
+ const result = await doAction(vmID, 'deleteSnapshot', { snapshotID: String(snapshotID) });
135
+ log('success', 'Snapshot deleted');
136
+ return result;
137
+ }
138
+
139
+ export async function enableAutoSnapshots(vmID) {
140
+ log('info', `Enabling auto snapshots on ${vmID}...`);
141
+ const result = await doAction(vmID, 'enableBackup');
142
+ log('success', 'Auto snapshots enabled');
143
+ return result;
144
+ }
145
+
146
+ export async function disableAutoSnapshots(vmID) {
147
+ log('info', `Disabling auto snapshots on ${vmID}...`);
148
+ const result = await doAction(vmID, 'disableBackup');
149
+ log('success', 'Auto snapshots disabled');
150
+ return result;
151
+ }
152
+
153
+ // ── S3 External Backups ──
154
+
155
+ export async function verifyS3Config(vmID, config) {
156
+ const { apiKey, secretKey, bucketName, endPoint, prefix = '', providerType = 's3' } = config;
157
+ if (!apiKey || !secretKey || !bucketName || !endPoint) throw new Error('Required: --key, --secret, --bucket, --endpoint');
158
+
159
+ log('info', `Verifying S3 configuration for ${vmID}...`);
160
+ const result = await doAction(vmID, 'verifyExternalBackupConfig', { apiKey, secretKey, bucketName, endPoint, prefix, providerType });
161
+ log('success', 'S3 configuration verified');
162
+ return result;
163
+ }
164
+
165
+ export async function enableS3Backup(vmID, config) {
166
+ const { apiKey, secretKey, bucketName, endPoint, prefix = '', providerType = 's3' } = config;
167
+ if (!apiKey || !secretKey || !bucketName || !endPoint) throw new Error('Required: --key, --secret, --bucket, --endpoint');
168
+
169
+ log('info', `Enabling S3 backup for ${vmID}...`);
170
+ const result = await doAction(vmID, 'enableExternalBackup', { apiKey, secretKey, bucketName, endPoint, prefix, providerType });
171
+ log('success', 'S3 backup enabled');
172
+ return result;
173
+ }
174
+
175
+ export async function disableS3Backup(vmID) {
176
+ log('info', `Disabling S3 backup for ${vmID}...`);
177
+ const result = await doAction(vmID, 'disableExternalBackup');
178
+ log('success', 'S3 backup disabled');
179
+ return result;
180
+ }
181
+
182
+ export async function takeS3Backup(vmID) {
183
+ log('info', `Taking S3 backup for ${vmID}...`);
184
+ const result = await doAction(vmID, 'takeExternalBackup');
185
+ log('success', 'S3 backup initiated');
186
+ return result;
187
+ }
188
+
189
+ export async function listS3Backups(vmID, json = false) {
190
+ const result = await doAction(vmID, 'listExternalBackup');
191
+ const backups = result.data?.backups || result.backups || [];
192
+ if (json) { outputJson(backups); return backups; }
193
+ if (backups.length === 0) { log('info', 'No S3 backups found'); return []; }
194
+
195
+ console.log(`\n${colors.bold}S3 Backups${colors.reset}\n`);
196
+ backups.forEach(b => console.log(` ${b.key || b.name || b}`));
197
+ console.log('');
198
+ return backups;
199
+ }
200
+
201
+ export async function restoreS3Backup(vmID, restoreKey) {
202
+ if (!restoreKey) throw new Error('Restore key required');
203
+ log('info', `Restoring S3 backup ${restoreKey}...`);
204
+ const result = await doAction(vmID, 'restoreExternalBackup', { restoreKey });
205
+ log('success', 'S3 backup restore initiated');
206
+ return result;
207
+ }
208
+
209
+ export async function deleteS3Backup(vmID, deleteKey) {
210
+ if (!deleteKey) throw new Error('Delete key required');
211
+ log('info', `Deleting S3 backup ${deleteKey}...`);
212
+ const result = await doAction(vmID, 'deleteExternalBackup', { deleteKey });
213
+ log('success', 'S3 backup deleted');
214
+ return result;
215
+ }
@@ -1,106 +1,106 @@
1
- import { apiRequest } from '../api.js';
2
- import { loadConfig } from '../config.js';
3
- import { formatTable, colors, log, outputJson } from '../utils.js';
4
-
5
- export async function getBillingSummary(json = false) {
6
- const projectsResponse = await apiRequest('/api/projects/getList');
7
- const projects = projectsResponse.data?.projects || [];
8
-
9
- if (projects.length === 0) {
10
- log('info', 'No projects found');
11
- return { data: {}, totalMonthly: 0, totalServices: 0 };
12
- }
13
-
14
- let totalMonthly = 0;
15
- let totalServices = 0;
16
- let totalSpent = 0;
17
- const data = {};
18
-
19
- for (const project of projects) {
20
- try {
21
- const response = await apiRequest('/api/billings/getProjectBillings', 'POST', {
22
- projectId: String(project.projectID)
23
- });
24
-
25
- if (response.status === 'OK' && response.data) {
26
- const billing = response.data;
27
- const monthly = parseFloat(billing.monthlyCost) || (parseFloat(billing.costPerHour) * 720);
28
- totalMonthly += monthly;
29
- totalServices += billing.nbServices || 0;
30
- totalSpent += parseFloat(billing.totalFromBeginning) || 0;
31
- data[project.projectID] = { ...billing, projectName: project.project_name };
32
- }
33
- } catch { /* skip */ }
34
- }
35
-
36
- const summary = { data, totalMonthly, totalServices, totalSpent };
37
-
38
- if (json) {
39
- outputJson(summary);
40
- return summary;
41
- }
42
-
43
- console.log(`\n${colors.bold}Billing Summary${colors.reset}\n`);
44
-
45
- Object.entries(data).forEach(([pid, billing]) => {
46
- const monthly = parseFloat(billing.monthlyCost) || (parseFloat(billing.costPerHour) * 720);
47
- console.log(` ${colors.cyan}${billing.projectName}${colors.reset} (ID: ${pid})`);
48
- console.log(` Services: ${billing.nbServices || 0}`);
49
- console.log(` Cost/hour: $${billing.costPerHour || '0.0000'}`);
50
- console.log(` Est. Monthly: $${monthly.toFixed(2)}`);
51
- console.log(` Total Spent: $${billing.totalFromBeginning || '0.00'}`);
52
- console.log('');
53
- });
54
-
55
- console.log(`${colors.bold}Total${colors.reset}`);
56
- console.log(` Services: ${totalServices}`);
57
- console.log(` Est. Monthly: ${colors.green}$${totalMonthly.toFixed(2)}${colors.reset}`);
58
- console.log(` Total Spent: $${totalSpent.toFixed(2)}`);
59
- console.log('');
60
- return summary;
61
- }
62
-
63
- export async function getProjectBilling(projectId = null, json = false) {
64
- const config = loadConfig();
65
- const pid = projectId || config.defaultProject;
66
- if (!pid) throw new Error('Project ID required');
67
-
68
- const response = await apiRequest('/api/billings/getProjectBillings', 'POST', { projectId: String(pid) });
69
- if (response.status !== 'OK') throw new Error(response.message || 'Failed');
70
-
71
- const billing = response.data || {};
72
- const services = billing.boardInformations || [];
73
-
74
- if (json) { outputJson(billing); return billing; }
75
-
76
- console.log(`\n${colors.bold}Project ${pid} Billing${colors.reset}\n`);
77
- console.log(` Services: ${billing.nbServices || 0}`);
78
- console.log(` Volumes: ${billing.nbVolumes || 0}`);
79
- console.log(` Cost/hour: $${billing.costPerHour || '0.0000'}`);
80
- console.log(` Est. Monthly: ${colors.green}$${billing.monthlyCost || '0.00'}${colors.reset}`);
81
- console.log(` Total Spent: $${billing.totalFromBeginning || '0.00'}`);
82
-
83
- if (services.length > 0) {
84
- const columns = [
85
- { key: 'displayName', label: 'Service' },
86
- { key: 'resourceType', label: 'Type' },
87
- { key: 'nbHoursUsed', label: 'Hours' },
88
- { key: 'amount', label: 'Amount' },
89
- { key: 'status', label: 'Status' }
90
- ];
91
-
92
- const data = services.map(s => ({
93
- displayName: s.displayName || 'N/A',
94
- resourceType: s.resourceType || 'VM',
95
- nbHoursUsed: s.nbHoursUsed || 0,
96
- amount: `$${parseFloat(s.amount || 0).toFixed(2)}`,
97
- status: s.isFinalized ? 'Finalized' : 'Active'
98
- }));
99
-
100
- console.log(`\n${colors.bold}Details${colors.reset}\n`);
101
- console.log(formatTable(data, columns));
102
- }
103
-
104
- console.log('');
105
- return billing;
106
- }
1
+ import { apiRequest } from '../api.js';
2
+ import { loadConfig } from '../config.js';
3
+ import { formatTable, colors, log, outputJson } from '../utils.js';
4
+
5
+ export async function getBillingSummary(json = false) {
6
+ const projectsResponse = await apiRequest('/api/projects/getList');
7
+ const projects = projectsResponse.data?.projects || [];
8
+
9
+ if (projects.length === 0) {
10
+ log('info', 'No projects found');
11
+ return { data: {}, totalMonthly: 0, totalServices: 0 };
12
+ }
13
+
14
+ let totalMonthly = 0;
15
+ let totalServices = 0;
16
+ let totalSpent = 0;
17
+ const data = {};
18
+
19
+ for (const project of projects) {
20
+ try {
21
+ const response = await apiRequest('/api/billings/getProjectBillings', 'POST', {
22
+ projectId: String(project.projectID)
23
+ });
24
+
25
+ if (response.status === 'OK' && response.data) {
26
+ const billing = response.data;
27
+ const monthly = parseFloat(billing.monthlyCost) || (parseFloat(billing.costPerHour) * 720);
28
+ totalMonthly += monthly;
29
+ totalServices += billing.nbServices || 0;
30
+ totalSpent += parseFloat(billing.totalFromBeginning) || 0;
31
+ data[project.projectID] = { ...billing, projectName: project.project_name };
32
+ }
33
+ } catch { /* skip */ }
34
+ }
35
+
36
+ const summary = { data, totalMonthly, totalServices, totalSpent };
37
+
38
+ if (json) {
39
+ outputJson(summary);
40
+ return summary;
41
+ }
42
+
43
+ console.log(`\n${colors.bold}Billing Summary${colors.reset}\n`);
44
+
45
+ Object.entries(data).forEach(([pid, billing]) => {
46
+ const monthly = parseFloat(billing.monthlyCost) || (parseFloat(billing.costPerHour) * 720);
47
+ console.log(` ${colors.cyan}${billing.projectName}${colors.reset} (ID: ${pid})`);
48
+ console.log(` Services: ${billing.nbServices || 0}`);
49
+ console.log(` Cost/hour: $${billing.costPerHour || '0.0000'}`);
50
+ console.log(` Est. Monthly: $${monthly.toFixed(2)}`);
51
+ console.log(` Total Spent: $${billing.totalFromBeginning || '0.00'}`);
52
+ console.log('');
53
+ });
54
+
55
+ console.log(`${colors.bold}Total${colors.reset}`);
56
+ console.log(` Services: ${totalServices}`);
57
+ console.log(` Est. Monthly: ${colors.green}$${totalMonthly.toFixed(2)}${colors.reset}`);
58
+ console.log(` Total Spent: $${totalSpent.toFixed(2)}`);
59
+ console.log('');
60
+ return summary;
61
+ }
62
+
63
+ export async function getProjectBilling(projectId = null, json = false) {
64
+ const config = loadConfig();
65
+ const pid = projectId || config.defaultProject;
66
+ if (!pid) throw new Error('Project ID required');
67
+
68
+ const response = await apiRequest('/api/billings/getProjectBillings', 'POST', { projectId: String(pid) });
69
+ if (response.status !== 'OK') throw new Error(response.message || 'Failed');
70
+
71
+ const billing = response.data || {};
72
+ const services = billing.boardInformations || [];
73
+
74
+ if (json) { outputJson(billing); return billing; }
75
+
76
+ console.log(`\n${colors.bold}Project ${pid} Billing${colors.reset}\n`);
77
+ console.log(` Services: ${billing.nbServices || 0}`);
78
+ console.log(` Volumes: ${billing.nbVolumes || 0}`);
79
+ console.log(` Cost/hour: $${billing.costPerHour || '0.0000'}`);
80
+ console.log(` Est. Monthly: ${colors.green}$${billing.monthlyCost || '0.00'}${colors.reset}`);
81
+ console.log(` Total Spent: $${billing.totalFromBeginning || '0.00'}`);
82
+
83
+ if (services.length > 0) {
84
+ const columns = [
85
+ { key: 'displayName', label: 'Service' },
86
+ { key: 'resourceType', label: 'Type' },
87
+ { key: 'nbHoursUsed', label: 'Hours' },
88
+ { key: 'amount', label: 'Amount' },
89
+ { key: 'status', label: 'Status' }
90
+ ];
91
+
92
+ const data = services.map(s => ({
93
+ displayName: s.displayName || 'N/A',
94
+ resourceType: s.resourceType || 'VM',
95
+ nbHoursUsed: s.nbHoursUsed || 0,
96
+ amount: `$${parseFloat(s.amount || 0).toFixed(2)}`,
97
+ status: s.isFinalized ? 'Finalized' : 'Active'
98
+ }));
99
+
100
+ console.log(`\n${colors.bold}Details${colors.reset}\n`);
101
+ console.log(formatTable(data, columns));
102
+ }
103
+
104
+ console.log('');
105
+ return billing;
106
+ }