From d05961ad15d460df71d38ead4ce8d33ca6166a4e Mon Sep 17 00:00:00 2001 From: NotNite Date: Tue, 9 May 2023 16:26:36 -0400 Subject: [PATCH] when that server action hits --- package-lock.json | 299 ++++++++---------- package.json | 5 +- src/actions/index.ts | 4 + src/actions/login.ts | 26 ++ .../register/route.ts => actions/register.ts} | 46 +-- .../api/unlink/route.ts => actions/unlink.ts} | 26 +- .../api/update/route.ts => actions/update.ts} | 62 ++-- src/app/api/login/route.ts | 29 -- src/app/login/LoginForm.tsx | 48 +-- src/app/me/AboutMe.module.css | 2 +- src/app/me/AboutMe.tsx | 154 +-------- src/app/register/RegisterForm.tsx | 75 ++--- src/auth/AuthProvider.ts | 2 +- src/components/Connection.tsx | 7 +- src/components/Input.tsx | 1 - src/components/PrettyForm.tsx | 2 +- src/schemas.ts | 116 +++---- 17 files changed, 325 insertions(+), 579 deletions(-) create mode 100644 src/actions/index.ts create mode 100644 src/actions/login.ts rename src/{app/api/register/route.ts => actions/register.ts} (53%) rename src/{app/api/unlink/route.ts => actions/unlink.ts} (69%) rename src/{app/api/update/route.ts => actions/update.ts} (59%) delete mode 100644 src/app/api/login/route.ts diff --git a/package-lock.json b/package-lock.json index f18c2a4..3825591 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,14 +21,15 @@ "formik": "^2.2.9", "graphql": "^16.6.0", "ldapts": "^4.2.5", - "next": "13.3.1", + "next": "^13.4.2-canary.3", "pino": "^8.11.0", "react": "18.2.0", "react-dom": "18.2.0", "sharp": "^0.32.0", "typescript": "5.0.4", "uuid": "^9.0.0", - "yup": "^1.1.1" + "zod": "^3.21.4", + "zod-formik-adapter": "^1.2.0" }, "devDependencies": { "@graphql-codegen/cli": "^3.3.1", @@ -1991,9 +1992,9 @@ "dev": true }, "node_modules/@next/env": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.3.1.tgz", - "integrity": "sha512-EDtCoedIZC7JlUQ3uaQpSc4aVmyhbLHmQVALg7pFfQgOTjgSnn7mKtA0DiCMkYvvsx6aFb5octGMtWrOtGXW9A==" + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.2-canary.3.tgz", + "integrity": "sha512-xPzFvIhHqh2V1pbcgtL/MD1QbJanKieiACs4U3ctVAvbd3WWXi0duZ321hPGrLHaVg3V4QmWPUJi0/68u+8hMQ==" }, "node_modules/@next/eslint-plugin-next": { "version": "13.3.1", @@ -2004,9 +2005,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.3.1.tgz", - "integrity": "sha512-UXPtriEc/pBP8luSLSCZBcbzPeVv+SSjs9cH/KygTbhmACye8/OOXRZO13Z2Wq1G0gLmEAIHQAOuF+vafPd2lw==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.2-canary.3.tgz", + "integrity": "sha512-soq2iMZNi9A4CXxhzYytNGhDkvyngB7WFhPk+UpfR6xb6DcWajqdrL1D+LE2c0UgXdejyN6CmMKgVMZdKJ6EZg==", "cpu": [ "arm64" ], @@ -2019,9 +2020,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.3.1.tgz", - "integrity": "sha512-lT36yYxosCfLtplFzJWgo0hrPu6/do8+msgM7oQkPeohDNdhjtjFUgOOwdSnPublLR6Mo2Ym4P/wl5OANuD2bw==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.2-canary.3.tgz", + "integrity": "sha512-z45IfyF0YM+TZ3jWpAV6sbqHPZOdx4XD8ClwZ0rA8SxmH4rvSPDcMxSp5Yz9nuTMTWp1Xz7ewXeaQV4kwY1qmw==", "cpu": [ "x64" ], @@ -2034,9 +2035,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.3.1.tgz", - "integrity": "sha512-wRb76nLWJhonH8s3kxC/1tFguEkeOPayIwe9mkaz1G/yeS3OrjeyKMJsb4+Kdg0zbTo53bNCOl59NNtDM7yyyw==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.2-canary.3.tgz", + "integrity": "sha512-Rk+VMv/PBjcvmSQY4lR87gCIAhPQIR/DV7PaCAWmtu5zbGsPjERQ7IwMj69QH2lcEtNLu0n3IkC9SuWLqpxFtg==", "cpu": [ "arm64" ], @@ -2049,9 +2050,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.3.1.tgz", - "integrity": "sha512-qz3BzjJRZ16Iq/jrp+pjiYOc0jTjHlfmxQmZk9x/+5uhRP6/eWQSTAPVJ33BMo6oK5O5N4644OgTAbzXzorecg==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.2-canary.3.tgz", + "integrity": "sha512-JQNxpHomFtQUny1os63AXdAr5ADc7UugA1Yo5bR0KvqviYHew50nehBuPdSWhWB80HePQRa7O5C5+x8afcBsGg==", "cpu": [ "arm64" ], @@ -2064,9 +2065,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.3.1.tgz", - "integrity": "sha512-6mgkLmwlyWlomQmpl21I3hxgqE5INoW4owTlcLpNsd1V4wP+J46BlI/5zV5KWWbzjfncIqzXoeGs5Eg+1GHODA==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.2-canary.3.tgz", + "integrity": "sha512-Te7h7ynuVkQ1bXOJouLQ01DQ+d94v+AZ2E7Z56+faR/cC7n1sJ+fLbBD49gCT/1Fd+cZ7KMrZ9h8SjQUxB7nyw==", "cpu": [ "x64" ], @@ -2079,9 +2080,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.3.1.tgz", - "integrity": "sha512-uqm5sielhQmKJM+qayIhgZv1KlS5pqTdQ99b+Z7hMWryXS96qE0DftTmMZowBcUL6x7s2vSXyH5wPtO1ON7LBg==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.2-canary.3.tgz", + "integrity": "sha512-SD2PWkZPeSXEmzTM8OfDkVAIYy5d/Q1slZ4kLL3ab581pEcorhUa59IXt4ocx4T8Wl+EDGUHb7lgRzcfHm4c8w==", "cpu": [ "x64" ], @@ -2094,9 +2095,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.3.1.tgz", - "integrity": "sha512-WomIiTj/v3LevltlibNQKmvrOymNRYL+a0dp5R73IwPWN5FvXWwSELN/kiNALig/+T3luc4qHNTyvMCp9L6U5Q==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.2-canary.3.tgz", + "integrity": "sha512-dG2VhnamPCiqCjqu/QM8rt8j6qTxthKN8QxbwhIHybSyKYELWHrKHouxSRyOK5Ai0FojcsdWmsMcu+MMiqnwmg==", "cpu": [ "arm64" ], @@ -2109,9 +2110,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.3.1.tgz", - "integrity": "sha512-M+PoH+0+q658wRUbs285RIaSTYnGBSTdweH/0CdzDgA6Q4rBM0sQs4DHmO3BPP0ltCO/vViIoyG7ks66XmCA5g==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.2-canary.3.tgz", + "integrity": "sha512-tnrhuv1MIXjOiHzDt6HnQSa3urbtAoanbd70SvlXMK9jTFajSgzeJ6o+ZO28RfYSUr/mXajS3HlF+4MBO8oVnw==", "cpu": [ "ia32" ], @@ -2124,9 +2125,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.3.1.tgz", - "integrity": "sha512-Sl1F4Vp5Z1rNXWZYqJwMuWRRol4bqOB6+/d7KqkgQ4AcafKPN1PZmpkCoxv4UFHtFNIB7EotnuIhtXu3zScicQ==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.2-canary.3.tgz", + "integrity": "sha512-L3RSWOY27A6W2aNa05RX4tFwpLzNEyKQbr7bGn/MMMctuoRQfPZiogsdSFqYe/s8d5GsMAIhfB1yJ1oGrRiYTA==", "cpu": [ "x64" ], @@ -2292,9 +2293,9 @@ "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" }, "node_modules/@swc/helpers": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.0.tgz", - "integrity": "sha512-SjY/p4MmECVVEWspzSRpQEM3sjR17sP8PbGxELWrT+YZMBfiUyt1MRUNjMV23zohwlG2HYtCQOsCwsTHguXkyg==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", + "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==", "dependencies": { "tslib": "^2.4.0" } @@ -6027,33 +6028,34 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, "node_modules/next": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/next/-/next-13.3.1.tgz", - "integrity": "sha512-eByWRxPzKHs2oQz1yE41LX35umhz86ZSZ+mYyXBqn2IBi2hyUqxBA88avywdr4uyH+hCJczegGsDGWbzQA5Rqw==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/next/-/next-13.4.2-canary.3.tgz", + "integrity": "sha512-23JMGWN3fs3Tq8x1dVslWsOAlxQh5jhPXXQrKszmps1UVzvv/hA+hhAUZYeKrltfjWUEk6tZ9JEM49IO4jFnTg==", "dependencies": { - "@next/env": "13.3.1", - "@swc/helpers": "0.5.0", + "@next/env": "13.4.2-canary.3", + "@swc/helpers": "0.5.1", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", "postcss": "8.4.14", - "styled-jsx": "5.1.1" + "styled-jsx": "5.1.1", + "zod": "3.21.4" }, "bin": { "next": "dist/bin/next" }, "engines": { - "node": ">=14.18.0" + "node": ">=16.8.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "13.3.1", - "@next/swc-darwin-x64": "13.3.1", - "@next/swc-linux-arm64-gnu": "13.3.1", - "@next/swc-linux-arm64-musl": "13.3.1", - "@next/swc-linux-x64-gnu": "13.3.1", - "@next/swc-linux-x64-musl": "13.3.1", - "@next/swc-win32-arm64-msvc": "13.3.1", - "@next/swc-win32-ia32-msvc": "13.3.1", - "@next/swc-win32-x64-msvc": "13.3.1" + "@next/swc-darwin-arm64": "13.4.2-canary.3", + "@next/swc-darwin-x64": "13.4.2-canary.3", + "@next/swc-linux-arm64-gnu": "13.4.2-canary.3", + "@next/swc-linux-arm64-musl": "13.4.2-canary.3", + "@next/swc-linux-x64-gnu": "13.4.2-canary.3", + "@next/swc-linux-x64-musl": "13.4.2-canary.3", + "@next/swc-win32-arm64-msvc": "13.4.2-canary.3", + "@next/swc-win32-ia32-msvc": "13.4.2-canary.3", + "@next/swc-win32-x64-msvc": "13.4.2-canary.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -6820,11 +6822,6 @@ "react-is": "^16.13.1" } }, - "node_modules/property-expr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", - "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==" - }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -7722,11 +7719,6 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, - "node_modules/tiny-case": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", - "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" - }, "node_modules/tiny-glob": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", @@ -7782,11 +7774,6 @@ "node": ">=8.0" } }, - "node_modules/toposort": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", - "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" - }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -8274,28 +8261,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yup": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/yup/-/yup-1.1.1.tgz", - "integrity": "sha512-KfCGHdAErqFZWA5tZf7upSUnGKuTOnsI3hUsLr7fgVtx+DK04NPV01A68/FslI4t3s/ZWpvXJmgXhd7q6ICnag==", - "dependencies": { - "property-expr": "^2.0.5", - "tiny-case": "^1.0.3", - "toposort": "^2.0.2", - "type-fest": "^2.19.0" - } - }, - "node_modules/yup/node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/zen-observable": { "version": "0.8.15", "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", @@ -8308,6 +8273,23 @@ "dependencies": { "zen-observable": "0.8.15" } + }, + "node_modules/zod": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", + "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-formik-adapter": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/zod-formik-adapter/-/zod-formik-adapter-1.2.0.tgz", + "integrity": "sha512-62U+Mf8U05pvLsIMUTC1H6d4K5SmsrXM+YgiZhCpPin5GNhnVuXCGySmJCs0FHuTJCBy7Et5Z1i8KF5ffuLttg==", + "peerDependencies": { + "formik": "^2.2.9", + "zod": "^3.14.4" + } } }, "dependencies": { @@ -9777,9 +9759,9 @@ } }, "@next/env": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.3.1.tgz", - "integrity": "sha512-EDtCoedIZC7JlUQ3uaQpSc4aVmyhbLHmQVALg7pFfQgOTjgSnn7mKtA0DiCMkYvvsx6aFb5octGMtWrOtGXW9A==" + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.2-canary.3.tgz", + "integrity": "sha512-xPzFvIhHqh2V1pbcgtL/MD1QbJanKieiACs4U3ctVAvbd3WWXi0duZ321hPGrLHaVg3V4QmWPUJi0/68u+8hMQ==" }, "@next/eslint-plugin-next": { "version": "13.3.1", @@ -9790,57 +9772,57 @@ } }, "@next/swc-darwin-arm64": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.3.1.tgz", - "integrity": "sha512-UXPtriEc/pBP8luSLSCZBcbzPeVv+SSjs9cH/KygTbhmACye8/OOXRZO13Z2Wq1G0gLmEAIHQAOuF+vafPd2lw==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.2-canary.3.tgz", + "integrity": "sha512-soq2iMZNi9A4CXxhzYytNGhDkvyngB7WFhPk+UpfR6xb6DcWajqdrL1D+LE2c0UgXdejyN6CmMKgVMZdKJ6EZg==", "optional": true }, "@next/swc-darwin-x64": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.3.1.tgz", - "integrity": "sha512-lT36yYxosCfLtplFzJWgo0hrPu6/do8+msgM7oQkPeohDNdhjtjFUgOOwdSnPublLR6Mo2Ym4P/wl5OANuD2bw==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.2-canary.3.tgz", + "integrity": "sha512-z45IfyF0YM+TZ3jWpAV6sbqHPZOdx4XD8ClwZ0rA8SxmH4rvSPDcMxSp5Yz9nuTMTWp1Xz7ewXeaQV4kwY1qmw==", "optional": true }, "@next/swc-linux-arm64-gnu": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.3.1.tgz", - "integrity": "sha512-wRb76nLWJhonH8s3kxC/1tFguEkeOPayIwe9mkaz1G/yeS3OrjeyKMJsb4+Kdg0zbTo53bNCOl59NNtDM7yyyw==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.2-canary.3.tgz", + "integrity": "sha512-Rk+VMv/PBjcvmSQY4lR87gCIAhPQIR/DV7PaCAWmtu5zbGsPjERQ7IwMj69QH2lcEtNLu0n3IkC9SuWLqpxFtg==", "optional": true }, "@next/swc-linux-arm64-musl": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.3.1.tgz", - "integrity": "sha512-qz3BzjJRZ16Iq/jrp+pjiYOc0jTjHlfmxQmZk9x/+5uhRP6/eWQSTAPVJ33BMo6oK5O5N4644OgTAbzXzorecg==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.2-canary.3.tgz", + "integrity": "sha512-JQNxpHomFtQUny1os63AXdAr5ADc7UugA1Yo5bR0KvqviYHew50nehBuPdSWhWB80HePQRa7O5C5+x8afcBsGg==", "optional": true }, "@next/swc-linux-x64-gnu": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.3.1.tgz", - "integrity": "sha512-6mgkLmwlyWlomQmpl21I3hxgqE5INoW4owTlcLpNsd1V4wP+J46BlI/5zV5KWWbzjfncIqzXoeGs5Eg+1GHODA==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.2-canary.3.tgz", + "integrity": "sha512-Te7h7ynuVkQ1bXOJouLQ01DQ+d94v+AZ2E7Z56+faR/cC7n1sJ+fLbBD49gCT/1Fd+cZ7KMrZ9h8SjQUxB7nyw==", "optional": true }, "@next/swc-linux-x64-musl": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.3.1.tgz", - "integrity": "sha512-uqm5sielhQmKJM+qayIhgZv1KlS5pqTdQ99b+Z7hMWryXS96qE0DftTmMZowBcUL6x7s2vSXyH5wPtO1ON7LBg==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.2-canary.3.tgz", + "integrity": "sha512-SD2PWkZPeSXEmzTM8OfDkVAIYy5d/Q1slZ4kLL3ab581pEcorhUa59IXt4ocx4T8Wl+EDGUHb7lgRzcfHm4c8w==", "optional": true }, "@next/swc-win32-arm64-msvc": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.3.1.tgz", - "integrity": "sha512-WomIiTj/v3LevltlibNQKmvrOymNRYL+a0dp5R73IwPWN5FvXWwSELN/kiNALig/+T3luc4qHNTyvMCp9L6U5Q==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.2-canary.3.tgz", + "integrity": "sha512-dG2VhnamPCiqCjqu/QM8rt8j6qTxthKN8QxbwhIHybSyKYELWHrKHouxSRyOK5Ai0FojcsdWmsMcu+MMiqnwmg==", "optional": true }, "@next/swc-win32-ia32-msvc": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.3.1.tgz", - "integrity": "sha512-M+PoH+0+q658wRUbs285RIaSTYnGBSTdweH/0CdzDgA6Q4rBM0sQs4DHmO3BPP0ltCO/vViIoyG7ks66XmCA5g==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.2-canary.3.tgz", + "integrity": "sha512-tnrhuv1MIXjOiHzDt6HnQSa3urbtAoanbd70SvlXMK9jTFajSgzeJ6o+ZO28RfYSUr/mXajS3HlF+4MBO8oVnw==", "optional": true }, "@next/swc-win32-x64-msvc": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.3.1.tgz", - "integrity": "sha512-Sl1F4Vp5Z1rNXWZYqJwMuWRRol4bqOB6+/d7KqkgQ4AcafKPN1PZmpkCoxv4UFHtFNIB7EotnuIhtXu3zScicQ==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.2-canary.3.tgz", + "integrity": "sha512-L3RSWOY27A6W2aNa05RX4tFwpLzNEyKQbr7bGn/MMMctuoRQfPZiogsdSFqYe/s8d5GsMAIhfB1yJ1oGrRiYTA==", "optional": true }, "@nodelib/fs.scandir": { @@ -9955,9 +9937,9 @@ "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" }, "@swc/helpers": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.0.tgz", - "integrity": "sha512-SjY/p4MmECVVEWspzSRpQEM3sjR17sP8PbGxELWrT+YZMBfiUyt1MRUNjMV23zohwlG2HYtCQOsCwsTHguXkyg==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", + "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==", "requires": { "tslib": "^2.4.0" } @@ -12704,25 +12686,26 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, "next": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/next/-/next-13.3.1.tgz", - "integrity": "sha512-eByWRxPzKHs2oQz1yE41LX35umhz86ZSZ+mYyXBqn2IBi2hyUqxBA88avywdr4uyH+hCJczegGsDGWbzQA5Rqw==", + "version": "13.4.2-canary.3", + "resolved": "https://registry.npmjs.org/next/-/next-13.4.2-canary.3.tgz", + "integrity": "sha512-23JMGWN3fs3Tq8x1dVslWsOAlxQh5jhPXXQrKszmps1UVzvv/hA+hhAUZYeKrltfjWUEk6tZ9JEM49IO4jFnTg==", "requires": { - "@next/env": "13.3.1", - "@next/swc-darwin-arm64": "13.3.1", - "@next/swc-darwin-x64": "13.3.1", - "@next/swc-linux-arm64-gnu": "13.3.1", - "@next/swc-linux-arm64-musl": "13.3.1", - "@next/swc-linux-x64-gnu": "13.3.1", - "@next/swc-linux-x64-musl": "13.3.1", - "@next/swc-win32-arm64-msvc": "13.3.1", - "@next/swc-win32-ia32-msvc": "13.3.1", - "@next/swc-win32-x64-msvc": "13.3.1", - "@swc/helpers": "0.5.0", + "@next/env": "13.4.2-canary.3", + "@next/swc-darwin-arm64": "13.4.2-canary.3", + "@next/swc-darwin-x64": "13.4.2-canary.3", + "@next/swc-linux-arm64-gnu": "13.4.2-canary.3", + "@next/swc-linux-arm64-musl": "13.4.2-canary.3", + "@next/swc-linux-x64-gnu": "13.4.2-canary.3", + "@next/swc-linux-x64-musl": "13.4.2-canary.3", + "@next/swc-win32-arm64-msvc": "13.4.2-canary.3", + "@next/swc-win32-ia32-msvc": "13.4.2-canary.3", + "@next/swc-win32-x64-msvc": "13.4.2-canary.3", + "@swc/helpers": "0.5.1", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", "postcss": "8.4.14", - "styled-jsx": "5.1.1" + "styled-jsx": "5.1.1", + "zod": "3.21.4" } }, "no-case": { @@ -13256,11 +13239,6 @@ "react-is": "^16.13.1" } }, - "property-expr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", - "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==" - }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -13915,11 +13893,6 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, - "tiny-case": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", - "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" - }, "tiny-glob": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", @@ -13966,11 +13939,6 @@ "is-number": "^7.0.0" } }, - "toposort": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", - "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" - }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -14319,24 +14287,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" }, - "yup": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/yup/-/yup-1.1.1.tgz", - "integrity": "sha512-KfCGHdAErqFZWA5tZf7upSUnGKuTOnsI3hUsLr7fgVtx+DK04NPV01A68/FslI4t3s/ZWpvXJmgXhd7q6ICnag==", - "requires": { - "property-expr": "^2.0.5", - "tiny-case": "^1.0.3", - "toposort": "^2.0.2", - "type-fest": "^2.19.0" - }, - "dependencies": { - "type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" - } - } - }, "zen-observable": { "version": "0.8.15", "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", @@ -14349,6 +14299,17 @@ "requires": { "zen-observable": "0.8.15" } + }, + "zod": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", + "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==" + }, + "zod-formik-adapter": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/zod-formik-adapter/-/zod-formik-adapter-1.2.0.tgz", + "integrity": "sha512-62U+Mf8U05pvLsIMUTC1H6d4K5SmsrXM+YgiZhCpPin5GNhnVuXCGySmJCs0FHuTJCBy7Et5Z1i8KF5ffuLttg==", + "requires": {} } } } diff --git a/package.json b/package.json index 2b544d4..1cc46ee 100644 --- a/package.json +++ b/package.json @@ -25,14 +25,15 @@ "formik": "^2.2.9", "graphql": "^16.6.0", "ldapts": "^4.2.5", - "next": "13.3.1", + "next": "^13.4.2-canary.3", "pino": "^8.11.0", "react": "18.2.0", "react-dom": "18.2.0", "sharp": "^0.32.0", "typescript": "5.0.4", "uuid": "^9.0.0", - "yup": "^1.1.1" + "zod": "^3.21.4", + "zod-formik-adapter": "^1.2.0" }, "devDependencies": { "@graphql-codegen/cli": "^3.3.1", diff --git a/src/actions/index.ts b/src/actions/index.ts new file mode 100644 index 0000000..edf20af --- /dev/null +++ b/src/actions/index.ts @@ -0,0 +1,4 @@ +export type ActionResponse = { + ok: boolean; + error?: string; +}; diff --git a/src/actions/login.ts b/src/actions/login.ts new file mode 100644 index 0000000..faf4ee2 --- /dev/null +++ b/src/actions/login.ts @@ -0,0 +1,26 @@ +"use server"; + +import * as ldap from "@/ldap"; +import { LoginSchema, loginSchema } from "@/schemas"; +import { ActionResponse } from "."; +import { getLogger } from "@/logger"; +import { authTicketLogin } from "@/auth/auth"; + +type Response = ActionResponse & { + ticket?: string; +}; + +export default async function login(data: LoginSchema): Promise { + const { username, password } = await loginSchema.parse(data); + + const valid = await ldap.validateUser(username, password); + if (!valid) { + return { + ok: false, + error: "invalidCredentials" + }; + } + + const [_, ticket] = await authTicketLogin(username); + return { ok: true, ticket: ticket.ticket }; +} diff --git a/src/app/api/register/route.ts b/src/actions/register.ts similarity index 53% rename from src/app/api/register/route.ts rename to src/actions/register.ts index 76697a3..69f66af 100644 --- a/src/app/api/register/route.ts +++ b/src/actions/register.ts @@ -1,38 +1,47 @@ +"use server"; + import * as ldap from "@/ldap"; import prisma from "@/prisma"; import { getUser } from "@/auth/auth"; import { getLogger } from "@/logger"; -import { registerServerSchema } from "@/schemas"; +import { RegisterSchema, registerSchema } from "@/schemas"; +import { ActionResponse } from "."; -const logger = getLogger("/api/register"); +const logger = getLogger("/actions/register"); -export async function POST(request: Request) { +export default async function register( + data: RegisterSchema +): Promise { const user = await getUser(); - if (user == null) return new Response(null, { status: 401 }); - // user already has an account, don't re-register + if (user == null) { + return { ok: false, error: "invalidAuth" }; + } + if (user.username != null) { logger.info( { username: user.username, id: user.id }, `user tried to register twice` ); - return new Response(null, { status: 403 }); + return { ok: false, error: "invalidAuth" }; } const { username, displayName, email, password, avatar } = - await registerServerSchema.validate(await request.json()); - let avatarBuf = avatar != null ? Buffer.from(avatar, "base64") : null; + await registerSchema.parse(data); + let avatarBuf = null; + if (avatar != null) { + const parts = avatar.split(","); + const data = parts.length === 2 ? parts[1] : parts[0]; + avatarBuf = Buffer.from(data, "base64"); + } const users = await ldap.getUsers(); for (const user of users) { if (user.id.toLowerCase() === username.toLowerCase()) { - return new Response( - JSON.stringify({ - ok: false, - error: "usernameTaken" - }), - { status: 400 } - ); + return { + ok: false, + error: "usernameTaken" + }; } } @@ -49,10 +58,5 @@ export async function POST(request: Request) { }); logger.info(outputUser, "registered user"); - return new Response( - JSON.stringify({ - ok: true - }), - { status: 201 } - ); + return { ok: true }; } diff --git a/src/app/api/unlink/route.ts b/src/actions/unlink.ts similarity index 69% rename from src/app/api/unlink/route.ts rename to src/actions/unlink.ts index 4a03828..c944ecc 100644 --- a/src/app/api/unlink/route.ts +++ b/src/actions/unlink.ts @@ -1,3 +1,6 @@ +"use server"; + +import { ValidAuthProvider } from "@/auth/AuthProvider"; import { AuthState, getAuthState, @@ -35,8 +38,7 @@ async function deleteUser(id: number) { } }); } - -export async function POST(request: Request) { +export default async function unlink(provider?: ValidAuthProvider) { const authState = await getAuthState(); if (authState == AuthState.Registering) { @@ -49,30 +51,22 @@ export async function POST(request: Request) { await deleteUser(registeringUser.id); - return new Response(null, { status: 200 }); + return; } const user = await getUser(); - if (user == null) return new Response(null, { status: 401 }); - - const { searchParams } = new URL(request.url); - const provider = searchParams.get("provider"); + if (user == null) return; switch (provider) { - case "discord": + case "Discord": const discord = await user.getDiscord(); - if (discord == null) return new Response(null, { status: 400 }); + if (discord == null) return; await unlinkDiscord(await discord.getId()); break; - case "github": + case "GitHub": const github = await user.getGitHub(); - if (github == null) return new Response(null, { status: 400 }); + if (github == null) return; await unlinkGitHub(await github.getId()); break; - - default: - return new Response(null, { status: 400 }); } - - return new Response(null, { status: 200 }); } diff --git a/src/app/api/update/route.ts b/src/actions/update.ts similarity index 59% rename from src/app/api/update/route.ts rename to src/actions/update.ts index 3e79cf6..c8b2c6a 100644 --- a/src/app/api/update/route.ts +++ b/src/actions/update.ts @@ -1,30 +1,28 @@ +"use server"; + +import { AboutMeSchema, aboutMeSchema } from "@/schemas"; +import { ActionResponse } from "."; +import { getLogger } from "@/logger"; import { getUser } from "@/auth/auth"; import { getUserInfo, updateUser } from "@/ldap"; -import { getLogger } from "@/logger"; -type RequestBody = { - displayName?: string; - email?: string; - avatar?: string; -}; - -export async function POST(request: Request) { - const logger = getLogger("/api/update"); +const logger = getLogger("/actions/update"); +export default async function update( + data: AboutMeSchema +): Promise { const user = await getUser(); - if (user == null) return new Response(null, { status: 401 }); + if (user == null) { + return { ok: false, error: "invalidAuth" }; + } const userInfo = await getUserInfo(user); if (userInfo == null) { // no user info = hasn't registered yet - return new Response(null, { status: 409 }); + return { ok: false, error: "invalidAuth" }; } - const { - displayName, - email, - avatar: avatarBase64 - } = (await request.json()) as RequestBody; + const { displayName, email, avatar } = await aboutMeSchema.parse(data); let changeDisplayName = false; if ( @@ -47,25 +45,24 @@ export async function POST(request: Request) { let avatarBuf = undefined; if ( - avatarBase64 !== undefined && - typeof avatarBase64 === "string" && - avatarBase64 !== userInfo.avatar + avatar !== undefined && + typeof avatar === "string" && + avatar !== userInfo.avatar ) { - avatarBuf = Buffer.from(avatarBase64, "base64"); + const parts = avatar.split(","); + const data = parts.length === 2 ? parts[1] : parts[0]; + avatarBuf = Buffer.from(data, "base64"); if (avatarBuf.length > 2_000_000) { - return new Response( - JSON.stringify({ - ok: false, - error: "avatarBig" - }), - { status: 400 } - ); + return { + ok: false, + error: "avatarBig" + }; } } if (!changeDisplayName && !changeEmail && !avatarBuf) { - return new Response(null, { status: 200 }); + return { ok: true }; } await updateUser( @@ -85,12 +82,5 @@ export async function POST(request: Request) { "updated user" ); - return new Response( - JSON.stringify({ - ok: true - }), - { - status: 200 - } - ); + return { ok: true }; } diff --git a/src/app/api/login/route.ts b/src/app/api/login/route.ts deleted file mode 100644 index b9565fb..0000000 --- a/src/app/api/login/route.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { authTicketLogin } from "@/auth/auth"; -import * as ldap from "@/ldap"; -import { loginSchema } from "@/schemas"; - -type RequestBody = { - username: string; - password: string; -}; - -export async function POST(request: Request) { - const { username, password } = await loginSchema.validate( - await request.json() - ); - - const valid = await ldap.validateUser(username, password); - if (!valid) { - return new Response( - JSON.stringify({ - ok: false, - error: "invalidCredentials" - }), - { status: 401 } - ); - } - - const [_, ticket] = await authTicketLogin(username); - // not confident if we can set-cookie and I cba to try - return new Response(JSON.stringify({ ok: true, ticket: ticket.ticket })); -} diff --git a/src/app/login/LoginForm.tsx b/src/app/login/LoginForm.tsx index 5a11573..f719839 100644 --- a/src/app/login/LoginForm.tsx +++ b/src/app/login/LoginForm.tsx @@ -1,54 +1,34 @@ "use client"; +import login from "@/actions/login"; import Input from "@/components/Input"; import PrettyForm from "@/components/PrettyForm"; -import { LoginFormValues, loginSchema } from "@/schemas"; +import { LoginSchema, loginSchema } from "@/schemas"; import { Form, Formik, FormikHelpers, FormikValues } from "formik"; import React from "react"; +import { toFormikValidationSchema } from "zod-formik-adapter"; export default function LoginForm() { const [globalError, setGlobalError] = React.useState(null); async function handleFormSubmit( - { username, password }: LoginFormValues, - { setSubmitting }: FormikHelpers + data: LoginSchema, + { setSubmitting }: FormikHelpers ) { setSubmitting(true); - if (username === "greets") { + if (data.username === "greets") { window.location.href = "/sekrit"; return; } - const req = await fetch("/api/login", { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - username, - password - }) - }); - - try { - const res: { - ok: boolean; - error?: string; - ticket: string; - } = await req.json(); - - if (res.ok) { - document.cookie = `ticket=${res.ticket}; path=/;`; - window.location.href = "/me"; - } else { - // only error is invalidCredentials, I am lazy - setGlobalError("Invalid credentials."); - } - } catch (err) { - console.error(err); - setGlobalError("shits fucked up yo"); - setSubmitting(false); + const res = await login(data); + if (res.ok) { + document.cookie = `ticket=${res.ticket}; path=/;`; + window.location.href = "/me"; + } else { + // only error is invalidCredentials, I am lazy + setGlobalError("Invalid credentials."); } } @@ -57,7 +37,7 @@ export default function LoginForm() { {({ isSubmitting }) => (
diff --git a/src/app/me/AboutMe.module.css b/src/app/me/AboutMe.module.css index 7aa7400..851744e 100644 --- a/src/app/me/AboutMe.module.css +++ b/src/app/me/AboutMe.module.css @@ -1,5 +1,5 @@ .content { - width: min(100vw, min-content); + width: min-content; margin: 2rem auto; } diff --git a/src/app/me/AboutMe.tsx b/src/app/me/AboutMe.tsx index 60c2cfe..e2ca182 100644 --- a/src/app/me/AboutMe.tsx +++ b/src/app/me/AboutMe.tsx @@ -7,12 +7,6 @@ import styles from "./AboutMe.module.css"; import AvatarChanger from "@/components/AvatarChanger"; import Input, { Hint, Label } from "@/components/Input"; import { Form, Formik, FormikHelpers } from "formik"; -import { - AboutMeFormValues, - PasswordUpdateFormValues, - aboutMeSchema, - passwordUpdateSchema -} from "@/schemas"; import PrettyForm from "@/components/PrettyForm"; import Toast from "@/components/Toast"; import { AuthProviderState } from "@/auth/AuthProvider"; @@ -21,11 +15,9 @@ import DiscordIcon from "@/components/icons/DiscordIcon"; import GitHubIcon from "@/components/icons/GitHubIcon"; import TailscaleIcon from "@/components/icons/TailscaleIcon"; import MigaduIcon from "@/components/icons/MigaduIcon"; - -type UpdateResponse = { - ok: boolean; - error?: string; -}; +import { AboutMeSchema, aboutMeSchema } from "@/schemas"; +import update from "@/actions/update"; +import { toFormikValidationSchema } from "zod-formik-adapter"; export default function AboutMe({ info, @@ -39,7 +31,7 @@ export default function AboutMe({ const [madeProfileChanges, setMadeChanges] = React.useState(false); const [madePasswordChanges, setMadePasswordChanges] = React.useState(false); - const initialValues: AboutMeFormValues = { + const initialValues: AboutMeSchema = { username: info.username, displayName: info.displayName, email: info.email, @@ -47,77 +39,20 @@ export default function AboutMe({ }; async function handleFormSubmit( - { displayName, email, avatar }: AboutMeFormValues, - { setSubmitting }: FormikHelpers + data: AboutMeSchema, + { setSubmitting }: FormikHelpers ) { setMadeChanges(false); setSubmitting(true); - const req = await fetch("/api/update", { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - displayName, - email, - avatar: avatar != null ? avatar.split(",")[1] : null - }) - }); + const res = await update(data); setSubmitting(false); - try { - const res: UpdateResponse = await req.json(); - - if (!res.ok && res.error !== null) { - switch (res.error) { - case "avatarBig": - break; - } - } + if (res.ok) { setMadeChanges(true); - } catch { - console.error(req); - } - } - - const [passwordError, setPasswordError] = React.useState(null); - const initialPasswordValues: PasswordUpdateFormValues = { - password: "", - newPassword: "", - confirmPassword: "" - }; - - async function handlePasswordSubmit( - { password, newPassword }: PasswordUpdateFormValues, - { setFieldError, setSubmitting }: FormikHelpers - ) { - setMadePasswordChanges(false); - setSubmitting(true); - const req = await fetch("/api/changePassword", { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - currentPassword: password, - newPassword: newPassword - }) - }); - setSubmitting(false); - - try { - const res: UpdateResponse = await req.json(); - - if (!res.ok && res.error !== null) { - switch (res.error) { - case "incorrectPassword": - setFieldError("password", "Incorrect password."); - break; - } + } else { + if (res.error != undefined) { + setGlobalError("Unknown error: " + res.error); } - setMadePasswordChanges(true); - } catch { - console.error(req); } } @@ -127,7 +62,7 @@ export default function AboutMe({ {({ isSubmitting }) => ( @@ -150,7 +85,7 @@ export default function AboutMe({

{info.username}

-
+
{madeProfileChanges ? ( Saved your changes. ) : null} @@ -233,69 +168,6 @@ export default function AboutMe({ )} - {/* - - {({ isSubmitting }) => ( - - {madePasswordChanges ? ( - Changed your password. - ) : null} - - - - - - - - - )} - - - -

Connections

-
- {providers.map((provider) => ( - - ))} -
- - { - document.cookie = - "ticket=; expires=" + new Date().toUTCString() + "; path=/"; - window.location.href = "/"; - }} - />*/}
); } diff --git a/src/app/register/RegisterForm.tsx b/src/app/register/RegisterForm.tsx index d69b25b..b4b0482 100644 --- a/src/app/register/RegisterForm.tsx +++ b/src/app/register/RegisterForm.tsx @@ -2,20 +2,17 @@ import React from "react"; import styles from "./RegisterForm.module.css"; -import { Form, Formik, FormikHelpers, yupToFormErrors } from "formik"; -import { RegisterFormValues, registerSchema } from "@/schemas"; +import { Form, Formik, FormikHelpers } from "formik"; +import { registerSchema, RegisterSchema } from "@/schemas"; import { useRouter } from "next/navigation"; -import { fileAsBase64 } from "@/forms"; import Input from "@/components/Input"; import PrettyForm from "@/components/PrettyForm"; import HugeSubmit from "@/components/HugeSubmit"; import AvatarChanger from "@/components/AvatarChanger"; import { ValidAuthProvider } from "@/auth/AuthProvider"; - -type RegisterResponse = { - ok: boolean; - error?: string; -}; +import { toFormikValidationSchema } from "zod-formik-adapter"; +import register from "@/actions/register"; +import unlink from "@/actions/unlink"; export default function RegisterForm({ initialDisplayName, @@ -31,7 +28,7 @@ export default function RegisterForm({ const [globalError, setGlobalError] = React.useState(null); const router = useRouter(); - const initialValues: RegisterFormValues = { + const initialValues: RegisterSchema = { username: "", displayName: initialDisplayName ?? "", email: initialEmail ?? "", @@ -41,49 +38,33 @@ export default function RegisterForm({ }; async function handleFormSubmit( - { avatar, username, displayName, email, password }: RegisterFormValues, - { setFieldError, setSubmitting }: FormikHelpers + data: RegisterSchema, + { setFieldError, setSubmitting }: FormikHelpers ) { setSubmitting(true); - const resp = await fetch(`/api/register`, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - username, - displayName, - email, - password, - avatar: avatar != null ? avatar.split(",")[1] : undefined - }) - }); + const res = await register(data); + if (res.ok) { + router.replace("/me"); + } else { + if (res.error !== null) { + switch (res.error) { + case "avatarBig": + setFieldError( + "avatar", + "avatar was too big, but only the server caught you what the fuck are you doing!!" + ); + break; - try { - const res: RegisterResponse = await resp.json(); + case "usernameTaken": + setFieldError("username", "Username is already taken."); + break; - if (res.ok) { - router.replace("/me"); - } else { - if (res.error !== null) { - switch (res.error) { - case "avatarBig": - setFieldError( - "avatar", - "avatar was too big, but only the server caught you what the fuck are you doing!!" - ); - break; - - case "usernameTaken": - setFieldError("username", "Username is already taken."); - break; - } + default: + setGlobalError("Unknown error: " + res.error); + break; } } - } catch (err) { - console.error(err); - setGlobalError("you done fucked up kiddo"); } setSubmitting(false); @@ -94,7 +75,7 @@ export default function RegisterForm({ {({ isSubmitting }) => ( @@ -163,7 +144,7 @@ export default function RegisterForm({ { - await fetch("/api/unlink", { method: "POST" }); + await unlink(); document.cookie = "ticket=; expires=" + new Date().toUTCString() + "; path=/"; window.location.href = "/"; diff --git a/src/auth/AuthProvider.ts b/src/auth/AuthProvider.ts index ae5f6bf..7ef53ea 100644 --- a/src/auth/AuthProvider.ts +++ b/src/auth/AuthProvider.ts @@ -2,7 +2,7 @@ export type ValidAuthProvider = "Discord" | "GitHub"; // Can't send the providers across the wire, do this instead export type AuthProviderState = { - name: string; + name: ValidAuthProvider; } & ({ connected: false } | { connected: true; id: string; username: string }); export abstract class AuthProvider { diff --git a/src/components/Connection.tsx b/src/components/Connection.tsx index 5ceb444..d5b5438 100644 --- a/src/components/Connection.tsx +++ b/src/components/Connection.tsx @@ -5,6 +5,7 @@ import classnames from "classnames"; import CheckIcon from "./icons/CheckIcon"; import { type AuthProviderState } from "@/auth/AuthProvider"; import { useRouter } from "next/navigation"; +import unlink from "@/actions/unlink"; export default function Connection({ service, @@ -23,11 +24,11 @@ export default function Connection({ const interval = React.useRef(); const execute = async () => { - const name = authState?.name.toLowerCase(); + const name = authState?.name; if (!authState?.connected) { - router.push(`/oauth/${name}/login`); + router.push(`/oauth/${name?.toLowerCase()}/login`); } else { - await fetch(`/api/unlink?provider=${name}`, { method: "POST" }); + await unlink(name); router.refresh(); } }; diff --git a/src/components/Input.tsx b/src/components/Input.tsx index 27b5ecc..31e0b5c 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -74,7 +74,6 @@ export default function Input( title={props.title} {...inputFields} onChange={(event) => { - console.log(event); if (props.customOnChange) { console.log("using custom on change"); props.customOnChange(event, form); diff --git a/src/components/PrettyForm.tsx b/src/components/PrettyForm.tsx index b062d8e..a0aa4e0 100644 --- a/src/components/PrettyForm.tsx +++ b/src/components/PrettyForm.tsx @@ -5,7 +5,7 @@ export default function PrettyForm({ globalError, children }: { - globalError: string | null; + globalError?: string | null; children: React.ReactNode; }) { return ( diff --git a/src/schemas.ts b/src/schemas.ts index 4e62f96..5ed020a 100644 --- a/src/schemas.ts +++ b/src/schemas.ts @@ -1,109 +1,71 @@ -import * as Yup from "yup"; +import { z } from "zod"; -const REQUIRED = "Required."; -const USERNAME = Yup.string() - .required(REQUIRED) +const USERNAME = z + .string() .min(1, "Username is too short.") - .matches(/^[a-z0-9]+$/, "Username must be lowercase letters and numbers."); - -const DISPLAY_NAME = Yup.string() - .required(REQUIRED) - .min(1, "Display name is too short."); -const EMAIL = Yup.string().required(REQUIRED).email("Not an email."); - -const PASSWORD = Yup.string() - .required(REQUIRED) + .regex(/^[a-z0-9]+$/, "Username must be lowercase alphanumeric."); +const DISPLAY_NAME = z.string().min(1, "Display name is too short."); +const EMAIL = z.string().email("Not an email."); +const PASSWORD = z + .string() .min(12, "Password must be at least 12 characters long."); -const CONFIRM_PASSWORD = (name: string) => - Yup.string() - .required(REQUIRED) - .oneOf([Yup.ref(name, {})], "Passwords must match."); - -const AVATAR = Yup.string().test( - "file-size", - "File is bigger than 2 MB.", - (value) => { - if (value == null) return true; +const AVATAR = z.string().refine( + (val) => { + const parts = val.split(","); + const data = parts.length === 2 ? parts[1] : parts[0]; try { - const buf = Buffer.from(value, "base64"); + const buf = Buffer.from(data, "base64"); return buf.length <= 2_000_000; } catch (e) { return false; } + }, + { + message: "File is bigger than 2 MB.", + path: ["avatar"] } ); -export const loginSchema = Yup.object().shape({ +export const loginSchema = z.object({ username: USERNAME, password: PASSWORD }); +export type LoginSchema = z.infer; -export type LoginFormValues = { - username: string; - password: string; -}; - -export const registerSchema: Yup.Schema = - Yup.object().shape({ +export const registerSchema = z + .object({ username: USERNAME, displayName: DISPLAY_NAME, email: EMAIL, password: PASSWORD, - confirmPassword: CONFIRM_PASSWORD("password"), - avatar: AVATAR + confirmPassword: PASSWORD, + avatar: AVATAR.optional() + }) + .refine((data) => data.password === data.confirmPassword, { + message: "Passwords do not match.", + path: ["confirmPassword"] }); -export interface RegisterFormValues { - username: string; - displayName: string; - email: string; - password: string; - confirmPassword: string; - avatar?: string; -} +export type RegisterSchema = z.infer; -export const aboutMeSchema: Yup.Schema = Yup.object().shape({ +export const aboutMeSchema = z.object({ username: USERNAME, displayName: DISPLAY_NAME, email: EMAIL, - avatar: AVATAR + avatar: AVATAR.optional() }); +export type AboutMeSchema = z.infer; -export interface AboutMeFormValues { - username: string; - displayName: string; - email: string; - avatar?: string; -} - -export const passwordUpdateSchema: Yup.Schema = - Yup.object().shape({ +export const passwordUpdateSchema = z + .object({ password: PASSWORD, newPassword: PASSWORD, - confirmPassword: CONFIRM_PASSWORD("newPassword") + confirmPassword: PASSWORD + }) + .refine((data) => data.newPassword === data.confirmPassword, { + message: "Passwords do not match.", + path: ["confirmPassword"] }); -export interface PasswordUpdateFormValues { - password: string; - newPassword: string; - confirmPassword: string; -} - -// Types specific to the server, because sometimes we omit fields (like confirmPassword) -export const registerServerSchema: Yup.Schema = - Yup.object().shape({ - username: USERNAME, - displayName: DISPLAY_NAME, - email: EMAIL, - password: PASSWORD, - avatar: AVATAR - }); - -export interface RegisterServerFormValues { - username: string; - displayName: string; - email: string; - password: string; - avatar?: string; -} +export type PasswordUpdateSchema = z.infer;