@aiquants/daily-report 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +13 -6
  2. package/dist/client.d.mts +363 -21
  3. package/dist/client.d.ts +363 -21
  4. package/dist/client.js +5 -4
  5. package/dist/client.js.map +1 -1
  6. package/dist/client.mjs +5 -4
  7. package/dist/client.mjs.map +1 -1
  8. package/dist/index.d.mts +76 -2
  9. package/dist/index.d.ts +76 -2
  10. package/dist/index.js +1 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +1 -1
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/server.d.mts +2 -0
  15. package/dist/server.d.ts +2 -0
  16. package/dist/server.js +8 -6
  17. package/dist/server.js.map +1 -1
  18. package/dist/server.mjs +7 -5
  19. package/dist/server.mjs.map +1 -1
  20. package/dist/styles/daily-report.standalone.css +1 -1
  21. package/package.json +3 -3
  22. package/src/client/components/daily-report-ids-stream-status.spec.tsx +93 -0
  23. package/src/client/components/daily-report-ids-stream-status.tsx +67 -0
  24. package/src/client/components/daily-report-page.spec.tsx +119 -0
  25. package/src/client/components/daily-report-page.tsx +56 -68
  26. package/src/client/components/daily-report-resolved-content.tsx +31 -7
  27. package/src/client/contexts/daily-report-action-context.spec.tsx +262 -0
  28. package/src/client/contexts/daily-report-action-context.tsx +149 -52
  29. package/src/client/hooks/use-animated-number.spec.ts +103 -0
  30. package/src/client/hooks/use-animated-number.ts +69 -0
  31. package/src/client/route-helpers.spec.ts +100 -0
  32. package/src/client/route-helpers.ts +20 -41
  33. package/src/client/streaming/daily-report-ids-stream-client.spec.ts +641 -0
  34. package/src/client/streaming/daily-report-ids-stream-client.ts +599 -0
  35. package/src/client/streaming/daily-report-ids-stream-session.spec.ts +360 -0
  36. package/src/client/streaming/daily-report-ids-stream-session.ts +280 -0
  37. package/src/client.ts +4 -0
  38. package/src/index.ts +1 -0
  39. package/src/server/handlers.ids-stream.spec.ts +362 -0
  40. package/src/server/handlers.ts +245 -5
  41. package/src/server/service.ts +40 -0
  42. package/src/server/test-helpers/handlers-config.ts +40 -1
  43. package/src/shared/ids-stream.ts +92 -0
package/dist/server.mjs CHANGED
@@ -1,10 +1,12 @@
1
- var We=r=>({async getEpoch(t){try{let o=await r?.getClient();if(!o)return 0;let i=await o.get(t);return i?Number(i):0}catch{return 0}},async incrementEpoch(t){try{let o=await r?.getClient();if(!o)return;await o.incr(t)}catch{}}}),Ee=class{constructor(t,o){this.config=t;this.epochStore=o;this.buckets=new Map;this.inFlight=new Map;this.invalidate=t=>{this.buckets.delete(t),this.inFlight.delete(t)};this.getOrFetch=async t=>{let{cacheKey:o,forceRefresh:i,snapshot:p}=t,c=this.buckets.get(o);if(i&&(this.buckets.delete(o),this.inFlight.delete(o)),!i&&c&&c.expireAt>Date.now())if(t.epochKey){let m=await this.epochStore.getEpoch(t.epochKey);if(c.epoch===m)return p?c.records.map(O=>structuredClone(O)):c.records;this.buckets.delete(o)}else return p?c.records.map(m=>structuredClone(m)):c.records;if(!i&&this.inFlight.has(o)){let m=await this.inFlight.get(o);return p?m.map(O=>structuredClone(O)):m}let x;x=(async()=>{let m=await t.fetcher();if(i||this.inFlight.get(o)===x){let O=Date.now()+(t.ttlMsOverride??this.config.defaultTtlMs),d=t.epochKey?await this.epochStore.getEpoch(t.epochKey):0;this.buckets.set(o,{records:m,expireAt:O,epoch:d}),this.buckets.size>500&&this.cleanExpired()}return m})(),i||this.inFlight.set(o,x);try{let m=await x;return p?m.map(O=>structuredClone(O)):m}finally{this.inFlight.delete(o)}};this.flush=t=>{this.buckets.delete(t),this.inFlight.delete(t)};this.clearAll=()=>{this.buckets.clear(),this.inFlight.clear()};this.invalidatePrefix=t=>{for(let o of this.buckets.keys())o.startsWith(t)&&this.buckets.delete(o);for(let o of this.inFlight.keys())o.startsWith(t)&&this.inFlight.delete(o)}}cleanExpired(){let t=Date.now();for(let[o,i]of this.buckets.entries())i.expireAt<=t&&this.buckets.delete(o)}};var wt=/^\d{4}-\d{2}-\d{2}$/,St=/^\d{4}\/\d{2}\/\d{2}$/,Ve=r=>r<10?`0${r}`:`${r}`,Ze=r=>{if(Number.isNaN(r.getTime()))return null;let t=r.getFullYear(),o=Ve(r.getMonth()+1),i=Ve(r.getDate());return`${t}-${o}-${i}`},ne=r=>{if(r==null)return null;if(r instanceof Date)return Ze(r);let t=r.trim();if(t==="")return null;if(wt.test(t))return t;if(St.test(t))return t.replaceAll("/","-");let o=new Date(t);return Ze(o)};var oe=(r,t,o=console)=>{let i=p=>typeof p=="string"?[`${t} ${p}`]:[t,p];return{debug:(p,...c)=>{r<=0&&o.debug(...i(p),...c)},info:(p,...c)=>{r<=1&&o.info(...i(p),...c)},warn:(p,...c)=>{r<=2&&o.warn(...i(p),...c)},error:(p,...c)=>{r<=3&&o.error(...i(p),...c)}}};import{z as s}from"zod";var It=s.object({name:s.string().nullish(),affiliation:s.string().nullish()}),At=s.object({name:s.string().nullish(),text:s.string().nullish(),color:s.string().nullish()}),_t=s.object({id:s.number(),name:s.string().nullish(),color:s.string().nullish()}),et=s.object({id:s.number(),userId:s.string().nullish(),userName:s.string().nullish(),content:s.string().nullish(),createdAt:s.string().nullish(),isMine:s.boolean()}),Ct=s.object({id:s.string(),fileName:s.string(),fileType:s.string().nullable(),fileSize:s.number().nullable(),createdAt:s.string().nullable(),state:s.enum(["present","absent","unknown"])}),Ye=s.object({reportHubId:s.number(),date:s.string().nullish(),createdAt:s.string().nullish(),author:s.string().nullish(),userId:s.string().nullish(),sourceType:s.string().nullish(),employeeName:s.string().nullish(),updatedBy:s.string().nullish(),updatedAt:s.string().nullish(),category:s.string().nullish(),creationCategory:s.string().nullish(),visitTimeFrom:s.string().nullish(),visitTimeTo:s.string().nullish(),customerName:s.string().nullish(),interviewers:s.array(It),subject:s.string().nullish(),content:s.string().nullish(),comments:s.array(At),isRead:s.boolean(),isStarred:s.boolean(),labels:s.array(_t),commentItems:s.array(et),attachments:s.array(Ct).default([])}),Tt=s.object({type:s.literal("connected")}),Ne=s.object({type:s.literal("status-update"),reportHubId:s.number(),statusType:s.enum(["star","read"]),value:s.boolean(),clientTempId:s.string(),recipientRawUserId:s.number().optional()}),Oe=s.object({type:s.literal("comment-add"),reportHubId:s.number(),comment:et,clientTempId:s.string()}),ze=s.object({type:s.literal("comment-delete"),reportHubId:s.number(),commentId:s.number(),clientTempId:s.string()}),Fe=s.object({type:s.literal("report-create"),reportHubId:s.number(),report:Ye,clientTempId:s.string(),recipientRawUserId:s.number().optional()}),Ke=s.object({type:s.literal("report-update"),reportHubId:s.number(),report:Ye,clientTempId:s.string(),recipientRawUserId:s.number().optional()}),je=s.object({type:s.literal("report-publish"),reportHubId:s.number(),report:Ye,clientTempId:s.string(),recipientRawUserId:s.number().optional()}),Je=s.object({type:s.literal("report-delete"),reportHubId:s.number(),clientTempId:s.string()}),tt=s.discriminatedUnion("type",[Tt,Ne,Oe,ze,Fe,Ke,je,Je]);import{createHash as Mt}from"crypto";var Qe=r=>{let t=JSON.stringify(r);return`"${Mt("sha256").update(t).digest("hex")}"`};var kt=oe(1,"[Response]"),de=(r,t,o,i=200,p=kt)=>{let c=Qe(o),x=r.headers.get("If-None-Match");p.info(`[jsonResponseWithETag] ETag: ${c}, If-None-Match: ${x}`);let m=new Headers({"Content-Type":"application/json","Cache-Control":"private, max-age=0, must-revalidate","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Content-Security-Policy":"default-src 'none'",ETag:c});return t&&m.append("Set-Cookie",t),r.method==="GET"&&i===200&&x===c?new Response(null,{status:304,headers:m}):new Response(JSON.stringify(o),{status:i,headers:m})};import{EventEmitter as vt}from"events";var $e=(r,t)=>{let[o,i]=r.split("-").map(Number),[p,c]=t.split("-").map(Number);return o!==p?o<p:i<=c},Le=class{constructor(t){this.config=t;this._emitter=new vt;this._state="idle";this._refCount=0;this._lastId="0-0";this._emitter.setMaxListeners(0),this._logger=t.logger??oe(1,"[SSE Reader]")}subscribe(t,o){return this._emitter.on("entry",t),o&&this._emitter.on("error",o),this._refCount++,this._refCount===1&&this._startLoop(),()=>{this._emitter.removeListener("entry",t),o&&this._emitter.removeListener("error",o),this._refCount--,this._refCount<=0&&(this._refCount=0,this._stopLoop())}}async destroy(){this._refCount=0,await this._stopLoop(),this._emitter.removeAllListeners(),this._lastId="0-0"}async _startLoop(){for(;this._state==="stopping";)await new Promise(t=>setTimeout(t,50));if(this._state!=="running"){this._state="running";try{this._client=await this.config.redis?.createClient()}catch(t){this._state="idle",this._emitter.emit("error",t instanceof Error?t:new Error("Failed to create Redis client for SSE reader"));return}if(!this._client){this._state="idle",this._emitter.emit("error",new Error("Failed to create Redis client for SSE reader"));return}this._client.on("error",t=>{this._logger.error("Redis client error:",t)});try{for(;this._state==="running"&&this._client?.isOpen;){let t=await this._client.xRead([{key:this.config.streamKey,id:this._lastId}],{BLOCK:5e3,COUNT:100});if(t)for(let o of t)for(let i of o.messages){this._lastId=i.id;try{let p=i.message?.data?JSON.parse(i.message.data):null,c=p?.type;if((c==="report-create"||c==="report-delete"||c==="report-publish")&&this.config.cache.invalidatePrefix("daily-report:ids"),c==="comment-add"||c==="comment-delete"||c==="status-update"){let x=p?.reportHubId;typeof x=="number"&&(this.config.cache.invalidate(`daily-report:detail:${x}`),this.config.cache.invalidatePrefix(`daily-report:detail:${x}:user:`))}}catch{}this._emitter.emit("entry",{id:i.id,message:i.message})}}}catch(t){!(t?.constructor?.name==="ClientClosedError")&&this._state==="running"&&(this._logger.error("xRead loop error:",t),this._emitter.emit("error",t))}finally{if(this._client?.isOpen)try{await this._client.quit()}catch{}this._client=void 0,this._state="idle",this._refCount>0&&this._startLoop()}}}async _stopLoop(){if(this._state==="running"){if(this._state="stopping",this._client?.isOpen)try{await this._client.quit()}catch{}for(;this._state!=="idle";)await new Promise(t=>setTimeout(t,50))}}};var N=(r,t)=>new Response(JSON.stringify(r),{status:t?.status??200,headers:{"Content-Type":"application/json"}}),Et=32*1024*1024,Nt=60,xt=4,Lt=12e4,$t=1024,Pt=60,Bt=5,Ut=new Set(["image/png","image/jpeg","image/gif","image/webp","application/pdf","text/plain"]),Ht=/^[\x20-\x7E]+$/,qt=/^[\w.+-]+\/[\w.+-]+/,rt=r=>r&&Ht.test(r)&&qt.test(r)?r:null,Yt=r=>encodeURIComponent(r).replace(/['()*]/g,t=>`%${t.charCodeAt(0).toString(16).toUpperCase()}`),Ot=r=>r.replace(/[^\x20-\x7E]/g,"_").replace(/["\\]/g,"_"),zt=r=>{switch(r){case"not_found":case"denied":case"invalid_path":return 404;case"too_large":return 413;default:return 502}},Ft={404:"Attachment not found",413:"Attachment too large",502:"Attachment storage unavailable"},Kt=r=>{let t=new Map;return(o,i)=>{let p=t.get(o);p&&t.delete(o);let c=p??{tokens:r,lastRefillMs:i},x=Math.max(0,i-c.lastRefillMs);x>0&&(c.tokens=Math.min(r,c.tokens+x*r/6e4),c.lastRefillMs=i);let m=c.tokens>=1;for(m&&(c.tokens-=1),t.set(o,c);t.size>$t;){let O=t.keys().next();if(O.done)break;t.delete(O.value)}return m}},jt=r=>{let t=0;return{tryAcquire:()=>t>=r?!1:(t+=1,!0),release:()=>{t>0&&(t-=1)}}},Jt=(r,t,o)=>{let i=setTimeout(t,o);i.unref?.();let p=()=>{clearTimeout(i),t()},c=!1;return new ReadableStream({pull(x){if(c){x.close(),p();return}c=!0,x.enqueue(r)},cancel(){p()}})};function nt(r){let{authenticate:t,service:o,encodeUserId:i,redis:p,sseReader:c,streamKey:x}=r,m=r.loginRedirectPath??"/auth/login",O=r.logger??oe(3,"[DailyReportAPI]"),d=r.logger??oe(1,"[DailyReportSSE]"),L=r.logger??oe(1,"[DailyReportAttachment]"),u=r.attachmentMaxBytes??Et,K=Kt(r.attachmentRateLimitPerMinute??Nt),j=jt(r.attachmentConcurrency??xt),B=async({request:T})=>{let{user:U,cookie:k}=await t(T,{failureRedirect:m}),M=new Headers;k&&M.append("Set-Cookie",k);let g=null;U&&(g=await o.getUserIdByExternalId(U.id));let w=g?i(g):null;return{data:{user:U,userId:w},headers:M}},Ie={"business-date":async(T,U,k,M)=>{let g=ne(T.searchParams.get("businessDate")),w=T.searchParams.get("forceRefresh")==="true";if(!g)return de(k,U,{error:{message:"Invalid business date"}},400);let H=await o.getDailyReportsByBusinessDateByExternalId(g,M.id,{forceRefresh:w});return de(k,U,{businessDate:g,reports:H},200)},ids:async(T,U,k,M)=>{let g=T.searchParams.get("forceRefresh")==="true",w=await o.getDailyReportIdsByExternalId(M.id,{forceRefresh:g});return de(k,U,{ids:w},200)},report:async(T,U,k,M)=>{let g=T.searchParams.get("reportHubId"),w=T.searchParams.get("forceRefresh")==="true",H=g?Number.parseInt(g,10):NaN;if(!Number.isFinite(H)||H<=0)return de(k,U,{error:{message:"Invalid reportHubId"}},400);let v=await o.getDailyReportDetailByIdByExternalId(H,M.id,{snapshot:!0,forceRefresh:w});return v?de(k,U,{report:v},200):de(k,U,{error:{message:"Report not found"}},404)}};return{index:{loader:B},api:{loader:async({request:T,params:U})=>{let{user:k,cookie:M}=await t(T,{failureRedirect:null}),g=M??null;if(!k)return de(T,g,{error:{message:"Unauthorized"}},401);if(T.method!=="GET")return de(T,g,{error:{message:"Method not allowed"}},405);let w=U.endpoint??"",H=Ie[w];if(!H)return de(T,g,{error:{message:"Unknown endpoint"}},404);let v=Date.now();try{let b=new URL(T.url);return await H(b,g,T,k)}catch(b){let I=Date.now()-v,A=b instanceof Error?b:new Error(String(b));return O.error(`500 endpoint=${w} elapsed=${I}ms name=${A.name} code=${"code"in A?A.code:"N/A"} message=${A.message}`),O.error("Stack:",A.stack),de(T,g,{error:{message:"Internal Server Error"}},500)}},action:async({request:T,params:U})=>{let{user:k}=await t(T,{failureRedirect:null});if(!k)return N({error:"Unauthorized"},{status:401});if(U.endpoint!=="action")return N({error:"Unknown endpoint"},{status:404});let g=await o.getUserIdByExternalId(k.id);if(!g)return N({error:"User not found"},{status:404});let w=await T.formData(),H=w.get("intent"),v=w.get("reportHubId"),b=v?Number(v):NaN,I=w.get("businessDate"),A=Number(w.get("operationTimestamp")),C=w.get("clientTempId");if(H==="clearCache")return await o.clearCache(),N({status:"OK",intent:"clearCache"});if(!C)return N({error:"clientTempId required"},{status:400});if(H!=="create"&&(!b||Number.isNaN(b)))return N({error:"Invalid reportHubId"},{status:400});switch(H){case"create":{if(!I)return N({error:"businessDate required"},{status:400});let D=await o.createDailyReport(g,I,C);return N({status:"OK",intent:"create",report:D,reportHubId:String(D.reportHubId),clientTempId:C})}case"update":{let D=w.get("title"),Y=w.get("content");try{return await o.updateDailyReport(b,g,{title:D,content:Y},C),N({status:"OK",intent:"update",reportHubId:String(b),clientTempId:C})}catch(q){if(q instanceof Error){if(q.message==="Unauthorized")return N({error:"Unauthorized"},{status:403});if(q.message==="Not Found")return N({error:"Report not found"},{status:404})}throw q}}case"publish":try{let D=await o.publishDailyReport(b,g,C);return N({status:"OK",intent:"publish",reportHubId:String(b),clientTempId:C,report:D})}catch(D){if(D instanceof Error){if(D.message==="Unauthorized")return N({error:"Unauthorized"},{status:403});if(D.message==="Not Found")return N({error:"Report not found"},{status:404})}throw D}case"delete":try{return await o.deleteDailyReport(b,g,C),N({status:"OK",intent:"delete",reportHubId:String(b),clientTempId:C})}catch(D){if(D instanceof Error){if(D.message==="Unauthorized")return N({error:"Unauthorized"},{status:403});if(D.message==="Not Found")return N({error:"Report not found"},{status:404})}throw D}case"toggleStar":{let D=w.get("isStarred");if(D===null)return N({error:"isStarred required"},{status:400});let Y=D==="true",q=await o.setStarStatus(g,b,I,Y,C);return N({status:"OK",intent:"toggleStar",updatedStatus:{isStarred:q.isStarred,isRead:q.isRead},operationTimestamp:A,reportHubId:String(b),clientTempId:C})}case"toggleRead":{let D=w.get("isRead");if(D===null)return N({error:"isRead required"},{status:400});let Y=D==="true",q=await o.setReadStatus(g,b,I,Y,C);return N({status:"OK",intent:"toggleRead",updatedStatus:{isStarred:q.isStarred,isRead:q.isRead},operationTimestamp:A,reportHubId:String(b),clientTempId:C})}case"addComment":{let D=w.get("content");if(!D)return N({error:"Content required"},{status:400});let Y=await o.addComment(g,b,D,I,C),q={...Y,userId:Y.userId};return N({status:"OK",intent:"addComment",newComment:q,reportHubId:String(b),clientTempId:C})}case"deleteComment":{let D=Number(w.get("commentId"));if(!D||Number.isNaN(D))return N({error:"Invalid commentId"},{status:400});try{return await o.deleteComment(g,b,D,I,C),N({status:"OK",reportHubId:String(b),deletedCommentId:String(D),clientTempId:C})}catch(Y){if(Y instanceof Error){if(Y.message==="Unauthorized")return N({error:"Unauthorized"},{status:403});if(Y.message==="Not Found")return N({error:"Comment not found"},{status:404})}throw Y}}default:return N({error:"Invalid intent"},{status:400})}}},sse:{loader:async({request:T,params:U})=>{let{user:k}=await t(T,{failureRedirect:null});if(!k)return new Response("Unauthorized",{status:401});let M=await o.getUserIdByExternalId(k.id);if(!M)return new Response("Forbidden",{status:403});if(U.endpoint!=="updates")return new Response("Not Found",{status:404});let w=new URL(T.url).searchParams.get("lastEventId")||T.headers.get("Last-Event-ID"),H=new TextEncoder,v=null,b=null,I=null,A=!1,C=()=>{if(!A){if(A=!0,b&&(clearInterval(b),b=null),I&&(I(),I=null),v)try{v.desiredSize!==null&&v.close()}catch{}finally{v=null}A=!1}},D=(q,Q)=>{if(Q.data)try{let S=JSON.parse(Q.data),V=tt.safeParse(S);if(!V.success){d.error("SSE message validation failed:",V.error.format());return}let te=V.data,se=typeof S.recipientRawUserId=="number"?S.recipientRawUserId:void 0;if(te.type==="status-update"){if(se===void 0||se!==M)return}else if(se!==void 0&&se!==M)return;let ye;if("recipientRawUserId"in S){let{recipientRawUserId:_e,...ie}=S;ye=JSON.stringify(ie)}else ye=Q.data;v&&v.desiredSize!==null&&v.enqueue(H.encode(`id: ${q}
2
- data: ${ye}
1
+ var Ze=t=>({async getEpoch(e){try{let a=await t?.getClient();if(!a)return 0;let o=await a.get(e);return o?Number(o):0}catch{return 0}},async incrementEpoch(e){try{let a=await t?.getClient();if(!a)return;await a.incr(e)}catch{}}}),Le=class{constructor(e,a){this.config=e;this.epochStore=a;this.buckets=new Map;this.inFlight=new Map;this.invalidate=e=>{this.buckets.delete(e),this.inFlight.delete(e)};this.getOrFetch=async e=>{let{cacheKey:a,forceRefresh:o,snapshot:p}=e,d=this.buckets.get(a);if(o&&(this.buckets.delete(a),this.inFlight.delete(a)),!o&&d&&d.expireAt>Date.now())if(e.epochKey){let c=await this.epochStore.getEpoch(e.epochKey);if(d.epoch===c)return p?d.records.map(L=>structuredClone(L)):d.records;this.buckets.delete(a)}else return p?d.records.map(c=>structuredClone(c)):d.records;if(!o&&this.inFlight.has(a)){let c=await this.inFlight.get(a);return p?c.map(L=>structuredClone(L)):c}let x;x=(async()=>{let c=await e.fetcher();if(o||this.inFlight.get(a)===x){let L=Date.now()+(e.ttlMsOverride??this.config.defaultTtlMs),l=e.epochKey?await this.epochStore.getEpoch(e.epochKey):0;this.buckets.set(a,{records:c,expireAt:L,epoch:l}),this.buckets.size>500&&this.cleanExpired()}return c})(),o||this.inFlight.set(a,x);try{let c=await x;return p?c.map(L=>structuredClone(L)):c}finally{this.inFlight.delete(a)}};this.flush=e=>{this.buckets.delete(e),this.inFlight.delete(e)};this.clearAll=()=>{this.buckets.clear(),this.inFlight.clear()};this.invalidatePrefix=e=>{for(let a of this.buckets.keys())a.startsWith(e)&&this.buckets.delete(a);for(let a of this.inFlight.keys())a.startsWith(e)&&this.inFlight.delete(a)}}cleanExpired(){let e=Date.now();for(let[a,o]of this.buckets.entries())o.expireAt<=e&&this.buckets.delete(a)}};var Tt=/^\d{4}-\d{2}-\d{2}$/,Mt=/^\d{4}\/\d{2}\/\d{2}$/,et=t=>t<10?`0${t}`:`${t}`,tt=t=>{if(Number.isNaN(t.getTime()))return null;let e=t.getFullYear(),a=et(t.getMonth()+1),o=et(t.getDate());return`${e}-${a}-${o}`},re=t=>{if(t==null)return null;if(t instanceof Date)return tt(t);let e=t.trim();if(e==="")return null;if(Tt.test(e))return e;if(Mt.test(e))return e.replaceAll("/","-");let a=new Date(e);return tt(a)};var kt=(t,e)=>t.businessDate!==e.businessDate?t.businessDate===null?1:e.businessDate===null||t.businessDate>e.businessDate?-1:1:e.reportHubId-t.reportHubId,rt=(t,e)=>{let a=t.findIndex(o=>kt({businessDate:o.businessDate,reportHubId:o.reportHubId},e)>0);return a===-1?t.length:a};var ie=(t,e,a=console)=>{let o=p=>typeof p=="string"?[`${e} ${p}`]:[e,p];return{debug:(p,...d)=>{t<=0&&a.debug(...o(p),...d)},info:(p,...d)=>{t<=1&&a.info(...o(p),...d)},warn:(p,...d)=>{t<=2&&a.warn(...o(p),...d)},error:(p,...d)=>{t<=3&&a.error(...o(p),...d)}}};import{z as i}from"zod";var Et=i.object({name:i.string().nullish(),affiliation:i.string().nullish()}),xt=i.object({name:i.string().nullish(),text:i.string().nullish(),color:i.string().nullish()}),vt=i.object({id:i.number(),name:i.string().nullish(),color:i.string().nullish()}),nt=i.object({id:i.number(),userId:i.string().nullish(),userName:i.string().nullish(),content:i.string().nullish(),createdAt:i.string().nullish(),isMine:i.boolean()}),Nt=i.object({id:i.string(),fileName:i.string(),fileType:i.string().nullable(),fileSize:i.number().nullable(),createdAt:i.string().nullable(),state:i.enum(["present","absent","unknown"])}),Fe=i.object({reportHubId:i.number(),date:i.string().nullish(),createdAt:i.string().nullish(),author:i.string().nullish(),userId:i.string().nullish(),sourceType:i.string().nullish(),employeeName:i.string().nullish(),updatedBy:i.string().nullish(),updatedAt:i.string().nullish(),category:i.string().nullish(),creationCategory:i.string().nullish(),visitTimeFrom:i.string().nullish(),visitTimeTo:i.string().nullish(),customerName:i.string().nullish(),interviewers:i.array(Et),subject:i.string().nullish(),content:i.string().nullish(),comments:i.array(xt),isRead:i.boolean(),isStarred:i.boolean(),labels:i.array(vt),commentItems:i.array(nt),attachments:i.array(Nt).default([])}),Lt=i.object({type:i.literal("connected")}),$e=i.object({type:i.literal("status-update"),reportHubId:i.number(),statusType:i.enum(["star","read"]),value:i.boolean(),clientTempId:i.string(),recipientRawUserId:i.number().optional()}),ze=i.object({type:i.literal("comment-add"),reportHubId:i.number(),comment:nt,clientTempId:i.string()}),Ke=i.object({type:i.literal("comment-delete"),reportHubId:i.number(),commentId:i.number(),clientTempId:i.string()}),Je=i.object({type:i.literal("report-create"),reportHubId:i.number(),report:Fe,clientTempId:i.string(),recipientRawUserId:i.number().optional()}),je=i.object({type:i.literal("report-update"),reportHubId:i.number(),report:Fe,clientTempId:i.string(),recipientRawUserId:i.number().optional()}),Ge=i.object({type:i.literal("report-publish"),reportHubId:i.number(),report:Fe,clientTempId:i.string(),recipientRawUserId:i.number().optional()}),Qe=i.object({type:i.literal("report-delete"),reportHubId:i.number(),clientTempId:i.string()}),at=i.discriminatedUnion("type",[Lt,$e,ze,Ke,Je,je,Ge,Qe]);import{createHash as $t}from"crypto";var Xe=t=>{let e=JSON.stringify(t);return`"${$t("sha256").update(e).digest("hex")}"`};var Pt=ie(1,"[Response]"),de=(t,e,a,o=200,p=Pt)=>{let d=Xe(a),x=t.headers.get("If-None-Match");p.info(`[jsonResponseWithETag] ETag: ${d}, If-None-Match: ${x}`);let c=new Headers({"Content-Type":"application/json","Cache-Control":"private, max-age=0, must-revalidate","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Content-Security-Policy":"default-src 'none'",ETag:d});return e&&c.append("Set-Cookie",e),t.method==="GET"&&o===200&&x===d?new Response(null,{status:304,headers:c}):new Response(JSON.stringify(a),{status:o,headers:c})};import{EventEmitter as Bt}from"events";var Ue=(t,e)=>{let[a,o]=t.split("-").map(Number),[p,d]=e.split("-").map(Number);return a!==p?a<p:o<=d},Be=class{constructor(e){this.config=e;this._emitter=new Bt;this._state="idle";this._refCount=0;this._lastId="0-0";this._emitter.setMaxListeners(0),this._logger=e.logger??ie(1,"[SSE Reader]")}subscribe(e,a){return this._emitter.on("entry",e),a&&this._emitter.on("error",a),this._refCount++,this._refCount===1&&this._startLoop(),()=>{this._emitter.removeListener("entry",e),a&&this._emitter.removeListener("error",a),this._refCount--,this._refCount<=0&&(this._refCount=0,this._stopLoop())}}async destroy(){this._refCount=0,await this._stopLoop(),this._emitter.removeAllListeners(),this._lastId="0-0"}async _startLoop(){for(;this._state==="stopping";)await new Promise(e=>setTimeout(e,50));if(this._state!=="running"){this._state="running";try{this._client=await this.config.redis?.createClient()}catch(e){this._state="idle",this._emitter.emit("error",e instanceof Error?e:new Error("Failed to create Redis client for SSE reader"));return}if(!this._client){this._state="idle",this._emitter.emit("error",new Error("Failed to create Redis client for SSE reader"));return}this._client.on("error",e=>{this._logger.error("Redis client error:",e)});try{for(;this._state==="running"&&this._client?.isOpen;){let e=await this._client.xRead([{key:this.config.streamKey,id:this._lastId}],{BLOCK:5e3,COUNT:100});if(e)for(let a of e)for(let o of a.messages){this._lastId=o.id;try{let p=o.message?.data?JSON.parse(o.message.data):null,d=p?.type;if((d==="report-create"||d==="report-delete"||d==="report-publish")&&this.config.cache.invalidatePrefix("daily-report:ids"),d==="comment-add"||d==="comment-delete"||d==="status-update"){let x=p?.reportHubId;typeof x=="number"&&(this.config.cache.invalidate(`daily-report:detail:${x}`),this.config.cache.invalidatePrefix(`daily-report:detail:${x}:user:`))}}catch{}this._emitter.emit("entry",{id:o.id,message:o.message})}}}catch(e){!(e?.constructor?.name==="ClientClosedError")&&this._state==="running"&&(this._logger.error("xRead loop error:",e),this._emitter.emit("error",e))}finally{if(this._client?.isOpen)try{await this._client.quit()}catch{}this._client=void 0,this._state="idle",this._refCount>0&&this._startLoop()}}}async _stopLoop(){if(this._state==="running"){if(this._state="stopping",this._client?.isOpen)try{await this._client.quit()}catch{}for(;this._state!=="idle";)await new Promise(e=>setTimeout(e,50))}}};var B=(t,e)=>new Response(JSON.stringify(t),{status:e?.status??200,headers:{"Content-Type":"application/json"}}),He=200,lt=4e3,Ut="stream interrupted by server error",Ht=/^\d{4}-\d{2}-\d{2}$/,Yt=t=>{let e=t.searchParams.get("cursor_report_hub_id"),a=t.searchParams.get("cursor_business_date");if(e===null)return a===null?{cursor:null}:{invalid:!0};let o=Number.parseInt(e,10);return!Number.isSafeInteger(o)||String(o)!==e||o<=0?{invalid:!0}:a===null?{cursor:{businessDate:null,reportHubId:o}}:Ht.test(a)?{cursor:{businessDate:a,reportHubId:o}}:{invalid:!0}};async function*st(t,e,a=He){let o=e?rt(t,e):0,p=!0;for(;;){let d=t.slice(o,o+(p?a:lt));o+=d.length;let x=o>=t.length,c=d[d.length-1];if(yield{items:d,nextCursor:x||!c?null:{businessDate:c.businessDate,reportHubId:c.reportHubId},isLast:x,...p?{total:t.length}:{}},p=!1,x)return}}var ot=(t,e,a,o)=>{let p=new TextEncoder,d=!1,x=new ReadableStream({async pull(L){if(d||a.signal.aborted){await e.return(void 0).catch(()=>{});try{L.close()}catch{}return}try{let{value:l,done:$}=await e.next();if($){L.close();return}if(d||a.signal.aborted){await e.return(void 0).catch(()=>{});try{L.close()}catch{}return}L.enqueue(p.encode(`${JSON.stringify(l)}
2
+ `))}catch(l){if(d||a.signal.aborted)return;o.error(`ids-stream emission failed: ${l instanceof Error?l.message:String(l)}`);try{L.enqueue(p.encode(`${JSON.stringify({error:Ut})}
3
+ `)),L.close()}catch{}}},cancel(){d=!0,e.return(void 0).catch(()=>{})}}),c=new Headers({"Content-Type":"application/x-ndjson; charset=utf-8","Cache-Control":"private, no-cache, no-transform","X-Content-Type-Options":"nosniff"});return t&&c.append("Set-Cookie",t),new Response(x,{status:200,headers:c})},qt=32*1024*1024,Ot=60,Ft=4,zt=12e4,Kt=1024,Jt=60,jt=5,Gt=new Set(["image/png","image/jpeg","image/gif","image/webp","application/pdf","text/plain"]),Qt=/^[\x20-\x7E]+$/,Xt=/^[\w.+-]+\/[\w.+-]+/,it=t=>t&&Qt.test(t)&&Xt.test(t)?t:null,Wt=t=>encodeURIComponent(t).replace(/['()*]/g,e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`),Vt=t=>t.replace(/[^\x20-\x7E]/g,"_").replace(/["\\]/g,"_"),Zt=t=>{switch(t){case"not_found":case"denied":case"invalid_path":return 404;case"too_large":return 413;default:return 502}},er={404:"Attachment not found",413:"Attachment too large",502:"Attachment storage unavailable"},tr=t=>{let e=new Map;return(a,o)=>{let p=e.get(a);p&&e.delete(a);let d=p??{tokens:t,lastRefillMs:o},x=Math.max(0,o-d.lastRefillMs);x>0&&(d.tokens=Math.min(t,d.tokens+x*t/6e4),d.lastRefillMs=o);let c=d.tokens>=1;for(c&&(d.tokens-=1),e.set(a,d);e.size>Kt;){let L=e.keys().next();if(L.done)break;e.delete(L.value)}return c}},rr=t=>{let e=0;return{tryAcquire:()=>e>=t?!1:(e+=1,!0),release:()=>{e>0&&(e-=1)}}},nr=(t,e,a)=>{let o=setTimeout(e,a);o.unref?.();let p=()=>{clearTimeout(o),e()},d=!1;return new ReadableStream({pull(x){if(d){x.close(),p();return}d=!0,x.enqueue(t)},cancel(){p()}})};function ut(t){let{authenticate:e,service:a,encodeUserId:o,redis:p,sseReader:d,streamKey:x}=t,c=t.loginRedirectPath??"/auth/login",L=t.logger??ie(3,"[DailyReportAPI]"),l=t.logger??ie(1,"[DailyReportSSE]"),$=t.logger??ie(1,"[DailyReportAttachment]"),m=t.attachmentMaxBytes??qt,K=tr(t.attachmentRateLimitPerMinute??Ot),J=rr(t.attachmentConcurrency??Ft),Y=async({request:M})=>{let{user:P,cookie:k}=await e(M,{failureRedirect:c}),E=new Headers;k&&E.append("Set-Cookie",k);let b=null;P&&(b=await a.getUserIdByExternalId(P.id));let A=b?o(b):null;return{data:{user:P,userId:A},headers:E}},Ae={"business-date":async(M,P,k,E)=>{let b=re(M.searchParams.get("businessDate")),A=M.searchParams.get("forceRefresh")==="true";if(!b)return de(k,P,{error:{message:"Invalid business date"}},400);let v=await a.getDailyReportsByBusinessDateByExternalId(b,E.id,{forceRefresh:A});return de(k,P,{businessDate:b,reports:v},200)},"ids-stream":async(M,P,k,E)=>{let b=Yt(M);if("invalid"in b)return de(k,P,{error:{message:"Invalid cursor"}},400);let A=M.searchParams.get("forceRefresh")==="true",v=a.getDailyReportIdsByExternalId(E.id,{forceRefresh:A});if(v.catch(()=>{}),b.cursor)return ot(P,st(await v,b.cursor),k,L);let C=a.getDailyReportIdsFirstPageByExternalId(E.id,He);C.catch(()=>{});let R=(async function*(){let w=await Promise.race([v.then(g=>({kind:"full",items:g}),g=>({kind:"full-error",error:g})),C.then(g=>({kind:"page",items:g}),()=>({kind:"page-error"}))]),S=null,I=null;if(w.kind==="full")I=w.items;else if(w.kind==="page"||w.kind==="full-error"){let g=w.kind==="page"?w.items:await C;if(g.length<He){yield{items:g,nextCursor:null,isLast:!0,total:g.length};return}let q=g[g.length-1];S={businessDate:q.businessDate,reportHubId:q.reportHubId},yield{items:g,nextCursor:S,isLast:!1}}I===null&&(I=await v),yield*st(I,S,S?lt:He)})();return ot(P,R,k,L)},report:async(M,P,k,E)=>{let b=M.searchParams.get("reportHubId"),A=M.searchParams.get("forceRefresh")==="true",v=b?Number.parseInt(b,10):NaN;if(!Number.isFinite(v)||v<=0)return de(k,P,{error:{message:"Invalid reportHubId"}},400);let C=await a.getDailyReportDetailByIdByExternalId(v,E.id,{snapshot:!0,forceRefresh:A});return C?de(k,P,{report:C},200):de(k,P,{error:{message:"Report not found"}},404)}};return{index:{loader:Y},api:{loader:async({request:M,params:P})=>{let{user:k,cookie:E}=await e(M,{failureRedirect:null}),b=E??null;if(!k)return de(M,b,{error:{message:"Unauthorized"}},401);if(M.method!=="GET")return de(M,b,{error:{message:"Method not allowed"}},405);let A=P.endpoint??"",v=Ae[A];if(!v)return de(M,b,{error:{message:"Unknown endpoint"}},404);let C=Date.now();try{let R=new URL(M.url);return await v(R,b,M,k)}catch(R){let w=Date.now()-C,S=R instanceof Error?R:new Error(String(R));return L.error(`500 endpoint=${A} elapsed=${w}ms name=${S.name} code=${"code"in S?S.code:"N/A"} message=${S.message}`),L.error("Stack:",S.stack),de(M,b,{error:{message:"Internal Server Error"}},500)}},action:async({request:M,params:P})=>{let{user:k}=await e(M,{failureRedirect:null});if(!k)return B({error:"Unauthorized"},{status:401});if(P.endpoint!=="action")return B({error:"Unknown endpoint"},{status:404});let b=await a.getUserIdByExternalId(k.id);if(!b)return B({error:"User not found"},{status:404});let A=await M.formData(),v=A.get("intent"),C=A.get("reportHubId"),R=C?Number(C):NaN,w=A.get("businessDate"),S=Number(A.get("operationTimestamp")),I=A.get("clientTempId");if(v==="clearCache")return await a.clearCache(),B({status:"OK",intent:"clearCache"});if(!I)return B({error:"clientTempId required"},{status:400});if(v!=="create"&&(!R||Number.isNaN(R)))return B({error:"Invalid reportHubId"},{status:400});switch(v){case"create":{if(!w)return B({error:"businessDate required"},{status:400});let g=await a.createDailyReport(b,w,I);return B({status:"OK",intent:"create",report:g,reportHubId:String(g.reportHubId),clientTempId:I})}case"update":{let g=A.get("title"),q=A.get("content");try{return await a.updateDailyReport(R,b,{title:g,content:q},I),B({status:"OK",intent:"update",reportHubId:String(R),clientTempId:I})}catch(O){if(O instanceof Error){if(O.message==="Unauthorized")return B({error:"Unauthorized"},{status:403});if(O.message==="Not Found")return B({error:"Report not found"},{status:404})}throw O}}case"publish":try{let g=await a.publishDailyReport(R,b,I);return B({status:"OK",intent:"publish",reportHubId:String(R),clientTempId:I,report:g})}catch(g){if(g instanceof Error){if(g.message==="Unauthorized")return B({error:"Unauthorized"},{status:403});if(g.message==="Not Found")return B({error:"Report not found"},{status:404})}throw g}case"delete":try{return await a.deleteDailyReport(R,b,I),B({status:"OK",intent:"delete",reportHubId:String(R),clientTempId:I})}catch(g){if(g instanceof Error){if(g.message==="Unauthorized")return B({error:"Unauthorized"},{status:403});if(g.message==="Not Found")return B({error:"Report not found"},{status:404})}throw g}case"toggleStar":{let g=A.get("isStarred");if(g===null)return B({error:"isStarred required"},{status:400});let q=g==="true",O=await a.setStarStatus(b,R,w,q,I);return B({status:"OK",intent:"toggleStar",updatedStatus:{isStarred:O.isStarred,isRead:O.isRead},operationTimestamp:S,reportHubId:String(R),clientTempId:I})}case"toggleRead":{let g=A.get("isRead");if(g===null)return B({error:"isRead required"},{status:400});let q=g==="true",O=await a.setReadStatus(b,R,w,q,I);return B({status:"OK",intent:"toggleRead",updatedStatus:{isStarred:O.isStarred,isRead:O.isRead},operationTimestamp:S,reportHubId:String(R),clientTempId:I})}case"addComment":{let g=A.get("content");if(!g)return B({error:"Content required"},{status:400});let q=await a.addComment(b,R,g,w,I),O={...q,userId:q.userId};return B({status:"OK",intent:"addComment",newComment:O,reportHubId:String(R),clientTempId:I})}case"deleteComment":{let g=Number(A.get("commentId"));if(!g||Number.isNaN(g))return B({error:"Invalid commentId"},{status:400});try{return await a.deleteComment(b,R,g,w,I),B({status:"OK",reportHubId:String(R),deletedCommentId:String(g),clientTempId:I})}catch(q){if(q instanceof Error){if(q.message==="Unauthorized")return B({error:"Unauthorized"},{status:403});if(q.message==="Not Found")return B({error:"Comment not found"},{status:404})}throw q}}default:return B({error:"Invalid intent"},{status:400})}}},sse:{loader:async({request:M,params:P})=>{let{user:k}=await e(M,{failureRedirect:null});if(!k)return new Response("Unauthorized",{status:401});let E=await a.getUserIdByExternalId(k.id);if(!E)return new Response("Forbidden",{status:403});if(P.endpoint!=="updates")return new Response("Not Found",{status:404});let A=new URL(M.url).searchParams.get("lastEventId")||M.headers.get("Last-Event-ID"),v=new TextEncoder,C=null,R=null,w=null,S=!1,I=()=>{if(!S){if(S=!0,R&&(clearInterval(R),R=null),w&&(w(),w=null),C)try{C.desiredSize!==null&&C.close()}catch{}finally{C=null}S=!1}},g=(O,Q)=>{if(Q.data)try{let _=JSON.parse(Q.data),V=at.safeParse(_);if(!V.success){l.error("SSE message validation failed:",V.error.format());return}let se=V.data,le=typeof _.recipientRawUserId=="number"?_.recipientRawUserId:void 0;if(se.type==="status-update"){if(le===void 0||le!==E)return}else if(le!==void 0&&le!==E)return;let oe;if("recipientRawUserId"in _){let{recipientRawUserId:Ce,...we}=_;oe=JSON.stringify(we)}else oe=Q.data;C&&C.desiredSize!==null&&C.enqueue(v.encode(`id: ${O}
4
+ data: ${oe}
3
5
 
4
- `))}catch(S){d.error(`[SSE:${M}] processEntry error:`,S)}},Y=new ReadableStream({async start(q){v=q,b=setInterval(()=>{try{v&&v.desiredSize!==null?v.enqueue(H.encode(`: keep-alive
6
+ `))}catch(_){l.error(`[SSE:${E}] processEntry error:`,_)}},q=new ReadableStream({async start(O){C=O,R=setInterval(()=>{try{C&&C.desiredSize!==null?C.enqueue(v.encode(`: keep-alive
5
7
 
6
- `)):C()}catch{C()}},5e3),T.signal.addEventListener("abort",()=>{C()});let Q=w||"0-0";if(I=c.subscribe(S=>{$e(S.id,Q)||(Q=S.id,D(S.id,S.message))},S=>{d.error(`[SSE:${M}] Fan-Out reader error:`,S),C()}),!w)try{let S=await p?.getClient();if(S){let V=await S.xRevRange(x,"+","-",{COUNT:1});V.length>0&&!$e(V[0].id,Q)&&(Q=V[0].id)}}catch{}if(v&&v.desiredSize!==null){let S=`data: ${JSON.stringify({type:"connected"})}
8
+ `)):I()}catch{I()}},5e3),M.signal.addEventListener("abort",()=>{I()});let Q=A||"0-0";if(w=d.subscribe(_=>{Ue(_.id,Q)||(Q=_.id,g(_.id,_.message))},_=>{l.error(`[SSE:${E}] Fan-Out reader error:`,_),I()}),!A)try{let _=await p?.getClient();if(_){let V=await _.xRevRange(x,"+","-",{COUNT:1});V.length>0&&!Ue(V[0].id,Q)&&(Q=V[0].id)}}catch{}if(C&&C.desiredSize!==null){let _=`data: ${JSON.stringify({type:"connected"})}
7
9
 
8
10
  `,V=Q!=="0-0"?`id: ${Q}
9
- ${S}`:S;v.enqueue(H.encode(V))}if(w)try{let S=await p?.getClient();if(S){let V=await S.xRange(x,w,"+",{COUNT:1e3});for(let te of V)te.id!==w&&($e(te.id,Q)||(Q=te.id,D(te.id,te.message)))}}catch(S){d.error(`[SSE:${M}] catch-up xRange error:`,S)}},cancel(){C()}});return new Response(Y,{headers:{"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive"}})}},attachment:{loader:async({request:T,params:U})=>{let k=null,M=(g,w,H)=>{let v=new Headers({"Content-Type":"application/json"});return H!==void 0&&v.set("Retry-After",String(H)),k&&v.append("Set-Cookie",k),new Response(JSON.stringify({error:{message:w}}),{status:g,headers:v})};try{let{user:g,cookie:w}=await t(T,{failureRedirect:null});if(k=w??null,!g)return M(401,"Unauthorized");if(T.method!=="GET"&&T.method!=="HEAD")return M(405,"Method not allowed");let H=r.attachmentIdCodec,v=r.readAttachment;if(!(H&&v))return M(404,"Attachment not found");let b=U.token??"",I=H.decode(b);if(I===null||!Number.isSafeInteger(I)||I<=0)return M(400,"Invalid attachment token");let A=await o.getUserIdByExternalId(g.id);if(!A)return M(403,"Forbidden");if(!K(A,Date.now()))return L.error(`429 attachment=${I} viewer=${A} reason=rate_limit`),M(429,"Too many requests",Pt);let C=await o.getAttachmentForUser(I,A);if(!C)return L.error(`404 attachment=${I} viewer=${A} reason=not_visible`),M(404,"Attachment not found");if(C.fileSize!=null&&C.fileSize>u)return L.error(`413 attachment=${I} viewer=${A} reason=db_size`),M(413,"Attachment too large");if(!j.tryAcquire())return L.error(`503 attachment=${I} viewer=${A} reason=concurrency`),M(503,"Too many concurrent downloads",Bt);let D=!1,Y=()=>{D||(D=!0,j.release())},q=!1;try{let Q=T.method==="HEAD",S=await v(C.filePath,{maxBytes:u,head:Q,principal:i(A)});if(!S.ok){let le=zt(S.reason);return S.reason==="not_found"&&o.markAttachmentMissing(I).catch(we=>{L.error(`markAttachmentMissing failed attachment=${I} message=${we instanceof Error?we.message:"unknown"}`)}),L.error(`${le} attachment=${I} viewer=${A} reason=${S.reason} code=${S.code??"-"}`),M(le,Ft[le]??"Attachment storage unavailable")}let V=rt(C.fileType)??rt(S.contentType)??"application/octet-stream",te=Ut.has(V),se=new URL(T.url).searchParams.get("download")==="1",ye=te?V:"application/octet-stream",_e=te&&!se?"inline":"attachment",ie=S.size??S.bytes.byteLength,he=new Headers({"Content-Type":ye,"Content-Length":String(ie),"Content-Disposition":`${_e}; filename="${Ot(C.fileName)}"; filename*=UTF-8''${Yt(C.fileName)}`,"Cache-Control":"private, no-store","X-Content-Type-Options":"nosniff","Content-Security-Policy":"default-src 'none'; sandbox","X-Frame-Options":"SAMEORIGIN"});return k&&he.append("Set-Cookie",k),o.markAttachmentPresent(I).catch(le=>{L.error(`markAttachmentPresent failed attachment=${I} message=${le instanceof Error?le.message:"unknown"}`)}),L.info(`200 attachment=${I} viewer=${A} type=${ye} disposition=${_e} bytes=${ie}`),Q?new Response(null,{status:200,headers:he}):(q=!0,new Response(Jt(S.bytes,Y,Lt),{status:200,headers:he}))}finally{q||Y()}}catch(g){if(g instanceof Response)throw g;return L.error(`500 attachment=? viewer=? reason=unexpected message=${g instanceof Error?g.message:"unknown"}`),M(500,"Attachment request failed")}}}}}import{aliasedTable as Pe,and as X,asc as Be,desc as Ue,eq as h,getColumns as Qt,inArray as ge,isNull as ce,or as He,sql as at}from"drizzle-orm";var Re=r=>Qt(r);function ot(r){let{db:t,tables:o,userTable:i,resolveUserId:p,encodeUserId:c,redis:x,cache:m,epochs:O}=r,{hub:d,internal:L,comment:u,label:K,hubLabel:j,userStatus:B}=o,Ie=r.externalSources??[],Te=r.draftLabelNames&&r.draftLabelNames.length>0?r.draftLabelNames:[r.draftLabelName??"\u4E0B\u66F8\u304D"],Me=Te[0]??"\u4E0B\u66F8\u304D",ee=r.logger??oe(1,"[DailyReportService]"),Ae="daily-report:ids",T=r.idsTtlMs??18e4,U="daily-report:ids:epoch",k="daily-report:business-date:",M=r.businessDateTtlMs??3e5,g="daily-report:date-epoch:",w="daily-report:detail-epoch:",H=r.streamKey??"daily-report:sse-stream",v=r.streamMaxLen??1e4,b=e=>O.incrementEpoch(e),I=async(e,n)=>{let a=await x?.getClient();if(!a){ee.warn(`[SSE] Redis client unavailable (${n})`);return}let l=Date.now();try{await a.xAdd(H,"*",{data:JSON.stringify(e)},{TRIM:{strategy:"MAXLEN",strategyModifier:"~",threshold:v}});let f=Date.now()-l;f>1e3&&ee.warn(`[SSE] Slow publish (${n}): ${f}ms`)}catch(f){ee.error(`[SSE] Redis publish failed (${n}):`,f)}},A=(e,n)=>{if(!e)return null;let a=e instanceof Date?e:new Date(e);if(Number.isNaN(a.getTime()))return typeof e=="string"?e:null;let l=n==="YYYY-MM-DD HH:mm:ss"?{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"}:{year:"numeric",month:"2-digit",day:"2-digit"};return(n==="YYYY-MM-DD HH:mm:ss"?a.toLocaleString("ja-JP",l):a.toLocaleDateString("ja-JP",l)).replace(/\//g,"-")},C=e=>e==null||e===""?null:/^\d+$/.test(e)?c(Number(e)):e,D=()=>{let e={};for(let n of Ie)e[`ext_${n.sourceType}`]=Re(n.table);return e},Y=e=>{let n=e;for(let a of Ie)n=n.leftJoin(a.table,X(h(d.sourceType,a.sourceType),h(d.sourceIdNum,a.idColumn)));return n},q=(e,n)=>{let{hub:a,internal:l}=e,f=a.summary,y=[],R=null,_=null,z=null,J=null,$=null,ae=null,me=[],fe=Ie.find(E=>E.sourceType===a.sourceType),Se=fe?e[`ext_${fe.sourceType}`]:void 0;if(fe&&Se){let E=fe.mapRow(Se);E.content!==void 0&&(f=E.content),E.employeeName!==void 0&&(ae=E.employeeName),E.category!==void 0&&(R=E.category),E.creationCategory!==void 0&&(_=E.creationCategory),E.visitTimeFrom!==void 0&&(z=E.visitTimeFrom),E.visitTimeTo!==void 0&&(J=E.visitTimeTo),E.customerName!==void 0&&($=E.customerName),E.interviewers!==void 0&&(y=E.interviewers),E.comments!==void 0&&(me=E.comments)}else a.sourceType==="Internal"&&l&&(f=l.body);let pe=(e.labels??[]).map(E=>({id:E.id,name:E.name,color:E.color})),re=e.comments??[],F=_?Te.includes(_):!1,G=ie?.[0]??null;return F&&G&&!pe.some(E=>E.id===G)&&pe.push({id:G,name:Me,color:null}),{reportHubId:a.id,date:A(a.businessDate,"YYYY-MM-DD"),author:e.creatorName??C(a.createdBy)??"",userId:a.userId?c(a.userId):"",sourceType:a.sourceType??"Internal",createdAt:A(a.createdAt,"YYYY-MM-DD HH:mm:ss"),updatedAt:A(a.updatedAt,"YYYY-MM-DD HH:mm:ss"),updatedBy:C(a.updatedBy),employeeName:ae??e.creatorName??C(a.createdBy),category:R,creationCategory:_,visitTimeFrom:z,visitTimeTo:J,customerName:$,interviewers:y,subject:a.title,content:f,comments:me,isRead:e.isRead??!1,isStarred:e.isStarred??!1,labels:pe,commentItems:re.map(E=>({...E,userId:c(E.userId),isMine:n?E.userId===n:!1})),attachments:e.attachments}},Q=async e=>{let n=Pe(j,"draft_label"),a=await le();return(await t.select({reportHubId:d.id,businessDate:d.businessDate,sourceType:d.sourceType}).from(d).leftJoin(n,X(h(n.hubId,d.id),ge(n.labelId,a))).where(X(ce(d.deletedAt),He(h(d.userId,e),ce(d.userId),ce(n.hubId)))).orderBy(Ue(d.businessDate),Ue(d.id))).map(f=>({...f,businessDate:A(f.businessDate,"YYYY-MM-DD")}))},S=async e=>{let n=await Y(t.select({hub:Re(d),internal:Re(L),creatorName:i.displayName,...D()}).from(d)).leftJoin(L,h(d.id,L.hubId)).leftJoin(i,h(d.userId,i.id)).where(X(h(d.businessDate,at`${e}`),ce(d.deletedAt))).orderBy(Ue(d.id)),a=await ke(n.map(l=>l.hub.id));return n.map(l=>q({...l,attachments:a.get(l.hub.id)??[]}))},V=e=>`${k}${e}`,te=new Map,se=async e=>{if(!r.disableUserIdCache&&te.has(e))return te.get(e)??null;let n=await p(e);return n!==null&&!r.disableUserIdCache&&te.set(e,n),n},ye=async(e,{forceRefresh:n=!1,snapshot:a=!1,ttlMsOverride:l}={})=>{let f=await se(e);if(!f)return[];let y=`${Ae}:user:${f}`;return m.getOrFetch({cacheKey:y,fetcher:async()=>await Q(f),forceRefresh:n,snapshot:a,ttlMsOverride:l??T,epochKey:U})},_e=(e,{forceRefresh:n=!1,snapshot:a=!1,ttlMsOverride:l}={})=>{let f=ne(e);return f?m.getOrFetch({cacheKey:V(f),fetcher:()=>S(f),forceRefresh:n,snapshot:a,ttlMsOverride:l??M}):Promise.resolve([])},ie=null,he=async()=>(ie!==null||(ie=(await t.select({id:K.id}).from(K).where(ge(K.name,Te)).orderBy(K.id)).map(n=>n.id)),ie),le=async()=>{let e=await he();return e.length>0?e:[-1]},we=async()=>{let e=await he();if(e.length===0)return null;let[n]=await t.select({id:K.id}).top(1).from(K).where(h(K.name,Me));return n?.id??e[0]},dt=(e,n)=>n!=null?"absent":e!=null?"present":"unknown",ke=async e=>{let n=new Map,a=o.attachment,l=r.attachmentIdCodec;if(e.length===0||!a||!l)return n;let f=await t.select({id:a.id,hubId:a.hubId,fileName:a.fileName,fileType:a.fileType,fileSize:a.fileSize,createdAt:a.createdAt,objectCheckedAt:a.objectCheckedAt,objectMissingAt:a.objectMissingAt}).from(a).where(ge(a.hubId,e)).orderBy(Be(a.createdAt),Be(a.id));for(let y of f){let R={id:l.encode(Number(y.id)),fileName:String(y.fileName),fileType:y.fileType?String(y.fileType):null,fileSize:y.fileSize!=null?Number(y.fileSize):null,createdAt:A(y.createdAt,"YYYY-MM-DD HH:mm:ss"),state:dt(y.objectCheckedAt,y.objectMissingAt)},_=n.get(Number(y.hubId));_?_.push(R):n.set(Number(y.hubId),[R])}return n},ut=async(e,n)=>{let a=o.attachment;if(!a)return null;let l=await le(),f=Pe(j,"draft_label");return(await t.select({fileName:a.fileName,filePath:a.filePath,fileType:a.fileType,fileSize:a.fileSize}).top(1).from(a).innerJoin(d,h(d.id,a.hubId)).leftJoin(f,X(h(f.hubId,d.id),ge(f.labelId,l))).where(X(h(a.id,e),ce(d.deletedAt),He(h(d.userId,n),ce(f.hubId)))))[0]??null},ct=async e=>{let n=o.attachment;if(!n)return;let a=new Date;try{await t.update(n).set({objectCheckedAt:a,objectMissingAt:a,updatedAt:a}).where(h(n.id,e))}catch(l){ee.error("markAttachmentMissing failed",l)}},mt=async e=>{let n=o.attachment;if(!n)return;let a=new Date;try{await t.update(n).set({objectCheckedAt:a,objectMissingAt:null,updatedAt:a}).where(h(n.id,e))}catch(l){ee.error("markAttachmentPresent failed",l)}},pt=async(e,n,{forceRefresh:a=!1,snapshot:l=!1,ttlMsOverride:f}={})=>{let y=await se(n);if(!y)return[];let R=ne(e);if(!R)return[];let _=`daily-report:date:${R}:user:${y}`;return m.getOrFetch({cacheKey:_,fetcher:async()=>{let z=await le(),J=Pe(j,"draft_label"),$=await Y(t.select({hub:Re(d),internal:Re(L),isRead:B.isRead,isStarred:B.isStarred,creatorName:i.displayName,...D()}).from(d)).leftJoin(L,h(d.id,L.hubId)).leftJoin(B,X(h(d.id,B.hubId),h(B.userId,y))).leftJoin(J,X(h(J.hubId,d.id),ge(J.labelId,z))).leftJoin(i,h(d.userId,i.id)).where(X(h(d.businessDate,at`${R}`),ce(d.deletedAt),He(h(d.userId,y),ce(J.hubId)))).orderBy(Ue(d.id));if($.length===0)return[];let ae=$.map(F=>F.hub.id),me=await t.select({hubId:j.hubId,id:K.id,name:K.name,color:K.color}).from(j).innerJoin(K,h(j.labelId,K.id)).where(ge(j.hubId,ae)),fe=await t.select({hubId:u.hubId,id:u.id,body:u.body,createdAt:u.createdAt,userId:u.userId,userName:i.displayName}).from(u).leftJoin(i,h(u.userId,i.id)).where(ge(u.hubId,ae)).orderBy(Be(u.createdAt)),Se=await ke(ae),pe=new Map,re=new Map;for(let F of me){let G=pe.get(F.hubId);G||(G=[],pe.set(F.hubId,G)),G.push({id:F.id,name:F.name,color:F.color})}for(let F of fe){let G=re.get(F.hubId);G||(G=[],re.set(F.hubId,G)),G.push({id:F.id,content:F.body,createdAt:A(F.createdAt,"YYYY-MM-DD HH:mm:ss")||"",userId:F.userId,userName:F.userName||""})}return $.map(F=>{let G=F.hub.id;return q({...F,attachments:Se.get(G)??[],labels:pe.get(G)||[],comments:re.get(G)||[]},y)})},forceRefresh:a,snapshot:l,ttlMsOverride:f??M,epochKey:`${g}${R}`})},Ce=async(e,n,{forceRefresh:a=!1,snapshot:l=!1,ttlMsOverride:f}={})=>{let y=`daily-report:detail:${e}:user:${n}`;return(await m.getOrFetch({cacheKey:y,fetcher:async()=>{let _=await le(),z=Pe(j,"draft_label"),J=await Y(t.select({hub:Re(d),internal:Re(L),isRead:B.isRead,isStarred:B.isStarred,creatorName:i.displayName,...D()}).from(d)).leftJoin(L,h(d.id,L.hubId)).leftJoin(B,X(h(d.id,B.hubId),h(B.userId,n))).leftJoin(z,X(h(z.hubId,d.id),ge(z.labelId,_))).leftJoin(i,h(d.userId,i.id)).where(X(h(d.id,e),ce(d.deletedAt),He(h(d.userId,n),ce(z.hubId))));if(J.length===0)return[];let $=J[0].hub.id,ae=await t.select({id:K.id,name:K.name,color:K.color}).from(j).innerJoin(K,h(j.labelId,K.id)).where(h(j.hubId,$)),me=await t.select({id:u.id,body:u.body,createdAt:u.createdAt,userId:u.userId,userName:i.displayName}).from(u).leftJoin(i,h(u.userId,i.id)).where(h(u.hubId,$)).orderBy(Be(u.createdAt)),fe=await ke([$]),Se=ae.map(re=>({id:re.id,name:re.name,color:re.color})),pe=me.map(re=>({id:re.id,content:re.body,createdAt:A(re.createdAt,"YYYY-MM-DD HH:mm:ss")||"",userId:re.userId,userName:re.userName||""}));return[q({...J[0],attachments:fe.get($)??[],labels:Se,comments:pe},n)]},forceRefresh:a,snapshot:l,ttlMsOverride:f??M,epochKey:`${w}${e}`}))[0]??null},yt=async(e,n,a={})=>{let l=await se(n);return l?Ce(e,l,a):null},ht=async(e,n,a,l,f)=>{let y=await t.select().top(1).from(B).where(X(h(B.hubId,n),h(B.userId,e))),R;if(y.length>0?y[0].isStarred!==l?R=(await t.update(B).set({isStarred:l,updatedAt:new Date,updatedBy:String(e)}).output().where(X(h(B.hubId,n),h(B.userId,e))))[0]:R=y[0]:R=(await t.insert(B).output().values({hubId:n,userId:e,isStarred:l,isRead:!1,createdAt:new Date,createdBy:String(e),updatedAt:new Date,updatedBy:String(e)}))[0],a){let _=ne(a);_&&(m.invalidate(`daily-report:date:${_}:user:${e}`),await b(`${g}${_}`))}return m.invalidate(`daily-report:detail:${n}`),m.invalidate(`daily-report:detail:${n}:user:${e}`),await b(`${w}${n}`),await I(Ne.parse({type:"status-update",reportHubId:n,recipientRawUserId:e,statusType:"star",value:l,clientTempId:f}),"setStarStatus"),R},ft=async(e,n,a,l,f)=>{let y=await t.select().top(1).from(B).where(X(h(B.hubId,n),h(B.userId,e))),R;if(y.length>0?y[0].isRead!==l?R=(await t.update(B).set({isRead:l,updatedAt:new Date,updatedBy:String(e)}).output().where(X(h(B.hubId,n),h(B.userId,e))))[0]:R=y[0]:R=(await t.insert(B).output().values({hubId:n,userId:e,isRead:l,isStarred:!1,createdAt:new Date,createdBy:String(e),updatedAt:new Date,updatedBy:String(e)}))[0],a){let _=ne(a);_&&(m.invalidate(`daily-report:date:${_}:user:${e}`),await b(`${g}${_}`))}return m.invalidate(`daily-report:detail:${n}`),m.invalidate(`daily-report:detail:${n}:user:${e}`),await b(`${w}${n}`),await I(Ne.parse({type:"status-update",reportHubId:n,recipientRawUserId:e,statusType:"read",value:l,clientTempId:f}),"setReadStatus"),R},gt=async(e,n,a,l,f)=>{let[y]=await t.select({sourceType:d.sourceType}).top(1).from(d).where(h(d.id,n));if(y&&y.sourceType.toLowerCase()!=="internal")throw new Error("Comments are restricted for external daily report sources");let[R]=await t.insert(u).output().values({hubId:n,userId:e,body:a,createdAt:new Date,createdBy:String(e),updatedAt:new Date,updatedBy:String(e)}),[_]=await t.select({displayName:i.displayName}).top(1).from(i).where(h(i.id,e)),z=_?.displayName??"Unknown";if(l){let $=ne(l);$&&(m.invalidatePrefix(`daily-report:date:${$}:user:`),await b(`${g}${$}`))}m.invalidate(`daily-report:detail:${n}`),m.invalidatePrefix(`daily-report:detail:${n}:user:`),await b(`${w}${n}`);let J={id:R.id,userId:c(R.userId),userName:z,content:R.body,createdAt:A(R.createdAt,"YYYY-MM-DD HH:mm:ss")??"",isMine:!0};return await I(Oe.parse({type:"comment-add",reportHubId:n,comment:J,clientTempId:f}),"addComment"),J},bt=async(e,n)=>await e.select().top(1).from(u).where(h(u.id,n)),Rt=async(e,n,a,l,f)=>{let y=await bt(t,a);if(y.length===0)throw new Error("Not Found");if(y[0].userId!==e)throw new Error("Unauthorized");if(await t.delete(u).where(h(u.id,a)),l){let R=ne(l);R&&(m.invalidatePrefix(`daily-report:date:${R}:user:`),await b(`${g}${R}`))}m.invalidate(`daily-report:detail:${n}`),m.invalidatePrefix(`daily-report:detail:${n}:user:`),await b(`${w}${n}`),await I(ze.parse({type:"comment-delete",reportHubId:n,commentId:a,clientTempId:f}),"deleteComment")},Dt=async(e,n,a)=>{ee.info("createDailyReport called",{userId:e,businessDate:n});let[l]=await t.select({displayName:i.displayName}).from(i).where(h(i.id,e)),f=l?.displayName??c(e);try{let y=await t.transaction(async z=>{ee.info("Starting transaction");let J=`internal-temp-${Date.now()}-${Math.random()}`,[$]=await z.insert(d).output().values({sourceType:"Internal",sourceId:J,businessDate:new Date(n),userId:e,title:"(\u7121\u984C)",createdAt:new Date,createdBy:String(e),updatedAt:new Date,updatedBy:String(e)});await z.update(d).set({sourceId:String($.id)}).where(h(d.id,$.id)),ee.info("Hub created",$),await z.insert(L).values({hubId:$.id,body:"",createdAt:new Date,createdBy:String(e),updatedAt:new Date,updatedBy:String(e)}),ee.info("Internal created"),ee.info("Calling getDraftLabelId");let ae=await we();ee.info("draftLabelId",ae);let me=[];return ae&&(await z.insert(j).values({hubId:$.id,labelId:ae,createdAt:new Date,createdBy:String(e)}),me.push({id:ae,name:Me,color:null})),ee.info("Label assigned"),{reportHubId:$.id,date:n,createdAt:A($.createdAt,"YYYY-MM-DD HH:mm:ss"),author:f,userId:c(e),sourceType:"Internal",employeeName:f,updatedBy:c(e),updatedAt:A($.updatedAt,"YYYY-MM-DD HH:mm:ss"),category:null,creationCategory:null,visitTimeFrom:null,visitTimeTo:null,customerName:null,interviewers:[],subject:$.title,content:"",comments:[],isRead:!0,isStarred:!1,labels:me,commentItems:[],attachments:[]}}),R=ne(n);R&&m.invalidate(`daily-report:date:${R}:user:${e}`),m.invalidatePrefix(k),m.invalidate(`${Ae}:user:${e}`),await b(U),R&&await b(`${g}${R}`);let _=await Ce(y.reportHubId,e,{forceRefresh:!0});if(_){let z=await he(),J=_.labels.some($=>z.includes($.id));await I(Fe.parse({type:"report-create",reportHubId:_.reportHubId,report:_,clientTempId:a,recipientRawUserId:J?e:void 0}),"createDailyReport")}return _??y}catch(y){throw ee.error("Error in createDailyReport",y),y}},ve=async(e,n)=>await e.select().top(1).from(d).where(h(d.id,n)),qe=async(e,n,a)=>{await e.update(d).set(a).where(h(d.id,n))},Ge=async(e,n,a)=>{await e.update(L).set(a).where(h(L.hubId,n))},Xe=async(e,n,a)=>{await e.delete(j).where(X(h(j.hubId,n),h(j.labelId,a)))};return{streamKey:H,streamMaxLen:v,clearCache:async()=>{m.clearAll(),te.clear(),ie=null,O&&await b(U),ee.info("[DailyReportService] Server-side DB/SQL caches cleared successfully.")},invalidateHubReadCaches:async(e,n)=>{m.invalidatePrefix(k);for(let a of new Set(e))m.invalidatePrefix(`daily-report:detail:${a}:user:`),await b(`${w}${a}`);for(let a of new Set(n)){let l=ne(a);l&&await b(`${g}${l}`)}},getUserIdByExternalId:se,getDailyReportIdsByExternalId:ye,getDailyReportsByBusinessDate:_e,getDailyReportsByBusinessDateByExternalId:pt,getDailyReportDetailById:Ce,getDailyReportDetailByIdByExternalId:yt,getDraftLabelId:we,getAttachmentsByHubIds:ke,getAttachmentForUser:ut,markAttachmentMissing:ct,markAttachmentPresent:mt,setStarStatus:ht,setReadStatus:ft,addComment:gt,deleteComment:Rt,createDailyReport:Dt,updateDailyReport:async(e,n,a,l)=>{let f=await ve(t,e);if(!f.length||f[0].deletedAt)throw new Error("Not Found");if(f[0].userId!==n)throw new Error("Unauthorized");await t.transaction(async R=>{a.title!==void 0&&await qe(R,e,{title:a.title,updatedAt:new Date,updatedBy:String(n)}),a.content!==void 0&&await Ge(R,e,{body:a.content,updatedAt:new Date,updatedBy:String(n)})}),m.invalidatePrefix(`daily-report:detail:${e}:user:`),m.invalidatePrefix(k);let y=await Ce(e,n,{forceRefresh:!0});if(y){let R=await he(),_=y.labels.some(z=>R.includes(z.id));await I(Ke.parse({type:"report-update",reportHubId:y.reportHubId,report:y,clientTempId:l,recipientRawUserId:_?n:void 0}),"updateDailyReport")}},publishDailyReport:async(e,n,a)=>{let l=await ve(t,e);if(!l.length)throw new Error("Not Found");if(l[0].userId!==n)throw new Error("Unauthorized");let f=await we();if(!f)return null;if(await Xe(t,e,f),m.invalidatePrefix(`daily-report:detail:${e}:user:`),m.invalidatePrefix(k),m.invalidatePrefix(Ae),await b(U),l[0].businessDate){let _=ne(A(l[0].businessDate,"YYYY-MM-DD"));_&&await b(`${g}${_}`)}let y=ne(A(l[0].businessDate,"YYYY-MM-DD"));y&&m.invalidate(`daily-report:date:${y}:user:${n}`);let R=await Ce(e,n,{forceRefresh:!0});return R&&await I(je.parse({type:"report-publish",reportHubId:R.reportHubId,report:R,clientTempId:a}),"publishDailyReport"),R??null},deleteDailyReport:async(e,n,a)=>{let l=await ve(t,e);if(!l.length||l[0].deletedAt)throw new Error("Not Found");if(l[0].userId!==n)throw new Error("Unauthorized");if(await qe(t,e,{deletedAt:new Date,deletedBy:String(n),updatedAt:new Date,updatedBy:String(n)}),m.invalidatePrefix(`daily-report:detail:${e}:user:`),m.invalidatePrefix(k),m.invalidatePrefix(Ae),await b(U),l[0].businessDate){let f=ne(A(l[0].businessDate,"YYYY-MM-DD"));f&&await b(`${g}${f}`)}await I(Je.parse({type:"report-delete",reportHubId:e,clientTempId:a}),"deleteDailyReport")},findDailyReportHubById:ve,updateDailyReportHub:qe,updateDailyReportInternal:Ge,deleteDailyReportLabel:Xe}}import{and as Gt,eq as st}from"drizzle-orm";function Xt(r){let t=[],o=r&&r.length>0?r:[{key:"internal",name:"Internal",description:"Internal daily reports"},{key:"external",name:"External",description:"External daily reports"}];for(let i of o){let c=`daily_report_${i.key.toLowerCase().replace(/[^a-z0-9_]/g,"_")}`;t.push({resourceKey:c,name:i.name,description:i.description??`${i.name} daily report access`}),i.includeCommentResource!==!1&&t.push({resourceKey:`${c}_comment`,name:`${i.name} Comment`,description:`${i.name} daily report comment access`})}return t}async function xr(r,t,o){let i=r,p=t.TMResource,c=o.appKey,x=o.actor??"system:daily-report",m=new Date,O=o.resources??Xt(o.sources);for(let d of O){let L=await i.select().from(p).where(Gt(st(p.appKey,c),st(p.resourceKey,d.resourceKey)));(!L||L.length===0)&&await i.insert(p).values({appKey:c,resourceKey:d.resourceKey,name:d.name,description:d.description??null,createdAt:m,createdBy:x,updatedAt:m,updatedBy:x})}}var Wt=oe(1,"[DailyReportExternalSource]"),Pr=(r,t,o,i=Wt)=>{if(!r)return[];try{let p=JSON.parse(r);return Array.isArray(p)?p.map(o).filter(c=>c!==null):(i.warn(`Unexpected ${t} format: not an array`),[])}catch(p){return i.warn(`Failed to parse ${t}:`,p),[]}};import{desc as it,sql as Vt}from"drizzle-orm";import{bigint as W,bit as lt,date as Zt,datetime2 as Z,foreignKey as ue,index as be,int as er,mssqlSchema as tr,nvarchar as P,primaryKey as De,uniqueIndex as rr}from"drizzle-orm/mssql-core";function qr(r,t){let o=tr(r),i=t.userTable,p=o.table("DailyReportHub",{id:W("id",{mode:"number"}).identity().notNull(),sourceType:P("source_type",{length:20}).notNull(),sourceId:P("source_id",{length:100}).notNull(),sourceIdNum:W("source_id_num",{mode:"number"}).generatedAlwaysAs(Vt`TRY_CAST(source_id AS BIGINT)`),businessDate:Zt("business_date"),userId:W("user_id",{mode:"number"}),title:P("title",{length:200}),summary:P("summary",{length:"max"}),createdAt:Z("created_at").notNull(),createdBy:P("created_by",{length:50}).notNull(),updatedAt:Z("updated_at").notNull(),updatedBy:P("updated_by",{length:50}).notNull(),deletedAt:Z("deleted_at"),deletedBy:P("deleted_by",{length:50})},u=>[De({name:`${r}_DailyReportHub_pk`,columns:[u.id]}),ue({name:`${r}_DailyReportHub_user_id_fk`,columns:[u.userId],foreignColumns:[i.id]}),be(`${r}_DailyReportHub_business_date_index`).on(u.businessDate),be(`${r}_DailyReportHub_updated_at_index`).on(u.updatedAt),be(`${r}_DailyReportHub_source_index`).on(u.sourceType,u.sourceId),be(`${r}_DailyReportHub_user_id_index`).on(u.userId),be(`${r}_DailyReportHub_business_date_id_index`).on(it(u.businessDate),it(u.id)),be(`${r}_DailyReportHub_deleted_at_index`).on(u.deletedAt)]),c=o.table("DailyReportInternal",{hubId:W("hub_id",{mode:"number"}).notNull(),body:P("body",{length:"max"}),metadata:P("metadata",{length:"max"}),createdAt:Z("created_at").notNull(),createdBy:P("created_by",{length:50}).notNull(),updatedAt:Z("updated_at").notNull(),updatedBy:P("updated_by",{length:50}).notNull()},u=>[De({name:`${r}_DailyReportInternal_pk`,columns:[u.hubId]}),ue({name:`${r}_DailyReportInternal_hub_id_fk`,columns:[u.hubId],foreignColumns:[p.id]})]),x=o.table("DailyReportComment",{id:W("id",{mode:"number"}).identity().notNull(),hubId:W("hub_id",{mode:"number"}).notNull(),userId:W("user_id",{mode:"number"}).notNull(),body:P("body",{length:"max"}).notNull(),createdAt:Z("created_at").notNull(),createdBy:P("created_by",{length:50}).notNull(),updatedAt:Z("updated_at").notNull(),updatedBy:P("updated_by",{length:50}).notNull()},u=>[De({name:`${r}_DailyReportComment_pk`,columns:[u.id]}),ue({name:`${r}_DailyReportComment_hub_id_fk`,columns:[u.hubId],foreignColumns:[p.id]}),ue({name:`${r}_DailyReportComment_user_id_fk`,columns:[u.userId],foreignColumns:[i.id]}),be(`${r}_DailyReportComment_hub_id_index`).on(u.hubId)]),m=o.table("DailyReportLabel",{id:W("id",{mode:"number"}).identity().notNull(),userId:W("user_id",{mode:"number"}),name:P("name",{length:50}).notNull(),color:P("color",{length:20}),sortOrder:er("sort_order"),createdAt:Z("created_at").notNull(),createdBy:P("created_by",{length:50}).notNull(),updatedAt:Z("updated_at").notNull(),updatedBy:P("updated_by",{length:50}).notNull()},u=>[De({name:`${r}_DailyReportLabel_pk`,columns:[u.id]}),ue({name:`${r}_DailyReportLabel_user_id_fk`,columns:[u.userId],foreignColumns:[i.id]})]),O=o.table("DailyReportHub_Label",{hubId:W("hub_id",{mode:"number"}).notNull(),labelId:W("label_id",{mode:"number"}).notNull(),createdAt:Z("created_at").notNull(),createdBy:P("created_by",{length:50}).notNull()},u=>[De({name:`${r}_DailyReportHub_Label_pk`,columns:[u.hubId,u.labelId]}),ue({name:`${r}_DailyReportHub_Label_hub_id_fk`,columns:[u.hubId],foreignColumns:[p.id]}),ue({name:`${r}_DailyReportHub_Label_label_id_fk`,columns:[u.labelId],foreignColumns:[m.id]})]),d=o.table("DailyReportUserStatus",{hubId:W("hub_id",{mode:"number"}).notNull(),userId:W("user_id",{mode:"number"}).notNull(),isRead:lt("is_read").default(!1).notNull(),isStarred:lt("is_starred").default(!1).notNull(),createdAt:Z("created_at").notNull(),createdBy:P("created_by",{length:50}).notNull(),updatedAt:Z("updated_at").notNull(),updatedBy:P("updated_by",{length:50}).notNull()},u=>[De({name:`${r}_DailyReportUserStatus_pk`,columns:[u.hubId,u.userId]}),ue({name:`${r}_DailyReportUserStatus_hub_id_fk`,columns:[u.hubId],foreignColumns:[p.id]}),ue({name:`${r}_DailyReportUserStatus_user_id_fk`,columns:[u.userId],foreignColumns:[i.id]})]),L=o.table("DailyReportAttachment",{id:W("id",{mode:"number"}).identity().notNull(),hubId:W("hub_id",{mode:"number"}).notNull(),uploadedBy:W("uploaded_by",{mode:"number"}),sourceKey:P("source_key",{length:200}).notNull(),fileName:P("file_name",{length:255}).notNull(),filePath:P("file_path",{length:"max"}).notNull(),fileType:P("file_type",{length:100}),fileSize:W("file_size",{mode:"number"}),objectCheckedAt:Z("object_checked_at"),objectMissingAt:Z("object_missing_at"),createdAt:Z("created_at").notNull(),createdBy:P("created_by",{length:50}).notNull(),updatedAt:Z("updated_at").notNull(),updatedBy:P("updated_by",{length:50}).notNull()},u=>[De({name:`${r}_DailyReportAttachment_pk`,columns:[u.id]}),ue({name:`${r}_DailyReportAttachment_hub_id_fk`,columns:[u.hubId],foreignColumns:[p.id]}),ue({name:`${r}_DailyReportAttachment_uploaded_by_fk`,columns:[u.uploadedBy],foreignColumns:[i.id]}),rr(`${r}_DailyReportAttachment_source_unique`).on(u.hubId,u.sourceKey),be(`${r}_DailyReportAttachment_hub_id_index`).on(u.hubId,u.createdAt)]);return{hub:p,internal:c,comment:x,attachment:L,label:m,hubLabel:O,userStatus:d}}function jr(r){let t=We(r.redis),o=new Ee({defaultTtlMs:r.cacheDefaultTtlMs??6e4},t),i=ot({...r,cache:o,epochs:t}),p=new Le({redis:r.redis,streamKey:i.streamKey,cache:o,logger:r.logger}),c=nt({authenticate:r.authenticate,service:i,encodeUserId:r.encodeUserId,attachmentIdCodec:r.attachmentIdCodec,readAttachment:r.readAttachment,attachmentMaxBytes:r.attachmentMaxBytes,attachmentRateLimitPerMinute:r.attachmentRateLimitPerMinute,attachmentConcurrency:r.attachmentConcurrency,redis:r.redis,sseReader:p,streamKey:i.streamKey,loginRedirectPath:r.loginRedirectPath,logger:r.logger});return{service:i,cache:o,epochs:t,sseReader:p,streamKey:i.streamKey,...c}}export{Le as DailyReportSseReader,Ee as SqlResultCache,Ot as asciiFallbackFileName,nt as createDailyReportHandlers,jr as createDailyReportServer,ot as createDailyReportService,We as createEpochStore,Xt as defineDailyReportAuthzResources,qr as defineDailyReportSchema,Yt as encodeRfc8187,Qe as generateETag,$e as isStreamIdLte,de as jsonResponseWithETag,rt as sanitizeMediaType,xr as seedDailyReportAuthzResources,Pr as transformJsonArray};
11
+ ${_}`:_;C.enqueue(v.encode(V))}if(A)try{let _=await p?.getClient();if(_){let V=await _.xRange(x,A,"+",{COUNT:1e3});for(let se of V)se.id!==A&&(Ue(se.id,Q)||(Q=se.id,g(se.id,se.message)))}}catch(_){l.error(`[SSE:${E}] catch-up xRange error:`,_)}},cancel(){I()}});return new Response(q,{headers:{"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive"}})}},attachment:{loader:async({request:M,params:P})=>{let k=null,E=(b,A,v)=>{let C=new Headers({"Content-Type":"application/json"});return v!==void 0&&C.set("Retry-After",String(v)),k&&C.append("Set-Cookie",k),new Response(JSON.stringify({error:{message:A}}),{status:b,headers:C})};try{let{user:b,cookie:A}=await e(M,{failureRedirect:null});if(k=A??null,!b)return E(401,"Unauthorized");if(M.method!=="GET"&&M.method!=="HEAD")return E(405,"Method not allowed");let v=t.attachmentIdCodec,C=t.readAttachment;if(!(v&&C))return E(404,"Attachment not found");let R=P.token??"",w=v.decode(R);if(w===null||!Number.isSafeInteger(w)||w<=0)return E(400,"Invalid attachment token");let S=await a.getUserIdByExternalId(b.id);if(!S)return E(403,"Forbidden");if(!K(S,Date.now()))return $.error(`429 attachment=${w} viewer=${S} reason=rate_limit`),E(429,"Too many requests",Jt);let I=await a.getAttachmentForUser(w,S);if(!I)return $.error(`404 attachment=${w} viewer=${S} reason=not_visible`),E(404,"Attachment not found");if(I.fileSize!=null&&I.fileSize>m)return $.error(`413 attachment=${w} viewer=${S} reason=db_size`),E(413,"Attachment too large");if(!J.tryAcquire())return $.error(`503 attachment=${w} viewer=${S} reason=concurrency`),E(503,"Too many concurrent downloads",jt);let g=!1,q=()=>{g||(g=!0,J.release())},O=!1;try{let Q=M.method==="HEAD",_=await C(I.filePath,{maxBytes:m,head:Q,principal:o(S)});if(!_.ok){let ue=Zt(_.reason);return _.reason==="not_found"&&a.markAttachmentMissing(w).catch(fe=>{$.error(`markAttachmentMissing failed attachment=${w} message=${fe instanceof Error?fe.message:"unknown"}`)}),$.error(`${ue} attachment=${w} viewer=${S} reason=${_.reason} code=${_.code??"-"}`),E(ue,er[ue]??"Attachment storage unavailable")}let V=it(I.fileType)??it(_.contentType)??"application/octet-stream",se=Gt.has(V),le=new URL(M.url).searchParams.get("download")==="1",oe=se?V:"application/octet-stream",Ce=se&&!le?"inline":"attachment",we=_.size??_.bytes.byteLength,me=new Headers({"Content-Type":oe,"Content-Length":String(we),"Content-Disposition":`${Ce}; filename="${Vt(I.fileName)}"; filename*=UTF-8''${Wt(I.fileName)}`,"Cache-Control":"private, no-store","X-Content-Type-Options":"nosniff","Content-Security-Policy":"default-src 'none'; sandbox","X-Frame-Options":"SAMEORIGIN"});return k&&me.append("Set-Cookie",k),a.markAttachmentPresent(w).catch(ue=>{$.error(`markAttachmentPresent failed attachment=${w} message=${ue instanceof Error?ue.message:"unknown"}`)}),$.info(`200 attachment=${w} viewer=${S} type=${oe} disposition=${Ce} bytes=${we}`),Q?new Response(null,{status:200,headers:me}):(O=!0,new Response(nr(_.bytes,q,zt),{status:200,headers:me}))}finally{O||q()}}catch(b){if(b instanceof Response)throw b;return $.error(`500 attachment=? viewer=? reason=unexpected message=${b instanceof Error?b.message:"unknown"}`),E(500,"Attachment request failed")}}}}}import{aliasedTable as Me,and as j,asc as Ye,desc as Ie,eq as y,getColumns as ar,inArray as he,isNull as ae,or as ke,sql as dt}from"drizzle-orm";var Re=t=>ar(t);function ct(t){let{db:e,tables:a,userTable:o,resolveUserId:p,encodeUserId:d,redis:x,cache:c,epochs:L}=t,{hub:l,internal:$,comment:m,label:K,hubLabel:J,userStatus:Y}=a,Ae=t.externalSources??[],Ee=t.draftLabelNames&&t.draftLabelNames.length>0?t.draftLabelNames:[t.draftLabelName??"\u4E0B\u66F8\u304D"],xe=Ee[0]??"\u4E0B\u66F8\u304D",ee=t.logger??ie(1,"[DailyReportService]"),_e="daily-report:ids",M=t.idsTtlMs??18e4,P="daily-report:ids:epoch",k="daily-report:business-date:",E=t.businessDateTtlMs??3e5,b="daily-report:date-epoch:",A="daily-report:detail-epoch:",v=t.streamKey??"daily-report:sse-stream",C=t.streamMaxLen??1e4,R=r=>L.incrementEpoch(r),w=async(r,n)=>{let s=await x?.getClient();if(!s){ee.warn(`[SSE] Redis client unavailable (${n})`);return}let u=Date.now();try{await s.xAdd(v,"*",{data:JSON.stringify(r)},{TRIM:{strategy:"MAXLEN",strategyModifier:"~",threshold:C}});let h=Date.now()-u;h>1e3&&ee.warn(`[SSE] Slow publish (${n}): ${h}ms`)}catch(h){ee.error(`[SSE] Redis publish failed (${n}):`,h)}},S=(r,n)=>{if(!r)return null;let s=r instanceof Date?r:new Date(r);if(Number.isNaN(s.getTime()))return typeof r=="string"?r:null;let u=n==="YYYY-MM-DD HH:mm:ss"?{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"}:{year:"numeric",month:"2-digit",day:"2-digit"};return(n==="YYYY-MM-DD HH:mm:ss"?s.toLocaleString("ja-JP",u):s.toLocaleDateString("ja-JP",u)).replace(/\//g,"-")},I=r=>r==null||r===""?null:/^\d+$/.test(r)?d(Number(r)):r,g=()=>{let r={};for(let n of Ae)r[`ext_${n.sourceType}`]=Re(n.table);return r},q=r=>{let n=r;for(let s of Ae)n=n.leftJoin(s.table,j(y(l.sourceType,s.sourceType),y(l.sourceIdNum,s.idColumn)));return n},O=(r,n)=>{let{hub:s,internal:u}=r,h=s.summary,f=[],D=null,T=null,F=null,G=null,U=null,ne=null,pe=[],ge=Ae.find(N=>N.sourceType===s.sourceType),Se=ge?r[`ext_${ge.sourceType}`]:void 0;if(ge&&Se){let N=ge.mapRow(Se);N.content!==void 0&&(h=N.content),N.employeeName!==void 0&&(ne=N.employeeName),N.category!==void 0&&(D=N.category),N.creationCategory!==void 0&&(T=N.creationCategory),N.visitTimeFrom!==void 0&&(F=N.visitTimeFrom),N.visitTimeTo!==void 0&&(G=N.visitTimeTo),N.customerName!==void 0&&(U=N.customerName),N.interviewers!==void 0&&(f=N.interviewers),N.comments!==void 0&&(pe=N.comments)}else s.sourceType==="Internal"&&u&&(h=u.body);let ye=(r.labels??[]).map(N=>({id:N.id,name:N.name,color:N.color})),te=r.comments??[],z=T?Ee.includes(T):!1,X=me?.[0]??null;return z&&X&&!ye.some(N=>N.id===X)&&ye.push({id:X,name:xe,color:null}),{reportHubId:s.id,date:S(s.businessDate,"YYYY-MM-DD"),author:r.creatorName??I(s.createdBy)??"",userId:s.userId?d(s.userId):"",sourceType:s.sourceType??"Internal",createdAt:S(s.createdAt,"YYYY-MM-DD HH:mm:ss"),updatedAt:S(s.updatedAt,"YYYY-MM-DD HH:mm:ss"),updatedBy:I(s.updatedBy),employeeName:ne??r.creatorName??I(s.createdBy),category:D,creationCategory:T,visitTimeFrom:F,visitTimeTo:G,customerName:U,interviewers:f,subject:s.title,content:h,comments:pe,isRead:r.isRead??!1,isStarred:r.isStarred??!1,labels:ye,commentItems:te.map(N=>({...N,userId:d(N.userId),isMine:n?N.userId===n:!1})),attachments:r.attachments}},Q=async r=>{let n=Me(J,"draft_label"),s=await fe();return(await e.select({reportHubId:l.id,businessDate:l.businessDate,sourceType:l.sourceType}).from(l).leftJoin(n,j(y(n.hubId,l.id),he(n.labelId,s))).where(j(ae(l.deletedAt),ke(y(l.userId,r),ae(l.userId),ae(n.hubId)))).orderBy(Ie(l.businessDate),Ie(l.id))).map(h=>({...h,businessDate:S(h.businessDate,"YYYY-MM-DD")}))},_=async(r,n)=>{let s=await oe(r);if(!s)return[];let u=Me(J,"draft_label"),h=await fe();return(await e.select({reportHubId:l.id,businessDate:l.businessDate,sourceType:l.sourceType}).top(n).from(l).leftJoin(u,j(y(u.hubId,l.id),he(u.labelId,h))).where(j(ae(l.deletedAt),ke(y(l.userId,s),ae(l.userId),ae(u.hubId)))).orderBy(Ie(l.businessDate),Ie(l.id))).map(D=>({...D,businessDate:S(D.businessDate,"YYYY-MM-DD")}))},V=async r=>{let n=await q(e.select({hub:Re(l),internal:Re($),creatorName:o.displayName,...g()}).from(l)).leftJoin($,y(l.id,$.hubId)).leftJoin(o,y(l.userId,o.id)).where(j(y(l.businessDate,dt`${r}`),ae(l.deletedAt))).orderBy(Ie(l.id)),s=await ve(n.map(u=>u.hub.id));return n.map(u=>O({...u,attachments:s.get(u.hub.id)??[]}))},se=r=>`${k}${r}`,le=new Map,oe=async r=>{if(!t.disableUserIdCache&&le.has(r))return le.get(r)??null;let n=await p(r);return n!==null&&!t.disableUserIdCache&&le.set(r,n),n},Ce=async(r,{forceRefresh:n=!1,snapshot:s=!1,ttlMsOverride:u}={})=>{let h=await oe(r);if(!h)return[];let f=`${_e}:user:${h}`;return c.getOrFetch({cacheKey:f,fetcher:async()=>await Q(h),forceRefresh:n,snapshot:s,ttlMsOverride:u??M,epochKey:P})},we=(r,{forceRefresh:n=!1,snapshot:s=!1,ttlMsOverride:u}={})=>{let h=re(r);return h?c.getOrFetch({cacheKey:se(h),fetcher:()=>V(h),forceRefresh:n,snapshot:s,ttlMsOverride:u??E}):Promise.resolve([])},me=null,ue=async()=>(me!==null||(me=(await e.select({id:K.id}).from(K).where(he(K.name,Ee)).orderBy(K.id)).map(n=>n.id)),me),fe=async()=>{let r=await ue();return r.length>0?r:[-1]},qe=async()=>{let r=await ue();if(r.length===0)return null;let[n]=await e.select({id:K.id}).top(1).from(K).where(y(K.name,xe));return n?.id??r[0]},ht=(r,n)=>n!=null?"absent":r!=null?"present":"unknown",ve=async r=>{let n=new Map,s=a.attachment,u=t.attachmentIdCodec;if(r.length===0||!s||!u)return n;let h=await e.select({id:s.id,hubId:s.hubId,fileName:s.fileName,fileType:s.fileType,fileSize:s.fileSize,createdAt:s.createdAt,objectCheckedAt:s.objectCheckedAt,objectMissingAt:s.objectMissingAt}).from(s).where(he(s.hubId,r)).orderBy(Ye(s.createdAt),Ye(s.id));for(let f of h){let D={id:u.encode(Number(f.id)),fileName:String(f.fileName),fileType:f.fileType?String(f.fileType):null,fileSize:f.fileSize!=null?Number(f.fileSize):null,createdAt:S(f.createdAt,"YYYY-MM-DD HH:mm:ss"),state:ht(f.objectCheckedAt,f.objectMissingAt)},T=n.get(Number(f.hubId));T?T.push(D):n.set(Number(f.hubId),[D])}return n},ft=async(r,n)=>{let s=a.attachment;if(!s)return null;let u=await fe(),h=Me(J,"draft_label");return(await e.select({fileName:s.fileName,filePath:s.filePath,fileType:s.fileType,fileSize:s.fileSize}).top(1).from(s).innerJoin(l,y(l.id,s.hubId)).leftJoin(h,j(y(h.hubId,l.id),he(h.labelId,u))).where(j(y(s.id,r),ae(l.deletedAt),ke(y(l.userId,n),ae(h.hubId)))))[0]??null},gt=async r=>{let n=a.attachment;if(!n)return;let s=new Date;try{await e.update(n).set({objectCheckedAt:s,objectMissingAt:s,updatedAt:s}).where(y(n.id,r))}catch(u){ee.error("markAttachmentMissing failed",u)}},bt=async r=>{let n=a.attachment;if(!n)return;let s=new Date;try{await e.update(n).set({objectCheckedAt:s,objectMissingAt:null,updatedAt:s}).where(y(n.id,r))}catch(u){ee.error("markAttachmentPresent failed",u)}},Rt=async(r,n,{forceRefresh:s=!1,snapshot:u=!1,ttlMsOverride:h}={})=>{let f=await oe(n);if(!f)return[];let D=re(r);if(!D)return[];let T=`daily-report:date:${D}:user:${f}`;return c.getOrFetch({cacheKey:T,fetcher:async()=>{let F=await fe(),G=Me(J,"draft_label"),U=await q(e.select({hub:Re(l),internal:Re($),isRead:Y.isRead,isStarred:Y.isStarred,creatorName:o.displayName,...g()}).from(l)).leftJoin($,y(l.id,$.hubId)).leftJoin(Y,j(y(l.id,Y.hubId),y(Y.userId,f))).leftJoin(G,j(y(G.hubId,l.id),he(G.labelId,F))).leftJoin(o,y(l.userId,o.id)).where(j(y(l.businessDate,dt`${D}`),ae(l.deletedAt),ke(y(l.userId,f),ae(G.hubId)))).orderBy(Ie(l.id));if(U.length===0)return[];let ne=U.map(z=>z.hub.id),pe=await e.select({hubId:J.hubId,id:K.id,name:K.name,color:K.color}).from(J).innerJoin(K,y(J.labelId,K.id)).where(he(J.hubId,ne)),ge=await e.select({hubId:m.hubId,id:m.id,body:m.body,createdAt:m.createdAt,userId:m.userId,userName:o.displayName}).from(m).leftJoin(o,y(m.userId,o.id)).where(he(m.hubId,ne)).orderBy(Ye(m.createdAt)),Se=await ve(ne),ye=new Map,te=new Map;for(let z of pe){let X=ye.get(z.hubId);X||(X=[],ye.set(z.hubId,X)),X.push({id:z.id,name:z.name,color:z.color})}for(let z of ge){let X=te.get(z.hubId);X||(X=[],te.set(z.hubId,X)),X.push({id:z.id,content:z.body,createdAt:S(z.createdAt,"YYYY-MM-DD HH:mm:ss")||"",userId:z.userId,userName:z.userName||""})}return U.map(z=>{let X=z.hub.id;return O({...z,attachments:Se.get(X)??[],labels:ye.get(X)||[],comments:te.get(X)||[]},f)})},forceRefresh:s,snapshot:u,ttlMsOverride:h??E,epochKey:`${b}${D}`})},Te=async(r,n,{forceRefresh:s=!1,snapshot:u=!1,ttlMsOverride:h}={})=>{let f=`daily-report:detail:${r}:user:${n}`;return(await c.getOrFetch({cacheKey:f,fetcher:async()=>{let T=await fe(),F=Me(J,"draft_label"),G=await q(e.select({hub:Re(l),internal:Re($),isRead:Y.isRead,isStarred:Y.isStarred,creatorName:o.displayName,...g()}).from(l)).leftJoin($,y(l.id,$.hubId)).leftJoin(Y,j(y(l.id,Y.hubId),y(Y.userId,n))).leftJoin(F,j(y(F.hubId,l.id),he(F.labelId,T))).leftJoin(o,y(l.userId,o.id)).where(j(y(l.id,r),ae(l.deletedAt),ke(y(l.userId,n),ae(F.hubId))));if(G.length===0)return[];let U=G[0].hub.id,ne=await e.select({id:K.id,name:K.name,color:K.color}).from(J).innerJoin(K,y(J.labelId,K.id)).where(y(J.hubId,U)),pe=await e.select({id:m.id,body:m.body,createdAt:m.createdAt,userId:m.userId,userName:o.displayName}).from(m).leftJoin(o,y(m.userId,o.id)).where(y(m.hubId,U)).orderBy(Ye(m.createdAt)),ge=await ve([U]),Se=ne.map(te=>({id:te.id,name:te.name,color:te.color})),ye=pe.map(te=>({id:te.id,content:te.body,createdAt:S(te.createdAt,"YYYY-MM-DD HH:mm:ss")||"",userId:te.userId,userName:te.userName||""}));return[O({...G[0],attachments:ge.get(U)??[],labels:Se,comments:ye},n)]},forceRefresh:s,snapshot:u,ttlMsOverride:h??E,epochKey:`${A}${r}`}))[0]??null},Dt=async(r,n,s={})=>{let u=await oe(n);return u?Te(r,u,s):null},wt=async(r,n,s,u,h)=>{let f=await e.select().top(1).from(Y).where(j(y(Y.hubId,n),y(Y.userId,r))),D;if(f.length>0?f[0].isStarred!==u?D=(await e.update(Y).set({isStarred:u,updatedAt:new Date,updatedBy:String(r)}).output().where(j(y(Y.hubId,n),y(Y.userId,r))))[0]:D=f[0]:D=(await e.insert(Y).output().values({hubId:n,userId:r,isStarred:u,isRead:!1,createdAt:new Date,createdBy:String(r),updatedAt:new Date,updatedBy:String(r)}))[0],s){let T=re(s);T&&(c.invalidate(`daily-report:date:${T}:user:${r}`),await R(`${b}${T}`))}return c.invalidate(`daily-report:detail:${n}`),c.invalidate(`daily-report:detail:${n}:user:${r}`),await R(`${A}${n}`),await w($e.parse({type:"status-update",reportHubId:n,recipientRawUserId:r,statusType:"star",value:u,clientTempId:h}),"setStarStatus"),D},St=async(r,n,s,u,h)=>{let f=await e.select().top(1).from(Y).where(j(y(Y.hubId,n),y(Y.userId,r))),D;if(f.length>0?f[0].isRead!==u?D=(await e.update(Y).set({isRead:u,updatedAt:new Date,updatedBy:String(r)}).output().where(j(y(Y.hubId,n),y(Y.userId,r))))[0]:D=f[0]:D=(await e.insert(Y).output().values({hubId:n,userId:r,isRead:u,isStarred:!1,createdAt:new Date,createdBy:String(r),updatedAt:new Date,updatedBy:String(r)}))[0],s){let T=re(s);T&&(c.invalidate(`daily-report:date:${T}:user:${r}`),await R(`${b}${T}`))}return c.invalidate(`daily-report:detail:${n}`),c.invalidate(`daily-report:detail:${n}:user:${r}`),await R(`${A}${n}`),await w($e.parse({type:"status-update",reportHubId:n,recipientRawUserId:r,statusType:"read",value:u,clientTempId:h}),"setReadStatus"),D},It=async(r,n,s,u,h)=>{let[f]=await e.select({sourceType:l.sourceType}).top(1).from(l).where(y(l.id,n));if(f&&f.sourceType.toLowerCase()!=="internal")throw new Error("Comments are restricted for external daily report sources");let[D]=await e.insert(m).output().values({hubId:n,userId:r,body:s,createdAt:new Date,createdBy:String(r),updatedAt:new Date,updatedBy:String(r)}),[T]=await e.select({displayName:o.displayName}).top(1).from(o).where(y(o.id,r)),F=T?.displayName??"Unknown";if(u){let U=re(u);U&&(c.invalidatePrefix(`daily-report:date:${U}:user:`),await R(`${b}${U}`))}c.invalidate(`daily-report:detail:${n}`),c.invalidatePrefix(`daily-report:detail:${n}:user:`),await R(`${A}${n}`);let G={id:D.id,userId:d(D.userId),userName:F,content:D.body,createdAt:S(D.createdAt,"YYYY-MM-DD HH:mm:ss")??"",isMine:!0};return await w(ze.parse({type:"comment-add",reportHubId:n,comment:G,clientTempId:h}),"addComment"),G},At=async(r,n)=>await r.select().top(1).from(m).where(y(m.id,n)),_t=async(r,n,s,u,h)=>{let f=await At(e,s);if(f.length===0)throw new Error("Not Found");if(f[0].userId!==r)throw new Error("Unauthorized");if(await e.delete(m).where(y(m.id,s)),u){let D=re(u);D&&(c.invalidatePrefix(`daily-report:date:${D}:user:`),await R(`${b}${D}`))}c.invalidate(`daily-report:detail:${n}`),c.invalidatePrefix(`daily-report:detail:${n}:user:`),await R(`${A}${n}`),await w(Ke.parse({type:"comment-delete",reportHubId:n,commentId:s,clientTempId:h}),"deleteComment")},Ct=async(r,n,s)=>{ee.info("createDailyReport called",{userId:r,businessDate:n});let[u]=await e.select({displayName:o.displayName}).from(o).where(y(o.id,r)),h=u?.displayName??d(r);try{let f=await e.transaction(async F=>{ee.info("Starting transaction");let G=`internal-temp-${Date.now()}-${Math.random()}`,[U]=await F.insert(l).output().values({sourceType:"Internal",sourceId:G,businessDate:new Date(n),userId:r,title:"(\u7121\u984C)",createdAt:new Date,createdBy:String(r),updatedAt:new Date,updatedBy:String(r)});await F.update(l).set({sourceId:String(U.id)}).where(y(l.id,U.id)),ee.info("Hub created",U),await F.insert($).values({hubId:U.id,body:"",createdAt:new Date,createdBy:String(r),updatedAt:new Date,updatedBy:String(r)}),ee.info("Internal created"),ee.info("Calling getDraftLabelId");let ne=await qe();ee.info("draftLabelId",ne);let pe=[];return ne&&(await F.insert(J).values({hubId:U.id,labelId:ne,createdAt:new Date,createdBy:String(r)}),pe.push({id:ne,name:xe,color:null})),ee.info("Label assigned"),{reportHubId:U.id,date:n,createdAt:S(U.createdAt,"YYYY-MM-DD HH:mm:ss"),author:h,userId:d(r),sourceType:"Internal",employeeName:h,updatedBy:d(r),updatedAt:S(U.updatedAt,"YYYY-MM-DD HH:mm:ss"),category:null,creationCategory:null,visitTimeFrom:null,visitTimeTo:null,customerName:null,interviewers:[],subject:U.title,content:"",comments:[],isRead:!0,isStarred:!1,labels:pe,commentItems:[],attachments:[]}}),D=re(n);D&&c.invalidate(`daily-report:date:${D}:user:${r}`),c.invalidatePrefix(k),c.invalidate(`${_e}:user:${r}`),await R(P),D&&await R(`${b}${D}`);let T=await Te(f.reportHubId,r,{forceRefresh:!0});if(T){let F=await ue(),G=T.labels.some(U=>F.includes(U.id));await w(Je.parse({type:"report-create",reportHubId:T.reportHubId,report:T,clientTempId:s,recipientRawUserId:G?r:void 0}),"createDailyReport")}return T??f}catch(f){throw ee.error("Error in createDailyReport",f),f}},Ne=async(r,n)=>await r.select().top(1).from(l).where(y(l.id,n)),Oe=async(r,n,s)=>{await r.update(l).set(s).where(y(l.id,n))},We=async(r,n,s)=>{await r.update($).set(s).where(y($.hubId,n))},Ve=async(r,n,s)=>{await r.delete(J).where(j(y(J.hubId,n),y(J.labelId,s)))};return{streamKey:v,streamMaxLen:C,clearCache:async()=>{c.clearAll(),le.clear(),me=null,L&&await R(P),ee.info("[DailyReportService] Server-side DB/SQL caches cleared successfully.")},invalidateHubReadCaches:async(r,n)=>{c.invalidatePrefix(k);for(let s of new Set(r))c.invalidatePrefix(`daily-report:detail:${s}:user:`),await R(`${A}${s}`);for(let s of new Set(n)){let u=re(s);u&&await R(`${b}${u}`)}},getUserIdByExternalId:oe,getDailyReportIdsByExternalId:Ce,getDailyReportIdsFirstPageByExternalId:_,getDailyReportsByBusinessDate:we,getDailyReportsByBusinessDateByExternalId:Rt,getDailyReportDetailById:Te,getDailyReportDetailByIdByExternalId:Dt,getDraftLabelId:qe,getAttachmentsByHubIds:ve,getAttachmentForUser:ft,markAttachmentMissing:gt,markAttachmentPresent:bt,setStarStatus:wt,setReadStatus:St,addComment:It,deleteComment:_t,createDailyReport:Ct,updateDailyReport:async(r,n,s,u)=>{let h=await Ne(e,r);if(!h.length||h[0].deletedAt)throw new Error("Not Found");if(h[0].userId!==n)throw new Error("Unauthorized");await e.transaction(async D=>{s.title!==void 0&&await Oe(D,r,{title:s.title,updatedAt:new Date,updatedBy:String(n)}),s.content!==void 0&&await We(D,r,{body:s.content,updatedAt:new Date,updatedBy:String(n)})}),c.invalidatePrefix(`daily-report:detail:${r}:user:`),c.invalidatePrefix(k);let f=await Te(r,n,{forceRefresh:!0});if(f){let D=await ue(),T=f.labels.some(F=>D.includes(F.id));await w(je.parse({type:"report-update",reportHubId:f.reportHubId,report:f,clientTempId:u,recipientRawUserId:T?n:void 0}),"updateDailyReport")}},publishDailyReport:async(r,n,s)=>{let u=await Ne(e,r);if(!u.length)throw new Error("Not Found");if(u[0].userId!==n)throw new Error("Unauthorized");let h=await qe();if(!h)return null;if(await Ve(e,r,h),c.invalidatePrefix(`daily-report:detail:${r}:user:`),c.invalidatePrefix(k),c.invalidatePrefix(_e),await R(P),u[0].businessDate){let T=re(S(u[0].businessDate,"YYYY-MM-DD"));T&&await R(`${b}${T}`)}let f=re(S(u[0].businessDate,"YYYY-MM-DD"));f&&c.invalidate(`daily-report:date:${f}:user:${n}`);let D=await Te(r,n,{forceRefresh:!0});return D&&await w(Ge.parse({type:"report-publish",reportHubId:D.reportHubId,report:D,clientTempId:s}),"publishDailyReport"),D??null},deleteDailyReport:async(r,n,s)=>{let u=await Ne(e,r);if(!u.length||u[0].deletedAt)throw new Error("Not Found");if(u[0].userId!==n)throw new Error("Unauthorized");if(await Oe(e,r,{deletedAt:new Date,deletedBy:String(n),updatedAt:new Date,updatedBy:String(n)}),c.invalidatePrefix(`daily-report:detail:${r}:user:`),c.invalidatePrefix(k),c.invalidatePrefix(_e),await R(P),u[0].businessDate){let h=re(S(u[0].businessDate,"YYYY-MM-DD"));h&&await R(`${b}${h}`)}await w(Qe.parse({type:"report-delete",reportHubId:r,clientTempId:s}),"deleteDailyReport")},findDailyReportHubById:Ne,updateDailyReportHub:Oe,updateDailyReportInternal:We,deleteDailyReportLabel:Ve}}import{and as sr,eq as mt}from"drizzle-orm";function or(t){let e=[],a=t&&t.length>0?t:[{key:"internal",name:"Internal",description:"Internal daily reports"},{key:"external",name:"External",description:"External daily reports"}];for(let o of a){let d=`daily_report_${o.key.toLowerCase().replace(/[^a-z0-9_]/g,"_")}`;e.push({resourceKey:d,name:o.name,description:o.description??`${o.name} daily report access`}),o.includeCommentResource!==!1&&e.push({resourceKey:`${d}_comment`,name:`${o.name} Comment`,description:`${o.name} daily report comment access`})}return e}async function Kr(t,e,a){let o=t,p=e.TMResource,d=a.appKey,x=a.actor??"system:daily-report",c=new Date,L=a.resources??or(a.sources);for(let l of L){let $=await o.select().from(p).where(sr(mt(p.appKey,d),mt(p.resourceKey,l.resourceKey)));(!$||$.length===0)&&await o.insert(p).values({appKey:d,resourceKey:l.resourceKey,name:l.name,description:l.description??null,createdAt:c,createdBy:x,updatedAt:c,updatedBy:x})}}var ir=ie(1,"[DailyReportExternalSource]"),Gr=(t,e,a,o=ir)=>{if(!t)return[];try{let p=JSON.parse(t);return Array.isArray(p)?p.map(a).filter(d=>d!==null):(o.warn(`Unexpected ${e} format: not an array`),[])}catch(p){return o.warn(`Failed to parse ${e}:`,p),[]}};import{desc as pt,sql as lr}from"drizzle-orm";import{bigint as W,bit as yt,date as ur,datetime2 as Z,foreignKey as ce,index as be,int as dr,mssqlSchema as cr,nvarchar as H,primaryKey as De,uniqueIndex as mr}from"drizzle-orm/mssql-core";function Vr(t,e){let a=cr(t),o=e.userTable,p=a.table("DailyReportHub",{id:W("id",{mode:"number"}).identity().notNull(),sourceType:H("source_type",{length:20}).notNull(),sourceId:H("source_id",{length:100}).notNull(),sourceIdNum:W("source_id_num",{mode:"number"}).generatedAlwaysAs(lr`TRY_CAST(source_id AS BIGINT)`),businessDate:ur("business_date"),userId:W("user_id",{mode:"number"}),title:H("title",{length:200}),summary:H("summary",{length:"max"}),createdAt:Z("created_at").notNull(),createdBy:H("created_by",{length:50}).notNull(),updatedAt:Z("updated_at").notNull(),updatedBy:H("updated_by",{length:50}).notNull(),deletedAt:Z("deleted_at"),deletedBy:H("deleted_by",{length:50})},m=>[De({name:`${t}_DailyReportHub_pk`,columns:[m.id]}),ce({name:`${t}_DailyReportHub_user_id_fk`,columns:[m.userId],foreignColumns:[o.id]}),be(`${t}_DailyReportHub_business_date_index`).on(m.businessDate),be(`${t}_DailyReportHub_updated_at_index`).on(m.updatedAt),be(`${t}_DailyReportHub_source_index`).on(m.sourceType,m.sourceId),be(`${t}_DailyReportHub_user_id_index`).on(m.userId),be(`${t}_DailyReportHub_business_date_id_index`).on(pt(m.businessDate),pt(m.id)),be(`${t}_DailyReportHub_deleted_at_index`).on(m.deletedAt)]),d=a.table("DailyReportInternal",{hubId:W("hub_id",{mode:"number"}).notNull(),body:H("body",{length:"max"}),metadata:H("metadata",{length:"max"}),createdAt:Z("created_at").notNull(),createdBy:H("created_by",{length:50}).notNull(),updatedAt:Z("updated_at").notNull(),updatedBy:H("updated_by",{length:50}).notNull()},m=>[De({name:`${t}_DailyReportInternal_pk`,columns:[m.hubId]}),ce({name:`${t}_DailyReportInternal_hub_id_fk`,columns:[m.hubId],foreignColumns:[p.id]})]),x=a.table("DailyReportComment",{id:W("id",{mode:"number"}).identity().notNull(),hubId:W("hub_id",{mode:"number"}).notNull(),userId:W("user_id",{mode:"number"}).notNull(),body:H("body",{length:"max"}).notNull(),createdAt:Z("created_at").notNull(),createdBy:H("created_by",{length:50}).notNull(),updatedAt:Z("updated_at").notNull(),updatedBy:H("updated_by",{length:50}).notNull()},m=>[De({name:`${t}_DailyReportComment_pk`,columns:[m.id]}),ce({name:`${t}_DailyReportComment_hub_id_fk`,columns:[m.hubId],foreignColumns:[p.id]}),ce({name:`${t}_DailyReportComment_user_id_fk`,columns:[m.userId],foreignColumns:[o.id]}),be(`${t}_DailyReportComment_hub_id_index`).on(m.hubId)]),c=a.table("DailyReportLabel",{id:W("id",{mode:"number"}).identity().notNull(),userId:W("user_id",{mode:"number"}),name:H("name",{length:50}).notNull(),color:H("color",{length:20}),sortOrder:dr("sort_order"),createdAt:Z("created_at").notNull(),createdBy:H("created_by",{length:50}).notNull(),updatedAt:Z("updated_at").notNull(),updatedBy:H("updated_by",{length:50}).notNull()},m=>[De({name:`${t}_DailyReportLabel_pk`,columns:[m.id]}),ce({name:`${t}_DailyReportLabel_user_id_fk`,columns:[m.userId],foreignColumns:[o.id]})]),L=a.table("DailyReportHub_Label",{hubId:W("hub_id",{mode:"number"}).notNull(),labelId:W("label_id",{mode:"number"}).notNull(),createdAt:Z("created_at").notNull(),createdBy:H("created_by",{length:50}).notNull()},m=>[De({name:`${t}_DailyReportHub_Label_pk`,columns:[m.hubId,m.labelId]}),ce({name:`${t}_DailyReportHub_Label_hub_id_fk`,columns:[m.hubId],foreignColumns:[p.id]}),ce({name:`${t}_DailyReportHub_Label_label_id_fk`,columns:[m.labelId],foreignColumns:[c.id]})]),l=a.table("DailyReportUserStatus",{hubId:W("hub_id",{mode:"number"}).notNull(),userId:W("user_id",{mode:"number"}).notNull(),isRead:yt("is_read").default(!1).notNull(),isStarred:yt("is_starred").default(!1).notNull(),createdAt:Z("created_at").notNull(),createdBy:H("created_by",{length:50}).notNull(),updatedAt:Z("updated_at").notNull(),updatedBy:H("updated_by",{length:50}).notNull()},m=>[De({name:`${t}_DailyReportUserStatus_pk`,columns:[m.hubId,m.userId]}),ce({name:`${t}_DailyReportUserStatus_hub_id_fk`,columns:[m.hubId],foreignColumns:[p.id]}),ce({name:`${t}_DailyReportUserStatus_user_id_fk`,columns:[m.userId],foreignColumns:[o.id]})]),$=a.table("DailyReportAttachment",{id:W("id",{mode:"number"}).identity().notNull(),hubId:W("hub_id",{mode:"number"}).notNull(),uploadedBy:W("uploaded_by",{mode:"number"}),sourceKey:H("source_key",{length:200}).notNull(),fileName:H("file_name",{length:255}).notNull(),filePath:H("file_path",{length:"max"}).notNull(),fileType:H("file_type",{length:100}),fileSize:W("file_size",{mode:"number"}),objectCheckedAt:Z("object_checked_at"),objectMissingAt:Z("object_missing_at"),createdAt:Z("created_at").notNull(),createdBy:H("created_by",{length:50}).notNull(),updatedAt:Z("updated_at").notNull(),updatedBy:H("updated_by",{length:50}).notNull()},m=>[De({name:`${t}_DailyReportAttachment_pk`,columns:[m.id]}),ce({name:`${t}_DailyReportAttachment_hub_id_fk`,columns:[m.hubId],foreignColumns:[p.id]}),ce({name:`${t}_DailyReportAttachment_uploaded_by_fk`,columns:[m.uploadedBy],foreignColumns:[o.id]}),mr(`${t}_DailyReportAttachment_source_unique`).on(m.hubId,m.sourceKey),be(`${t}_DailyReportAttachment_hub_id_index`).on(m.hubId,m.createdAt)]);return{hub:p,internal:d,comment:x,attachment:$,label:c,hubLabel:L,userStatus:l}}function an(t){let e=Ze(t.redis),a=new Le({defaultTtlMs:t.cacheDefaultTtlMs??6e4},e),o=ct({...t,cache:a,epochs:e}),p=new Be({redis:t.redis,streamKey:o.streamKey,cache:a,logger:t.logger}),d=ut({authenticate:t.authenticate,service:o,encodeUserId:t.encodeUserId,attachmentIdCodec:t.attachmentIdCodec,readAttachment:t.readAttachment,attachmentMaxBytes:t.attachmentMaxBytes,attachmentRateLimitPerMinute:t.attachmentRateLimitPerMinute,attachmentConcurrency:t.attachmentConcurrency,redis:t.redis,sseReader:p,streamKey:o.streamKey,loginRedirectPath:t.loginRedirectPath,logger:t.logger});return{service:o,cache:a,epochs:e,sseReader:p,streamKey:o.streamKey,...d}}export{Be as DailyReportSseReader,Le as SqlResultCache,Vt as asciiFallbackFileName,ut as createDailyReportHandlers,an as createDailyReportServer,ct as createDailyReportService,Ze as createEpochStore,or as defineDailyReportAuthzResources,Vr as defineDailyReportSchema,Wt as encodeRfc8187,Xe as generateETag,Ue as isStreamIdLte,de as jsonResponseWithETag,it as sanitizeMediaType,Kr as seedDailyReportAuthzResources,Gr as transformJsonArray};
10
12
  //# sourceMappingURL=server.mjs.map