dcae228a24
- 모든 관리자 페이지에서 DB 데이터를 서버에서 직접 HTML에 주입 - __INIT__ 글로벌 변수로 초기 데이터 전달 (fetch 불필요) - 대시보드/사이트관리/AdSense/도메인/로그/사이트상세 전부 적용 - trust proxy 설정 (Traefik 뒤 동작) - 저장/삭제/크롤링 등 액션은 여전히 API fetch 사용 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
62 lines
3.4 KiB
Plaintext
62 lines
3.4 KiB
Plaintext
<%- include('layout', { page: 'domains', pageTitle: '도메인 매핑', body: `
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2>도메인 매핑</h2>
|
|
<button class="btn btn-primary" onclick="openModal()">+ 도메인 추가</button>
|
|
</div>
|
|
<p class="text-muted" style="margin-bottom:1rem;font-size:.82rem">
|
|
도메인을 특정 사이트에 연결하면, 해당 도메인으로 접속 시 크롤링 결과가 자동으로 표시됩니다.
|
|
</p>
|
|
<table>
|
|
<thead><tr><th>도메인</th><th>연결 사이트</th><th>상태</th><th>등록일</th><th>액션</th></tr></thead>
|
|
<tbody id="dom-tbody"></tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="modal-overlay" id="domModal">
|
|
<div class="modal">
|
|
<h3>도메인 추가</h3>
|
|
<div class="form-group"><label>도메인 (서브도메인 포함)</label><input id="dom-domain" placeholder="rank.example.com"></div>
|
|
<div class="form-group"><label>연결할 사이트</label><select id="dom-site"></select></div>
|
|
<div class="form-group"><label>AdSense 설정 (선택)</label><select id="dom-adsense"><option value="">사이트 기본값 사용</option></select></div>
|
|
<div class="flex" style="justify-content:flex-end;gap:.5rem;margin-top:1rem">
|
|
<button class="btn btn-outline" onclick="document.getElementById('domModal').classList.remove('active')">취소</button>
|
|
<button class="btn btn-primary" onclick="saveDomain()">저장</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
var _d = __INIT__ || {};
|
|
var domains = _d.domains || [];
|
|
var domSites = _d.sites || [];
|
|
var domAdsense = _d.adsense || [];
|
|
|
|
function renderDomains(){
|
|
document.getElementById('dom-site').innerHTML = domSites.map(function(s){return '<option value="'+s.id+'">'+s.name+'</option>';}).join('');
|
|
document.getElementById('dom-adsense').innerHTML = '<option value="">사이트 기본값</option>'+domAdsense.map(function(a){return '<option value="'+a.id+'">'+a.name+'</option>';}).join('');
|
|
document.getElementById('dom-tbody').innerHTML = domains.map(function(d){
|
|
return '<tr><td><strong>'+d.domain+'</strong></td><td>'+(d.site_name||'-')+'</td>'+
|
|
'<td><span class="badge badge-'+(d.is_active?'success">활성':'danger">비활성')+'</span></td>'+
|
|
'<td class="text-muted">'+timeAgo(d.created_at)+'</td>'+
|
|
'<td><button class="btn btn-danger btn-sm" onclick="deleteDomain('+d.id+')">삭제</button></td></tr>';
|
|
}).join('') || '<tr><td colspan="5" class="text-muted" style="text-align:center;padding:2rem">도메인 매핑을 추가하세요</td></tr>';
|
|
}
|
|
renderDomains();
|
|
|
|
function openModal(){document.getElementById('domModal').classList.add('active');}
|
|
async function saveDomain(){
|
|
var data={domain:document.getElementById('dom-domain').value,site_id:parseInt(document.getElementById('dom-site').value),adsense_config_id:document.getElementById('dom-adsense').value||null};
|
|
if(!data.domain){toast('도메인을 입력하세요','error');return;}
|
|
await api('POST','/api/domains',data);toast('도메인 추가 완료');
|
|
document.getElementById('domModal').classList.remove('active');
|
|
var r=await api('GET','/api/domains');if(r&&r.length!==undefined)domains=r;renderDomains();
|
|
}
|
|
async function deleteDomain(id){
|
|
if(!confirm('삭제하시겠습니까?'))return;await api('DELETE','/api/domains/'+id);toast('삭제 완료');
|
|
var r=await api('GET','/api/domains');if(r&&r.length!==undefined)domains=r;renderDomains();
|
|
}
|
|
</script>
|
|
` }) %>
|