Added Chatbot to the App!

This commit is contained in:
Donald Zou 2025-03-28 00:13:38 +08:00
parent 5067485e94
commit f0c3ef0aa1
14 changed files with 854 additions and 25 deletions

View File

@ -56,4 +56,4 @@
<img src="https://donaldzou.github.io/WGDashboard-Documentation/images/configuration.png" alt=""/>
<img src="https://donaldzou.github.io/WGDashboard-Documentation/images/add-peers.png" alt="" />
<img src="https://donaldzou.github.io/WGDashboard-Documentation/images/ping.png" alt=""/>
<img src="https://donaldzou.github.io/WGDashboard-Documentation/images/traceroute.png" alt=""/>
<img src="https://donaldzou.github.io/WGDashboard-Documentation/images/traceroute.png" alt=""/>

458
package-lock.json generated Normal file
View File

@ -0,0 +1,458 @@
{
"name": "Wireguard-Dashboard",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"marked": "^15.0.7",
"openai": "^4.89.0"
}
},
"node_modules/@types/node": {
"version": "18.19.81",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-18.19.81.tgz",
"integrity": "sha512-7KO9oZ2//ivtSsryp0LQUqq79zyGXzwq1WqfywpC9ucjY7YyltMMmxWgtRFRKCxwa7VPxVBVy4kHf5UC1E8Lug==",
"license": "MIT",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/node-fetch": {
"version": "2.6.12",
"resolved": "https://registry.npmmirror.com/@types/node-fetch/-/node-fetch-2.6.12.tgz",
"integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==",
"license": "MIT",
"dependencies": {
"@types/node": "*",
"form-data": "^4.0.0"
}
},
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"license": "MIT",
"dependencies": {
"event-target-shim": "^5.0.0"
},
"engines": {
"node": ">=6.5"
}
},
"node_modules/agentkeepalive": {
"version": "4.6.0",
"resolved": "https://registry.npmmirror.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz",
"integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==",
"license": "MIT",
"dependencies": {
"humanize-ms": "^1.2.1"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/form-data": {
"version": "4.0.2",
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.2.tgz",
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/form-data-encoder": {
"version": "1.7.2",
"resolved": "https://registry.npmmirror.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz",
"integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==",
"license": "MIT"
},
"node_modules/formdata-node": {
"version": "4.4.1",
"resolved": "https://registry.npmmirror.com/formdata-node/-/formdata-node-4.4.1.tgz",
"integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==",
"license": "MIT",
"dependencies": {
"node-domexception": "1.0.0",
"web-streams-polyfill": "4.0.0-beta.3"
},
"engines": {
"node": ">= 12.20"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/humanize-ms/-/humanize-ms-1.2.1.tgz",
"integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.0.0"
}
},
"node_modules/marked": {
"version": "15.0.7",
"resolved": "https://registry.npmmirror.com/marked/-/marked-15.0.7.tgz",
"integrity": "sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"license": "MIT",
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"license": "MIT",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/openai": {
"version": "4.89.0",
"resolved": "https://registry.npmmirror.com/openai/-/openai-4.89.0.tgz",
"integrity": "sha512-XNI0q2l8/Os6jmojxaID5EhyQjxZgzR2gWcpEjYWK5hGKwE7AcifxEY7UNwFDDHJQXqeiosQ0CJwQN+rvnwdjA==",
"license": "Apache-2.0",
"dependencies": {
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4",
"abort-controller": "^3.0.0",
"agentkeepalive": "^4.2.1",
"form-data-encoder": "1.7.2",
"formdata-node": "^4.3.2",
"node-fetch": "^2.6.7"
},
"bin": {
"openai": "bin/cli"
},
"peerDependencies": {
"ws": "^8.18.0",
"zod": "^3.23.8"
},
"peerDependenciesMeta": {
"ws": {
"optional": true
},
"zod": {
"optional": true
}
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"license": "MIT"
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"license": "MIT"
},
"node_modules/web-streams-polyfill": {
"version": "4.0.0-beta.3",
"resolved": "https://registry.npmmirror.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
"integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"license": "BSD-2-Clause"
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"license": "MIT",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
}
}
}

6
package.json Normal file
View File

@ -0,0 +1,6 @@
{
"dependencies": {
"marked": "^15.0.7",
"openai": "^4.89.0"
}
}

View File

@ -2218,27 +2218,25 @@ def API_UpdateWireguardConfigurationRawFile():
@app.post(f'{APP_PREFIX}/api/deleteWireguardConfiguration')
def API_deleteWireguardConfiguration():
data = request.get_json()
if "Name" not in data.keys() or data.get("Name") is None or data.get("Name") not in WireguardConfigurations.keys():
if "ConfigurationName" not in data.keys() or data.get("ConfigurationName") is None or data.get("ConfigurationName") not in WireguardConfigurations.keys():
return ResponseObject(False, "Please provide the configuration name you want to delete")
status = WireguardConfigurations[data.get("Name")].deleteConfiguration()
status = WireguardConfigurations[data.get("ConfigurationName")].deleteConfiguration()
if status:
WireguardConfigurations.pop(data.get("Name"))
WireguardConfigurations.pop(data.get("ConfigurationName"))
return ResponseObject(status)
@app.post(f'{APP_PREFIX}/api/renameWireguardConfiguration')
def API_renameWireguardConfiguration():
data = request.get_json()
keys = ["Name", "NewConfigurationName"]
keys = ["ConfigurationName", "NewConfigurationName"]
for k in keys:
if (k not in data.keys() or data.get(k) is None or len(data.get(k)) == 0 or
(k == "Name" and data.get(k) not in WireguardConfigurations.keys())):
(k == "ConfigurationName" and data.get(k) not in WireguardConfigurations.keys())):
return ResponseObject(False, "Please provide the configuration name you want to rename")
status, message = WireguardConfigurations[data.get("Name")].renameConfiguration(data.get("NewConfigurationName"))
status, message = WireguardConfigurations[data.get("ConfigurationName")].renameConfiguration(data.get("NewConfigurationName"))
if status:
WireguardConfigurations.pop(data.get("Name"))
WireguardConfigurations.pop(data.get("ConfigurationName"))
WireguardConfigurations[data.get("NewConfigurationName")] = WireguardConfiguration(data.get("NewConfigurationName"))
return ResponseObject(status, message)
@ -3156,4 +3154,4 @@ def startThreads():
if __name__ == "__main__":
startThreads()
app.run(host=app_ip, debug=False, port=app_port)
app.run(host=app_ip, debug=False, port=app_port)

View File

@ -16,7 +16,7 @@ const deleteConfiguration = () => {
clearInterval(store.Peers.RefreshInterval)
deleting.value = true;
fetchPost("/api/deleteWireguardConfiguration", {
Name: configurationName
ConfigurationName: configurationName
}, (res) => {
if (res.status){
router.push('/')
@ -30,7 +30,6 @@ const deleteConfiguration = () => {
const loading = ref(true)
const backups = ref([])
let timeout = undefined;
const getBackup = () => {
loading.value = true;
fetchGet("/api/getWireguardConfigurationBackup", {

View File

@ -28,7 +28,7 @@ const rename = async () => {
loading.value = true
clearInterval(dashboardConfigurationStore.Peers.RefreshInterval)
await fetchPost("/api/renameWireguardConfiguration", {
Name: props.configurationName,
ConfigurationName: props.configurationName,
NewConfigurationName: newConfigurationName.data
}, async (res) => {
if (res.status){

View File

@ -5,10 +5,11 @@ import {fetchGet} from "@/utilities/fetch.js";
import LocaleText from "@/components/text/localeText.vue";
import {GetLocale} from "@/utilities/locale.js";
import HelpModal from "@/components/navbarComponents/helpModal.vue";
import AgentModal from "@/components/navbarComponents/agentModal.vue";
export default {
name: "navbar",
components: {HelpModal, LocaleText},
components: {HelpModal, LocaleText, AgentModal},
setup(){
const wireguardConfigurationsStore = WireguardConfigurationsStore();
const dashboardConfigurationStore = DashboardConfigurationStore();
@ -20,6 +21,7 @@ export default {
updateMessage: "Checking for update...",
updateUrl: "",
openHelpModal: false,
openAgentModal: true,
}
},
computed: {
@ -78,7 +80,7 @@ export default {
</RouterLink>
</li>
<li class="nav-item">
<a class="nav-link rounded-3" role="button" @click="openHelpModal = true">
<a class="nav-link rounded-3" role="button" @click="openAgentModal = true">
<i class="bi bi-question-circle me-2"></i>
<LocaleText t="Help"></LocaleText>
</a>
@ -146,19 +148,23 @@ export default {
<Transition name="zoom">
<HelpModal v-if="this.openHelpModal" @close="openHelpModal = false;"></HelpModal>
</Transition>
<Transition name="slideIn">
<AgentModal v-if="this.openAgentModal" @close="openAgentModal = false"></AgentModal>
</Transition>
</div>
</template>
<style scoped>
@media screen and (max-width: 768px) {
.navbar-container{
position: absolute;
position: absolute !important;
z-index: 1000;
animation-duration: 0.4s;
animation-fill-mode: both;
display: none;
animation-timing-function: cubic-bezier(0.82, 0.58, 0.17, 0.9);
}
.navbar-container.active{
animation-direction: normal;
display: block !important;
@ -168,14 +174,14 @@ export default {
.navbar-container{
height: 100vh;
position: relative;
}
@supports (height: 100dvh) {
@media screen and (max-width: 768px){
.navbar-container{
height: calc(100dvh - 50px);
height: calc(100dvh - 58px);
}
}
@ -195,4 +201,16 @@ export default {
filter: blur(0px);
}
}
.slideIn-enter-active,
.slideIn-leave-active{
transition: all 0.3s cubic-bezier(0.82, 0.58, 0.17, 1);
}
.slideIn-enter-from,
.slideIn-leave-to {
transform: translateX(30px);
filter: blur(3px);
opacity: 0;
}
</style>

View File

@ -0,0 +1,56 @@
<script setup>
import {computed, onMounted} from "vue";
import {marked} from "marked";
const props = defineProps({
message: {
content: String,
id: String,
role: String,
time: String
},
ind: Number
})
onMounted(() => {
document.querySelector(".agentChatroomBody").scrollTop =
document.querySelector(".agentChatroomBody").scrollHeight
})
const convertMarkdown = computed(() => {
return marked.parse(props.message.content)
})
</script>
<template>
<div :class="{'d-flex flex-row align-items-end gap-2': message.role === 'assistant', 'mt-auto': ind === 0}">
<div class="p-2 rounded-5 text-bg-secondary" style="line-height: 1" v-if="message.role === 'assistant'">
<i class="bi bi-robot"></i>
</div>
<div class="d-flex text-body agentMessage" :class="{'ms-auto': message.role === 'user'}" >
<div class="px-3 py-2 rounded-3 shadow-sm"
:class="[ message.role === 'user' ?
'text-bg-primary ms-auto align-items-end':'text-bg-secondary align-items-start']">
{{ message.content }}
</div>
</div>
</div>
</template>
<style scoped>
.agentMessage{
white-space: break-spaces;
max-width: 80%;
display: flex;
flex-direction: column;
word-wrap: break-word;
}
.text-bg-secondary{
background-color: RGBA(var(--bs-secondary-rgb), 0.7) !important;
}
.text-bg-primary{
background-color: RGBA(var(--bs-primary-rgb), 0.7) !important;
}
</style>

View File

@ -0,0 +1,291 @@
<script setup>
import {onBeforeMount, onBeforeUnmount, onMounted, reactive, ref} from "vue";
import { v4 } from "uuid"
import dayjs from "dayjs";
import AgentMessage from "@/components/navbarComponents/agentMessage.vue";
import {fetchGet} from "@/utilities/fetch.js";
import LocaleText from "@/components/text/localeText.vue";
import {GetLocale} from "@/utilities/locale.js";
const userPrompt = ref("")
const waitingMessage = ref(false)
const messages = reactive({})
const agentBaseUrl = "https://agent-aee6a811e474080613b1-ie5yg.ondigitalocean.app";
const agentChatbotId = "JrYZtArj_C5FGRts06op58QUHPFCgUzo";
const agentId = "1150ab95-025b-11f0-bf8f-4e013e2ddde4"
let refreshTokenInterval = undefined;
let agentToken = ref(undefined);
const agentHealth = ref(false);
const checkingAgentHealth = ref(false);
const scrollMessageBody = () => {
document.querySelector(".agentChatroomBody").scrollTop = document.querySelector(".agentChatroomBody").scrollHeight
}
const newMessage = (role, content) => {
let sentMsgId = v4().toString();
messages[sentMsgId] = {
id: sentMsgId,
role: role,
content: content,
time: dayjs().format("YYYY-MM-DD HH:mm:ss")
}
return sentMsgId
}
const pushPrompt = () => {
if (userPrompt.value){
newMessage('user', userPrompt.value)
userPrompt.value = "";
waitingMessage.value = true;
fetch(`${agentBaseUrl}/api/v1/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${agentToken.value.access_token}`
},
body: JSON.stringify({
"include_functions_info": false,
"include_retrieval_info": false,
"include_guardrails_info": false,
"stream": true,
"messages": Object.values(messages)
})
}).then(response => {
if (response.ok){
const stream = response.body;
const reader = stream.getReader();
const decoder = new TextDecoder();
let recvMsgId = newMessage('assistant', '')
const readChunk = () => {
reader.read()
.then(({value, done}) => {
if (done) {
waitingMessage.value = false
return
}
let chunkString = decoder.decode(value, {stream: true}).trim();
chunkString = chunkString.split("\n")
chunkString.forEach(x => {
if (x){
x = x.replace(/^data:\s*/, "")
if (x !== "[DONE]"){
let d = JSON.parse(x);
d.choices.forEach(c => {
if (c.delta.content){
messages[recvMsgId].content += c.delta.content
scrollMessageBody()
}
})
}
}
})
readChunk();
})
.catch(error => {
waitingMessage.value = false
messages[recvMsgId].content = "Sorry, the bot is not responding."
});
};
readChunk();
}else{
waitingMessage.value = false;
throw new Error("Invalid response")
}
});
}
}
const initAgent = async () => {
checkingAgentHealth.value = true;
await fetch(`${agentBaseUrl}/health`, {
signal: AbortSignal.timeout(3000)
}).then(res => res.json()).then(res => {
agentHealth.value = res.status === 'ok'
}).catch(() => {
checkingAgentHealth.value = false;
agentHealth.value = false;
})
if (agentHealth.value){
await fetch(`https://cloud.digitalocean.com/gen-ai/auth/agents/${agentId}/token`, {
headers: {
'Content-Type': 'application/json',
'X-Api-Key': agentChatbotId
},
method: "POST",
body: JSON.stringify({})
}).then(res => {
if (!res.ok){
throw new Error('Access token not available')
}else{
return res.json()
}
}).then(res => {
agentToken.value = res;
checkingAgentHealth.value = false;
}).catch(() => {
checkingAgentHealth.value = false;
agentHealth.value = false;
})
}
}
const refreshAgentToken = async () => {
if (agentToken.value){
await fetch(
`https://cloud.digitalocean.com/gen-ai/auth/agents/${agentId}/token?refresh_token=${agentToken.value.refresh_token}`, {
headers: {
'Content-Type': 'application/json',
'X-Api-Key': agentChatbotId
},
method: "PUT",
body: JSON.stringify({})
}).then(res => res.json()).then(res => {
agentHealth.value = true;
agentToken.value = res;
}).catch(err => {
agentHealth.value = false;
console.log(err)
})
}
}
onBeforeMount(() => {
newMessage('assistant', GetLocale('Hi! How can I help you today?'))
})
onMounted(async () => {
await initAgent();
refreshTokenInterval = setInterval(async () => {
await refreshAgentToken()
}, 60000)
})
onBeforeUnmount(() => {
clearInterval(refreshTokenInterval);
})
const emits = defineEmits(['close'])
</script>
<template>
<div class="agentContainer m-2 rounded-3 d-flex flex-column text-body" :class="{'connected': agentHealth && !checkingAgentHealth}">
<TransitionGroup name="agent-message">
<div key="header" class=" shadow ">
<div class="p-3 d-flex gap-2 flex-column ">
<div class="d-flex text-body" >
<div class="d-flex flex-column align-items-start gap-1">
<h5 class="mb-0">
<LocaleText t="Help"></LocaleText>
</h5>
</div>
<a role="button" class="ms-auto text-body" @click="emits('close')">
<h5 class="mb-0">
<i class="bi bi-x-lg"></i>
</h5>
</a>
</div>
<p class="mb-0">
<LocaleText t="You can visit our: "></LocaleText>
</p>
<div class="list-group">
<a href="https://donaldzou.github.io/WGDashboard-Documentation/"
target="_blank" class="list-group-item list-group-item-action d-flex align-items-center">
<i class="bi bi-book-fill"></i>
<LocaleText class="ms-auto" t="Official Documentation"></LocaleText>
</a>
<a target="_blank" role="button" href="https://discord.gg/72TwzjeuWm"
class="list-group-item list-group-item-action d-flex align-items-center">
<i class="bi bi-discord"></i>
<LocaleText class="ms-auto" t="Discord Server"></LocaleText>
</a>
</div>
</div>
<div class="d-flex align-items-center p-3">
<h5 class="mb-0"><LocaleText t="WGDashboard Help Bot"></LocaleText></h5>
<h6 class="mb-0 ms-auto">
<span class="mb-0 text-muted d-flex gap-2 align-items-center" v-if="checkingAgentHealth">
<span class="spinner-border spinner-border-sm"></span>
<small>
<LocaleText t="Connecting..."></LocaleText>
</small>
</span>
<span class="mb-0 d-flex gap-2 align-items-center badge"
:class="[agentHealth ? 'text-bg-success':'text-bg-danger']"
v-else>
{{ agentHealth ? 'Connected':'Not Connected'}}
</span>
</h6>
</div>
</div>
<div class="agentChatroomBody p-3 pb-5 d-flex flex-column gap-3 flex-grow-1"
key="body"
v-if="agentHealth && !checkingAgentHealth">
<TransitionGroup name="agent-message">
<AgentMessage :message="msg" v-for="(msg, index) in Object.values(messages)" :key="msg.id" :ind="index"></AgentMessage>
</TransitionGroup>
</div>
<div class="d-flex text-white align-items-center p-3 gap-3 rounded-bottom-3 mt-auto"
key="input"
v-if="agentHealth && !checkingAgentHealth"
style="box-shadow: 1px -1rem 3rem 0 rgba(0, 0, 0, 0.175) !important">
<input type="text" class="form-control rounded-3 bg-transparent border-0"
:placeholder="GetLocale('What do you want to ask?')"
@keyup.enter="pushPrompt"
v-model="userPrompt" :disabled="waitingMessage">
<a role="button" class="agentChatroomSendButton text-body" @click="pushPrompt">
<i class="bi bi-send-fill" v-if="!waitingMessage"></i>
<span class="spinner-border spinner-border-sm" v-else></span>
</a>
</div>
</TransitionGroup>
</div>
</template>
<style scoped>
.agentContainer{
--agentHeight: 100vh;
position: absolute;
z-index: 9999;
top: 0;
left: 100%;
width: 450px;
box-shadow: 0px 10px 30px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(8px);
background: linear-gradient(var(--degree), #009dff52 var(--distance2), #ff4a0052 100%);
}
.agentContainer.connected{
height: calc(var(--agentHeight) - 1rem);
}
@media screen and (max-width: 768px) {
.agentContainer{
--agentHeight: 100vh !important;
top: 0;
left: 0;
max-height: calc(var(--agentHeight) - 58px - 1rem);
width: calc( 100% - 1rem);
}
}
.agentChatroomBody{
flex: 1 1 auto;
overflow-y: auto;
max-height: calc(var(--agentHeight) - 70px - 244px);
}
.agent-message-move, /* apply transition to moving elements */
.agent-message-enter-active,
.agent-message-leave-active {
transition: all 0.5s cubic-bezier(0.82, 0.58, 0.17, 1);
}
.agent-message-enter-from,
.agent-message-leave-to {
opacity: 0;
filter: blur(8px);
transform: translateY(30px);
}
.agent-message-leave-active {
position: absolute;
}
</style>

View File

@ -1,7 +1,6 @@
<script setup>
import LocaleText from "@/components/text/localeText.vue";
import {onMounted, ref} from "vue";
import {fetchGet} from "@/utilities/fetch.js";
const discordLoading = ref(true)
const discord = ref(undefined)

View File

@ -113,8 +113,8 @@
}
.btn-brand:hover, .dashboardLogo:hover{
--brandColor1: #009dff;
--brandColor2: #ff875b;
--brandColor1: rgba(0, 157, 255, 1);
--brandColor2: rgba(255, 135, 91, 1);
--distance2: 30%;
}

View File

@ -49,7 +49,7 @@ export default {
@supports (height: 100dvh) {
@media screen and (max-width: 768px) {
main{
height: calc(100dvh - 50px);
height: calc(100dvh - 58px);
}
}
}

View File

@ -308,5 +308,7 @@
"Deleted ([0-9]{1,}) peer\\(s\\)": "",
"Deleted ([0-9]{1,}) peer\\(s\\) successfully. Failed to delete ([0-9]{1,}) peer\\(s\\)": "",
"Restricted ([0-9]{1,}) peer\\(s\\)": "",
"Restricted ([0-9]{1,}) peer\\(s\\) successfully. Failed to restrict ([0-9]{1,}) peer\\(s\\)": ""
"Restricted ([0-9]{1,}) peer\\(s\\) successfully. Failed to restrict ([0-9]{1,}) peer\\(s\\)": "",
"Hi! How can I help you today\\?": "",
"What do you want to ask\\?": ""
}

View File

@ -308,5 +308,7 @@
"Deleted ([0-9]{1,}) peer\\(s\\)": "删除了$1个端点",
"Deleted ([0-9]{1,}) peer\\(s\\) successfully. Failed to delete ([0-9]{1,}) peer\\(s\\)": "成功删除了$1个端点失败删除了$2个端点",
"Restricted ([0-9]{1,}) peer\\(s\\)": "限制访问了$1个端点",
"Restricted ([0-9]{1,}) peer\\(s\\) successfully. Failed to restrict ([0-9]{1,}) peer\\(s\\)": "成功限制访问了$1个端点失败限制访问了$2个端点"
"Restricted ([0-9]{1,}) peer\\(s\\) successfully. Failed to restrict ([0-9]{1,}) peer\\(s\\)": "成功限制访问了$1个端点失败限制访问了$2个端点",
"Hi! How can I help you today\\?": "您好!有什么可以帮到您的吗?",
"What do you want to ask\\?": "您想问些什么?"
}