feat: add links page to optimize SEO

This commit is contained in:
Guoqi Sun 2024-12-23 01:28:46 +08:00
parent 7fa06f4127
commit bacac5a5c3
25 changed files with 261 additions and 20 deletions

View file

@ -4,6 +4,8 @@ A minimalism, personal blog theme for Astro
[![Built with Astro](https://astro.badg.es/v1/built-with-astro/tiny.svg)](https://astro.build) [![Netlify Status](https://api.netlify.com/api/v1/badges/a4eb6e88-606d-4ea6-9a53-179e03a7e2ef/deploy-status)](https://app.netlify.com/sites/astro-air/deploys)
<img src="/src/public/preview.png" alt="Astro Air" width="200">
>
> If you find this project helpful, please consider giving it a star ⭐️

View file

@ -1,10 +1,9 @@
import mdx from "@astrojs/mdx"
import react from "@astrojs/react"
import sitemap from "@astrojs/sitemap"
import tailwind from "@astrojs/tailwind"
import { defineConfig } from "astro/config"
import sitemap from "@astrojs/sitemap";
// https://astro.build/config
export default defineConfig({
output: "static",
@ -15,4 +14,4 @@ export default defineConfig({
},
},
integrations: [react(), tailwind(), mdx(), sitemap()],
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

23
public/links/astro.svg Normal file
View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="logosandtypes_com" data-name="logosandtypes com" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 150 150">
<defs>
<style>
.cls-1 {
fill: none;
}
.cls-2 {
fill: url(#linear-gradient);
}
</style>
<linearGradient id="linear-gradient" x1="75.54" y1="113" x2="75.54" y2="143.15" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#ee46e0"/>
<stop offset="1" stop-color="#da3737"/>
</linearGradient>
</defs>
<g id="Layer_2" data-name="Layer 2">
<path id="Layer_3" data-name="Layer 3" class="cls-1" d="M0,0H150V150H0V0Z"/>
</g>
<path class="cls-2" d="M57.53,128.19c-5.86-5.34-7.57-16.57-5.13-24.7,4.23,5.13,10.1,6.75,16.17,7.67,10.41,1.56,21.21,.76,30.3-5.19,2.64,7.89-1.02,16.49-7.6,21.14-8.36,5.63-14.92,8.86-10.92,20.26-6.02-2.64-9.6-8.59-9.64-15-.02-1.6-.02-3.21-.24-4.79-.53-3.84-2.33-5.56-5.74-5.66-3.89-.16-6.6,2.57-7.21,6.27h0Z"/>
<path d="M24.1,102.13s17.35-8.43,34.74-8.43l13.12-40.49c.49-1.96,1.92-3.29,3.54-3.29s3.05,1.33,3.54,3.29l13.12,40.49c20.6,0,34.74,8.43,34.74,8.43,0,0-29.47-80.07-29.52-80.23-.85-2.37-2.27-3.89-4.2-3.89H57.82c-1.92,0-3.29,1.52-4.2,3.89-.06,.16-29.53,80.23-29.53,80.23Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
public/preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 KiB

View file

@ -0,0 +1,32 @@
---
interface Props {
name: string
link: string
description: string
avatar: string
}
const { name, link, description, avatar } = Astro.props
---
<a
href={link}
target="_blank"
class="flex items-center gap-4 rounded-lg border border-neutral-200 p-4 hover:scale-[1.02] hover:bg-neutral-100 hover:shadow-lg dark:border-neutral-800 dark:hover:bg-neutral-900"
>
<img
src={avatar}
alt={name}
class="h-12 w-12 rounded-full object-cover transition-transform duration-300 hover:scale-110"
/>
<div class="flex h-full flex-col">
<span class="text-lg font-medium transition-colors">
{name}
</span>
<span
class="line-clamp-2 flex-1 text-sm text-neutral-600 dark:text-neutral-400"
>
{description}
</span>
</div>
</a>

View file

@ -5,11 +5,13 @@ import { getLangFromUrl, useTranslations } from "~/i18n/utils"
const lang = getLangFromUrl(Astro.url)
const t = useTranslations(lang)
const { home, archive, custom, about } =
const { home, archive, custom, links, about } =
lang === "zh" ? zh.navigation : en.navigation
---
<div class="mb-10 mt-4 flex gap-4 overflow-x-auto whitespace-nowrap text-lg">
<div
class="mb-10 mt-4 flex gap-4 overflow-x-auto whitespace-nowrap text-lg [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
>
{
home && (
<a
@ -44,6 +46,17 @@ const { home, archive, custom, about } =
</a>
))
}
{
links && (
<a
href={`/${lang}/links`}
class="hover:underline hover:underline-offset-4"
data-umami-event="nav-links"
>
<p>{t("nav.links")}</p>
</a>
)
}
{
about && (
<a

View file

@ -1,6 +1,6 @@
export const title = "About Me"
export const title = "Hello, I'm Guoqi Sun ~"
{title}
<h3 class="my-10 text-center text-xl font-bold">{title}</h3>
<img
className="block dark:hidden"

22
src/config/en/links.mdx Normal file
View file

@ -0,0 +1,22 @@
export const title = "My Link"
<h3 class="my-10 text-center text-xl font-bold">{title}</h3>
```json
name: "Guoqi Sun"
description: "Try, fail, retry. That's the rhythm of growth."
link: "https://blog.sunguoqi.com"
avatar: "https://assets.guoqi.dev/images/avatar.png"
```
<h4 class="my-10 text-center">
Want to add a friend link? Please leave a comment in the comment area below,
and I will add it as soon as possible.
</h4>
```json
name: "your site name"
description: "your site description"
link: "your site link"
avatar: "your site avatar"
```

View file

@ -3,6 +3,7 @@ import { Github, Twitter } from "lucide-react"
export const defaultLanguage: string = "en"
export const common = {
domain: "https://astro-air.guoqi.dev",
meta: {
favicon: "/avatar.png",
url: "https://blog.sunguoqi.com",
@ -30,6 +31,7 @@ export const common = {
link: "https://camlife.cn",
},
],
links: true,
about: true,
},
latestPosts: 8,
@ -60,6 +62,28 @@ export const zh = {
},
],
},
pageMeta: {
home: {
title: "小孙同学",
description: "读书、摄影、编程、旅行,热爱可抵岁月漫长~",
ogImage: "/preview.png",
},
archive: {
title: "归档",
description: "小孙同学的所有文章",
ogImage: "/images/page-meta/zh/archive.png",
},
links: {
title: "朋友们",
description: "小孙同学的和他朋友们",
ogImage: "/images/page-meta/zh/links.png",
},
about: {
title: "关于我",
description: "小孙同学的自我介绍",
ogImage: "/images/page-meta/zh/about.png",
},
},
}
export const en = {
@ -80,4 +104,26 @@ export const en = {
},
],
},
pageMeta: {
home: {
title: "Guoqi Sun",
description: "Reading, Photography, Programming, Traveling",
ogImage: "/preview.png",
},
archive: {
title: "All Posts",
description: "Here are Guoqi Sun's all posts",
ogImage: "/images/page-meta/en/archive.png",
},
links: {
title: "My Friends",
description: "Here are Guoqi Sun's friends",
ogImage: "/images/page-meta/en/links.png",
},
about: {
title: "About Me",
description: "Here is Guoqi Sun's self-introduction",
ogImage: "/images/page-meta/en/about.png",
},
},
}

14
src/config/links.ts Normal file
View file

@ -0,0 +1,14 @@
export const links = [
{
name: "Astro",
link: "https://astro.build",
description: "The web framework for content-driven websites",
avatar: "/links/astro.svg",
},
{
name: "Guoqi Sun",
link: "https://blog.sunguoqi.com",
description: "Try, fail, retry. That's the rhythm of growth.",
avatar: "https://assets.guoqi.dev/images/avatar.png",
},
]

View file

@ -1,6 +1,6 @@
export const title = "关于我"
export const title = "你好,我是小孙同学~"
{title}
<h3 class="my-10 text-center text-xl font-bold">{title}</h3>
<img
className="block dark:hidden"

21
src/config/zh/links.mdx Normal file
View file

@ -0,0 +1,21 @@
export const title = "我的链接"
<h3 class="my-10 text-center text-xl font-bold">{title}</h3>
```json
name: "Guoqi Sun"
description: "Try, fail, retry. That's the rhythm of growth."
link: "https://blog.sunguoqi.com"
avatar: "https://assets.guoqi.dev/images/avatar.png"
```
<h4 class="my-10 text-center">
想要添加友情链接?请按照如下格式在评论区留言,我会及时添加。
</h4>
```json
name: "your site name"
description: "your site description"
link: "your site link"
avatar: "your site avatar"
```

View file

@ -12,17 +12,19 @@ export const ui = {
"nav.home": "Home",
"nav.archive": "Archive",
"nav.about": "About",
"nav.twitter": "Twitter",
"nav.links": "Links",
"blog.latest": "Latest Posts",
"archive.title": "All Posts",
"links.title": "My Friends",
"tag.title": "Tag:",
"tag.no_posts": "No posts found for tag",
},
zh: {
"nav.home": "首页",
"nav.about": "关于",
"nav.blog": "推特",
"nav.archive": "归档",
"nav.links": "友链",
"links.title": "朋友们",
"blog.latest": "近期文章",
"archive.title": "所有文章",
"tag.title": "标签:",

View file

@ -19,7 +19,7 @@ const openGraphImage = !ogImage ? `/og/${filename}.png` : ogImage
<Header />
<Navigation />
<slot />
<div class="my-10">
<div class="my-20">
{needComment && <Comments />}
</div>
</main>

View file

@ -1,4 +1,5 @@
---
import { en, zh } from "~/config"
import AboutContentEn from "~/config/en/about.mdx"
import AboutContentZh from "~/config/zh/about.mdx"
import { getLangFromUrl } from "~/i18n/utils"
@ -10,18 +11,17 @@ export function getStaticPaths() {
}
const lang = getLangFromUrl(Astro.url)
const pageMeta = lang === "zh" ? zh.pageMeta : en.pageMeta
const AboutContent = lang === "zh" ? AboutContentZh : AboutContentEn
const ogImage = "https://sunguoqi.com/me.png"
---
<MainLayout
title="about"
description="test"
ogImage={ogImage}
title={pageMeta.about.title}
description={pageMeta.about.description}
ogImage={pageMeta.about.ogImage}
needComment={true}
>
<div class="prose dark:prose-invert">
<div class="prose max-w-3xl dark:prose-invert">
<AboutContent />
</div>
</MainLayout>

View file

@ -1,11 +1,13 @@
---
import PostList from "~/components/astro/post-list.astro"
import { en, zh } from "~/config"
import { getLangFromUrl } from "~/i18n/utils"
import MainLayout from "~/layouts/main.astro"
import { getPostsByLocale } from "~/utils"
import { getLanguagePaths } from "~/utils/langs"
const lang = getLangFromUrl(Astro.url)
const pageMeta = lang === "zh" ? zh.pageMeta : en.pageMeta
export function getStaticPaths() {
return getLanguagePaths()
@ -28,7 +30,11 @@ const postsByYear = posts.reduce(
const years = Object.keys(postsByYear).sort((a, b) => Number(b) - Number(a))
---
<MainLayout>
<MainLayout
title={pageMeta.archive.title}
description={pageMeta.archive.description}
ogImage={pageMeta.archive.ogImage}
>
{
years.map((year) => (
<div class="year-group my-8">

View file

@ -2,15 +2,24 @@
import Footer from "~/components/astro/footer.astro"
import Intro from "~/components/astro/intro.astro"
import RecentBlogs from "~/components/astro/recent-blogs.astro"
import { en, zh } from "~/config"
import { getLangFromUrl } from "~/i18n/utils"
import MainLayout from "~/layouts/main.astro"
import { getLanguagePaths } from "~/utils/langs"
export function getStaticPaths() {
return getLanguagePaths()
}
const lang = getLangFromUrl(Astro.url)
const pageMeta = lang === "zh" ? zh.pageMeta : en.pageMeta
---
<MainLayout>
<MainLayout
title={pageMeta.home.title}
description={pageMeta.home.description}
ogImage={pageMeta.home.ogImage}
>
<Intro />
<RecentBlogs />
<Footer />

View file

@ -0,0 +1,35 @@
---
import LinkCard from "~/components/astro/link-card.astro"
import { en, zh } from "~/config"
import LinksContentEn from "~/config/en/links.mdx"
import { links } from "~/config/links"
import LinksContentZh from "~/config/zh/links.mdx"
import { getLangFromUrl, useTranslations } from "~/i18n/utils"
import MainLayout from "~/layouts/main.astro"
import { getLanguagePaths } from "~/utils/langs"
export function getStaticPaths() {
return getLanguagePaths()
}
const lang = getLangFromUrl(Astro.url)
const t = useTranslations(lang)
const LinksContent = lang === "zh" ? LinksContentZh : LinksContentEn
const pageMeta = lang === "zh" ? zh.pageMeta : en.pageMeta
---
<MainLayout
title={pageMeta.links.title}
description={pageMeta.links.description}
ogImage={pageMeta.links.ogImage}
needComment={true}
>
<h3 class="my-10 text-center text-xl font-bold">{t("links.title")}</h3>
<div class="grid grid-cols-1 gap-6 md:grid-cols-2">
{links.map((link) => <LinkCard {...link} />)}
</div>
<div class="prose max-w-3xl dark:prose-invert">
<LinksContent />
</div>
</MainLayout>

17
src/pages/robots.txt.ts Normal file
View file

@ -0,0 +1,17 @@
import type { APIRoute } from "astro"
import { common } from "~/config"
const robots = `
User-agent: *
Disallow:
User-agent: *
Allow: /
Sitemap: ${new URL("sitemap-index.xml", common.domain).href}
`.trim()
export const GET: APIRoute = () =>
new Response(robots, {
headers: { "Content-Type": "text/plain" },
})