Compare commits

..

28 Commits

Author SHA1 Message Date
philkunz bc53e9f872 1.1.0 2024-06-15 12:26:30 +02:00
philkunz 646ab4d18c feat(volumes and firewalls): enable volumes and firewalls management. 2024-06-15 12:26:29 +02:00
philkunz aaeb025217 1.0.18 2024-02-29 12:01:54 +01:00
philkunz 21e37a7a16 fix(core): update 2024-02-29 12:01:53 +01:00
philkunz 4e0dfb0356 1.0.17 2024-02-18 23:45:04 +01:00
philkunz 84780647da fix(core): update 2024-02-18 23:45:03 +01:00
philkunz 1645d7881a 1.0.16 2024-02-18 23:43:41 +01:00
philkunz 88351db20f fix(core): update 2024-02-18 23:43:40 +01:00
philkunz 18cbf8693f 1.0.15 2024-02-18 23:41:51 +01:00
philkunz 7d64a2bc4d fix(core): update 2024-02-18 23:41:50 +01:00
philkunz 4c327e5e32 1.0.14 2024-02-18 23:25:57 +01:00
philkunz a898928bd3 fix(core): update 2024-02-18 23:25:57 +01:00
philkunz aba8182422 1.0.13 2024-02-18 23:23:21 +01:00
philkunz 5412ab524b fix(core): update 2024-02-18 23:23:20 +01:00
philkunz 2f175e9d64 1.0.12 2024-02-18 01:17:15 +01:00
philkunz d2b1018234 fix(core): update 2024-02-18 01:17:15 +01:00
philkunz e7babf5222 1.0.11 2024-02-17 21:59:11 +01:00
philkunz 63660ecc03 fix(core): update 2024-02-17 21:59:10 +01:00
philkunz 336e27f383 1.0.10 2024-02-17 21:58:11 +01:00
philkunz 9174571ea4 fix(core): update 2024-02-17 21:58:10 +01:00
philkunz 4a722b79b7 1.0.9 2024-02-17 21:55:53 +01:00
philkunz 866dc35403 fix(core): update 2024-02-17 21:55:52 +01:00
philkunz 84d74a131a 1.0.8 2024-02-17 21:55:26 +01:00
philkunz 929404fadd fix(core): update 2024-02-17 21:55:25 +01:00
philkunz 332a4a4195 1.0.7 2024-02-17 20:41:22 +01:00
philkunz 07d625fa1f fix(core): update 2024-02-17 20:41:21 +01:00
philkunz e7cafd9c1c 1.0.6 2024-01-29 21:17:14 +01:00
philkunz 60725ecdcb fix(core): update 2024-01-29 21:17:14 +01:00
13 changed files with 386 additions and 74 deletions
+5 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@apiclient.xyz/hetznercloud",
"version": "1.0.5",
"version": "1.1.0",
"private": false,
"description": "an unofficial api client for the hetzner cloud api",
"main": "dist_ts/index.js",
@@ -19,12 +19,14 @@
"@git.zone/tsbundle": "^2.0.5",
"@git.zone/tsrun": "^1.2.46",
"@git.zone/tstest": "^1.0.44",
"@push.rocks/qenv": "^6.0.5",
"@push.rocks/tapbundle": "^5.0.15",
"@types/node": "^20.8.7"
"@types/node": "^20.11.22"
},
"dependencies": {
"@push.rocks/smartrequest": "^2.0.21",
"@tempfix/hetzner-openapi": "^1.0.4"
"@tempfix/hetzner-openapi": "^1.0.4",
"@tsclass/tsclass": "^4.0.52"
},
"repository": {
"type": "git",
+68 -48
View File
@@ -11,6 +11,9 @@ dependencies:
'@tempfix/hetzner-openapi':
specifier: ^1.0.4
version: 1.0.4
'@tsclass/tsclass':
specifier: ^4.0.52
version: 4.0.52
devDependencies:
'@git.zone/tsbuild':
@@ -21,16 +24,19 @@ devDependencies:
version: 2.0.15
'@git.zone/tsrun':
specifier: ^1.2.46
version: 1.2.46(@types/node@20.11.10)
version: 1.2.46(@types/node@20.11.22)
'@git.zone/tstest':
specifier: ^1.0.44
version: 1.0.86(@types/node@20.11.10)(sinon@17.0.1)
version: 1.0.86(@types/node@20.11.22)(sinon@17.0.1)
'@push.rocks/qenv':
specifier: ^6.0.5
version: 6.0.5
'@push.rocks/tapbundle':
specifier: ^5.0.15
version: 5.0.15(sinon@17.0.1)
'@types/node':
specifier: ^20.8.7
version: 20.11.10
specifier: ^20.11.22
version: 20.11.22
packages:
@@ -74,7 +80,7 @@ packages:
'@push.rocks/smartstream': 3.0.30
'@push.rocks/smarttime': 4.0.6
'@push.rocks/webstore': 2.0.13
'@tsclass/tsclass': 4.0.46
'@tsclass/tsclass': 4.0.52
'@types/express': 4.17.21
body-parser: 1.20.2
cors: 2.8.5
@@ -134,6 +140,12 @@ packages:
js-tokens: 4.0.0
dev: true
/@configvault.io/interfaces@1.0.17:
resolution: {integrity: sha512-bEcCUR2VBDJsTin8HQh8Uw/mlYl2v8A3jMIaQ+MTB9Hrqd6CZL2dL7iJdWyFl/3EIX+LDxWFR+Oq7liIq7w+1Q==}
dependencies:
'@api.global/typedrequest-interfaces': 3.0.1
dev: true
/@cspotcode/source-map-support@0.8.1:
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
@@ -383,13 +395,13 @@ packages:
- supports-color
dev: true
/@git.zone/tsrun@1.2.46(@types/node@20.11.10):
/@git.zone/tsrun@1.2.46(@types/node@20.11.22):
resolution: {integrity: sha512-8miFVBle9Mnjx+uPGI/P+EuWcIOXWjBAkdjN5IYbdp5Ytt4xQODCLh4JSnC9h56UeU1nUxCAxZeJs2e9TXrivA==}
hasBin: true
dependencies:
'@push.rocks/smartfile': 10.0.41
'@push.rocks/smartshell': 3.0.3
ts-node: 10.9.2(@types/node@20.11.10)(typescript@5.1.6)
ts-node: 10.9.2(@types/node@20.11.22)(typescript@5.1.6)
typescript: 5.1.6
transitivePeerDependencies:
- '@swc/core'
@@ -397,13 +409,13 @@ packages:
- '@types/node'
dev: true
/@git.zone/tstest@1.0.86(@types/node@20.11.10)(sinon@17.0.1):
/@git.zone/tstest@1.0.86(@types/node@20.11.22)(sinon@17.0.1):
resolution: {integrity: sha512-ec95MNeA21L+ob+lvLVCmwPTTC1BY+v/JHLYZ9DOZ9+9buLgx+cJ7VkwGBJCnlWJtqEtJosUrFKTih36iNuT3g==}
hasBin: true
dependencies:
'@api.global/typedserver': 3.0.20
'@git.zone/tsbundle': 2.0.15
'@git.zone/tsrun': 1.2.46(@types/node@20.11.10)
'@git.zone/tsrun': 1.2.46(@types/node@20.11.22)
'@push.rocks/consolecolor': 2.0.1
'@push.rocks/smartbrowser': 2.0.6
'@push.rocks/smartdelay': 3.0.5
@@ -596,6 +608,16 @@ packages:
symbol-tree: 3.2.4
dev: true
/@push.rocks/qenv@6.0.5:
resolution: {integrity: sha512-Id/eSKKqSDUGe+0Cp5HEJ58J1iVv1jQseLUMs9kFTPYwG+NJSETUCRsJV50w5cPv8bRFcSkSU+xVbUbOc1p29A==}
dependencies:
'@api.global/typedrequest': 3.0.4
'@configvault.io/interfaces': 1.0.17
'@push.rocks/smartfile': 11.0.4
'@push.rocks/smartlog': 3.0.3
'@push.rocks/smartpath': 5.0.11
dev: true
/@push.rocks/smartbrowser@2.0.6:
resolution: {integrity: sha512-Ne+KCVhV/DROc1rHRRw59K6h0+LpQAK9fdOUtgDZ7laLPmB/tmnbUh3IuRDNcIY1iVA9pydoobwjnTjVgio9eQ==}
dependencies:
@@ -817,7 +839,7 @@ packages:
'@push.rocks/smartpromise': 4.0.3
'@push.rocks/smartpuppeteer': 2.0.2
'@push.rocks/smartunique': 3.0.6
'@tsclass/tsclass': 4.0.46
'@tsclass/tsclass': 4.0.52
'@types/express': 4.17.21
express: 4.18.2
pdf-merger-js: 3.4.0
@@ -880,7 +902,7 @@ packages:
'@push.rocks/smartxml': 1.0.8
'@push.rocks/smartyaml': 2.0.5
'@push.rocks/webrequest': 3.0.34
'@tsclass/tsclass': 4.0.46
'@tsclass/tsclass': 4.0.52
dev: true
/@push.rocks/smartsocket@2.0.24:
@@ -1321,11 +1343,10 @@ packages:
type-fest: 2.19.0
dev: true
/@tsclass/tsclass@4.0.46:
resolution: {integrity: sha512-UovPUvlozv1ftJp4KW5tt4MP/LQCNP3lSCinjyIrLkopOymczyzONUGvSAAwOBf1XBE9bO0tI4KtRuXWN9XBXQ==}
/@tsclass/tsclass@4.0.52:
resolution: {integrity: sha512-yjASmfnDvgWA1OKYXbz4diLIrPBSk5BpPStKuVkAhrhN8Xw4lc6/oSiJpsosEd8GDwr/FPsY2lgN8/5674vM0w==}
dependencies:
type-fest: 4.10.1
dev: true
type-fest: 4.10.2
/@tsconfig/node10@1.0.9:
resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
@@ -1346,7 +1367,7 @@ packages:
/@types/accepts@1.3.7:
resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
dependencies:
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/babel__code-frame@7.0.6:
@@ -1357,7 +1378,7 @@ packages:
resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
dependencies:
'@types/connect': 3.4.38
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/buffer-json@2.0.3:
@@ -1377,21 +1398,21 @@ packages:
/@types/clean-css@4.2.11:
resolution: {integrity: sha512-Y8n81lQVTAfP2TOdtJJEsCoYl1AnOkqDqMvXb9/7pfgZZ7r8YrEyurrAvAoAjHOGXKRybay+5CsExqIH6liccw==}
dependencies:
'@types/node': 20.11.10
'@types/node': 20.11.22
source-map: 0.6.1
dev: true
/@types/co-body@6.1.3:
resolution: {integrity: sha512-UhuhrQ5hclX6UJctv5m4Rfp52AfG9o9+d9/HwjxhVB5NjXxr5t9oKgJxN8xRHgr35oo8meUEHUPFWiKg6y71aA==}
dependencies:
'@types/node': 20.11.10
'@types/node': 20.11.22
'@types/qs': 6.9.11
dev: true
/@types/connect@3.4.38:
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
dependencies:
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/content-disposition@0.5.8:
@@ -1412,13 +1433,13 @@ packages:
'@types/connect': 3.4.38
'@types/express': 4.17.21
'@types/keygrip': 1.0.6
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/cors@2.8.17:
resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==}
dependencies:
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/debounce@1.2.4:
@@ -1432,7 +1453,7 @@ packages:
/@types/express-serve-static-core@4.17.42:
resolution: {integrity: sha512-ckM3jm2bf/MfB3+spLPWYPUH573plBFwpOhqQ2WottxYV85j1HQFlxmnTq57X1yHY9awZPig06hL/cLMgNWHIQ==}
dependencies:
'@types/node': 20.11.10
'@types/node': 20.11.22
'@types/qs': 6.9.11
'@types/range-parser': 1.2.7
'@types/send': 0.17.4
@@ -1450,21 +1471,21 @@ packages:
/@types/from2@2.3.5:
resolution: {integrity: sha512-giavnjf3kNlJnE+HpZA1CQ3UKHxgehzsTuIMGda8pGMbOiLYGVNADEskey44OZcADHm/HOoBany8IV+0x28XFw==}
dependencies:
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/fs-extra@11.0.4:
resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==}
dependencies:
'@types/jsonfile': 6.1.4
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/glob@8.1.0:
resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==}
dependencies:
'@types/minimatch': 5.1.2
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/html-minifier@4.0.5:
@@ -1514,7 +1535,7 @@ packages:
/@types/jsonfile@6.1.4:
resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==}
dependencies:
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/keygrip@1.0.6:
@@ -1537,7 +1558,7 @@ packages:
'@types/http-errors': 2.0.4
'@types/keygrip': 1.0.6
'@types/koa-compose': 3.2.8
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/mime-types@2.1.4:
@@ -1564,8 +1585,8 @@ packages:
resolution: {integrity: sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==}
dev: true
/@types/node@20.11.10:
resolution: {integrity: sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg==}
/@types/node@20.11.22:
resolution: {integrity: sha512-/G+IxWxma6V3E+pqK1tSl2Fo1kl41pK1yeCyDsgkF9WlVAme4j5ISYM2zR11bgLFJGLN5sVK40T4RJNuiZbEjA==}
dependencies:
undici-types: 5.26.5
dev: true
@@ -1598,7 +1619,7 @@ packages:
resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==}
dependencies:
'@types/mime': 1.3.5
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/serve-static@1.15.5:
@@ -1606,7 +1627,7 @@ packages:
dependencies:
'@types/http-errors': 2.0.4
'@types/mime': 3.0.4
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/sinon-chai@3.2.12:
@@ -1633,7 +1654,7 @@ packages:
/@types/through2@2.0.41:
resolution: {integrity: sha512-ryQ0tidWkb1O1JuYvWKyMLYEtOWDqF5mHerJzKz/gQpoAaJq2l/dsMPBF0B5BNVT34rbARYJ5/tsZwLfUi2kwQ==}
dependencies:
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/trusted-types@2.0.7:
@@ -1661,20 +1682,20 @@ packages:
/@types/ws@7.4.7:
resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==}
dependencies:
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/ws@8.5.10:
resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
dependencies:
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
/@types/yauzl@2.10.3:
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
requiresBuild: true
dependencies:
'@types/node': 20.11.10
'@types/node': 20.11.22
dev: true
optional: true
@@ -2519,8 +2540,8 @@ packages:
engines: {node: '>=0.3.1'}
dev: true
/diff@5.1.0:
resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
/diff@5.2.0:
resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
engines: {node: '>=0.3.1'}
dev: true
@@ -2607,7 +2628,7 @@ packages:
dependencies:
'@types/cookie': 0.4.1
'@types/cors': 2.8.17
'@types/node': 20.11.10
'@types/node': 20.11.22
accepts: 1.3.8
base64id: 2.0.0
cookie: 0.4.2
@@ -4107,8 +4128,8 @@ packages:
engines: {node: '>= 0.6'}
dev: true
/nise@5.1.7:
resolution: {integrity: sha512-wWtNUhkT7k58uvWTB/Gy26eA/EJKtPZFVAhEilN5UYVmmGRYOURbejRUyKm0Uu9XVEW7K5nBOZfR8VMB4QR2RQ==}
/nise@5.1.9:
resolution: {integrity: sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==}
dependencies:
'@sinonjs/commons': 3.0.1
'@sinonjs/fake-timers': 11.2.2
@@ -4835,8 +4856,8 @@ packages:
'@sinonjs/commons': 3.0.1
'@sinonjs/fake-timers': 11.2.2
'@sinonjs/samsam': 8.0.0
diff: 5.1.0
nise: 5.1.7
diff: 5.2.0
nise: 5.1.9
supports-color: 7.2.0
dev: true
@@ -5177,7 +5198,7 @@ packages:
hasBin: true
dev: true
/ts-node@10.9.2(@types/node@20.11.10)(typescript@5.1.6):
/ts-node@10.9.2(@types/node@20.11.22)(typescript@5.1.6):
resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
hasBin: true
peerDependencies:
@@ -5196,7 +5217,7 @@ packages:
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
'@types/node': 20.11.10
'@types/node': 20.11.22
acorn: 8.11.3
acorn-walk: 8.3.2
arg: 4.1.3
@@ -5232,10 +5253,9 @@ packages:
engines: {node: '>=12.20'}
dev: true
/type-fest@4.10.1:
resolution: {integrity: sha512-7ZnJYTp6uc04uYRISWtiX3DSKB/fxNQT0B5o1OUeCqiQiwF+JC9+rJiZIDrPrNCLLuTqyQmh4VdQqh/ZOkv9MQ==}
/type-fest@4.10.2:
resolution: {integrity: sha512-anpAG63wSpdEbLwOqH8L84urkL6PiVIov3EMmgIhhThevh9aiMQov+6Btx0wldNcvm4wV+e2/Rt1QdDwKHFbHw==}
engines: {node: '>=16'}
dev: true
/type-is@1.6.18:
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
+2
View File
@@ -0,0 +1,2 @@
required:
- 'HETZNER_API_TOKEN'
+2 -1
View File
@@ -26,9 +26,10 @@ BundlePhobia (total size when bundled) | [![BundlePhobia](https://badgen.net/bun
A modern approach to talking to the hetzner API.
```typescript
// assuming top level await here
import hetznerCloud from '@apiclient.xyz/hetznercloud'
const myhetznerAccount = new hetznerCloud.HetznerAccount('myToken');
const servers = myhetznerAccount.getServers();
const servers = await myhetznerAccount.getServers();
for (const server of servers) {
await server.delete();
}
+39 -5
View File
@@ -1,8 +1,42 @@
import { expect, expectAsync, tap } from '@push.rocks/tapbundle';
import * as hetznercloud from '../ts/index.js'
import * as hetznercloud from '../ts/index.js';
import * as qenv from '@push.rocks/qenv';
const testQenv = new qenv.Qenv('./', './.nogit/');
tap.test('first test', async () => {
console.log(hetznercloud)
})
let testAccount: hetznercloud.HetznerAccount;
tap.start()
tap.test('should create a valid hetzer account', async () => {
testAccount = new hetznercloud.HetznerAccount(
await testQenv.getEnvVarOnDemand('HETZNER_API_TOKEN')
);
expect(testAccount).toBeInstanceOf(hetznercloud.HetznerAccount);
});
tap.test('should be able to list all servers', async () => {
const servers = await testAccount.getServers();
expect(servers).toBeArray();
console.log(JSON.stringify(servers, null, 2));
});
const testserver = tap.test('should be able to create a server', async (toolsArg) => {
const newServer = await testAccount.createServer({
name: 'testserver',
location: 'nbg1',
type: 'cpx41',
labels: {
servezoneId: 'testzone',
},
});
expect(newServer).toBeInstanceOf(hetznercloud.HetznerServer);
console.log(newServer);
await toolsArg.delayFor(10000);
return newServer;
});
tap.test('should be able to delete a server', async () => {
const testServer: hetznercloud.HetznerServer =
await (testserver.testResultPromise as Promise<hetznercloud.HetznerServer>);
// await testServer.delete();
});
tap.start();
+1 -1
View File
@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@apiclient.xyz/hetznercloud',
version: '1.0.5',
version: '1.1.0',
description: 'an unofficial api client for the hetzner cloud api'
}
+14
View File
@@ -11,6 +11,14 @@ export class HetznerAccount {
return HetznerServer.getServers(this);
}
public async getServersByLabel(labelObject: plugins.tsclass.typeFestOwn.SecondArgument<typeof HetznerServer.getServersByLabel>) {
return HetznerServer.getServersByLabel(this, labelObject);
}
public async createServer(optionsArg: plugins.tsclass.typeFestOwn.SecondArgument<typeof HetznerServer.create>) {
return HetznerServer.create(this, optionsArg);
}
/**
* request things from the hetzner API
* @param methodArg
@@ -19,13 +27,19 @@ export class HetznerAccount {
*/
public request = async (methodArg: string, pathArg: string, payloadArg: any) => {
const url = `https://api.hetzner.cloud/v1${pathArg}`;
console.log(`Url: ${url}`);
console.log(`Method: ${methodArg}`);
console.log(`Payload: ${JSON.stringify(payloadArg, null, 2)}`);
const response = await plugins.smartrequest.request(url, {
method: methodArg,
headers: {
Authorization: `Bearer ${this.token}`,
},
requestBody: payloadArg,
keepAlive: false,
});
console.log(response.statusCode);
console.log(response.body);
return response;
}
}
+74
View File
@@ -0,0 +1,74 @@
// Hetzner Cloud Firewall Class
import type { HetznerAccount } from './classes.account.js';
import * as plugins from './hetznercloud.plugins.js';
import * as types from './types.js';
export class HetznerFirewall {
// STATIC
public static create = async (
hetznerAccountRefArg: HetznerAccount,
optionsArg: {
name: string;
labels?: {[key: string]: string},
rules: types.IFirewall['rules'],
}
) => {
const firewall = new HetznerFirewall(hetznerAccountRefArg);
const createFirewallUrl = '/firewalls';
const createFirewallPayload: types.TFirewallCreateRequestBody = {
name: optionsArg.name,
labels: optionsArg.labels || {} as any,
rules: optionsArg.rules
};
const response = await firewall.hetznerAccountRef.request(
'POST',
createFirewallUrl,
createFirewallPayload
);
firewall.data = (response.body as types.TFirewallCreateResponseBody).firewall;
return firewall;
}
public static getFirewalls = async (hetznerAccountRefArg: HetznerAccount) => {
const firewallsGetUrl = '/firewalls';
const response = await hetznerAccountRefArg.request('GET', firewallsGetUrl, {});
const firewallsDataArray = (response.body as types.TFirewallsGetResponseBody).firewalls;
const firewalls: HetznerFirewall[] = [];
for (const firewallData of firewallsDataArray) {
const firewall = new HetznerFirewall(hetznerAccountRefArg);
firewall.data = firewallData;
firewalls.push(firewall);
}
return firewalls;
}
public static getFirewallsByLabel = async (hetznerAccountRefArg: HetznerAccount, labelObject: {[key: string]: string}) => {
const firewalls = await HetznerFirewall.getFirewalls(hetznerAccountRefArg);
const results: HetznerFirewall[] = [];
for (const firewall of firewalls) {
let isMatch = true;
for (const key in labelObject) {
if (firewall.data.labels[key] !== labelObject[key]) {
isMatch = false;
}
}
if (isMatch) {
results.push(firewall);
}
}
return results;
}
// INSTANCE
public data: types.IFirewall;
public hetznerAccountRef: HetznerAccount;
constructor(hetznerAccountRefArg: HetznerAccount) {
this.hetznerAccountRef = hetznerAccountRefArg;
}
public async delete() {
await this.hetznerAccountRef.request('DELETE', `/firewalls/${this.data.id}`, {});
}
}
+46 -16
View File
@@ -4,24 +4,37 @@ import * as types from './types.js';
export class HetznerServer {
// STATIC
public static async create(hetznerAccountRefArg: HetznerAccount, optionsArg: {
name: string,
datacenter: 'nbg1-dc3',
}) {
public static async create(
hetznerAccountRefArg: HetznerAccount,
optionsArg: {
name: string;
type: types.THetznerCloudServerName;
location: types.THetznerCloudLocationName;
labels?: {[key: string]: string},
userData?: string,
}
) {
const server = new HetznerServer(hetznerAccountRefArg);
const createServerUrl = '/servers';
const createServerPayload: types.TServerCreateRequestBody =
{
name: optionsArg.name,
datacenter: optionsArg.datacenter,
image: '',
server_type: '',
start_after_create: true,
user_data: '',
};
const response = await server.hetznerAccountRef.request('POST', createServerUrl, createServerPayload);
const createServerPayload: types.TServerCreateRequestBody = {
name: optionsArg.name,
image: 'ubuntu-22.04',
server_type: optionsArg.type,
start_after_create: true,
labels: optionsArg.labels || {} as any,
location: optionsArg.location,
user_data: optionsArg.userData || '',
public_net: {
enable_ipv4: true,
enable_ipv6: true,
},
};
const response = await server.hetznerAccountRef.request(
'POST',
createServerUrl,
createServerPayload
);
server.data = (response.body as types.TServerCreateResponseBody).server;
return server;
}
@@ -29,7 +42,7 @@ export class HetznerServer {
public static async getServers(hetznerAccountRefArg: HetznerAccount) {
const serversGetUrl = '/servers';
const response = await hetznerAccountRefArg.request('GET', serversGetUrl, {});
const serversDataArray = (response.body as types.TServersGetResponseBody).servers;
const serversDataArray = (response.body as types.TServersGetResponseBody).servers;
const servers: HetznerServer[] = [];
for (const serverData of serversDataArray) {
const server = new HetznerServer(hetznerAccountRefArg);
@@ -39,6 +52,23 @@ export class HetznerServer {
return servers;
}
public static async getServersByLabel(hetznerAccountRefArg: HetznerAccount, labelObject: {[key: string]: string}) {
const servers = await HetznerServer.getServers(hetznerAccountRefArg);
const results: HetznerServer[] = [];
for (const server of servers) {
let isMatch = true;
for (const key in labelObject) {
if (server.data.labels[key] !== labelObject[key]) {
isMatch = false;
}
}
if (isMatch) {
results.push(server);
}
}
return results;
}
// INSTANCE
public hetznerAccountRef: HetznerAccount;
public data: types.IServer;
+77
View File
@@ -0,0 +1,77 @@
import type { HetznerAccount } from './classes.account.js';
import type { HetznerServer } from './classes.server.js';
import * as plugins from './hetznercloud.plugins.js';
import * as types from './types.js';
export class Volume {
public static create = async (
hetznerAccountRefArg: HetznerAccount,
optionsArg: {
name: string;
size: number;
location: types.THetznerCloudLocationName;
labels?: {[key: string]: string},
server: HetznerServer,
}
) => {
const volume = new Volume(hetznerAccountRefArg);
const createVolumeUrl = '/volumes';
const createVolumePayload: types.TVolumeCreateRequestBody = {
name: optionsArg.name,
size: optionsArg.size,
location: optionsArg.location,
labels: optionsArg.labels || {} as any,
server: optionsArg.server.data.id,
format: 'xfs'
};
const response = await volume.hetznerAccountRef.request(
'POST',
createVolumeUrl,
createVolumePayload
);
volume.data = (response.body as types.TVolumeCreateResponseBody).volume;
return volume;
}
public static getVolumes = async (hetznerAccountRefArg: HetznerAccount) => {
const volumesGetUrl = '/volumes';
const response = await hetznerAccountRefArg.request('GET', volumesGetUrl, {});
const volumesDataArray = (response.body as types.TVolumeGetResponseBody).volumes;
const volumes: Volume[] = [];
for (const volumeData of volumesDataArray) {
const volume = new Volume(hetznerAccountRefArg);
volume.data = volumeData;
volumes.push(volume);
}
return volumes;
}
public static getVolumesByLabel = async (hetznerAccountRefArg: HetznerAccount, labelObject: {[key: string]: string}) => {
const volumes = await Volume.getVolumes(hetznerAccountRefArg);
const results: Volume[] = [];
for (const volume of volumes) {
let isMatch = true;
for (const key in labelObject) {
if (volume.data.labels[key] !== labelObject[key]) {
isMatch = false;
}
}
if (isMatch) {
results.push(volume);
}
}
return results;
}
public data: types.IVolume;
public hetznerAccountRef: HetznerAccount;
constructor(hetznerAccountRefArg: HetznerAccount) {
this.hetznerAccountRef = hetznerAccountRefArg;
}
public delete = async () => {
await this.hetznerAccountRef.request('DELETE', `/volumes/${this.data.id}`, {});
}
}
+7
View File
@@ -10,3 +10,10 @@ import * as smartrequest from '@push.rocks/smartrequest';
export {
smartrequest,
}
// @tsclass scope
import * as tsclass from '@tsclass/tsclass';
export {
tsclass,
}
+4
View File
@@ -0,0 +1,4 @@
export * from './classes.account.js';
export * from './classes.server.js';
export * from './classes.volume.js';
export * from './classes.firewall.js';
+47
View File
@@ -9,4 +9,51 @@ export type TServersGetRequestBody = {};
export type TServersGetResponseBody = plugins.hetznerOpenapi.paths['/servers']['get']['responses']['200']['content']['application/json'];
export type TServerCreateRequestBody = plugins.hetznerOpenapi.paths['/servers']['post']['requestBody']['content']['application/json'];
export type TServerCreateResponseBody = plugins.hetznerOpenapi.paths['/servers']['post']['responses']['201']['content']['application/json'];
export type TServerDeleteRequestBody = plugins.hetznerOpenapi.paths['/servers/{id}']['delete'];
// server types
export type THetznerCloudServerName =
| 'cx11'
| 'cx21'
| 'cx31'
| 'cx41'
| 'cx51'
| 'ccx11'
| 'ccx21'
| 'ccx31'
| 'ccx41'
| 'ccx51'
| 'ccx13'
| 'ccx23'
| 'ccx33'
| 'ccx43'
| 'ccx53'
| 'ccx63'
| 'cpx11'
| 'cpx21'
| 'cpx31'
| 'cpx41'
| 'cpx51'
| 'cpx61'
| 'cpx70'
| 'cpx71'
| 'cpx90';
// location types
export type THetznerCloudLocationName = 'fsn1' | 'nbg1' | 'hel1' | 'ash' | 'hil';
// volumes
export type IVolume = plugins.hetznerOpenapi.paths['/volumes/{id}']['get']['responses']['200']['content']['application/json']['volume'];
export type TVolumeGetRequestBody = {};
export type TVolumeGetResponseBody = plugins.hetznerOpenapi.paths['/volumes']['get']['responses']['200']['content']['application/json'];
export type TVolumeCreateRequestBody = plugins.hetznerOpenapi.paths['/volumes']['post']['requestBody']['content']['application/json'];
export type TVolumeCreateResponseBody = plugins.hetznerOpenapi.paths['/volumes']['post']['responses']['201']['content']['application/json'];
export type TVolumeDeleteRequestBody = plugins.hetznerOpenapi.paths['/volumes/{id}']['delete'];
// firewalls
export type IFirewall = plugins.hetznerOpenapi.paths['/firewalls/{id}']['get']['responses']['200']['content']['application/json']['firewall'];
export type TFirewallsGetRequestBody = {};
export type TFirewallsGetResponseBody = plugins.hetznerOpenapi.paths['/firewalls']['get']['responses']['200']['content']['application/json'];
export type TFirewallCreateRequestBody = plugins.hetznerOpenapi.paths['/firewalls']['post']['requestBody']['content']['application/json'];
export type TFirewallCreateResponseBody = plugins.hetznerOpenapi.paths['/firewalls']['post']['responses']['201']['content']['application/json'];
export type TFirewallDeleteRequestBody = plugins.hetznerOpenapi.paths['/firewalls/{id}']['delete'];