From 4eba6fe4768212aab3fc5344db5674f528a9afe2 Mon Sep 17 00:00:00 2001 From: JCBSComputer Date: Tue, 30 Jun 2026 16:48:59 +0200 Subject: [PATCH] Fixed Themes not showing --- Web_Backgrounds/cyberpunk-theme.css | 40 ++++++++++++++++++++++ Web_Backgrounds/spidey-theme.css | 1 + src/app/api/auth/login/route.ts | 27 +++++++++++---- src/app/login/page.tsx | 18 ++++++++++ src/components/settings/theme-settings.tsx | 1 + src/lib/rate-limit.ts | 31 +++++++++++++++++ src/providers/website-theme-provider.tsx | 1 + 7 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 src/lib/rate-limit.ts diff --git a/Web_Backgrounds/cyberpunk-theme.css b/Web_Backgrounds/cyberpunk-theme.css index fc413b9..9c9e438 100644 --- a/Web_Backgrounds/cyberpunk-theme.css +++ b/Web_Backgrounds/cyberpunk-theme.css @@ -929,3 +929,43 @@ .dark.theme-cyberpunk .dark\:text-\[\#666666\].text-xs.font-semibold.tracking-widest.uppercase { color: hsl(210, 20%, 88%) !important; } + +/* ============================================================================ + Pipeline Color Bars — Cyberpunk neon pink/cyan + ============================================================================ */ +.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(odd) .group .h-1.w-full { + background: linear-gradient(to right, hsl(330, 100%, 50%), hsl(330, 100%, 65%), hsl(330, 100%, 50%)) !important; + box-shadow: 0 0 8px rgba(255, 0, 127, 0.3) !important; +} +.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(even) .group .h-1.w-full { + background: linear-gradient(to right, hsl(180, 100%, 50%), hsl(180, 100%, 65%), hsl(180, 100%, 50%)) !important; + box-shadow: 0 0 8px rgba(0, 240, 255, 0.3) !important; +} +.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(odd) .group .rounded-xl.shrink-0 { + background: hsla(330, 100%, 50%, 0.12) !important; +} +.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(even) .group .rounded-xl.shrink-0 { + background: hsla(180, 100%, 50%, 0.1) !important; +} +.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(odd) .group .rounded-xl.shrink-0 svg { + color: hsl(330, 100%, 50%) !important; +} +.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(even) .group .rounded-xl.shrink-0 svg { + color: hsl(180, 100%, 50%) !important; +} +.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(odd) .group .absolute.bottom-0.h-1 { + background: linear-gradient(to right, transparent, hsla(330, 100%, 50%, 0.3), transparent) !important; +} +.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 > .relative:nth-child(even) .group .absolute.bottom-0.h-1 { + background: linear-gradient(to right, transparent, hsla(180, 100%, 50%, 0.25), transparent) !important; +} +.dark.theme-cyberpunk .sm\:grid-cols-2.lg\:grid-cols-3.gap-4.mt-4 .rounded-full.bg-gradient-to-r { + background: linear-gradient(to right, hsl(330, 100%, 50%), hsl(180, 100%, 50%)) !important; +} + +.dark.theme-cyberpunk .growth-word, +.light.theme-cyberpunk .growth-word { + -webkit-text-fill-color: #1BB0CE !important; + color: #1BB0CE !important; + background: none !important; +} diff --git a/Web_Backgrounds/spidey-theme.css b/Web_Backgrounds/spidey-theme.css index fe07e93..63c5a63 100644 --- a/Web_Backgrounds/spidey-theme.css +++ b/Web_Backgrounds/spidey-theme.css @@ -1,4 +1,5 @@ @import url("./cosmic-theme.css"); +@import url("./cyberpunk-theme.css"); @import url("./cyber2-theme.css"); @import url("./pumpkin-theme.css"); @import url("./bw-theme.css"); diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index ea1cd8a..8b3d95c 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -12,16 +12,34 @@ import { setSessionContext, SESSION_COOKIE, } from "@/lib/auth" +import { checkRateLimit } from "@/lib/rate-limit" -function jsonResponse(data: unknown, status: number) { +function jsonResponse(data: unknown, status: number, headers?: Record) { return new Response(JSON.stringify(data), { status, - headers: { "Content-Type": "application/json" }, + headers: { "Content-Type": "application/json", ...headers }, }) } export async function POST(request: NextRequest) { try { + const ipAddress = + request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || + request.headers.get("x-real-ip") || + "127.0.0.1" + + const rateCheck = checkRateLimit(`login:${ipAddress}`, 10, 60000) + if (!rateCheck.allowed) { + return jsonResponse( + { error: "Too many login attempts. Try again in 60 seconds." }, + 429, + { + "Retry-After": String(Math.ceil((rateCheck.resetAt - Date.now()) / 1000)), + "X-RateLimit-Remaining": "0", + }, + ) + } + const { email, username, password } = await request.json() const credential = email || username @@ -40,11 +58,6 @@ export async function POST(request: NextRequest) { ) } - const ipAddress = - request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || - request.headers.get("x-real-ip") || - "127.0.0.1" - const userAgent = request.headers.get("user-agent") || null // Try to find user by email first, then by username diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 587932b..d730956 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -34,9 +34,19 @@ function LoginForm() { const [remember, setRemember] = useState(false) const [error, setError] = useState("") const [counts, setCounts] = useState({ leads: 0, conversion: 0, users: 0 }) + const [checkingSession, setCheckingSession] = useState(true) const counterStarted = useRef(false) const [testimonialIndex, setTestimonialIndex] = useState(0) + useEffect(() => { + fetch("/api/auth/me", { credentials: "include" }) + .then((r) => { + if (r.ok) router.push("/dashboard") + else setCheckingSession(false) + }) + .catch(() => setCheckingSession(false)) + }, [router]) + const testimonials = [ { text: "This CRM transformed how we manage our sales pipeline. We've seen a 40% increase in lead conversion.", @@ -240,6 +250,14 @@ function LoginForm() { } } + if (checkingSession) { + return ( +
+
+
+ ) + } + return (
diff --git a/src/components/settings/theme-settings.tsx b/src/components/settings/theme-settings.tsx index 8c874a4..b487e3a 100644 --- a/src/components/settings/theme-settings.tsx +++ b/src/components/settings/theme-settings.tsx @@ -34,6 +34,7 @@ const backgroundOptions = [ { value: "default", label: "None", icon: Shield, color: "bg-gray-400", ring: "ring-gray-400", desc: "No background theme" }, { value: "spidey", label: "Spidey", icon: Shield, color: "bg-red-600", ring: "ring-red-600", desc: "Dark theme with red accents" }, { value: "cosmic", label: "Cosmic", icon: Shield, color: "bg-gradient-to-r from-purple-500 to-green-500", ring: "ring-purple-500", desc: "Deep space with purple nebula and cyan accents" }, + { value: "cyberpunk", label: "Cyberpunk", icon: Shield, color: "bg-gradient-to-r from-pink-500 to-cyan-400", ring: "ring-pink-500", desc: "Dark retro-futuristic synthwave aesthetic" }, { value: "cyber2", label: "Cyber2.0", icon: Shield, color: "bg-gradient-to-r from-cyan-400 to-fuchsia-500", ring: "ring-cyan-400", desc: "Neon grid with magenta and cyan glow" }, { value: "pumpkin", label: "Pumpkin Spice", icon: Shield, color: "bg-gradient-to-r from-orange-600 to-amber-500", ring: "ring-orange-600", desc: "Warm autumn orange with spicy accents" }, { value: "bw", label: "Black & White", icon: Shield, color: "bg-gradient-to-r from-gray-900 to-gray-400", ring: "ring-gray-500", desc: "Clean monochrome with subtle texture" }, diff --git a/src/lib/rate-limit.ts b/src/lib/rate-limit.ts new file mode 100644 index 0000000..7e3db9c --- /dev/null +++ b/src/lib/rate-limit.ts @@ -0,0 +1,31 @@ +const attempts = new Map() + +export function checkRateLimit( + key: string, + maxAttempts = 5, + windowMs = 60000, +): { allowed: boolean; remaining: number; resetAt: number } { + const now = Date.now() + const entry = attempts.get(key) + + if (!entry || now > entry.resetAt) { + attempts.set(key, { count: 1, resetAt: now + windowMs }) + return { allowed: true, remaining: maxAttempts - 1, resetAt: now + windowMs } + } + + entry.count++ + + if (entry.count > maxAttempts) { + return { allowed: false, remaining: 0, resetAt: entry.resetAt } + } + + return { allowed: true, remaining: maxAttempts - entry.count, resetAt: entry.resetAt } +} + +// Cleanup stale entries every 5 minutes +setInterval(() => { + const now = Date.now() + for (const [key, entry] of attempts) { + if (now > entry.resetAt) attempts.delete(key) + } +}, 300000) diff --git a/src/providers/website-theme-provider.tsx b/src/providers/website-theme-provider.tsx index 4130485..b08d06d 100644 --- a/src/providers/website-theme-provider.tsx +++ b/src/providers/website-theme-provider.tsx @@ -7,6 +7,7 @@ const WEBSITE_THEME_KEY = "crm-website-theme" const themeClasses: Record = { spidey: "theme-spidey", cosmic: "theme-cosmic", + cyberpunk: "theme-cyberpunk", cyber2: "theme-cyber2", pumpkin: "theme-pumpkin", bw: "theme-bw",