Files
chpark dcae228a24 fix: 전 페이지 서버사이드 렌더링으로 전환 (초기 데이터 fetch 제거)
- 모든 관리자 페이지에서 DB 데이터를 서버에서 직접 HTML에 주입
- __INIT__ 글로벌 변수로 초기 데이터 전달 (fetch 불필요)
- 대시보드/사이트관리/AdSense/도메인/로그/사이트상세 전부 적용
- trust proxy 설정 (Traefik 뒤 동작)
- 저장/삭제/크롤링 등 액션은 여전히 API fetch 사용

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 11:24:57 +09:00

53 lines
3.0 KiB
Plaintext

<%- include('layout', { page: 'dashboard', pageTitle: '대시보드', body: `
<div class="stats-grid" id="stats">
<div class="stat-card"><div class="number" id="stat-sites">-</div><div class="label">등록된 사이트</div></div>
<div class="stat-card"><div class="number" id="stat-active">-</div><div class="label">스케줄 활성</div></div>
<div class="stat-card"><div class="number" id="stat-crawls">-</div><div class="label">총 크롤링 횟수</div></div>
<div class="stat-card"><div class="number" id="stat-adsense">-</div><div class="label">AdSense 설정</div></div>
</div>
<div class="card">
<div class="card-header">
<h2>사이트 현황</h2>
<a href="/admin/sites" class="btn btn-primary btn-sm">사이트 관리 &rarr;</a>
</div>
<table>
<thead><tr><th>사이트명</th><th>URL</th><th>스케줄</th><th>마지막 크롤링</th><th>상태</th><th>공개 URL</th></tr></thead>
<tbody id="site-table"></tbody>
</table>
</div>
<div class="card">
<div class="card-header"><h2>최근 로그</h2></div>
<table>
<thead><tr><th>시간</th><th>사이트</th><th>액션</th><th>메시지</th></tr></thead>
<tbody id="log-table"></tbody>
</table>
</div>
<script>
(function(){
var d = __INIT__ || {};
var sites = d.sites || [];
var adsense = d.adsense || [];
var logs = d.logs || [];
document.getElementById('stat-sites').textContent = sites.length;
document.getElementById('stat-active').textContent = sites.filter(function(s){return s.schedule_active}).length;
document.getElementById('stat-crawls').textContent = sites.reduce(function(a,s){return a+parseInt(s.crawl_count||0)},0);
document.getElementById('stat-adsense').textContent = adsense.length;
document.getElementById('site-table').innerHTML = sites.map(function(s){
var sched = s.schedule_active ? '<span class="badge badge-success">'+(s.cron_schedule||'ON')+'</span>' : '<span class="badge badge-danger">OFF</span>';
var slug = s.slug ? '<a href="/s/'+s.slug+'" target="_blank" style="color:var(--primary)">/s/'+s.slug+'</a>' : '-';
return '<tr><td><strong>'+s.name+'</strong></td><td class="text-muted" style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+s.url+'</td><td>'+sched+'</td><td>'+timeAgo(s.last_crawled_at)+'</td><td><span class="badge badge-'+(s.status==='active'?'success':'danger')+'">'+s.status+'</span></td><td>'+slug+'</td></tr>';
}).join('') || '<tr><td colspan="6" class="text-muted" style="text-align:center;padding:2rem">등록된 사이트가 없습니다. <a href="/admin/sites" style="color:var(--primary)">사이트를 추가하세요</a></td></tr>';
document.getElementById('log-table').innerHTML = logs.map(function(l){
return '<tr><td class="text-muted">'+timeAgo(l.created_at)+'</td><td>'+(l.site_name||'-')+'</td><td><span class="badge badge-info">'+l.action+'</span></td><td>'+(l.message||'').substring(0,80)+'</td></tr>';
}).join('') || '<tr><td colspan="4" class="text-muted" style="text-align:center">로그가 없습니다</td></tr>';
})();
</script>
` }) %>