From ca55932a4117c3c4a9fcafd3f650af270d992160 Mon Sep 17 00:00:00 2001 From: counterweight Date: Thu, 18 Dec 2025 22:31:19 +0100 Subject: [PATCH] second round of review --- backend/auth.py | 7 +- backend/tests/helpers.py | 7 ++ backend/tests/test_auth.py | 7 +- backend/tests/test_counter.py | 7 +- frontend/app/auth-context.tsx | 2 +- frontend/app/config.ts | 2 + frontend/app/login/page.tsx | 106 +------------------------------ frontend/app/page.tsx | 3 +- frontend/app/signup/page.tsx | 106 +------------------------------ frontend/app/styles/auth-form.ts | 106 +++++++++++++++++++++++++++++++ 10 files changed, 124 insertions(+), 229 deletions(-) create mode 100644 backend/tests/helpers.py create mode 100644 frontend/app/config.ts create mode 100644 frontend/app/styles/auth-form.ts diff --git a/backend/auth.py b/backend/auth.py index bd04d16..5badff6 100644 --- a/backend/auth.py +++ b/backend/auth.py @@ -18,14 +18,13 @@ ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7 # 7 days COOKIE_NAME = "auth_token" -class UserCreate(BaseModel): +class UserCredentials(BaseModel): email: EmailStr password: str -class UserLogin(BaseModel): - email: EmailStr - password: str +UserCreate = UserCredentials +UserLogin = UserCredentials class UserResponse(BaseModel): diff --git a/backend/tests/helpers.py b/backend/tests/helpers.py new file mode 100644 index 0000000..8c11693 --- /dev/null +++ b/backend/tests/helpers.py @@ -0,0 +1,7 @@ +import uuid + + +def unique_email(prefix: str = "test") -> str: + """Generate a unique email for tests sharing the same database.""" + return f"{prefix}-{uuid.uuid4().hex[:8]}@example.com" + diff --git a/backend/tests/test_auth.py b/backend/tests/test_auth.py index edc2f98..8909784 100644 --- a/backend/tests/test_auth.py +++ b/backend/tests/test_auth.py @@ -1,12 +1,7 @@ import pytest -import uuid from auth import COOKIE_NAME - - -def unique_email(prefix: str = "test") -> str: - """Generate a unique email for tests sharing the same database.""" - return f"{prefix}-{uuid.uuid4().hex[:8]}@example.com" +from tests.helpers import unique_email # Registration tests diff --git a/backend/tests/test_counter.py b/backend/tests/test_counter.py index 20f1690..080c7b9 100644 --- a/backend/tests/test_counter.py +++ b/backend/tests/test_counter.py @@ -1,12 +1,7 @@ import pytest -import uuid from auth import COOKIE_NAME - - -def unique_email(prefix: str = "counter") -> str: - """Generate a unique email for tests sharing the same database.""" - return f"{prefix}-{uuid.uuid4().hex[:8]}@example.com" +from tests.helpers import unique_email # Protected endpoint tests - without auth diff --git a/frontend/app/auth-context.tsx b/frontend/app/auth-context.tsx index 2e8c79c..b1bef54 100644 --- a/frontend/app/auth-context.tsx +++ b/frontend/app/auth-context.tsx @@ -2,7 +2,7 @@ import { createContext, useContext, useState, useEffect, ReactNode } from "react"; -const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"; +import { API_URL } from "./config"; interface User { id: number; diff --git a/frontend/app/config.ts b/frontend/app/config.ts new file mode 100644 index 0000000..9fc6e9a --- /dev/null +++ b/frontend/app/config.ts @@ -0,0 +1,2 @@ +export const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"; + diff --git a/frontend/app/login/page.tsx b/frontend/app/login/page.tsx index 507892a..ab76967 100644 --- a/frontend/app/login/page.tsx +++ b/frontend/app/login/page.tsx @@ -3,6 +3,7 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; import { useAuth } from "../auth-context"; +import { authFormStyles as styles } from "../styles/auth-form"; export default function LoginPage() { const [email, setEmail] = useState(""); @@ -88,108 +89,3 @@ export default function LoginPage() { ); } - -const styles: Record = { - main: { - minHeight: "100vh", - background: "linear-gradient(135deg, #0f0f23 0%, #1a1a3e 50%, #2d1b4e 100%)", - display: "flex", - alignItems: "center", - justifyContent: "center", - padding: "1rem", - }, - container: { - width: "100%", - maxWidth: "420px", - }, - card: { - background: "rgba(255, 255, 255, 0.03)", - backdropFilter: "blur(10px)", - border: "1px solid rgba(255, 255, 255, 0.08)", - borderRadius: "24px", - padding: "3rem 2.5rem", - boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)", - }, - header: { - textAlign: "center" as const, - marginBottom: "2.5rem", - }, - title: { - fontFamily: "'Instrument Serif', Georgia, serif", - fontSize: "2.5rem", - fontWeight: 400, - color: "#fff", - margin: 0, - letterSpacing: "-0.02em", - }, - subtitle: { - fontFamily: "'DM Sans', system-ui, sans-serif", - color: "rgba(255, 255, 255, 0.5)", - marginTop: "0.5rem", - fontSize: "0.95rem", - }, - form: { - display: "flex", - flexDirection: "column" as const, - gap: "1.5rem", - }, - field: { - display: "flex", - flexDirection: "column" as const, - gap: "0.5rem", - }, - label: { - fontFamily: "'DM Sans', system-ui, sans-serif", - color: "rgba(255, 255, 255, 0.7)", - fontSize: "0.875rem", - fontWeight: 500, - }, - input: { - fontFamily: "'DM Sans', system-ui, sans-serif", - padding: "0.875rem 1rem", - fontSize: "1rem", - background: "rgba(255, 255, 255, 0.05)", - border: "1px solid rgba(255, 255, 255, 0.1)", - borderRadius: "12px", - color: "#fff", - outline: "none", - transition: "border-color 0.2s, box-shadow 0.2s", - }, - button: { - fontFamily: "'DM Sans', system-ui, sans-serif", - marginTop: "0.5rem", - padding: "1rem", - fontSize: "1rem", - fontWeight: 600, - background: "linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)", - color: "#fff", - border: "none", - borderRadius: "12px", - cursor: "pointer", - transition: "transform 0.2s, box-shadow 0.2s", - boxShadow: "0 4px 14px rgba(99, 102, 241, 0.4)", - }, - error: { - fontFamily: "'DM Sans', system-ui, sans-serif", - padding: "0.875rem 1rem", - background: "rgba(239, 68, 68, 0.1)", - border: "1px solid rgba(239, 68, 68, 0.3)", - borderRadius: "12px", - color: "#fca5a5", - fontSize: "0.875rem", - textAlign: "center" as const, - }, - footer: { - fontFamily: "'DM Sans', system-ui, sans-serif", - textAlign: "center" as const, - marginTop: "2rem", - color: "rgba(255, 255, 255, 0.5)", - fontSize: "0.875rem", - }, - link: { - color: "#a78bfa", - textDecoration: "none", - fontWeight: 500, - }, -}; - diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 718d68f..aff961b 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -3,8 +3,7 @@ import { useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import { useAuth } from "./auth-context"; - -const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"; +import { API_URL } from "./config"; export default function Home() { const [count, setCount] = useState(null); diff --git a/frontend/app/signup/page.tsx b/frontend/app/signup/page.tsx index 439b61b..a5a9e17 100644 --- a/frontend/app/signup/page.tsx +++ b/frontend/app/signup/page.tsx @@ -3,6 +3,7 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; import { useAuth } from "../auth-context"; +import { authFormStyles as styles } from "../styles/auth-form"; export default function SignupPage() { const [email, setEmail] = useState(""); @@ -113,108 +114,3 @@ export default function SignupPage() { ); } - -const styles: Record = { - main: { - minHeight: "100vh", - background: "linear-gradient(135deg, #0f0f23 0%, #1a1a3e 50%, #2d1b4e 100%)", - display: "flex", - alignItems: "center", - justifyContent: "center", - padding: "1rem", - }, - container: { - width: "100%", - maxWidth: "420px", - }, - card: { - background: "rgba(255, 255, 255, 0.03)", - backdropFilter: "blur(10px)", - border: "1px solid rgba(255, 255, 255, 0.08)", - borderRadius: "24px", - padding: "3rem 2.5rem", - boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)", - }, - header: { - textAlign: "center" as const, - marginBottom: "2.5rem", - }, - title: { - fontFamily: "'Instrument Serif', Georgia, serif", - fontSize: "2.5rem", - fontWeight: 400, - color: "#fff", - margin: 0, - letterSpacing: "-0.02em", - }, - subtitle: { - fontFamily: "'DM Sans', system-ui, sans-serif", - color: "rgba(255, 255, 255, 0.5)", - marginTop: "0.5rem", - fontSize: "0.95rem", - }, - form: { - display: "flex", - flexDirection: "column" as const, - gap: "1.5rem", - }, - field: { - display: "flex", - flexDirection: "column" as const, - gap: "0.5rem", - }, - label: { - fontFamily: "'DM Sans', system-ui, sans-serif", - color: "rgba(255, 255, 255, 0.7)", - fontSize: "0.875rem", - fontWeight: 500, - }, - input: { - fontFamily: "'DM Sans', system-ui, sans-serif", - padding: "0.875rem 1rem", - fontSize: "1rem", - background: "rgba(255, 255, 255, 0.05)", - border: "1px solid rgba(255, 255, 255, 0.1)", - borderRadius: "12px", - color: "#fff", - outline: "none", - transition: "border-color 0.2s, box-shadow 0.2s", - }, - button: { - fontFamily: "'DM Sans', system-ui, sans-serif", - marginTop: "0.5rem", - padding: "1rem", - fontSize: "1rem", - fontWeight: 600, - background: "linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)", - color: "#fff", - border: "none", - borderRadius: "12px", - cursor: "pointer", - transition: "transform 0.2s, box-shadow 0.2s", - boxShadow: "0 4px 14px rgba(99, 102, 241, 0.4)", - }, - error: { - fontFamily: "'DM Sans', system-ui, sans-serif", - padding: "0.875rem 1rem", - background: "rgba(239, 68, 68, 0.1)", - border: "1px solid rgba(239, 68, 68, 0.3)", - borderRadius: "12px", - color: "#fca5a5", - fontSize: "0.875rem", - textAlign: "center" as const, - }, - footer: { - fontFamily: "'DM Sans', system-ui, sans-serif", - textAlign: "center" as const, - marginTop: "2rem", - color: "rgba(255, 255, 255, 0.5)", - fontSize: "0.875rem", - }, - link: { - color: "#a78bfa", - textDecoration: "none", - fontWeight: 500, - }, -}; - diff --git a/frontend/app/styles/auth-form.ts b/frontend/app/styles/auth-form.ts new file mode 100644 index 0000000..c3cfc98 --- /dev/null +++ b/frontend/app/styles/auth-form.ts @@ -0,0 +1,106 @@ +import { CSSProperties } from "react"; + +export const authFormStyles: Record = { + main: { + minHeight: "100vh", + background: "linear-gradient(135deg, #0f0f23 0%, #1a1a3e 50%, #2d1b4e 100%)", + display: "flex", + alignItems: "center", + justifyContent: "center", + padding: "1rem", + }, + container: { + width: "100%", + maxWidth: "420px", + }, + card: { + background: "rgba(255, 255, 255, 0.03)", + backdropFilter: "blur(10px)", + border: "1px solid rgba(255, 255, 255, 0.08)", + borderRadius: "24px", + padding: "3rem 2.5rem", + boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)", + }, + header: { + textAlign: "center" as const, + marginBottom: "2.5rem", + }, + title: { + fontFamily: "'Instrument Serif', Georgia, serif", + fontSize: "2.5rem", + fontWeight: 400, + color: "#fff", + margin: 0, + letterSpacing: "-0.02em", + }, + subtitle: { + fontFamily: "'DM Sans', system-ui, sans-serif", + color: "rgba(255, 255, 255, 0.5)", + marginTop: "0.5rem", + fontSize: "0.95rem", + }, + form: { + display: "flex", + flexDirection: "column" as const, + gap: "1.5rem", + }, + field: { + display: "flex", + flexDirection: "column" as const, + gap: "0.5rem", + }, + label: { + fontFamily: "'DM Sans', system-ui, sans-serif", + color: "rgba(255, 255, 255, 0.7)", + fontSize: "0.875rem", + fontWeight: 500, + }, + input: { + fontFamily: "'DM Sans', system-ui, sans-serif", + padding: "0.875rem 1rem", + fontSize: "1rem", + background: "rgba(255, 255, 255, 0.05)", + border: "1px solid rgba(255, 255, 255, 0.1)", + borderRadius: "12px", + color: "#fff", + outline: "none", + transition: "border-color 0.2s, box-shadow 0.2s", + }, + button: { + fontFamily: "'DM Sans', system-ui, sans-serif", + marginTop: "0.5rem", + padding: "1rem", + fontSize: "1rem", + fontWeight: 600, + background: "linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)", + color: "#fff", + border: "none", + borderRadius: "12px", + cursor: "pointer", + transition: "transform 0.2s, box-shadow 0.2s", + boxShadow: "0 4px 14px rgba(99, 102, 241, 0.4)", + }, + error: { + fontFamily: "'DM Sans', system-ui, sans-serif", + padding: "0.875rem 1rem", + background: "rgba(239, 68, 68, 0.1)", + border: "1px solid rgba(239, 68, 68, 0.3)", + borderRadius: "12px", + color: "#fca5a5", + fontSize: "0.875rem", + textAlign: "center" as const, + }, + footer: { + fontFamily: "'DM Sans', system-ui, sans-serif", + textAlign: "center" as const, + marginTop: "2rem", + color: "rgba(255, 255, 255, 0.5)", + fontSize: "0.875rem", + }, + link: { + color: "#a78bfa", + textDecoration: "none", + fontWeight: 500, + }, +}; +