diff --git a/app/about/page.tsx b/app/about/page.tsx index f15096e..20233de 100644 --- a/app/about/page.tsx +++ b/app/about/page.tsx @@ -1,3 +1,9 @@ +export const metadata = { + title: "درباره ما", + description: + "فیپ یک گروه متخصص در حوزه اینتگریشن و روش‌های نوین یکپارچه‌سازی. ماموریت، چشم‌انداز و ارزش‌های ما.", +}; + export default function AboutPage() { return ( <> diff --git a/app/api/revalidate/route.ts b/app/api/revalidate/route.ts new file mode 100644 index 0000000..66c71eb --- /dev/null +++ b/app/api/revalidate/route.ts @@ -0,0 +1,47 @@ +import { revalidateTag } from "next/cache"; +import { NextRequest, NextResponse } from "next/server"; + +/** + * پاک کردن کش مقالات وردپرس. + * وقتی در وردپرس مقاله‌ای منتشر یا ویرایش شد، این آدرس را با secret فراخوانی کنید + * تا کش دانشنامه و مقاله‌ها بدون ریستارت سرور به‌روز شود. + * + * استفاده در وردپرس: + * - با پلاگین Webhook یا کد تم: بعد از save_post درخواست به این آدرس بفرست. + * - مثال: GET یا POST /api/revalidate?secret=YOUR_REVALIDATE_SECRET + * - یا هدر: x-revalidate-secret: YOUR_REVALIDATE_SECRET + */ +export async function GET(request: NextRequest) { + return handleRevalidate(request); +} + +export async function POST(request: NextRequest) { + return handleRevalidate(request); +} + +async function handleRevalidate(request: NextRequest) { + const secret = process.env.REVALIDATE_SECRET; + if (!secret) { + return NextResponse.json( + { error: "REVALIDATE_SECRET تنظیم نشده است." }, + { status: 500 } + ); + } + + const provided = + request.nextUrl.searchParams.get("secret") ?? + request.headers.get("x-revalidate-secret"); + if (provided !== secret) { + return NextResponse.json({ error: "غیرمجاز" }, { status: 401 }); + } + + try { + revalidateTag("blog-posts"); + return NextResponse.json({ revalidated: true, now: Date.now() }); + } catch (err) { + return NextResponse.json( + { error: "خطا در پاک کردن کش" }, + { status: 500 } + ); + } +} diff --git a/app/blog/[slug]/page.tsx b/app/blog/[slug]/page.tsx index bca79ea..c98f32b 100644 --- a/app/blog/[slug]/page.tsx +++ b/app/blog/[slug]/page.tsx @@ -20,17 +20,33 @@ interface BlogPostPageProps { params: Promise<{ slug: string }>; } +function stripHtml(html: string): string { + return html.replace(/<[^>]*>/g, "").trim(); +} + export async function generateMetadata({ params, }: BlogPostPageProps): Promise { const { slug } = await params; const post = await getPostBySlug(slug); if (!post) return { title: "پست یافت نشد" }; - const title = post.title?.rendered ?? "پست"; - const excerpt = post.excerpt?.rendered?.replace(/<[^>]*>/g, "").trim().slice(0, 160) ?? ""; + const titleRaw = post.title?.rendered ?? "پست"; + const title = stripHtml(titleRaw); + const excerpt = stripHtml(post.excerpt?.rendered ?? "").slice(0, 160); + const imageUrl = getFeaturedImageUrl(post, "medium_large"); return { - title, + title: titleRaw, description: excerpt, + openGraph: { + title, + description: excerpt, + ...(imageUrl && { images: [{ url: imageUrl, alt: title }] }), + }, + twitter: { + card: "summary_large_image", + title, + description: excerpt, + }, }; } diff --git a/app/blog/page.tsx b/app/blog/page.tsx index 560f642..6c74565 100644 --- a/app/blog/page.tsx +++ b/app/blog/page.tsx @@ -11,6 +11,10 @@ const PER_PAGE = 9; export const metadata: Metadata = { title: "دانشنامه", description: "مقالات و مطالب فیپ در زمینه یکپارچه‌سازی، اتوماسیون و بهره‌وری سازمانی.", + openGraph: { + title: "دانشنامه", + description: "مقالات و مطالب فیپ در زمینه یکپارچه‌سازی، اتوماسیون و بهره‌وری سازمانی.", + }, }; interface BlogPageProps { diff --git a/app/consulting/page.tsx b/app/consulting/page.tsx index dc21146..c15c3ec 100644 --- a/app/consulting/page.tsx +++ b/app/consulting/page.tsx @@ -115,7 +115,7 @@ const faqItems = [ ] export const metadata = { - title: 'مشاوره سازمانی | فیپ', + title: 'مشاوره سازمانی', description: 'مشاوره تخصصی در حوزه اینتگریشن، هوشمندسازی، RAG، Performance و معماری نرم‌افزار. در سریع‌ترین زمان با شما تماس می‌گیریم.', } diff --git a/app/global-not-found.tsx b/app/global-not-found.tsx index b70bc26..3c19747 100644 --- a/app/global-not-found.tsx +++ b/app/global-not-found.tsx @@ -1,4 +1,5 @@ import './globals.css' +import Link from 'next/link' export const metadata = { title: '۴۰۴ | صفحه یافت نشد', @@ -8,15 +9,32 @@ export const metadata = { export default function GlobalNotFound() { return ( - -

۴۰۴

-

این صفحه وجود ندارد.

- - بازگشت به صفحه اصلی - + +
+
+ + ۴۰۴ + +
+ +

+ صفحه پیدا نشد +

+

+ به نظر می‌رسد این آدرس وجود ندارد یا منتقل شده است. +

+ + + + صفحه اصلی + +
) diff --git a/app/layout.tsx b/app/layout.tsx index 1714c82..79fcd52 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -5,10 +5,16 @@ import { ThemeProvider } from "@/components/theme-provider"; import { TopProgressBar } from "@/components/top-progress-bar"; +const SITE_NAME = "فیپ"; +const DEFAULT_DESCRIPTION = + "فیپ یک سامانه برای ایجاد و طراحی راهکارهای مناسب برای کسب و کار شما است."; + export const metadata: Metadata = { - title: "فیپ", - description: - "فیپ یک سامانه برای ایجاد و طراحی راهکارهای مناسب برای کسب و کار شما است.", + title: { + default: `${SITE_NAME} | راهکارهای یکپارچه و هوشمند برای کسب‌وکار`, + template: `%s | ${SITE_NAME}`, + }, + description: DEFAULT_DESCRIPTION, icons: { icon: "/assets/feep-Logo.svg", }, diff --git a/app/login/page.tsx b/app/login/page.tsx index 3d82022..1ea1755 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -1,5 +1,11 @@ +import type { Metadata } from "next"; import { LoginForm } from "@/components/login-form"; +export const metadata: Metadata = { + title: "ورود", + description: "ورود به حساب فیپ", +}; + export default function LoginPage() { return (
diff --git a/app/not-found.tsx b/app/not-found.tsx index 1a00814..25ca37e 100644 --- a/app/not-found.tsx +++ b/app/not-found.tsx @@ -1,21 +1,39 @@ import Link from 'next/link' import { Button } from '@/components/ui/button' +import { Home } from 'lucide-react' + +export const metadata = { + title: '۴۰۴', + description: 'صفحه‌ای که دنبال آن هستید یافت نشد.', +} export default function NotFound() { return ( -
-

۴۰۴

-

- صفحه‌ای که دنبال آن هستید یافت نشد. -

-
- -
-
+ ) } diff --git a/app/page.tsx b/app/page.tsx index 4b65625..e259557 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,9 +1,15 @@ +import type { Metadata } from "next"; import ContentSection from "@/components/content-1"; import Features from "@/components/features-12"; import { FeaturedPostsGrid } from "@/components/featured-posts-grid"; import { getPosts } from "@/lib/wp-api"; import HeroSection from "@/components/hero-section"; +export const metadata: Metadata = { + description: + "فیپ؛ راهکارهای یکپارچه و هوشمند برای اتصال سیستم‌ها، اتوماسیون فرآیندها و بهره‌وری کسب‌وکار.", +}; + const FEATURED_CATEGORY_ID = 3; const FEATURED_POSTS_COUNT = 4; diff --git a/app/pricing/page.tsx b/app/pricing/page.tsx index 3e3dc61..d8a6d2f 100644 --- a/app/pricing/page.tsx +++ b/app/pricing/page.tsx @@ -1,6 +1,12 @@ +import type { Metadata } from "next"; import Pricing from "@/components/pricing"; import PricingComparator from "@/components/pricing-comparator"; +export const metadata: Metadata = { + title: "قیمت‌گذاری", + description: "تعرفه و پلن‌های خدمات فیپ. انتخاب مناسب برای کسب‌وکار شما.", +}; + export default function PricingPage() { return ( <> diff --git a/app/solutions/page.tsx b/app/solutions/page.tsx index 2e45a8e..d00c519 100644 --- a/app/solutions/page.tsx +++ b/app/solutions/page.tsx @@ -1,7 +1,14 @@ +import type { Metadata } from "next"; import { Button } from "@/components/ui/button"; import { Link as LinkIcon, BarChart3, Share2, Zap, TrendingUp, Workflow } from "lucide-react"; import Link from "next/link"; +export const metadata: Metadata = { + title: "راه حل‌ها", + description: + "راهکارهای فیپ: یکپارچه‌سازی سیستم‌ها، پنل‌های مدیریتی، اتوماسیون فرآیندها و تحلیل داده.", +}; + const solutions = [ { icon: LinkIcon, diff --git a/lib/wp-api.ts b/lib/wp-api.ts index 38f2dd7..8927f80 100644 --- a/lib/wp-api.ts +++ b/lib/wp-api.ts @@ -1,6 +1,10 @@ const WP_API_BASE = "https://wpa.feep.ir/wp-json/wp/v2"; const FETCH_TIMEOUT_MS = 15_000; +/** کش مقالات وردپرس: یک ساعت. با revalidateTag('blog-posts') از API می‌توان کش را پاک کرد. */ +const CACHE_REVALIDATE_SECONDS = 3600; +const CACHE_TAG_BLOG = "blog-posts"; + export interface WpMediaSize { source_url: string; width?: number; @@ -86,7 +90,10 @@ async function fetchWithTimeout( const res = await fetch(url, { ...options, signal: controller.signal, - next: { revalidate: 0 }, + next: { + revalidate: CACHE_REVALIDATE_SECONDS, + tags: [CACHE_TAG_BLOG], + }, }); clearTimeout(timeout); return res;