From aef12e853d59324a9d7f6345be2bba6b26e29e62 Mon Sep 17 00:00:00 2001 From: Web-serfer Date: Wed, 15 Apr 2026 12:36:58 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D1=8B=D0=B5=20=D0=B8=D0=B7?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B2=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=B4=D0=B5=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 57 +++++++++++++++++++ .../pb_migrations/1776236760_updated_posts.js | 29 ++++++++++ .../pb_migrations/1776236785_updated_posts.js | 29 ++++++++++ frontend/package.json | 1 + frontend/src/components/blog/BlogCard.astro | 6 +- .../src/components/blog/RelatedPosts.astro | 5 +- frontend/src/lib/pb.ts | 15 ++++- frontend/src/pages/api/posts/index.ts | 15 ++++- frontend/src/pages/blog/[slug].astro | 5 +- frontend/src/pages/blog/index.astro | 4 +- frontend/src/pages/blog/page/[page].astro | 4 +- frontend/src/pages/blog/search.astro | 4 +- 12 files changed, 159 insertions(+), 15 deletions(-) create mode 100644 AGENTS.md create mode 100644 backend/pb_migrations/1776236760_updated_posts.js create mode 100644 backend/pb_migrations/1776236785_updated_posts.js diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..242cf44 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,57 @@ +# Правила взаимодействия с Агентом + +## Основные принципы + +1. **Изменения в коде возможны только с явного разрешения пользователя** + - Перед внесением любых изменений в файлы ассистент должен получить подтверждение от пользователя + - Все изменения должны быть предварительно объяснены пользователю + - Перед решением конкретной задачи всегда составлять план + - После внесения изменений в код - проводить проверку - только после этого приступать к дальнейшему решению задачи + +2. **Прозрачность действий** + - Ассистент должен объяснить, какие изменения планируется внести + - Необходимо указать, в какие файлы будут внесены изменения + - Следует объяснить последствия предполагаемых изменений + +3. **Безопасность кода** + - Все изменения должны проходить проверку на безопасность + - Не должны вноситься изменения, которые могут повредить функциональность приложения + - Рекомендуется создание резервных копий при значительных изменениях + +4. **Согласование архитектурных решений** + - При внесении изменений, затрагивающих архитектуру приложения, необходима дискуссия с пользователем + - Предложения по улучшению архитектуры должны обсуждаться до реализации + +5. **Работа с разными типами проектов** + - Уважать существующую архитектуру и стиль кода проекта + - Следовать установленным в проекте принципам и паттернам + +6. **Использование Bun** + - Все команды должны выполняться с использованием Bun (bun install, bun dev, bun build и т.д.) + - При создании скриптов в package.json, они должны быть совместимы с Bun + +7. **Язык общения** + - Всё общение с пользователем происходит на русском языке + +8. **Проверка изменений** + - После внесения изменений в код не требуется запускать сервер разработки для проверки + - Пользователь самостоятельно запускает сервер и проверяет изменения + +9. **Проверка типов данных** + - Проверять проект на ошибки типизации через команду `bun run tsc --noEmit -p frontend/tsconfig.json` + - В проекте не должно быть типов any + - Все интерфейсы компонентов прописывать в файле globalInterfaces.ts + - При работе с PocketBase использовать актуальные сигнатуры методов из файла `D:\Verstka\production\astro_minivan\frontend\node_modules\pocketbase\dist\pocketbase.es.d.ts` + +10. **Плагин @astrojs/sitemap** + - Обязательно к установке в проект пакета @astrojs/sitemap + - Обязательно к созданию в проекте файла .nvmrc + +11. **Замена хоста при развертывании проекта** + - Обязательно нужно изменить http://localhost:3000/ в шаблонах писем на реальный + +## Ccылки на документацию +- URL документации Astro: https://docs.astro.build/en/getting-started/ +- URL документации PocketBase: https://pocketbase.io/docs/ +- URL документации SolidJS: https://docs.solidjs.com/solid-start/getting-started +- URL документации astro-icons: https://www.astroicon.dev/getting-started/ diff --git a/backend/pb_migrations/1776236760_updated_posts.js b/backend/pb_migrations/1776236760_updated_posts.js new file mode 100644 index 0000000..e95018b --- /dev/null +++ b/backend/pb_migrations/1776236760_updated_posts.js @@ -0,0 +1,29 @@ +/// +migrate((app) => { + const collection = app.findCollectionByNameOrId("pbc_1125843985") + + // remove field + collection.fields.removeById("text2548032275") + + return app.save(collection) +}, (app) => { + const collection = app.findCollectionByNameOrId("pbc_1125843985") + + // add field + collection.fields.addAt(3, new Field({ + "autogeneratePattern": "", + "hidden": false, + "id": "text2548032275", + "max": 0, + "min": 0, + "name": "imageUrl", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + })) + + return app.save(collection) +}) diff --git a/backend/pb_migrations/1776236785_updated_posts.js b/backend/pb_migrations/1776236785_updated_posts.js new file mode 100644 index 0000000..a054c3e --- /dev/null +++ b/backend/pb_migrations/1776236785_updated_posts.js @@ -0,0 +1,29 @@ +/// +migrate((app) => { + const collection = app.findCollectionByNameOrId("pbc_1125843985") + + // add field + collection.fields.addAt(11, new Field({ + "hidden": false, + "id": "file3309110367", + "maxSelect": 1, + "maxSize": 0, + "mimeTypes": [], + "name": "image", + "presentable": false, + "protected": false, + "required": false, + "system": false, + "thumbs": [], + "type": "file" + })) + + return app.save(collection) +}, (app) => { + const collection = app.findCollectionByNameOrId("pbc_1125843985") + + // remove field + collection.fields.removeById("file3309110367") + + return app.save(collection) +}) diff --git a/frontend/package.json b/frontend/package.json index ca99f49..c6ef46a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,6 +19,7 @@ "astro": "^6.0.8", "astro-icon": "^1.1.5", "marked": "^18.0.0", + "pocketbase": "^0.21.0", "tailwindcss": "^4.2.2" }, "devDependencies": { diff --git a/frontend/src/components/blog/BlogCard.astro b/frontend/src/components/blog/BlogCard.astro index 73df787..feee50e 100644 --- a/frontend/src/components/blog/BlogCard.astro +++ b/frontend/src/components/blog/BlogCard.astro @@ -6,7 +6,7 @@ export interface Props { categoryColor?: string; date: string; readTime: string; - imageUrl?: string; + image?: string; slug?: string; } @@ -17,9 +17,11 @@ const { categoryColor = 'bg-gold', date, readTime, - imageUrl = '/images/blog/default.avif', + image, slug = '#' } = Astro.props; + +const imageUrl = image || '/images/blog/default.avif'; ---
diff --git a/frontend/src/components/blog/RelatedPosts.astro b/frontend/src/components/blog/RelatedPosts.astro index 22c1946..93b30f6 100644 --- a/frontend/src/components/blog/RelatedPosts.astro +++ b/frontend/src/components/blog/RelatedPosts.astro @@ -1,5 +1,6 @@ --- import BlogCard from '@components/blog/BlogCard.astro'; +import { getPostImageUrl } from '@lib/pb'; interface Post { id: string; @@ -10,7 +11,7 @@ interface Post { categoryColor: string; date: string; readTime: string; - imageUrl: string; + image: string; } interface Props { @@ -47,7 +48,7 @@ const filteredPosts = currentSlug categoryColor={post.categoryColor} date={formatDate(post.date)} readTime={post.readTime} - imageUrl={post.imageUrl} + image={getPostImageUrl(post)} slug={`/blog/${post.slug}`} /> ))} diff --git a/frontend/src/lib/pb.ts b/frontend/src/lib/pb.ts index d2983e8..68dafe6 100644 --- a/frontend/src/lib/pb.ts +++ b/frontend/src/lib/pb.ts @@ -4,6 +4,8 @@ const PB_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090'; export const pb = new PocketBase(PB_URL); +pb.collection('_superusers').authRefresh().catch(() => {}); + export interface Post { id: string; slug: string; @@ -14,7 +16,7 @@ export interface Post { categoryColor: string; date: string; readTime: string; - imageUrl: string; + image: string; content?: string; draft: boolean; } @@ -71,4 +73,15 @@ export async function getAllCategories(): Promise { const categories = (result || []).map((post: any) => post.category).filter(Boolean); return ['Все', ...new Set(categories)]; +} + +export function getPostImageUrl(post: any): 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'; } \ No newline at end of file diff --git a/frontend/src/pages/api/posts/index.ts b/frontend/src/pages/api/posts/index.ts index 522cb05..bfce8b3 100644 --- a/frontend/src/pages/api/posts/index.ts +++ b/frontend/src/pages/api/posts/index.ts @@ -1,9 +1,11 @@ import type { APIRoute } from 'astro'; import PocketBase from 'pocketbase'; +const PB_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090'; + export const GET: APIRoute = async ({ url }) => { try { - const pb = new PocketBase(import.meta.env.POCKETBASE_URL); + const pb = new PocketBase(PB_URL); const page = parseInt(url.searchParams.get('page') || '1'); const perPage = parseInt(url.searchParams.get('per_page') || '10'); @@ -25,6 +27,15 @@ export const GET: APIRoute = async ({ url }) => { sort: '-date', }); + const getImageUrl = (post: any) => { + if (!post.image) return null; + const fileUrl = pb.files.getUrl(post, post.image); + if (fileUrl.startsWith('/')) { + return `${PB_URL}${fileUrl}`; + } + return fileUrl; + }; + return new Response(JSON.stringify({ posts: result.items.map(post => ({ id: post.id, @@ -36,7 +47,7 @@ export const GET: APIRoute = async ({ url }) => { categoryColor: post.categoryColor, date: post.date, readTime: post.readTime, - imageUrl: post.imageUrl, + image: getImageUrl(post), })), total: result.totalItems, page: result.pageInfo.page, diff --git a/frontend/src/pages/blog/[slug].astro b/frontend/src/pages/blog/[slug].astro index 7048ac4..3a45293 100644 --- a/frontend/src/pages/blog/[slug].astro +++ b/frontend/src/pages/blog/[slug].astro @@ -4,7 +4,7 @@ import { SITE_URL } from '@constants'; import PostCommentForm from '@components/blog/PostCommentForm.astro'; import RelatedPosts from '@components/blog/RelatedPosts.astro'; import ArticleTableOfContents from '@components/blog/ArticleTableOfContents.astro'; -import { getPostBySlug, getPosts } from '@lib/pb'; +import { getPostBySlug, getPosts, getPostImageUrl } from '@lib/pb'; import { marked } from 'marked'; export const prerender = false; @@ -50,6 +50,7 @@ const { posts: relatedPosts } = await getPosts({ perPage: 4, category: post.cate const filteredRelated = relatedPosts.filter(p => p.slug !== slug).slice(0, 3); const currentUrl = `${SITE_URL}/blog/${slug}`; +const heroImage = getPostImageUrl(post); --- { categoryColor={post.categoryColor} date={formatDate(post.date)} readTime={post.readTime} - imageUrl={post.imageUrl} + image={getPostImageUrl(post)} slug={`/blog/${post.slug}`} />
diff --git a/frontend/src/pages/blog/page/[page].astro b/frontend/src/pages/blog/page/[page].astro index 24afb59..69bebf7 100644 --- a/frontend/src/pages/blog/page/[page].astro +++ b/frontend/src/pages/blog/page/[page].astro @@ -7,7 +7,7 @@ import BlogCard from '@components/blog/BlogCard.astro'; import Pagination from '@components/base/Pagination.astro'; import CTA from '@components/base/CTA.astro'; import SearchModal from '@components/base/SearchModal.astro'; -import { getPosts, getAllCategories } from '@lib/pb'; +import { getPosts, getAllCategories, getPostImageUrl } from '@lib/pb'; export const prerender = false; @@ -66,7 +66,7 @@ const formatDate = (date: string) => { categoryColor={post.categoryColor} date={formatDate(post.date)} readTime={post.readTime} - imageUrl={post.imageUrl} + image={getPostImageUrl(post)} slug={`/blog/${post.slug}`} /> diff --git a/frontend/src/pages/blog/search.astro b/frontend/src/pages/blog/search.astro index 75ce409..b350b03 100644 --- a/frontend/src/pages/blog/search.astro +++ b/frontend/src/pages/blog/search.astro @@ -3,7 +3,7 @@ import Layout from '@layouts/Layout.astro'; import { SITE_URL } from '@constants'; import BlogCard from '@components/blog/BlogCard.astro'; import SearchModal from '@components/base/SearchModal.astro'; -import { getPosts } from '@lib/pb'; +import { getPosts, getPostImageUrl } from '@lib/pb'; const url = new URL(Astro.request.url); const searchQuery = url.searchParams.get('q') || ''; @@ -78,7 +78,7 @@ const formatDate = (date: string) => { categoryColor={post.categoryColor} date={formatDate(post.date)} readTime={post.readTime} - imageUrl={post.imageUrl} + image={getPostImageUrl(post)} slug={`/blog/${post.slug}`} /> ))}