6 lines
15 KiB
JavaScript
6 lines
15 KiB
JavaScript
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[82946],{37990:(e,a,t)=>{t.d(a,{ReportButton:()=>i});var l=t(95155),n=t(12115);let s=[{key:"spam",label:"광고/스팸"},{key:"point_farming",label:"포인트성 글/댓글"},{key:"spoiler",label:"스포일러 포함"},{key:"external_site",label:"타사이트 언급"},{key:"personal",label:"개인정보 노출"},{key:"other",label:"기타"}];function i({targetType:e,targetId:a,loggedIn:t,className:r,hidden:d}){let[o,c]=(0,n.useState)(!1),[h,u]=(0,n.useState)(""),[m,p]=(0,n.useState)(""),[b,f]=(0,n.useState)(!1),[k,x]=(0,n.useState)(!1);if(d)return null;let j=async()=>{if(!t)return void alert("로그인 후 이용해주세요");if(!b){if(!h)return void alert("신고 분류 선택이 필요합니다");f(!0);try{let t=await fetch("/api/board/report",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({targetType:e,targetId:a,reason:h,detail:m})}),l=await t.json().catch(()=>null);if(!t.ok||!l?.ok){alert(l?.error||`신고 실패 (${t.status})`),f(!1);return}x(!0),f(!1),setTimeout(()=>{c(!1)},1200)}catch(e){alert(e instanceof Error?e.message:"신고 실패"),f(!1)}}};return(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)("button",{type:"button",className:`report-btn${r?` ${r}`:""}`,onClick:()=>{t?(c(!0),x(!1),u("")):alert("로그인 후 이용해주세요")},title:"신고","aria-label":"신고",children:[(0,l.jsx)("span",{className:"report-btn-icon","aria-hidden":!0,children:"\uD83D\uDEA9"}),(0,l.jsx)("span",{className:"report-btn-text",children:"신고"})]}),o&&(0,l.jsxs)("div",{className:"report-modal",role:"dialog","aria-modal":"true","aria-label":"신고",children:[(0,l.jsx)("div",{className:"report-modal-backdrop",onClick:()=>!b&&c(!1)}),(0,l.jsxs)("div",{className:"report-modal-card",children:[(0,l.jsxs)("div",{className:"report-modal-head",children:[(0,l.jsxs)("span",{children:["\uD83D\uDEA9 ","post"===e?"글":"댓글"," 신고"]}),(0,l.jsx)("button",{type:"button",className:"report-modal-close",onClick:()=>!b&&c(!1),"aria-label":"닫기",children:"✕"})]}),k?(0,l.jsxs)("div",{className:"report-modal-done",children:[(0,l.jsx)("div",{className:"report-modal-done-em",children:"✅"}),(0,l.jsx)("div",{children:"신고가 접수되었습니다."}),(0,l.jsx)("div",{className:"report-modal-done-sub",children:"관리자가 검토 후 처리합니다."})]}):(0,l.jsxs)("div",{className:"report-modal-body",children:[(0,l.jsx)("p",{className:"report-modal-desc",children:"반복적인 허위신고는 신고자가 제재될 수 있습니다."}),(0,l.jsxs)("label",{className:"report-modal-field",children:[(0,l.jsx)("span",{className:"report-modal-label",children:"사유"}),(0,l.jsx)("div",{className:"report-modal-reasons",children:s.map(e=>(0,l.jsx)("button",{type:"button",className:`report-modal-reason${h===e.key?" is-active":""}`,onClick:()=>u(e.key),"aria-pressed":h===e.key,children:e.label},e.key))})]}),(0,l.jsxs)("label",{className:"report-modal-field",children:[(0,l.jsxs)("span",{className:"report-modal-label",children:["상세 내용 ",(0,l.jsx)("span",{className:"report-modal-hint",children:"(선택, 최대 400자)"})]}),(0,l.jsx)("textarea",{className:"report-modal-textarea",rows:4,maxLength:400,value:m,onChange:e=>p(e.target.value),placeholder:"구체적인 사유를 남기면 빠르게 처리됩니다."})]}),(0,l.jsxs)("div",{className:"report-modal-actions",children:[(0,l.jsx)("button",{type:"button",className:"btn btn--outline",onClick:()=>!b&&c(!1),disabled:b,children:"취소"}),(0,l.jsx)("button",{type:"button",className:"btn btn--danger",onClick:j,disabled:b,children:b?"전송 중...":"신고 접수"})]})]})]})]})]})}},52438:(e,a,t)=>{t.d(a,{s:()=>n});var l=t(95155);function n({points:e,level:a,role:t,extraRoles:s,isBoardAdmin:i,avatarUrl:r,className:d,size:o=22,anonymous:c}){var h;if(c)return(0,l.jsx)("span",{className:`lv-pill lv-pill--anon ${d??""}`,title:"익명",style:{width:o,height:o},children:"익"});let u="admin"===t||Array.isArray(s)&&s.includes("admin"),m=a??((h=e??0)&&!(h<1e3)?Math.min(1+Math.floor(Math.sqrt(h/1e3)),99):1),p=!!i&&!u,b=m>=70?"cosmic":m>=45?"aurora":m>=25?"shimmer":m>=5?"glow":"plain",f=function(e){let a=Math.max(1,Math.min(99,Math.floor(e)));if(1===a)return{bgLight:"hsl(220 6% 78%)",bgMid:"hsl(220 6% 64%)",bgDark:"hsl(220 6% 48%)",fg:"hsl(220 10% 22%)"};let t=((220-(a-2)*12)%360+360)%360,l=50+Math.round((a-2)/97*42),n=70-Math.round((a-2)/97*28),s=Math.min(92,n+18),i=Math.max(18,n-22),r=`hsl(${t} ${l}% ${s}%)`,d=`hsl(${t} ${l}% ${n}%)`;return{bgLight:r,bgMid:d,bgDark:`hsl(${t} ${l}% ${i}%)`,fg:n>=55?`hsl(${t} 60% 18%)`:`hsl(${t} 30% 96%)`}}(m),k={background:`linear-gradient(160deg, ${f.bgLight}, ${f.bgMid} 55%, ${f.bgDark})`,color:f.fg},x=1===m,j=x?"뉴비":String(m),g=x?"레벨 1 \xb7 뉴비":`레벨 ${m}`,v=x?{fontSize:Math.max(8,Math.round(.4*o)),letterSpacing:"-0.04em"}:null;if(r){let e=u?"lv-pill--admin":p?"lv-pill--board-admin":`lv-pill--fx-${b}`,a=u?"M":p?"★":j,t=u?"운영자":p?"게시판 관리자":g,n=o-4,s=!x||u||p?null:{fontSize:Math.max(7,Math.round(.4*n)),letterSpacing:"-0.05em"},i=u||p?{width:n,height:n}:{width:n,height:n,...k,...s};return(0,l.jsxs)("span",{className:`lv-with-avatar ${d??""}`,title:t,children:[(0,l.jsx)("img",{src:r,alt:"",className:"lv-avatar",style:{width:o,height:o},loading:"lazy"}),(0,l.jsx)("span",{className:`lv-pill ${e}`,style:i,children:a})]})}return p?(0,l.jsxs)("span",{className:`lv-with-avatar ${d??""}`,title:`${g} \xb7 게시판 관리자`,children:[(0,l.jsx)("span",{className:`lv-pill lv-pill--fx-${b}`,style:{width:o,height:o,...k,...v},children:j}),(0,l.jsx)("span",{className:"lv-pill lv-pill--board-admin",style:{width:o-4,height:o-4},children:"★"})]}):u?(0,l.jsx)("span",{className:`lv-pill lv-pill--admin ${d??""}`,title:"운영자",style:{width:o,height:o},children:"M"}):(0,l.jsx)("span",{className:`lv-pill lv-pill--fx-${b} ${d??""}`,title:g,style:{width:o,height:o,...k,...v},children:j})}},64027:(e,a,t)=>{t.d(a,{AdminBlockButtons:()=>i});var l=t(95155),n=t(12115);function s(e,a,t,l){let n=t.filter(e=>e.id!==l);if(0===n.length)return`${e} ${a}
|
|
→ 이 작성자만 사용 (충돌 없음)`;let s=n.map(e=>`${e.nickname}(id ${e.id})`).join(", ");return`${e} ${a}
|
|
→ 같이 차단되는 사용자 ${n.length}명: ${s}`}function i({authorId:e,authorNickname:a,authorIp:t,authorFp:r,ipCollision:d,fpCollision:o,reason:c,triggerPostId:h,onSuccess:u,className:m,userOnly:p=!1}){let[b,f]=(0,n.useTransition)(),k=l=>{let n=l.map(e=>e.toUpperCase()).join("+"),i=[];i.push(`작성자(${a}, id ${e})를 ${n} 로 차단합니다.`),i.push(""),i.push(`사유: ${c}`),i.push(""),l.includes("ip")&&t&&i.push(s("IP",t,d,e)),l.includes("fp")&&r&&i.push(s("FP",r,o,e)),i.push(""),i.push("차단되는 사용자들의 모든 글/댓글이 삭제됩니다."),h&&i.push("(이 글은 사유 추적을 위해 유지됨)"),l.includes("ip")&&i.push("Cloudflare 방화벽에도 영구 등록됩니다."),i.push(""),i.push("진행할까요?"),confirm(i.join("\n"))&&f(async()=>{let a=[],s=!1;for(let n=0;n<l.length;n++){let i=l[n],d="ip"===i?t:r;if(!d)continue;let o={kind:i,value:d,reason:c,purge:s?"0":"1",banUser:"1",trigger_author_id:e};h&&(o.trigger_post_id=h);let u=await fetch("/api/admin/ban",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(o)}),m=await u.json().catch(()=>null);if(!u.ok||!m?.ok)return void alert(`${i.toUpperCase()} 차단 실패: ${m?.error??u.status}`);if(s=!0,"ip"===i){let e=m?.cloudflare??[];e.length>0&&a.push(e.every(e=>e.ok)?"Cloudflare 등록 성공":`Cloudflare 일부 실패: ${e.find(e=>!e.ok)?.error??""}`)}0===n&&(m?.bannedUsers??0)>0&&a.push(`회원 ${m.bannedUsers}명 영구정지 (세션 ${m.sessionsKilled??0}개 종료)`)}alert(`${n} 차단 완료${a.length?"\n"+a.join("\n"):""}`),u?u():window.location.reload()})};return(0,l.jsxs)("span",{className:`admin-block-btns ${m??""}`,children:[(0,l.jsx)("button",{type:"button",className:"btn btn--outline post-del",onClick:()=>{let t=[`작성자(${a}, id ${e}) 계정만 영구 정지합니다.`,"",`사유: ${c}`,"","이 계정의 모든 글/댓글이 삭제됩니다 (게시판/작품/만화/소설 전부)."];h&&t.push("(이 글은 사유 추적을 위해 유지됨)"),t.push("","⚠ IP/FP 는 차단하지 않습니다 — 같은 IP/FP 의 다른 계정으로 재가입 가능.","","진행할까요?"),confirm(t.join("\n"))&&f(async()=>{let a={kind:"user",value:e,reason:c,purge:"1",banUser:"1",trigger_author_id:e};h&&(a.trigger_post_id=h);let t=await fetch("/api/admin/ban",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(a)}),l=await t.json().catch(()=>null);t.ok&&l?.ok?(alert(`계정 차단 완료
|
|
회원 ${l.bannedUsers??0}명 영구정지 (세션 ${l.sessionsKilled??0}개 종료)
|
|
글 ${l.purgedPosts??0}개 \xb7 댓글 ${l.purgedComments??0}개 삭제`),u?u():window.location.reload()):alert(`계정 차단 실패: ${l?.error??t.status}`)})},disabled:b,title:"계정만 영구정지 (IP/FP 미차단). 같은 IP/FP 의 다른 사용자는 영향 없음.",children:"\uD83D\uDEAB 계정만+삭제"}),!p&&t&&(0,l.jsx)("button",{type:"button",className:"btn btn--outline post-del",onClick:()=>k(["ip"]),disabled:b,title:`계정 + IP 차단 (IP: ${t}). 같은 IP 사용자 다 ban. Cloudflare 도 등록.`,children:"\uD83D\uDEAB 계정+IP차단+삭제"}),!p&&r&&(0,l.jsx)("button",{type:"button",className:"btn btn--outline post-del",onClick:()=>k(["fp"]),disabled:b,title:`계정 + FP 차단 (FP: ${r}). FP collision 있으면 skip.`,children:"\uD83D\uDEAB 계정+FP차단+삭제"}),!p&&t&&r&&(0,l.jsx)("button",{type:"button",className:"btn btn--outline post-del",onClick:()=>k(["ip","fp"]),disabled:b,title:"계정 + IP + FP 동시 차단. 가장 강한 옵션.",children:"\uD83D\uDEAB 계정+IP+FP차단+삭제"})]})}},69402:(e,a,t)=>{t.d(a,{sT:()=>l});function l(e,a,t=10){let n,s=Math.max(1,Math.floor(a)),i=Math.max(1,Math.floor(t)),r=(n=Math.max(1,Math.floor(s)),Number.isFinite(e)?Math.min(Math.max(1,Math.floor(e)),n):1),d=Math.floor((r-1)/i)*i+1,o=Math.min(s,d+i-1),c=Array.from({length:o-d+1},(e,a)=>d+a);return{page:r,totalPages:s,start:d,end:o,pages:c,prevBlockPage:d>1?d-1:null,nextBlockPage:o<s?o+1:null}}},71595:(e,a,t)=>{t.d(a,{NicknameDropdown:()=>o});var l=t(95155),n=t(12115),s=t(47650),i=t(73321);let r={admin:"관리자",manhwa_editor:"만화 관리자",uploader:"업로더",user:"회원",banned:"정지된 회원"},d={admin:"nick-dd-role-admin",manhwa_editor:"nick-dd-role-editor",uploader:"nick-dd-role-uploader",banned:"nick-dd-role-banned"};function o({userId:e,nickname:a,isBlockedByMe:t=!1,searchHref:c,isMe:h=!1,targetIsAdmin:u=!1,role:m,points:p,avatarUrl:b,className:f,effect:k,children:x}){let j=k?[k.rainbow?"nick-fx-rainbow":"",k.glow?"nick-fx-glow":"",k.bold?"nick-fx-bold":"",k.color?"nick-fx-color":"",k.frame?"nick-fx-frame":""].filter(Boolean).join(" "):"",g=k?.color?{color:k.color}:void 0,v=(0,i.useRouter)(),[N,$]=(0,n.useState)(!1),[y,w]=(0,n.useState)(t),[P,M]=(0,n.useTransition)(),[C,S]=(0,n.useState)(null),E=(0,n.useRef)(null),_=(0,n.useRef)(null);if((0,n.useEffect)(()=>{w(t)},[t]),(0,n.useLayoutEffect)(()=>{if(!N)return;let e=E.current?.getBoundingClientRect();if(e){let a=e.left;a+260>window.innerWidth-8&&(a=Math.max(8,e.right-260));let t=e.bottom+6;t+240>window.innerHeight-8&&(t=Math.max(8,e.top-240-6)),S({top:t,left:a})}},[N]),(0,n.useEffect)(()=>{if(!N)return;let e=e=>{let a=e.target;E.current?.contains(a)||_.current?.contains(a)||$(!1)},a=e=>{"Escape"===e.key&&$(!1)},t=()=>$(!1);return document.addEventListener("click",e),document.addEventListener("keydown",a),window.addEventListener("scroll",t,{passive:!0}),()=>{document.removeEventListener("click",e),document.removeEventListener("keydown",a),window.removeEventListener("scroll",t)}},[N]),h)return(0,l.jsx)("span",{className:`nick-dd nick-dd--me ${j} ${f??""}`,style:g,children:x});let I=m??"user",F=r[I]??"회원",T=d[I]??"";return(0,l.jsxs)("span",{className:`nick-dd ${j} ${f??""}`,style:g,children:[(0,l.jsx)("button",{ref:E,type:"button",className:"nick-dd-trigger",onClick:e=>{e.preventDefault(),e.stopPropagation(),$(e=>!e)},children:x}),N&&C&&"u">typeof document&&(0,s.createPortal)((0,l.jsxs)("div",{ref:_,className:"nick-dd-menu",role:"menu",style:{position:"fixed",top:C.top,left:C.left},children:[(0,l.jsxs)("div",{className:"nick-dd-card",children:[(0,l.jsx)("div",{className:"nick-dd-card-avatar",children:b?(0,l.jsx)("img",{src:b,alt:""}):(0,l.jsx)("span",{className:"nick-dd-card-avatar-fallback",children:a.slice(0,1)})}),(0,l.jsxs)("div",{className:"nick-dd-card-meta",children:[(0,l.jsx)("div",{className:"nick-dd-card-name",children:a}),(0,l.jsxs)("div",{className:"nick-dd-card-row",children:[(0,l.jsx)("span",{className:`nick-dd-card-role ${T}`,children:F}),"number"==typeof p&&p>0&&(0,l.jsxs)("span",{className:"nick-dd-card-pts",children:[p.toLocaleString(),"P"]})]})]})]}),(0,l.jsx)("div",{className:"nick-dd-divider"}),(0,l.jsxs)("div",{className:"nick-dd-list",children:[c&&(0,l.jsxs)("button",{type:"button",className:"nick-dd-item",onClick:()=>{c&&($(!1),v.push(`${c}?nick=${encodeURIComponent(a)}`))},disabled:P,children:[(0,l.jsx)("span",{className:"nick-dd-ico",children:"\uD83D\uDD0D"}),(0,l.jsxs)("span",{className:"nick-dd-item-body",children:[(0,l.jsx)("span",{className:"nick-dd-item-label",children:"이 닉네임으로 검색"}),(0,l.jsx)("span",{className:"nick-dd-item-sub",children:"이 게시판에서 작성 글 모아보기"})]})]}),!u&&(0,l.jsxs)("button",{type:"button",className:`nick-dd-item ${y?"nick-dd-item--unblock":"nick-dd-item--block"}`,onClick:()=>{P||M(async()=>{try{if(y){let a=await fetch(`/api/me/blocks?userId=${encodeURIComponent(e)}`,{method:"DELETE"}),t=await a.json().catch(()=>null);if(!a.ok||!t?.ok)return void alert(t?.error||`해제 실패 (${a.status})`);w(!1)}else{if(!confirm(`${a} 님을 차단할까요?
|
|
이 사용자의 글/댓글이 본인 화면에서 가려집니다.`))return;let t=(window.prompt("차단 사유 (선택 \xb7 본인만 볼 수 있어요)","")||"").trim().slice(0,200),l=await fetch("/api/me/blocks",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({targetUserId:e,reason:t})}),n=await l.json().catch(()=>null);if(!l.ok||!n?.ok)return void alert(n?.error||`차단 실패 (${l.status})`);w(!0)}$(!1),v.refresh()}catch(e){alert(e instanceof Error?e.message:"오류")}})},disabled:P,children:[(0,l.jsx)("span",{className:"nick-dd-ico",children:y?"✓":"\uD83D\uDEAB"}),(0,l.jsxs)("span",{className:"nick-dd-item-body",children:[(0,l.jsx)("span",{className:"nick-dd-item-label",children:y?"차단 해제":"차단"}),(0,l.jsx)("span",{className:"nick-dd-item-sub",children:y?"다시 보이게 합니다":"글/댓글이 내 화면에서 가려집니다"})]}),P&&(0,l.jsx)("span",{className:"nick-dd-spinner",children:"⌛"})]}),u&&(0,l.jsx)("div",{className:"nick-dd-disabled",children:"관리자는 차단할 수 없습니다"})]})]}),document.body)]})}},73321:(e,a,t)=>{var l=t(74645);t.o(l,"usePathname")&&t.d(a,{usePathname:function(){return l.usePathname}}),t.o(l,"useRouter")&&t.d(a,{useRouter:function(){return l.useRouter}}),t.o(l,"useSearchParams")&&t.d(a,{useSearchParams:function(){return l.useSearchParams}})}}]); |