188 lines
No EOL
5.2 KiB
TypeScript
188 lines
No EOL
5.2 KiB
TypeScript
import PocketBase from 'pocketbase';
|
||
import type { Post } from '../globalInterfaces';
|
||
|
||
const PB_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
|
||
|
||
export const pb = new PocketBase(PB_URL);
|
||
|
||
if (typeof window !== 'undefined') {
|
||
const token = localStorage.getItem('auth_token');
|
||
const userStr = localStorage.getItem('user');
|
||
|
||
// Инициализируем куку из localStorage если её нет
|
||
if (token && !document.cookie.includes('pb_auth')) {
|
||
document.cookie = `pb_auth=${token}; path=/; max-age=${7 * 24 * 60 * 60}; SameSite=Lax`;
|
||
}
|
||
|
||
if (token && userStr) {
|
||
try {
|
||
const user = JSON.parse(userStr);
|
||
pb.authStore.save(token, user);
|
||
} catch (e) {
|
||
console.error('Failed to restore auth:', e);
|
||
}
|
||
}
|
||
}
|
||
|
||
export interface PostVotes {
|
||
id: string;
|
||
post_id: string;
|
||
user_id: string;
|
||
vote_type: 'like' | 'dislike';
|
||
created: string;
|
||
updated: string;
|
||
}
|
||
|
||
export interface VoteStats {
|
||
likes: number;
|
||
dislikes: number;
|
||
userVote: 'like' | 'dislike' | null;
|
||
}
|
||
|
||
export async function getPostVotes(postId: string): Promise<VoteStats> {
|
||
const votes = await pb.collection('post_votes').getList(1, 1000, {
|
||
filter: `post_id="${postId}"`,
|
||
});
|
||
|
||
const likes = votes.items.filter((v) => v.vote_type === 'like').length;
|
||
const dislikes = votes.items.filter((v) => v.vote_type === 'dislike').length;
|
||
|
||
let userVote: 'like' | 'dislike' | null = null;
|
||
if (pb.authStore.isValid) {
|
||
const userId = pb.authStore.model?.id;
|
||
const userVoteRecord = votes.items.find((v) => v.user_id === userId);
|
||
if (userVoteRecord) {
|
||
userVote = userVoteRecord.vote_type as 'like' | 'dislike';
|
||
}
|
||
}
|
||
|
||
return { likes, dislikes, userVote };
|
||
}
|
||
|
||
export async function getPostVotesStats(postId: string): Promise<{ likes: number; dislikes: number }> {
|
||
// Получаем данные из коллекции голосов
|
||
const votes = await pb.collection('post_votes').getList(1, 1000, {
|
||
filter: `post="${postId}"`,
|
||
});
|
||
|
||
const likes = votes.items.filter((v: any) => v.vote_type === 'like').length;
|
||
const dislikes = votes.items.filter((v: any) => v.vote_type === 'dislike').length;
|
||
|
||
return { likes, dislikes };
|
||
}
|
||
|
||
export async function vote(postId: string, voteType: 'like' | 'dislike'): Promise<VoteStats> {
|
||
const userId = pb.authStore.model?.id;
|
||
|
||
if (!userId) {
|
||
throw new Error('Требуется авторизация');
|
||
}
|
||
|
||
const existingVotes = await pb.collection('post_votes').getList(1, 1, {
|
||
filter: `post_id="${postId}" && user_id="${userId}"`,
|
||
});
|
||
|
||
if (existingVotes.items.length > 0) {
|
||
const existingVote = existingVotes.items[0] as unknown as PostVotes;
|
||
|
||
if (existingVote.vote_type === voteType) {
|
||
await pb.collection('post_votes').delete(existingVote.id);
|
||
} else {
|
||
await pb.collection('post_votes').update(existingVote.id, {
|
||
vote_type: voteType,
|
||
});
|
||
}
|
||
} else {
|
||
await pb.collection('post_votes').create({
|
||
post_id: postId,
|
||
user_id: userId,
|
||
vote_type: voteType,
|
||
});
|
||
}
|
||
|
||
return getPostVotes(postId);
|
||
}
|
||
|
||
export async function getPosts(options?: {
|
||
page?: number;
|
||
perPage?: number;
|
||
category?: string;
|
||
search?: string;
|
||
}): Promise<{ posts: Post[]; total: number; page: number; totalPages: number }> {
|
||
const page = options?.page || 1;
|
||
const perPage = options?.perPage || 10;
|
||
|
||
const filter: string[] = ['draft = false'];
|
||
|
||
if (options?.category && options.category !== 'Все') {
|
||
filter.push(`category = "${options.category}"`);
|
||
}
|
||
|
||
if (options?.search) {
|
||
filter.push(`(title ~ "${options.search}" || description ~ "${options.search}")`);
|
||
}
|
||
|
||
const result = await pb.collection('posts').getList(page, perPage, {
|
||
filter: filter.join(' && '),
|
||
sort: '-date',
|
||
});
|
||
|
||
return {
|
||
posts: (result.items || []) as unknown as Post[],
|
||
total: result.totalItems || 0,
|
||
page: result.page || 1,
|
||
totalPages: result.totalPages || 1,
|
||
};
|
||
}
|
||
|
||
export async function getPostBySlug(slug: string): Promise<Post | null> {
|
||
const result = await pb.collection('posts').getList(1, 1, {
|
||
filter: `slug="${slug}" && draft = false`,
|
||
});
|
||
|
||
if (!result.items || result.totalItems === 0) {
|
||
return null;
|
||
}
|
||
|
||
return result.items[0] as unknown as Post;
|
||
}
|
||
|
||
export const FIXED_BLOG_CATEGORIES = [
|
||
'ДТП',
|
||
'Лишение прав',
|
||
'Страховые споры',
|
||
'Штрафы ГИБДД',
|
||
'Возмещение ущерба',
|
||
'Судебные дела',
|
||
];
|
||
|
||
export async function getAllCategories(): Promise<string[]> {
|
||
return ['Все', ...FIXED_BLOG_CATEGORIES];
|
||
}
|
||
|
||
export function getPostImageUrl(post: { image?: string }): string {
|
||
if (post.image) {
|
||
const fileUrl = pb.files.getUrl(post, post.image);
|
||
if (fileUrl.startsWith('/')) {
|
||
return `${PB_URL}${fileUrl}`;
|
||
}
|
||
return fileUrl;
|
||
}
|
||
return '/images/blog/default.avif';
|
||
}
|
||
|
||
export async function getPostViews(postId: string): Promise<number> {
|
||
try {
|
||
const post = await pb.collection('posts').getOne(postId);
|
||
return post.views || 0;
|
||
} catch {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
export async function incrementPostViews(postId: string): Promise<number> {
|
||
const post = await pb.collection('posts').getOne(postId);
|
||
const newViews = (post.views || 0) + 1;
|
||
await pb.collection('posts').update(postId, { views: newViews });
|
||
return newViews;
|
||
} |