PageSourceSearch

https://5sun.kr/assets/MemberPhoneAuthFlow-_UZZXfae.js

js 5sun.kr collected 2026-10-08 07:20:00 UTC 11,425 bytes, 1 lines download raw bytes

1var B=Object.defineProperty;var H=(e,t,n)=>t in e?B(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var $=(e,t,n)=>H(e,typeof t!="symbol"?t+"":t,n);import{r as d,j as r,f as G,g as O,p as K}from"./index-zwE6cOSU.js";import{T as U}from"./TouchKeyboard-COZMex0r.js";import{C as q}from"./circle-alert-Bsk6vZWo.js";import{R as J}from"./rotate-ccw-9-i55Rnq.js";const P="010-",w=8;function Q(e){let t=e;return e.startsWith(P)?t=e.slice(P.length):e.startsWith("010")?t=e.slice(3):e.length<P.length&&(t=""),t.replace(/\D/gu,"").slice(0,w)}function W(e){const t=e.replace(/\D/gu,"").slice(0,w);return t.length<=4?`${P}${t}`:`${P}${t.slice(0,4)}-${t.slice(4)}`}function X(e){const t=e.replace(/\D/gu,"").slice(0,w);return t.length===w?`010${t}`:null}function V({suffix:e,onSuffixChange:t,onConfirm:n,pending:s=!1,retryAfterSeconds:i=0}){const a=d.useRef(null),o=e.length===w,m=i>0,x=()=>{o&&!m&&n()};return r.jsxs("section",{className:"memberPhoneEntry","aria-labelledby":"member-phone-title",children:[r.jsxs("div",{className:"memberPhoneField",children:[r.jsx("label",{id:"member-phone-title",htmlFor:"member-phone-input",children:"회원 전화번호"}),r.jsxs("div",{className:"memberPhoneControl",children:[r.jsx("input",{id:"member-phone-input",ref:a,"aria-label":"회원 전화번호","aria-describedby":"member-phone-help",autoComplete:"tel-national",disabled:s,inputMode:"numeric",value:W(e),onChange:l=>t(Q(l.target.value)),onKeyDown:l=>{l.key==="Enter"&&x()}}),r.jsx("button",{type:"button",className:"memberPhoneConfirm","aria-label":"전화번호 확인",disabled:!o||s||m,onClick:x,children:s?"확인 중":m?`${i}초 후`:"확인"})]})]}),r.jsxs("p",{className:"memberPhoneHelp",id:"member-phone-help",children:["010- 뒤 여덟 자리를 입력하면 문자 인증번호를 ",r.jsx("span",{className:"quickEntryKeepPhrase","data-keep-phrase":"보내드려요.",children:"보내드려요."})]}),r.jsx(U,{className:"memberPhoneKeyboard",value:e,onChange:t,mode:"numeric",availableModes:["numeric"],maxLength:w,disabled:s,inputRef:a,onDone:x,doneLabel:s?"전송 중":m?`${i}초 후 재발송`:"인증번호 받기",doneDisabled:m})]})}const Y=Symbol("MemberGrant"),Z=Symbol("MemberGrantId"),v=new WeakSet,ee=120,te=/^5[Ss][Uu][Nn]1\.([0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[1-5][0-9A-Fa-f]{3}-[89AaBb][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12})\.[0-9A-Fa-f]{64}$/;class ne extends Error{constructor(){super("회원 QR 형식을 확인해 주세요.");$(this,"name","InvalidMemberGrantError")}}function re(e){return typeof e=="object"&&e!==null&&v.has(e)}function me(e){if(typeof e!="string"||e.length>ee)return{kind:"invalid"};const t=te.exec(e),n=t==null?void 0:t[1];if(n===void 0)return{kind:"invalid"};const s=Object.freeze({id:Object.freeze({value:n.toLowerCase(),[Z]:!0}),raw:e,[Y]:!0});return v.add(s),{kind:"accepted",grant:s}}function g(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function p(e){return typeof e=="string"&&e.length>0?e:null}function E(e){return typeof e=="number"&&Number.isSafeInteger(e)&&e>0?e:null}function se(e){const t=`${e}=`,n=document.cookie.split(";").map(s=>s.trim()).find(s=>s.startsWith(t));return n?decodeURIComponent(n.slice(t.length)):null}function j(e={}){const t=se("5sun_csrf");return{...e,...t?{"X-CSRF-Token":t,"X-Request-Nonce":crypto.randomUUID()}:{}}}function T(e){if(!g(e)||typeof e.kind!="string")throw new Error("빠른 입장 응답을 확인할 수 없어요.");if(e.kind==="store_selection")return{kind:"store_selection"};const t=p(e.store_id);if(e.kind==="membership"&&t)return{kind:"membership",storeId:t};const n=p(e.room_id);if(e.kind==="reservation"){const s=E(e.reservation_id),i=E(e.seat_id),a=p(e.start_at),o=E(e.duration_minutes);if(t&&n&&s&&i&&a&&o&&Number.isFinite(new Date(a).getTime()))return{kind:"reservation",storeId:t,roomId:n,reservationId:s,seatId:i,startAt:a,durationMinutes:o}}if(e.kind==="active_session"){const s=E(e.session_id),i=E(e.seat_id),a=p(e.start_at),o=p(e.end_at);if(t&&n&&s&&i&&a&&o&&Number.isFinite(new Date(a).getTime())&&Number.isFinite(new Date(o).getTime()))return{kind:"active_session",storeId:t,roomId:n,sessionId:s,seatId:i,startAt:a,endAt:o}}throw new Error("빠른 입장 응답을 확인할 수 없어요.")}async function M(e,t,n){const s=await fetch(e,{...t,signal:n,credentials:"same-origin",headers:{Accept:"application/json",...t==null?void 0:t.headers}});if(!s.ok){const i=new Error("빠른 입장 연결을 확인해 주세요.");throw Object.assign(i,{status:s.status}),i}return s.json()}async function he(e){const t=await M("/api/auth/quick-entry/pairings",{method:"POST",headers:j()},e);if(!g(t))throw new Error("빠른 입장 QR을 준비하지 못했어요.");const n=p(t.pairing_id),s=p(t.qr_url),i=t.expires_in;if(!n||n.length!==36||!s||typeof i!="number"||i<1||i>300)throw new Error("빠른 입장 QR을 준비하지 못했어요.");const a=new URL(s);if(a.searchParams.size!==1||a.searchParams.get("pair")!==n)throw new Error("빠른 입장 QR을 준비하지 못했어요.");return{pairingId:n,qrUrl:s,expiresIn:i}}async function pe(e,t){const n=await M(`/api/auth/quick-entry/pairings/${encodeURIComponent(e)}`,void 0,t);
1if(g(n)&&(n.status==="waiting"||n.status==="claimed"))return n.status;throw new Error("빠른 입장 상태를 확인하지 못했어요.")}async function fe(e,t){const n=await M(`/api/auth/quick-entry/pairings/${encodeURIComponent(e)}/redeem`,{method:"POST",headers:j()},t);if(!g(n))throw new Error("빠른 입장 결과를 확인하지 못했어요.");return T(n.continuation)}async function ie(e,t){const n=await G("/api/auth/member-phone/start",{method:"POST",headers:j({"Content-Type":"application/json","Idempotency-Key":t}),body:JSON.stringify({phone:e})});if(!g(n))throw new Error("문자 인증 요청을 확인하지 못했어요.");const s=p(n.challenge_id),i=n.expires_in,a=n.remaining_attempts,o=n.resend_available_in;if(!s||s.length!==36||typeof i!="number"||i<0||i>300||typeof a!="number"||a<0||a>5||typeof o!="number"||o<0||o>60)throw new Error("문자 인증 요청을 확인하지 못했어요.");return{challengeId:s,expiresIn:i,remainingAttempts:a,resendAvailableIn:o}}async function ae(e,t){const n=await M("/api/auth/member-phone/verify",{method:"POST",headers:j({"Content-Type":"application/json"}),body:JSON.stringify({challenge_id:e,code:t})});if(!g(n)||n.kind!=="authenticated"&&n.kind!=="signup_required")throw new Error("문자 인증 결과를 확인하지 못했어요.");return n.kind==="signup_required"?{kind:"signup_required"}:{kind:"authenticated",continuation:T(n.continuation)}}async function be(e){if((await fetch(`/api/auth/quick-entry/pairings/${encodeURIComponent(e)}/claim`,{method:"POST",credentials:"same-origin",headers:j({Accept:"application/json"})})).status!==204)throw new Error("키오스크 연결을 완료하지 못했어요.")}async function ge(e,t){if(!re(e))throw new ne;try{const n=await G("/api/auth/quick-entry/member-grants/redeem",{method:"POST",headers:j(),body:JSON.stringify({grant:e.raw}),signal:t});return g(n)?{kind:"success",continuation:T(n.continuation)}:{kind:"ambiguous"}}catch(n){if(n instanceof DOMException&&n.name==="AbortError")throw n;if(!(n instanceof O))return{kind:"ambiguous"};switch(n.status){case 404:return{kind:"unavailable"};case 409:return{kind:"used"};case 410:return{kind:"expired"};default:return{kind:"ambiguous"}}}}async function ye(){return(await fetch("/api/auth/session",{credentials:"same-origin",headers:{Accept:"application/json"}})).ok}function we({suffix:e,onSuffixChange:t,onAuthenticated:n,onFallbackConfirm:s,onSignup:i}){const[a,o]=d.useState("phone"),[m,x]=d.useState(null),[l,N]=d.useState(""),[u,I]=d.useState(!1),[R,f]=d.useState(""),[h,k]=d.useState(0),_=d.useRef(null),y=d.useRef(!1),S=d.useRef(!1);d.useEffect(()=>{if(h<=0)return;const c=window.setTimeout(()=>{h===1?(k(0),f("")):k(h-1)},1e3);return()=>window.clearTimeout(c)},[h]);const z=c=>{c!==e&&(_.current=null,k(0),f("")),t(c)},L=async()=>{var F;if(!n){s==null||s();return}const c=X(e);if(!c||y.current||h>0)return;y.current=!0,I(!0),f(""),k(0);const D=((F=_.current)==null?void 0:F.phone)===c?_.current:{phone:c,key:crypto.randomUUID()};_.current=D;try{const b=await ie(c,D.key);x(b.challengeId),N(""),o("otp")}catch(b){const C=b instanceof O&&Number.isInteger(b.metadata.retry_after)?b.metadata.retry_after??0:0;b instanceof O&&b.code!=="request_in_progress"&&(_.current=null),k(C),f(C>0?`${C}초 후 인증번호를 다시 받을 수 있습니다.`:"인증번호를 보내지 못했어요. 잠시 후 다시 시도해 주세요.")}finally{y.current=!1,I(!1)}},A=async()=>{if(!(!m||l.length!==6||y.current||S.current)){y.current=!0,I(!0),f("");try{const c=await ae(m,l);if(c.kind==="signup_required"){o("signup_required");return}if(S.current)return;S.current=!0,o("success"),n==null||n(c.continuation)}catch{f("인증번호가 맞지 않거나 시간이 지났어요. 다시 확인해 주세요.")}finally{y.current=!1,I(!1)}}};return a==="phone"?r.jsxs("div",{className:"memberAuthFlow","data-state":u?"loading":"phone","aria-busy":u,children:[r.jsx(V,{suffix:e,onSuffixChange:z,onConfirm:()=>void L(),pending:u,retryAfterSeconds:h}),u&&r.jsx("p",{className:"memberAuthPending",role:"status",children:"인증번호를 보내고 있어요."}),R&&r.jsxs("p",{className:"memberAuthError",role:"alert",children:[r.jsx(q,{size:17,"aria-hidden":"true"}),h>0?`${h}초 후 인증번호를 다시 받을 수 있습니다.`:R]})]}):a==="signup_required"?r.jsxs("section",{className:"memberAuthResult",role:"status",children:[r.jsx(q,{size:28,"aria-hidden":"true"}),r.jsx("strong",{children:"가입된 회원 정보를 찾지 못했어요"}),r.jsx("p",{children:"전화번호 확인은 완료됐습니다. 새 회원가입에서 계속해 주세요."}),r.jsx("button",{type:"button",className:"primary full",onClick:i,children:"회원가입으로 이동"})]}):a==="success"?r.jsxs("section",{className:"memberAuthResult",role:"status",children:[r.jsx(K,{size:30,"aria-hidden":"true"}),r.jsx("strong",{children:"회원 확인이 완료됐어요"})]}):r.jsxs("section",{className:"memberOtpEntry","aria-labelledby":"member-otp-title","data-state":u?"loading":"otp","aria-busy":u,children:[r.jsxs("div",{className:"memberOtpHeading",children:[r.jsxs("div",{children:[r.jsx("strong",{id:"member-otp-title",children:"문자 인증번호 6자리"}),r.jsx("small",{children:"5분 안에 입력해 주세요."})]}),r.jsxs("button",{type:"button",onClick:()=>{o("phone"),N(""),f("")},children:[r.jsx(J,{size:16,"aria-hidden":"true"})," 다른 번호"]})]}),r.jsx("input",{autoFocus:!0,"aria-label":"문자 인증번호",inputMode:"numeric",autoComplete:"one-time-code",disabled:u,value:l,onCh
1ange:c=>N(c.target.value.replace(/\D/gu,"").slice(0,6)),onKeyDown:c=>{c.key==="Enter"&&A()}}),r.jsx("button",{type:"button",className:"primary full memberOtpConfirm","aria-label":"인증번호 확인",disabled:u||l.length!==6,onClick:()=>void A(),children:u?"확인 중…":"확인"}),u&&r.jsx("p",{className:"memberAuthPending",role:"status",children:"인증번호를 확인하고 있어요."}),r.jsx(U,{value:l,onChange:N,mode:"numeric",availableModes:["numeric"],maxLength:6,disabled:u,onDone:()=>void A(),doneLabel:u?"확인 중":"인증 확인"}),R&&r.jsxs("p",{className:"memberAuthError",role:"alert",children:[r.jsx(q,{size:17,"aria-hidden":"true"}),R]})]})}export{we as M,fe as a,be as c,pe as g,ye as h,he as i,me as p,ge as r};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.