본문으로 건너뛰기
Public Source Viewer

비나래아카이브 개발자 포털

실제 서비스 구조를 살펴볼 수 있는 공개용 코드 뷰어입니다. 인증, 세션, 외부 연동, 토큰, 관리자 식별 등 보안상 민감한 구현은 파일 단위 또는 줄 단위로 검열됩니다.

Redacted View
src/services/home.service.ts
공개 가능
1 import fs from 'fs/promises';
2 import path from 'path';
3 import sanitizeHtml from 'sanitize-html';
4 [SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
5 import { decodeHtmlEntities } from './blog.service';
6
7 export type HomeEntry = {
8 title: string;
9 excerpt: string;
10 category: '아카이브' | '블로그';
11 date: string;
12 url: string;
13 };
14
15 function plain(value: unknown): string {
16 return decodeHtmlEntities(sanitizeHtml(String(value || ''), {
17 allowedTags: [],
18 allowedAttributes: {},
19 nonTextTags: ['script', 'style', 'textarea', 'noscript']
20 })).replace(/\s+/g, ' ').trim();
21 }
22
23 async function readEntries(file: string, library: boolean): Promise<HomeEntry[]> {
24 try {
25 [SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
26 if (!Array.isArray(data)) return [];
27 return data.filter(post => post && !post.isPrivate
28 && ['string', 'number'].includes(typeof post.id) && String(post.id).trim()).map(post => {
29 const excerpt = plain(post.content);
30 const timestamp = Date.parse(library ? post.createdAt : post.timestamp);
31 return {
32 title: plain(post.title).slice(0, 100) || excerpt.slice(0, 56) || '제목 없는 기록',
33 excerpt: excerpt.slice(0, 140),
34 category: library ? '블로그' as const : '아카이브' as const,
35 date: Number.isFinite(timestamp) ? new Date(timestamp).toISOString() : '',
36 url: library ? `/hinana/post/${encodeURIComponent(String(post.id))}`
37 : `/hinana/index?selectedId=${encodeURIComponent(String(post.id))}`
38 };
39 });
40 } catch {
41 // Missing or damaged data must not make the entry page unavailable.
42 return [];
43 }
44 }
45
46 export async function getHomeEntries(): Promise<HomeEntry[]> {
47 let files: string[];
48 [SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
49 [SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
50 const entries = await Promise.all(sources.map(file => readEntries(file, file !== 'posts.json')));
51 return entries.flat().sort((a, b) => (Date.parse(b.date) || 0) - (Date.parse(a.date) || 0)).slice(0, 3);
52 }
53