add auth to backend and frontend

This commit is contained in:
2026-02-24 10:42:36 -05:00
parent 11eb192e2a
commit 0d5cc4d21f
14 changed files with 336 additions and 35 deletions
+1 -1
View File
@@ -20,7 +20,7 @@
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/node": "^25.2.3",
"@types/react": "^19.2.7",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"eslint": "^9.39.1",
+16 -13
View File
@@ -6,18 +6,21 @@ import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
rules: {
'react-refresh/only-export-components': 'off',
},
},
},
])
+1 -1
View File
@@ -25,7 +25,7 @@
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/node": "^25.2.3",
"@types/react": "^19.2.7",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"eslint": "^9.39.1",
+14 -12
View File
@@ -4,7 +4,7 @@ import LatestTemperature from "@/components/latest-temperature"
import TemperatureHistory from "@/components/temperature-history"
import TemperaturePlot from "@/components/temperature-plot"
import RegisterForm from './components/register-form'
// import { Button } from "@/components/ui/button"
import { AuthProvider } from './contexts/auth-context'
@@ -12,18 +12,20 @@ import RegisterForm from './components/register-form'
function App() {
return (
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<main className="min-h-screen bg-background p-6 w-full">
<div>
<RegisterForm />
</div>
<div className="w-full mx-auto space-y-2">
<TemperaturePlot />
<div className="grid grid-cols-4 gap-6">
<LatestTemperature />
<TemperatureHistory />
<AuthProvider>
<main className="min-h-screen bg-background p-6 w-full">
<div>
<RegisterForm />
</div>
</div>
</main>
<div className="w-full mx-auto space-y-2">
<TemperaturePlot />
<div className="grid grid-cols-4 gap-6">
<LatestTemperature />
<TemperatureHistory />
</div>
</div>
</main>
</AuthProvider>
</ThemeProvider>
)
}
+81
View File
@@ -0,0 +1,81 @@
import {
createContext,
useState,
useEffect,
} from "react"
import type { ReactNode } from "react"
type User = {
id: number
email: string
create_at: string
}
type AuthContextType = {
user: User | null
isAuthenticated: boolean
login: (email: string, passwor: string) => Promise<void>
logout: () => void
isLoading: boolean
}
export const AuthContext = createContext<AuthContextType | null>(null)
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [isLoading, setIsLoading] = useState(true)
useEffect(() => {
checkAuth()
}, [])
async function checkAuth() {
try {
const res = await fetch("/api/me", {
credentials: "include",
})
if (res.ok) {
const data = await res.json()
setUser(data.user)
}
}
catch {
setUser(null)
}
finally {
setIsLoading(false)
}
}
async function login(email: string, password: string) {
const res = await fetch("/api/login", {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password })
})
if (!res.ok) {
const msg = await res.text()
throw new Error(msg || "Invalid credentials")
}
const data = await res.json()
setUser(data.user)
}
async function logout() {
await fetch("api/logout", {
method: "POST",
credentials: "include",
})
setUser(null)
}
return (
<AuthContext.Provider value={{ user, isAuthenticated: !!user, login, logout, isLoading }}>
{children}
</AuthContext.Provider>
)
}
+8
View File
@@ -0,0 +1,8 @@
import { AuthContext } from "@/contexts/auth-context"
import { useContext } from "react"
export function useAuth() {
const ctx = useContext(AuthContext)
if (!ctx) throw new Error("useAuth must be used within AuthProvider")
return ctx
}