From 5a6ee3da96fa2cda52d47ca5bb497b5d5cd08a34 Mon Sep 17 00:00:00 2001 From: Brent Salisbury Date: Tue, 28 May 2024 01:12:34 -0400 Subject: [PATCH] Add Oauth org support - This restricts GH OAuth logins to be members of the InstructLab org - Adds an error page with info for failed login attempts - Adds logging for GH OAuth attempts - Fixed a couple of lint + formatting fixes that snuck through - Updated env.Example - Moved a couple of interfaces into @/types Signed-off-by: Brent Salisbury --- .gitignore | 1 + ui/.env.example | 4 +- ui/package-lock.json | 302 ++++++++++++++++-- ui/package.json | 8 +- ui/src/app/api/auth/[...nextauth]/route.ts | 54 ++++ ui/src/app/error/error.module.css | 41 +++ ui/src/app/error/page.tsx | 36 +++ ui/src/app/playground/chat/page.tsx | 14 +- ui/src/app/playground/devchat/page.tsx | 11 +- .../playground/devchat/useModelSelector.ts | 8 +- .../Contribute/Knowledge/knowledge.css | 23 +- ui/src/components/Contribute/Skill/skill.css | 23 +- ui/src/types/index.ts | 11 + 13 files changed, 465 insertions(+), 71 deletions(-) create mode 100644 ui/src/app/error/error.module.css create mode 100644 ui/src/app/error/page.tsx diff --git a/.gitignore b/.gitignore index 08519c7..5d9f313 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ yarn-error.log yarn.lock stats.json .next/ +auth.log diff --git a/ui/.env.example b/ui/.env.example index 66fca35..aee17b0 100644 --- a/ui/.env.example +++ b/ui/.env.example @@ -1,7 +1,8 @@ IL_UI_ADMIN_USERNAME=admin IL_UI_ADMIN_PASSWORD=password +IL_UI_API_SERVER_USERNAME=kitteh +IL_UI_API_SERVER_PASSWORD=floofykittens IL_UI_API_SERVER_URL=http://:8000 -IL_UI_API_CHAT_URL=http:///:8000 OAUTH_GITHUB_ID= OAUTH_GITHUB_SECRET= NEXTAUTH_SECRET= @@ -10,3 +11,4 @@ IL_GRANITE_API= IL_GRANITE_MODEL_NAME= IL_MERLINITE_API= IL_MERLINITE_MODEL_NAME= +GITHUB_TOKEN= diff --git a/ui/package-lock.json b/ui/package-lock.json index 708db8a..bb5c62f 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -20,6 +20,7 @@ "@types/node": "20.3.1", "@types/react": "18.2.13", "@types/react-dom": "18.2.6", + "axios": "^1.7.2", "eslint": "^8.56.0", "https": "^1.0.0", "next": "^14.2.3", @@ -28,7 +29,8 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-query": "^3.39.3", - "typescript": "5.1.3" + "typescript": "5.1.3", + "winston": "^3.13.0" }, "devDependencies": { "@next/eslint-plugin-next": "^14.2.3", @@ -397,6 +399,24 @@ "node": ">=6.9.0" } }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -1222,6 +1242,11 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", @@ -1928,6 +1953,16 @@ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/attr-accept": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", @@ -1960,6 +1995,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -2147,12 +2192,19 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "peer": true, "dependencies": { "color-name": "1.1.3" } @@ -2160,9 +2212,36 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "peer": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } }, "node_modules/commander": { "version": "2.20.3", @@ -2512,6 +2591,14 @@ "delaunator": "^4.0.0" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -2568,6 +2655,11 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, "node_modules/enhanced-resolve": { "version": "5.16.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", @@ -3480,6 +3572,11 @@ "reusify": "^1.0.4" } }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -3569,6 +3666,11 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, "node_modules/focus-trap": { "version": "7.5.2", "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.2.tgz", @@ -3577,6 +3679,25 @@ "tabbable": "^6.2.0" } }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -3602,6 +3723,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -4016,6 +4150,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/is-async-function": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", @@ -4267,6 +4406,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -4544,6 +4694,11 @@ "json-buffer": "3.0.1" } }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, "node_modules/language-subtag-registry": { "version": "0.3.22", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", @@ -4609,6 +4764,22 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/logform": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", + "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4678,9 +4849,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">= 0.6" } @@ -4689,9 +4857,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "optional": true, - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -5086,6 +5251,14 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/openid-client": { "version": "5.6.5", "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.5.tgz", @@ -5460,6 +5633,11 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5567,6 +5745,19 @@ } } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -5756,7 +5947,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -5770,9 +5960,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "optional": true, - "peer": true + ] }, "node_modules/safe-regex-test": { "version": "1.0.3", @@ -5791,6 +5979,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -5920,6 +6116,14 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -5960,6 +6164,14 @@ "source-map": "^0.6.0" } }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -5968,6 +6180,14 @@ "node": ">=10.0.0" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -6269,6 +6489,11 @@ } } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -6296,6 +6521,14 @@ "node": ">=8.0" } }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -6510,8 +6743,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { "version": "9.0.1", @@ -7000,6 +7232,40 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/winston": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", + "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", + "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/ui/package.json b/ui/package.json index 0dad061..8bb0575 100644 --- a/ui/package.json +++ b/ui/package.json @@ -26,6 +26,7 @@ "@types/node": "20.3.1", "@types/react": "18.2.13", "@types/react-dom": "18.2.6", + "axios": "^1.7.2", "eslint": "^8.56.0", "https": "^1.0.0", "next": "^14.2.3", @@ -34,19 +35,20 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-query": "^3.39.3", - "typescript": "5.1.3" + "typescript": "5.1.3", + "winston": "^3.13.0" }, "devDependencies": { "@next/eslint-plugin-next": "^14.2.3", "@redhat-cloud-services/eslint-config-redhat-cloud-services": "^2.0.4", "@types/styled-components": "^5.1.26", "@types/uuid": "^9.0.8", - "uuid": "^9.0.1", "@typescript-eslint/eslint-plugin": "^6.1.0", "css-loader": "^7.1.1", "eslint-config-next": "^14.2.3", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^4.2.1", - "prettier": "2.8.8" + "prettier": "2.8.8", + "uuid": "^9.0.1" } } diff --git a/ui/src/app/api/auth/[...nextauth]/route.ts b/ui/src/app/api/auth/[...nextauth]/route.ts index 0e3213a..a757653 100644 --- a/ui/src/app/api/auth/[...nextauth]/route.ts +++ b/ui/src/app/api/auth/[...nextauth]/route.ts @@ -3,12 +3,29 @@ import NextAuth from 'next-auth'; import { NextAuthOptions } from 'next-auth'; import GitHubProvider from 'next-auth/providers/github'; import CredentialsProvider from 'next-auth/providers/credentials'; +import axios from 'axios'; +import winston from 'winston'; +import path from 'path'; + +const logger = winston.createLogger({ + level: 'info', + format: winston.format.combine( + winston.format.timestamp(), + winston.format.printf(({ timestamp, level, message }) => { + return `${timestamp} ${level}: ${message}`; + }) + ), + transports: [new winston.transports.Console(), new winston.transports.File({ filename: path.join(process.cwd(), 'auth.log') })], +}); + +const ORG = 'instructlab'; export const authOptions: NextAuthOptions = { providers: [ GitHubProvider({ clientId: process.env.OAUTH_GITHUB_ID!, clientSecret: process.env.OAUTH_GITHUB_SECRET!, + authorization: { params: { scope: 'read:user' } }, }), CredentialsProvider({ name: 'Credentials', @@ -21,8 +38,10 @@ export const authOptions: NextAuthOptions = { credentials?.username === (process.env.IL_UI_ADMIN_USERNAME || 'admin') && credentials?.password === (process.env.IL_UI_ADMIN_PASSWORD || 'password') ) { + logger.info(`User ${credentials.username} logged in successfully with credentials`); return { id: '1', name: 'Admin', email: 'admin@example.com' }; } + logger.warn(`Failed login attempt with username: ${credentials?.username}`); return null; }, }), @@ -44,9 +63,44 @@ export const authOptions: NextAuthOptions = { } return session; }, + async signIn({ account, profile }) { + if (account.provider === 'github') { + try { + const response = await axios.get(`https://api.github.com/orgs/${ORG}/members/${profile.login}`, { + headers: { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${process.env.GITHUB_TOKEN}`, + 'X-GitHub-Api-Version': '2022-11-28', + }, + validateStatus: (status) => { + return [204, 302, 404].includes(status); + }, + }); + + if (response.status === 204) { + console.log(`User ${profile.login} logged in successfully with GitHub`); + logger.info(`User ${profile.login} logged in successfully with GitHub`); + return true; + } else if (response.status === 404) { + console.log(`User ${profile.login} is not a member of the ${ORG} organization`); + logger.warn(`User ${profile.login} is not a member of the ${ORG} organization`); + return `/error?error=AccessDenied`; // Redirect to custom error page + } else { + console.log(`Unexpected error for user ${profile.login} during organization membership verification`); + logger.error(`Unexpected error for user ${profile.login} during organization membership verification`); + return false; + } + } catch (error) { + logger.error(`Error fetching GitHub organization membership for user ${profile.login}: ${error.message}`); + return false; + } + } + return true; + }, }, pages: { signIn: '/login', + error: '/error', }, }; diff --git a/ui/src/app/error/error.module.css b/ui/src/app/error/error.module.css new file mode 100644 index 0000000..056902f --- /dev/null +++ b/ui/src/app/error/error.module.css @@ -0,0 +1,41 @@ +/* src/app/error/error.module.css */ +.errorContainer { + text-align: center; + padding: 50px; + font-family: Arial, sans-serif; +} + +.errorTitle { + font-size: 72px; + margin-bottom: 20px; +} + +.errorMessage { + font-size: 24px; + margin-bottom: 20px; +} + +.backLink { + font-size: 20px; + color: #007bff; + text-decoration: none; + display: block; + margin-bottom: 20px; +} + +.backLink:hover { + text-decoration: underline; +} + +.orgLink { + font-size: 18px; +} + +.inlineLink { + color: #007bff; + text-decoration: none; +} + +.inlineLink:hover { + text-decoration: underline; +} diff --git a/ui/src/app/error/page.tsx b/ui/src/app/error/page.tsx new file mode 100644 index 0000000..17dea2d --- /dev/null +++ b/ui/src/app/error/page.tsx @@ -0,0 +1,36 @@ +// src/app/error/page.tsx +'use client'; + +import { useSearchParams } from 'next/navigation'; +import React from 'react'; +import styles from './error.module.css'; + +const ErrorPage = () => { + const searchParams = useSearchParams(); + const error = searchParams.get('error'); + + let errorMessage = 'Oh noes, something went wrong.'; + if (error === 'AccessDenied') { + errorMessage = 'Whoops! You need to be a member of the InstructLab org to access this site. Try joining and then come back!'; + } + + return ( +
+

404

+

{errorMessage}

+ + Return to the Login Page + +

+ Want to join the InstructLab organization? Visit our + + {' '} + GitHub page + + . +

+
+ ); +}; + +export default ErrorPage; diff --git a/ui/src/app/playground/chat/page.tsx b/ui/src/app/playground/chat/page.tsx index 3a4739c..519f879 100644 --- a/ui/src/app/playground/chat/page.tsx +++ b/ui/src/app/playground/chat/page.tsx @@ -15,17 +15,7 @@ import UserIcon from '@patternfly/react-icons/dist/dynamic/icons/user-icon'; import CopyIcon from '@patternfly/react-icons/dist/dynamic/icons/copy-icon'; import Image from 'next/image'; import styles from './chat.module.css'; - -interface Message { - text: string; - isUser: boolean; -} - -interface Model { - name: string; - apiURL: string; - modelName: string; -} +import { Endpoint, Message, Model } from '@/types'; const ChatPage: React.FC = () => { const [question, setQuestion] = useState(''); @@ -53,7 +43,7 @@ const ChatPage: React.FC = () => { const storedEndpoints = localStorage.getItem('endpoints'); const customModels = storedEndpoints - ? JSON.parse(storedEndpoints).map((endpoint: any) => ({ + ? JSON.parse(storedEndpoints).map((endpoint: Endpoint) => ({ name: endpoint.modelName, apiURL: `${endpoint.url}`, modelName: endpoint.modelName, diff --git a/ui/src/app/playground/devchat/page.tsx b/ui/src/app/playground/devchat/page.tsx index 044ab19..fd686fd 100644 --- a/ui/src/app/playground/devchat/page.tsx +++ b/ui/src/app/playground/devchat/page.tsx @@ -38,7 +38,7 @@ const ChatPage: React.FC = () => { const [question, setQuestion] = useState(''); const [systemRole, setSystemRole] = useState( 'You are a cautious assistant. You carefully follow instructions.' + - ' You are helpful and harmless and you follow ethical guidelines and promote positive behavior.' + ' You are helpful and harmless and you follow ethical guidelines and promote positive behavior.' ); const [messages, setMessages] = useState([]); const [newMessages, setNewMessages] = useState([]); @@ -52,14 +52,7 @@ const ChatPage: React.FC = () => { const [isModalOpen, setIsModalOpen] = useState(false); const messagesContainerRef = useRef(null); - const { - isSelectOpen, - selectedModel, - customModels, - setIsSelectOpen, - onToggleClick, - onSelect, - } = useModelSelector(); + const { isSelectOpen, selectedModel, customModels, setIsSelectOpen, onToggleClick, onSelect } = useModelSelector(); const toggle = (toggleRef: React.Ref) => ( diff --git a/ui/src/app/playground/devchat/useModelSelector.ts b/ui/src/app/playground/devchat/useModelSelector.ts index bc700be..171db18 100644 --- a/ui/src/app/playground/devchat/useModelSelector.ts +++ b/ui/src/app/playground/devchat/useModelSelector.ts @@ -35,10 +35,10 @@ export const useModelSelector = () => { const customModels = storedEndpoints ? JSON.parse(storedEndpoints).map((endpoint: Endpoint) => ({ - name: endpoint.modelName, - apiURL: `${endpoint.url}`, - modelName: endpoint.modelName, - })) + name: endpoint.modelName, + apiURL: `${endpoint.url}`, + modelName: endpoint.modelName, + })) : []; console.log('Custom Models:', customModels); diff --git a/ui/src/components/Contribute/Knowledge/knowledge.css b/ui/src/components/Contribute/Knowledge/knowledge.css index 2d4c433..1a707ec 100644 --- a/ui/src/components/Contribute/Knowledge/knowledge.css +++ b/ui/src/components/Contribute/Knowledge/knowledge.css @@ -1,25 +1,24 @@ /* Knowledge CSS */ .form-k { - width: 80%; - padding-left: 20%; + width: 80%; + padding-left: 20%; } - .submit-k { - display: inline-block; - margin-top: 30px; - margin-left: 50%; + display: inline-block; + margin-top: 30px; + margin-left: 50%; } .submit-k:hover { - background-color: #45a049; - /* Darker Green */ + background-color: #45a049; + /* Darker Green */ } .heading-k { - text-align: left; - text-shadow: #45a049; - font-size: large; - color: rgb(4, 4, 135); + text-align: left; + text-shadow: #45a049; + font-size: large; + color: rgb(4, 4, 135); } diff --git a/ui/src/components/Contribute/Skill/skill.css b/ui/src/components/Contribute/Skill/skill.css index e3f6927..28716d6 100644 --- a/ui/src/components/Contribute/Skill/skill.css +++ b/ui/src/components/Contribute/Skill/skill.css @@ -1,25 +1,24 @@ /* Skill CSS */ .form { - width: 80%; - padding-left: 20%; + width: 80%; + padding-left: 20%; } - .submit { - display: inline-block; - margin-top: 30px; - margin-left: 50%; + display: inline-block; + margin-top: 30px; + margin-left: 50%; } .submit:hover { - background-color: #45a049; - /* Darker Green */ + background-color: #45a049; + /* Darker Green */ } .heading { - text-align: left; - text-shadow: #45a049; - font-size: large; - color: rgb(4, 4, 135); + text-align: left; + text-shadow: #45a049; + font-size: large; + color: rgb(4, 4, 135); } diff --git a/ui/src/types/index.ts b/ui/src/types/index.ts index 238026a..d32c7fd 100644 --- a/ui/src/types/index.ts +++ b/ui/src/types/index.ts @@ -6,3 +6,14 @@ export interface Endpoint { apiKey: string; modelName: string; } + +export interface Message { + text: string; + isUser: boolean; +} + +export interface Model { + name: string; + apiURL: string; + modelName: string; +}