diff --git a/LICENSE b/LICENSE index 30343a1..744f2fe 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2023 - Present, Zeon Studio +Copyright (c) 2024 - Present, javayhu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 31201d0..1b5097e 100755 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@

如果觉得这个项目不错的话,可以点个⭐,非常感谢

+![image](public/images/screenshot.png) + ## 📌 Key Features - 🎯 海棠诗社按诗集、朝代、诗人、诗词等方式检索,内容丰富,信息齐全 @@ -11,31 +13,15 @@ - 🔍 海棠诗社全站响应式布局,兼容移动端,支持暗黑模式,响应速度快 - 👤 海棠诗社支持添加自己喜欢的诗词到个人诗单,永久收藏世间的美好 -### 📄 Tech Stacks - -- 🖥️ Frontend: @astrodotbuild + @tailwindcss + @shadcn -- 👤 Authentication: @supabase auth -- 📫 Email: @resend -- 📊 Analytics: google analytics + umami -- 🌏 Domain: @Namecheap -- 🗂️ Database: @supabase + @DrizzleORM - -## 🔗 Integrations +## 📄 Tech Stacks -- astro/react -- astro/sitemap -- astro/tailwind -- astro/vercel +- 🖥️ Frontend: Astro + Tailwind + Shadcn/ui +- 📊 Analytics: Umami + Google Analytics +- 🗂️ Database: SQLite + Drizzle +- 💬 Comment: Giscus ## 🚀 Getting Started -### 📦 Dependencies - -- astro 4.0+ -- node v20.10+ -- npm v10.2+ -- tailwind v3.3+ - ### 👉 Install Dependencies ```bash @@ -71,6 +57,6 @@ docker run -p 3000:80 haitang ## 📝 License -Copyright (c) 2023 - Present, Designed & Developed by [javayhu](https://javayhu.site) +Copyright (c) 2024 - Present, Designed & Developed by [javayhu](https://javayhu.site) **Code License:** Released under the [MIT](LICENSE) license. diff --git a/package.json b/package.json index cbdd439..07a76c1 100755 --- a/package.json +++ b/package.json @@ -34,7 +34,6 @@ "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-tooltip": "^1.0.7", - "@supabase/supabase-js": "^2.42.4", "@vercel/analytics": "^1.2.2", "@vercel/speed-insights": "^1.0.10", "astro": "^4.3.2", diff --git a/resources/favicon.png b/resources/favicon.png deleted file mode 100644 index 49d4e88..0000000 Binary files a/resources/favicon.png and /dev/null differ diff --git a/resources/favicon.svg b/resources/favicon.svg deleted file mode 100644 index 9a4ea6b..0000000 --- a/resources/favicon.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - flower - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/resources/screenshot.png b/resources/screenshot.png deleted file mode 100644 index 02899b5..0000000 Binary files a/resources/screenshot.png and /dev/null differ diff --git a/resources/screenshots/screenshot_about.png b/resources/screenshots/screenshot_about.png deleted file mode 100644 index 4e0c2b6..0000000 Binary files a/resources/screenshots/screenshot_about.png and /dev/null differ diff --git a/resources/screenshots/screenshot_calltoaction.png b/resources/screenshots/screenshot_calltoaction.png deleted file mode 100644 index 0933c5f..0000000 Binary files a/resources/screenshots/screenshot_calltoaction.png and /dev/null differ diff --git a/resources/screenshots/screenshot_collection.png b/resources/screenshots/screenshot_collection.png deleted file mode 100644 index a5ec591..0000000 Binary files a/resources/screenshots/screenshot_collection.png and /dev/null differ diff --git a/resources/screenshots/screenshot_collections.png b/resources/screenshots/screenshot_collections.png deleted file mode 100644 index 5475971..0000000 Binary files a/resources/screenshots/screenshot_collections.png and /dev/null differ diff --git a/resources/screenshots/screenshot_comment.png b/resources/screenshots/screenshot_comment.png deleted file mode 100644 index 4111466..0000000 Binary files a/resources/screenshots/screenshot_comment.png and /dev/null differ diff --git a/resources/screenshots/screenshot_darkmode.png b/resources/screenshots/screenshot_darkmode.png deleted file mode 100644 index fa08f78..0000000 Binary files a/resources/screenshots/screenshot_darkmode.png and /dev/null differ diff --git a/resources/screenshots/screenshot_dynasties.png b/resources/screenshots/screenshot_dynasties.png deleted file mode 100644 index 4582a85..0000000 Binary files a/resources/screenshots/screenshot_dynasties.png and /dev/null differ diff --git a/resources/screenshots/screenshot_dynasty.png b/resources/screenshots/screenshot_dynasty.png deleted file mode 100644 index 33cdce2..0000000 Binary files a/resources/screenshots/screenshot_dynasty.png and /dev/null differ diff --git a/resources/screenshots/screenshot_favorite.png b/resources/screenshots/screenshot_favorite.png deleted file mode 100644 index 6dcaf01..0000000 Binary files a/resources/screenshots/screenshot_favorite.png and /dev/null differ diff --git a/resources/screenshots/screenshot_features.png b/resources/screenshots/screenshot_features.png deleted file mode 100644 index bc471c1..0000000 Binary files a/resources/screenshots/screenshot_features.png and /dev/null differ diff --git a/resources/screenshots/screenshot_homepage.png b/resources/screenshots/screenshot_homepage.png deleted file mode 100644 index d6e8b7d..0000000 Binary files a/resources/screenshots/screenshot_homepage.png and /dev/null differ diff --git a/resources/screenshots/screenshot_index.png b/resources/screenshots/screenshot_index.png deleted file mode 100644 index be87c78..0000000 Binary files a/resources/screenshots/screenshot_index.png and /dev/null differ diff --git a/resources/screenshots/screenshot_members.png b/resources/screenshots/screenshot_members.png deleted file mode 100644 index 02e2573..0000000 Binary files a/resources/screenshots/screenshot_members.png and /dev/null differ diff --git a/resources/screenshots/screenshot_register.png b/resources/screenshots/screenshot_register.png deleted file mode 100644 index beb71a0..0000000 Binary files a/resources/screenshots/screenshot_register.png and /dev/null differ diff --git a/resources/screenshots/screenshot_search.png b/resources/screenshots/screenshot_search.png deleted file mode 100644 index 0305821..0000000 Binary files a/resources/screenshots/screenshot_search.png and /dev/null differ diff --git a/resources/screenshots/screenshot_testimonial.png b/resources/screenshots/screenshot_testimonial.png deleted file mode 100644 index 5a4399a..0000000 Binary files a/resources/screenshots/screenshot_testimonial.png and /dev/null differ diff --git a/resources/screenshots/screenshot_today.png b/resources/screenshots/screenshot_today.png deleted file mode 100644 index 3e7f9a4..0000000 Binary files a/resources/screenshots/screenshot_today.png and /dev/null differ diff --git a/resources/screenshots/screenshot_work.png b/resources/screenshots/screenshot_work.png deleted file mode 100644 index 224ecde..0000000 Binary files a/resources/screenshots/screenshot_work.png and /dev/null differ diff --git a/src/components/FavoriteButton.tsx b/src/components/FavoriteButton.tsx deleted file mode 100644 index e91c2f6..0000000 --- a/src/components/FavoriteButton.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Button } from '@/components/ui/button'; -import { Heart, HeartOff, Loader2 } from 'lucide-react'; -import { toast } from 'sonner'; - -interface FavoriteButtonProps { - works_id: number; - works_title: string; - works_author: string; - works_dynasty: string; -} - -const FavoriteButton: React.FC = ({ works_id, works_title, works_author, works_dynasty }) => { - const [isFavorite, setIsFavorite] = useState(null); - const [isLoading, setIsLoading] = useState(false); - - useEffect(() => { - const checkFavoriteStatus = async () => { - const hasToken = document.cookie.split(';').some((item) => item.trim().startsWith('sb-access-token=')); - if (!hasToken) { - console.log('checkFavorite, no token'); - setIsFavorite(false); - return; - } - - setIsLoading(true); - console.log('checkFavorite, works_id:', works_id); - const response = await fetch(`/api/favorite?works_id=${works_id}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - - if (!response.ok) { - console.error('checkFavorite, fail:', await response.text()); - } else { - const data = await response.json(); - console.log('checkFavorite, success:', data); - if (data.length > 0) { - setIsFavorite(true); - } else { - setIsFavorite(false); - } - } - setIsLoading(false); - }; - checkFavoriteStatus(); - }, [works_id]); - - const toggleFavorite = async () => { - setIsLoading(true); - if (isFavorite) { - const hasToken = document.cookie.split(';').some((item) => item.trim().startsWith('sb-access-token=')); - if (!hasToken) { - console.log('removeFavorite, no token'); - window.location.href = '/signin'; - return; - } - - console.log('removeFavorite, works_id:', works_id); - const response = await fetch('/api/favorite', { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ works_id }), - }); - - if (!response.ok) { - toast.error('操作失败,请稍后再试'); - console.error('removeFavorite, fail:', await response.text()); - } else { - toast.success('已取消收藏!'); - setIsFavorite(!isFavorite); - console.log('removeFavorite, success:', await response.json()); - } - } else { - const hasToken = document.cookie.split(';').some((item) => item.trim().startsWith('sb-access-token=')); - if (!hasToken) { - console.log('addFavorite, no token'); - window.location.href = '/signin'; - return; - } - - console.log('addFavorite, works_id:', works_id, ', works_title:', works_title); - const response = await fetch('/api/favorite', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ works_id, works_title, works_author, works_dynasty }), - }); - - if (!response.ok) { - toast.error('操作失败,请稍后再试'); - console.error('addFavorite, fail:', await response.text()); - } else { - toast.success('收藏成功!'); - setIsFavorite(!isFavorite); - console.log('addFavorite, success:', await response.json()); - } - } - setIsLoading(false); - }; - - return ( - - ); -}; - -export default FavoriteButton; diff --git a/src/components/FavoriteList.tsx b/src/components/FavoriteList.tsx deleted file mode 100644 index 5b4e650..0000000 --- a/src/components/FavoriteList.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '@/components/ui/card'; -import { Loader2 } from 'lucide-react'; - -interface Favorite { - id: number; - works_id: number; - works_title: string; - works_author: string; - works_dynasty: string; -} - -const FavoriteList = () => { - const [favorites, setFavorites] = useState([]); - const [loading, setLoading] = useState(true); // 添加loading状态 - - useEffect(() => { - const fetchFavorites = async () => { - try { - setLoading(true); - const response = await fetch('/api/favorite'); - if (!response.ok) { - console.error('Network response was not ok'); - throw new Error('Network response was not ok'); - } - const data = await response.json(); - setFavorites(data); - } catch (error) { - console.error('Failed to fetch favorites:', error); - } finally { - setLoading(false); - } - }; - - fetchFavorites(); - }, []); - - if (loading) { - return
加载中...
; - } - - return ( -
- { - favorites.map((favorite, index) => ( - - )) - } -
- ); -}; - -export default FavoriteList; diff --git a/src/components/RegisterForm.tsx b/src/components/RegisterForm.tsx deleted file mode 100644 index 98badaf..0000000 --- a/src/components/RegisterForm.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import React, { useState } from 'react'; -import { Button } from '@/components/ui/button'; -import { Loader2 } from 'lucide-react'; -import { toast } from 'sonner'; - -const RegisterForm = () => { - const [isLoading, setIsLoading] = useState(false); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [errorMessage, setErrorMessage] = useState(''); - - // 验证邮箱地址格式 - const validateEmail = (email: string) => { - return /\S+@\S+\.\S+/.test(email); - }; - - // 验证密码长度 - const validatePassword = (password: string) => { - return password.length >= 6; - }; - - // 处理表单提交 - const handleSubmit = async (e) => { - console.log('handleSubmit, email:', email); - e.preventDefault(); // 阻止表单默认提交行为 - - // 清除之前的错误消息 - setErrorMessage(''); - - // 验证邮箱和密码 - if (!validateEmail(email)) { - setErrorMessage('请输入有效的邮箱地址'); - return; - } - - if (!validatePassword(password)) { - setErrorMessage('密码长度至少为6个字符'); - return; - } - - // 发送注册请求 - try { - setIsLoading(true); - const response = await fetch('/api/auth/register', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ email, password }), - }); - - const data = await response.json(); - - if (!response.ok) { - console.error('register error, data:', data); - throw new Error(data.message || '注册失败,请稍后再试'); - } - - console.log('register success', data); - toast.success('注册成功,请前往邮箱确认'); - window.location.href = '/signin'; - } catch (error: any) { - setErrorMessage(error.message); - } - setIsLoading(false); - }; - - return ( -
-

- 注册 -

-

- 若注册新账号,记得检查邮件确认邮箱,若已注册账号?  - - 登录 - -

- -
-
- - setEmail(e.target.value)} - className="rounded-md py-2 px-3 text-sm dark:bg-zinc-800 dark:text-zinc-300 border bg-zinc-50 border-zinc-300 dark:border-zinc-700 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-600 dark:focus:bg-zinc-900 focus:bg-white focus:ring-opacity-60" - /> -
-
- - setPassword(e.target.value)} - className="rounded-md py-2 px-3 text-sm dark:bg-zinc-800 dark:text-zinc-300 border bg-zinc-50 border-zinc-300 dark:border-zinc-700 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-600 dark:focus:bg-zinc-900 focus:bg-white focus:ring-opacity-60" - /> -
- {errorMessage &&

{errorMessage}

} - -
-
- ); -}; - -export default RegisterForm; diff --git a/src/components/SigninForm.tsx b/src/components/SigninForm.tsx deleted file mode 100644 index 726152f..0000000 --- a/src/components/SigninForm.tsx +++ /dev/null @@ -1,182 +0,0 @@ -import React, { useState } from 'react'; -import { Button } from '@/components/ui/button'; -import { Loader2 } from 'lucide-react'; -import { toast } from 'sonner'; - -const SigninForm = () => { - const [isLoading, setIsLoading] = useState(false); - const [isGithubLoading, setIsGithubLoading] = useState(false); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [errorMessage, setErrorMessage] = useState(''); - - // 验证邮箱地址格式 - const validateEmail = (email: string) => { - return /\S+@\S+\.\S+/.test(email); - }; - - // 验证密码长度 - const validatePassword = (password: string) => { - return password.length >= 6; - }; - - // 处理邮箱登录 - const handleSubmit = async (e: React.FormEvent) => { - console.log('handleSubmit, email:', email); - e.preventDefault(); // 阻止表单默认提交行为 - - // 清除之前的错误消息 - setErrorMessage(''); - - // 验证邮箱和密码 - if (!validateEmail(email)) { - setErrorMessage('请输入有效的邮箱地址'); - return; - } - - if (!validatePassword(password)) { - setErrorMessage('密码长度至少为6个字符'); - return; - } - - // 发送登录请求 - try { - setIsLoading(true); - const response = await fetch('/api/auth/signin', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ email, password }), - }); - - const data = await response.json(); - - if (!response.ok) { - console.error('signin error, data:', data); - throw new Error(data.message || '登录失败,请稍后再试'); - } - - console.log('signin success', data); - toast.success('登录成功,进入诗社中...'); - window.location.href = '/dashboard'; - } catch (error: any) { - console.error('signin error, data:', error); - setErrorMessage(error.message); - } - setIsLoading(false); - }; - - // 处理Github登录 - const handleGithubSubmit = async (e: any) => { - console.log('handleGithubSubmit'); - e.preventDefault(); // 阻止表单默认提交行为 - - // 清除之前的错误消息 - setErrorMessage(''); - - // 发送登录请求 - try { - setIsGithubLoading(true); - const response = await fetch('/api/auth/signin', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ 'provider': 'github' }), - }); - - const data = await response.json(); - - if (!response.ok) { - console.error('signin error, data:', data); - throw new Error(data.message || '登录失败,请稍后再试'); - } - - console.log('signin success', data); - toast.success('Github登录授权,页面跳转中...'); - // window.location.href = '/dashboard'; - window.location.href = data.url; - } catch (error: any) { - console.error('signin github error, data:', error); - // setErrorMessage(error.message); - toast.error(error.message); - } - setIsGithubLoading(false); - }; - - return ( -
-

- 登录 -

-

- 若已注册账号,记得检查邮件确认邮箱,若还没有账号?  - - 注册 - -

- -
-
- - setEmail(e.target.value)} - className="rounded-md py-2 px-3 text-sm dark:bg-zinc-800 dark:text-zinc-300 border bg-zinc-50 border-zinc-300 dark:border-zinc-700 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-600 dark:focus:bg-zinc-900 focus:bg-white focus:ring-opacity-60" - /> -
-
- - setPassword(e.target.value)} - className="rounded-md py-2 px-3 text-sm dark:bg-zinc-800 dark:text-zinc-300 border bg-zinc-50 border-zinc-300 dark:border-zinc-700 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-600 dark:focus:bg-zinc-900 focus:bg-white focus:ring-opacity-60" - /> -
- {errorMessage &&

{errorMessage}

} - -
-
-

- - 其他方式登录 - -

-
- -
-
- ); -}; - -export default SigninForm; diff --git a/src/config/FooterConfig.ts b/src/config/FooterConfig.ts index 3a0fa2b..a5703bc 100644 --- a/src/config/FooterConfig.ts +++ b/src/config/FooterConfig.ts @@ -53,7 +53,7 @@ export const footerConfig = { ], socialLinks: [ // { ariaLabel: 'X', icon: 'tabler:brand-x', href: 'https://twitter.com/javayhu' }, - // { ariaLabel: 'Github', icon: 'tabler:brand-github', href: 'https://github.com/javayhu' }, + // { ariaLabel: 'Github', icon: 'tabler:brand-github', href: 'https://github.com/javayhu/haitang' }, // { ariaLabel: 'Instagram', icon: 'tabler:brand-instagram', href: '#' }, // { ariaLabel: 'Facebook', icon: 'tabler:brand-facebook', href: '#' }, // { ariaLabel: 'RSS', icon: 'tabler:rss', href: '/rss.xml' }, diff --git a/src/config/HeaderConfig.ts b/src/config/HeaderConfig.ts index 772c753..c4fbb60 100644 --- a/src/config/HeaderConfig.ts +++ b/src/config/HeaderConfig.ts @@ -12,10 +12,6 @@ export const headerConfig = { text: '每日一诗 🔥', href: '/today', }, - { - text: '我的收藏', - href: '/dashboard', - }, { text: '诗集', // href: '/collections', diff --git a/src/content/pages/privacy-policy.md b/src/content/pages/privacy-policy.md index c057a63..5e45989 100644 --- a/src/content/pages/privacy-policy.md +++ b/src/content/pages/privacy-policy.md @@ -28,7 +28,7 @@ For the purposes of this Privacy Policy: - **Service** refers to the Website. - **Service Provider** means any natural or legal person who processes the data on behalf of the Company. It refers to third-party companies or individuals employed by the Company to facilitate the Service, to provide the Service on behalf of the Company, to perform services related to the Service or to assist the Company in analyzing how the Service is used. - **Usage Data** refers to data collected automatically, either generated by the use of the Service or from the Service infrastructure itself (for example, the duration of a page visit). -- **Website** refers to HaiTang, accessible from [https://haitang.vercel.app](https://haitang.vercel.app) +- **Website** refers to HaiTang, accessible from [https://haitang.app](https://haitang.app) - **You** means the individual accessing or using the Service, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable. ## Collecting and Using Your Personal Data diff --git a/src/content/pages/terms-of-service.md b/src/content/pages/terms-of-service.md index a0c4f3d..bd0989c 100644 --- a/src/content/pages/terms-of-service.md +++ b/src/content/pages/terms-of-service.md @@ -31,7 +31,7 @@ For the purposes of these Terms and Conditions: - **Third-party Social Media Service** means any services or content (including data, information, products or services) provided by a third-party that may be displayed, included or made available by the Service. -- **Website** refers to HaiTang, accessible from [https://haitang.vercel.app](https://haitang.vercel.app) +- **Website** refers to HaiTang, accessible from [https://haitang.app](https://haitang.app) - **You** means the individual accessing or using the Service, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable. diff --git a/src/content/sections/call-to-action.md b/src/content/sections/call-to-action.md index de35bc1..e46572b 100644 --- a/src/content/sections/call-to-action.md +++ b/src/content/sections/call-to-action.md @@ -7,8 +7,6 @@ image: "/images/call-to-action.png" description: "加入海棠诗社,共赏诗词之美,和社友共享诗词的快乐时光" button: enable: true - label: "🔥 我要入社" - link: "/dashboard" - # label: "🔥 我要作诗" - # link: "/ai" + label: "🔥 每日一诗" + link: "/today" --- diff --git a/src/content/sections/hero.md b/src/content/sections/hero.md index f904675..cb74541 100644 --- a/src/content/sections/hero.md +++ b/src/content/sections/hero.md @@ -8,14 +8,11 @@ image: "@/assets/hero.png" primaryButton: enable: true - label: "🔥 我要入社" - link: "/dashboard" - # label: "🔥 我要作诗" - # link: "/ai" + label: "🔥 每日一诗" + link: "/today" secondaryButton: enable: true - label: "📚 我要读诗" - # link: "/collections" - link: "/today" + label: "📚 精选诗集" + link: "/collections" --- diff --git a/src/env.d.ts b/src/env.d.ts index 2ade315..acef35f 100755 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -1,19 +1,2 @@ /// /// - -interface ImportMetaEnv { - readonly SUPABASE_URL: string - readonly SUPABASE_ANON_KEY: string -} - -interface ImportMeta { - readonly env: ImportMetaEnv -} - -declare namespace App { - interface Locals { - username: string; - useremail: string; - avatarurl: string; - } -} diff --git a/src/layouts/Analytics.astro b/src/layouts/Analytics.astro index bb70c6f..8ef9fb4 100644 --- a/src/layouts/Analytics.astro +++ b/src/layouts/Analytics.astro @@ -1,3 +1,7 @@ +--- +// please input your own website analytics id +--- +