4 lines
15 KiB
JavaScript
4 lines
15 KiB
JavaScript
/*! third party licenses: js/vendor.LICENSE.txt */
|
|
import{c as M}from"./index-C0mkXnFg.chunk.mjs";import{g as j}from"./index-Anv74-sp.chunk.mjs";import{R as y}from"./whiteboard-main.mjs";const P=r=>{let t;const e=new Set,o=(s,l)=>{const d=typeof s=="function"?s(t):s;if(!Object.is(d,t)){const g=t;t=l??(typeof d!="object"||d===null)?d:Object.assign({},t,d),e.forEach(c=>c(t,g))}},i=()=>t,n={setState:o,getState:i,getInitialState:()=>a,subscribe:s=>(e.add(s),()=>e.delete(s))},a=t=r(o,i,n);return n},N=r=>r?P(r):P,z=r=>r;function C(r,t=z){const e=y.useSyncExternalStore(r.subscribe,y.useCallback(()=>t(r.getState()),[r,t]),y.useCallback(()=>t(r.getInitialState()),[r,t]));return y.useDebugValue(e),e}const O=r=>{const t=N(r),e=o=>C(t,o);return Object.assign(e,t),e},E=r=>r?O(r):O,W=()=>{let r,t;const e=new Promise((o,i)=>{r=o,t=i});return e.resolve=r,e.reject=t,e},f=E()((r,t)=>({fileId:0,fileName:"",publicSharingToken:null,isReadOnly:!1,isEmbedded:!1,initialDataPromise:W(),pendingInitialDataPromises:[],collabBackendUrl:"",isVersionPreview:!1,versionSource:null,fileVersion:null,zenModeEnabled:!1,gridModeEnabled:!1,setConfig:e=>{r(e)},resolveInitialData:e=>{const{initialDataPromise:o,pendingInitialDataPromises:i}=t();i.forEach(n=>{n.resolve(e)}),o.resolve(e),i.length>0&&r({pendingInitialDataPromises:[]})},resetInitialDataPromise:()=>r(e=>({pendingInitialDataPromises:[...e.pendingInitialDataPromises,e.initialDataPromise],initialDataPromise:W()})),resetStore:()=>{const{fileId:e,fileName:o,publicSharingToken:i,isEmbedded:n,collabBackendUrl:a}=t();r({fileId:e,fileName:o,publicSharingToken:i,isEmbedded:n,collabBackendUrl:a,isVersionPreview:!1,versionSource:null,fileVersion:null,isReadOnly:!1,initialDataPromise:W(),pendingInitialDataPromises:[],zenModeEnabled:!1,gridModeEnabled:!1})},setZenModeEnabled:e=>r({zenModeEnabled:e}),setGridModeEnabled:e=>r({gridModeEnabled:e}),setReadOnly:e=>{if(t().isVersionPreview){r({isReadOnly:!0});return}r({isReadOnly:e})}})),H=()=>{if(typeof window>"u"||!window.__whiteboardTest)return;window.__whiteboardTestHooks=window.__whiteboardTestHooks||{};const r=window.__whiteboardTestHooks;r.whiteboardConfigStore=f,!(!r.blockInitialData||r.originalResolveInitialData)&&(r.pendingInitialData=null,r.originalResolveInitialData=f.getState().resolveInitialData,f.setState({resolveInitialData:t=>{r.pendingInitialData=t}}),r.restoreResolveInitialData=()=>{const t=r.originalResolveInitialData;t&&f.setState({resolveInitialData:t})})};H();function _(r,t){let e;try{e=r()}catch{return}return{getItem:o=>{var i;const n=s=>s===null?null:JSON.parse(s,void 0),a=(i=e.getItem(o))!=null?i:null;return a instanceof Promise?a.then(n):n(a)},setItem:(o,i)=>e.setItem(o,JSON.stringify(i,void 0)),removeItem:o=>e.removeItem(o)}}const I=r=>t=>{try{const e=r(t);return e instanceof Promise?e:{then(o){return I(o)(e)},catch(o){return this}}}catch(e){return{then(o){return this},catch(o){return I(o)(e)}}}},V=(r,t)=>(e,o,i)=>{let n={storage:_(()=>localStorage),partialize:u=>u,version:0,merge:(u,k)=>({...k,...u}),...t},a=!1,s=0;const l=new Set,d=new Set;let g=n.storage;if(!g)return r((...u)=>{console.warn(`[zustand persist middleware] Unable to update item '${n.name}', the given storage is currently unavailable.`),e(...u)},o,i);const c=()=>{const u=n.partialize({...o()});return g.setItem(n.name,{state:u,version:n.version})},p=i.setState;i.setState=(u,k)=>(p(u,k),c());const h=r((...u)=>(e(...u),c()),o,i);i.getInitialState=()=>h;let w;const b=()=>{var u,k;if(!g)return;const v=++s;a=!1,l.forEach(m=>{var T;return m((T=o())!=null?T:h)});const R=((k=n.onRehydrateStorage)==null?void 0:k.call(n,(u=o())!=null?u:h))||void 0;return I(g.getItem.bind(g))(n.name).then(m=>{if(m)if(typeof m.version=="number"&&m.version!==n.version){if(n.migrate){const T=n.migrate(m.state,m.version);return T instanceof Promise?T.then(J=>[!0,J]):[!0,T]}console.error("State loaded from storage couldn't be migrated since no migrate function was provided")}else return[!1,m.state];return[!1,void 0]}).then(m=>{var T;if(v!==s)return;const[J,$]=m;if(w=n.merge($,(T=o())!=null?T:h),e(w,!0),J)return c()}).then(()=>{v===s&&(R?.(w,void 0),w=o(),a=!0,d.forEach(m=>m(w)))}).catch(m=>{v===s&&R?.(void 0,m)})};return i.persist={setOptions:u=>{n={...n,...u},u.storage&&(g=u.storage)},clearStorage:()=>{g?.removeItem(n.name)},getOptions:()=>n,rehydrate:()=>b(),hasHydrated:()=>a,onHydrate:u=>(l.add(u),()=>{l.delete(u)}),onFinishHydration:u=>(d.add(u),()=>{d.delete(u)})},n.skipHydration||b(),w||h},U=V,A={type:null,message:null,consecutiveFailures:0,lastFailureTime:null,isPersistent:!1},x={status:"offline",socket:null,isDedicatedSyncer:!1,authError:A,followedUserId:null,isInRoom:!1,presenterId:null,isPresentationMode:!1,isPresenting:!1,presentationStartTime:null,autoFollowPresenter:!0,votings:[]},q=3,B=5*60*1e3,S=E()(r=>({...x,setStatus:t=>r(e=>e.status===t?{}:{status:t}),setSocket:t=>r({socket:t}),setDedicatedSyncer:t=>r({isDedicatedSyncer:t}),setIsInRoom:t=>r({isInRoom:t}),setAuthError:t=>r(e=>({authError:{...e.authError,...t}})),incrementAuthFailure:(t,e)=>r(o=>{const i=Date.now(),n=o.authError.consecutiveFailures+1,a=n>=q&&(o.authError.lastFailureTime===null||i-o.authError.lastFailureTime<B);return{authError:{type:t,message:e,consecutiveFailures:n,lastFailureTime:i,isPersistent:a}}}),clearAuthError:()=>r({authError:A}),resetStore:()=>r(x),setPresentationState:t=>r(e=>({...e,...t})),setAutoFollowPresenter:t=>r({autoFollowPresenter:t}),addVoting:t=>r(e=>({votings:[...e.votings,t]})),updateVoting:t=>r(e=>({votings:e.votings.map(o=>o.uuid===t.uuid?t:o)})),setVotings:t=>r({votings:t})})),G=()=>{typeof window>"u"||!window.__whiteboardTest||(window.__whiteboardTestHooks=window.__whiteboardTestHooks||{},window.__whiteboardTestHooks.collaborationStore=S)};G();const D=90;function F(r){const t=r.split(".");return t.length===3&&!!t[0]&&!!t[1]&&!!t[2]}const Z=E()(U((r,t)=>({tokens:{},tokenExpiries:{},autoRefreshTimers:{},refreshPromise:null,parseJwt:e=>{try{if(!F(e))return console.error("[JWTStore] Invalid token format"),null;const o=e.split(".")[1].replace(/-/g,"+").replace(/_/g,"/"),i=decodeURIComponent(atob(o).split("").map(s=>"%"+("00"+s.charCodeAt(0).toString(16)).slice(-2)).join("")),n=JSON.parse(i);if(!n||typeof n!="object")return console.error("[JWTStore] Invalid token payload structure"),null;if(!n.exp||!n.iat||!n.userid||n.fileId===void 0)return console.error("[JWTStore] Token missing required fields",n),null;const a=Math.floor(Date.now()/1e3);return n.exp<a&&console.error("[JWTStore] Token has expired"),n}catch(o){return console.error("[JWTStore] Error parsing JWT:",o),null}},validateJwtIntegrity:e=>{try{if(!F(e))return!1;const o=t().parseJwt(e);if(!o)return!1;const i=Math.floor(Date.now()/1e3);return o.exp<i?(console.warn("[JWTStore] Token validation failed: expired"),!1):o.iat>i+60?(console.warn("[JWTStore] Token validation failed: issued in future"),!1):!o.userid||typeof o.userid!="string"?(console.warn("[JWTStore] Token validation failed: invalid userid"),!1):o.fileId===void 0||typeof o.fileId!="number"?(console.warn("[JWTStore] Token validation failed: invalid fileId"),!1):!0}catch(o){return console.error("[JWTStore] Error validating JWT integrity:",o),!1}},isTokenExpired:e=>{const{tokens:o,tokenExpiries:i}=t(),n=o[e],a=i[e];return!n||!a?!0:Math.floor(Date.now()/1e3)>=a-D},getJWT:async()=>{const{fileId:e}=f.getState(),{authError:o}=S.getState();if(o.isPersistent)return console.warn("[JWTStore] Persistent auth error detected, working in local-only mode"),f.getState().setReadOnly(!0),null;const{tokens:i,isTokenExpired:n,setupAutoRefresh:a,validateJwtIntegrity:s}=t(),l=i[e],d=String(e);if(l)if(!s(l))console.error("[JWTStore] Stored token failed integrity validation"),console.log("[JWTStore] Clearing invalid token and refreshing"),r(c=>{const p={...c.tokens},h={...c.tokenExpiries};return delete p[e],delete h[e],{tokens:p,tokenExpiries:h}}),S.getState().incrementAuthFailure("jwt_secret_mismatch","Stored JWT token failed integrity validation"),console.warn("[JWTStore] Invalid token cleared, enforcing read-only mode until refresh completes"),f.getState().setReadOnly(!0);else if(n(d))console.log("[JWTStore] Token is expired, clearing and refreshing"),r(c=>{const p={...c.tokens},h={...c.tokenExpiries};return delete p[e],delete h[e],{tokens:p,tokenExpiries:h}}),console.warn("[JWTStore] Expired token cleared, enforcing read-only mode until refresh completes"),f.getState().setReadOnly(!0);else{const c=t().parseJwt(l);if(!c)console.error("[JWTStore] Failed to parse valid token"),f.getState().setReadOnly(!0);else{if(c.fileId===e)return a(d),c.isFileReadOnly!==void 0&&(console.log(`[JWTStore] Setting permissions from valid token: ${c.isFileReadOnly?"read-only":"write"} access`),f.getState().setReadOnly(c.isFileReadOnly)),l;console.error(`[JWTStore] Stored token is for fileId ${c.fileId}, but requested fileId is ${e}`),r(p=>{const h={...p.tokens},w={...p.tokenExpiries};return delete h[e],delete w[e],{tokens:h,tokenExpiries:w}}),f.getState().setReadOnly(!0)}}if(t().refreshPromise)return console.log("[JWTStore] Token refresh already in progress, waiting for it to complete"),t().refreshPromise;const g=t().refreshJWT();r({refreshPromise:g});try{const c=await g;return c&&a(d),c}finally{r({refreshPromise:null})}},refreshJWT:async()=>{const{fileId:e,publicSharingToken:o}=f.getState(),{authError:i}=S.getState();if(i.isPersistent)return console.warn("[JWTStore] Persistent auth error detected, skipping token refresh"),f.getState().setReadOnly(!0),null;try{console.log(`[JWTStore] Refreshing JWT for room ${e}`);const n=j(`apps/whiteboard/${e}/token`),a=o?`${n}?publicSharingToken=${encodeURIComponent(o)}`:n,s=await M.get(a,{withCredentials:!0}),{token:l}=s.data;if(!l)return console.error("[JWTStore] No token received from server"),null;if(!t().validateJwtIntegrity(l))return console.error("[JWTStore] Refreshed token failed integrity validation"),S.getState().incrementAuthFailure("jwt_secret_mismatch","Refreshed JWT token failed integrity validation - possible secret mismatch"),f.getState().setReadOnly(!0),null;const d=t().parseJwt(l);if(!d)return console.error("[JWTStore] Failed to parse validated token"),null;if(d.fileId!==e)return console.error(`[JWTStore] Token fileId (${d.fileId}) doesn't match requested fileId (${e})`),null;r(c=>({tokens:{...c.tokens,[e]:l},tokenExpiries:{...c.tokenExpiries,[e]:d.exp}})),console.log(`[JWTStore] JWT refreshed for room ${e}, expires at ${new Date(d.exp*1e3).toISOString()}`);const{authError:g}=S.getState();return g.type!=="jwt_secret_mismatch"&&S.getState().clearAuthError(),d.isFileReadOnly!==void 0&&(console.log(`[JWTStore] JWT indicates ${d.isFileReadOnly?"read-only":"write"} access`),f.getState().setReadOnly(d.isFileReadOnly)),l}catch(n){if(console.error("[JWTStore] Error refreshing JWT:",n),n instanceof Error&&"response"in n&&n.response){const s=n.response.status;(s===401||s===403)&&(console.warn("[JWTStore] Authentication error during token refresh"),S.getState().incrementAuthFailure("unauthorized","Authentication failed during token refresh"))}n instanceof Error&&(n.message.includes("Network Error")||n.message.includes("Failed to fetch")||n.message.includes("timeout"))&&console.log("[JWTStore] Network error detected during token refresh");const a=t().tokens[e];if(a){const s=t().parseJwt(a);return s?s.isFileReadOnly!==void 0&&(console.log(`[JWTStore] Setting permissions from fallback token: ${s.isFileReadOnly?"read-only":"write"} access`),f.getState().setReadOnly(s.isFileReadOnly)):(console.warn("[JWTStore] Invalid token after refresh failure, enforcing read-only mode"),f.getState().setReadOnly(!0)),console.log("[JWTStore] Using existing token after refresh failure"),a}return console.warn("[JWTStore] No token available after refresh failure, enforcing read-only mode"),f.getState().setReadOnly(!0),null}},executeWithJWT:async e=>{try{const o=await t().getJWT();if(!o)throw console.warn("[JWTStore] No token available for API call, enforcing read-only mode"),f.getState().setReadOnly(!0),new Error("Failed to obtain JWT token");const i=await e(o),{authError:n}=S.getState();return n.type!=="jwt_secret_mismatch"&&S.getState().clearAuthError(),i}catch(o){if(o instanceof Error&&"response"in o&&o.response&&o.response.status!==void 0&&(o.response.status===401||o.response.status===403)){console.warn("[JWTStore] Authentication error, enforcing read-only mode"),f.getState().setReadOnly(!0);const i=o.response.status,n=i===401?"token_expired":"unauthorized";S.getState().incrementAuthFailure(n,`Authentication failed with status ${i}`);const{authError:a}=S.getState();if(a.isPersistent)throw console.warn("[JWTStore] Persistent auth error detected, not attempting token refresh"),f.getState().setReadOnly(!0),new Error("Persistent authentication error - working in local-only mode");if(console.log("[JWTStore] Token expired or invalid, refreshing and retrying..."),t().refreshPromise){console.log("[JWTStore] Token refresh already in progress, waiting for it to complete");const l=await t().refreshPromise;if(!l)throw new Error("Failed to refresh JWT token");return await e(l)}const s=t().refreshJWT();r({refreshPromise:s});try{const l=await s;if(!l)throw new Error("Failed to refresh JWT token");return await e(l)}finally{r({refreshPromise:null})}}throw o}},setupAutoRefresh:e=>{const{autoRefreshTimers:o,tokenExpiries:i,refreshJWT:n}=t();o[e]&&clearTimeout(o[e]);const a=i[e];if(!a){console.log(`[JWTStore] No expiry found for room ${e}, skipping auto-refresh setup`);return}const s=Math.floor(Date.now()/1e3),l=Math.max(0,(a-D-s)*1e3);console.log(`[JWTStore] Setting up auto-refresh for room ${e} in ${Math.round(l/1e3)} seconds (at ${new Date(Date.now()+l).toISOString()})`);const d=window.setTimeout(async()=>{console.log(`[JWTStore] Auto-refresh timer triggered for room ${e}`);const{authError:g}=S.getState();if(g.isPersistent){console.warn("[JWTStore] Persistent auth error detected, skipping auto-refresh");return}await n(),t().setupAutoRefresh(e)},l);r(g=>({autoRefreshTimers:{...g.autoRefreshTimers,[e]:d}}))},clearAutoRefresh:e=>{const{autoRefreshTimers:o}=t();o[e]&&(clearTimeout(o[e]),r(i=>{const n={...i.autoRefreshTimers};return delete n[e],{autoRefreshTimers:n}}),console.log(`[JWTStore] Cleared auto-refresh timer for room ${e}`))},clearTokens:()=>{const{autoRefreshTimers:e}=t();Object.values(e).forEach(o=>{clearTimeout(o)}),r({tokens:{},tokenExpiries:{},autoRefreshTimers:{}}),console.log("[JWTStore] All tokens and timers cleared")}}),{name:"jwt-storage",storage:_(()=>localStorage),version:2,partialize:r=>({tokens:r.tokens,tokenExpiries:r.tokenExpiries,autoRefreshTimers:{},refreshPromise:null}),migrate:(r,t)=>t<2?(console.log("[JWTStore] Storage version changed, clearing old tokens"),{tokens:{},tokenExpiries:{},autoRefreshTimers:{},refreshPromise:null}):r})),Q=Object.freeze(Object.defineProperty({__proto__:null,useJWTStore:Z},Symbol.toStringTag,{value:"Module"}));export{f as a,S as b,E as c,_ as d,Q as e,U as p,Z as u};
|
|
//# sourceMappingURL=useJwtStore-B1DPLYgs.chunk.mjs.map
|