feat: implement authentication layout and pages, including login and registration forms, with validation and API integration

This commit is contained in:
2025-09-06 19:19:20 +02:00
parent e7cf76a0d8
commit c7cb3f537d
11 changed files with 572 additions and 157 deletions

View File

@@ -18,42 +18,107 @@ export const ThoughtSchema = z.object({
content: z.string(),
visibility: z.enum(["Public", "FriendsOnly", "Private"]),
replyToId: z.uuid().nullable(),
createdAt: z.string().datetime(),
createdAt: z.coerce.date(),
});
export const RegisterSchema = z.object({
username: z.string().min(3),
email: z.email(),
password: z.string().min(6),
});
export const LoginSchema = z.object({
username: z.string().min(3),
password: z.string().min(6),
});
export const CreateThoughtSchema = z.object({
content: z.string().min(1).max(128),
visibility: z.enum(["Public", "FriendsOnly", "Private"]).optional(),
replyToId: z.string().uuid().optional(),
});
export type User = z.infer<typeof UserSchema>;
export type Thought = z.infer<typeof ThoughtSchema>;
export type Register = z.infer<typeof RegisterSchema>;
export type Login = z.infer<typeof LoginSchema>;
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000";
async function apiFetch<T>(
endpoint: string,
options: RequestInit = {},
schema: z.ZodType<T>
schema: z.ZodType<T>,
token?: string | null
): Promise<T> {
const headers: Record<string, string> = {
"Content-Type": "application/json",
...(options.headers as Record<string, string>),
};
if (token) {
headers["Authorization"] = `Bearer ${token}`;
}
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
...options,
headers: {
"Content-Type": "application/json",
...options.headers,
},
headers,
});
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}`);
}
if (response.status === 204) {
return null as T;
}
const data = await response.json();
return schema.parse(data);
}
// --- User API Functions ---
export const getUserProfile = (username: string) =>
apiFetch(`/users/${username}`, {}, UserSchema);
export const registerUser = (data: z.infer<typeof RegisterSchema>) =>
apiFetch("/auth/register", {
method: "POST",
body: JSON.stringify(data),
}, UserSchema);
export const getUserThoughts = (username: string) =>
export const loginUser = (data: z.infer<typeof LoginSchema>) =>
apiFetch("/auth/login", {
method: "POST",
body: JSON.stringify(data),
}, z.object({ token: z.string() }));
export const getFeed = (token: string) =>
apiFetch(
"/feed",
{},
z.object({ thoughts: z.array(ThoughtSchema) }),
token
);
// --- User API Functions ---
export const getUserProfile = (username: string, token: string | null) =>
apiFetch(`/users/${username}`, {}, UserSchema, token);
export const getUserThoughts = (username: string, token: string | null) =>
apiFetch(
`/users/${username}/thoughts`,
{},
z.object({ thoughts: z.array(ThoughtSchema) })
z.object({ thoughts: z.array(ThoughtSchema) }),
token
);
export const createThought = (
data: z.infer<typeof CreateThoughtSchema>,
token: string
) =>
apiFetch(
"/thoughts",
{
method: "POST",
body: JSON.stringify(data),
},
ThoughtSchema,
token
);