hostinger-api-mcp 0.1.37 → 0.1.41
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/.github/workflows/build-release.yaml +1 -0
- package/README.md +179 -740
- package/build.js +7 -2
- package/package.json +11 -5
- package/src/core/runtime.js +2106 -0
- package/src/core/runtime.ts +2137 -0
- package/{server.ts → src/core/tools/all.js} +238 -2267
- package/{server.js → src/core/tools/all.ts} +248 -2236
- package/src/core/tools/billing.js +163 -0
- package/src/core/tools/billing.ts +174 -0
- package/src/core/tools/dns.js +343 -0
- package/src/core/tools/dns.ts +354 -0
- package/src/core/tools/domains.js +533 -0
- package/src/core/tools/domains.ts +544 -0
- package/src/core/tools/hosting.js +448 -0
- package/src/core/tools/hosting.ts +459 -0
- package/src/core/tools/reach.js +349 -0
- package/src/core/tools/reach.ts +360 -0
- package/src/core/tools/vps.js +1816 -0
- package/src/core/tools/vps.ts +1827 -0
- package/src/servers/all.js +6 -0
- package/src/servers/all.ts +6 -0
- package/src/servers/billing.js +6 -0
- package/src/servers/billing.ts +6 -0
- package/src/servers/dns.js +6 -0
- package/src/servers/dns.ts +6 -0
- package/src/servers/domains.js +6 -0
- package/src/servers/domains.ts +6 -0
- package/src/servers/hosting.js +6 -0
- package/src/servers/hosting.ts +6 -0
- package/src/servers/reach.js +6 -0
- package/src/servers/reach.ts +6 -0
- package/src/servers/vps.js +6 -0
- package/src/servers/vps.ts +6 -0
- package/tsconfig.json +3 -2
|
@@ -1,44 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
-
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
6
|
-
import minimist from 'minimist';
|
|
7
|
-
import cors from "cors";
|
|
8
|
-
import express from "express";
|
|
9
|
-
import {Request, Response} from "express";
|
|
10
|
-
import { config as dotenvConfig } from "dotenv";
|
|
11
|
-
import {
|
|
12
|
-
ListToolsRequestSchema,
|
|
13
|
-
CallToolRequestSchema,
|
|
14
|
-
Tool,
|
|
15
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
16
|
-
import axios,{ AxiosRequestConfig, AxiosError, AxiosResponse } from "axios";
|
|
17
|
-
import * as tus from "tus-js-client";
|
|
18
|
-
import fs from "fs";
|
|
19
|
-
import path from "path";
|
|
20
|
-
|
|
21
|
-
// Load environment variables
|
|
22
|
-
dotenvConfig();
|
|
23
|
-
|
|
24
|
-
// Define tool and security scheme types
|
|
25
|
-
interface OpenApiTool extends Tool {
|
|
26
|
-
method: string;
|
|
27
|
-
path: string;
|
|
28
|
-
security: any[];
|
|
29
|
-
custom?: boolean;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
interface SecurityScheme {
|
|
33
|
-
type: string;
|
|
34
|
-
description?: string;
|
|
35
|
-
name?: string;
|
|
36
|
-
in?: string;
|
|
37
|
-
scheme?: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Define tool schemas
|
|
41
|
-
const TOOLS: OpenApiTool[] = [
|
|
1
|
+
// Auto-generated tool list for group: all
|
|
2
|
+
export default [
|
|
42
3
|
{
|
|
43
4
|
"name": "hosting_importWordpressWebsite",
|
|
44
5
|
"topic": "hosting",
|
|
@@ -71,7 +32,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
71
32
|
"custom": true,
|
|
72
33
|
"templateFile": "import-wordpress.template.js",
|
|
73
34
|
"templateFileTS": "import-wordpress.template.ts",
|
|
74
|
-
"handlerMethod": "handleWordpressWebsiteImport"
|
|
35
|
+
"handlerMethod": "handleWordpressWebsiteImport",
|
|
36
|
+
"group": "hosting"
|
|
75
37
|
},
|
|
76
38
|
{
|
|
77
39
|
"name": "hosting_deployWordpressPlugin",
|
|
@@ -105,7 +67,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
105
67
|
"custom": true,
|
|
106
68
|
"templateFile": "deploy-wordpress-plugin.template.js",
|
|
107
69
|
"templateFileTS": "deploy-wordpress-plugin.template.ts",
|
|
108
|
-
"handlerMethod": "handleWordpressPluginDeploy"
|
|
70
|
+
"handlerMethod": "handleWordpressPluginDeploy",
|
|
71
|
+
"group": "hosting"
|
|
109
72
|
},
|
|
110
73
|
{
|
|
111
74
|
"name": "hosting_deployWordpressTheme",
|
|
@@ -143,7 +106,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
143
106
|
"custom": true,
|
|
144
107
|
"templateFile": "deploy-wordpress-theme.template.js",
|
|
145
108
|
"templateFileTS": "deploy-wordpress-theme.template.ts",
|
|
146
|
-
"handlerMethod": "handleWordpressThemeDeploy"
|
|
109
|
+
"handlerMethod": "handleWordpressThemeDeploy",
|
|
110
|
+
"group": "hosting"
|
|
147
111
|
},
|
|
148
112
|
{
|
|
149
113
|
"name": "hosting_deployJsApplication",
|
|
@@ -176,7 +140,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
176
140
|
"custom": true,
|
|
177
141
|
"templateFile": "deploy-javascript-app.template.js",
|
|
178
142
|
"templateFileTS": "deploy-javascript-app.template.ts",
|
|
179
|
-
"handlerMethod": "handleJavascriptApplicationDeploy"
|
|
143
|
+
"handlerMethod": "handleJavascriptApplicationDeploy",
|
|
144
|
+
"group": "hosting"
|
|
180
145
|
},
|
|
181
146
|
{
|
|
182
147
|
"name": "hosting_deployStaticWebsite",
|
|
@@ -209,7 +174,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
209
174
|
"custom": true,
|
|
210
175
|
"templateFile": "deploy-static-website.template.js",
|
|
211
176
|
"templateFileTS": "deploy-static-website.template.ts",
|
|
212
|
-
"handlerMethod": "handleStaticWebsiteDeploy"
|
|
177
|
+
"handlerMethod": "handleStaticWebsiteDeploy",
|
|
178
|
+
"group": "hosting"
|
|
213
179
|
},
|
|
214
180
|
{
|
|
215
181
|
"name": "hosting_listJsDeployments",
|
|
@@ -254,7 +220,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
254
220
|
"custom": true,
|
|
255
221
|
"templateFile": "list-javascript-deployments.template.js",
|
|
256
222
|
"templateFileTS": "list-javascript-deployments.template.ts",
|
|
257
|
-
"handlerMethod": "handleListJavascriptDeployments"
|
|
223
|
+
"handlerMethod": "handleListJavascriptDeployments",
|
|
224
|
+
"group": "hosting"
|
|
258
225
|
},
|
|
259
226
|
{
|
|
260
227
|
"name": "hosting_showJsDeploymentLogs",
|
|
@@ -287,7 +254,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
287
254
|
"custom": true,
|
|
288
255
|
"templateFile": "show-javascript-deployment-logs.template.js",
|
|
289
256
|
"templateFileTS": "show-javascript-deployment-logs.template.ts",
|
|
290
|
-
"handlerMethod": "handleShowJsDeploymentLogs"
|
|
257
|
+
"handlerMethod": "handleShowJsDeploymentLogs",
|
|
258
|
+
"group": "hosting"
|
|
291
259
|
},
|
|
292
260
|
{
|
|
293
261
|
"name": "billing_getCatalogItemListV1",
|
|
@@ -316,7 +284,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
316
284
|
{
|
|
317
285
|
"apiToken": []
|
|
318
286
|
}
|
|
319
|
-
]
|
|
287
|
+
],
|
|
288
|
+
"group": "billing"
|
|
320
289
|
},
|
|
321
290
|
{
|
|
322
291
|
"name": "billing_setDefaultPaymentMethodV1",
|
|
@@ -339,7 +308,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
339
308
|
{
|
|
340
309
|
"apiToken": []
|
|
341
310
|
}
|
|
342
|
-
]
|
|
311
|
+
],
|
|
312
|
+
"group": "billing"
|
|
343
313
|
},
|
|
344
314
|
{
|
|
345
315
|
"name": "billing_deletePaymentMethodV1",
|
|
@@ -362,7 +332,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
362
332
|
{
|
|
363
333
|
"apiToken": []
|
|
364
334
|
}
|
|
365
|
-
]
|
|
335
|
+
],
|
|
336
|
+
"group": "billing"
|
|
366
337
|
},
|
|
367
338
|
{
|
|
368
339
|
"name": "billing_getPaymentMethodListV1",
|
|
@@ -378,7 +349,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
378
349
|
{
|
|
379
350
|
"apiToken": []
|
|
380
351
|
}
|
|
381
|
-
]
|
|
352
|
+
],
|
|
353
|
+
"group": "billing"
|
|
382
354
|
},
|
|
383
355
|
{
|
|
384
356
|
"name": "billing_getSubscriptionListV1",
|
|
@@ -394,7 +366,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
394
366
|
{
|
|
395
367
|
"apiToken": []
|
|
396
368
|
}
|
|
397
|
-
]
|
|
369
|
+
],
|
|
370
|
+
"group": "billing"
|
|
398
371
|
},
|
|
399
372
|
{
|
|
400
373
|
"name": "billing_disableAutoRenewalV1",
|
|
@@ -417,7 +390,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
417
390
|
{
|
|
418
391
|
"apiToken": []
|
|
419
392
|
}
|
|
420
|
-
]
|
|
393
|
+
],
|
|
394
|
+
"group": "billing"
|
|
421
395
|
},
|
|
422
396
|
{
|
|
423
397
|
"name": "billing_enableAutoRenewalV1",
|
|
@@ -440,7 +414,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
440
414
|
{
|
|
441
415
|
"apiToken": []
|
|
442
416
|
}
|
|
443
|
-
]
|
|
417
|
+
],
|
|
418
|
+
"group": "billing"
|
|
444
419
|
},
|
|
445
420
|
{
|
|
446
421
|
"name": "DNS_getDNSSnapshotV1",
|
|
@@ -468,7 +443,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
468
443
|
{
|
|
469
444
|
"apiToken": []
|
|
470
445
|
}
|
|
471
|
-
]
|
|
446
|
+
],
|
|
447
|
+
"group": "dns"
|
|
472
448
|
},
|
|
473
449
|
{
|
|
474
450
|
"name": "DNS_getDNSSnapshotListV1",
|
|
@@ -491,7 +467,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
491
467
|
{
|
|
492
468
|
"apiToken": []
|
|
493
469
|
}
|
|
494
|
-
]
|
|
470
|
+
],
|
|
471
|
+
"group": "dns"
|
|
495
472
|
},
|
|
496
473
|
{
|
|
497
474
|
"name": "DNS_restoreDNSSnapshotV1",
|
|
@@ -519,7 +496,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
519
496
|
{
|
|
520
497
|
"apiToken": []
|
|
521
498
|
}
|
|
522
|
-
]
|
|
499
|
+
],
|
|
500
|
+
"group": "dns"
|
|
523
501
|
},
|
|
524
502
|
{
|
|
525
503
|
"name": "DNS_getDNSRecordsV1",
|
|
@@ -542,7 +520,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
542
520
|
{
|
|
543
521
|
"apiToken": []
|
|
544
522
|
}
|
|
545
|
-
]
|
|
523
|
+
],
|
|
524
|
+
"group": "dns"
|
|
546
525
|
},
|
|
547
526
|
{
|
|
548
527
|
"name": "DNS_updateDNSRecordsV1",
|
|
@@ -626,7 +605,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
626
605
|
{
|
|
627
606
|
"apiToken": []
|
|
628
607
|
}
|
|
629
|
-
]
|
|
608
|
+
],
|
|
609
|
+
"group": "dns"
|
|
630
610
|
},
|
|
631
611
|
{
|
|
632
612
|
"name": "DNS_deleteDNSRecordsV1",
|
|
@@ -649,7 +629,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
649
629
|
{
|
|
650
630
|
"apiToken": []
|
|
651
631
|
}
|
|
652
|
-
]
|
|
632
|
+
],
|
|
633
|
+
"group": "dns"
|
|
653
634
|
},
|
|
654
635
|
{
|
|
655
636
|
"name": "DNS_resetDNSRecordsV1",
|
|
@@ -688,7 +669,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
688
669
|
{
|
|
689
670
|
"apiToken": []
|
|
690
671
|
}
|
|
691
|
-
]
|
|
672
|
+
],
|
|
673
|
+
"group": "dns"
|
|
692
674
|
},
|
|
693
675
|
{
|
|
694
676
|
"name": "DNS_validateDNSRecordsV1",
|
|
@@ -772,7 +754,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
772
754
|
{
|
|
773
755
|
"apiToken": []
|
|
774
756
|
}
|
|
775
|
-
]
|
|
757
|
+
],
|
|
758
|
+
"group": "dns"
|
|
776
759
|
},
|
|
777
760
|
{
|
|
778
761
|
"name": "v2_getDomainVerificationsDIRECT",
|
|
@@ -788,7 +771,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
788
771
|
{
|
|
789
772
|
"apiToken": []
|
|
790
773
|
}
|
|
791
|
-
]
|
|
774
|
+
],
|
|
775
|
+
"group": "domains"
|
|
792
776
|
},
|
|
793
777
|
{
|
|
794
778
|
"name": "domains_checkDomainAvailabilityV1",
|
|
@@ -824,7 +808,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
824
808
|
{
|
|
825
809
|
"apiToken": []
|
|
826
810
|
}
|
|
827
|
-
]
|
|
811
|
+
],
|
|
812
|
+
"group": "domains"
|
|
828
813
|
},
|
|
829
814
|
{
|
|
830
815
|
"name": "domains_getDomainForwardingV1",
|
|
@@ -847,7 +832,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
847
832
|
{
|
|
848
833
|
"apiToken": []
|
|
849
834
|
}
|
|
850
|
-
]
|
|
835
|
+
],
|
|
836
|
+
"group": "domains"
|
|
851
837
|
},
|
|
852
838
|
{
|
|
853
839
|
"name": "domains_deleteDomainForwardingV1",
|
|
@@ -870,7 +856,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
870
856
|
{
|
|
871
857
|
"apiToken": []
|
|
872
858
|
}
|
|
873
|
-
]
|
|
859
|
+
],
|
|
860
|
+
"group": "domains"
|
|
874
861
|
},
|
|
875
862
|
{
|
|
876
863
|
"name": "domains_createDomainForwardingV1",
|
|
@@ -907,7 +894,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
907
894
|
{
|
|
908
895
|
"apiToken": []
|
|
909
896
|
}
|
|
910
|
-
]
|
|
897
|
+
],
|
|
898
|
+
"group": "domains"
|
|
911
899
|
},
|
|
912
900
|
{
|
|
913
901
|
"name": "domains_enableDomainLockV1",
|
|
@@ -930,7 +918,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
930
918
|
{
|
|
931
919
|
"apiToken": []
|
|
932
920
|
}
|
|
933
|
-
]
|
|
921
|
+
],
|
|
922
|
+
"group": "domains"
|
|
934
923
|
},
|
|
935
924
|
{
|
|
936
925
|
"name": "domains_disableDomainLockV1",
|
|
@@ -953,7 +942,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
953
942
|
{
|
|
954
943
|
"apiToken": []
|
|
955
944
|
}
|
|
956
|
-
]
|
|
945
|
+
],
|
|
946
|
+
"group": "domains"
|
|
957
947
|
},
|
|
958
948
|
{
|
|
959
949
|
"name": "domains_getDomainDetailsV1",
|
|
@@ -976,7 +966,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
976
966
|
{
|
|
977
967
|
"apiToken": []
|
|
978
968
|
}
|
|
979
|
-
]
|
|
969
|
+
],
|
|
970
|
+
"group": "domains"
|
|
980
971
|
},
|
|
981
972
|
{
|
|
982
973
|
"name": "domains_getDomainListV1",
|
|
@@ -992,7 +983,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
992
983
|
{
|
|
993
984
|
"apiToken": []
|
|
994
985
|
}
|
|
995
|
-
]
|
|
986
|
+
],
|
|
987
|
+
"group": "domains"
|
|
996
988
|
},
|
|
997
989
|
{
|
|
998
990
|
"name": "domains_purchaseNewDomainV1",
|
|
@@ -1059,7 +1051,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1059
1051
|
{
|
|
1060
1052
|
"apiToken": []
|
|
1061
1053
|
}
|
|
1062
|
-
]
|
|
1054
|
+
],
|
|
1055
|
+
"group": "domains"
|
|
1063
1056
|
},
|
|
1064
1057
|
{
|
|
1065
1058
|
"name": "domains_enablePrivacyProtectionV1",
|
|
@@ -1082,7 +1075,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1082
1075
|
{
|
|
1083
1076
|
"apiToken": []
|
|
1084
1077
|
}
|
|
1085
|
-
]
|
|
1078
|
+
],
|
|
1079
|
+
"group": "domains"
|
|
1086
1080
|
},
|
|
1087
1081
|
{
|
|
1088
1082
|
"name": "domains_disablePrivacyProtectionV1",
|
|
@@ -1105,7 +1099,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1105
1099
|
{
|
|
1106
1100
|
"apiToken": []
|
|
1107
1101
|
}
|
|
1108
|
-
]
|
|
1102
|
+
],
|
|
1103
|
+
"group": "domains"
|
|
1109
1104
|
},
|
|
1110
1105
|
{
|
|
1111
1106
|
"name": "domains_updateDomainNameserversV1",
|
|
@@ -1146,7 +1141,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1146
1141
|
{
|
|
1147
1142
|
"apiToken": []
|
|
1148
1143
|
}
|
|
1149
|
-
]
|
|
1144
|
+
],
|
|
1145
|
+
"group": "domains"
|
|
1150
1146
|
},
|
|
1151
1147
|
{
|
|
1152
1148
|
"name": "domains_getWHOISProfileV1",
|
|
@@ -1169,7 +1165,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1169
1165
|
{
|
|
1170
1166
|
"apiToken": []
|
|
1171
1167
|
}
|
|
1172
|
-
]
|
|
1168
|
+
],
|
|
1169
|
+
"group": "domains"
|
|
1173
1170
|
},
|
|
1174
1171
|
{
|
|
1175
1172
|
"name": "domains_deleteWHOISProfileV1",
|
|
@@ -1192,7 +1189,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1192
1189
|
{
|
|
1193
1190
|
"apiToken": []
|
|
1194
1191
|
}
|
|
1195
|
-
]
|
|
1192
|
+
],
|
|
1193
|
+
"group": "domains"
|
|
1196
1194
|
},
|
|
1197
1195
|
{
|
|
1198
1196
|
"name": "domains_getWHOISProfileListV1",
|
|
@@ -1213,7 +1211,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1213
1211
|
{
|
|
1214
1212
|
"apiToken": []
|
|
1215
1213
|
}
|
|
1216
|
-
]
|
|
1214
|
+
],
|
|
1215
|
+
"group": "domains"
|
|
1217
1216
|
},
|
|
1218
1217
|
{
|
|
1219
1218
|
"name": "domains_createWHOISProfileV1",
|
|
@@ -1261,7 +1260,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1261
1260
|
{
|
|
1262
1261
|
"apiToken": []
|
|
1263
1262
|
}
|
|
1264
|
-
]
|
|
1263
|
+
],
|
|
1264
|
+
"group": "domains"
|
|
1265
1265
|
},
|
|
1266
1266
|
{
|
|
1267
1267
|
"name": "domains_getWHOISProfileUsageV1",
|
|
@@ -1284,7 +1284,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1284
1284
|
{
|
|
1285
1285
|
"apiToken": []
|
|
1286
1286
|
}
|
|
1287
|
-
]
|
|
1287
|
+
],
|
|
1288
|
+
"group": "domains"
|
|
1288
1289
|
},
|
|
1289
1290
|
{
|
|
1290
1291
|
"name": "hosting_listAvailableDatacentersV1",
|
|
@@ -1307,7 +1308,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1307
1308
|
{
|
|
1308
1309
|
"apiToken": []
|
|
1309
1310
|
}
|
|
1310
|
-
]
|
|
1311
|
+
],
|
|
1312
|
+
"group": "hosting"
|
|
1311
1313
|
},
|
|
1312
1314
|
{
|
|
1313
1315
|
"name": "hosting_generateAFreeSubdomainV1",
|
|
@@ -1323,7 +1325,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1323
1325
|
{
|
|
1324
1326
|
"apiToken": []
|
|
1325
1327
|
}
|
|
1326
|
-
]
|
|
1328
|
+
],
|
|
1329
|
+
"group": "hosting"
|
|
1327
1330
|
},
|
|
1328
1331
|
{
|
|
1329
1332
|
"name": "hosting_verifyDomainOwnershipV1",
|
|
@@ -1346,7 +1349,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1346
1349
|
{
|
|
1347
1350
|
"apiToken": []
|
|
1348
1351
|
}
|
|
1349
|
-
]
|
|
1352
|
+
],
|
|
1353
|
+
"group": "hosting"
|
|
1350
1354
|
},
|
|
1351
1355
|
{
|
|
1352
1356
|
"name": "hosting_listOrdersV1",
|
|
@@ -1393,7 +1397,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1393
1397
|
{
|
|
1394
1398
|
"apiToken": []
|
|
1395
1399
|
}
|
|
1396
|
-
]
|
|
1400
|
+
],
|
|
1401
|
+
"group": "hosting"
|
|
1397
1402
|
},
|
|
1398
1403
|
{
|
|
1399
1404
|
"name": "hosting_listWebsitesV1",
|
|
@@ -1434,7 +1439,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1434
1439
|
{
|
|
1435
1440
|
"apiToken": []
|
|
1436
1441
|
}
|
|
1437
|
-
]
|
|
1442
|
+
],
|
|
1443
|
+
"group": "hosting"
|
|
1438
1444
|
},
|
|
1439
1445
|
{
|
|
1440
1446
|
"name": "hosting_createWebsiteV1",
|
|
@@ -1466,7 +1472,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1466
1472
|
{
|
|
1467
1473
|
"apiToken": []
|
|
1468
1474
|
}
|
|
1469
|
-
]
|
|
1475
|
+
],
|
|
1476
|
+
"group": "hosting"
|
|
1470
1477
|
},
|
|
1471
1478
|
{
|
|
1472
1479
|
"name": "reach_deleteAContactV1",
|
|
@@ -1489,7 +1496,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1489
1496
|
{
|
|
1490
1497
|
"apiToken": []
|
|
1491
1498
|
}
|
|
1492
|
-
]
|
|
1499
|
+
],
|
|
1500
|
+
"group": "reach"
|
|
1493
1501
|
},
|
|
1494
1502
|
{
|
|
1495
1503
|
"name": "reach_listContactGroupsV1",
|
|
@@ -1505,7 +1513,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1505
1513
|
{
|
|
1506
1514
|
"apiToken": []
|
|
1507
1515
|
}
|
|
1508
|
-
]
|
|
1516
|
+
],
|
|
1517
|
+
"group": "reach"
|
|
1509
1518
|
},
|
|
1510
1519
|
{
|
|
1511
1520
|
"name": "reach_listContactsV1",
|
|
@@ -1538,7 +1547,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1538
1547
|
{
|
|
1539
1548
|
"apiToken": []
|
|
1540
1549
|
}
|
|
1541
|
-
]
|
|
1550
|
+
],
|
|
1551
|
+
"group": "reach"
|
|
1542
1552
|
},
|
|
1543
1553
|
{
|
|
1544
1554
|
"name": "reach_createANewContactV1",
|
|
@@ -1573,7 +1583,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1573
1583
|
{
|
|
1574
1584
|
"apiToken": []
|
|
1575
1585
|
}
|
|
1576
|
-
]
|
|
1586
|
+
],
|
|
1587
|
+
"group": "reach"
|
|
1577
1588
|
},
|
|
1578
1589
|
{
|
|
1579
1590
|
"name": "reach_listSegmentsV1",
|
|
@@ -1589,7 +1600,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1589
1600
|
{
|
|
1590
1601
|
"apiToken": []
|
|
1591
1602
|
}
|
|
1592
|
-
]
|
|
1603
|
+
],
|
|
1604
|
+
"group": "reach"
|
|
1593
1605
|
},
|
|
1594
1606
|
{
|
|
1595
1607
|
"name": "reach_createANewContactSegmentV1",
|
|
@@ -1692,7 +1704,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1692
1704
|
{
|
|
1693
1705
|
"apiToken": []
|
|
1694
1706
|
}
|
|
1695
|
-
]
|
|
1707
|
+
],
|
|
1708
|
+
"group": "reach"
|
|
1696
1709
|
},
|
|
1697
1710
|
{
|
|
1698
1711
|
"name": "reach_listSegmentContactsV1",
|
|
@@ -1723,7 +1736,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1723
1736
|
{
|
|
1724
1737
|
"apiToken": []
|
|
1725
1738
|
}
|
|
1726
|
-
]
|
|
1739
|
+
],
|
|
1740
|
+
"group": "reach"
|
|
1727
1741
|
},
|
|
1728
1742
|
{
|
|
1729
1743
|
"name": "reach_getSegmentDetailsV1",
|
|
@@ -1746,7 +1760,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1746
1760
|
{
|
|
1747
1761
|
"apiToken": []
|
|
1748
1762
|
}
|
|
1749
|
-
]
|
|
1763
|
+
],
|
|
1764
|
+
"group": "reach"
|
|
1750
1765
|
},
|
|
1751
1766
|
{
|
|
1752
1767
|
"name": "reach_createNewContactsV1",
|
|
@@ -1786,7 +1801,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1786
1801
|
{
|
|
1787
1802
|
"apiToken": []
|
|
1788
1803
|
}
|
|
1789
|
-
]
|
|
1804
|
+
],
|
|
1805
|
+
"group": "reach"
|
|
1790
1806
|
},
|
|
1791
1807
|
{
|
|
1792
1808
|
"name": "reach_listProfilesV1",
|
|
@@ -1802,7 +1818,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1802
1818
|
{
|
|
1803
1819
|
"apiToken": []
|
|
1804
1820
|
}
|
|
1805
|
-
]
|
|
1821
|
+
],
|
|
1822
|
+
"group": "reach"
|
|
1806
1823
|
},
|
|
1807
1824
|
{
|
|
1808
1825
|
"name": "VPS_getDataCenterListV1",
|
|
@@ -1818,7 +1835,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1818
1835
|
{
|
|
1819
1836
|
"apiToken": []
|
|
1820
1837
|
}
|
|
1821
|
-
]
|
|
1838
|
+
],
|
|
1839
|
+
"group": "vps"
|
|
1822
1840
|
},
|
|
1823
1841
|
{
|
|
1824
1842
|
"name": "VPS_getProjectContainersV1",
|
|
@@ -1846,7 +1864,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1846
1864
|
{
|
|
1847
1865
|
"apiToken": []
|
|
1848
1866
|
}
|
|
1849
|
-
]
|
|
1867
|
+
],
|
|
1868
|
+
"group": "vps"
|
|
1850
1869
|
},
|
|
1851
1870
|
{
|
|
1852
1871
|
"name": "VPS_getProjectContentsV1",
|
|
@@ -1874,7 +1893,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1874
1893
|
{
|
|
1875
1894
|
"apiToken": []
|
|
1876
1895
|
}
|
|
1877
|
-
]
|
|
1896
|
+
],
|
|
1897
|
+
"group": "vps"
|
|
1878
1898
|
},
|
|
1879
1899
|
{
|
|
1880
1900
|
"name": "VPS_deleteProjectV1",
|
|
@@ -1902,7 +1922,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1902
1922
|
{
|
|
1903
1923
|
"apiToken": []
|
|
1904
1924
|
}
|
|
1905
|
-
]
|
|
1925
|
+
],
|
|
1926
|
+
"group": "vps"
|
|
1906
1927
|
},
|
|
1907
1928
|
{
|
|
1908
1929
|
"name": "VPS_getProjectListV1",
|
|
@@ -1925,7 +1946,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1925
1946
|
{
|
|
1926
1947
|
"apiToken": []
|
|
1927
1948
|
}
|
|
1928
|
-
]
|
|
1949
|
+
],
|
|
1950
|
+
"group": "vps"
|
|
1929
1951
|
},
|
|
1930
1952
|
{
|
|
1931
1953
|
"name": "VPS_createNewProjectV1",
|
|
@@ -1962,7 +1984,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1962
1984
|
{
|
|
1963
1985
|
"apiToken": []
|
|
1964
1986
|
}
|
|
1965
|
-
]
|
|
1987
|
+
],
|
|
1988
|
+
"group": "vps"
|
|
1966
1989
|
},
|
|
1967
1990
|
{
|
|
1968
1991
|
"name": "VPS_getProjectLogsV1",
|
|
@@ -1990,7 +2013,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1990
2013
|
{
|
|
1991
2014
|
"apiToken": []
|
|
1992
2015
|
}
|
|
1993
|
-
]
|
|
2016
|
+
],
|
|
2017
|
+
"group": "vps"
|
|
1994
2018
|
},
|
|
1995
2019
|
{
|
|
1996
2020
|
"name": "VPS_restartProjectV1",
|
|
@@ -2018,7 +2042,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2018
2042
|
{
|
|
2019
2043
|
"apiToken": []
|
|
2020
2044
|
}
|
|
2021
|
-
]
|
|
2045
|
+
],
|
|
2046
|
+
"group": "vps"
|
|
2022
2047
|
},
|
|
2023
2048
|
{
|
|
2024
2049
|
"name": "VPS_startProjectV1",
|
|
@@ -2046,7 +2071,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2046
2071
|
{
|
|
2047
2072
|
"apiToken": []
|
|
2048
2073
|
}
|
|
2049
|
-
]
|
|
2074
|
+
],
|
|
2075
|
+
"group": "vps"
|
|
2050
2076
|
},
|
|
2051
2077
|
{
|
|
2052
2078
|
"name": "VPS_stopProjectV1",
|
|
@@ -2074,7 +2100,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2074
2100
|
{
|
|
2075
2101
|
"apiToken": []
|
|
2076
2102
|
}
|
|
2077
|
-
]
|
|
2103
|
+
],
|
|
2104
|
+
"group": "vps"
|
|
2078
2105
|
},
|
|
2079
2106
|
{
|
|
2080
2107
|
"name": "VPS_updateProjectV1",
|
|
@@ -2102,7 +2129,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2102
2129
|
{
|
|
2103
2130
|
"apiToken": []
|
|
2104
2131
|
}
|
|
2105
|
-
]
|
|
2132
|
+
],
|
|
2133
|
+
"group": "vps"
|
|
2106
2134
|
},
|
|
2107
2135
|
{
|
|
2108
2136
|
"name": "VPS_activateFirewallV1",
|
|
@@ -2130,7 +2158,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2130
2158
|
{
|
|
2131
2159
|
"apiToken": []
|
|
2132
2160
|
}
|
|
2133
|
-
]
|
|
2161
|
+
],
|
|
2162
|
+
"group": "vps"
|
|
2134
2163
|
},
|
|
2135
2164
|
{
|
|
2136
2165
|
"name": "VPS_deactivateFirewallV1",
|
|
@@ -2158,7 +2187,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2158
2187
|
{
|
|
2159
2188
|
"apiToken": []
|
|
2160
2189
|
}
|
|
2161
|
-
]
|
|
2190
|
+
],
|
|
2191
|
+
"group": "vps"
|
|
2162
2192
|
},
|
|
2163
2193
|
{
|
|
2164
2194
|
"name": "VPS_getFirewallDetailsV1",
|
|
@@ -2181,7 +2211,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2181
2211
|
{
|
|
2182
2212
|
"apiToken": []
|
|
2183
2213
|
}
|
|
2184
|
-
]
|
|
2214
|
+
],
|
|
2215
|
+
"group": "vps"
|
|
2185
2216
|
},
|
|
2186
2217
|
{
|
|
2187
2218
|
"name": "VPS_deleteFirewallV1",
|
|
@@ -2204,7 +2235,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2204
2235
|
{
|
|
2205
2236
|
"apiToken": []
|
|
2206
2237
|
}
|
|
2207
|
-
]
|
|
2238
|
+
],
|
|
2239
|
+
"group": "vps"
|
|
2208
2240
|
},
|
|
2209
2241
|
{
|
|
2210
2242
|
"name": "VPS_getFirewallListV1",
|
|
@@ -2225,7 +2257,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2225
2257
|
{
|
|
2226
2258
|
"apiToken": []
|
|
2227
2259
|
}
|
|
2228
|
-
]
|
|
2260
|
+
],
|
|
2261
|
+
"group": "vps"
|
|
2229
2262
|
},
|
|
2230
2263
|
{
|
|
2231
2264
|
"name": "VPS_createNewFirewallV1",
|
|
@@ -2248,7 +2281,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2248
2281
|
{
|
|
2249
2282
|
"apiToken": []
|
|
2250
2283
|
}
|
|
2251
|
-
]
|
|
2284
|
+
],
|
|
2285
|
+
"group": "vps"
|
|
2252
2286
|
},
|
|
2253
2287
|
{
|
|
2254
2288
|
"name": "VPS_updateFirewallRuleV1",
|
|
@@ -2315,7 +2349,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2315
2349
|
{
|
|
2316
2350
|
"apiToken": []
|
|
2317
2351
|
}
|
|
2318
|
-
]
|
|
2352
|
+
],
|
|
2353
|
+
"group": "vps"
|
|
2319
2354
|
},
|
|
2320
2355
|
{
|
|
2321
2356
|
"name": "VPS_deleteFirewallRuleV1",
|
|
@@ -2343,7 +2378,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2343
2378
|
{
|
|
2344
2379
|
"apiToken": []
|
|
2345
2380
|
}
|
|
2346
|
-
]
|
|
2381
|
+
],
|
|
2382
|
+
"group": "vps"
|
|
2347
2383
|
},
|
|
2348
2384
|
{
|
|
2349
2385
|
"name": "VPS_createFirewallRuleV1",
|
|
@@ -2405,7 +2441,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2405
2441
|
{
|
|
2406
2442
|
"apiToken": []
|
|
2407
2443
|
}
|
|
2408
|
-
]
|
|
2444
|
+
],
|
|
2445
|
+
"group": "vps"
|
|
2409
2446
|
},
|
|
2410
2447
|
{
|
|
2411
2448
|
"name": "VPS_syncFirewallV1",
|
|
@@ -2433,7 +2470,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2433
2470
|
{
|
|
2434
2471
|
"apiToken": []
|
|
2435
2472
|
}
|
|
2436
|
-
]
|
|
2473
|
+
],
|
|
2474
|
+
"group": "vps"
|
|
2437
2475
|
},
|
|
2438
2476
|
{
|
|
2439
2477
|
"name": "VPS_getPostInstallScriptV1",
|
|
@@ -2456,7 +2494,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2456
2494
|
{
|
|
2457
2495
|
"apiToken": []
|
|
2458
2496
|
}
|
|
2459
|
-
]
|
|
2497
|
+
],
|
|
2498
|
+
"group": "vps"
|
|
2460
2499
|
},
|
|
2461
2500
|
{
|
|
2462
2501
|
"name": "VPS_updatePostInstallScriptV1",
|
|
@@ -2489,7 +2528,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2489
2528
|
{
|
|
2490
2529
|
"apiToken": []
|
|
2491
2530
|
}
|
|
2492
|
-
]
|
|
2531
|
+
],
|
|
2532
|
+
"group": "vps"
|
|
2493
2533
|
},
|
|
2494
2534
|
{
|
|
2495
2535
|
"name": "VPS_deletePostInstallScriptV1",
|
|
@@ -2512,7 +2552,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2512
2552
|
{
|
|
2513
2553
|
"apiToken": []
|
|
2514
2554
|
}
|
|
2515
|
-
]
|
|
2555
|
+
],
|
|
2556
|
+
"group": "vps"
|
|
2516
2557
|
},
|
|
2517
2558
|
{
|
|
2518
2559
|
"name": "VPS_getPostInstallScriptsV1",
|
|
@@ -2533,7 +2574,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2533
2574
|
{
|
|
2534
2575
|
"apiToken": []
|
|
2535
2576
|
}
|
|
2536
|
-
]
|
|
2577
|
+
],
|
|
2578
|
+
"group": "vps"
|
|
2537
2579
|
},
|
|
2538
2580
|
{
|
|
2539
2581
|
"name": "VPS_createPostInstallScriptV1",
|
|
@@ -2561,7 +2603,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2561
2603
|
{
|
|
2562
2604
|
"apiToken": []
|
|
2563
2605
|
}
|
|
2564
|
-
]
|
|
2606
|
+
],
|
|
2607
|
+
"group": "vps"
|
|
2565
2608
|
},
|
|
2566
2609
|
{
|
|
2567
2610
|
"name": "VPS_attachPublicKeyV1",
|
|
@@ -2593,7 +2636,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2593
2636
|
{
|
|
2594
2637
|
"apiToken": []
|
|
2595
2638
|
}
|
|
2596
|
-
]
|
|
2639
|
+
],
|
|
2640
|
+
"group": "vps"
|
|
2597
2641
|
},
|
|
2598
2642
|
{
|
|
2599
2643
|
"name": "VPS_deletePublicKeyV1",
|
|
@@ -2616,7 +2660,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2616
2660
|
{
|
|
2617
2661
|
"apiToken": []
|
|
2618
2662
|
}
|
|
2619
|
-
]
|
|
2663
|
+
],
|
|
2664
|
+
"group": "vps"
|
|
2620
2665
|
},
|
|
2621
2666
|
{
|
|
2622
2667
|
"name": "VPS_getPublicKeysV1",
|
|
@@ -2637,7 +2682,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2637
2682
|
{
|
|
2638
2683
|
"apiToken": []
|
|
2639
2684
|
}
|
|
2640
|
-
]
|
|
2685
|
+
],
|
|
2686
|
+
"group": "vps"
|
|
2641
2687
|
},
|
|
2642
2688
|
{
|
|
2643
2689
|
"name": "VPS_createPublicKeyV1",
|
|
@@ -2665,7 +2711,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2665
2711
|
{
|
|
2666
2712
|
"apiToken": []
|
|
2667
2713
|
}
|
|
2668
|
-
]
|
|
2714
|
+
],
|
|
2715
|
+
"group": "vps"
|
|
2669
2716
|
},
|
|
2670
2717
|
{
|
|
2671
2718
|
"name": "VPS_getTemplateDetailsV1",
|
|
@@ -2688,7 +2735,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2688
2735
|
{
|
|
2689
2736
|
"apiToken": []
|
|
2690
2737
|
}
|
|
2691
|
-
]
|
|
2738
|
+
],
|
|
2739
|
+
"group": "vps"
|
|
2692
2740
|
},
|
|
2693
2741
|
{
|
|
2694
2742
|
"name": "VPS_getTemplatesV1",
|
|
@@ -2704,7 +2752,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2704
2752
|
{
|
|
2705
2753
|
"apiToken": []
|
|
2706
2754
|
}
|
|
2707
|
-
]
|
|
2755
|
+
],
|
|
2756
|
+
"group": "vps"
|
|
2708
2757
|
},
|
|
2709
2758
|
{
|
|
2710
2759
|
"name": "VPS_getActionDetailsV1",
|
|
@@ -2732,7 +2781,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2732
2781
|
{
|
|
2733
2782
|
"apiToken": []
|
|
2734
2783
|
}
|
|
2735
|
-
]
|
|
2784
|
+
],
|
|
2785
|
+
"group": "vps"
|
|
2736
2786
|
},
|
|
2737
2787
|
{
|
|
2738
2788
|
"name": "VPS_getActionsV1",
|
|
@@ -2759,7 +2809,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2759
2809
|
{
|
|
2760
2810
|
"apiToken": []
|
|
2761
2811
|
}
|
|
2762
|
-
]
|
|
2812
|
+
],
|
|
2813
|
+
"group": "vps"
|
|
2763
2814
|
},
|
|
2764
2815
|
{
|
|
2765
2816
|
"name": "VPS_getAttachedPublicKeysV1",
|
|
@@ -2786,7 +2837,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2786
2837
|
{
|
|
2787
2838
|
"apiToken": []
|
|
2788
2839
|
}
|
|
2789
|
-
]
|
|
2840
|
+
],
|
|
2841
|
+
"group": "vps"
|
|
2790
2842
|
},
|
|
2791
2843
|
{
|
|
2792
2844
|
"name": "VPS_getBackupsV1",
|
|
@@ -2813,7 +2865,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2813
2865
|
{
|
|
2814
2866
|
"apiToken": []
|
|
2815
2867
|
}
|
|
2816
|
-
]
|
|
2868
|
+
],
|
|
2869
|
+
"group": "vps"
|
|
2817
2870
|
},
|
|
2818
2871
|
{
|
|
2819
2872
|
"name": "VPS_restoreBackupV1",
|
|
@@ -2841,7 +2894,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2841
2894
|
{
|
|
2842
2895
|
"apiToken": []
|
|
2843
2896
|
}
|
|
2844
|
-
]
|
|
2897
|
+
],
|
|
2898
|
+
"group": "vps"
|
|
2845
2899
|
},
|
|
2846
2900
|
{
|
|
2847
2901
|
"name": "VPS_setHostnameV1",
|
|
@@ -2869,7 +2923,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2869
2923
|
{
|
|
2870
2924
|
"apiToken": []
|
|
2871
2925
|
}
|
|
2872
|
-
]
|
|
2926
|
+
],
|
|
2927
|
+
"group": "vps"
|
|
2873
2928
|
},
|
|
2874
2929
|
{
|
|
2875
2930
|
"name": "VPS_resetHostnameV1",
|
|
@@ -2892,7 +2947,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2892
2947
|
{
|
|
2893
2948
|
"apiToken": []
|
|
2894
2949
|
}
|
|
2895
|
-
]
|
|
2950
|
+
],
|
|
2951
|
+
"group": "vps"
|
|
2896
2952
|
},
|
|
2897
2953
|
{
|
|
2898
2954
|
"name": "VPS_getVirtualMachineDetailsV1",
|
|
@@ -2915,7 +2971,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2915
2971
|
{
|
|
2916
2972
|
"apiToken": []
|
|
2917
2973
|
}
|
|
2918
|
-
]
|
|
2974
|
+
],
|
|
2975
|
+
"group": "vps"
|
|
2919
2976
|
},
|
|
2920
2977
|
{
|
|
2921
2978
|
"name": "VPS_getVirtualMachinesV1",
|
|
@@ -2931,7 +2988,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2931
2988
|
{
|
|
2932
2989
|
"apiToken": []
|
|
2933
2990
|
}
|
|
2934
|
-
]
|
|
2991
|
+
],
|
|
2992
|
+
"group": "vps"
|
|
2935
2993
|
},
|
|
2936
2994
|
{
|
|
2937
2995
|
"name": "VPS_purchaseNewVirtualMachineV1",
|
|
@@ -2971,7 +3029,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2971
3029
|
{
|
|
2972
3030
|
"apiToken": []
|
|
2973
3031
|
}
|
|
2974
|
-
]
|
|
3032
|
+
],
|
|
3033
|
+
"group": "vps"
|
|
2975
3034
|
},
|
|
2976
3035
|
{
|
|
2977
3036
|
"name": "VPS_getScanMetricsV1",
|
|
@@ -2994,7 +3053,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2994
3053
|
{
|
|
2995
3054
|
"apiToken": []
|
|
2996
3055
|
}
|
|
2997
|
-
]
|
|
3056
|
+
],
|
|
3057
|
+
"group": "vps"
|
|
2998
3058
|
},
|
|
2999
3059
|
{
|
|
3000
3060
|
"name": "VPS_installMonarxV1",
|
|
@@ -3017,7 +3077,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3017
3077
|
{
|
|
3018
3078
|
"apiToken": []
|
|
3019
3079
|
}
|
|
3020
|
-
]
|
|
3080
|
+
],
|
|
3081
|
+
"group": "vps"
|
|
3021
3082
|
},
|
|
3022
3083
|
{
|
|
3023
3084
|
"name": "VPS_uninstallMonarxV1",
|
|
@@ -3040,7 +3101,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3040
3101
|
{
|
|
3041
3102
|
"apiToken": []
|
|
3042
3103
|
}
|
|
3043
|
-
]
|
|
3104
|
+
],
|
|
3105
|
+
"group": "vps"
|
|
3044
3106
|
},
|
|
3045
3107
|
{
|
|
3046
3108
|
"name": "VPS_getMetricsV1",
|
|
@@ -3073,7 +3135,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3073
3135
|
{
|
|
3074
3136
|
"apiToken": []
|
|
3075
3137
|
}
|
|
3076
|
-
]
|
|
3138
|
+
],
|
|
3139
|
+
"group": "vps"
|
|
3077
3140
|
},
|
|
3078
3141
|
{
|
|
3079
3142
|
"name": "VPS_setNameserversV1",
|
|
@@ -3109,7 +3172,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3109
3172
|
{
|
|
3110
3173
|
"apiToken": []
|
|
3111
3174
|
}
|
|
3112
|
-
]
|
|
3175
|
+
],
|
|
3176
|
+
"group": "vps"
|
|
3113
3177
|
},
|
|
3114
3178
|
{
|
|
3115
3179
|
"name": "VPS_createPTRRecordV1",
|
|
@@ -3142,7 +3206,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3142
3206
|
{
|
|
3143
3207
|
"apiToken": []
|
|
3144
3208
|
}
|
|
3145
|
-
]
|
|
3209
|
+
],
|
|
3210
|
+
"group": "vps"
|
|
3146
3211
|
},
|
|
3147
3212
|
{
|
|
3148
3213
|
"name": "VPS_deletePTRRecordV1",
|
|
@@ -3170,7 +3235,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3170
3235
|
{
|
|
3171
3236
|
"apiToken": []
|
|
3172
3237
|
}
|
|
3173
|
-
]
|
|
3238
|
+
],
|
|
3239
|
+
"group": "vps"
|
|
3174
3240
|
},
|
|
3175
3241
|
{
|
|
3176
3242
|
"name": "VPS_setPanelPasswordV1",
|
|
@@ -3198,7 +3264,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3198
3264
|
{
|
|
3199
3265
|
"apiToken": []
|
|
3200
3266
|
}
|
|
3201
|
-
]
|
|
3267
|
+
],
|
|
3268
|
+
"group": "vps"
|
|
3202
3269
|
},
|
|
3203
3270
|
{
|
|
3204
3271
|
"name": "VPS_startRecoveryModeV1",
|
|
@@ -3226,7 +3293,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3226
3293
|
{
|
|
3227
3294
|
"apiToken": []
|
|
3228
3295
|
}
|
|
3229
|
-
]
|
|
3296
|
+
],
|
|
3297
|
+
"group": "vps"
|
|
3230
3298
|
},
|
|
3231
3299
|
{
|
|
3232
3300
|
"name": "VPS_stopRecoveryModeV1",
|
|
@@ -3249,7 +3317,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3249
3317
|
{
|
|
3250
3318
|
"apiToken": []
|
|
3251
3319
|
}
|
|
3252
|
-
]
|
|
3320
|
+
],
|
|
3321
|
+
"group": "vps"
|
|
3253
3322
|
},
|
|
3254
3323
|
{
|
|
3255
3324
|
"name": "VPS_recreateVirtualMachineV1",
|
|
@@ -3289,7 +3358,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3289
3358
|
{
|
|
3290
3359
|
"apiToken": []
|
|
3291
3360
|
}
|
|
3292
|
-
]
|
|
3361
|
+
],
|
|
3362
|
+
"group": "vps"
|
|
3293
3363
|
},
|
|
3294
3364
|
{
|
|
3295
3365
|
"name": "VPS_restartVirtualMachineV1",
|
|
@@ -3312,7 +3382,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3312
3382
|
{
|
|
3313
3383
|
"apiToken": []
|
|
3314
3384
|
}
|
|
3315
|
-
]
|
|
3385
|
+
],
|
|
3386
|
+
"group": "vps"
|
|
3316
3387
|
},
|
|
3317
3388
|
{
|
|
3318
3389
|
"name": "VPS_setRootPasswordV1",
|
|
@@ -3340,7 +3411,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3340
3411
|
{
|
|
3341
3412
|
"apiToken": []
|
|
3342
3413
|
}
|
|
3343
|
-
]
|
|
3414
|
+
],
|
|
3415
|
+
"group": "vps"
|
|
3344
3416
|
},
|
|
3345
3417
|
{
|
|
3346
3418
|
"name": "VPS_setupPurchasedVirtualMachineV1",
|
|
@@ -3415,7 +3487,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3415
3487
|
{
|
|
3416
3488
|
"apiToken": []
|
|
3417
3489
|
}
|
|
3418
|
-
]
|
|
3490
|
+
],
|
|
3491
|
+
"group": "vps"
|
|
3419
3492
|
},
|
|
3420
3493
|
{
|
|
3421
3494
|
"name": "VPS_getSnapshotV1",
|
|
@@ -3438,7 +3511,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3438
3511
|
{
|
|
3439
3512
|
"apiToken": []
|
|
3440
3513
|
}
|
|
3441
|
-
]
|
|
3514
|
+
],
|
|
3515
|
+
"group": "vps"
|
|
3442
3516
|
},
|
|
3443
3517
|
{
|
|
3444
3518
|
"name": "VPS_createSnapshotV1",
|
|
@@ -3461,7 +3535,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3461
3535
|
{
|
|
3462
3536
|
"apiToken": []
|
|
3463
3537
|
}
|
|
3464
|
-
]
|
|
3538
|
+
],
|
|
3539
|
+
"group": "vps"
|
|
3465
3540
|
},
|
|
3466
3541
|
{
|
|
3467
3542
|
"name": "VPS_deleteSnapshotV1",
|
|
@@ -3484,7 +3559,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3484
3559
|
{
|
|
3485
3560
|
"apiToken": []
|
|
3486
3561
|
}
|
|
3487
|
-
]
|
|
3562
|
+
],
|
|
3563
|
+
"group": "vps"
|
|
3488
3564
|
},
|
|
3489
3565
|
{
|
|
3490
3566
|
"name": "VPS_restoreSnapshotV1",
|
|
@@ -3507,7 +3583,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3507
3583
|
{
|
|
3508
3584
|
"apiToken": []
|
|
3509
3585
|
}
|
|
3510
|
-
]
|
|
3586
|
+
],
|
|
3587
|
+
"group": "vps"
|
|
3511
3588
|
},
|
|
3512
3589
|
{
|
|
3513
3590
|
"name": "VPS_startVirtualMachineV1",
|
|
@@ -3530,7 +3607,8 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3530
3607
|
{
|
|
3531
3608
|
"apiToken": []
|
|
3532
3609
|
}
|
|
3533
|
-
]
|
|
3610
|
+
],
|
|
3611
|
+
"group": "vps"
|
|
3534
3612
|
},
|
|
3535
3613
|
{
|
|
3536
3614
|
"name": "VPS_stopVirtualMachineV1",
|
|
@@ -3553,2114 +3631,7 @@ const TOOLS: OpenApiTool[] = [
|
|
|
3553
3631
|
{
|
|
3554
3632
|
"apiToken": []
|
|
3555
3633
|
}
|
|
3556
|
-
]
|
|
3634
|
+
],
|
|
3635
|
+
"group": "vps"
|
|
3557
3636
|
}
|
|
3558
3637
|
];
|
|
3559
|
-
const SECURITY_SCHEMES: Record<string, SecurityScheme> = {
|
|
3560
|
-
"apiToken": {
|
|
3561
|
-
"type": "http",
|
|
3562
|
-
"description": "API Token authentication",
|
|
3563
|
-
"scheme": "bearer"
|
|
3564
|
-
}
|
|
3565
|
-
};
|
|
3566
|
-
|
|
3567
|
-
/**
|
|
3568
|
-
* MCP Server for Hostinger API
|
|
3569
|
-
* Generated from OpenAPI spec version 0.11.7
|
|
3570
|
-
*/
|
|
3571
|
-
class MCPServer {
|
|
3572
|
-
private server: Server;
|
|
3573
|
-
private tools: Map<string, Tool> = new Map();
|
|
3574
|
-
private debug: boolean;
|
|
3575
|
-
private baseUrl: string;
|
|
3576
|
-
private headers: Record<string, string>;
|
|
3577
|
-
|
|
3578
|
-
constructor() {
|
|
3579
|
-
// Initialize properties
|
|
3580
|
-
this.debug = process.env.DEBUG === "true";
|
|
3581
|
-
this.baseUrl = process.env.API_BASE_URL || "https://developers.hostinger.com";
|
|
3582
|
-
this.headers = this.parseHeaders(process.env.API_HEADERS || "");
|
|
3583
|
-
|
|
3584
|
-
// Initialize tools map - do this before creating server
|
|
3585
|
-
this.initializeTools();
|
|
3586
|
-
|
|
3587
|
-
// Create MCP server with correct capabilities
|
|
3588
|
-
this.server = new Server(
|
|
3589
|
-
{
|
|
3590
|
-
name: "hostinger-api-mcp",
|
|
3591
|
-
version: "0.1.37",
|
|
3592
|
-
},
|
|
3593
|
-
{
|
|
3594
|
-
capabilities: {
|
|
3595
|
-
tools: {}, // Enable tools capability
|
|
3596
|
-
},
|
|
3597
|
-
}
|
|
3598
|
-
);
|
|
3599
|
-
|
|
3600
|
-
// Set up request handlers - don't log here
|
|
3601
|
-
this.setupHandlers();
|
|
3602
|
-
}
|
|
3603
|
-
|
|
3604
|
-
/**
|
|
3605
|
-
* Parse headers from string
|
|
3606
|
-
*/
|
|
3607
|
-
private parseHeaders(headerStr: string): Record<string, string> {
|
|
3608
|
-
const headers: Record<string, string> = {};
|
|
3609
|
-
if (headerStr) {
|
|
3610
|
-
headerStr.split(",").forEach((header) => {
|
|
3611
|
-
const [key, value] = header.split(":");
|
|
3612
|
-
if (key && value) headers[key.trim()] = value.trim();
|
|
3613
|
-
});
|
|
3614
|
-
}
|
|
3615
|
-
|
|
3616
|
-
headers['User-Agent'] = 'hostinger-mcp-server/0.1.37';
|
|
3617
|
-
|
|
3618
|
-
return headers;
|
|
3619
|
-
}
|
|
3620
|
-
|
|
3621
|
-
/**
|
|
3622
|
-
* Initialize tools map from OpenAPI spec
|
|
3623
|
-
* This runs before the server is connected, so don't log here
|
|
3624
|
-
*/
|
|
3625
|
-
private initializeTools(): void {
|
|
3626
|
-
// Initialize each tool in the tools map
|
|
3627
|
-
for (const tool of TOOLS) {
|
|
3628
|
-
this.tools.set(tool.name, {
|
|
3629
|
-
name: tool.name,
|
|
3630
|
-
description: tool.description,
|
|
3631
|
-
inputSchema: tool.inputSchema,
|
|
3632
|
-
// Don't include security at the tool level
|
|
3633
|
-
});
|
|
3634
|
-
}
|
|
3635
|
-
|
|
3636
|
-
// Don't log here, we're not connected yet
|
|
3637
|
-
console.error(`Initialized ${this.tools.size} tools`);
|
|
3638
|
-
}
|
|
3639
|
-
|
|
3640
|
-
/**
|
|
3641
|
-
* Set up request handlers
|
|
3642
|
-
*/
|
|
3643
|
-
private setupHandlers(): void {
|
|
3644
|
-
// Handle tool listing requests
|
|
3645
|
-
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
3646
|
-
this.log('debug', "Handling ListTools request");
|
|
3647
|
-
// Return tools in the format expected by MCP SDK
|
|
3648
|
-
return {
|
|
3649
|
-
tools: Array.from(this.tools.entries()).map(([id, tool]) => ({
|
|
3650
|
-
id,
|
|
3651
|
-
...tool,
|
|
3652
|
-
})),
|
|
3653
|
-
};
|
|
3654
|
-
});
|
|
3655
|
-
|
|
3656
|
-
// Handle tool execution requests
|
|
3657
|
-
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
3658
|
-
const { id, name, arguments: params } = request.params;
|
|
3659
|
-
this.log('debug', "Handling CallTool request", { id, name, params });
|
|
3660
|
-
|
|
3661
|
-
let toolName: string | undefined;
|
|
3662
|
-
let toolDetails: OpenApiTool | undefined;
|
|
3663
|
-
|
|
3664
|
-
// Find the requested tool
|
|
3665
|
-
for (const [tid, tool] of this.tools.entries()) {
|
|
3666
|
-
if (tool.name === name) {
|
|
3667
|
-
toolName = name;
|
|
3668
|
-
break;
|
|
3669
|
-
}
|
|
3670
|
-
}
|
|
3671
|
-
|
|
3672
|
-
if (!toolName) {
|
|
3673
|
-
throw new Error(`Tool not found: ${name}`)
|
|
3674
|
-
}
|
|
3675
|
-
|
|
3676
|
-
toolDetails = TOOLS.find(t => t.name === toolName);
|
|
3677
|
-
if (!toolDetails) {
|
|
3678
|
-
throw new Error(`Tool details not found for ID: ${toolName}`);
|
|
3679
|
-
}
|
|
3680
|
-
|
|
3681
|
-
try {
|
|
3682
|
-
this.log('info', `Executing tool: ${toolName}`);
|
|
3683
|
-
|
|
3684
|
-
let result: any;
|
|
3685
|
-
|
|
3686
|
-
if (toolDetails.custom) {
|
|
3687
|
-
result = await this.executeCustomTool(toolDetails, params || {});
|
|
3688
|
-
} else {
|
|
3689
|
-
result = await this.executeApiCall(toolDetails, params || {});
|
|
3690
|
-
}
|
|
3691
|
-
|
|
3692
|
-
// Return the result in correct MCP format
|
|
3693
|
-
return {
|
|
3694
|
-
content: [
|
|
3695
|
-
{
|
|
3696
|
-
type: "text",
|
|
3697
|
-
text: JSON.stringify(result)
|
|
3698
|
-
}
|
|
3699
|
-
]
|
|
3700
|
-
};
|
|
3701
|
-
|
|
3702
|
-
} catch (error) {
|
|
3703
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3704
|
-
this.log('error', `Error executing tool ${toolName}: ${errorMessage}`);
|
|
3705
|
-
|
|
3706
|
-
throw error;
|
|
3707
|
-
}
|
|
3708
|
-
});
|
|
3709
|
-
}
|
|
3710
|
-
|
|
3711
|
-
private async executeCustomTool(tool: OpenApiTool, params: Record<string, any>): Promise<any> {
|
|
3712
|
-
switch (tool.name) {
|
|
3713
|
-
case 'hosting_importWordpressWebsite':
|
|
3714
|
-
return await this.handleWordpressWebsiteImport(params);
|
|
3715
|
-
case 'hosting_deployWordpressPlugin':
|
|
3716
|
-
return await this.handleWordpressPluginDeploy(params);
|
|
3717
|
-
case 'hosting_deployWordpressTheme':
|
|
3718
|
-
return await this.handleWordpressThemeDeploy(params);
|
|
3719
|
-
case 'hosting_deployJsApplication':
|
|
3720
|
-
return await this.handleJavascriptApplicationDeploy(params);
|
|
3721
|
-
case 'hosting_deployStaticWebsite':
|
|
3722
|
-
return await this.handleStaticWebsiteDeploy(params);
|
|
3723
|
-
case 'hosting_listJsDeployments':
|
|
3724
|
-
return await this.handleListJavascriptDeployments(params);
|
|
3725
|
-
case 'hosting_showJsDeploymentLogs':
|
|
3726
|
-
return await this.handleShowJsDeploymentLogs(params);
|
|
3727
|
-
default:
|
|
3728
|
-
throw new Error(`Unknown custom tool: ${tool.name}`);
|
|
3729
|
-
}
|
|
3730
|
-
}
|
|
3731
|
-
|
|
3732
|
-
private normalizePath(pathString: string): string {
|
|
3733
|
-
return pathString.replace(/\\/g, '/');
|
|
3734
|
-
}
|
|
3735
|
-
|
|
3736
|
-
private async resolveUsername(domain: string): Promise<string> {
|
|
3737
|
-
const baseUrl = this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`;
|
|
3738
|
-
const url = new URL(`api/hosting/v1/websites?domain=${encodeURIComponent(domain)}`, baseUrl).toString();
|
|
3739
|
-
|
|
3740
|
-
try {
|
|
3741
|
-
const bearerToken = process.env['API_TOKEN'] || process.env['APITOKEN'];
|
|
3742
|
-
if (!bearerToken) {
|
|
3743
|
-
throw new Error('API_TOKEN environment variable not found');
|
|
3744
|
-
}
|
|
3745
|
-
|
|
3746
|
-
const config: AxiosRequestConfig = {
|
|
3747
|
-
method: 'get',
|
|
3748
|
-
url,
|
|
3749
|
-
headers: {
|
|
3750
|
-
...this.headers,
|
|
3751
|
-
'Authorization': `Bearer ${bearerToken}`
|
|
3752
|
-
},
|
|
3753
|
-
timeout: 60000, // 60s
|
|
3754
|
-
validateStatus: function (status: number): boolean {
|
|
3755
|
-
return status < 500;
|
|
3756
|
-
}
|
|
3757
|
-
};
|
|
3758
|
-
|
|
3759
|
-
const response = await axios(config);
|
|
3760
|
-
|
|
3761
|
-
if (response.status !== 200) {
|
|
3762
|
-
throw new Error(`API returned status ${response.status}: ${JSON.stringify(response.data)}`);
|
|
3763
|
-
}
|
|
3764
|
-
|
|
3765
|
-
const websites = response.data?.data;
|
|
3766
|
-
if (!websites || websites.length === 0) {
|
|
3767
|
-
throw new Error(`No website found for domain: ${domain}`);
|
|
3768
|
-
}
|
|
3769
|
-
|
|
3770
|
-
const username = websites[0].username;
|
|
3771
|
-
if (!username) {
|
|
3772
|
-
throw new Error(`Username not found in website data for domain: ${domain}`);
|
|
3773
|
-
}
|
|
3774
|
-
|
|
3775
|
-
this.log('info', `Resolved username: ${username} for domain: ${domain}`);
|
|
3776
|
-
return username;
|
|
3777
|
-
|
|
3778
|
-
} catch (error) {
|
|
3779
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3780
|
-
this.log('error', `Failed to resolve username for domain ${domain}: ${errorMessage}`);
|
|
3781
|
-
|
|
3782
|
-
if (axios.isAxiosError(error)) {
|
|
3783
|
-
const responseData = error.response?.data;
|
|
3784
|
-
const responseStatus = error.response?.status;
|
|
3785
|
-
this.log('error', 'API Error Details:', {
|
|
3786
|
-
status: responseStatus,
|
|
3787
|
-
data: typeof responseData === 'object' ? JSON.stringify(responseData) : responseData
|
|
3788
|
-
});
|
|
3789
|
-
}
|
|
3790
|
-
|
|
3791
|
-
throw error;
|
|
3792
|
-
}
|
|
3793
|
-
}
|
|
3794
|
-
|
|
3795
|
-
private async fetchUploadCredentials(username: string, domain: string): Promise<{ uploadUrl: string; authRestToken: string; authToken: string }> {
|
|
3796
|
-
const baseUrl = this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`;
|
|
3797
|
-
const url = new URL('api/hosting/v1/files/upload-urls', baseUrl).toString();
|
|
3798
|
-
|
|
3799
|
-
try {
|
|
3800
|
-
const bearerToken = process.env['API_TOKEN'] || process.env['APITOKEN'];
|
|
3801
|
-
if (!bearerToken) {
|
|
3802
|
-
throw new Error('API_TOKEN environment variable not found');
|
|
3803
|
-
}
|
|
3804
|
-
|
|
3805
|
-
const config: AxiosRequestConfig = {
|
|
3806
|
-
method: 'post',
|
|
3807
|
-
url,
|
|
3808
|
-
headers: {
|
|
3809
|
-
...this.headers,
|
|
3810
|
-
'Authorization': `Bearer ${bearerToken}`,
|
|
3811
|
-
'Content-Type': 'application/json'
|
|
3812
|
-
},
|
|
3813
|
-
data: {
|
|
3814
|
-
username,
|
|
3815
|
-
domain
|
|
3816
|
-
},
|
|
3817
|
-
timeout: 60000, // 60s
|
|
3818
|
-
validateStatus: function (status: number): boolean {
|
|
3819
|
-
return status < 500;
|
|
3820
|
-
}
|
|
3821
|
-
};
|
|
3822
|
-
|
|
3823
|
-
const response = await axios(config);
|
|
3824
|
-
|
|
3825
|
-
if (response.status !== 200) {
|
|
3826
|
-
throw new Error(`API returned status ${response.status}: ${JSON.stringify(response.data)}`);
|
|
3827
|
-
}
|
|
3828
|
-
|
|
3829
|
-
return response.data;
|
|
3830
|
-
|
|
3831
|
-
} catch (error) {
|
|
3832
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3833
|
-
this.log('error', `Failed to fetch upload credentials: ${errorMessage}`);
|
|
3834
|
-
|
|
3835
|
-
if (axios.isAxiosError(error)) {
|
|
3836
|
-
const responseData = error.response?.data;
|
|
3837
|
-
const responseStatus = error.response?.status;
|
|
3838
|
-
this.log('error', 'API Error Details:', {
|
|
3839
|
-
status: responseStatus,
|
|
3840
|
-
data: typeof responseData === 'object' ? JSON.stringify(responseData) : responseData
|
|
3841
|
-
});
|
|
3842
|
-
}
|
|
3843
|
-
|
|
3844
|
-
throw error;
|
|
3845
|
-
}
|
|
3846
|
-
}
|
|
3847
|
-
|
|
3848
|
-
private async uploadFile(
|
|
3849
|
-
filePath: string,
|
|
3850
|
-
relativePath: string,
|
|
3851
|
-
uploadUrl: string,
|
|
3852
|
-
authRestToken: string,
|
|
3853
|
-
authToken: string
|
|
3854
|
-
): Promise<{ url: string; filename: string }> {
|
|
3855
|
-
return new Promise(async (resolve, reject) => {
|
|
3856
|
-
try {
|
|
3857
|
-
const stats = fs.statSync(filePath);
|
|
3858
|
-
const fileStream = fs.createReadStream(filePath);
|
|
3859
|
-
|
|
3860
|
-
const cleanUploadUrl = uploadUrl.replace(new RegExp('/$'), '');
|
|
3861
|
-
const normalizedPath = this.normalizePath(relativePath);
|
|
3862
|
-
const uploadUrlWithFile = `${cleanUploadUrl}/${normalizedPath}?override=true`;
|
|
3863
|
-
|
|
3864
|
-
const requestHeaders: Record<string, string> = {
|
|
3865
|
-
'X-Auth': authToken,
|
|
3866
|
-
'X-Auth-Rest': authRestToken,
|
|
3867
|
-
'upload-length': stats.size.toString(),
|
|
3868
|
-
'upload-offset': '0'
|
|
3869
|
-
};
|
|
3870
|
-
|
|
3871
|
-
try {
|
|
3872
|
-
this.log('debug', `Making pre-upload POST request to ${uploadUrlWithFile}`);
|
|
3873
|
-
await axios.post(uploadUrlWithFile, '', {
|
|
3874
|
-
headers: requestHeaders,
|
|
3875
|
-
timeout: 60000, // 60s
|
|
3876
|
-
validateStatus: function (status: number): boolean {
|
|
3877
|
-
return status == 201;
|
|
3878
|
-
}
|
|
3879
|
-
});
|
|
3880
|
-
} catch (error) {
|
|
3881
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3882
|
-
|
|
3883
|
-
if (axios.isAxiosError(error)) {
|
|
3884
|
-
const responseData = error.response?.data;
|
|
3885
|
-
const responseStatus = error.response?.status;
|
|
3886
|
-
const responseHeaders = error.response?.headers;
|
|
3887
|
-
const responseText = typeof responseData === 'object' ? JSON.stringify(responseData) : responseData;
|
|
3888
|
-
|
|
3889
|
-
this.log('error', 'Pre-upload POST request failed - Full Response Details:', {
|
|
3890
|
-
status: responseStatus,
|
|
3891
|
-
headers: responseHeaders,
|
|
3892
|
-
data: responseText,
|
|
3893
|
-
message: errorMessage
|
|
3894
|
-
});
|
|
3895
|
-
reject(new Error(`Pre-upload request failed: ${errorMessage}`));
|
|
3896
|
-
return;
|
|
3897
|
-
} else {
|
|
3898
|
-
this.log('error', `Pre-upload POST request failed: ${errorMessage}`);
|
|
3899
|
-
reject(new Error(`Pre-upload request failed: ${errorMessage}`));
|
|
3900
|
-
return;
|
|
3901
|
-
}
|
|
3902
|
-
}
|
|
3903
|
-
|
|
3904
|
-
const upload = new tus.Upload(fileStream, {
|
|
3905
|
-
uploadUrl: uploadUrlWithFile,
|
|
3906
|
-
retryDelays: [1000, 2000, 4000, 8000, 16000, 20000],
|
|
3907
|
-
uploadDataDuringCreation: false,
|
|
3908
|
-
parallelUploads: 1,
|
|
3909
|
-
chunkSize: 10485760,
|
|
3910
|
-
headers: requestHeaders,
|
|
3911
|
-
removeFingerprintOnSuccess: true,
|
|
3912
|
-
uploadSize: stats.size,
|
|
3913
|
-
metadata: {
|
|
3914
|
-
filename: path.basename(relativePath)
|
|
3915
|
-
},
|
|
3916
|
-
onError: (error: Error) => {
|
|
3917
|
-
this.log('error', `TUS upload error for ${relativePath}`, { error: error.message });
|
|
3918
|
-
reject(new Error(`Upload failed: ${error.message}`));
|
|
3919
|
-
},
|
|
3920
|
-
onSuccess: () => {
|
|
3921
|
-
this.log('info', `TUS upload completed for ${relativePath}`, { url: upload.url });
|
|
3922
|
-
resolve({
|
|
3923
|
-
url: upload.url || uploadUrlWithFile,
|
|
3924
|
-
filename: relativePath
|
|
3925
|
-
});
|
|
3926
|
-
}
|
|
3927
|
-
});
|
|
3928
|
-
|
|
3929
|
-
upload.start();
|
|
3930
|
-
|
|
3931
|
-
} catch (error) {
|
|
3932
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3933
|
-
this.log('error', `Error preparing upload for ${filePath}`, { error: errorMessage });
|
|
3934
|
-
reject(new Error(`Failed to prepare upload: ${errorMessage}`));
|
|
3935
|
-
}
|
|
3936
|
-
});
|
|
3937
|
-
}
|
|
3938
|
-
|
|
3939
|
-
private hosting_importWordpressWebsite_validateArchiveFormat(filePath: string): boolean {
|
|
3940
|
-
const validExtensions = ['zip', 'tar', 'tar.gz', 'tgz', '7z', 'gz', 'gzip'];
|
|
3941
|
-
const fileName = path.basename(filePath).toLowerCase();
|
|
3942
|
-
|
|
3943
|
-
for (const ext of validExtensions) {
|
|
3944
|
-
if (fileName.endsWith(`.${ext}`)) {
|
|
3945
|
-
return true;
|
|
3946
|
-
}
|
|
3947
|
-
}
|
|
3948
|
-
|
|
3949
|
-
return false;
|
|
3950
|
-
}
|
|
3951
|
-
|
|
3952
|
-
private hosting_importWordpressWebsite_validateRequiredParams(params: Record<string, any>): void {
|
|
3953
|
-
const { domain, archivePath, databaseDump } = params;
|
|
3954
|
-
|
|
3955
|
-
if (!domain || typeof domain !== 'string') {
|
|
3956
|
-
throw new Error('domain is required and must be a string');
|
|
3957
|
-
}
|
|
3958
|
-
|
|
3959
|
-
if (!archivePath || typeof archivePath !== 'string') {
|
|
3960
|
-
throw new Error('archivePath is required and must be a string');
|
|
3961
|
-
}
|
|
3962
|
-
|
|
3963
|
-
if (!databaseDump || typeof databaseDump !== 'string') {
|
|
3964
|
-
throw new Error('databaseDump is required and must be a string');
|
|
3965
|
-
}
|
|
3966
|
-
}
|
|
3967
|
-
|
|
3968
|
-
private hosting_importWordpressWebsite_validateArchiveFile(archivePath: string): void {
|
|
3969
|
-
if (!fs.existsSync(archivePath)) {
|
|
3970
|
-
throw new Error(`Archive file not found: ${archivePath}`);
|
|
3971
|
-
}
|
|
3972
|
-
|
|
3973
|
-
const archiveStats = fs.statSync(archivePath);
|
|
3974
|
-
if (!archiveStats.isFile()) {
|
|
3975
|
-
throw new Error(`Archive path is not a file: ${archivePath}`);
|
|
3976
|
-
}
|
|
3977
|
-
|
|
3978
|
-
if (!this.hosting_importWordpressWebsite_validateArchiveFormat(archivePath)) {
|
|
3979
|
-
throw new Error('Invalid archive format. Supported formats: zip, tar, tar.gz, tgz, 7z, gz, gzip');
|
|
3980
|
-
}
|
|
3981
|
-
}
|
|
3982
|
-
|
|
3983
|
-
private hosting_importWordpressWebsite_validateDatabaseFile(databaseDump: string): void {
|
|
3984
|
-
if (!fs.existsSync(databaseDump)) {
|
|
3985
|
-
throw new Error(`Database dump file not found: ${databaseDump}`);
|
|
3986
|
-
}
|
|
3987
|
-
|
|
3988
|
-
const dbStats = fs.statSync(databaseDump);
|
|
3989
|
-
if (!dbStats.isFile()) {
|
|
3990
|
-
throw new Error(`Database dump path is not a file: ${databaseDump}`);
|
|
3991
|
-
}
|
|
3992
|
-
|
|
3993
|
-
if (!databaseDump.toLowerCase().endsWith('.sql')) {
|
|
3994
|
-
throw new Error('Database dump must be a .sql file');
|
|
3995
|
-
}
|
|
3996
|
-
}
|
|
3997
|
-
|
|
3998
|
-
private async hosting_importWordpressWebsite_checkWebsiteIsEmpty(username: string, domain: string): Promise<boolean> {
|
|
3999
|
-
const baseUrl = this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`;
|
|
4000
|
-
const url = new URL(`api/hosting/v1/accounts/${username}/domains/${domain}/is-empty`, baseUrl).toString();
|
|
4001
|
-
|
|
4002
|
-
try {
|
|
4003
|
-
const bearerToken = process.env['API_TOKEN'] || process.env['APITOKEN'];
|
|
4004
|
-
if (!bearerToken) {
|
|
4005
|
-
throw new Error('API_TOKEN environment variable not found');
|
|
4006
|
-
}
|
|
4007
|
-
|
|
4008
|
-
const config: AxiosRequestConfig = {
|
|
4009
|
-
method: 'get',
|
|
4010
|
-
url,
|
|
4011
|
-
headers: {
|
|
4012
|
-
...this.headers,
|
|
4013
|
-
'Authorization': `Bearer ${bearerToken}`
|
|
4014
|
-
},
|
|
4015
|
-
timeout: 60000, // 60s
|
|
4016
|
-
validateStatus: function (status: number): boolean {
|
|
4017
|
-
return status < 500;
|
|
4018
|
-
}
|
|
4019
|
-
};
|
|
4020
|
-
|
|
4021
|
-
const response = await axios(config);
|
|
4022
|
-
|
|
4023
|
-
if (response.status !== 200) {
|
|
4024
|
-
throw new Error(`API returned status ${response.status}: ${JSON.stringify(response.data)}`);
|
|
4025
|
-
}
|
|
4026
|
-
|
|
4027
|
-
const { is_empty } = response.data;
|
|
4028
|
-
|
|
4029
|
-
if (!is_empty) {
|
|
4030
|
-
throw new Error('Website is not empty. WordPress import can only be performed on empty sites. Please visit hPanel (https://hpanel.hostinger.com) and remove all existing files from the website before attempting to import.');
|
|
4031
|
-
}
|
|
4032
|
-
|
|
4033
|
-
this.log('info', `Website ${domain} is empty, proceeding with import`);
|
|
4034
|
-
return true;
|
|
4035
|
-
|
|
4036
|
-
} catch (error) {
|
|
4037
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4038
|
-
this.log('error', `Failed to check if website is empty: ${errorMessage}`);
|
|
4039
|
-
|
|
4040
|
-
if (axios.isAxiosError(error)) {
|
|
4041
|
-
const responseData = error.response?.data;
|
|
4042
|
-
const responseStatus = error.response?.status;
|
|
4043
|
-
this.log('error', 'API Error Details:', {
|
|
4044
|
-
status: responseStatus,
|
|
4045
|
-
data: typeof responseData === 'object' ? JSON.stringify(responseData) : responseData
|
|
4046
|
-
});
|
|
4047
|
-
}
|
|
4048
|
-
|
|
4049
|
-
throw error;
|
|
4050
|
-
}
|
|
4051
|
-
}
|
|
4052
|
-
|
|
4053
|
-
private async hosting_importWordpressWebsite_extractFiles(username: string, domain: string, archivePath: string, databaseDump: string): Promise<boolean> {
|
|
4054
|
-
const baseUrl = this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`;
|
|
4055
|
-
const url = new URL(`api/hosting/v1/accounts/${username}/websites/${domain}/wordpress/import`, baseUrl).toString();
|
|
4056
|
-
|
|
4057
|
-
try {
|
|
4058
|
-
const bearerToken = process.env['API_TOKEN'] || process.env['APITOKEN'];
|
|
4059
|
-
if (!bearerToken) {
|
|
4060
|
-
throw new Error('API_TOKEN environment variable not found');
|
|
4061
|
-
}
|
|
4062
|
-
|
|
4063
|
-
const config: AxiosRequestConfig = {
|
|
4064
|
-
method: 'post',
|
|
4065
|
-
url,
|
|
4066
|
-
headers: {
|
|
4067
|
-
...this.headers,
|
|
4068
|
-
'Authorization': `Bearer ${bearerToken}`,
|
|
4069
|
-
'Content-Type': 'application/json'
|
|
4070
|
-
},
|
|
4071
|
-
data: {
|
|
4072
|
-
archive_path: path.basename(archivePath),
|
|
4073
|
-
sql_path: path.basename(databaseDump)
|
|
4074
|
-
},
|
|
4075
|
-
timeout: 60000, // 60s
|
|
4076
|
-
validateStatus: function (status: number): boolean {
|
|
4077
|
-
return status < 500;
|
|
4078
|
-
}
|
|
4079
|
-
};
|
|
4080
|
-
|
|
4081
|
-
const response = await axios(config);
|
|
4082
|
-
|
|
4083
|
-
this.log('info', `Successfully triggered file extraction for ${domain}`);
|
|
4084
|
-
return true;
|
|
4085
|
-
|
|
4086
|
-
} catch (error) {
|
|
4087
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4088
|
-
this.log('error', `Failed to trigger file extraction: ${errorMessage}`);
|
|
4089
|
-
|
|
4090
|
-
if (axios.isAxiosError(error)) {
|
|
4091
|
-
const responseData = error.response?.data;
|
|
4092
|
-
const responseStatus = error.response?.status;
|
|
4093
|
-
this.log('error', 'API Error Details:', {
|
|
4094
|
-
status: responseStatus,
|
|
4095
|
-
data: typeof responseData === 'object' ? JSON.stringify(responseData) : responseData
|
|
4096
|
-
});
|
|
4097
|
-
}
|
|
4098
|
-
|
|
4099
|
-
throw error;
|
|
4100
|
-
}
|
|
4101
|
-
}
|
|
4102
|
-
|
|
4103
|
-
private async handleWordpressWebsiteImport(params: Record<string, any>): Promise<any> {
|
|
4104
|
-
const { domain, archivePath, databaseDump } = params;
|
|
4105
|
-
|
|
4106
|
-
this.hosting_importWordpressWebsite_validateRequiredParams(params);
|
|
4107
|
-
this.hosting_importWordpressWebsite_validateArchiveFile(archivePath);
|
|
4108
|
-
this.hosting_importWordpressWebsite_validateDatabaseFile(databaseDump);
|
|
4109
|
-
|
|
4110
|
-
// Auto-resolve username from domain
|
|
4111
|
-
this.log('info', `Resolving username from domain: ${domain}`);
|
|
4112
|
-
const username = await this.resolveUsername(domain);
|
|
4113
|
-
|
|
4114
|
-
await this.hosting_importWordpressWebsite_checkWebsiteIsEmpty(username, domain);
|
|
4115
|
-
|
|
4116
|
-
const filesToUpload: Array<{absolutePath: string, relativePath: string, type: string}> = [{
|
|
4117
|
-
absolutePath: archivePath,
|
|
4118
|
-
relativePath: path.basename(archivePath),
|
|
4119
|
-
type: 'archive'
|
|
4120
|
-
}, {
|
|
4121
|
-
absolutePath: databaseDump,
|
|
4122
|
-
relativePath: path.basename(databaseDump),
|
|
4123
|
-
type: 'database'
|
|
4124
|
-
}];
|
|
4125
|
-
|
|
4126
|
-
let uploadCredentials: any;
|
|
4127
|
-
try {
|
|
4128
|
-
uploadCredentials = await this.fetchUploadCredentials(username, domain);
|
|
4129
|
-
} catch (error) {
|
|
4130
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4131
|
-
throw new Error(`Failed to fetch upload credentials: ${errorMessage}`);
|
|
4132
|
-
}
|
|
4133
|
-
|
|
4134
|
-
const { url: uploadUrl, auth_key: authToken, rest_auth_key: authRestToken } = uploadCredentials;
|
|
4135
|
-
|
|
4136
|
-
if (!uploadUrl || !authToken || !authRestToken) {
|
|
4137
|
-
throw new Error('Invalid upload credentials received from API');
|
|
4138
|
-
}
|
|
4139
|
-
|
|
4140
|
-
this.log('info', `Starting website archive import to ${uploadUrl}`);
|
|
4141
|
-
|
|
4142
|
-
const results: any[] = [];
|
|
4143
|
-
let successCount = 0;
|
|
4144
|
-
let failureCount = 0;
|
|
4145
|
-
|
|
4146
|
-
for (const fileInfo of filesToUpload) {
|
|
4147
|
-
try {
|
|
4148
|
-
this.log('info', `Uploading ${fileInfo.type}: ${fileInfo.absolutePath}`);
|
|
4149
|
-
|
|
4150
|
-
const stats = fs.statSync(fileInfo.absolutePath);
|
|
4151
|
-
const uploadResult = await this.uploadFile(
|
|
4152
|
-
fileInfo.absolutePath,
|
|
4153
|
-
fileInfo.relativePath,
|
|
4154
|
-
uploadUrl,
|
|
4155
|
-
authRestToken,
|
|
4156
|
-
authToken
|
|
4157
|
-
);
|
|
4158
|
-
|
|
4159
|
-
results.push({
|
|
4160
|
-
file: fileInfo.absolutePath,
|
|
4161
|
-
remotePath: fileInfo.relativePath,
|
|
4162
|
-
type: fileInfo.type,
|
|
4163
|
-
status: 'success',
|
|
4164
|
-
uploadUrl: uploadResult.url,
|
|
4165
|
-
size: stats.size
|
|
4166
|
-
});
|
|
4167
|
-
|
|
4168
|
-
successCount++;
|
|
4169
|
-
this.log('info', `Successfully uploaded ${fileInfo.type}: ${fileInfo.relativePath}`);
|
|
4170
|
-
|
|
4171
|
-
} catch (error) {
|
|
4172
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4173
|
-
results.push({
|
|
4174
|
-
file: fileInfo.absolutePath,
|
|
4175
|
-
remotePath: fileInfo.relativePath,
|
|
4176
|
-
type: fileInfo.type,
|
|
4177
|
-
status: 'error',
|
|
4178
|
-
error: errorMessage
|
|
4179
|
-
});
|
|
4180
|
-
|
|
4181
|
-
failureCount++;
|
|
4182
|
-
this.log('error', `Failed to upload ${fileInfo.type} ${fileInfo.absolutePath}: ${errorMessage}`);
|
|
4183
|
-
}
|
|
4184
|
-
}
|
|
4185
|
-
|
|
4186
|
-
const overallStatus = failureCount === 0 ? 'success' : (successCount === 0 ? 'failure' : 'partial');
|
|
4187
|
-
|
|
4188
|
-
if (failureCount === 0) {
|
|
4189
|
-
try {
|
|
4190
|
-
this.log('info', 'All files uploaded successfully, triggering extraction...');
|
|
4191
|
-
await this.hosting_importWordpressWebsite_extractFiles(username, domain, archivePath, databaseDump);
|
|
4192
|
-
} catch (error) {
|
|
4193
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4194
|
-
this.log('error', `File extraction failed: ${errorMessage}`);
|
|
4195
|
-
return {
|
|
4196
|
-
status: 'partial',
|
|
4197
|
-
summary: {
|
|
4198
|
-
total: filesToUpload.length,
|
|
4199
|
-
successful: successCount,
|
|
4200
|
-
failed: failureCount
|
|
4201
|
-
},
|
|
4202
|
-
results,
|
|
4203
|
-
extractionError: errorMessage
|
|
4204
|
-
};
|
|
4205
|
-
}
|
|
4206
|
-
}
|
|
4207
|
-
|
|
4208
|
-
return {
|
|
4209
|
-
status: overallStatus,
|
|
4210
|
-
summary: {
|
|
4211
|
-
total: filesToUpload.length,
|
|
4212
|
-
successful: successCount,
|
|
4213
|
-
failed: failureCount
|
|
4214
|
-
},
|
|
4215
|
-
results
|
|
4216
|
-
};
|
|
4217
|
-
}
|
|
4218
|
-
|
|
4219
|
-
private hosting_deployWordpressPlugin_generateRandomString(length: number): string {
|
|
4220
|
-
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
4221
|
-
let result = '';
|
|
4222
|
-
for (let i = 0; i < length; i++) {
|
|
4223
|
-
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
4224
|
-
}
|
|
4225
|
-
return result;
|
|
4226
|
-
}
|
|
4227
|
-
|
|
4228
|
-
private hosting_deployWordpressPlugin_validateRequiredParams(params: Record<string, any>): void {
|
|
4229
|
-
const { domain, slug, pluginPath } = params;
|
|
4230
|
-
|
|
4231
|
-
if (!domain || typeof domain !== 'string') {
|
|
4232
|
-
throw new Error('domain is required and must be a string');
|
|
4233
|
-
}
|
|
4234
|
-
|
|
4235
|
-
if (!slug || typeof slug !== 'string') {
|
|
4236
|
-
throw new Error('slug is required and must be a string');
|
|
4237
|
-
}
|
|
4238
|
-
|
|
4239
|
-
if (!pluginPath || typeof pluginPath !== 'string') {
|
|
4240
|
-
throw new Error('pluginPath is required and must be a string');
|
|
4241
|
-
}
|
|
4242
|
-
}
|
|
4243
|
-
|
|
4244
|
-
private hosting_deployWordpressPlugin_validatePluginDirectory(pluginPath: string): void {
|
|
4245
|
-
if (!fs.existsSync(pluginPath)) {
|
|
4246
|
-
throw new Error(`Plugin directory not found: ${pluginPath}`);
|
|
4247
|
-
}
|
|
4248
|
-
|
|
4249
|
-
const pluginStats = fs.statSync(pluginPath);
|
|
4250
|
-
if (!pluginStats.isDirectory()) {
|
|
4251
|
-
throw new Error(`Plugin path is not a directory: ${pluginPath}`);
|
|
4252
|
-
}
|
|
4253
|
-
}
|
|
4254
|
-
|
|
4255
|
-
private hosting_deployWordpressPlugin_scanDirectory(dirPath: string, basePath: string = dirPath): Array<{absolutePath: string, relativePath: string}> {
|
|
4256
|
-
const files: Array<{absolutePath: string, relativePath: string}> = [];
|
|
4257
|
-
|
|
4258
|
-
const scanDir = (currentPath: string) => {
|
|
4259
|
-
const items = fs.readdirSync(currentPath);
|
|
4260
|
-
|
|
4261
|
-
for (const item of items) {
|
|
4262
|
-
const itemPath = path.join(currentPath, item);
|
|
4263
|
-
const stats = fs.statSync(itemPath);
|
|
4264
|
-
|
|
4265
|
-
if (stats.isDirectory()) {
|
|
4266
|
-
scanDir(itemPath);
|
|
4267
|
-
} else if (stats.isFile()) {
|
|
4268
|
-
const relativePath = path.relative(basePath, itemPath);
|
|
4269
|
-
files.push({
|
|
4270
|
-
absolutePath: itemPath,
|
|
4271
|
-
relativePath: relativePath
|
|
4272
|
-
});
|
|
4273
|
-
}
|
|
4274
|
-
}
|
|
4275
|
-
};
|
|
4276
|
-
|
|
4277
|
-
scanDir(dirPath);
|
|
4278
|
-
return files;
|
|
4279
|
-
}
|
|
4280
|
-
|
|
4281
|
-
private async hosting_deployWordpressPlugin_deployPlugin(username: string, domain: string, slug: string, pluginPath: string): Promise<boolean> {
|
|
4282
|
-
const baseUrl = this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`;
|
|
4283
|
-
const url = new URL(`api/hosting/v1/accounts/${username}/websites/${domain}/wordpress/plugins/deploy`, baseUrl).toString();
|
|
4284
|
-
|
|
4285
|
-
try {
|
|
4286
|
-
const bearerToken = process.env['API_TOKEN'] || process.env['APITOKEN'];
|
|
4287
|
-
if (!bearerToken) {
|
|
4288
|
-
throw new Error('API_TOKEN environment variable not found');
|
|
4289
|
-
}
|
|
4290
|
-
|
|
4291
|
-
const config: AxiosRequestConfig = {
|
|
4292
|
-
method: 'post',
|
|
4293
|
-
url,
|
|
4294
|
-
headers: {
|
|
4295
|
-
...this.headers,
|
|
4296
|
-
'Authorization': `Bearer ${bearerToken}`,
|
|
4297
|
-
'Content-Type': 'application/json'
|
|
4298
|
-
},
|
|
4299
|
-
data: {
|
|
4300
|
-
slug,
|
|
4301
|
-
plugin_path: pluginPath
|
|
4302
|
-
},
|
|
4303
|
-
timeout: 60000, // 60s
|
|
4304
|
-
validateStatus: function (status: number): boolean {
|
|
4305
|
-
return status < 500;
|
|
4306
|
-
}
|
|
4307
|
-
};
|
|
4308
|
-
|
|
4309
|
-
const response = await axios(config);
|
|
4310
|
-
|
|
4311
|
-
if (response.status !== 200) {
|
|
4312
|
-
throw new Error(`API returned status ${response.status}: ${JSON.stringify(response.data)}`);
|
|
4313
|
-
}
|
|
4314
|
-
|
|
4315
|
-
this.log('info', `Successfully triggered plugin deployment for ${domain}`);
|
|
4316
|
-
return true;
|
|
4317
|
-
|
|
4318
|
-
} catch (error) {
|
|
4319
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4320
|
-
this.log('error', `Failed to trigger plugin deployment: ${errorMessage}`);
|
|
4321
|
-
|
|
4322
|
-
if (axios.isAxiosError(error)) {
|
|
4323
|
-
const responseData = error.response?.data;
|
|
4324
|
-
const responseStatus = error.response?.status;
|
|
4325
|
-
this.log('error', 'API Error Details:', {
|
|
4326
|
-
status: responseStatus,
|
|
4327
|
-
data: typeof responseData === 'object' ? JSON.stringify(responseData) : responseData
|
|
4328
|
-
});
|
|
4329
|
-
}
|
|
4330
|
-
|
|
4331
|
-
throw error;
|
|
4332
|
-
}
|
|
4333
|
-
}
|
|
4334
|
-
|
|
4335
|
-
private async handleWordpressPluginDeploy(params: Record<string, any>): Promise<any> {
|
|
4336
|
-
const { domain, slug, pluginPath } = params;
|
|
4337
|
-
|
|
4338
|
-
this.hosting_deployWordpressPlugin_validateRequiredParams(params);
|
|
4339
|
-
this.hosting_deployWordpressPlugin_validatePluginDirectory(pluginPath);
|
|
4340
|
-
|
|
4341
|
-
this.log('info', `Resolving username from domain: ${domain}`);
|
|
4342
|
-
const username = await this.resolveUsername(domain);
|
|
4343
|
-
|
|
4344
|
-
const randomSuffix = this.hosting_deployWordpressPlugin_generateRandomString(8);
|
|
4345
|
-
const uploadDirName = `${slug}-${randomSuffix}`;
|
|
4346
|
-
|
|
4347
|
-
this.log('info', `Scanning plugin directory: ${pluginPath}`);
|
|
4348
|
-
const pluginFiles = this.hosting_deployWordpressPlugin_scanDirectory(pluginPath);
|
|
4349
|
-
|
|
4350
|
-
if (pluginFiles.length === 0) {
|
|
4351
|
-
throw new Error(`No files found in plugin directory: ${pluginPath}`);
|
|
4352
|
-
}
|
|
4353
|
-
|
|
4354
|
-
this.log('info', `Found ${pluginFiles.length} files to upload`);
|
|
4355
|
-
|
|
4356
|
-
let uploadCredentials: any;
|
|
4357
|
-
try {
|
|
4358
|
-
uploadCredentials = await this.fetchUploadCredentials(username, domain);
|
|
4359
|
-
} catch (error) {
|
|
4360
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4361
|
-
throw new Error(`Failed to fetch upload credentials: ${errorMessage}`);
|
|
4362
|
-
}
|
|
4363
|
-
|
|
4364
|
-
const { url: uploadUrl, auth_key: authToken, rest_auth_key: authRestToken } = uploadCredentials;
|
|
4365
|
-
|
|
4366
|
-
if (!uploadUrl || !authToken || !authRestToken) {
|
|
4367
|
-
throw new Error('Invalid upload credentials received from API');
|
|
4368
|
-
}
|
|
4369
|
-
|
|
4370
|
-
this.log('info', `Starting plugin file upload to ${uploadUrl}`);
|
|
4371
|
-
|
|
4372
|
-
const results: any[] = [];
|
|
4373
|
-
let successCount = 0;
|
|
4374
|
-
let failureCount = 0;
|
|
4375
|
-
|
|
4376
|
-
for (const fileInfo of pluginFiles) {
|
|
4377
|
-
try {
|
|
4378
|
-
const normalizedRelativePath = this.normalizePath(fileInfo.relativePath);
|
|
4379
|
-
const uploadPath = `wp-content/plugins/${uploadDirName}/${normalizedRelativePath}`;
|
|
4380
|
-
this.log('info', `Uploading: ${fileInfo.absolutePath} -> ${uploadPath}`);
|
|
4381
|
-
|
|
4382
|
-
const stats = fs.statSync(fileInfo.absolutePath);
|
|
4383
|
-
const uploadResult = await this.uploadFile(
|
|
4384
|
-
fileInfo.absolutePath,
|
|
4385
|
-
uploadPath,
|
|
4386
|
-
uploadUrl,
|
|
4387
|
-
authRestToken,
|
|
4388
|
-
authToken
|
|
4389
|
-
);
|
|
4390
|
-
|
|
4391
|
-
results.push({
|
|
4392
|
-
file: fileInfo.absolutePath,
|
|
4393
|
-
remotePath: uploadPath,
|
|
4394
|
-
status: 'success',
|
|
4395
|
-
uploadUrl: uploadResult.url,
|
|
4396
|
-
size: stats.size
|
|
4397
|
-
});
|
|
4398
|
-
|
|
4399
|
-
successCount++;
|
|
4400
|
-
this.log('info', `Successfully uploaded: ${uploadPath}`);
|
|
4401
|
-
|
|
4402
|
-
} catch (error) {
|
|
4403
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4404
|
-
const normalizedRelativePath = this.normalizePath(fileInfo.relativePath);
|
|
4405
|
-
const uploadPath = `wp-content/plugins/${uploadDirName}/${normalizedRelativePath}`;
|
|
4406
|
-
|
|
4407
|
-
results.push({
|
|
4408
|
-
file: fileInfo.absolutePath,
|
|
4409
|
-
remotePath: uploadPath,
|
|
4410
|
-
status: 'error',
|
|
4411
|
-
error: errorMessage
|
|
4412
|
-
});
|
|
4413
|
-
|
|
4414
|
-
failureCount++;
|
|
4415
|
-
this.log('error', `Failed to upload ${fileInfo.absolutePath}: ${errorMessage}`);
|
|
4416
|
-
}
|
|
4417
|
-
}
|
|
4418
|
-
|
|
4419
|
-
const overallStatus = failureCount === 0 ? 'success' : (successCount === 0 ? 'failure' : 'partial');
|
|
4420
|
-
|
|
4421
|
-
if (failureCount === 0) {
|
|
4422
|
-
try {
|
|
4423
|
-
this.log('info', 'All files uploaded successfully, triggering plugin deployment...');
|
|
4424
|
-
await this.hosting_deployWordpressPlugin_deployPlugin(username, domain, slug, uploadDirName);
|
|
4425
|
-
} catch (error) {
|
|
4426
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4427
|
-
this.log('error', `Plugin deployment failed: ${errorMessage}`);
|
|
4428
|
-
return {
|
|
4429
|
-
status: 'partial',
|
|
4430
|
-
summary: {
|
|
4431
|
-
total: pluginFiles.length,
|
|
4432
|
-
successful: successCount,
|
|
4433
|
-
failed: failureCount
|
|
4434
|
-
},
|
|
4435
|
-
results,
|
|
4436
|
-
deploymentError: errorMessage
|
|
4437
|
-
};
|
|
4438
|
-
}
|
|
4439
|
-
}
|
|
4440
|
-
|
|
4441
|
-
return {
|
|
4442
|
-
status: overallStatus,
|
|
4443
|
-
summary: {
|
|
4444
|
-
total: pluginFiles.length,
|
|
4445
|
-
successful: successCount,
|
|
4446
|
-
failed: failureCount
|
|
4447
|
-
},
|
|
4448
|
-
results,
|
|
4449
|
-
uploadDirName
|
|
4450
|
-
};
|
|
4451
|
-
}
|
|
4452
|
-
|
|
4453
|
-
private hosting_deployWordpressTheme_generateRandomString(length: number): string {
|
|
4454
|
-
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
4455
|
-
let result = '';
|
|
4456
|
-
for (let i = 0; i < length; i++) {
|
|
4457
|
-
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
4458
|
-
}
|
|
4459
|
-
return result;
|
|
4460
|
-
}
|
|
4461
|
-
|
|
4462
|
-
private hosting_deployWordpressTheme_validateRequiredParams(params: Record<string, any>): void {
|
|
4463
|
-
const { domain, slug, themePath } = params;
|
|
4464
|
-
|
|
4465
|
-
if (!domain || typeof domain !== 'string') {
|
|
4466
|
-
throw new Error('domain is required and must be a string');
|
|
4467
|
-
}
|
|
4468
|
-
|
|
4469
|
-
if (!slug || typeof slug !== 'string') {
|
|
4470
|
-
throw new Error('slug is required and must be a string');
|
|
4471
|
-
}
|
|
4472
|
-
|
|
4473
|
-
if (!themePath || typeof themePath !== 'string') {
|
|
4474
|
-
throw new Error('themePath is required and must be a string');
|
|
4475
|
-
}
|
|
4476
|
-
}
|
|
4477
|
-
|
|
4478
|
-
private hosting_deployWordpressTheme_validateThemeDirectory(themePath: string): void {
|
|
4479
|
-
if (!fs.existsSync(themePath)) {
|
|
4480
|
-
throw new Error(`Theme directory not found: ${themePath}`);
|
|
4481
|
-
}
|
|
4482
|
-
|
|
4483
|
-
const themeStats = fs.statSync(themePath);
|
|
4484
|
-
if (!themeStats.isDirectory()) {
|
|
4485
|
-
throw new Error(`Theme path is not a directory: ${themePath}`);
|
|
4486
|
-
}
|
|
4487
|
-
}
|
|
4488
|
-
|
|
4489
|
-
private hosting_deployWordpressTheme_scanDirectory(dirPath: string, basePath: string = dirPath): Array<{absolutePath: string, relativePath: string}> {
|
|
4490
|
-
const files: Array<{absolutePath: string, relativePath: string}> = [];
|
|
4491
|
-
|
|
4492
|
-
const scanDir = (currentPath: string) => {
|
|
4493
|
-
const items = fs.readdirSync(currentPath);
|
|
4494
|
-
|
|
4495
|
-
for (const item of items) {
|
|
4496
|
-
const itemPath = path.join(currentPath, item);
|
|
4497
|
-
const stats = fs.statSync(itemPath);
|
|
4498
|
-
|
|
4499
|
-
if (stats.isDirectory()) {
|
|
4500
|
-
scanDir(itemPath);
|
|
4501
|
-
} else if (stats.isFile()) {
|
|
4502
|
-
const relativePath = path.relative(basePath, itemPath);
|
|
4503
|
-
files.push({
|
|
4504
|
-
absolutePath: itemPath,
|
|
4505
|
-
relativePath: relativePath
|
|
4506
|
-
});
|
|
4507
|
-
}
|
|
4508
|
-
}
|
|
4509
|
-
};
|
|
4510
|
-
|
|
4511
|
-
scanDir(dirPath);
|
|
4512
|
-
return files;
|
|
4513
|
-
}
|
|
4514
|
-
|
|
4515
|
-
private async hosting_deployWordpressTheme_deployTheme(username: string, domain: string, slug: string, themePath: string, activate: boolean = false): Promise<boolean> {
|
|
4516
|
-
const baseUrl = this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`;
|
|
4517
|
-
const url = new URL(`api/hosting/v1/accounts/${username}/websites/${domain}/wordpress/themes/deploy`, baseUrl).toString();
|
|
4518
|
-
|
|
4519
|
-
try {
|
|
4520
|
-
const bearerToken = process.env['API_TOKEN'] || process.env['APITOKEN'];
|
|
4521
|
-
if (!bearerToken) {
|
|
4522
|
-
throw new Error('API_TOKEN environment variable not found');
|
|
4523
|
-
}
|
|
4524
|
-
|
|
4525
|
-
const config: AxiosRequestConfig = {
|
|
4526
|
-
method: 'post',
|
|
4527
|
-
url,
|
|
4528
|
-
headers: {
|
|
4529
|
-
...this.headers,
|
|
4530
|
-
'Authorization': `Bearer ${bearerToken}`,
|
|
4531
|
-
'Content-Type': 'application/json'
|
|
4532
|
-
},
|
|
4533
|
-
data: {
|
|
4534
|
-
slug,
|
|
4535
|
-
theme_path: themePath,
|
|
4536
|
-
is_activated: activate
|
|
4537
|
-
},
|
|
4538
|
-
timeout: 60000, // 60s
|
|
4539
|
-
validateStatus: function (status: number): boolean {
|
|
4540
|
-
return status < 500;
|
|
4541
|
-
}
|
|
4542
|
-
};
|
|
4543
|
-
|
|
4544
|
-
const response = await axios(config);
|
|
4545
|
-
|
|
4546
|
-
if (response.status !== 200) {
|
|
4547
|
-
throw new Error(`API returned status ${response.status}: ${JSON.stringify(response.data)}`);
|
|
4548
|
-
}
|
|
4549
|
-
|
|
4550
|
-
this.log('info', `Successfully triggered theme deployment for ${domain}`);
|
|
4551
|
-
return true;
|
|
4552
|
-
|
|
4553
|
-
} catch (error) {
|
|
4554
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4555
|
-
this.log('error', `Failed to trigger theme deployment: ${errorMessage}`);
|
|
4556
|
-
|
|
4557
|
-
if (axios.isAxiosError(error)) {
|
|
4558
|
-
const responseData = error.response?.data;
|
|
4559
|
-
const responseStatus = error.response?.status;
|
|
4560
|
-
this.log('error', 'API Error Details:', {
|
|
4561
|
-
status: responseStatus,
|
|
4562
|
-
data: typeof responseData === 'object' ? JSON.stringify(responseData) : responseData
|
|
4563
|
-
});
|
|
4564
|
-
}
|
|
4565
|
-
|
|
4566
|
-
throw error;
|
|
4567
|
-
}
|
|
4568
|
-
}
|
|
4569
|
-
|
|
4570
|
-
private async handleWordpressThemeDeploy(params: Record<string, any>): Promise<any> {
|
|
4571
|
-
const { domain, slug, themePath, activate = false } = params;
|
|
4572
|
-
|
|
4573
|
-
this.hosting_deployWordpressTheme_validateRequiredParams(params);
|
|
4574
|
-
this.hosting_deployWordpressTheme_validateThemeDirectory(themePath);
|
|
4575
|
-
|
|
4576
|
-
this.log('info', `Resolving username from domain: ${domain}`);
|
|
4577
|
-
const username = await this.resolveUsername(domain);
|
|
4578
|
-
|
|
4579
|
-
const randomSuffix = this.hosting_deployWordpressTheme_generateRandomString(8);
|
|
4580
|
-
const uploadDirName = `${slug}-${randomSuffix}`;
|
|
4581
|
-
|
|
4582
|
-
this.log('info', `Scanning theme directory: ${themePath}`);
|
|
4583
|
-
const themeFiles = this.hosting_deployWordpressTheme_scanDirectory(themePath);
|
|
4584
|
-
|
|
4585
|
-
if (themeFiles.length === 0) {
|
|
4586
|
-
throw new Error(`No files found in theme directory: ${themePath}`);
|
|
4587
|
-
}
|
|
4588
|
-
|
|
4589
|
-
this.log('info', `Found ${themeFiles.length} files to upload`);
|
|
4590
|
-
|
|
4591
|
-
let uploadCredentials: any;
|
|
4592
|
-
try {
|
|
4593
|
-
uploadCredentials = await this.fetchUploadCredentials(username, domain);
|
|
4594
|
-
} catch (error) {
|
|
4595
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4596
|
-
throw new Error(`Failed to fetch upload credentials: ${errorMessage}`);
|
|
4597
|
-
}
|
|
4598
|
-
|
|
4599
|
-
const { url: uploadUrl, auth_key: authToken, rest_auth_key: authRestToken } = uploadCredentials;
|
|
4600
|
-
|
|
4601
|
-
if (!uploadUrl || !authToken || !authRestToken) {
|
|
4602
|
-
throw new Error('Invalid upload credentials received from API');
|
|
4603
|
-
}
|
|
4604
|
-
|
|
4605
|
-
this.log('info', `Starting theme file upload to ${uploadUrl}`);
|
|
4606
|
-
|
|
4607
|
-
const results: any[] = [];
|
|
4608
|
-
let successCount = 0;
|
|
4609
|
-
let failureCount = 0;
|
|
4610
|
-
|
|
4611
|
-
for (const fileInfo of themeFiles) {
|
|
4612
|
-
try {
|
|
4613
|
-
const normalizedRelativePath = this.normalizePath(fileInfo.relativePath);
|
|
4614
|
-
const uploadPath = `wp-content/themes/${uploadDirName}/${normalizedRelativePath}`;
|
|
4615
|
-
this.log('info', `Uploading: ${fileInfo.absolutePath} -> ${uploadPath}`);
|
|
4616
|
-
|
|
4617
|
-
const stats = fs.statSync(fileInfo.absolutePath);
|
|
4618
|
-
const uploadResult = await this.uploadFile(
|
|
4619
|
-
fileInfo.absolutePath,
|
|
4620
|
-
uploadPath,
|
|
4621
|
-
uploadUrl,
|
|
4622
|
-
authRestToken,
|
|
4623
|
-
authToken
|
|
4624
|
-
);
|
|
4625
|
-
|
|
4626
|
-
results.push({
|
|
4627
|
-
file: fileInfo.absolutePath,
|
|
4628
|
-
remotePath: uploadPath,
|
|
4629
|
-
status: 'success',
|
|
4630
|
-
uploadUrl: uploadResult.url,
|
|
4631
|
-
size: stats.size
|
|
4632
|
-
});
|
|
4633
|
-
|
|
4634
|
-
successCount++;
|
|
4635
|
-
this.log('info', `Successfully uploaded: ${uploadPath}`);
|
|
4636
|
-
|
|
4637
|
-
} catch (error) {
|
|
4638
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4639
|
-
const normalizedRelativePath = this.normalizePath(fileInfo.relativePath);
|
|
4640
|
-
const uploadPath = `wp-content/themes/${uploadDirName}/${normalizedRelativePath}`;
|
|
4641
|
-
|
|
4642
|
-
results.push({
|
|
4643
|
-
file: fileInfo.absolutePath,
|
|
4644
|
-
remotePath: uploadPath,
|
|
4645
|
-
status: 'error',
|
|
4646
|
-
error: errorMessage
|
|
4647
|
-
});
|
|
4648
|
-
|
|
4649
|
-
failureCount++;
|
|
4650
|
-
this.log('error', `Failed to upload ${fileInfo.absolutePath}: ${errorMessage}`);
|
|
4651
|
-
}
|
|
4652
|
-
}
|
|
4653
|
-
|
|
4654
|
-
const overallStatus = failureCount === 0 ? 'success' : (successCount === 0 ? 'failure' : 'partial');
|
|
4655
|
-
|
|
4656
|
-
if (failureCount === 0) {
|
|
4657
|
-
try {
|
|
4658
|
-
this.log('info', 'All files uploaded successfully, triggering theme deployment...');
|
|
4659
|
-
await this.hosting_deployWordpressTheme_deployTheme(username, domain, slug, uploadDirName, activate);
|
|
4660
|
-
} catch (error) {
|
|
4661
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4662
|
-
this.log('error', `Theme deployment failed: ${errorMessage}`);
|
|
4663
|
-
return {
|
|
4664
|
-
status: 'partial',
|
|
4665
|
-
summary: {
|
|
4666
|
-
total: themeFiles.length,
|
|
4667
|
-
successful: successCount,
|
|
4668
|
-
failed: failureCount
|
|
4669
|
-
},
|
|
4670
|
-
results,
|
|
4671
|
-
deploymentError: errorMessage
|
|
4672
|
-
};
|
|
4673
|
-
}
|
|
4674
|
-
}
|
|
4675
|
-
|
|
4676
|
-
return {
|
|
4677
|
-
status: overallStatus,
|
|
4678
|
-
summary: {
|
|
4679
|
-
total: themeFiles.length,
|
|
4680
|
-
successful: successCount,
|
|
4681
|
-
failed: failureCount
|
|
4682
|
-
},
|
|
4683
|
-
results,
|
|
4684
|
-
uploadDirName,
|
|
4685
|
-
activated: activate
|
|
4686
|
-
};
|
|
4687
|
-
}
|
|
4688
|
-
|
|
4689
|
-
private hosting_deployJsApplication_validateArchiveFormat(filePath: string): boolean {
|
|
4690
|
-
const validExtensions = ['zip', 'tar', 'tar.gz', 'tgz', '7z', 'gz', 'gzip'];
|
|
4691
|
-
const fileName = path.basename(filePath).toLowerCase();
|
|
4692
|
-
|
|
4693
|
-
for (const ext of validExtensions) {
|
|
4694
|
-
if (fileName.endsWith(`.${ext}`)) {
|
|
4695
|
-
return true;
|
|
4696
|
-
}
|
|
4697
|
-
}
|
|
4698
|
-
|
|
4699
|
-
return false;
|
|
4700
|
-
}
|
|
4701
|
-
|
|
4702
|
-
private hosting_deployJsApplication_validateRequiredParams(params: Record<string, any>): void {
|
|
4703
|
-
const { domain, archivePath, removeArchive } = params;
|
|
4704
|
-
|
|
4705
|
-
if (!domain || typeof domain !== 'string') {
|
|
4706
|
-
throw new Error('domain is required and must be a string');
|
|
4707
|
-
}
|
|
4708
|
-
|
|
4709
|
-
if (!archivePath || typeof archivePath !== 'string') {
|
|
4710
|
-
throw new Error('archivePath is required and must be a string');
|
|
4711
|
-
}
|
|
4712
|
-
|
|
4713
|
-
if (removeArchive !== undefined && typeof removeArchive !== 'boolean') {
|
|
4714
|
-
throw new Error('removeArchive must be a boolean if provided');
|
|
4715
|
-
}
|
|
4716
|
-
}
|
|
4717
|
-
|
|
4718
|
-
private hosting_deployJsApplication_removeArchive(archivePath: string, removeArchive: boolean): boolean {
|
|
4719
|
-
if (!removeArchive) {
|
|
4720
|
-
return false;
|
|
4721
|
-
}
|
|
4722
|
-
|
|
4723
|
-
try {
|
|
4724
|
-
this.log('info', `Removing archive file: ${archivePath}`);
|
|
4725
|
-
fs.unlinkSync(archivePath);
|
|
4726
|
-
this.log('info', `Successfully removed archive file: ${archivePath}`);
|
|
4727
|
-
return true;
|
|
4728
|
-
} catch (error) {
|
|
4729
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4730
|
-
this.log('error', `Failed to remove archive file: ${errorMessage}`);
|
|
4731
|
-
// Don't fail the entire operation if archive removal fails
|
|
4732
|
-
return false;
|
|
4733
|
-
}
|
|
4734
|
-
}
|
|
4735
|
-
|
|
4736
|
-
private hosting_deployJsApplication_validateArchiveFile(archivePath: string): void {
|
|
4737
|
-
if (!fs.existsSync(archivePath)) {
|
|
4738
|
-
throw new Error(`Archive file not found: ${archivePath}`);
|
|
4739
|
-
}
|
|
4740
|
-
|
|
4741
|
-
const archiveStats = fs.statSync(archivePath);
|
|
4742
|
-
if (!archiveStats.isFile()) {
|
|
4743
|
-
throw new Error(`Archive path is not a file: ${archivePath}`);
|
|
4744
|
-
}
|
|
4745
|
-
|
|
4746
|
-
if (!this.hosting_deployJsApplication_validateArchiveFormat(archivePath)) {
|
|
4747
|
-
throw new Error('Invalid archive format. Supported formats: zip, tar, tar.gz, tgz, 7z, gz, gzip');
|
|
4748
|
-
}
|
|
4749
|
-
}
|
|
4750
|
-
|
|
4751
|
-
private async hosting_deployJsApplication_fetchBuildSettings(username: string, domain: string, archivePath: string): Promise<any> {
|
|
4752
|
-
const baseUrl = this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`;
|
|
4753
|
-
const archiveBasename = path.basename(archivePath);
|
|
4754
|
-
const url = new URL(`api/hosting/v1/accounts/${username}/websites/${domain}/nodejs/builds/settings/from-archive?archive_path=${encodeURIComponent(archiveBasename)}`, baseUrl).toString();
|
|
4755
|
-
|
|
4756
|
-
try {
|
|
4757
|
-
const bearerToken = process.env['API_TOKEN'] || process.env['APITOKEN'];
|
|
4758
|
-
if (!bearerToken) {
|
|
4759
|
-
throw new Error('API_TOKEN environment variable not found');
|
|
4760
|
-
}
|
|
4761
|
-
|
|
4762
|
-
const config: AxiosRequestConfig = {
|
|
4763
|
-
method: 'get',
|
|
4764
|
-
url,
|
|
4765
|
-
headers: {
|
|
4766
|
-
...this.headers,
|
|
4767
|
-
'Authorization': `Bearer ${bearerToken}`
|
|
4768
|
-
},
|
|
4769
|
-
timeout: 60000, // 60s
|
|
4770
|
-
validateStatus: function (status: number): boolean {
|
|
4771
|
-
return status < 500;
|
|
4772
|
-
}
|
|
4773
|
-
};
|
|
4774
|
-
|
|
4775
|
-
const response = await axios(config);
|
|
4776
|
-
|
|
4777
|
-
if (response.status !== 200) {
|
|
4778
|
-
throw new Error(`API returned status ${response.status}: ${JSON.stringify(response.data)}`);
|
|
4779
|
-
}
|
|
4780
|
-
|
|
4781
|
-
this.log('info', `Successfully fetched build settings for ${domain}`);
|
|
4782
|
-
return response.data;
|
|
4783
|
-
|
|
4784
|
-
} catch (error) {
|
|
4785
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4786
|
-
this.log('error', `Failed to fetch build settings: ${errorMessage}`);
|
|
4787
|
-
|
|
4788
|
-
if (axios.isAxiosError(error)) {
|
|
4789
|
-
const responseData = error.response?.data;
|
|
4790
|
-
const responseStatus = error.response?.status;
|
|
4791
|
-
this.log('error', 'API Error Details:', {
|
|
4792
|
-
status: responseStatus,
|
|
4793
|
-
data: typeof responseData === 'object' ? JSON.stringify(responseData) : responseData
|
|
4794
|
-
});
|
|
4795
|
-
}
|
|
4796
|
-
|
|
4797
|
-
throw error;
|
|
4798
|
-
}
|
|
4799
|
-
}
|
|
4800
|
-
|
|
4801
|
-
private async hosting_deployJsApplication_triggerBuild(username: string, domain: string, archivePath: string, buildSettings: any): Promise<any> {
|
|
4802
|
-
const baseUrl = this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`;
|
|
4803
|
-
const url = new URL(`api/hosting/v1/accounts/${username}/websites/${domain}/nodejs/builds`, baseUrl).toString();
|
|
4804
|
-
|
|
4805
|
-
try {
|
|
4806
|
-
const bearerToken = process.env['API_TOKEN'] || process.env['APITOKEN'];
|
|
4807
|
-
if (!bearerToken) {
|
|
4808
|
-
throw new Error('API_TOKEN environment variable not found');
|
|
4809
|
-
}
|
|
4810
|
-
|
|
4811
|
-
const archiveBasename = path.basename(archivePath);
|
|
4812
|
-
const buildData = {
|
|
4813
|
-
...buildSettings,
|
|
4814
|
-
node_version: buildSettings?.node_version || 20,
|
|
4815
|
-
source_type: 'archive',
|
|
4816
|
-
source_options: {
|
|
4817
|
-
archive_path: archiveBasename
|
|
4818
|
-
}
|
|
4819
|
-
};
|
|
4820
|
-
|
|
4821
|
-
const config: AxiosRequestConfig = {
|
|
4822
|
-
method: 'post',
|
|
4823
|
-
url,
|
|
4824
|
-
headers: {
|
|
4825
|
-
...this.headers,
|
|
4826
|
-
'Authorization': `Bearer ${bearerToken}`,
|
|
4827
|
-
'Content-Type': 'application/json'
|
|
4828
|
-
},
|
|
4829
|
-
data: buildData,
|
|
4830
|
-
timeout: 60000, // 60s
|
|
4831
|
-
validateStatus: function (status: number): boolean {
|
|
4832
|
-
return status < 500;
|
|
4833
|
-
}
|
|
4834
|
-
};
|
|
4835
|
-
|
|
4836
|
-
const response = await axios(config);
|
|
4837
|
-
|
|
4838
|
-
if (response.status !== 200) {
|
|
4839
|
-
throw new Error(`API returned status ${response.status}: ${JSON.stringify(response.data)}`);
|
|
4840
|
-
}
|
|
4841
|
-
|
|
4842
|
-
this.log('info', `Successfully triggered build for ${domain}`);
|
|
4843
|
-
return response.data;
|
|
4844
|
-
|
|
4845
|
-
} catch (error) {
|
|
4846
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4847
|
-
this.log('error', `Failed to trigger build: ${errorMessage}`);
|
|
4848
|
-
|
|
4849
|
-
if (axios.isAxiosError(error)) {
|
|
4850
|
-
const responseData = error.response?.data;
|
|
4851
|
-
const responseStatus = error.response?.status;
|
|
4852
|
-
this.log('error', 'API Error Details:', {
|
|
4853
|
-
status: responseStatus,
|
|
4854
|
-
data: typeof responseData === 'object' ? JSON.stringify(responseData) : responseData
|
|
4855
|
-
});
|
|
4856
|
-
}
|
|
4857
|
-
|
|
4858
|
-
throw error;
|
|
4859
|
-
}
|
|
4860
|
-
}
|
|
4861
|
-
|
|
4862
|
-
private async handleJavascriptApplicationDeploy(params: Record<string, any>): Promise<any> {
|
|
4863
|
-
const { domain, archivePath, removeArchive = false } = params;
|
|
4864
|
-
|
|
4865
|
-
this.hosting_deployJsApplication_validateRequiredParams(params);
|
|
4866
|
-
this.hosting_deployJsApplication_validateArchiveFile(archivePath);
|
|
4867
|
-
|
|
4868
|
-
// Auto-resolve username from domain
|
|
4869
|
-
this.log('info', `Resolving username from domain: ${domain}`);
|
|
4870
|
-
const username = await this.resolveUsername(domain);
|
|
4871
|
-
|
|
4872
|
-
// Upload archive file
|
|
4873
|
-
this.log('info', `Starting archive upload for ${domain}`);
|
|
4874
|
-
|
|
4875
|
-
let uploadCredentials: any;
|
|
4876
|
-
try {
|
|
4877
|
-
uploadCredentials = await this.fetchUploadCredentials(username, domain);
|
|
4878
|
-
} catch (error) {
|
|
4879
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4880
|
-
throw new Error(`Failed to fetch upload credentials: ${errorMessage}`);
|
|
4881
|
-
}
|
|
4882
|
-
|
|
4883
|
-
const { url: uploadUrl, auth_key: authToken, rest_auth_key: authRestToken } = uploadCredentials;
|
|
4884
|
-
|
|
4885
|
-
if (!uploadUrl || !authToken || !authRestToken) {
|
|
4886
|
-
throw new Error('Invalid upload credentials received from API');
|
|
4887
|
-
}
|
|
4888
|
-
|
|
4889
|
-
const archiveBasename = path.basename(archivePath);
|
|
4890
|
-
let uploadResult: any;
|
|
4891
|
-
try {
|
|
4892
|
-
const stats = fs.statSync(archivePath);
|
|
4893
|
-
uploadResult = await this.uploadFile(
|
|
4894
|
-
archivePath,
|
|
4895
|
-
archiveBasename,
|
|
4896
|
-
uploadUrl,
|
|
4897
|
-
authRestToken,
|
|
4898
|
-
authToken
|
|
4899
|
-
);
|
|
4900
|
-
|
|
4901
|
-
this.log('info', `Successfully uploaded archive: ${archiveBasename}`);
|
|
4902
|
-
} catch (error) {
|
|
4903
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4904
|
-
throw new Error(`Failed to upload archive: ${errorMessage}`);
|
|
4905
|
-
}
|
|
4906
|
-
|
|
4907
|
-
// Fetch build settings
|
|
4908
|
-
let buildSettings: any;
|
|
4909
|
-
try {
|
|
4910
|
-
this.log('info', `Fetching build settings for ${domain}`);
|
|
4911
|
-
buildSettings = await this.hosting_deployJsApplication_fetchBuildSettings(username, domain, archivePath);
|
|
4912
|
-
} catch (error) {
|
|
4913
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4914
|
-
this.log('error', `Failed to fetch build settings: ${errorMessage}`);
|
|
4915
|
-
const archiveRemoved = this.hosting_deployJsApplication_removeArchive(archivePath, removeArchive);
|
|
4916
|
-
|
|
4917
|
-
return {
|
|
4918
|
-
upload: {
|
|
4919
|
-
status: 'success',
|
|
4920
|
-
data: {
|
|
4921
|
-
filename: uploadResult.filename
|
|
4922
|
-
}
|
|
4923
|
-
},
|
|
4924
|
-
resolveSettings: {
|
|
4925
|
-
status: 'error',
|
|
4926
|
-
error: errorMessage
|
|
4927
|
-
},
|
|
4928
|
-
build: {
|
|
4929
|
-
status: 'skipped'
|
|
4930
|
-
},
|
|
4931
|
-
removeArchive: {
|
|
4932
|
-
status: archiveRemoved ? 'success' : 'skipped'
|
|
4933
|
-
}
|
|
4934
|
-
};
|
|
4935
|
-
}
|
|
4936
|
-
|
|
4937
|
-
// Trigger build
|
|
4938
|
-
let buildResult: any;
|
|
4939
|
-
try {
|
|
4940
|
-
this.log('info', `Triggering build for ${domain}`);
|
|
4941
|
-
buildResult = await this.hosting_deployJsApplication_triggerBuild(username, domain, archivePath, buildSettings);
|
|
4942
|
-
} catch (error) {
|
|
4943
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4944
|
-
this.log('error', `Failed to trigger build: ${errorMessage}`);
|
|
4945
|
-
const archiveRemoved = this.hosting_deployJsApplication_removeArchive(archivePath, removeArchive);
|
|
4946
|
-
|
|
4947
|
-
return {
|
|
4948
|
-
upload: {
|
|
4949
|
-
status: 'success',
|
|
4950
|
-
data: {
|
|
4951
|
-
filename: uploadResult.filename
|
|
4952
|
-
}
|
|
4953
|
-
},
|
|
4954
|
-
resolveSettings: {
|
|
4955
|
-
status: 'success',
|
|
4956
|
-
data: buildSettings
|
|
4957
|
-
},
|
|
4958
|
-
build: {
|
|
4959
|
-
status: 'error',
|
|
4960
|
-
error: errorMessage
|
|
4961
|
-
},
|
|
4962
|
-
removeArchive: {
|
|
4963
|
-
status: archiveRemoved ? 'success' : 'skipped'
|
|
4964
|
-
}
|
|
4965
|
-
};
|
|
4966
|
-
}
|
|
4967
|
-
|
|
4968
|
-
const archiveRemoved = this.hosting_deployJsApplication_removeArchive(archivePath, removeArchive);
|
|
4969
|
-
|
|
4970
|
-
return {
|
|
4971
|
-
upload: {
|
|
4972
|
-
status: 'success',
|
|
4973
|
-
data: {
|
|
4974
|
-
filename: uploadResult.filename
|
|
4975
|
-
}
|
|
4976
|
-
},
|
|
4977
|
-
resolveSettings: {
|
|
4978
|
-
status: 'success',
|
|
4979
|
-
data: buildSettings
|
|
4980
|
-
},
|
|
4981
|
-
build: {
|
|
4982
|
-
status: 'success',
|
|
4983
|
-
data: buildResult
|
|
4984
|
-
},
|
|
4985
|
-
removeArchive: {
|
|
4986
|
-
status: archiveRemoved ? 'success' : 'skipped'
|
|
4987
|
-
}
|
|
4988
|
-
};
|
|
4989
|
-
}
|
|
4990
|
-
|
|
4991
|
-
private hosting_deployStaticWebsite_validateArchiveFormat(filePath: string): boolean {
|
|
4992
|
-
const validExtensions = ['zip', 'tar', 'tar.gz', 'tgz', '7z', 'gz', 'gzip'];
|
|
4993
|
-
const fileName = path.basename(filePath).toLowerCase();
|
|
4994
|
-
|
|
4995
|
-
for (const ext of validExtensions) {
|
|
4996
|
-
if (fileName.endsWith(`.${ext}`)) {
|
|
4997
|
-
return true;
|
|
4998
|
-
}
|
|
4999
|
-
}
|
|
5000
|
-
|
|
5001
|
-
return false;
|
|
5002
|
-
}
|
|
5003
|
-
|
|
5004
|
-
private hosting_deployStaticWebsite_validateRequiredParams(params: Record<string, any>): void {
|
|
5005
|
-
const { domain, archivePath, removeArchive } = params;
|
|
5006
|
-
|
|
5007
|
-
if (!domain || typeof domain !== 'string') {
|
|
5008
|
-
throw new Error('domain is required and must be a string');
|
|
5009
|
-
}
|
|
5010
|
-
|
|
5011
|
-
if (!archivePath || typeof archivePath !== 'string') {
|
|
5012
|
-
throw new Error('archivePath is required and must be a string');
|
|
5013
|
-
}
|
|
5014
|
-
|
|
5015
|
-
if (removeArchive !== undefined && typeof removeArchive !== 'boolean') {
|
|
5016
|
-
throw new Error('removeArchive must be a boolean if provided');
|
|
5017
|
-
}
|
|
5018
|
-
}
|
|
5019
|
-
|
|
5020
|
-
private hosting_deployStaticWebsite_removeArchive(archivePath: string, removeArchive: boolean): boolean {
|
|
5021
|
-
if (!removeArchive) {
|
|
5022
|
-
return false;
|
|
5023
|
-
}
|
|
5024
|
-
|
|
5025
|
-
try {
|
|
5026
|
-
this.log('info', `Removing archive file: ${archivePath}`);
|
|
5027
|
-
fs.unlinkSync(archivePath);
|
|
5028
|
-
this.log('info', `Successfully removed archive file: ${archivePath}`);
|
|
5029
|
-
return true;
|
|
5030
|
-
} catch (error) {
|
|
5031
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5032
|
-
this.log('error', `Failed to remove archive file: ${errorMessage}`);
|
|
5033
|
-
return false;
|
|
5034
|
-
}
|
|
5035
|
-
}
|
|
5036
|
-
|
|
5037
|
-
private hosting_deployStaticWebsite_validateArchiveFile(archivePath: string): void {
|
|
5038
|
-
if (!fs.existsSync(archivePath)) {
|
|
5039
|
-
throw new Error(`Archive file not found: ${archivePath}`);
|
|
5040
|
-
}
|
|
5041
|
-
|
|
5042
|
-
const archiveStats = fs.statSync(archivePath);
|
|
5043
|
-
if (!archiveStats.isFile()) {
|
|
5044
|
-
throw new Error(`Archive path is not a file: ${archivePath}`);
|
|
5045
|
-
}
|
|
5046
|
-
|
|
5047
|
-
if (!this.hosting_deployStaticWebsite_validateArchiveFormat(archivePath)) {
|
|
5048
|
-
throw new Error('Invalid archive format. Supported formats: zip, tar, tar.gz, tgz, 7z, gz, gzip');
|
|
5049
|
-
}
|
|
5050
|
-
}
|
|
5051
|
-
|
|
5052
|
-
private async hosting_deployStaticWebsite_triggerDeploy(username: string, domain: string, archivePath: string): Promise<any> {
|
|
5053
|
-
const baseUrl = this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`;
|
|
5054
|
-
const url = new URL(`api/hosting/v1/accounts/${username}/websites/${domain}/deploy`, baseUrl).toString();
|
|
5055
|
-
|
|
5056
|
-
try {
|
|
5057
|
-
const bearerToken = process.env['API_TOKEN'] || process.env['APITOKEN'];
|
|
5058
|
-
if (!bearerToken) {
|
|
5059
|
-
throw new Error('API_TOKEN environment variable not found');
|
|
5060
|
-
}
|
|
5061
|
-
|
|
5062
|
-
const archiveBasename = path.basename(archivePath);
|
|
5063
|
-
const deployData = {
|
|
5064
|
-
archive_path: archiveBasename
|
|
5065
|
-
};
|
|
5066
|
-
|
|
5067
|
-
const config: AxiosRequestConfig = {
|
|
5068
|
-
method: 'post',
|
|
5069
|
-
url,
|
|
5070
|
-
headers: {
|
|
5071
|
-
...this.headers,
|
|
5072
|
-
'Authorization': `Bearer ${bearerToken}`,
|
|
5073
|
-
'Content-Type': 'application/json'
|
|
5074
|
-
},
|
|
5075
|
-
data: deployData,
|
|
5076
|
-
timeout: 60000,
|
|
5077
|
-
validateStatus: function (status: number): boolean {
|
|
5078
|
-
return status < 500;
|
|
5079
|
-
}
|
|
5080
|
-
};
|
|
5081
|
-
|
|
5082
|
-
const response = await axios(config);
|
|
5083
|
-
|
|
5084
|
-
if (response.status !== 200) {
|
|
5085
|
-
throw new Error(`API returned status ${response.status}: ${JSON.stringify(response.data)}`);
|
|
5086
|
-
}
|
|
5087
|
-
|
|
5088
|
-
this.log('info', `Successfully triggered deployment for ${domain}`);
|
|
5089
|
-
return response.data;
|
|
5090
|
-
|
|
5091
|
-
} catch (error) {
|
|
5092
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5093
|
-
this.log('error', `Failed to trigger deployment: ${errorMessage}`);
|
|
5094
|
-
|
|
5095
|
-
if (axios.isAxiosError(error)) {
|
|
5096
|
-
const responseData = error.response?.data;
|
|
5097
|
-
const responseStatus = error.response?.status;
|
|
5098
|
-
this.log('error', 'API Error Details:', {
|
|
5099
|
-
status: responseStatus,
|
|
5100
|
-
data: typeof responseData === 'object' ? JSON.stringify(responseData) : responseData
|
|
5101
|
-
});
|
|
5102
|
-
}
|
|
5103
|
-
|
|
5104
|
-
throw error;
|
|
5105
|
-
}
|
|
5106
|
-
}
|
|
5107
|
-
|
|
5108
|
-
private async handleStaticWebsiteDeploy(params: Record<string, any>): Promise<any> {
|
|
5109
|
-
const { domain, archivePath, removeArchive = false } = params;
|
|
5110
|
-
|
|
5111
|
-
this.hosting_deployStaticWebsite_validateRequiredParams(params);
|
|
5112
|
-
this.hosting_deployStaticWebsite_validateArchiveFile(archivePath);
|
|
5113
|
-
|
|
5114
|
-
this.log('info', `Resolving username from domain: ${domain}`);
|
|
5115
|
-
const username = await this.resolveUsername(domain);
|
|
5116
|
-
|
|
5117
|
-
this.log('info', `Starting archive upload for ${domain}`);
|
|
5118
|
-
|
|
5119
|
-
let uploadCredentials: any;
|
|
5120
|
-
try {
|
|
5121
|
-
uploadCredentials = await this.fetchUploadCredentials(username, domain);
|
|
5122
|
-
} catch (error) {
|
|
5123
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5124
|
-
throw new Error(`Failed to fetch upload credentials: ${errorMessage}`);
|
|
5125
|
-
}
|
|
5126
|
-
|
|
5127
|
-
const { url: uploadUrl, auth_key: authToken, rest_auth_key: authRestToken } = uploadCredentials;
|
|
5128
|
-
|
|
5129
|
-
if (!uploadUrl || !authToken || !authRestToken) {
|
|
5130
|
-
throw new Error('Invalid upload credentials received from API');
|
|
5131
|
-
}
|
|
5132
|
-
|
|
5133
|
-
const archiveBasename = path.basename(archivePath);
|
|
5134
|
-
let uploadResult: any;
|
|
5135
|
-
try {
|
|
5136
|
-
const stats = fs.statSync(archivePath);
|
|
5137
|
-
uploadResult = await this.uploadFile(
|
|
5138
|
-
archivePath,
|
|
5139
|
-
archiveBasename,
|
|
5140
|
-
uploadUrl,
|
|
5141
|
-
authRestToken,
|
|
5142
|
-
authToken
|
|
5143
|
-
);
|
|
5144
|
-
|
|
5145
|
-
this.log('info', `Successfully uploaded archive: ${archiveBasename}`);
|
|
5146
|
-
} catch (error) {
|
|
5147
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5148
|
-
throw new Error(`Failed to upload archive: ${errorMessage}`);
|
|
5149
|
-
}
|
|
5150
|
-
|
|
5151
|
-
let deployResult: any;
|
|
5152
|
-
try {
|
|
5153
|
-
this.log('info', `Triggering deployment for ${domain}`);
|
|
5154
|
-
deployResult = await this.hosting_deployStaticWebsite_triggerDeploy(username, domain, archivePath);
|
|
5155
|
-
} catch (error) {
|
|
5156
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5157
|
-
this.log('error', `Failed to trigger deployment: ${errorMessage}`);
|
|
5158
|
-
const archiveRemoved = this.hosting_deployStaticWebsite_removeArchive(archivePath, removeArchive);
|
|
5159
|
-
|
|
5160
|
-
return {
|
|
5161
|
-
upload: {
|
|
5162
|
-
status: 'success',
|
|
5163
|
-
data: {
|
|
5164
|
-
filename: uploadResult.filename
|
|
5165
|
-
}
|
|
5166
|
-
},
|
|
5167
|
-
deploy: {
|
|
5168
|
-
status: 'error',
|
|
5169
|
-
error: errorMessage
|
|
5170
|
-
},
|
|
5171
|
-
removeArchive: {
|
|
5172
|
-
status: archiveRemoved ? 'success' : 'skipped'
|
|
5173
|
-
}
|
|
5174
|
-
};
|
|
5175
|
-
}
|
|
5176
|
-
|
|
5177
|
-
const archiveRemoved = this.hosting_deployStaticWebsite_removeArchive(archivePath, removeArchive);
|
|
5178
|
-
|
|
5179
|
-
return {
|
|
5180
|
-
upload: {
|
|
5181
|
-
status: 'success',
|
|
5182
|
-
data: {
|
|
5183
|
-
filename: uploadResult.filename
|
|
5184
|
-
}
|
|
5185
|
-
},
|
|
5186
|
-
deploy: {
|
|
5187
|
-
status: 'success',
|
|
5188
|
-
data: deployResult
|
|
5189
|
-
},
|
|
5190
|
-
removeArchive: {
|
|
5191
|
-
status: archiveRemoved ? 'success' : 'skipped'
|
|
5192
|
-
}
|
|
5193
|
-
};
|
|
5194
|
-
}
|
|
5195
|
-
|
|
5196
|
-
private hosting_listJsDeployments_validateRequiredParams(params: Record<string, any>): void {
|
|
5197
|
-
const { domain } = params;
|
|
5198
|
-
|
|
5199
|
-
if (!domain || typeof domain !== 'string') {
|
|
5200
|
-
throw new Error('domain is required and must be a string');
|
|
5201
|
-
}
|
|
5202
|
-
}
|
|
5203
|
-
|
|
5204
|
-
private hosting_listJsDeployments_buildQueryParams(params: Record<string, any>): string {
|
|
5205
|
-
const { page, perPage, states } = params;
|
|
5206
|
-
const queryParams = new URLSearchParams();
|
|
5207
|
-
|
|
5208
|
-
if (page !== undefined && page !== null) {
|
|
5209
|
-
queryParams.append('page', page.toString());
|
|
5210
|
-
}
|
|
5211
|
-
|
|
5212
|
-
if (perPage !== undefined && perPage !== null) {
|
|
5213
|
-
queryParams.append('per_page', perPage.toString());
|
|
5214
|
-
}
|
|
5215
|
-
|
|
5216
|
-
if (states && Array.isArray(states) && states.length > 0) {
|
|
5217
|
-
states.forEach(state => {
|
|
5218
|
-
queryParams.append('states[]', state);
|
|
5219
|
-
});
|
|
5220
|
-
}
|
|
5221
|
-
|
|
5222
|
-
return queryParams.toString();
|
|
5223
|
-
}
|
|
5224
|
-
|
|
5225
|
-
private async hosting_listJsDeployments_fetchDeployments(username: string, domain: string, queryParams: string): Promise<any> {
|
|
5226
|
-
const baseUrl = this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`;
|
|
5227
|
-
const url = new URL(`api/hosting/v1/accounts/${username}/websites/${domain}/nodejs/builds`, baseUrl).toString();
|
|
5228
|
-
|
|
5229
|
-
const fullUrl = queryParams ? `${url}?${queryParams}` : url;
|
|
5230
|
-
|
|
5231
|
-
try {
|
|
5232
|
-
const bearerToken = process.env['API_TOKEN'] || process.env['APITOKEN'];
|
|
5233
|
-
if (!bearerToken) {
|
|
5234
|
-
throw new Error('API_TOKEN environment variable not found');
|
|
5235
|
-
}
|
|
5236
|
-
|
|
5237
|
-
const config: AxiosRequestConfig = {
|
|
5238
|
-
method: 'get',
|
|
5239
|
-
url: fullUrl,
|
|
5240
|
-
headers: {
|
|
5241
|
-
...this.headers,
|
|
5242
|
-
'Authorization': `Bearer ${bearerToken}`
|
|
5243
|
-
},
|
|
5244
|
-
timeout: 60000, // 60s
|
|
5245
|
-
validateStatus: function (status: number): boolean {
|
|
5246
|
-
return status < 500;
|
|
5247
|
-
}
|
|
5248
|
-
};
|
|
5249
|
-
|
|
5250
|
-
const response = await axios(config);
|
|
5251
|
-
|
|
5252
|
-
if (response.status !== 200) {
|
|
5253
|
-
throw new Error(`API returned status ${response.status}: ${JSON.stringify(response.data)}`);
|
|
5254
|
-
}
|
|
5255
|
-
|
|
5256
|
-
this.log('info', `Successfully fetched deployments for ${domain}`);
|
|
5257
|
-
return response.data;
|
|
5258
|
-
|
|
5259
|
-
} catch (error) {
|
|
5260
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5261
|
-
this.log('error', `Failed to fetch deployments: ${errorMessage}`);
|
|
5262
|
-
|
|
5263
|
-
if (axios.isAxiosError(error)) {
|
|
5264
|
-
const responseData = error.response?.data;
|
|
5265
|
-
const responseStatus = error.response?.status;
|
|
5266
|
-
this.log('error', 'API Error Details:', {
|
|
5267
|
-
status: responseStatus,
|
|
5268
|
-
data: typeof responseData === 'object' ? JSON.stringify(responseData) : responseData
|
|
5269
|
-
});
|
|
5270
|
-
}
|
|
5271
|
-
|
|
5272
|
-
throw error;
|
|
5273
|
-
}
|
|
5274
|
-
}
|
|
5275
|
-
|
|
5276
|
-
private async handleListJavascriptDeployments(params: Record<string, any>): Promise<any> {
|
|
5277
|
-
const { domain, page, perPage, states } = params;
|
|
5278
|
-
|
|
5279
|
-
this.hosting_listJsDeployments_validateRequiredParams(params);
|
|
5280
|
-
|
|
5281
|
-
// Auto-resolve username from domain
|
|
5282
|
-
this.log('info', `Resolving username from domain: ${domain}`);
|
|
5283
|
-
const username = await this.resolveUsername(domain);
|
|
5284
|
-
|
|
5285
|
-
// Build query parameters
|
|
5286
|
-
const queryParams = this.hosting_listJsDeployments_buildQueryParams(params);
|
|
5287
|
-
|
|
5288
|
-
// Fetch deployments
|
|
5289
|
-
let deployments: any;
|
|
5290
|
-
try {
|
|
5291
|
-
this.log('info', `Fetching deployments for ${domain}`);
|
|
5292
|
-
deployments = await this.hosting_listJsDeployments_fetchDeployments(username, domain, queryParams);
|
|
5293
|
-
} catch (error) {
|
|
5294
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5295
|
-
this.log('error', `Failed to fetch deployments: ${errorMessage}`);
|
|
5296
|
-
throw error;
|
|
5297
|
-
}
|
|
5298
|
-
|
|
5299
|
-
return {
|
|
5300
|
-
status: 'success',
|
|
5301
|
-
domain,
|
|
5302
|
-
username,
|
|
5303
|
-
queryParams: {
|
|
5304
|
-
page,
|
|
5305
|
-
perPage,
|
|
5306
|
-
states
|
|
5307
|
-
},
|
|
5308
|
-
deployments
|
|
5309
|
-
};
|
|
5310
|
-
}
|
|
5311
|
-
|
|
5312
|
-
private hosting_showJsDeploymentLogs_validateRequiredParams(params: Record<string, any>): void {
|
|
5313
|
-
const { domain, buildUuid, fromLine } = params;
|
|
5314
|
-
|
|
5315
|
-
if (!domain || typeof domain !== 'string') {
|
|
5316
|
-
throw new Error('domain is required and must be a string');
|
|
5317
|
-
}
|
|
5318
|
-
|
|
5319
|
-
if (!buildUuid || typeof buildUuid !== 'string') {
|
|
5320
|
-
throw new Error('buildUuid is required and must be a string');
|
|
5321
|
-
}
|
|
5322
|
-
|
|
5323
|
-
if (fromLine !== undefined && (typeof fromLine !== 'number' || !Number.isInteger(fromLine) || fromLine < 0)) {
|
|
5324
|
-
throw new Error('fromLine must be a non-negative integer when provided');
|
|
5325
|
-
}
|
|
5326
|
-
}
|
|
5327
|
-
|
|
5328
|
-
private hosting_showJsDeploymentLogs_buildQueryParams(params: Record<string, any>): string {
|
|
5329
|
-
const { fromLine } = params;
|
|
5330
|
-
const queryParams = new URLSearchParams();
|
|
5331
|
-
|
|
5332
|
-
const line = (typeof fromLine === 'number' && Number.isInteger(fromLine) && fromLine >= 0) ? fromLine : 0;
|
|
5333
|
-
queryParams.append('from_line', line.toString());
|
|
5334
|
-
|
|
5335
|
-
return queryParams.toString();
|
|
5336
|
-
}
|
|
5337
|
-
|
|
5338
|
-
private async hosting_showJsDeploymentLogs_fetchLogs(username: string, domain: string, buildUuid: string, queryParams: string): Promise<any> {
|
|
5339
|
-
const baseUrl = this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`;
|
|
5340
|
-
const url = new URL(`api/hosting/v1/accounts/${username}/websites/${domain}/nodejs/builds/${buildUuid}/logs`, baseUrl).toString();
|
|
5341
|
-
|
|
5342
|
-
const fullUrl = queryParams ? `${url}?${queryParams}` : url;
|
|
5343
|
-
|
|
5344
|
-
try {
|
|
5345
|
-
const bearerToken = process.env['API_TOKEN'] || process.env['APITOKEN'];
|
|
5346
|
-
if (!bearerToken) {
|
|
5347
|
-
throw new Error('API_TOKEN environment variable not found');
|
|
5348
|
-
}
|
|
5349
|
-
|
|
5350
|
-
const config: AxiosRequestConfig = {
|
|
5351
|
-
method: 'get',
|
|
5352
|
-
url: fullUrl,
|
|
5353
|
-
headers: {
|
|
5354
|
-
...this.headers,
|
|
5355
|
-
'Authorization': `Bearer ${bearerToken}`
|
|
5356
|
-
},
|
|
5357
|
-
timeout: 60000,
|
|
5358
|
-
validateStatus: function (status: number): boolean {
|
|
5359
|
-
return status < 500;
|
|
5360
|
-
}
|
|
5361
|
-
};
|
|
5362
|
-
|
|
5363
|
-
const response = await axios(config);
|
|
5364
|
-
|
|
5365
|
-
if (response.status !== 200) {
|
|
5366
|
-
throw new Error(`API returned status ${response.status}: ${JSON.stringify(response.data)}`);
|
|
5367
|
-
}
|
|
5368
|
-
|
|
5369
|
-
this.log('info', `Successfully fetched logs for ${domain} build ${buildUuid}`);
|
|
5370
|
-
return response.data;
|
|
5371
|
-
|
|
5372
|
-
} catch (error) {
|
|
5373
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5374
|
-
this.log('error', `Failed to fetch logs: ${errorMessage}`);
|
|
5375
|
-
|
|
5376
|
-
if (axios.isAxiosError(error)) {
|
|
5377
|
-
const responseData = error.response?.data;
|
|
5378
|
-
const responseStatus = error.response?.status;
|
|
5379
|
-
this.log('error', 'API Error Details:', {
|
|
5380
|
-
status: responseStatus,
|
|
5381
|
-
data: typeof responseData === 'object' ? JSON.stringify(responseData) : responseData
|
|
5382
|
-
});
|
|
5383
|
-
}
|
|
5384
|
-
|
|
5385
|
-
throw error;
|
|
5386
|
-
}
|
|
5387
|
-
}
|
|
5388
|
-
|
|
5389
|
-
private async handleShowJsDeploymentLogs(params: Record<string, any>): Promise<any> {
|
|
5390
|
-
const { domain, buildUuid, fromLine } = params;
|
|
5391
|
-
|
|
5392
|
-
this.hosting_showJsDeploymentLogs_validateRequiredParams(params);
|
|
5393
|
-
|
|
5394
|
-
this.log('info', `Resolving username from domain: ${domain}`);
|
|
5395
|
-
const username = await this.resolveUsername(domain);
|
|
5396
|
-
|
|
5397
|
-
const queryParams = this.hosting_showJsDeploymentLogs_buildQueryParams(params);
|
|
5398
|
-
|
|
5399
|
-
let logs: any;
|
|
5400
|
-
try {
|
|
5401
|
-
this.log('info', `Fetching logs for ${domain}, build ${buildUuid}`);
|
|
5402
|
-
logs = await this.hosting_showJsDeploymentLogs_fetchLogs(username, domain, buildUuid, queryParams);
|
|
5403
|
-
} catch (error) {
|
|
5404
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5405
|
-
this.log('error', `Failed to fetch logs: ${errorMessage}`);
|
|
5406
|
-
throw error;
|
|
5407
|
-
}
|
|
5408
|
-
|
|
5409
|
-
const effectiveFromLine = (typeof fromLine === 'number' && Number.isInteger(fromLine) && fromLine >= 0) ? fromLine : 0;
|
|
5410
|
-
|
|
5411
|
-
return {
|
|
5412
|
-
domain,
|
|
5413
|
-
username,
|
|
5414
|
-
buildUuid,
|
|
5415
|
-
fromLine: effectiveFromLine,
|
|
5416
|
-
logs
|
|
5417
|
-
};
|
|
5418
|
-
}
|
|
5419
|
-
|
|
5420
|
-
/**
|
|
5421
|
-
* Execute an API call for a tool
|
|
5422
|
-
*/
|
|
5423
|
-
private async executeApiCall(tool: OpenApiTool, params: Record<string, any>): Promise<any> {
|
|
5424
|
-
// Get method and path from tool
|
|
5425
|
-
const method = tool.method;
|
|
5426
|
-
let path = tool.path;
|
|
5427
|
-
|
|
5428
|
-
// Clone params to avoid modifying the original
|
|
5429
|
-
const requestParams = { ...params };
|
|
5430
|
-
|
|
5431
|
-
// Replace path parameters with values from params
|
|
5432
|
-
Object.entries(requestParams).forEach(([key, value]) => {
|
|
5433
|
-
const placeholder = `{${key}}`;
|
|
5434
|
-
if (path.includes(placeholder)) {
|
|
5435
|
-
path = path.replace(placeholder, encodeURIComponent(String(value)));
|
|
5436
|
-
delete requestParams[key]; // Remove used parameter
|
|
5437
|
-
}
|
|
5438
|
-
});
|
|
5439
|
-
|
|
5440
|
-
// Build the full URL
|
|
5441
|
-
const baseUrl = this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`;
|
|
5442
|
-
const cleanPath = path.startsWith("/") ? path.slice(1) : path;
|
|
5443
|
-
const url = new URL(cleanPath, baseUrl).toString();
|
|
5444
|
-
|
|
5445
|
-
this.log('debug', `API Request: ${method} ${url}`);
|
|
5446
|
-
|
|
5447
|
-
try {
|
|
5448
|
-
// Configure the request
|
|
5449
|
-
const config: AxiosRequestConfig = {
|
|
5450
|
-
method: method.toLowerCase(),
|
|
5451
|
-
url,
|
|
5452
|
-
headers: { ...this.headers },
|
|
5453
|
-
timeout: 60000, // 60s
|
|
5454
|
-
validateStatus: function (status: number): boolean {
|
|
5455
|
-
return status < 500; // Resolve only if the status code is less than 500
|
|
5456
|
-
}
|
|
5457
|
-
};
|
|
5458
|
-
|
|
5459
|
-
const bearerToken = process.env['API_TOKEN'] || process.env['APITOKEN']; // APITOKEN for backwards compatibility
|
|
5460
|
-
if (bearerToken && config.headers) {
|
|
5461
|
-
config.headers['Authorization'] = `Bearer ${bearerToken}`;
|
|
5462
|
-
} else {
|
|
5463
|
-
this.log('error', `Bearer Token environment variable not found: API_TOKEN`);
|
|
5464
|
-
}
|
|
5465
|
-
|
|
5466
|
-
// Add parameters based on request method
|
|
5467
|
-
if (["GET", "DELETE"].includes(method)) {
|
|
5468
|
-
// For GET/DELETE, send params as query string
|
|
5469
|
-
config.params = { ...(config.params || {}), ...requestParams };
|
|
5470
|
-
} else {
|
|
5471
|
-
// For POST/PUT/PATCH, send params as JSON body
|
|
5472
|
-
config.data = requestParams;
|
|
5473
|
-
if (config.headers) {
|
|
5474
|
-
config.headers["Content-Type"] = "application/json";
|
|
5475
|
-
}
|
|
5476
|
-
}
|
|
5477
|
-
|
|
5478
|
-
this.log('debug', "Request config:", {
|
|
5479
|
-
url: config.url,
|
|
5480
|
-
method: config.method,
|
|
5481
|
-
params: config.params,
|
|
5482
|
-
headers: config.headers ? Object.keys(config.headers) : []
|
|
5483
|
-
});
|
|
5484
|
-
|
|
5485
|
-
// Execute the request
|
|
5486
|
-
const response = await axios(config);
|
|
5487
|
-
this.log('debug', `Response status: ${response.status}`);
|
|
5488
|
-
|
|
5489
|
-
return response.data;
|
|
5490
|
-
|
|
5491
|
-
} catch (error) {
|
|
5492
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5493
|
-
this.log('error', `API request failed: ${errorMessage}`);
|
|
5494
|
-
|
|
5495
|
-
if (axios.isAxiosError(error)) {
|
|
5496
|
-
const axiosError = error as AxiosError;
|
|
5497
|
-
const responseData = axiosError.response?.data;
|
|
5498
|
-
const responseStatus = axiosError.response?.status;
|
|
5499
|
-
|
|
5500
|
-
this.log('error', 'API Error Details:', {
|
|
5501
|
-
status: responseStatus,
|
|
5502
|
-
data: typeof responseData === 'object' ? JSON.stringify(responseData) : String(responseData)
|
|
5503
|
-
});
|
|
5504
|
-
|
|
5505
|
-
// Rethrow with more context for better error handling
|
|
5506
|
-
const detailedError = new Error(`API request failed with status ${responseStatus}: ${errorMessage}`);
|
|
5507
|
-
(detailedError as any).response = axiosError.response;
|
|
5508
|
-
throw detailedError;
|
|
5509
|
-
}
|
|
5510
|
-
|
|
5511
|
-
throw error;
|
|
5512
|
-
}
|
|
5513
|
-
}
|
|
5514
|
-
|
|
5515
|
-
/**
|
|
5516
|
-
* Log messages with appropriate level
|
|
5517
|
-
* Only sends to MCP if we're connected
|
|
5518
|
-
*/
|
|
5519
|
-
private log(level: 'debug' | 'info' | 'warning' | 'error', message: string, data?: any): void {
|
|
5520
|
-
// Always log to stderr for visibility
|
|
5521
|
-
console.error(`[${level.toUpperCase()}] ${message}${data ? ': ' + JSON.stringify(data) : ''}`);
|
|
5522
|
-
|
|
5523
|
-
// Only try to send via MCP if we're in debug mode or it's important
|
|
5524
|
-
if (this.debug || level !== 'debug') {
|
|
5525
|
-
try {
|
|
5526
|
-
// Only send if server exists and is connected
|
|
5527
|
-
if (this.server && (this.server as any).isConnected) {
|
|
5528
|
-
this.server.sendLoggingMessage({
|
|
5529
|
-
level,
|
|
5530
|
-
data: `[MCP Server] ${message}${data ? ': ' + JSON.stringify(data) : ''}`
|
|
5531
|
-
});
|
|
5532
|
-
}
|
|
5533
|
-
} catch (e) {
|
|
5534
|
-
// If logging fails, log to stderr
|
|
5535
|
-
console.error('Failed to send log via MCP:', (e as Error).message);
|
|
5536
|
-
}
|
|
5537
|
-
}
|
|
5538
|
-
}
|
|
5539
|
-
|
|
5540
|
-
/**
|
|
5541
|
-
* Create and configure Express app with shared middleware
|
|
5542
|
-
*/
|
|
5543
|
-
createApp() {
|
|
5544
|
-
const app = express();
|
|
5545
|
-
app.use(express.json());
|
|
5546
|
-
app.use(cors());
|
|
5547
|
-
return app;
|
|
5548
|
-
}
|
|
5549
|
-
|
|
5550
|
-
/**
|
|
5551
|
-
* Start the server with HTTP streaming transport
|
|
5552
|
-
*/
|
|
5553
|
-
async startHttp(host: string, port: number): Promise<void> {
|
|
5554
|
-
try {
|
|
5555
|
-
const app = this.createApp();
|
|
5556
|
-
|
|
5557
|
-
// Create HTTP transport with session management
|
|
5558
|
-
const httpTransport = new StreamableHTTPServerTransport({
|
|
5559
|
-
sessionIdGenerator: () => {
|
|
5560
|
-
// Generate a simple session ID
|
|
5561
|
-
return `session-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
5562
|
-
},
|
|
5563
|
-
});
|
|
5564
|
-
|
|
5565
|
-
// Set up CORS for all routes
|
|
5566
|
-
app.options("*", (req, res) => {
|
|
5567
|
-
res.header("Access-Control-Allow-Origin", "*");
|
|
5568
|
-
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
5569
|
-
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, x-session-id");
|
|
5570
|
-
res.sendStatus(200);
|
|
5571
|
-
});
|
|
5572
|
-
|
|
5573
|
-
// Health check endpoint
|
|
5574
|
-
app.get("/health", (req, res) => {
|
|
5575
|
-
res.status(200).json({ status: "ok", transport: "http" });
|
|
5576
|
-
});
|
|
5577
|
-
|
|
5578
|
-
// Set up the HTTP transport endpoint
|
|
5579
|
-
app.post("/", async (req, res) => {
|
|
5580
|
-
res.header("Access-Control-Allow-Origin", "*");
|
|
5581
|
-
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
5582
|
-
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, x-session-id");
|
|
5583
|
-
await httpTransport.handleRequest(req, res, req.body);
|
|
5584
|
-
});
|
|
5585
|
-
|
|
5586
|
-
// Start the server
|
|
5587
|
-
const server = app.listen(port, host, () => {
|
|
5588
|
-
this.log('info', `MCP Server with HTTP streaming transport started successfully with ${this.tools.size} tools`);
|
|
5589
|
-
this.log('info', `Listening on http://${host}:${port}`);
|
|
5590
|
-
});
|
|
5591
|
-
|
|
5592
|
-
// Connect the MCP server to the transport
|
|
5593
|
-
await this.server.connect(httpTransport);
|
|
5594
|
-
|
|
5595
|
-
} catch (error) {
|
|
5596
|
-
console.error("Failed to start MCP server:", error);
|
|
5597
|
-
process.exit(1);
|
|
5598
|
-
}
|
|
5599
|
-
}
|
|
5600
|
-
|
|
5601
|
-
/**
|
|
5602
|
-
* Start the stdio server
|
|
5603
|
-
*/
|
|
5604
|
-
async startStdio(): Promise<void> {
|
|
5605
|
-
try {
|
|
5606
|
-
// Create stdio transport
|
|
5607
|
-
const transport = new StdioServerTransport();
|
|
5608
|
-
console.error("MCP Server starting on stdio transport");
|
|
5609
|
-
|
|
5610
|
-
// Connect to the transport
|
|
5611
|
-
await this.server.connect(transport);
|
|
5612
|
-
|
|
5613
|
-
// Now we can safely log via MCP
|
|
5614
|
-
console.error(`Registered ${this.tools.size} tools`);
|
|
5615
|
-
this.log('info', `MCP Server with stdio transport started successfully with ${this.tools.size} tools`);
|
|
5616
|
-
} catch (error) {
|
|
5617
|
-
console.error("Failed to start MCP server:", error);
|
|
5618
|
-
process.exit(1);
|
|
5619
|
-
}
|
|
5620
|
-
}
|
|
5621
|
-
}
|
|
5622
|
-
|
|
5623
|
-
// Start the server
|
|
5624
|
-
async function main(): Promise<void> {
|
|
5625
|
-
try {
|
|
5626
|
-
const argv = minimist(process.argv.slice(2), {
|
|
5627
|
-
string: ['host'],
|
|
5628
|
-
boolean: ['stdio', 'http', 'help'],
|
|
5629
|
-
default: {
|
|
5630
|
-
host: '127.0.0.1',
|
|
5631
|
-
port: 8100,
|
|
5632
|
-
stdio: true,
|
|
5633
|
-
}
|
|
5634
|
-
});
|
|
5635
|
-
|
|
5636
|
-
// Show help if requested
|
|
5637
|
-
if (argv.help) {
|
|
5638
|
-
console.log(`
|
|
5639
|
-
Hostinger API MCP Server
|
|
5640
|
-
Usage: hostinger-api-mcp [options]
|
|
5641
|
-
Options:
|
|
5642
|
-
--http Use HTTP streaming transport
|
|
5643
|
-
--stdio Use standard input/output transport (default)
|
|
5644
|
-
--host <host> Host to bind to (default: 127.0.0.1)
|
|
5645
|
-
--port <port> Port to bind to (default: 8100)
|
|
5646
|
-
--help Show this help message
|
|
5647
|
-
Environment Variables:
|
|
5648
|
-
API_TOKEN Your Hostinger API token (required)
|
|
5649
|
-
DEBUG Enable debug logging (true/false)
|
|
5650
|
-
`);
|
|
5651
|
-
process.exit(0);
|
|
5652
|
-
}
|
|
5653
|
-
|
|
5654
|
-
const server = new MCPServer();
|
|
5655
|
-
if (argv.http) {
|
|
5656
|
-
await server.startHttp(argv.host, argv.port);
|
|
5657
|
-
} else {
|
|
5658
|
-
await server.startStdio();
|
|
5659
|
-
}
|
|
5660
|
-
} catch (error) {
|
|
5661
|
-
console.error("Failed to start server:", error);
|
|
5662
|
-
process.exit(1);
|
|
5663
|
-
}
|
|
5664
|
-
}
|
|
5665
|
-
|
|
5666
|
-
main();
|