From 8de73452b37488b6df6c27cf36caf3b839dc8b5a Mon Sep 17 00:00:00 2001 From: hellogirls-js Date: Mon, 23 Dec 2024 15:27:50 -0600 Subject: [PATCH 1/6] feat(login): add login via username --- package.json | 3 +- pages/api/fetchEmail.page.ts | 32 ++++++++++++++++ pages/login/Login.tsx | 47 +++++++++++++++--------- services/data.ts | 5 --- services/firebase/authentication.ts | 57 +++++++++++++++++++++++++++++ services/firebase/firestore.ts | 4 +- yarn.lock | 8 ++++ 7 files changed, 131 insertions(+), 25 deletions(-) create mode 100644 pages/api/fetchEmail.page.ts diff --git a/package.json b/package.json index 957e8940..e6676384 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,8 @@ "seedrandom": "^3.0.5", "strapi-sdk-js": "^2.2.0", "swr": "^2.0.0", - "use-debounce": "^9.0.2" + "use-debounce": "^9.0.2", + "zod": "^3.24.1" }, "scripts": { "dev": "next dev", diff --git a/pages/api/fetchEmail.page.ts b/pages/api/fetchEmail.page.ts new file mode 100644 index 00000000..098905bd --- /dev/null +++ b/pages/api/fetchEmail.page.ts @@ -0,0 +1,32 @@ +import { + collection, + getDocs, + getFirestore, + query, + where, +} from "firebase/firestore"; +import { NextApiRequest, NextApiResponse } from "next"; +import { validateUsernameDb } from "services/firebase/firestore"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + try { + const username = req.body.username; + const isUsernameValid = await validateUsernameDb(username); + if (!isUsernameValid) + throw new TypeError("Could not find user with this username"); + const db = getFirestore(); + const usernameQuery = query( + collection(db, "users"), + where("username", "==", username) + ); + const querySnap = await getDocs(usernameQuery); + const userData = querySnap.docs[0]; + const userEmail = userData.data().email; + res.status(200).send({ userEmail }); + } catch (error) { + res.status(500).send(error); + } +} diff --git a/pages/login/Login.tsx b/pages/login/Login.tsx index b97f13c2..353a7a45 100644 --- a/pages/login/Login.tsx +++ b/pages/login/Login.tsx @@ -25,8 +25,10 @@ import { signInWithGoogle, signInWithEmail, signUpWithEmail, + signInFunction, } from "services/firebase/authentication"; import { showNotification } from "@mantine/notifications"; +import { z } from "zod"; function Login() { const [isRegister, setIsRegister] = useState(false); @@ -37,21 +39,19 @@ function Login() { function signOnAlertMsg(error: string) { const codeRegex = /\(([^)]+)\)/; - const code = codeRegex.exec(error)?.[1].split("/")[1] ?? ""; + const code = codeRegex.exec(error)?.[1].split("/")[1] ?? error; let message; switch (code) { case "wrong-password": - message = The password is incorrect. Please try again.; - break; + return The password is incorrect. Please try again.; case "user-not-found": - message = ( + return ( A user with this email address could not be found. Please try again. ); - break; case "timeout": - message = ( + return ( The operation has timed out. Please try again or{" "} @@ -60,39 +60,45 @@ function Login() { if the problem is persistent. ); - break; case "too-many-requests": - message = ( + return ( The server has received too many sign on requests. Please wait and try again later. ); - break; case "email-already-in-use": - message = ( + return ( The email you tried to sign up with is already in use. Please try registering with a new email or login into the account associated with the provided email. ); - break; + case "taken-username": + return ( + + The provided username is already in use. Please provide another + username + + ); + case "short-username": + return ( + The provided username must be 8 characters or longer + ); default: - message = ( + return ( An unknown sign on error has occured. Please try again or{" "} submit an issue if the problem is persistent. ); - break; } - - return message; } useEffect(() => { + console.log({ signOnError }); if (signOnError) { showNotification({ id: "signinError", @@ -113,7 +119,12 @@ function Login() { }, validate: { - email: (val) => (/^\S+@\S+$/.test(val) ? null : "Invalid email"), + email: (val) => + !isRegister + ? null + : z.string().email().safeParse(val).success + ? null + : "Please provide a valid email", password: (val) => val.length >= 6 ? null : "Password must be over 6 characters long", terms: (val: boolean) => @@ -205,7 +216,7 @@ function Login() { onError ); } else { - signInWithEmail( + signInFunction( form.values.email, form.values.password, onError @@ -226,7 +237,7 @@ function Login() { diff --git a/services/data.ts b/services/data.ts index 72350e42..ea3851b6 100644 --- a/services/data.ts +++ b/services/data.ts @@ -75,11 +75,6 @@ export async function getLocalizedDataArray< const jaData = await getData(data, "ja", true, fields); const enFanData = await getData(data, "en", false, fields); const enData = await getData(data, "en", true, fields); - console.log( - "en data", - data, - enData.data?.filter((data) => (data as Event).event_id === 60910006) - ); let localized = [enFanData, enData, jaData]; if (locale === "ja") { diff --git a/services/firebase/authentication.ts b/services/firebase/authentication.ts index 2d25fec6..eb4e19d1 100644 --- a/services/firebase/authentication.ts +++ b/services/firebase/authentication.ts @@ -12,6 +12,17 @@ import { updatePassword, reauthenticateWithPopup, } from "firebase/auth"; +import { + collection, + doc, + getDocs, + getFirestore, + query, + setDoc, + where, +} from "firebase/firestore"; +import { validateUsernameDb } from "./firestore"; +import z from "zod"; const parseKey = (key: string) => { return key?.replace(/\\n/g, "\n")?.replace(/'/g, ""); @@ -159,6 +170,52 @@ export async function signInWithEmail( } } +export async function signInWithUsername( + username: string, + password: string, + onError: (error: Error) => void +) { + try { + const isUsernameValid = await validateUsernameDb(username); + if (!isUsernameValid) + throw new TypeError("Could not find user with this username"); + const db = getFirestore(); + const usernameQuery = query( + collection(db, "users"), + where("username", "==", username) + ); + const querySnap = await getDocs(usernameQuery); + const userData = querySnap.docs[0]; + const userEmail = userData.data().email; + + await signInWithEmail(userEmail, password, onError); + } catch (error) { + onError(error as Error); + } +} + +export async function signInFunction( + emailOrUsername: string, + password: string, + onError: (error: Error) => void +) { + try { + const parseUsernameEmail = z.string().email().safeParse(emailOrUsername); + + if (!parseUsernameEmail.success) { + // this is a username + const username = emailOrUsername; + await signInWithUsername(username, password, onError); + } else { + // this is an email + const email = emailOrUsername; + await signInWithEmail(email, password, onError); + } + } catch (error) { + onError(error as Error); + } +} + export async function signUpWithEmail( email: string, password: string, diff --git a/services/firebase/firestore.ts b/services/firebase/firestore.ts index 35362e75..b7c1bcaa 100644 --- a/services/firebase/firestore.ts +++ b/services/firebase/firestore.ts @@ -83,7 +83,9 @@ export async function getFirestorePrivateUserData(uid: string) { } } -export async function validateUsernameDb(username: string | undefined) { +export async function validateUsernameDb( + username: string | undefined +): Promise { if (!username) return undefined; const db = getFirestore(); const q = query(collection(db, "users"), where("username", "==", username)); diff --git a/yarn.lock b/yarn.lock index 7ec4f6d3..e1a907b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8079,6 +8079,7 @@ __metadata: swr: ^2.0.0 typescript: ^4.9.4 use-debounce: ^9.0.2 + zod: ^3.24.1 languageName: unknown linkType: soft @@ -11339,3 +11340,10 @@ __metadata: checksum: f77b3d8d00310def622123df93d4ee654fc6a0096182af8bd60679ddcdfb3474c56c6c7190817c84a2785648cdee9d721c0154eb45698c62176c322fb46fc700 languageName: node linkType: hard + +"zod@npm:^3.24.1": + version: 3.24.1 + resolution: "zod@npm:3.24.1" + checksum: dcd5334725b29555593c186fd6505878bb7ccb4f5954f728d2de24bf71f9397492d83bdb69d5b8a376eb500a02273ae0691b57deb1eb8718df3f64c77cc5534a + languageName: node + linkType: hard From a3e8fec8387ab53f8fccaaffc9a8283a16e04b83 Mon Sep 17 00:00:00 2001 From: hellogirls-js Date: Thu, 26 Dec 2024 20:40:33 -0600 Subject: [PATCH 2/6] feat(login): add secure login via username + fix missing friend buttons --- package.json | 1 + pages/[user]/components/ProfileButtons.tsx | 3 +- pages/api/fetchEmail.page.ts | 32 -- .../{login.page.ts => login/index.page.ts} | 3 +- pages/api/login/token.page.ts | 85 ++++ pages/api/username/validate.page.ts | 24 ++ pages/login/Login.tsx | 1 - .../account/DebouncedUsernameInput.tsx | 21 +- services/firebase/authentication.ts | 22 +- services/firebase/firestore.ts | 1 - yarn.lock | 389 ++++++++++++++---- 11 files changed, 446 insertions(+), 136 deletions(-) delete mode 100644 pages/api/fetchEmail.page.ts rename pages/api/{login.page.ts => login/index.page.ts} (96%) create mode 100644 pages/api/login/token.page.ts create mode 100644 pages/api/username/validate.page.ts diff --git a/package.json b/package.json index e6676384..f0f0e758 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@tanstack/react-query": "^5.56.2", "@types/seedrandom": "^3.0.5", "@vercel/analytics": "^0.1.6", + "axios": "^1.7.9", "cookies-next": "^2.1.1", "country-flag-icons": "^1.5.5", "dayjs": "^1.11.7", diff --git a/pages/[user]/components/ProfileButtons.tsx b/pages/[user]/components/ProfileButtons.tsx index 7fbf8367..91dc0c49 100644 --- a/pages/[user]/components/ProfileButtons.tsx +++ b/pages/[user]/components/ProfileButtons.tsx @@ -8,7 +8,6 @@ import { Text, useMantineTheme, } from "@mantine/core"; -import { showNotification, updateNotification } from "@mantine/notifications"; import { IconPencil, IconCopy, @@ -114,7 +113,7 @@ function ProfileButtons({ {!isOwnProfile && ( <> - {!user && !isFriend && !isOutgoingReq && !isIncomingReq && ( + {user && !isFriend && !isOutgoingReq && !isIncomingReq && ( sendFriendReq()} diff --git a/pages/api/fetchEmail.page.ts b/pages/api/fetchEmail.page.ts deleted file mode 100644 index 098905bd..00000000 --- a/pages/api/fetchEmail.page.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - collection, - getDocs, - getFirestore, - query, - where, -} from "firebase/firestore"; -import { NextApiRequest, NextApiResponse } from "next"; -import { validateUsernameDb } from "services/firebase/firestore"; - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { - try { - const username = req.body.username; - const isUsernameValid = await validateUsernameDb(username); - if (!isUsernameValid) - throw new TypeError("Could not find user with this username"); - const db = getFirestore(); - const usernameQuery = query( - collection(db, "users"), - where("username", "==", username) - ); - const querySnap = await getDocs(usernameQuery); - const userData = querySnap.docs[0]; - const userEmail = userData.data().email; - res.status(200).send({ userEmail }); - } catch (error) { - res.status(500).send(error); - } -} diff --git a/pages/api/login.page.ts b/pages/api/login/index.page.ts similarity index 96% rename from pages/api/login.page.ts rename to pages/api/login/index.page.ts index 596880c4..357eeb5c 100644 --- a/pages/api/login.page.ts +++ b/pages/api/login/index.page.ts @@ -6,9 +6,10 @@ import { import { FieldValue } from "firebase-admin/firestore"; import { NextApiRequest, NextApiResponse } from "next"; -import { migrateCollection } from "./collections/migrate.page"; +import { migrateCollection } from "../collections/migrate.page"; import { initAuthentication } from "services/firebase/authentication"; +import { getAuth } from "firebase/auth"; try { initAuthentication(); diff --git a/pages/api/login/token.page.ts b/pages/api/login/token.page.ts new file mode 100644 index 00000000..f0ff52b6 --- /dev/null +++ b/pages/api/login/token.page.ts @@ -0,0 +1,85 @@ +import { + collection, + Firestore, + getDocs, + getFirestore, + query, + where, +} from "firebase/firestore"; +import { NextApiRequest, NextApiResponse } from "next"; +import { getAuth, signInWithEmailAndPassword } from "firebase/auth"; +import { getFirebaseAdmin } from "next-firebase-auth"; + +enum Errors { + NOT_FOUND = "Could not find user with this username", + INCOMPLETE = "Missing credentials", + UNAUTHED = "Invalid credentials", +} + +async function validateUsernameDb( + username: string | undefined +): Promise { + try { + const db = getFirestore(); + const q = query( + collection(db as unknown as Firestore, "users"), + where("username", "==", username) + ); + const querySnap = await getDocs(q); + const usernameValid = !querySnap.empty; + return usernameValid; + } catch { + return false; + } +} + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + try { + const auth = getAuth(); + const admin = getFirebaseAdmin(); + const username = req.body.username; + const password = req.body.password; + if (!username || !password) { + throw new TypeError(Errors.INCOMPLETE); + } + const isUsernameValid = await validateUsernameDb(username); + if (!isUsernameValid) throw new TypeError(Errors.NOT_FOUND); + const db = getFirestore(); + const usernameQuery = query( + collection(db, "users"), + where("username", "==", username) + ); + const querySnap = await getDocs(usernameQuery); + const userData = querySnap.docs[0]; + const email = userData.data().email; + const emailUserData = await admin.auth().getUserByEmail(email); + if (!!!emailUserData) { + throw new TypeError(Errors.NOT_FOUND); + } + const uid = userData.id; + await signInWithEmailAndPassword(auth, email, password); + const customToken = await admin.auth().createCustomToken(uid); + res.status(200).send({ customToken }); + } catch (error) { + if (error.message) { + switch (error.message) { + case Errors.NOT_FOUND: + res.status(404).send(error); + break; + case Errors.INCOMPLETE: + res.status(400).send(error); + break; + case Errors.UNAUTHED: + res.status(403).send(error); + break; + default: + res.status(500).send(error); + } + } else { + res.status(500).send(error); + } + } +} diff --git a/pages/api/username/validate.page.ts b/pages/api/username/validate.page.ts new file mode 100644 index 00000000..e8d8aa14 --- /dev/null +++ b/pages/api/username/validate.page.ts @@ -0,0 +1,24 @@ +import { + collection, + getDocs, + getFirestore, + query, + where, +} from "firebase/firestore"; +import { NextApiRequest, NextApiResponse } from "next"; + +export default async function handlers( + req: NextApiRequest, + res: NextApiResponse +) { + const username = req.body.username; + const db = getFirestore(); + const q = query(collection(db, "users"), where("username", "==", username)); + const querySnap = await getDocs(q); + const usernameValid = !!!querySnap.size; + if (usernameValid) { + res.status(200).send({ valid: true }); + } else { + res.status(400).send({ valid: false }); + } +} diff --git a/pages/login/Login.tsx b/pages/login/Login.tsx index 353a7a45..16760e9b 100644 --- a/pages/login/Login.tsx +++ b/pages/login/Login.tsx @@ -23,7 +23,6 @@ import { useRouter } from "next/router"; import useUser from "services/firebase/user"; import { signInWithGoogle, - signInWithEmail, signUpWithEmail, signInFunction, } from "services/firebase/authentication"; diff --git a/pages/settings/account/DebouncedUsernameInput.tsx b/pages/settings/account/DebouncedUsernameInput.tsx index d2ed0be2..0c6a3428 100644 --- a/pages/settings/account/DebouncedUsernameInput.tsx +++ b/pages/settings/account/DebouncedUsernameInput.tsx @@ -11,6 +11,7 @@ import { import { IconCheck, IconX, IconAt } from "@tabler/icons-react"; import Image from "next/image"; import useTranslation from "next-translate/useTranslation"; +import axios from "axios"; import kinnie from "./kinnie.png"; @@ -376,14 +377,22 @@ function DebouncedUsernameInput({ changedCallback = () => {} }) { setUsernameMsg(t("account.usernameErrorLong")); setUsernameJudgement(true); } else { - const usernameValid = await validateUsernameDb(value); - if (!usernameValid) { + try { + const res = await axios.post("/api/username/validate", { + username: value, + }); + const usernameValid = res.data.valid; + if (!usernameValid) { + setUsernameMsg(t("account.usernameTaken")); + setUsernameJudgement(true); + } else { + setNewUsername(value); + setUsernameMsg(t("account.usernameAvailable")); + setUsernameJudgement(true); + } + } catch { setUsernameMsg(t("account.usernameTaken")); setUsernameJudgement(true); - } else { - setNewUsername(value); - setUsernameMsg(t("account.usernameAvailable")); - setUsernameJudgement(true); } } } diff --git a/services/firebase/authentication.ts b/services/firebase/authentication.ts index eb4e19d1..c7b4a3c1 100644 --- a/services/firebase/authentication.ts +++ b/services/firebase/authentication.ts @@ -11,6 +11,7 @@ import { updateEmail, updatePassword, reauthenticateWithPopup, + signInWithCustomToken, } from "firebase/auth"; import { collection, @@ -23,6 +24,7 @@ import { } from "firebase/firestore"; import { validateUsernameDb } from "./firestore"; import z from "zod"; +import axios from "axios"; const parseKey = (key: string) => { return key?.replace(/\\n/g, "\n")?.replace(/'/g, ""); @@ -176,19 +178,13 @@ export async function signInWithUsername( onError: (error: Error) => void ) { try { - const isUsernameValid = await validateUsernameDb(username); - if (!isUsernameValid) - throw new TypeError("Could not find user with this username"); - const db = getFirestore(); - const usernameQuery = query( - collection(db, "users"), - where("username", "==", username) - ); - const querySnap = await getDocs(usernameQuery); - const userData = querySnap.docs[0]; - const userEmail = userData.data().email; - - await signInWithEmail(userEmail, password, onError); + const clientAuth = getAuth(); + const res = await axios.post("/api/login/token", { + username, + password, + }); + const token = res.data.customToken; + await signInWithCustomToken(clientAuth, token); } catch (error) { onError(error as Error); } diff --git a/services/firebase/firestore.ts b/services/firebase/firestore.ts index b7c1bcaa..d9f12dbd 100644 --- a/services/firebase/firestore.ts +++ b/services/firebase/firestore.ts @@ -86,7 +86,6 @@ export async function getFirestorePrivateUserData(uid: string) { export async function validateUsernameDb( username: string | undefined ): Promise { - if (!username) return undefined; const db = getFirestore(); const q = query(collection(db, "users"), where("username", "==", username)); const querySnap = await getDocs(q); diff --git a/yarn.lock b/yarn.lock index e1a907b8..4ce63551 100644 --- a/yarn.lock +++ b/yarn.lock @@ -287,6 +287,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-string-parser@npm:7.25.9" + checksum: 6435ee0849e101681c1849868278b5aee82686ba2c1e27280e5e8aca6233af6810d39f8e4e693d2f2a44a3728a6ccfd66f72d71826a94105b86b731697cdfa99 + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.22.20": version: 7.22.20 resolution: "@babel/helper-validator-identifier@npm:7.22.20" @@ -294,6 +301,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.25.9": + version: 7.25.9 + resolution: "@babel/helper-validator-identifier@npm:7.25.9" + checksum: 5b85918cb1a92a7f3f508ea02699e8d2422fe17ea8e82acd445006c0ef7520fbf48e3dbcdaf7b0a1d571fc3a2715a29719e5226636cb6042e15fe6ed2a590944 + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.22.15, @babel/helper-validator-option@npm:^7.23.5": version: 7.23.5 resolution: "@babel/helper-validator-option@npm:7.23.5" @@ -334,7 +348,18 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.20.15, @babel/parser@npm:^7.23.9": +"@babel/parser@npm:^7.20.15": + version: 7.26.3 + resolution: "@babel/parser@npm:7.26.3" + dependencies: + "@babel/types": ^7.26.3 + bin: + parser: ./bin/babel-parser.js + checksum: e2bff2e9fa6540ee18fecc058bc74837eda2ddcecbe13454667314a93fc0ba26c1fb862c812d84f6d5f225c3bd8d191c3a42d4296e287a882c4e1f82ff2815ff + languageName: node + linkType: hard + +"@babel/parser@npm:^7.23.9": version: 7.23.9 resolution: "@babel/parser@npm:7.23.9" bin: @@ -1453,6 +1478,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.26.3": + version: 7.26.3 + resolution: "@babel/types@npm:7.26.3" + dependencies: + "@babel/helper-string-parser": ^7.25.9 + "@babel/helper-validator-identifier": ^7.25.9 + checksum: 195f428080dcaadbcecc9445df7f91063beeaa91b49ccd78f38a5af6b75a6a58391d0c6614edb1ea322e57889a1684a0aab8e667951f820196901dd341f931e9 + languageName: node + linkType: hard + "@commitlint/config-validator@npm:^19.5.0": version: 19.5.0 resolution: "@commitlint/config-validator@npm:19.5.0" @@ -2372,12 +2407,12 @@ __metadata: linkType: hard "@grpc/grpc-js@npm:~1.8.0": - version: 1.8.21 - resolution: "@grpc/grpc-js@npm:1.8.21" + version: 1.8.22 + resolution: "@grpc/grpc-js@npm:1.8.22" dependencies: "@grpc/proto-loader": ^0.7.0 "@types/node": ">=12.12.47" - checksum: 32bbb3667c20005987eaef0268898fcb49b7bf46e8f338f3ad6f3343e5ff125d63da9aa869b6bca2a918adacf39715d29431461f233c677012206faedbd71169 + checksum: 4e7be493f568ce7f6d196b28d1177cab2714261c2df61a5900b5cc93e2f61362c780e57d0dae556972375006b72d39a9e0860d5c78bbe5e354a0bddf0d3da121 languageName: node linkType: hard @@ -2492,11 +2527,11 @@ __metadata: linkType: hard "@jsdoc/salty@npm:^0.2.1": - version: 0.2.7 - resolution: "@jsdoc/salty@npm:0.2.7" + version: 0.2.9 + resolution: "@jsdoc/salty@npm:0.2.9" dependencies: lodash: ^4.17.21 - checksum: 020bc5a7f7270c281b854c73ca989c3a8947f0a520cd5142d3d0532ecc54cff05efef56ec2b04ee7628f605776d054033aa7948cd605963c406fe4c6cd4285df + checksum: 79969d40b4226f1a820e54cd8f96b2b0e75340c38fe494ac0be302062bae71e27ffce928347c59fdbf9783602e2349bca573389cd6481cf4bd2f22fc01264a52 languageName: node linkType: hard @@ -3432,10 +3467,10 @@ __metadata: languageName: node linkType: hard -"@types/linkify-it@npm:*": - version: 3.0.5 - resolution: "@types/linkify-it@npm:3.0.5" - checksum: fac28f41a6e576282300a459d70ea0d33aab70dbb77c3d09582bb0335bb00d862b6de69585792a4d590aae4173fbab0bf28861e2d90ca7b2b1439b52688e9ff6 +"@types/linkify-it@npm:^5": + version: 5.0.0 + resolution: "@types/linkify-it@npm:5.0.0" + checksum: ec98e03aa883f70153a17a1e6ed9e28b39a604049b485daeddae3a1482ec65cac0817520be6e301d99fd1a934b3950cf0f855655aae6ec27da2bb676ba4a148e languageName: node linkType: hard @@ -3453,13 +3488,13 @@ __metadata: languageName: node linkType: hard -"@types/markdown-it@npm:^12.2.3": - version: 12.2.3 - resolution: "@types/markdown-it@npm:12.2.3" +"@types/markdown-it@npm:^14.1.1": + version: 14.1.2 + resolution: "@types/markdown-it@npm:14.1.2" dependencies: - "@types/linkify-it": "*" - "@types/mdurl": "*" - checksum: 868824a3e4d00718ba9cd4762cf16694762a670860f4b402e6e9f952b6841a2027488bdc55d05c2b960bf5078df21a9d041270af7e8949514645fe88fdb722ac + "@types/linkify-it": ^5 + "@types/mdurl": ^2 + checksum: ad66e0b377d6af09a155bb65f675d1e2cb27d20a3d407377fe4508eb29cde1e765430b99d5129f89012e2524abb5525d629f7057a59ff9fd0967e1ff645b9ec6 languageName: node linkType: hard @@ -3470,10 +3505,10 @@ __metadata: languageName: node linkType: hard -"@types/mdurl@npm:*": - version: 1.0.5 - resolution: "@types/mdurl@npm:1.0.5" - checksum: e8e872e8da8f517a9c748b06cec61c947cb73fd3069e8aeb0926670ec5dfac5d30549b3d0f1634950401633e812f9b7263f2d5dbe7e98fce12bcb2c659aa4b21 +"@types/mdurl@npm:^2": + version: 2.0.0 + resolution: "@types/mdurl@npm:2.0.0" + checksum: 78746e96c655ceed63db06382da466fd52c7e9dc54d60b12973dfdd110cae06b9439c4b90e17bb8d4461109184b3ea9f3e9f96b3e4bf4aa9fe18b6ac35f283c8 languageName: node linkType: hard @@ -4150,6 +4185,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:^1.7.9": + version: 1.7.9 + resolution: "axios@npm:1.7.9" + dependencies: + follow-redirects: ^1.15.6 + form-data: ^4.0.0 + proxy-from-env: ^1.1.0 + checksum: cb8ce291818effda09240cb60f114d5625909b345e10f389a945320e06acf0bc949d0f8422d25720f5dd421362abee302c99f5e97edec4c156c8939814b23d19 + languageName: node + linkType: hard + "axobject-query@npm:^3.2.1": version: 3.2.1 resolution: "axobject-query@npm:3.2.1" @@ -4384,6 +4430,16 @@ __metadata: languageName: node linkType: hard +"call-bind-apply-helpers@npm:^1.0.1": + version: 1.0.1 + resolution: "call-bind-apply-helpers@npm:1.0.1" + dependencies: + es-errors: ^1.3.0 + function-bind: ^1.1.2 + checksum: 3c55343261bb387c58a4762d15ad9d42053659a62681ec5eb50690c6b52a4a666302a01d557133ce6533e8bd04530ee3b209f23dd06c9577a1925556f8fcccdf + languageName: node + linkType: hard + "call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.6, call-bind@npm:^1.0.7": version: 1.0.7 resolution: "call-bind@npm:1.0.7" @@ -4397,6 +4453,16 @@ __metadata: languageName: node linkType: hard +"call-bound@npm:^1.0.2, call-bound@npm:^1.0.3": + version: 1.0.3 + resolution: "call-bound@npm:1.0.3" + dependencies: + call-bind-apply-helpers: ^1.0.1 + get-intrinsic: ^1.2.6 + checksum: a93bbe0f2d0a2d6c144a4349ccd0593d5d0d5d9309b69101710644af8964286420062f2cc3114dca120b9bc8cc07507952d4b1b3ea7672e0d7f6f1675efedb32 + languageName: node + linkType: hard + "callsites@npm:^3.0.0": version: 3.1.0 resolution: "callsites@npm:3.1.0" @@ -5155,6 +5221,17 @@ __metadata: languageName: node linkType: hard +"dunder-proto@npm:^1.0.0": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: ^1.0.1 + es-errors: ^1.3.0 + gopd: ^1.2.0 + checksum: 149207e36f07bd4941921b0ca929e3a28f1da7bd6b6ff8ff7f4e2f2e460675af4576eeba359c635723dc189b64cdd4787e0255897d5b135ccc5d15cb8685fc90 + languageName: node + linkType: hard + "duplexer2@npm:^0.1.2": version: 0.1.4 resolution: "duplexer2@npm:0.1.4" @@ -5283,9 +5360,14 @@ __metadata: linkType: hard "ent@npm:^2.2.0": - version: 2.2.0 - resolution: "ent@npm:2.2.0" - checksum: f588b5707d6fef36011ea10d530645912a69530a1eb0831f8708c498ac028363a7009f45cfadd28ceb4dafd9ac17ec15213f88d09ce239cd033cfe1328dd7d7d + version: 2.2.2 + resolution: "ent@npm:2.2.2" + dependencies: + call-bound: ^1.0.3 + es-errors: ^1.3.0 + punycode: ^1.4.1 + safe-regex-test: ^1.1.0 + checksum: f356c7894c0a2f02b9c1a81dcca17dd87a9000c015e754b9a00fa42bc5d554f231f481bde035a605a7a95150aacac9c545de75842a8cba67c9777b0d07d9319b languageName: node linkType: hard @@ -5310,13 +5392,6 @@ __metadata: languageName: node linkType: hard -"entities@npm:~2.1.0": - version: 2.1.0 - resolution: "entities@npm:2.1.0" - checksum: a10a877e489586a3f6a691fe49bf3fc4e58f06c8e80522f08214a5150ba457e7017b447d4913a3fa041bda06ee4c92517baa4d8d75373eaa79369e9639225ffd - languageName: node - linkType: hard - "env-paths@npm:^2.2.0, env-paths@npm:^2.2.1": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -5405,6 +5480,13 @@ __metadata: languageName: node linkType: hard +"es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 0512f4e5d564021c9e3a644437b0155af2679d10d80f21adaf868e64d30efdfbd321631956f20f42d655fedb2e3a027da479fad3fa6048f768eb453a80a5f80a + languageName: node + linkType: hard + "es-errors@npm:^1.0.0, es-errors@npm:^1.1.0, es-errors@npm:^1.2.1, es-errors@npm:^1.3.0": version: 1.3.0 resolution: "es-errors@npm:1.3.0" @@ -5435,6 +5517,15 @@ __metadata: languageName: node linkType: hard +"es-object-atoms@npm:^1.0.0": + version: 1.0.0 + resolution: "es-object-atoms@npm:1.0.0" + dependencies: + es-errors: ^1.3.0 + checksum: 26f0ff78ab93b63394e8403c353842b2272836968de4eafe97656adfb8a7c84b9099bf0fe96ed58f4a4cddc860f6e34c77f91649a58a5daa4a9c40b902744e3c + languageName: node + linkType: hard + "es-set-tostringtag@npm:^2.0.2": version: 2.0.2 resolution: "es-set-tostringtag@npm:2.0.2" @@ -5967,13 +6058,13 @@ __metadata: linkType: hard "fast-xml-parser@npm:^4.2.2": - version: 4.3.4 - resolution: "fast-xml-parser@npm:4.3.4" + version: 4.5.1 + resolution: "fast-xml-parser@npm:4.5.1" dependencies: strnum: ^1.0.5 bin: fxparser: src/cli/cli.js - checksum: ab88177343f6d3d971d53462db3011003a83eb8a8db704840127ddaaf27105ea90cdf7903a0f9b2e1279ccc4adfca8dfc0277b33bae6262406f10c16bd60ccf9 + checksum: aab32d7f08a95b20f9ecdc2d769531a9dc454faf12740873972f8169c04ab9335ac5df1029ebfe829a01ddbb0ec60572cb7769d6be2409e95a9be8fc6a86e92c languageName: node linkType: hard @@ -6164,6 +6255,16 @@ __metadata: languageName: node linkType: hard +"follow-redirects@npm:^1.15.6": + version: 1.15.9 + resolution: "follow-redirects@npm:1.15.9" + peerDependenciesMeta: + debug: + optional: true + checksum: 859e2bacc7a54506f2bf9aacb10d165df78c8c1b0ceb8023f966621b233717dab56e8d08baadc3ad3b9db58af290413d585c999694b7c146aaf2616340c3d2a6 + languageName: node + linkType: hard + "for-each@npm:^0.3.3": version: 0.3.3 resolution: "for-each@npm:0.3.3" @@ -6339,6 +6440,24 @@ __metadata: languageName: node linkType: hard +"get-intrinsic@npm:^1.2.6": + version: 1.2.6 + resolution: "get-intrinsic@npm:1.2.6" + dependencies: + call-bind-apply-helpers: ^1.0.1 + dunder-proto: ^1.0.0 + es-define-property: ^1.0.1 + es-errors: ^1.3.0 + es-object-atoms: ^1.0.0 + function-bind: ^1.1.2 + gopd: ^1.2.0 + has-symbols: ^1.1.0 + hasown: ^2.0.2 + math-intrinsics: ^1.0.0 + checksum: a7592a0b7f023a2e83c0121fa9449ca83780e370a5feeebe8452119474d148016e43b455049134ae7a683b9b11b93d3f65eac199a0ad452ab740d5f0c299de47 + languageName: node + linkType: hard + "get-symbol-description@npm:^1.0.2": version: 1.0.2 resolution: "get-symbol-description@npm:1.0.2" @@ -6575,6 +6694,13 @@ __metadata: languageName: node linkType: hard +"gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: cc6d8e655e360955bdccaca51a12a474268f95bb793fc3e1f2bdadb075f28bfd1fd988dab872daf77a61d78cbaf13744bc8727a17cfb1d150d76047d805375f3 + languageName: node + linkType: hard + "graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" @@ -6653,7 +6779,14 @@ __metadata: languageName: node linkType: hard -"has-tostringtag@npm:^1.0.0, has-tostringtag@npm:^1.0.1": +"has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: b2316c7302a0e8ba3aaba215f834e96c22c86f192e7310bdf689dd0e6999510c89b00fbc5742571507cebf25764d68c988b3a0da217369a73596191ac0ce694b + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.0, has-tostringtag@npm:^1.0.1, has-tostringtag@npm:^1.0.2": version: 1.0.2 resolution: "has-tostringtag@npm:1.0.2" dependencies: @@ -6671,6 +6804,15 @@ __metadata: languageName: node linkType: hard +"hasown@npm:^2.0.2": + version: 2.0.2 + resolution: "hasown@npm:2.0.2" + dependencies: + function-bind: ^1.1.2 + checksum: e8516f776a15149ca6c6ed2ae3110c417a00b62260e222590e54aa367cbcd6ed99122020b37b7fbdf05748df57b265e70095d7bf35a47660587619b15ffb93db + languageName: node + linkType: hard + "hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.1, hoist-non-react-statics@npm:^3.3.2": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" @@ -7182,6 +7324,18 @@ __metadata: languageName: node linkType: hard +"is-regex@npm:^1.2.1": + version: 1.2.1 + resolution: "is-regex@npm:1.2.1" + dependencies: + call-bound: ^1.0.2 + gopd: ^1.2.0 + has-tostringtag: ^1.0.2 + hasown: ^2.0.2 + checksum: 99ee0b6d30ef1bb61fa4b22fae7056c6c9b3c693803c0c284ff7a8570f83075a7d38cda53b06b7996d441215c27895ea5d1af62124562e13d91b3dbec41a5e13 + languageName: node + linkType: hard + "is-set@npm:^2.0.1": version: 2.0.2 resolution: "is-set@npm:2.0.2" @@ -7416,27 +7570,27 @@ __metadata: linkType: hard "jsdoc@npm:^4.0.0": - version: 4.0.2 - resolution: "jsdoc@npm:4.0.2" + version: 4.0.4 + resolution: "jsdoc@npm:4.0.4" dependencies: "@babel/parser": ^7.20.15 "@jsdoc/salty": ^0.2.1 - "@types/markdown-it": ^12.2.3 + "@types/markdown-it": ^14.1.1 bluebird: ^3.7.2 catharsis: ^0.9.0 escape-string-regexp: ^2.0.0 js2xmlparser: ^4.0.2 klaw: ^3.0.0 - markdown-it: ^12.3.2 - markdown-it-anchor: ^8.4.1 + markdown-it: ^14.1.0 + markdown-it-anchor: ^8.6.7 marked: ^4.0.10 mkdirp: ^1.0.4 requizzle: ^0.2.3 strip-json-comments: ^3.1.0 underscore: ~1.13.2 bin: - jsdoc: jsdoc.js - checksum: 04bf5ab005349b7581bd0e72ed99933eb71a41dcb47235b486b7d9146fbdf212a53e0cc044abe48ccf46012bd812dc1dfc007c6d679660ebdd053cd000242515 + jsdoc: ./jsdoc.js + checksum: f4372a15a262ffd5abfe71315bbf9ad0fd3dd633ca04298702c0b0d3bacd615a35e9f11877bd7aa4e1bb04adb731a55fb15c3e14e69a8e740e86c45548ad39b6 languageName: node linkType: hard @@ -7744,12 +7898,12 @@ __metadata: languageName: node linkType: hard -"linkify-it@npm:^3.0.1": - version: 3.0.3 - resolution: "linkify-it@npm:3.0.3" +"linkify-it@npm:^5.0.0": + version: 5.0.0 + resolution: "linkify-it@npm:5.0.0" dependencies: - uc.micro: ^1.0.1 - checksum: 31367a4bb70c5bbc9703246236b504b0a8e049bcd4e0de4291fa50f0ebdebf235b5eb54db6493cb0b1319357c6eeafc4324c9f4aa34b0b943d9f2e11a1268fbc + uc.micro: ^2.0.0 + checksum: b0b86cadaf816b64c947a83994ceaad1c15f9fe7e079776ab88699fb71afd7b8fc3fd3d0ae5ebec8c92c1d347be9ba257b8aef338c0ebf81b0d27dcf429a765a languageName: node linkType: hard @@ -8026,6 +8180,7 @@ __metadata: "@types/react-dom": ^18.0.9 "@types/seedrandom": ^3.0.5 "@vercel/analytics": ^0.1.6 + axios: ^1.7.9 cookies-next: ^2.1.1 country-flag-icons: ^1.5.5 cz-conventional-changelog: ^3.3.0 @@ -8083,7 +8238,7 @@ __metadata: languageName: unknown linkType: soft -"markdown-it-anchor@npm:^8.4.1": +"markdown-it-anchor@npm:^8.6.7": version: 8.6.7 resolution: "markdown-it-anchor@npm:8.6.7" peerDependencies: @@ -8093,18 +8248,19 @@ __metadata: languageName: node linkType: hard -"markdown-it@npm:^12.3.2": - version: 12.3.2 - resolution: "markdown-it@npm:12.3.2" +"markdown-it@npm:^14.1.0": + version: 14.1.0 + resolution: "markdown-it@npm:14.1.0" dependencies: argparse: ^2.0.1 - entities: ~2.1.0 - linkify-it: ^3.0.1 - mdurl: ^1.0.1 - uc.micro: ^1.0.5 + entities: ^4.4.0 + linkify-it: ^5.0.0 + mdurl: ^2.0.0 + punycode.js: ^2.3.1 + uc.micro: ^2.1.0 bin: - markdown-it: bin/markdown-it.js - checksum: 890555711c1c00fa03b936ca2b213001a3b9b37dea140d8445ae4130ce16628392aad24b12e2a0a9935336ca5951f2957a38f4e5309a2e38eab44e25ff32a41e + markdown-it: bin/markdown-it.mjs + checksum: 07296b45ebd0b13a55611a24d1b1ad002c6729ec54f558f597846994b0b7b1de79d13cd99ff3e7b6e9e027f36b63125cdcf69174da294ecabdd4e6b9fff39e5d languageName: node linkType: hard @@ -8129,6 +8285,13 @@ __metadata: languageName: node linkType: hard +"math-intrinsics@npm:^1.0.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 0e513b29d120f478c85a70f49da0b8b19bc638975eca466f2eeae0071f3ad00454c621bf66e16dd435896c208e719fc91ad79bbfba4e400fe0b372e7c1c9c9a2 + languageName: node + linkType: hard + "mdn-data@npm:2.0.14": version: 2.0.14 resolution: "mdn-data@npm:2.0.14" @@ -8136,10 +8299,10 @@ __metadata: languageName: node linkType: hard -"mdurl@npm:^1.0.1": - version: 1.0.1 - resolution: "mdurl@npm:1.0.1" - checksum: 71731ecba943926bfbf9f9b51e28b5945f9411c4eda80894221b47cc105afa43ba2da820732b436f0798fd3edbbffcd1fc1415843c41a87fea08a41cc1e3d02b +"mdurl@npm:^2.0.0": + version: 2.0.0 + resolution: "mdurl@npm:2.0.0" + checksum: 880bc289ef668df0bb34c5b2b5aaa7b6ea755052108cdaf4a5e5968ad01cf27e74927334acc9ebcc50a8628b65272ae6b1fd51fae1330c130e261c0466e1a3b2 languageName: node linkType: hard @@ -8193,13 +8356,20 @@ __metadata: languageName: node linkType: hard -"mime-db@npm:1.52.0, mime-db@npm:>= 1.43.0 < 2": +"mime-db@npm:1.52.0": version: 1.52.0 resolution: "mime-db@npm:1.52.0" checksum: 0d99a03585f8b39d68182803b12ac601d9c01abfa28ec56204fa330bc9f3d1c5e14beb049bafadb3dbdf646dfb94b87e24d4ec7b31b7279ef906a8ea9b6a513f languageName: node linkType: hard +"mime-db@npm:>= 1.43.0 < 2": + version: 1.53.0 + resolution: "mime-db@npm:1.53.0" + checksum: 3fd9380bdc0b085d0b56b580e4f89ca4fc3b823722310d795c248f0806b9a80afd5d8f4347f015ad943b9ecfa7cc0b71dffa0db96fa776d01a13474821a2c7fb + languageName: node + linkType: hard + "mime-types@npm:^2.0.8, mime-types@npm:^2.1.12": version: 2.1.35 resolution: "mime-types@npm:2.1.35" @@ -9113,7 +9283,27 @@ __metadata: languageName: node linkType: hard -"protobufjs@npm:^7.0.0, protobufjs@npm:^7.2.4, protobufjs@npm:^7.2.5": +"protobufjs@npm:^7.0.0": + version: 7.4.0 + resolution: "protobufjs@npm:7.4.0" + dependencies: + "@protobufjs/aspromise": ^1.1.2 + "@protobufjs/base64": ^1.1.2 + "@protobufjs/codegen": ^2.0.4 + "@protobufjs/eventemitter": ^1.1.0 + "@protobufjs/fetch": ^1.1.0 + "@protobufjs/float": ^1.0.2 + "@protobufjs/inquire": ^1.1.0 + "@protobufjs/path": ^1.1.2 + "@protobufjs/pool": ^1.1.0 + "@protobufjs/utf8": ^1.1.0 + "@types/node": ">=13.7.0" + long: ^5.0.0 + checksum: ba0e6b60541bbf818bb148e90f5eb68bd99004e29a6034ad9895a381cbd352be8dce5376e47ae21b2e05559f2505b4a5f4a3c8fa62402822c6ab4dcdfb89ffb3 + languageName: node + linkType: hard + +"protobufjs@npm:^7.2.4, protobufjs@npm:^7.2.5": version: 7.2.6 resolution: "protobufjs@npm:7.2.6" dependencies: @@ -9133,6 +9323,13 @@ __metadata: languageName: node linkType: hard +"proxy-from-env@npm:^1.1.0": + version: 1.1.0 + resolution: "proxy-from-env@npm:1.1.0" + checksum: ed7fcc2ba0a33404958e34d95d18638249a68c430e30fcb6c478497d72739ba64ce9810a24f53a7d921d0c065e5b78e3822759800698167256b04659366ca4d4 + languageName: node + linkType: hard + "pseudomap@npm:^1.0.1": version: 1.0.2 resolution: "pseudomap@npm:1.0.2" @@ -9147,6 +9344,20 @@ __metadata: languageName: node linkType: hard +"punycode.js@npm:^2.3.1": + version: 2.3.1 + resolution: "punycode.js@npm:2.3.1" + checksum: 13466d7ed5e8dacdab8c4cc03837e7dd14218a59a40eb14a837f1f53ca396e18ef2c4ee6d7766b8ed2fc391d6a3ac489eebf2de83b3596f5a54e86df4a251b72 + languageName: node + linkType: hard + +"punycode@npm:^1.4.1": + version: 1.4.1 + resolution: "punycode@npm:1.4.1" + checksum: fa6e698cb53db45e4628559e557ddaf554103d2a96a1d62892c8f4032cd3bc8871796cae9eabc1bc700e2b6677611521ce5bb1d9a27700086039965d0cf34518 + languageName: node + linkType: hard + "punycode@npm:^2.1.0, punycode@npm:^2.1.1": version: 2.3.1 resolution: "punycode@npm:2.3.1" @@ -9784,7 +9995,7 @@ __metadata: languageName: node linkType: hard -"rimraf@npm:^3.0.0, rimraf@npm:^3.0.2": +"rimraf@npm:^3.0.2": version: 3.0.2 resolution: "rimraf@npm:3.0.2" dependencies: @@ -9857,6 +10068,17 @@ __metadata: languageName: node linkType: hard +"safe-regex-test@npm:^1.1.0": + version: 1.1.0 + resolution: "safe-regex-test@npm:1.1.0" + dependencies: + call-bound: ^1.0.2 + es-errors: ^1.3.0 + is-regex: ^1.2.1 + checksum: 3c809abeb81977c9ed6c869c83aca6873ea0f3ab0f806b8edbba5582d51713f8a6e9757d24d2b4b088f563801475ea946c8e77e7713e8c65cdd02305b6caedab + languageName: node + linkType: hard + "safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -9946,7 +10168,16 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.1.2, semver@npm:^7.3.5, semver@npm:^7.5.4": +"semver@npm:^7.1.2": + version: 7.6.3 + resolution: "semver@npm:7.6.3" + bin: + semver: bin/semver.js + checksum: 4110ec5d015c9438f322257b1c51fe30276e5f766a3f64c09edd1d7ea7118ecbc3f379f3b69032bacf13116dc7abc4ad8ce0d7e2bd642e26b0d271b56b61a7d8 + languageName: node + linkType: hard + +"semver@npm:^7.3.5, semver@npm:^7.5.4": version: 7.6.0 resolution: "semver@npm:7.6.0" dependencies: @@ -10507,11 +10738,9 @@ __metadata: linkType: hard "tmp@npm:^0.2.1": - version: 0.2.1 - resolution: "tmp@npm:0.2.1" - dependencies: - rimraf: ^3.0.0 - checksum: 8b1214654182575124498c87ca986ac53dc76ff36e8f0e0b67139a8d221eaecfdec108c0e6ec54d76f49f1f72ab9325500b246f562b926f85bcdfca8bf35df9e + version: 0.2.3 + resolution: "tmp@npm:0.2.3" + checksum: 73b5c96b6e52da7e104d9d44afb5d106bb1e16d9fa7d00dbeb9e6522e61b571fbdb165c756c62164be9a3bbe192b9b268c236d370a2a0955c7689cd2ae377b95 languageName: node linkType: hard @@ -10721,10 +10950,10 @@ __metadata: languageName: node linkType: hard -"uc.micro@npm:^1.0.1, uc.micro@npm:^1.0.5": - version: 1.0.6 - resolution: "uc.micro@npm:1.0.6" - checksum: 6898bb556319a38e9cf175e3628689347bd26fec15fc6b29fa38e0045af63075ff3fea4cf1fdba9db46c9f0cbf07f2348cd8844889dd31ebd288c29fe0d27e7a +"uc.micro@npm:^2.0.0, uc.micro@npm:^2.1.0": + version: 2.1.0 + resolution: "uc.micro@npm:2.1.0" + checksum: 37197358242eb9afe367502d4638ac8c5838b78792ab218eafe48287b0ed28aaca268ec0392cc5729f6c90266744de32c06ae938549aee041fc93b0f9672d6b2 languageName: node linkType: hard @@ -10736,11 +10965,11 @@ __metadata: linkType: hard "uglify-js@npm:^3.7.7": - version: 3.17.4 - resolution: "uglify-js@npm:3.17.4" + version: 3.19.3 + resolution: "uglify-js@npm:3.19.3" bin: uglifyjs: bin/uglifyjs - checksum: 7b3897df38b6fc7d7d9f4dcd658599d81aa2b1fb0d074829dd4e5290f7318dbca1f4af2f45acb833b95b1fe0ed4698662ab61b87e94328eb4c0a0d3435baf924 + checksum: 7ed6272fba562eb6a3149cfd13cda662f115847865c03099e3995a0e7a910eba37b82d4fccf9e88271bb2bcbe505bb374967450f433c17fa27aa36d94a8d0553 languageName: node linkType: hard @@ -10757,9 +10986,9 @@ __metadata: linkType: hard "underscore@npm:~1.13.2": - version: 1.13.6 - resolution: "underscore@npm:1.13.6" - checksum: d5cedd14a9d0d91dd38c1ce6169e4455bb931f0aaf354108e47bd46d3f2da7464d49b2171a5cf786d61963204a42d01ea1332a903b7342ad428deaafaf70ec36 + version: 1.13.7 + resolution: "underscore@npm:1.13.7" + checksum: 174b011af29e4fbe2c70eb2baa8bfab0d0336cf2f5654f364484967bc6264a86224d0134b9176e4235c8cceae00d11839f0fd4824268de04b11c78aca1241684 languageName: node linkType: hard From dec256d89ad57f8473b0bbf45045397e7344c9a1 Mon Sep 17 00:00:00 2001 From: hellogirls-js Date: Thu, 26 Dec 2024 21:49:22 -0600 Subject: [PATCH 3/6] feat(calendar): add month dropdown --- pages/calendar/components/CalendarHeader.tsx | 52 ++++++++++++++++++-- pages/calendar/index.page.tsx | 12 ++--- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/pages/calendar/components/CalendarHeader.tsx b/pages/calendar/components/CalendarHeader.tsx index 00028467..0fe42a38 100644 --- a/pages/calendar/components/CalendarHeader.tsx +++ b/pages/calendar/components/CalendarHeader.tsx @@ -1,16 +1,26 @@ import { + ActionIcon, Box, Button, createStyles, + Group, MediaQuery, + Menu, + NativeSelect, Stack, Title, } from "@mantine/core"; -import { IconArrowLeft, IconArrowRight } from "@tabler/icons-react"; +import { Calendar } from "@mantine/dates"; +import { + IconArrowLeft, + IconArrowRight, + IconSelector, +} from "@tabler/icons-react"; import useTranslation from "next-translate/useTranslation"; -import { ReactElement, useCallback } from "react"; +import { ReactElement, useCallback, useState } from "react"; import { useDayjs } from "services/libraries/dayjs"; +import { Birthday, Event, Scout } from "types/game"; const useStyles = createStyles((theme, _params, getRef) => ({ header: { @@ -30,10 +40,12 @@ const useStyles = createStyles((theme, _params, getRef) => ({ function CalendarHeader({ calendarTime, setCalendarTime, + events, children, }: { calendarTime: string; setCalendarTime: (a: string) => void; + events: Array; children?: ReactElement; }) { const { t } = useTranslation("calendar"); @@ -47,6 +59,15 @@ function CalendarHeader({ [setCalendarTime, calendarTime, dayjs] ); + const sortedEvents = events + .filter((event) => !(event as Birthday).character_id) + .sort((a, b) => dayjs(a.end.en).diff(dayjs(b.end.en))); + + const earliestEvent = sortedEvents[0]; + const latestEvent = sortedEvents[sortedEvents.length - 1]; + + const [openMonths, setOpenMonths] = useState(false); + return ( + + {isEmailValid && ( + + + An account associated with the given email has been found! + Click the button below to send a password reset link to the + provided email. + + + + )} + {showEmailFromUsername && ( + + + The following email address has been found for this username: + {showEmailFromUsername}Click the + button below to send a password reset link to this email + address. + + + + )} + {isEmailValid === false && ( + + } + title="Error" + color="red" + > + An account with this email address could not be found. Please + try another email address or{" "} + + register a new account + {" "} + with the given address. + + + )} + {showEmailFromUsername === false && ( + + An account with that username doesn't exist. Please use a known + email address or{" "} + + register a new account + + . + + )} + {(isEmailValid || showEmailFromUsername) && ( + { + setCaptchaComplete(value); + }} + /> + )} diff --git a/yarn.lock b/yarn.lock index 4ce63551..a5b38a2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3631,6 +3631,15 @@ __metadata: languageName: node linkType: hard +"@types/react-google-recaptcha@npm:^2.1.9": + version: 2.1.9 + resolution: "@types/react-google-recaptcha@npm:2.1.9" + dependencies: + "@types/react": "*" + checksum: 5a90bb50fe0a49f2e2ceb7950859cfdbe019aa0a75261451fb50ad83dcd4aa360a1941ab6c9b3c627f2b7a0dab2c6c09d6297469d3d4d02ebaf36b3fc34429b6 + languageName: node + linkType: hard + "@types/react-redux@npm:^7.1.20": version: 7.1.33 resolution: "@types/react-redux@npm:7.1.33" @@ -8178,6 +8187,7 @@ __metadata: "@types/react-beautiful-dnd": ^13.1.3 "@types/react-collapse": ^5.0.1 "@types/react-dom": ^18.0.9 + "@types/react-google-recaptcha": ^2.1.9 "@types/seedrandom": ^3.0.5 "@vercel/analytics": ^0.1.6 axios: ^1.7.9 @@ -8223,6 +8233,7 @@ __metadata: react-firebase-hooks: ^5.1.1 react-frame-component: ^5.2.3 react-gesture-responder: ^2.1.0 + react-google-recaptcha: ^3.1.0 react-grid-drag: ^1.0.0 react-image-crop: ^10.0.9 react-infinite-scroll-component: ^6.1.0 @@ -9196,7 +9207,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.5.8, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": +"prop-types@npm:^15.5.0, prop-types@npm:^15.5.8, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -9441,6 +9452,18 @@ __metadata: languageName: node linkType: hard +"react-async-script@npm:^1.2.0": + version: 1.2.0 + resolution: "react-async-script@npm:1.2.0" + dependencies: + hoist-non-react-statics: ^3.3.0 + prop-types: ^15.5.0 + peerDependencies: + react: ">=16.4.1" + checksum: 303890eeaf9e18d59fca77f9c891bf3b52d2ec9ea88f0af9d19c160a1f101b447c5104ca46e2dd84c19de756d4797f1f054d041b888a3d57204d9145f4b1b532 + languageName: node + linkType: hard + "react-beautiful-dnd@npm:^13.1.1": version: 13.1.1 resolution: "react-beautiful-dnd@npm:13.1.1" @@ -9539,6 +9562,18 @@ __metadata: languageName: node linkType: hard +"react-google-recaptcha@npm:^3.1.0": + version: 3.1.0 + resolution: "react-google-recaptcha@npm:3.1.0" + dependencies: + prop-types: ^15.5.0 + react-async-script: ^1.2.0 + peerDependencies: + react: ">=16.4.1" + checksum: 9dc64daf9684d979b1f66d97e00a42c9ceaa9b9fe8b29c4d02d77edf86781e2008f2ae1bef14509351ff3b2ffbcc26463f0d88dd612d7280b455b8c07101e663 + languageName: node + linkType: hard + "react-grid-drag@npm:^1.0.0": version: 1.0.0 resolution: "react-grid-drag@npm:1.0.0" From ce4e1b2bcc5c6b0a0885b711843150a3ae0da41b Mon Sep 17 00:00:00 2001 From: hellogirls-js Date: Tue, 31 Dec 2024 10:21:58 -0600 Subject: [PATCH 5/6] feat(login): add recaptcha for forgotten password --- .env.development | 4 +++- pages/login/reset_password.page.tsx | 17 +++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.env.development b/.env.development index d82fae7b..3774c194 100644 --- a/.env.development +++ b/.env.development @@ -5,5 +5,7 @@ NEXT_PUBLIC_FIREBASE_PROJECT_ID=ensemble-square-dev NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=ensemble-square-dev.appspot.com NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID="135380920875" NEXT_PUBLIC_FIREBASE_APP_ID="1:135380920875:web:300170c3c9a3f100ce186e" +NEXT_PUBLIC_CAPTCHA_SITE_KEY=6LePMaoqAAAAALq5rY-tApn0j_7CJL0HDjMMuqMC +CAPTCHA_SECRET_KEY=6LePMaoqAAAAALD9YTugizQkfu0eoFXf5ynd1hhi COOKIE_SECRET_CURRENT=50cee4fa6b5406f5636d605659c44b79c2c6c7d452f20292ae4d30b6f0e30b96 -COOKIE_SECRET_PREVIOUS=61666d418e8407274bc29a6c01a1dc13e083d71f60774e892a14b5bbc0635e5c \ No newline at end of file +COOKIE_SECRET_PREVIOUS=61666d418e8407274bc29a6c01a1dc13e083d71f60774e892a14b5bbc0635e5c diff --git a/pages/login/reset_password.page.tsx b/pages/login/reset_password.page.tsx index d89a149b..d0c2fd07 100644 --- a/pages/login/reset_password.page.tsx +++ b/pages/login/reset_password.page.tsx @@ -195,14 +195,15 @@ function Page() { . )} - {(isEmailValid || showEmailFromUsername) && ( - { - setCaptchaComplete(value); - }} - /> - )} + {(isEmailValid || showEmailFromUsername) && + process.env.NEXT_PUBLIC_CAPTCHA_SITE_KEY && ( + { + setCaptchaComplete(value); + }} + /> + )} From 6c42b77374e620df7d70006ade134d3bfc8ac5ed Mon Sep 17 00:00:00 2001 From: hellogirls-js Date: Fri, 3 Jan 2025 17:36:11 -0600 Subject: [PATCH 6/6] fix(login): fix build issue --- pages/login/reset_password.page.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pages/login/reset_password.page.tsx b/pages/login/reset_password.page.tsx index d0c2fd07..13c4d044 100644 --- a/pages/login/reset_password.page.tsx +++ b/pages/login/reset_password.page.tsx @@ -21,7 +21,6 @@ import { sendPasswordReset } from "services/firebase/firestore"; import z from "zod"; import axios from "axios"; import ReCAPTCHA from "react-google-recaptcha"; -import { CONSTANTS } from "services/makotools/constants"; function Page() { const [isEmailValid, setIsEmailValid] = useState(null); @@ -69,7 +68,7 @@ function Page() { Forgot password? - First, let's locate your account. + First, let's locate your account.
- An account with that username doesn't exist. Please use a known + An account with that username does not exist. Please use a known email address or{" "} register a new account