auth checkpoint

auth dependecies not instaled in dev container
This commit is contained in:
dimitar committed 2026-02-04 19:24:03 +01:00
1 parent 7378d37b36
commit 42002f8e6f
50 files changed
+3680 -636

No files matched your search

+279 -41
View File
@@ -4,6 +4,42 @@ const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000/api/
console.log('API_BASE_URL:', API_BASE_URL);
console.log('VITE_API_URL env:', import.meta.env.VITE_API_URL);
// Helper function to get auth headers
function getAuthHeaders(): HeadersInit {
const token = localStorage.getItem('token');
const headers: HeadersInit = {
'Content-Type': 'application/json',
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
return headers;
}
// Enhanced fetch wrapper
async function authFetch(url: string, options: RequestInit = {}): Promise<Response> {
const headers = getAuthHeaders();
const response = await fetch(url, {
...options,
headers: {
...headers,
...options.headers,
},
});
// Handle 401 unauthorized
if (response.status === 401) {
localStorage.removeItem('token');
localStorage.removeItem('user');
window.location.href = '/auth';
}
return response;
}
export interface Article {
id: string;
title: string;
@@ -110,7 +146,7 @@ export async function fetchArticles(params: FindArticlesParams = {}): Promise<Ar
const url = `${API_BASE_URL}/articles?${searchParams}`;
console.log('Fetching from:', url);
const response = await fetch(url);
const response = await authFetch(url);
console.log('Response status:', response.status, 'ok:', response.ok);
if (!response.ok) {
@@ -123,7 +159,7 @@ export async function fetchArticles(params: FindArticlesParams = {}): Promise<Ar
}
export async function fetchArticleBySlug(slug: string): Promise<Article> {
const response = await fetch(`${API_BASE_URL}/articles/slug/${slug}`);
const response = await authFetch(`${API_BASE_URL}/articles/slug/${slug}`);
if (!response.ok) {
throw new Error('Failed to fetch article');
}
@@ -131,7 +167,7 @@ export async function fetchArticleBySlug(slug: string): Promise<Article> {
}
export async function fetchArticleById(id: string): Promise<Article> {
const response = await fetch(`${API_BASE_URL}/articles/${id}`);
const response = await authFetch(`${API_BASE_URL}/articles/${id}`);
if (!response.ok) {
throw new Error('Failed to fetch article');
}
@@ -139,11 +175,8 @@ export async function fetchArticleById(id: string): Promise<Article> {
}
export async function createArticle(dto: CreateArticleDto): Promise<Article> {
const response = await fetch(`${API_BASE_URL}/articles`, {
const response = await authFetch(`${API_BASE_URL}/articles`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(dto),
});
if (!response.ok) {
@@ -153,11 +186,8 @@ export async function createArticle(dto: CreateArticleDto): Promise<Article> {
}
export async function updateArticle(id: string, dto: UpdateArticleDto): Promise<Article> {
const response = await fetch(`${API_BASE_URL}/articles/${id}`, {
const response = await authFetch(`${API_BASE_URL}/articles/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(dto),
});
if (!response.ok) {
@@ -167,7 +197,7 @@ export async function updateArticle(id: string, dto: UpdateArticleDto): Promise<
}
export async function deleteArticle(id: string): Promise<void> {
const response = await fetch(`${API_BASE_URL}/articles/${id}`, {
const response = await authFetch(`${API_BASE_URL}/articles/${id}`, {
method: 'DELETE',
});
if (!response.ok) {
@@ -176,7 +206,7 @@ export async function deleteArticle(id: string): Promise<void> {
}
export async function archiveArticle(id: string): Promise<Article> {
const response = await fetch(`${API_BASE_URL}/articles/${id}/archive`, {
const response = await authFetch(`${API_BASE_URL}/articles/${id}/archive`, {
method: 'PATCH',
});
if (!response.ok) {
@@ -186,7 +216,7 @@ export async function archiveArticle(id: string): Promise<Article> {
}
export async function publishArticle(id: string, status: 'draft' | 'published' = 'published'): Promise<Article> {
const response = await fetch(`${API_BASE_URL}/articles/${id}/publish?status=${status}`, {
const response = await authFetch(`${API_BASE_URL}/articles/${id}/publish?status=${status}`, {
method: 'PATCH',
});
if (!response.ok) {
@@ -323,7 +353,7 @@ export async function fetchLiveBlogs(params: FindLiveBlogsParams = {}): Promise<
}
});
const response = await fetch(`${API_BASE_URL}/live-blogs?${searchParams}`);
const response = await authFetch(`${API_BASE_URL}/live-blogs?${searchParams}`);
if (!response.ok) {
throw new Error('Failed to fetch live blogs');
}
@@ -331,7 +361,7 @@ export async function fetchLiveBlogs(params: FindLiveBlogsParams = {}): Promise<
}
export async function fetchLiveBlogBySlug(slug: string): Promise<LiveBlog> {
const response = await fetch(`${API_BASE_URL}/live-blogs/slug/${slug}`);
const response = await authFetch(`${API_BASE_URL}/live-blogs/slug/${slug}`);
if (!response.ok) {
throw new Error('Failed to fetch live blog');
}
@@ -339,7 +369,7 @@ export async function fetchLiveBlogBySlug(slug: string): Promise<LiveBlog> {
}
export async function fetchLiveBlogById(id: string): Promise<LiveBlog> {
const response = await fetch(`${API_BASE_URL}/live-blogs/${id}`);
const response = await authFetch(`${API_BASE_URL}/live-blogs/${id}`);
if (!response.ok) {
throw new Error('Failed to fetch live blog');
}
@@ -351,7 +381,7 @@ export async function fetchLiveBlogUpdates(
page = 1,
limit = 50
): Promise<LiveBlogUpdatesResponse> {
const response = await fetch(
const response = await authFetch(
`${API_BASE_URL}/live-blogs/${liveBlogId}/updates?page=${page}&limit=${limit}`
);
if (!response.ok) {
@@ -361,7 +391,7 @@ export async function fetchLiveBlogUpdates(
}
export async function fetchRecentLiveBlogs(): Promise<LiveBlog[]> {
const response = await fetch(`${API_BASE_URL}/live-blogs/recent`);
const response = await authFetch(`${API_BASE_URL}/live-blogs/recent`);
if (!response.ok) {
throw new Error('Failed to fetch recent live blogs');
}
@@ -369,7 +399,7 @@ export async function fetchRecentLiveBlogs(): Promise<LiveBlog[]> {
}
export async function fetchPinnedLiveBlogs(): Promise<LiveBlog[]> {
const response = await fetch(`${API_BASE_URL}/live-blogs/featured`);
const response = await authFetch(`${API_BASE_URL}/live-blogs/featured`);
if (!response.ok) {
throw new Error('Failed to fetch pinned live blogs');
}
@@ -377,7 +407,7 @@ export async function fetchPinnedLiveBlogs(): Promise<LiveBlog[]> {
}
export async function fetchActiveLiveBlogs(): Promise<LiveBlog[]> {
const response = await fetch(`${API_BASE_URL}/live-blogs/active`);
const response = await authFetch(`${API_BASE_URL}/live-blogs/active`);
if (!response.ok) {
throw new Error('Failed to fetch active live blogs');
}
@@ -389,11 +419,8 @@ export async function createLiveBlogUpdate(
liveBlogId: string,
dto: CreateLiveBlogUpdateDto
): Promise<LiveBlogUpdate> {
const response = await fetch(`${API_BASE_URL}/live-blogs/${liveBlogId}/updates`, {
const response = await authFetch(`${API_BASE_URL}/live-blogs/${liveBlogId}/updates`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(dto),
});
if (!response.ok) {
@@ -407,11 +434,8 @@ export async function updateLiveBlogUpdate(
updateId: string,
dto: UpdateLiveBlogUpdateDto
): Promise<LiveBlogUpdate> {
const response = await fetch(`${API_BASE_URL}/live-blogs/${liveBlogId}/updates/${updateId}`, {
const response = await authFetch(`${API_BASE_URL}/live-blogs/${liveBlogId}/updates/${updateId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(dto),
});
if (!response.ok) {
@@ -421,7 +445,7 @@ export async function updateLiveBlogUpdate(
}
export async function deleteLiveBlogUpdate(liveBlogId: string, updateId: string): Promise<void> {
const response = await fetch(`${API_BASE_URL}/live-blogs/${liveBlogId}/updates/${updateId}`, {
const response = await authFetch(`${API_BASE_URL}/live-blogs/${liveBlogId}/updates/${updateId}`, {
method: 'DELETE',
});
if (!response.ok) {
@@ -430,11 +454,8 @@ export async function deleteLiveBlogUpdate(liveBlogId: string, updateId: string)
}
export async function createLiveBlog(dto: CreateLiveBlogDto): Promise<LiveBlog> {
const response = await fetch(`${API_BASE_URL}/live-blogs`, {
const response = await authFetch(`${API_BASE_URL}/live-blogs`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(dto),
});
if (!response.ok) {
@@ -444,11 +465,8 @@ export async function createLiveBlog(dto: CreateLiveBlogDto): Promise<LiveBlog>
}
export async function updateLiveBlog(id: string, dto: UpdateLiveBlogDto): Promise<LiveBlog> {
const response = await fetch(`${API_BASE_URL}/live-blogs/${id}`, {
const response = await authFetch(`${API_BASE_URL}/live-blogs/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(dto),
});
if (!response.ok) {
@@ -458,7 +476,7 @@ export async function updateLiveBlog(id: string, dto: UpdateLiveBlogDto): Promis
}
export async function deleteLiveBlog(id: string): Promise<void> {
const response = await fetch(`${API_BASE_URL}/live-blogs/${id}`, {
const response = await authFetch(`${API_BASE_URL}/live-blogs/${id}`, {
method: 'DELETE',
});
if (!response.ok) {
@@ -467,7 +485,7 @@ export async function deleteLiveBlog(id: string): Promise<void> {
}
export async function archiveLiveBlog(id: string): Promise<LiveBlog> {
const response = await fetch(`${API_BASE_URL}/live-blogs/${id}/archive`, {
const response = await authFetch(`${API_BASE_URL}/live-blogs/${id}/archive`, {
method: 'PATCH',
});
if (!response.ok) {
@@ -477,7 +495,7 @@ export async function archiveLiveBlog(id: string): Promise<LiveBlog> {
}
export async function publishLiveBlog(id: string, status: 'draft' | 'live' | 'ended' = 'draft'): Promise<LiveBlog> {
const response = await fetch(`${API_BASE_URL}/live-blogs/${id}/publish?status=${status}`, {
const response = await authFetch(`${API_BASE_URL}/live-blogs/${id}/publish?status=${status}`, {
method: 'PATCH',
});
if (!response.ok) {
@@ -485,3 +503,223 @@ export async function publishLiveBlog(id: string, status: 'draft' | 'live' | 'en
}
return response.json();
}
// Auth Types
import type { User, LoginDto, RegisterDto, AuthResponse } from '@/types';
// Auth API Functions
export async function login(dto: LoginDto): Promise<AuthResponse> {
const response = await fetch(`${API_BASE_URL}/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(dto),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Login failed');
}
return response.json();
}
export async function register(dto: RegisterDto): Promise<AuthResponse> {
const response = await fetch(`${API_BASE_URL}/auth/register`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(dto),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Registration failed');
}
return response.json();
}
export async function getProfile(): Promise<User> {
const response = await authFetch(`${API_BASE_URL}/users/profile`, {
method: 'GET',
});
if (!response.ok) {
throw new Error('Failed to fetch profile');
}
return response.json();
}
export async function logout(): Promise<void> {
localStorage.removeItem('token');
localStorage.removeItem('user');
}
// Comment Types
export interface Comment {
id: string;
content: string;
articleId: string | null;
liveBlogId: string | null;
parentCommentId: string | null;
userId: string;
isVisible: boolean;
user?: {
id: string;
username: string;
email: string;
role: string;
};
reactions?: {
likes: number;
dislikes: number;
};
createdAt: string;
updatedAt: string;
}
export interface CreateCommentDto {
content: string;
articleId?: string;
liveBlogId?: string;
parentCommentId?: string;
}
export interface UpdateCommentDto {
content?: string;
isVisible?: boolean;
}
export interface FindCommentsParams {
articleId?: string;
liveBlogId?: string;
parentCommentId?: string;
page?: number;
limit?: number;
}
export interface CommentsResponse {
data: Comment[];
total: number;
page?: number;
limit?: number;
}
export interface ReactionCounts {
likes: number;
dislikes: number;
}
export interface CreateReactionDto {
type: 'like' | 'dislike';
articleId?: string;
liveBlogId?: string;
commentId?: string;
}
// Comment API Functions
export async function fetchComments(params: FindCommentsParams = {}): Promise<CommentsResponse> {
const searchParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
if (typeof value === 'number') {
searchParams.append(key, value.toString());
} else {
searchParams.append(key, String(value));
}
}
});
const response = await authFetch(`${API_BASE_URL}/comments?${searchParams}`);
if (!response.ok) {
throw new Error('Failed to fetch comments');
}
return response.json();
}
export async function createComment(dto: CreateCommentDto): Promise<Comment> {
const response = await authFetch(`${API_BASE_URL}/comments`, {
method: 'POST',
body: JSON.stringify(dto),
});
if (!response.ok) {
throw new Error('Failed to create comment');
}
return response.json();
}
export async function updateComment(id: string, dto: UpdateCommentDto): Promise<Comment> {
const response = await authFetch(`${API_BASE_URL}/comments/${id}`, {
method: 'PUT',
body: JSON.stringify(dto),
});
if (!response.ok) {
throw new Error('Failed to update comment');
}
return response.json();
}
export async function deleteComment(id: string): Promise<void> {
const response = await authFetch(`${API_BASE_URL}/comments/${id}`, {
method: 'DELETE',
});
if (!response.ok) {
throw new Error('Failed to delete comment');
}
}
export async function addReaction(dto: CreateReactionDto): Promise<void> {
const response = await authFetch(`${API_BASE_URL}/comments/reactions`, {
method: 'POST',
body: JSON.stringify(dto),
});
if (!response.ok) {
throw new Error('Failed to add reaction');
}
}
export async function getReactionCounts(
articleId?: string,
liveBlogId?: string,
commentId?: string
): Promise<ReactionCounts> {
const searchParams = new URLSearchParams();
if (articleId) searchParams.append('articleId', articleId);
if (liveBlogId) searchParams.append('liveBlogId', liveBlogId);
if (commentId) searchParams.append('commentId', commentId);
const url = `${API_BASE_URL}/comments/reactions/counts${searchParams.toString() ? `?${searchParams}` : ''}`;
const response = await authFetch(url);
if (!response.ok) {
throw new Error('Failed to fetch reaction counts');
}
return response.json();
}
export async function getUserReaction(
articleId?: string,
liveBlogId?: string,
commentId?: string
): Promise<{ type: string | null }> {
const searchParams = new URLSearchParams();
if (articleId) searchParams.append('articleId', articleId);
if (liveBlogId) searchParams.append('liveBlogId', liveBlogId);
if (commentId) searchParams.append('commentId', commentId);
const url = `${API_BASE_URL}/comments/reactions/user${searchParams.toString() ? `?${searchParams}` : ''}`;
const response = await authFetch(url);
if (!response.ok) {
if (response.status === 401) {
return { type: null };
}
throw new Error('Failed to fetch user reaction');
}
return response.json();
}