Skip to content

Commit

Permalink
Merge pull request #157 from besscroft/feature-rss
Browse files Browse the repository at this point in the history
Feature rss
  • Loading branch information
besscroft authored Nov 15, 2024
2 parents 9f994ee + 10d9d69 commit 6240f40
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 7 deletions.
44 changes: 41 additions & 3 deletions app/admin/settings/preferences/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ export default function Preferences() {
const [title, setTitle] = useState('')
const [customFaviconUrl, setCustomFaviconUrl] = useState('')
const [customAuthor, setCustomAuthor] = useState('')
const [feedId, setFeedId] = useState('')
const [userId, setUserId] = useState('')
const [loading, setLoading] = useState(false)

const { data, isValidating, isLoading } = useSWR('/api/v1/settings/get-custom-info', fetcher)

async function updateTitle() {
async function updateInfo() {
try {
setLoading(true)
await fetch('/api/v1/settings/update-custom-info', {
Expand All @@ -27,6 +29,8 @@ export default function Preferences() {
title: title,
customFaviconUrl: customFaviconUrl,
customAuthor: customAuthor,
feedId: feedId,
userId: userId,
}),
}).then(res => res.json())
toast.success('修改成功!')
Expand All @@ -41,6 +45,8 @@ export default function Preferences() {
setTitle(data?.find((item: any) => item.config_key === 'custom_title')?.config_value || '')
setCustomFaviconUrl(data?.find((item: any) => item.config_key === 'custom_favicon_url')?.config_value || '')
setCustomAuthor(data?.find((item: any) => item.config_key === 'custom_author')?.config_value || '')
setFeedId(data?.find((item: any) => item.config_key === 'rss_feed_id')?.config_value || '')
setUserId(data?.find((item: any) => item.config_key === 'rss_user_id')?.config_value || '')
}, [data])

return (
Expand Down Expand Up @@ -93,11 +99,43 @@ export default function Preferences() {
className="mt-1 w-full border-none p-0 focus:border-transparent focus:outline-none focus:ring-0 sm:text-sm"
/>
</label>
<label
htmlFor="feedId"
className="w-full sm:w-64 block overflow-hidden rounded-md border border-gray-200 px-3 py-2 shadow-sm focus-within:border-blue-600 focus-within:ring-1 focus-within:ring-blue-600"
>
<span className="text-xs font-medium text-gray-700"> RSS feedId </span>

<input
type="text"
id="feedId"
disabled={isValidating || isLoading}
value={feedId || ''}
placeholder="请输入 RSS feedId"
onChange={(e) => setFeedId(e.target.value)}
className="mt-1 w-full border-none p-0 focus:border-transparent focus:outline-none focus:ring-0 sm:text-sm"
/>
</label>
<label
htmlFor="userId"
className="w-full sm:w-64 block overflow-hidden rounded-md border border-gray-200 px-3 py-2 shadow-sm focus-within:border-blue-600 focus-within:ring-1 focus-within:ring-blue-600"
>
<span className="text-xs font-medium text-gray-700"> RSS userId </span>

<input
type="text"
id="userId"
disabled={isValidating || isLoading}
value={userId || ''}
placeholder="请输入 RSS userId"
onChange={(e) => setUserId(e.target.value)}
className="mt-1 w-full border-none p-0 focus:border-transparent focus:outline-none focus:ring-0 sm:text-sm"
/>
</label>
<div className="flex w-full sm:w-64 items-center justify-center space-x-1">
<Button
variant="outline"
disabled={loading}
onClick={() => updateTitle()}
disabled={loading || isValidating}
onClick={() => updateInfo()}
aria-label="提交"
>
{loading && <ReloadIcon className="mr-2 h-4 w-4 animate-spin"/>}
Expand Down
74 changes: 74 additions & 0 deletions app/rss.xml/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import 'server-only'
import RSS from 'rss'
import { fetchCustomInfo, getRSSImages } from '~/server/db/query'

export async function GET(request: Request) {
const data = await fetchCustomInfo()

const url = new URL(request.url);

const feedId = data?.find((item: any) => item.config_key === 'rss_feed_id')?.config_value?.toString();
const userId = data?.find((item: any) => item.config_key === 'rss_user_id')?.config_value?.toString();

const customElements = feedId && userId
? [
{
follow_challenge: [
{ feedId: feedId },
{ userId: userId }
]
}
]
: [];

const feed = new RSS({
title: data?.find((item: any) => item.config_key === 'custom_title')?.config_value?.toString() || '相册',
generator: 'RSS for Next.js',
feed_url: `${url.origin}/rss.xml`,
site_url: url.origin,
copyright: `© 2024${new Date().getFullYear().toString() === '2024' ? '' : `-${new Date().getFullYear().toString()}`} ${
data?.find((item: any) => item.config_key === 'custom_author')?.config_value?.toString() || ''
}.`,
pubDate: new Date().toUTCString(),
ttl: 60,
custom_elements: customElements,
});

const images = await getRSSImages()
if (Array.isArray(images) && images.length > 0) {
images?.map(item => {
feed.item({
title: item.title || '图片',
description: `
<div>
<img src="${item.preview_url || item.url}" alt="${item.detail}" />
<p>${item.detail}</p>
<a href="${url.origin + (item.album_value === '/' ? '/preview/' : item.album_value + '/preview/') + item.id}" target="_blank">查看图片信息</a>
</div>
`,
url: url.origin + (item.album_value === '/' ? '/preview/' : item.album_value + '/preview/') + item.id,
guid: item.id,
date: item.created_at,
enclosure: {
url: item.preview_url || item.url,
type: 'image/jpeg',
},
media: {
content: {
url: item.preview_url || item.url,
type: 'image/jpeg',
},
thumbnail: {
url: item.preview_url || item.url,
},
},
})
})
}

return new Response(feed.xml(), {
headers: {
'Content-Type': 'application/xml',
}
});
}
2 changes: 1 addition & 1 deletion hono/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ app.put('/update-s3-info', async (c) => {
app.put('/update-custom-info', async (c) => {
const query = await c.req.json()
try {
await updateCustomInfo(query.title, query.customFaviconUrl, query.customAuthor);
await updateCustomInfo(query.title, query.customFaviconUrl, query.customAuthor, query.feedId, query.userId);
return c.json({
code: 200,
message: '更新成功!'
Expand Down
2 changes: 2 additions & 0 deletions instrumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export async function register() {
{ config_key: 'auth_secret', config_value: '', detail: '双因素验证种子密钥' },
{ config_key: 'custom_favicon_url', config_value: '', detail: '用户自定义的 favicon 地址' },
{ config_key: 'custom_author', config_value: '', detail: '网站归属者名称' },
{ config_key: 'rss_feed_id', config_value: '', detail: 'Follow feedId' },
{ config_key: 'rss_user_id', config_value: '', detail: 'Follow userId' },
],
skipDuplicates: true,
})
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"react-dom": "19.0.0-rc-5c56b873-20241107",
"react-hook-form": "^7.53.1",
"react-photo-album": "^3.0.2",
"rss": "^1.2.2",
"server-only": "^0.0.1",
"sharp": "^0.33.5",
"sonner": "^1.5.0",
Expand All @@ -80,6 +81,7 @@
"@types/node": "^20.17.1",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@types/rss": "^0.0.32",
"autoprefixer": "^10.4.20",
"eslint": "^8.57.1",
"eslint-config-next": "^15.0.3",
Expand Down
38 changes: 38 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 19 additions & 1 deletion server/db/operate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ export async function updateCopyrightShow(id: string, show: number) {
return resultRow
}

export async function updateCustomInfo(title: string, customFaviconUrl: string, customAuthor: string) {
export async function updateCustomInfo(title: string, customFaviconUrl: string, customAuthor: string, feedId: string, userId: string) {
await db.$transaction(async (tx) => {
await tx.configs.update({
where: {
Expand Down Expand Up @@ -407,6 +407,24 @@ export async function updateCustomInfo(title: string, customFaviconUrl: string,
updatedAt: new Date()
}
})
await tx.configs.update({
where: {
config_key: 'rss_feed_id'
},
data: {
config_value: feedId,
updatedAt: new Date()
}
})
await tx.configs.update({
where: {
config_key: 'rss_user_id'
},
data: {
config_value: userId,
updatedAt: new Date()
}
})
})
}

Expand Down
30 changes: 28 additions & 2 deletions server/db/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ export async function fetchCustomInfo() {
const find = await db.configs.findMany({
where: {
config_key: {
in: ['custom_title', 'custom_favicon_url', 'custom_author']
in: ['custom_title', 'custom_favicon_url', 'custom_author', 'rss_feed_id', 'rss_user_id']
}
},
select: {
Expand Down Expand Up @@ -603,4 +603,30 @@ export async function queryAuthSecret() {
})

return find;
}
}

export async function getRSSImages() {
// 每个相册取最新 10 张照片
const find = await db.$queryRaw`
WITH RankedImages AS (
SELECT
i.*,
A.album_value,
ROW_NUMBER() OVER (PARTITION BY A.album_value ORDER BY i.created_at DESC) AS rn
FROM
images i
INNER JOIN images_albums_relation iar ON i.ID = iar."imageId"
INNER JOIN albums A ON iar.album_value = A.album_value
WHERE
A.del = 0
AND A."show" = 0
AND i.del = 0
AND i."show" = 0
)
SELECT *
FROM RankedImages
WHERE rn <= 10;
`

return find;
}

0 comments on commit 6240f40

Please sign in to comment.