- Delete old Vite+Svelte frontend - Initialize new SvelteKit project with TypeScript - Configure Tailwind CSS v4 + DaisyUI - Implement JWT authentication with auto-refresh - Create login page with form validation (Zod) - Add protected route guards - Update Docker configuration for single-stage build - Add E2E tests with Playwright (6/11 passing) - Fix Svelte 5 reactivity with $state() runes Known issues: - 5 E2E tests failing (timing/async issues) - Token refresh implementation needs debugging - Validation error display timing
2 lines
3.5 KiB
JavaScript
2 lines
3.5 KiB
JavaScript
import{ab as E,ac as A}from"./CCV2x70u.js";const S="http://localhost:3000/api",y="headroom_access_token",k="headroom_refresh_token";function T(){return typeof localStorage>"u"?null:localStorage.getItem(y)}function b(){return typeof localStorage>"u"?null:localStorage.getItem(k)}function _(e,t){typeof localStorage>"u"||(localStorage.setItem(y,e),localStorage.setItem(k,t))}function w(){typeof localStorage>"u"||(localStorage.removeItem(y),localStorage.removeItem(k))}class L extends Error{constructor(t,o,r){super(t),this.name="ApiError",this.status=o,this.data=r}}let f=!1,h=[];function R(e){h.push(e)}function v(e){h.forEach(t=>{t(e)}),h=[]}async function j(){const e=b();if(!e)throw new Error("No refresh token available");const t=await fetch(`${S}/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:e})});if(!t.ok)throw w(),new Error("Token refresh failed");const o=await t.json();return _(o.access_token,o.refresh_token),o.access_token}async function u(e,t={}){const o=`${S}${e}`,r={"Content-Type":"application/json",...t.headers},n=T();n&&(r.Authorization=`Bearer ${n}`);const c={...t,headers:r};t.body&&typeof t.body=="object"&&(c.body=JSON.stringify(t.body));try{let a=await fetch(o,c);if(a.status===401&&n){if(!f){f=!0;try{const i=await j();f=!1,v(i)}catch{throw f=!1,h=[],w(),typeof window<"u"&&window.dispatchEvent(new CustomEvent("auth:logout")),new L("Session expired. Please log in again.",401,null)}}return new Promise((i,l)=>{R(g=>{c.headers={...c.headers,Authorization:`Bearer ${g}`},fetch(o,c).then(P=>m(P)).then(i).catch(l)})})}return m(a)}catch(a){throw a}}async function m(e){var n,c,a,i;const t=((c=(n=e.headers)==null?void 0:n.get)==null?void 0:c.call(n,"content-type"))||((i=(a=e.headers)==null?void 0:a.get)==null?void 0:i.call(a,"Content-Type")),r=t&&t.includes("application/json")?await e.json():await e.text();if(!e.ok){const l=typeof r=="object"?r:{message:r},g=l.message||"API request failed";throw new L(g,e.status,l)}return r}const p={get:(e,t={})=>u(e,{...t,method:"GET"}),post:(e,t,o={})=>u(e,{...o,method:"POST",body:t}),put:(e,t,o={})=>u(e,{...o,method:"PUT",body:t}),patch:(e,t,o={})=>u(e,{...o,method:"PATCH",body:t}),delete:(e,t={})=>u(e,{...t,method:"DELETE"})},I={login:e=>p.post("/auth/login",e),logout:()=>p.post("/auth/logout"),refresh:()=>p.post("/auth/refresh",{refresh_token:b()})};function C(){const{subscribe:e,set:t,update:o}=A(null);return{subscribe:e,set:t,update:o,clear:()=>t(null)}}const d=C();function O(){const{subscribe:e,set:t,update:o}=A({isAuthenticated:!1,isLoading:!1,error:null});return{subscribe:e,set:t,update:o,setLoading:r=>o(n=>({...n,isLoading:r})),setError:r=>o(n=>({...n,error:r})),clearError:()=>o(r=>({...r,error:null})),setAuthenticated:r=>o(n=>({...n,isAuthenticated:r}))}}const s=O(),x=E([d,s],([e,t])=>e!==null&&t.isAuthenticated);E(d,e=>(e==null?void 0:e.role)||null);function J(){T()&&s.setAuthenticated(!0)}async function K(e){s.setLoading(!0),s.clearError();try{const t=await I.login(e);if(t.access_token&&t.refresh_token)return _(t.access_token,t.refresh_token),d.set(t.user||null),s.setAuthenticated(!0),{success:!0,user:t.user};throw new Error("Invalid response from server")}catch(t){const o=t instanceof Error?t.message:"Login failed";return s.setError(o),{success:!1,error:o}}finally{s.setLoading(!1)}}async function q(){s.setLoading(!0);try{await I.logout()}catch(e){console.error("Logout API error:",e)}finally{w(),d.clear(),s.setAuthenticated(!1),s.setLoading(!1)}}export{J as a,K as b,s as c,x as i,q as l,d as u};
|