config centralized

This commit is contained in:
Michael Freno
2026-01-01 02:22:33 -05:00
parent a6417c650f
commit 8e77727148
24 changed files with 519 additions and 143 deletions

View File

@@ -2,6 +2,7 @@ import { Title, Meta } from "@solidjs/meta";
import { HttpStatusCode } from "@solidjs/start";
import { useNavigate } from "@solidjs/router";
import { createEffect, createSignal, For } from "solid-js";
import { ERROR_PAGE_CONFIG } from "~/config";
export default function Page_401() {
const navigate = useNavigate();
@@ -24,15 +25,18 @@ export default function Page_401() {
}
setGlitchText(glitched);
setTimeout(() => setGlitchText(originalText), 100);
setTimeout(
() => setGlitchText(originalText),
ERROR_PAGE_CONFIG.GLITCH_DURATION_MS
);
}
}, 300);
}, ERROR_PAGE_CONFIG.GLITCH_INTERVAL_MS);
return () => clearInterval(glitchInterval);
});
const createParticles = () => {
return Array.from({ length: 45 }, (_, i) => ({
return Array.from({ length: ERROR_PAGE_CONFIG.PARTICLE_COUNT }, (_, i) => ({
id: i,
left: `${Math.random() * 100}%`,
top: `${Math.random() * 100}%`,

View File

@@ -12,6 +12,7 @@ import Dropzone from "~/components/blog/Dropzone";
import AddImageToS3 from "~/lib/s3upload";
import { validatePassword, isValidEmail } from "~/lib/validation";
import { TerminalSplash } from "~/components/TerminalSplash";
import { VALIDATION_CONFIG } from "~/config";
import type { UserProfile } from "~/types/user";
@@ -806,7 +807,7 @@ export default function AccountPage() {
ref={oldPasswordRef}
type={showOldPasswordInput() ? "text" : "password"}
required
minlength="8"
minlength={VALIDATION_CONFIG.MIN_PASSWORD_LENGTH}
disabled={passwordChangeLoading()}
placeholder=" "
title="Password must be at least 8 characters"
@@ -997,7 +998,7 @@ export default function AccountPage() {
ref={deleteAccountPasswordRef}
type="password"
required
minlength="8"
minlength={VALIDATION_CONFIG.MIN_PASSWORD_LENGTH}
disabled={deleteAccountButtonLoading()}
placeholder=" "
title="Enter your password to confirm account deletion"

View File

@@ -8,6 +8,7 @@ import TagSelector from "~/components/blog/TagSelector";
import PostSorting from "~/components/blog/PostSorting";
import PublishStatusToggle from "~/components/blog/PublishStatusToggle";
import { TerminalSplash } from "~/components/TerminalSplash";
import { CACHE_CONFIG } from "~/config";
const getPosts = query(async () => {
"use server";
@@ -17,11 +18,14 @@ const getPosts = query(async () => {
const event = getRequestEvent()!;
const privilegeLevel = await getPrivilegeLevel(event.nativeEvent);
return withCache(`posts-${privilegeLevel}`, 5 * 60 * 1000, async () => {
const conn = ConnectionFactory();
return withCache(
`posts-${privilegeLevel}`,
CACHE_CONFIG.BLOG_POSTS_LIST_CACHE_TTL_MS,
async () => {
const conn = ConnectionFactory();
// Fetch all posts with aggregated data
let postsQuery = `
// Fetch all posts with aggregated data
let postsQuery = `
SELECT
p.id,
p.title,
@@ -41,17 +45,17 @@ const getPosts = query(async () => {
LEFT JOIN Comment c ON p.id = c.post_id
`;
if (privilegeLevel !== "admin") {
postsQuery += ` WHERE p.published = TRUE`;
}
if (privilegeLevel !== "admin") {
postsQuery += ` WHERE p.published = TRUE`;
}
postsQuery += ` GROUP BY p.id, p.title, p.subtitle, p.body, p.banner_photo, p.date, p.published, p.category, p.author_id, p.reads, p.attachments`;
postsQuery += ` ORDER BY p.date ASC;`;
postsQuery += ` GROUP BY p.id, p.title, p.subtitle, p.body, p.banner_photo, p.date, p.published, p.category, p.author_id, p.reads, p.attachments`;
postsQuery += ` ORDER BY p.date ASC;`;
const postsResult = await conn.execute(postsQuery);
const posts = postsResult.rows;
const postsResult = await conn.execute(postsQuery);
const posts = postsResult.rows;
const tagsQuery = `
const tagsQuery = `
SELECT t.value, t.post_id
FROM Tag t
JOIN Post p ON t.post_id = p.id
@@ -59,17 +63,18 @@ const getPosts = query(async () => {
ORDER BY t.value ASC
`;
const tagsResult = await conn.execute(tagsQuery);
const tags = tagsResult.rows;
const tagsResult = await conn.execute(tagsQuery);
const tags = tagsResult.rows;
const tagMap: Record<string, number> = {};
tags.forEach((tag: any) => {
const key = `${tag.value}`;
tagMap[key] = (tagMap[key] || 0) + 1;
});
const tagMap: Record<string, number> = {};
tags.forEach((tag: any) => {
const key = `${tag.value}`;
tagMap[key] = (tagMap[key] || 0) + 1;
});
return { posts, tags, tagMap, privilegeLevel };
});
return { posts, tags, tagMap, privilegeLevel };
}
);
}, "posts");
export default function BlogIndex() {

View File

@@ -26,6 +26,12 @@ import {
TimeoutError,
APIError
} from "~/server/fetch-utils";
import {
NETWORK_CONFIG,
COOLDOWN_TIMERS,
VALIDATION_CONFIG,
COUNTDOWN_CONFIG
} from "~/config";
const getContactData = query(async () => {
"use server";
@@ -52,7 +58,7 @@ const sendContactEmail = action(async (formData: FormData) => {
message: z
.string()
.min(1, "Message is required")
.max(500, "Message too long")
.max(VALIDATION_CONFIG.MAX_CONTACT_MESSAGE_LENGTH, "Message too long")
});
try {
@@ -99,20 +105,20 @@ const sendContactEmail = action(async (formData: FormData) => {
"content-type": "application/json"
},
body: JSON.stringify(sendinblueData),
timeout: 15000
timeout: NETWORK_CONFIG.EMAIL_API_TIMEOUT_MS
});
await checkResponse(response);
return response;
},
{
maxRetries: 2,
retryDelay: 1000
maxRetries: NETWORK_CONFIG.MAX_RETRIES,
retryDelay: NETWORK_CONFIG.RETRY_DELAY_MS
}
);
// Set cooldown cookie
const exp = new Date(Date.now() + 1 * 60 * 1000);
const exp = new Date(Date.now() + COOLDOWN_TIMERS.CONTACT_REQUEST_MS);
setCookie("contactRequestSent", exp.toUTCString(), {
expires: exp,
path: "/"
@@ -418,7 +424,7 @@ export default function ContactPage() {
title="Please enter your message"
class="underlinedInput w-full bg-transparent"
rows={4}
maxlength={500}
maxlength={VALIDATION_CONFIG.MAX_CONTACT_MESSAGE_LENGTH}
/>
<span class="bar" />
<label class="underlinedInputLabel">Message</label>
@@ -456,7 +462,7 @@ export default function ContactPage() {
}
>
<CountdownCircleTimer
duration={60}
duration={COUNTDOWN_CONFIG.CONTACT_FORM_DURATION_S}
initialRemainingTime={countDown()}
size={48}
strokeWidth={6}

View File

@@ -16,6 +16,7 @@ import CountdownCircleTimer from "~/components/CountdownCircleTimer";
import { isValidEmail, validatePassword } from "~/lib/validation";
import { getClientCookie } from "~/lib/cookies.client";
import { env } from "~/env/client";
import { VALIDATION_CONFIG, COUNTDOWN_CONFIG } from "~/config";
const checkAuth = query(async () => {
"use server";
@@ -325,7 +326,7 @@ export default function LoginPage() {
};
const checkPasswordLength = (password: string) => {
if (password.length >= 8) {
if (password.length >= VALIDATION_CONFIG.MIN_PASSWORD_LENGTH) {
setPasswordLengthSufficient(true);
setShowPasswordLengthWarning(false);
} else {
@@ -628,7 +629,7 @@ export default function LoginPage() {
}
>
<CountdownCircleTimer
duration={120}
duration={COUNTDOWN_CONFIG.EMAIL_LOGIN_LINK_DURATION_S}
initialRemainingTime={countDown()}
size={48}
strokeWidth={6}

View File

@@ -6,6 +6,7 @@ import Eye from "~/components/icons/Eye";
import EyeSlash from "~/components/icons/EyeSlash";
import { validatePassword } from "~/lib/validation";
import { api } from "~/lib/api";
import { VALIDATION_CONFIG, COUNTDOWN_CONFIG } from "~/config";
export default function PasswordResetPage() {
const navigate = useNavigate();
@@ -98,7 +99,7 @@ export default function PasswordResetPage() {
};
const checkPasswordLength = (password: string) => {
if (password.length >= 8) {
if (password.length >= VALIDATION_CONFIG.MIN_PASSWORD_LENGTH) {
setPasswordLengthSufficient(true);
setShowPasswordLengthWarning(false);
} else {
@@ -223,7 +224,8 @@ export default function PasswordResetPage() {
showPasswordLengthWarning() ? "" : "opacity-0 select-none"
} text-red text-center transition-opacity duration-200 ease-in-out`}
>
Password too short! Min Length: 8
Password too short! Min Length:{" "}
{VALIDATION_CONFIG.MIN_PASSWORD_LENGTH}
</div>
{/* Password Confirmation Input */}
@@ -279,7 +281,8 @@ export default function PasswordResetPage() {
!passwordsMatch() &&
passwordLengthSufficient() &&
newPasswordConfRef &&
newPasswordConfRef.value.length >= 6
newPasswordConfRef.value.length >=
VALIDATION_CONFIG.MIN_PASSWORD_CONF_LENGTH_FOR_ERROR
? ""
: "opacity-0 select-none"
} text-red text-center transition-opacity duration-200 ease-in-out`}
@@ -307,7 +310,7 @@ export default function PasswordResetPage() {
<div class="mx-auto pt-4">
<CountdownCircleTimer
isPlaying={countDown()}
duration={5}
duration={COUNTDOWN_CONFIG.PASSWORD_RESET_SUCCESS_DURATION_S}
size={200}
strokeWidth={12}
colors="var(--color-blue)"

View File

@@ -4,6 +4,7 @@ import { Title, Meta } from "@solidjs/meta";
import CountdownCircleTimer from "~/components/CountdownCircleTimer";
import { isValidEmail } from "~/lib/validation";
import { getClientCookie } from "~/lib/cookies.client";
import { COUNTDOWN_CONFIG } from "~/config";
export default function RequestPasswordResetPage() {
const navigate = useNavigate();
@@ -176,7 +177,7 @@ export default function RequestPasswordResetPage() {
<div class="mx-auto pt-4">
<CountdownCircleTimer
isPlaying={true}
duration={300}
duration={COUNTDOWN_CONFIG.PASSWORD_RESET_DURATION_S}
initialRemainingTime={countDown()}
size={48}
strokeWidth={6}

View File

@@ -2,6 +2,7 @@ import { Title, Meta } from "@solidjs/meta";
import { createSignal, onMount, Show, For } from "solid-js";
import { isServer } from "solid-js/web";
import { TerminalSplash } from "~/components/TerminalSplash";
import { PDF_CONFIG } from "~/config";
export default function Resume() {
const [pages, setPages] = createSignal<HTMLCanvasElement[]>([]);
@@ -26,7 +27,7 @@ export default function Resume() {
// Render each page
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
const page = await pdf.getPage(pageNum);
const viewport = page.getViewport({ scale: 1.5 });
const viewport = page.getViewport({ scale: PDF_CONFIG.RENDER_SCALE });
// Create canvas
const canvas = document.createElement("canvas");