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