PageSourceSearch

https://townsy.ai/assets/SitemapStatus-B11ORrHn.js

js townsy.ai collected 2026-10-05 13:58:26 UTC 11,629 bytes, 1 lines download raw bytes

1import{r as c,j as e}from"./react-vendor-DV5ktuDV.js";import{_ as le,B as b,aR as ie,R as M,C as j,aG as g,aJ as p,c as o,b as N,Q as V,aI as R,a2 as C,g as f,aE as ce}from"./index-Ddmjp9wg.js";import{C as oe}from"./circle-x-DrlftcGL.js";import"./supabase-CwJNLV8D.js";import"./radix-ui-DWvHXyxJ.js";import"./ui-utils-B2rm_Apj.js";import"./framer-motion-ahggqEuq.js";import"./react-markdown-eG3dqjFT.js";const k="https://townsy.ai/sitemap.xml",B="/sitemap.xml";function de(s){return(s.match(/<loc>/g)||[]).length}function z(s,i){const t=s.match(new RegExp(`<${i}>([^<]+)</${i}>`));return t==null?void 0:t[1]}async function I(s){try{const i=await fetch(s,{cache:"no-store"}),t=await i.text();return{url:s,ok:i.ok&&t.includes("<urlset"),status:i.status,urlCount:de(t),bytes:t.length,firstLoc:z(t,"loc"),firstLastmod:z(t,"lastmod")}}catch(i){return{url:s,ok:!1,status:0,urlCount:0,bytes:0,error:i instanceof Error?i.message:String(i)}}}function Ne(){const[s,i]=c.useState(null),[t,y]=c.useState(null),[d,J]=c.useState(null),[n,K]=c.useState(null),[O,Q]=c.useState(null),[v,X]=c.useState(null),[Y,q]=c.useState(null),[Z,ee]=c.useState(null),[_,se]=c.useState([]),[U,E]=c.useState(!1),[G,P]=c.useState(!1),[D,T]=c.useState(!1);async function S(){P(!0);try{try{const l=await fetch("/sitemap-manifest.json",{cache:"no-store"});l.ok?i(await l.json()):y(`HTTP ${l.status}`)}catch(l){y(l instanceof Error?l.message:String(l))}const[a,r]=await Promise.all([I(B),I(k)]);J(a),K(r),await A()}finally{P(!1)}}async function A(){const{data:a,error:r}=await C.from("sitemap_audit_log").select("*").order("checked_at",{ascending:!1}).limit(20);!r&&a&&se(a)}async function te(){var a;E(!0);try{const{data:r,error:l}=await C.functions.invoke("sitemap-verify",{body:{triggered_by:"manual"}});if(l)throw l;const m=r==null?void 0:r.drift;(a=r==null?void 0:r.alert)!=null&&a.sent?f.warning(`Drift detected (${m} URLs). Alert email sent.`):m>=0?f.success(`Verification complete — drift ${m} URLs.`):f.error("Verification ran but a sitemap fetch failed."),await A(),await S()}catch(r){f.error(r instanceof Error?r.message:"Verification failed")}finally{E(!1)}}async function ae(){var a,r,l,m,$;T(!0);try{const{data:h,error:F}=await C.functions.invoke("gsc-sitemap",{body:{action:"status"}});if(F)throw F;const x=(a=h==null?void 0:h.sitemap)==null?void 0:a.body;Q((x==null?void 0:x.isPending)??null),X((x==null?void 0:x.lastDownloaded)??null),q(((l=(r=x==null?void 0:x.contents)==null?void 0:r[0])==null?void 0:l.submitted)??null),ee((($=(m=x==null?void 0:x.contents)==null?void 0:m[0])==null?void 0:$.indexed)??null),f.success("Pulled latest Google Search Console status")}catch(h){f.error(h instanceof Error?h.message:"GSC check failed")}finally{T(!1)}}c.useEffect(()=>{S()},[]);const w=(s==null?void 0:s.minUrls)??50,re=!!(n!=null&&n.ok)&&n.urlCount>=w,ne=!!(d!=null&&d.ok)&&d.urlCount>=w,u=d&&n&&d.ok&&n.ok?Math.abs(d.urlCount-n.urlCount):null,L=u!==null&&u>25,H=s?Math.round((Date.now()-new Date(s.generatedAt).getTime())/6e4):null;return e.jsxs("div",{className:"container mx-auto px-4 py-8 max-w-5xl",children:[e.jsx(le,{title:"Sitemap Status — Townsy",noindex:!0}),e.jsxs("div",{className:"flex items-center justify-between mb-6 gap-3 flex-wrap",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-3xl font-bold",children:"Sitemap Status"}),e.jsx("p",{className:"text-muted-foreground",children:"Build manifest, live publish verification, and Google Search Console state."})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(b,{variant:"outline",size:"sm",onClick:S,disabled:G,children:[e.jsx(ie,{className:`h-4 w-4 mr-2 ${G?"animate-spin":""}`}),"Refresh"]}),e.jsx(b,{variant:"outline",size:"sm",asChild:!0,children:e.jsxs("a",{href:"/admin/gsc",children:["GSC panel ",e.jsx(M,{className:"h-3 w-3 ml-1"})]})})]})]}),e.jsxs("div",{className:"grid gap-4 md:grid-cols-3 mb-6",children:[e.jsxs(j,{children:[e.jsx(g,{children:e.jsxs(p,{className:"text-base flex items-center gap-2",children:["Build manifest",(s==null?void 0:s.source)==="dynamic"&&e.jsx(o,{className:"bg-green-600",children:"dynamic"}),(s==null?void 0:s.source)==="existing"&&e.jsx(o,{variant:"secondary",children:"stale (kept existing)"}),(s==null?void 0:s.source)==="failed"&&e.jsx(o,{variant:"destructive",children:"failed"})]})}),e.jsxs(N,{className:"text-sm space-y-2",children:[t&&e.jsxs("p",{className:"text-destructive",children:["Manifest not found: ",t]}),s&&e.jsxs("dl",{className:"grid grid-cols-2 gap-1",children:[e.jsx("dt",{className:"text-muted-foreground",children:"URLs at build"}),e.jsx("dd",{className:"font-mono",children:s.urlCount.toLocaleStr
1ing()}),e.jsx("dt",{className:"text-muted-foreground",children:"Generated"}),e.jsx("dd",{children:new Date(s.generatedAt).toLocaleString()}),e.jsx("dt",{className:"text-muted-foreground",children:"Age"}),e.jsx("dd",{children:H!==null?`${H} min`:"—"}),e.jsx("dt",{className:"text-muted-foreground",children:"Min threshold"}),e.jsx("dd",{className:"font-mono",children:s.minUrls}),s.error&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-muted-foreground",children:"Note"}),e.jsx("dd",{className:"text-xs text-destructive break-all",children:s.error})]})]})]})]}),e.jsx(W,{title:"Preview sitemap",subtitle:B,data:d,healthy:ne,min:w}),e.jsx(W,{title:"Published sitemap",subtitle:k,data:n,healthy:re,min:w})]}),e.jsxs(j,{className:"mb-6",children:[e.jsx(g,{children:e.jsxs(p,{className:"text-base flex items-center gap-2",children:["Publish verification",u===null?e.jsx(o,{variant:"secondary",children:"pending"}):L?e.jsxs(o,{variant:"destructive",children:["drift ",u]}):e.jsx(o,{className:"bg-green-600",children:"in sync"})]})}),e.jsxs(N,{className:"text-sm space-y-2",children:[d&&n&&e.jsxs("p",{children:["Preview has"," ",e.jsx("span",{className:"font-mono",children:d.urlCount.toLocaleString()})," URLs; published"," ",e.jsxs("a",{href:k,target:"_blank",rel:"noreferrer",className:"text-primary inline-flex items-center gap-1",children:["townsy.ai/sitemap.xml ",e.jsx(M,{className:"h-3 w-3"})]})," ","has ",e.jsx("span",{className:"font-mono",children:n.urlCount.toLocaleString()}),".",L&&` Difference of ${u} URLs — republish to push the latest build live.`,!L&&u!==null&&` Difference of ${u} URLs (within ±25 tolerance).`]}),!(n!=null&&n.ok)&&n&&e.jsxs("p",{className:"text-destructive text-xs",children:["Live fetch failed (HTTP ",n.status,"). ",n.error]}),e.jsxs("div",{className:"pt-2",children:[e.jsxs(b,{onClick:te,disabled:U,size:"sm",variant:"default",children:[U?e.jsx(V,{className:"h-4 w-4 mr-2 animate-spin"}):e.jsx(R,{className:"h-4 w-4 mr-2"}),"Run verification now"]}),e.jsx("span",{className:"ml-3 text-xs text-muted-foreground",children:"Auto-runs daily at 13:00 UTC. Alerts [email protected] when drift > 25 URLs or a fetch fails."})]})]})]}),e.jsxs(j,{className:"mb-6",children:[e.jsx(g,{children:e.jsx(p,{className:"text-base",children:"Verification history"})}),e.jsx(N,{className:"text-sm",children:_.length===0?e.jsx("p",{className:"text-muted-foreground text-xs",children:"No checks recorded yet. Run verification or wait for the daily cron."}):e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-xs",children:[e.jsx("thead",{className:"text-muted-foreground border-b",children:e.jsxs("tr",{children:[e.jsx("th",{className:"text-left py-2 pr-3 font-medium",children:"When"}),e.jsx("th",{className:"text-right py-2 px-3 font-medium",children:"Preview"}),e.jsx("th",{className:"text-right py-2 px-3 font-medium",children:"Live"}),e.jsx("th",{className:"text-right py-2 px-3 font-medium",children:"Drift"}),e.jsx("th",{className:"text-left py-2 px-3 font-medium",children:"Trigger"}),e.jsx("th",{className:"text-left py-2 px-3 font-medium",children:"Status"}),e.jsx("th",{className:"text-left py-2 pl-3 font-medium",children:"Notes"})]})}),e.jsx("tbody",{children:_.map(a=>{var l,m;const r=a.preview_ok&&a.live_ok&&(a.drift??0)<=25;return e.jsxs("tr",{className:"border-b last:border-0",children:[e.jsx("td",{className:"py-2 pr-3 whitespace-nowrap",children:new Date(a.checked_at).toLocaleString()}),e.jsxs("td",{className:"py-2 px-3 text-right font-mono",children:[((l=a.preview_url_count)==null?void 0:l.toLocaleString())??"—",!a.preview_ok&&e.jsx("span",{className:"text-destructive",children:" ✗"})]}),e.jsxs("td",{className:"py-2 px-3 text-right font-mono",children:[((m=a.live_url_count)==null?void 0:m.toLocaleString())??"—",!a.live_ok&&e.jsx("span",{className:"text-destructive",children:" ✗"})]}),e.jsx("td",{className:"py-2 px-3 text-right font-mono",children:a.drift===null?"—":a.drift}),e.jsx("td",{className:"py-2 px-3",children:a.triggered_by}),e.jsx("td",{className:"py-2 px-3",children:r?e.jsx(o,{className:"bg-green-600",children:"ok"}):a.alert_sent?e.jsx(o,{variant:"destructive",children:"alerted"}):e.jsx(o,{variant:"secondary",children:"issue"})}),e.jsx("td",{className:"py-2 pl-3 text-muted-foreground max-w-xs truncate",children:a.notes??""})]},a.id)})})]})})})]}),e.jsxs(j,{children:[e.jsx(g,{children:e.jsx(p,{className:"text-base",children:"Google Search Console verification"})}),e.jsxs(N,{className:"text-sm space-y-3",children:[e.jsxs(b,{onClick:ae,disabled:D,size:"sm",children:[D?e.jsx(V,{className:"h-4 w-4 mr-2 animate-spin"}):e.jsx(R,{className:"h-4 w-4 mr-2"}),"Check GSC status"]}),v!==null&&e.jsxs("dl",{className:"grid grid-cols-2 gap-1 max-w-md",children:[e.jsx("dt",{className:"text-muted-foreground",children:"Last downloaded by Google"}
1),e.jsx("dd",{children:v?new Date(v).toLocaleString():"—"}),e.jsx("dt",{className:"text-muted-foreground",children:"Pending"}),e.jsx("dd",{children:O?"Yes":"No"}),e.jsx("dt",{className:"text-muted-foreground",children:"Submitted URLs"}),e.jsx("dd",{className:"font-mono",children:Y??"—"}),e.jsx("dt",{className:"text-muted-foreground",children:"Indexed URLs"}),e.jsx("dd",{className:"font-mono",children:Z??"—"})]})]})]})]})}function W({title:s,subtitle:i,data:t,healthy:y,min:d}){return e.jsxs(j,{children:[e.jsx(g,{children:e.jsxs(p,{className:"text-base flex items-center gap-2",children:[s,t==null?e.jsx(o,{variant:"secondary",children:"…"}):y?e.jsxs(o,{className:"bg-green-600",children:[e.jsx(R,{className:"h-3 w-3 mr-1"})," healthy"]}):t.urlCount>0&&t.urlCount<d?e.jsxs(o,{variant:"destructive",children:[e.jsx(ce,{className:"h-3 w-3 mr-1"})," below min"]}):e.jsxs(o,{variant:"destructive",children:[e.jsx(oe,{className:"h-3 w-3 mr-1"})," failed"]})]})}),e.jsxs(N,{className:"text-sm space-y-2",children:[e.jsx("p",{className:"text-xs text-muted-foreground font-mono break-all",children:i}),t&&e.jsxs("dl",{className:"grid grid-cols-2 gap-1",children:[e.jsx("dt",{className:"text-muted-foreground",children:"URLs"}),e.jsx("dd",{className:"font-mono",children:t.urlCount.toLocaleString()}),e.jsx("dt",{className:"text-muted-foreground",children:"HTTP"}),e.jsx("dd",{className:"font-mono",children:t.status}),e.jsx("dt",{className:"text-muted-foreground",children:"Size"}),e.jsxs("dd",{className:"font-mono",children:[(t.bytes/1024).toFixed(1)," KB"]}),t.firstLoc&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-muted-foreground",children:"First URL"}),e.jsx("dd",{className:"font-mono text-xs break-all",children:t.firstLoc})]}),t.firstLastmod&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-muted-foreground",children:"First lastmod"}),e.jsx("dd",{className:"font-mono",children:t.firstLastmod})]}),t.error&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-muted-foreground",children:"Error"}),e.jsx("dd",{className:"text-destructive text-xs",children:t.error})]})]})]})]})}export{Ne as default};

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.