@rollupdate/hctl 0.0.0-stage → 3.1.0
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.
- package/LICENSE +21 -0
- package/README.md +1071 -2
- package/dist/auction/cache.d.ts +6 -0
- package/dist/auction/cache.js +55 -0
- package/dist/auction/client.d.ts +24 -0
- package/dist/auction/client.js +236 -0
- package/dist/auction/commands.d.ts +2 -0
- package/dist/auction/commands.js +440 -0
- package/dist/auction/diff-formatter.d.ts +3 -0
- package/dist/auction/diff-formatter.js +61 -0
- package/dist/auction/diff.d.ts +11 -0
- package/dist/auction/diff.js +28 -0
- package/dist/auction/formatter.d.ts +21 -0
- package/dist/auction/formatter.js +182 -0
- package/dist/auction/store.d.ts +24 -0
- package/dist/auction/store.js +177 -0
- package/dist/auction/types.d.ts +74 -0
- package/dist/auction/types.js +5 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +74 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +4 -0
- package/dist/cloud/browser-bootstrap.d.ts +28 -0
- package/dist/cloud/browser-bootstrap.js +480 -0
- package/dist/cloud/client.d.ts +520 -0
- package/dist/cloud/client.js +755 -0
- package/dist/cloud/commands/certificate.d.ts +2 -0
- package/dist/cloud/commands/certificate.js +108 -0
- package/dist/cloud/commands/context.d.ts +2 -0
- package/dist/cloud/commands/context.js +133 -0
- package/dist/cloud/commands/datacenter.d.ts +2 -0
- package/dist/cloud/commands/datacenter.js +24 -0
- package/dist/cloud/commands/firewall.d.ts +2 -0
- package/dist/cloud/commands/firewall.js +142 -0
- package/dist/cloud/commands/floating-ip.d.ts +2 -0
- package/dist/cloud/commands/floating-ip.js +138 -0
- package/dist/cloud/commands/image.d.ts +2 -0
- package/dist/cloud/commands/image.js +98 -0
- package/dist/cloud/commands/index.d.ts +2 -0
- package/dist/cloud/commands/index.js +47 -0
- package/dist/cloud/commands/inventory.d.ts +2 -0
- package/dist/cloud/commands/inventory.js +13 -0
- package/dist/cloud/commands/iso.d.ts +2 -0
- package/dist/cloud/commands/iso.js +27 -0
- package/dist/cloud/commands/load-balancer-type.d.ts +2 -0
- package/dist/cloud/commands/load-balancer-type.js +24 -0
- package/dist/cloud/commands/load-balancer.d.ts +2 -0
- package/dist/cloud/commands/load-balancer.js +269 -0
- package/dist/cloud/commands/location.d.ts +2 -0
- package/dist/cloud/commands/location.js +24 -0
- package/dist/cloud/commands/network.d.ts +2 -0
- package/dist/cloud/commands/network.js +157 -0
- package/dist/cloud/commands/placement-group.d.ts +2 -0
- package/dist/cloud/commands/placement-group.js +79 -0
- package/dist/cloud/commands/pricing.d.ts +2 -0
- package/dist/cloud/commands/pricing.js +15 -0
- package/dist/cloud/commands/primary-ip.d.ts +2 -0
- package/dist/cloud/commands/primary-ip.js +139 -0
- package/dist/cloud/commands/server-type.d.ts +2 -0
- package/dist/cloud/commands/server-type.js +24 -0
- package/dist/cloud/commands/server.d.ts +2 -0
- package/dist/cloud/commands/server.js +346 -0
- package/dist/cloud/commands/ssh-key.d.ts +2 -0
- package/dist/cloud/commands/ssh-key.js +94 -0
- package/dist/cloud/commands/volume.d.ts +2 -0
- package/dist/cloud/commands/volume.js +138 -0
- package/dist/cloud/context.d.ts +34 -0
- package/dist/cloud/context.js +215 -0
- package/dist/cloud/formatter.d.ts +40 -0
- package/dist/cloud/formatter.js +824 -0
- package/dist/cloud/helpers.d.ts +27 -0
- package/dist/cloud/helpers.js +67 -0
- package/dist/cloud/inventory.d.ts +100 -0
- package/dist/cloud/inventory.js +229 -0
- package/dist/cloud/pricing.d.ts +27 -0
- package/dist/cloud/pricing.js +103 -0
- package/dist/cloud/token-instructions.d.ts +2 -0
- package/dist/cloud/token-instructions.js +21 -0
- package/dist/cloud/types.d.ts +437 -0
- package/dist/cloud/types.js +5 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +2 -0
- package/dist/formatter.d.ts +3 -0
- package/dist/formatter.js +6 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +21 -0
- package/dist/robot/client.d.ts +256 -0
- package/dist/robot/client.js +745 -0
- package/dist/robot/commands/auth.d.ts +2 -0
- package/dist/robot/commands/auth.js +54 -0
- package/dist/robot/commands/boot.d.ts +2 -0
- package/dist/robot/commands/boot.js +76 -0
- package/dist/robot/commands/cancel.d.ts +2 -0
- package/dist/robot/commands/cancel.js +37 -0
- package/dist/robot/commands/failover.d.ts +2 -0
- package/dist/robot/commands/failover.js +46 -0
- package/dist/robot/commands/firewall.d.ts +2 -0
- package/dist/robot/commands/firewall.js +72 -0
- package/dist/robot/commands/index.d.ts +2 -0
- package/dist/robot/commands/index.js +36 -0
- package/dist/robot/commands/interactive.d.ts +2 -0
- package/dist/robot/commands/interactive.js +152 -0
- package/dist/robot/commands/ip.d.ts +2 -0
- package/dist/robot/commands/ip.js +55 -0
- package/dist/robot/commands/key.d.ts +2 -0
- package/dist/robot/commands/key.js +68 -0
- package/dist/robot/commands/order.d.ts +2 -0
- package/dist/robot/commands/order.js +33 -0
- package/dist/robot/commands/rdns.d.ts +2 -0
- package/dist/robot/commands/rdns.js +41 -0
- package/dist/robot/commands/reset.d.ts +2 -0
- package/dist/robot/commands/reset.js +77 -0
- package/dist/robot/commands/server.d.ts +2 -0
- package/dist/robot/commands/server.js +32 -0
- package/dist/robot/commands/storagebox.d.ts +2 -0
- package/dist/robot/commands/storagebox.js +126 -0
- package/dist/robot/commands/subnet.d.ts +2 -0
- package/dist/robot/commands/subnet.js +24 -0
- package/dist/robot/commands/traffic.d.ts +2 -0
- package/dist/robot/commands/traffic.js +23 -0
- package/dist/robot/commands/vswitch.d.ts +2 -0
- package/dist/robot/commands/vswitch.js +81 -0
- package/dist/robot/commands/wol.d.ts +2 -0
- package/dist/robot/commands/wol.js +20 -0
- package/dist/robot/formatter.d.ts +57 -0
- package/dist/robot/formatter.js +582 -0
- package/dist/robot/types.d.ts +359 -0
- package/dist/robot/types.js +5 -0
- package/dist/shared/config.d.ts +86 -0
- package/dist/shared/config.js +289 -0
- package/dist/shared/formatter.d.ts +29 -0
- package/dist/shared/formatter.js +121 -0
- package/dist/shared/helpers.d.ts +39 -0
- package/dist/shared/helpers.js +86 -0
- package/dist/shared/init.d.ts +2 -0
- package/dist/shared/init.js +107 -0
- package/dist/shared/reference.d.ts +5 -0
- package/dist/shared/reference.js +689 -0
- package/dist/shared/schemas.d.ts +1 -0
- package/dist/shared/schemas.js +1031 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.js +2 -0
- package/package.json +127 -4
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type AuctionFetchOptions, type AuctionFetchResult } from "./client.js";
|
|
2
|
+
export interface AuctionCacheFetchOptions extends AuctionFetchOptions {
|
|
3
|
+
allowLocalFallback?: boolean;
|
|
4
|
+
dbPath?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function fetchAuctionDataWithCache(currency?: "EUR" | "USD", options?: AuctionCacheFetchOptions): Promise<AuctionFetchResult>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { fetchAuctionData, getAuctionDataUrl, } from "./client.js";
|
|
2
|
+
import { getAuctionCache, openDatabase, saveAuctionCache } from "./store.js";
|
|
3
|
+
export async function fetchAuctionDataWithCache(currency = "EUR", options = {}) {
|
|
4
|
+
try {
|
|
5
|
+
const result = await fetchAuctionData(currency, options);
|
|
6
|
+
saveLatestAuctionCache(currency, options, result);
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
if (options.allowLocalFallback === true) {
|
|
11
|
+
const cached = readLatestAuctionCache(currency, options);
|
|
12
|
+
if (cached !== null) {
|
|
13
|
+
return cached;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
throw createAuctionFetchUnavailableError(currency, options, error);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function saveLatestAuctionCache(currency, options, result) {
|
|
20
|
+
let db = null;
|
|
21
|
+
try {
|
|
22
|
+
db = openDatabase(options.dbPath);
|
|
23
|
+
saveAuctionCache(db, currency, result);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// The local cache is a convenience fallback. Fetch success should not fail
|
|
27
|
+
// just because the user's cache database cannot be written.
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
db?.close();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function readLatestAuctionCache(currency, options) {
|
|
34
|
+
let db = null;
|
|
35
|
+
try {
|
|
36
|
+
db = openDatabase(options.dbPath);
|
|
37
|
+
return getAuctionCache(db, currency);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
db?.close();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function createAuctionFetchUnavailableError(currency, options, error) {
|
|
47
|
+
const url = getAuctionDataUrl(currency, options);
|
|
48
|
+
const reason = error instanceof Error
|
|
49
|
+
? ` Reason: ${error.message}.`
|
|
50
|
+
: " The request failed.";
|
|
51
|
+
const cacheHint = options.allowLocalFallback === true
|
|
52
|
+
? " Check your internet connection. No local auction cache is available yet. Run `hctl auction list` once while online to seed it."
|
|
53
|
+
: " Check your internet connection and try again when the network is available.";
|
|
54
|
+
return new Error(`Unable to reach auction data at ${url}.${reason}${cacheHint}`);
|
|
55
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AuctionFilterOptions, AuctionResponse, AuctionServer } from "./types.js";
|
|
2
|
+
export declare const DEFAULT_AUCTION_CACHE_BASE_URL = "https://auction.hctl.dev";
|
|
3
|
+
export type AuctionSource = "cache" | "direct";
|
|
4
|
+
export interface AuctionFetchOptions {
|
|
5
|
+
cacheBaseUrl?: string;
|
|
6
|
+
source?: AuctionSource;
|
|
7
|
+
}
|
|
8
|
+
export interface AuctionFetchMetadata {
|
|
9
|
+
ageSeconds?: number;
|
|
10
|
+
fetchedAt: string;
|
|
11
|
+
source: string;
|
|
12
|
+
stale?: boolean;
|
|
13
|
+
updatedAt?: string;
|
|
14
|
+
url: string;
|
|
15
|
+
}
|
|
16
|
+
export interface AuctionFetchResult {
|
|
17
|
+
data: AuctionResponse;
|
|
18
|
+
metadata: AuctionFetchMetadata;
|
|
19
|
+
}
|
|
20
|
+
export declare function getAuctionDataUrl(currency?: "EUR" | "USD", options?: AuctionFetchOptions): string;
|
|
21
|
+
export declare function fetchAuctionData(currency?: "EUR" | "USD", options?: AuctionFetchOptions): Promise<AuctionFetchResult>;
|
|
22
|
+
export declare function fetchAuctionServers(currency?: "EUR" | "USD", options?: AuctionFetchOptions): Promise<AuctionResponse>;
|
|
23
|
+
export declare function filterAuctionServers(servers: AuctionServer[], filters: AuctionFilterOptions): AuctionServer[];
|
|
24
|
+
export declare function sortAuctionServers(servers: AuctionServer[], field: string, desc: boolean): AuctionServer[];
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Hetzner Auction API Client (public, no auth required)
|
|
3
|
+
// Endpoint: https://www.hetzner.com/_resources/app/data/app/live_data_sb_EUR.json
|
|
4
|
+
// ============================================================================
|
|
5
|
+
const HETZNER_AUCTION_BASE_URL = "https://www.hetzner.com/_resources/app/data/app";
|
|
6
|
+
export const DEFAULT_AUCTION_CACHE_BASE_URL = "https://auction.hctl.dev";
|
|
7
|
+
function trimTrailingSlash(url) {
|
|
8
|
+
return url.endsWith("/") ? url.slice(0, -1) : url;
|
|
9
|
+
}
|
|
10
|
+
function resolveAuctionSource(options) {
|
|
11
|
+
if (options.source !== undefined) {
|
|
12
|
+
return options.source;
|
|
13
|
+
}
|
|
14
|
+
const envSource = process.env.HCTL_AUCTION_SOURCE ?? process.env.HETZNER_CLI_AUCTION_SOURCE;
|
|
15
|
+
return envSource === "direct" ? "direct" : "cache";
|
|
16
|
+
}
|
|
17
|
+
function resolveAuctionBaseUrl(options) {
|
|
18
|
+
if (resolveAuctionSource(options) === "direct") {
|
|
19
|
+
return HETZNER_AUCTION_BASE_URL;
|
|
20
|
+
}
|
|
21
|
+
return (options.cacheBaseUrl ??
|
|
22
|
+
process.env.HCTL_AUCTION_CACHE_URL ??
|
|
23
|
+
process.env.HETZNER_CLI_AUCTION_CACHE_URL ??
|
|
24
|
+
DEFAULT_AUCTION_CACHE_BASE_URL);
|
|
25
|
+
}
|
|
26
|
+
export function getAuctionDataUrl(currency = "EUR", options = {}) {
|
|
27
|
+
return `${trimTrailingSlash(resolveAuctionBaseUrl(options))}/live_data_sb_${currency}.json`;
|
|
28
|
+
}
|
|
29
|
+
function parseOptionalNumber(value) {
|
|
30
|
+
if (value === null) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const parsed = Number(value);
|
|
34
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
35
|
+
}
|
|
36
|
+
function parseOptionalBoolean(value) {
|
|
37
|
+
if (value === null) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
if (value === "true") {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
if (value === "false") {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
function getResponseHeader(response, name) {
|
|
49
|
+
return response.headers?.get?.(name) ?? null;
|
|
50
|
+
}
|
|
51
|
+
export async function fetchAuctionData(currency = "EUR", options = {}) {
|
|
52
|
+
const url = getAuctionDataUrl(currency, options);
|
|
53
|
+
const response = await fetch(url);
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
throw new Error(`Failed to fetch auction data: HTTP ${response.status}`);
|
|
56
|
+
}
|
|
57
|
+
const data = (await response.json());
|
|
58
|
+
return {
|
|
59
|
+
data,
|
|
60
|
+
metadata: {
|
|
61
|
+
ageSeconds: parseOptionalNumber(getResponseHeader(response, "X-Hctl-Auction-Age-Seconds")),
|
|
62
|
+
fetchedAt: new Date().toISOString(),
|
|
63
|
+
source: getResponseHeader(response, "X-Hctl-Auction-Source") ??
|
|
64
|
+
resolveAuctionSource(options),
|
|
65
|
+
stale: parseOptionalBoolean(getResponseHeader(response, "X-Hctl-Auction-Stale")),
|
|
66
|
+
updatedAt: getResponseHeader(response, "X-Hctl-Auction-Updated-At") ?? undefined,
|
|
67
|
+
url,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export async function fetchAuctionServers(currency = "EUR", options = {}) {
|
|
72
|
+
const { data } = await fetchAuctionData(currency, options);
|
|
73
|
+
return data;
|
|
74
|
+
}
|
|
75
|
+
function getTotalDisk(s) {
|
|
76
|
+
return [
|
|
77
|
+
...s.serverDiskData.nvme,
|
|
78
|
+
...s.serverDiskData.sata,
|
|
79
|
+
...s.serverDiskData.hdd,
|
|
80
|
+
].reduce((sum, d) => sum + d, 0);
|
|
81
|
+
}
|
|
82
|
+
function matchesPriceFilters(s, filters) {
|
|
83
|
+
if (filters.minPrice !== undefined && s.price < filters.minPrice) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if (filters.maxPrice !== undefined && s.price > filters.maxPrice) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (filters.maxHourlyPrice !== undefined &&
|
|
90
|
+
s.hourly_price > filters.maxHourlyPrice) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
if (filters.maxSetupPrice !== undefined &&
|
|
94
|
+
s.setup_price > filters.maxSetupPrice) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
if (filters.fixedPrice !== undefined &&
|
|
98
|
+
s.fixed_price !== filters.fixedPrice) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
function matchesHardwareFilters(s, filters) {
|
|
104
|
+
if (filters.minRam !== undefined && s.ram_size < filters.minRam) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
if (filters.maxRam !== undefined && s.ram_size > filters.maxRam) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
if (filters.cpu && !s.cpu.toLowerCase().includes(filters.cpu.toLowerCase())) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
if (filters.minCpuCount !== undefined && s.cpu_count < filters.minCpuCount) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
if (filters.maxCpuCount !== undefined && s.cpu_count > filters.maxCpuCount) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
if (filters.minBandwidth !== undefined &&
|
|
120
|
+
s.bandwidth < filters.minBandwidth) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
function matchesDiskFilters(s, filters) {
|
|
126
|
+
if (filters.minDiskSize !== undefined || filters.maxDiskSize !== undefined) {
|
|
127
|
+
const total = getTotalDisk(s);
|
|
128
|
+
if (filters.minDiskSize !== undefined && total < filters.minDiskSize) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
if (filters.maxDiskSize !== undefined && total > filters.maxDiskSize) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (filters.minDiskCount !== undefined &&
|
|
136
|
+
s.hdd_count < filters.minDiskCount) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
if (filters.maxDiskCount !== undefined &&
|
|
140
|
+
s.hdd_count > filters.maxDiskCount) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
if (filters.diskType && s.serverDiskData[filters.diskType].length === 0) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
function matchesFeatureFilters(s, filters) {
|
|
149
|
+
if (filters.ecc !== undefined && s.is_ecc !== filters.ecc) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
if (filters.gpu !== undefined) {
|
|
153
|
+
const hasGpu = s.specials.includes("GPU");
|
|
154
|
+
if (filters.gpu !== hasGpu) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (filters.inic !== undefined) {
|
|
159
|
+
const hasInic = s.specials.includes("iNIC");
|
|
160
|
+
if (filters.inic !== hasInic) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (filters.highio !== undefined && s.is_highio !== filters.highio) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
if (filters.specials) {
|
|
168
|
+
const search = filters.specials.toLowerCase();
|
|
169
|
+
if (!s.specials.some((sp) => sp.toLowerCase().includes(search))) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
function matchesTextAndLocationFilters(s, filters) {
|
|
176
|
+
if (filters.datacenter &&
|
|
177
|
+
!s.datacenter.toLowerCase().includes(filters.datacenter.toLowerCase())) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
if (filters.text) {
|
|
181
|
+
const searchText = filters.text.toLowerCase();
|
|
182
|
+
const haystack = s.description.join(" ").toLowerCase();
|
|
183
|
+
if (!haystack.includes(searchText)) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
export function filterAuctionServers(servers, filters) {
|
|
190
|
+
return servers.filter((s) => {
|
|
191
|
+
if (!matchesPriceFilters(s, filters)) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
if (!matchesHardwareFilters(s, filters)) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
if (!matchesDiskFilters(s, filters)) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
if (!matchesFeatureFilters(s, filters)) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
return matchesTextAndLocationFilters(s, filters);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
export function sortAuctionServers(servers, field, desc) {
|
|
207
|
+
const sorted = [...servers].sort((a, b) => {
|
|
208
|
+
switch (field) {
|
|
209
|
+
case "price":
|
|
210
|
+
return a.price - b.price;
|
|
211
|
+
case "hourly":
|
|
212
|
+
return a.hourly_price - b.hourly_price;
|
|
213
|
+
case "setup":
|
|
214
|
+
return a.setup_price - b.setup_price;
|
|
215
|
+
case "ram":
|
|
216
|
+
return a.ram_size - b.ram_size;
|
|
217
|
+
case "disk":
|
|
218
|
+
return getTotalDisk(a) - getTotalDisk(b);
|
|
219
|
+
case "disk_count":
|
|
220
|
+
return a.hdd_count - b.hdd_count;
|
|
221
|
+
case "cpu":
|
|
222
|
+
return a.cpu.localeCompare(b.cpu);
|
|
223
|
+
case "cpu_count":
|
|
224
|
+
return a.cpu_count - b.cpu_count;
|
|
225
|
+
case "datacenter":
|
|
226
|
+
return a.datacenter.localeCompare(b.datacenter);
|
|
227
|
+
case "bandwidth":
|
|
228
|
+
return a.bandwidth - b.bandwidth;
|
|
229
|
+
case "next_reduce":
|
|
230
|
+
return a.next_reduce - b.next_reduce;
|
|
231
|
+
default:
|
|
232
|
+
return a.price - b.price;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
return desc ? sorted.reverse() : sorted;
|
|
236
|
+
}
|