Skip to content

Commit

Permalink
Merge pull request #58 from Rahuletto/next
Browse files Browse the repository at this point in the history
Prevent spams and support captcha
  • Loading branch information
Rahuletto committed Sep 21, 2024
2 parents 52c282f + dcae77f commit dc2cdb6
Show file tree
Hide file tree
Showing 25 changed files with 324 additions and 1,124 deletions.
12 changes: 11 additions & 1 deletion app/academia/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { Suspense, useEffect, useState } from "react";
import dynamic from "next/dynamic";
import { Sidebar } from "@/components/Sidebar";
import Loading from "@/components/States/Loading";
import { Cookie } from "@/utils/Cookies";
import Storage from "@/utils/Storage";
import { useRouter } from "next/navigation";

const Attendance = dynamic(
() => import("./components/Attendance").then((a) => a.default),
Expand All @@ -19,10 +22,17 @@ const Timetable = dynamic(

export default function Academia() {
const [mounted, setMounted] = useState(false);
const router = useRouter();

useEffect(() => {
setMounted(true);
}, []);
const key = Cookie.get("key");
if (!key || key.includes("undefined")) {
Storage.clear();
Cookie.clear();
router.push("/auth/login");
}
}, [router]);

if (!mounted) return null;

Expand Down
184 changes: 131 additions & 53 deletions app/auth/login/components/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { useCallback, useState } from "react";
import UidInput from "./form/UidInput";
import PasswordInput from "./form/PasswordInput";
import { Cookie as cookies } from "@/utils/Cookies";
import { getAllUrls, getUrl } from "@/utils/URL";
import { getUrl } from "@/utils/URL";
import Button from "@/components/Button";
import { useTransitionRouter as useRouter } from "next-view-transitions";
import { token } from "@/utils/Encrypt";
Expand All @@ -13,46 +13,100 @@ export default function Form() {

const [uid, setUid] = useState("");
const [pass, setPass] = useState("");
const [captcha, setCaptcha] = useState("");
const [response, setResponse] = useState<any>(null);

const [error, setError] = useState<number | null>(null);
const [message, setMessage] = useState("");

const handleCaptcha = useCallback(async () => {
setError(-1);

const r = await fetch(`${getUrl()}/login`, {
method: "POST",
headers: {
Authorization: `Bearer ${token()}`,
Connection: "keep-alive",
"content-type": "application/json",
Origin: "https://academia-pro.vercel.app",
},
body: JSON.stringify({
account: uid.replaceAll(" ", "").replace("@srmist.edu.in", ""),
password: pass,
captcha: captcha,
identifier: response.identifier,
digest: response.digest,
cdigest: response.cdigest,
}),
});

if (!response.ok) {
setError(1);
setMessage("Server down.");
}
const res = await r.json();
if (res.authenticated) {
setError(2);
cookies.set("key", res.cookies);
console.log("Logged in");
router.refresh();
} else if (res?.message) {
setError(1);
setMessage(res?.message);
}
}, [captcha, uid, pass, router]);

const handleLogin = useCallback(async () => {
setError(-1);
const urls = getAllUrls();
for (const url of urls) {
try {
const response = await fetch(`${url}/login`, {
method: "POST",
headers: {
Authorization: `Bearer ${token()}`,
Connection: "keep-alive",
"content-type": "application/json",
Origin: "https://academia-pro.vercel.app",
},
body: JSON.stringify({
account: uid.replaceAll(" ", "").replace("@srmist.edu.in", ""),
password: pass,
}),
});
if (!response.ok) continue;

const res = await response.json();
if (res.cookies) {
setError(2);
cookies.set("key", res.cookies);
console.log("Logged in");
router.refresh();
} else if (res.message) {
try {
const response = await fetch(`${getUrl()}/login`, {
method: "POST",
headers: {
Authorization: `Bearer ${token()}`,
Connection: "keep-alive",
"content-type": "application/json",
Origin: "https://academia-pro.vercel.app",
},
body: JSON.stringify({
account: uid.replaceAll(" ", "").replace("@srmist.edu.in", ""),
password: pass,
}),
});
if (!response.ok) {
setError(1);
setMessage("Server down.");
}

const res = await response.json();

if(res.cookies && !res.cookies?.includes("undefined")) {
setError(1);
setMessage("Session invalid?");
}
if (res.captcha) {
setResponse(res);
} else if (res?.message?.includes("HIP")) {
if (res?.localized_message) {
setError(1);
setMessage(res.message);
setMessage(res.localized_message);
} else {
setError(1);
setMessage(res?.message);
}
return;
} catch (error) {
console.warn(error);
} finally {
setTimeout(() => setError(0), 6000);
} else if (res.authenticated) {
setError(2);
cookies.set("key", res.cookies);
console.log("Logged in");
router.refresh();
} else if (res?.message) {
setError(1);
setMessage(res?.message);
}
} catch (error) {
console.warn(error);
} finally {
setTimeout(() => setError(0), 6000);
}
}, [uid, pass, router]);

Expand All @@ -61,35 +115,59 @@ export default function Form() {
className="flex flex-col gap-6"
onSubmit={(e) => {
e.preventDefault();
handleLogin();
response?.captcha ? handleCaptcha() : handleLogin();
}}
>
{error === 1 && (
<p className="rounded-2xl bg-light-error-background px-4 py-2 text-light-error-color dark:bg-dark-error-background dark:text-dark-error-color">
SRM: {message}
</p>
)}
<div className="relative flex flex-col gap-1">
<UidInput uid={uid} setUid={setUid} />
<PasswordInput password={pass} setPassword={setPass} />
</div>

<Button
disabled={!uid || !pass}
className={
error === 2
? "border border-light-success-color bg-light-success-background text-light-success-color dark:border-dark-success-color dark:bg-dark-success-background dark:text-dark-success-color"
: error === -1
? "border border-light-warn-color bg-light-warn-background text-light-warn-color dark:border-dark-warn-color dark:bg-dark-warn-background dark:text-dark-warn-color"
: error === 1
? "border border-light-error-color bg-light-error-background text-light-error-color dark:border-dark-error-color dark:bg-dark-error-background dark:text-dark-error-color"
: ""
}
type="submit"
onClick={handleLogin}
>
{error === -1 ? "Authenticating" : error === 2 ? "Success" : "Login"}
</Button>
{!response?.captcha && (
<div className="relative flex flex-col gap-1">
<UidInput uid={uid} setUid={setUid} />
<PasswordInput password={pass} setPassword={setPass} />
</div>
)}
{response?.captcha && (
<>
<div className="flex flex-col gap-2">
<img src={response?.captcha} alt="captcha" className="rounded-xl" />
<input
type="text"
className="rounded-2xl border border-light-background-darker bg-dark-input px-6 py-3 font-sans font-medium text-light-color dark:border-dark-background-darker dark:text-dark-color"
placeholder="Enter captcha"
value={captcha}
onChange={(e) => setCaptcha(e.target.value)}
/>
</div>
<button
type="submit"
onClick={handleCaptcha}
className={`w-fit transform rounded-xl bg-light-accent px-4 py-2 font-medium text-light-background-dark transition-all duration-200 hover:scale-95 hover:opacity-80 hover:shadow-lg active:scale-90 dark:bg-dark-accent dark:text-dark-background-dark`}
>
Validate
</button>
</>
)}
{!response?.captcha && (
<Button
disabled={!uid || !pass}
className={
error === 2
? "border border-light-success-color bg-light-success-background text-light-success-color dark:border-dark-success-color dark:bg-dark-success-background dark:text-dark-success-color"
: error === -1
? "border border-light-warn-color bg-light-warn-background text-light-warn-color dark:border-dark-warn-color dark:bg-dark-warn-background dark:text-dark-warn-color"
: error === 1
? "border border-light-error-color bg-light-error-background text-light-error-color dark:border-dark-error-color dark:bg-dark-error-background dark:text-dark-error-color"
: ""
}
type="submit"
onClick={handleLogin}
>
{error === -1 ? "Authenticating" : error === 2 ? "Success" : "Login"}
</Button>
)}
</form>
);
}
44 changes: 19 additions & 25 deletions app/auth/logout/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { Cookie } from "@/utils/Cookies";
import { token } from "@/utils/Encrypt";
import Storage from "@/utils/Storage";
import { getAllUrls, getUrl } from "@/utils/URL";
import { useTransitionRouter as useRouter } from "next-view-transitions";
import React, { useEffect, useMemo } from "react";
Expand All @@ -11,40 +12,36 @@ export default function Logout() {
const router = useRouter();
useMemo(() => {
const logout = async () => {
const urls = getAllUrls();

try {
for (const url of urls) {
const response = await fetch(`${url}/logout`, {
await fetch(
`${process.env.NEXT_PUBLIC_API_URL_MORN}/logout`,
{
method: "DELETE",
headers: {
cookie: `key=${Cookie.get("key")}`,
Authorization: `Bearer ${token()}`,
"X-CSRF-Token": Cookie.get("key") as string,
Origin: "https://academia-pro.vercel.app",
},
});
},
);

Cookie.clear();
Storage.clear();
sessionStorage.clear();

if (!response.ok) {
alert("An error occurred! Try to clear cookies manually.");
continue;
} else {
Cookie.clear();
sessionStorage.clear();
if ("caches" in window) {
const cacheNames = await caches.keys();
for (const name of cacheNames) {
await caches.delete(name);
}
}
router.push("/");
break;
if ("caches" in window) {
const cacheNames = await caches.keys();
for (const name of cacheNames) {
await caches.delete(name);
}
}

router.push("/auth/login");

} catch (error) {
alert("An error occurred! Try to clear cookies manually.");
console.warn(error);
setError(1);
} finally {
setTimeout(() => setError(0), 6000);
}
};

Expand All @@ -61,6 +58,3 @@ export default function Logout() {
</main>
);
}
function setError(arg0: number) {
throw new Error("Function not implemented.");
}
1 change: 0 additions & 1 deletion app/courses/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"use client";
import { useCourses } from "@/provider/CourseProvider";
import React, { useEffect } from "react";
import Loading from "@/components/States/Loading";
import Error from "@/components/States/Error";
Expand Down
2 changes: 1 addition & 1 deletion app/home/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default function Academia() {
href="/auth/login"
className="flex items-center justify-center px-3 py-1 text-md hover:px-7 text-lg hover:bg-transparent hover:dark:border-dark-accent hover:border-light-accent border-2 border-transparent dark:bg-dark-accent bg-light-accent text-dark-color hover:text-light-accent hover:dark:text-dark-accent dark:text-light-color font-semibold rounded-xl transition-all duration-300 transform w-52"
>
Go to Dashboard
Dashboard
</Link>
:
<Link
Expand Down
7 changes: 0 additions & 7 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,12 @@ import type { Metadata } from "next";
import { GeistSans } from "geist/font/sans";
import { GeistMono } from "geist/font/mono";
import { Analytics } from "@vercel/analytics/react";
import { UserProvider } from "@/provider/UserProvider";

import "./globals.css";
import { DayProvider } from "@/provider/DayProvider";
import { TableProvider } from "@/provider/TimetableProvider";
import { ThemeProvider } from "@/provider/ThemeProvider";
import { MarksProvider } from "@/provider/MarksProvider";
import { cookies } from "next/headers";
import { Themes } from "@/misc/theme";
import { ViewTransitions } from "next-view-transitions";
import { AttendanceProvider } from "@/provider/AttendanceProvider";
import { CalendarProvider } from "@/provider/CalendarProvider";
import { CourseProvider } from "@/provider/CourseProvider";
import { ReactNode } from "react";
import ErrorBoundary from "./Boundary";
import { DataProvider } from "@/provider/DataProvider";
Expand Down
2 changes: 0 additions & 2 deletions app/timetable/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
import Link from "@/components/Link";
import Loading from "@/components/States/Loading";
import { useData } from "@/provider/DataProvider";
import { useTimetable } from "@/provider/TimetableProvider";
import { useUser } from "@/provider/UserProvider";
import Image from "next/image";
import React, { useEffect, useState } from "react";
import { FaArrowLeft } from "react-icons/fa";
Expand Down
Binary file modified bun.lockb
Binary file not shown.
Loading

0 comments on commit dc2cdb6

Please sign in to comment.