백업 현황을 사이트 용량으로 재정의하고 파일 집계를 이미지·영상·오디오·첨부 파일·보내기로 명확히 분리해 합계 불일치를 해소했다. 설정 화면의 통계 카드 스타일을 통일해 가독성을 높였다. Co-authored-by: Cursor <cursoragent@cursor.com>
150 lines
4.7 KiB
JavaScript
150 lines
4.7 KiB
JavaScript
import { requireAdminSession } from '../../../../utils/admin-auth'
|
|
import { getBackupStorageSummary } from '../../../../utils/backup-storage-summary'
|
|
import { getPostgresClient } from '../../../../repositories/postgres-client'
|
|
|
|
/**
|
|
* 빈 콘텐츠 집계를 만든다.
|
|
* @returns {Object} 콘텐츠 집계
|
|
*/
|
|
const createEmptyContentSummary = () => ({
|
|
posts: {
|
|
total: 0,
|
|
published: 0,
|
|
draft: 0,
|
|
private: 0,
|
|
members: 0,
|
|
scheduled: 0
|
|
},
|
|
pages: {
|
|
total: 0,
|
|
published: 0,
|
|
draft: 0,
|
|
private: 0,
|
|
listed: 0,
|
|
featured: 0
|
|
},
|
|
tags: {
|
|
total: 0,
|
|
managed: 0,
|
|
general: 0,
|
|
used: 0
|
|
},
|
|
comments: {
|
|
total: 0,
|
|
published: 0,
|
|
pending: 0,
|
|
blocked: 0
|
|
},
|
|
members: {
|
|
total: 0,
|
|
owner: 0,
|
|
admin: 0,
|
|
member: 0
|
|
}
|
|
})
|
|
|
|
/**
|
|
* 숫자 값을 안전하게 정수로 변환한다.
|
|
* @param {unknown} value - 원본 값
|
|
* @returns {number} 정수 값
|
|
*/
|
|
const toCount = (value) => Number(value || 0)
|
|
|
|
/**
|
|
* 백업 점검용 콘텐츠 수량 집계를 조회한다.
|
|
* @returns {Promise<Object>} 콘텐츠 수량 집계
|
|
*/
|
|
const getBackupContentSummary = async () => {
|
|
const sql = getPostgresClient()
|
|
|
|
if (!sql) {
|
|
return createEmptyContentSummary()
|
|
}
|
|
|
|
const rows = await sql`
|
|
SELECT
|
|
(SELECT COUNT(*)::int FROM posts) AS "postsTotal",
|
|
(SELECT COUNT(*)::int FROM posts WHERE status = 'published') AS "postsPublished",
|
|
(SELECT COUNT(*)::int FROM posts WHERE status = 'draft') AS "postsDraft",
|
|
(SELECT COUNT(*)::int FROM posts WHERE status = 'private') AS "postsPrivate",
|
|
(SELECT COUNT(*)::int FROM posts WHERE status = 'members') AS "postsMembers",
|
|
(SELECT COUNT(*)::int FROM posts WHERE status IN ('published', 'members') AND published_at > now()) AS "postsScheduled",
|
|
(SELECT COUNT(*)::int FROM pages) AS "pagesTotal",
|
|
(SELECT COUNT(*)::int FROM pages WHERE status = 'published') AS "pagesPublished",
|
|
(SELECT COUNT(*)::int FROM pages WHERE status = 'draft') AS "pagesDraft",
|
|
(SELECT COUNT(*)::int FROM pages WHERE status = 'private') AS "pagesPrivate",
|
|
(SELECT COUNT(*)::int FROM pages WHERE is_listed = true) AS "pagesListed",
|
|
(SELECT COUNT(*)::int FROM pages WHERE is_featured = true) AS "pagesFeatured",
|
|
(SELECT COUNT(*)::int FROM tags) AS "tagsTotal",
|
|
(SELECT COUNT(*)::int FROM tags WHERE tag_type = 'managed') AS "tagsManaged",
|
|
(SELECT COUNT(*)::int FROM tags WHERE tag_type = 'general') AS "tagsGeneral",
|
|
(SELECT COUNT(DISTINCT tag_id)::int FROM post_tags) AS "tagsUsed",
|
|
(SELECT COUNT(*)::int FROM comments) AS "commentsTotal",
|
|
(SELECT COUNT(*)::int FROM comments WHERE status = 'published') AS "commentsPublished",
|
|
(SELECT COUNT(*)::int FROM comments WHERE status = 'pending') AS "commentsPending",
|
|
(SELECT COUNT(*)::int FROM comments WHERE status = 'blocked') AS "commentsBlocked",
|
|
(SELECT COUNT(*)::int FROM users) AS "membersTotal",
|
|
(SELECT COUNT(*)::int FROM users WHERE user_role = 'owner') AS "membersOwner",
|
|
(SELECT COUNT(*)::int FROM users WHERE user_role = 'admin') AS "membersAdmin",
|
|
(SELECT COUNT(*)::int FROM users WHERE user_role = 'member') AS "membersMember"
|
|
`
|
|
const row = rows[0] || {}
|
|
|
|
return {
|
|
posts: {
|
|
total: toCount(row.postsTotal),
|
|
published: toCount(row.postsPublished),
|
|
draft: toCount(row.postsDraft),
|
|
private: toCount(row.postsPrivate),
|
|
members: toCount(row.postsMembers),
|
|
scheduled: toCount(row.postsScheduled)
|
|
},
|
|
pages: {
|
|
total: toCount(row.pagesTotal),
|
|
published: toCount(row.pagesPublished),
|
|
draft: toCount(row.pagesDraft),
|
|
private: toCount(row.pagesPrivate),
|
|
listed: toCount(row.pagesListed),
|
|
featured: toCount(row.pagesFeatured)
|
|
},
|
|
tags: {
|
|
total: toCount(row.tagsTotal),
|
|
managed: toCount(row.tagsManaged),
|
|
general: toCount(row.tagsGeneral),
|
|
used: toCount(row.tagsUsed)
|
|
},
|
|
comments: {
|
|
total: toCount(row.commentsTotal),
|
|
published: toCount(row.commentsPublished),
|
|
pending: toCount(row.commentsPending),
|
|
blocked: toCount(row.commentsBlocked)
|
|
},
|
|
members: {
|
|
total: toCount(row.membersTotal),
|
|
owner: toCount(row.membersOwner),
|
|
admin: toCount(row.membersAdmin),
|
|
member: toCount(row.membersMember)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 관리자 사이트 용량 조회 API
|
|
* @param {import('h3').H3Event} event - 요청 이벤트
|
|
* @returns {Promise<Object>} 백업 점검용 현황
|
|
*/
|
|
export default defineEventHandler(async (event) => {
|
|
requireAdminSession(event)
|
|
|
|
const [content, storage] = await Promise.all([
|
|
getBackupContentSummary(),
|
|
getBackupStorageSummary()
|
|
])
|
|
|
|
return {
|
|
generatedAt: new Date().toISOString(),
|
|
content,
|
|
storage
|
|
}
|
|
})
|