auth checkpoint
auth dependecies not instaled in dev container
This commit is contained in:
1 parent
7378d37b36
commit
42002f8e6f
50 files changed
+3680
-636
No files matched your search
+279
-41
@@ -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();
|
||||
}
|
||||
Reference in new issue
Block a user