removed excess comments
This commit is contained in:
@@ -123,7 +123,6 @@ export default function AccountPage() {
|
||||
}
|
||||
});
|
||||
|
||||
// Profile image handlers
|
||||
const handleImageDrop = (acceptedFiles: File[]) => {
|
||||
acceptedFiles.forEach((file: File) => {
|
||||
setProfileImage(file);
|
||||
@@ -184,7 +183,6 @@ export default function AccountPage() {
|
||||
setProfileImageStateChange(false);
|
||||
setTimeout(() => setShowImageSuccess(false), 3000);
|
||||
|
||||
// Update preSetHolder with new image
|
||||
setPreSetHolder(imageUrl || null);
|
||||
} else {
|
||||
alert("Error updating profile image!");
|
||||
@@ -197,7 +195,6 @@ export default function AccountPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// Email update handler
|
||||
const setEmailTrigger = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (!emailRef) return;
|
||||
@@ -231,7 +228,6 @@ export default function AccountPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// Display name update handler
|
||||
const setDisplayNameTrigger = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (!displayNameRef) return;
|
||||
@@ -260,14 +256,12 @@ export default function AccountPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// Password change/set handler
|
||||
const handlePasswordSubmit = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
const userProfile = currentUser();
|
||||
if (!userProfile) return;
|
||||
|
||||
if (userProfile.hasPassword) {
|
||||
// Change password (requires old password)
|
||||
if (!oldPasswordRef || !newPasswordRef || !newPasswordConfRef) return;
|
||||
|
||||
const oldPassword = oldPasswordRef.value;
|
||||
@@ -316,7 +310,6 @@ export default function AccountPage() {
|
||||
setPasswordChangeLoading(false);
|
||||
}
|
||||
} else {
|
||||
// Set password (first time for OAuth users)
|
||||
if (!newPasswordRef || !newPasswordConfRef) return;
|
||||
|
||||
const newPassword = newPasswordRef.value;
|
||||
@@ -348,7 +341,6 @@ export default function AccountPage() {
|
||||
|
||||
const result = await response.json();
|
||||
if (response.ok && result.result?.data?.success) {
|
||||
// Refresh user data to show hasPassword = true
|
||||
const profileResponse = await fetch("/api/trpc/user.getProfile");
|
||||
const profileResult = await profileResponse.json();
|
||||
if (profileResult.result?.data) {
|
||||
@@ -370,7 +362,6 @@ export default function AccountPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// Delete account handler
|
||||
const deleteAccountTrigger = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (!deleteAccountPasswordRef) return;
|
||||
@@ -400,7 +391,6 @@ export default function AccountPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// Resend email verification
|
||||
const sendEmailVerification = async () => {
|
||||
const userProfile = currentUser();
|
||||
if (!userProfile?.email) return;
|
||||
@@ -460,7 +450,6 @@ export default function AccountPage() {
|
||||
setPasswordBlurred(true);
|
||||
};
|
||||
|
||||
// Sign out handler
|
||||
const handleSignOut = async () => {
|
||||
setSignOutLoading(true);
|
||||
try {
|
||||
@@ -472,7 +461,6 @@ export default function AccountPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// Helper to get provider display name
|
||||
const getProviderName = (provider: UserProfile["provider"]) => {
|
||||
switch (provider) {
|
||||
case "google":
|
||||
@@ -486,7 +474,6 @@ export default function AccountPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// Helper to get provider icon color
|
||||
const getProviderColor = (provider: UserProfile["provider"]) => {
|
||||
switch (provider) {
|
||||
case "google":
|
||||
|
||||
@@ -22,11 +22,9 @@ export async function GET(event: APIEvent) {
|
||||
}
|
||||
|
||||
try {
|
||||
// Create tRPC caller to invoke the githubCallback procedure
|
||||
const ctx = await createTRPCContext(event);
|
||||
const caller = appRouter.createCaller(ctx);
|
||||
|
||||
// Call the GitHub callback handler
|
||||
const result = await caller.auth.githubCallback({ code });
|
||||
|
||||
if (result.success) {
|
||||
|
||||
@@ -22,11 +22,9 @@ export async function GET(event: APIEvent) {
|
||||
}
|
||||
|
||||
try {
|
||||
// Create tRPC caller to invoke the googleCallback procedure
|
||||
const ctx = await createTRPCContext(event);
|
||||
const caller = appRouter.createCaller(ctx);
|
||||
|
||||
// Call the Google callback handler
|
||||
const result = await caller.auth.googleCallback({ code });
|
||||
|
||||
if (result.success) {
|
||||
|
||||
@@ -5,14 +5,13 @@ export async function POST() {
|
||||
"use server";
|
||||
const event = getEvent()!;
|
||||
|
||||
// Clear the userIDToken cookie (the actual session cookie)
|
||||
setCookie(event, "userIDToken", "", {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true, // Always enforce secure cookies
|
||||
secure: true,
|
||||
sameSite: "lax",
|
||||
maxAge: 0, // Expire immediately
|
||||
expires: new Date(0) // Set expiry to past date
|
||||
maxAge: 0,
|
||||
expires: new Date(0)
|
||||
});
|
||||
|
||||
return new Response(null, {
|
||||
|
||||
@@ -4,16 +4,11 @@ import { appRouter } from "~/server/api/root";
|
||||
import { createTRPCContext } from "~/server/api/utils";
|
||||
|
||||
const handler = (event: APIEvent) => {
|
||||
// adapts tRPC to fetch API style requests
|
||||
return fetchRequestHandler({
|
||||
// the endpoint handling the requests
|
||||
endpoint: "/api/trpc",
|
||||
// the request object
|
||||
req: event.request,
|
||||
// the router for handling the requests
|
||||
router: appRouter,
|
||||
// any arbitrary data that should be available to all actions
|
||||
createContext: () => createTRPCContext(event),
|
||||
createContext: () => createTRPCContext(event)
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ const getPostByTitle = query(
|
||||
const userID = await getUserID(event.nativeEvent);
|
||||
const conn = ConnectionFactory();
|
||||
|
||||
// Handle by-id route: lookup post by ID and redirect to title-based URL
|
||||
if (title === "by-id") {
|
||||
const url = new URL(event.request.url);
|
||||
const id = url.searchParams.get("id");
|
||||
@@ -128,7 +127,6 @@ const getPostByTitle = query(
|
||||
};
|
||||
}
|
||||
|
||||
// Build conditional evaluation context
|
||||
const conditionalContext = {
|
||||
isAuthenticated: userID !== null,
|
||||
privilegeLevel: privilegeLevel,
|
||||
@@ -143,14 +141,11 @@ const getPostByTitle = query(
|
||||
post.body = parseConditionals(post.body, conditionalContext);
|
||||
} catch (error) {
|
||||
console.error("Error parsing conditionals in post body:", error);
|
||||
// Fall back to showing original content
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch comments with sorting
|
||||
let commentQuery = "SELECT * FROM Comment WHERE post_id = ?";
|
||||
|
||||
// Build ORDER BY clause based on sortBy parameter
|
||||
switch (sortBy) {
|
||||
case "newest":
|
||||
commentQuery += " ORDER BY date DESC";
|
||||
@@ -159,7 +154,6 @@ const getPostByTitle = query(
|
||||
commentQuery += " ORDER BY date ASC";
|
||||
break;
|
||||
case "highest_rated":
|
||||
// Calculate net score (upvotes - downvotes) for each comment
|
||||
commentQuery = `
|
||||
SELECT c.*,
|
||||
COALESCE((
|
||||
@@ -177,7 +171,6 @@ const getPostByTitle = query(
|
||||
`;
|
||||
break;
|
||||
case "hot":
|
||||
// Calculate hot score: (upvotes - downvotes) / log10(age_in_hours + 2)
|
||||
commentQuery = `
|
||||
SELECT c.*,
|
||||
(COALESCE((
|
||||
@@ -201,16 +194,13 @@ const getPostByTitle = query(
|
||||
await conn.execute({ sql: commentQuery, args: [post.id] })
|
||||
).rows;
|
||||
|
||||
// Fetch likes
|
||||
const likeQuery = "SELECT * FROM PostLike WHERE post_id = ?";
|
||||
const likes = (await conn.execute({ sql: likeQuery, args: [post.id] }))
|
||||
.rows;
|
||||
|
||||
// Fetch tags
|
||||
const tagQuery = "SELECT * FROM Tag WHERE post_id = ?";
|
||||
const tags = (await conn.execute({ sql: tagQuery, args: [post.id] })).rows;
|
||||
|
||||
// Build commenter map
|
||||
const commenterToCommentIDMap = new Map<string, number[]>();
|
||||
comments.forEach((comment: any) => {
|
||||
const prev = commenterToCommentIDMap.get(comment.commenter_id) || [];
|
||||
@@ -220,7 +210,6 @@ const getPostByTitle = query(
|
||||
const commenterQuery =
|
||||
"SELECT email, display_name, image FROM User WHERE id = ?";
|
||||
|
||||
// Convert to serializable array format
|
||||
const userCommentArray: Array<[UserPublicData, number[]]> = [];
|
||||
|
||||
for (const [key, value] of commenterToCommentIDMap.entries()) {
|
||||
@@ -231,7 +220,6 @@ const getPostByTitle = query(
|
||||
}
|
||||
}
|
||||
|
||||
// Get reaction map as serializable array
|
||||
const reactionArray: Array<[number, CommentReaction[]]> = [];
|
||||
for (const comment of comments) {
|
||||
const reactionQuery =
|
||||
@@ -243,7 +231,6 @@ const getPostByTitle = query(
|
||||
reactionArray.push([(comment as any).id, res.rows as CommentReaction[]]);
|
||||
}
|
||||
|
||||
// Filter top-level comments (preserve sort order from SQL)
|
||||
const topLevelComments = comments.filter(
|
||||
(c: any) => c.parent_comment_id == null
|
||||
);
|
||||
@@ -283,7 +270,6 @@ export default function PostPage() {
|
||||
{ deferStream: true }
|
||||
);
|
||||
|
||||
// Increment read count when post loads
|
||||
createEffect(() => {
|
||||
const postData = data();
|
||||
if (postData?.post?.id) {
|
||||
@@ -310,7 +296,6 @@ export default function PostPage() {
|
||||
}
|
||||
>
|
||||
{(loadedData) => {
|
||||
// Handle redirect for by-id route
|
||||
if ("redirect" in loadedData()) {
|
||||
return <Navigate href={(loadedData() as any).redirect} />;
|
||||
}
|
||||
@@ -323,7 +308,6 @@ export default function PostPage() {
|
||||
{(p) => {
|
||||
const postData = loadedData();
|
||||
|
||||
// Convert arrays back to Maps for component
|
||||
const userCommentMap = new Map<UserPublicData, number[]>(
|
||||
postData.userCommentArray || []
|
||||
);
|
||||
@@ -345,7 +329,6 @@ export default function PostPage() {
|
||||
/>
|
||||
|
||||
<div class="blog-overide relative -mt-16 overflow-x-hidden">
|
||||
{/* Fixed banner image background */}
|
||||
<div class="fixed inset-0 top-0 left-0 z-0 aspect-auto max-h-3/4 w-full overflow-hidden brightness-75 md:ml-62.5 md:max-h-[50vh] md:w-[calc(100vw-500px)]">
|
||||
<img
|
||||
src={p().banner_photo || "/blueprint.jpg"}
|
||||
@@ -368,7 +351,6 @@ export default function PostPage() {
|
||||
</div>
|
||||
|
||||
<div class="z-10 pt-80 backdrop-blur-[0.01px] sm:pt-96 md:pt-[50vh]">
|
||||
{/* Content that slides over the fixed image */}
|
||||
<div class="bg-base relative pb-24">
|
||||
<div class="flex w-full flex-col justify-center pt-8 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div class="flex flex-col gap-2 px-4 md:px-8">
|
||||
@@ -465,24 +447,12 @@ export default function PostPage() {
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
{/* Post body */}
|
||||
|
||||
<PostBodyClient
|
||||
body={p().body}
|
||||
hasCodeBlock={hasCodeBlock(p().body)}
|
||||
/>
|
||||
|
||||
<Show when={postData.privilegeLevel === "admin"}>
|
||||
<div class="flex justify-center">
|
||||
<A
|
||||
class="border-blue bg-blue z-10 h-fit rounded border px-4 py-2 text-base shadow-md transition-all duration-300 ease-in-out hover:brightness-125 active:scale-90"
|
||||
href={`/blog/edit/${p().id}`}
|
||||
>
|
||||
Edit
|
||||
</A>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Comments section */}
|
||||
<div
|
||||
id="comments"
|
||||
class="mx-4 pt-12 pb-12 md:mx-8 lg:mx-12"
|
||||
|
||||
@@ -24,7 +24,6 @@ const getPosts = query(async () => {
|
||||
async () => {
|
||||
const conn = ConnectionFactory();
|
||||
|
||||
// Fetch all posts with aggregated data
|
||||
let postsQuery = `
|
||||
SELECT
|
||||
p.id,
|
||||
|
||||
@@ -80,7 +80,6 @@ const sendContactEmail = action(async (formData: FormData) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Send email
|
||||
const apiKey = env.SENDINBLUE_KEY;
|
||||
const apiUrl = "https://api.sendinblue.com/v3/smtp/email";
|
||||
|
||||
@@ -117,7 +116,6 @@ const sendContactEmail = action(async (formData: FormData) => {
|
||||
}
|
||||
);
|
||||
|
||||
// Set cooldown cookie
|
||||
const exp = new Date(Date.now() + COOLDOWN_TIMERS.CONTACT_REQUEST_MS);
|
||||
setCookie("contactRequestSent", exp.toUTCString(), {
|
||||
expires: exp,
|
||||
@@ -186,7 +184,6 @@ export default function ContactPage() {
|
||||
onMount(() => {
|
||||
setJsEnabled(true);
|
||||
|
||||
// Initialize countdown from server data
|
||||
const serverData = contactData();
|
||||
if (serverData?.remainingTime) {
|
||||
setCountDown(serverData.remainingTime);
|
||||
@@ -197,7 +194,6 @@ export default function ContactPage() {
|
||||
timerIdRef = setInterval(() => calcRemainder(timer), 1000);
|
||||
}
|
||||
|
||||
// Fetch user data if authenticated
|
||||
api.user.getProfile
|
||||
.query()
|
||||
.then((userData) => {
|
||||
@@ -205,11 +201,8 @@ export default function ContactPage() {
|
||||
setUser(userData);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// User not authenticated, no problem
|
||||
});
|
||||
.catch(() => {});
|
||||
|
||||
// Clear URL params after reading them (for better UX on refresh)
|
||||
if (searchParams.success || searchParams.error) {
|
||||
const timer = setTimeout(() => {
|
||||
const newUrl =
|
||||
@@ -230,9 +223,7 @@ export default function ContactPage() {
|
||||
});
|
||||
});
|
||||
|
||||
// Progressive enhancement: JS-enhanced form submission
|
||||
const sendEmailTrigger = async (e: Event) => {
|
||||
// Only intercept if JS is enabled
|
||||
if (!jsEnabled()) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
@@ -3,7 +3,6 @@ import { createSignal, onMount } from "solid-js";
|
||||
export default function ErrorTest() {
|
||||
const [shouldCrash, setShouldCrash] = createSignal(false);
|
||||
|
||||
// Crash on mount if flag is set
|
||||
if (shouldCrash()) {
|
||||
throw new Error("Test error - Error boundary triggered!");
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ export default function LoginPage() {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
// Derive state directly from URL parameters (no signals needed)
|
||||
const register = () => searchParams.mode === "register";
|
||||
const usePassword = () => searchParams.auth === "password";
|
||||
|
||||
@@ -62,7 +61,6 @@ export default function LoginPage() {
|
||||
let rememberMeRef: HTMLInputElement | undefined;
|
||||
let timerInterval: number | undefined;
|
||||
|
||||
// Environment variables
|
||||
const googleClientId = env.VITE_GOOGLE_CLIENT_ID;
|
||||
const githubClientId = env.VITE_GITHUB_CLIENT_ID;
|
||||
const domain = env.VITE_DOMAIN || "https://www.freno.me";
|
||||
@@ -124,7 +122,6 @@ export default function LoginPage() {
|
||||
|
||||
try {
|
||||
if (register()) {
|
||||
// Registration flow
|
||||
if (!emailRef || !passwordRef || !passwordConfRef) {
|
||||
setError("Please fill in all fields");
|
||||
setLoading(false);
|
||||
@@ -154,7 +151,6 @@ export default function LoginPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Call registration endpoint
|
||||
const response = await fetch("/api/trpc/auth.emailRegistration", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
@@ -176,15 +172,12 @@ export default function LoginPage() {
|
||||
"Registration failed";
|
||||
const errorCode = result.error?.data?.code;
|
||||
|
||||
// Check for rate limiting
|
||||
if (
|
||||
errorCode === "TOO_MANY_REQUESTS" ||
|
||||
errorMsg.includes("Too many attempts")
|
||||
) {
|
||||
setError(errorMsg);
|
||||
}
|
||||
// Check for duplicate email
|
||||
else if (
|
||||
} else if (
|
||||
errorMsg.includes("duplicate") ||
|
||||
errorMsg.includes("already exists")
|
||||
) {
|
||||
@@ -194,7 +187,6 @@ export default function LoginPage() {
|
||||
}
|
||||
}
|
||||
} else if (usePassword()) {
|
||||
// Password login flow
|
||||
if (!emailRef || !passwordRef || !rememberMeRef) {
|
||||
setError("Please fill in all fields");
|
||||
setLoading(false);
|
||||
@@ -219,32 +211,25 @@ export default function LoginPage() {
|
||||
navigate("/account", { replace: true });
|
||||
}, 500);
|
||||
} else {
|
||||
// Handle specific error types
|
||||
const errorMessage = result.error?.message || "";
|
||||
const errorCode = result.error?.data?.code;
|
||||
|
||||
// Check for rate limiting
|
||||
if (
|
||||
errorCode === "TOO_MANY_REQUESTS" ||
|
||||
errorMessage.includes("Too many attempts")
|
||||
) {
|
||||
setError(errorMessage);
|
||||
}
|
||||
// Check for account lockout
|
||||
else if (
|
||||
} else if (
|
||||
errorCode === "FORBIDDEN" ||
|
||||
errorMessage.includes("Account locked") ||
|
||||
errorMessage.includes("Account is locked")
|
||||
) {
|
||||
setError(errorMessage);
|
||||
}
|
||||
// Generic login failure
|
||||
else {
|
||||
} else {
|
||||
setShowPasswordError(true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Email link login flow
|
||||
if (!emailRef || !rememberMeRef) {
|
||||
setError("Please enter your email");
|
||||
setLoading(false);
|
||||
@@ -287,7 +272,6 @@ export default function LoginPage() {
|
||||
"Failed to send email";
|
||||
const errorCode = result.error?.data?.code;
|
||||
|
||||
// Check for rate limiting or countdown not expired
|
||||
if (
|
||||
errorCode === "TOO_MANY_REQUESTS" ||
|
||||
errorMsg.includes("countdown not expired") ||
|
||||
@@ -342,9 +326,7 @@ export default function LoginPage() {
|
||||
content="Sign in to your account or register for a new account to access personalized features and manage your profile."
|
||||
/>
|
||||
<div class="flex h-dvh flex-row justify-evenly">
|
||||
{/* Main content */}
|
||||
<div class="relative pt-12 md:pt-24">
|
||||
{/* Error message */}
|
||||
<Show when={error()}>
|
||||
<div class="border-maroon bg-red mb-4 w-full max-w-md rounded-lg border px-4 py-3 text-center">
|
||||
<Show when={error() === "passwordMismatch"}>
|
||||
@@ -389,12 +371,10 @@ export default function LoginPage() {
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Title */}
|
||||
<div class="py-2 pl-6 text-2xl md:pl-0">
|
||||
{register() ? "Register" : "Login"}
|
||||
</div>
|
||||
|
||||
{/* Toggle Register/Login */}
|
||||
<Show
|
||||
when={!register()}
|
||||
fallback={
|
||||
@@ -420,9 +400,7 @@ export default function LoginPage() {
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Form */}
|
||||
<form onSubmit={formHandler} class="flex flex-col px-2 py-4">
|
||||
{/* Email input */}
|
||||
<div class="flex justify-center">
|
||||
<div class="input-group mx-4">
|
||||
<input
|
||||
@@ -438,7 +416,6 @@ export default function LoginPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Password input - shown for login with password or registration */}
|
||||
<Show when={usePassword() || register()}>
|
||||
<div class="-mt-4 flex justify-center">
|
||||
<div class="input-group mx-4 flex">
|
||||
@@ -485,14 +462,12 @@ export default function LoginPage() {
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Password strength meter - shown only for registration */}
|
||||
<Show when={register()}>
|
||||
<div class="mx-auto flex justify-center px-4 py-2">
|
||||
<PasswordStrengthMeter password={password()} />
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Password confirmation - shown only for registration */}
|
||||
<Show when={register()}>
|
||||
<div class="flex justify-center">
|
||||
<div class="input-group mx-4">
|
||||
@@ -550,13 +525,11 @@ export default function LoginPage() {
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Remember Me checkbox */}
|
||||
<div class="mx-auto flex pt-4">
|
||||
<input type="checkbox" class="my-auto" ref={rememberMeRef} />
|
||||
<div class="my-auto px-2 text-sm font-normal">Remember Me</div>
|
||||
</div>
|
||||
|
||||
{/* Error/Success messages */}
|
||||
<div
|
||||
class={`${
|
||||
showPasswordError()
|
||||
@@ -574,7 +547,6 @@ export default function LoginPage() {
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
{/* Submit button or countdown timer */}
|
||||
<div class="flex justify-center py-4">
|
||||
<Show
|
||||
when={!register() && !usePassword() && countDown() > 0}
|
||||
@@ -607,7 +579,6 @@ export default function LoginPage() {
|
||||
</CountdownCircleTimer>
|
||||
</Show>
|
||||
|
||||
{/* Toggle password/email link */}
|
||||
<Show when={!register() && !usePassword()}>
|
||||
<A
|
||||
href="/login?auth=password"
|
||||
@@ -627,7 +598,6 @@ export default function LoginPage() {
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{/* Password reset link */}
|
||||
<Show when={usePassword()}>
|
||||
<div class="pb-4 text-center text-sm">
|
||||
Trouble Logging In?{" "}
|
||||
@@ -640,7 +610,6 @@ export default function LoginPage() {
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Email sent confirmation */}
|
||||
<div
|
||||
class={`${
|
||||
emailSent() ? "" : "user-select opacity-0"
|
||||
@@ -649,10 +618,8 @@ export default function LoginPage() {
|
||||
<Show when={emailSent()}>Email Sent!</Show>
|
||||
</div>
|
||||
|
||||
{/* Or divider */}
|
||||
<div class="rule-around text-center">Or</div>
|
||||
|
||||
{/* OAuth buttons */}
|
||||
<div class="my-2 flex justify-center">
|
||||
<div class="mx-auto mb-4 flex flex-col">
|
||||
{/* Google OAuth */}
|
||||
|
||||
@@ -28,7 +28,6 @@ export default function PasswordResetPage() {
|
||||
let newPasswordRef: HTMLInputElement | undefined;
|
||||
let newPasswordConfRef: HTMLInputElement | undefined;
|
||||
|
||||
// Get token from URL
|
||||
const token = searchParams.token;
|
||||
|
||||
createEffect(() => {
|
||||
@@ -50,7 +49,6 @@ export default function PasswordResetPage() {
|
||||
const newPassword = newPasswordRef.value;
|
||||
const newPasswordConf = newPasswordConfRef.value;
|
||||
|
||||
// Validate password
|
||||
const passwordValidation = validatePassword(newPassword);
|
||||
if (!passwordValidation.isValid) {
|
||||
setError(passwordValidation.errors[0] || "Invalid password");
|
||||
@@ -140,7 +138,6 @@ export default function PasswordResetPage() {
|
||||
passwordLengthBlurCheck();
|
||||
};
|
||||
|
||||
// Render countdown timer
|
||||
const renderTime = (timeRemaining: number) => {
|
||||
if (timeRemaining === 0) {
|
||||
navigate("/login");
|
||||
@@ -171,7 +168,6 @@ export default function PasswordResetPage() {
|
||||
class="mt-4 flex w-full justify-center"
|
||||
>
|
||||
<div class="flex w-full max-w-md flex-col justify-center px-4">
|
||||
{/* New Password Input */}
|
||||
<div class="flex justify-center">
|
||||
<div class="input-group mx-4 flex">
|
||||
<input
|
||||
@@ -218,7 +214,6 @@ export default function PasswordResetPage() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Password Length Warning */}
|
||||
<div
|
||||
class={`${
|
||||
showPasswordLengthWarning() ? "" : "opacity-0 select-none"
|
||||
@@ -228,7 +223,6 @@ export default function PasswordResetPage() {
|
||||
{VALIDATION_CONFIG.MIN_PASSWORD_LENGTH}
|
||||
</div>
|
||||
|
||||
{/* Password Confirmation Input */}
|
||||
<div class="-mt-4 flex justify-center">
|
||||
<div class="input-group mx-4 flex">
|
||||
<input
|
||||
@@ -275,7 +269,6 @@ export default function PasswordResetPage() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Password Mismatch Warning */}
|
||||
<div
|
||||
class={`${
|
||||
!passwordsMatch() &&
|
||||
@@ -290,7 +283,6 @@ export default function PasswordResetPage() {
|
||||
Passwords do not match!
|
||||
</div>
|
||||
|
||||
{/* Countdown Timer or Submit Button */}
|
||||
<Show
|
||||
when={countDown()}
|
||||
fallback={
|
||||
@@ -323,14 +315,12 @@ export default function PasswordResetPage() {
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{/* Error Message */}
|
||||
<Show when={error() && !showRequestNewEmail()}>
|
||||
<div class="mt-4 flex justify-center">
|
||||
<div class="text-red text-sm italic">{error()}</div>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Token Expired Message */}
|
||||
<div
|
||||
class={`${
|
||||
showRequestNewEmail() ? "" : "opacity-0 select-none"
|
||||
@@ -345,7 +335,6 @@ export default function PasswordResetPage() {
|
||||
</A>
|
||||
</div>
|
||||
|
||||
{/* Back to Login Link */}
|
||||
<Show when={!countDown()}>
|
||||
<div class="mt-6 flex justify-center">
|
||||
<A
|
||||
|
||||
@@ -60,7 +60,6 @@ export default function RequestPasswordResetPage() {
|
||||
|
||||
const email = emailRef.value;
|
||||
|
||||
// Validate email
|
||||
if (!isValidEmail(email)) {
|
||||
setError("Invalid email address");
|
||||
return;
|
||||
@@ -81,7 +80,6 @@ export default function RequestPasswordResetPage() {
|
||||
setShowSuccessMessage(true);
|
||||
setError("");
|
||||
|
||||
// Start countdown timer
|
||||
const timer = getClientCookie("passwordResetRequested");
|
||||
if (timer) {
|
||||
if (timerInterval) {
|
||||
@@ -95,15 +93,12 @@ export default function RequestPasswordResetPage() {
|
||||
const errorMsg = result.error?.message || "Failed to send reset email";
|
||||
const errorCode = result.error?.data?.code;
|
||||
|
||||
// Handle rate limiting
|
||||
if (
|
||||
errorCode === "TOO_MANY_REQUESTS" ||
|
||||
errorMsg.includes("Too many attempts")
|
||||
) {
|
||||
setError(errorMsg);
|
||||
}
|
||||
// Handle countdown not expired
|
||||
else if (errorMsg.includes("countdown not expired")) {
|
||||
} else if (errorMsg.includes("countdown not expired")) {
|
||||
setError("Please wait before requesting another reset email");
|
||||
} else {
|
||||
setError(errorMsg);
|
||||
@@ -141,7 +136,6 @@ export default function RequestPasswordResetPage() {
|
||||
class="mt-4 flex w-full justify-center"
|
||||
>
|
||||
<div class="flex flex-col justify-center">
|
||||
{/* Email Input */}
|
||||
<div class="input-group mx-4">
|
||||
<input
|
||||
ref={emailRef}
|
||||
@@ -157,7 +151,6 @@ export default function RequestPasswordResetPage() {
|
||||
<label class="underlinedInputLabel">Enter Email</label>
|
||||
</div>
|
||||
|
||||
{/* Countdown Timer or Submit Button */}
|
||||
<Show
|
||||
when={countDown() > 0}
|
||||
fallback={
|
||||
@@ -191,7 +184,6 @@ export default function RequestPasswordResetPage() {
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{/* Success Message */}
|
||||
<div
|
||||
class={`${
|
||||
showSuccessMessage() ? "" : "opacity-0 select-none"
|
||||
@@ -200,7 +192,6 @@ export default function RequestPasswordResetPage() {
|
||||
If email exists, you will receive an email shortly!
|
||||
</div>
|
||||
|
||||
{/* Error Message */}
|
||||
<Show when={error()}>
|
||||
<div class="mt-4 flex justify-center">
|
||||
<div
|
||||
@@ -230,7 +221,6 @@ export default function RequestPasswordResetPage() {
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Back to Login Link */}
|
||||
<div class="mt-6 flex justify-center">
|
||||
<A
|
||||
href="/login"
|
||||
|
||||
@@ -4,7 +4,6 @@ import { onCleanup, onMount } from "solid-js";
|
||||
export default function Resume() {
|
||||
let iframeRef: HTMLIFrameElement | undefined;
|
||||
|
||||
// this error kept happening in production, so I added this to prevent it, idk what was happening
|
||||
onMount(() => {
|
||||
const handleError = (e: ErrorEvent) => {
|
||||
if (e.filename?.includes("resume.pdf") || e.message === "Script error.") {
|
||||
|
||||
@@ -8,10 +8,6 @@ import {
|
||||
passwordsMatch
|
||||
} from "~/lib/validation";
|
||||
|
||||
/**
|
||||
* Test page to validate Task 01 components and utilities
|
||||
* Navigate to /test-utils to view
|
||||
*/
|
||||
export default function TestUtilsPage() {
|
||||
const [email, setEmail] = createSignal("");
|
||||
const [password, setPassword] = createSignal("");
|
||||
@@ -40,7 +36,6 @@ export default function TestUtilsPage() {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
|
||||
// Simulate API call
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
|
||||
alert(`Form submitted!\nEmail: ${email()}\nPassword: ${password()}`);
|
||||
|
||||
@@ -31,9 +31,6 @@ type RouterSection = {
|
||||
};
|
||||
|
||||
const routerSections: RouterSection[] = [
|
||||
// ============================================================
|
||||
// Example Router
|
||||
// ============================================================
|
||||
{
|
||||
name: "Example Router",
|
||||
description:
|
||||
@@ -66,9 +63,6 @@ const routerSections: RouterSection[] = [
|
||||
]
|
||||
},
|
||||
|
||||
// ============================================================
|
||||
// Auth Router
|
||||
// ============================================================
|
||||
{
|
||||
name: "Auth Router",
|
||||
description: "OAuth callbacks and email-based authentication",
|
||||
@@ -190,9 +184,6 @@ const routerSections: RouterSection[] = [
|
||||
]
|
||||
},
|
||||
|
||||
// ============================================================
|
||||
// Database Router
|
||||
// ============================================================
|
||||
{
|
||||
name: "Database - Comment Reactions",
|
||||
description: "Add/remove reactions to comments",
|
||||
@@ -385,9 +376,6 @@ const routerSections: RouterSection[] = [
|
||||
]
|
||||
},
|
||||
|
||||
// ============================================================
|
||||
// User Router
|
||||
// ============================================================
|
||||
{
|
||||
name: "User Router",
|
||||
description: "User profile management and account operations",
|
||||
@@ -464,9 +452,6 @@ const routerSections: RouterSection[] = [
|
||||
]
|
||||
},
|
||||
|
||||
// ============================================================
|
||||
// Misc Router
|
||||
// ============================================================
|
||||
{
|
||||
name: "Misc - Downloads",
|
||||
description: "Generate signed URLs for downloadable assets",
|
||||
@@ -546,9 +531,6 @@ const routerSections: RouterSection[] = [
|
||||
]
|
||||
},
|
||||
|
||||
// ============================================================
|
||||
// Lineage Router
|
||||
// ============================================================
|
||||
{
|
||||
name: "Lineage - JSON Service",
|
||||
description: "Static game data - no authentication required",
|
||||
@@ -896,7 +878,6 @@ export default function TestPage() {
|
||||
}
|
||||
}
|
||||
|
||||
// Navigate the router path (handles nested routers like "lineage.auth")
|
||||
const routerParts = endpoint.router.split(".");
|
||||
let currentRouter: any = api;
|
||||
|
||||
@@ -914,7 +895,6 @@ export default function TestPage() {
|
||||
);
|
||||
}
|
||||
|
||||
// Call the tRPC procedure with proper method
|
||||
const data =
|
||||
endpoint.method === "query"
|
||||
? await procedure.query(input)
|
||||
@@ -997,7 +977,6 @@ export default function TestPage() {
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{/* Section Content */}
|
||||
<Show when={isExpanded()}>
|
||||
<div class="border-base space-y-4 border-t p-6">
|
||||
<For each={section.endpoints}>
|
||||
@@ -1009,7 +988,6 @@ export default function TestPage() {
|
||||
if (inputEdits()[key]) {
|
||||
return inputEdits()[key];
|
||||
}
|
||||
// Handle primitive values (string, number, boolean)
|
||||
if (typeof endpoint.sampleInput === "string") {
|
||||
return `"${endpoint.sampleInput}"`;
|
||||
}
|
||||
@@ -1019,7 +997,6 @@ export default function TestPage() {
|
||||
) {
|
||||
return String(endpoint.sampleInput);
|
||||
}
|
||||
// Handle objects and arrays
|
||||
return JSON.stringify(
|
||||
endpoint.sampleInput,
|
||||
null,
|
||||
@@ -1029,7 +1006,6 @@ export default function TestPage() {
|
||||
|
||||
return (
|
||||
<div class="bg-surface2 border-surface1 rounded-lg border p-4">
|
||||
{/* Endpoint Header */}
|
||||
<div class="mb-3 flex items-start justify-between">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -1070,7 +1046,6 @@ export default function TestPage() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Input Editor */}
|
||||
<Show when={hasInput}>
|
||||
<div class="mb-3">
|
||||
<label class="text-text mb-1 block text-xs font-semibold">
|
||||
@@ -1090,7 +1065,6 @@ export default function TestPage() {
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Error Display */}
|
||||
<Show when={errors()[key]}>
|
||||
<div class="mb-3 rounded border border-red-200 bg-red-50 p-3">
|
||||
<p class="text-sm font-semibold text-red-800">
|
||||
@@ -1102,7 +1076,6 @@ export default function TestPage() {
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Results Display */}
|
||||
<Show when={results()[key]}>
|
||||
<div class="rounded bg-gray-900 p-3">
|
||||
<p class="mb-2 text-xs font-semibold text-green-400">
|
||||
@@ -1129,7 +1102,6 @@ export default function TestPage() {
|
||||
</For>
|
||||
</div>
|
||||
|
||||
{/* Footer Instructions */}
|
||||
<div class="bg-overlay2 mt-6 rounded-lg p-6 shadow-lg">
|
||||
<h2 class="text-crust mb-4 text-2xl font-bold">Testing Guide</h2>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user