From 7e80ef29a5a1a97a1f40d8c6f96cd4c6174f4353 Mon Sep 17 00:00:00 2001 From: "Lim, gyuseong" <100032171+gyural@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:46:29 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=B0=8F=20=EC=A0=95=EC=A0=81=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=B4=88=EA=B8=B0=20=EC=84=B8=ED=8C=85=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../static/assets/index-kQJbKSsj.css | 1 - .../resources/static/assets/index-pvm8va9e.js | 1349 ----------------- src/main/resources/static/favicon.ico | Bin 1588 -> 0 bytes src/main/resources/static/index.html | 26 - ...24\352\265\254\354\202\254\355\225\255.md" | 468 +----- ...24\352\265\254\354\202\254\355\225\255.md" | 139 +- 7 files changed, 137 insertions(+), 1848 deletions(-) delete mode 100644 src/main/resources/static/assets/index-kQJbKSsj.css delete mode 100644 src/main/resources/static/assets/index-pvm8va9e.js delete mode 100644 src/main/resources/static/favicon.ico delete mode 100644 src/main/resources/static/index.html diff --git a/build.gradle b/build.gradle index bac2cf3c5..d8bcd32fa 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ jacocoTestReport { } group = 'com.sprint.mission' -version = '2.0-M9' +version = '2.1-M10' java { toolchain { diff --git a/src/main/resources/static/assets/index-kQJbKSsj.css b/src/main/resources/static/assets/index-kQJbKSsj.css deleted file mode 100644 index 096eb4112..000000000 --- a/src/main/resources/static/assets/index-kQJbKSsj.css +++ /dev/null @@ -1 +0,0 @@ -:root{font-family:Inter,system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{margin:0;display:flex;place-items:center;min-width:320px;min-height:100vh}h1{font-size:3.2em;line-height:1.1}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}} diff --git a/src/main/resources/static/assets/index-pvm8va9e.js b/src/main/resources/static/assets/index-pvm8va9e.js deleted file mode 100644 index b43a85536..000000000 --- a/src/main/resources/static/assets/index-pvm8va9e.js +++ /dev/null @@ -1,1349 +0,0 @@ -var xg=Object.defineProperty;var wg=(r,i,s)=>i in r?xg(r,i,{enumerable:!0,configurable:!0,writable:!0,value:s}):r[i]=s;var sf=(r,i,s)=>wg(r,typeof i!="symbol"?i+"":i,s);(function(){const i=document.createElement("link").relList;if(i&&i.supports&&i.supports("modulepreload"))return;for(const c of document.querySelectorAll('link[rel="modulepreload"]'))l(c);new MutationObserver(c=>{for(const d of c)if(d.type==="childList")for(const p of d.addedNodes)p.tagName==="LINK"&&p.rel==="modulepreload"&&l(p)}).observe(document,{childList:!0,subtree:!0});function s(c){const d={};return c.integrity&&(d.integrity=c.integrity),c.referrerPolicy&&(d.referrerPolicy=c.referrerPolicy),c.crossOrigin==="use-credentials"?d.credentials="include":c.crossOrigin==="anonymous"?d.credentials="omit":d.credentials="same-origin",d}function l(c){if(c.ep)return;c.ep=!0;const d=s(c);fetch(c.href,d)}})();function Sg(r){return r&&r.__esModule&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r}var wa={exports:{}},vo={},Sa={exports:{}},pe={};/** - * @license React - * react.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var lf;function Cg(){if(lf)return pe;lf=1;var r=Symbol.for("react.element"),i=Symbol.for("react.portal"),s=Symbol.for("react.fragment"),l=Symbol.for("react.strict_mode"),c=Symbol.for("react.profiler"),d=Symbol.for("react.provider"),p=Symbol.for("react.context"),g=Symbol.for("react.forward_ref"),w=Symbol.for("react.suspense"),v=Symbol.for("react.memo"),S=Symbol.for("react.lazy"),j=Symbol.iterator;function R(C){return C===null||typeof C!="object"?null:(C=j&&C[j]||C["@@iterator"],typeof C=="function"?C:null)}var L={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},T=Object.assign,O={};function _(C,D,le){this.props=C,this.context=D,this.refs=O,this.updater=le||L}_.prototype.isReactComponent={},_.prototype.setState=function(C,D){if(typeof C!="object"&&typeof C!="function"&&C!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,C,D,"setState")},_.prototype.forceUpdate=function(C){this.updater.enqueueForceUpdate(this,C,"forceUpdate")};function b(){}b.prototype=_.prototype;function U(C,D,le){this.props=C,this.context=D,this.refs=O,this.updater=le||L}var B=U.prototype=new b;B.constructor=U,T(B,_.prototype),B.isPureReactComponent=!0;var W=Array.isArray,I=Object.prototype.hasOwnProperty,M={current:null},V={key:!0,ref:!0,__self:!0,__source:!0};function ne(C,D,le){var ue,he={},fe=null,Ce=null;if(D!=null)for(ue in D.ref!==void 0&&(Ce=D.ref),D.key!==void 0&&(fe=""+D.key),D)I.call(D,ue)&&!V.hasOwnProperty(ue)&&(he[ue]=D[ue]);var ge=arguments.length-2;if(ge===1)he.children=le;else if(1>>1,D=Y[C];if(0>>1;Cc(he,Q))fec(Ce,he)?(Y[C]=Ce,Y[fe]=Q,C=fe):(Y[C]=he,Y[ue]=Q,C=ue);else if(fec(Ce,Q))Y[C]=Ce,Y[fe]=Q,C=fe;else break e}}return te}function c(Y,te){var Q=Y.sortIndex-te.sortIndex;return Q!==0?Q:Y.id-te.id}if(typeof performance=="object"&&typeof performance.now=="function"){var d=performance;r.unstable_now=function(){return d.now()}}else{var p=Date,g=p.now();r.unstable_now=function(){return p.now()-g}}var w=[],v=[],S=1,j=null,R=3,L=!1,T=!1,O=!1,_=typeof setTimeout=="function"?setTimeout:null,b=typeof clearTimeout=="function"?clearTimeout:null,U=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function B(Y){for(var te=s(v);te!==null;){if(te.callback===null)l(v);else if(te.startTime<=Y)l(v),te.sortIndex=te.expirationTime,i(w,te);else break;te=s(v)}}function W(Y){if(O=!1,B(Y),!T)if(s(w)!==null)T=!0,Ue(I);else{var te=s(v);te!==null&&je(W,te.startTime-Y)}}function I(Y,te){T=!1,O&&(O=!1,b(ne),ne=-1),L=!0;var Q=R;try{for(B(te),j=s(w);j!==null&&(!(j.expirationTime>te)||Y&&!ie());){var C=j.callback;if(typeof C=="function"){j.callback=null,R=j.priorityLevel;var D=C(j.expirationTime<=te);te=r.unstable_now(),typeof D=="function"?j.callback=D:j===s(w)&&l(w),B(te)}else l(w);j=s(w)}if(j!==null)var le=!0;else{var ue=s(v);ue!==null&&je(W,ue.startTime-te),le=!1}return le}finally{j=null,R=Q,L=!1}}var M=!1,V=null,ne=-1,ye=5,Ie=-1;function ie(){return!(r.unstable_now()-IeY||125C?(Y.sortIndex=Q,i(v,Y),s(w)===null&&Y===s(v)&&(O?(b(ne),ne=-1):O=!0,je(W,Q-C))):(Y.sortIndex=D,i(w,Y),T||L||(T=!0,Ue(I))),Y},r.unstable_shouldYield=ie,r.unstable_wrapCallback=function(Y){var te=R;return function(){var Q=R;R=te;try{return Y.apply(this,arguments)}finally{R=Q}}}}(Ea)),Ea}var ff;function Ag(){return ff||(ff=1,ka.exports=jg()),ka.exports}/** - * @license React - * react-dom.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var pf;function Rg(){if(pf)return dt;pf=1;var r=tu(),i=Ag();function s(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),w=Object.prototype.hasOwnProperty,v=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,S={},j={};function R(e){return w.call(j,e)?!0:w.call(S,e)?!1:v.test(e)?j[e]=!0:(S[e]=!0,!1)}function L(e,t,n,o){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return o?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function T(e,t,n,o){if(t===null||typeof t>"u"||L(e,t,n,o))return!0;if(o)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function O(e,t,n,o,a,u,f){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=o,this.attributeNamespace=a,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=u,this.removeEmptyString=f}var _={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){_[e]=new O(e,0,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];_[t]=new O(t,1,!1,e[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){_[e]=new O(e,2,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){_[e]=new O(e,2,!1,e,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){_[e]=new O(e,3,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){_[e]=new O(e,3,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){_[e]=new O(e,4,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){_[e]=new O(e,6,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){_[e]=new O(e,5,!1,e.toLowerCase(),null,!1,!1)});var b=/[\-:]([a-z])/g;function U(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(b,U);_[t]=new O(t,1,!1,e,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(b,U);_[t]=new O(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(b,U);_[t]=new O(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){_[e]=new O(e,1,!1,e.toLowerCase(),null,!1,!1)}),_.xlinkHref=new O("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){_[e]=new O(e,1,!1,e.toLowerCase(),null,!0,!0)});function B(e,t,n,o){var a=_.hasOwnProperty(t)?_[t]:null;(a!==null?a.type!==0:o||!(2m||a[f]!==u[m]){var y=` -`+a[f].replace(" at new "," at ");return e.displayName&&y.includes("")&&(y=y.replace("",e.displayName)),y}while(1<=f&&0<=m);break}}}finally{le=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?D(e):""}function he(e){switch(e.tag){case 5:return D(e.type);case 16:return D("Lazy");case 13:return D("Suspense");case 19:return D("SuspenseList");case 0:case 2:case 15:return e=ue(e.type,!1),e;case 11:return e=ue(e.type.render,!1),e;case 1:return e=ue(e.type,!0),e;default:return""}}function fe(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case V:return"Fragment";case M:return"Portal";case ye:return"Profiler";case ne:return"StrictMode";case me:return"Suspense";case _e:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case ie:return(e.displayName||"Context")+".Consumer";case Ie:return(e._context.displayName||"Context")+".Provider";case de:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Se:return t=e.displayName||null,t!==null?t:fe(e.type)||"Memo";case Ue:t=e._payload,e=e._init;try{return fe(e(t))}catch{}}return null}function Ce(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return fe(t);case 8:return t===ne?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function ge(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function xe(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Ge(e){var t=xe(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),o=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var a=n.get,u=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return a.call(this)},set:function(f){o=""+f,u.call(this,f)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return o},setValue:function(f){o=""+f},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Yt(e){e._valueTracker||(e._valueTracker=Ge(e))}function Tt(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),o="";return e&&(o=xe(e)?e.checked?"true":"false":e.value),e=o,e!==n?(t.setValue(e),!0):!1}function zo(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Rs(e,t){var n=t.checked;return Q({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function fu(e,t){var n=t.defaultValue==null?"":t.defaultValue,o=t.checked!=null?t.checked:t.defaultChecked;n=ge(t.value!=null?t.value:n),e._wrapperState={initialChecked:o,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function pu(e,t){t=t.checked,t!=null&&B(e,"checked",t,!1)}function Ps(e,t){pu(e,t);var n=ge(t.value),o=t.type;if(n!=null)o==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(o==="submit"||o==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?Ts(e,t.type,n):t.hasOwnProperty("defaultValue")&&Ts(e,t.type,ge(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function hu(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var o=t.type;if(!(o!=="submit"&&o!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function Ts(e,t,n){(t!=="number"||zo(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Or=Array.isArray;function Qn(e,t,n,o){if(e=e.options,t){t={};for(var a=0;a"+t.valueOf().toString()+"",t=$o.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Mr(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Lr={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Eh=["Webkit","ms","Moz","O"];Object.keys(Lr).forEach(function(e){Eh.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Lr[t]=Lr[e]})});function wu(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Lr.hasOwnProperty(e)&&Lr[e]?(""+t).trim():t+"px"}function Su(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var o=n.indexOf("--")===0,a=wu(n,t[n],o);n==="float"&&(n="cssFloat"),o?e.setProperty(n,a):e[n]=a}}var jh=Q({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Os(e,t){if(t){if(jh[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(s(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(s(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(s(61))}if(t.style!=null&&typeof t.style!="object")throw Error(s(62))}}function Ms(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Ls=null;function Is(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Ds=null,Gn=null,Kn=null;function Cu(e){if(e=no(e)){if(typeof Ds!="function")throw Error(s(280));var t=e.stateNode;t&&(t=li(t),Ds(e.stateNode,e.type,t))}}function ku(e){Gn?Kn?Kn.push(e):Kn=[e]:Gn=e}function Eu(){if(Gn){var e=Gn,t=Kn;if(Kn=Gn=null,Cu(e),t)for(e=0;e>>=0,e===0?32:31-(Dh(e)/zh|0)|0}var Ho=64,Vo=4194304;function $r(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Wo(e,t){var n=e.pendingLanes;if(n===0)return 0;var o=0,a=e.suspendedLanes,u=e.pingedLanes,f=n&268435455;if(f!==0){var m=f&~a;m!==0?o=$r(m):(u&=f,u!==0&&(o=$r(u)))}else f=n&~a,f!==0?o=$r(f):u!==0&&(o=$r(u));if(o===0)return 0;if(t!==0&&t!==o&&!(t&a)&&(a=o&-o,u=t&-t,a>=u||a===16&&(u&4194240)!==0))return t;if(o&4&&(o|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=o;0n;n++)t.push(e);return t}function Fr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-_t(t),e[t]=n}function bh(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var o=e.eventTimes;for(e=e.expirationTimes;0=qr),Ju=" ",Zu=!1;function ec(e,t){switch(e){case"keyup":return mm.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function tc(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Zn=!1;function ym(e,t){switch(e){case"compositionend":return tc(t);case"keypress":return t.which!==32?null:(Zu=!0,Ju);case"textInput":return e=t.data,e===Ju&&Zu?null:e;default:return null}}function vm(e,t){if(Zn)return e==="compositionend"||!el&&ec(e,t)?(e=Yu(),Ko=Qs=an=null,Zn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=o}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=ac(n)}}function cc(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?cc(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function dc(){for(var e=window,t=zo();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=zo(e.document)}return t}function rl(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Rm(e){var t=dc(),n=e.focusedElem,o=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&cc(n.ownerDocument.documentElement,n)){if(o!==null&&rl(n)){if(t=o.start,e=o.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var a=n.textContent.length,u=Math.min(o.start,a);o=o.end===void 0?u:Math.min(o.end,a),!e.extend&&u>o&&(a=o,o=u,u=a),a=uc(n,u);var f=uc(n,o);a&&f&&(e.rangeCount!==1||e.anchorNode!==a.node||e.anchorOffset!==a.offset||e.focusNode!==f.node||e.focusOffset!==f.offset)&&(t=t.createRange(),t.setStart(a.node,a.offset),e.removeAllRanges(),u>o?(e.addRange(t),e.extend(f.node,f.offset)):(t.setEnd(f.node,f.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,er=null,ol=null,Xr=null,il=!1;function fc(e,t,n){var o=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;il||er==null||er!==zo(o)||(o=er,"selectionStart"in o&&rl(o)?o={start:o.selectionStart,end:o.selectionEnd}:(o=(o.ownerDocument&&o.ownerDocument.defaultView||window).getSelection(),o={anchorNode:o.anchorNode,anchorOffset:o.anchorOffset,focusNode:o.focusNode,focusOffset:o.focusOffset}),Xr&&Kr(Xr,o)||(Xr=o,o=oi(ol,"onSelect"),0ir||(e.current=yl[ir],yl[ir]=null,ir--)}function Ae(e,t){ir++,yl[ir]=e.current,e.current=t}var fn={},Je=dn(fn),st=dn(!1),Tn=fn;function sr(e,t){var n=e.type.contextTypes;if(!n)return fn;var o=e.stateNode;if(o&&o.__reactInternalMemoizedUnmaskedChildContext===t)return o.__reactInternalMemoizedMaskedChildContext;var a={},u;for(u in n)a[u]=t[u];return o&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=a),a}function lt(e){return e=e.childContextTypes,e!=null}function ai(){Pe(st),Pe(Je)}function Rc(e,t,n){if(Je.current!==fn)throw Error(s(168));Ae(Je,t),Ae(st,n)}function Pc(e,t,n){var o=e.stateNode;if(t=t.childContextTypes,typeof o.getChildContext!="function")return n;o=o.getChildContext();for(var a in o)if(!(a in t))throw Error(s(108,Ce(e)||"Unknown",a));return Q({},n,o)}function ui(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||fn,Tn=Je.current,Ae(Je,e),Ae(st,st.current),!0}function Tc(e,t,n){var o=e.stateNode;if(!o)throw Error(s(169));n?(e=Pc(e,t,Tn),o.__reactInternalMemoizedMergedChildContext=e,Pe(st),Pe(Je),Ae(Je,e)):Pe(st),Ae(st,n)}var Qt=null,ci=!1,vl=!1;function _c(e){Qt===null?Qt=[e]:Qt.push(e)}function Fm(e){ci=!0,_c(e)}function pn(){if(!vl&&Qt!==null){vl=!0;var e=0,t=Ee;try{var n=Qt;for(Ee=1;e>=f,a-=f,Gt=1<<32-_t(t)+a|n<se?(qe=oe,oe=null):qe=oe.sibling;var we=z(E,oe,A[se],H);if(we===null){oe===null&&(oe=qe);break}e&&oe&&we.alternate===null&&t(E,oe),x=u(we,x,se),re===null?ee=we:re.sibling=we,re=we,oe=qe}if(se===A.length)return n(E,oe),Ne&&Nn(E,se),ee;if(oe===null){for(;sese?(qe=oe,oe=null):qe=oe.sibling;var Cn=z(E,oe,we.value,H);if(Cn===null){oe===null&&(oe=qe);break}e&&oe&&Cn.alternate===null&&t(E,oe),x=u(Cn,x,se),re===null?ee=Cn:re.sibling=Cn,re=Cn,oe=qe}if(we.done)return n(E,oe),Ne&&Nn(E,se),ee;if(oe===null){for(;!we.done;se++,we=A.next())we=F(E,we.value,H),we!==null&&(x=u(we,x,se),re===null?ee=we:re.sibling=we,re=we);return Ne&&Nn(E,se),ee}for(oe=o(E,oe);!we.done;se++,we=A.next())we=G(oe,E,se,we.value,H),we!==null&&(e&&we.alternate!==null&&oe.delete(we.key===null?se:we.key),x=u(we,x,se),re===null?ee=we:re.sibling=we,re=we);return e&&oe.forEach(function(vg){return t(E,vg)}),Ne&&Nn(E,se),ee}function $e(E,x,A,H){if(typeof A=="object"&&A!==null&&A.type===V&&A.key===null&&(A=A.props.children),typeof A=="object"&&A!==null){switch(A.$$typeof){case I:e:{for(var ee=A.key,re=x;re!==null;){if(re.key===ee){if(ee=A.type,ee===V){if(re.tag===7){n(E,re.sibling),x=a(re,A.props.children),x.return=E,E=x;break e}}else if(re.elementType===ee||typeof ee=="object"&&ee!==null&&ee.$$typeof===Ue&&Dc(ee)===re.type){n(E,re.sibling),x=a(re,A.props),x.ref=ro(E,re,A),x.return=E,E=x;break e}n(E,re);break}else t(E,re);re=re.sibling}A.type===V?(x=Fn(A.props.children,E.mode,H,A.key),x.return=E,E=x):(H=$i(A.type,A.key,A.props,null,E.mode,H),H.ref=ro(E,x,A),H.return=E,E=H)}return f(E);case M:e:{for(re=A.key;x!==null;){if(x.key===re)if(x.tag===4&&x.stateNode.containerInfo===A.containerInfo&&x.stateNode.implementation===A.implementation){n(E,x.sibling),x=a(x,A.children||[]),x.return=E,E=x;break e}else{n(E,x);break}else t(E,x);x=x.sibling}x=ma(A,E.mode,H),x.return=E,E=x}return f(E);case Ue:return re=A._init,$e(E,x,re(A._payload),H)}if(Or(A))return J(E,x,A,H);if(te(A))return Z(E,x,A,H);hi(E,A)}return typeof A=="string"&&A!==""||typeof A=="number"?(A=""+A,x!==null&&x.tag===6?(n(E,x.sibling),x=a(x,A),x.return=E,E=x):(n(E,x),x=ha(A,E.mode,H),x.return=E,E=x),f(E)):n(E,x)}return $e}var cr=zc(!0),$c=zc(!1),mi=dn(null),gi=null,dr=null,El=null;function jl(){El=dr=gi=null}function Al(e){var t=mi.current;Pe(mi),e._currentValue=t}function Rl(e,t,n){for(;e!==null;){var o=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,o!==null&&(o.childLanes|=t)):o!==null&&(o.childLanes&t)!==t&&(o.childLanes|=t),e===n)break;e=e.return}}function fr(e,t){gi=e,El=dr=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(at=!0),e.firstContext=null)}function Et(e){var t=e._currentValue;if(El!==e)if(e={context:e,memoizedValue:t,next:null},dr===null){if(gi===null)throw Error(s(308));dr=e,gi.dependencies={lanes:0,firstContext:e}}else dr=dr.next=e;return t}var On=null;function Pl(e){On===null?On=[e]:On.push(e)}function Fc(e,t,n,o){var a=t.interleaved;return a===null?(n.next=n,Pl(t)):(n.next=a.next,a.next=n),t.interleaved=n,Xt(e,o)}function Xt(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var hn=!1;function Tl(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Bc(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Jt(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function mn(e,t,n){var o=e.updateQueue;if(o===null)return null;if(o=o.shared,ve&2){var a=o.pending;return a===null?t.next=t:(t.next=a.next,a.next=t),o.pending=t,Xt(e,n)}return a=o.interleaved,a===null?(t.next=t,Pl(o)):(t.next=a.next,a.next=t),o.interleaved=t,Xt(e,n)}function yi(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var o=t.lanes;o&=e.pendingLanes,n|=o,t.lanes=n,Hs(e,n)}}function bc(e,t){var n=e.updateQueue,o=e.alternate;if(o!==null&&(o=o.updateQueue,n===o)){var a=null,u=null;if(n=n.firstBaseUpdate,n!==null){do{var f={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};u===null?a=u=f:u=u.next=f,n=n.next}while(n!==null);u===null?a=u=t:u=u.next=t}else a=u=t;n={baseState:o.baseState,firstBaseUpdate:a,lastBaseUpdate:u,shared:o.shared,effects:o.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function vi(e,t,n,o){var a=e.updateQueue;hn=!1;var u=a.firstBaseUpdate,f=a.lastBaseUpdate,m=a.shared.pending;if(m!==null){a.shared.pending=null;var y=m,P=y.next;y.next=null,f===null?u=P:f.next=P,f=y;var $=e.alternate;$!==null&&($=$.updateQueue,m=$.lastBaseUpdate,m!==f&&(m===null?$.firstBaseUpdate=P:m.next=P,$.lastBaseUpdate=y))}if(u!==null){var F=a.baseState;f=0,$=P=y=null,m=u;do{var z=m.lane,G=m.eventTime;if((o&z)===z){$!==null&&($=$.next={eventTime:G,lane:0,tag:m.tag,payload:m.payload,callback:m.callback,next:null});e:{var J=e,Z=m;switch(z=t,G=n,Z.tag){case 1:if(J=Z.payload,typeof J=="function"){F=J.call(G,F,z);break e}F=J;break e;case 3:J.flags=J.flags&-65537|128;case 0:if(J=Z.payload,z=typeof J=="function"?J.call(G,F,z):J,z==null)break e;F=Q({},F,z);break e;case 2:hn=!0}}m.callback!==null&&m.lane!==0&&(e.flags|=64,z=a.effects,z===null?a.effects=[m]:z.push(m))}else G={eventTime:G,lane:z,tag:m.tag,payload:m.payload,callback:m.callback,next:null},$===null?(P=$=G,y=F):$=$.next=G,f|=z;if(m=m.next,m===null){if(m=a.shared.pending,m===null)break;z=m,m=z.next,z.next=null,a.lastBaseUpdate=z,a.shared.pending=null}}while(!0);if($===null&&(y=F),a.baseState=y,a.firstBaseUpdate=P,a.lastBaseUpdate=$,t=a.shared.interleaved,t!==null){a=t;do f|=a.lane,a=a.next;while(a!==t)}else u===null&&(a.shared.lanes=0);In|=f,e.lanes=f,e.memoizedState=F}}function Uc(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var o=Ll.transition;Ll.transition={};try{e(!1),t()}finally{Ee=n,Ll.transition=o}}function ld(){return jt().memoizedState}function Hm(e,t,n){var o=xn(e);if(n={lane:o,action:n,hasEagerState:!1,eagerState:null,next:null},ad(e))ud(t,n);else if(n=Fc(e,t,n,o),n!==null){var a=it();Dt(n,e,o,a),cd(n,t,o)}}function Vm(e,t,n){var o=xn(e),a={lane:o,action:n,hasEagerState:!1,eagerState:null,next:null};if(ad(e))ud(t,a);else{var u=e.alternate;if(e.lanes===0&&(u===null||u.lanes===0)&&(u=t.lastRenderedReducer,u!==null))try{var f=t.lastRenderedState,m=u(f,n);if(a.hasEagerState=!0,a.eagerState=m,Nt(m,f)){var y=t.interleaved;y===null?(a.next=a,Pl(t)):(a.next=y.next,y.next=a),t.interleaved=a;return}}catch{}finally{}n=Fc(e,t,a,o),n!==null&&(a=it(),Dt(n,e,o,a),cd(n,t,o))}}function ad(e){var t=e.alternate;return e===Le||t!==null&&t===Le}function ud(e,t){lo=Si=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function cd(e,t,n){if(n&4194240){var o=t.lanes;o&=e.pendingLanes,n|=o,t.lanes=n,Hs(e,n)}}var Ei={readContext:Et,useCallback:Ze,useContext:Ze,useEffect:Ze,useImperativeHandle:Ze,useInsertionEffect:Ze,useLayoutEffect:Ze,useMemo:Ze,useReducer:Ze,useRef:Ze,useState:Ze,useDebugValue:Ze,useDeferredValue:Ze,useTransition:Ze,useMutableSource:Ze,useSyncExternalStore:Ze,useId:Ze,unstable_isNewReconciler:!1},Wm={readContext:Et,useCallback:function(e,t){return Ut().memoizedState=[e,t===void 0?null:t],e},useContext:Et,useEffect:Zc,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Ci(4194308,4,nd.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Ci(4194308,4,e,t)},useInsertionEffect:function(e,t){return Ci(4,2,e,t)},useMemo:function(e,t){var n=Ut();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var o=Ut();return t=n!==void 0?n(t):t,o.memoizedState=o.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},o.queue=e,e=e.dispatch=Hm.bind(null,Le,e),[o.memoizedState,e]},useRef:function(e){var t=Ut();return e={current:e},t.memoizedState=e},useState:Xc,useDebugValue:bl,useDeferredValue:function(e){return Ut().memoizedState=e},useTransition:function(){var e=Xc(!1),t=e[0];return e=Um.bind(null,e[1]),Ut().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var o=Le,a=Ut();if(Ne){if(n===void 0)throw Error(s(407));n=n()}else{if(n=t(),Ye===null)throw Error(s(349));Ln&30||Yc(o,t,n)}a.memoizedState=n;var u={value:n,getSnapshot:t};return a.queue=u,Zc(Qc.bind(null,o,u,e),[e]),o.flags|=2048,co(9,qc.bind(null,o,u,n,t),void 0,null),n},useId:function(){var e=Ut(),t=Ye.identifierPrefix;if(Ne){var n=Kt,o=Gt;n=(o&~(1<<32-_t(o)-1)).toString(32)+n,t=":"+t+"R"+n,n=ao++,0<\/script>",e=e.removeChild(e.firstChild)):typeof o.is=="string"?e=f.createElement(n,{is:o.is}):(e=f.createElement(n),n==="select"&&(f=e,o.multiple?f.multiple=!0:o.size&&(f.size=o.size))):e=f.createElementNS(e,n),e[Bt]=t,e[to]=o,_d(e,t,!1,!1),t.stateNode=e;e:{switch(f=Ms(n,o),n){case"dialog":Re("cancel",e),Re("close",e),a=o;break;case"iframe":case"object":case"embed":Re("load",e),a=o;break;case"video":case"audio":for(a=0;ayr&&(t.flags|=128,o=!0,fo(u,!1),t.lanes=4194304)}else{if(!o)if(e=xi(f),e!==null){if(t.flags|=128,o=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),fo(u,!0),u.tail===null&&u.tailMode==="hidden"&&!f.alternate&&!Ne)return et(t),null}else 2*ze()-u.renderingStartTime>yr&&n!==1073741824&&(t.flags|=128,o=!0,fo(u,!1),t.lanes=4194304);u.isBackwards?(f.sibling=t.child,t.child=f):(n=u.last,n!==null?n.sibling=f:t.child=f,u.last=f)}return u.tail!==null?(t=u.tail,u.rendering=t,u.tail=t.sibling,u.renderingStartTime=ze(),t.sibling=null,n=Me.current,Ae(Me,o?n&1|2:n&1),t):(et(t),null);case 22:case 23:return da(),o=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==o&&(t.flags|=8192),o&&t.mode&1?yt&1073741824&&(et(t),t.subtreeFlags&6&&(t.flags|=8192)):et(t),null;case 24:return null;case 25:return null}throw Error(s(156,t.tag))}function Zm(e,t){switch(wl(t),t.tag){case 1:return lt(t.type)&&ai(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return pr(),Pe(st),Pe(Je),Ml(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return Nl(t),null;case 13:if(Pe(Me),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(s(340));ur()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return Pe(Me),null;case 4:return pr(),null;case 10:return Al(t.type._context),null;case 22:case 23:return da(),null;case 24:return null;default:return null}}var Pi=!1,tt=!1,eg=typeof WeakSet=="function"?WeakSet:Set,X=null;function mr(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(o){De(e,t,o)}else n.current=null}function Zl(e,t,n){try{n()}catch(o){De(e,t,o)}}var Md=!1;function tg(e,t){if(dl=Qo,e=dc(),rl(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var o=n.getSelection&&n.getSelection();if(o&&o.rangeCount!==0){n=o.anchorNode;var a=o.anchorOffset,u=o.focusNode;o=o.focusOffset;try{n.nodeType,u.nodeType}catch{n=null;break e}var f=0,m=-1,y=-1,P=0,$=0,F=e,z=null;t:for(;;){for(var G;F!==n||a!==0&&F.nodeType!==3||(m=f+a),F!==u||o!==0&&F.nodeType!==3||(y=f+o),F.nodeType===3&&(f+=F.nodeValue.length),(G=F.firstChild)!==null;)z=F,F=G;for(;;){if(F===e)break t;if(z===n&&++P===a&&(m=f),z===u&&++$===o&&(y=f),(G=F.nextSibling)!==null)break;F=z,z=F.parentNode}F=G}n=m===-1||y===-1?null:{start:m,end:y}}else n=null}n=n||{start:0,end:0}}else n=null;for(fl={focusedElem:e,selectionRange:n},Qo=!1,X=t;X!==null;)if(t=X,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,X=e;else for(;X!==null;){t=X;try{var J=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(J!==null){var Z=J.memoizedProps,$e=J.memoizedState,E=t.stateNode,x=E.getSnapshotBeforeUpdate(t.elementType===t.type?Z:Mt(t.type,Z),$e);E.__reactInternalSnapshotBeforeUpdate=x}break;case 3:var A=t.stateNode.containerInfo;A.nodeType===1?A.textContent="":A.nodeType===9&&A.documentElement&&A.removeChild(A.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(s(163))}}catch(H){De(t,t.return,H)}if(e=t.sibling,e!==null){e.return=t.return,X=e;break}X=t.return}return J=Md,Md=!1,J}function po(e,t,n){var o=t.updateQueue;if(o=o!==null?o.lastEffect:null,o!==null){var a=o=o.next;do{if((a.tag&e)===e){var u=a.destroy;a.destroy=void 0,u!==void 0&&Zl(t,n,u)}a=a.next}while(a!==o)}}function Ti(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var o=n.create;n.destroy=o()}n=n.next}while(n!==t)}}function ea(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function Ld(e){var t=e.alternate;t!==null&&(e.alternate=null,Ld(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Bt],delete t[to],delete t[gl],delete t[zm],delete t[$m])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function Id(e){return e.tag===5||e.tag===3||e.tag===4}function Dd(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Id(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function ta(e,t,n){var o=e.tag;if(o===5||o===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=si));else if(o!==4&&(e=e.child,e!==null))for(ta(e,t,n),e=e.sibling;e!==null;)ta(e,t,n),e=e.sibling}function na(e,t,n){var o=e.tag;if(o===5||o===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(o!==4&&(e=e.child,e!==null))for(na(e,t,n),e=e.sibling;e!==null;)na(e,t,n),e=e.sibling}var Ke=null,Lt=!1;function gn(e,t,n){for(n=n.child;n!==null;)zd(e,t,n),n=n.sibling}function zd(e,t,n){if(Ft&&typeof Ft.onCommitFiberUnmount=="function")try{Ft.onCommitFiberUnmount(Uo,n)}catch{}switch(n.tag){case 5:tt||mr(n,t);case 6:var o=Ke,a=Lt;Ke=null,gn(e,t,n),Ke=o,Lt=a,Ke!==null&&(Lt?(e=Ke,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):Ke.removeChild(n.stateNode));break;case 18:Ke!==null&&(Lt?(e=Ke,n=n.stateNode,e.nodeType===8?ml(e.parentNode,n):e.nodeType===1&&ml(e,n),Vr(e)):ml(Ke,n.stateNode));break;case 4:o=Ke,a=Lt,Ke=n.stateNode.containerInfo,Lt=!0,gn(e,t,n),Ke=o,Lt=a;break;case 0:case 11:case 14:case 15:if(!tt&&(o=n.updateQueue,o!==null&&(o=o.lastEffect,o!==null))){a=o=o.next;do{var u=a,f=u.destroy;u=u.tag,f!==void 0&&(u&2||u&4)&&Zl(n,t,f),a=a.next}while(a!==o)}gn(e,t,n);break;case 1:if(!tt&&(mr(n,t),o=n.stateNode,typeof o.componentWillUnmount=="function"))try{o.props=n.memoizedProps,o.state=n.memoizedState,o.componentWillUnmount()}catch(m){De(n,t,m)}gn(e,t,n);break;case 21:gn(e,t,n);break;case 22:n.mode&1?(tt=(o=tt)||n.memoizedState!==null,gn(e,t,n),tt=o):gn(e,t,n);break;default:gn(e,t,n)}}function $d(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new eg),t.forEach(function(o){var a=cg.bind(null,e,o);n.has(o)||(n.add(o),o.then(a,a))})}}function It(e,t){var n=t.deletions;if(n!==null)for(var o=0;oa&&(a=f),o&=~u}if(o=a,o=ze()-o,o=(120>o?120:480>o?480:1080>o?1080:1920>o?1920:3e3>o?3e3:4320>o?4320:1960*rg(o/1960))-o,10e?16:e,vn===null)var o=!1;else{if(e=vn,vn=null,Li=0,ve&6)throw Error(s(331));var a=ve;for(ve|=4,X=e.current;X!==null;){var u=X,f=u.child;if(X.flags&16){var m=u.deletions;if(m!==null){for(var y=0;yze()-ia?zn(e,0):oa|=n),ct(e,t)}function Xd(e,t){t===0&&(e.mode&1?(t=Vo,Vo<<=1,!(Vo&130023424)&&(Vo=4194304)):t=1);var n=it();e=Xt(e,t),e!==null&&(Fr(e,t,n),ct(e,n))}function ug(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Xd(e,n)}function cg(e,t){var n=0;switch(e.tag){case 13:var o=e.stateNode,a=e.memoizedState;a!==null&&(n=a.retryLane);break;case 19:o=e.stateNode;break;default:throw Error(s(314))}o!==null&&o.delete(t),Xd(e,n)}var Jd;Jd=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||st.current)at=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return at=!1,Xm(e,t,n);at=!!(e.flags&131072)}else at=!1,Ne&&t.flags&1048576&&Nc(t,fi,t.index);switch(t.lanes=0,t.tag){case 2:var o=t.type;Ri(e,t),e=t.pendingProps;var a=sr(t,Je.current);fr(t,n),a=Dl(null,t,o,e,a,n);var u=zl();return t.flags|=1,typeof a=="object"&&a!==null&&typeof a.render=="function"&&a.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,lt(o)?(u=!0,ui(t)):u=!1,t.memoizedState=a.state!==null&&a.state!==void 0?a.state:null,Tl(t),a.updater=ji,t.stateNode=a,a._reactInternals=t,Hl(t,o,e,n),t=ql(null,t,o,!0,u,n)):(t.tag=0,Ne&&u&&xl(t),ot(null,t,a,n),t=t.child),t;case 16:o=t.elementType;e:{switch(Ri(e,t),e=t.pendingProps,a=o._init,o=a(o._payload),t.type=o,a=t.tag=fg(o),e=Mt(o,e),a){case 0:t=Yl(null,t,o,e,n);break e;case 1:t=Ed(null,t,o,e,n);break e;case 11:t=xd(null,t,o,e,n);break e;case 14:t=wd(null,t,o,Mt(o.type,e),n);break e}throw Error(s(306,o,""))}return t;case 0:return o=t.type,a=t.pendingProps,a=t.elementType===o?a:Mt(o,a),Yl(e,t,o,a,n);case 1:return o=t.type,a=t.pendingProps,a=t.elementType===o?a:Mt(o,a),Ed(e,t,o,a,n);case 3:e:{if(jd(t),e===null)throw Error(s(387));o=t.pendingProps,u=t.memoizedState,a=u.element,Bc(e,t),vi(t,o,null,n);var f=t.memoizedState;if(o=f.element,u.isDehydrated)if(u={element:o,isDehydrated:!1,cache:f.cache,pendingSuspenseBoundaries:f.pendingSuspenseBoundaries,transitions:f.transitions},t.updateQueue.baseState=u,t.memoizedState=u,t.flags&256){a=hr(Error(s(423)),t),t=Ad(e,t,o,n,a);break e}else if(o!==a){a=hr(Error(s(424)),t),t=Ad(e,t,o,n,a);break e}else for(gt=cn(t.stateNode.containerInfo.firstChild),mt=t,Ne=!0,Ot=null,n=$c(t,null,o,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(ur(),o===a){t=Zt(e,t,n);break e}ot(e,t,o,n)}t=t.child}return t;case 5:return Hc(t),e===null&&Cl(t),o=t.type,a=t.pendingProps,u=e!==null?e.memoizedProps:null,f=a.children,pl(o,a)?f=null:u!==null&&pl(o,u)&&(t.flags|=32),kd(e,t),ot(e,t,f,n),t.child;case 6:return e===null&&Cl(t),null;case 13:return Rd(e,t,n);case 4:return _l(t,t.stateNode.containerInfo),o=t.pendingProps,e===null?t.child=cr(t,null,o,n):ot(e,t,o,n),t.child;case 11:return o=t.type,a=t.pendingProps,a=t.elementType===o?a:Mt(o,a),xd(e,t,o,a,n);case 7:return ot(e,t,t.pendingProps,n),t.child;case 8:return ot(e,t,t.pendingProps.children,n),t.child;case 12:return ot(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(o=t.type._context,a=t.pendingProps,u=t.memoizedProps,f=a.value,Ae(mi,o._currentValue),o._currentValue=f,u!==null)if(Nt(u.value,f)){if(u.children===a.children&&!st.current){t=Zt(e,t,n);break e}}else for(u=t.child,u!==null&&(u.return=t);u!==null;){var m=u.dependencies;if(m!==null){f=u.child;for(var y=m.firstContext;y!==null;){if(y.context===o){if(u.tag===1){y=Jt(-1,n&-n),y.tag=2;var P=u.updateQueue;if(P!==null){P=P.shared;var $=P.pending;$===null?y.next=y:(y.next=$.next,$.next=y),P.pending=y}}u.lanes|=n,y=u.alternate,y!==null&&(y.lanes|=n),Rl(u.return,n,t),m.lanes|=n;break}y=y.next}}else if(u.tag===10)f=u.type===t.type?null:u.child;else if(u.tag===18){if(f=u.return,f===null)throw Error(s(341));f.lanes|=n,m=f.alternate,m!==null&&(m.lanes|=n),Rl(f,n,t),f=u.sibling}else f=u.child;if(f!==null)f.return=u;else for(f=u;f!==null;){if(f===t){f=null;break}if(u=f.sibling,u!==null){u.return=f.return,f=u;break}f=f.return}u=f}ot(e,t,a.children,n),t=t.child}return t;case 9:return a=t.type,o=t.pendingProps.children,fr(t,n),a=Et(a),o=o(a),t.flags|=1,ot(e,t,o,n),t.child;case 14:return o=t.type,a=Mt(o,t.pendingProps),a=Mt(o.type,a),wd(e,t,o,a,n);case 15:return Sd(e,t,t.type,t.pendingProps,n);case 17:return o=t.type,a=t.pendingProps,a=t.elementType===o?a:Mt(o,a),Ri(e,t),t.tag=1,lt(o)?(e=!0,ui(t)):e=!1,fr(t,n),fd(t,o,a),Hl(t,o,a,n),ql(null,t,o,!0,e,n);case 19:return Td(e,t,n);case 22:return Cd(e,t,n)}throw Error(s(156,t.tag))};function Zd(e,t){return Ou(e,t)}function dg(e,t,n,o){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=o,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Rt(e,t,n,o){return new dg(e,t,n,o)}function pa(e){return e=e.prototype,!(!e||!e.isReactComponent)}function fg(e){if(typeof e=="function")return pa(e)?1:0;if(e!=null){if(e=e.$$typeof,e===de)return 11;if(e===Se)return 14}return 2}function Sn(e,t){var n=e.alternate;return n===null?(n=Rt(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function $i(e,t,n,o,a,u){var f=2;if(o=e,typeof e=="function")pa(e)&&(f=1);else if(typeof e=="string")f=5;else e:switch(e){case V:return Fn(n.children,a,u,t);case ne:f=8,a|=8;break;case ye:return e=Rt(12,n,t,a|2),e.elementType=ye,e.lanes=u,e;case me:return e=Rt(13,n,t,a),e.elementType=me,e.lanes=u,e;case _e:return e=Rt(19,n,t,a),e.elementType=_e,e.lanes=u,e;case je:return Fi(n,a,u,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Ie:f=10;break e;case ie:f=9;break e;case de:f=11;break e;case Se:f=14;break e;case Ue:f=16,o=null;break e}throw Error(s(130,e==null?e:typeof e,""))}return t=Rt(f,n,t,a),t.elementType=e,t.type=o,t.lanes=u,t}function Fn(e,t,n,o){return e=Rt(7,e,o,t),e.lanes=n,e}function Fi(e,t,n,o){return e=Rt(22,e,o,t),e.elementType=je,e.lanes=n,e.stateNode={isHidden:!1},e}function ha(e,t,n){return e=Rt(6,e,null,t),e.lanes=n,e}function ma(e,t,n){return t=Rt(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function pg(e,t,n,o,a){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Us(0),this.expirationTimes=Us(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Us(0),this.identifierPrefix=o,this.onRecoverableError=a,this.mutableSourceEagerHydrationData=null}function ga(e,t,n,o,a,u,f,m,y){return e=new pg(e,t,n,m,y),t===1?(t=1,u===!0&&(t|=8)):t=0,u=Rt(3,null,null,t),e.current=u,u.stateNode=e,u.memoizedState={element:o,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Tl(u),e}function hg(e,t,n){var o=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(r)}catch(i){console.error(i)}}return r(),Ca.exports=Rg(),Ca.exports}var mf;function Tg(){if(mf)return Yi;mf=1;var r=Pg();return Yi.createRoot=r.createRoot,Yi.hydrateRoot=r.hydrateRoot,Yi}var _g=Tg(),rt=function(){return rt=Object.assign||function(i){for(var s,l=1,c=arguments.length;l0?Qe(Rr,--Pt):0,kr--,Be===10&&(kr=1,gs--),Be}function zt(){return Be=Pt2||Ba(Be)>3?"":" "}function Bg(r,i){for(;--i&&zt()&&!(Be<48||Be>102||Be>57&&Be<65||Be>70&&Be<97););return vs(r,ts()+(i<6&&Un()==32&&zt()==32))}function ba(r){for(;zt();)switch(Be){case r:return Pt;case 34:case 39:r!==34&&r!==39&&ba(Be);break;case 40:r===41&&ba(r);break;case 92:zt();break}return Pt}function bg(r,i){for(;zt()&&r+Be!==57;)if(r+Be===84&&Un()===47)break;return"/*"+vs(i,Pt-1)+"*"+ru(r===47?r:zt())}function Ug(r){for(;!Ba(Un());)zt();return vs(r,Pt)}function Hg(r){return $g(ns("",null,null,null,[""],r=zg(r),0,[0],r))}function ns(r,i,s,l,c,d,p,g,w){for(var v=0,S=0,j=p,R=0,L=0,T=0,O=1,_=1,b=1,U=0,B="",W=c,I=d,M=l,V=B;_;)switch(T=U,U=zt()){case 40:if(T!=108&&Qe(V,j-1)==58){es(V+=ce(ja(U),"&","&\f"),"&\f",mp(v?g[v-1]:0))!=-1&&(b=-1);break}case 34:case 39:case 91:V+=ja(U);break;case 9:case 10:case 13:case 32:V+=Fg(T);break;case 92:V+=Bg(ts()-1,7);continue;case 47:switch(Un()){case 42:case 47:ko(Vg(bg(zt(),ts()),i,s,w),w);break;default:V+="/"}break;case 123*O:g[v++]=Wt(V)*b;case 125*O:case 59:case 0:switch(U){case 0:case 125:_=0;case 59+S:b==-1&&(V=ce(V,/\f/g,"")),L>0&&Wt(V)-j&&ko(L>32?vf(V+";",l,s,j-1,w):vf(ce(V," ","")+";",l,s,j-2,w),w);break;case 59:V+=";";default:if(ko(M=yf(V,i,s,v,S,c,g,B,W=[],I=[],j,d),d),U===123)if(S===0)ns(V,i,M,M,W,d,j,g,I);else switch(R===99&&Qe(V,3)===110?100:R){case 100:case 108:case 109:case 115:ns(r,M,M,l&&ko(yf(r,M,M,0,0,c,g,B,c,W=[],j,I),I),c,I,j,g,l?W:I);break;default:ns(V,M,M,M,[""],I,0,g,I)}}v=S=L=0,O=b=1,B=V="",j=p;break;case 58:j=1+Wt(V),L=T;default:if(O<1){if(U==123)--O;else if(U==125&&O++==0&&Dg()==125)continue}switch(V+=ru(U),U*O){case 38:b=S>0?1:(V+="\f",-1);break;case 44:g[v++]=(Wt(V)-1)*b,b=1;break;case 64:Un()===45&&(V+=ja(zt())),R=Un(),S=j=Wt(B=V+=Ug(ts())),U++;break;case 45:T===45&&Wt(V)==2&&(O=0)}}return d}function yf(r,i,s,l,c,d,p,g,w,v,S,j){for(var R=c-1,L=c===0?d:[""],T=yp(L),O=0,_=0,b=0;O0?L[U]+" "+B:ce(B,/&\f/g,L[U])))&&(w[b++]=W);return ys(r,i,s,c===0?ms:g,w,v,S,j)}function Vg(r,i,s,l){return ys(r,i,s,pp,ru(Ig()),Cr(r,2,-2),0,l)}function vf(r,i,s,l,c){return ys(r,i,s,nu,Cr(r,0,l),Cr(r,l+1,-1),l,c)}function xp(r,i,s){switch(Mg(r,i)){case 5103:return ke+"print-"+r+r;case 5737:case 4201:case 3177:case 3433:case 1641:case 4457:case 2921:case 5572:case 6356:case 5844:case 3191:case 6645:case 3005:case 6391:case 5879:case 5623:case 6135:case 4599:case 4855:case 4215:case 6389:case 5109:case 5365:case 5621:case 3829:return ke+r+r;case 4789:return Eo+r+r;case 5349:case 4246:case 4810:case 6968:case 2756:return ke+r+Eo+r+Te+r+r;case 5936:switch(Qe(r,i+11)){case 114:return ke+r+Te+ce(r,/[svh]\w+-[tblr]{2}/,"tb")+r;case 108:return ke+r+Te+ce(r,/[svh]\w+-[tblr]{2}/,"tb-rl")+r;case 45:return ke+r+Te+ce(r,/[svh]\w+-[tblr]{2}/,"lr")+r}case 6828:case 4268:case 2903:return ke+r+Te+r+r;case 6165:return ke+r+Te+"flex-"+r+r;case 5187:return ke+r+ce(r,/(\w+).+(:[^]+)/,ke+"box-$1$2"+Te+"flex-$1$2")+r;case 5443:return ke+r+Te+"flex-item-"+ce(r,/flex-|-self/g,"")+(tn(r,/flex-|baseline/)?"":Te+"grid-row-"+ce(r,/flex-|-self/g,""))+r;case 4675:return ke+r+Te+"flex-line-pack"+ce(r,/align-content|flex-|-self/g,"")+r;case 5548:return ke+r+Te+ce(r,"shrink","negative")+r;case 5292:return ke+r+Te+ce(r,"basis","preferred-size")+r;case 6060:return ke+"box-"+ce(r,"-grow","")+ke+r+Te+ce(r,"grow","positive")+r;case 4554:return ke+ce(r,/([^-])(transform)/g,"$1"+ke+"$2")+r;case 6187:return ce(ce(ce(r,/(zoom-|grab)/,ke+"$1"),/(image-set)/,ke+"$1"),r,"")+r;case 5495:case 3959:return ce(r,/(image-set\([^]*)/,ke+"$1$`$1");case 4968:return ce(ce(r,/(.+:)(flex-)?(.*)/,ke+"box-pack:$3"+Te+"flex-pack:$3"),/s.+-b[^;]+/,"justify")+ke+r+r;case 4200:if(!tn(r,/flex-|baseline/))return Te+"grid-column-align"+Cr(r,i)+r;break;case 2592:case 3360:return Te+ce(r,"template-","")+r;case 4384:case 3616:return s&&s.some(function(l,c){return i=c,tn(l.props,/grid-\w+-end/)})?~es(r+(s=s[i].value),"span",0)?r:Te+ce(r,"-start","")+r+Te+"grid-row-span:"+(~es(s,"span",0)?tn(s,/\d+/):+tn(s,/\d+/)-+tn(r,/\d+/))+";":Te+ce(r,"-start","")+r;case 4896:case 4128:return s&&s.some(function(l){return tn(l.props,/grid-\w+-start/)})?r:Te+ce(ce(r,"-end","-span"),"span ","")+r;case 4095:case 3583:case 4068:case 2532:return ce(r,/(.+)-inline(.+)/,ke+"$1$2")+r;case 8116:case 7059:case 5753:case 5535:case 5445:case 5701:case 4933:case 4677:case 5533:case 5789:case 5021:case 4765:if(Wt(r)-1-i>6)switch(Qe(r,i+1)){case 109:if(Qe(r,i+4)!==45)break;case 102:return ce(r,/(.+:)(.+)-([^]+)/,"$1"+ke+"$2-$3$1"+Eo+(Qe(r,i+3)==108?"$3":"$2-$3"))+r;case 115:return~es(r,"stretch",0)?xp(ce(r,"stretch","fill-available"),i,s)+r:r}break;case 5152:case 5920:return ce(r,/(.+?):(\d+)(\s*\/\s*(span)?\s*(\d+))?(.*)/,function(l,c,d,p,g,w,v){return Te+c+":"+d+v+(p?Te+c+"-span:"+(g?w:+w-+d)+v:"")+r});case 4949:if(Qe(r,i+6)===121)return ce(r,":",":"+ke)+r;break;case 6444:switch(Qe(r,Qe(r,14)===45?18:11)){case 120:return ce(r,/(.+:)([^;\s!]+)(;|(\s+)?!.+)?/,"$1"+ke+(Qe(r,14)===45?"inline-":"")+"box$3$1"+ke+"$2$3$1"+Te+"$2box$3")+r;case 100:return ce(r,":",":"+Te)+r}break;case 5719:case 2647:case 2135:case 3927:case 2391:return ce(r,"scroll-","scroll-snap-")+r}return r}function us(r,i){for(var s="",l=0;l-1&&!r.return)switch(r.type){case nu:r.return=xp(r.value,r.length,s);return;case hp:return us([kn(r,{value:ce(r.value,"@","@"+ke)})],l);case ms:if(r.length)return Lg(s=r.props,function(c){switch(tn(c,l=/(::plac\w+|:read-\w+)/)){case":read-only":case":read-write":xr(kn(r,{props:[ce(c,/:(read-\w+)/,":"+Eo+"$1")]})),xr(kn(r,{props:[c]})),Fa(r,{props:gf(s,l)});break;case"::placeholder":xr(kn(r,{props:[ce(c,/:(plac\w+)/,":"+ke+"input-$1")]})),xr(kn(r,{props:[ce(c,/:(plac\w+)/,":"+Eo+"$1")]})),xr(kn(r,{props:[ce(c,/:(plac\w+)/,Te+"input-$1")]})),xr(kn(r,{props:[c]})),Fa(r,{props:gf(s,l)});break}return""})}}var Gg={animationIterationCount:1,aspectRatio:1,borderImageOutset:1,borderImageSlice:1,borderImageWidth:1,boxFlex:1,boxFlexGroup:1,boxOrdinalGroup:1,columnCount:1,columns:1,flex:1,flexGrow:1,flexPositive:1,flexShrink:1,flexNegative:1,flexOrder:1,gridRow:1,gridRowEnd:1,gridRowSpan:1,gridRowStart:1,gridColumn:1,gridColumnEnd:1,gridColumnSpan:1,gridColumnStart:1,msGridRow:1,msGridRowSpan:1,msGridColumn:1,msGridColumnSpan:1,fontWeight:1,lineHeight:1,opacity:1,order:1,orphans:1,tabSize:1,widows:1,zIndex:1,zoom:1,WebkitLineClamp:1,fillOpacity:1,floodOpacity:1,stopOpacity:1,strokeDasharray:1,strokeDashoffset:1,strokeMiterlimit:1,strokeOpacity:1,strokeWidth:1},vt={},Er=typeof process<"u"&&vt!==void 0&&(vt.REACT_APP_SC_ATTR||vt.SC_ATTR)||"data-styled",wp="active",Sp="data-styled-version",xs="6.1.14",ou=`/*!sc*/ -`,cs=typeof window<"u"&&"HTMLElement"in window,Kg=!!(typeof SC_DISABLE_SPEEDY=="boolean"?SC_DISABLE_SPEEDY:typeof process<"u"&&vt!==void 0&&vt.REACT_APP_SC_DISABLE_SPEEDY!==void 0&&vt.REACT_APP_SC_DISABLE_SPEEDY!==""?vt.REACT_APP_SC_DISABLE_SPEEDY!=="false"&&vt.REACT_APP_SC_DISABLE_SPEEDY:typeof process<"u"&&vt!==void 0&&vt.SC_DISABLE_SPEEDY!==void 0&&vt.SC_DISABLE_SPEEDY!==""&&vt.SC_DISABLE_SPEEDY!=="false"&&vt.SC_DISABLE_SPEEDY),ws=Object.freeze([]),jr=Object.freeze({});function Xg(r,i,s){return s===void 0&&(s=jr),r.theme!==s.theme&&r.theme||i||s.theme}var Cp=new Set(["a","abbr","address","area","article","aside","audio","b","base","bdi","bdo","big","blockquote","body","br","button","canvas","caption","cite","code","col","colgroup","data","datalist","dd","del","details","dfn","dialog","div","dl","dt","em","embed","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","hr","html","i","iframe","img","input","ins","kbd","keygen","label","legend","li","link","main","map","mark","menu","menuitem","meta","meter","nav","noscript","object","ol","optgroup","option","output","p","param","picture","pre","progress","q","rp","rt","ruby","s","samp","script","section","select","small","source","span","strong","style","sub","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","track","u","ul","use","var","video","wbr","circle","clipPath","defs","ellipse","foreignObject","g","image","line","linearGradient","marker","mask","path","pattern","polygon","polyline","radialGradient","rect","stop","svg","text","tspan"]),Jg=/[!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~-]+/g,Zg=/(^-|-$)/g;function xf(r){return r.replace(Jg,"-").replace(Zg,"")}var ey=/(a)(d)/gi,qi=52,wf=function(r){return String.fromCharCode(r+(r>25?39:97))};function Ua(r){var i,s="";for(i=Math.abs(r);i>qi;i=i/qi|0)s=wf(i%qi)+s;return(wf(i%qi)+s).replace(ey,"$1-$2")}var Aa,kp=5381,wr=function(r,i){for(var s=i.length;s;)r=33*r^i.charCodeAt(--s);return r},Ep=function(r){return wr(kp,r)};function ty(r){return Ua(Ep(r)>>>0)}function ny(r){return r.displayName||r.name||"Component"}function Ra(r){return typeof r=="string"&&!0}var jp=typeof Symbol=="function"&&Symbol.for,Ap=jp?Symbol.for("react.memo"):60115,ry=jp?Symbol.for("react.forward_ref"):60112,oy={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},iy={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},Rp={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},sy=((Aa={})[ry]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},Aa[Ap]=Rp,Aa);function Sf(r){return("type"in(i=r)&&i.type.$$typeof)===Ap?Rp:"$$typeof"in r?sy[r.$$typeof]:oy;var i}var ly=Object.defineProperty,ay=Object.getOwnPropertyNames,Cf=Object.getOwnPropertySymbols,uy=Object.getOwnPropertyDescriptor,cy=Object.getPrototypeOf,kf=Object.prototype;function Pp(r,i,s){if(typeof i!="string"){if(kf){var l=cy(i);l&&l!==kf&&Pp(r,l,s)}var c=ay(i);Cf&&(c=c.concat(Cf(i)));for(var d=Sf(r),p=Sf(i),g=0;g0?" Args: ".concat(i.join(", ")):""))}var dy=function(){function r(i){this.groupSizes=new Uint32Array(512),this.length=512,this.tag=i}return r.prototype.indexOfGroup=function(i){for(var s=0,l=0;l=this.groupSizes.length){for(var l=this.groupSizes,c=l.length,d=c;i>=d;)if((d<<=1)<0)throw Yn(16,"".concat(i));this.groupSizes=new Uint32Array(d),this.groupSizes.set(l),this.length=d;for(var p=c;p=this.length||this.groupSizes[i]===0)return s;for(var l=this.groupSizes[i],c=this.indexOfGroup(i),d=c+l,p=c;p=0){var l=document.createTextNode(s);return this.element.insertBefore(l,this.nodes[i]||null),this.length++,!0}return!1},r.prototype.deleteRule=function(i){this.element.removeChild(this.nodes[i]),this.length--},r.prototype.getRule=function(i){return i0&&(_+="".concat(b,","))}),w+="".concat(T).concat(O,'{content:"').concat(_,'"}').concat(ou)},S=0;S0?".".concat(i):R},S=w.slice();S.push(function(R){R.type===ms&&R.value.includes("&")&&(R.props[0]=R.props[0].replace(Cy,s).replace(l,v))}),p.prefix&&S.push(Qg),S.push(Wg);var j=function(R,L,T,O){L===void 0&&(L=""),T===void 0&&(T=""),O===void 0&&(O="&"),i=O,s=L,l=new RegExp("\\".concat(s,"\\b"),"g");var _=R.replace(ky,""),b=Hg(T||L?"".concat(T," ").concat(L," { ").concat(_," }"):_);p.namespace&&(b=Np(b,p.namespace));var U=[];return us(b,Yg(S.concat(qg(function(B){return U.push(B)})))),U};return j.hash=w.length?w.reduce(function(R,L){return L.name||Yn(15),wr(R,L.name)},kp).toString():"",j}var jy=new _p,Va=Ey(),Op=xt.createContext({shouldForwardProp:void 0,styleSheet:jy,stylis:Va});Op.Consumer;xt.createContext(void 0);function Rf(){return K.useContext(Op)}var Ay=function(){function r(i,s){var l=this;this.inject=function(c,d){d===void 0&&(d=Va);var p=l.name+d.hash;c.hasNameForId(l.id,p)||c.insertRules(l.id,p,d(l.rules,p,"@keyframes"))},this.name=i,this.id="sc-keyframes-".concat(i),this.rules=s,su(this,function(){throw Yn(12,String(l.name))})}return r.prototype.getName=function(i){return i===void 0&&(i=Va),this.name+i.hash},r}(),Ry=function(r){return r>="A"&&r<="Z"};function Pf(r){for(var i="",s=0;s>>0);if(!s.hasNameForId(this.componentId,p)){var g=l(d,".".concat(p),void 0,this.componentId);s.insertRules(this.componentId,p,g)}c=Bn(c,p),this.staticRulesId=p}else{for(var w=wr(this.baseHash,l.hash),v="",S=0;S>>0);s.hasNameForId(this.componentId,L)||s.insertRules(this.componentId,L,l(v,".".concat(L),void 0,this.componentId)),c=Bn(c,L)}}return c},r}(),fs=xt.createContext(void 0);fs.Consumer;function Tf(r){var i=xt.useContext(fs),s=K.useMemo(function(){return function(l,c){if(!l)throw Yn(14);if(Wn(l)){var d=l(c);return d}if(Array.isArray(l)||typeof l!="object")throw Yn(8);return c?rt(rt({},c),l):l}(r.theme,i)},[r.theme,i]);return r.children?xt.createElement(fs.Provider,{value:s},r.children):null}var Pa={};function Ny(r,i,s){var l=iu(r),c=r,d=!Ra(r),p=i.attrs,g=p===void 0?ws:p,w=i.componentId,v=w===void 0?function(W,I){var M=typeof W!="string"?"sc":xf(W);Pa[M]=(Pa[M]||0)+1;var V="".concat(M,"-").concat(ty(xs+M+Pa[M]));return I?"".concat(I,"-").concat(V):V}(i.displayName,i.parentComponentId):w,S=i.displayName,j=S===void 0?function(W){return Ra(W)?"styled.".concat(W):"Styled(".concat(ny(W),")")}(r):S,R=i.displayName&&i.componentId?"".concat(xf(i.displayName),"-").concat(i.componentId):i.componentId||v,L=l&&c.attrs?c.attrs.concat(g).filter(Boolean):g,T=i.shouldForwardProp;if(l&&c.shouldForwardProp){var O=c.shouldForwardProp;if(i.shouldForwardProp){var _=i.shouldForwardProp;T=function(W,I){return O(W,I)&&_(W,I)}}else T=O}var b=new _y(s,R,l?c.componentStyle:void 0);function U(W,I){return function(M,V,ne){var ye=M.attrs,Ie=M.componentStyle,ie=M.defaultProps,de=M.foldedComponentIds,me=M.styledComponentId,_e=M.target,Se=xt.useContext(fs),Ue=Rf(),je=M.shouldForwardProp||Ue.shouldForwardProp,Y=Xg(V,Se,ie)||jr,te=function(he,fe,Ce){for(var ge,xe=rt(rt({},fe),{className:void 0,theme:Ce}),Ge=0;Ge{let i;const s=new Set,l=(v,S)=>{const j=typeof v=="function"?v(i):v;if(!Object.is(j,i)){const R=i;i=S??(typeof j!="object"||j===null)?j:Object.assign({},i,j),s.forEach(L=>L(i,R))}},c=()=>i,g={setState:l,getState:c,getInitialState:()=>w,subscribe:v=>(s.add(v),()=>s.delete(v))},w=i=r(l,c,g);return g},My=r=>r?Of(r):Of,Ly=r=>r;function Iy(r,i=Ly){const s=xt.useSyncExternalStore(r.subscribe,()=>i(r.getState()),()=>i(r.getInitialState()));return xt.useDebugValue(s),s}const Mf=r=>{const i=My(r),s=l=>Iy(i,l);return Object.assign(s,i),s},Pr=r=>r?Mf(r):Mf;function Dp(r,i){return function(){return r.apply(i,arguments)}}const{toString:Dy}=Object.prototype,{getPrototypeOf:lu}=Object,Ss=(r=>i=>{const s=Dy.call(i);return r[s]||(r[s]=s.slice(8,-1).toLowerCase())})(Object.create(null)),$t=r=>(r=r.toLowerCase(),i=>Ss(i)===r),Cs=r=>i=>typeof i===r,{isArray:Tr}=Array,No=Cs("undefined");function zy(r){return r!==null&&!No(r)&&r.constructor!==null&&!No(r.constructor)&&wt(r.constructor.isBuffer)&&r.constructor.isBuffer(r)}const zp=$t("ArrayBuffer");function $y(r){let i;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?i=ArrayBuffer.isView(r):i=r&&r.buffer&&zp(r.buffer),i}const Fy=Cs("string"),wt=Cs("function"),$p=Cs("number"),ks=r=>r!==null&&typeof r=="object",By=r=>r===!0||r===!1,is=r=>{if(Ss(r)!=="object")return!1;const i=lu(r);return(i===null||i===Object.prototype||Object.getPrototypeOf(i)===null)&&!(Symbol.toStringTag in r)&&!(Symbol.iterator in r)},by=$t("Date"),Uy=$t("File"),Hy=$t("Blob"),Vy=$t("FileList"),Wy=r=>ks(r)&&wt(r.pipe),Yy=r=>{let i;return r&&(typeof FormData=="function"&&r instanceof FormData||wt(r.append)&&((i=Ss(r))==="formdata"||i==="object"&&wt(r.toString)&&r.toString()==="[object FormData]"))},qy=$t("URLSearchParams"),[Qy,Gy,Ky,Xy]=["ReadableStream","Request","Response","Headers"].map($t),Jy=r=>r.trim?r.trim():r.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function Lo(r,i,{allOwnKeys:s=!1}={}){if(r===null||typeof r>"u")return;let l,c;if(typeof r!="object"&&(r=[r]),Tr(r))for(l=0,c=r.length;l0;)if(c=s[l],i===c.toLowerCase())return c;return null}const bn=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global,Bp=r=>!No(r)&&r!==bn;function Ya(){const{caseless:r}=Bp(this)&&this||{},i={},s=(l,c)=>{const d=r&&Fp(i,c)||c;is(i[d])&&is(l)?i[d]=Ya(i[d],l):is(l)?i[d]=Ya({},l):Tr(l)?i[d]=l.slice():i[d]=l};for(let l=0,c=arguments.length;l(Lo(i,(c,d)=>{s&&wt(c)?r[d]=Dp(c,s):r[d]=c},{allOwnKeys:l}),r),e0=r=>(r.charCodeAt(0)===65279&&(r=r.slice(1)),r),t0=(r,i,s,l)=>{r.prototype=Object.create(i.prototype,l),r.prototype.constructor=r,Object.defineProperty(r,"super",{value:i.prototype}),s&&Object.assign(r.prototype,s)},n0=(r,i,s,l)=>{let c,d,p;const g={};if(i=i||{},r==null)return i;do{for(c=Object.getOwnPropertyNames(r),d=c.length;d-- >0;)p=c[d],(!l||l(p,r,i))&&!g[p]&&(i[p]=r[p],g[p]=!0);r=s!==!1&&lu(r)}while(r&&(!s||s(r,i))&&r!==Object.prototype);return i},r0=(r,i,s)=>{r=String(r),(s===void 0||s>r.length)&&(s=r.length),s-=i.length;const l=r.indexOf(i,s);return l!==-1&&l===s},o0=r=>{if(!r)return null;if(Tr(r))return r;let i=r.length;if(!$p(i))return null;const s=new Array(i);for(;i-- >0;)s[i]=r[i];return s},i0=(r=>i=>r&&i instanceof r)(typeof Uint8Array<"u"&&lu(Uint8Array)),s0=(r,i)=>{const l=(r&&r[Symbol.iterator]).call(r);let c;for(;(c=l.next())&&!c.done;){const d=c.value;i.call(r,d[0],d[1])}},l0=(r,i)=>{let s;const l=[];for(;(s=r.exec(i))!==null;)l.push(s);return l},a0=$t("HTMLFormElement"),u0=r=>r.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(s,l,c){return l.toUpperCase()+c}),Lf=(({hasOwnProperty:r})=>(i,s)=>r.call(i,s))(Object.prototype),c0=$t("RegExp"),bp=(r,i)=>{const s=Object.getOwnPropertyDescriptors(r),l={};Lo(s,(c,d)=>{let p;(p=i(c,d,r))!==!1&&(l[d]=p||c)}),Object.defineProperties(r,l)},d0=r=>{bp(r,(i,s)=>{if(wt(r)&&["arguments","caller","callee"].indexOf(s)!==-1)return!1;const l=r[s];if(wt(l)){if(i.enumerable=!1,"writable"in i){i.writable=!1;return}i.set||(i.set=()=>{throw Error("Can not rewrite read-only method '"+s+"'")})}})},f0=(r,i)=>{const s={},l=c=>{c.forEach(d=>{s[d]=!0})};return Tr(r)?l(r):l(String(r).split(i)),s},p0=()=>{},h0=(r,i)=>r!=null&&Number.isFinite(r=+r)?r:i,Ta="abcdefghijklmnopqrstuvwxyz",If="0123456789",Up={DIGIT:If,ALPHA:Ta,ALPHA_DIGIT:Ta+Ta.toUpperCase()+If},m0=(r=16,i=Up.ALPHA_DIGIT)=>{let s="";const{length:l}=i;for(;r--;)s+=i[Math.random()*l|0];return s};function g0(r){return!!(r&&wt(r.append)&&r[Symbol.toStringTag]==="FormData"&&r[Symbol.iterator])}const y0=r=>{const i=new Array(10),s=(l,c)=>{if(ks(l)){if(i.indexOf(l)>=0)return;if(!("toJSON"in l)){i[c]=l;const d=Tr(l)?[]:{};return Lo(l,(p,g)=>{const w=s(p,c+1);!No(w)&&(d[g]=w)}),i[c]=void 0,d}}return l};return s(r,0)},v0=$t("AsyncFunction"),x0=r=>r&&(ks(r)||wt(r))&&wt(r.then)&&wt(r.catch),Hp=((r,i)=>r?setImmediate:i?((s,l)=>(bn.addEventListener("message",({source:c,data:d})=>{c===bn&&d===s&&l.length&&l.shift()()},!1),c=>{l.push(c),bn.postMessage(s,"*")}))(`axios@${Math.random()}`,[]):s=>setTimeout(s))(typeof setImmediate=="function",wt(bn.postMessage)),w0=typeof queueMicrotask<"u"?queueMicrotask.bind(bn):typeof process<"u"&&process.nextTick||Hp,N={isArray:Tr,isArrayBuffer:zp,isBuffer:zy,isFormData:Yy,isArrayBufferView:$y,isString:Fy,isNumber:$p,isBoolean:By,isObject:ks,isPlainObject:is,isReadableStream:Qy,isRequest:Gy,isResponse:Ky,isHeaders:Xy,isUndefined:No,isDate:by,isFile:Uy,isBlob:Hy,isRegExp:c0,isFunction:wt,isStream:Wy,isURLSearchParams:qy,isTypedArray:i0,isFileList:Vy,forEach:Lo,merge:Ya,extend:Zy,trim:Jy,stripBOM:e0,inherits:t0,toFlatObject:n0,kindOf:Ss,kindOfTest:$t,endsWith:r0,toArray:o0,forEachEntry:s0,matchAll:l0,isHTMLForm:a0,hasOwnProperty:Lf,hasOwnProp:Lf,reduceDescriptors:bp,freezeMethods:d0,toObjectSet:f0,toCamelCase:u0,noop:p0,toFiniteNumber:h0,findKey:Fp,global:bn,isContextDefined:Bp,ALPHABET:Up,generateString:m0,isSpecCompliantForm:g0,toJSONObject:y0,isAsyncFn:v0,isThenable:x0,setImmediate:Hp,asap:w0};function ae(r,i,s,l,c){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack,this.message=r,this.name="AxiosError",i&&(this.code=i),s&&(this.config=s),l&&(this.request=l),c&&(this.response=c,this.status=c.status?c.status:null)}N.inherits(ae,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:N.toJSONObject(this.config),code:this.code,status:this.status}}});const Vp=ae.prototype,Wp={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach(r=>{Wp[r]={value:r}});Object.defineProperties(ae,Wp);Object.defineProperty(Vp,"isAxiosError",{value:!0});ae.from=(r,i,s,l,c,d)=>{const p=Object.create(Vp);return N.toFlatObject(r,p,function(w){return w!==Error.prototype},g=>g!=="isAxiosError"),ae.call(p,r.message,i,s,l,c),p.cause=r,p.name=r.name,d&&Object.assign(p,d),p};const S0=null;function qa(r){return N.isPlainObject(r)||N.isArray(r)}function Yp(r){return N.endsWith(r,"[]")?r.slice(0,-2):r}function Df(r,i,s){return r?r.concat(i).map(function(c,d){return c=Yp(c),!s&&d?"["+c+"]":c}).join(s?".":""):i}function C0(r){return N.isArray(r)&&!r.some(qa)}const k0=N.toFlatObject(N,{},null,function(i){return/^is[A-Z]/.test(i)});function Es(r,i,s){if(!N.isObject(r))throw new TypeError("target must be an object");i=i||new FormData,s=N.toFlatObject(s,{metaTokens:!0,dots:!1,indexes:!1},!1,function(O,_){return!N.isUndefined(_[O])});const l=s.metaTokens,c=s.visitor||S,d=s.dots,p=s.indexes,w=(s.Blob||typeof Blob<"u"&&Blob)&&N.isSpecCompliantForm(i);if(!N.isFunction(c))throw new TypeError("visitor must be a function");function v(T){if(T===null)return"";if(N.isDate(T))return T.toISOString();if(!w&&N.isBlob(T))throw new ae("Blob is not supported. Use a Buffer instead.");return N.isArrayBuffer(T)||N.isTypedArray(T)?w&&typeof Blob=="function"?new Blob([T]):Buffer.from(T):T}function S(T,O,_){let b=T;if(T&&!_&&typeof T=="object"){if(N.endsWith(O,"{}"))O=l?O:O.slice(0,-2),T=JSON.stringify(T);else if(N.isArray(T)&&C0(T)||(N.isFileList(T)||N.endsWith(O,"[]"))&&(b=N.toArray(T)))return O=Yp(O),b.forEach(function(B,W){!(N.isUndefined(B)||B===null)&&i.append(p===!0?Df([O],W,d):p===null?O:O+"[]",v(B))}),!1}return qa(T)?!0:(i.append(Df(_,O,d),v(T)),!1)}const j=[],R=Object.assign(k0,{defaultVisitor:S,convertValue:v,isVisitable:qa});function L(T,O){if(!N.isUndefined(T)){if(j.indexOf(T)!==-1)throw Error("Circular reference detected in "+O.join("."));j.push(T),N.forEach(T,function(b,U){(!(N.isUndefined(b)||b===null)&&c.call(i,b,N.isString(U)?U.trim():U,O,R))===!0&&L(b,O?O.concat(U):[U])}),j.pop()}}if(!N.isObject(r))throw new TypeError("data must be an object");return L(r),i}function zf(r){const i={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(r).replace(/[!'()~]|%20|%00/g,function(l){return i[l]})}function au(r,i){this._pairs=[],r&&Es(r,this,i)}const qp=au.prototype;qp.append=function(i,s){this._pairs.push([i,s])};qp.toString=function(i){const s=i?function(l){return i.call(this,l,zf)}:zf;return this._pairs.map(function(c){return s(c[0])+"="+s(c[1])},"").join("&")};function E0(r){return encodeURIComponent(r).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function Qp(r,i,s){if(!i)return r;const l=s&&s.encode||E0;N.isFunction(s)&&(s={serialize:s});const c=s&&s.serialize;let d;if(c?d=c(i,s):d=N.isURLSearchParams(i)?i.toString():new au(i,s).toString(l),d){const p=r.indexOf("#");p!==-1&&(r=r.slice(0,p)),r+=(r.indexOf("?")===-1?"?":"&")+d}return r}class $f{constructor(){this.handlers=[]}use(i,s,l){return this.handlers.push({fulfilled:i,rejected:s,synchronous:l?l.synchronous:!1,runWhen:l?l.runWhen:null}),this.handlers.length-1}eject(i){this.handlers[i]&&(this.handlers[i]=null)}clear(){this.handlers&&(this.handlers=[])}forEach(i){N.forEach(this.handlers,function(l){l!==null&&i(l)})}}const Gp={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},j0=typeof URLSearchParams<"u"?URLSearchParams:au,A0=typeof FormData<"u"?FormData:null,R0=typeof Blob<"u"?Blob:null,P0={isBrowser:!0,classes:{URLSearchParams:j0,FormData:A0,Blob:R0},protocols:["http","https","file","blob","url","data"]},uu=typeof window<"u"&&typeof document<"u",Qa=typeof navigator=="object"&&navigator||void 0,T0=uu&&(!Qa||["ReactNative","NativeScript","NS"].indexOf(Qa.product)<0),_0=typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope&&typeof self.importScripts=="function",N0=uu&&window.location.href||"http://localhost",O0=Object.freeze(Object.defineProperty({__proto__:null,hasBrowserEnv:uu,hasStandardBrowserEnv:T0,hasStandardBrowserWebWorkerEnv:_0,navigator:Qa,origin:N0},Symbol.toStringTag,{value:"Module"})),nt={...O0,...P0};function M0(r,i){return Es(r,new nt.classes.URLSearchParams,Object.assign({visitor:function(s,l,c,d){return nt.isNode&&N.isBuffer(s)?(this.append(l,s.toString("base64")),!1):d.defaultVisitor.apply(this,arguments)}},i))}function L0(r){return N.matchAll(/\w+|\[(\w*)]/g,r).map(i=>i[0]==="[]"?"":i[1]||i[0])}function I0(r){const i={},s=Object.keys(r);let l;const c=s.length;let d;for(l=0;l=s.length;return p=!p&&N.isArray(c)?c.length:p,w?(N.hasOwnProp(c,p)?c[p]=[c[p],l]:c[p]=l,!g):((!c[p]||!N.isObject(c[p]))&&(c[p]=[]),i(s,l,c[p],d)&&N.isArray(c[p])&&(c[p]=I0(c[p])),!g)}if(N.isFormData(r)&&N.isFunction(r.entries)){const s={};return N.forEachEntry(r,(l,c)=>{i(L0(l),c,s,0)}),s}return null}function D0(r,i,s){if(N.isString(r))try{return(i||JSON.parse)(r),N.trim(r)}catch(l){if(l.name!=="SyntaxError")throw l}return(0,JSON.stringify)(r)}const Io={transitional:Gp,adapter:["xhr","http","fetch"],transformRequest:[function(i,s){const l=s.getContentType()||"",c=l.indexOf("application/json")>-1,d=N.isObject(i);if(d&&N.isHTMLForm(i)&&(i=new FormData(i)),N.isFormData(i))return c?JSON.stringify(Kp(i)):i;if(N.isArrayBuffer(i)||N.isBuffer(i)||N.isStream(i)||N.isFile(i)||N.isBlob(i)||N.isReadableStream(i))return i;if(N.isArrayBufferView(i))return i.buffer;if(N.isURLSearchParams(i))return s.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),i.toString();let g;if(d){if(l.indexOf("application/x-www-form-urlencoded")>-1)return M0(i,this.formSerializer).toString();if((g=N.isFileList(i))||l.indexOf("multipart/form-data")>-1){const w=this.env&&this.env.FormData;return Es(g?{"files[]":i}:i,w&&new w,this.formSerializer)}}return d||c?(s.setContentType("application/json",!1),D0(i)):i}],transformResponse:[function(i){const s=this.transitional||Io.transitional,l=s&&s.forcedJSONParsing,c=this.responseType==="json";if(N.isResponse(i)||N.isReadableStream(i))return i;if(i&&N.isString(i)&&(l&&!this.responseType||c)){const p=!(s&&s.silentJSONParsing)&&c;try{return JSON.parse(i)}catch(g){if(p)throw g.name==="SyntaxError"?ae.from(g,ae.ERR_BAD_RESPONSE,this,null,this.response):g}}return i}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:nt.classes.FormData,Blob:nt.classes.Blob},validateStatus:function(i){return i>=200&&i<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};N.forEach(["delete","get","head","post","put","patch"],r=>{Io.headers[r]={}});const z0=N.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),$0=r=>{const i={};let s,l,c;return r&&r.split(` -`).forEach(function(p){c=p.indexOf(":"),s=p.substring(0,c).trim().toLowerCase(),l=p.substring(c+1).trim(),!(!s||i[s]&&z0[s])&&(s==="set-cookie"?i[s]?i[s].push(l):i[s]=[l]:i[s]=i[s]?i[s]+", "+l:l)}),i},Ff=Symbol("internals");function xo(r){return r&&String(r).trim().toLowerCase()}function ss(r){return r===!1||r==null?r:N.isArray(r)?r.map(ss):String(r)}function F0(r){const i=Object.create(null),s=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let l;for(;l=s.exec(r);)i[l[1]]=l[2];return i}const B0=r=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(r.trim());function _a(r,i,s,l,c){if(N.isFunction(l))return l.call(this,i,s);if(c&&(i=s),!!N.isString(i)){if(N.isString(l))return i.indexOf(l)!==-1;if(N.isRegExp(l))return l.test(i)}}function b0(r){return r.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(i,s,l)=>s.toUpperCase()+l)}function U0(r,i){const s=N.toCamelCase(" "+i);["get","set","has"].forEach(l=>{Object.defineProperty(r,l+s,{value:function(c,d,p){return this[l].call(this,i,c,d,p)},configurable:!0})})}class ft{constructor(i){i&&this.set(i)}set(i,s,l){const c=this;function d(g,w,v){const S=xo(w);if(!S)throw new Error("header name must be a non-empty string");const j=N.findKey(c,S);(!j||c[j]===void 0||v===!0||v===void 0&&c[j]!==!1)&&(c[j||w]=ss(g))}const p=(g,w)=>N.forEach(g,(v,S)=>d(v,S,w));if(N.isPlainObject(i)||i instanceof this.constructor)p(i,s);else if(N.isString(i)&&(i=i.trim())&&!B0(i))p($0(i),s);else if(N.isHeaders(i))for(const[g,w]of i.entries())d(w,g,l);else i!=null&&d(s,i,l);return this}get(i,s){if(i=xo(i),i){const l=N.findKey(this,i);if(l){const c=this[l];if(!s)return c;if(s===!0)return F0(c);if(N.isFunction(s))return s.call(this,c,l);if(N.isRegExp(s))return s.exec(c);throw new TypeError("parser must be boolean|regexp|function")}}}has(i,s){if(i=xo(i),i){const l=N.findKey(this,i);return!!(l&&this[l]!==void 0&&(!s||_a(this,this[l],l,s)))}return!1}delete(i,s){const l=this;let c=!1;function d(p){if(p=xo(p),p){const g=N.findKey(l,p);g&&(!s||_a(l,l[g],g,s))&&(delete l[g],c=!0)}}return N.isArray(i)?i.forEach(d):d(i),c}clear(i){const s=Object.keys(this);let l=s.length,c=!1;for(;l--;){const d=s[l];(!i||_a(this,this[d],d,i,!0))&&(delete this[d],c=!0)}return c}normalize(i){const s=this,l={};return N.forEach(this,(c,d)=>{const p=N.findKey(l,d);if(p){s[p]=ss(c),delete s[d];return}const g=i?b0(d):String(d).trim();g!==d&&delete s[d],s[g]=ss(c),l[g]=!0}),this}concat(...i){return this.constructor.concat(this,...i)}toJSON(i){const s=Object.create(null);return N.forEach(this,(l,c)=>{l!=null&&l!==!1&&(s[c]=i&&N.isArray(l)?l.join(", "):l)}),s}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([i,s])=>i+": "+s).join(` -`)}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(i){return i instanceof this?i:new this(i)}static concat(i,...s){const l=new this(i);return s.forEach(c=>l.set(c)),l}static accessor(i){const l=(this[Ff]=this[Ff]={accessors:{}}).accessors,c=this.prototype;function d(p){const g=xo(p);l[g]||(U0(c,p),l[g]=!0)}return N.isArray(i)?i.forEach(d):d(i),this}}ft.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);N.reduceDescriptors(ft.prototype,({value:r},i)=>{let s=i[0].toUpperCase()+i.slice(1);return{get:()=>r,set(l){this[s]=l}}});N.freezeMethods(ft);function Na(r,i){const s=this||Io,l=i||s,c=ft.from(l.headers);let d=l.data;return N.forEach(r,function(g){d=g.call(s,d,c.normalize(),i?i.status:void 0)}),c.normalize(),d}function Xp(r){return!!(r&&r.__CANCEL__)}function _r(r,i,s){ae.call(this,r??"canceled",ae.ERR_CANCELED,i,s),this.name="CanceledError"}N.inherits(_r,ae,{__CANCEL__:!0});function Jp(r,i,s){const l=s.config.validateStatus;!s.status||!l||l(s.status)?r(s):i(new ae("Request failed with status code "+s.status,[ae.ERR_BAD_REQUEST,ae.ERR_BAD_RESPONSE][Math.floor(s.status/100)-4],s.config,s.request,s))}function H0(r){const i=/^([-+\w]{1,25})(:?\/\/|:)/.exec(r);return i&&i[1]||""}function V0(r,i){r=r||10;const s=new Array(r),l=new Array(r);let c=0,d=0,p;return i=i!==void 0?i:1e3,function(w){const v=Date.now(),S=l[d];p||(p=v),s[c]=w,l[c]=v;let j=d,R=0;for(;j!==c;)R+=s[j++],j=j%r;if(c=(c+1)%r,c===d&&(d=(d+1)%r),v-p{s=S,c=null,d&&(clearTimeout(d),d=null),r.apply(null,v)};return[(...v)=>{const S=Date.now(),j=S-s;j>=l?p(v,S):(c=v,d||(d=setTimeout(()=>{d=null,p(c)},l-j)))},()=>c&&p(c)]}const ps=(r,i,s=3)=>{let l=0;const c=V0(50,250);return W0(d=>{const p=d.loaded,g=d.lengthComputable?d.total:void 0,w=p-l,v=c(w),S=p<=g;l=p;const j={loaded:p,total:g,progress:g?p/g:void 0,bytes:w,rate:v||void 0,estimated:v&&g&&S?(g-p)/v:void 0,event:d,lengthComputable:g!=null,[i?"download":"upload"]:!0};r(j)},s)},Bf=(r,i)=>{const s=r!=null;return[l=>i[0]({lengthComputable:s,total:r,loaded:l}),i[1]]},bf=r=>(...i)=>N.asap(()=>r(...i)),Y0=nt.hasStandardBrowserEnv?((r,i)=>s=>(s=new URL(s,nt.origin),r.protocol===s.protocol&&r.host===s.host&&(i||r.port===s.port)))(new URL(nt.origin),nt.navigator&&/(msie|trident)/i.test(nt.navigator.userAgent)):()=>!0,q0=nt.hasStandardBrowserEnv?{write(r,i,s,l,c,d){const p=[r+"="+encodeURIComponent(i)];N.isNumber(s)&&p.push("expires="+new Date(s).toGMTString()),N.isString(l)&&p.push("path="+l),N.isString(c)&&p.push("domain="+c),d===!0&&p.push("secure"),document.cookie=p.join("; ")},read(r){const i=document.cookie.match(new RegExp("(^|;\\s*)("+r+")=([^;]*)"));return i?decodeURIComponent(i[3]):null},remove(r){this.write(r,"",Date.now()-864e5)}}:{write(){},read(){return null},remove(){}};function Q0(r){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(r)}function G0(r,i){return i?r.replace(/\/?\/$/,"")+"/"+i.replace(/^\/+/,""):r}function Zp(r,i){return r&&!Q0(i)?G0(r,i):i}const Uf=r=>r instanceof ft?{...r}:r;function qn(r,i){i=i||{};const s={};function l(v,S,j,R){return N.isPlainObject(v)&&N.isPlainObject(S)?N.merge.call({caseless:R},v,S):N.isPlainObject(S)?N.merge({},S):N.isArray(S)?S.slice():S}function c(v,S,j,R){if(N.isUndefined(S)){if(!N.isUndefined(v))return l(void 0,v,j,R)}else return l(v,S,j,R)}function d(v,S){if(!N.isUndefined(S))return l(void 0,S)}function p(v,S){if(N.isUndefined(S)){if(!N.isUndefined(v))return l(void 0,v)}else return l(void 0,S)}function g(v,S,j){if(j in i)return l(v,S);if(j in r)return l(void 0,v)}const w={url:d,method:d,data:d,baseURL:p,transformRequest:p,transformResponse:p,paramsSerializer:p,timeout:p,timeoutMessage:p,withCredentials:p,withXSRFToken:p,adapter:p,responseType:p,xsrfCookieName:p,xsrfHeaderName:p,onUploadProgress:p,onDownloadProgress:p,decompress:p,maxContentLength:p,maxBodyLength:p,beforeRedirect:p,transport:p,httpAgent:p,httpsAgent:p,cancelToken:p,socketPath:p,responseEncoding:p,validateStatus:g,headers:(v,S,j)=>c(Uf(v),Uf(S),j,!0)};return N.forEach(Object.keys(Object.assign({},r,i)),function(S){const j=w[S]||c,R=j(r[S],i[S],S);N.isUndefined(R)&&j!==g||(s[S]=R)}),s}const eh=r=>{const i=qn({},r);let{data:s,withXSRFToken:l,xsrfHeaderName:c,xsrfCookieName:d,headers:p,auth:g}=i;i.headers=p=ft.from(p),i.url=Qp(Zp(i.baseURL,i.url),r.params,r.paramsSerializer),g&&p.set("Authorization","Basic "+btoa((g.username||"")+":"+(g.password?unescape(encodeURIComponent(g.password)):"")));let w;if(N.isFormData(s)){if(nt.hasStandardBrowserEnv||nt.hasStandardBrowserWebWorkerEnv)p.setContentType(void 0);else if((w=p.getContentType())!==!1){const[v,...S]=w?w.split(";").map(j=>j.trim()).filter(Boolean):[];p.setContentType([v||"multipart/form-data",...S].join("; "))}}if(nt.hasStandardBrowserEnv&&(l&&N.isFunction(l)&&(l=l(i)),l||l!==!1&&Y0(i.url))){const v=c&&d&&q0.read(d);v&&p.set(c,v)}return i},K0=typeof XMLHttpRequest<"u",X0=K0&&function(r){return new Promise(function(s,l){const c=eh(r);let d=c.data;const p=ft.from(c.headers).normalize();let{responseType:g,onUploadProgress:w,onDownloadProgress:v}=c,S,j,R,L,T;function O(){L&&L(),T&&T(),c.cancelToken&&c.cancelToken.unsubscribe(S),c.signal&&c.signal.removeEventListener("abort",S)}let _=new XMLHttpRequest;_.open(c.method.toUpperCase(),c.url,!0),_.timeout=c.timeout;function b(){if(!_)return;const B=ft.from("getAllResponseHeaders"in _&&_.getAllResponseHeaders()),I={data:!g||g==="text"||g==="json"?_.responseText:_.response,status:_.status,statusText:_.statusText,headers:B,config:r,request:_};Jp(function(V){s(V),O()},function(V){l(V),O()},I),_=null}"onloadend"in _?_.onloadend=b:_.onreadystatechange=function(){!_||_.readyState!==4||_.status===0&&!(_.responseURL&&_.responseURL.indexOf("file:")===0)||setTimeout(b)},_.onabort=function(){_&&(l(new ae("Request aborted",ae.ECONNABORTED,r,_)),_=null)},_.onerror=function(){l(new ae("Network Error",ae.ERR_NETWORK,r,_)),_=null},_.ontimeout=function(){let W=c.timeout?"timeout of "+c.timeout+"ms exceeded":"timeout exceeded";const I=c.transitional||Gp;c.timeoutErrorMessage&&(W=c.timeoutErrorMessage),l(new ae(W,I.clarifyTimeoutError?ae.ETIMEDOUT:ae.ECONNABORTED,r,_)),_=null},d===void 0&&p.setContentType(null),"setRequestHeader"in _&&N.forEach(p.toJSON(),function(W,I){_.setRequestHeader(I,W)}),N.isUndefined(c.withCredentials)||(_.withCredentials=!!c.withCredentials),g&&g!=="json"&&(_.responseType=c.responseType),v&&([R,T]=ps(v,!0),_.addEventListener("progress",R)),w&&_.upload&&([j,L]=ps(w),_.upload.addEventListener("progress",j),_.upload.addEventListener("loadend",L)),(c.cancelToken||c.signal)&&(S=B=>{_&&(l(!B||B.type?new _r(null,r,_):B),_.abort(),_=null)},c.cancelToken&&c.cancelToken.subscribe(S),c.signal&&(c.signal.aborted?S():c.signal.addEventListener("abort",S)));const U=H0(c.url);if(U&&nt.protocols.indexOf(U)===-1){l(new ae("Unsupported protocol "+U+":",ae.ERR_BAD_REQUEST,r));return}_.send(d||null)})},J0=(r,i)=>{const{length:s}=r=r?r.filter(Boolean):[];if(i||s){let l=new AbortController,c;const d=function(v){if(!c){c=!0,g();const S=v instanceof Error?v:this.reason;l.abort(S instanceof ae?S:new _r(S instanceof Error?S.message:S))}};let p=i&&setTimeout(()=>{p=null,d(new ae(`timeout ${i} of ms exceeded`,ae.ETIMEDOUT))},i);const g=()=>{r&&(p&&clearTimeout(p),p=null,r.forEach(v=>{v.unsubscribe?v.unsubscribe(d):v.removeEventListener("abort",d)}),r=null)};r.forEach(v=>v.addEventListener("abort",d));const{signal:w}=l;return w.unsubscribe=()=>N.asap(g),w}},Z0=function*(r,i){let s=r.byteLength;if(s{const c=ev(r,i);let d=0,p,g=w=>{p||(p=!0,l&&l(w))};return new ReadableStream({async pull(w){try{const{done:v,value:S}=await c.next();if(v){g(),w.close();return}let j=S.byteLength;if(s){let R=d+=j;s(R)}w.enqueue(new Uint8Array(S))}catch(v){throw g(v),v}},cancel(w){return g(w),c.return()}},{highWaterMark:2})},js=typeof fetch=="function"&&typeof Request=="function"&&typeof Response=="function",th=js&&typeof ReadableStream=="function",nv=js&&(typeof TextEncoder=="function"?(r=>i=>r.encode(i))(new TextEncoder):async r=>new Uint8Array(await new Response(r).arrayBuffer())),nh=(r,...i)=>{try{return!!r(...i)}catch{return!1}},rv=th&&nh(()=>{let r=!1;const i=new Request(nt.origin,{body:new ReadableStream,method:"POST",get duplex(){return r=!0,"half"}}).headers.has("Content-Type");return r&&!i}),Vf=64*1024,Ga=th&&nh(()=>N.isReadableStream(new Response("").body)),hs={stream:Ga&&(r=>r.body)};js&&(r=>{["text","arrayBuffer","blob","formData","stream"].forEach(i=>{!hs[i]&&(hs[i]=N.isFunction(r[i])?s=>s[i]():(s,l)=>{throw new ae(`Response type '${i}' is not supported`,ae.ERR_NOT_SUPPORT,l)})})})(new Response);const ov=async r=>{if(r==null)return 0;if(N.isBlob(r))return r.size;if(N.isSpecCompliantForm(r))return(await new Request(nt.origin,{method:"POST",body:r}).arrayBuffer()).byteLength;if(N.isArrayBufferView(r)||N.isArrayBuffer(r))return r.byteLength;if(N.isURLSearchParams(r)&&(r=r+""),N.isString(r))return(await nv(r)).byteLength},iv=async(r,i)=>{const s=N.toFiniteNumber(r.getContentLength());return s??ov(i)},sv=js&&(async r=>{let{url:i,method:s,data:l,signal:c,cancelToken:d,timeout:p,onDownloadProgress:g,onUploadProgress:w,responseType:v,headers:S,withCredentials:j="same-origin",fetchOptions:R}=eh(r);v=v?(v+"").toLowerCase():"text";let L=J0([c,d&&d.toAbortSignal()],p),T;const O=L&&L.unsubscribe&&(()=>{L.unsubscribe()});let _;try{if(w&&rv&&s!=="get"&&s!=="head"&&(_=await iv(S,l))!==0){let I=new Request(i,{method:"POST",body:l,duplex:"half"}),M;if(N.isFormData(l)&&(M=I.headers.get("content-type"))&&S.setContentType(M),I.body){const[V,ne]=Bf(_,ps(bf(w)));l=Hf(I.body,Vf,V,ne)}}N.isString(j)||(j=j?"include":"omit");const b="credentials"in Request.prototype;T=new Request(i,{...R,signal:L,method:s.toUpperCase(),headers:S.normalize().toJSON(),body:l,duplex:"half",credentials:b?j:void 0});let U=await fetch(T);const B=Ga&&(v==="stream"||v==="response");if(Ga&&(g||B&&O)){const I={};["status","statusText","headers"].forEach(ye=>{I[ye]=U[ye]});const M=N.toFiniteNumber(U.headers.get("content-length")),[V,ne]=g&&Bf(M,ps(bf(g),!0))||[];U=new Response(Hf(U.body,Vf,V,()=>{ne&&ne(),O&&O()}),I)}v=v||"text";let W=await hs[N.findKey(hs,v)||"text"](U,r);return!B&&O&&O(),await new Promise((I,M)=>{Jp(I,M,{data:W,headers:ft.from(U.headers),status:U.status,statusText:U.statusText,config:r,request:T})})}catch(b){throw O&&O(),b&&b.name==="TypeError"&&/fetch/i.test(b.message)?Object.assign(new ae("Network Error",ae.ERR_NETWORK,r,T),{cause:b.cause||b}):ae.from(b,b&&b.code,r,T)}}),Ka={http:S0,xhr:X0,fetch:sv};N.forEach(Ka,(r,i)=>{if(r){try{Object.defineProperty(r,"name",{value:i})}catch{}Object.defineProperty(r,"adapterName",{value:i})}});const Wf=r=>`- ${r}`,lv=r=>N.isFunction(r)||r===null||r===!1,rh={getAdapter:r=>{r=N.isArray(r)?r:[r];const{length:i}=r;let s,l;const c={};for(let d=0;d`adapter ${g} `+(w===!1?"is not supported by the environment":"is not available in the build"));let p=i?d.length>1?`since : -`+d.map(Wf).join(` -`):" "+Wf(d[0]):"as no adapter specified";throw new ae("There is no suitable adapter to dispatch the request "+p,"ERR_NOT_SUPPORT")}return l},adapters:Ka};function Oa(r){if(r.cancelToken&&r.cancelToken.throwIfRequested(),r.signal&&r.signal.aborted)throw new _r(null,r)}function Yf(r){return Oa(r),r.headers=ft.from(r.headers),r.data=Na.call(r,r.transformRequest),["post","put","patch"].indexOf(r.method)!==-1&&r.headers.setContentType("application/x-www-form-urlencoded",!1),rh.getAdapter(r.adapter||Io.adapter)(r).then(function(l){return Oa(r),l.data=Na.call(r,r.transformResponse,l),l.headers=ft.from(l.headers),l},function(l){return Xp(l)||(Oa(r),l&&l.response&&(l.response.data=Na.call(r,r.transformResponse,l.response),l.response.headers=ft.from(l.response.headers))),Promise.reject(l)})}const oh="1.7.9",As={};["object","boolean","number","function","string","symbol"].forEach((r,i)=>{As[r]=function(l){return typeof l===r||"a"+(i<1?"n ":" ")+r}});const qf={};As.transitional=function(i,s,l){function c(d,p){return"[Axios v"+oh+"] Transitional option '"+d+"'"+p+(l?". "+l:"")}return(d,p,g)=>{if(i===!1)throw new ae(c(p," has been removed"+(s?" in "+s:"")),ae.ERR_DEPRECATED);return s&&!qf[p]&&(qf[p]=!0,console.warn(c(p," has been deprecated since v"+s+" and will be removed in the near future"))),i?i(d,p,g):!0}};As.spelling=function(i){return(s,l)=>(console.warn(`${l} is likely a misspelling of ${i}`),!0)};function av(r,i,s){if(typeof r!="object")throw new ae("options must be an object",ae.ERR_BAD_OPTION_VALUE);const l=Object.keys(r);let c=l.length;for(;c-- >0;){const d=l[c],p=i[d];if(p){const g=r[d],w=g===void 0||p(g,d,r);if(w!==!0)throw new ae("option "+d+" must be "+w,ae.ERR_BAD_OPTION_VALUE);continue}if(s!==!0)throw new ae("Unknown option "+d,ae.ERR_BAD_OPTION)}}const ls={assertOptions:av,validators:As},Vt=ls.validators;class Vn{constructor(i){this.defaults=i,this.interceptors={request:new $f,response:new $f}}async request(i,s){try{return await this._request(i,s)}catch(l){if(l instanceof Error){let c={};Error.captureStackTrace?Error.captureStackTrace(c):c=new Error;const d=c.stack?c.stack.replace(/^.+\n/,""):"";try{l.stack?d&&!String(l.stack).endsWith(d.replace(/^.+\n.+\n/,""))&&(l.stack+=` -`+d):l.stack=d}catch{}}throw l}}_request(i,s){typeof i=="string"?(s=s||{},s.url=i):s=i||{},s=qn(this.defaults,s);const{transitional:l,paramsSerializer:c,headers:d}=s;l!==void 0&&ls.assertOptions(l,{silentJSONParsing:Vt.transitional(Vt.boolean),forcedJSONParsing:Vt.transitional(Vt.boolean),clarifyTimeoutError:Vt.transitional(Vt.boolean)},!1),c!=null&&(N.isFunction(c)?s.paramsSerializer={serialize:c}:ls.assertOptions(c,{encode:Vt.function,serialize:Vt.function},!0)),ls.assertOptions(s,{baseUrl:Vt.spelling("baseURL"),withXsrfToken:Vt.spelling("withXSRFToken")},!0),s.method=(s.method||this.defaults.method||"get").toLowerCase();let p=d&&N.merge(d.common,d[s.method]);d&&N.forEach(["delete","get","head","post","put","patch","common"],T=>{delete d[T]}),s.headers=ft.concat(p,d);const g=[];let w=!0;this.interceptors.request.forEach(function(O){typeof O.runWhen=="function"&&O.runWhen(s)===!1||(w=w&&O.synchronous,g.unshift(O.fulfilled,O.rejected))});const v=[];this.interceptors.response.forEach(function(O){v.push(O.fulfilled,O.rejected)});let S,j=0,R;if(!w){const T=[Yf.bind(this),void 0];for(T.unshift.apply(T,g),T.push.apply(T,v),R=T.length,S=Promise.resolve(s);j{if(!l._listeners)return;let d=l._listeners.length;for(;d-- >0;)l._listeners[d](c);l._listeners=null}),this.promise.then=c=>{let d;const p=new Promise(g=>{l.subscribe(g),d=g}).then(c);return p.cancel=function(){l.unsubscribe(d)},p},i(function(d,p,g){l.reason||(l.reason=new _r(d,p,g),s(l.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(i){if(this.reason){i(this.reason);return}this._listeners?this._listeners.push(i):this._listeners=[i]}unsubscribe(i){if(!this._listeners)return;const s=this._listeners.indexOf(i);s!==-1&&this._listeners.splice(s,1)}toAbortSignal(){const i=new AbortController,s=l=>{i.abort(l)};return this.subscribe(s),i.signal.unsubscribe=()=>this.unsubscribe(s),i.signal}static source(){let i;return{token:new cu(function(c){i=c}),cancel:i}}}function uv(r){return function(s){return r.apply(null,s)}}function cv(r){return N.isObject(r)&&r.isAxiosError===!0}const Xa={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(Xa).forEach(([r,i])=>{Xa[i]=r});function ih(r){const i=new Vn(r),s=Dp(Vn.prototype.request,i);return N.extend(s,Vn.prototype,i,{allOwnKeys:!0}),N.extend(s,i,null,{allOwnKeys:!0}),s.create=function(c){return ih(qn(r,c))},s}const be=ih(Io);be.Axios=Vn;be.CanceledError=_r;be.CancelToken=cu;be.isCancel=Xp;be.VERSION=oh;be.toFormData=Es;be.AxiosError=ae;be.Cancel=be.CanceledError;be.all=function(i){return Promise.all(i)};be.spread=uv;be.isAxiosError=cv;be.mergeConfig=qn;be.AxiosHeaders=ft;be.formToJSON=r=>Kp(N.isHTMLForm(r)?new FormData(r):r);be.getAdapter=rh.getAdapter;be.HttpStatusCode=Xa;be.default=be;const dv={apiBaseUrl:"/api"};class fv{constructor(){sf(this,"events",{})}on(i,s){return this.events[i]||(this.events[i]=[]),this.events[i].push(s),()=>this.off(i,s)}off(i,s){this.events[i]&&(this.events[i]=this.events[i].filter(l=>l!==s))}emit(i,s){this.events[i]&&this.events[i].forEach(l=>{l(s)})}}const Oo=new fv,Oe=be.create({baseURL:dv.apiBaseUrl,headers:{"Content-Type":"application/json"},withCredentials:!0});Oe.interceptors.request.use(r=>r,r=>Promise.reject(r));Oe.interceptors.response.use(r=>r,r=>{var s,l,c,d;const i=(s=r.response)==null?void 0:s.data;if(i){const p=(c=(l=r.response)==null?void 0:l.headers)==null?void 0:c["discodeit-request-id"];p&&(i.requestId=p),r.response.data=i}return console.log({error:r,errorResponse:i}),Oo.emit("api-error",{error:r,alert:((d=r.response)==null?void 0:d.status)===403}),r.response&&r.response.status===401&&Oo.emit("auth-error"),Promise.reject(r)});const pv=()=>Oe.defaults.baseURL,hv=async(r,i)=>(await Oe.patch(`/users/${r}`,i,{headers:{"Content-Type":"multipart/form-data"}})).data,mv=async()=>(await Oe.get("/users")).data,Ar=Pr(r=>({users:[],fetchUsers:async()=>{try{const i=await mv();r({users:i})}catch(i){console.error("사용자 목록 조회 실패:",i)}}})),gv=async(r,i,s)=>{const l=new FormData;return l.append("username",r),l.append("password",i),(await Oe.post("/auth/login",l,{params:{"remember-me":s?"true":"false"},headers:{"Content-Type":"multipart/form-data"}})).data},yv=async r=>(await Oe.post("/users",r,{headers:{"Content-Type":"multipart/form-data"}})).data,vv=async()=>{await Oe.get("/auth/csrf-token")},xv=async()=>(await Oe.get("/auth/me")).data,wv=async()=>{await Oe.post("/auth/logout")},Sv=async(r,i)=>{const s={userId:r,newRole:i};return(await Oe.put("/auth/role",s)).data},pt=Pr((r,i)=>({currentUser:null,login:async(s,l,c=!1)=>{const d=await gv(s,l,c);await i().fetchCsrfToken(),r({currentUser:d})},logout:async()=>{await wv(),i().clear(),i().fetchCsrfToken()},fetchCsrfToken:async()=>{await vv()},fetchMe:async()=>{const s=await xv();r({currentUser:s})},clear:()=>{r({currentUser:null})},updateUserRole:async(s,l)=>{await Sv(s,l)}})),q={colors:{brand:{primary:"#5865F2",hover:"#4752C4"},background:{primary:"#1a1a1a",secondary:"#2a2a2a",tertiary:"#333333",input:"#40444B",hover:"rgba(255, 255, 255, 0.1)"},text:{primary:"#ffffff",secondary:"#cccccc",muted:"#999999"},status:{online:"#43b581",idle:"#faa61a",dnd:"#f04747",offline:"#747f8d",error:"#ED4245"},border:{primary:"#404040"}}},sh=k.div` - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; -`,lh=k.div` - background: ${q.colors.background.primary}; - padding: 32px; - border-radius: 8px; - width: 440px; - - h2 { - color: ${q.colors.text.primary}; - margin-bottom: 24px; - font-size: 24px; - font-weight: bold; - } - - form { - display: flex; - flex-direction: column; - gap: 16px; - } -`,jo=k.input` - width: 100%; - padding: 10px; - border-radius: 4px; - background: ${q.colors.background.input}; - border: none; - color: ${q.colors.text.primary}; - font-size: 16px; - - &::placeholder { - color: ${q.colors.text.muted}; - } - - &:focus { - outline: none; - } -`,Cv=k.input.attrs({type:"checkbox"})` - width: 16px; - height: 16px; - padding: 0; - border-radius: 4px; - background: ${q.colors.background.input}; - border: none; - color: ${q.colors.text.primary}; - cursor: pointer; - - &:focus { - outline: none; - } - - &:checked { - background: ${q.colors.brand.primary}; - } -`,ah=k.button` - width: 100%; - padding: 12px; - border-radius: 4px; - background: ${q.colors.brand.primary}; - color: white; - font-size: 16px; - font-weight: 500; - border: none; - cursor: pointer; - transition: background-color 0.2s; - - &:hover { - background: ${q.colors.brand.hover}; - } -`,uh=k.div` - color: ${q.colors.status.error}; - font-size: 14px; - text-align: center; -`,kv=k.p` - text-align: center; - margin-top: 16px; - color: ${({theme:r})=>r.colors.text.muted}; - font-size: 14px; -`,Ev=k.span` - color: ${({theme:r})=>r.colors.brand.primary}; - cursor: pointer; - - &:hover { - text-decoration: underline; - } -`,Gi=k.div` - margin-bottom: 20px; -`,Ki=k.label` - display: block; - color: ${({theme:r})=>r.colors.text.muted}; - font-size: 12px; - font-weight: 700; - margin-bottom: 8px; -`,Ma=k.span` - color: ${({theme:r})=>r.colors.status.error}; -`,jv=k.div` - display: flex; - flex-direction: column; - align-items: center; - margin: 10px 0; -`,Av=k.img` - width: 80px; - height: 80px; - border-radius: 50%; - margin-bottom: 10px; - object-fit: cover; -`,Rv=k.input` - display: none; -`,Pv=k.label` - color: ${({theme:r})=>r.colors.brand.primary}; - cursor: pointer; - font-size: 14px; - - &:hover { - text-decoration: underline; - } -`,Tv=k.span` - color: ${({theme:r})=>r.colors.brand.primary}; - cursor: pointer; - - &:hover { - text-decoration: underline; - } -`,_v=k(Tv)` - display: block; - text-align: center; - margin-top: 16px; -`,St="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAADwCAYAAAA+VemSAAAACXBIWXMAACE4AAAhOAFFljFgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAw2SURBVHgB7d3PT1XpHcfxBy5g6hipSMolGViACThxJDbVRZ2FXejKlf9h/4GmC1fTRdkwC8fE0JgyJuICFkCjEA04GeZe6P0cPC0698I95zzPc57v5f1K6DSto3A8n/v9nufXGfrr338+dgBMGnYAzCLAgGEEGDCMAAOGEWDAMAIMGEaAAcMIMGAYAQYMI8CAYQQYMIwAA4YRYMAwAgwYRoABwwgwYBgBBgwjwIBhBBgwjAADhhFgwDACDBhGgAHDCDBgGAEGDCPAgGEEGDCMAAOGEWDAMAIMGEaAAcMIMGAYAQYMI8CAYQQYMIwAA4YRYMAwAgwYRoABwwgwYBgBBgwjwIBhBBgwjAADhhFgwDACDBhGgAHDCDBgGAEGDCPAgGEEGDCMAAOGEWDAMAIMGEaAAcMIMGAYAQYMI8CAYQQYMIwAA4YRYMAwAgwYRoABwwgwYBgBBgwbcTDvyuWh//33w1/1dexwMRBgYxTW5vVh9/vxYTcxPpR9jY0OffZrdt8fu82ttlvfbLv9j4R5kBHgxCmcE1eH3NfTDTc7PfxZte3lJNgjbmlxxK3+1HKrr1oOg4kAJ0pVdnG+4ZqTw7+psEUoxF91Qv/Di1+db/q+ZpvD7g+T6gb04XLyv6mF3//osuqvTmDn3RGdQCAEOCG6+W/ONdzNTnCrhPZLN2Yb2T99hVhdwOLcSOf37f7hknUN4yedgLoGeb3Rdv/qdAIE2S8CnIDzAuGDQrzXeTZee1OtndaHy9LCSOHvU3++vv693nLPX9LS+0KAa6QQLC2o4sb5a1A7rYGtMqPU+l7v3hpx85+qeVnfdH7W2c7z/Pcrh1RjD5gHromq2JOHY9HCK2Ojzk1dL1fhH90fqxzenDoO/X79DMjhbAQ4Mg1OPXl4KauGodrls6j6FaXKq+dZn/IQ13ENBgkBjiRvQR99V2/lmZos9lc+PxOuxdd1uL3gp6pfVDwDR6Ab9cG9Me9VLAZ1CiHpmXhz6yibakJxVODAZpoN9/iBzfCq+sboFkJ/SAwyrlxAujE1WJWSIiO/sYKlxSpTnbEBqnBxVOBA9LybWnjloM8An6ysitc1NCe5FcvgqgVw/85o1OmhItY32n39uqnJuC3/FAEuhavmmcLra77UN7XP2322qRNX494aqvgojqvmUcrhFa1+6tdXkae6tMiEhR3FEWBPNOCTcni1rZCli4OHAHuQ4mjzaewJHlxMI1Wked5Uw7v99ijbwqd/FnVQQ7WmQyiOAFegZ7a736ZzCU820h+7nbfHbnO7XSq4p3+vmHbfMwdcBgGuoO4dNQrZxtaR+08nqNueT73Y2D7qTIW5aLRXGcUR4JL03FtHeBXa9Y2jyhX2PHudiqg/K9ZuoY3t/uan8TkCXIKCG/u5V2Fae9N2a+vtKO2tjqfVnxfj5zw5O4sWugwCXIJa51hiB/e0tfVWdkZX6CrMCHl5BLigWDt0RCc6rrxo1XZQu6rw6qt2tq47FD0G9Lu8E79FgAvIWucIO3QU2B9ftpK4sVWFZ5rDQTYbqHUOcdztRcJCjgLUToauvrqpny4fJlWVlp/5P4BOH1IcbFcdAe6Tght6h5FeiaLwpnZTq5VW2HzN1eYfUoS3OgLcp9sL4cOrkKT6YrI8dFUHnDQYR3j94Rm4D9kLxQLuV009vKdpXbXae00vFdm8UWVZJ3ojwH3QcS+hnn1VifSMaemVoPqeVzqDT6rG2oivQS5dH33l70ZS262w7n04yhae8MrTMAhwH0KNPFsfyNH3vd+pxkwD1Ydn4HOodQ5VfTXHyrMgqiDA55ibCbNJX1VLc6xAFQT4HCEGr9Q6s3wQPhDgM4RqnzWVQusMHwjwGTS66puCS/WFLwT4DCHOKia88IkA96BjTkOcVbzDQgZ4RIB7CBFejTzz7AufCHAPWn3lGwse4BsB7uGa5wqcLS3k7XvwjAD3cOWy84pnX4RAgHvw/QzMLhyEQIC7CLF4Y4+DyxEAAe4iRIB3PzD6DP8IcBejnncPagCL/bAIgQB34fsc5P2PtM8IgwBHcMjJqQiEAHfBm+JhBQGO4IDlkwiEAHdx2PIbuFhv+MPFQ4C7ODx0Xo2OOiAIAhwBz9QIhQB34XvOlhYaoRDgLg5+dl7pcACqMEIgwF2EWDV1bZwAwz8C3IVOzfAd4omrXGr4x13Vg++jb6YmudTwj7uqh733fgOsM6YZzIJvBLiH3Q/+NyDMB3pNCy4u3k7Yw+57/wNZM9PDbu2NGwjqJiauDrmvpxufXiv6+f+v63fw8SjrZDgLLBwC3INO0NBAls+2V220jurZNXw6h8K6ODfibsye/UjQnNR/nnQcGk/IX/DNsbp+EeAetAVQVaQ56fe5dXGu4X54YTPASwsj7uZ8o/CHmkJ/Y7aRfb3eaBNkj3gGPsNOgNZPN7G1RR36fh8/uJS96LxqR6Kf/9H9MRa2eEKAz7C5FaZS3l6w0/goaArchMeFKPkHwrVxbr+quIJn0LNqiFZPVSjEmx98U7UNVS016PWXe6NU4ooI8DnWN8O8DuX+H0eTnxdeWgjb7uv3/vMd9lpWQYDPEep9Rrp5by+kOy+s7+/mfPhWXyPzFrqRVHHlzpFPgYTwTScg87NphjhmZdTgGMohwH1YexPupdx3b40mN5ij6tuMuHabKlweV60PGo0OdTB7ioM5WjEWW5PNHqVw1fq09ibcu33zqZpUQjzTjN/Ws1urHK5an9bWW0Ffj5JSiOv4HiaYEy6Fq9YnLa1cfRWuCku+wOHmXL2DOnUEmGOHyiHABagKh17Dqxv57rcj7k+3RpKfJ0b9CHBBKy/ivOhIU0yPH4xdqD3EV37HB1ZRBLignc6c8MZW2FY6p5ZSK7b0bNyMOM3CTiE7CHAJz1+2or7vV1Msj74by4IcoyKHOMygH4fhptsHFgEuQRXqx5fx7zYFWRX5ycNL2UqpUFV5512cDuNLvAS9ONawlaQ10jpSJsZ64S+d3iCvm3777XGntW9nx9fsfqh+JK5+Nq0Qi43WvTgCXMHqq5abma53g75Gqmen9fX/alz1CBtNmenfj7k6yvIxQ3Wiha5AN/r3K4fJtX55hVarvVTy8AB9OMV0GGdwf+AQ4IpU4f75LN27Tzt9HtwbKzynrNF2zXvHsvOWClwGAfZAN18dg1r9UnuthSFF6WeK1doS4HIIsCeqVrHbziLUUpdZornc6S5iDC5p8A3FEWCPVn9KO8RlTpVUeJ8u/xLsUAPR780UUjkE2LOUQ6x11jPN4n/l+WDdaqDznEOdO3YREOAAFOJUn4mrTA3p51KQNU/sM8g8/5bHPHAgeibWAND9O2mdtlF147yCm2/o0IeBXlyuAwDKfjDotBMWcJRHBQ5IlUUVa1Bv0O1squnkVSllvd5kAXQVBDiwfBAo5pyqFbo2od5+cVEQ4Ag0CKRnYrWedVfjlLqBlEfsrSDAEWnwJx8Eqsve+zQCrA+SOq/DoCDAkeWDQE+X63k23txKIzRUXz8IcE00Qv23f/wSta3Odim9q/+Zc6Pz3Ev19YNppJrpRtaXXrGinUMhp5zUvqfg+Uu2HvlCgBORB1nzqYtzDTc77ffoHC3CSGEAS4N5zPv6Q4ATo7lVfV253MoWXegMrKob6xWaFKax9PzNdJpfBDhRqlL7n6qy2mqFWeuY9QaDfttsfRCoXd1NYOS5rnPEBh0BNuB0mGVifOgk1Ncb2VJGbVLIdxnp12qqaHO7HXQHURH6ngZ5RVqdCLBBqqj62jCwiknbBJefEd5QCDCCUWgV3hRa+EFFgBEEbXMcBBjeabR55UWLUzYiIMDwRoHVK1iZKoqHAMMLqm49CDAqyxefID42MwCGEWDAMAIMGEaAAcMIMGAYAQYMI8CAYQQYMIwAA4YRYMAwAgwYRoABwwgwYBgBBgwjwIBhBBgwjAADhhFgwDACDBhGgAHDCDBgGAEGDCPAgGEEGDCMAAOGEWDAMAIMGEaAAcMIMGAYAQYMI8CAYQQYMIwAA4YRYMAwAgwYRoABwwgwYBgBBgwjwIBhBBgwjAADhhFgwDACDBhGgAHDCDBgGAEGDCPAgGEEGDCMAAOGEWDAMAIMGEaAAcMIMGAYAQYMI8CAYQQYMIwAA4YRYMAwAgwYRoABwwgwYBgBBgwjwIBhBBgwjAADhv0XZkN9IbEGbp4AAAAASUVORK5CYII=",Nv=({isOpen:r,onClose:i})=>{const[s,l]=K.useState(""),[c,d]=K.useState(""),[p,g]=K.useState(""),[w,v]=K.useState(null),[S,j]=K.useState(null),[R,L]=K.useState(""),{fetchCsrfToken:T}=pt(),O=K.useCallback(()=>{S&&URL.revokeObjectURL(S),j(null),v(null),l(""),d(""),g(""),L("")},[S]),_=K.useCallback(()=>{O(),i()},[]),b=B=>{var I;const W=(I=B.target.files)==null?void 0:I[0];if(W){v(W);const M=new FileReader;M.onloadend=()=>{j(M.result)},M.readAsDataURL(W)}},U=async B=>{B.preventDefault(),L("");try{const W=new FormData;W.append("userCreateRequest",new Blob([JSON.stringify({email:s,username:c,password:p})],{type:"application/json"})),w&&W.append("profile",w),await yv(W),await T(),i()}catch{L("회원가입에 실패했습니다.")}};return r?h.jsx(sh,{children:h.jsxs(lh,{children:[h.jsx("h2",{children:"계정 만들기"}),h.jsxs("form",{onSubmit:U,children:[h.jsxs(Gi,{children:[h.jsxs(Ki,{children:["이메일 ",h.jsx(Ma,{children:"*"})]}),h.jsx(jo,{type:"email",value:s,onChange:B=>l(B.target.value),required:!0})]}),h.jsxs(Gi,{children:[h.jsxs(Ki,{children:["사용자명 ",h.jsx(Ma,{children:"*"})]}),h.jsx(jo,{type:"text",value:c,onChange:B=>d(B.target.value),required:!0})]}),h.jsxs(Gi,{children:[h.jsxs(Ki,{children:["비밀번호 ",h.jsx(Ma,{children:"*"})]}),h.jsx(jo,{type:"password",value:p,onChange:B=>g(B.target.value),required:!0})]}),h.jsxs(Gi,{children:[h.jsx(Ki,{children:"프로필 이미지"}),h.jsxs(jv,{children:[h.jsx(Av,{src:S||St,alt:"profile"}),h.jsx(Rv,{type:"file",accept:"image/*",onChange:b,id:"profile-image"}),h.jsx(Pv,{htmlFor:"profile-image",children:"이미지 변경"})]})]}),R&&h.jsx(uh,{children:R}),h.jsx(ah,{type:"submit",children:"계속하기"}),h.jsx(_v,{onClick:_,children:"이미 계정이 있으신가요?"})]})]})}):null},Ov=({isOpen:r,onClose:i})=>{const[s,l]=K.useState(""),[c,d]=K.useState(""),[p,g]=K.useState(""),[w,v]=K.useState(!1),[S,j]=K.useState(!1),{login:R}=pt(),{fetchUsers:L}=Ar(),T=K.useCallback(()=>{l(""),d(""),g(""),j(!1),v(!1)},[]),O=K.useCallback(()=>{T(),v(!0)},[T,i]),_=async()=>{var b;try{await R(s,c,S),await L(),T(),i()}catch(U){console.error("로그인 에러:",U),((b=U.response)==null?void 0:b.status)===401?g("아이디 또는 비밀번호가 올바르지 않습니다."):g("로그인에 실패했습니다.")}};return r?h.jsxs(h.Fragment,{children:[h.jsx(sh,{children:h.jsxs(lh,{children:[h.jsx("h2",{children:"돌아오신 것을 환영해요!"}),h.jsxs("form",{onSubmit:b=>{b.preventDefault(),_()},children:[h.jsx(jo,{type:"text",placeholder:"사용자 이름",value:s,onChange:b=>l(b.target.value)}),h.jsx(jo,{type:"password",placeholder:"비밀번호",value:c,onChange:b=>d(b.target.value)}),h.jsxs(Mv,{children:[h.jsx(Cv,{id:"rememberMe",checked:S,onChange:b=>j(b.target.checked)}),h.jsx(Lv,{htmlFor:"rememberMe",children:"로그인 유지"})]}),p&&h.jsx(uh,{children:p}),h.jsx(ah,{type:"submit",children:"로그인"})]}),h.jsxs(kv,{children:["계정이 필요한가요? ",h.jsx(Ev,{onClick:O,children:"가입하기"})]})]})}),h.jsx(Nv,{isOpen:w,onClose:()=>v(!1)})]}):null},Mv=k.div` - display: flex; - align-items: center; - margin: 10px 0; - justify-content: flex-start; -`,Lv=k.label` - margin-left: 8px; - font-size: 14px; - color: #666; - cursor: pointer; - text-align: left; -`,Iv=async r=>(await Oe.get(`/channels?userId=${r}`)).data,Dv=async r=>(await Oe.post("/channels/public",r)).data,zv=async r=>{const i={participantIds:r};return(await Oe.post("/channels/private",i)).data},$v=async(r,i)=>(await Oe.patch(`/channels/${r}`,i)).data,Fv=async r=>{await Oe.delete(`/channels/${r}`)},Bv=async r=>(await Oe.get("/readStatuses",{params:{userId:r}})).data,bv=async(r,i)=>{const s={newLastReadAt:i};return(await Oe.patch(`/readStatuses/${r}`,s)).data},Uv=async(r,i,s)=>{const l={userId:r,channelId:i,lastReadAt:s};return(await Oe.post("/readStatuses",l)).data},Ao=Pr((r,i)=>({readStatuses:{},fetchReadStatuses:async()=>{try{const{currentUser:s}=pt.getState();if(!s)return;const c=(await Bv(s.id)).reduce((d,p)=>(d[p.channelId]={id:p.id,lastReadAt:p.lastReadAt},d),{});r({readStatuses:c})}catch(s){console.error("읽음 상태 조회 실패:",s)}},updateReadStatus:async s=>{try{const{currentUser:l}=pt.getState();if(!l)return;const c=i().readStatuses[s];let d;c?d=await bv(c.id,new Date().toISOString()):d=await Uv(l.id,s,new Date().toISOString()),r(p=>({readStatuses:{...p.readStatuses,[s]:{id:d.id,lastReadAt:d.lastReadAt}}}))}catch(l){console.error("읽음 상태 업데이트 실패:",l)}},hasUnreadMessages:(s,l)=>{const c=i().readStatuses[s],d=c==null?void 0:c.lastReadAt;return!d||new Date(l)>new Date(d)}})),jn=Pr((r,i)=>({channels:[],pollingInterval:null,loading:!1,error:null,fetchChannels:async s=>{r({loading:!0,error:null});try{const l=await Iv(s);r(d=>{const p=new Set(d.channels.map(S=>S.id)),g=l.filter(S=>!p.has(S.id));return{channels:[...d.channels.filter(S=>l.some(j=>j.id===S.id)),...g],loading:!1}});const{fetchReadStatuses:c}=Ao.getState();return c(),l}catch(l){return r({error:l,loading:!1}),[]}},startPolling:s=>{const l=i().pollingInterval;l&&clearInterval(l);const c=setInterval(()=>{i().fetchChannels(s)},3e3);r({pollingInterval:c})},stopPolling:()=>{const s=i().pollingInterval;s&&(clearInterval(s),r({pollingInterval:null}))},createPublicChannel:async s=>{try{const l=await Dv(s);return r(c=>c.channels.some(p=>p.id===l.id)?c:{channels:[...c.channels,{...l,participantIds:[],lastMessageAt:new Date().toISOString()}]}),l}catch(l){throw console.error("공개 채널 생성 실패:",l),l}},createPrivateChannel:async s=>{try{const l=await zv(s);return r(c=>c.channels.some(p=>p.id===l.id)?c:{channels:[...c.channels,{...l,participantIds:s,lastMessageAt:new Date().toISOString()}]}),l}catch(l){throw console.error("비공개 채널 생성 실패:",l),l}},updatePublicChannel:async(s,l)=>{try{const c=await $v(s,l);return r(d=>({channels:d.channels.map(p=>p.id===s?{...p,...c}:p)})),c}catch(c){throw console.error("채널 수정 실패:",c),c}},deleteChannel:async s=>{try{await Fv(s),r(l=>({channels:l.channels.filter(c=>c.id!==s)}))}catch(l){throw console.error("채널 삭제 실패:",l),l}}})),Hv=async r=>(await Oe.get(`/binaryContents/${r}`)).data,Vv=r=>`${pv()}/binaryContents/${r}/download`,An=Pr((r,i)=>({binaryContents:{},fetchBinaryContent:async s=>{if(i().binaryContents[s])return i().binaryContents[s];try{const l=await Hv(s),{contentType:c,fileName:d,size:p}=l,w={url:Vv(s),contentType:c,fileName:d,size:p};return r(v=>({binaryContents:{...v.binaryContents,[s]:w}})),w}catch(l){return console.error("첨부파일 정보 조회 실패:",l),null}}})),Do=k.div` - position: absolute; - bottom: -3px; - right: -3px; - width: 16px; - height: 16px; - border-radius: 50%; - background: ${r=>r.$online?q.colors.status.online:q.colors.status.offline}; - border: 4px solid ${r=>r.$background||q.colors.background.secondary}; -`;k.div` - width: 8px; - height: 8px; - border-radius: 50%; - margin-right: 8px; - background: ${r=>q.colors.status[r.status||"offline"]||q.colors.status.offline}; -`;const Nr=k.div` - position: relative; - width: ${r=>r.$size||"32px"}; - height: ${r=>r.$size||"32px"}; - flex-shrink: 0; - margin: ${r=>r.$margin||"0"}; -`,nn=k.img` - width: 100%; - height: 100%; - border-radius: 50%; - object-fit: cover; - border: ${r=>r.$border||"none"}; -`;function Wv({isOpen:r,onClose:i,user:s}){var M,V;const[l,c]=K.useState(s.username),[d,p]=K.useState(s.email),[g,w]=K.useState(""),[v,S]=K.useState(null),[j,R]=K.useState(""),[L,T]=K.useState(null),{binaryContents:O,fetchBinaryContent:_}=An(),{logout:b,fetchMe:U}=pt();K.useEffect(()=>{var ne;(ne=s.profile)!=null&&ne.id&&!O[s.profile.id]&&_(s.profile.id)},[s.profile,O,_]);const B=()=>{c(s.username),p(s.email),w(""),S(null),T(null),R(""),i()},W=ne=>{var Ie;const ye=(Ie=ne.target.files)==null?void 0:Ie[0];if(ye){S(ye);const ie=new FileReader;ie.onloadend=()=>{T(ie.result)},ie.readAsDataURL(ye)}},I=async ne=>{ne.preventDefault(),R("");try{const ye=new FormData,Ie={};l!==s.username&&(Ie.newUsername=l),d!==s.email&&(Ie.newEmail=d),g&&(Ie.newPassword=g),(Object.keys(Ie).length>0||v)&&(ye.append("userUpdateRequest",new Blob([JSON.stringify(Ie)],{type:"application/json"})),v&&ye.append("profile",v),await hv(s.id,ye),await U()),i()}catch{R("사용자 정보 수정에 실패했습니다.")}};return r?h.jsx(Yv,{children:h.jsxs(qv,{children:[h.jsx("h2",{children:"프로필 수정"}),h.jsxs("form",{onSubmit:I,children:[h.jsxs(Xi,{children:[h.jsx(Ji,{children:"프로필 이미지"}),h.jsxs(Gv,{children:[h.jsx(Kv,{src:L||((M=s.profile)!=null&&M.id?(V=O[s.profile.id])==null?void 0:V.url:void 0)||St,alt:"profile"}),h.jsx(Xv,{type:"file",accept:"image/*",onChange:W,id:"profile-image"}),h.jsx(Jv,{htmlFor:"profile-image",children:"이미지 변경"})]})]}),h.jsxs(Xi,{children:[h.jsxs(Ji,{children:["사용자명 ",h.jsx(Gf,{children:"*"})]}),h.jsx(La,{type:"text",value:l,onChange:ne=>c(ne.target.value),required:!0})]}),h.jsxs(Xi,{children:[h.jsxs(Ji,{children:["이메일 ",h.jsx(Gf,{children:"*"})]}),h.jsx(La,{type:"email",value:d,onChange:ne=>p(ne.target.value),required:!0})]}),h.jsxs(Xi,{children:[h.jsx(Ji,{children:"새 비밀번호"}),h.jsx(La,{type:"password",placeholder:"변경하지 않으려면 비워두세요",value:g,onChange:ne=>w(ne.target.value)})]}),j&&h.jsx(Qv,{children:j}),h.jsxs(Zv,{children:[h.jsx(Qf,{type:"button",onClick:B,$secondary:!0,children:"취소"}),h.jsx(Qf,{type:"submit",children:"저장"})]})]}),h.jsx(ex,{onClick:b,children:"로그아웃"})]})}):null}const Yv=k.div` - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; -`,qv=k.div` - background: ${({theme:r})=>r.colors.background.secondary}; - padding: 32px; - border-radius: 5px; - width: 100%; - max-width: 480px; - - h2 { - color: ${({theme:r})=>r.colors.text.primary}; - margin-bottom: 24px; - text-align: center; - font-size: 24px; - } -`,La=k.input` - width: 100%; - padding: 10px; - margin-bottom: 10px; - border: none; - border-radius: 4px; - background: ${({theme:r})=>r.colors.background.input}; - color: ${({theme:r})=>r.colors.text.primary}; - - &::placeholder { - color: ${({theme:r})=>r.colors.text.muted}; - } - - &:focus { - outline: none; - box-shadow: 0 0 0 2px ${({theme:r})=>r.colors.brand.primary}; - } -`,Qf=k.button` - width: 100%; - padding: 10px; - border: none; - border-radius: 4px; - background: ${({$secondary:r,theme:i})=>r?"transparent":i.colors.brand.primary}; - color: ${({theme:r})=>r.colors.text.primary}; - cursor: pointer; - font-weight: 500; - - &:hover { - background: ${({$secondary:r,theme:i})=>r?i.colors.background.hover:i.colors.brand.hover}; - } -`,Qv=k.div` - color: ${({theme:r})=>r.colors.status.error}; - font-size: 14px; - margin-bottom: 10px; -`,Gv=k.div` - display: flex; - flex-direction: column; - align-items: center; - margin-bottom: 20px; -`,Kv=k.img` - width: 100px; - height: 100px; - border-radius: 50%; - margin-bottom: 10px; - object-fit: cover; -`,Xv=k.input` - display: none; -`,Jv=k.label` - color: ${({theme:r})=>r.colors.brand.primary}; - cursor: pointer; - font-size: 14px; - - &:hover { - text-decoration: underline; - } -`,Zv=k.div` - display: flex; - gap: 10px; - margin-top: 20px; -`,ex=k.button` - width: 100%; - padding: 10px; - margin-top: 16px; - border: none; - border-radius: 4px; - background: transparent; - color: ${({theme:r})=>r.colors.status.error}; - cursor: pointer; - font-weight: 500; - - &:hover { - background: ${({theme:r})=>r.colors.status.error}20; - } -`,Xi=k.div` - margin-bottom: 20px; -`,Ji=k.label` - display: block; - color: ${({theme:r})=>r.colors.text.muted}; - font-size: 12px; - font-weight: 700; - margin-bottom: 8px; -`,Gf=k.span` - color: ${({theme:r})=>r.colors.status.error}; -`,tx=k.div` - display: flex; - align-items: center; - gap: 0.75rem; - padding: 0.5rem 0.75rem; - background-color: ${({theme:r})=>r.colors.background.tertiary}; - width: 100%; - height: 52px; -`,nx=k(Nr)``;k(nn)``;const rx=k.div` - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - justify-content: center; -`,ox=k.div` - font-weight: 500; - color: ${({theme:r})=>r.colors.text.primary}; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - font-size: 0.875rem; - line-height: 1.2; -`,ix=k.div` - font-size: 0.75rem; - color: ${({theme:r})=>r.colors.text.secondary}; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - line-height: 1.2; -`,sx=k.div` - display: flex; - align-items: center; - flex-shrink: 0; -`,lx=k.button` - background: none; - border: none; - padding: 0.25rem; - cursor: pointer; - color: ${({theme:r})=>r.colors.text.secondary}; - font-size: 18px; - - &:hover { - color: ${({theme:r})=>r.colors.text.primary}; - } -`;function ax({user:r}){var d,p;const[i,s]=K.useState(!1),{binaryContents:l,fetchBinaryContent:c}=An();return K.useEffect(()=>{var g;(g=r.profile)!=null&&g.id&&!l[r.profile.id]&&c(r.profile.id)},[r.profile,l,c]),h.jsxs(h.Fragment,{children:[h.jsxs(tx,{children:[h.jsxs(nx,{children:[h.jsx(nn,{src:(d=r.profile)!=null&&d.id?(p=l[r.profile.id])==null?void 0:p.url:St,alt:r.username}),h.jsx(Do,{$online:!0})]}),h.jsxs(rx,{children:[h.jsx(ox,{children:r.username}),h.jsx(ix,{children:"온라인"})]}),h.jsx(sx,{children:h.jsx(lx,{onClick:()=>s(!0),children:"⚙️"})})]}),h.jsx(Wv,{isOpen:i,onClose:()=>s(!1),user:r})]})}const ux=k.div` - width: 240px; - background: ${q.colors.background.secondary}; - border-right: 1px solid ${q.colors.border.primary}; - display: flex; - flex-direction: column; -`,cx=k.div` - flex: 1; - overflow-y: auto; -`,dx=k.div` - padding: 16px; - font-size: 16px; - font-weight: bold; - color: ${q.colors.text.primary}; -`,du=k.div` - height: 34px; - padding: 0 8px; - margin: 1px 8px; - display: flex; - align-items: center; - gap: 6px; - color: ${r=>r.$hasUnread?r.theme.colors.text.primary:r.theme.colors.text.muted}; - font-weight: ${r=>r.$hasUnread?"600":"normal"}; - cursor: pointer; - background: ${r=>r.$isActive?r.theme.colors.background.hover:"transparent"}; - border-radius: 4px; - - &:hover { - background: ${r=>r.theme.colors.background.hover}; - color: ${r=>r.theme.colors.text.primary}; - } -`,Kf=k.div` - margin-bottom: 8px; -`,Ja=k.div` - padding: 8px 16px; - display: flex; - align-items: center; - color: ${q.colors.text.muted}; - text-transform: uppercase; - font-size: 12px; - font-weight: 600; - cursor: pointer; - user-select: none; - - & > span:nth-child(2) { - flex: 1; - margin-right: auto; - } - - &:hover { - color: ${q.colors.text.primary}; - } -`,Xf=k.span` - margin-right: 4px; - font-size: 10px; - transition: transform 0.2s; - transform: rotate(${r=>r.$folded?"-90deg":"0deg"}); -`,Jf=k.div` - display: ${r=>r.$folded?"none":"block"}; -`,Za=k(du)` - height: ${r=>r.hasSubtext?"42px":"34px"}; -`,fx=k(Nr)` - width: 32px; - height: 32px; - margin: 0 8px; -`,Zf=k.div` - font-size: 16px; - line-height: 18px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - color: ${r=>r.$isActive||r.$hasUnread?r.theme.colors.text.primary:r.theme.colors.text.muted}; - font-weight: ${r=>r.$hasUnread?"600":"normal"}; -`;k(Do)` - border-color: ${q.colors.background.primary}; -`;const ep=k.button` - background: none; - border: none; - color: ${q.colors.text.muted}; - font-size: 18px; - padding: 0; - cursor: pointer; - width: 16px; - height: 16px; - display: flex; - align-items: center; - justify-content: center; - opacity: 0; - transition: opacity 0.2s, color 0.2s; - - ${Ja}:hover & { - opacity: 1; - } - - &:hover { - color: ${q.colors.text.primary}; - } -`,px=k(Nr)` - width: 40px; - height: 24px; - margin: 0 8px; -`,hx=k.div` - font-size: 12px; - line-height: 13px; - color: ${q.colors.text.muted}; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -`,tp=k.div` - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - justify-content: center; - gap: 2px; -`,ch=k.div` - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.85); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; -`,dh=k.div` - background: ${q.colors.background.primary}; - border-radius: 4px; - width: 440px; - max-width: 90%; -`,fh=k.div` - padding: 16px; - display: flex; - justify-content: space-between; - align-items: center; -`,ph=k.h2` - color: ${q.colors.text.primary}; - font-size: 20px; - font-weight: 600; - margin: 0; -`,hh=k.div` - padding: 0 16px 16px; -`,mh=k.form` - display: flex; - flex-direction: column; - gap: 16px; -`,Ro=k.div` - display: flex; - flex-direction: column; - gap: 8px; -`,Po=k.label` - color: ${q.colors.text.primary}; - font-size: 12px; - font-weight: 600; - text-transform: uppercase; -`,gh=k.p` - color: ${q.colors.text.muted}; - font-size: 14px; - margin: -4px 0 0; -`,Mo=k.input` - padding: 10px; - background: ${q.colors.background.tertiary}; - border: none; - border-radius: 3px; - color: ${q.colors.text.primary}; - font-size: 16px; - - &:focus { - outline: none; - box-shadow: 0 0 0 2px ${q.colors.status.online}; - } - - &::placeholder { - color: ${q.colors.text.muted}; - } -`,yh=k.button` - margin-top: 8px; - padding: 12px; - background: ${q.colors.status.online}; - color: white; - border: none; - border-radius: 3px; - font-size: 14px; - font-weight: 500; - cursor: pointer; - transition: background 0.2s; - - &:hover { - background: #3ca374; - } -`,vh=k.button` - background: none; - border: none; - color: ${q.colors.text.muted}; - font-size: 24px; - cursor: pointer; - padding: 4px; - line-height: 1; - - &:hover { - color: ${q.colors.text.primary}; - } -`,mx=k(Mo)` - margin-bottom: 8px; -`,gx=k.div` - max-height: 300px; - overflow-y: auto; - background: ${q.colors.background.tertiary}; - border-radius: 4px; -`,yx=k.div` - display: flex; - align-items: center; - padding: 8px 12px; - cursor: pointer; - transition: background 0.2s; - - &:hover { - background: ${q.colors.background.hover}; - } - - & + & { - border-top: 1px solid ${q.colors.border.primary}; - } -`,vx=k.input` - margin-right: 12px; - width: 16px; - height: 16px; - cursor: pointer; -`,np=k.img` - width: 32px; - height: 32px; - border-radius: 50%; - margin-right: 12px; -`,xx=k.div` - flex: 1; - min-width: 0; -`,wx=k.div` - color: ${q.colors.text.primary}; - font-size: 14px; - font-weight: 500; -`,Sx=k.div` - color: ${q.colors.text.muted}; - font-size: 12px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -`,Cx=k.div` - padding: 16px; - text-align: center; - color: ${q.colors.text.muted}; -`,xh=k.div` - color: ${q.colors.status.error}; - font-size: 14px; - padding: 8px 0; - text-align: center; - background-color: ${({theme:r})=>r.colors.background.tertiary}; - border-radius: 4px; - margin-bottom: 8px; -`,Ia=k.div` - position: relative; - margin-left: auto; - z-index: 99999; -`,Da=k.button` - background: none; - border: none; - color: ${({theme:r})=>r.colors.text.muted}; - font-size: 16px; - cursor: pointer; - padding: 4px; - border-radius: 3px; - opacity: 0; - transition: opacity 0.2s, background 0.2s; - - &:hover { - background: ${({theme:r})=>r.colors.background.hover}; - color: ${({theme:r})=>r.colors.text.primary}; - } - - ${du}:hover &, - ${Za}:hover & { - opacity: 1; - } -`,za=k.div` - position: absolute; - top: 100%; - right: 0; - background: ${({theme:r})=>r.colors.background.primary}; - border: 1px solid ${({theme:r})=>r.colors.border.primary}; - border-radius: 4px; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3); - min-width: 120px; - z-index: 100000; -`,Zi=k.div` - padding: 8px 12px; - color: ${({theme:r})=>r.colors.text.primary}; - cursor: pointer; - font-size: 14px; - display: flex; - align-items: center; - gap: 8px; - - &:hover { - background: ${({theme:r})=>r.colors.background.hover}; - } - - &:first-child { - border-radius: 4px 4px 0 0; - } - - &:last-child { - border-radius: 0 0 4px 4px; - } - - &:only-child { - border-radius: 4px; - } -`;function kx(){return h.jsx(dx,{children:"채널 목록"})}var En=(r=>(r.USER="USER",r.CHANNEL_MANAGER="CHANNEL_MANAGER",r.ADMIN="ADMIN",r))(En||{});function Ex({isOpen:r,channel:i,onClose:s,onUpdateSuccess:l}){const[c,d]=K.useState({name:"",description:""}),[p,g]=K.useState(""),[w,v]=K.useState(!1),{updatePublicChannel:S}=jn();K.useEffect(()=>{i&&r&&(d({name:i.name||"",description:i.description||""}),g(""))},[i,r]);const j=L=>{const{name:T,value:O}=L.target;d(_=>({..._,[T]:O}))},R=async L=>{var T,O;if(L.preventDefault(),!!i){g(""),v(!0);try{if(!c.name.trim()){g("채널 이름을 입력해주세요."),v(!1);return}const _={newName:c.name.trim(),newDescription:c.description.trim()},b=await S(i.id,_);l(b)}catch(_){console.error("채널 수정 실패:",_),g(((O=(T=_.response)==null?void 0:T.data)==null?void 0:O.message)||"채널 수정에 실패했습니다. 다시 시도해주세요.")}finally{v(!1)}}};return!r||!i||i.type!=="PUBLIC"?null:h.jsx(ch,{onClick:s,children:h.jsxs(dh,{onClick:L=>L.stopPropagation(),children:[h.jsxs(fh,{children:[h.jsx(ph,{children:"채널 수정"}),h.jsx(vh,{onClick:s,children:"×"})]}),h.jsx(hh,{children:h.jsxs(mh,{onSubmit:R,children:[p&&h.jsx(xh,{children:p}),h.jsxs(Ro,{children:[h.jsx(Po,{children:"채널 이름"}),h.jsx(Mo,{name:"name",value:c.name,onChange:j,placeholder:"새로운-채널",required:!0,disabled:w})]}),h.jsxs(Ro,{children:[h.jsx(Po,{children:"채널 설명"}),h.jsx(gh,{children:"이 채널의 주제를 설명해주세요."}),h.jsx(Mo,{name:"description",value:c.description,onChange:j,placeholder:"채널 설명을 입력하세요",disabled:w})]}),h.jsx(yh,{type:"submit",disabled:w,children:w?"수정 중...":"채널 수정"})]})})]})})}function rp({channel:r,isActive:i,onClick:s,hasUnread:l}){var U;const{currentUser:c}=pt(),{binaryContents:d}=An(),{deleteChannel:p}=jn(),[g,w]=K.useState(null),[v,S]=K.useState(!1),j=(c==null?void 0:c.role)===En.ADMIN||(c==null?void 0:c.role)===En.CHANNEL_MANAGER;K.useEffect(()=>{const B=()=>{g&&w(null)};if(g)return document.addEventListener("click",B),()=>document.removeEventListener("click",B)},[g]);const R=B=>{w(g===B?null:B)},L=()=>{w(null),S(!0)},T=B=>{S(!1),console.log("Channel updated successfully:",B)},O=()=>{S(!1)},_=async B=>{var I;w(null);const W=r.type==="PUBLIC"?r.name:r.type==="PRIVATE"&&r.participants.length>2?`그룹 채팅 (멤버 ${r.participants.length}명)`:((I=r.participants.filter(M=>M.id!==(c==null?void 0:c.id))[0])==null?void 0:I.username)||"1:1 채팅";if(confirm(`"${W}" 채널을 삭제하시겠습니까?`))try{await p(B),console.log("Channel deleted successfully:",B)}catch(M){console.error("Channel delete failed:",M),alert("채널 삭제에 실패했습니다. 다시 시도해주세요.")}};let b;if(r.type==="PUBLIC")b=h.jsxs(du,{$isActive:i,onClick:s,$hasUnread:l,children:["# ",r.name,j&&h.jsxs(Ia,{children:[h.jsx(Da,{onClick:B=>{B.stopPropagation(),R(r.id)},children:"⋯"}),g===r.id&&h.jsxs(za,{onClick:B=>B.stopPropagation(),children:[h.jsx(Zi,{onClick:()=>L(),children:"✏️ 수정"}),h.jsx(Zi,{onClick:()=>_(r.id),children:"🗑️ 삭제"})]})]})]});else{const B=r.participants;if(B.length>2){const W=B.filter(I=>I.id!==(c==null?void 0:c.id)).map(I=>I.username).join(", ");b=h.jsxs(Za,{$isActive:i,onClick:s,children:[h.jsx(px,{children:B.filter(I=>I.id!==(c==null?void 0:c.id)).slice(0,2).map((I,M)=>{var V;return h.jsx(nn,{src:I.profile?(V=d[I.profile.id])==null?void 0:V.url:St,style:{position:"absolute",left:M*16,zIndex:2-M,width:"24px",height:"24px",border:"2px solid #2a2a2a"}},I.id)})}),h.jsxs(tp,{children:[h.jsx(Zf,{$hasUnread:l,children:W}),h.jsxs(hx,{children:["멤버 ",B.length,"명"]})]}),j&&h.jsxs(Ia,{children:[h.jsx(Da,{onClick:I=>{I.stopPropagation(),R(r.id)},children:"⋯"}),g===r.id&&h.jsx(za,{onClick:I=>I.stopPropagation(),children:h.jsx(Zi,{onClick:()=>_(r.id),children:"🗑️ 삭제"})})]})]})}else{const W=B.filter(I=>I.id!==(c==null?void 0:c.id))[0];b=W?h.jsxs(Za,{$isActive:i,onClick:s,children:[h.jsxs(fx,{children:[h.jsx(nn,{src:W.profile?(U=d[W.profile.id])==null?void 0:U.url:St,alt:"profile"}),h.jsx(Do,{$online:W.online})]}),h.jsx(tp,{children:h.jsx(Zf,{$hasUnread:l,children:W.username})}),j&&h.jsxs(Ia,{children:[h.jsx(Da,{onClick:I=>{I.stopPropagation(),R(r.id)},children:"⋯"}),g===r.id&&h.jsx(za,{onClick:I=>I.stopPropagation(),children:h.jsx(Zi,{onClick:()=>_(r.id),children:"🗑️ 삭제"})})]})]}):h.jsx("div",{})}}return h.jsxs(h.Fragment,{children:[b,h.jsx(Ex,{isOpen:v,channel:r,onClose:O,onUpdateSuccess:T})]})}function jx({isOpen:r,type:i,onClose:s,onCreateSuccess:l}){const[c,d]=K.useState({name:"",description:""}),[p,g]=K.useState(""),[w,v]=K.useState([]),[S,j]=K.useState(""),R=Ar(I=>I.users),L=An(I=>I.binaryContents),{currentUser:T}=pt(),O=K.useMemo(()=>R.filter(I=>I.id!==(T==null?void 0:T.id)).filter(I=>I.username.toLowerCase().includes(p.toLowerCase())||I.email.toLowerCase().includes(p.toLowerCase())),[p,R,T]),_=jn(I=>I.createPublicChannel),b=jn(I=>I.createPrivateChannel),U=I=>{const{name:M,value:V}=I.target;d(ne=>({...ne,[M]:V}))},B=I=>{v(M=>M.includes(I)?M.filter(V=>V!==I):[...M,I])},W=async I=>{var M,V;I.preventDefault(),j("");try{let ne;if(i==="PUBLIC"){if(!c.name.trim()){j("채널 이름을 입력해주세요.");return}const ye={name:c.name,description:c.description};ne=await _(ye)}else{if(w.length===0){j("대화 상대를 선택해주세요.");return}const ye=(T==null?void 0:T.id)&&[...w,T.id]||w;ne=await b(ye)}l(ne)}catch(ne){console.error("채널 생성 실패:",ne),j(((V=(M=ne.response)==null?void 0:M.data)==null?void 0:V.message)||"채널 생성에 실패했습니다. 다시 시도해주세요.")}};return r?h.jsx(ch,{onClick:s,children:h.jsxs(dh,{onClick:I=>I.stopPropagation(),children:[h.jsxs(fh,{children:[h.jsx(ph,{children:i==="PUBLIC"?"채널 만들기":"개인 메시지 시작하기"}),h.jsx(vh,{onClick:s,children:"×"})]}),h.jsx(hh,{children:h.jsxs(mh,{onSubmit:W,children:[S&&h.jsx(xh,{children:S}),i==="PUBLIC"?h.jsxs(h.Fragment,{children:[h.jsxs(Ro,{children:[h.jsx(Po,{children:"채널 이름"}),h.jsx(Mo,{name:"name",value:c.name,onChange:U,placeholder:"새로운-채널",required:!0})]}),h.jsxs(Ro,{children:[h.jsx(Po,{children:"채널 설명"}),h.jsx(gh,{children:"이 채널의 주제를 설명해주세요."}),h.jsx(Mo,{name:"description",value:c.description,onChange:U,placeholder:"채널 설명을 입력하세요"})]})]}):h.jsxs(Ro,{children:[h.jsx(Po,{children:"사용자 검색"}),h.jsx(mx,{type:"text",value:p,onChange:I=>g(I.target.value),placeholder:"사용자명 또는 이메일로 검색"}),h.jsx(gx,{children:O.length>0?O.map(I=>h.jsxs(yx,{children:[h.jsx(vx,{type:"checkbox",checked:w.includes(I.id),onChange:()=>B(I.id)}),I.profile?h.jsx(np,{src:L[I.profile.id].url}):h.jsx(np,{src:St}),h.jsxs(xx,{children:[h.jsx(wx,{children:I.username}),h.jsx(Sx,{children:I.email})]})]},I.id)):h.jsx(Cx,{children:"검색 결과가 없습니다."})})]}),h.jsx(yh,{type:"submit",children:i==="PUBLIC"?"채널 만들기":"대화 시작하기"})]})})]})}):null}function Ax({currentUser:r,activeChannel:i,onChannelSelect:s}){var W,I;const[l,c]=K.useState({PUBLIC:!1,PRIVATE:!1}),[d,p]=K.useState({isOpen:!1,type:null}),g=jn(M=>M.channels),w=jn(M=>M.fetchChannels),v=jn(M=>M.startPolling),S=jn(M=>M.stopPolling),j=Ao(M=>M.fetchReadStatuses),R=Ao(M=>M.updateReadStatus),L=Ao(M=>M.hasUnreadMessages);K.useEffect(()=>{if(r)return w(r.id),j(),v(r.id),()=>{S()}},[r,w,j,v,S]);const T=M=>{c(V=>({...V,[M]:!V[M]}))},O=(M,V)=>{V.stopPropagation(),p({isOpen:!0,type:M})},_=()=>{p({isOpen:!1,type:null})},b=async M=>{try{const ne=(await w(r.id)).find(ye=>ye.id===M.id);ne&&s(ne),_()}catch(V){console.error("채널 생성 실패:",V)}},U=M=>{s(M),R(M.id)},B=g.reduce((M,V)=>(M[V.type]||(M[V.type]=[]),M[V.type].push(V),M),{});return h.jsxs(ux,{children:[h.jsx(kx,{}),h.jsxs(cx,{children:[h.jsxs(Kf,{children:[h.jsxs(Ja,{onClick:()=>T("PUBLIC"),children:[h.jsx(Xf,{$folded:l.PUBLIC,children:"▼"}),h.jsx("span",{children:"일반 채널"}),h.jsx(ep,{onClick:M=>O("PUBLIC",M),children:"+"})]}),h.jsx(Jf,{$folded:l.PUBLIC,children:(W=B.PUBLIC)==null?void 0:W.map(M=>h.jsx(rp,{channel:M,isActive:(i==null?void 0:i.id)===M.id,hasUnread:L(M.id,M.lastMessageAt),onClick:()=>U(M)},M.id))})]}),h.jsxs(Kf,{children:[h.jsxs(Ja,{onClick:()=>T("PRIVATE"),children:[h.jsx(Xf,{$folded:l.PRIVATE,children:"▼"}),h.jsx("span",{children:"개인 메시지"}),h.jsx(ep,{onClick:M=>O("PRIVATE",M),children:"+"})]}),h.jsx(Jf,{$folded:l.PRIVATE,children:(I=B.PRIVATE)==null?void 0:I.map(M=>h.jsx(rp,{channel:M,isActive:(i==null?void 0:i.id)===M.id,hasUnread:L(M.id,M.lastMessageAt),onClick:()=>U(M)},M.id))})]})]}),h.jsx(Rx,{children:h.jsx(ax,{user:r})}),h.jsx(jx,{isOpen:d.isOpen,type:d.type,onClose:_,onCreateSuccess:b})]})}const Rx=k.div` - margin-top: auto; - border-top: 1px solid ${({theme:r})=>r.colors.border.primary}; - background-color: ${({theme:r})=>r.colors.background.tertiary}; -`,Px=k.div` - flex: 1; - display: flex; - flex-direction: column; - background: ${({theme:r})=>r.colors.background.primary}; -`,Tx=k.div` - display: flex; - flex-direction: column; - height: 100%; - background: ${({theme:r})=>r.colors.background.primary}; -`,_x=k(Tx)` - justify-content: center; - align-items: center; - flex: 1; - padding: 0 20px; -`,Nx=k.div` - text-align: center; - max-width: 400px; - padding: 20px; - margin-bottom: 80px; -`,Ox=k.div` - font-size: 48px; - margin-bottom: 16px; - animation: wave 2s infinite; - transform-origin: 70% 70%; - - @keyframes wave { - 0% { transform: rotate(0deg); } - 10% { transform: rotate(14deg); } - 20% { transform: rotate(-8deg); } - 30% { transform: rotate(14deg); } - 40% { transform: rotate(-4deg); } - 50% { transform: rotate(10deg); } - 60% { transform: rotate(0deg); } - 100% { transform: rotate(0deg); } - } -`,Mx=k.h2` - color: ${({theme:r})=>r.colors.text.primary}; - font-size: 28px; - font-weight: 700; - margin-bottom: 16px; -`,Lx=k.p` - color: ${({theme:r})=>r.colors.text.muted}; - font-size: 16px; - line-height: 1.6; - word-break: keep-all; -`,op=k.div` - height: 48px; - padding: 0 16px; - background: ${q.colors.background.primary}; - border-bottom: 1px solid ${q.colors.border.primary}; - display: flex; - align-items: center; -`,ip=k.div` - display: flex; - align-items: center; - gap: 8px; - height: 100%; -`,Ix=k.div` - display: flex; - align-items: center; - gap: 12px; - height: 100%; -`,Dx=k(Nr)` - width: 24px; - height: 24px; -`;k.img` - width: 24px; - height: 24px; - border-radius: 50%; -`;const zx=k.div` - position: relative; - width: 40px; - height: 24px; - flex-shrink: 0; -`,$x=k(Do)` - border-color: ${q.colors.background.primary}; - bottom: -3px; - right: -3px; -`,Fx=k.div` - font-size: 12px; - color: ${q.colors.text.muted}; - line-height: 13px; -`,sp=k.div` - font-weight: bold; - color: ${q.colors.text.primary}; - line-height: 20px; - font-size: 16px; -`,Bx=k.div` - flex: 1; - display: flex; - flex-direction: column-reverse; - overflow-y: auto; - position: relative; -`,bx=k.div` - padding: 16px; - display: flex; - flex-direction: column; -`,wh=k.div` - margin-bottom: 16px; - display: flex; - align-items: flex-start; - position: relative; - z-index: 1; -`,Ux=k(Nr)` - margin-right: 16px; - width: 40px; - height: 40px; -`;k.img` - width: 40px; - height: 40px; - border-radius: 50%; -`;const Hx=k.div` - display: flex; - align-items: center; - margin-bottom: 4px; - position: relative; -`,Vx=k.span` - font-weight: bold; - color: ${q.colors.text.primary}; - margin-right: 8px; -`,Wx=k.span` - font-size: 0.75rem; - color: ${q.colors.text.muted}; -`,Yx=k.div` - color: ${q.colors.text.secondary}; - margin-top: 4px; -`,qx=k.form` - display: flex; - align-items: center; - gap: 8px; - padding: 16px; - background: ${({theme:r})=>r.colors.background.secondary}; - position: relative; - z-index: 1; -`,Qx=k.textarea` - flex: 1; - padding: 12px; - background: ${({theme:r})=>r.colors.background.tertiary}; - border: none; - border-radius: 4px; - color: ${({theme:r})=>r.colors.text.primary}; - font-size: 14px; - resize: none; - min-height: 44px; - max-height: 144px; - - &:focus { - outline: none; - } - - &::placeholder { - color: ${({theme:r})=>r.colors.text.muted}; - } -`,Gx=k.button` - background: none; - border: none; - color: ${({theme:r})=>r.colors.text.muted}; - font-size: 24px; - cursor: pointer; - padding: 4px 8px; - display: flex; - align-items: center; - justify-content: center; - - &:hover { - color: ${({theme:r})=>r.colors.text.primary}; - } -`;k.div` - flex: 1; - display: flex; - align-items: center; - justify-content: center; - color: ${q.colors.text.muted}; - font-size: 16px; - font-weight: 500; - padding: 20px; - text-align: center; -`;const lp=k.div` - display: flex; - flex-wrap: wrap; - gap: 8px; - margin-top: 8px; - width: 100%; -`,Kx=k.a` - display: block; - border-radius: 4px; - overflow: hidden; - max-width: 300px; - - img { - width: 100%; - height: auto; - display: block; - } -`,Xx=k.a` - display: flex; - align-items: center; - gap: 12px; - padding: 12px; - background: ${({theme:r})=>r.colors.background.tertiary}; - border-radius: 8px; - text-decoration: none; - width: fit-content; - - &:hover { - background: ${({theme:r})=>r.colors.background.hover}; - } -`,Jx=k.div` - width: 40px; - height: 40px; - display: flex; - align-items: center; - justify-content: center; - font-size: 40px; - color: #0B93F6; -`,Zx=k.div` - display: flex; - flex-direction: column; - gap: 2px; -`,e1=k.span` - font-size: 14px; - color: #0B93F6; - font-weight: 500; -`,t1=k.span` - font-size: 13px; - color: ${({theme:r})=>r.colors.text.muted}; -`,n1=k.div` - display: flex; - flex-wrap: wrap; - gap: 8px; - padding: 8px 0; -`,Sh=k.div` - position: relative; - display: flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - background: ${({theme:r})=>r.colors.background.tertiary}; - border-radius: 4px; - max-width: 300px; -`,r1=k(Sh)` - padding: 0; - overflow: hidden; - width: 200px; - height: 120px; - - img { - width: 100%; - height: 100%; - object-fit: cover; - } -`,o1=k.div` - color: #0B93F6; - font-size: 20px; -`,i1=k.div` - font-size: 13px; - color: ${({theme:r})=>r.colors.text.primary}; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -`,ap=k.button` - position: absolute; - top: -6px; - right: -6px; - width: 20px; - height: 20px; - border-radius: 50%; - background: ${({theme:r})=>r.colors.background.secondary}; - border: none; - color: ${({theme:r})=>r.colors.text.muted}; - font-size: 16px; - line-height: 1; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - padding: 0; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - - &:hover { - color: ${({theme:r})=>r.colors.text.primary}; - } -`,s1=k.div` - position: relative; - margin-left: auto; - z-index: 99999; -`,l1=k.button` - background: none; - border: none; - color: ${({theme:r})=>r.colors.text.muted}; - font-size: 16px; - cursor: pointer; - padding: 4px 8px; - border-radius: 4px; - display: flex; - align-items: center; - justify-content: center; - opacity: 0; - transition: opacity 0.2s ease; - - &:hover { - color: ${({theme:r})=>r.colors.text.primary}; - background: ${({theme:r})=>r.colors.background.hover}; - } - - ${wh}:hover & { - opacity: 1; - } -`,a1=k.div` - position: absolute; - top: 0; - background: ${({theme:r})=>r.colors.background.primary}; - border: 1px solid ${({theme:r})=>r.colors.border.primary}; - border-radius: 6px; - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15); - width: 80px; - z-index: 99999; - overflow: hidden; -`,up=k.button` - display: flex; - align-items: center; - gap: 8px; - width: fit-content; - background: none; - border: none; - color: ${({theme:r})=>r.colors.text.primary}; - font-size: 14px; - cursor: pointer; - text-align: center ; - - &:hover { - background: ${({theme:r})=>r.colors.background.hover}; - } - - &:first-child { - border-radius: 6px 6px 0 0; - } - - &:last-child { - border-radius: 0 0 6px 6px; - } -`,u1=k.div` - margin-top: 4px; -`,c1=k.textarea` - width: 100%; - max-width: 600px; - min-height: 80px; - padding: 12px 16px; - background: ${({theme:r})=>r.colors.background.tertiary}; - border: 1px solid ${({theme:r})=>r.colors.border.primary}; - border-radius: 4px; - color: ${({theme:r})=>r.colors.text.primary}; - font-size: 14px; - font-family: inherit; - resize: vertical; - outline: none; - box-sizing: border-box; - - &:focus { - border-color: ${({theme:r})=>r.colors.primary}; - } - - &::placeholder { - color: ${({theme:r})=>r.colors.text.muted}; - } -`,d1=k.div` - display: flex; - gap: 8px; - margin-top: 8px; -`,cp=k.button` - padding: 6px 12px; - border-radius: 4px; - font-size: 12px; - font-weight: 500; - cursor: pointer; - border: none; - transition: background-color 0.2s ease; - - ${({variant:r,theme:i})=>r==="primary"?` - background: ${i.colors.primary}; - color: white; - - &:hover { - background: ${i.colors.primaryHover||i.colors.primary}; - } - `:` - background: ${i.colors.background.secondary}; - color: ${i.colors.text.secondary}; - - &:hover { - background: ${i.colors.background.hover}; - } - `} -`;function f1({channel:r}){var w;const{currentUser:i}=pt(),s=Ar(v=>v.users),l=An(v=>v.binaryContents);if(!r)return null;if(r.type==="PUBLIC")return h.jsx(op,{children:h.jsx(ip,{children:h.jsxs(sp,{children:["# ",r.name]})})});const c=r.participants.map(v=>s.find(S=>S.id===v.id)).filter(Boolean),d=c.filter(v=>v.id!==(i==null?void 0:i.id)),p=c.length>2,g=c.filter(v=>v.id!==(i==null?void 0:i.id)).map(v=>v.username).join(", ");return h.jsx(op,{children:h.jsx(ip,{children:h.jsxs(Ix,{children:[p?h.jsx(zx,{children:d.slice(0,2).map((v,S)=>{var j;return h.jsx(nn,{src:v.profile?(j=l[v.profile.id])==null?void 0:j.url:St,style:{position:"absolute",left:S*16,zIndex:2-S,width:"24px",height:"24px"}},v.id)})}):h.jsxs(Dx,{children:[h.jsx(nn,{src:d[0].profile?(w=l[d[0].profile.id])==null?void 0:w.url:St}),h.jsx($x,{$online:d[0].online})]}),h.jsxs("div",{children:[h.jsx(sp,{children:g}),p&&h.jsxs(Fx,{children:["멤버 ",c.length,"명"]})]})]})})})}const p1=async(r,i,s)=>{var c;return(await Oe.get("/messages",{params:{channelId:r,cursor:i,size:s.size,sort:(c=s.sort)==null?void 0:c.join(",")}})).data},h1=async(r,i)=>{const s=new FormData,l={content:r.content,channelId:r.channelId,authorId:r.authorId};return s.append("messageCreateRequest",new Blob([JSON.stringify(l)],{type:"application/json"})),i&&i.length>0&&i.forEach(d=>{s.append("attachments",d)}),(await Oe.post("/messages",s,{headers:{"Content-Type":"multipart/form-data"}})).data},m1=async(r,i)=>(await Oe.patch(`/messages/${r}`,i)).data,g1=async r=>{await Oe.delete(`/messages/${r}`)},$a={size:50,sort:["createdAt,desc"]},Ch=Pr((r,i)=>({messages:[],pollingIntervals:{},lastMessageId:null,pagination:{nextCursor:null,pageSize:50,hasNext:!1},fetchMessages:async(s,l,c=$a)=>{try{const d=await p1(s,l,c),p=d.content,g=p.length>0?p[0]:null,w=(g==null?void 0:g.id)!==i().lastMessageId;return r(v=>{var O;const S=!l,j=s!==((O=v.messages[0])==null?void 0:O.channelId),R=S&&(v.messages.length===0||j);let L=[],T={...v.pagination};if(R)L=p,T={nextCursor:d.nextCursor,pageSize:d.size,hasNext:d.hasNext};else if(S){const _=new Set(v.messages.map(U=>U.id));L=[...p.filter(U=>!_.has(U.id)&&(v.messages.length===0||U.createdAt>v.messages[0].createdAt)),...v.messages]}else{const _=new Set(v.messages.map(U=>U.id)),b=p.filter(U=>!_.has(U.id));L=[...v.messages,...b],T={nextCursor:d.nextCursor,pageSize:d.size,hasNext:d.hasNext}}return{messages:L,lastMessageId:(g==null?void 0:g.id)||null,pagination:T}}),w}catch(d){return console.error("메시지 목록 조회 실패:",d),!1}},loadMoreMessages:async s=>{const{pagination:l}=i();l.hasNext&&await i().fetchMessages(s,l.nextCursor,{...$a})},startPolling:s=>{const l=i();if(l.pollingIntervals[s]){const g=l.pollingIntervals[s];typeof g=="number"&&clearTimeout(g)}let c=300;const d=3e3;r(g=>({pollingIntervals:{...g.pollingIntervals,[s]:!0}}));const p=async()=>{const g=i();if(!g.pollingIntervals[s])return;const w=await g.fetchMessages(s,null,$a);if(!(i().messages.length==0)&&w?c=300:c=Math.min(c*1.5,d),i().pollingIntervals[s]){const S=setTimeout(p,c);r(j=>({pollingIntervals:{...j.pollingIntervals,[s]:S}}))}};p()},stopPolling:s=>{const{pollingIntervals:l}=i();if(l[s]){const c=l[s];typeof c=="number"&&clearTimeout(c),r(d=>{const p={...d.pollingIntervals};return delete p[s],{pollingIntervals:p}})}},createMessage:async(s,l)=>{try{const c=await h1(s,l),d=Ao.getState().updateReadStatus;return await d(s.channelId),r(p=>p.messages.some(w=>w.id===c.id)?p:{messages:[c,...p.messages],lastMessageId:c.id}),c}catch(c){throw console.error("메시지 생성 실패:",c),c}},updateMessage:async(s,l)=>{try{const c=await m1(s,{newContent:l});return r(d=>({messages:d.messages.map(p=>p.id===s?{...p,content:l}:p)})),c}catch(c){throw console.error("메시지 업데이트 실패:",c),c}},deleteMessage:async s=>{try{await g1(s),r(l=>({messages:l.messages.filter(c=>c.id!==s)}))}catch(l){throw console.error("메시지 삭제 실패:",l),l}}}));function y1({channel:r}){const[i,s]=K.useState(""),[l,c]=K.useState([]),d=Ch(R=>R.createMessage),{currentUser:p}=pt(),g=async R=>{if(R.preventDefault(),!(!i.trim()&&l.length===0))try{await d({content:i.trim(),channelId:r.id,authorId:(p==null?void 0:p.id)??""},l),s(""),c([])}catch(L){console.error("메시지 전송 실패:",L)}},w=R=>{const L=Array.from(R.target.files||[]);c(T=>[...T,...L]),R.target.value=""},v=R=>{c(L=>L.filter((T,O)=>O!==R))},S=R=>{if(R.key==="Enter"&&!R.shiftKey){if(console.log("Enter key pressed"),R.preventDefault(),R.nativeEvent.isComposing)return;g(R)}},j=(R,L)=>R.type.startsWith("image/")?h.jsxs(r1,{children:[h.jsx("img",{src:URL.createObjectURL(R),alt:R.name}),h.jsx(ap,{onClick:()=>v(L),children:"×"})]},L):h.jsxs(Sh,{children:[h.jsx(o1,{children:"📎"}),h.jsx(i1,{children:R.name}),h.jsx(ap,{onClick:()=>v(L),children:"×"})]},L);return K.useEffect(()=>()=>{l.forEach(R=>{R.type.startsWith("image/")&&URL.revokeObjectURL(URL.createObjectURL(R))})},[l]),r?h.jsxs(h.Fragment,{children:[l.length>0&&h.jsx(n1,{children:l.map((R,L)=>j(R,L))}),h.jsxs(qx,{onSubmit:g,children:[h.jsxs(Gx,{as:"label",children:["+",h.jsx("input",{type:"file",multiple:!0,onChange:w,style:{display:"none"}})]}),h.jsx(Qx,{value:i,onChange:R=>s(R.target.value),onKeyDown:S,placeholder:r.type==="PUBLIC"?`#${r.name}에 메시지 보내기`:"메시지 보내기"})]})]}):null}/*! ***************************************************************************** -Copyright (c) Microsoft Corporation. All rights reserved. -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at http://www.apache.org/licenses/LICENSE-2.0 - -THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED -WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, -MERCHANTABLITY OR NON-INFRINGEMENT. - -See the Apache Version 2.0 License for specific language governing permissions -and limitations under the License. -***************************************************************************** */var eu=function(r,i){return eu=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(s,l){s.__proto__=l}||function(s,l){for(var c in l)l.hasOwnProperty(c)&&(s[c]=l[c])},eu(r,i)};function v1(r,i){eu(r,i);function s(){this.constructor=r}r.prototype=i===null?Object.create(i):(s.prototype=i.prototype,new s)}var To=function(){return To=Object.assign||function(i){for(var s,l=1,c=arguments.length;lr?L():i!==!0&&(c=setTimeout(l?T:L,l===void 0?r-j:r))}return v.cancel=w,v}var Sr={Pixel:"Pixel",Percent:"Percent"},dp={unit:Sr.Percent,value:.8};function fp(r){return typeof r=="number"?{unit:Sr.Percent,value:r*100}:typeof r=="string"?r.match(/^(\d*(\.\d+)?)px$/)?{unit:Sr.Pixel,value:parseFloat(r)}:r.match(/^(\d*(\.\d+)?)%$/)?{unit:Sr.Percent,value:parseFloat(r)}:(console.warn('scrollThreshold format is invalid. Valid formats: "120px", "50%"...'),dp):(console.warn("scrollThreshold should be string or number"),dp)}var w1=function(r){v1(i,r);function i(s){var l=r.call(this,s)||this;return l.lastScrollTop=0,l.actionTriggered=!1,l.startY=0,l.currentY=0,l.dragging=!1,l.maxPullDownDistance=0,l.getScrollableTarget=function(){return l.props.scrollableTarget instanceof HTMLElement?l.props.scrollableTarget:typeof l.props.scrollableTarget=="string"?document.getElementById(l.props.scrollableTarget):(l.props.scrollableTarget===null&&console.warn(`You are trying to pass scrollableTarget but it is null. This might - happen because the element may not have been added to DOM yet. - See https://github.com/ankeetmaini/react-infinite-scroll-component/issues/59 for more info. - `),null)},l.onStart=function(c){l.lastScrollTop||(l.dragging=!0,c instanceof MouseEvent?l.startY=c.pageY:c instanceof TouchEvent&&(l.startY=c.touches[0].pageY),l.currentY=l.startY,l._infScroll&&(l._infScroll.style.willChange="transform",l._infScroll.style.transition="transform 0.2s cubic-bezier(0,0,0.31,1)"))},l.onMove=function(c){l.dragging&&(c instanceof MouseEvent?l.currentY=c.pageY:c instanceof TouchEvent&&(l.currentY=c.touches[0].pageY),!(l.currentY=Number(l.props.pullDownToRefreshThreshold)&&l.setState({pullToRefreshThresholdBreached:!0}),!(l.currentY-l.startY>l.maxPullDownDistance*1.5)&&l._infScroll&&(l._infScroll.style.overflow="visible",l._infScroll.style.transform="translate3d(0px, "+(l.currentY-l.startY)+"px, 0px)")))},l.onEnd=function(){l.startY=0,l.currentY=0,l.dragging=!1,l.state.pullToRefreshThresholdBreached&&(l.props.refreshFunction&&l.props.refreshFunction(),l.setState({pullToRefreshThresholdBreached:!1})),requestAnimationFrame(function(){l._infScroll&&(l._infScroll.style.overflow="auto",l._infScroll.style.transform="none",l._infScroll.style.willChange="unset")})},l.onScrollListener=function(c){typeof l.props.onScroll=="function"&&setTimeout(function(){return l.props.onScroll&&l.props.onScroll(c)},0);var d=l.props.height||l._scrollableNode?c.target:document.documentElement.scrollTop?document.documentElement:document.body;if(!l.actionTriggered){var p=l.props.inverse?l.isElementAtTop(d,l.props.scrollThreshold):l.isElementAtBottom(d,l.props.scrollThreshold);p&&l.props.hasMore&&(l.actionTriggered=!0,l.setState({showLoader:!0}),l.props.next&&l.props.next()),l.lastScrollTop=d.scrollTop}},l.state={showLoader:!1,pullToRefreshThresholdBreached:!1,prevDataLength:s.dataLength},l.throttledOnScrollListener=x1(150,l.onScrollListener).bind(l),l.onStart=l.onStart.bind(l),l.onMove=l.onMove.bind(l),l.onEnd=l.onEnd.bind(l),l}return i.prototype.componentDidMount=function(){if(typeof this.props.dataLength>"u")throw new Error('mandatory prop "dataLength" is missing. The prop is needed when loading more content. Check README.md for usage');if(this._scrollableNode=this.getScrollableTarget(),this.el=this.props.height?this._infScroll:this._scrollableNode||window,this.el&&this.el.addEventListener("scroll",this.throttledOnScrollListener),typeof this.props.initialScrollY=="number"&&this.el&&this.el instanceof HTMLElement&&this.el.scrollHeight>this.props.initialScrollY&&this.el.scrollTo(0,this.props.initialScrollY),this.props.pullDownToRefresh&&this.el&&(this.el.addEventListener("touchstart",this.onStart),this.el.addEventListener("touchmove",this.onMove),this.el.addEventListener("touchend",this.onEnd),this.el.addEventListener("mousedown",this.onStart),this.el.addEventListener("mousemove",this.onMove),this.el.addEventListener("mouseup",this.onEnd),this.maxPullDownDistance=this._pullDown&&this._pullDown.firstChild&&this._pullDown.firstChild.getBoundingClientRect().height||0,this.forceUpdate(),typeof this.props.refreshFunction!="function"))throw new Error(`Mandatory prop "refreshFunction" missing. - Pull Down To Refresh functionality will not work - as expected. Check README.md for usage'`)},i.prototype.componentWillUnmount=function(){this.el&&(this.el.removeEventListener("scroll",this.throttledOnScrollListener),this.props.pullDownToRefresh&&(this.el.removeEventListener("touchstart",this.onStart),this.el.removeEventListener("touchmove",this.onMove),this.el.removeEventListener("touchend",this.onEnd),this.el.removeEventListener("mousedown",this.onStart),this.el.removeEventListener("mousemove",this.onMove),this.el.removeEventListener("mouseup",this.onEnd)))},i.prototype.componentDidUpdate=function(s){this.props.dataLength!==s.dataLength&&(this.actionTriggered=!1,this.setState({showLoader:!1}))},i.getDerivedStateFromProps=function(s,l){var c=s.dataLength!==l.prevDataLength;return c?To(To({},l),{prevDataLength:s.dataLength}):null},i.prototype.isElementAtTop=function(s,l){l===void 0&&(l=.8);var c=s===document.body||s===document.documentElement?window.screen.availHeight:s.clientHeight,d=fp(l);return d.unit===Sr.Pixel?s.scrollTop<=d.value+c-s.scrollHeight+1:s.scrollTop<=d.value/100+c-s.scrollHeight+1},i.prototype.isElementAtBottom=function(s,l){l===void 0&&(l=.8);var c=s===document.body||s===document.documentElement?window.screen.availHeight:s.clientHeight,d=fp(l);return d.unit===Sr.Pixel?s.scrollTop+c>=s.scrollHeight-d.value:s.scrollTop+c>=d.value/100*s.scrollHeight},i.prototype.render=function(){var s=this,l=To({height:this.props.height||"auto",overflow:"auto",WebkitOverflowScrolling:"touch"},this.props.style),c=this.props.hasChildren||!!(this.props.children&&this.props.children instanceof Array&&this.props.children.length),d=this.props.pullDownToRefresh&&this.props.height?{overflow:"auto"}:{};return xt.createElement("div",{style:d,className:"infinite-scroll-component__outerdiv"},xt.createElement("div",{className:"infinite-scroll-component "+(this.props.className||""),ref:function(p){return s._infScroll=p},style:l},this.props.pullDownToRefresh&&xt.createElement("div",{style:{position:"relative"},ref:function(p){return s._pullDown=p}},xt.createElement("div",{style:{position:"absolute",left:0,right:0,top:-1*this.maxPullDownDistance}},this.state.pullToRefreshThresholdBreached?this.props.releaseToRefreshContent:this.props.pullDownToRefreshContent)),this.props.children,!this.state.showLoader&&!c&&this.props.hasMore&&this.props.loader,this.state.showLoader&&this.props.hasMore&&this.props.loader,!this.props.hasMore&&this.props.endMessage))},i}(K.Component);const S1=r=>r<1024?r+" B":r<1024*1024?(r/1024).toFixed(2)+" KB":r<1024*1024*1024?(r/(1024*1024)).toFixed(2)+" MB":(r/(1024*1024*1024)).toFixed(2)+" GB";function C1({channel:r}){const{messages:i,fetchMessages:s,loadMoreMessages:l,pagination:c,startPolling:d,stopPolling:p,updateMessage:g,deleteMessage:w}=Ch(),{binaryContents:v,fetchBinaryContent:S}=An(),{currentUser:j}=pt(),[R,L]=K.useState(null),[T,O]=K.useState(null),[_,b]=K.useState("");K.useEffect(()=>{if(r!=null&&r.id)return s(r.id,null),d(r.id),()=>{p(r.id)}},[r==null?void 0:r.id,s,d,p]),K.useEffect(()=>{i.forEach(ie=>{var de;(de=ie.attachments)==null||de.forEach(me=>{v[me.id]||S(me.id)})})},[i,v,S]),K.useEffect(()=>{const ie=()=>{R&&L(null)};if(R)return document.addEventListener("click",ie),()=>document.removeEventListener("click",ie)},[R]);const U=async ie=>{try{const{url:de,fileName:me}=ie,_e=document.createElement("a");_e.href=de,_e.download=me,_e.style.display="none",document.body.appendChild(_e);try{const Ue=await(await window.showSaveFilePicker({suggestedName:ie.fileName,types:[{description:"Files",accept:{"*/*":[".txt",".pdf",".doc",".docx",".xls",".xlsx",".jpg",".jpeg",".png",".gif"]}}]})).createWritable(),Y=await(await fetch(de)).blob();await Ue.write(Y),await Ue.close()}catch(Se){Se.name!=="AbortError"&&_e.click()}document.body.removeChild(_e),window.URL.revokeObjectURL(de)}catch(de){console.error("파일 다운로드 실패:",de)}},B=ie=>ie!=null&&ie.length?ie.map(de=>{const me=v[de.id];return me?me.contentType.startsWith("image/")?h.jsx(lp,{children:h.jsx(Kx,{href:"#",onClick:Se=>{Se.preventDefault(),U(me)},children:h.jsx("img",{src:me.url,alt:me.fileName})})},me.url):h.jsx(lp,{children:h.jsxs(Xx,{href:"#",onClick:Se=>{Se.preventDefault(),U(me)},children:[h.jsx(Jx,{children:h.jsxs("svg",{width:"40",height:"40",viewBox:"0 0 40 40",fill:"none",children:[h.jsx("path",{d:"M8 3C8 1.89543 8.89543 1 10 1H22L32 11V37C32 38.1046 31.1046 39 30 39H10C8.89543 39 8 38.1046 8 37V3Z",fill:"#0B93F6",fillOpacity:"0.1"}),h.jsx("path",{d:"M22 1L32 11H24C22.8954 11 22 10.1046 22 9V1Z",fill:"#0B93F6",fillOpacity:"0.3"}),h.jsx("path",{d:"M13 19H27M13 25H27M13 31H27",stroke:"#0B93F6",strokeWidth:"2",strokeLinecap:"round"})]})}),h.jsxs(Zx,{children:[h.jsx(e1,{children:me.fileName}),h.jsx(t1,{children:S1(me.size)})]})]})},me.url):null}):null,W=ie=>new Date(ie).toLocaleTimeString(),I=()=>{r!=null&&r.id&&l(r.id)},M=ie=>{L(R===ie?null:ie)},V=ie=>{L(null);const de=i.find(me=>me.id===ie);de&&(O(ie),b(de.content))},ne=ie=>{g(ie,_).catch(de=>{console.error("메시지 수정 실패:",de),Oo.emit("api-error",{error:de,alert:!0})}),O(null),b("")},ye=()=>{O(null),b("")},Ie=ie=>{L(null),w(ie)};return h.jsx(Bx,{children:h.jsx("div",{id:"scrollableDiv",style:{height:"100%",overflow:"auto",display:"flex",flexDirection:"column-reverse"},children:h.jsx(w1,{dataLength:i.length,next:I,hasMore:c.hasNext,loader:h.jsx("h4",{style:{textAlign:"center"},children:"메시지를 불러오는 중..."}),scrollableTarget:"scrollableDiv",style:{display:"flex",flexDirection:"column-reverse"},inverse:!0,endMessage:h.jsx("p",{style:{textAlign:"center"},children:h.jsx("b",{children:c.nextCursor!==null?"모든 메시지를 불러왔습니다":""})}),children:h.jsx(bx,{children:[...i].reverse().map(ie=>{var _e;const de=ie.author,me=j&&de&&de.id===j.id;return h.jsxs(wh,{children:[h.jsx(Ux,{children:h.jsx(nn,{src:de&&de.profile?(_e=v[de.profile.id])==null?void 0:_e.url:St,alt:de&&de.username||"알 수 없음"})}),h.jsxs("div",{children:[h.jsxs(Hx,{children:[h.jsx(Vx,{children:de&&de.username||"알 수 없음"}),h.jsx(Wx,{children:W(ie.createdAt)}),me&&h.jsxs(s1,{children:[h.jsx(l1,{onClick:Se=>{Se.stopPropagation(),M(ie.id)},children:"⋯"}),R===ie.id&&h.jsxs(a1,{onClick:Se=>Se.stopPropagation(),children:[h.jsx(up,{onClick:()=>V(ie.id),children:"✏️ 수정"}),h.jsx(up,{onClick:()=>Ie(ie.id),children:"🗑️ 삭제"})]})]})]}),T===ie.id?h.jsxs(u1,{children:[h.jsx(c1,{value:_,onChange:Se=>b(Se.target.value),onKeyDown:Se=>{Se.key==="Escape"?ye():Se.key==="Enter"&&(Se.ctrlKey||Se.metaKey)&&(Se.preventDefault(),ne(ie.id))},placeholder:"메시지를 입력하세요..."}),h.jsxs(d1,{children:[h.jsx(cp,{variant:"secondary",onClick:ye,children:"취소"}),h.jsx(cp,{variant:"primary",onClick:()=>ne(ie.id),children:"저장"})]})]}):h.jsx(Yx,{children:ie.content}),B(ie.attachments)]})]},ie.id)})})})})})}function k1({channel:r}){return r?h.jsxs(Px,{children:[h.jsx(f1,{channel:r}),h.jsx(C1,{channel:r}),h.jsx(y1,{channel:r})]}):h.jsx(_x,{children:h.jsxs(Nx,{children:[h.jsx(Ox,{children:"👋"}),h.jsx(Mx,{children:"채널을 선택해주세요"}),h.jsxs(Lx,{children:["왼쪽의 채널 목록에서 채널을 선택하여",h.jsx("br",{}),"대화를 시작하세요."]})]})})}function E1(r,i="yyyy-MM-dd HH:mm:ss"){if(!r||!(r instanceof Date)||isNaN(r.getTime()))return"";const s=r.getFullYear(),l=String(r.getMonth()+1).padStart(2,"0"),c=String(r.getDate()).padStart(2,"0"),d=String(r.getHours()).padStart(2,"0"),p=String(r.getMinutes()).padStart(2,"0"),g=String(r.getSeconds()).padStart(2,"0");return i.replace("yyyy",s.toString()).replace("MM",l).replace("dd",c).replace("HH",d).replace("mm",p).replace("ss",g)}const j1=k.div` - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.7); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; -`,A1=k.div` - background: ${({theme:r})=>r.colors.background.primary}; - border-radius: 8px; - width: 500px; - max-width: 90%; - padding: 24px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); -`,R1=k.div` - display: flex; - align-items: center; - margin-bottom: 16px; -`,P1=k.div` - color: ${({theme:r})=>r.colors.status.error}; - font-size: 24px; - margin-right: 12px; -`,T1=k.h3` - color: ${({theme:r})=>r.colors.text.primary}; - margin: 0; - font-size: 18px; -`,_1=k.div` - background: ${({theme:r})=>r.colors.background.tertiary}; - color: ${({theme:r})=>r.colors.text.muted}; - padding: 2px 8px; - border-radius: 4px; - font-size: 14px; - margin-left: auto; -`,N1=k.p` - color: ${({theme:r})=>r.colors.text.secondary}; - margin-bottom: 20px; - line-height: 1.5; - font-weight: 500; -`,O1=k.div` - margin-bottom: 20px; - background: ${({theme:r})=>r.colors.background.secondary}; - border-radius: 6px; - padding: 12px; -`,wo=k.div` - display: flex; - margin-bottom: 8px; - font-size: 14px; -`,So=k.span` - color: ${({theme:r})=>r.colors.text.muted}; - min-width: 100px; -`,Co=k.span` - color: ${({theme:r})=>r.colors.text.secondary}; - word-break: break-word; -`,M1=k.button` - background: ${({theme:r})=>r.colors.brand.primary}; - color: white; - border: none; - border-radius: 4px; - padding: 8px 16px; - font-size: 14px; - font-weight: 500; - cursor: pointer; - width: 100%; - - &:hover { - background: ${({theme:r})=>r.colors.brand.hover}; - } -`;function L1({isOpen:r,onClose:i,error:s}){var R,L;if(!r)return null;console.log({error:s});const l=(R=s==null?void 0:s.response)==null?void 0:R.data,c=(l==null?void 0:l.status)||((L=s==null?void 0:s.response)==null?void 0:L.status)||"오류",d=(l==null?void 0:l.code)||"",p=(l==null?void 0:l.message)||(s==null?void 0:s.message)||"알 수 없는 오류가 발생했습니다.",g=l!=null&&l.timestamp?new Date(l.timestamp):new Date,w=E1(g),v=(l==null?void 0:l.exceptionType)||"",S=(l==null?void 0:l.details)||{},j=(l==null?void 0:l.requestId)||"";return h.jsx(j1,{onClick:i,children:h.jsxs(A1,{onClick:T=>T.stopPropagation(),children:[h.jsxs(R1,{children:[h.jsx(P1,{children:"⚠️"}),h.jsx(T1,{children:"오류가 발생했습니다"}),h.jsxs(_1,{children:[c,d?` (${d})`:""]})]}),h.jsx(N1,{children:p}),h.jsxs(O1,{children:[h.jsxs(wo,{children:[h.jsx(So,{children:"시간:"}),h.jsx(Co,{children:w})]}),j&&h.jsxs(wo,{children:[h.jsx(So,{children:"요청 ID:"}),h.jsx(Co,{children:j})]}),d&&h.jsxs(wo,{children:[h.jsx(So,{children:"에러 코드:"}),h.jsx(Co,{children:d})]}),v&&h.jsxs(wo,{children:[h.jsx(So,{children:"예외 유형:"}),h.jsx(Co,{children:v})]}),Object.keys(S).length>0&&h.jsxs(wo,{children:[h.jsx(So,{children:"상세 정보:"}),h.jsx(Co,{children:Object.entries(S).map(([T,O])=>h.jsxs("div",{children:[T,": ",String(O)]},T))})]})]}),h.jsx(M1,{onClick:i,children:"확인"})]})})}const I1=k.div` - width: 240px; - background: ${q.colors.background.secondary}; - border-left: 1px solid ${q.colors.border.primary}; -`,D1=k.div` - padding: 16px; - font-size: 14px; - font-weight: bold; - color: ${q.colors.text.muted}; - text-transform: uppercase; -`,z1=k.div` - padding: 8px 16px; - display: flex; - align-items: center; - color: ${q.colors.text.muted}; - &:hover { - background: ${q.colors.background.primary}; - cursor: pointer; - } -`,$1=k(Nr)` - margin-right: 12px; -`;k(nn)``;const F1=k.div` - display: flex; - align-items: center; -`;function B1({member:r}){var l,c,d;const{binaryContents:i,fetchBinaryContent:s}=An();return K.useEffect(()=>{var p;(p=r.profile)!=null&&p.id&&!i[r.profile.id]&&s(r.profile.id)},[(l=r.profile)==null?void 0:l.id,i,s]),h.jsxs(z1,{children:[h.jsxs($1,{children:[h.jsx(nn,{src:(c=r.profile)!=null&&c.id&&((d=i[r.profile.id])==null?void 0:d.url)||St,alt:r.username}),h.jsx(Do,{$online:r.online})]}),h.jsx(F1,{children:r.username})]})}function b1({member:r,onClose:i}){var L,T,O;const{binaryContents:s,fetchBinaryContent:l}=An(),{currentUser:c,updateUserRole:d}=pt(),[p,g]=K.useState(r.role),[w,v]=K.useState(!1);K.useEffect(()=>{var _;(_=r.profile)!=null&&_.id&&!s[r.profile.id]&&l(r.profile.id)},[(L=r.profile)==null?void 0:L.id,s,l]);const S={[En.USER]:{name:"사용자",color:"#2ed573"},[En.CHANNEL_MANAGER]:{name:"채널 관리자",color:"#ff4757"},[En.ADMIN]:{name:"어드민",color:"#0097e6"}},j=_=>{g(_),v(!0)},R=()=>{d(r.id,p),v(!1)};return h.jsx(V1,{onClick:i,children:h.jsxs(W1,{onClick:_=>_.stopPropagation(),children:[h.jsx("h2",{children:"사용자 정보"}),h.jsxs(Y1,{children:[h.jsx(q1,{src:(T=r.profile)!=null&&T.id&&((O=s[r.profile.id])==null?void 0:O.url)||St,alt:r.username}),h.jsx(Q1,{children:r.username}),h.jsx(G1,{children:r.email}),h.jsx(K1,{$online:r.online,children:r.online?"온라인":"오프라인"}),(c==null?void 0:c.role)===En.ADMIN?h.jsx(H1,{value:p,onChange:_=>j(_.target.value),children:Object.entries(S).map(([_,b])=>h.jsx("option",{value:_,style:{marginTop:"8px",textAlign:"center"},children:b.name},_))}):h.jsx(U1,{style:{backgroundColor:S[r.role].color},children:S[r.role].name})]}),h.jsx(X1,{children:(c==null?void 0:c.role)===En.ADMIN&&w&&h.jsx(J1,{onClick:R,disabled:!w,$secondary:!w,children:"저장"})})]})})}const U1=k.div` - padding: 6px 16px; - border-radius: 20px; - font-size: 13px; - font-weight: 600; - color: white; - margin-top: 12px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - letter-spacing: 0.3px; -`,H1=k.select` - padding: 10px 16px; - border-radius: 8px; - border: 1.5px solid ${q.colors.border.primary}; - background: ${q.colors.background.primary}; - color: ${q.colors.text.primary}; - font-size: 14px; - width: 140px; - cursor: pointer; - transition: all 0.2s ease; - margin-top: 12px; - font-weight: 500; - - &:hover { - border-color: ${q.colors.brand.primary}; - } - - &:focus { - outline: none; - border-color: ${q.colors.brand.primary}; - box-shadow: 0 0 0 2px ${q.colors.brand.primary}20; - } - - option { - background: ${q.colors.background.primary}; - color: ${q.colors.text.primary}; - padding: 12px; - } -`,V1=k.div` - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.6); - backdrop-filter: blur(4px); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; -`,W1=k.div` - background: ${q.colors.background.secondary}; - padding: 40px; - border-radius: 16px; - width: 100%; - max-width: 420px; - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12); - - h2 { - color: ${q.colors.text.primary}; - margin-bottom: 32px; - text-align: center; - font-size: 26px; - font-weight: 600; - letter-spacing: -0.5px; - } -`,Y1=k.div` - display: flex; - flex-direction: column; - align-items: center; - margin-bottom: 32px; - padding: 24px; - background: ${q.colors.background.primary}; - border-radius: 12px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); -`,q1=k.img` - width: 140px; - height: 140px; - border-radius: 50%; - margin-bottom: 20px; - object-fit: cover; - border: 4px solid ${q.colors.background.secondary}; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); -`,Q1=k.div` - font-size: 22px; - font-weight: 600; - color: ${q.colors.text.primary}; - margin-bottom: 8px; - letter-spacing: -0.3px; -`,G1=k.div` - font-size: 14px; - color: ${q.colors.text.muted}; - margin-bottom: 16px; - font-weight: 500; -`,K1=k.div` - padding: 6px 16px; - border-radius: 20px; - font-size: 13px; - font-weight: 600; - background-color: ${({$online:r,theme:i})=>r?i.colors.status.online:i.colors.status.offline}; - color: white; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - letter-spacing: 0.3px; -`,X1=k.div` - display: flex; - gap: 12px; - margin-top: 24px; -`,J1=k.button` - width: 100%; - padding: 12px; - border: none; - border-radius: 8px; - background: ${({$secondary:r,theme:i})=>r?"transparent":i.colors.brand.primary}; - color: ${({$secondary:r,theme:i})=>r?i.colors.text.primary:"white"}; - cursor: pointer; - font-weight: 600; - font-size: 15px; - transition: all 0.2s ease; - border: ${({$secondary:r,theme:i})=>r?`1.5px solid ${i.colors.border.primary}`:"none"}; - - &:hover { - background: ${({$secondary:r,theme:i})=>r?i.colors.background.hover:i.colors.brand.hover}; - transform: translateY(-1px); - } - - &:active { - transform: translateY(0); - } -`;function Z1(){const r=Ar(p=>p.users),i=Ar(p=>p.fetchUsers),{currentUser:s}=pt(),[l,c]=K.useState(null);K.useEffect(()=>{i()},[i]);const d=[...r].sort((p,g)=>p.id===(s==null?void 0:s.id)?-1:g.id===(s==null?void 0:s.id)?1:p.online&&!g.online?-1:!p.online&&g.online?1:p.username.localeCompare(g.username));return h.jsxs(I1,{children:[h.jsxs(D1,{children:["멤버 목록 - ",r.length]}),d.map(p=>h.jsx("div",{onClick:()=>c(p),children:h.jsx(B1,{member:p},p.id)},p.id)),l&&h.jsx(b1,{member:l,onClose:()=>c(null)})]})}function ew(){const{logout:r,fetchCsrfToken:i,fetchMe:s}=pt(),{fetchUsers:l}=Ar(),[c,d]=K.useState(null),[p,g]=K.useState(null),[w,v]=K.useState(!1),[S,j]=K.useState(!0),{currentUser:R}=pt();K.useEffect(()=>{i(),s()},[]),K.useEffect(()=>{(async()=>{try{if(R)try{await l()}catch(O){console.warn("사용자 상태 업데이트 실패. 로그아웃합니다.",O),r()}}catch(O){console.error("초기화 오류:",O)}finally{j(!1)}})()},[R,l,r]),K.useEffect(()=>{const T=U=>{U!=null&&U.error&&g(U.error),U!=null&&U.alert&&v(!0)},O=()=>{r()},_=Oo.on("api-error",T),b=Oo.on("auth-error",O);return()=>{_("api-error",T),b("auth-error",O)}},[r]),K.useEffect(()=>{if(R){const T=setInterval(()=>{l()},6e4);return()=>{clearInterval(T)}}},[R,l]);const L=()=>{v(!1),g(null)};return S?h.jsx(Tf,{theme:q,children:h.jsx(nw,{children:h.jsx(rw,{})})}):h.jsxs(Tf,{theme:q,children:[R?h.jsxs(tw,{children:[h.jsx(Ax,{currentUser:R,activeChannel:c,onChannelSelect:d}),h.jsx(k1,{channel:c}),h.jsx(Z1,{})]}):h.jsx(Ov,{isOpen:!0,onClose:()=>{}}),h.jsx(L1,{isOpen:w,onClose:L,error:p})]})}const tw=k.div` - display: flex; - height: 100vh; - width: 100vw; - position: relative; -`,nw=k.div` - display: flex; - justify-content: center; - align-items: center; - height: 100vh; - width: 100vw; - background-color: ${({theme:r})=>r.colors.background.primary}; -`,rw=k.div` - width: 40px; - height: 40px; - border: 4px solid ${({theme:r})=>r.colors.background.tertiary}; - border-top: 4px solid ${({theme:r})=>r.colors.brand.primary}; - border-radius: 50%; - animation: spin 1s linear infinite; - - @keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } - } -`,kh=document.getElementById("root");if(!kh)throw new Error("Root element not found");_g.createRoot(kh).render(h.jsx(K.StrictMode,{children:h.jsx(ew,{})})); diff --git a/src/main/resources/static/favicon.ico b/src/main/resources/static/favicon.ico deleted file mode 100644 index 479bed6a3da0a8dbdd08a51d81b30e4d4fabae89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1588 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvI6;x#X;^) z4C~IxyacIC_6YK2V5m}KU}$JzVE6?TYIwoGP-?)y@G60U!Dv>Mu*Du8ycRt4Yw>0&$ytddU zdTHwA$vlU)7;*ZQn^d>r9eiw}SEV3v&DP3PpZVm?c2D=&D? zJg+7dT;x9cg;(mDqrovi2QemjySudY+_R1aaySb-B8!2p69!>MhFNnYfC{QST^vI! zPM@6=9?WDY()wLtM|S>=KoQ44K~Zk4us5=<8xs!eeY>~&=ly4!jD%AXj+wvro>aU~ zrMO$=?`j4U&ZyW$Je*!Zo0>H2RZVqmn^V&mZ(9Dkv!~|IuDF1RBN|EPJE zX3ok)rzF<3&vZKWEj4ag73&t}uJvVk^<~M;*V0n54#8@&v!WGjE_hAaeAZEF z$~V4aF>{^dUc7o%=f8f9m%*2vzjfI@vJ2Z97)VU5x-s2*r@e{H>FEn3A3Dr3G&8U| z)>wFiQO&|Yl6}UkXAQ>%q$jNWac-tTL*)AEyto|onkmnmcJLf?71w_<>4WODmBMxF zwGM7``txcQgT`x>(tH-DrT2Kg=4LzpNv>|+a@TgYDZ`5^$KJVb`K=%k^tRpoxP|4? zwXb!O5~dXYKYt*j(YSx+#_rP{TNcK=40T|)+k3s|?t||EQTgwGgs{E0Y+(QPL&Wx4 zMP23By&sn`zn7oCQQLp%-(Axm|M=5-u;TlFiTn5B^PWnb%fAPV8r2flh?11Vl2ohY zqEsNoU}Ruqple{LYiJr`U}|M-Vr62aZD3$!V6dZTmJ5o8-29Zxv`X9>PU+TH>UWRL)v7?M$%n`C9>lAm0fo0?Z*WfcHaTFhX${Qqu! zG&Nv5t*kOqGt)Cl7z{0q_!){?fojB&%z>&2&rB)F04ce=Mv()kL=s7fZ)R?4No7GQ z1K3si1$pWAo5K9i%<&BYs$wuSHMcY{Gc&O;(${(hEL0izk<1CstV(4taB`Zm$nFhL zDhx>~G{}=7Ei)$-=zaa%ypo*!bp5o%vdrZCykdPs#ORw@rkW)uCz=~4Cz={1nkQNs oC7PHSBpVtgnwc6|q*&+yb?5=zccWrGsMu%lboFyt=akR{0N~++#sB~S diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html deleted file mode 100644 index 1c6311fc0..000000000 --- a/src/main/resources/static/index.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - Discodeit - - - - - -
- - diff --git "a/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" "b/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" index a5e191c86..0956e1d50 100644 --- "a/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" +++ "b/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" @@ -1,219 +1,34 @@ -## Spring Security 환경설정 +## JWT 컴포넌트 구현 -- [x] 프로젝트에 Spring Security 의존성을 추가하세요. +- [ ] JWT 의존성을 추가하세요. -- [x] Security 설정 클래스를 생성하세요. - - 패키지명: com.sprint.mission.discodeit.config - - 클래스명: SecurityConfig +implementation 'com.nimbusds:nimbus-jose-jwt:10.3' -- [x] SecurityFilterChain Bean을 선언하세요. - - [x] 가장 기본적인 SecurityFilterChain을 등록하고, 이때 등록되는 필터 목록을 디버깅해보세요. 필터 목록은 PR에 첨부하세요. +- [ ] 토큰을 발급, 갱신, 유효성 검사를 담당하는 컴포넌트(JwtTokenProvider)를 구현하세요. - ```java - -@Bean -public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - return http.build(); -} - ``` - -- [x] 개발 환경에서 Spring Security 모듈의 로깅 레벨을 trace로 설정하세요. - - 각 요청마다 통과하는 필터 목록을 확인할 수 있습니다. +![](https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=14437&version=1&directory=/s0yti3992-image.png&name=s0yti3992-image.png) --- -## CSRF 보호 설정하기 - -![](https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=14403&version=1&directory=/66i8irpxz-image.png&name=66i8irpxz-image.png) +## 리팩토링 - 로그인 -> 디스코드잇은 CSR 방식이기 때문에 CSRF 토큰은 다음과 같이 처리합니다.
-> 1.클라이언트에서 페이지가 로드될 때 CSRF 토큰 발급 API를 명시적으로 호출
-> 2.서버는 CSRF 토큰을 응답 헤더(Set-Cookie)를 통해 쿠키에 저장
-> 3.클라이언트에서 매 요청마다 쿠키에 저장된 CSRF 토큰을 헤더(X-XSRF-TOKEN)에 포함
-> 4.서버는 요청 헤더에 포함된 두 토큰 값(X-XSRF-TOKEN, Cookie)을 비교해 유효성 검증
+미션 9와 마찬가지로 Spring Security의 formLogin + 미션 9의 인증 흐름은 그대로 유지하면서 필요한 부분만 대체합니다. -- [x] `CsrfTokenRepository` 구현체를 `CookieCsrfTokenRepository`로 설정하세요. - - 디폴트 구현체는 HttpSessionCsrfTokenRepository입니다. +- [ ] 세션 생성 정책을 `STATELESS`로 변경하고, `sessionConcurrency` 설정을 삭제하세요. ```java - http - .csrf(csrf ->csrf - . + http + .sessionManagement(session ->session + ... + . -csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) +sessionCreationPolicy(...) ) - ``` -- 이때 클라이언트에서 쿠키에 저장된 CSRF 토큰에 접근해야 하므로 Http Only는 false로 설정합니다. - -- [x] `CsrfTokenRequestHandler` 컴포넌트를 대체하세요. - - 디폴트 구현체는 `XorCsrfTokenRequestAttributeHandler`입니다. - - Spring 공식문서에서 권장하는 CSR+SPA(Single Page Application) 환경에 적합한 구현체를 정의하세요. - -```java -public class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler { - private final CsrfTokenRequestHandler plain = new CsrfTokenRequestAttributeHandler(); - private final CsrfTokenRequestHandler xor = new XorCsrfTokenRequestAttributeHandler(); - - @Override - public void handle(HttpServletRequest request, HttpServletResponse response, Supplier csrfToken) { - /* - * Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of - * the CsrfToken when it is rendered in the response body. - */ - this.xor.handle(request, response, csrfToken); - /* - * Render the token value to a cookie by causing the deferred token to be loaded. - */ - csrfToken.get(); - } - - @Override - public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) { - String headerValue = request.getHeader(csrfToken.getHeaderName()); - /* - * If the request contains a request header, use CsrfTokenRequestAttributeHandler - * to resolve the CsrfToken. This applies when a single-page application includes - * the header value automatically, which was obtained via a cookie containing the - * raw CsrfToken. - * - * In all other cases (e.g. if the request contains a request parameter), use - * XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies - * when a server-side rendered form includes the _csrf request parameter as a - * hidden input. - */ - return (StringUtils.hasText(headerValue) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken); - } -} - -``` - -```java -http - .csrf(csrf ->csrf -... - . - -csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler()) - ) - -``` - -- [x] CSRF 토큰을 발급하는 API를 구현하세요. - - - API 스펙 - - - 엔드포인트: GET /api/auth/csrf-token - - 요청: 없음 - - 응답: 203 Void - - ```java - @GetMapping("csrf-token") - public ResponseEntity getCsrfToken(CsrfToken csrfToken) { - String tokenValue = csrfToken.getToken(); - log.debug("CSRF 토큰 요청: {}", tokenValue); - ... - } - ``` -- CsrfToken 파라미터를 메서드 인자로 선언하면, HandlerMethodArgumentResolver를 통해 자동으로 주입됩니다. (공식문서) -- GET 요청에는 CSRF 인증이 이루어지지 않기 때문에 토큰이 초기화되지 않습니다. 따라서 명시적으로 메소드에서 토큰을 호출합니다. - ---- - -## 회원가입 - -- [x] 회원가입 API 스펙은 유지합니다. - - API 스펙 - - 엔드포인트: POST /api/users - - 요청: Body UserCreateRequest, MultipartFile - - 응답: 200 UserDto -- [x] 회원가입 시 비밀번호는 PasswordEncoder를 통해 해시로 저장하세요. - - PasswordEncoder의 구현체는 BCryptPasswordEncoder를 활용하세요 - ---- - -## 인증 - 로그인 - -- [x] formLogin 을 기본값으로 활성화하고, 추가된 필터를 확인해보세요. - -```java -http - .formLogin(Customizer.withDefaults()) -``` - -- Spring Security의 - formLogin 인증 - 흐름은 그대로 - 유지하면서 필요한 - 부분만 대체합니다. - -![](https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=14405&version=1&directory=/c9g464dhi-image.png&name=c9g464dhi-image.png) - -- 이번 미션에서는 보라색 음영 처리된 5가지 컴포넌트를 대체합니다. - 1. `UserDetails` - 2. `UserDetailsService` - 3. `PasswordEncoder`: 이전에 정의한 `BCryptPasswordEncoder`로 대체됩니다. - 4. `AuthenticationSuccessHandler` - 5. `AuthenticationFailureHandler` - - - 각 컴포넌트의 기본 구현체가 무엇인지 디버깅해보세요. - -- [x] 로그인을 처리할 url을 /api/auth/login로 설정하세요. - -```java -http - .formLogin(login ->login - . - -loginProcessingUrl(...) -) -``` - -- [x] `UserDetailsService` 컴포넌트를 대체하세요. - - 디폴트 구현체는 `InMemoryUserDetailsManager`입니다. - - `DiscodeitUserDetailsService`를 정의하세요. - - ```java - -@Service -@RequiredArgsConstructor -public class DiscodeitUserDetailsService implements UserDetailsService { - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - ... - } -} - -``` - -- 디스코드잇 DB에서 자체 관리하는 사용자 정보로 UserDetails 객체를 생성합니다. -- 구현체를 Bean으로 등록하면 자동으로 대체됩니다. - -- [x] `UserDetails` 컴포넌트를 대체하세요. - - 디폴트 구현체는 org.springframework.security.core.userdetails.User입니다. - - `DiscodeitUserDetails`를 정의하세요. - -```java - -@Getter -@RequiredArgsConstructor -public class DiscodeitUserDetails implements UserDetails { - private final UserDto userDto; - private final String password; -... -} -``` - -- 인증 정보(Principal)에 담을 수 있는 정보를 자유롭게 확장할 수 있습니다. -- UserDto와 비밀번호 정보를 저장하세요. -- 앞서 정의한 `DiscodeitUserDetailsService`에서 `DiscodeitUserDetails`를 생성 후 반환하세요. - - -- [x] AuthenticationSuccessHandler 컴포넌트를 대체하세요. - - - 디폴트 구현체는 `SavedRequestAwareAuthenticationSuccessHandler`입니다. - - `LoginSuccessHandler`를 정의하고 대체하세요. +- [ ] AuthenticationSuccessHandler 컴포넌트를 대체하세요. + - 기존 구현체는 LoginSuccessHandler입니다. + - JwtLoginSuccessHandler를 정의하고 대체하세요. ```java @@ -227,242 +42,115 @@ public class LoginSuccessHandler implements AuthenticationSuccessHandler { ... } } - ``` - -- 인증 성공 시 200 UserDto로 응답합니다. -- 설정에 추가하세요. -```java -http - .formLogin(login ->login -... - . - -successHandler(loginSuccessHandler) -) ``` -- [x] `AuthenticiationFailureHandler` 컴포넌트를 대체하세요. - - 디폴트 구현체는 `SimpleUrlAuthenticationFailureHandler`입니다. - - `LoginFailureHandler`를 정의하고 대체하세요. - - ```java - -@Component -public class LoginFailureHandler implements AuthenticationFailureHandler { - ... +- 인증 성공 시 JwtProvider를 활용해 토큰을 발급하세요. + - 엑세스 토큰은 응답 Body에 포함하세요. + - 리프레시 토큰은 쿠키(REFRESH_TOKEN)에 저장하세요. + - `200 JwtDto`로 응답합니다. - @Override - public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, - AuthenticationException exception) throws IOException, ServletException { - ... - } -} - ``` + ![](https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=14438&version=1&directory=/7s8mi349r-image.png&name=7s8mi349r-image.png) -- 인증 실패 시 401 ErrorResponse로 응답합니다. - 설정에 추가하세요. - -```java -http - .formLogin(login ->login -... - . - -failureHandler(loginFailureHandler) -) -``` - -- [x] 이제 로그인 처리는 SecurityFilterChain에서 모두 처리되기 때문에 기존에 구현했던 로그인 관련 코드는 제거하세요. - - - `AuthApi.login`, `AuthController.login` - - `AuthService.login` - - `LoginRequest` + ```java + http + .formLogin(login -> login + ... + .successHandler(jwtLoginSuccessHandler) + ) + + ``` --- -## 인증 - 세션을 활용한 현재 사용자 정보 조회 +## JWT 인증 필터 구현 -> 이전 버전까지의 디스코드잇 프론트엔드에서는 현재 사용자 정보를 브라우저의 세션 스토리지(user-storage)에서 관리해왔습니다.
-> 브라우저의 세션 스토리지는 Javascript로 접근이 가능하기 때문에, XSS(Cross-Site Scripting) 공격에 취약합니다.
-> 따라서 프론트엔드 2.0.x 부터는 사용자 정보를 브라우저의 메모리에서 관리하도록 변경되었습니다.
-> 하지만, 메모리에 저장된 정보는 브라우저 새로고침 시 모두 삭제됩니다.
-> 따라서 새로고침 시 쿠키에 저장된 세션 ID를 통해 현재 사용자 정보를 조회합니다 +- [ ] 엑세스 토큰을 통해 인증하는 필터(JwtAuthenticationFilter)를 구현하세요. -- [x] 세션ID를 통해 사용자의 기본 정보(UserDto)를 가져올 수 있도록 API를 정의하세요. - - API 스펙 - - 엔드포인트: GET /api/auth/me - - 요청: `Header(자동 포함) Cookie: JSESSIONID=…` - - 응답: `200 UserDto` - - `SecurityFilterChain`의 필터를 통해 인증에 성공하면 `Controller`에서
`@AuthenticationPrincipal`를 통해 인증 정보에 접근할 수 있습니다. +```java +public class JwtAuthenticationFilter extends OncePerRequestFilter { ---- + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException {...} -## 인증 - 로그아웃 +``` -- Spring Security의 logout 흐름은 그대로 유지하면서 필요한 부분만 대체합니다. -- 이번 미션에서는 2가지 요소를 대체합니다. - - Logout 처리 URL - - LogoutSuccessHandler -- [x] 로그아웃을 처리할 url을 `/api/auth/logout`로 설정하세요. +- 요청 당 한번만 실행되도록 OncePerRequestFilter를 상속하세요. -```java -http - .logout(logout ->logout - . +- 요청 헤더(Authorization)에 Bearer 토큰이 포함된 경우에만 인증을 시도하세요. -logoutUrl(...) -) -``` +- JwtProvider를 통해 엑세스 토큰의 유효성을 검사하세요. -- [x] `LogoutSuccessHandler` 컴포넌트를 대체하세요. - - 디폴트 구현체는 SimpleUrlLogoutSuccessHandler입니다. - - `HttpStatusReturningLogoutSuccessHandler`로 대체하세요. +- 유효한 토큰인 경우 UsernamePasswordAuthenticationToken 객체를 활용해 인증 완료 처리하세요. - ```java - http - .logout(logout ->logout - ... - . +```java +UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken( + userDetails, + null, + userDetails.getAuthorities() + ); +SecurityContextHolder. -logoutSuccessHandler(...) - ) +getContext(). +setAuthentication(authentication); ``` -- `204 Void` 응답을 반환하세요. - --- -## 인가 - 권한 정의 +## 리프레시 토큰을 활용한 엑세스 토큰 재발급 -- [x] 다음과 같이 권한을 정의하세요. - ![](https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=14408&version=1&directory=/faef2l3uk-image.png&name=faef2l3uk-image.png) - - 관리자: ADMIN - - 채널 매니저: CHANNEL_MANAGER - - 일반 사용자: USER -- [x] 데이터베이스 스키마를 변경하세요. +- [ ] 리프레시 토큰을 활용해 엑세스 토큰을 재발급하는 API를 구현하세요. -```java -CREATE TABLE - -users - ( -... - role varchar(20) NOT NULL -); - -ALTER TABLE -users -ADD role - -varchar(20) NOT NULL; -``` + - API 스펙 + - 엔드포인트: `POST /api/auth/refresh` + - 요청: `Header Cookie: REFRESH_TOKEN=…` + - 응답 + - 리프레시 토큰이 유효한 경우: `200 JwtDto` + - 리프레시 토큰이 유효하지 않은 경우: `401 ErrorResponse` + - permitAll 설정에 포함하세요. + - 이 API는 엑세스 토큰이 없거나 만료된 상태에서 호출하게 됩니다. -- [x] 회원 가입 시 모든 사용자는 USER 권한을 기본 권한으로 설정하세요. -- [x] 사용자 권한을 수정하는 API를 구현하세요. +- [ ] 리프레시 토큰 Rotation을 통해 보안을 강화하세요. - - API 스펙 - - 엔드포인트: `PUT /api/auth/role` - - 요청: `Body UserRoleUpdateRequest` - - 응답: `200 UserDto` +- [ ] 토큰 재발급 API로 대체할 수 있는 컴포넌트를 모두 삭제하세요. + - Me API (GET /auth/me) + > - 프론트엔드 2.0.x과 마찬가지로 2.1.x에서는 사용자 정보와 엑세스 토큰 정보를 브라우저의 메모리에서 관리합니다. + > - 따라서 새로고침 시 쿠키에 저장된 리프레시 토큰을 통해 엑세스 토큰을 갱신합니다. - ![](https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=14408&version=1&directory=/qu8jij3u4-image.png&name=qu8jij3u4-image.png) +- RememberMe -- [x] 애플리케이션 실행 시 ADMIN 권한을 가진 어드민 계정이 초기화되도록 구현하세요. - - 어드민 계정이 없는 경우에만 초기화하세요. -- [x] DiscodietUserDetails.getAuthorities를 수정하세요. + - 쿠키에 저장된 리프레시 토큰이 RememberMe의 기능을 대체할 수 있습니다. --- -## 인가 - 권한 적용 +## 리팩토링 - 로그아웃 -- [x] `authorizeHttpRequests`를 활성화하고, 모든 요청을 인증하도록 설정하세요. +- [ ] 쿠키에 저장된 리프레시 토큰을 삭제하는 LogoutHandler를 구현하세요. ```java -http - .authorizeHttpRequests(auth ->auth - . +public class JwtLogoutHandler implements LogoutHandler { -anyRequest(). + @Override + public void logout(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) {...} -authenticated() -) ``` -- [ ] 다음의 요청은 인증하지 않도록 설정하세요. +- [ ] 구현한 핸들러를 추가하세요. ```java http - .authorizeHttpRequests(auth ->auth + .logout(logout ->logout ... . -requestMatchers(...). - -permitAll() +addLogoutHandler(jwtLogoutHandler) ) ``` -- Csrf Token 발급 -- 회원가입 -- 로그인 -- 로그아웃 - API가 아닌 요청(Swagger, Actuator 등) - - -- [x] Method Security를 활성화하세요. - -```java -... - -@EnableMethodSecurity -public class SecurityConfig {... -} -``` - -- [x] Service의 메소드 별로 아래의 조건에 맞게 권한을 수정하세요. - - 퍼블릭 채널 생성, 수정, 삭제는 CHANNEL_MANAGER 권한을 가져야합니다. - - 사용자 권한 수정은 ADMIN 권한을 가져야합니다. -- [x] 적절한 권한이 없는 경우 403 응답을 반환하세요. - - - `SecurityFilterChain` - - ```java - http - .exceptionHandling(ex ->ex - . - -authenticationEntryPoint(...) - . - -accessDeniedHandler(...) - ) -GlobalExceptionHandler - -@ExceptionHandler(MethodArgumentNotValidException.class) - -public ResponseEntity handleValidationExceptions(MethodArgumentNotValidException ex) {...} - ``` - -- [x] `RoleHierarchy`를 활용해 권한의 계층 구조를 정의하세요. - - - 관리자 > 채널 매니저 > 일반 사용자 - - 관리자 권한은 채널 매니저, 일반 사용자 권한을 포함합니다. - - 채널 매니저 권한은 일반 사용자 권한을 포함합니다. - - ```java - -@Bean -public RoleHierarchy roleHierarchy() {...} - -@Bean -static MethodSecurityExpressionHandler methodSecurityExpressionHandler( - RoleHierarchy roleHierarchy) { - DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler(); - handler.setRoleHierarchy(roleHierarchy); - return handler; -} - - ``` \ No newline at end of file +--- \ No newline at end of file diff --git "a/\354\213\254\355\231\224\354\232\224\352\265\254\354\202\254\355\225\255.md" "b/\354\213\254\355\231\224\354\232\224\352\265\254\354\202\254\355\225\255.md" index 9dd2d0d4a..e5270c1d5 100644 --- "a/\354\213\254\355\231\224\354\232\224\352\265\254\354\202\254\355\225\255.md" +++ "b/\354\213\254\355\231\224\354\232\224\352\265\254\354\202\254\355\225\255.md" @@ -1,110 +1,87 @@ -## 세션 관리 고도화 +## 리팩토링 - 토큰 상태 관리 -- [x] 동일한 계정으로 동시 로그인할 수 없도록 설정하세요. - - `sessionConcurrency` 설정을 활용하세요. +- 토큰 기반 인증 방식은 세션 기반 인증 방식과 달리 무상태(stateless)이기 때문에 사용자의 로그인 상태를 제어하기 어렵습니다. - ```java - http - .sessionManagement(management ->management - . - -sessionConcurrency(concurrency ->concurrency - ... - ) - ) - ``` +- 따라서 SessionRegistry를 통해 세션의 상태를 관리했던 것처럼, JWT의 상태를 관리할 수 있는 컴포넌트를 추가해야합니다. + + - [ ] 토큰의 상태를 관리하는 JwtRegistry를 구현하세요. - - 세션의 동일성을 보장하기 위해 DiscodeitUserDetails의 equals(), hashcode() 메소드를 오버라이딩하세요. + ![](https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=14442&version=1&directory=/9da9kvl8y-image.png&name=9da9kvl8y-image.png) -> [공식 문서](https://docs.spring.io/spring-security/reference/servlet/authentication/session-management.html#ns-concurrent-sessions) -
If you are using a custom implementation of UserDetails, ensure you override the equals() and hashCode() methods. -> The -> default SessionRegistry implementation in Spring Security relies on an in-memory Map that uses these methods to -> correctly identify and manage user sessions. Failing to override them may lead to issues where session tracking and -> user -> comparison behave unexpectedly. + - `JwtRegistry` + - `registerJwtInformation` + - 로그인 성공 시 `JwtInformation`을 등록합니다. + - 최대 동시 로그인 수(`1`)를 제어합니다. + - `invalidateJwtInformationByUserId`: UserId로 해당 유저의 모든 JwtInformation 정보를 삭제합니다. + - `hasActiveJwtInformationBy`*: JwtInformation이 Registry에 존재하는지 확인합니다. + - `ByUserId`: 사용자의 로그인 상태를 판단할 때 활용합니다. + - `ByAccessToken`: 필터에서 유효한 토큰인지 확인할 때 활용합니다. + - `ByRefreshToken`: 토큰 재발급 시 유효한 토큰인지 확인할 때 활용합니다. + - `rotateJwtInformation`: 토큰 재발급 시 토큰 로테이션을 수행합니다. + - `clearExpiredJwtInformation`: 만료된 JwtInformation을 삭제합니다. + - `InMemoryJwtRegistry` + - 메모리에 JwtInformation을 저장하는 JwtRegistry 구현체입니다. -- [x] 권한이 변경된 사용자가 로그인 상태라면 세션을 무효화하세요. - - `sessionRegistry`를 활용하세요. + - 동시성 처리를 위해 다음과 같이 구성하세요. 동시성에 대해서는 다음 미션에서 학습합니다. ```java +public class InMemoryJwtRegistry implements JwtRegistry { -@Bean -public SecurityFilterChain filterChain( -... - HttpSecurity http, - SessionRegistry sessionRegistry -) { - http - .sessionManagement(management -> management - .sessionConcurrency(concurrency -> concurrency -... -.sessionRegistry(sessionRegistry) -) -) + // > + private final Map> origin = new ConcurrentHashMap<>(); + private final int maxActiveJwtCount; ... } - -@Bean -public SessionRegistry sessionRegistry() {...} ``` -- `httpSessionEventPublisher`: HttpSession이 만료된 경우 이벤트를 통해 SessionRegistry의 SessionInformation도 자동으로 만료하기 위해 필요한 - Bean입니다. +- [ ] JwtAuthenticationFilter에서 JwtRegistry를 활용해 토큰의 상태를 검사하는 로직을 추가하세요. + +- [ ] JwtRegistry를 활용해 동시 로그인 제한 기능을 리팩토링하세요. + - 동일한 계정으로 로그인 시 기존 로그인 세션을 무효화합니다. + +- [ ] JwtRegistry를 활용해 권한이 변경된 사용자가 로그인 상태라면 강제로 로그아웃되도록 하세요. + +- [ ] JwtRegistry를 활용해 사용자의 로그인 여부를 판단하도록 리팩토링하세요. + +- [ ] JwtLogoutHandler에서 JwtRegistry를 활용해 로그아웃 시 토큰을 무효화하세요. ```java -@Service -public class BasicAuthService implements AuthService { +@Override +public void logout(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) { ... - private final SessionRegistry sessionRegistry; + Arrays.stream(request.getCookies()) + .filter(cookie -> cookie.getName().equals(JwtTokenProvider.REFRESH_TOKEN_COOKIE_NAME)) + .findFirst() + .ifPresent(cookie -> {...}); ... } ``` -- [x] UserStatus 엔티티 대신 SessionRegistry를 활용해 사용자의 로그인 여부를 판단하도록 리팩토링하세요. +- 로그아웃 API는 인증이 필요없기 때문에 Authentication 정보가 없을 수 있습니다. +- 따라서 요청 쿠키의 리프레시 토큰을 활용해 토큰을 무효화합니다. - - UserStatus 엔티티와 관련된 코드는 모두 삭제하세요. - - (로그아웃처럼) `HttpSession` 만료 시 `SessionRegistry`의 `SessionInformation`도 자동으로 만료 처리할 수 있도록 `HttpSessionEventPublisher` - 를 Bean으로 - 등록합니다. +- [ ] 주기적으로 만료된 토큰 정보를 레지스트리에서 삭제하세요. + - @EnableScheduling를 추가하세요. ```java - -@Bean -public HttpSessionEventPublisher httpSessionEventPublisher() { - return new HttpSessionEventPublisher(); -} + + @Configuration + @EnableJpaAuditing + @EnableScheduling + public class AppConfig { + + } + ``` ---- - -## 로그인 고도화 - RememberMe - -- [x] 로그인 요청 파라미터(remember-me)가 true인 경우 세션이 무효화되어도 자동으로 다시 로그인되도록 하세요. - - 로그인 화면에서 로그인 유지 체크 후 로그인하면 remember-me 파라미터가 true로 설정되어 요청합니다. - ![](https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=14411&version=1&directory=/otona45pe-image.png&name=otona45pe-image.png) - -- `remeberMe` 설정을 활용하세요. +- `@Scheduled`를 활용해서 5분마다 만료된 토큰을 삭제하세요. ```java -http - .rememberMe(...) -``` - -- 로그인 상태에서 `JESSIONID` 쿠키를 삭제 후 새로고침했을 때 인증 상태가 유지 되는지 확인해보세요. - ![](https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=14411&version=1&directory=/aroseetgw-image.png&name=aroseetgw-image.png) ---- +@Scheduled(fixedDelay = 1000 * 60 * 5) +@Override +public void clearExpiredJwtInformation() {...} -## 권한 적용 고도화 - -- [x] SpEL을 활용해 Method Security 기반 리소스 보호 정책을 강화해보세요. - - 사용자 정보 수정, 삭제는 본인만 할 수 있습니다. - - 메시지 수정, 삭제는 해당 메시지를 작성한 사람만 할 수 있습니다. - ---- - -## 첨부 - -![img.png](img.png) \ No newline at end of file +``` From a6853e476c4b213b4e2fa7ebecdd60a4b7135dcc Mon Sep 17 00:00:00 2001 From: "Lim, gyuseong" <100032171+gyural@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:46:37 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=EC=9A=94=EA=B5=AC=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=B0=8F=20=EC=A0=95=EC=A0=81=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=B4=88=EA=B8=B0=20=EC=84=B8=ED=8C=85=20=EC=99=84=EB=A3=8C?= =?UTF-8?q?=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/static/assets/index-COLcXNzv.js | 1338 +++++++++++++++++ .../static/assets/index-kQJbKSsj.css | 1 + src/main/resources/static/favicon.ico | Bin 0 -> 1588 bytes src/main/resources/static/index.html | 26 + 4 files changed, 1365 insertions(+) create mode 100644 src/main/resources/static/assets/index-COLcXNzv.js create mode 100644 src/main/resources/static/assets/index-kQJbKSsj.css create mode 100644 src/main/resources/static/favicon.ico create mode 100644 src/main/resources/static/index.html diff --git a/src/main/resources/static/assets/index-COLcXNzv.js b/src/main/resources/static/assets/index-COLcXNzv.js new file mode 100644 index 000000000..af587fd74 --- /dev/null +++ b/src/main/resources/static/assets/index-COLcXNzv.js @@ -0,0 +1,1338 @@ +var Cg=Object.defineProperty;var Eg=(r,i,s)=>i in r?Cg(r,i,{enumerable:!0,configurable:!0,writable:!0,value:s}):r[i]=s;var uf=(r,i,s)=>Eg(r,typeof i!="symbol"?i+"":i,s);(function(){const i=document.createElement("link").relList;if(i&&i.supports&&i.supports("modulepreload"))return;for(const c of document.querySelectorAll('link[rel="modulepreload"]'))l(c);new MutationObserver(c=>{for(const d of c)if(d.type==="childList")for(const p of d.addedNodes)p.tagName==="LINK"&&p.rel==="modulepreload"&&l(p)}).observe(document,{childList:!0,subtree:!0});function s(c){const d={};return c.integrity&&(d.integrity=c.integrity),c.referrerPolicy&&(d.referrerPolicy=c.referrerPolicy),c.crossOrigin==="use-credentials"?d.credentials="include":c.crossOrigin==="anonymous"?d.credentials="omit":d.credentials="same-origin",d}function l(c){if(c.ep)return;c.ep=!0;const d=s(c);fetch(c.href,d)}})();function jg(r){return r&&r.__esModule&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r}var Ca={exports:{}},xo={},Ea={exports:{}},pe={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var cf;function Ag(){if(cf)return pe;cf=1;var r=Symbol.for("react.element"),i=Symbol.for("react.portal"),s=Symbol.for("react.fragment"),l=Symbol.for("react.strict_mode"),c=Symbol.for("react.profiler"),d=Symbol.for("react.provider"),p=Symbol.for("react.context"),m=Symbol.for("react.forward_ref"),w=Symbol.for("react.suspense"),v=Symbol.for("react.memo"),S=Symbol.for("react.lazy"),j=Symbol.iterator;function R(k){return k===null||typeof k!="object"?null:(k=j&&k[j]||k["@@iterator"],typeof k=="function"?k:null)}var L={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},T=Object.assign,N={};function _(k,D,ae){this.props=k,this.context=D,this.refs=N,this.updater=ae||L}_.prototype.isReactComponent={},_.prototype.setState=function(k,D){if(typeof k!="object"&&typeof k!="function"&&k!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,k,D,"setState")},_.prototype.forceUpdate=function(k){this.updater.enqueueForceUpdate(this,k,"forceUpdate")};function V(){}V.prototype=_.prototype;function U(k,D,ae){this.props=k,this.context=D,this.refs=N,this.updater=ae||L}var B=U.prototype=new V;B.constructor=U,T(B,_.prototype),B.isPureReactComponent=!0;var W=Array.isArray,I=Object.prototype.hasOwnProperty,M={current:null},H={key:!0,ref:!0,__self:!0,__source:!0};function ie(k,D,ae){var ce,he={},fe=null,ke=null;if(D!=null)for(ce in D.ref!==void 0&&(ke=D.ref),D.key!==void 0&&(fe=""+D.key),D)I.call(D,ce)&&!H.hasOwnProperty(ce)&&(he[ce]=D[ce]);var ye=arguments.length-2;if(ye===1)he.children=ae;else if(1>>1,D=q[k];if(0>>1;kc(he,Q))fec(ke,he)?(q[k]=ke,q[fe]=Q,k=fe):(q[k]=he,q[ce]=Q,k=ce);else if(fec(ke,Q))q[k]=ke,q[fe]=Q,k=fe;else break e}}return ee}function c(q,ee){var Q=q.sortIndex-ee.sortIndex;return Q!==0?Q:q.id-ee.id}if(typeof performance=="object"&&typeof performance.now=="function"){var d=performance;r.unstable_now=function(){return d.now()}}else{var p=Date,m=p.now();r.unstable_now=function(){return p.now()-m}}var w=[],v=[],S=1,j=null,R=3,L=!1,T=!1,N=!1,_=typeof setTimeout=="function"?setTimeout:null,V=typeof clearTimeout=="function"?clearTimeout:null,U=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function B(q){for(var ee=s(v);ee!==null;){if(ee.callback===null)l(v);else if(ee.startTime<=q)l(v),ee.sortIndex=ee.expirationTime,i(w,ee);else break;ee=s(v)}}function W(q){if(N=!1,B(q),!T)if(s(w)!==null)T=!0,ge(I);else{var ee=s(v);ee!==null&&Ee(W,ee.startTime-q)}}function I(q,ee){T=!1,N&&(N=!1,V(ie),ie=-1),L=!0;var Q=R;try{for(B(ee),j=s(w);j!==null&&(!(j.expirationTime>ee)||q&&!ot());){var k=j.callback;if(typeof k=="function"){j.callback=null,R=j.priorityLevel;var D=k(j.expirationTime<=ee);ee=r.unstable_now(),typeof D=="function"?j.callback=D:j===s(w)&&l(w),B(ee)}else l(w);j=s(w)}if(j!==null)var ae=!0;else{var ce=s(v);ce!==null&&Ee(W,ce.startTime-ee),ae=!1}return ae}finally{j=null,R=Q,L=!1}}var M=!1,H=null,ie=-1,ve=5,Oe=-1;function ot(){return!(r.unstable_now()-Oeq||125k?(q.sortIndex=Q,i(v,q),s(w)===null&&q===s(v)&&(N?(V(ie),ie=-1):N=!0,Ee(W,Q-k))):(q.sortIndex=D,i(w,q),T||L||(T=!0,ge(I))),q},r.unstable_shouldYield=ot,r.unstable_wrapCallback=function(q){var ee=R;return function(){var Q=R;R=ee;try{return q.apply(this,arguments)}finally{R=Q}}}}(Ra)),Ra}var mf;function _g(){return mf||(mf=1,Aa.exports=Tg()),Aa.exports}/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var gf;function Ng(){if(gf)return ft;gf=1;var r=ou(),i=_g();function s(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),w=Object.prototype.hasOwnProperty,v=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,S={},j={};function R(e){return w.call(j,e)?!0:w.call(S,e)?!1:v.test(e)?j[e]=!0:(S[e]=!0,!1)}function L(e,t,n,o){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return o?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function T(e,t,n,o){if(t===null||typeof t>"u"||L(e,t,n,o))return!0;if(o)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function N(e,t,n,o,a,u,f){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=o,this.attributeNamespace=a,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=u,this.removeEmptyString=f}var _={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){_[e]=new N(e,0,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];_[t]=new N(t,1,!1,e[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){_[e]=new N(e,2,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){_[e]=new N(e,2,!1,e,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){_[e]=new N(e,3,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){_[e]=new N(e,3,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){_[e]=new N(e,4,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){_[e]=new N(e,6,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){_[e]=new N(e,5,!1,e.toLowerCase(),null,!1,!1)});var V=/[\-:]([a-z])/g;function U(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(V,U);_[t]=new N(t,1,!1,e,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(V,U);_[t]=new N(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(V,U);_[t]=new N(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){_[e]=new N(e,1,!1,e.toLowerCase(),null,!1,!1)}),_.xlinkHref=new N("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){_[e]=new N(e,1,!1,e.toLowerCase(),null,!0,!0)});function B(e,t,n,o){var a=_.hasOwnProperty(t)?_[t]:null;(a!==null?a.type!==0:o||!(2g||a[f]!==u[g]){var y=` +`+a[f].replace(" at new "," at ");return e.displayName&&y.includes("")&&(y=y.replace("",e.displayName)),y}while(1<=f&&0<=g);break}}}finally{ae=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?D(e):""}function he(e){switch(e.tag){case 5:return D(e.type);case 16:return D("Lazy");case 13:return D("Suspense");case 19:return D("SuspenseList");case 0:case 2:case 15:return e=ce(e.type,!1),e;case 11:return e=ce(e.type.render,!1),e;case 1:return e=ce(e.type,!0),e;default:return""}}function fe(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case H:return"Fragment";case M:return"Portal";case ve:return"Profiler";case ie:return"StrictMode";case le:return"Suspense";case me:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case ot:return(e.displayName||"Context")+".Consumer";case Oe:return(e._context.displayName||"Context")+".Provider";case ne:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Re:return t=e.displayName||null,t!==null?t:fe(e.type)||"Memo";case ge:t=e._payload,e=e._init;try{return fe(e(t))}catch{}}return null}function ke(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return fe(t);case 8:return t===ie?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function ye(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function we(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Qe(e){var t=we(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),o=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var a=n.get,u=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return a.call(this)},set:function(f){o=""+f,u.call(this,f)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return o},setValue:function(f){o=""+f},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function qt(e){e._valueTracker||(e._valueTracker=Qe(e))}function Tt(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),o="";return e&&(o=we(e)?e.checked?"true":"false":e.value),e=o,e!==n?(t.setValue(e),!0):!1}function Bo(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function _s(e,t){var n=t.checked;return Q({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function mu(e,t){var n=t.defaultValue==null?"":t.defaultValue,o=t.checked!=null?t.checked:t.defaultChecked;n=ye(t.value!=null?t.value:n),e._wrapperState={initialChecked:o,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function gu(e,t){t=t.checked,t!=null&&B(e,"checked",t,!1)}function Ns(e,t){gu(e,t);var n=ye(t.value),o=t.type;if(n!=null)o==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(o==="submit"||o==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?Os(e,t.type,n):t.hasOwnProperty("defaultValue")&&Os(e,t.type,ye(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function yu(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var o=t.type;if(!(o!=="submit"&&o!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function Os(e,t,n){(t!=="number"||Bo(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Mr=Array.isArray;function Qn(e,t,n,o){if(e=e.options,t){t={};for(var a=0;a"+t.valueOf().toString()+"",t=Fo.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Lr(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Ir={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Ph=["Webkit","ms","Moz","O"];Object.keys(Ir).forEach(function(e){Ph.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Ir[t]=Ir[e]})});function Cu(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Ir.hasOwnProperty(e)&&Ir[e]?(""+t).trim():t+"px"}function Eu(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var o=n.indexOf("--")===0,a=Cu(n,t[n],o);n==="float"&&(n="cssFloat"),o?e.setProperty(n,a):e[n]=a}}var Th=Q({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Is(e,t){if(t){if(Th[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(s(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(s(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(s(61))}if(t.style!=null&&typeof t.style!="object")throw Error(s(62))}}function Ds(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var zs=null;function $s(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var Bs=null,Gn=null,Kn=null;function ju(e){if(e=ro(e)){if(typeof Bs!="function")throw Error(s(280));var t=e.stateNode;t&&(t=ui(t),Bs(e.stateNode,e.type,t))}}function Au(e){Gn?Kn?Kn.push(e):Kn=[e]:Gn=e}function Ru(){if(Gn){var e=Gn,t=Kn;if(Kn=Gn=null,ju(e),t)for(e=0;e>>=0,e===0?32:31-(Fh(e)/bh|0)|0}var Wo=64,qo=4194304;function Br(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Yo(e,t){var n=e.pendingLanes;if(n===0)return 0;var o=0,a=e.suspendedLanes,u=e.pingedLanes,f=n&268435455;if(f!==0){var g=f&~a;g!==0?o=Br(g):(u&=f,u!==0&&(o=Br(u)))}else f=n&~a,f!==0?o=Br(f):u!==0&&(o=Br(u));if(o===0)return 0;if(t!==0&&t!==o&&!(t&a)&&(a=o&-o,u=t&-t,a>=u||a===16&&(u&4194240)!==0))return t;if(o&4&&(o|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=o;0n;n++)t.push(e);return t}function Fr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-_t(t),e[t]=n}function Wh(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var o=e.eventTimes;for(e=e.expirationTimes;0=Qr),tc=" ",nc=!1;function rc(e,t){switch(e){case"keyup":return xm.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function oc(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Zn=!1;function Sm(e,t){switch(e){case"compositionend":return oc(t);case"keypress":return t.which!==32?null:(nc=!0,tc);case"textInput":return e=t.data,e===tc&&nc?null:e;default:return null}}function km(e,t){if(Zn)return e==="compositionend"||!rl&&rc(e,t)?(e=Gu(),Jo=Xs=an=null,Zn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=o}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=dc(n)}}function pc(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?pc(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function hc(){for(var e=window,t=Bo();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=Bo(e.document)}return t}function sl(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Nm(e){var t=hc(),n=e.focusedElem,o=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&pc(n.ownerDocument.documentElement,n)){if(o!==null&&sl(n)){if(t=o.start,e=o.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var a=n.textContent.length,u=Math.min(o.start,a);o=o.end===void 0?u:Math.min(o.end,a),!e.extend&&u>o&&(a=o,o=u,u=a),a=fc(n,u);var f=fc(n,o);a&&f&&(e.rangeCount!==1||e.anchorNode!==a.node||e.anchorOffset!==a.offset||e.focusNode!==f.node||e.focusOffset!==f.offset)&&(t=t.createRange(),t.setStart(a.node,a.offset),e.removeAllRanges(),u>o?(e.addRange(t),e.extend(f.node,f.offset)):(t.setEnd(f.node,f.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,er=null,ll=null,Jr=null,al=!1;function mc(e,t,n){var o=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;al||er==null||er!==Bo(o)||(o=er,"selectionStart"in o&&sl(o)?o={start:o.selectionStart,end:o.selectionEnd}:(o=(o.ownerDocument&&o.ownerDocument.defaultView||window).getSelection(),o={anchorNode:o.anchorNode,anchorOffset:o.anchorOffset,focusNode:o.focusNode,focusOffset:o.focusOffset}),Jr&&Xr(Jr,o)||(Jr=o,o=si(ll,"onSelect"),0ir||(e.current=wl[ir],wl[ir]=null,ir--)}function Ae(e,t){ir++,wl[ir]=e.current,e.current=t}var fn={},Xe=dn(fn),lt=dn(!1),Tn=fn;function sr(e,t){var n=e.type.contextTypes;if(!n)return fn;var o=e.stateNode;if(o&&o.__reactInternalMemoizedUnmaskedChildContext===t)return o.__reactInternalMemoizedMaskedChildContext;var a={},u;for(u in n)a[u]=t[u];return o&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=a),a}function at(e){return e=e.childContextTypes,e!=null}function ci(){Te(lt),Te(Xe)}function _c(e,t,n){if(Xe.current!==fn)throw Error(s(168));Ae(Xe,t),Ae(lt,n)}function Nc(e,t,n){var o=e.stateNode;if(t=t.childContextTypes,typeof o.getChildContext!="function")return n;o=o.getChildContext();for(var a in o)if(!(a in t))throw Error(s(108,ke(e)||"Unknown",a));return Q({},n,o)}function di(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||fn,Tn=Xe.current,Ae(Xe,e),Ae(lt,lt.current),!0}function Oc(e,t,n){var o=e.stateNode;if(!o)throw Error(s(169));n?(e=Nc(e,t,Tn),o.__reactInternalMemoizedMergedChildContext=e,Te(lt),Te(Xe),Ae(Xe,e)):Te(lt),Ae(lt,n)}var Qt=null,fi=!1,Sl=!1;function Mc(e){Qt===null?Qt=[e]:Qt.push(e)}function Hm(e){fi=!0,Mc(e)}function pn(){if(!Sl&&Qt!==null){Sl=!0;var e=0,t=je;try{var n=Qt;for(je=1;e>=f,a-=f,Gt=1<<32-_t(t)+a|n<se?(qe=oe,oe=null):qe=oe.sibling;var Se=z(E,oe,A[se],b);if(Se===null){oe===null&&(oe=qe);break}e&&oe&&Se.alternate===null&&t(E,oe),x=u(Se,x,se),re===null?te=Se:re.sibling=Se,re=Se,oe=qe}if(se===A.length)return n(E,oe),Ne&&Nn(E,se),te;if(oe===null){for(;sese?(qe=oe,oe=null):qe=oe.sibling;var kn=z(E,oe,Se.value,b);if(kn===null){oe===null&&(oe=qe);break}e&&oe&&kn.alternate===null&&t(E,oe),x=u(kn,x,se),re===null?te=kn:re.sibling=kn,re=kn,oe=qe}if(Se.done)return n(E,oe),Ne&&Nn(E,se),te;if(oe===null){for(;!Se.done;se++,Se=A.next())Se=F(E,Se.value,b),Se!==null&&(x=u(Se,x,se),re===null?te=Se:re.sibling=Se,re=Se);return Ne&&Nn(E,se),te}for(oe=o(E,oe);!Se.done;se++,Se=A.next())Se=G(oe,E,se,Se.value,b),Se!==null&&(e&&Se.alternate!==null&&oe.delete(Se.key===null?se:Se.key),x=u(Se,x,se),re===null?te=Se:re.sibling=Se,re=Se);return e&&oe.forEach(function(kg){return t(E,kg)}),Ne&&Nn(E,se),te}function ze(E,x,A,b){if(typeof A=="object"&&A!==null&&A.type===H&&A.key===null&&(A=A.props.children),typeof A=="object"&&A!==null){switch(A.$$typeof){case I:e:{for(var te=A.key,re=x;re!==null;){if(re.key===te){if(te=A.type,te===H){if(re.tag===7){n(E,re.sibling),x=a(re,A.props.children),x.return=E,E=x;break e}}else if(re.elementType===te||typeof te=="object"&&te!==null&&te.$$typeof===ge&&Bc(te)===re.type){n(E,re.sibling),x=a(re,A.props),x.ref=oo(E,re,A),x.return=E,E=x;break e}n(E,re);break}else t(E,re);re=re.sibling}A.type===H?(x=Bn(A.props.children,E.mode,b,A.key),x.return=E,E=x):(b=Fi(A.type,A.key,A.props,null,E.mode,b),b.ref=oo(E,x,A),b.return=E,E=b)}return f(E);case M:e:{for(re=A.key;x!==null;){if(x.key===re)if(x.tag===4&&x.stateNode.containerInfo===A.containerInfo&&x.stateNode.implementation===A.implementation){n(E,x.sibling),x=a(x,A.children||[]),x.return=E,E=x;break e}else{n(E,x);break}else t(E,x);x=x.sibling}x=va(A,E.mode,b),x.return=E,E=x}return f(E);case ge:return re=A._init,ze(E,x,re(A._payload),b)}if(Mr(A))return J(E,x,A,b);if(ee(A))return Z(E,x,A,b);gi(E,A)}return typeof A=="string"&&A!==""||typeof A=="number"?(A=""+A,x!==null&&x.tag===6?(n(E,x.sibling),x=a(x,A),x.return=E,E=x):(n(E,x),x=ya(A,E.mode,b),x.return=E,E=x),f(E)):n(E,x)}return ze}var cr=Fc(!0),bc=Fc(!1),yi=dn(null),vi=null,dr=null,Rl=null;function Pl(){Rl=dr=vi=null}function Tl(e){var t=yi.current;Te(yi),e._currentValue=t}function _l(e,t,n){for(;e!==null;){var o=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,o!==null&&(o.childLanes|=t)):o!==null&&(o.childLanes&t)!==t&&(o.childLanes|=t),e===n)break;e=e.return}}function fr(e,t){vi=e,Rl=dr=null,e=e.dependencies,e!==null&&e.firstContext!==null&&(e.lanes&t&&(ut=!0),e.firstContext=null)}function Et(e){var t=e._currentValue;if(Rl!==e)if(e={context:e,memoizedValue:t,next:null},dr===null){if(vi===null)throw Error(s(308));dr=e,vi.dependencies={lanes:0,firstContext:e}}else dr=dr.next=e;return t}var On=null;function Nl(e){On===null?On=[e]:On.push(e)}function Uc(e,t,n,o){var a=t.interleaved;return a===null?(n.next=n,Nl(t)):(n.next=a.next,a.next=n),t.interleaved=n,Xt(e,o)}function Xt(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var hn=!1;function Ol(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Hc(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Jt(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function mn(e,t,n){var o=e.updateQueue;if(o===null)return null;if(o=o.shared,xe&2){var a=o.pending;return a===null?t.next=t:(t.next=a.next,a.next=t),o.pending=t,Xt(e,n)}return a=o.interleaved,a===null?(t.next=t,Nl(o)):(t.next=a.next,a.next=t),o.interleaved=t,Xt(e,n)}function xi(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var o=t.lanes;o&=e.pendingLanes,n|=o,t.lanes=n,qs(e,n)}}function Vc(e,t){var n=e.updateQueue,o=e.alternate;if(o!==null&&(o=o.updateQueue,n===o)){var a=null,u=null;if(n=n.firstBaseUpdate,n!==null){do{var f={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};u===null?a=u=f:u=u.next=f,n=n.next}while(n!==null);u===null?a=u=t:u=u.next=t}else a=u=t;n={baseState:o.baseState,firstBaseUpdate:a,lastBaseUpdate:u,shared:o.shared,effects:o.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function wi(e,t,n,o){var a=e.updateQueue;hn=!1;var u=a.firstBaseUpdate,f=a.lastBaseUpdate,g=a.shared.pending;if(g!==null){a.shared.pending=null;var y=g,P=y.next;y.next=null,f===null?u=P:f.next=P,f=y;var $=e.alternate;$!==null&&($=$.updateQueue,g=$.lastBaseUpdate,g!==f&&(g===null?$.firstBaseUpdate=P:g.next=P,$.lastBaseUpdate=y))}if(u!==null){var F=a.baseState;f=0,$=P=y=null,g=u;do{var z=g.lane,G=g.eventTime;if((o&z)===z){$!==null&&($=$.next={eventTime:G,lane:0,tag:g.tag,payload:g.payload,callback:g.callback,next:null});e:{var J=e,Z=g;switch(z=t,G=n,Z.tag){case 1:if(J=Z.payload,typeof J=="function"){F=J.call(G,F,z);break e}F=J;break e;case 3:J.flags=J.flags&-65537|128;case 0:if(J=Z.payload,z=typeof J=="function"?J.call(G,F,z):J,z==null)break e;F=Q({},F,z);break e;case 2:hn=!0}}g.callback!==null&&g.lane!==0&&(e.flags|=64,z=a.effects,z===null?a.effects=[g]:z.push(g))}else G={eventTime:G,lane:z,tag:g.tag,payload:g.payload,callback:g.callback,next:null},$===null?(P=$=G,y=F):$=$.next=G,f|=z;if(g=g.next,g===null){if(g=a.shared.pending,g===null)break;z=g,g=z.next,z.next=null,a.lastBaseUpdate=z,a.shared.pending=null}}while(!0);if($===null&&(y=F),a.baseState=y,a.firstBaseUpdate=P,a.lastBaseUpdate=$,t=a.shared.interleaved,t!==null){a=t;do f|=a.lane,a=a.next;while(a!==t)}else u===null&&(a.shared.lanes=0);In|=f,e.lanes=f,e.memoizedState=F}}function Wc(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var o=zl.transition;zl.transition={};try{e(!1),t()}finally{je=n,zl.transition=o}}function cd(){return jt().memoizedState}function Ym(e,t,n){var o=xn(e);if(n={lane:o,action:n,hasEagerState:!1,eagerState:null,next:null},dd(e))fd(t,n);else if(n=Uc(e,t,n,o),n!==null){var a=st();Dt(n,e,o,a),pd(n,t,o)}}function Qm(e,t,n){var o=xn(e),a={lane:o,action:n,hasEagerState:!1,eagerState:null,next:null};if(dd(e))fd(t,a);else{var u=e.alternate;if(e.lanes===0&&(u===null||u.lanes===0)&&(u=t.lastRenderedReducer,u!==null))try{var f=t.lastRenderedState,g=u(f,n);if(a.hasEagerState=!0,a.eagerState=g,Nt(g,f)){var y=t.interleaved;y===null?(a.next=a,Nl(t)):(a.next=y.next,y.next=a),t.interleaved=a;return}}catch{}finally{}n=Uc(e,t,a,o),n!==null&&(a=st(),Dt(n,e,o,a),pd(n,t,o))}}function dd(e){var t=e.alternate;return e===Le||t!==null&&t===Le}function fd(e,t){ao=Ci=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function pd(e,t,n){if(n&4194240){var o=t.lanes;o&=e.pendingLanes,n|=o,t.lanes=n,qs(e,n)}}var Ai={readContext:Et,useCallback:Je,useContext:Je,useEffect:Je,useImperativeHandle:Je,useInsertionEffect:Je,useLayoutEffect:Je,useMemo:Je,useReducer:Je,useRef:Je,useState:Je,useDebugValue:Je,useDeferredValue:Je,useTransition:Je,useMutableSource:Je,useSyncExternalStore:Je,useId:Je,unstable_isNewReconciler:!1},Gm={readContext:Et,useCallback:function(e,t){return Ut().memoizedState=[e,t===void 0?null:t],e},useContext:Et,useEffect:nd,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Ei(4194308,4,id.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Ei(4194308,4,e,t)},useInsertionEffect:function(e,t){return Ei(4,2,e,t)},useMemo:function(e,t){var n=Ut();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var o=Ut();return t=n!==void 0?n(t):t,o.memoizedState=o.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},o.queue=e,e=e.dispatch=Ym.bind(null,Le,e),[o.memoizedState,e]},useRef:function(e){var t=Ut();return e={current:e},t.memoizedState=e},useState:ed,useDebugValue:Vl,useDeferredValue:function(e){return Ut().memoizedState=e},useTransition:function(){var e=ed(!1),t=e[0];return e=qm.bind(null,e[1]),Ut().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var o=Le,a=Ut();if(Ne){if(n===void 0)throw Error(s(407));n=n()}else{if(n=t(),We===null)throw Error(s(349));Ln&30||Gc(o,t,n)}a.memoizedState=n;var u={value:n,getSnapshot:t};return a.queue=u,nd(Xc.bind(null,o,u,e),[e]),o.flags|=2048,fo(9,Kc.bind(null,o,u,n,t),void 0,null),n},useId:function(){var e=Ut(),t=We.identifierPrefix;if(Ne){var n=Kt,o=Gt;n=(o&~(1<<32-_t(o)-1)).toString(32)+n,t=":"+t+"R"+n,n=uo++,0<\/script>",e=e.removeChild(e.firstChild)):typeof o.is=="string"?e=f.createElement(n,{is:o.is}):(e=f.createElement(n),n==="select"&&(f=e,o.multiple?f.multiple=!0:o.size&&(f.size=o.size))):e=f.createElementNS(e,n),e[Ft]=t,e[no]=o,Md(e,t,!1,!1),t.stateNode=e;e:{switch(f=Ds(n,o),n){case"dialog":Pe("cancel",e),Pe("close",e),a=o;break;case"iframe":case"object":case"embed":Pe("load",e),a=o;break;case"video":case"audio":for(a=0;ayr&&(t.flags|=128,o=!0,po(u,!1),t.lanes=4194304)}else{if(!o)if(e=Si(f),e!==null){if(t.flags|=128,o=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),po(u,!0),u.tail===null&&u.tailMode==="hidden"&&!f.alternate&&!Ne)return Ze(t),null}else 2*De()-u.renderingStartTime>yr&&n!==1073741824&&(t.flags|=128,o=!0,po(u,!1),t.lanes=4194304);u.isBackwards?(f.sibling=t.child,t.child=f):(n=u.last,n!==null?n.sibling=f:t.child=f,u.last=f)}return u.tail!==null?(t=u.tail,u.rendering=t,u.tail=t.sibling,u.renderingStartTime=De(),t.sibling=null,n=Me.current,Ae(Me,o?n&1|2:n&1),t):(Ze(t),null);case 22:case 23:return ha(),o=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==o&&(t.flags|=8192),o&&t.mode&1?yt&1073741824&&(Ze(t),t.subtreeFlags&6&&(t.flags|=8192)):Ze(t),null;case 24:return null;case 25:return null}throw Error(s(156,t.tag))}function rg(e,t){switch(Cl(t),t.tag){case 1:return at(t.type)&&ci(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return pr(),Te(lt),Te(Xe),Dl(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return Ll(t),null;case 13:if(Te(Me),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(s(340));ur()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return Te(Me),null;case 4:return pr(),null;case 10:return Tl(t.type._context),null;case 22:case 23:return ha(),null;case 24:return null;default:return null}}var _i=!1,et=!1,og=typeof WeakSet=="function"?WeakSet:Set,X=null;function mr(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(o){Ie(e,t,o)}else n.current=null}function na(e,t,n){try{n()}catch(o){Ie(e,t,o)}}var Dd=!1;function ig(e,t){if(hl=Ko,e=hc(),sl(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var o=n.getSelection&&n.getSelection();if(o&&o.rangeCount!==0){n=o.anchorNode;var a=o.anchorOffset,u=o.focusNode;o=o.focusOffset;try{n.nodeType,u.nodeType}catch{n=null;break e}var f=0,g=-1,y=-1,P=0,$=0,F=e,z=null;t:for(;;){for(var G;F!==n||a!==0&&F.nodeType!==3||(g=f+a),F!==u||o!==0&&F.nodeType!==3||(y=f+o),F.nodeType===3&&(f+=F.nodeValue.length),(G=F.firstChild)!==null;)z=F,F=G;for(;;){if(F===e)break t;if(z===n&&++P===a&&(g=f),z===u&&++$===o&&(y=f),(G=F.nextSibling)!==null)break;F=z,z=F.parentNode}F=G}n=g===-1||y===-1?null:{start:g,end:y}}else n=null}n=n||{start:0,end:0}}else n=null;for(ml={focusedElem:e,selectionRange:n},Ko=!1,X=t;X!==null;)if(t=X,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,X=e;else for(;X!==null;){t=X;try{var J=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(J!==null){var Z=J.memoizedProps,ze=J.memoizedState,E=t.stateNode,x=E.getSnapshotBeforeUpdate(t.elementType===t.type?Z:Mt(t.type,Z),ze);E.__reactInternalSnapshotBeforeUpdate=x}break;case 3:var A=t.stateNode.containerInfo;A.nodeType===1?A.textContent="":A.nodeType===9&&A.documentElement&&A.removeChild(A.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(s(163))}}catch(b){Ie(t,t.return,b)}if(e=t.sibling,e!==null){e.return=t.return,X=e;break}X=t.return}return J=Dd,Dd=!1,J}function ho(e,t,n){var o=t.updateQueue;if(o=o!==null?o.lastEffect:null,o!==null){var a=o=o.next;do{if((a.tag&e)===e){var u=a.destroy;a.destroy=void 0,u!==void 0&&na(t,n,u)}a=a.next}while(a!==o)}}function Ni(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var o=n.create;n.destroy=o()}n=n.next}while(n!==t)}}function ra(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function zd(e){var t=e.alternate;t!==null&&(e.alternate=null,zd(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Ft],delete t[no],delete t[xl],delete t[bm],delete t[Um])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function $d(e){return e.tag===5||e.tag===3||e.tag===4}function Bd(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||$d(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function oa(e,t,n){var o=e.tag;if(o===5||o===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=ai));else if(o!==4&&(e=e.child,e!==null))for(oa(e,t,n),e=e.sibling;e!==null;)oa(e,t,n),e=e.sibling}function ia(e,t,n){var o=e.tag;if(o===5||o===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(o!==4&&(e=e.child,e!==null))for(ia(e,t,n),e=e.sibling;e!==null;)ia(e,t,n),e=e.sibling}var Ge=null,Lt=!1;function gn(e,t,n){for(n=n.child;n!==null;)Fd(e,t,n),n=n.sibling}function Fd(e,t,n){if(Bt&&typeof Bt.onCommitFiberUnmount=="function")try{Bt.onCommitFiberUnmount(Vo,n)}catch{}switch(n.tag){case 5:et||mr(n,t);case 6:var o=Ge,a=Lt;Ge=null,gn(e,t,n),Ge=o,Lt=a,Ge!==null&&(Lt?(e=Ge,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):Ge.removeChild(n.stateNode));break;case 18:Ge!==null&&(Lt?(e=Ge,n=n.stateNode,e.nodeType===8?vl(e.parentNode,n):e.nodeType===1&&vl(e,n),Wr(e)):vl(Ge,n.stateNode));break;case 4:o=Ge,a=Lt,Ge=n.stateNode.containerInfo,Lt=!0,gn(e,t,n),Ge=o,Lt=a;break;case 0:case 11:case 14:case 15:if(!et&&(o=n.updateQueue,o!==null&&(o=o.lastEffect,o!==null))){a=o=o.next;do{var u=a,f=u.destroy;u=u.tag,f!==void 0&&(u&2||u&4)&&na(n,t,f),a=a.next}while(a!==o)}gn(e,t,n);break;case 1:if(!et&&(mr(n,t),o=n.stateNode,typeof o.componentWillUnmount=="function"))try{o.props=n.memoizedProps,o.state=n.memoizedState,o.componentWillUnmount()}catch(g){Ie(n,t,g)}gn(e,t,n);break;case 21:gn(e,t,n);break;case 22:n.mode&1?(et=(o=et)||n.memoizedState!==null,gn(e,t,n),et=o):gn(e,t,n);break;default:gn(e,t,n)}}function bd(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new og),t.forEach(function(o){var a=hg.bind(null,e,o);n.has(o)||(n.add(o),o.then(a,a))})}}function It(e,t){var n=t.deletions;if(n!==null)for(var o=0;oa&&(a=f),o&=~u}if(o=a,o=De()-o,o=(120>o?120:480>o?480:1080>o?1080:1920>o?1920:3e3>o?3e3:4320>o?4320:1960*lg(o/1960))-o,10e?16:e,vn===null)var o=!1;else{if(e=vn,vn=null,Di=0,xe&6)throw Error(s(331));var a=xe;for(xe|=4,X=e.current;X!==null;){var u=X,f=u.child;if(X.flags&16){var g=u.deletions;if(g!==null){for(var y=0;yDe()-aa?zn(e,0):la|=n),dt(e,t)}function ef(e,t){t===0&&(e.mode&1?(t=qo,qo<<=1,!(qo&130023424)&&(qo=4194304)):t=1);var n=st();e=Xt(e,t),e!==null&&(Fr(e,t,n),dt(e,n))}function pg(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),ef(e,n)}function hg(e,t){var n=0;switch(e.tag){case 13:var o=e.stateNode,a=e.memoizedState;a!==null&&(n=a.retryLane);break;case 19:o=e.stateNode;break;default:throw Error(s(314))}o!==null&&o.delete(t),ef(e,n)}var tf;tf=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||lt.current)ut=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return ut=!1,tg(e,t,n);ut=!!(e.flags&131072)}else ut=!1,Ne&&t.flags&1048576&&Lc(t,hi,t.index);switch(t.lanes=0,t.tag){case 2:var o=t.type;Ti(e,t),e=t.pendingProps;var a=sr(t,Xe.current);fr(t,n),a=Bl(null,t,o,e,a,n);var u=Fl();return t.flags|=1,typeof a=="object"&&a!==null&&typeof a.render=="function"&&a.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,at(o)?(u=!0,di(t)):u=!1,t.memoizedState=a.state!==null&&a.state!==void 0?a.state:null,Ol(t),a.updater=Ri,t.stateNode=a,a._reactInternals=t,ql(t,o,e,n),t=Kl(null,t,o,!0,u,n)):(t.tag=0,Ne&&u&&kl(t),it(null,t,a,n),t=t.child),t;case 16:o=t.elementType;e:{switch(Ti(e,t),e=t.pendingProps,a=o._init,o=a(o._payload),t.type=o,a=t.tag=gg(o),e=Mt(o,e),a){case 0:t=Gl(null,t,o,e,n);break e;case 1:t=Rd(null,t,o,e,n);break e;case 11:t=kd(null,t,o,e,n);break e;case 14:t=Cd(null,t,o,Mt(o.type,e),n);break e}throw Error(s(306,o,""))}return t;case 0:return o=t.type,a=t.pendingProps,a=t.elementType===o?a:Mt(o,a),Gl(e,t,o,a,n);case 1:return o=t.type,a=t.pendingProps,a=t.elementType===o?a:Mt(o,a),Rd(e,t,o,a,n);case 3:e:{if(Pd(t),e===null)throw Error(s(387));o=t.pendingProps,u=t.memoizedState,a=u.element,Hc(e,t),wi(t,o,null,n);var f=t.memoizedState;if(o=f.element,u.isDehydrated)if(u={element:o,isDehydrated:!1,cache:f.cache,pendingSuspenseBoundaries:f.pendingSuspenseBoundaries,transitions:f.transitions},t.updateQueue.baseState=u,t.memoizedState=u,t.flags&256){a=hr(Error(s(423)),t),t=Td(e,t,o,n,a);break e}else if(o!==a){a=hr(Error(s(424)),t),t=Td(e,t,o,n,a);break e}else for(gt=cn(t.stateNode.containerInfo.firstChild),mt=t,Ne=!0,Ot=null,n=bc(t,null,o,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(ur(),o===a){t=Zt(e,t,n);break e}it(e,t,o,n)}t=t.child}return t;case 5:return qc(t),e===null&&jl(t),o=t.type,a=t.pendingProps,u=e!==null?e.memoizedProps:null,f=a.children,gl(o,a)?f=null:u!==null&&gl(o,u)&&(t.flags|=32),Ad(e,t),it(e,t,f,n),t.child;case 6:return e===null&&jl(t),null;case 13:return _d(e,t,n);case 4:return Ml(t,t.stateNode.containerInfo),o=t.pendingProps,e===null?t.child=cr(t,null,o,n):it(e,t,o,n),t.child;case 11:return o=t.type,a=t.pendingProps,a=t.elementType===o?a:Mt(o,a),kd(e,t,o,a,n);case 7:return it(e,t,t.pendingProps,n),t.child;case 8:return it(e,t,t.pendingProps.children,n),t.child;case 12:return it(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(o=t.type._context,a=t.pendingProps,u=t.memoizedProps,f=a.value,Ae(yi,o._currentValue),o._currentValue=f,u!==null)if(Nt(u.value,f)){if(u.children===a.children&&!lt.current){t=Zt(e,t,n);break e}}else for(u=t.child,u!==null&&(u.return=t);u!==null;){var g=u.dependencies;if(g!==null){f=u.child;for(var y=g.firstContext;y!==null;){if(y.context===o){if(u.tag===1){y=Jt(-1,n&-n),y.tag=2;var P=u.updateQueue;if(P!==null){P=P.shared;var $=P.pending;$===null?y.next=y:(y.next=$.next,$.next=y),P.pending=y}}u.lanes|=n,y=u.alternate,y!==null&&(y.lanes|=n),_l(u.return,n,t),g.lanes|=n;break}y=y.next}}else if(u.tag===10)f=u.type===t.type?null:u.child;else if(u.tag===18){if(f=u.return,f===null)throw Error(s(341));f.lanes|=n,g=f.alternate,g!==null&&(g.lanes|=n),_l(f,n,t),f=u.sibling}else f=u.child;if(f!==null)f.return=u;else for(f=u;f!==null;){if(f===t){f=null;break}if(u=f.sibling,u!==null){u.return=f.return,f=u;break}f=f.return}u=f}it(e,t,a.children,n),t=t.child}return t;case 9:return a=t.type,o=t.pendingProps.children,fr(t,n),a=Et(a),o=o(a),t.flags|=1,it(e,t,o,n),t.child;case 14:return o=t.type,a=Mt(o,t.pendingProps),a=Mt(o.type,a),Cd(e,t,o,a,n);case 15:return Ed(e,t,t.type,t.pendingProps,n);case 17:return o=t.type,a=t.pendingProps,a=t.elementType===o?a:Mt(o,a),Ti(e,t),t.tag=1,at(o)?(e=!0,di(t)):e=!1,fr(t,n),md(t,o,a),ql(t,o,a,n),Kl(null,t,o,!0,e,n);case 19:return Od(e,t,n);case 22:return jd(e,t,n)}throw Error(s(156,t.tag))};function nf(e,t){return Iu(e,t)}function mg(e,t,n,o){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=o,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Rt(e,t,n,o){return new mg(e,t,n,o)}function ga(e){return e=e.prototype,!(!e||!e.isReactComponent)}function gg(e){if(typeof e=="function")return ga(e)?1:0;if(e!=null){if(e=e.$$typeof,e===ne)return 11;if(e===Re)return 14}return 2}function Sn(e,t){var n=e.alternate;return n===null?(n=Rt(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Fi(e,t,n,o,a,u){var f=2;if(o=e,typeof e=="function")ga(e)&&(f=1);else if(typeof e=="string")f=5;else e:switch(e){case H:return Bn(n.children,a,u,t);case ie:f=8,a|=8;break;case ve:return e=Rt(12,n,t,a|2),e.elementType=ve,e.lanes=u,e;case le:return e=Rt(13,n,t,a),e.elementType=le,e.lanes=u,e;case me:return e=Rt(19,n,t,a),e.elementType=me,e.lanes=u,e;case Ee:return bi(n,a,u,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Oe:f=10;break e;case ot:f=9;break e;case ne:f=11;break e;case Re:f=14;break e;case ge:f=16,o=null;break e}throw Error(s(130,e==null?e:typeof e,""))}return t=Rt(f,n,t,a),t.elementType=e,t.type=o,t.lanes=u,t}function Bn(e,t,n,o){return e=Rt(7,e,o,t),e.lanes=n,e}function bi(e,t,n,o){return e=Rt(22,e,o,t),e.elementType=Ee,e.lanes=n,e.stateNode={isHidden:!1},e}function ya(e,t,n){return e=Rt(6,e,null,t),e.lanes=n,e}function va(e,t,n){return t=Rt(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function yg(e,t,n,o,a){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Ws(0),this.expirationTimes=Ws(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Ws(0),this.identifierPrefix=o,this.onRecoverableError=a,this.mutableSourceEagerHydrationData=null}function xa(e,t,n,o,a,u,f,g,y){return e=new yg(e,t,n,g,y),t===1?(t=1,u===!0&&(t|=8)):t=0,u=Rt(3,null,null,t),e.current=u,u.stateNode=e,u.memoizedState={element:o,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Ol(u),e}function vg(e,t,n){var o=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(r)}catch(i){console.error(i)}}return r(),ja.exports=Ng(),ja.exports}var vf;function Mg(){if(vf)return Qi;vf=1;var r=Og();return Qi.createRoot=r.createRoot,Qi.hydrateRoot=r.hydrateRoot,Qi}var Lg=Mg(),nt=function(){return nt=Object.assign||function(i){for(var s,l=1,c=arguments.length;l0?Ye(Pr,--Pt):0,Er--,be===10&&(Er=1,xs--),be}function zt(){return be=Pt2||Ha(be)>3?"":" "}function Vg(r,i){for(;--i&&zt()&&!(be<48||be>102||be>57&&be<65||be>70&&be<97););return Ss(r,os()+(i<6&&Un()==32&&zt()==32))}function Va(r){for(;zt();)switch(be){case r:return Pt;case 34:case 39:r!==34&&r!==39&&Va(be);break;case 40:r===41&&Va(r);break;case 92:zt();break}return Pt}function Wg(r,i){for(;zt()&&r+be!==57;)if(r+be===84&&Un()===47)break;return"/*"+Ss(i,Pt-1)+"*"+su(r===47?r:zt())}function qg(r){for(;!Ha(Un());)zt();return Ss(r,Pt)}function Yg(r){return Ug(is("",null,null,null,[""],r=bg(r),0,[0],r))}function is(r,i,s,l,c,d,p,m,w){for(var v=0,S=0,j=p,R=0,L=0,T=0,N=1,_=1,V=1,U=0,B="",W=c,I=d,M=l,H=B;_;)switch(T=U,U=zt()){case 40:if(T!=108&&Ye(H,j-1)==58){rs(H+=de(Pa(U),"&","&\f"),"&\f",vp(v?m[v-1]:0))!=-1&&(V=-1);break}case 34:case 39:case 91:H+=Pa(U);break;case 9:case 10:case 13:case 32:H+=Hg(T);break;case 92:H+=Vg(os()-1,7);continue;case 47:switch(Un()){case 42:case 47:jo(Qg(Wg(zt(),os()),i,s,w),w);break;default:H+="/"}break;case 123*N:m[v++]=Wt(H)*V;case 125*N:case 59:case 0:switch(U){case 0:case 125:_=0;case 59+S:V==-1&&(H=de(H,/\f/g,"")),L>0&&Wt(H)-j&&jo(L>32?Sf(H+";",l,s,j-1,w):Sf(de(H," ","")+";",l,s,j-2,w),w);break;case 59:H+=";";default:if(jo(M=wf(H,i,s,v,S,c,m,B,W=[],I=[],j,d),d),U===123)if(S===0)is(H,i,M,M,W,d,j,m,I);else switch(R===99&&Ye(H,3)===110?100:R){case 100:case 108:case 109:case 115:is(r,M,M,l&&jo(wf(r,M,M,0,0,c,m,B,c,W=[],j,I),I),c,I,j,m,l?W:I);break;default:is(H,M,M,M,[""],I,0,m,I)}}v=S=L=0,N=V=1,B=H="",j=p;break;case 58:j=1+Wt(H),L=T;default:if(N<1){if(U==123)--N;else if(U==125&&N++==0&&Fg()==125)continue}switch(H+=su(U),U*N){case 38:V=S>0?1:(H+="\f",-1);break;case 44:m[v++]=(Wt(H)-1)*V,V=1;break;case 64:Un()===45&&(H+=Pa(zt())),R=Un(),S=j=Wt(B=H+=qg(os())),U++;break;case 45:T===45&&Wt(H)==2&&(N=0)}}return d}function wf(r,i,s,l,c,d,p,m,w,v,S,j){for(var R=c-1,L=c===0?d:[""],T=wp(L),N=0,_=0,V=0;N0?L[U]+" "+B:de(B,/&\f/g,L[U])))&&(w[V++]=W);return ws(r,i,s,c===0?vs:m,w,v,S,j)}function Qg(r,i,s,l){return ws(r,i,s,gp,su(Bg()),Cr(r,2,-2),0,l)}function Sf(r,i,s,l,c){return ws(r,i,s,iu,Cr(r,0,l),Cr(r,l+1,-1),l,c)}function kp(r,i,s){switch(zg(r,i)){case 5103:return Ce+"print-"+r+r;case 5737:case 4201:case 3177:case 3433:case 1641:case 4457:case 2921:case 5572:case 6356:case 5844:case 3191:case 6645:case 3005:case 6391:case 5879:case 5623:case 6135:case 4599:case 4855:case 4215:case 6389:case 5109:case 5365:case 5621:case 3829:return Ce+r+r;case 4789:return Ao+r+r;case 5349:case 4246:case 4810:case 6968:case 2756:return Ce+r+Ao+r+_e+r+r;case 5936:switch(Ye(r,i+11)){case 114:return Ce+r+_e+de(r,/[svh]\w+-[tblr]{2}/,"tb")+r;case 108:return Ce+r+_e+de(r,/[svh]\w+-[tblr]{2}/,"tb-rl")+r;case 45:return Ce+r+_e+de(r,/[svh]\w+-[tblr]{2}/,"lr")+r}case 6828:case 4268:case 2903:return Ce+r+_e+r+r;case 6165:return Ce+r+_e+"flex-"+r+r;case 5187:return Ce+r+de(r,/(\w+).+(:[^]+)/,Ce+"box-$1$2"+_e+"flex-$1$2")+r;case 5443:return Ce+r+_e+"flex-item-"+de(r,/flex-|-self/g,"")+(tn(r,/flex-|baseline/)?"":_e+"grid-row-"+de(r,/flex-|-self/g,""))+r;case 4675:return Ce+r+_e+"flex-line-pack"+de(r,/align-content|flex-|-self/g,"")+r;case 5548:return Ce+r+_e+de(r,"shrink","negative")+r;case 5292:return Ce+r+_e+de(r,"basis","preferred-size")+r;case 6060:return Ce+"box-"+de(r,"-grow","")+Ce+r+_e+de(r,"grow","positive")+r;case 4554:return Ce+de(r,/([^-])(transform)/g,"$1"+Ce+"$2")+r;case 6187:return de(de(de(r,/(zoom-|grab)/,Ce+"$1"),/(image-set)/,Ce+"$1"),r,"")+r;case 5495:case 3959:return de(r,/(image-set\([^]*)/,Ce+"$1$`$1");case 4968:return de(de(r,/(.+:)(flex-)?(.*)/,Ce+"box-pack:$3"+_e+"flex-pack:$3"),/s.+-b[^;]+/,"justify")+Ce+r+r;case 4200:if(!tn(r,/flex-|baseline/))return _e+"grid-column-align"+Cr(r,i)+r;break;case 2592:case 3360:return _e+de(r,"template-","")+r;case 4384:case 3616:return s&&s.some(function(l,c){return i=c,tn(l.props,/grid-\w+-end/)})?~rs(r+(s=s[i].value),"span",0)?r:_e+de(r,"-start","")+r+_e+"grid-row-span:"+(~rs(s,"span",0)?tn(s,/\d+/):+tn(s,/\d+/)-+tn(r,/\d+/))+";":_e+de(r,"-start","")+r;case 4896:case 4128:return s&&s.some(function(l){return tn(l.props,/grid-\w+-start/)})?r:_e+de(de(r,"-end","-span"),"span ","")+r;case 4095:case 3583:case 4068:case 2532:return de(r,/(.+)-inline(.+)/,Ce+"$1$2")+r;case 8116:case 7059:case 5753:case 5535:case 5445:case 5701:case 4933:case 4677:case 5533:case 5789:case 5021:case 4765:if(Wt(r)-1-i>6)switch(Ye(r,i+1)){case 109:if(Ye(r,i+4)!==45)break;case 102:return de(r,/(.+:)(.+)-([^]+)/,"$1"+Ce+"$2-$3$1"+Ao+(Ye(r,i+3)==108?"$3":"$2-$3"))+r;case 115:return~rs(r,"stretch",0)?kp(de(r,"stretch","fill-available"),i,s)+r:r}break;case 5152:case 5920:return de(r,/(.+?):(\d+)(\s*\/\s*(span)?\s*(\d+))?(.*)/,function(l,c,d,p,m,w,v){return _e+c+":"+d+v+(p?_e+c+"-span:"+(m?w:+w-+d)+v:"")+r});case 4949:if(Ye(r,i+6)===121)return de(r,":",":"+Ce)+r;break;case 6444:switch(Ye(r,Ye(r,14)===45?18:11)){case 120:return de(r,/(.+:)([^;\s!]+)(;|(\s+)?!.+)?/,"$1"+Ce+(Ye(r,14)===45?"inline-":"")+"box$3$1"+Ce+"$2$3$1"+_e+"$2box$3")+r;case 100:return de(r,":",":"+_e)+r}break;case 5719:case 2647:case 2135:case 3927:case 2391:return de(r,"scroll-","scroll-snap-")+r}return r}function fs(r,i){for(var s="",l=0;l-1&&!r.return)switch(r.type){case iu:r.return=kp(r.value,r.length,s);return;case yp:return fs([Cn(r,{value:de(r.value,"@","@"+Ce)})],l);case vs:if(r.length)return $g(s=r.props,function(c){switch(tn(c,l=/(::plac\w+|:read-\w+)/)){case":read-only":case":read-write":xr(Cn(r,{props:[de(c,/:(read-\w+)/,":"+Ao+"$1")]})),xr(Cn(r,{props:[c]})),Ua(r,{props:xf(s,l)});break;case"::placeholder":xr(Cn(r,{props:[de(c,/:(plac\w+)/,":"+Ce+"input-$1")]})),xr(Cn(r,{props:[de(c,/:(plac\w+)/,":"+Ao+"$1")]})),xr(Cn(r,{props:[de(c,/:(plac\w+)/,_e+"input-$1")]})),xr(Cn(r,{props:[c]})),Ua(r,{props:xf(s,l)});break}return""})}}var Zg={animationIterationCount:1,aspectRatio:1,borderImageOutset:1,borderImageSlice:1,borderImageWidth:1,boxFlex:1,boxFlexGroup:1,boxOrdinalGroup:1,columnCount:1,columns:1,flex:1,flexGrow:1,flexPositive:1,flexShrink:1,flexNegative:1,flexOrder:1,gridRow:1,gridRowEnd:1,gridRowSpan:1,gridRowStart:1,gridColumn:1,gridColumnEnd:1,gridColumnSpan:1,gridColumnStart:1,msGridRow:1,msGridRowSpan:1,msGridColumn:1,msGridColumnSpan:1,fontWeight:1,lineHeight:1,opacity:1,order:1,orphans:1,tabSize:1,widows:1,zIndex:1,zoom:1,WebkitLineClamp:1,fillOpacity:1,floodOpacity:1,stopOpacity:1,strokeDasharray:1,strokeDashoffset:1,strokeMiterlimit:1,strokeOpacity:1,strokeWidth:1},vt={},jr=typeof process<"u"&&vt!==void 0&&(vt.REACT_APP_SC_ATTR||vt.SC_ATTR)||"data-styled",Cp="active",Ep="data-styled-version",ks="6.1.14",lu=`/*!sc*/ +`,ps=typeof window<"u"&&"HTMLElement"in window,ey=!!(typeof SC_DISABLE_SPEEDY=="boolean"?SC_DISABLE_SPEEDY:typeof process<"u"&&vt!==void 0&&vt.REACT_APP_SC_DISABLE_SPEEDY!==void 0&&vt.REACT_APP_SC_DISABLE_SPEEDY!==""?vt.REACT_APP_SC_DISABLE_SPEEDY!=="false"&&vt.REACT_APP_SC_DISABLE_SPEEDY:typeof process<"u"&&vt!==void 0&&vt.SC_DISABLE_SPEEDY!==void 0&&vt.SC_DISABLE_SPEEDY!==""&&vt.SC_DISABLE_SPEEDY!=="false"&&vt.SC_DISABLE_SPEEDY),Cs=Object.freeze([]),Ar=Object.freeze({});function ty(r,i,s){return s===void 0&&(s=Ar),r.theme!==s.theme&&r.theme||i||s.theme}var jp=new Set(["a","abbr","address","area","article","aside","audio","b","base","bdi","bdo","big","blockquote","body","br","button","canvas","caption","cite","code","col","colgroup","data","datalist","dd","del","details","dfn","dialog","div","dl","dt","em","embed","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","hr","html","i","iframe","img","input","ins","kbd","keygen","label","legend","li","link","main","map","mark","menu","menuitem","meta","meter","nav","noscript","object","ol","optgroup","option","output","p","param","picture","pre","progress","q","rp","rt","ruby","s","samp","script","section","select","small","source","span","strong","style","sub","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","track","u","ul","use","var","video","wbr","circle","clipPath","defs","ellipse","foreignObject","g","image","line","linearGradient","marker","mask","path","pattern","polygon","polyline","radialGradient","rect","stop","svg","text","tspan"]),ny=/[!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~-]+/g,ry=/(^-|-$)/g;function kf(r){return r.replace(ny,"-").replace(ry,"")}var oy=/(a)(d)/gi,Gi=52,Cf=function(r){return String.fromCharCode(r+(r>25?39:97))};function Wa(r){var i,s="";for(i=Math.abs(r);i>Gi;i=i/Gi|0)s=Cf(i%Gi)+s;return(Cf(i%Gi)+s).replace(oy,"$1-$2")}var Ta,Ap=5381,wr=function(r,i){for(var s=i.length;s;)r=33*r^i.charCodeAt(--s);return r},Rp=function(r){return wr(Ap,r)};function iy(r){return Wa(Rp(r)>>>0)}function sy(r){return r.displayName||r.name||"Component"}function _a(r){return typeof r=="string"&&!0}var Pp=typeof Symbol=="function"&&Symbol.for,Tp=Pp?Symbol.for("react.memo"):60115,ly=Pp?Symbol.for("react.forward_ref"):60112,ay={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},uy={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},_p={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},cy=((Ta={})[ly]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},Ta[Tp]=_p,Ta);function Ef(r){return("type"in(i=r)&&i.type.$$typeof)===Tp?_p:"$$typeof"in r?cy[r.$$typeof]:ay;var i}var dy=Object.defineProperty,fy=Object.getOwnPropertyNames,jf=Object.getOwnPropertySymbols,py=Object.getOwnPropertyDescriptor,hy=Object.getPrototypeOf,Af=Object.prototype;function Np(r,i,s){if(typeof i!="string"){if(Af){var l=hy(i);l&&l!==Af&&Np(r,l,s)}var c=fy(i);jf&&(c=c.concat(jf(i)));for(var d=Ef(r),p=Ef(i),m=0;m0?" Args: ".concat(i.join(", ")):""))}var my=function(){function r(i){this.groupSizes=new Uint32Array(512),this.length=512,this.tag=i}return r.prototype.indexOfGroup=function(i){for(var s=0,l=0;l=this.groupSizes.length){for(var l=this.groupSizes,c=l.length,d=c;i>=d;)if((d<<=1)<0)throw qn(16,"".concat(i));this.groupSizes=new Uint32Array(d),this.groupSizes.set(l),this.length=d;for(var p=c;p=this.length||this.groupSizes[i]===0)return s;for(var l=this.groupSizes[i],c=this.indexOfGroup(i),d=c+l,p=c;p=0){var l=document.createTextNode(s);return this.element.insertBefore(l,this.nodes[i]||null),this.length++,!0}return!1},r.prototype.deleteRule=function(i){this.element.removeChild(this.nodes[i]),this.length--},r.prototype.getRule=function(i){return i0&&(_+="".concat(V,","))}),w+="".concat(T).concat(N,'{content:"').concat(_,'"}').concat(lu)},S=0;S0?".".concat(i):R},S=w.slice();S.push(function(R){R.type===vs&&R.value.includes("&")&&(R.props[0]=R.props[0].replace(Ay,s).replace(l,v))}),p.prefix&&S.push(Jg),S.push(Gg);var j=function(R,L,T,N){L===void 0&&(L=""),T===void 0&&(T=""),N===void 0&&(N="&"),i=N,s=L,l=new RegExp("\\".concat(s,"\\b"),"g");var _=R.replace(Ry,""),V=Yg(T||L?"".concat(T," ").concat(L," { ").concat(_," }"):_);p.namespace&&(V=Lp(V,p.namespace));var U=[];return fs(V,Kg(S.concat(Xg(function(B){return U.push(B)})))),U};return j.hash=w.length?w.reduce(function(R,L){return L.name||qn(15),wr(R,L.name)},Ap).toString():"",j}var Ty=new Mp,Ya=Py(),Ip=xt.createContext({shouldForwardProp:void 0,styleSheet:Ty,stylis:Ya});Ip.Consumer;xt.createContext(void 0);function _f(){return K.useContext(Ip)}var _y=function(){function r(i,s){var l=this;this.inject=function(c,d){d===void 0&&(d=Ya);var p=l.name+d.hash;c.hasNameForId(l.id,p)||c.insertRules(l.id,p,d(l.rules,p,"@keyframes"))},this.name=i,this.id="sc-keyframes-".concat(i),this.rules=s,uu(this,function(){throw qn(12,String(l.name))})}return r.prototype.getName=function(i){return i===void 0&&(i=Ya),this.name+i.hash},r}(),Ny=function(r){return r>="A"&&r<="Z"};function Nf(r){for(var i="",s=0;s>>0);if(!s.hasNameForId(this.componentId,p)){var m=l(d,".".concat(p),void 0,this.componentId);s.insertRules(this.componentId,p,m)}c=Fn(c,p),this.staticRulesId=p}else{for(var w=wr(this.baseHash,l.hash),v="",S=0;S>>0);s.hasNameForId(this.componentId,L)||s.insertRules(this.componentId,L,l(v,".".concat(L),void 0,this.componentId)),c=Fn(c,L)}}return c},r}(),ms=xt.createContext(void 0);ms.Consumer;function Of(r){var i=xt.useContext(ms),s=K.useMemo(function(){return function(l,c){if(!l)throw qn(14);if(Wn(l)){var d=l(c);return d}if(Array.isArray(l)||typeof l!="object")throw qn(8);return c?nt(nt({},c),l):l}(r.theme,i)},[r.theme,i]);return r.children?xt.createElement(ms.Provider,{value:s},r.children):null}var Na={};function Iy(r,i,s){var l=au(r),c=r,d=!_a(r),p=i.attrs,m=p===void 0?Cs:p,w=i.componentId,v=w===void 0?function(W,I){var M=typeof W!="string"?"sc":kf(W);Na[M]=(Na[M]||0)+1;var H="".concat(M,"-").concat(iy(ks+M+Na[M]));return I?"".concat(I,"-").concat(H):H}(i.displayName,i.parentComponentId):w,S=i.displayName,j=S===void 0?function(W){return _a(W)?"styled.".concat(W):"Styled(".concat(sy(W),")")}(r):S,R=i.displayName&&i.componentId?"".concat(kf(i.displayName),"-").concat(i.componentId):i.componentId||v,L=l&&c.attrs?c.attrs.concat(m).filter(Boolean):m,T=i.shouldForwardProp;if(l&&c.shouldForwardProp){var N=c.shouldForwardProp;if(i.shouldForwardProp){var _=i.shouldForwardProp;T=function(W,I){return N(W,I)&&_(W,I)}}else T=N}var V=new Ly(s,R,l?c.componentStyle:void 0);function U(W,I){return function(M,H,ie){var ve=M.attrs,Oe=M.componentStyle,ot=M.defaultProps,ne=M.foldedComponentIds,le=M.styledComponentId,me=M.target,Re=xt.useContext(ms),ge=_f(),Ee=M.shouldForwardProp||ge.shouldForwardProp,q=ty(H,Re,ot)||Ar,ee=function(he,fe,ke){for(var ye,we=nt(nt({},fe),{className:void 0,theme:ke}),Qe=0;Qe{let i;const s=new Set,l=(v,S)=>{const j=typeof v=="function"?v(i):v;if(!Object.is(j,i)){const R=i;i=S??(typeof j!="object"||j===null)?j:Object.assign({},i,j),s.forEach(L=>L(i,R))}},c=()=>i,m={setState:l,getState:c,getInitialState:()=>w,subscribe:v=>(s.add(v),()=>s.delete(v))},w=i=r(l,c,m);return m},zy=r=>r?If(r):If,$y=r=>r;function By(r,i=$y){const s=xt.useSyncExternalStore(r.subscribe,()=>i(r.getState()),()=>i(r.getInitialState()));return xt.useDebugValue(s),s}const Df=r=>{const i=zy(r),s=l=>By(i,l);return Object.assign(s,i),s},Tr=r=>r?Df(r):Df;function Bp(r,i){return function(){return r.apply(i,arguments)}}const{toString:Fy}=Object.prototype,{getPrototypeOf:cu}=Object,Es=(r=>i=>{const s=Fy.call(i);return r[s]||(r[s]=s.slice(8,-1).toLowerCase())})(Object.create(null)),$t=r=>(r=r.toLowerCase(),i=>Es(i)===r),js=r=>i=>typeof i===r,{isArray:_r}=Array,Mo=js("undefined");function by(r){return r!==null&&!Mo(r)&&r.constructor!==null&&!Mo(r.constructor)&&wt(r.constructor.isBuffer)&&r.constructor.isBuffer(r)}const Fp=$t("ArrayBuffer");function Uy(r){let i;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?i=ArrayBuffer.isView(r):i=r&&r.buffer&&Fp(r.buffer),i}const Hy=js("string"),wt=js("function"),bp=js("number"),As=r=>r!==null&&typeof r=="object",Vy=r=>r===!0||r===!1,as=r=>{if(Es(r)!=="object")return!1;const i=cu(r);return(i===null||i===Object.prototype||Object.getPrototypeOf(i)===null)&&!(Symbol.toStringTag in r)&&!(Symbol.iterator in r)},Wy=$t("Date"),qy=$t("File"),Yy=$t("Blob"),Qy=$t("FileList"),Gy=r=>As(r)&&wt(r.pipe),Ky=r=>{let i;return r&&(typeof FormData=="function"&&r instanceof FormData||wt(r.append)&&((i=Es(r))==="formdata"||i==="object"&&wt(r.toString)&&r.toString()==="[object FormData]"))},Xy=$t("URLSearchParams"),[Jy,Zy,e0,t0]=["ReadableStream","Request","Response","Headers"].map($t),n0=r=>r.trim?r.trim():r.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function Io(r,i,{allOwnKeys:s=!1}={}){if(r===null||typeof r>"u")return;let l,c;if(typeof r!="object"&&(r=[r]),_r(r))for(l=0,c=r.length;l0;)if(c=s[l],i===c.toLowerCase())return c;return null}const bn=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global,Hp=r=>!Mo(r)&&r!==bn;function Ga(){const{caseless:r}=Hp(this)&&this||{},i={},s=(l,c)=>{const d=r&&Up(i,c)||c;as(i[d])&&as(l)?i[d]=Ga(i[d],l):as(l)?i[d]=Ga({},l):_r(l)?i[d]=l.slice():i[d]=l};for(let l=0,c=arguments.length;l(Io(i,(c,d)=>{s&&wt(c)?r[d]=Bp(c,s):r[d]=c},{allOwnKeys:l}),r),o0=r=>(r.charCodeAt(0)===65279&&(r=r.slice(1)),r),i0=(r,i,s,l)=>{r.prototype=Object.create(i.prototype,l),r.prototype.constructor=r,Object.defineProperty(r,"super",{value:i.prototype}),s&&Object.assign(r.prototype,s)},s0=(r,i,s,l)=>{let c,d,p;const m={};if(i=i||{},r==null)return i;do{for(c=Object.getOwnPropertyNames(r),d=c.length;d-- >0;)p=c[d],(!l||l(p,r,i))&&!m[p]&&(i[p]=r[p],m[p]=!0);r=s!==!1&&cu(r)}while(r&&(!s||s(r,i))&&r!==Object.prototype);return i},l0=(r,i,s)=>{r=String(r),(s===void 0||s>r.length)&&(s=r.length),s-=i.length;const l=r.indexOf(i,s);return l!==-1&&l===s},a0=r=>{if(!r)return null;if(_r(r))return r;let i=r.length;if(!bp(i))return null;const s=new Array(i);for(;i-- >0;)s[i]=r[i];return s},u0=(r=>i=>r&&i instanceof r)(typeof Uint8Array<"u"&&cu(Uint8Array)),c0=(r,i)=>{const l=(r&&r[Symbol.iterator]).call(r);let c;for(;(c=l.next())&&!c.done;){const d=c.value;i.call(r,d[0],d[1])}},d0=(r,i)=>{let s;const l=[];for(;(s=r.exec(i))!==null;)l.push(s);return l},f0=$t("HTMLFormElement"),p0=r=>r.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(s,l,c){return l.toUpperCase()+c}),zf=(({hasOwnProperty:r})=>(i,s)=>r.call(i,s))(Object.prototype),h0=$t("RegExp"),Vp=(r,i)=>{const s=Object.getOwnPropertyDescriptors(r),l={};Io(s,(c,d)=>{let p;(p=i(c,d,r))!==!1&&(l[d]=p||c)}),Object.defineProperties(r,l)},m0=r=>{Vp(r,(i,s)=>{if(wt(r)&&["arguments","caller","callee"].indexOf(s)!==-1)return!1;const l=r[s];if(wt(l)){if(i.enumerable=!1,"writable"in i){i.writable=!1;return}i.set||(i.set=()=>{throw Error("Can not rewrite read-only method '"+s+"'")})}})},g0=(r,i)=>{const s={},l=c=>{c.forEach(d=>{s[d]=!0})};return _r(r)?l(r):l(String(r).split(i)),s},y0=()=>{},v0=(r,i)=>r!=null&&Number.isFinite(r=+r)?r:i,Oa="abcdefghijklmnopqrstuvwxyz",$f="0123456789",Wp={DIGIT:$f,ALPHA:Oa,ALPHA_DIGIT:Oa+Oa.toUpperCase()+$f},x0=(r=16,i=Wp.ALPHA_DIGIT)=>{let s="";const{length:l}=i;for(;r--;)s+=i[Math.random()*l|0];return s};function w0(r){return!!(r&&wt(r.append)&&r[Symbol.toStringTag]==="FormData"&&r[Symbol.iterator])}const S0=r=>{const i=new Array(10),s=(l,c)=>{if(As(l)){if(i.indexOf(l)>=0)return;if(!("toJSON"in l)){i[c]=l;const d=_r(l)?[]:{};return Io(l,(p,m)=>{const w=s(p,c+1);!Mo(w)&&(d[m]=w)}),i[c]=void 0,d}}return l};return s(r,0)},k0=$t("AsyncFunction"),C0=r=>r&&(As(r)||wt(r))&&wt(r.then)&&wt(r.catch),qp=((r,i)=>r?setImmediate:i?((s,l)=>(bn.addEventListener("message",({source:c,data:d})=>{c===bn&&d===s&&l.length&&l.shift()()},!1),c=>{l.push(c),bn.postMessage(s,"*")}))(`axios@${Math.random()}`,[]):s=>setTimeout(s))(typeof setImmediate=="function",wt(bn.postMessage)),E0=typeof queueMicrotask<"u"?queueMicrotask.bind(bn):typeof process<"u"&&process.nextTick||qp,O={isArray:_r,isArrayBuffer:Fp,isBuffer:by,isFormData:Ky,isArrayBufferView:Uy,isString:Hy,isNumber:bp,isBoolean:Vy,isObject:As,isPlainObject:as,isReadableStream:Jy,isRequest:Zy,isResponse:e0,isHeaders:t0,isUndefined:Mo,isDate:Wy,isFile:qy,isBlob:Yy,isRegExp:h0,isFunction:wt,isStream:Gy,isURLSearchParams:Xy,isTypedArray:u0,isFileList:Qy,forEach:Io,merge:Ga,extend:r0,trim:n0,stripBOM:o0,inherits:i0,toFlatObject:s0,kindOf:Es,kindOfTest:$t,endsWith:l0,toArray:a0,forEachEntry:c0,matchAll:d0,isHTMLForm:f0,hasOwnProperty:zf,hasOwnProp:zf,reduceDescriptors:Vp,freezeMethods:m0,toObjectSet:g0,toCamelCase:p0,noop:y0,toFiniteNumber:v0,findKey:Up,global:bn,isContextDefined:Hp,ALPHABET:Wp,generateString:x0,isSpecCompliantForm:w0,toJSONObject:S0,isAsyncFn:k0,isThenable:C0,setImmediate:qp,asap:E0};function ue(r,i,s,l,c){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack,this.message=r,this.name="AxiosError",i&&(this.code=i),s&&(this.config=s),l&&(this.request=l),c&&(this.response=c,this.status=c.status?c.status:null)}O.inherits(ue,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:O.toJSONObject(this.config),code:this.code,status:this.status}}});const Yp=ue.prototype,Qp={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach(r=>{Qp[r]={value:r}});Object.defineProperties(ue,Qp);Object.defineProperty(Yp,"isAxiosError",{value:!0});ue.from=(r,i,s,l,c,d)=>{const p=Object.create(Yp);return O.toFlatObject(r,p,function(w){return w!==Error.prototype},m=>m!=="isAxiosError"),ue.call(p,r.message,i,s,l,c),p.cause=r,p.name=r.name,d&&Object.assign(p,d),p};const j0=null;function Ka(r){return O.isPlainObject(r)||O.isArray(r)}function Gp(r){return O.endsWith(r,"[]")?r.slice(0,-2):r}function Bf(r,i,s){return r?r.concat(i).map(function(c,d){return c=Gp(c),!s&&d?"["+c+"]":c}).join(s?".":""):i}function A0(r){return O.isArray(r)&&!r.some(Ka)}const R0=O.toFlatObject(O,{},null,function(i){return/^is[A-Z]/.test(i)});function Rs(r,i,s){if(!O.isObject(r))throw new TypeError("target must be an object");i=i||new FormData,s=O.toFlatObject(s,{metaTokens:!0,dots:!1,indexes:!1},!1,function(N,_){return!O.isUndefined(_[N])});const l=s.metaTokens,c=s.visitor||S,d=s.dots,p=s.indexes,w=(s.Blob||typeof Blob<"u"&&Blob)&&O.isSpecCompliantForm(i);if(!O.isFunction(c))throw new TypeError("visitor must be a function");function v(T){if(T===null)return"";if(O.isDate(T))return T.toISOString();if(!w&&O.isBlob(T))throw new ue("Blob is not supported. Use a Buffer instead.");return O.isArrayBuffer(T)||O.isTypedArray(T)?w&&typeof Blob=="function"?new Blob([T]):Buffer.from(T):T}function S(T,N,_){let V=T;if(T&&!_&&typeof T=="object"){if(O.endsWith(N,"{}"))N=l?N:N.slice(0,-2),T=JSON.stringify(T);else if(O.isArray(T)&&A0(T)||(O.isFileList(T)||O.endsWith(N,"[]"))&&(V=O.toArray(T)))return N=Gp(N),V.forEach(function(B,W){!(O.isUndefined(B)||B===null)&&i.append(p===!0?Bf([N],W,d):p===null?N:N+"[]",v(B))}),!1}return Ka(T)?!0:(i.append(Bf(_,N,d),v(T)),!1)}const j=[],R=Object.assign(R0,{defaultVisitor:S,convertValue:v,isVisitable:Ka});function L(T,N){if(!O.isUndefined(T)){if(j.indexOf(T)!==-1)throw Error("Circular reference detected in "+N.join("."));j.push(T),O.forEach(T,function(V,U){(!(O.isUndefined(V)||V===null)&&c.call(i,V,O.isString(U)?U.trim():U,N,R))===!0&&L(V,N?N.concat(U):[U])}),j.pop()}}if(!O.isObject(r))throw new TypeError("data must be an object");return L(r),i}function Ff(r){const i={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(r).replace(/[!'()~]|%20|%00/g,function(l){return i[l]})}function du(r,i){this._pairs=[],r&&Rs(r,this,i)}const Kp=du.prototype;Kp.append=function(i,s){this._pairs.push([i,s])};Kp.toString=function(i){const s=i?function(l){return i.call(this,l,Ff)}:Ff;return this._pairs.map(function(c){return s(c[0])+"="+s(c[1])},"").join("&")};function P0(r){return encodeURIComponent(r).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function Xp(r,i,s){if(!i)return r;const l=s&&s.encode||P0;O.isFunction(s)&&(s={serialize:s});const c=s&&s.serialize;let d;if(c?d=c(i,s):d=O.isURLSearchParams(i)?i.toString():new du(i,s).toString(l),d){const p=r.indexOf("#");p!==-1&&(r=r.slice(0,p)),r+=(r.indexOf("?")===-1?"?":"&")+d}return r}class bf{constructor(){this.handlers=[]}use(i,s,l){return this.handlers.push({fulfilled:i,rejected:s,synchronous:l?l.synchronous:!1,runWhen:l?l.runWhen:null}),this.handlers.length-1}eject(i){this.handlers[i]&&(this.handlers[i]=null)}clear(){this.handlers&&(this.handlers=[])}forEach(i){O.forEach(this.handlers,function(l){l!==null&&i(l)})}}const Jp={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},T0=typeof URLSearchParams<"u"?URLSearchParams:du,_0=typeof FormData<"u"?FormData:null,N0=typeof Blob<"u"?Blob:null,O0={isBrowser:!0,classes:{URLSearchParams:T0,FormData:_0,Blob:N0},protocols:["http","https","file","blob","url","data"]},fu=typeof window<"u"&&typeof document<"u",Xa=typeof navigator=="object"&&navigator||void 0,M0=fu&&(!Xa||["ReactNative","NativeScript","NS"].indexOf(Xa.product)<0),L0=typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope&&typeof self.importScripts=="function",I0=fu&&window.location.href||"http://localhost",D0=Object.freeze(Object.defineProperty({__proto__:null,hasBrowserEnv:fu,hasStandardBrowserEnv:M0,hasStandardBrowserWebWorkerEnv:L0,navigator:Xa,origin:I0},Symbol.toStringTag,{value:"Module"})),tt={...D0,...O0};function z0(r,i){return Rs(r,new tt.classes.URLSearchParams,Object.assign({visitor:function(s,l,c,d){return tt.isNode&&O.isBuffer(s)?(this.append(l,s.toString("base64")),!1):d.defaultVisitor.apply(this,arguments)}},i))}function $0(r){return O.matchAll(/\w+|\[(\w*)]/g,r).map(i=>i[0]==="[]"?"":i[1]||i[0])}function B0(r){const i={},s=Object.keys(r);let l;const c=s.length;let d;for(l=0;l=s.length;return p=!p&&O.isArray(c)?c.length:p,w?(O.hasOwnProp(c,p)?c[p]=[c[p],l]:c[p]=l,!m):((!c[p]||!O.isObject(c[p]))&&(c[p]=[]),i(s,l,c[p],d)&&O.isArray(c[p])&&(c[p]=B0(c[p])),!m)}if(O.isFormData(r)&&O.isFunction(r.entries)){const s={};return O.forEachEntry(r,(l,c)=>{i($0(l),c,s,0)}),s}return null}function F0(r,i,s){if(O.isString(r))try{return(i||JSON.parse)(r),O.trim(r)}catch(l){if(l.name!=="SyntaxError")throw l}return(0,JSON.stringify)(r)}const Do={transitional:Jp,adapter:["xhr","http","fetch"],transformRequest:[function(i,s){const l=s.getContentType()||"",c=l.indexOf("application/json")>-1,d=O.isObject(i);if(d&&O.isHTMLForm(i)&&(i=new FormData(i)),O.isFormData(i))return c?JSON.stringify(Zp(i)):i;if(O.isArrayBuffer(i)||O.isBuffer(i)||O.isStream(i)||O.isFile(i)||O.isBlob(i)||O.isReadableStream(i))return i;if(O.isArrayBufferView(i))return i.buffer;if(O.isURLSearchParams(i))return s.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),i.toString();let m;if(d){if(l.indexOf("application/x-www-form-urlencoded")>-1)return z0(i,this.formSerializer).toString();if((m=O.isFileList(i))||l.indexOf("multipart/form-data")>-1){const w=this.env&&this.env.FormData;return Rs(m?{"files[]":i}:i,w&&new w,this.formSerializer)}}return d||c?(s.setContentType("application/json",!1),F0(i)):i}],transformResponse:[function(i){const s=this.transitional||Do.transitional,l=s&&s.forcedJSONParsing,c=this.responseType==="json";if(O.isResponse(i)||O.isReadableStream(i))return i;if(i&&O.isString(i)&&(l&&!this.responseType||c)){const p=!(s&&s.silentJSONParsing)&&c;try{return JSON.parse(i)}catch(m){if(p)throw m.name==="SyntaxError"?ue.from(m,ue.ERR_BAD_RESPONSE,this,null,this.response):m}}return i}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:tt.classes.FormData,Blob:tt.classes.Blob},validateStatus:function(i){return i>=200&&i<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};O.forEach(["delete","get","head","post","put","patch"],r=>{Do.headers[r]={}});const b0=O.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),U0=r=>{const i={};let s,l,c;return r&&r.split(` +`).forEach(function(p){c=p.indexOf(":"),s=p.substring(0,c).trim().toLowerCase(),l=p.substring(c+1).trim(),!(!s||i[s]&&b0[s])&&(s==="set-cookie"?i[s]?i[s].push(l):i[s]=[l]:i[s]=i[s]?i[s]+", "+l:l)}),i},Uf=Symbol("internals");function wo(r){return r&&String(r).trim().toLowerCase()}function us(r){return r===!1||r==null?r:O.isArray(r)?r.map(us):String(r)}function H0(r){const i=Object.create(null),s=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let l;for(;l=s.exec(r);)i[l[1]]=l[2];return i}const V0=r=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(r.trim());function Ma(r,i,s,l,c){if(O.isFunction(l))return l.call(this,i,s);if(c&&(i=s),!!O.isString(i)){if(O.isString(l))return i.indexOf(l)!==-1;if(O.isRegExp(l))return l.test(i)}}function W0(r){return r.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(i,s,l)=>s.toUpperCase()+l)}function q0(r,i){const s=O.toCamelCase(" "+i);["get","set","has"].forEach(l=>{Object.defineProperty(r,l+s,{value:function(c,d,p){return this[l].call(this,i,c,d,p)},configurable:!0})})}class pt{constructor(i){i&&this.set(i)}set(i,s,l){const c=this;function d(m,w,v){const S=wo(w);if(!S)throw new Error("header name must be a non-empty string");const j=O.findKey(c,S);(!j||c[j]===void 0||v===!0||v===void 0&&c[j]!==!1)&&(c[j||w]=us(m))}const p=(m,w)=>O.forEach(m,(v,S)=>d(v,S,w));if(O.isPlainObject(i)||i instanceof this.constructor)p(i,s);else if(O.isString(i)&&(i=i.trim())&&!V0(i))p(U0(i),s);else if(O.isHeaders(i))for(const[m,w]of i.entries())d(w,m,l);else i!=null&&d(s,i,l);return this}get(i,s){if(i=wo(i),i){const l=O.findKey(this,i);if(l){const c=this[l];if(!s)return c;if(s===!0)return H0(c);if(O.isFunction(s))return s.call(this,c,l);if(O.isRegExp(s))return s.exec(c);throw new TypeError("parser must be boolean|regexp|function")}}}has(i,s){if(i=wo(i),i){const l=O.findKey(this,i);return!!(l&&this[l]!==void 0&&(!s||Ma(this,this[l],l,s)))}return!1}delete(i,s){const l=this;let c=!1;function d(p){if(p=wo(p),p){const m=O.findKey(l,p);m&&(!s||Ma(l,l[m],m,s))&&(delete l[m],c=!0)}}return O.isArray(i)?i.forEach(d):d(i),c}clear(i){const s=Object.keys(this);let l=s.length,c=!1;for(;l--;){const d=s[l];(!i||Ma(this,this[d],d,i,!0))&&(delete this[d],c=!0)}return c}normalize(i){const s=this,l={};return O.forEach(this,(c,d)=>{const p=O.findKey(l,d);if(p){s[p]=us(c),delete s[d];return}const m=i?W0(d):String(d).trim();m!==d&&delete s[d],s[m]=us(c),l[m]=!0}),this}concat(...i){return this.constructor.concat(this,...i)}toJSON(i){const s=Object.create(null);return O.forEach(this,(l,c)=>{l!=null&&l!==!1&&(s[c]=i&&O.isArray(l)?l.join(", "):l)}),s}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([i,s])=>i+": "+s).join(` +`)}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(i){return i instanceof this?i:new this(i)}static concat(i,...s){const l=new this(i);return s.forEach(c=>l.set(c)),l}static accessor(i){const l=(this[Uf]=this[Uf]={accessors:{}}).accessors,c=this.prototype;function d(p){const m=wo(p);l[m]||(q0(c,p),l[m]=!0)}return O.isArray(i)?i.forEach(d):d(i),this}}pt.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);O.reduceDescriptors(pt.prototype,({value:r},i)=>{let s=i[0].toUpperCase()+i.slice(1);return{get:()=>r,set(l){this[s]=l}}});O.freezeMethods(pt);function La(r,i){const s=this||Do,l=i||s,c=pt.from(l.headers);let d=l.data;return O.forEach(r,function(m){d=m.call(s,d,c.normalize(),i?i.status:void 0)}),c.normalize(),d}function eh(r){return!!(r&&r.__CANCEL__)}function Nr(r,i,s){ue.call(this,r??"canceled",ue.ERR_CANCELED,i,s),this.name="CanceledError"}O.inherits(Nr,ue,{__CANCEL__:!0});function th(r,i,s){const l=s.config.validateStatus;!s.status||!l||l(s.status)?r(s):i(new ue("Request failed with status code "+s.status,[ue.ERR_BAD_REQUEST,ue.ERR_BAD_RESPONSE][Math.floor(s.status/100)-4],s.config,s.request,s))}function Y0(r){const i=/^([-+\w]{1,25})(:?\/\/|:)/.exec(r);return i&&i[1]||""}function Q0(r,i){r=r||10;const s=new Array(r),l=new Array(r);let c=0,d=0,p;return i=i!==void 0?i:1e3,function(w){const v=Date.now(),S=l[d];p||(p=v),s[c]=w,l[c]=v;let j=d,R=0;for(;j!==c;)R+=s[j++],j=j%r;if(c=(c+1)%r,c===d&&(d=(d+1)%r),v-p{s=S,c=null,d&&(clearTimeout(d),d=null),r.apply(null,v)};return[(...v)=>{const S=Date.now(),j=S-s;j>=l?p(v,S):(c=v,d||(d=setTimeout(()=>{d=null,p(c)},l-j)))},()=>c&&p(c)]}const gs=(r,i,s=3)=>{let l=0;const c=Q0(50,250);return G0(d=>{const p=d.loaded,m=d.lengthComputable?d.total:void 0,w=p-l,v=c(w),S=p<=m;l=p;const j={loaded:p,total:m,progress:m?p/m:void 0,bytes:w,rate:v||void 0,estimated:v&&m&&S?(m-p)/v:void 0,event:d,lengthComputable:m!=null,[i?"download":"upload"]:!0};r(j)},s)},Hf=(r,i)=>{const s=r!=null;return[l=>i[0]({lengthComputable:s,total:r,loaded:l}),i[1]]},Vf=r=>(...i)=>O.asap(()=>r(...i)),K0=tt.hasStandardBrowserEnv?((r,i)=>s=>(s=new URL(s,tt.origin),r.protocol===s.protocol&&r.host===s.host&&(i||r.port===s.port)))(new URL(tt.origin),tt.navigator&&/(msie|trident)/i.test(tt.navigator.userAgent)):()=>!0,X0=tt.hasStandardBrowserEnv?{write(r,i,s,l,c,d){const p=[r+"="+encodeURIComponent(i)];O.isNumber(s)&&p.push("expires="+new Date(s).toGMTString()),O.isString(l)&&p.push("path="+l),O.isString(c)&&p.push("domain="+c),d===!0&&p.push("secure"),document.cookie=p.join("; ")},read(r){const i=document.cookie.match(new RegExp("(^|;\\s*)("+r+")=([^;]*)"));return i?decodeURIComponent(i[3]):null},remove(r){this.write(r,"",Date.now()-864e5)}}:{write(){},read(){return null},remove(){}};function J0(r){return/^([a-z][a-z\d+\-.]*:)?\/\//i.test(r)}function Z0(r,i){return i?r.replace(/\/?\/$/,"")+"/"+i.replace(/^\/+/,""):r}function nh(r,i){return r&&!J0(i)?Z0(r,i):i}const Wf=r=>r instanceof pt?{...r}:r;function Yn(r,i){i=i||{};const s={};function l(v,S,j,R){return O.isPlainObject(v)&&O.isPlainObject(S)?O.merge.call({caseless:R},v,S):O.isPlainObject(S)?O.merge({},S):O.isArray(S)?S.slice():S}function c(v,S,j,R){if(O.isUndefined(S)){if(!O.isUndefined(v))return l(void 0,v,j,R)}else return l(v,S,j,R)}function d(v,S){if(!O.isUndefined(S))return l(void 0,S)}function p(v,S){if(O.isUndefined(S)){if(!O.isUndefined(v))return l(void 0,v)}else return l(void 0,S)}function m(v,S,j){if(j in i)return l(v,S);if(j in r)return l(void 0,v)}const w={url:d,method:d,data:d,baseURL:p,transformRequest:p,transformResponse:p,paramsSerializer:p,timeout:p,timeoutMessage:p,withCredentials:p,withXSRFToken:p,adapter:p,responseType:p,xsrfCookieName:p,xsrfHeaderName:p,onUploadProgress:p,onDownloadProgress:p,decompress:p,maxContentLength:p,maxBodyLength:p,beforeRedirect:p,transport:p,httpAgent:p,httpsAgent:p,cancelToken:p,socketPath:p,responseEncoding:p,validateStatus:m,headers:(v,S,j)=>c(Wf(v),Wf(S),j,!0)};return O.forEach(Object.keys(Object.assign({},r,i)),function(S){const j=w[S]||c,R=j(r[S],i[S],S);O.isUndefined(R)&&j!==m||(s[S]=R)}),s}const rh=r=>{const i=Yn({},r);let{data:s,withXSRFToken:l,xsrfHeaderName:c,xsrfCookieName:d,headers:p,auth:m}=i;i.headers=p=pt.from(p),i.url=Xp(nh(i.baseURL,i.url),r.params,r.paramsSerializer),m&&p.set("Authorization","Basic "+btoa((m.username||"")+":"+(m.password?unescape(encodeURIComponent(m.password)):"")));let w;if(O.isFormData(s)){if(tt.hasStandardBrowserEnv||tt.hasStandardBrowserWebWorkerEnv)p.setContentType(void 0);else if((w=p.getContentType())!==!1){const[v,...S]=w?w.split(";").map(j=>j.trim()).filter(Boolean):[];p.setContentType([v||"multipart/form-data",...S].join("; "))}}if(tt.hasStandardBrowserEnv&&(l&&O.isFunction(l)&&(l=l(i)),l||l!==!1&&K0(i.url))){const v=c&&d&&X0.read(d);v&&p.set(c,v)}return i},ev=typeof XMLHttpRequest<"u",tv=ev&&function(r){return new Promise(function(s,l){const c=rh(r);let d=c.data;const p=pt.from(c.headers).normalize();let{responseType:m,onUploadProgress:w,onDownloadProgress:v}=c,S,j,R,L,T;function N(){L&&L(),T&&T(),c.cancelToken&&c.cancelToken.unsubscribe(S),c.signal&&c.signal.removeEventListener("abort",S)}let _=new XMLHttpRequest;_.open(c.method.toUpperCase(),c.url,!0),_.timeout=c.timeout;function V(){if(!_)return;const B=pt.from("getAllResponseHeaders"in _&&_.getAllResponseHeaders()),I={data:!m||m==="text"||m==="json"?_.responseText:_.response,status:_.status,statusText:_.statusText,headers:B,config:r,request:_};th(function(H){s(H),N()},function(H){l(H),N()},I),_=null}"onloadend"in _?_.onloadend=V:_.onreadystatechange=function(){!_||_.readyState!==4||_.status===0&&!(_.responseURL&&_.responseURL.indexOf("file:")===0)||setTimeout(V)},_.onabort=function(){_&&(l(new ue("Request aborted",ue.ECONNABORTED,r,_)),_=null)},_.onerror=function(){l(new ue("Network Error",ue.ERR_NETWORK,r,_)),_=null},_.ontimeout=function(){let W=c.timeout?"timeout of "+c.timeout+"ms exceeded":"timeout exceeded";const I=c.transitional||Jp;c.timeoutErrorMessage&&(W=c.timeoutErrorMessage),l(new ue(W,I.clarifyTimeoutError?ue.ETIMEDOUT:ue.ECONNABORTED,r,_)),_=null},d===void 0&&p.setContentType(null),"setRequestHeader"in _&&O.forEach(p.toJSON(),function(W,I){_.setRequestHeader(I,W)}),O.isUndefined(c.withCredentials)||(_.withCredentials=!!c.withCredentials),m&&m!=="json"&&(_.responseType=c.responseType),v&&([R,T]=gs(v,!0),_.addEventListener("progress",R)),w&&_.upload&&([j,L]=gs(w),_.upload.addEventListener("progress",j),_.upload.addEventListener("loadend",L)),(c.cancelToken||c.signal)&&(S=B=>{_&&(l(!B||B.type?new Nr(null,r,_):B),_.abort(),_=null)},c.cancelToken&&c.cancelToken.subscribe(S),c.signal&&(c.signal.aborted?S():c.signal.addEventListener("abort",S)));const U=Y0(c.url);if(U&&tt.protocols.indexOf(U)===-1){l(new ue("Unsupported protocol "+U+":",ue.ERR_BAD_REQUEST,r));return}_.send(d||null)})},nv=(r,i)=>{const{length:s}=r=r?r.filter(Boolean):[];if(i||s){let l=new AbortController,c;const d=function(v){if(!c){c=!0,m();const S=v instanceof Error?v:this.reason;l.abort(S instanceof ue?S:new Nr(S instanceof Error?S.message:S))}};let p=i&&setTimeout(()=>{p=null,d(new ue(`timeout ${i} of ms exceeded`,ue.ETIMEDOUT))},i);const m=()=>{r&&(p&&clearTimeout(p),p=null,r.forEach(v=>{v.unsubscribe?v.unsubscribe(d):v.removeEventListener("abort",d)}),r=null)};r.forEach(v=>v.addEventListener("abort",d));const{signal:w}=l;return w.unsubscribe=()=>O.asap(m),w}},rv=function*(r,i){let s=r.byteLength;if(s{const c=ov(r,i);let d=0,p,m=w=>{p||(p=!0,l&&l(w))};return new ReadableStream({async pull(w){try{const{done:v,value:S}=await c.next();if(v){m(),w.close();return}let j=S.byteLength;if(s){let R=d+=j;s(R)}w.enqueue(new Uint8Array(S))}catch(v){throw m(v),v}},cancel(w){return m(w),c.return()}},{highWaterMark:2})},Ps=typeof fetch=="function"&&typeof Request=="function"&&typeof Response=="function",oh=Ps&&typeof ReadableStream=="function",sv=Ps&&(typeof TextEncoder=="function"?(r=>i=>r.encode(i))(new TextEncoder):async r=>new Uint8Array(await new Response(r).arrayBuffer())),ih=(r,...i)=>{try{return!!r(...i)}catch{return!1}},lv=oh&&ih(()=>{let r=!1;const i=new Request(tt.origin,{body:new ReadableStream,method:"POST",get duplex(){return r=!0,"half"}}).headers.has("Content-Type");return r&&!i}),Yf=64*1024,Ja=oh&&ih(()=>O.isReadableStream(new Response("").body)),ys={stream:Ja&&(r=>r.body)};Ps&&(r=>{["text","arrayBuffer","blob","formData","stream"].forEach(i=>{!ys[i]&&(ys[i]=O.isFunction(r[i])?s=>s[i]():(s,l)=>{throw new ue(`Response type '${i}' is not supported`,ue.ERR_NOT_SUPPORT,l)})})})(new Response);const av=async r=>{if(r==null)return 0;if(O.isBlob(r))return r.size;if(O.isSpecCompliantForm(r))return(await new Request(tt.origin,{method:"POST",body:r}).arrayBuffer()).byteLength;if(O.isArrayBufferView(r)||O.isArrayBuffer(r))return r.byteLength;if(O.isURLSearchParams(r)&&(r=r+""),O.isString(r))return(await sv(r)).byteLength},uv=async(r,i)=>{const s=O.toFiniteNumber(r.getContentLength());return s??av(i)},cv=Ps&&(async r=>{let{url:i,method:s,data:l,signal:c,cancelToken:d,timeout:p,onDownloadProgress:m,onUploadProgress:w,responseType:v,headers:S,withCredentials:j="same-origin",fetchOptions:R}=rh(r);v=v?(v+"").toLowerCase():"text";let L=nv([c,d&&d.toAbortSignal()],p),T;const N=L&&L.unsubscribe&&(()=>{L.unsubscribe()});let _;try{if(w&&lv&&s!=="get"&&s!=="head"&&(_=await uv(S,l))!==0){let I=new Request(i,{method:"POST",body:l,duplex:"half"}),M;if(O.isFormData(l)&&(M=I.headers.get("content-type"))&&S.setContentType(M),I.body){const[H,ie]=Hf(_,gs(Vf(w)));l=qf(I.body,Yf,H,ie)}}O.isString(j)||(j=j?"include":"omit");const V="credentials"in Request.prototype;T=new Request(i,{...R,signal:L,method:s.toUpperCase(),headers:S.normalize().toJSON(),body:l,duplex:"half",credentials:V?j:void 0});let U=await fetch(T);const B=Ja&&(v==="stream"||v==="response");if(Ja&&(m||B&&N)){const I={};["status","statusText","headers"].forEach(ve=>{I[ve]=U[ve]});const M=O.toFiniteNumber(U.headers.get("content-length")),[H,ie]=m&&Hf(M,gs(Vf(m),!0))||[];U=new Response(qf(U.body,Yf,H,()=>{ie&&ie(),N&&N()}),I)}v=v||"text";let W=await ys[O.findKey(ys,v)||"text"](U,r);return!B&&N&&N(),await new Promise((I,M)=>{th(I,M,{data:W,headers:pt.from(U.headers),status:U.status,statusText:U.statusText,config:r,request:T})})}catch(V){throw N&&N(),V&&V.name==="TypeError"&&/fetch/i.test(V.message)?Object.assign(new ue("Network Error",ue.ERR_NETWORK,r,T),{cause:V.cause||V}):ue.from(V,V&&V.code,r,T)}}),Za={http:j0,xhr:tv,fetch:cv};O.forEach(Za,(r,i)=>{if(r){try{Object.defineProperty(r,"name",{value:i})}catch{}Object.defineProperty(r,"adapterName",{value:i})}});const Qf=r=>`- ${r}`,dv=r=>O.isFunction(r)||r===null||r===!1,sh={getAdapter:r=>{r=O.isArray(r)?r:[r];const{length:i}=r;let s,l;const c={};for(let d=0;d`adapter ${m} `+(w===!1?"is not supported by the environment":"is not available in the build"));let p=i?d.length>1?`since : +`+d.map(Qf).join(` +`):" "+Qf(d[0]):"as no adapter specified";throw new ue("There is no suitable adapter to dispatch the request "+p,"ERR_NOT_SUPPORT")}return l},adapters:Za};function Ia(r){if(r.cancelToken&&r.cancelToken.throwIfRequested(),r.signal&&r.signal.aborted)throw new Nr(null,r)}function Gf(r){return Ia(r),r.headers=pt.from(r.headers),r.data=La.call(r,r.transformRequest),["post","put","patch"].indexOf(r.method)!==-1&&r.headers.setContentType("application/x-www-form-urlencoded",!1),sh.getAdapter(r.adapter||Do.adapter)(r).then(function(l){return Ia(r),l.data=La.call(r,r.transformResponse,l),l.headers=pt.from(l.headers),l},function(l){return eh(l)||(Ia(r),l&&l.response&&(l.response.data=La.call(r,r.transformResponse,l.response),l.response.headers=pt.from(l.response.headers))),Promise.reject(l)})}const lh="1.7.9",Ts={};["object","boolean","number","function","string","symbol"].forEach((r,i)=>{Ts[r]=function(l){return typeof l===r||"a"+(i<1?"n ":" ")+r}});const Kf={};Ts.transitional=function(i,s,l){function c(d,p){return"[Axios v"+lh+"] Transitional option '"+d+"'"+p+(l?". "+l:"")}return(d,p,m)=>{if(i===!1)throw new ue(c(p," has been removed"+(s?" in "+s:"")),ue.ERR_DEPRECATED);return s&&!Kf[p]&&(Kf[p]=!0,console.warn(c(p," has been deprecated since v"+s+" and will be removed in the near future"))),i?i(d,p,m):!0}};Ts.spelling=function(i){return(s,l)=>(console.warn(`${l} is likely a misspelling of ${i}`),!0)};function fv(r,i,s){if(typeof r!="object")throw new ue("options must be an object",ue.ERR_BAD_OPTION_VALUE);const l=Object.keys(r);let c=l.length;for(;c-- >0;){const d=l[c],p=i[d];if(p){const m=r[d],w=m===void 0||p(m,d,r);if(w!==!0)throw new ue("option "+d+" must be "+w,ue.ERR_BAD_OPTION_VALUE);continue}if(s!==!0)throw new ue("Unknown option "+d,ue.ERR_BAD_OPTION)}}const cs={assertOptions:fv,validators:Ts},Vt=cs.validators;class Vn{constructor(i){this.defaults=i,this.interceptors={request:new bf,response:new bf}}async request(i,s){try{return await this._request(i,s)}catch(l){if(l instanceof Error){let c={};Error.captureStackTrace?Error.captureStackTrace(c):c=new Error;const d=c.stack?c.stack.replace(/^.+\n/,""):"";try{l.stack?d&&!String(l.stack).endsWith(d.replace(/^.+\n.+\n/,""))&&(l.stack+=` +`+d):l.stack=d}catch{}}throw l}}_request(i,s){typeof i=="string"?(s=s||{},s.url=i):s=i||{},s=Yn(this.defaults,s);const{transitional:l,paramsSerializer:c,headers:d}=s;l!==void 0&&cs.assertOptions(l,{silentJSONParsing:Vt.transitional(Vt.boolean),forcedJSONParsing:Vt.transitional(Vt.boolean),clarifyTimeoutError:Vt.transitional(Vt.boolean)},!1),c!=null&&(O.isFunction(c)?s.paramsSerializer={serialize:c}:cs.assertOptions(c,{encode:Vt.function,serialize:Vt.function},!0)),cs.assertOptions(s,{baseUrl:Vt.spelling("baseURL"),withXsrfToken:Vt.spelling("withXSRFToken")},!0),s.method=(s.method||this.defaults.method||"get").toLowerCase();let p=d&&O.merge(d.common,d[s.method]);d&&O.forEach(["delete","get","head","post","put","patch","common"],T=>{delete d[T]}),s.headers=pt.concat(p,d);const m=[];let w=!0;this.interceptors.request.forEach(function(N){typeof N.runWhen=="function"&&N.runWhen(s)===!1||(w=w&&N.synchronous,m.unshift(N.fulfilled,N.rejected))});const v=[];this.interceptors.response.forEach(function(N){v.push(N.fulfilled,N.rejected)});let S,j=0,R;if(!w){const T=[Gf.bind(this),void 0];for(T.unshift.apply(T,m),T.push.apply(T,v),R=T.length,S=Promise.resolve(s);j{if(!l._listeners)return;let d=l._listeners.length;for(;d-- >0;)l._listeners[d](c);l._listeners=null}),this.promise.then=c=>{let d;const p=new Promise(m=>{l.subscribe(m),d=m}).then(c);return p.cancel=function(){l.unsubscribe(d)},p},i(function(d,p,m){l.reason||(l.reason=new Nr(d,p,m),s(l.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(i){if(this.reason){i(this.reason);return}this._listeners?this._listeners.push(i):this._listeners=[i]}unsubscribe(i){if(!this._listeners)return;const s=this._listeners.indexOf(i);s!==-1&&this._listeners.splice(s,1)}toAbortSignal(){const i=new AbortController,s=l=>{i.abort(l)};return this.subscribe(s),i.signal.unsubscribe=()=>this.unsubscribe(s),i.signal}static source(){let i;return{token:new pu(function(c){i=c}),cancel:i}}}function pv(r){return function(s){return r.apply(null,s)}}function hv(r){return O.isObject(r)&&r.isAxiosError===!0}const eu={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(eu).forEach(([r,i])=>{eu[i]=r});function ah(r){const i=new Vn(r),s=Bp(Vn.prototype.request,i);return O.extend(s,Vn.prototype,i,{allOwnKeys:!0}),O.extend(s,i,null,{allOwnKeys:!0}),s.create=function(c){return ah(Yn(r,c))},s}const Be=ah(Do);Be.Axios=Vn;Be.CanceledError=Nr;Be.CancelToken=pu;Be.isCancel=eh;Be.VERSION=lh;Be.toFormData=Rs;Be.AxiosError=ue;Be.Cancel=Be.CanceledError;Be.all=function(i){return Promise.all(i)};Be.spread=pv;Be.isAxiosError=hv;Be.mergeConfig=Yn;Be.AxiosHeaders=pt;Be.formToJSON=r=>Zp(O.isHTMLForm(r)?new FormData(r):r);Be.getAdapter=sh.getAdapter;Be.HttpStatusCode=eu;Be.default=Be;const uh={apiBaseUrl:"/api"};class mv{constructor(){uf(this,"events",{})}on(i,s){return this.events[i]||(this.events[i]=[]),this.events[i].push(s),()=>this.off(i,s)}off(i,s){this.events[i]&&(this.events[i]=this.events[i].filter(l=>l!==s))}emit(i,s){this.events[i]&&this.events[i].forEach(l=>{l(s)})}}const Sr=new mv,gv=async(r,i)=>{const s=new FormData;return s.append("username",r),s.append("password",i),(await zo.post("/auth/login",s,{headers:{"Content-Type":"multipart/form-data"}})).data},yv=async r=>(await zo.post("/users",r,{headers:{"Content-Type":"multipart/form-data"}})).data,vv=async()=>{await zo.get("/auth/csrf-token")},xv=async()=>{await zo.post("/auth/logout")},wv=async()=>(await zo.post("/auth/refresh")).data,Sv=async(r,i)=>{const s={userId:r,newRole:i};return(await $e.put("/auth/role",s)).data},rt=Tr((r,i)=>({currentUser:null,accessToken:null,login:async(s,l)=>{const{userDto:c,accessToken:d}=await gv(s,l);await i().fetchCsrfToken(),r({currentUser:c,accessToken:d})},logout:async()=>{await xv(),i().clear(),i().fetchCsrfToken()},fetchCsrfToken:async()=>{await vv()},refreshToken:async()=>{i().clear();const{userDto:s,accessToken:l}=await wv();r({currentUser:s,accessToken:l})},clear:()=>{r({currentUser:null,accessToken:null})},updateUserRole:async(s,l)=>{await Sv(s,l)}}));let So=[],Xi=!1;const $e=Be.create({baseURL:uh.apiBaseUrl,headers:{"Content-Type":"application/json"},withCredentials:!0}),zo=Be.create({baseURL:uh.apiBaseUrl,headers:{"Content-Type":"application/json"},withCredentials:!0});$e.interceptors.request.use(r=>{const i=rt.getState().accessToken;return i&&(r.headers.Authorization=`Bearer ${i}`),r},r=>Promise.reject(r));$e.interceptors.response.use(r=>r,async r=>{var s,l,c,d;const i=(s=r.response)==null?void 0:s.data;if(i){const p=(c=(l=r.response)==null?void 0:l.headers)==null?void 0:c["discodeit-request-id"];p&&(i.requestId=p),r.response.data=i}if(console.log({error:r,errorResponse:i}),Sr.emit("api-error",{error:r,alert:((d=r.response)==null?void 0:d.status)===403}),r.response&&r.response.status===401){const p=r.config;if(p&&p.headers&&p.headers._retry)return Sr.emit("auth-error"),Promise.reject(r);if(Xi&&p)return new Promise((m,w)=>{So.push({config:p,resolve:m,reject:w})});if(p){Xi=!0;try{return await rt.getState().refreshToken(),So.forEach(({config:m,resolve:w,reject:v})=>{m.headers=m.headers||{},m.headers._retry="true",$e(m).then(w).catch(v)}),p.headers=p.headers||{},p.headers._retry="true",So=[],Xi=!1,$e(p)}catch(m){return So.forEach(({reject:w})=>w(m)),So=[],Xi=!1,Sr.emit("auth-error"),Promise.reject(m)}}}return Promise.reject(r)});const kv=async(r,i)=>(await $e.patch(`/users/${r}`,i,{headers:{"Content-Type":"multipart/form-data"}})).data,Cv=async()=>(await $e.get("/users")).data,Rr=Tr(r=>({users:[],fetchUsers:async()=>{try{const i=await Cv();r({users:i})}catch(i){console.error("사용자 목록 조회 실패:",i)}}})),Y={colors:{brand:{primary:"#5865F2",hover:"#4752C4"},background:{primary:"#1a1a1a",secondary:"#2a2a2a",tertiary:"#333333",input:"#40444B",hover:"rgba(255, 255, 255, 0.1)"},text:{primary:"#ffffff",secondary:"#cccccc",muted:"#999999"},status:{online:"#43b581",idle:"#faa61a",dnd:"#f04747",offline:"#747f8d",error:"#ED4245"},border:{primary:"#404040"}}},ch=C.div` + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +`,dh=C.div` + background: ${Y.colors.background.primary}; + padding: 32px; + border-radius: 8px; + width: 440px; + + h2 { + color: ${Y.colors.text.primary}; + margin-bottom: 24px; + font-size: 24px; + font-weight: bold; + } + + form { + display: flex; + flex-direction: column; + gap: 16px; + } +`,Ro=C.input` + width: 100%; + padding: 10px; + border-radius: 4px; + background: ${Y.colors.background.input}; + border: none; + color: ${Y.colors.text.primary}; + font-size: 16px; + + &::placeholder { + color: ${Y.colors.text.muted}; + } + + &:focus { + outline: none; + } +`;C.input.attrs({type:"checkbox"})` + width: 16px; + height: 16px; + padding: 0; + border-radius: 4px; + background: ${Y.colors.background.input}; + border: none; + color: ${Y.colors.text.primary}; + cursor: pointer; + + &:focus { + outline: none; + } + + &:checked { + background: ${Y.colors.brand.primary}; + } +`;const fh=C.button` + width: 100%; + padding: 12px; + border-radius: 4px; + background: ${Y.colors.brand.primary}; + color: white; + font-size: 16px; + font-weight: 500; + border: none; + cursor: pointer; + transition: background-color 0.2s; + + &:hover { + background: ${Y.colors.brand.hover}; + } +`,ph=C.div` + color: ${Y.colors.status.error}; + font-size: 14px; + text-align: center; +`,Ev=C.p` + text-align: center; + margin-top: 16px; + color: ${({theme:r})=>r.colors.text.muted}; + font-size: 14px; +`,jv=C.span` + color: ${({theme:r})=>r.colors.brand.primary}; + cursor: pointer; + + &:hover { + text-decoration: underline; + } +`,Ji=C.div` + margin-bottom: 20px; +`,Zi=C.label` + display: block; + color: ${({theme:r})=>r.colors.text.muted}; + font-size: 12px; + font-weight: 700; + margin-bottom: 8px; +`,Da=C.span` + color: ${({theme:r})=>r.colors.status.error}; +`,Av=C.div` + display: flex; + flex-direction: column; + align-items: center; + margin: 10px 0; +`,Rv=C.img` + width: 80px; + height: 80px; + border-radius: 50%; + margin-bottom: 10px; + object-fit: cover; +`,Pv=C.input` + display: none; +`,Tv=C.label` + color: ${({theme:r})=>r.colors.brand.primary}; + cursor: pointer; + font-size: 14px; + + &:hover { + text-decoration: underline; + } +`,_v=C.span` + color: ${({theme:r})=>r.colors.brand.primary}; + cursor: pointer; + + &:hover { + text-decoration: underline; + } +`,Nv=C(_v)` + display: block; + text-align: center; + margin-top: 16px; +`,St="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAADwCAYAAAA+VemSAAAACXBIWXMAACE4AAAhOAFFljFgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAw2SURBVHgB7d3PT1XpHcfxBy5g6hipSMolGViACThxJDbVRZ2FXejKlf9h/4GmC1fTRdkwC8fE0JgyJuICFkCjEA04GeZe6P0cPC0698I95zzPc57v5f1K6DSto3A8n/v9nufXGfrr338+dgBMGnYAzCLAgGEEGDCMAAOGEWDAMAIMGEaAAcMIMGAYAQYMI8CAYQQYMIwAA4YRYMAwAgwYRoABwwgwYBgBBgwjwIBhBBgwjAADhhFgwDACDBhGgAHDCDBgGAEGDCPAgGEEGDCMAAOGEWDAMAIMGEaAAcMIMGAYAQYMI8CAYQQYMIwAA4YRYMAwAgwYRoABwwgwYBgBBgwjwIBhBBgwjAADhhFgwDACDBhGgAHDCDBgGAEGDCPAgGEEGDCMAAOGEWDAMAIMGEaAAcMIMGAYAQYMI8CAYQQYMIwAA4YRYMAwAgwYRoABwwgwYBgBBgwbcTDvyuWh//33w1/1dexwMRBgYxTW5vVh9/vxYTcxPpR9jY0OffZrdt8fu82ttlvfbLv9j4R5kBHgxCmcE1eH3NfTDTc7PfxZte3lJNgjbmlxxK3+1HKrr1oOg4kAJ0pVdnG+4ZqTw7+psEUoxF91Qv/Di1+db/q+ZpvD7g+T6gb04XLyv6mF3//osuqvTmDn3RGdQCAEOCG6+W/ONdzNTnCrhPZLN2Yb2T99hVhdwOLcSOf37f7hknUN4yedgLoGeb3Rdv/qdAIE2S8CnIDzAuGDQrzXeTZee1OtndaHy9LCSOHvU3++vv693nLPX9LS+0KAa6QQLC2o4sb5a1A7rYGtMqPU+l7v3hpx85+qeVnfdH7W2c7z/Pcrh1RjD5gHromq2JOHY9HCK2Ojzk1dL1fhH90fqxzenDoO/X79DMjhbAQ4Mg1OPXl4KauGodrls6j6FaXKq+dZn/IQ13ENBgkBjiRvQR99V2/lmZos9lc+PxOuxdd1uL3gp6pfVDwDR6Ab9cG9Me9VLAZ1CiHpmXhz6yibakJxVODAZpoN9/iBzfCq+sboFkJ/SAwyrlxAujE1WJWSIiO/sYKlxSpTnbEBqnBxVOBA9LybWnjloM8An6ysitc1NCe5FcvgqgVw/85o1OmhItY32n39uqnJuC3/FAEuhavmmcLra77UN7XP2322qRNX494aqvgojqvmUcrhFa1+6tdXkae6tMiEhR3FEWBPNOCTcni1rZCli4OHAHuQ4mjzaewJHlxMI1Wked5Uw7v99ijbwqd/FnVQQ7WmQyiOAFegZ7a736ZzCU820h+7nbfHbnO7XSq4p3+vmHbfMwdcBgGuoO4dNQrZxtaR+08nqNueT73Y2D7qTIW5aLRXGcUR4JL03FtHeBXa9Y2jyhX2PHudiqg/K9ZuoY3t/uan8TkCXIKCG/u5V2Fae9N2a+vtKO2tjqfVnxfj5zw5O4sWugwCXIJa51hiB/e0tfVWdkZX6CrMCHl5BLigWDt0RCc6rrxo1XZQu6rw6qt2tq47FD0G9Lu8E79FgAvIWucIO3QU2B9ftpK4sVWFZ5rDQTYbqHUOcdztRcJCjgLUToauvrqpny4fJlWVlp/5P4BOH1IcbFcdAe6Tght6h5FeiaLwpnZTq5VW2HzN1eYfUoS3OgLcp9sL4cOrkKT6YrI8dFUHnDQYR3j94Rm4D9kLxQLuV009vKdpXbXae00vFdm8UWVZJ3ojwH3QcS+hnn1VifSMaemVoPqeVzqDT6rG2oivQS5dH33l70ZS262w7n04yhae8MrTMAhwH0KNPFsfyNH3vd+pxkwD1Ydn4HOodQ5VfTXHyrMgqiDA55ibCbNJX1VLc6xAFQT4HCEGr9Q6s3wQPhDgM4RqnzWVQusMHwjwGTS66puCS/WFLwT4DCHOKia88IkA96BjTkOcVbzDQgZ4RIB7CBFejTzz7AufCHAPWn3lGwse4BsB7uGa5wqcLS3k7XvwjAD3cOWy84pnX4RAgHvw/QzMLhyEQIC7CLF4Y4+DyxEAAe4iRIB3PzD6DP8IcBejnncPagCL/bAIgQB34fsc5P2PtM8IgwBHcMjJqQiEAHfBm+JhBQGO4IDlkwiEAHdx2PIbuFhv+MPFQ4C7ODx0Xo2OOiAIAhwBz9QIhQB34XvOlhYaoRDgLg5+dl7pcACqMEIgwF2EWDV1bZwAwz8C3IVOzfAd4omrXGr4x13Vg++jb6YmudTwj7uqh733fgOsM6YZzIJvBLiH3Q/+NyDMB3pNCy4u3k7Yw+57/wNZM9PDbu2NGwjqJiauDrmvpxufXiv6+f+v63fw8SjrZDgLLBwC3INO0NBAls+2V220jurZNXw6h8K6ODfibsye/UjQnNR/nnQcGk/IX/DNsbp+EeAetAVQVaQ56fe5dXGu4X54YTPASwsj7uZ8o/CHmkJ/Y7aRfb3eaBNkj3gGPsNOgNZPN7G1RR36fh8/uJS96LxqR6Kf/9H9MRa2eEKAz7C5FaZS3l6w0/goaArchMeFKPkHwrVxbr+quIJn0LNqiFZPVSjEmx98U7UNVS016PWXe6NU4ooI8DnWN8O8DuX+H0eTnxdeWgjb7uv3/vMd9lpWQYDPEep9Rrp5by+kOy+s7+/mfPhWXyPzFrqRVHHlzpFPgYTwTScg87NphjhmZdTgGMohwH1YexPupdx3b40mN5ij6tuMuHabKlweV60PGo0OdTB7ioM5WjEWW5PNHqVw1fq09ibcu33zqZpUQjzTjN/Ws1urHK5an9bWW0Ffj5JSiOv4HiaYEy6Fq9YnLa1cfRWuCku+wOHmXL2DOnUEmGOHyiHABagKh17Dqxv57rcj7k+3RpKfJ0b9CHBBKy/ivOhIU0yPH4xdqD3EV37HB1ZRBLignc6c8MZW2FY6p5ZSK7b0bNyMOM3CTiE7CHAJz1+2or7vV1Msj74by4IcoyKHOMygH4fhptsHFgEuQRXqx5fx7zYFWRX5ycNL2UqpUFV5512cDuNLvAS9ONawlaQ10jpSJsZ64S+d3iCvm3777XGntW9nx9fsfqh+JK5+Nq0Qi43WvTgCXMHqq5abma53g75Gqmen9fX/alz1CBtNmenfj7k6yvIxQ3Wiha5AN/r3K4fJtX55hVarvVTy8AB9OMV0GGdwf+AQ4IpU4f75LN27Tzt9HtwbKzynrNF2zXvHsvOWClwGAfZAN18dg1r9UnuthSFF6WeK1doS4HIIsCeqVrHbziLUUpdZornc6S5iDC5p8A3FEWCPVn9KO8RlTpVUeJ8u/xLsUAPR780UUjkE2LOUQ6x11jPN4n/l+WDdaqDznEOdO3YREOAAFOJUn4mrTA3p51KQNU/sM8g8/5bHPHAgeibWAND9O2mdtlF147yCm2/o0IeBXlyuAwDKfjDotBMWcJRHBQ5IlUUVa1Bv0O1squnkVSllvd5kAXQVBDiwfBAo5pyqFbo2od5+cVEQ4Ag0CKRnYrWedVfjlLqBlEfsrSDAEWnwJx8Eqsve+zQCrA+SOq/DoCDAkeWDQE+X63k23txKIzRUXz8IcE00Qv23f/wSta3Odim9q/+Zc6Pz3Ev19YNppJrpRtaXXrGinUMhp5zUvqfg+Uu2HvlCgBORB1nzqYtzDTc77ffoHC3CSGEAS4N5zPv6Q4ATo7lVfV253MoWXegMrKob6xWaFKax9PzNdJpfBDhRqlL7n6qy2mqFWeuY9QaDfttsfRCoXd1NYOS5rnPEBh0BNuB0mGVifOgk1Ncb2VJGbVLIdxnp12qqaHO7HXQHURH6ngZ5RVqdCLBBqqj62jCwiknbBJefEd5QCDCCUWgV3hRa+EFFgBEEbXMcBBjeabR55UWLUzYiIMDwRoHVK1iZKoqHAMMLqm49CDAqyxefID42MwCGEWDAMAIMGEaAAcMIMGAYAQYMI8CAYQQYMIwAA4YRYMAwAgwYRoABwwgwYBgBBgwjwIBhBBgwjAADhhFgwDACDBhGgAHDCDBgGAEGDCPAgGEEGDCMAAOGEWDAMAIMGEaAAcMIMGAYAQYMI8CAYQQYMIwAA4YRYMAwAgwYRoABwwgwYBgBBgwjwIBhBBgwjAADhhFgwDACDBhGgAHDCDBgGAEGDCPAgGEEGDCMAAOGEWDAMAIMGEaAAcMIMGAYAQYMI8CAYQQYMIwAA4YRYMAwAgwYRoABwwgwYBgBBgwjwIBhBBgwjAADhv0XZkN9IbEGbp4AAAAASUVORK5CYII=",Ov=({isOpen:r,onClose:i})=>{const[s,l]=K.useState(""),[c,d]=K.useState(""),[p,m]=K.useState(""),[w,v]=K.useState(null),[S,j]=K.useState(null),[R,L]=K.useState(""),{fetchCsrfToken:T}=rt(),N=K.useCallback(()=>{S&&URL.revokeObjectURL(S),j(null),v(null),l(""),d(""),m(""),L("")},[S]),_=K.useCallback(()=>{N(),i()},[]),V=B=>{var I;const W=(I=B.target.files)==null?void 0:I[0];if(W){v(W);const M=new FileReader;M.onloadend=()=>{j(M.result)},M.readAsDataURL(W)}},U=async B=>{B.preventDefault(),L("");try{const W=new FormData;W.append("userCreateRequest",new Blob([JSON.stringify({email:s,username:c,password:p})],{type:"application/json"})),w&&W.append("profile",w),await yv(W),await T(),i()}catch{L("회원가입에 실패했습니다.")}};return r?h.jsx(ch,{children:h.jsxs(dh,{children:[h.jsx("h2",{children:"계정 만들기"}),h.jsxs("form",{onSubmit:U,children:[h.jsxs(Ji,{children:[h.jsxs(Zi,{children:["이메일 ",h.jsx(Da,{children:"*"})]}),h.jsx(Ro,{type:"email",value:s,onChange:B=>l(B.target.value),required:!0})]}),h.jsxs(Ji,{children:[h.jsxs(Zi,{children:["사용자명 ",h.jsx(Da,{children:"*"})]}),h.jsx(Ro,{type:"text",value:c,onChange:B=>d(B.target.value),required:!0})]}),h.jsxs(Ji,{children:[h.jsxs(Zi,{children:["비밀번호 ",h.jsx(Da,{children:"*"})]}),h.jsx(Ro,{type:"password",value:p,onChange:B=>m(B.target.value),required:!0})]}),h.jsxs(Ji,{children:[h.jsx(Zi,{children:"프로필 이미지"}),h.jsxs(Av,{children:[h.jsx(Rv,{src:S||St,alt:"profile"}),h.jsx(Pv,{type:"file",accept:"image/*",onChange:V,id:"profile-image"}),h.jsx(Tv,{htmlFor:"profile-image",children:"이미지 변경"})]})]}),R&&h.jsx(ph,{children:R}),h.jsx(fh,{type:"submit",children:"계속하기"}),h.jsx(Nv,{onClick:_,children:"이미 계정이 있으신가요?"})]})]})}):null},Mv=({isOpen:r,onClose:i})=>{const[s,l]=K.useState(""),[c,d]=K.useState(""),[p,m]=K.useState(""),[w,v]=K.useState(!1),{login:S}=rt(),{fetchUsers:j}=Rr(),R=K.useCallback(()=>{l(""),d(""),m(""),v(!1)},[]),L=K.useCallback(()=>{R(),v(!0)},[R,i]),T=async()=>{var N;try{await S(s,c),await j(),R(),i()}catch(_){console.error("로그인 에러:",_),((N=_.response)==null?void 0:N.status)===401?m("아이디 또는 비밀번호가 올바르지 않습니다."):m("로그인에 실패했습니다.")}};return r?h.jsxs(h.Fragment,{children:[h.jsx(ch,{children:h.jsxs(dh,{children:[h.jsx("h2",{children:"돌아오신 것을 환영해요!"}),h.jsxs("form",{onSubmit:N=>{N.preventDefault(),T()},children:[h.jsx(Ro,{type:"text",placeholder:"사용자 이름",value:s,onChange:N=>l(N.target.value)}),h.jsx(Ro,{type:"password",placeholder:"비밀번호",value:c,onChange:N=>d(N.target.value)}),p&&h.jsx(ph,{children:p}),h.jsx(fh,{type:"submit",children:"로그인"})]}),h.jsxs(Ev,{children:["계정이 필요한가요? ",h.jsx(jv,{onClick:L,children:"가입하기"})]})]})}),h.jsx(Ov,{isOpen:w,onClose:()=>v(!1)})]}):null},Lv=async r=>(await $e.get(`/channels?userId=${r}`)).data,Iv=async r=>(await $e.post("/channels/public",r)).data,Dv=async r=>{const i={participantIds:r};return(await $e.post("/channels/private",i)).data},zv=async(r,i)=>(await $e.patch(`/channels/${r}`,i)).data,$v=async r=>{await $e.delete(`/channels/${r}`)},Bv=async r=>(await $e.get("/readStatuses",{params:{userId:r}})).data,Fv=async(r,i)=>{const s={newLastReadAt:i};return(await $e.patch(`/readStatuses/${r}`,s)).data},bv=async(r,i,s)=>{const l={userId:r,channelId:i,lastReadAt:s};return(await $e.post("/readStatuses",l)).data},Po=Tr((r,i)=>({readStatuses:{},fetchReadStatuses:async()=>{try{const{currentUser:s}=rt.getState();if(!s)return;const c=(await Bv(s.id)).reduce((d,p)=>(d[p.channelId]={id:p.id,lastReadAt:p.lastReadAt},d),{});r({readStatuses:c})}catch(s){console.error("읽음 상태 조회 실패:",s)}},updateReadStatus:async s=>{try{const{currentUser:l}=rt.getState();if(!l)return;const c=i().readStatuses[s];let d;c?d=await Fv(c.id,new Date().toISOString()):d=await bv(l.id,s,new Date().toISOString()),r(p=>({readStatuses:{...p.readStatuses,[s]:{id:d.id,lastReadAt:d.lastReadAt}}}))}catch(l){console.error("읽음 상태 업데이트 실패:",l)}},hasUnreadMessages:(s,l)=>{const c=i().readStatuses[s],d=c==null?void 0:c.lastReadAt;return!d||new Date(l)>new Date(d)}})),jn=Tr((r,i)=>({channels:[],pollingInterval:null,loading:!1,error:null,fetchChannels:async s=>{r({loading:!0,error:null});try{const l=await Lv(s);r(d=>{const p=new Set(d.channels.map(S=>S.id)),m=l.filter(S=>!p.has(S.id));return{channels:[...d.channels.filter(S=>l.some(j=>j.id===S.id)),...m],loading:!1}});const{fetchReadStatuses:c}=Po.getState();return c(),l}catch(l){return r({error:l,loading:!1}),[]}},startPolling:s=>{const l=i().pollingInterval;l&&clearInterval(l);const c=setInterval(()=>{i().fetchChannels(s)},3e3);r({pollingInterval:c})},stopPolling:()=>{const s=i().pollingInterval;s&&(clearInterval(s),r({pollingInterval:null}))},createPublicChannel:async s=>{try{const l=await Iv(s);return r(c=>c.channels.some(p=>p.id===l.id)?c:{channels:[...c.channels,{...l,participantIds:[],lastMessageAt:new Date().toISOString()}]}),l}catch(l){throw console.error("공개 채널 생성 실패:",l),l}},createPrivateChannel:async s=>{try{const l=await Dv(s);return r(c=>c.channels.some(p=>p.id===l.id)?c:{channels:[...c.channels,{...l,participantIds:s,lastMessageAt:new Date().toISOString()}]}),l}catch(l){throw console.error("비공개 채널 생성 실패:",l),l}},updatePublicChannel:async(s,l)=>{try{const c=await zv(s,l);return r(d=>({channels:d.channels.map(p=>p.id===s?{...p,...c}:p)})),c}catch(c){throw console.error("채널 수정 실패:",c),c}},deleteChannel:async s=>{try{await $v(s),r(l=>({channels:l.channels.filter(c=>c.id!==s)}))}catch(l){throw console.error("채널 삭제 실패:",l),l}}})),Uv=async r=>(await $e.get(`/binaryContents/${r}`)).data,Hv=async r=>({blob:(await $e.get(`/binaryContents/${r}/download`,{responseType:"blob"})).data}),An=Tr((r,i)=>({binaryContents:{},fetchBinaryContent:async s=>{if(i().binaryContents[s])return i().binaryContents[s];try{const l=await Uv(s),{contentType:c,fileName:d,size:p}=l,m=await Hv(s),w=URL.createObjectURL(m.blob),v={url:w,contentType:c,fileName:d,size:p,revokeUrl:()=>URL.revokeObjectURL(w)};return r(S=>({binaryContents:{...S.binaryContents,[s]:v}})),v}catch(l){return console.error("첨부파일 정보 조회 실패:",l),null}},clearBinaryContent:s=>{const{binaryContents:l}=i(),c=l[s];c!=null&&c.revokeUrl&&(c.revokeUrl(),r(d=>{const{[s]:p,...m}=d.binaryContents;return{binaryContents:m}}))},clearBinaryContents:s=>{const{binaryContents:l}=i(),c=[];s.forEach(d=>{const p=l[d];p&&(p.revokeUrl&&p.revokeUrl(),c.push(d))}),c.length>0&&r(d=>{const p={...d.binaryContents};return c.forEach(m=>{delete p[m]}),{binaryContents:p}})},clearAllBinaryContents:()=>{const{binaryContents:s}=i();Object.values(s).forEach(l=>{l.revokeUrl&&l.revokeUrl()}),r({binaryContents:{}})}})),$o=C.div` + position: absolute; + bottom: -3px; + right: -3px; + width: 16px; + height: 16px; + border-radius: 50%; + background: ${r=>r.$online?Y.colors.status.online:Y.colors.status.offline}; + border: 4px solid ${r=>r.$background||Y.colors.background.secondary}; +`;C.div` + width: 8px; + height: 8px; + border-radius: 50%; + margin-right: 8px; + background: ${r=>Y.colors.status[r.status||"offline"]||Y.colors.status.offline}; +`;const Or=C.div` + position: relative; + width: ${r=>r.$size||"32px"}; + height: ${r=>r.$size||"32px"}; + flex-shrink: 0; + margin: ${r=>r.$margin||"0"}; +`,nn=C.img` + width: 100%; + height: 100%; + border-radius: 50%; + object-fit: cover; + border: ${r=>r.$border||"none"}; +`;function Vv({isOpen:r,onClose:i,user:s}){var M,H;const[l,c]=K.useState(s.username),[d,p]=K.useState(s.email),[m,w]=K.useState(""),[v,S]=K.useState(null),[j,R]=K.useState(""),[L,T]=K.useState(null),{binaryContents:N,fetchBinaryContent:_}=An(),{logout:V,refreshToken:U}=rt();K.useEffect(()=>{var ie;(ie=s.profile)!=null&&ie.id&&!N[s.profile.id]&&_(s.profile.id)},[s.profile,N,_]);const B=()=>{c(s.username),p(s.email),w(""),S(null),T(null),R(""),i()},W=ie=>{var Oe;const ve=(Oe=ie.target.files)==null?void 0:Oe[0];if(ve){S(ve);const ot=new FileReader;ot.onloadend=()=>{T(ot.result)},ot.readAsDataURL(ve)}},I=async ie=>{ie.preventDefault(),R("");try{const ve=new FormData,Oe={};l!==s.username&&(Oe.newUsername=l),d!==s.email&&(Oe.newEmail=d),m&&(Oe.newPassword=m),(Object.keys(Oe).length>0||v)&&(ve.append("userUpdateRequest",new Blob([JSON.stringify(Oe)],{type:"application/json"})),v&&ve.append("profile",v),await kv(s.id,ve),await U()),i()}catch{R("사용자 정보 수정에 실패했습니다.")}};return r?h.jsx(Wv,{children:h.jsxs(qv,{children:[h.jsx("h2",{children:"프로필 수정"}),h.jsxs("form",{onSubmit:I,children:[h.jsxs(es,{children:[h.jsx(ts,{children:"프로필 이미지"}),h.jsxs(Qv,{children:[h.jsx(Gv,{src:L||((M=s.profile)!=null&&M.id?(H=N[s.profile.id])==null?void 0:H.url:void 0)||St,alt:"profile"}),h.jsx(Kv,{type:"file",accept:"image/*",onChange:W,id:"profile-image"}),h.jsx(Xv,{htmlFor:"profile-image",children:"이미지 변경"})]})]}),h.jsxs(es,{children:[h.jsxs(ts,{children:["사용자명 ",h.jsx(Jf,{children:"*"})]}),h.jsx(za,{type:"text",value:l,onChange:ie=>c(ie.target.value),required:!0})]}),h.jsxs(es,{children:[h.jsxs(ts,{children:["이메일 ",h.jsx(Jf,{children:"*"})]}),h.jsx(za,{type:"email",value:d,onChange:ie=>p(ie.target.value),required:!0})]}),h.jsxs(es,{children:[h.jsx(ts,{children:"새 비밀번호"}),h.jsx(za,{type:"password",placeholder:"변경하지 않으려면 비워두세요",value:m,onChange:ie=>w(ie.target.value)})]}),j&&h.jsx(Yv,{children:j}),h.jsxs(Jv,{children:[h.jsx(Xf,{type:"button",onClick:B,$secondary:!0,children:"취소"}),h.jsx(Xf,{type:"submit",children:"저장"})]})]}),h.jsx(Zv,{onClick:V,children:"로그아웃"})]})}):null}const Wv=C.div` + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +`,qv=C.div` + background: ${({theme:r})=>r.colors.background.secondary}; + padding: 32px; + border-radius: 5px; + width: 100%; + max-width: 480px; + + h2 { + color: ${({theme:r})=>r.colors.text.primary}; + margin-bottom: 24px; + text-align: center; + font-size: 24px; + } +`,za=C.input` + width: 100%; + padding: 10px; + margin-bottom: 10px; + border: none; + border-radius: 4px; + background: ${({theme:r})=>r.colors.background.input}; + color: ${({theme:r})=>r.colors.text.primary}; + + &::placeholder { + color: ${({theme:r})=>r.colors.text.muted}; + } + + &:focus { + outline: none; + box-shadow: 0 0 0 2px ${({theme:r})=>r.colors.brand.primary}; + } +`,Xf=C.button` + width: 100%; + padding: 10px; + border: none; + border-radius: 4px; + background: ${({$secondary:r,theme:i})=>r?"transparent":i.colors.brand.primary}; + color: ${({theme:r})=>r.colors.text.primary}; + cursor: pointer; + font-weight: 500; + + &:hover { + background: ${({$secondary:r,theme:i})=>r?i.colors.background.hover:i.colors.brand.hover}; + } +`,Yv=C.div` + color: ${({theme:r})=>r.colors.status.error}; + font-size: 14px; + margin-bottom: 10px; +`,Qv=C.div` + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 20px; +`,Gv=C.img` + width: 100px; + height: 100px; + border-radius: 50%; + margin-bottom: 10px; + object-fit: cover; +`,Kv=C.input` + display: none; +`,Xv=C.label` + color: ${({theme:r})=>r.colors.brand.primary}; + cursor: pointer; + font-size: 14px; + + &:hover { + text-decoration: underline; + } +`,Jv=C.div` + display: flex; + gap: 10px; + margin-top: 20px; +`,Zv=C.button` + width: 100%; + padding: 10px; + margin-top: 16px; + border: none; + border-radius: 4px; + background: transparent; + color: ${({theme:r})=>r.colors.status.error}; + cursor: pointer; + font-weight: 500; + + &:hover { + background: ${({theme:r})=>r.colors.status.error}20; + } +`,es=C.div` + margin-bottom: 20px; +`,ts=C.label` + display: block; + color: ${({theme:r})=>r.colors.text.muted}; + font-size: 12px; + font-weight: 700; + margin-bottom: 8px; +`,Jf=C.span` + color: ${({theme:r})=>r.colors.status.error}; +`,ex=C.div` + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.5rem 0.75rem; + background-color: ${({theme:r})=>r.colors.background.tertiary}; + width: 100%; + height: 52px; +`,tx=C(Or)``;C(nn)``;const nx=C.div` + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + justify-content: center; +`,rx=C.div` + font-weight: 500; + color: ${({theme:r})=>r.colors.text.primary}; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 0.875rem; + line-height: 1.2; +`,ox=C.div` + font-size: 0.75rem; + color: ${({theme:r})=>r.colors.text.secondary}; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + line-height: 1.2; +`,ix=C.div` + display: flex; + align-items: center; + flex-shrink: 0; +`,sx=C.button` + background: none; + border: none; + padding: 0.25rem; + cursor: pointer; + color: ${({theme:r})=>r.colors.text.secondary}; + font-size: 18px; + + &:hover { + color: ${({theme:r})=>r.colors.text.primary}; + } +`;function lx({user:r}){var d,p;const[i,s]=K.useState(!1),{binaryContents:l,fetchBinaryContent:c}=An();return K.useEffect(()=>{var m;(m=r.profile)!=null&&m.id&&!l[r.profile.id]&&c(r.profile.id)},[r.profile,l,c]),h.jsxs(h.Fragment,{children:[h.jsxs(ex,{children:[h.jsxs(tx,{children:[h.jsx(nn,{src:(d=r.profile)!=null&&d.id?(p=l[r.profile.id])==null?void 0:p.url:St,alt:r.username}),h.jsx($o,{$online:!0})]}),h.jsxs(nx,{children:[h.jsx(rx,{children:r.username}),h.jsx(ox,{children:"온라인"})]}),h.jsx(ix,{children:h.jsx(sx,{onClick:()=>s(!0),children:"⚙️"})})]}),h.jsx(Vv,{isOpen:i,onClose:()=>s(!1),user:r})]})}const ax=C.div` + width: 240px; + background: ${Y.colors.background.secondary}; + border-right: 1px solid ${Y.colors.border.primary}; + display: flex; + flex-direction: column; +`,ux=C.div` + flex: 1; + overflow-y: auto; +`,cx=C.div` + padding: 16px; + font-size: 16px; + font-weight: bold; + color: ${Y.colors.text.primary}; +`,hu=C.div` + height: 34px; + padding: 0 8px; + margin: 1px 8px; + display: flex; + align-items: center; + gap: 6px; + color: ${r=>r.$hasUnread?r.theme.colors.text.primary:r.theme.colors.text.muted}; + font-weight: ${r=>r.$hasUnread?"600":"normal"}; + cursor: pointer; + background: ${r=>r.$isActive?r.theme.colors.background.hover:"transparent"}; + border-radius: 4px; + + &:hover { + background: ${r=>r.theme.colors.background.hover}; + color: ${r=>r.theme.colors.text.primary}; + } +`,Zf=C.div` + margin-bottom: 8px; +`,tu=C.div` + padding: 8px 16px; + display: flex; + align-items: center; + color: ${Y.colors.text.muted}; + text-transform: uppercase; + font-size: 12px; + font-weight: 600; + cursor: pointer; + user-select: none; + + & > span:nth-child(2) { + flex: 1; + margin-right: auto; + } + + &:hover { + color: ${Y.colors.text.primary}; + } +`,ep=C.span` + margin-right: 4px; + font-size: 10px; + transition: transform 0.2s; + transform: rotate(${r=>r.$folded?"-90deg":"0deg"}); +`,tp=C.div` + display: ${r=>r.$folded?"none":"block"}; +`,nu=C(hu)` + height: ${r=>r.hasSubtext?"42px":"34px"}; +`,dx=C(Or)` + width: 32px; + height: 32px; + margin: 0 8px; +`,np=C.div` + font-size: 16px; + line-height: 18px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: ${r=>r.$isActive||r.$hasUnread?r.theme.colors.text.primary:r.theme.colors.text.muted}; + font-weight: ${r=>r.$hasUnread?"600":"normal"}; +`;C($o)` + border-color: ${Y.colors.background.primary}; +`;const rp=C.button` + background: none; + border: none; + color: ${Y.colors.text.muted}; + font-size: 18px; + padding: 0; + cursor: pointer; + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 0.2s, color 0.2s; + + ${tu}:hover & { + opacity: 1; + } + + &:hover { + color: ${Y.colors.text.primary}; + } +`,fx=C(Or)` + width: 40px; + height: 24px; + margin: 0 8px; +`,px=C.div` + font-size: 12px; + line-height: 13px; + color: ${Y.colors.text.muted}; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +`,op=C.div` + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + justify-content: center; + gap: 2px; +`,hh=C.div` + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.85); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +`,mh=C.div` + background: ${Y.colors.background.primary}; + border-radius: 4px; + width: 440px; + max-width: 90%; +`,gh=C.div` + padding: 16px; + display: flex; + justify-content: space-between; + align-items: center; +`,yh=C.h2` + color: ${Y.colors.text.primary}; + font-size: 20px; + font-weight: 600; + margin: 0; +`,vh=C.div` + padding: 0 16px 16px; +`,xh=C.form` + display: flex; + flex-direction: column; + gap: 16px; +`,To=C.div` + display: flex; + flex-direction: column; + gap: 8px; +`,_o=C.label` + color: ${Y.colors.text.primary}; + font-size: 12px; + font-weight: 600; + text-transform: uppercase; +`,wh=C.p` + color: ${Y.colors.text.muted}; + font-size: 14px; + margin: -4px 0 0; +`,Lo=C.input` + padding: 10px; + background: ${Y.colors.background.tertiary}; + border: none; + border-radius: 3px; + color: ${Y.colors.text.primary}; + font-size: 16px; + + &:focus { + outline: none; + box-shadow: 0 0 0 2px ${Y.colors.status.online}; + } + + &::placeholder { + color: ${Y.colors.text.muted}; + } +`,Sh=C.button` + margin-top: 8px; + padding: 12px; + background: ${Y.colors.status.online}; + color: white; + border: none; + border-radius: 3px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: background 0.2s; + + &:hover { + background: #3ca374; + } +`,kh=C.button` + background: none; + border: none; + color: ${Y.colors.text.muted}; + font-size: 24px; + cursor: pointer; + padding: 4px; + line-height: 1; + + &:hover { + color: ${Y.colors.text.primary}; + } +`,hx=C(Lo)` + margin-bottom: 8px; +`,mx=C.div` + max-height: 300px; + overflow-y: auto; + background: ${Y.colors.background.tertiary}; + border-radius: 4px; +`,gx=C.div` + display: flex; + align-items: center; + padding: 8px 12px; + cursor: pointer; + transition: background 0.2s; + + &:hover { + background: ${Y.colors.background.hover}; + } + + & + & { + border-top: 1px solid ${Y.colors.border.primary}; + } +`,yx=C.input` + margin-right: 12px; + width: 16px; + height: 16px; + cursor: pointer; +`,ip=C.img` + width: 32px; + height: 32px; + border-radius: 50%; + margin-right: 12px; +`,vx=C.div` + flex: 1; + min-width: 0; +`,xx=C.div` + color: ${Y.colors.text.primary}; + font-size: 14px; + font-weight: 500; +`,wx=C.div` + color: ${Y.colors.text.muted}; + font-size: 12px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +`,Sx=C.div` + padding: 16px; + text-align: center; + color: ${Y.colors.text.muted}; +`,Ch=C.div` + color: ${Y.colors.status.error}; + font-size: 14px; + padding: 8px 0; + text-align: center; + background-color: ${({theme:r})=>r.colors.background.tertiary}; + border-radius: 4px; + margin-bottom: 8px; +`,$a=C.div` + position: relative; + margin-left: auto; + z-index: 99999; +`,Ba=C.button` + background: none; + border: none; + color: ${({theme:r})=>r.colors.text.muted}; + font-size: 16px; + cursor: pointer; + padding: 4px; + border-radius: 3px; + opacity: 0; + transition: opacity 0.2s, background 0.2s; + + &:hover { + background: ${({theme:r})=>r.colors.background.hover}; + color: ${({theme:r})=>r.colors.text.primary}; + } + + ${hu}:hover &, + ${nu}:hover & { + opacity: 1; + } +`,Fa=C.div` + position: absolute; + top: 100%; + right: 0; + background: ${({theme:r})=>r.colors.background.primary}; + border: 1px solid ${({theme:r})=>r.colors.border.primary}; + border-radius: 4px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3); + min-width: 120px; + z-index: 100000; +`,ns=C.div` + padding: 8px 12px; + color: ${({theme:r})=>r.colors.text.primary}; + cursor: pointer; + font-size: 14px; + display: flex; + align-items: center; + gap: 8px; + + &:hover { + background: ${({theme:r})=>r.colors.background.hover}; + } + + &:first-child { + border-radius: 4px 4px 0 0; + } + + &:last-child { + border-radius: 0 0 4px 4px; + } + + &:only-child { + border-radius: 4px; + } +`;function kx(){return h.jsx(cx,{children:"채널 목록"})}var En=(r=>(r.USER="USER",r.CHANNEL_MANAGER="CHANNEL_MANAGER",r.ADMIN="ADMIN",r))(En||{});function Cx({isOpen:r,channel:i,onClose:s,onUpdateSuccess:l}){const[c,d]=K.useState({name:"",description:""}),[p,m]=K.useState(""),[w,v]=K.useState(!1),{updatePublicChannel:S}=jn();K.useEffect(()=>{i&&r&&(d({name:i.name||"",description:i.description||""}),m(""))},[i,r]);const j=L=>{const{name:T,value:N}=L.target;d(_=>({..._,[T]:N}))},R=async L=>{var T,N;if(L.preventDefault(),!!i){m(""),v(!0);try{if(!c.name.trim()){m("채널 이름을 입력해주세요."),v(!1);return}const _={newName:c.name.trim(),newDescription:c.description.trim()},V=await S(i.id,_);l(V)}catch(_){console.error("채널 수정 실패:",_),m(((N=(T=_.response)==null?void 0:T.data)==null?void 0:N.message)||"채널 수정에 실패했습니다. 다시 시도해주세요.")}finally{v(!1)}}};return!r||!i||i.type!=="PUBLIC"?null:h.jsx(hh,{onClick:s,children:h.jsxs(mh,{onClick:L=>L.stopPropagation(),children:[h.jsxs(gh,{children:[h.jsx(yh,{children:"채널 수정"}),h.jsx(kh,{onClick:s,children:"×"})]}),h.jsx(vh,{children:h.jsxs(xh,{onSubmit:R,children:[p&&h.jsx(Ch,{children:p}),h.jsxs(To,{children:[h.jsx(_o,{children:"채널 이름"}),h.jsx(Lo,{name:"name",value:c.name,onChange:j,placeholder:"새로운-채널",required:!0,disabled:w})]}),h.jsxs(To,{children:[h.jsx(_o,{children:"채널 설명"}),h.jsx(wh,{children:"이 채널의 주제를 설명해주세요."}),h.jsx(Lo,{name:"description",value:c.description,onChange:j,placeholder:"채널 설명을 입력하세요",disabled:w})]}),h.jsx(Sh,{type:"submit",disabled:w,children:w?"수정 중...":"채널 수정"})]})})]})})}function sp({channel:r,isActive:i,onClick:s,hasUnread:l}){var U;const{currentUser:c}=rt(),{binaryContents:d}=An(),{deleteChannel:p}=jn(),[m,w]=K.useState(null),[v,S]=K.useState(!1),j=(c==null?void 0:c.role)===En.ADMIN||(c==null?void 0:c.role)===En.CHANNEL_MANAGER;K.useEffect(()=>{const B=()=>{m&&w(null)};if(m)return document.addEventListener("click",B),()=>document.removeEventListener("click",B)},[m]);const R=B=>{w(m===B?null:B)},L=()=>{w(null),S(!0)},T=B=>{S(!1),console.log("Channel updated successfully:",B)},N=()=>{S(!1)},_=async B=>{var I;w(null);const W=r.type==="PUBLIC"?r.name:r.type==="PRIVATE"&&r.participants.length>2?`그룹 채팅 (멤버 ${r.participants.length}명)`:((I=r.participants.filter(M=>M.id!==(c==null?void 0:c.id))[0])==null?void 0:I.username)||"1:1 채팅";if(confirm(`"${W}" 채널을 삭제하시겠습니까?`))try{await p(B),console.log("Channel deleted successfully:",B)}catch(M){console.error("Channel delete failed:",M),alert("채널 삭제에 실패했습니다. 다시 시도해주세요.")}};let V;if(r.type==="PUBLIC")V=h.jsxs(hu,{$isActive:i,onClick:s,$hasUnread:l,children:["# ",r.name,j&&h.jsxs($a,{children:[h.jsx(Ba,{onClick:B=>{B.stopPropagation(),R(r.id)},children:"⋯"}),m===r.id&&h.jsxs(Fa,{onClick:B=>B.stopPropagation(),children:[h.jsx(ns,{onClick:()=>L(),children:"✏️ 수정"}),h.jsx(ns,{onClick:()=>_(r.id),children:"🗑️ 삭제"})]})]})]});else{const B=r.participants;if(B.length>2){const W=B.filter(I=>I.id!==(c==null?void 0:c.id)).map(I=>I.username).join(", ");V=h.jsxs(nu,{$isActive:i,onClick:s,children:[h.jsx(fx,{children:B.filter(I=>I.id!==(c==null?void 0:c.id)).slice(0,2).map((I,M)=>{var H;return h.jsx(nn,{src:I.profile?(H=d[I.profile.id])==null?void 0:H.url:St,style:{position:"absolute",left:M*16,zIndex:2-M,width:"24px",height:"24px",border:"2px solid #2a2a2a"}},I.id)})}),h.jsxs(op,{children:[h.jsx(np,{$hasUnread:l,children:W}),h.jsxs(px,{children:["멤버 ",B.length,"명"]})]}),j&&h.jsxs($a,{children:[h.jsx(Ba,{onClick:I=>{I.stopPropagation(),R(r.id)},children:"⋯"}),m===r.id&&h.jsx(Fa,{onClick:I=>I.stopPropagation(),children:h.jsx(ns,{onClick:()=>_(r.id),children:"🗑️ 삭제"})})]})]})}else{const W=B.filter(I=>I.id!==(c==null?void 0:c.id))[0];V=W?h.jsxs(nu,{$isActive:i,onClick:s,children:[h.jsxs(dx,{children:[h.jsx(nn,{src:W.profile?(U=d[W.profile.id])==null?void 0:U.url:St,alt:"profile"}),h.jsx($o,{$online:W.online})]}),h.jsx(op,{children:h.jsx(np,{$hasUnread:l,children:W.username})}),j&&h.jsxs($a,{children:[h.jsx(Ba,{onClick:I=>{I.stopPropagation(),R(r.id)},children:"⋯"}),m===r.id&&h.jsx(Fa,{onClick:I=>I.stopPropagation(),children:h.jsx(ns,{onClick:()=>_(r.id),children:"🗑️ 삭제"})})]})]}):h.jsx("div",{})}}return h.jsxs(h.Fragment,{children:[V,h.jsx(Cx,{isOpen:v,channel:r,onClose:N,onUpdateSuccess:T})]})}function Ex({isOpen:r,type:i,onClose:s,onCreateSuccess:l}){const[c,d]=K.useState({name:"",description:""}),[p,m]=K.useState(""),[w,v]=K.useState([]),[S,j]=K.useState(""),R=Rr(I=>I.users),L=An(I=>I.binaryContents),{currentUser:T}=rt(),N=K.useMemo(()=>R.filter(I=>I.id!==(T==null?void 0:T.id)).filter(I=>I.username.toLowerCase().includes(p.toLowerCase())||I.email.toLowerCase().includes(p.toLowerCase())),[p,R,T]),_=jn(I=>I.createPublicChannel),V=jn(I=>I.createPrivateChannel),U=I=>{const{name:M,value:H}=I.target;d(ie=>({...ie,[M]:H}))},B=I=>{v(M=>M.includes(I)?M.filter(H=>H!==I):[...M,I])},W=async I=>{var M,H;I.preventDefault(),j("");try{let ie;if(i==="PUBLIC"){if(!c.name.trim()){j("채널 이름을 입력해주세요.");return}const ve={name:c.name,description:c.description};ie=await _(ve)}else{if(w.length===0){j("대화 상대를 선택해주세요.");return}const ve=(T==null?void 0:T.id)&&[...w,T.id]||w;ie=await V(ve)}l(ie)}catch(ie){console.error("채널 생성 실패:",ie),j(((H=(M=ie.response)==null?void 0:M.data)==null?void 0:H.message)||"채널 생성에 실패했습니다. 다시 시도해주세요.")}};return r?h.jsx(hh,{onClick:s,children:h.jsxs(mh,{onClick:I=>I.stopPropagation(),children:[h.jsxs(gh,{children:[h.jsx(yh,{children:i==="PUBLIC"?"채널 만들기":"개인 메시지 시작하기"}),h.jsx(kh,{onClick:s,children:"×"})]}),h.jsx(vh,{children:h.jsxs(xh,{onSubmit:W,children:[S&&h.jsx(Ch,{children:S}),i==="PUBLIC"?h.jsxs(h.Fragment,{children:[h.jsxs(To,{children:[h.jsx(_o,{children:"채널 이름"}),h.jsx(Lo,{name:"name",value:c.name,onChange:U,placeholder:"새로운-채널",required:!0})]}),h.jsxs(To,{children:[h.jsx(_o,{children:"채널 설명"}),h.jsx(wh,{children:"이 채널의 주제를 설명해주세요."}),h.jsx(Lo,{name:"description",value:c.description,onChange:U,placeholder:"채널 설명을 입력하세요"})]})]}):h.jsxs(To,{children:[h.jsx(_o,{children:"사용자 검색"}),h.jsx(hx,{type:"text",value:p,onChange:I=>m(I.target.value),placeholder:"사용자명 또는 이메일로 검색"}),h.jsx(mx,{children:N.length>0?N.map(I=>h.jsxs(gx,{children:[h.jsx(yx,{type:"checkbox",checked:w.includes(I.id),onChange:()=>B(I.id)}),I.profile?h.jsx(ip,{src:L[I.profile.id].url}):h.jsx(ip,{src:St}),h.jsxs(vx,{children:[h.jsx(xx,{children:I.username}),h.jsx(wx,{children:I.email})]})]},I.id)):h.jsx(Sx,{children:"검색 결과가 없습니다."})})]}),h.jsx(Sh,{type:"submit",children:i==="PUBLIC"?"채널 만들기":"대화 시작하기"})]})})]})}):null}function jx({currentUser:r,activeChannel:i,onChannelSelect:s}){var W,I;const[l,c]=K.useState({PUBLIC:!1,PRIVATE:!1}),[d,p]=K.useState({isOpen:!1,type:null}),m=jn(M=>M.channels),w=jn(M=>M.fetchChannels),v=jn(M=>M.startPolling),S=jn(M=>M.stopPolling),j=Po(M=>M.fetchReadStatuses),R=Po(M=>M.updateReadStatus),L=Po(M=>M.hasUnreadMessages);K.useEffect(()=>{if(r)return w(r.id),j(),v(r.id),()=>{S()}},[r,w,j,v,S]);const T=M=>{c(H=>({...H,[M]:!H[M]}))},N=(M,H)=>{H.stopPropagation(),p({isOpen:!0,type:M})},_=()=>{p({isOpen:!1,type:null})},V=async M=>{try{const ie=(await w(r.id)).find(ve=>ve.id===M.id);ie&&s(ie),_()}catch(H){console.error("채널 생성 실패:",H)}},U=M=>{s(M),R(M.id)},B=m.reduce((M,H)=>(M[H.type]||(M[H.type]=[]),M[H.type].push(H),M),{});return h.jsxs(ax,{children:[h.jsx(kx,{}),h.jsxs(ux,{children:[h.jsxs(Zf,{children:[h.jsxs(tu,{onClick:()=>T("PUBLIC"),children:[h.jsx(ep,{$folded:l.PUBLIC,children:"▼"}),h.jsx("span",{children:"일반 채널"}),h.jsx(rp,{onClick:M=>N("PUBLIC",M),children:"+"})]}),h.jsx(tp,{$folded:l.PUBLIC,children:(W=B.PUBLIC)==null?void 0:W.map(M=>h.jsx(sp,{channel:M,isActive:(i==null?void 0:i.id)===M.id,hasUnread:L(M.id,M.lastMessageAt),onClick:()=>U(M)},M.id))})]}),h.jsxs(Zf,{children:[h.jsxs(tu,{onClick:()=>T("PRIVATE"),children:[h.jsx(ep,{$folded:l.PRIVATE,children:"▼"}),h.jsx("span",{children:"개인 메시지"}),h.jsx(rp,{onClick:M=>N("PRIVATE",M),children:"+"})]}),h.jsx(tp,{$folded:l.PRIVATE,children:(I=B.PRIVATE)==null?void 0:I.map(M=>h.jsx(sp,{channel:M,isActive:(i==null?void 0:i.id)===M.id,hasUnread:L(M.id,M.lastMessageAt),onClick:()=>U(M)},M.id))})]})]}),h.jsx(Ax,{children:h.jsx(lx,{user:r})}),h.jsx(Ex,{isOpen:d.isOpen,type:d.type,onClose:_,onCreateSuccess:V})]})}const Ax=C.div` + margin-top: auto; + border-top: 1px solid ${({theme:r})=>r.colors.border.primary}; + background-color: ${({theme:r})=>r.colors.background.tertiary}; +`,Rx=C.div` + flex: 1; + display: flex; + flex-direction: column; + background: ${({theme:r})=>r.colors.background.primary}; +`,Px=C.div` + display: flex; + flex-direction: column; + height: 100%; + background: ${({theme:r})=>r.colors.background.primary}; +`,Tx=C(Px)` + justify-content: center; + align-items: center; + flex: 1; + padding: 0 20px; +`,_x=C.div` + text-align: center; + max-width: 400px; + padding: 20px; + margin-bottom: 80px; +`,Nx=C.div` + font-size: 48px; + margin-bottom: 16px; + animation: wave 2s infinite; + transform-origin: 70% 70%; + + @keyframes wave { + 0% { transform: rotate(0deg); } + 10% { transform: rotate(14deg); } + 20% { transform: rotate(-8deg); } + 30% { transform: rotate(14deg); } + 40% { transform: rotate(-4deg); } + 50% { transform: rotate(10deg); } + 60% { transform: rotate(0deg); } + 100% { transform: rotate(0deg); } + } +`,Ox=C.h2` + color: ${({theme:r})=>r.colors.text.primary}; + font-size: 28px; + font-weight: 700; + margin-bottom: 16px; +`,Mx=C.p` + color: ${({theme:r})=>r.colors.text.muted}; + font-size: 16px; + line-height: 1.6; + word-break: keep-all; +`,lp=C.div` + height: 48px; + padding: 0 16px; + background: ${Y.colors.background.primary}; + border-bottom: 1px solid ${Y.colors.border.primary}; + display: flex; + align-items: center; +`,ap=C.div` + display: flex; + align-items: center; + gap: 8px; + height: 100%; +`,Lx=C.div` + display: flex; + align-items: center; + gap: 12px; + height: 100%; +`,Ix=C(Or)` + width: 24px; + height: 24px; +`;C.img` + width: 24px; + height: 24px; + border-radius: 50%; +`;const Dx=C.div` + position: relative; + width: 40px; + height: 24px; + flex-shrink: 0; +`,zx=C($o)` + border-color: ${Y.colors.background.primary}; + bottom: -3px; + right: -3px; +`,$x=C.div` + font-size: 12px; + color: ${Y.colors.text.muted}; + line-height: 13px; +`,up=C.div` + font-weight: bold; + color: ${Y.colors.text.primary}; + line-height: 20px; + font-size: 16px; +`,Bx=C.div` + flex: 1; + display: flex; + flex-direction: column-reverse; + overflow-y: auto; + position: relative; +`,Fx=C.div` + padding: 16px; + display: flex; + flex-direction: column; +`,Eh=C.div` + margin-bottom: 16px; + display: flex; + align-items: flex-start; + position: relative; + z-index: 1; +`,bx=C(Or)` + margin-right: 16px; + width: 40px; + height: 40px; +`;C.img` + width: 40px; + height: 40px; + border-radius: 50%; +`;const Ux=C.div` + display: flex; + align-items: center; + margin-bottom: 4px; + position: relative; +`,Hx=C.span` + font-weight: bold; + color: ${Y.colors.text.primary}; + margin-right: 8px; +`,Vx=C.span` + font-size: 0.75rem; + color: ${Y.colors.text.muted}; +`,Wx=C.div` + color: ${Y.colors.text.secondary}; + margin-top: 4px; +`,qx=C.form` + display: flex; + align-items: center; + gap: 8px; + padding: 16px; + background: ${({theme:r})=>r.colors.background.secondary}; + position: relative; + z-index: 1; +`,Yx=C.textarea` + flex: 1; + padding: 12px; + background: ${({theme:r})=>r.colors.background.tertiary}; + border: none; + border-radius: 4px; + color: ${({theme:r})=>r.colors.text.primary}; + font-size: 14px; + resize: none; + min-height: 44px; + max-height: 144px; + + &:focus { + outline: none; + } + + &::placeholder { + color: ${({theme:r})=>r.colors.text.muted}; + } +`,Qx=C.button` + background: none; + border: none; + color: ${({theme:r})=>r.colors.text.muted}; + font-size: 24px; + cursor: pointer; + padding: 4px 8px; + display: flex; + align-items: center; + justify-content: center; + + &:hover { + color: ${({theme:r})=>r.colors.text.primary}; + } +`;C.div` + flex: 1; + display: flex; + align-items: center; + justify-content: center; + color: ${Y.colors.text.muted}; + font-size: 16px; + font-weight: 500; + padding: 20px; + text-align: center; +`;const cp=C.div` + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 8px; + width: 100%; +`,Gx=C.a` + display: block; + border-radius: 4px; + overflow: hidden; + max-width: 300px; + + img { + width: 100%; + height: auto; + display: block; + } +`,Kx=C.a` + display: flex; + align-items: center; + gap: 12px; + padding: 12px; + background: ${({theme:r})=>r.colors.background.tertiary}; + border-radius: 8px; + text-decoration: none; + width: fit-content; + + &:hover { + background: ${({theme:r})=>r.colors.background.hover}; + } +`,Xx=C.div` + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + font-size: 40px; + color: #0B93F6; +`,Jx=C.div` + display: flex; + flex-direction: column; + gap: 2px; +`,Zx=C.span` + font-size: 14px; + color: #0B93F6; + font-weight: 500; +`,e1=C.span` + font-size: 13px; + color: ${({theme:r})=>r.colors.text.muted}; +`,t1=C.div` + display: flex; + flex-wrap: wrap; + gap: 8px; + padding: 8px 0; +`,jh=C.div` + position: relative; + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + background: ${({theme:r})=>r.colors.background.tertiary}; + border-radius: 4px; + max-width: 300px; +`,n1=C(jh)` + padding: 0; + overflow: hidden; + width: 200px; + height: 120px; + + img { + width: 100%; + height: 100%; + object-fit: cover; + } +`,r1=C.div` + color: #0B93F6; + font-size: 20px; +`,o1=C.div` + font-size: 13px; + color: ${({theme:r})=>r.colors.text.primary}; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +`,dp=C.button` + position: absolute; + top: -6px; + right: -6px; + width: 20px; + height: 20px; + border-radius: 50%; + background: ${({theme:r})=>r.colors.background.secondary}; + border: none; + color: ${({theme:r})=>r.colors.text.muted}; + font-size: 16px; + line-height: 1; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + padding: 0; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + + &:hover { + color: ${({theme:r})=>r.colors.text.primary}; + } +`,i1=C.div` + position: relative; + margin-left: auto; + z-index: 99999; +`,s1=C.button` + background: none; + border: none; + color: ${({theme:r})=>r.colors.text.muted}; + font-size: 16px; + cursor: pointer; + padding: 4px 8px; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 0.2s ease; + + &:hover { + color: ${({theme:r})=>r.colors.text.primary}; + background: ${({theme:r})=>r.colors.background.hover}; + } + + ${Eh}:hover & { + opacity: 1; + } +`,l1=C.div` + position: absolute; + top: 0; + background: ${({theme:r})=>r.colors.background.primary}; + border: 1px solid ${({theme:r})=>r.colors.border.primary}; + border-radius: 6px; + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15); + width: 80px; + z-index: 99999; + overflow: hidden; +`,fp=C.button` + display: flex; + align-items: center; + gap: 8px; + width: fit-content; + background: none; + border: none; + color: ${({theme:r})=>r.colors.text.primary}; + font-size: 14px; + cursor: pointer; + text-align: center ; + + &:hover { + background: ${({theme:r})=>r.colors.background.hover}; + } + + &:first-child { + border-radius: 6px 6px 0 0; + } + + &:last-child { + border-radius: 0 0 6px 6px; + } +`,a1=C.div` + margin-top: 4px; +`,u1=C.textarea` + width: 100%; + max-width: 600px; + min-height: 80px; + padding: 12px 16px; + background: ${({theme:r})=>r.colors.background.tertiary}; + border: 1px solid ${({theme:r})=>r.colors.border.primary}; + border-radius: 4px; + color: ${({theme:r})=>r.colors.text.primary}; + font-size: 14px; + font-family: inherit; + resize: vertical; + outline: none; + box-sizing: border-box; + + &:focus { + border-color: ${({theme:r})=>r.colors.primary}; + } + + &::placeholder { + color: ${({theme:r})=>r.colors.text.muted}; + } +`,c1=C.div` + display: flex; + gap: 8px; + margin-top: 8px; +`,pp=C.button` + padding: 6px 12px; + border-radius: 4px; + font-size: 12px; + font-weight: 500; + cursor: pointer; + border: none; + transition: background-color 0.2s ease; + + ${({variant:r,theme:i})=>r==="primary"?` + background: ${i.colors.primary}; + color: white; + + &:hover { + background: ${i.colors.primaryHover||i.colors.primary}; + } + `:` + background: ${i.colors.background.secondary}; + color: ${i.colors.text.secondary}; + + &:hover { + background: ${i.colors.background.hover}; + } + `} +`;function d1({channel:r}){var w;const{currentUser:i}=rt(),s=Rr(v=>v.users),l=An(v=>v.binaryContents);if(!r)return null;if(r.type==="PUBLIC")return h.jsx(lp,{children:h.jsx(ap,{children:h.jsxs(up,{children:["# ",r.name]})})});const c=r.participants.map(v=>s.find(S=>S.id===v.id)).filter(Boolean),d=c.filter(v=>v.id!==(i==null?void 0:i.id)),p=c.length>2,m=c.filter(v=>v.id!==(i==null?void 0:i.id)).map(v=>v.username).join(", ");return h.jsx(lp,{children:h.jsx(ap,{children:h.jsxs(Lx,{children:[p?h.jsx(Dx,{children:d.slice(0,2).map((v,S)=>{var j;return h.jsx(nn,{src:v.profile?(j=l[v.profile.id])==null?void 0:j.url:St,style:{position:"absolute",left:S*16,zIndex:2-S,width:"24px",height:"24px"}},v.id)})}):h.jsxs(Ix,{children:[h.jsx(nn,{src:d[0].profile?(w=l[d[0].profile.id])==null?void 0:w.url:St}),h.jsx(zx,{$online:d[0].online})]}),h.jsxs("div",{children:[h.jsx(up,{children:m}),p&&h.jsxs($x,{children:["멤버 ",c.length,"명"]})]})]})})})}const f1=async(r,i,s)=>{var c;return(await $e.get("/messages",{params:{channelId:r,cursor:i,size:s.size,sort:(c=s.sort)==null?void 0:c.join(",")}})).data},p1=async(r,i)=>{const s=new FormData,l={content:r.content,channelId:r.channelId,authorId:r.authorId};return s.append("messageCreateRequest",new Blob([JSON.stringify(l)],{type:"application/json"})),i&&i.length>0&&i.forEach(d=>{s.append("attachments",d)}),(await $e.post("/messages",s,{headers:{"Content-Type":"multipart/form-data"}})).data},h1=async(r,i)=>(await $e.patch(`/messages/${r}`,i)).data,m1=async r=>{await $e.delete(`/messages/${r}`)},ba={size:50,sort:["createdAt,desc"]},Ah=Tr((r,i)=>({messages:[],pollingIntervals:{},lastMessageId:null,pagination:{nextCursor:null,pageSize:50,hasNext:!1},fetchMessages:async(s,l,c=ba)=>{try{const d=await f1(s,l,c),p=d.content,m=p.length>0?p[0]:null,w=(m==null?void 0:m.id)!==i().lastMessageId;return r(v=>{var N;const S=!l,j=s!==((N=v.messages[0])==null?void 0:N.channelId),R=S&&(v.messages.length===0||j);let L=[],T={...v.pagination};if(R)L=p,T={nextCursor:d.nextCursor,pageSize:d.size,hasNext:d.hasNext};else if(S){const _=new Set(v.messages.map(U=>U.id));L=[...p.filter(U=>!_.has(U.id)&&(v.messages.length===0||U.createdAt>v.messages[0].createdAt)),...v.messages]}else{const _=new Set(v.messages.map(U=>U.id)),V=p.filter(U=>!_.has(U.id));L=[...v.messages,...V],T={nextCursor:d.nextCursor,pageSize:d.size,hasNext:d.hasNext}}return{messages:L,lastMessageId:(m==null?void 0:m.id)||null,pagination:T}}),w}catch(d){return console.error("메시지 목록 조회 실패:",d),!1}},loadMoreMessages:async s=>{const{pagination:l}=i();l.hasNext&&await i().fetchMessages(s,l.nextCursor,{...ba})},startPolling:s=>{const l=i();if(l.pollingIntervals[s]){const m=l.pollingIntervals[s];typeof m=="number"&&clearTimeout(m)}let c=300;const d=3e3;r(m=>({pollingIntervals:{...m.pollingIntervals,[s]:!0}}));const p=async()=>{const m=i();if(!m.pollingIntervals[s])return;const w=await m.fetchMessages(s,null,ba);if(!(i().messages.length==0)&&w?c=300:c=Math.min(c*1.5,d),i().pollingIntervals[s]){const S=setTimeout(p,c);r(j=>({pollingIntervals:{...j.pollingIntervals,[s]:S}}))}};p()},stopPolling:s=>{const{pollingIntervals:l}=i();if(l[s]){const c=l[s];typeof c=="number"&&clearTimeout(c),r(d=>{const p={...d.pollingIntervals};return delete p[s],{pollingIntervals:p}})}},createMessage:async(s,l)=>{try{const c=await p1(s,l),d=Po.getState().updateReadStatus;return await d(s.channelId),r(p=>p.messages.some(w=>w.id===c.id)?p:{messages:[c,...p.messages],lastMessageId:c.id}),c}catch(c){throw console.error("메시지 생성 실패:",c),c}},updateMessage:async(s,l)=>{try{const c=await h1(s,{newContent:l});return r(d=>({messages:d.messages.map(p=>p.id===s?{...p,content:l}:p)})),c}catch(c){throw console.error("메시지 업데이트 실패:",c),c}},deleteMessage:async s=>{try{await m1(s),r(l=>({messages:l.messages.filter(c=>c.id!==s)}))}catch(l){throw console.error("메시지 삭제 실패:",l),l}}}));function g1({channel:r}){const[i,s]=K.useState(""),[l,c]=K.useState([]),d=Ah(R=>R.createMessage),{currentUser:p}=rt(),m=async R=>{if(R.preventDefault(),!(!i.trim()&&l.length===0))try{await d({content:i.trim(),channelId:r.id,authorId:(p==null?void 0:p.id)??""},l),s(""),c([])}catch(L){console.error("메시지 전송 실패:",L)}},w=R=>{const L=Array.from(R.target.files||[]);c(T=>[...T,...L]),R.target.value=""},v=R=>{c(L=>L.filter((T,N)=>N!==R))},S=R=>{if(R.key==="Enter"&&!R.shiftKey){if(console.log("Enter key pressed"),R.preventDefault(),R.nativeEvent.isComposing)return;m(R)}},j=(R,L)=>R.type.startsWith("image/")?h.jsxs(n1,{children:[h.jsx("img",{src:URL.createObjectURL(R),alt:R.name}),h.jsx(dp,{onClick:()=>v(L),children:"×"})]},L):h.jsxs(jh,{children:[h.jsx(r1,{children:"📎"}),h.jsx(o1,{children:R.name}),h.jsx(dp,{onClick:()=>v(L),children:"×"})]},L);return K.useEffect(()=>()=>{l.forEach(R=>{R.type.startsWith("image/")&&URL.revokeObjectURL(URL.createObjectURL(R))})},[l]),r?h.jsxs(h.Fragment,{children:[l.length>0&&h.jsx(t1,{children:l.map((R,L)=>j(R,L))}),h.jsxs(qx,{onSubmit:m,children:[h.jsxs(Qx,{as:"label",children:["+",h.jsx("input",{type:"file",multiple:!0,onChange:w,style:{display:"none"}})]}),h.jsx(Yx,{value:i,onChange:R=>s(R.target.value),onKeyDown:S,placeholder:r.type==="PUBLIC"?`#${r.name}에 메시지 보내기`:"메시지 보내기"})]})]}):null}/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */var ru=function(r,i){return ru=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(s,l){s.__proto__=l}||function(s,l){for(var c in l)l.hasOwnProperty(c)&&(s[c]=l[c])},ru(r,i)};function y1(r,i){ru(r,i);function s(){this.constructor=r}r.prototype=i===null?Object.create(i):(s.prototype=i.prototype,new s)}var No=function(){return No=Object.assign||function(i){for(var s,l=1,c=arguments.length;lr?L():i!==!0&&(c=setTimeout(l?T:L,l===void 0?r-j:r))}return v.cancel=w,v}var kr={Pixel:"Pixel",Percent:"Percent"},hp={unit:kr.Percent,value:.8};function mp(r){return typeof r=="number"?{unit:kr.Percent,value:r*100}:typeof r=="string"?r.match(/^(\d*(\.\d+)?)px$/)?{unit:kr.Pixel,value:parseFloat(r)}:r.match(/^(\d*(\.\d+)?)%$/)?{unit:kr.Percent,value:parseFloat(r)}:(console.warn('scrollThreshold format is invalid. Valid formats: "120px", "50%"...'),hp):(console.warn("scrollThreshold should be string or number"),hp)}var x1=function(r){y1(i,r);function i(s){var l=r.call(this,s)||this;return l.lastScrollTop=0,l.actionTriggered=!1,l.startY=0,l.currentY=0,l.dragging=!1,l.maxPullDownDistance=0,l.getScrollableTarget=function(){return l.props.scrollableTarget instanceof HTMLElement?l.props.scrollableTarget:typeof l.props.scrollableTarget=="string"?document.getElementById(l.props.scrollableTarget):(l.props.scrollableTarget===null&&console.warn(`You are trying to pass scrollableTarget but it is null. This might + happen because the element may not have been added to DOM yet. + See https://github.com/ankeetmaini/react-infinite-scroll-component/issues/59 for more info. + `),null)},l.onStart=function(c){l.lastScrollTop||(l.dragging=!0,c instanceof MouseEvent?l.startY=c.pageY:c instanceof TouchEvent&&(l.startY=c.touches[0].pageY),l.currentY=l.startY,l._infScroll&&(l._infScroll.style.willChange="transform",l._infScroll.style.transition="transform 0.2s cubic-bezier(0,0,0.31,1)"))},l.onMove=function(c){l.dragging&&(c instanceof MouseEvent?l.currentY=c.pageY:c instanceof TouchEvent&&(l.currentY=c.touches[0].pageY),!(l.currentY=Number(l.props.pullDownToRefreshThreshold)&&l.setState({pullToRefreshThresholdBreached:!0}),!(l.currentY-l.startY>l.maxPullDownDistance*1.5)&&l._infScroll&&(l._infScroll.style.overflow="visible",l._infScroll.style.transform="translate3d(0px, "+(l.currentY-l.startY)+"px, 0px)")))},l.onEnd=function(){l.startY=0,l.currentY=0,l.dragging=!1,l.state.pullToRefreshThresholdBreached&&(l.props.refreshFunction&&l.props.refreshFunction(),l.setState({pullToRefreshThresholdBreached:!1})),requestAnimationFrame(function(){l._infScroll&&(l._infScroll.style.overflow="auto",l._infScroll.style.transform="none",l._infScroll.style.willChange="unset")})},l.onScrollListener=function(c){typeof l.props.onScroll=="function"&&setTimeout(function(){return l.props.onScroll&&l.props.onScroll(c)},0);var d=l.props.height||l._scrollableNode?c.target:document.documentElement.scrollTop?document.documentElement:document.body;if(!l.actionTriggered){var p=l.props.inverse?l.isElementAtTop(d,l.props.scrollThreshold):l.isElementAtBottom(d,l.props.scrollThreshold);p&&l.props.hasMore&&(l.actionTriggered=!0,l.setState({showLoader:!0}),l.props.next&&l.props.next()),l.lastScrollTop=d.scrollTop}},l.state={showLoader:!1,pullToRefreshThresholdBreached:!1,prevDataLength:s.dataLength},l.throttledOnScrollListener=v1(150,l.onScrollListener).bind(l),l.onStart=l.onStart.bind(l),l.onMove=l.onMove.bind(l),l.onEnd=l.onEnd.bind(l),l}return i.prototype.componentDidMount=function(){if(typeof this.props.dataLength>"u")throw new Error('mandatory prop "dataLength" is missing. The prop is needed when loading more content. Check README.md for usage');if(this._scrollableNode=this.getScrollableTarget(),this.el=this.props.height?this._infScroll:this._scrollableNode||window,this.el&&this.el.addEventListener("scroll",this.throttledOnScrollListener),typeof this.props.initialScrollY=="number"&&this.el&&this.el instanceof HTMLElement&&this.el.scrollHeight>this.props.initialScrollY&&this.el.scrollTo(0,this.props.initialScrollY),this.props.pullDownToRefresh&&this.el&&(this.el.addEventListener("touchstart",this.onStart),this.el.addEventListener("touchmove",this.onMove),this.el.addEventListener("touchend",this.onEnd),this.el.addEventListener("mousedown",this.onStart),this.el.addEventListener("mousemove",this.onMove),this.el.addEventListener("mouseup",this.onEnd),this.maxPullDownDistance=this._pullDown&&this._pullDown.firstChild&&this._pullDown.firstChild.getBoundingClientRect().height||0,this.forceUpdate(),typeof this.props.refreshFunction!="function"))throw new Error(`Mandatory prop "refreshFunction" missing. + Pull Down To Refresh functionality will not work + as expected. Check README.md for usage'`)},i.prototype.componentWillUnmount=function(){this.el&&(this.el.removeEventListener("scroll",this.throttledOnScrollListener),this.props.pullDownToRefresh&&(this.el.removeEventListener("touchstart",this.onStart),this.el.removeEventListener("touchmove",this.onMove),this.el.removeEventListener("touchend",this.onEnd),this.el.removeEventListener("mousedown",this.onStart),this.el.removeEventListener("mousemove",this.onMove),this.el.removeEventListener("mouseup",this.onEnd)))},i.prototype.componentDidUpdate=function(s){this.props.dataLength!==s.dataLength&&(this.actionTriggered=!1,this.setState({showLoader:!1}))},i.getDerivedStateFromProps=function(s,l){var c=s.dataLength!==l.prevDataLength;return c?No(No({},l),{prevDataLength:s.dataLength}):null},i.prototype.isElementAtTop=function(s,l){l===void 0&&(l=.8);var c=s===document.body||s===document.documentElement?window.screen.availHeight:s.clientHeight,d=mp(l);return d.unit===kr.Pixel?s.scrollTop<=d.value+c-s.scrollHeight+1:s.scrollTop<=d.value/100+c-s.scrollHeight+1},i.prototype.isElementAtBottom=function(s,l){l===void 0&&(l=.8);var c=s===document.body||s===document.documentElement?window.screen.availHeight:s.clientHeight,d=mp(l);return d.unit===kr.Pixel?s.scrollTop+c>=s.scrollHeight-d.value:s.scrollTop+c>=d.value/100*s.scrollHeight},i.prototype.render=function(){var s=this,l=No({height:this.props.height||"auto",overflow:"auto",WebkitOverflowScrolling:"touch"},this.props.style),c=this.props.hasChildren||!!(this.props.children&&this.props.children instanceof Array&&this.props.children.length),d=this.props.pullDownToRefresh&&this.props.height?{overflow:"auto"}:{};return xt.createElement("div",{style:d,className:"infinite-scroll-component__outerdiv"},xt.createElement("div",{className:"infinite-scroll-component "+(this.props.className||""),ref:function(p){return s._infScroll=p},style:l},this.props.pullDownToRefresh&&xt.createElement("div",{style:{position:"relative"},ref:function(p){return s._pullDown=p}},xt.createElement("div",{style:{position:"absolute",left:0,right:0,top:-1*this.maxPullDownDistance}},this.state.pullToRefreshThresholdBreached?this.props.releaseToRefreshContent:this.props.pullDownToRefreshContent)),this.props.children,!this.state.showLoader&&!c&&this.props.hasMore&&this.props.loader,this.state.showLoader&&this.props.hasMore&&this.props.loader,!this.props.hasMore&&this.props.endMessage))},i}(K.Component);const w1=r=>r<1024?r+" B":r<1024*1024?(r/1024).toFixed(2)+" KB":r<1024*1024*1024?(r/(1024*1024)).toFixed(2)+" MB":(r/(1024*1024*1024)).toFixed(2)+" GB";function S1({channel:r}){const{messages:i,fetchMessages:s,loadMoreMessages:l,pagination:c,startPolling:d,stopPolling:p,updateMessage:m,deleteMessage:w}=Ah(),{binaryContents:v,fetchBinaryContent:S,clearBinaryContents:j}=An(),{currentUser:R}=rt(),[L,T]=K.useState(null),[N,_]=K.useState(null),[V,U]=K.useState("");K.useEffect(()=>{if(r!=null&&r.id)return s(r.id,null),d(r.id),()=>{p(r.id)}},[r==null?void 0:r.id,s,d,p]),K.useEffect(()=>{i.forEach(ne=>{var le;(le=ne.attachments)==null||le.forEach(me=>{v[me.id]||S(me.id)})})},[i,v,S]),K.useEffect(()=>()=>{const ne=i.map(le=>{var me;return(me=le.attachments)==null?void 0:me.map(Re=>Re.id)}).flat();j(ne)},[j]),K.useEffect(()=>{const ne=()=>{L&&T(null)};if(L)return document.addEventListener("click",ne),()=>document.removeEventListener("click",ne)},[L]);const B=async ne=>{try{const{url:le,fileName:me}=ne,Re=document.createElement("a");Re.href=le,Re.download=me,Re.style.display="none",document.body.appendChild(Re);try{const Ee=await(await window.showSaveFilePicker({suggestedName:ne.fileName,types:[{description:"Files",accept:{"*/*":[".txt",".pdf",".doc",".docx",".xls",".xlsx",".jpg",".jpeg",".png",".gif"]}}]})).createWritable(),ee=await(await fetch(le)).blob();await Ee.write(ee),await Ee.close()}catch(ge){ge.name!=="AbortError"&&Re.click()}document.body.removeChild(Re),window.URL.revokeObjectURL(le)}catch(le){console.error("파일 다운로드 실패:",le)}},W=ne=>ne!=null&&ne.length?ne.map(le=>{const me=v[le.id];return me?me.contentType.startsWith("image/")?h.jsx(cp,{children:h.jsx(Gx,{href:"#",onClick:ge=>{ge.preventDefault(),B(me)},children:h.jsx("img",{src:me.url,alt:me.fileName})})},me.url):h.jsx(cp,{children:h.jsxs(Kx,{href:"#",onClick:ge=>{ge.preventDefault(),B(me)},children:[h.jsx(Xx,{children:h.jsxs("svg",{width:"40",height:"40",viewBox:"0 0 40 40",fill:"none",children:[h.jsx("path",{d:"M8 3C8 1.89543 8.89543 1 10 1H22L32 11V37C32 38.1046 31.1046 39 30 39H10C8.89543 39 8 38.1046 8 37V3Z",fill:"#0B93F6",fillOpacity:"0.1"}),h.jsx("path",{d:"M22 1L32 11H24C22.8954 11 22 10.1046 22 9V1Z",fill:"#0B93F6",fillOpacity:"0.3"}),h.jsx("path",{d:"M13 19H27M13 25H27M13 31H27",stroke:"#0B93F6",strokeWidth:"2",strokeLinecap:"round"})]})}),h.jsxs(Jx,{children:[h.jsx(Zx,{children:me.fileName}),h.jsx(e1,{children:w1(me.size)})]})]})},me.url):null}):null,I=ne=>new Date(ne).toLocaleTimeString(),M=()=>{r!=null&&r.id&&l(r.id)},H=ne=>{T(L===ne?null:ne)},ie=ne=>{T(null);const le=i.find(me=>me.id===ne);le&&(_(ne),U(le.content))},ve=ne=>{m(ne,V).catch(le=>{console.error("메시지 수정 실패:",le),Sr.emit("api-error",{error:le,alert:!0})}),_(null),U("")},Oe=()=>{_(null),U("")},ot=ne=>{T(null),w(ne)};return h.jsx(Bx,{children:h.jsx("div",{id:"scrollableDiv",style:{height:"100%",overflow:"auto",display:"flex",flexDirection:"column-reverse"},children:h.jsx(x1,{dataLength:i.length,next:M,hasMore:c.hasNext,loader:h.jsx("h4",{style:{textAlign:"center"},children:"메시지를 불러오는 중..."}),scrollableTarget:"scrollableDiv",style:{display:"flex",flexDirection:"column-reverse"},inverse:!0,endMessage:h.jsx("p",{style:{textAlign:"center"},children:h.jsx("b",{children:c.nextCursor!==null?"모든 메시지를 불러왔습니다":""})}),children:h.jsx(Fx,{children:[...i].reverse().map(ne=>{var Re;const le=ne.author,me=R&&le&&le.id===R.id;return h.jsxs(Eh,{children:[h.jsx(bx,{children:h.jsx(nn,{src:le&&le.profile?(Re=v[le.profile.id])==null?void 0:Re.url:St,alt:le&&le.username||"알 수 없음"})}),h.jsxs("div",{children:[h.jsxs(Ux,{children:[h.jsx(Hx,{children:le&&le.username||"알 수 없음"}),h.jsx(Vx,{children:I(ne.createdAt)}),me&&h.jsxs(i1,{children:[h.jsx(s1,{onClick:ge=>{ge.stopPropagation(),H(ne.id)},children:"⋯"}),L===ne.id&&h.jsxs(l1,{onClick:ge=>ge.stopPropagation(),children:[h.jsx(fp,{onClick:()=>ie(ne.id),children:"✏️ 수정"}),h.jsx(fp,{onClick:()=>ot(ne.id),children:"🗑️ 삭제"})]})]})]}),N===ne.id?h.jsxs(a1,{children:[h.jsx(u1,{value:V,onChange:ge=>U(ge.target.value),onKeyDown:ge=>{ge.key==="Escape"?Oe():ge.key==="Enter"&&(ge.ctrlKey||ge.metaKey)&&(ge.preventDefault(),ve(ne.id))},placeholder:"메시지를 입력하세요..."}),h.jsxs(c1,{children:[h.jsx(pp,{variant:"secondary",onClick:Oe,children:"취소"}),h.jsx(pp,{variant:"primary",onClick:()=>ve(ne.id),children:"저장"})]})]}):h.jsx(Wx,{children:ne.content}),W(ne.attachments)]})]},ne.id)})})})})})}function k1({channel:r}){return r?h.jsxs(Rx,{children:[h.jsx(d1,{channel:r}),h.jsx(S1,{channel:r}),h.jsx(g1,{channel:r})]}):h.jsx(Tx,{children:h.jsxs(_x,{children:[h.jsx(Nx,{children:"👋"}),h.jsx(Ox,{children:"채널을 선택해주세요"}),h.jsxs(Mx,{children:["왼쪽의 채널 목록에서 채널을 선택하여",h.jsx("br",{}),"대화를 시작하세요."]})]})})}function C1(r,i="yyyy-MM-dd HH:mm:ss"){if(!r||!(r instanceof Date)||isNaN(r.getTime()))return"";const s=r.getFullYear(),l=String(r.getMonth()+1).padStart(2,"0"),c=String(r.getDate()).padStart(2,"0"),d=String(r.getHours()).padStart(2,"0"),p=String(r.getMinutes()).padStart(2,"0"),m=String(r.getSeconds()).padStart(2,"0");return i.replace("yyyy",s.toString()).replace("MM",l).replace("dd",c).replace("HH",d).replace("mm",p).replace("ss",m)}const E1=C.div` + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.7); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +`,j1=C.div` + background: ${({theme:r})=>r.colors.background.primary}; + border-radius: 8px; + width: 500px; + max-width: 90%; + padding: 24px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); +`,A1=C.div` + display: flex; + align-items: center; + margin-bottom: 16px; +`,R1=C.div` + color: ${({theme:r})=>r.colors.status.error}; + font-size: 24px; + margin-right: 12px; +`,P1=C.h3` + color: ${({theme:r})=>r.colors.text.primary}; + margin: 0; + font-size: 18px; +`,T1=C.div` + background: ${({theme:r})=>r.colors.background.tertiary}; + color: ${({theme:r})=>r.colors.text.muted}; + padding: 2px 8px; + border-radius: 4px; + font-size: 14px; + margin-left: auto; +`,_1=C.p` + color: ${({theme:r})=>r.colors.text.secondary}; + margin-bottom: 20px; + line-height: 1.5; + font-weight: 500; +`,N1=C.div` + margin-bottom: 20px; + background: ${({theme:r})=>r.colors.background.secondary}; + border-radius: 6px; + padding: 12px; +`,ko=C.div` + display: flex; + margin-bottom: 8px; + font-size: 14px; +`,Co=C.span` + color: ${({theme:r})=>r.colors.text.muted}; + min-width: 100px; +`,Eo=C.span` + color: ${({theme:r})=>r.colors.text.secondary}; + word-break: break-word; +`,O1=C.button` + background: ${({theme:r})=>r.colors.brand.primary}; + color: white; + border: none; + border-radius: 4px; + padding: 8px 16px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + width: 100%; + + &:hover { + background: ${({theme:r})=>r.colors.brand.hover}; + } +`;function M1({isOpen:r,onClose:i,error:s}){var R,L;if(!r)return null;console.log({error:s});const l=(R=s==null?void 0:s.response)==null?void 0:R.data,c=(l==null?void 0:l.status)||((L=s==null?void 0:s.response)==null?void 0:L.status)||"오류",d=(l==null?void 0:l.code)||"",p=(l==null?void 0:l.message)||(s==null?void 0:s.message)||"알 수 없는 오류가 발생했습니다.",m=l!=null&&l.timestamp?new Date(l.timestamp):new Date,w=C1(m),v=(l==null?void 0:l.exceptionType)||"",S=(l==null?void 0:l.details)||{},j=(l==null?void 0:l.requestId)||"";return h.jsx(E1,{onClick:i,children:h.jsxs(j1,{onClick:T=>T.stopPropagation(),children:[h.jsxs(A1,{children:[h.jsx(R1,{children:"⚠️"}),h.jsx(P1,{children:"오류가 발생했습니다"}),h.jsxs(T1,{children:[c,d?` (${d})`:""]})]}),h.jsx(_1,{children:p}),h.jsxs(N1,{children:[h.jsxs(ko,{children:[h.jsx(Co,{children:"시간:"}),h.jsx(Eo,{children:w})]}),j&&h.jsxs(ko,{children:[h.jsx(Co,{children:"요청 ID:"}),h.jsx(Eo,{children:j})]}),d&&h.jsxs(ko,{children:[h.jsx(Co,{children:"에러 코드:"}),h.jsx(Eo,{children:d})]}),v&&h.jsxs(ko,{children:[h.jsx(Co,{children:"예외 유형:"}),h.jsx(Eo,{children:v})]}),Object.keys(S).length>0&&h.jsxs(ko,{children:[h.jsx(Co,{children:"상세 정보:"}),h.jsx(Eo,{children:Object.entries(S).map(([T,N])=>h.jsxs("div",{children:[T,": ",String(N)]},T))})]})]}),h.jsx(O1,{onClick:i,children:"확인"})]})})}const L1=C.div` + width: 240px; + background: ${Y.colors.background.secondary}; + border-left: 1px solid ${Y.colors.border.primary}; +`,I1=C.div` + padding: 16px; + font-size: 14px; + font-weight: bold; + color: ${Y.colors.text.muted}; + text-transform: uppercase; +`,D1=C.div` + padding: 8px 16px; + display: flex; + align-items: center; + color: ${Y.colors.text.muted}; + &:hover { + background: ${Y.colors.background.primary}; + cursor: pointer; + } +`,z1=C(Or)` + margin-right: 12px; +`;C(nn)``;const $1=C.div` + display: flex; + align-items: center; +`;function B1({member:r}){var l,c,d;const{binaryContents:i,fetchBinaryContent:s}=An();return K.useEffect(()=>{var p;(p=r.profile)!=null&&p.id&&!i[r.profile.id]&&s(r.profile.id)},[(l=r.profile)==null?void 0:l.id,i,s]),h.jsxs(D1,{children:[h.jsxs(z1,{children:[h.jsx(nn,{src:(c=r.profile)!=null&&c.id&&((d=i[r.profile.id])==null?void 0:d.url)||St,alt:r.username}),h.jsx($o,{$online:r.online})]}),h.jsx($1,{children:r.username})]})}function F1({member:r,onClose:i}){var L,T,N;const{binaryContents:s,fetchBinaryContent:l}=An(),{currentUser:c,updateUserRole:d}=rt(),[p,m]=K.useState(r.role),[w,v]=K.useState(!1);K.useEffect(()=>{var _;(_=r.profile)!=null&&_.id&&!s[r.profile.id]&&l(r.profile.id)},[(L=r.profile)==null?void 0:L.id,s,l]);const S={[En.USER]:{name:"사용자",color:"#2ed573"},[En.CHANNEL_MANAGER]:{name:"채널 관리자",color:"#ff4757"},[En.ADMIN]:{name:"어드민",color:"#0097e6"}},j=_=>{m(_),v(!0)},R=()=>{d(r.id,p),v(!1)};return h.jsx(H1,{onClick:i,children:h.jsxs(V1,{onClick:_=>_.stopPropagation(),children:[h.jsx("h2",{children:"사용자 정보"}),h.jsxs(W1,{children:[h.jsx(q1,{src:(T=r.profile)!=null&&T.id&&((N=s[r.profile.id])==null?void 0:N.url)||St,alt:r.username}),h.jsx(Y1,{children:r.username}),h.jsx(Q1,{children:r.email}),h.jsx(G1,{$online:r.online,children:r.online?"온라인":"오프라인"}),(c==null?void 0:c.role)===En.ADMIN?h.jsx(U1,{value:p,onChange:_=>j(_.target.value),children:Object.entries(S).map(([_,V])=>h.jsx("option",{value:_,style:{marginTop:"8px",textAlign:"center"},children:V.name},_))}):h.jsx(b1,{style:{backgroundColor:S[r.role].color},children:S[r.role].name})]}),h.jsx(K1,{children:(c==null?void 0:c.role)===En.ADMIN&&w&&h.jsx(X1,{onClick:R,disabled:!w,$secondary:!w,children:"저장"})})]})})}const b1=C.div` + padding: 6px 16px; + border-radius: 20px; + font-size: 13px; + font-weight: 600; + color: white; + margin-top: 12px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + letter-spacing: 0.3px; +`,U1=C.select` + padding: 10px 16px; + border-radius: 8px; + border: 1.5px solid ${Y.colors.border.primary}; + background: ${Y.colors.background.primary}; + color: ${Y.colors.text.primary}; + font-size: 14px; + width: 140px; + cursor: pointer; + transition: all 0.2s ease; + margin-top: 12px; + font-weight: 500; + + &:hover { + border-color: ${Y.colors.brand.primary}; + } + + &:focus { + outline: none; + border-color: ${Y.colors.brand.primary}; + box-shadow: 0 0 0 2px ${Y.colors.brand.primary}20; + } + + option { + background: ${Y.colors.background.primary}; + color: ${Y.colors.text.primary}; + padding: 12px; + } +`,H1=C.div` + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(4px); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +`,V1=C.div` + background: ${Y.colors.background.secondary}; + padding: 40px; + border-radius: 16px; + width: 100%; + max-width: 420px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12); + + h2 { + color: ${Y.colors.text.primary}; + margin-bottom: 32px; + text-align: center; + font-size: 26px; + font-weight: 600; + letter-spacing: -0.5px; + } +`,W1=C.div` + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 32px; + padding: 24px; + background: ${Y.colors.background.primary}; + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); +`,q1=C.img` + width: 140px; + height: 140px; + border-radius: 50%; + margin-bottom: 20px; + object-fit: cover; + border: 4px solid ${Y.colors.background.secondary}; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); +`,Y1=C.div` + font-size: 22px; + font-weight: 600; + color: ${Y.colors.text.primary}; + margin-bottom: 8px; + letter-spacing: -0.3px; +`,Q1=C.div` + font-size: 14px; + color: ${Y.colors.text.muted}; + margin-bottom: 16px; + font-weight: 500; +`,G1=C.div` + padding: 6px 16px; + border-radius: 20px; + font-size: 13px; + font-weight: 600; + background-color: ${({$online:r,theme:i})=>r?i.colors.status.online:i.colors.status.offline}; + color: white; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + letter-spacing: 0.3px; +`,K1=C.div` + display: flex; + gap: 12px; + margin-top: 24px; +`,X1=C.button` + width: 100%; + padding: 12px; + border: none; + border-radius: 8px; + background: ${({$secondary:r,theme:i})=>r?"transparent":i.colors.brand.primary}; + color: ${({$secondary:r,theme:i})=>r?i.colors.text.primary:"white"}; + cursor: pointer; + font-weight: 600; + font-size: 15px; + transition: all 0.2s ease; + border: ${({$secondary:r,theme:i})=>r?`1.5px solid ${i.colors.border.primary}`:"none"}; + + &:hover { + background: ${({$secondary:r,theme:i})=>r?i.colors.background.hover:i.colors.brand.hover}; + transform: translateY(-1px); + } + + &:active { + transform: translateY(0); + } +`;function J1(){const r=Rr(p=>p.users),i=Rr(p=>p.fetchUsers),{currentUser:s}=rt(),[l,c]=K.useState(null);K.useEffect(()=>{i()},[i]);const d=[...r].sort((p,m)=>p.id===(s==null?void 0:s.id)?-1:m.id===(s==null?void 0:s.id)?1:p.online&&!m.online?-1:!p.online&&m.online?1:p.username.localeCompare(m.username));return h.jsxs(L1,{children:[h.jsxs(I1,{children:["멤버 목록 - ",r.length]}),d.map(p=>h.jsx("div",{onClick:()=>c(p),children:h.jsx(B1,{member:p},p.id)},p.id)),l&&h.jsx(F1,{member:l,onClose:()=>c(null)})]})}function Z1(){const{logout:r,fetchCsrfToken:i,refreshToken:s}=rt(),{fetchUsers:l}=Rr(),[c,d]=K.useState(null),[p,m]=K.useState(null),[w,v]=K.useState(!1),[S,j]=K.useState(!0),{currentUser:R}=rt();K.useEffect(()=>{i(),s()},[]),K.useEffect(()=>{(async()=>{try{if(R)try{await l()}catch(N){console.warn("사용자 상태 업데이트 실패. 로그아웃합니다.",N),r()}}catch(N){console.error("초기화 오류:",N)}finally{j(!1)}})()},[R,l,r]),K.useEffect(()=>{const T=U=>{U!=null&&U.error&&m(U.error),U!=null&&U.alert&&v(!0)},N=()=>{r()},_=Sr.on("api-error",T),V=Sr.on("auth-error",N);return()=>{_("api-error",T),V("auth-error",N)}},[r]),K.useEffect(()=>{if(R){const T=setInterval(()=>{l()},6e4);return()=>{clearInterval(T)}}},[R,l]);const L=()=>{v(!1),m(null)};return S?h.jsx(Of,{theme:Y,children:h.jsx(tw,{children:h.jsx(nw,{})})}):h.jsxs(Of,{theme:Y,children:[R?h.jsxs(ew,{children:[h.jsx(jx,{currentUser:R,activeChannel:c,onChannelSelect:d}),h.jsx(k1,{channel:c}),h.jsx(J1,{})]}):h.jsx(Mv,{isOpen:!0,onClose:()=>{}}),h.jsx(M1,{isOpen:w,onClose:L,error:p})]})}const ew=C.div` + display: flex; + height: 100vh; + width: 100vw; + position: relative; +`,tw=C.div` + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + width: 100vw; + background-color: ${({theme:r})=>r.colors.background.primary}; +`,nw=C.div` + width: 40px; + height: 40px; + border: 4px solid ${({theme:r})=>r.colors.background.tertiary}; + border-top: 4px solid ${({theme:r})=>r.colors.brand.primary}; + border-radius: 50%; + animation: spin 1s linear infinite; + + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } +`,Rh=document.getElementById("root");if(!Rh)throw new Error("Root element not found");Lg.createRoot(Rh).render(h.jsx(K.StrictMode,{children:h.jsx(Z1,{})})); diff --git a/src/main/resources/static/assets/index-kQJbKSsj.css b/src/main/resources/static/assets/index-kQJbKSsj.css new file mode 100644 index 000000000..096eb4112 --- /dev/null +++ b/src/main/resources/static/assets/index-kQJbKSsj.css @@ -0,0 +1 @@ +:root{font-family:Inter,system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{margin:0;display:flex;place-items:center;min-width:320px;min-height:100vh}h1{font-size:3.2em;line-height:1.1}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}@media (prefers-color-scheme: light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}} diff --git a/src/main/resources/static/favicon.ico b/src/main/resources/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..479bed6a3da0a8dbdd08a51d81b30e4d4fabae89 GIT binary patch literal 1588 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvI6;x#X;^) z4C~IxyacIC_6YK2V5m}KU}$JzVE6?TYIwoGP-?)y@G60U!Dv>Mu*Du8ycRt4Yw>0&$ytddU zdTHwA$vlU)7;*ZQn^d>r9eiw}SEV3v&DP3PpZVm?c2D=&D? zJg+7dT;x9cg;(mDqrovi2QemjySudY+_R1aaySb-B8!2p69!>MhFNnYfC{QST^vI! zPM@6=9?WDY()wLtM|S>=KoQ44K~Zk4us5=<8xs!eeY>~&=ly4!jD%AXj+wvro>aU~ zrMO$=?`j4U&ZyW$Je*!Zo0>H2RZVqmn^V&mZ(9Dkv!~|IuDF1RBN|EPJE zX3ok)rzF<3&vZKWEj4ag73&t}uJvVk^<~M;*V0n54#8@&v!WGjE_hAaeAZEF z$~V4aF>{^dUc7o%=f8f9m%*2vzjfI@vJ2Z97)VU5x-s2*r@e{H>FEn3A3Dr3G&8U| z)>wFiQO&|Yl6}UkXAQ>%q$jNWac-tTL*)AEyto|onkmnmcJLf?71w_<>4WODmBMxF zwGM7``txcQgT`x>(tH-DrT2Kg=4LzpNv>|+a@TgYDZ`5^$KJVb`K=%k^tRpoxP|4? zwXb!O5~dXYKYt*j(YSx+#_rP{TNcK=40T|)+k3s|?t||EQTgwGgs{E0Y+(QPL&Wx4 zMP23By&sn`zn7oCQQLp%-(Axm|M=5-u;TlFiTn5B^PWnb%fAPV8r2flh?11Vl2ohY zqEsNoU}Ruqple{LYiJr`U}|M-Vr62aZD3$!V6dZTmJ5o8-29Zxv`X9>PU+TH>UWRL)v7?M$%n`C9>lAm0fo0?Z*WfcHaTFhX${Qqu! zG&Nv5t*kOqGt)Cl7z{0q_!){?fojB&%z>&2&rB)F04ce=Mv()kL=s7fZ)R?4No7GQ z1K3si1$pWAo5K9i%<&BYs$wuSHMcY{Gc&O;(${(hEL0izk<1CstV(4taB`Zm$nFhL zDhx>~G{}=7Ei)$-=zaa%ypo*!bp5o%vdrZCykdPs#ORw@rkW)uCz=~4Cz={1nkQNs oC7PHSBpVtgnwc6|q*&+yb?5=zccWrGsMu%lboFyt=akR{0N~++#sB~S literal 0 HcmV?d00001 diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html new file mode 100644 index 000000000..f4fcc0e9f --- /dev/null +++ b/src/main/resources/static/index.html @@ -0,0 +1,26 @@ + + + + + + Discodeit + + + + + +
+ + From 305667eacbda480709f804bb041f8ab7b12cd47f Mon Sep 17 00:00:00 2001 From: "Lim, gyuseong" <100032171+gyural@users.noreply.github.com> Date: Tue, 25 Nov 2025 10:30:09 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20jwt=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EB=B0=8F=20=EC=9D=B8=EC=A6=9D=ED=95=84=ED=84=B0=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 + .../advice/GlobalExceptionHandler.java | 6 +- .../discodeit/domain/dto/jwt/JwtDto.java | 6 + .../discodeit/exception/ErrorCode.java | 5 +- .../discodeit/exception/ErrorResponse.java | 12 + .../auth/NotAuthenticationException.java | 11 + .../security/JwtAuthenticationFilter.java | 90 +++++++ .../security/JwtLoginSuccessHandler.java | 65 ++++++ .../discodeit/security/JwtTokenProvider.java | 221 ++++++++++++++++++ .../discodeit/security/SecurityConfig.java | 46 ++-- .../discodeit/security/SecurityWhitelist.java | 54 +++++ .../discodeit/security/TokenComponent.java | 10 + src/main/resources/application.yaml | 8 + ...24\352\265\254\354\202\254\355\225\255.md" | 10 +- 14 files changed, 515 insertions(+), 31 deletions(-) create mode 100644 src/main/java/com/sprint/mission/discodeit/domain/dto/jwt/JwtDto.java create mode 100644 src/main/java/com/sprint/mission/discodeit/exception/auth/NotAuthenticationException.java create mode 100644 src/main/java/com/sprint/mission/discodeit/security/JwtAuthenticationFilter.java create mode 100644 src/main/java/com/sprint/mission/discodeit/security/JwtLoginSuccessHandler.java create mode 100644 src/main/java/com/sprint/mission/discodeit/security/JwtTokenProvider.java create mode 100644 src/main/java/com/sprint/mission/discodeit/security/SecurityWhitelist.java create mode 100644 src/main/java/com/sprint/mission/discodeit/security/TokenComponent.java diff --git a/build.gradle b/build.gradle index d8bcd32fa..0ac569454 100644 --- a/build.gradle +++ b/build.gradle @@ -48,6 +48,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'de.codecentric:spring-boot-admin-starter-client:3.4.5' implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'com.nimbusds:nimbus-jose-jwt:10.3' testImplementation 'org.springframework.security:spring-security-test' compileOnly 'org.projectlombok:lombok' @@ -71,6 +72,7 @@ dependencies { // dotENV implementation("io.github.cdimascio:java-dotenv:5.2.2") + } dependencyManagement { diff --git a/src/main/java/com/sprint/mission/discodeit/advice/GlobalExceptionHandler.java b/src/main/java/com/sprint/mission/discodeit/advice/GlobalExceptionHandler.java index 54c45e4da..f0046220c 100644 --- a/src/main/java/com/sprint/mission/discodeit/advice/GlobalExceptionHandler.java +++ b/src/main/java/com/sprint/mission/discodeit/advice/GlobalExceptionHandler.java @@ -106,12 +106,12 @@ private HttpStatus determineHttpStatus(DiscodeitException exception) { LOGIN_FAIL -> UNAUTHORIZED; case NOT_AUTHORIZED -> FORBIDDEN; - + case INTERNAL_SERVER_ERROR, URI_CREATE_FAIL, SAVE_TO_FILE_STORAGE_FAIL, - BINARY_CONTENT_READ_FAIL -> HttpStatus.INTERNAL_SERVER_ERROR; - + BINARY_CONTENT_READ_FAIL, + ACCESS_TOKEN_CREATE_FAIL -> HttpStatus.INTERNAL_SERVER_ERROR; }; } } diff --git a/src/main/java/com/sprint/mission/discodeit/domain/dto/jwt/JwtDto.java b/src/main/java/com/sprint/mission/discodeit/domain/dto/jwt/JwtDto.java new file mode 100644 index 000000000..46c348f39 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/domain/dto/jwt/JwtDto.java @@ -0,0 +1,6 @@ +package com.sprint.mission.discodeit.domain.dto.jwt; + +import com.sprint.mission.discodeit.domain.dto.user.UserDto; + +public record JwtDto(UserDto userDto, String accToken) { +} diff --git a/src/main/java/com/sprint/mission/discodeit/exception/ErrorCode.java b/src/main/java/com/sprint/mission/discodeit/exception/ErrorCode.java index 92596aecb..9428ba2d7 100644 --- a/src/main/java/com/sprint/mission/discodeit/exception/ErrorCode.java +++ b/src/main/java/com/sprint/mission/discodeit/exception/ErrorCode.java @@ -48,7 +48,10 @@ public enum ErrorCode { READ_STATUS_DUPLICATE("이미 존재하는 읽음 상태입니다."), // VALIDATION_ERROR - VALIDATION_ERROR("요청값이 잘못되었습니다."); + VALIDATION_ERROR("요청값이 잘못되었습니다."), + + // JWT_ERROR + ACCESS_TOKEN_CREATE_FAIL("Access 토큰 생성에 실패했습니다."); private final String message; } diff --git a/src/main/java/com/sprint/mission/discodeit/exception/ErrorResponse.java b/src/main/java/com/sprint/mission/discodeit/exception/ErrorResponse.java index 17822d77e..aaedaaa06 100644 --- a/src/main/java/com/sprint/mission/discodeit/exception/ErrorResponse.java +++ b/src/main/java/com/sprint/mission/discodeit/exception/ErrorResponse.java @@ -29,4 +29,16 @@ public static ErrorResponse of(ErrorCode code, int status, Exception e) { status ); } + + public static ErrorResponse of(DiscodeitException e, int status) { + ErrorCode errorCode = e.getErrorCode(); + return new ErrorResponse( + Instant.now(), + errorCode.name(), + errorCode.getMessage(), + Collections.emptyMap(), + e.getClass().getSimpleName(), + status + ); + } } diff --git a/src/main/java/com/sprint/mission/discodeit/exception/auth/NotAuthenticationException.java b/src/main/java/com/sprint/mission/discodeit/exception/auth/NotAuthenticationException.java new file mode 100644 index 000000000..d2c80b59c --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/exception/auth/NotAuthenticationException.java @@ -0,0 +1,11 @@ +package com.sprint.mission.discodeit.exception.auth; + +import com.sprint.mission.discodeit.exception.ErrorCode; +import com.sprint.mission.discodeit.exception.user.UserException; + +public class NotAuthenticationException extends UserException { + + public NotAuthenticationException() { + super(ErrorCode.NOT_AUTHORIZED); + } +} diff --git a/src/main/java/com/sprint/mission/discodeit/security/JwtAuthenticationFilter.java b/src/main/java/com/sprint/mission/discodeit/security/JwtAuthenticationFilter.java new file mode 100644 index 000000000..ff8d112c9 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/security/JwtAuthenticationFilter.java @@ -0,0 +1,90 @@ +package com.sprint.mission.discodeit.security; + +import static jakarta.servlet.http.HttpServletResponse.*; +import static org.springframework.http.MediaType.*; + +import java.io.IOException; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.sprint.mission.discodeit.exception.ErrorResponse; +import com.sprint.mission.discodeit.exception.auth.NotAuthenticationException; +import com.sprint.mission.discodeit.repository.UserRepository; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtTokenProvider jwtTokenProvider; + private final ObjectMapper objectMapper; + private final UserRepository userRepository; + private final UserDetailsService userDetailsService; + + // 필터에서 제외할 request를 탐지할 메서드 + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + + String path = request.getServletPath(); + return SecurityWhitelist.matchesJwtWhiteList(path); + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + + String accToken = getAccessToken(request); + + if (!jwtTokenProvider.validateAccessToken(accToken)) { + sendErrorResponse(response); + return; + } + + String username = jwtTokenProvider.getUsernameFromToken(accToken); + DiscodeitUserDetails userDetails = (DiscodeitUserDetails)userDetailsService.loadUserByUsername(username); + + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken( + userDetails, + null, + userDetails.getAuthorities() + ); + SecurityContextHolder.getContext().setAuthentication(authentication); + + filterChain.doFilter(request, response); + } + + private String getAccessToken(HttpServletRequest request) { + String authorization = request.getHeader("Authorization"); + + if (authorization == null || authorization.isBlank()) { + return null; + } + + if (!authorization.startsWith("Bearer ")) { + return null; + } + + String token = authorization.substring("Bearer ".length()).trim(); + return token.isEmpty() ? null : token; + } + + private void sendErrorResponse(HttpServletResponse response) throws IOException { + response.setStatus(SC_UNAUTHORIZED); + response.setContentType(APPLICATION_PROBLEM_JSON_VALUE); + response.setCharacterEncoding("UTF-8"); + ErrorResponse errorResponse = ErrorResponse.of(new NotAuthenticationException(), SC_UNAUTHORIZED); + response.getWriter() + .write(objectMapper.writeValueAsString(errorResponse)); + } +} diff --git a/src/main/java/com/sprint/mission/discodeit/security/JwtLoginSuccessHandler.java b/src/main/java/com/sprint/mission/discodeit/security/JwtLoginSuccessHandler.java new file mode 100644 index 000000000..6739547a6 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/security/JwtLoginSuccessHandler.java @@ -0,0 +1,65 @@ +package com.sprint.mission.discodeit.security; + +import static com.sprint.mission.discodeit.exception.ErrorCode.*; +import static jakarta.servlet.http.HttpServletResponse.*; +import static org.springframework.http.MediaType.*; + +import java.io.IOException; + +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.JOSEException; +import com.sprint.mission.discodeit.domain.dto.jwt.JwtDto; +import com.sprint.mission.discodeit.domain.dto.user.UserDto; +import com.sprint.mission.discodeit.exception.ErrorResponse; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +@RequiredArgsConstructor +public class JwtLoginSuccessHandler implements AuthenticationSuccessHandler { + private final ObjectMapper objectMapper; + private final JwtTokenProvider jwtTokenProvider; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { + + DiscodeitUserDetails userDetails = (DiscodeitUserDetails)authentication.getPrincipal(); + try { + UserDto userDto = userDetails.getUserDto(); + String accessToken = jwtTokenProvider.generateAccessToken(userDetails); + String refreshToken = jwtTokenProvider.generateRefreshToken(userDetails); + Cookie refreshCookie = jwtTokenProvider.genereateRefreshTokenCookie(refreshToken); + + JwtDto jwtDto = new JwtDto(userDto, accessToken); + + response.addCookie(refreshCookie); + response.setContentType(APPLICATION_JSON_VALUE); + response.setStatus(HttpServletResponse.SC_OK); + response.getWriter().write(objectMapper.writeValueAsString(jwtDto)); + + } catch (JOSEException e) { + log.error("Failed to generate access token In login success", e); + int status = SC_INTERNAL_SERVER_ERROR; + + response.setStatus(status); + ErrorResponse errorResponse = ErrorResponse.of(ACCESS_TOKEN_CREATE_FAIL, status, e); + response.getWriter().write(objectMapper.writeValueAsString(errorResponse)); + } + + response.getWriter() + .write(objectMapper.writeValueAsString(userDetails.getUserDto())); + + } + +} diff --git a/src/main/java/com/sprint/mission/discodeit/security/JwtTokenProvider.java b/src/main/java/com/sprint/mission/discodeit/security/JwtTokenProvider.java new file mode 100644 index 000000000..d74fd2ee3 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/security/JwtTokenProvider.java @@ -0,0 +1,221 @@ +package com.sprint.mission.discodeit.security; + +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.stereotype.Component; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSSigner; +import com.nimbusds.jose.JWSVerifier; +import com.nimbusds.jose.crypto.MACSigner; +import com.nimbusds.jose.crypto.MACVerifier; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import com.sprint.mission.discodeit.domain.dto.user.UserDto; + +import jakarta.servlet.http.Cookie; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +public class JwtTokenProvider { + // todo 요구사항과 비교 해야함 + public static final String REFRESH_TOKEN_COOKIE_NAME = "REFRESH_TOKEN"; + + private final String issuer; + private final long accessTokenExpirationMs; // 1시간 + private final long refreshTokenExpirationMs; // 하루 + + private final JWSSigner accessTokenSigner; + private final JWSVerifier accessTokenVerifier; + private final JWSSigner refreshTokenSigner; + private final JWSVerifier refreshTokenVerifier; + + public JwtTokenProvider( + @Value("${security.jwt.secret}") String secret, + @Value("${security.jwt.access-token-validity-seconds}") long accessTokenValiditySeconds, + @Value("${security.jwt.refresh-token-validity-seconds}") long refreshTokenValiditySeconds, + @Value("${security.jwt.issuer}") String issuer + ) throws JOSEException { + this.issuer = issuer; + this.accessTokenExpirationMs = accessTokenValiditySeconds * 1000L; + this.refreshTokenExpirationMs = refreshTokenValiditySeconds * 1000L; + + byte[] secretBytes = secret.getBytes(StandardCharsets.UTF_8); + + this.accessTokenSigner = new MACSigner(secretBytes); + this.accessTokenVerifier = new MACVerifier(secretBytes); + this.refreshTokenSigner = new MACSigner(secretBytes); + this.refreshTokenVerifier = new MACVerifier(secretBytes); + } + + /** + * Create AccessToken + * @param userDetails + * @return + * @throws JOSEException + */ + public String generateAccessToken(DiscodeitUserDetails userDetails) throws JOSEException { + TokenComponent tokenComponent = TokenComponent.of(accessTokenExpirationMs, accessTokenSigner, "access"); + return generateToken(userDetails, tokenComponent); + } + + /** + * Create RefreshToken + * @param userDetails + * @return + * @throws JOSEException + */ + public String generateRefreshToken(DiscodeitUserDetails userDetails) throws JOSEException { + + TokenComponent tokenComponent = TokenComponent.of(refreshTokenExpirationMs, refreshTokenSigner, "refresh"); + return generateToken(userDetails, tokenComponent); + } + + /** + * generate Cookie with refresh token + * @param refreshToken 리프레시 토큰 + * @return Cookie + */ + public Cookie genereateRefreshTokenCookie(String refreshToken) { + Cookie cookie = new Cookie(REFRESH_TOKEN_COOKIE_NAME, refreshToken); // 키쌍으로 만들어짐 + cookie.setHttpOnly(true); // 브라우저에서 읽기 금지 + cookie.setSecure(true); // https에서만 통용됨 + cookie.setPath("/"); + cookie.setMaxAge((int)(refreshTokenExpirationMs / 1000L)); // 쿠키 저장시간 + return cookie; + } + + /** + * generate Cookie with expired refresh token + * @return Cookie + */ + public Cookie genereateRefreshTokenExpirationCookie() { + Cookie cookie = new Cookie(REFRESH_TOKEN_COOKIE_NAME, ""); // 값 지우기 + cookie.setHttpOnly(true); + cookie.setSecure(true); + cookie.setPath("/"); + cookie.setMaxAge(0); // 무효화 시간 정하기 + return cookie; + } + + /** + * @param token 토큰 + * @return Acc 토큰 유효성 검증 결과 + */ + public boolean validateAccessToken(String token) { + return validateToken(token, accessTokenVerifier, "access"); + } + + /** + * @param token 토큰 + * @return Refresh 토큰 유효성 검증 결과 + */ + public boolean validateRefreshToken(String token) { + return validateToken(token, refreshTokenVerifier, "refresh"); + } + + /** + * + * @param token accessToken + * @return accessToken + */ + public String getUsernameFromToken(String token) { + try { + SignedJWT signedJWT = SignedJWT.parse(token); + return signedJWT.getJWTClaimsSet().getSubject(); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid JWT token", e); + } + } + + private boolean validateToken(String token, JWSVerifier verifier, String expectedType) { + try { + SignedJWT signedJWT = SignedJWT.parse(token); + + // 1) 서명 검증 + if (!signedJWT.verify(verifier)) { + log.debug("JWT signature verification failed for {} token", expectedType); + return false; + } + + // 2) type 클레임 검증 + String tokenType = (String)signedJWT.getJWTClaimsSet().getClaim("type"); + if (!expectedType.equals(tokenType)) { + log.debug("JWT token type mismatch: expected {}, got {}", expectedType, tokenType); + return false; + } + + // 3) 만료 시간 + Date expirationTime = signedJWT.getJWTClaimsSet().getExpirationTime(); + if (expirationTime == null || expirationTime.before(new Date())) { + log.debug("JWT {} token expired", expectedType); + return false; + } + + return true; + + } catch (Exception e) { + log.debug("JWT {} token validation failed: {}", expectedType, e.getMessage()); + return false; + } + } + + private String generateToken(DiscodeitUserDetails userDetails, TokenComponent tokenComponent) throws JOSEException { + + UserDto user = userDetails.getUserDto(); + String tokenType = tokenComponent.tokenType(); + JWSSigner signer = tokenComponent.signer(); + + JWTClaimsSet claimsSet = getClaimsFromTokenAndUser(user, tokenComponent, userDetails); + + SignedJWT signedJWT = new SignedJWT( + new JWSHeader(JWSAlgorithm.HS256), + claimsSet + ); + + signedJWT.sign(signer); + String token = signedJWT.serialize(); + + log.debug("Generated {} token for username: {}", tokenType, user.getUsername()); + return token; + } + + private JWTClaimsSet getClaimsFromTokenAndUser( + UserDto userDto, + TokenComponent tokenComponent, + DiscodeitUserDetails userDetails + ) throws JOSEException { + + UUID userID = userDto.getId(); + String username = userDto.getUsername(); + UUID tokenId = UUID.randomUUID(); + String tokenType = tokenComponent.tokenType(); + long expirationMs = tokenComponent.expirationMs(); + + List authorities = userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList(); + + Date now = new Date(); + Date expiryDate = new Date(now.getTime() + expirationMs); + + return new JWTClaimsSet.Builder() + .subject(username) + .jwtID(tokenId.toString()) + .issuer(issuer) + .claim("userId", userID) // Long 그대로 저장 + .claim("type", tokenType) + // .claim("email", user.email()) + .claim("roles", authorities) + .issueTime(now) + .expirationTime(expiryDate) + .build(); + + } +} diff --git a/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java b/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java index 3dcf4ae56..f57f93ebd 100644 --- a/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java +++ b/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java @@ -3,11 +3,11 @@ import static com.sprint.mission.discodeit.domain.enums.Role.*; import static com.sprint.mission.discodeit.exception.ErrorCode.*; import static org.springframework.http.HttpStatus.*; +import static org.springframework.security.config.http.SessionCreationPolicy.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.access.hierarchicalroles.RoleHierarchy; @@ -22,9 +22,11 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler; import org.springframework.security.web.csrf.CookieCsrfTokenRepository; import org.springframework.security.web.session.HttpSessionEventPublisher; +import org.springframework.web.cors.CorsConfiguration; import com.fasterxml.jackson.databind.ObjectMapper; import com.sprint.mission.discodeit.exception.ErrorResponse; @@ -43,39 +45,29 @@ public class SecurityConfig { @Bean public SecurityFilterChain filterChain( HttpSecurity http, - LoginSuccessHandler loginSuccessHandler, + JwtLoginSuccessHandler jwtLoginSuccessHandler, LoginFailureHandler loginFailureHandler, DaoAuthenticationProvider daoAuthenticationProvider, - SessionRegistry sessionRegistry + SessionRegistry sessionRegistry, + JwtAuthenticationFilter jwtAuthenticationFilter ) throws Exception { http - .authenticationProvider(daoAuthenticationProvider) .authorizeHttpRequests(auth -> auth // permitAll 경로 설정 - .requestMatchers("/api/auth/login", "/error", "/", "/index.html").permitAll() - .requestMatchers("/api/auth/csrf-token").permitAll() + .requestMatchers(SecurityWhitelist.WHITE_LIST.toArray(String[]::new)).permitAll() .requestMatchers(HttpMethod.POST, "/api/users").permitAll() // 회원 가입 - .requestMatchers( - "/css/**", - "/js/**", - "/images/**", - "/webjars/**", - "/favicon.ico", - "/swagger-ui/**", - "/assets/**", - "/actuator/**" - ).permitAll() + .anyRequest().authenticated() ) .formLogin(login -> login .loginProcessingUrl("/api/auth/login") - .successHandler(loginSuccessHandler) + .successHandler(jwtLoginSuccessHandler) .failureHandler(loginFailureHandler) ) .logout(logout -> logout .logoutUrl("/api/auth/logout") - .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.NO_CONTENT)) + .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(NO_CONTENT)) ) .csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) @@ -98,14 +90,24 @@ public SecurityFilterChain filterChain( }) ) - .sessionManagement(session -> session - .maximumSessions(1) - .maxSessionsPreventsLogin(false) - .sessionRegistry(sessionRegistry) + .sessionManagement(session -> session.sessionCreationPolicy(STATELESS)) + .addFilterBefore( + jwtAuthenticationFilter, + UsernamePasswordAuthenticationFilter.class ) // .rememberMe(Customizer.withDefaults()) ; + // 8) cors 설정 추가 + http.cors(cors -> cors.configurationSource(request -> { + CorsConfiguration config = new CorsConfiguration(); + config.addAllowedOriginPattern("*"); // 모든 Origin 허용 + config.addAllowedHeader("*"); // 모든 Header 허용 + config.addAllowedMethod("*"); // 모든 Method 허용 + config.setAllowCredentials(true); // 쿠키/인증정보 허용 (필요할 때만) + return config; + })); + return http.build(); } diff --git a/src/main/java/com/sprint/mission/discodeit/security/SecurityWhitelist.java b/src/main/java/com/sprint/mission/discodeit/security/SecurityWhitelist.java new file mode 100644 index 000000000..9c3f85ee8 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/security/SecurityWhitelist.java @@ -0,0 +1,54 @@ +package com.sprint.mission.discodeit.security; + +import java.util.ArrayList; +import java.util.List; + +public class SecurityWhitelist { + // 공통 리스트 + private static final List COMMON_LIST = List.of( + "/api/auth/login", + "/api/auth/refresh", + "/api/auth/csrf-token", + "/error", + "/", + "/index.html", + "/swagger-ui/", + "/favicon.ico", + "/css/**", + "/js/**", + "/images/**", + "/webjars/**", + "/favicon.ico", + "/swagger-ui/**", + "/assets/**", + "/actuator/**" + ); + + // WHITE_LIST = 공통 리스트 그대로 + public static final List WHITE_LIST = COMMON_LIST; + + // JWT_SKIP_LIST = 공통 + 추가 스킵 리스트 + public static final List JWT_SKIP_LIST = new ArrayList<>() {{ + addAll(COMMON_LIST); + add("/api/users"); + }}; + + public static final List JWT_SKIP_LIST_START_WITH = List.of( + "/css/", + "/js/", + "/images/", + "/webjars/", + "/swagger-ui/", + "/assets/", + "/actuator/" + ); + + public static boolean matchesWhiteList(String path) { + return WHITE_LIST.stream().anyMatch(white -> white.equals(path)); + } + + public static boolean matchesJwtWhiteList(String path) { + return JWT_SKIP_LIST.stream().anyMatch(white -> white.equals(path)) || + JWT_SKIP_LIST_START_WITH.stream().anyMatch(path::startsWith); + } +} diff --git a/src/main/java/com/sprint/mission/discodeit/security/TokenComponent.java b/src/main/java/com/sprint/mission/discodeit/security/TokenComponent.java new file mode 100644 index 000000000..fe975d3de --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/security/TokenComponent.java @@ -0,0 +1,10 @@ +package com.sprint.mission.discodeit.security; + +import com.nimbusds.jose.JWSSigner; + +public record TokenComponent(long expirationMs, JWSSigner signer, String tokenType) { + + public static TokenComponent of(long expirationMs, JWSSigner signer, String tokenType) { + return new TokenComponent(expirationMs, signer, tokenType); + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index c48adfb23..ed084df04 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -92,6 +92,14 @@ management: build: enabled: true +security: + jwt: + issuer: codeit-blog + secret: your-jwt-secret-key-here-12345628219012345 + access-token-validity-seconds: 3600 # 1시간 + refresh-token-validity-seconds: 86400 # 1일 + + server: servlet: session: diff --git "a/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" "b/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" index 0956e1d50..5e2ab30d8 100644 --- "a/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" +++ "b/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" @@ -1,10 +1,10 @@ ## JWT 컴포넌트 구현 -- [ ] JWT 의존성을 추가하세요. +- [x] JWT 의존성을 추가하세요. implementation 'com.nimbusds:nimbus-jose-jwt:10.3' -- [ ] 토큰을 발급, 갱신, 유효성 검사를 담당하는 컴포넌트(JwtTokenProvider)를 구현하세요. +- [x] 토큰을 발급, 갱신, 유효성 검사를 담당하는 컴포넌트(JwtTokenProvider)를 구현하세요. ![](https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=14437&version=1&directory=/s0yti3992-image.png&name=s0yti3992-image.png) @@ -14,7 +14,7 @@ implementation 'com.nimbusds:nimbus-jose-jwt:10.3' 미션 9와 마찬가지로 Spring Security의 formLogin + 미션 9의 인증 흐름은 그대로 유지하면서 필요한 부분만 대체합니다. -- [ ] 세션 생성 정책을 `STATELESS`로 변경하고, `sessionConcurrency` 설정을 삭제하세요. +- [x] 세션 생성 정책을 `STATELESS`로 변경하고, `sessionConcurrency` 설정을 삭제하세요. ```java http @@ -26,7 +26,7 @@ sessionCreationPolicy(...) ) ``` -- [ ] AuthenticationSuccessHandler 컴포넌트를 대체하세요. +- [x] AuthenticationSuccessHandler 컴포넌트를 대체하세요. - 기존 구현체는 LoginSuccessHandler입니다. - JwtLoginSuccessHandler를 정의하고 대체하세요. @@ -66,7 +66,7 @@ public class LoginSuccessHandler implements AuthenticationSuccessHandler { ## JWT 인증 필터 구현 -- [ ] 엑세스 토큰을 통해 인증하는 필터(JwtAuthenticationFilter)를 구현하세요. +- [x] 엑세스 토큰을 통해 인증하는 필터(JwtAuthenticationFilter)를 구현하세요. ```java public class JwtAuthenticationFilter extends OncePerRequestFilter { From 890c5157d02a37f51be35c6e48aace0acbbcbd18 Mon Sep 17 00:00:00 2001 From: "Lim, gyuseong" <100032171+gyural@users.noreply.github.com> Date: Fri, 28 Nov 2025 18:07:56 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20jwt=20=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=EB=B0=8F=20refresh=20api=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../discodeit/controller/AuthController.java | 28 ++++++++-- .../controller/ChannelController.java | 1 + .../discodeit/controller/UserController.java | 1 + .../dto/command/GetNewAccTokenCommand.java | 12 +++++ .../domain/dto/response/JwtResponse.java | 6 +++ .../mission/discodeit/mapper/AuthMapper.java | 15 ++++++ .../security/JwtAuthenticationFilter.java | 6 +++ .../discodeit/security/SecurityConfig.java | 3 -- .../discodeit/security/SecurityWhitelist.java | 1 - .../discodeit/service/AuthService.java | 5 ++ .../service/basic/BasicAuthService.java | 51 +++++++++++++++++++ src/main/resources/application-prod.yaml | 3 +- ...24\352\265\254\354\202\254\355\225\255.md" | 4 +- 13 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/sprint/mission/discodeit/domain/dto/command/GetNewAccTokenCommand.java create mode 100644 src/main/java/com/sprint/mission/discodeit/domain/dto/response/JwtResponse.java create mode 100644 src/main/java/com/sprint/mission/discodeit/mapper/AuthMapper.java diff --git a/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java b/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java index 672749771..e94dc36cd 100644 --- a/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java +++ b/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java @@ -1,22 +1,27 @@ package com.sprint.mission.discodeit.controller; +import static com.sprint.mission.discodeit.security.JwtTokenProvider.*; import static org.springframework.http.HttpStatus.*; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.sprint.mission.discodeit.domain.dto.command.GetNewAccTokenCommand; import com.sprint.mission.discodeit.domain.dto.command.UpdateRoleCommand; +import com.sprint.mission.discodeit.domain.dto.jwt.JwtDto; import com.sprint.mission.discodeit.domain.dto.request.UserRoleUpdateRequest; +import com.sprint.mission.discodeit.domain.dto.response.JwtResponse; import com.sprint.mission.discodeit.domain.dto.user.UserDto; -import com.sprint.mission.discodeit.security.DiscodeitUserDetails; +import com.sprint.mission.discodeit.mapper.AuthMapper; import com.sprint.mission.discodeit.service.AuthService; import io.swagger.v3.oas.annotations.tags.Tag; @@ -32,6 +37,7 @@ public class AuthController { private final AuthService authService; + private final AuthMapper authMapper; @GetMapping("/csrf-token") public ResponseEntity getCsrfToken(CsrfToken csrfToken, HttpServletResponse response) { @@ -47,9 +53,8 @@ public ResponseEntity getCsrfToken(CsrfToken csrfToken, HttpServletRespons } @GetMapping("/me") - private ResponseEntity me(@AuthenticationPrincipal DiscodeitUserDetails userDetails) { - UserDto userDto = userDetails.getUserDto(); - + private ResponseEntity me(@CookieValue(REFRESH_TOKEN_COOKIE_NAME) String refreshToken) { + UserDto userDto = authService.getProfile(refreshToken); return ResponseEntity.ok(userDto); } @@ -62,6 +67,19 @@ private ResponseEntity updateRole(@RequestBody UserRoleUpdateRequest re } + @PostMapping("/refresh") + ResponseEntity refresh( + @CookieValue(REFRESH_TOKEN_COOKIE_NAME) String refreshToken, + HttpServletResponse response + ) { + GetNewAccTokenCommand command = GetNewAccTokenCommand.from(refreshToken, response); + JwtDto result = authService.getNewAccToken(command); + JwtResponse jwtResponse = authMapper.toResponse(result); + + return ResponseEntity.ok(jwtResponse); + + } + private ResponseCookie getCsrfCookie(String tokenValue) { return ResponseCookie.from("XSRF-TOKEN", tokenValue) diff --git a/src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java b/src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java index cad672966..94a0c8102 100644 --- a/src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java +++ b/src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java @@ -141,6 +141,7 @@ public ResponseEntity updatePublicChannel( public ResponseEntity> getAllByUserId( @Parameter(description = "조회할 User ID") @RequestParam UUID userId) { + List channels = channelService.readAllByUserId(userId); List body = channels.stream().map(channelMapper::toResponse).toList(); diff --git a/src/main/java/com/sprint/mission/discodeit/controller/UserController.java b/src/main/java/com/sprint/mission/discodeit/controller/UserController.java index 0e5a780c5..b09ff930a 100644 --- a/src/main/java/com/sprint/mission/discodeit/controller/UserController.java +++ b/src/main/java/com/sprint/mission/discodeit/controller/UserController.java @@ -80,6 +80,7 @@ public ResponseEntity createUser( @GetMapping public ResponseEntity> getAllUser() { + System.out.println("???"); List body = userService.readAll().stream() .map(userMapper::toResponse) .toList(); diff --git a/src/main/java/com/sprint/mission/discodeit/domain/dto/command/GetNewAccTokenCommand.java b/src/main/java/com/sprint/mission/discodeit/domain/dto/command/GetNewAccTokenCommand.java new file mode 100644 index 000000000..f7410a397 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/domain/dto/command/GetNewAccTokenCommand.java @@ -0,0 +1,12 @@ +package com.sprint.mission.discodeit.domain.dto.command; + +import jakarta.servlet.http.HttpServletResponse; + +public record GetNewAccTokenCommand(String refreshToken, HttpServletResponse response) { + public static GetNewAccTokenCommand from( + String refreshToken, + HttpServletResponse response) { + + return new GetNewAccTokenCommand(refreshToken, response); + } +} diff --git a/src/main/java/com/sprint/mission/discodeit/domain/dto/response/JwtResponse.java b/src/main/java/com/sprint/mission/discodeit/domain/dto/response/JwtResponse.java new file mode 100644 index 000000000..3f9a24b15 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/domain/dto/response/JwtResponse.java @@ -0,0 +1,6 @@ +package com.sprint.mission.discodeit.domain.dto.response; + +import com.sprint.mission.discodeit.domain.dto.user.UserDto; + +public record JwtResponse(UserDto userDto, String accToken) { +} diff --git a/src/main/java/com/sprint/mission/discodeit/mapper/AuthMapper.java b/src/main/java/com/sprint/mission/discodeit/mapper/AuthMapper.java new file mode 100644 index 000000000..83a5c2aad --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/mapper/AuthMapper.java @@ -0,0 +1,15 @@ +package com.sprint.mission.discodeit.mapper; + +import org.mapstruct.Mapper; + +import com.sprint.mission.discodeit.domain.dto.binaryContent.BinaryContentDto; +import com.sprint.mission.discodeit.domain.dto.binaryContent.BinaryContentResponse; +import com.sprint.mission.discodeit.domain.dto.jwt.JwtDto; +import com.sprint.mission.discodeit.domain.dto.response.JwtResponse; + +@Mapper(componentModel = "spring") +public interface AuthMapper { + JwtResponse toResponse(JwtDto jwtDto); + + BinaryContentResponse toResponse(BinaryContentDto dto); +} diff --git a/src/main/java/com/sprint/mission/discodeit/security/JwtAuthenticationFilter.java b/src/main/java/com/sprint/mission/discodeit/security/JwtAuthenticationFilter.java index ff8d112c9..c19576b23 100644 --- a/src/main/java/com/sprint/mission/discodeit/security/JwtAuthenticationFilter.java +++ b/src/main/java/com/sprint/mission/discodeit/security/JwtAuthenticationFilter.java @@ -36,6 +36,12 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { String path = request.getServletPath(); + + String method = request.getMethod(); + if (method.equals("POST") && path.equals("/api/users")) { + return true; + } + return SecurityWhitelist.matchesJwtWhiteList(path); } diff --git a/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java b/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java index f57f93ebd..e196a4ace 100644 --- a/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java +++ b/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java @@ -47,8 +47,6 @@ public SecurityFilterChain filterChain( HttpSecurity http, JwtLoginSuccessHandler jwtLoginSuccessHandler, LoginFailureHandler loginFailureHandler, - DaoAuthenticationProvider daoAuthenticationProvider, - SessionRegistry sessionRegistry, JwtAuthenticationFilter jwtAuthenticationFilter ) throws Exception { @@ -57,7 +55,6 @@ public SecurityFilterChain filterChain( // permitAll 경로 설정 .requestMatchers(SecurityWhitelist.WHITE_LIST.toArray(String[]::new)).permitAll() .requestMatchers(HttpMethod.POST, "/api/users").permitAll() // 회원 가입 - .anyRequest().authenticated() ) .formLogin(login -> login diff --git a/src/main/java/com/sprint/mission/discodeit/security/SecurityWhitelist.java b/src/main/java/com/sprint/mission/discodeit/security/SecurityWhitelist.java index 9c3f85ee8..17306d72d 100644 --- a/src/main/java/com/sprint/mission/discodeit/security/SecurityWhitelist.java +++ b/src/main/java/com/sprint/mission/discodeit/security/SecurityWhitelist.java @@ -30,7 +30,6 @@ public class SecurityWhitelist { // JWT_SKIP_LIST = 공통 + 추가 스킵 리스트 public static final List JWT_SKIP_LIST = new ArrayList<>() {{ addAll(COMMON_LIST); - add("/api/users"); }}; public static final List JWT_SKIP_LIST_START_WITH = List.of( diff --git a/src/main/java/com/sprint/mission/discodeit/service/AuthService.java b/src/main/java/com/sprint/mission/discodeit/service/AuthService.java index aaa99d388..e55e0cc67 100644 --- a/src/main/java/com/sprint/mission/discodeit/service/AuthService.java +++ b/src/main/java/com/sprint/mission/discodeit/service/AuthService.java @@ -1,10 +1,15 @@ package com.sprint.mission.discodeit.service; +import com.sprint.mission.discodeit.domain.dto.command.GetNewAccTokenCommand; import com.sprint.mission.discodeit.domain.dto.command.UpdateRoleCommand; +import com.sprint.mission.discodeit.domain.dto.jwt.JwtDto; import com.sprint.mission.discodeit.domain.dto.user.UserDto; public interface AuthService { UserDto updateRole(UpdateRoleCommand command); + JwtDto getNewAccToken(GetNewAccTokenCommand command); + + UserDto getProfile(String refreshToken); } diff --git a/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java b/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java index b11de0317..518019570 100644 --- a/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java +++ b/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java @@ -1,6 +1,7 @@ package com.sprint.mission.discodeit.service.basic; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -12,17 +13,26 @@ import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; +import com.nimbusds.jose.JOSEException; import com.sprint.mission.discodeit.domain.dto.binaryContent.BinaryContentDto; +import com.sprint.mission.discodeit.domain.dto.command.GetNewAccTokenCommand; import com.sprint.mission.discodeit.domain.dto.command.UpdateRoleCommand; +import com.sprint.mission.discodeit.domain.dto.jwt.JwtDto; import com.sprint.mission.discodeit.domain.dto.user.UserDto; import com.sprint.mission.discodeit.domain.entity.User; import com.sprint.mission.discodeit.domain.enums.Role; +import com.sprint.mission.discodeit.exception.auth.NotAuthenticationException; +import com.sprint.mission.discodeit.exception.server.InternalServerErrorException; import com.sprint.mission.discodeit.exception.user.UserNotFoundException; import com.sprint.mission.discodeit.repository.UserRepository; import com.sprint.mission.discodeit.security.DiscodeitUserDetails; +import com.sprint.mission.discodeit.security.DiscodeitUserDetailsService; +import com.sprint.mission.discodeit.security.JwtTokenProvider; import com.sprint.mission.discodeit.security.SecurityService; import com.sprint.mission.discodeit.service.AuthService; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @Service @@ -32,6 +42,8 @@ public class BasicAuthService implements AuthService { private final UserRepository userRepository; private final SessionRegistry sessionRegistry; private final SecurityService securityService; + private final JwtTokenProvider jwtTokenProvider; + private final DiscodeitUserDetailsService discodeitUserDetailsService; @Override @Transactional @@ -53,6 +65,45 @@ public UserDto updateRole(UpdateRoleCommand command) { return UserDto.of(user, profile, isOnline); } + @Override + public JwtDto getNewAccToken(GetNewAccTokenCommand command) { + String refreshToken = command.refreshToken(); + HttpServletResponse response = command.response(); + + // 유효하지 않다면 401 + if (!jwtTokenProvider.validateRefreshToken(refreshToken)) { + throw new NotAuthenticationException(); + } + + String username = jwtTokenProvider.getUsernameFromToken(refreshToken); + DiscodeitUserDetails userDetails = discodeitUserDetailsService.loadUserByUsername(username); + + try { + String newAccToken = jwtTokenProvider.generateAccessToken(userDetails); + String newRefreshToken = jwtTokenProvider.generateRefreshToken(userDetails); + Cookie newAccTokenCookie = jwtTokenProvider.genereateRefreshTokenCookie(newRefreshToken); + + response.addCookie(newAccTokenCookie); + return new JwtDto(userDetails.getUserDto(), newAccToken); + + } catch (JOSEException e) { + throw new InternalServerErrorException(Map + .of("auth service", "generateAccessToken 실패")); + } + + } + + @Override + public UserDto getProfile(String refreshToken) { + if (!jwtTokenProvider.validateRefreshToken(refreshToken)) { + throw new NotAuthenticationException(); + } + String username = jwtTokenProvider.getUsernameFromToken(refreshToken); + DiscodeitUserDetails userDetails = discodeitUserDetailsService.loadUserByUsername(username); + + return userDetails.getUserDto(); + } + private void registerExpireSessionAfterCommit(User user) { TransactionSynchronizationManager.registerSynchronization( new TransactionSynchronization() { diff --git a/src/main/resources/application-prod.yaml b/src/main/resources/application-prod.yaml index aa0a142e2..97c7ae996 100644 --- a/src/main/resources/application-prod.yaml +++ b/src/main/resources/application-prod.yaml @@ -28,4 +28,5 @@ spring: logging: level: - root: info \ No newline at end of file + root: info + org.springframework.security: trace \ No newline at end of file diff --git "a/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" "b/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" index 5e2ab30d8..47db02487 100644 --- "a/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" +++ "b/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" @@ -114,9 +114,9 @@ setAuthentication(authentication); - permitAll 설정에 포함하세요. - 이 API는 엑세스 토큰이 없거나 만료된 상태에서 호출하게 됩니다. -- [ ] 리프레시 토큰 Rotation을 통해 보안을 강화하세요. +- [x] 리프레시 토큰 Rotation을 통해 보안을 강화하세요. -- [ ] 토큰 재발급 API로 대체할 수 있는 컴포넌트를 모두 삭제하세요. +- [x] 토큰 재발급 API로 대체할 수 있는 컴포넌트를 모두 삭제하세요. - Me API (GET /auth/me) > - 프론트엔드 2.0.x과 마찬가지로 2.1.x에서는 사용자 정보와 엑세스 토큰 정보를 브라우저의 메모리에서 관리합니다. > - 따라서 새로고침 시 쿠키에 저장된 리프레시 토큰을 통해 엑세스 토큰을 갱신합니다. From 6546ba6b20f293861bdc45b5bbcdd32b0a63abfb Mon Sep 17 00:00:00 2001 From: "Lim, gyuseong" <100032171+gyural@users.noreply.github.com> Date: Fri, 28 Nov 2025 18:17:14 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20jwt=20logout=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../discodeit/security/JwtLogoutHandler.java | 24 +++++++++++++++++++ .../discodeit/security/SecurityConfig.java | 7 +++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/sprint/mission/discodeit/security/JwtLogoutHandler.java diff --git a/src/main/java/com/sprint/mission/discodeit/security/JwtLogoutHandler.java b/src/main/java/com/sprint/mission/discodeit/security/JwtLogoutHandler.java new file mode 100644 index 000000000..2b8426b42 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/security/JwtLogoutHandler.java @@ -0,0 +1,24 @@ +package com.sprint.mission.discodeit.security; + +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutHandler; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class JwtLogoutHandler implements LogoutHandler { + + private final JwtTokenProvider jwtTokenProvider; + + @Override + public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { + + Cookie cookie = jwtTokenProvider.genereateRefreshTokenExpirationCookie(); + response.addCookie(cookie); + } +} diff --git a/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java b/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java index e196a4ace..17a784230 100644 --- a/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java +++ b/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java @@ -8,6 +8,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.access.hierarchicalroles.RoleHierarchy; @@ -46,6 +47,7 @@ public class SecurityConfig { public SecurityFilterChain filterChain( HttpSecurity http, JwtLoginSuccessHandler jwtLoginSuccessHandler, + JwtLogoutHandler jwtLogoutHandler, LoginFailureHandler loginFailureHandler, JwtAuthenticationFilter jwtAuthenticationFilter ) throws Exception { @@ -64,11 +66,14 @@ public SecurityFilterChain filterChain( ) .logout(logout -> logout .logoutUrl("/api/auth/logout") - .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(NO_CONTENT)) + .addLogoutHandler(jwtLogoutHandler) + .logoutSuccessHandler( + new HttpStatusReturningLogoutSuccessHandler(HttpStatus.NO_CONTENT)) ) .csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler()) + .ignoringRequestMatchers("/api/auth/logout") ) .exceptionHandling(ex -> ex From ff5315786c0e2eff736b7f5854284419ab44fc1e Mon Sep 17 00:00:00 2001 From: "Lim, gyuseong" <100032171+gyural@users.noreply.github.com> Date: Mon, 1 Dec 2025 16:41:56 +0900 Subject: [PATCH 6/6] =?UTF-8?q?feat:=20jwt=20=EC=8B=AC=ED=99=94=20?= =?UTF-8?q?=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sprint/mission/DiscodeitApplication.java | 2 + .../discodeit/controller/AuthController.java | 2 +- .../discodeit/controller/UserController.java | 1 - .../discodeit/security/SecurityConfig.java | 3 + .../discodeit/security/SecurityService.java | 60 ++------ .../security/jwt/InMemoryJwtRegistry.java | 130 ++++++++++++++++++ .../{ => jwt}/JwtAuthenticationFilter.java | 12 +- .../security/jwt/JwtInformation.java | 21 +++ .../{ => jwt}/JwtLoginSuccessHandler.java | 7 +- .../security/{ => jwt}/JwtLogoutHandler.java | 11 +- .../discodeit/security/jwt/JwtRegistry.java | 48 +++++++ .../security/{ => jwt}/JwtTokenProvider.java | 5 +- .../service/basic/BasicAuthService.java | 25 +--- src/main/resources/application.yaml | 1 + ...24\352\265\254\354\202\254\355\225\255.md" | 4 +- ...24\352\265\254\354\202\254\355\225\255.md" | 34 ++--- 16 files changed, 266 insertions(+), 100 deletions(-) create mode 100644 src/main/java/com/sprint/mission/discodeit/security/jwt/InMemoryJwtRegistry.java rename src/main/java/com/sprint/mission/discodeit/security/{ => jwt}/JwtAuthenticationFilter.java (90%) create mode 100644 src/main/java/com/sprint/mission/discodeit/security/jwt/JwtInformation.java rename src/main/java/com/sprint/mission/discodeit/security/{ => jwt}/JwtLoginSuccessHandler.java (88%) rename src/main/java/com/sprint/mission/discodeit/security/{ => jwt}/JwtLogoutHandler.java (66%) create mode 100644 src/main/java/com/sprint/mission/discodeit/security/jwt/JwtRegistry.java rename src/main/java/com/sprint/mission/discodeit/security/{ => jwt}/JwtTokenProvider.java (97%) diff --git a/src/main/java/com/sprint/mission/DiscodeitApplication.java b/src/main/java/com/sprint/mission/DiscodeitApplication.java index c88939465..051e3438b 100644 --- a/src/main/java/com/sprint/mission/DiscodeitApplication.java +++ b/src/main/java/com/sprint/mission/DiscodeitApplication.java @@ -4,9 +4,11 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableJpaAuditing +@EnableScheduling public class DiscodeitApplication { public static void main(String[] args) { diff --git a/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java b/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java index e94dc36cd..94f4f3b8a 100644 --- a/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java +++ b/src/main/java/com/sprint/mission/discodeit/controller/AuthController.java @@ -1,6 +1,6 @@ package com.sprint.mission.discodeit.controller; -import static com.sprint.mission.discodeit.security.JwtTokenProvider.*; +import static com.sprint.mission.discodeit.security.jwt.JwtTokenProvider.*; import static org.springframework.http.HttpStatus.*; import org.springframework.http.HttpHeaders; diff --git a/src/main/java/com/sprint/mission/discodeit/controller/UserController.java b/src/main/java/com/sprint/mission/discodeit/controller/UserController.java index b09ff930a..0e5a780c5 100644 --- a/src/main/java/com/sprint/mission/discodeit/controller/UserController.java +++ b/src/main/java/com/sprint/mission/discodeit/controller/UserController.java @@ -80,7 +80,6 @@ public ResponseEntity createUser( @GetMapping public ResponseEntity> getAllUser() { - System.out.println("???"); List body = userService.readAll().stream() .map(userMapper::toResponse) .toList(); diff --git a/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java b/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java index 17a784230..05692b304 100644 --- a/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java +++ b/src/main/java/com/sprint/mission/discodeit/security/SecurityConfig.java @@ -31,6 +31,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.sprint.mission.discodeit.exception.ErrorResponse; +import com.sprint.mission.discodeit.security.jwt.JwtAuthenticationFilter; +import com.sprint.mission.discodeit.security.jwt.JwtLoginSuccessHandler; +import com.sprint.mission.discodeit.security.jwt.JwtLogoutHandler; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/com/sprint/mission/discodeit/security/SecurityService.java b/src/main/java/com/sprint/mission/discodeit/security/SecurityService.java index b830452d2..5784c5c0a 100644 --- a/src/main/java/com/sprint/mission/discodeit/security/SecurityService.java +++ b/src/main/java/com/sprint/mission/discodeit/security/SecurityService.java @@ -2,20 +2,16 @@ import static java.util.stream.Collectors.*; -import java.util.Comparator; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.function.BinaryOperator; -import java.util.function.Function; import org.springframework.security.core.session.SessionRegistry; import org.springframework.stereotype.Service; -import com.sprint.mission.discodeit.domain.dto.user.UserSessionStatus; import com.sprint.mission.discodeit.domain.entity.User; import com.sprint.mission.discodeit.repository.MessageRepository; +import com.sprint.mission.discodeit.security.jwt.JwtRegistry; import lombok.RequiredArgsConstructor; @@ -25,6 +21,7 @@ public class SecurityService { private final MessageRepository messageRepository; private final SessionRegistry sessionRegistry; + private final JwtRegistry jwtRegistry; public boolean isMessageOwner(UUID messageId, UUID userId) { return messageRepository.findById(messageId) @@ -36,60 +33,19 @@ public boolean isMessageOwner(UUID messageId, UUID userId) { } public Map getUserId2OnlineMap(Set userIds) { - - final long ONLINE_THRESHOLD_MS = 5 * 60 * 1000L; - long now = System.currentTimeMillis(); - - // 1) 모든 UserDetails 추출 - List allUsers = sessionRegistry.getAllPrincipals().stream() - .filter(p -> p instanceof DiscodeitUserDetails) - .filter(p -> userIds - .contains( - ((DiscodeitUserDetails)p).getUserDto().getId() - ) - ) - .map(p -> (DiscodeitUserDetails)p) - .toList(); - - // 2) 각 유저별 모든 세션 정보 수집 - List allSessions = allUsers.stream() - .flatMap(user -> sessionRegistry.getAllSessions(user, false).stream() - .map(si -> new UserSessionStatus( - user.getUserId(), - si.getLastRequest().getTime(), - now - si.getLastRequest().getTime() <= ONLINE_THRESHOLD_MS - )) - ) - .toList(); - - // 3) 유저별 최신 세션만 선택 - Map latestSessionPerUser = allSessions.stream() - .collect(toMap( - UserSessionStatus::userID, - Function.identity(), - BinaryOperator.maxBy(Comparator.comparingLong(UserSessionStatus::lastRequest))) - ); - - // 4) userId 별 최종값 반환 return userIds.stream().collect(toMap( id -> id, - id -> latestSessionPerUser.containsKey(id) && latestSessionPerUser.get(id).online() + this::isOnline )); } public boolean isOnline(User user) { - final long ONLINE_THRESHOLD_MS = 5 * 60 * 1000L; // 5분(5 * 60초 * 1000ms) - long now = System.currentTimeMillis(); + UUID userId = user.getId(); + return isOnline(userId); + } - return sessionRegistry.getAllPrincipals().stream() - .filter(principal -> principal instanceof DiscodeitUserDetails) - .map(principal -> (DiscodeitUserDetails)principal) - .filter(details -> details.getUsername().equals(user.getUsername())) - .flatMap(details -> sessionRegistry.getAllSessions(details, false).stream()) - .anyMatch(sessionInfo -> { - long lastRequest = sessionInfo.getLastRequest().getTime(); - return (now - lastRequest) <= ONLINE_THRESHOLD_MS; - }); + public boolean isOnline(UUID userId) { + return jwtRegistry.hasActiveJwtInformationByUserId(userId); } } diff --git a/src/main/java/com/sprint/mission/discodeit/security/jwt/InMemoryJwtRegistry.java b/src/main/java/com/sprint/mission/discodeit/security/jwt/InMemoryJwtRegistry.java new file mode 100644 index 000000000..f34f0c3ca --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/security/jwt/InMemoryJwtRegistry.java @@ -0,0 +1,130 @@ +package com.sprint.mission.discodeit.security.jwt; + +import java.util.ArrayDeque; +import java.util.Map; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +public class InMemoryJwtRegistry implements JwtRegistry { + // > + private final Map> origin = new ConcurrentHashMap<>(); + private final Set accessTokenIndexes = ConcurrentHashMap.newKeySet(); + private final Set refreshTokenIndexes = ConcurrentHashMap.newKeySet(); + private final int maxActiveJwtCount; + private final JwtTokenProvider jwtTokenProvider; + + public InMemoryJwtRegistry(@Value("${discodeit.max-login}") int maxActiveJwtCount, + JwtTokenProvider jwtTokenProvider) { + this.maxActiveJwtCount = maxActiveJwtCount; + this.jwtTokenProvider = jwtTokenProvider; + } + + @Override + public void registerJwtInformation(JwtInformation jwtInformation) { + UUID userId = jwtInformation.getUserDto().getId(); + insertOrigin(userId, jwtInformation); + } + + @Override + public void invalidateJwtInformationByUserId(UUID userId) { + origin.get(userId).forEach((jwtInformation) -> { + deleteOrigin(userId, jwtInformation); + }); + } + + @Override + public boolean hasActiveJwtInformationByUserId(UUID userId) { + return Optional.ofNullable(origin.get(userId)) + .map(queues -> !queues.isEmpty()) + .orElse(false); + } + + @Override + public boolean hasActiveJwtInformationByAccessToken(String accessToken) { + return accessTokenIndexes.contains(accessToken); + } + + @Override + public boolean hasActiveJwtInformationByRefreshToken(String refreshToken) { + return refreshTokenIndexes.contains(refreshToken); + } + + @Override + public void rotateJwtInformation(String refreshToken, JwtInformation newJwtInformation) { + // 1. 기존 토큰 전부 삭제 + UUID userId = newJwtInformation.getUserDto().getId(); + clearOrigin(userId); + insertOrigin(userId, newJwtInformation); + } + + @Scheduled(fixedDelay = 1000 * 60 * 5) + @Override + public void clearExpiredJwtInformation() { + origin.values().forEach(queue -> + queue.removeIf(QEl -> + { + String accToken = QEl.getAccessToken(); + String refreshToken = QEl.getRefreshToken(); + + boolean isExpired = !jwtTokenProvider.validateAccessToken(accToken) || + !jwtTokenProvider.validateRefreshToken(refreshToken); + + if (isExpired) { + accessTokenIndexes.remove(accToken); + accessTokenIndexes.remove(refreshToken); + } + return isExpired; + } + ) + ); + } + + private void insertOrigin(UUID key, JwtInformation jwtInformation) { + origin.putIfAbsent(key, new ArrayDeque<>()); + + Queue Q = origin.get(key); + + Q.add(jwtInformation); + System.out.println("added " + jwtInformation); + System.out.println(Q.size()); + while (Q.size() > maxActiveJwtCount) { + JwtInformation jwtInfo = Q.poll(); + accessTokenIndexes.remove(jwtInfo.getAccessToken()); + refreshTokenIndexes.remove(jwtInfo.getRefreshToken()); + } + + accessTokenIndexes.add(jwtInformation.getAccessToken()); + refreshTokenIndexes.add(jwtInformation.getRefreshToken()); + } + + private void deleteOrigin(UUID key, JwtInformation jwtInformation) { + String toDeleteAccToken = jwtInformation.getAccessToken(); + String toDeleteRefreshToken = jwtInformation.getRefreshToken(); + accessTokenIndexes.remove(toDeleteAccToken); + accessTokenIndexes.remove(toDeleteRefreshToken); + + origin.get(key).removeIf(jwt -> jwt.equals(jwtInformation)); + + } + + private void clearOrigin(UUID userId) { + origin.get(userId).forEach(jwt + -> { + String accessToken = jwt.getAccessToken(); + String refreshToken = jwt.getRefreshToken(); + accessTokenIndexes.remove(accessToken); + refreshTokenIndexes.remove(refreshToken); + }); + + origin.get(userId).clear(); + + } +} diff --git a/src/main/java/com/sprint/mission/discodeit/security/JwtAuthenticationFilter.java b/src/main/java/com/sprint/mission/discodeit/security/jwt/JwtAuthenticationFilter.java similarity index 90% rename from src/main/java/com/sprint/mission/discodeit/security/JwtAuthenticationFilter.java rename to src/main/java/com/sprint/mission/discodeit/security/jwt/JwtAuthenticationFilter.java index c19576b23..87315707a 100644 --- a/src/main/java/com/sprint/mission/discodeit/security/JwtAuthenticationFilter.java +++ b/src/main/java/com/sprint/mission/discodeit/security/jwt/JwtAuthenticationFilter.java @@ -1,4 +1,4 @@ -package com.sprint.mission.discodeit.security; +package com.sprint.mission.discodeit.security.jwt; import static jakarta.servlet.http.HttpServletResponse.*; import static org.springframework.http.MediaType.*; @@ -14,7 +14,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.sprint.mission.discodeit.exception.ErrorResponse; import com.sprint.mission.discodeit.exception.auth.NotAuthenticationException; -import com.sprint.mission.discodeit.repository.UserRepository; +import com.sprint.mission.discodeit.security.DiscodeitUserDetails; +import com.sprint.mission.discodeit.security.SecurityWhitelist; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -28,7 +29,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtTokenProvider jwtTokenProvider; private final ObjectMapper objectMapper; - private final UserRepository userRepository; + private final JwtRegistry jwtRegistry; private final UserDetailsService userDetailsService; // 필터에서 제외할 request를 탐지할 메서드 @@ -56,6 +57,11 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse return; } + if (!jwtRegistry.hasActiveJwtInformationByAccessToken(accToken)) { + sendErrorResponse(response); + return; + } + String username = jwtTokenProvider.getUsernameFromToken(accToken); DiscodeitUserDetails userDetails = (DiscodeitUserDetails)userDetailsService.loadUserByUsername(username); diff --git a/src/main/java/com/sprint/mission/discodeit/security/jwt/JwtInformation.java b/src/main/java/com/sprint/mission/discodeit/security/jwt/JwtInformation.java new file mode 100644 index 000000000..44c389e33 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/security/jwt/JwtInformation.java @@ -0,0 +1,21 @@ +package com.sprint.mission.discodeit.security.jwt; + +import com.sprint.mission.discodeit.domain.dto.user.UserDto; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@AllArgsConstructor +@EqualsAndHashCode +public class JwtInformation { + private final UserDto userDto; + private final String accessToken; + private final String refreshToken; + + // todo 구현해야함 + public void rotate(String accessToken, String refreshToken) { + return; + } +} diff --git a/src/main/java/com/sprint/mission/discodeit/security/JwtLoginSuccessHandler.java b/src/main/java/com/sprint/mission/discodeit/security/jwt/JwtLoginSuccessHandler.java similarity index 88% rename from src/main/java/com/sprint/mission/discodeit/security/JwtLoginSuccessHandler.java rename to src/main/java/com/sprint/mission/discodeit/security/jwt/JwtLoginSuccessHandler.java index 6739547a6..cedc4aed3 100644 --- a/src/main/java/com/sprint/mission/discodeit/security/JwtLoginSuccessHandler.java +++ b/src/main/java/com/sprint/mission/discodeit/security/jwt/JwtLoginSuccessHandler.java @@ -1,4 +1,4 @@ -package com.sprint.mission.discodeit.security; +package com.sprint.mission.discodeit.security.jwt; import static com.sprint.mission.discodeit.exception.ErrorCode.*; import static jakarta.servlet.http.HttpServletResponse.*; @@ -15,6 +15,7 @@ import com.sprint.mission.discodeit.domain.dto.jwt.JwtDto; import com.sprint.mission.discodeit.domain.dto.user.UserDto; import com.sprint.mission.discodeit.exception.ErrorResponse; +import com.sprint.mission.discodeit.security.DiscodeitUserDetails; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; @@ -29,6 +30,7 @@ public class JwtLoginSuccessHandler implements AuthenticationSuccessHandler { private final ObjectMapper objectMapper; private final JwtTokenProvider jwtTokenProvider; + private final JwtRegistry jwtRegistry; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, @@ -43,6 +45,9 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo JwtDto jwtDto = new JwtDto(userDto, accessToken); + JwtInformation jwtInformation = new JwtInformation(userDto, accessToken, refreshToken); + jwtRegistry.registerJwtInformation(jwtInformation); + response.addCookie(refreshCookie); response.setContentType(APPLICATION_JSON_VALUE); response.setStatus(HttpServletResponse.SC_OK); diff --git a/src/main/java/com/sprint/mission/discodeit/security/JwtLogoutHandler.java b/src/main/java/com/sprint/mission/discodeit/security/jwt/JwtLogoutHandler.java similarity index 66% rename from src/main/java/com/sprint/mission/discodeit/security/JwtLogoutHandler.java rename to src/main/java/com/sprint/mission/discodeit/security/jwt/JwtLogoutHandler.java index 2b8426b42..3d8e8452c 100644 --- a/src/main/java/com/sprint/mission/discodeit/security/JwtLogoutHandler.java +++ b/src/main/java/com/sprint/mission/discodeit/security/jwt/JwtLogoutHandler.java @@ -1,9 +1,13 @@ -package com.sprint.mission.discodeit.security; +package com.sprint.mission.discodeit.security.jwt; + +import java.util.UUID; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutHandler; import org.springframework.stereotype.Component; +import com.sprint.mission.discodeit.security.DiscodeitUserDetails; + import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -14,10 +18,15 @@ public class JwtLogoutHandler implements LogoutHandler { private final JwtTokenProvider jwtTokenProvider; + private final JwtRegistry jwtRegistry; @Override public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { + DiscodeitUserDetails userDetails = (DiscodeitUserDetails)authentication.getPrincipal(); + UUID userId = userDetails.getUserId(); + jwtRegistry.invalidateJwtInformationByUserId(userId); + Cookie cookie = jwtTokenProvider.genereateRefreshTokenExpirationCookie(); response.addCookie(cookie); } diff --git a/src/main/java/com/sprint/mission/discodeit/security/jwt/JwtRegistry.java b/src/main/java/com/sprint/mission/discodeit/security/jwt/JwtRegistry.java new file mode 100644 index 000000000..fa5407268 --- /dev/null +++ b/src/main/java/com/sprint/mission/discodeit/security/jwt/JwtRegistry.java @@ -0,0 +1,48 @@ +package com.sprint.mission.discodeit.security.jwt; + +import java.util.UUID; + +public interface JwtRegistry { + /** + * 로그인 성공 시 JwtInformation을 등록합니다. + * 최대 동시 로그인 수(1)를 제어합니다. + * @param jwtInformation + */ + void registerJwtInformation(JwtInformation jwtInformation); + + /** + * UserId로 해당 유저의 모든 JwtInformation 정보를 삭제합니다. + * @param userId + */ + void invalidateJwtInformationByUserId(UUID userId); + + /** + * JwtInformation이 Registry에 존재하는지 확인합니다. + * @param userId + */ + boolean hasActiveJwtInformationByUserId(UUID userId); + + /** + * 필터에서 유효한 토큰인지 확인할 때 활용합니다. + * @param accessToken + */ + boolean hasActiveJwtInformationByAccessToken(String accessToken); + + /** + * 토큰 재발급 시 유효한 토큰인지 확인할 때 활용합니다. + * @param refreshToken + */ + boolean hasActiveJwtInformationByRefreshToken(String refreshToken); + + /** + * 토큰 재발급 시 토큰 로테이션을 수행합니다. + * @param refreshToken + * @param newJwtInformation + */ + void rotateJwtInformation(String refreshToken, JwtInformation newJwtInformation); + + /** + * 만료된 JwtInformation을 삭제합니다. + */ + void clearExpiredJwtInformation(); +} diff --git a/src/main/java/com/sprint/mission/discodeit/security/JwtTokenProvider.java b/src/main/java/com/sprint/mission/discodeit/security/jwt/JwtTokenProvider.java similarity index 97% rename from src/main/java/com/sprint/mission/discodeit/security/JwtTokenProvider.java rename to src/main/java/com/sprint/mission/discodeit/security/jwt/JwtTokenProvider.java index d74fd2ee3..5371321b9 100644 --- a/src/main/java/com/sprint/mission/discodeit/security/JwtTokenProvider.java +++ b/src/main/java/com/sprint/mission/discodeit/security/jwt/JwtTokenProvider.java @@ -1,4 +1,4 @@ -package com.sprint.mission.discodeit.security; +package com.sprint.mission.discodeit.security.jwt; import java.nio.charset.StandardCharsets; import java.util.Date; @@ -19,6 +19,8 @@ import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import com.sprint.mission.discodeit.domain.dto.user.UserDto; +import com.sprint.mission.discodeit.security.DiscodeitUserDetails; +import com.sprint.mission.discodeit.security.TokenComponent; import jakarta.servlet.http.Cookie; import lombok.extern.slf4j.Slf4j; @@ -26,7 +28,6 @@ @Slf4j @Component public class JwtTokenProvider { - // todo 요구사항과 비교 해야함 public static final String REFRESH_TOKEN_COOKIE_NAME = "REFRESH_TOKEN"; private final String issuer; diff --git a/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java b/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java index 518019570..e4159b052 100644 --- a/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java +++ b/src/main/java/com/sprint/mission/discodeit/service/basic/BasicAuthService.java @@ -1,13 +1,10 @@ package com.sprint.mission.discodeit.service.basic; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.session.SessionInformation; -import org.springframework.security.core.session.SessionRegistry; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronization; @@ -27,8 +24,9 @@ import com.sprint.mission.discodeit.repository.UserRepository; import com.sprint.mission.discodeit.security.DiscodeitUserDetails; import com.sprint.mission.discodeit.security.DiscodeitUserDetailsService; -import com.sprint.mission.discodeit.security.JwtTokenProvider; import com.sprint.mission.discodeit.security.SecurityService; +import com.sprint.mission.discodeit.security.jwt.JwtRegistry; +import com.sprint.mission.discodeit.security.jwt.JwtTokenProvider; import com.sprint.mission.discodeit.service.AuthService; import jakarta.servlet.http.Cookie; @@ -40,7 +38,7 @@ public class BasicAuthService implements AuthService { private final UserRepository userRepository; - private final SessionRegistry sessionRegistry; + private final JwtRegistry jwtRegistry; private final SecurityService securityService; private final JwtTokenProvider jwtTokenProvider; private final DiscodeitUserDetailsService discodeitUserDetailsService; @@ -110,27 +108,14 @@ private void registerExpireSessionAfterCommit(User user) { @Override public void afterCommit() { expireUserSessions(user); - System.out.println("afterCommit"); } } ); } private void expireUserSessions(User user) { - String username = user.getUsername(); - - List targetUserDetails = sessionRegistry.getAllPrincipals().stream() - .filter(principal -> principal instanceof DiscodeitUserDetails) // 타입 체크 - .map(principal -> (DiscodeitUserDetails)principal) // 캐스팅 - .filter(details -> details.getUsername().equals(username)) // username 필터 - .toList(); - - List sessions = targetUserDetails.stream() - .map(details -> sessionRegistry.getAllSessions(details, false)) // List - .flatMap(List::stream) - .toList(); - - sessions.forEach(SessionInformation::expireNow); + UUID userId = user.getId(); + jwtRegistry.invalidateJwtInformationByUserId(userId); } } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index ed084df04..78ccb8908 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -13,6 +13,7 @@ discodeit: bucket: ${AWS_S3_BUCKET:AWS_S3_BUCKET} # 추가 prefix: ${AWS_S3_PREFIX:defaultPrefix} presigned-url-expiration: ${AWS_S3_PRESIGNED_URL_EXPIRATION:600} # (기본값: 10분) # 추가 + max-login: 1 spring: config: diff --git "a/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" "b/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" index 47db02487..6e57c0fda 100644 --- "a/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" +++ "b/\352\270\260\353\263\270\354\232\224\352\265\254\354\202\254\355\225\255.md" @@ -129,7 +129,7 @@ setAuthentication(authentication); ## 리팩토링 - 로그아웃 -- [ ] 쿠키에 저장된 리프레시 토큰을 삭제하는 LogoutHandler를 구현하세요. +- [x] 쿠키에 저장된 리프레시 토큰을 삭제하는 LogoutHandler를 구현하세요. ```java public class JwtLogoutHandler implements LogoutHandler { @@ -140,7 +140,7 @@ public class JwtLogoutHandler implements LogoutHandler { ``` -- [ ] 구현한 핸들러를 추가하세요. +- [x] 구현한 핸들러를 추가하세요. ```java http diff --git "a/\354\213\254\355\231\224\354\232\224\352\265\254\354\202\254\355\225\255.md" "b/\354\213\254\355\231\224\354\232\224\352\265\254\354\202\254\355\225\255.md" index e5270c1d5..5cfe53492 100644 --- "a/\354\213\254\355\231\224\354\232\224\352\265\254\354\202\254\355\225\255.md" +++ "b/\354\213\254\355\231\224\354\232\224\352\265\254\354\202\254\355\225\255.md" @@ -4,7 +4,7 @@ - 따라서 SessionRegistry를 통해 세션의 상태를 관리했던 것처럼, JWT의 상태를 관리할 수 있는 컴포넌트를 추가해야합니다. - - [ ] 토큰의 상태를 관리하는 JwtRegistry를 구현하세요. + - [x] 토큰의 상태를 관리하는 JwtRegistry를 구현하세요. ![](https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=14442&version=1&directory=/9da9kvl8y-image.png&name=9da9kvl8y-image.png) @@ -34,16 +34,16 @@ public class InMemoryJwtRegistry implements JwtRegistry { } ``` -- [ ] JwtAuthenticationFilter에서 JwtRegistry를 활용해 토큰의 상태를 검사하는 로직을 추가하세요. +- [x] JwtAuthenticationFilter에서 JwtRegistry를 활용해 토큰의 상태를 검사하는 로직을 추가하세요. -- [ ] JwtRegistry를 활용해 동시 로그인 제한 기능을 리팩토링하세요. +- [x] JwtRegistry를 활용해 동시 로그인 제한 기능을 리팩토링하세요. - 동일한 계정으로 로그인 시 기존 로그인 세션을 무효화합니다. -- [ ] JwtRegistry를 활용해 권한이 변경된 사용자가 로그인 상태라면 강제로 로그아웃되도록 하세요. +- [x] JwtRegistry를 활용해 권한이 변경된 사용자가 로그인 상태라면 강제로 로그아웃되도록 하세요. -- [ ] JwtRegistry를 활용해 사용자의 로그인 여부를 판단하도록 리팩토링하세요. +- [x] JwtRegistry를 활용해 사용자의 로그인 여부를 판단하도록 리팩토링하세요. -- [ ] JwtLogoutHandler에서 JwtRegistry를 활용해 로그아웃 시 토큰을 무효화하세요. +- [x] JwtLogoutHandler에서 JwtRegistry를 활용해 로그아웃 시 토큰을 무효화하세요. ```java @@ -62,19 +62,19 @@ public void logout(HttpServletRequest request, HttpServletResponse response, - 로그아웃 API는 인증이 필요없기 때문에 Authentication 정보가 없을 수 있습니다. - 따라서 요청 쿠키의 리프레시 토큰을 활용해 토큰을 무효화합니다. -- [ ] 주기적으로 만료된 토큰 정보를 레지스트리에서 삭제하세요. +- [x] 주기적으로 만료된 토큰 정보를 레지스트리에서 삭제하세요. - @EnableScheduling를 추가하세요. - ```java - - @Configuration - @EnableJpaAuditing - @EnableScheduling - public class AppConfig { - - } - - ``` +```java + +@Configuration +@EnableJpaAuditing +@EnableScheduling +public class AppConfig { + +} + +``` - `@Scheduled`를 활용해서 5분마다 만료된 토큰을 삭제하세요.