171 lines
3.7 KiB
JavaScript
171 lines
3.7 KiB
JavaScript
import {
|
|
getSamplePageBySlug,
|
|
getSamplePages,
|
|
getSamplePostBySlug,
|
|
getSamplePosts,
|
|
getSampleTags
|
|
} from '../utils/sample-content'
|
|
import { getPostgresClient } from './postgres-client'
|
|
|
|
/**
|
|
* 게시물 행을 API 응답 구조로 변환
|
|
* @param {Object} row - 게시물 행
|
|
* @returns {Object} 게시물 응답
|
|
*/
|
|
const mapPostRow = (row) => ({
|
|
id: row.id,
|
|
title: row.title,
|
|
slug: row.slug,
|
|
content: row.content,
|
|
excerpt: row.excerpt,
|
|
featuredImage: row.featured_image,
|
|
status: row.status,
|
|
publishedAt: row.published_at ? row.published_at.toISOString() : null,
|
|
createdAt: row.created_at.toISOString(),
|
|
updatedAt: row.updated_at.toISOString(),
|
|
tags: row.tags || []
|
|
})
|
|
|
|
/**
|
|
* 고정 페이지 행을 API 응답 구조로 변환
|
|
* @param {Object} row - 고정 페이지 행
|
|
* @returns {Object} 고정 페이지 응답
|
|
*/
|
|
const mapPageRow = (row) => ({
|
|
id: row.id,
|
|
title: row.title,
|
|
slug: row.slug,
|
|
content: row.content,
|
|
featuredImage: row.featured_image,
|
|
createdAt: row.created_at.toISOString(),
|
|
updatedAt: row.updated_at.toISOString()
|
|
})
|
|
|
|
/**
|
|
* 태그 행을 API 응답 구조로 변환
|
|
* @param {Object} row - 태그 행
|
|
* @returns {Object} 태그 응답
|
|
*/
|
|
const mapTagRow = (row) => ({
|
|
id: row.id,
|
|
name: row.name,
|
|
slug: row.slug,
|
|
description: row.description
|
|
})
|
|
|
|
/**
|
|
* 공개 게시물 목록 조회
|
|
* @returns {Promise<Array>} 게시물 목록
|
|
*/
|
|
export const listPosts = async () => {
|
|
const sql = getPostgresClient()
|
|
|
|
if (!sql) {
|
|
return getSamplePosts()
|
|
}
|
|
|
|
const rows = await sql`
|
|
SELECT
|
|
posts.*,
|
|
COALESCE(array_agg(tags.slug) FILTER (WHERE tags.slug IS NOT NULL), '{}') AS tags
|
|
FROM posts
|
|
LEFT JOIN post_tags ON post_tags.post_id = posts.id
|
|
LEFT JOIN tags ON tags.id = post_tags.tag_id
|
|
WHERE posts.status = 'published'
|
|
GROUP BY posts.id
|
|
ORDER BY posts.published_at DESC NULLS LAST, posts.created_at DESC
|
|
`
|
|
|
|
return rows.map(mapPostRow)
|
|
}
|
|
|
|
/**
|
|
* 공개 게시물 상세 조회
|
|
* @param {string} slug - 게시물 슬러그
|
|
* @returns {Promise<Object | null>} 게시물 상세
|
|
*/
|
|
export const getPostBySlug = async (slug) => {
|
|
const sql = getPostgresClient()
|
|
|
|
if (!sql) {
|
|
return getSamplePostBySlug(slug)
|
|
}
|
|
|
|
const rows = await sql`
|
|
SELECT
|
|
posts.*,
|
|
COALESCE(array_agg(tags.slug) FILTER (WHERE tags.slug IS NOT NULL), '{}') AS tags
|
|
FROM posts
|
|
LEFT JOIN post_tags ON post_tags.post_id = posts.id
|
|
LEFT JOIN tags ON tags.id = post_tags.tag_id
|
|
WHERE posts.slug = ${slug}
|
|
AND posts.status = 'published'
|
|
GROUP BY posts.id
|
|
LIMIT 1
|
|
`
|
|
|
|
return rows[0] ? mapPostRow(rows[0]) : null
|
|
}
|
|
|
|
/**
|
|
* 공개 고정 페이지 목록 조회
|
|
* @returns {Promise<Array>} 고정 페이지 목록
|
|
*/
|
|
export const listPages = async () => {
|
|
const sql = getPostgresClient()
|
|
|
|
if (!sql) {
|
|
return getSamplePages()
|
|
}
|
|
|
|
const rows = await sql`
|
|
SELECT *
|
|
FROM pages
|
|
ORDER BY created_at DESC
|
|
`
|
|
|
|
return rows.map(mapPageRow)
|
|
}
|
|
|
|
/**
|
|
* 공개 고정 페이지 상세 조회
|
|
* @param {string} slug - 페이지 슬러그
|
|
* @returns {Promise<Object | null>} 고정 페이지 상세
|
|
*/
|
|
export const getPageBySlug = async (slug) => {
|
|
const sql = getPostgresClient()
|
|
|
|
if (!sql) {
|
|
return getSamplePageBySlug(slug)
|
|
}
|
|
|
|
const rows = await sql`
|
|
SELECT *
|
|
FROM pages
|
|
WHERE slug = ${slug}
|
|
LIMIT 1
|
|
`
|
|
|
|
return rows[0] ? mapPageRow(rows[0]) : null
|
|
}
|
|
|
|
/**
|
|
* 공개 태그 목록 조회
|
|
* @returns {Promise<Array>} 태그 목록
|
|
*/
|
|
export const listTags = async () => {
|
|
const sql = getPostgresClient()
|
|
|
|
if (!sql) {
|
|
return getSampleTags()
|
|
}
|
|
|
|
const rows = await sql`
|
|
SELECT *
|
|
FROM tags
|
|
ORDER BY name ASC
|
|
`
|
|
|
|
return rows.map(mapTagRow)
|
|
}
|