feature: Implement first backend.
This commit is contained in:
parent
69498c2a6e
commit
c8f317b0e5
7 changed files with 465 additions and 29 deletions
145
package-lock.json
generated
145
package-lock.json
generated
|
@ -11,11 +11,13 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^5.13.0",
|
"@prisma/client": "^5.13.0",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"fastify": "^4.26.2"
|
"fastify": "^4.26.2",
|
||||||
|
"node-ssh": "^13.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bcrypt": "^5.0.2",
|
"@types/bcrypt": "^5.0.2",
|
||||||
"@types/node": "^20.12.7",
|
"@types/node": "^20.12.7",
|
||||||
|
"@types/ssh2": "^1.15.0",
|
||||||
"nodemon": "^3.0.3",
|
"nodemon": "^3.0.3",
|
||||||
"prisma": "^5.13.0",
|
"prisma": "^5.13.0",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
|
@ -165,6 +167,24 @@
|
||||||
"undici-types": "~5.26.4"
|
"undici-types": "~5.26.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/ssh2": {
|
||||||
|
"version": "1.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.0.tgz",
|
||||||
|
"integrity": "sha512-YcT8jP5F8NzWeevWvcyrrLB3zcneVjzYY9ZDSMAMboI+2zR1qYWFhwsyOFVzT7Jorn67vqxC0FRiw8YyG9P1ww==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "^18.11.18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/ssh2/node_modules/@types/node": {
|
||||||
|
"version": "18.19.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz",
|
||||||
|
"integrity": "sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/abbrev": {
|
"node_modules/abbrev": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
|
@ -284,6 +304,14 @@
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/asn1": {
|
||||||
|
"version": "0.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||||
|
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": "~2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/atomic-sleep": {
|
"node_modules/atomic-sleep": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
||||||
|
@ -340,6 +368,14 @@
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bcrypt-pbkdf": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
||||||
|
"dependencies": {
|
||||||
|
"tweetnacl": "^0.14.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/binary-extensions": {
|
"node_modules/binary-extensions": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||||
|
@ -393,6 +429,15 @@
|
||||||
"ieee754": "^1.2.1"
|
"ieee754": "^1.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/buildcheck": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz",
|
||||||
|
"integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
|
@ -454,6 +499,20 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cpu-features": {
|
||||||
|
"version": "0.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.9.tgz",
|
||||||
|
"integrity": "sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"buildcheck": "~0.0.6",
|
||||||
|
"nan": "^2.17.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
@ -858,6 +917,17 @@
|
||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-stream": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/json-schema-ref-resolver": {
|
"node_modules/json-schema-ref-resolver": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz",
|
||||||
|
@ -972,6 +1042,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/nan": {
|
||||||
|
"version": "2.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz",
|
||||||
|
"integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/node-addon-api": {
|
"node_modules/node-addon-api": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||||
|
@ -996,6 +1072,22 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-ssh": {
|
||||||
|
"version": "13.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-ssh/-/node-ssh-13.2.0.tgz",
|
||||||
|
"integrity": "sha512-7vsKR2Bbs66th6IWCy/7SN4MSwlVt+G6QrHB631BjRUM8/LmvDugtYhi0uAmgvHS/+PVurfNBOmELf30rm0MZg==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-stream": "^2.0.0",
|
||||||
|
"make-dir": "^3.1.0",
|
||||||
|
"sb-promise-queue": "^2.1.0",
|
||||||
|
"sb-scandir": "^3.1.0",
|
||||||
|
"shell-escape": "^0.2.0",
|
||||||
|
"ssh2": "^1.14.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nodemon": {
|
"node_modules/nodemon": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz",
|
||||||
|
@ -1312,6 +1404,30 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
|
},
|
||||||
|
"node_modules/sb-promise-queue": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sb-promise-queue/-/sb-promise-queue-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-zwq4YuP1FQFkGx2Q7GIkZYZ6PqWpV+bg0nIO1sJhWOyGyhqbj0MsTvK6lCFo5TQwX5pZr6SCQ75e8PCDCuNvkg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sb-scandir": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sb-scandir/-/sb-scandir-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-70BVm2xz9jn94zSQdpvYrEG101/UV9TVGcfWr9T5iob3QhCK4lYXeculfBqPGFv3XTeKgx4dpWyYIDeZUqo4kg==",
|
||||||
|
"dependencies": {
|
||||||
|
"sb-promise-queue": "^2.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/secure-json-parse": {
|
"node_modules/secure-json-parse": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
|
||||||
|
@ -1341,6 +1457,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
|
||||||
"integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ=="
|
"integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/shell-escape": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shell-escape/-/shell-escape-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-uRRBT2MfEOyxuECseCZd28jC1AJ8hmqqneWQ4VWUTgCAFvb3wKU1jLqj6egC4Exrr88ogg3dp+zroH4wJuaXzw=="
|
||||||
|
},
|
||||||
"node_modules/signal-exit": {
|
"node_modules/signal-exit": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
|
@ -1374,6 +1495,23 @@
|
||||||
"node": ">= 10.x"
|
"node": ">= 10.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ssh2": {
|
||||||
|
"version": "1.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz",
|
||||||
|
"integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"asn1": "^0.2.6",
|
||||||
|
"bcrypt-pbkdf": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.16.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"cpu-features": "~0.0.9",
|
||||||
|
"nan": "^2.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string_decoder": {
|
"node_modules/string_decoder": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
|
@ -1479,6 +1617,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/tweetnacl": {
|
||||||
|
"version": "0.14.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||||
|
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
|
||||||
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.3.3",
|
"version": "5.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bcrypt": "^5.0.2",
|
"@types/bcrypt": "^5.0.2",
|
||||||
"@types/node": "^20.12.7",
|
"@types/node": "^20.12.7",
|
||||||
|
"@types/ssh2": "^1.15.0",
|
||||||
"nodemon": "^3.0.3",
|
"nodemon": "^3.0.3",
|
||||||
"prisma": "^5.13.0",
|
"prisma": "^5.13.0",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^5.13.0",
|
"@prisma/client": "^5.13.0",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"fastify": "^4.26.2"
|
"fastify": "^4.26.2",
|
||||||
|
"node-ssh": "^13.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,35 +3,57 @@ export type ParameterReturnedValue = {
|
||||||
message?: string
|
message?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ConnectedDevice = {
|
export type ForwardRule = {
|
||||||
sourceIP: string,
|
sourceIP: string,
|
||||||
sourcePort: number,
|
sourcePort: number,
|
||||||
destPort: number,
|
destPort: number
|
||||||
|
|
||||||
protocol: "tcp" | "udp"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface BackendInterface {
|
export type ConnectedClient = {
|
||||||
new(): {
|
ip: string,
|
||||||
addConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void;
|
port: number,
|
||||||
removeConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void;
|
|
||||||
|
|
||||||
start(): Promise<boolean>,
|
connectionDetails: ForwardRule
|
||||||
stop(): Promise<boolean>,
|
};
|
||||||
|
|
||||||
getAllConnections(): {
|
export class BackendBaseClass {
|
||||||
sourceIP: string,
|
state: "stopped" | "stopping" | "started" | "starting";
|
||||||
sourcePort: number,
|
|
||||||
destPort: number,
|
|
||||||
protocol: "tcp" | "udp"
|
|
||||||
}[];
|
|
||||||
|
|
||||||
state: "stopped" | "running" | "starting";
|
clients?: ConnectedClient[]; // Not required to be implemented, but more consistency
|
||||||
|
logs: string[];
|
||||||
|
|
||||||
probeConnectedClients: ConnectedDevice[],
|
constructor(parameters: string) {
|
||||||
logs: string[]
|
this.logs = [];
|
||||||
},
|
this.clients = [];
|
||||||
|
|
||||||
checkParametersConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): ParameterReturnedValue;
|
this.state = "stopped";
|
||||||
checkParametersBackendInstance(data: string): ParameterReturnedValue;
|
}
|
||||||
|
|
||||||
|
addConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void {};
|
||||||
|
removeConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void {};
|
||||||
|
|
||||||
|
async start(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async stop(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
getAllConnections(): ConnectedClient[] {
|
||||||
|
if (this.clients == null) return [];
|
||||||
|
return this.clients;
|
||||||
|
};
|
||||||
|
|
||||||
|
static checkParametersConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): ParameterReturnedValue {
|
||||||
|
return {
|
||||||
|
success: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static checkParametersBackendInstance(data: string): ParameterReturnedValue {
|
||||||
|
return {
|
||||||
|
success: true
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
6
src/backendimpl/index.ts
Normal file
6
src/backendimpl/index.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import type { BackendBaseClass } from "./base.js";
|
||||||
|
import { SSHBackendProvider } from "./ssh.js";
|
||||||
|
|
||||||
|
export const backendProviders: Record<string, typeof BackendBaseClass> = {
|
||||||
|
"ssh": SSHBackendProvider
|
||||||
|
};
|
211
src/backendimpl/ssh.ts
Normal file
211
src/backendimpl/ssh.ts
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
import { NodeSSH } from "node-ssh";
|
||||||
|
import { Socket } from "node:net";
|
||||||
|
|
||||||
|
import type { BackendBaseClass, ForwardRule, ConnectedClient, ParameterReturnedValue } from "./base.js";
|
||||||
|
|
||||||
|
// Fight me (for better naming)
|
||||||
|
type BackendParsedProviderString = {
|
||||||
|
ip: string,
|
||||||
|
port: number,
|
||||||
|
|
||||||
|
username: string,
|
||||||
|
privateKey: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseBackendProviderString(data: string): BackendParsedProviderString {
|
||||||
|
try {
|
||||||
|
JSON.parse(data);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error("Payload body is not JSON")
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsonData = JSON.parse(data);
|
||||||
|
|
||||||
|
if (typeof jsonData.ip != "string") throw new Error("IP field is not a string");
|
||||||
|
if (typeof jsonData.port != "number") throw new Error("Port is not a number");
|
||||||
|
|
||||||
|
if (typeof jsonData.username != "string") throw new Error("Username is not a string");
|
||||||
|
if (typeof jsonData.privateKey != "string") throw new Error("Private key is not a string");
|
||||||
|
|
||||||
|
return {
|
||||||
|
ip: jsonData.ip,
|
||||||
|
port: jsonData.port,
|
||||||
|
|
||||||
|
username: jsonData.username,
|
||||||
|
privateKey: jsonData.privateKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SSHBackendProvider implements BackendBaseClass {
|
||||||
|
state: "stopped" | "stopping" | "started" | "starting";
|
||||||
|
|
||||||
|
clients: ConnectedClient[];
|
||||||
|
proxies: ForwardRule[];
|
||||||
|
logs: string[];
|
||||||
|
|
||||||
|
sshInstance: NodeSSH;
|
||||||
|
options: BackendParsedProviderString;
|
||||||
|
|
||||||
|
constructor(parameters: string) {
|
||||||
|
this.logs = [];
|
||||||
|
this.proxies = [];
|
||||||
|
this.clients = [];
|
||||||
|
|
||||||
|
this.options = parseBackendProviderString(parameters);
|
||||||
|
|
||||||
|
this.state = "stopped";
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(): Promise<boolean> {
|
||||||
|
this.state = "starting";
|
||||||
|
this.logs.push("Starting SSHBackendProvider...");
|
||||||
|
|
||||||
|
if (this.sshInstance) {
|
||||||
|
this.sshInstance.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sshInstance = new NodeSSH();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.sshInstance.connect({
|
||||||
|
host: this.options.ip,
|
||||||
|
port: this.options.port,
|
||||||
|
|
||||||
|
username: this.options.username,
|
||||||
|
privateKey: this.options.privateKey
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
this.logs.push(`Failed to start SSHBackendProvider! Error: '${e}'`);
|
||||||
|
this.state = "stopped";
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
this.sshInstance = null;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.state = "started";
|
||||||
|
this.logs.push("Successfully started SSHBackendProvider.");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async stop(): Promise<boolean> {
|
||||||
|
this.state = "stopping";
|
||||||
|
this.logs.push("Stopping SSHBackendProvider...");
|
||||||
|
|
||||||
|
this.proxies.splice(0, this.proxies.length);
|
||||||
|
|
||||||
|
this.sshInstance.dispose();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
this.sshInstance = null;
|
||||||
|
|
||||||
|
this.logs.push("Successfully stopped SSHBackendProvider.");
|
||||||
|
this.state = "stopped";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
addConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void {
|
||||||
|
const connectionCheck = SSHBackendProvider.checkParametersConnection(sourceIP, sourcePort, destPort, protocol);
|
||||||
|
if (!connectionCheck.success) throw new Error(connectionCheck.message);
|
||||||
|
|
||||||
|
const foundProxyEntry = this.proxies.find((i) => i.sourceIP == sourceIP && i.sourcePort == sourcePort && i.destPort == destPort);
|
||||||
|
if (foundProxyEntry) return;
|
||||||
|
|
||||||
|
console.log("connection added");
|
||||||
|
|
||||||
|
(async() => {
|
||||||
|
await this.sshInstance.forwardIn("0.0.0.0", destPort, (info, accept, reject) => {
|
||||||
|
const foundProxyEntry = this.proxies.find((i) => i.sourceIP == sourceIP && i.sourcePort == sourcePort && i.destPort == destPort);
|
||||||
|
if (!foundProxyEntry) return reject();
|
||||||
|
|
||||||
|
const client: ConnectedClient = {
|
||||||
|
ip: info.srcIP,
|
||||||
|
port: info.srcPort,
|
||||||
|
|
||||||
|
connectionDetails: foundProxyEntry
|
||||||
|
};
|
||||||
|
|
||||||
|
this.clients.push(client);
|
||||||
|
|
||||||
|
const srcConn = new Socket();
|
||||||
|
|
||||||
|
srcConn.connect({
|
||||||
|
host: sourceIP,
|
||||||
|
port: sourcePort
|
||||||
|
});
|
||||||
|
|
||||||
|
// Why is this so confusing
|
||||||
|
const destConn = accept();
|
||||||
|
|
||||||
|
destConn.on("data", (chunk: Uint8Array) => {
|
||||||
|
srcConn.write(chunk);
|
||||||
|
});
|
||||||
|
|
||||||
|
destConn.on("exit", () => {
|
||||||
|
this.clients.splice(this.clients.indexOf(client), 1);
|
||||||
|
srcConn.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
srcConn.on("data", (data) => {
|
||||||
|
destConn.write(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
srcConn.on("end", () => {
|
||||||
|
this.clients.splice(this.clients.indexOf(client), 1);
|
||||||
|
destConn.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
this.proxies.push({
|
||||||
|
sourceIP,
|
||||||
|
sourcePort,
|
||||||
|
destPort
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
removeConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): void {
|
||||||
|
const connectionCheck = SSHBackendProvider.checkParametersConnection(sourceIP, sourcePort, destPort, protocol);
|
||||||
|
if (!connectionCheck.success) throw new Error(connectionCheck.message);
|
||||||
|
|
||||||
|
const foundProxyEntry = this.proxies.find((i) => i.sourceIP == sourceIP && i.sourcePort == sourcePort && i.destPort == destPort);
|
||||||
|
if (!foundProxyEntry) return;
|
||||||
|
|
||||||
|
const proxyIndex = this.proxies.indexOf(foundProxyEntry);
|
||||||
|
this.proxies.splice(proxyIndex, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
getAllConnections(): ConnectedClient[] {
|
||||||
|
return this.clients;
|
||||||
|
};
|
||||||
|
|
||||||
|
static checkParametersConnection(sourceIP: string, sourcePort: number, destPort: number, protocol: "tcp" | "udp"): ParameterReturnedValue {
|
||||||
|
if (protocol == "udp") return {
|
||||||
|
success: false,
|
||||||
|
message: "SSH does not support UDP tunneling! Please use something like PortCopier instead (if it gets done)"
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static checkParametersBackendInstance(data: string): ParameterReturnedValue {
|
||||||
|
try {
|
||||||
|
parseBackendProviderString(data);
|
||||||
|
// @ts-ignore
|
||||||
|
} catch (e: Error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: e.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
56
src/index.ts
56
src/index.ts
|
@ -4,7 +4,9 @@ import { PrismaClient } from '@prisma/client';
|
||||||
import Fastify from "fastify";
|
import Fastify from "fastify";
|
||||||
|
|
||||||
import type { ServerOptions, SessionToken, RouteOptions } from "./libs/types.js";
|
import type { ServerOptions, SessionToken, RouteOptions } from "./libs/types.js";
|
||||||
import type { BackendInterface } from "./backendimpl/base.js";
|
import type { BackendBaseClass } from "./backendimpl/base.js";
|
||||||
|
|
||||||
|
import { backendProviders } from "./backendimpl/index.js";
|
||||||
|
|
||||||
import { route as getPermissions } from "./routes/getPermissions.js";
|
import { route as getPermissions } from "./routes/getPermissions.js";
|
||||||
|
|
||||||
|
@ -20,6 +22,7 @@ import { route as userCreate } from "./routes/user/create.js";
|
||||||
import { route as userRemove } from "./routes/user/remove.js";
|
import { route as userRemove } from "./routes/user/remove.js";
|
||||||
import { route as userLookup } from "./routes/user/lookup.js";
|
import { route as userLookup } from "./routes/user/lookup.js";
|
||||||
import { route as userLogin } from "./routes/user/login.js";
|
import { route as userLogin } from "./routes/user/login.js";
|
||||||
|
import { connect } from "node:http2";
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
@ -40,7 +43,7 @@ const serverOptions: ServerOptions = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const sessionTokens: Record<number, SessionToken[]> = {};
|
const sessionTokens: Record<number, SessionToken[]> = {};
|
||||||
const backends: Record<number, BackendInterface> = {};
|
const backends: Record<number, BackendBaseClass> = {};
|
||||||
|
|
||||||
const fastify = Fastify({
|
const fastify = Fastify({
|
||||||
logger: true
|
logger: true
|
||||||
|
@ -55,6 +58,55 @@ const routeOptions: RouteOptions = {
|
||||||
backends: backends
|
backends: backends
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log("Initializing forwarding rules...");
|
||||||
|
|
||||||
|
const createdBackends = await prisma.desinationProvider.findMany();
|
||||||
|
|
||||||
|
for (const backend of createdBackends) {
|
||||||
|
console.log(`Running init steps for ID '${backend.id}' (${backend.name})`);
|
||||||
|
|
||||||
|
const ourProvider = backendProviders[backend.backend];
|
||||||
|
|
||||||
|
if (!ourProvider) {
|
||||||
|
console.log(" - Error: Invalid backend recieved!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(" - Initializing backend...");
|
||||||
|
|
||||||
|
backends[backend.id] = new ourProvider(backend.connectionDetails);
|
||||||
|
const ourBackend = backends[backend.id];
|
||||||
|
|
||||||
|
if (!await ourBackend.start()) {
|
||||||
|
console.log(" - Error initializing backend!");
|
||||||
|
console.log(" - " + ourBackend.logs.join("\n - "));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(" - Initializing clients...");
|
||||||
|
|
||||||
|
const clients = await prisma.forwardRule.findMany({
|
||||||
|
where: {
|
||||||
|
destProviderID: backend.id,
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const client of clients) {
|
||||||
|
if (client.protocol != "tcp" && client.protocol != "udp") {
|
||||||
|
console.error(` - Error: Client with ID of '${client.id}' has an invalid protocol! (must be either TCP or UDP)`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ourBackend.addConnection(client.sourceIP, client.sourcePort, client.destPort, client.protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Init successful.");
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Done.");
|
||||||
|
|
||||||
getPermissions(routeOptions);
|
getPermissions(routeOptions);
|
||||||
|
|
||||||
backendCreate(routeOptions);
|
backendCreate(routeOptions);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { PrismaClient } from "@prisma/client";
|
import type { PrismaClient } from "@prisma/client";
|
||||||
import type { FastifyInstance } from "fastify";
|
import type { FastifyInstance } from "fastify";
|
||||||
|
|
||||||
import type { BackendInterface } from "../backendimpl/base.js";
|
import type { BackendBaseClass } from "../backendimpl/base.js";
|
||||||
|
|
||||||
export type ServerOptions = {
|
export type ServerOptions = {
|
||||||
isSignupEnabled: boolean;
|
isSignupEnabled: boolean;
|
||||||
|
@ -24,5 +24,5 @@ export type RouteOptions = {
|
||||||
tokens: Record<number, SessionToken[]>,
|
tokens: Record<number, SessionToken[]>,
|
||||||
|
|
||||||
options: ServerOptions,
|
options: ServerOptions,
|
||||||
backends: Record<number, BackendInterface>
|
backends: Record<number, BackendBaseClass>
|
||||||
};
|
};
|
Loading…
Add table
Add a link
Reference in a new issue