config centralized
This commit is contained in:
@@ -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}%`,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user