pushy-server 2026.4.2-140d7621 → 2026.4.5-7d4f400f

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/lib/dailyjob.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- import{a as W,b as M,d as A,f as K,i as k}from"./chunk-0z5yvyqg.js";import{B as F,C as h,E,H as J,K as I,L as S,ba as U}from"./chunk-jgwaa8g2.js";import"./chunk-nqzvntbw.js";import{la as j}from"./chunk-snjkfert.js";var rn=j(J(),1);var N=!1,q=Number(process.env.DIFF_RETENTION_DAYS||14),Z=Number(process.env.OSS_LIST_MAX_KEYS||1000),y=3,P=6,d=30,nn=30,tn=5,v=Math.max(1,Number(process.env.INACTIVE_CLEANUP_DAILY_MAIL_LIMIT||2000)),V=Math.max(1,Number(process.env.INACTIVE_USER_QUERY_LOOKBACK_DAYS||14)),x={pendingCleanupUsers:"inactive_cleanup:pending_users",skipScanUsers:"inactive_cleanup:skip_users"};async function en(n={}){try{let i=n.dryRun===!0,e=rn.default().subtract(181,"day").toDate();console.log(`Start cleaning audit_logs data before ${e.toISOString()} (181 days ago)`);let r=await h.audit_logs.count({where:{createdAt:{lt:e}}});if(console.log(`Found ${r} records to delete`),r===0)return console.log("No audit_logs data to clean"),{cutoffDate:e.toISOString(),countToDelete:r,deletedCount:0,skippedDeletion:!1};if(N||i){if(i)console.log("Dry-run mode: skip actual audit_logs deletion");else console.log("Development mode: skip actual deletion");return{cutoffDate:e.toISOString(),countToDelete:r,deletedCount:0,skippedDeletion:!0}}let t=await h.audit_logs.deleteMany({where:{createdAt:{lt:e}}});return console.log(`Successfully deleted ${t.count} audit_logs records`),{cutoffDate:e.toISOString(),countToDelete:r,deletedCount:t.count,skippedDeletion:!1}}catch(i){throw console.error("Error cleaning audit_logs:",i),i}}var D=j(J(),1);function On(){return D.default().subtract(y,"year").toDate()}function Rn(){return D.default().subtract(P,"month").toDate()}function on(){return D.default().subtract(nn,"day").toDate()}function An(){return D.default().subtract(tn,"year").toDate()}function kn(){return D.default().add(d,"day").valueOf()}function cn(n){return n.replace("@",'<span style="display:none"></span>@<span style="display:none"></span>')}function pn(n){return n instanceof Error?n.message:String(n)}function mn(n){return/quota/i.test(n)}function sn(){return`\u6E05\u7406\u901A\u77E5\u90AE\u4EF6\u5DF2\u8FBE\u5230\u672C\u6B21\u626B\u63CF\u4E0A\u9650 ${v} \u5C01`}function fn(n){return`\u6E05\u7406\u901A\u77E5\u90AE\u4EF6\u89E6\u53D1 quota\uFF0C\u5DF2\u505C\u6B62\u672C\u6B21\u626B\u63CF: ${n}`}function Tn(n){return D.default(n).format("YYYY-MM-DD")}function Nn(n){return D.default(n).format("YYYY-MM-DD HH:mm:ss")}function w({email:n,createdAt:i}){return{email:n,createdAt:i??"INVAL"}}async function z(n){if(n.length===0)return new Map;let i=An(),e=on(),r=await h.$queryRaw(F.sql`
3
+ import{a as y,b as F,d as A,f as Z,i as k}from"./chunk-0z5yvyqg.js";import{B as H,C as h,E as b,H as V,K as I,L as U,ba as v}from"./chunk-jgwaa8g2.js";import"./chunk-nqzvntbw.js";import{la as P}from"./chunk-snjkfert.js";var on=P(V(),1);var N=!1,d=Number(process.env.DIFF_RETENTION_DAYS||14),nn=Number(process.env.OSS_LIST_MAX_KEYS||1000),tn=3,B=6,rn=30,en=30,sn=5,M=Math.max(1,Number(process.env.INACTIVE_CLEANUP_DAILY_MAIL_LIMIT||2000)),z=Math.max(1,Number(process.env.INACTIVE_USER_QUERY_LOOKBACK_DAYS||14)),a={pendingCleanupUsers:"inactive_cleanup:pending_users",skipScanUsers:"inactive_cleanup:skip_users"};async function cn(n={}){try{let i=n.dryRun===!0,e=on.default().subtract(181,"day").toDate();console.log(`Start cleaning audit_logs data before ${e.toISOString()} (181 days ago)`);let r=await h.audit_logs.count({where:{createdAt:{lt:e}}});if(console.log(`Found ${r} records to delete`),r===0)return console.log("No audit_logs data to clean"),{cutoffDate:e.toISOString(),countToDelete:r,deletedCount:0,skippedDeletion:!1};if(N||i){if(i)console.log("Dry-run mode: skip actual audit_logs deletion");else console.log("Development mode: skip actual deletion");return{cutoffDate:e.toISOString(),countToDelete:r,deletedCount:0,skippedDeletion:!0}}let t=await h.audit_logs.deleteMany({where:{createdAt:{lt:e}}});return console.log(`Successfully deleted ${t.count} audit_logs records`),{cutoffDate:e.toISOString(),countToDelete:r,deletedCount:t.count,skippedDeletion:!1}}catch(i){throw console.error("Error cleaning audit_logs:",i),i}}var $=P(V(),1);function kn(){return $.default().subtract(tn,"year").toDate()}function Tn(){return $.default().subtract(B,"month").toDate()}function mn(){return $.default().subtract(en,"day").toDate()}function Nn(){return $.default().subtract(sn,"year").toDate()}function Ln(){return $.default().add(rn,"day").valueOf()}function fn(n){return n.replace("@",'<span style="display:none"></span>@<span style="display:none"></span>')}function un(n){return n instanceof Error?n.message:String(n)}function Cn(n){return/quota/i.test(n)}function pn(){return`\u6E05\u7406\u901A\u77E5\u90AE\u4EF6\u5DF2\u8FBE\u5230\u672C\u6B21\u626B\u63CF\u4E0A\u9650 ${M} \u5C01`}function gn(n){return`\u6E05\u7406\u901A\u77E5\u90AE\u4EF6\u89E6\u53D1 quota\uFF0C\u5DF2\u505C\u6B62\u672C\u6B21\u626B\u63CF: ${n}`}function wn(n){return $.default(n).format("YYYY-MM-DD")}function Sn(n){return $.default(n).format("YYYY-MM-DD HH:mm:ss")}function S({email:n,createdAt:i}){return{email:n,createdAt:i??"INVAL"}}async function G(n){if(n.length===0)return new Map;let i=Nn(),e=mn(),r=await h.$queryRaw(H.sql`
4
4
  SELECT
5
5
  id,
6
6
  NULLIF(CAST(createdAt AS CHAR(19)), '0000-00-00 00:00:00') AS createdAtText,
@@ -15,8 +15,8 @@ import{a as W,b as M,d as A,f as K,i as k}from"./chunk-0z5yvyqg.js";import{B as
15
15
  ELSE 0
16
16
  END AS olderThanUnverifiedCutoff
17
17
  FROM users
18
- WHERE id IN (${F.join(n)})
19
- `);return new Map(r.map((t)=>[t.id,{createdAtText:t.createdAtText,olderThanSilentPendingCutoff:Number(t.olderThanSilentPendingCutoff)>0,olderThanUnverifiedCutoff:Number(t.olderThanUnverifiedCutoff)>0}]))}async function H(n){let i=await E.zrange(n,0,-1,"WITHSCORES"),e=[];for(let r=0;r<i.length;r+=2){let t=Number(i[r]),c=Number(i[r+1]);if(!Number.isFinite(t)||!Number.isFinite(c))continue;e.push({userId:t,score:c})}return e}async function Ln(n){let i=n.filter(Boolean);if(i.length===0)return null;let e=E.pipeline(),r=[];for(let c=0;c<V;c++){let o=D.default().subtract(c,"day");r.push(o),e.zmscore(S.appCheckUpdate(o),i)}let t=await e.exec();for(let c=0;c<r.length;c++)if((t?.[c]?.[1]||[]).some((p)=>Number(p||0)>0))return r[c].endOf("day").toDate();return null}async function un({userId:n,appKeys:i}){let[e,r]=await Promise.all([h.audit_logs.findFirst({where:{userId:n,createdAt:{gte:Rn()}},orderBy:{createdAt:"desc"},select:{createdAt:!0}}),Ln(i)]),t=e?.createdAt??null,c=[];if(t)c.push(D.default(t).add(P,"month").toDate());if(r)c.push(D.default(r).add(V,"day").endOf("day").toDate());let o=c.length>0?new Date(Math.max(...c.map((p)=>p.getTime()))):null;return{latestAuditAt:t,latestQueryAt:r,skipUntil:o,cleanupEligible:!t&&!r}}async function wn({email:n,deleteAfterMs:i}){let e=A(n,"inactiveUsers.pending"),r=cn(e),t=D.default(i).format("YYYY-MM-DD");await k({to:e,subject:"React Native\u4E2D\u6587\u7F51: \u60A8\u7684 Pushy \u70ED\u66F4\u65B0\u8D26\u6237\u5373\u5C06\u88AB\u6E05\u7406",html:`
18
+ WHERE id IN (${H.join(n)})
19
+ `);return new Map(r.map((t)=>[t.id,{createdAtText:t.createdAtText,olderThanSilentPendingCutoff:Number(t.olderThanSilentPendingCutoff)>0,olderThanUnverifiedCutoff:Number(t.olderThanUnverifiedCutoff)>0}]))}async function X(n){let i=await b.zrange(n,0,-1,"WITHSCORES"),e=[];for(let r=0;r<i.length;r+=2){let t=Number(i[r]),c=Number(i[r+1]);if(!Number.isFinite(t)||!Number.isFinite(c))continue;e.push({userId:t,score:c})}return e}async function Un(n){let i=n.filter(Boolean);if(i.length===0)return null;let e=b.pipeline(),r=[];for(let c=0;c<z;c++){let o=$.default().subtract(c,"day");r.push(o),e.zmscore(U.appCheckUpdate(o),i)}let t=await e.exec();for(let c=0;c<r.length;c++)if((t?.[c]?.[1]||[]).some((p)=>Number(p||0)>0))return r[c].endOf("day").toDate();return null}async function hn({userId:n,appKeys:i}){let[e,r]=await Promise.all([h.audit_logs.findFirst({where:{userId:n,createdAt:{gte:Tn()}},orderBy:{createdAt:"desc"},select:{createdAt:!0}}),Un(i)]),t=e?.createdAt??null,c=[];if(t)c.push($.default(t).add(B,"month").toDate());if(r)c.push($.default(r).add(z,"day").endOf("day").toDate());let o=c.length>0?new Date(Math.max(...c.map((p)=>p.getTime()))):null;return{latestAuditAt:t,latestQueryAt:r,skipUntil:o,cleanupEligible:!t&&!r}}async function vn({email:n,deleteAfterMs:i}){let e=A(n,"inactiveUsers.pending"),r=fn(e),t=$.default(i).format("YYYY-MM-DD");await k({to:e,subject:"React Native\u4E2D\u6587\u7F51: \u60A8\u7684 Pushy \u70ED\u66F4\u65B0\u8D26\u6237\u5373\u5C06\u88AB\u6E05\u7406",html:`
20
20
  <p>
21
21
  <span style="font-size: 14px;">\u4EB2\u7231\u7684 <b>${r}</b>\uFF0C\u60A8\u597D\uFF01</span>
22
22
  </p>
@@ -37,7 +37,7 @@ import{a as W,b as M,d as A,f as K,i as k}from"./chunk-0z5yvyqg.js";import{B as
37
37
  <p style="margin-bottom: 0px; padding: 0px; font-size: 14px;">
38
38
  React Native\u4E2D\u6587\u7F51 Pushy\u70ED\u66F4\u65B0\u670D\u52A1
39
39
  </p>
40
- `},{throwOnError:!0,retryOnError:!1})}async function Sn({email:n,appCount:i}){let e=A(n,"inactiveUsers.deleted"),r=cn(e),t=D.default().format("YYYY-MM-DD HH:mm:ss");await k({to:e,subject:"React Native\u4E2D\u6587\u7F51: \u60A8\u7684 Pushy \u70ED\u66F4\u65B0\u8D26\u6237\u6570\u636E\u5DF2\u88AB\u6E05\u7406",html:`
40
+ `},{throwOnError:!0,retryOnError:!1})}async function jn({email:n,appCount:i}){let e=A(n,"inactiveUsers.deleted"),r=fn(e),t=$.default().format("YYYY-MM-DD HH:mm:ss");await k({to:e,subject:"React Native\u4E2D\u6587\u7F51: \u60A8\u7684 Pushy \u70ED\u66F4\u65B0\u8D26\u6237\u6570\u636E\u5DF2\u88AB\u6E05\u7406",html:`
41
41
  <p>
42
42
  <span style="font-size: 14px;">\u4EB2\u7231\u7684 <b>${r}</b>\uFF0C\u60A8\u597D\uFF01</span>
43
43
  </p>
@@ -55,8 +55,8 @@ import{a as W,b as M,d as A,f as K,i as k}from"./chunk-0z5yvyqg.js";import{B as
55
55
  <p style="margin-bottom: 0px; padding: 0px; font-size: 14px;">
56
56
  React Native\u4E2D\u6587\u7F51 Pushy\u70ED\u66F4\u65B0\u670D\u52A1
57
57
  </p>
58
- `},{throwOnError:!0,retryOnError:!1})}async function Un({userId:n,hashes:i,dryRun:e}){if(i.length===0)return;let[r,t]=await Promise.all([h.packages.findMany({where:{hash:{in:i},apps:{is:{userId:{not:n}}}},select:{hash:!0}}),h.versions.findMany({where:{hash:{in:i},apps:{is:{userId:{not:n}}}},select:{hash:!0}})]),c=new Set([...r.map((p)=>p.hash),...t.map((p)=>p.hash)]),o=i.filter((p)=>!c.has(p));if(N||o.length===0)return;if(e){console.log(`Dry-run: would delete ${o.length} OSS objects for inactive user ${n}`);return}for(let p=0;p<o.length;p+=1000){let C=o.slice(p,p+1000);if(C.length===0)continue;await U.deleteMulti(C)}}async function B(n,i={}){let e=i.dryRun===!0,r=i.notifyDeletedMail!==!1,t=await h.users.findUnique({where:{id:n},select:{id:!0,email:!0,apps:{select:{id:!0,appKey:!0,packages:{select:{hash:!0}},versions:{select:{hash:!0}}}},apiTokens:{select:{id:!0}}}}),o=(await z([n])).get(n);if(!t){if(e)console.log(`Dry-run: would remove missing inactive user ${n} from cleanup queues`);else await E.zrem(x.pendingCleanupUsers,String(n)),await E.zrem(x.skipScanUsers,String(n));return null}if(N)return console.log(`Development mode: skip deleting inactive user ${n}`),null;let p=Array.from(new Set(t.apps.flatMap((f)=>[...f.packages.map((u)=>u.hash),...f.versions.map((u)=>u.hash)]))),C=t.apiTokens.map((f)=>f.id);if(await Un({userId:n,hashes:p,dryRun:e}),e)return console.log(`Dry-run: would delete inactive user ${n}`),{email:t.email,createdAt:o?.createdAtText??null,appCount:t.apps.length};await h.$transaction(async(f)=>{if(C.length>0)await f.audit_logs.deleteMany({where:{apiTokenId:{in:C}}});if(await f.audit_logs.deleteMany({where:{userId:n}}),p.length>0)await f.tasks.deleteMany({where:{OR:[{fromHash:{in:p}},{toHash:{in:p}}]}});await f.users.delete({where:{id:n}})});let m;if(r)try{await Sn({email:t.email,appCount:t.apps.length})}catch(f){m=pn(f),console.error("Failed to send inactive cleanup deletion email",{email:t.email,userId:n,error:f})}let s=E.pipeline();s.del(`userId_${t.id}`),s.del(`userEmail_${t.email}`),s.del(`apps_${t.id}`),s.zrem(x.pendingCleanupUsers,String(t.id)),s.zrem(x.skipScanUsers,String(t.id));for(let f of t.apps)if(s.del(`appId_${f.id}`),f.appKey)s.del(`appKey_${f.appKey}`);return await s.exec(),{email:t.email,createdAt:o?.createdAtText??null,appCount:t.apps.length,mailError:m}}async function vn({userId:n,skipUntil:i,dryRun:e}){if(e){console.log(`Dry-run: would move user ${n} back to skip-scan until ${i.toISOString()}`);return}let r=E.pipeline();r.zrem(x.pendingCleanupUsers,String(n)),r.zadd(x.skipScanUsers,i.getTime(),String(n)),await r.exec()}async function jn(n={}){let i=n.dryRun===!0,e=await H(x.pendingCleanupUsers),r=await z(e.map((g)=>g.userId)),t=0,c=0,o=0,p=0,C=!1,m=null,s=[],f=[],u=[],a=[];for(let g of e){let l=await h.users.findUnique({where:{id:g.userId},select:{id:!0,email:!0,status:!0,apps:{select:{appKey:!0}}}});if(!l||M(l.email)){if(i)console.log(`Dry-run: would remove user ${g.userId} from cleanup queues`);else await E.zrem(x.pendingCleanupUsers,String(g.userId)),await E.zrem(x.skipScanUsers,String(g.userId));p++;continue}let Y=r.get(l.id);if(l.status==="unverified"&&Y?.olderThanUnverifiedCutoff){let $=await B(l.id,{...n,notifyDeletedMail:!1});if($){if(c++,u.push({...w($),appCount:$.appCount}),$.mailError)a.push(`deleted:${$.email} - ${$.mailError}`)}continue}let b=l.apps.map(($)=>$.appKey).filter(($)=>Boolean($)),R=await un({userId:l.id,appKeys:b});if(!R.cleanupEligible&&R.skipUntil){await vn({userId:l.id,skipUntil:R.skipUntil,dryRun:i}),o++,s.push({...w({email:l.email,createdAt:Y?.createdAtText??null}),skipUntil:Nn(R.skipUntil)});continue}if(Date.now()<g.score)continue;let O=await B(l.id,n);if(O){if(t++,f.push({...w(O),appCount:O.appCount}),O.mailError){if(a.push(`deleted:${O.email} - ${O.mailError}`),!C&&mn(O.mailError))C=!0,m=fn(O.mailError)}}}return{deletedPendingUsers:t,deletedUnverifiedUsers:c,restoredUsers:o,removedUsers:p,stopScanning:C,scanStopReason:m,restoredEntries:s,deletedPendingEntries:f,deletedUnverifiedEntries:u,mailErrors:a}}async function Jn(n={}){let i=n.dryRun===!0,e=Date.now(),r=await H(x.skipScanUsers),t=r.filter((o)=>o.score<=e).map((o)=>String(o.userId));if(!i&&t.length>0)await E.zremrangebyscore(x.skipScanUsers,0,e);let c=r.filter((o)=>o.score>e).map((o)=>o.userId);return{expiredCount:t.length,activeSkipUserIds:c}}async function Mn({excludedUserIds:n,dryRun:i}){let e=await h.users.findMany({where:{status:"unverified",createdAt:{lt:on()},...n.length>0?{id:{notIn:n}}:{}},select:{id:!0,email:!0}}),r=0,t=[],c=[];for(let o of e){if(M(o.email))continue;c.push(o.id);let p=await B(o.id,{dryRun:i,notifyDeletedMail:!1});if(p)r++,t.push({...w(p),appCount:p.appCount})}return{deletedUnverifiedUsers:r,deletedUnverifiedEntries:t,deletedUserIds:c}}async function Pn({excludedUserIds:n,dryRun:i}){let e=await h.users.findMany({where:{createdAt:{lt:On()},...n.length>0?{id:{notIn:n}}:{}},select:{id:!0,email:!0,apps:{select:{appKey:!0}}}}),r=await z(e.map((s)=>s.id)),t=0,c=0,o=!1,p=null,C=[],m=[];for(let s of e){if(M(s.email))continue;let f=s.apps.map((Y)=>Y.appKey).filter((Y)=>Boolean(Y)),u=await un({userId:s.id,appKeys:f});if(!u.cleanupEligible){if(u.skipUntil)if(i)console.log(`Dry-run: would keep user ${s.id} in skip-scan until ${u.skipUntil.toISOString()}`);else await E.zadd(x.skipScanUsers,u.skipUntil.getTime(),String(s.id));continue}let a=kn(),g=r.get(s.id),l=g?.olderThanSilentPendingCutoff===!0;if(i)if(l)console.log(`Dry-run: would queue >5y inactive user ${s.email} without warning email`);else console.log(`Dry-run: would send inactive cleanup warning email to ${s.email}`);else if(l)console.log(`Queue >5y inactive user ${s.email} without warning email`);else{if(t>=v){o=!0,p=sn(),console.warn(p);break}try{await wn({email:s.email,deleteAfterMs:a})}catch(Y){let b=pn(Y);if(console.error("Failed to send inactive cleanup warning email",{email:s.email,userId:s.id,error:Y}),m.push(`pending:${s.email} - ${b}`),mn(b)){o=!0,p=fn(b),console.warn(p);break}continue}}if(!i)await E.zadd(x.pendingCleanupUsers,a,String(s.id));if(l)c++;else if(t++,!i&&t>=v)o=!0,p=sn();if(C.push({...w({email:s.email,createdAt:g?.createdAtText??null}),deleteAfter:Tn(a)}),o){console.warn(p);break}}return{pendingUsers:t+c,pendingNotifiedUsers:t,pendingSilentUsers:c,scanStopped:o,scanStopReason:p,pendingEntries:C,mailErrors:m}}async function Cn(n={}){let i=await H(x.pendingCleanupUsers),{deletedPendingUsers:e,deletedUnverifiedUsers:r,restoredUsers:t,removedUsers:c,stopScanning:o,scanStopReason:p,restoredEntries:C,deletedPendingEntries:m,deletedUnverifiedEntries:s,mailErrors:f}=await jn(n),{expiredCount:u,activeSkipUserIds:a}=await Jn(n),{deletedUnverifiedUsers:g,deletedUnverifiedEntries:l,deletedUserIds:Y}=await Mn({excludedUserIds:i.map(($)=>$.userId),dryRun:n.dryRun}),b=o?{pendingUsers:0,pendingNotifiedUsers:0,pendingSilentUsers:0,scanStopped:!0,scanStopReason:p,pendingEntries:[],mailErrors:[]}:await Pn({excludedUserIds:[...i.map(($)=>$.userId),...a,...Y],dryRun:n.dryRun}),R=r+g,O=e+R;return console.log(`Inactive cleanup processed. pending=${b.pendingUsers}, notified=${b.pendingNotifiedUsers}, silent=${b.pendingSilentUsers}, deletedPending=${e}, deletedUnverified=${R}, restored=${t}, removed=${c}, skipExpired=${u}, scanStopped=${b.scanStopped}`),{pendingUsers:b.pendingUsers,pendingNotifiedUsers:b.pendingNotifiedUsers,pendingSilentUsers:b.pendingSilentUsers,restoredUsers:t,deletedUsers:O,deletedPendingUsers:e,deletedUnverifiedUsers:R,removedUsers:c,expiredSkipUsers:u,scanStopped:b.scanStopped,scanStopReason:b.scanStopReason,pendingEntries:b.pendingEntries,restoredEntries:C,deletedPendingEntries:m,deletedUnverifiedEntries:[...s,...l],mailErrors:[...f,...b.mailErrors]}}function Vn(n){return/(\.(p|ph|h)?diff)$|(\.empty)$/.test(n)}async function Fn(n){if(N||n.length===0)return;await U.deleteMulti(n).catch((i)=>{console.error(`Failed to delete ${n.length} objects: ${n.join(`
59
- `)}`,i)})}async function Bn(n){if(n.length===0)return new Set;let[i,e]=await Promise.all([h.versions.findMany({where:{hash:{in:n}},select:{hash:!0}}),h.packages.findMany({where:{hash:{in:n}},select:{hash:!0}})]);return new Set([...i.map((r)=>r.hash),...e.map((r)=>r.hash)])}async function zn(n,i,e){let t=Date.now()-q*24*3600*1000,c=[],o=[];for(let{name:s,lastModified:f}of n){if(Vn(s)){if(new Date(f).getTime()<=t)i.deletedCount++,i.diffCount++,console.log(`${i.deletedCount} diff/${i.diffCount} ${s} ${new Date(f).toISOString()}`),c.push(s);continue}o.push({name:s,lastModified:f})}let p=await Bn(o.map((s)=>s.name)),C=o.reduce((s,f)=>{if(!p.has(f.name))i.deletedCount++,i.pvCount++,console.log(`${i.deletedCount} pv/${i.pvCount} ${f.name} ${new Date(f.lastModified).toISOString()}`),s.push(f.name);return s},[]),m=c.concat(C);if(e.dryRun){if(m.length>0)console.log(`Dry-run mode: skip deleting ${m.length} OSS objects`);return}await Fn(m)}async function gn(n){let{objects:i=[],nextContinuationToken:e}=await U.listV2({"continuation-token":n,"max-keys":Z},{});if(e){let r=await gn(e);return[...i,...r]}return i}async function hn(n={}){let i={diffCount:0,pvCount:0,totalCount:0,deletedCount:0},e=(await gn()).filter((r)=>r.name!=="test");if(i.totalCount=e.length,e.length>0){for(let r=0;r<e.length;r+=1000)await zn(e.slice(r,r+1000),i,n);console.log(`total: ${i.totalCount}, diff: ${i.diffCount}, p/v: ${i.pvCount}`)}return console.log("OSS cleanup completed"),i}var L=j(J(),1);var Q="https://pushy.reactnative.cn/pricing.html";async function Hn(n){let i=A(n.email,"tierCheck.expired"),e=i.replace("@",'<span style="display:none"></span>@<span style="display:none"></span>');await k({to:i,subject:"React Native\u4E2D\u6587\u7F51: \u60A8\u7684Pushy\u70ED\u66F4\u65B0\u670D\u52A1\u5DF2\u7ECF\u5230\u671F",html:`
58
+ `},{throwOnError:!0,retryOnError:!1})}async function Jn({userId:n,hashes:i,dryRun:e}){if(i.length===0)return;let[r,t]=await Promise.all([h.packages.findMany({where:{hash:{in:i},apps:{is:{userId:{not:n}}}},select:{hash:!0}}),h.versions.findMany({where:{hash:{in:i},apps:{is:{userId:{not:n}}}},select:{hash:!0}})]),c=new Set([...r.map((p)=>p.hash),...t.map((p)=>p.hash)]),o=i.filter((p)=>!c.has(p));if(N||o.length===0)return;if(e){console.log(`Dry-run: would delete ${o.length} OSS objects for inactive user ${n}`);return}for(let p=0;p<o.length;p+=1000){let g=o.slice(p,p+1000);if(g.length===0)continue;await v.deleteMulti(g)}}async function Q(n,i={}){let e=i.dryRun===!0,r=i.notifyDeletedMail!==!1,t=await h.users.findUnique({where:{id:n},select:{id:!0,email:!0,apps:{select:{id:!0,appKey:!0,packages:{select:{hash:!0}},versions:{select:{hash:!0}}}},apiTokens:{select:{id:!0}}}}),o=(await G([n])).get(n);if(!t){if(e)console.log(`Dry-run: would remove missing inactive user ${n} from cleanup queues`);else await b.zrem(a.pendingCleanupUsers,String(n)),await b.zrem(a.skipScanUsers,String(n));return null}if(N)return console.log(`Development mode: skip deleting inactive user ${n}`),null;let p=Array.from(new Set(t.apps.flatMap((f)=>[...f.packages.map((u)=>u.hash),...f.versions.map((u)=>u.hash)]))),g=t.apiTokens.map((f)=>f.id);if(await Jn({userId:n,hashes:p,dryRun:e}),e)return console.log(`Dry-run: would delete inactive user ${n}`),{email:t.email,createdAt:o?.createdAtText??null,appCount:t.apps.length};await h.$transaction(async(f)=>{if(g.length>0)await f.audit_logs.deleteMany({where:{apiTokenId:{in:g}}});if(await f.audit_logs.deleteMany({where:{userId:n}}),p.length>0)await f.tasks.deleteMany({where:{OR:[{fromHash:{in:p}},{toHash:{in:p}}]}});await f.users.delete({where:{id:n}})});let m;if(r)try{await jn({email:t.email,appCount:t.apps.length})}catch(f){m=un(f),console.error("Failed to send inactive cleanup deletion email",{email:t.email,userId:n,error:f})}let s=b.pipeline();s.del(`userId_${t.id}`),s.del(`userEmail_${t.email}`),s.del(`apps_${t.id}`),s.zrem(a.pendingCleanupUsers,String(t.id)),s.zrem(a.skipScanUsers,String(t.id));for(let f of t.apps)if(s.del(`appId_${f.id}`),f.appKey)s.del(`appKey_${f.appKey}`);return await s.exec(),{email:t.email,createdAt:o?.createdAtText??null,appCount:t.apps.length,mailError:m}}async function Mn({userId:n,skipUntil:i,dryRun:e}){if(e){console.log(`Dry-run: would move user ${n} back to skip-scan until ${i.toISOString()}`);return}let r=b.pipeline();r.zrem(a.pendingCleanupUsers,String(n)),r.zadd(a.skipScanUsers,i.getTime(),String(n)),await r.exec()}async function Pn(n={}){let i=n.dryRun===!0,e=await X(a.pendingCleanupUsers),r=e.map((C)=>C.userId),[t,c]=await Promise.all([G(r),r.length>0?h.users.findMany({where:{id:{in:r}},select:{id:!0,email:!0,status:!0,apps:{select:{appKey:!0}}}}):[]]),o=new Map(c.map((C)=>[C.id,C])),p=0,g=0,m=0,s=0,f=!1,u=null,l=[],x=[],Y=[],D=[];for(let C of e){let E=o.get(C.userId);if(!E||F(E.email)){if(i)console.log(`Dry-run: would remove user ${C.userId} from cleanup queues`);else await b.zrem(a.pendingCleanupUsers,String(C.userId)),await b.zrem(a.skipScanUsers,String(C.userId));s++;continue}let j=t.get(E.id);if(E.status==="unverified"&&j?.olderThanUnverifiedCutoff){let O=await Q(E.id,{...n,notifyDeletedMail:!1});if(O){if(g++,Y.push({...S(O),appCount:O.appCount}),O.mailError)D.push(`deleted:${O.email} - ${O.mailError}`)}continue}let w=E.apps.map((O)=>O.appKey).filter((O)=>Boolean(O)),J=await hn({userId:E.id,appKeys:w});if(!J.cleanupEligible&&J.skipUntil){await Mn({userId:E.id,skipUntil:J.skipUntil,dryRun:i}),m++,l.push({...S({email:E.email,createdAt:j?.createdAtText??null}),skipUntil:Sn(J.skipUntil)});continue}if(Date.now()<C.score)continue;let R=await Q(E.id,n);if(R){if(p++,x.push({...S(R),appCount:R.appCount}),R.mailError){if(D.push(`deleted:${R.email} - ${R.mailError}`),!f&&Cn(R.mailError))f=!0,u=gn(R.mailError)}}}return{deletedPendingUsers:p,deletedUnverifiedUsers:g,restoredUsers:m,removedUsers:s,stopScanning:f,scanStopReason:u,restoredEntries:l,deletedPendingEntries:x,deletedUnverifiedEntries:Y,mailErrors:D}}async function Vn(n={}){let i=n.dryRun===!0,e=Date.now(),r=await X(a.skipScanUsers),t=r.filter((o)=>o.score<=e).map((o)=>String(o.userId));if(!i&&t.length>0)await b.zremrangebyscore(a.skipScanUsers,0,e);let c=r.filter((o)=>o.score>e).map((o)=>o.userId);return{expiredCount:t.length,activeSkipUserIds:c}}async function Fn({excludedUserIds:n,dryRun:i}){let e=await h.users.findMany({where:{status:"unverified",createdAt:{lt:mn()},...n.length>0?{id:{notIn:n}}:{}},select:{id:!0,email:!0}}),r=0,t=[],c=[];for(let o of e){if(F(o.email))continue;c.push(o.id);let p=await Q(o.id,{dryRun:i,notifyDeletedMail:!1});if(p)r++,t.push({...S(p),appCount:p.appCount})}return{deletedUnverifiedUsers:r,deletedUnverifiedEntries:t,deletedUserIds:c}}async function Bn({excludedUserIds:n,dryRun:i}){let e=await h.users.findMany({where:{createdAt:{lt:kn()},...n.length>0?{id:{notIn:n}}:{}},select:{id:!0,email:!0,apps:{select:{appKey:!0}}}}),r=await G(e.map((s)=>s.id)),t=0,c=0,o=!1,p=null,g=[],m=[];for(let s of e){if(F(s.email))continue;let f=s.apps.map((D)=>D.appKey).filter((D)=>Boolean(D)),u=await hn({userId:s.id,appKeys:f});if(!u.cleanupEligible){if(u.skipUntil)if(i)console.log(`Dry-run: would keep user ${s.id} in skip-scan until ${u.skipUntil.toISOString()}`);else await b.zadd(a.skipScanUsers,u.skipUntil.getTime(),String(s.id));continue}let l=Ln(),x=r.get(s.id),Y=x?.olderThanSilentPendingCutoff===!0;if(i)if(Y)console.log(`Dry-run: would queue >5y inactive user ${s.email} without warning email`);else console.log(`Dry-run: would send inactive cleanup warning email to ${s.email}`);else if(Y)console.log(`Queue >5y inactive user ${s.email} without warning email`);else{if(t>=M){o=!0,p=pn(),console.warn(p);break}try{await vn({email:s.email,deleteAfterMs:l})}catch(D){let C=un(D);if(console.error("Failed to send inactive cleanup warning email",{email:s.email,userId:s.id,error:D}),m.push(`pending:${s.email} - ${C}`),Cn(C)){o=!0,p=gn(C),console.warn(p);break}continue}}if(!i)await b.zadd(a.pendingCleanupUsers,l,String(s.id));if(Y)c++;else if(t++,!i&&t>=M)o=!0,p=pn();if(g.push({...S({email:s.email,createdAt:x?.createdAtText??null}),deleteAfter:wn(l)}),o){console.warn(p);break}}return{pendingUsers:t+c,pendingNotifiedUsers:t,pendingSilentUsers:c,scanStopped:o,scanStopReason:p,pendingEntries:g,mailErrors:m}}async function ln(n={}){let i=await X(a.pendingCleanupUsers),{deletedPendingUsers:e,deletedUnverifiedUsers:r,restoredUsers:t,removedUsers:c,stopScanning:o,scanStopReason:p,restoredEntries:g,deletedPendingEntries:m,deletedUnverifiedEntries:s,mailErrors:f}=await Pn(n),{expiredCount:u,activeSkipUserIds:l}=await Vn(n),{deletedUnverifiedUsers:x,deletedUnverifiedEntries:Y,deletedUserIds:D}=await Fn({excludedUserIds:i.map((w)=>w.userId),dryRun:n.dryRun}),C=o?{pendingUsers:0,pendingNotifiedUsers:0,pendingSilentUsers:0,scanStopped:!0,scanStopReason:p,pendingEntries:[],mailErrors:[]}:await Bn({excludedUserIds:[...i.map((w)=>w.userId),...l,...D],dryRun:n.dryRun}),E=r+x,j=e+E;return console.log(`Inactive cleanup processed. pending=${C.pendingUsers}, notified=${C.pendingNotifiedUsers}, silent=${C.pendingSilentUsers}, deletedPending=${e}, deletedUnverified=${E}, restored=${t}, removed=${c}, skipExpired=${u}, scanStopped=${C.scanStopped}`),{pendingUsers:C.pendingUsers,pendingNotifiedUsers:C.pendingNotifiedUsers,pendingSilentUsers:C.pendingSilentUsers,restoredUsers:t,deletedUsers:j,deletedPendingUsers:e,deletedUnverifiedUsers:E,removedUsers:c,expiredSkipUsers:u,scanStopped:C.scanStopped,scanStopReason:C.scanStopReason,pendingEntries:C.pendingEntries,restoredEntries:g,deletedPendingEntries:m,deletedUnverifiedEntries:[...s,...Y],mailErrors:[...f,...C.mailErrors]}}function zn(n){return/(\.(p|ph|h)?diff)$|(\.empty)$/.test(n)}async function Hn(n){if(N||n.length===0)return;await v.deleteMulti(n).catch((i)=>{console.error(`Failed to delete ${n.length} objects: ${n.join(`
59
+ `)}`,i)})}async function Qn(n){if(n.length===0)return new Set;let[i,e]=await Promise.all([h.versions.findMany({where:{hash:{in:n}},select:{hash:!0}}),h.packages.findMany({where:{hash:{in:n}},select:{hash:!0}})]);return new Set([...i.map((r)=>r.hash),...e.map((r)=>r.hash)])}async function Gn(n,i,e){let t=Date.now()-d*24*3600*1000,c=[],o=[];for(let{name:s,lastModified:f}of n){if(zn(s)){if(new Date(f).getTime()<=t)i.deletedCount++,i.diffCount++,console.log(`${i.deletedCount} diff/${i.diffCount} ${s} ${new Date(f).toISOString()}`),c.push(s);continue}o.push({name:s,lastModified:f})}let p=await Qn(o.map((s)=>s.name)),g=o.reduce((s,f)=>{if(!p.has(f.name))i.deletedCount++,i.pvCount++,console.log(`${i.deletedCount} pv/${i.pvCount} ${f.name} ${new Date(f.lastModified).toISOString()}`),s.push(f.name);return s},[]),m=c.concat(g);if(e.dryRun){if(m.length>0)console.log(`Dry-run mode: skip deleting ${m.length} OSS objects`);return}await Hn(m)}async function an(n){let{objects:i=[],nextContinuationToken:e}=await v.listV2({"continuation-token":n,"max-keys":nn},{});if(e){let r=await an(e);return[...i,...r]}return i}async function bn(n={}){let i={diffCount:0,pvCount:0,totalCount:0,deletedCount:0},e=(await an()).filter((r)=>r.name!=="test");if(i.totalCount=e.length,e.length>0){for(let r=0;r<e.length;r+=1000)await Gn(e.slice(r,r+1000),i,n);console.log(`total: ${i.totalCount}, diff: ${i.diffCount}, p/v: ${i.pvCount}`)}return console.log("OSS cleanup completed"),i}var L=P(V(),1);var K="https://pushy.reactnative.cn/pricing.html";async function Xn(n){let i=A(n.email,"tierCheck.expired"),e=i.replace("@",'<span style="display:none"></span>@<span style="display:none"></span>');await k({to:i,subject:"React Native\u4E2D\u6587\u7F51: \u60A8\u7684Pushy\u70ED\u66F4\u65B0\u670D\u52A1\u5DF2\u7ECF\u5230\u671F",html:`
60
60
  <p>
61
61
  <span style="font-size: 14px;">\u4EB2\u7231\u7684 <b>${e}</b></span><span style="font-size: 14px;">\uFF0C\u60A8\u597D\uFF01</span>
62
62
  </p>
@@ -67,11 +67,11 @@ import{a as W,b as M,d as A,f as K,i as k}from"./chunk-0z5yvyqg.js";import{B as
67
67
  <span style="font-size: 14px;">&nbsp;&nbsp;&nbsp;&nbsp;\u611F\u8C22\u60A8\u4F7F\u7528React Native\u4E2D\u6587\u7F51\u63D0\u4F9B\u7684\u70ED\u66F4\u65B0\u670D\u52A1\uFF0C\u60A8\u73B0\u6709\u7684\u4EA7\u54C1\u5DF2\u7ECF\u8FC7\u671F\uFF1A</span>
68
68
  </p>
69
69
  <ul>
70
- <li>\u4EA7\u54C1\u540D\u79F0\uFF1A${K[n.tier].title}</li>
70
+ <li>\u4EA7\u54C1\u540D\u79F0\uFF1A${Z[n.tier].title}</li>
71
71
  <li>\u6709\u6548\u671F\u81F3\uFF1A${L.default(n.tierExpiresAt).format("YYYY-MM-DD")}</li>
72
72
  </ul>
73
73
  <p>
74
- <span style="font-size: 14px;">&nbsp;&nbsp;&nbsp;&nbsp;\u4E3A\u4FDD\u8BC1\u60A8\u7684\u5E94\u7528\u70ED\u66F4\u65B0\u670D\u52A1\u80FD\u591F\u6301\u7EED\u7A33\u5B9A\u8FD0\u884C\uFF0C\u8BF7\u60A8\u5C3D\u5FEB<a href="${Q}">\u70B9\u51FB\u6B64\u5904\u7EED\u8D39</a>\u3002</span>
74
+ <span style="font-size: 14px;">&nbsp;&nbsp;&nbsp;&nbsp;\u4E3A\u4FDD\u8BC1\u60A8\u7684\u5E94\u7528\u70ED\u66F4\u65B0\u670D\u52A1\u80FD\u591F\u6301\u7EED\u7A33\u5B9A\u8FD0\u884C\uFF0C\u8BF7\u60A8\u5C3D\u5FEB<a href="${K}">\u70B9\u51FB\u6B64\u5904\u7EED\u8D39</a>\u3002</span>
75
75
  </p>
76
76
  <hr />
77
77
  <p style="margin-top: 0px; padding: 0px 0px 20px; line-height: inherit; border: 0px; list-style: none; color: rgb(51, 51, 51); font-family: \u5FAE\u8F6F\u96C5\u9ED1; font-size: 14px; white-space: normal;">
@@ -81,7 +81,7 @@ import{a as W,b as M,d as A,f as K,i as k}from"./chunk-0z5yvyqg.js";import{B as
81
81
  <p style="margin-bottom: 0px; padding: 0px; font-size: 14px;">
82
82
  React Native\u4E2D\u6587\u7F51 Pushy\u70ED\u66F4\u65B0\u670D\u52A1
83
83
  </p>
84
- `},{throwOnError:!0})}async function ln(n,i){let e=A(n.email,"tierCheck.expiring"),r=e.replace("@",'<span style="display:none"></span>@<span style="display:none"></span>');await k({to:e,subject:"React Native\u4E2D\u6587\u7F51: \u60A8\u7684Pushy\u70ED\u66F4\u65B0\u670D\u52A1\u5373\u5C06\u5230\u671F",html:`
84
+ `},{throwOnError:!0})}async function xn(n,i){let e=A(n.email,"tierCheck.expiring"),r=e.replace("@",'<span style="display:none"></span>@<span style="display:none"></span>');await k({to:e,subject:"React Native\u4E2D\u6587\u7F51: \u60A8\u7684Pushy\u70ED\u66F4\u65B0\u670D\u52A1\u5373\u5C06\u5230\u671F",html:`
85
85
  <p>
86
86
  <span style="font-size: 14px;">\u4EB2\u7231\u7684 <b>${r}</b></span><span style="font-size: 14px;">\uFF0C\u60A8\u597D\uFF01</span>
87
87
  </p>
@@ -92,11 +92,11 @@ React Native\u4E2D\u6587\u7F51 Pushy\u70ED\u66F4\u65B0\u670D\u52A1
92
92
  <span style="font-size: 14px;">&nbsp;&nbsp;&nbsp;&nbsp;\u611F\u8C22\u60A8\u4F7F\u7528React Native\u4E2D\u6587\u7F51\u63D0\u4F9B\u7684\u70ED\u66F4\u65B0\u670D\u52A1\uFF0C\u60A8\u73B0\u6709\u7684\u4EA7\u54C1\u6709\u6548\u671F\u9650\u5DF2\u7ECF\u4E0D\u8DB3${i}\u65E5\uFF1A</span>
93
93
  </p>
94
94
  <ul>
95
- <li>\u4EA7\u54C1\u540D\u79F0\uFF1A${K[n.tier].title}</li>
95
+ <li>\u4EA7\u54C1\u540D\u79F0\uFF1A${Z[n.tier].title}</li>
96
96
  <li>\u6709\u6548\u671F\u81F3\uFF1A${L.default(n.tierExpiresAt).format("YYYY-MM-DD")}</li>
97
97
  </ul>
98
98
  <p>
99
- <span style="font-size: 14px;">&nbsp;&nbsp;&nbsp;&nbsp;\u4E3A\u4FDD\u8BC1\u60A8\u7684\u5E94\u7528\u70ED\u66F4\u65B0\u670D\u52A1\u80FD\u591F\u6301\u7EED\u7A33\u5B9A\u8FD0\u884C\uFF0C\u8BF7\u60A8\u5C3D\u5FEB<a href="${Q}">\u70B9\u51FB\u6B64\u5904\u7EED\u8D39</a>\u3002</span>
99
+ <span style="font-size: 14px;">&nbsp;&nbsp;&nbsp;&nbsp;\u4E3A\u4FDD\u8BC1\u60A8\u7684\u5E94\u7528\u70ED\u66F4\u65B0\u670D\u52A1\u80FD\u591F\u6301\u7EED\u7A33\u5B9A\u8FD0\u884C\uFF0C\u8BF7\u60A8\u5C3D\u5FEB<a href="${K}">\u70B9\u51FB\u6B64\u5904\u7EED\u8D39</a>\u3002</span>
100
100
  </p>
101
101
  <hr />
102
102
  <p style="margin-top: 0px; padding: 0px 0px 20px; line-height: inherit; border: 0px; list-style: none; color: rgb(51, 51, 51); font-family: \u5FAE\u8F6F\u96C5\u9ED1; font-size: 14px; white-space: normal;">
@@ -106,19 +106,19 @@ React Native\u4E2D\u6587\u7F51 Pushy\u70ED\u66F4\u65B0\u670D\u52A1
106
106
  <p style="margin-bottom: 0px; padding: 0px; font-size: 14px;">
107
107
  React Native\u4E2D\u6587\u7F51 Pushy\u70ED\u66F4\u65B0\u670D\u52A1
108
108
  </p>
109
- `},{throwOnError:!0})}function G(n){return n instanceof Error?n.message:String(n)}function X(n){return{email:n.email,tier:n.tier,tierExpiresAt:n.tierExpiresAt?L.default(n.tierExpiresAt).format("YYYY-MM-DD"):null}}async function an(n={}){let i=n.dryRun===!0,e=L.default(),r=await h.users.findMany({select:{id:!0,name:!0,email:!0,tier:!0,tierExpiresAt:!0},where:{status:"normal",tier:{not:"free"}}})||[];console.log(`Paid users: ${r.length}`);let t=[],c=[],o=[],p=[];for(let m of r){if(!m.tierExpiresAt)continue;let s=L.default(m.tierExpiresAt),f=s.subtract(7,"day"),u=s.subtract(30,"day");if(e.isAfter(s,"day"))t.push(m);else if(e.isSame(f,"day"))c.push(m);else if(e.isSame(u,"day"))o.push(m)}let C=[];if(i){for(let m of t)console.log(`Dry-run: would send expired tier email to ${m.email}`);for(let m of c)console.log(`Dry-run: would send 7-day tier email to ${m.email}`);for(let m of o)console.log(`Dry-run: would send 30-day tier email to ${m.email}`)}else{for(let m of t)console.log(`Expired: ${m.email}`),C.push(Hn(m).catch((s)=>{let f=G(s);console.error("Failed to send expired tier email",{email:m.email,error:s}),p.push(`expired:${m.email} - ${f}`)}));for(let m of c)console.log(`Expiring in 7d: ${m.email}`),C.push(ln(m,7).catch((s)=>{let f=G(s);console.error("Failed to send 7-day tier email",{email:m.email,error:s}),p.push(`expiring7:${m.email} - ${f}`)}));for(let m of o)console.log(`Expiring in 30d: ${m.email}`),C.push(ln(m,30).catch((s)=>{let f=G(s);console.error("Failed to send 30-day tier email",{email:m.email,error:s}),p.push(`expiring30:${m.email} - ${f}`)}))}if(i&&t.length>0)console.log(`Dry-run: would downgrade ${t.length} expired users to free tier`);else if(t.length>0)await h.users.updateMany({where:{id:{in:t.map((m)=>m.id)}},data:{tier:"free",tierExpiresAt:null}});return await Promise.all(C),console.log(`Finished tier checking.
109
+ `},{throwOnError:!0})}function W(n){return n instanceof Error?n.message:String(n)}function q(n){return{email:n.email,tier:n.tier,tierExpiresAt:n.tierExpiresAt?L.default(n.tierExpiresAt).format("YYYY-MM-DD"):null}}async function En(n={}){let i=n.dryRun===!0,e=L.default(),r=await h.users.findMany({select:{id:!0,name:!0,email:!0,tier:!0,tierExpiresAt:!0},where:{status:"normal",tier:{not:"free"}}})||[];console.log(`Paid users: ${r.length}`);let t=[],c=[],o=[],p=[];for(let m of r){if(!m.tierExpiresAt)continue;let s=L.default(m.tierExpiresAt),f=s.subtract(7,"day"),u=s.subtract(30,"day");if(e.isAfter(s,"day"))t.push(m);else if(e.isSame(f,"day"))c.push(m);else if(e.isSame(u,"day"))o.push(m)}let g=[];if(i){for(let m of t)console.log(`Dry-run: would send expired tier email to ${m.email}`);for(let m of c)console.log(`Dry-run: would send 7-day tier email to ${m.email}`);for(let m of o)console.log(`Dry-run: would send 30-day tier email to ${m.email}`)}else{for(let m of t)console.log(`Expired: ${m.email}`),g.push(Xn(m).catch((s)=>{let f=W(s);console.error("Failed to send expired tier email",{email:m.email,error:s}),p.push(`expired:${m.email} - ${f}`)}));for(let m of c)console.log(`Expiring in 7d: ${m.email}`),g.push(xn(m,7).catch((s)=>{let f=W(s);console.error("Failed to send 7-day tier email",{email:m.email,error:s}),p.push(`expiring7:${m.email} - ${f}`)}));for(let m of o)console.log(`Expiring in 30d: ${m.email}`),g.push(xn(m,30).catch((s)=>{let f=W(s);console.error("Failed to send 30-day tier email",{email:m.email,error:s}),p.push(`expiring30:${m.email} - ${f}`)}))}if(i&&t.length>0)console.log(`Dry-run: would downgrade ${t.length} expired users to free tier`);else if(t.length>0)await h.users.updateMany({where:{id:{in:t.map((m)=>m.id)}},data:{tier:"free",tierExpiresAt:null}});return await Promise.all(g),console.log(`Finished tier checking.
110
110
  Expired: ${t.length}
111
111
  Expiring in 7d: ${c.length}
112
- Expiring in 30d: ${o.length}`),{paidUsers:r.length,expiredUsers:t.length,expiring7Users:c.length,expiring30Users:o.length,expiredEntries:t.map(X),expiring7Entries:c.map(X),expiring30Entries:o.map(X),mailErrors:p}}var T=j(J(),1);import{mkdir as Qn,writeFile as Gn}from"fs/promises";import bn from"path";function Xn(n){return String(n??"-").replaceAll("|","\\|").replaceAll(`
113
- `,"<br>")}function _(n,i){if(i.length===0)return"_\u65E0_";return[`| ${n.join(" | ")} |`,`| ${n.map(()=>"---").join(" | ")} |`,...i.map((e)=>`| ${e.map(Xn).join(" | ")} |`)].join(`
114
- `)}function xn(n,i,e){return _(["\u90AE\u7BB1","\u6CE8\u518C\u65F6\u95F4",e],n.map((r)=>[r.email,r.createdAt,r[i]??"-"]))}function En(n){return _(["\u90AE\u7BB1","\u6CE8\u518C\u65F6\u95F4"],n.map((i)=>[i.email,i.createdAt]))}function Kn(n){let i=Array.from(new Set(n.map((e)=>e.deleteAfter).filter(Boolean)));if(i.length===0)return"### \u65B0\u8FDB\u5165\u5F85\u6E05\u7406";return`### \u65B0\u8FDB\u5165\u5F85\u6E05\u7406\uFF08\u9884\u8BA1\u5220\u9664\uFF1A${i.join(" / ")}\uFF09`}function $n(n){return[n.dryRun?"DRY_RUN \u6A21\u62DF\u6267\u884C":null,n.tierCheck?`${n.dryRun?"\u6A21\u62DF":""}\u5957\u9910\u5DF2\u8FC7\u671F ${n.tierCheck.expiredUsers}`:"\u5957\u9910\u68C0\u67E5\u672A\u6267\u884C",n.inactiveUsers?`${n.dryRun?"\u6A21\u62DF\u65B0\u589E\u5F85\u6E05\u7406":"\u65B0\u589E\u5F85\u6E05\u7406"} ${n.inactiveUsers.pendingUsers}`:"\u975E\u6D3B\u8DC3\u7528\u6237\u68C0\u67E5\u672A\u6267\u884C",n.inactiveUsers?.scanStopped?"\u975E\u6D3B\u8DC3\u626B\u63CF\u63D0\u524D\u505C\u6B62":null,n.inactiveUsers?`${n.dryRun?"\u6A21\u62DF\u89E3\u9664\u6E05\u7406":"\u89E3\u9664\u6E05\u7406"} ${n.inactiveUsers.restoredUsers}`:null,n.inactiveUsers?`${n.dryRun?"\u6A21\u62DF\u5220\u9664\u7528\u6237":"\u5B9E\u9645\u5220\u9664\u7528\u6237"} ${n.inactiveUsers.deletedUsers}`:null,n.auditLogs?`${n.dryRun?"\u6A21\u62DF\u6E05\u7406":"\u6E05\u7406"} audit logs ${n.auditLogs.deletedCount}`:"audit logs \u6E05\u7406\u672A\u6267\u884C",n.ossCleanup?`${n.dryRun?"\u6A21\u62DF\u6E05\u7406":"\u6E05\u7406"} OSS \u6587\u4EF6 ${n.ossCleanup.deletedCount}`:"OSS \u6E05\u7406\u672A\u6267\u884C"].filter(Boolean)}function Wn(n){let i=$n(n),e=[["Tier \u68C0\u67E5",n.tierCheck?`\u4ED8\u8D39\u7528\u6237 ${n.tierCheck.paidUsers}\uFF0C\u5DF2\u8FC7\u671F ${n.tierCheck.expiredUsers}\uFF0C7 \u5929\u63D0\u9192 ${n.tierCheck.expiring7Users}\uFF0C30 \u5929\u63D0\u9192 ${n.tierCheck.expiring30Users}`:"\u672A\u6267\u884C"],["Audit Logs \u6E05\u7406",n.auditLogs?`\u5019\u9009 ${n.auditLogs.countToDelete}\uFF0C\u5B9E\u9645\u5220\u9664 ${n.auditLogs.deletedCount}${n.auditLogs.skippedDeletion?n.dryRun?"\uFF08dry-run \u8DF3\u8FC7\uFF09":"\uFF08\u5F00\u53D1\u73AF\u5883\u8DF3\u8FC7\uFF09":""}`:"\u672A\u6267\u884C"],["\u975E\u6D3B\u8DC3\u7528\u6237",n.inactiveUsers?`\u5F85\u6E05\u7406 ${n.inactiveUsers.pendingUsers}\uFF08\u53D1\u4FE1 ${n.inactiveUsers.pendingNotifiedUsers}\uFF0C\u9759\u9ED8 ${n.inactiveUsers.pendingSilentUsers}\uFF09\uFF0C\u89E3\u9664\u6E05\u7406 ${n.inactiveUsers.restoredUsers}\uFF0C\u5F85\u6E05\u7406\u671F\u540E\u5220\u9664 ${n.inactiveUsers.deletedPendingUsers}\uFF0C\u672A\u9A8C\u8BC1\u76F4\u5220 ${n.inactiveUsers.deletedUnverifiedUsers}\uFF0C\u514D\u626B\u63CF\u8FC7\u671F ${n.inactiveUsers.expiredSkipUsers}${n.inactiveUsers.scanStopped?"\uFF0C\u626B\u63CF\u5DF2\u63D0\u524D\u505C\u6B62":""}`:"\u672A\u6267\u884C"],["OSS \u6E05\u7406",n.ossCleanup?`\u626B\u63CF ${n.ossCleanup.totalCount}\uFF0C\u5220\u9664 diff ${n.ossCleanup.diffCount}\uFF0C\u5220\u9664 p/v ${n.ossCleanup.pvCount}`:"\u672A\u6267\u884C"],["Redis TTL",n.redisExpire?n.redisExpire.skipped?"dry-run \u672A\u5237\u65B0":`appCheckUpdate=${n.redisExpire.appCheckUpdate}, userCheckUpdateQuota=${n.redisExpire.userCheckUpdateQuota}`:"\u672A\u6267\u884C"],["\u9519\u8BEF\u6570",n.errors.length]],r=[`# Pushy Daily Job \u65E5\u62A5${n.dryRun?" (DRY RUN)":""} - ${T.default(n.startedAt).format("YYYY-MM-DD")}`,"","## \u603B\u7ED3",`- \u5F00\u59CB\u65F6\u95F4: ${T.default(n.startedAt).format("YYYY-MM-DD HH:mm:ss")}`,`- \u7ED3\u675F\u65F6\u95F4: ${T.default(n.finishedAt).format("YYYY-MM-DD HH:mm:ss")}`,`- \u6267\u884C\u6A21\u5F0F: ${n.dryRun?"DRY_RUN\uFF08\u4EC5\u751F\u6210\u5E76\u6295\u9012\u65E5\u62A5\uFF0C\u4E0D\u6267\u884C DB/Redis/OSS \u5199\u5165\u6216\u7528\u6237\u901A\u77E5\uFF09":"LIVE"}`,`- \u603B\u4F53\u72B6\u6001: ${n.errors.length>0?"PARTIAL_FAILURE":n.dryRun?"DRY_RUN":"SUCCESS"}`,`- \u5173\u952E\u53D8\u66F4: ${i.join("\uFF0C")}`,"",_(["\u6A21\u5757","\u6458\u8981"],e)];if(n.tierCheck)r.push("","## Tier \u68C0\u67E5",_(["\u6307\u6807","\u6570\u91CF"],[["\u4ED8\u8D39\u7528\u6237\u626B\u63CF\u6570",n.tierCheck.paidUsers],["\u5DF2\u8FC7\u671F",n.tierCheck.expiredUsers],["7 \u5929\u63D0\u9192",n.tierCheck.expiring7Users],["30 \u5929\u63D0\u9192",n.tierCheck.expiring30Users]]),"","### \u5DF2\u8FC7\u671F\u8BE6\u60C5",_(["\u90AE\u7BB1","\u5957\u9910","\u6709\u6548\u671F\u81F3"],n.tierCheck.expiredEntries.map((t)=>[t.email,t.tier,t.tierExpiresAt])),"","### 7 \u5929\u63D0\u9192\u8BE6\u60C5",_(["\u90AE\u7BB1","\u5957\u9910","\u6709\u6548\u671F\u81F3"],n.tierCheck.expiring7Entries.map((t)=>[t.email,t.tier,t.tierExpiresAt])),"","### 30 \u5929\u63D0\u9192\u8BE6\u60C5",_(["\u90AE\u7BB1","\u5957\u9910","\u6709\u6548\u671F\u81F3"],n.tierCheck.expiring30Entries.map((t)=>[t.email,t.tier,t.tierExpiresAt])));if(n.auditLogs)r.push("","## Audit Logs \u6E05\u7406",_(["\u6307\u6807","\u503C"],[["\u6E05\u7406\u622A\u6B62\u65F6\u95F4",n.auditLogs.cutoffDate],["\u5019\u9009\u6570\u91CF",n.auditLogs.countToDelete],["\u5220\u9664\u6570\u91CF",n.auditLogs.deletedCount],["\u662F\u5426\u8DF3\u8FC7\u5220\u9664",n.auditLogs.skippedDeletion?n.dryRun?"\u662F\uFF08dry-run\uFF09":"\u662F":"\u5426"]]));if(n.inactiveUsers)r.push("","## \u975E\u6D3B\u8DC3\u7528\u6237\u6E05\u7406",_(["\u6307\u6807","\u6570\u91CF"],[["\u65B0\u8FDB\u5165\u5F85\u6E05\u7406",n.inactiveUsers.pendingUsers],["\u5176\u4E2D\u5DF2\u53D1\u901A\u77E5",n.inactiveUsers.pendingNotifiedUsers],["\u5176\u4E2D\u9759\u9ED8\u5165\u961F\uFF08>5\u5E74\uFF09",n.inactiveUsers.pendingSilentUsers],["\u89E3\u9664\u6E05\u7406",n.inactiveUsers.restoredUsers],["\u5F85\u6E05\u7406\u671F\u540E\u5220\u9664",n.inactiveUsers.deletedPendingUsers],["\u672A\u9A8C\u8BC1\u76F4\u5220",n.inactiveUsers.deletedUnverifiedUsers],["\u603B\u5220\u9664",n.inactiveUsers.deletedUsers],["Redis \u6E05\u7406\u79FB\u9664",n.inactiveUsers.removedUsers],["\u514D\u626B\u63CF\u8FC7\u671F",n.inactiveUsers.expiredSkipUsers],["\u626B\u63CF\u662F\u5426\u63D0\u524D\u505C\u6B62",n.inactiveUsers.scanStopped?"\u662F":"\u5426"],["\u505C\u6B62\u539F\u56E0",n.inactiveUsers.scanStopReason]]),"",Kn(n.inactiveUsers.pendingEntries),En(n.inactiveUsers.pendingEntries),"","### \u89E3\u9664\u5F85\u6E05\u7406",xn(n.inactiveUsers.restoredEntries,"skipUntil","\u514D\u626B\u63CF\u81F3"),"","### \u5DF2\u5220\u9664\uFF08\u5F85\u6E05\u7406\u671F\u7ED3\u675F\uFF09",xn(n.inactiveUsers.deletedPendingEntries,"appCount","\u5E94\u7528\u6570"),"","### \u5DF2\u5220\u9664\uFF08\u672A\u9A8C\u8BC1\u8D85\u671F\uFF09",En(n.inactiveUsers.deletedUnverifiedEntries));if(n.redisExpire)r.push("","## Redis TTL \u5237\u65B0",_(["Key","\u7ED3\u679C"],[["appCheckUpdate",n.redisExpire.skipped?"dry-run \u672A\u5237\u65B0":n.redisExpire.appCheckUpdate],["userCheckUpdateQuota",n.redisExpire.skipped?"dry-run \u672A\u5237\u65B0":n.redisExpire.userCheckUpdateQuota]]));if(n.ossCleanup)r.push("","## OSS \u6E05\u7406",_(["\u6307\u6807","\u6570\u91CF"],[["\u626B\u63CF\u5BF9\u8C61\u6570",n.ossCleanup.totalCount],["\u5220\u9664 diff \u6587\u4EF6",n.ossCleanup.diffCount],["\u5220\u9664 p/v \u6587\u4EF6",n.ossCleanup.pvCount],["\u603B\u5220\u9664\u6570",n.ossCleanup.deletedCount]]));return r.push("","## \u9519\u8BEF\u8BE6\u60C5"),r.push(_(["\u4EFB\u52A1","\u9519\u8BEF\u4FE1\u606F"],n.errors.map((t)=>[t.task,t.message]))),r.join(`
115
- `)}async function Dn(n){let i=Wn(n),e=$n(n),r=n.errors.length>0?"PARTIAL_FAILURE":n.dryRun?"DRY_RUN":"SUCCESS",t=bn.resolve(process.cwd(),"stats","dailyjob"),c=`dailyjob-${T.default(n.startedAt).format("YYYY-MM-DD")}${n.dryRun?"-dry-run":""}.md`,o=bn.join(t,c);if(await Qn(t,{recursive:!0}),await Gn(o,i,"utf8"),W.length>0){let p=W.map((C)=>A(C,"adminEmails"));await k({to:p.join(","),subject:`Pushy Daily Job \u65E5\u62A5${n.dryRun?" [DRY RUN]":""} - ${T.default(n.startedAt).format("YYYY-MM-DD")}`,html:`
112
+ Expiring in 30d: ${o.length}`),{paidUsers:r.length,expiredUsers:t.length,expiring7Users:c.length,expiring30Users:o.length,expiredEntries:t.map(q),expiring7Entries:c.map(q),expiring30Entries:o.map(q),mailErrors:p}}var T=P(V(),1);import{mkdir as Kn,writeFile as Wn}from"fs/promises";import $n from"path";function qn(n){return String(n??"-").replaceAll("|","\\|").replaceAll(`
113
+ `,"<br>")}function _(n,i){if(i.length===0)return"_\u65E0_";return[`| ${n.join(" | ")} |`,`| ${n.map(()=>"---").join(" | ")} |`,...i.map((e)=>`| ${e.map(qn).join(" | ")} |`)].join(`
114
+ `)}function Dn(n,i,e){return _(["\u90AE\u7BB1","\u6CE8\u518C\u65F6\u95F4",e],n.map((r)=>[r.email,r.createdAt,r[i]??"-"]))}function _n(n){return _(["\u90AE\u7BB1","\u6CE8\u518C\u65F6\u95F4"],n.map((i)=>[i.email,i.createdAt]))}function Zn(n){let i=Array.from(new Set(n.map((e)=>e.deleteAfter).filter(Boolean)));if(i.length===0)return"### \u65B0\u8FDB\u5165\u5F85\u6E05\u7406";return`### \u65B0\u8FDB\u5165\u5F85\u6E05\u7406\uFF08\u9884\u8BA1\u5220\u9664\uFF1A${i.join(" / ")}\uFF09`}function Yn(n){return[n.dryRun?"DRY_RUN \u6A21\u62DF\u6267\u884C":null,n.tierCheck?`${n.dryRun?"\u6A21\u62DF":""}\u5957\u9910\u5DF2\u8FC7\u671F ${n.tierCheck.expiredUsers}`:"\u5957\u9910\u68C0\u67E5\u672A\u6267\u884C",n.inactiveUsers?`${n.dryRun?"\u6A21\u62DF\u65B0\u589E\u5F85\u6E05\u7406":"\u65B0\u589E\u5F85\u6E05\u7406"} ${n.inactiveUsers.pendingUsers}`:"\u975E\u6D3B\u8DC3\u7528\u6237\u68C0\u67E5\u672A\u6267\u884C",n.inactiveUsers?.scanStopped?"\u975E\u6D3B\u8DC3\u626B\u63CF\u63D0\u524D\u505C\u6B62":null,n.inactiveUsers?`${n.dryRun?"\u6A21\u62DF\u89E3\u9664\u6E05\u7406":"\u89E3\u9664\u6E05\u7406"} ${n.inactiveUsers.restoredUsers}`:null,n.inactiveUsers?`${n.dryRun?"\u6A21\u62DF\u5220\u9664\u7528\u6237":"\u5B9E\u9645\u5220\u9664\u7528\u6237"} ${n.inactiveUsers.deletedUsers}`:null,n.auditLogs?`${n.dryRun?"\u6A21\u62DF\u6E05\u7406":"\u6E05\u7406"} audit logs ${n.auditLogs.deletedCount}`:"audit logs \u6E05\u7406\u672A\u6267\u884C",n.ossCleanup?`${n.dryRun?"\u6A21\u62DF\u6E05\u7406":"\u6E05\u7406"} OSS \u6587\u4EF6 ${n.ossCleanup.deletedCount}`:"OSS \u6E05\u7406\u672A\u6267\u884C"].filter(Boolean)}function yn(n){let i=Yn(n),e=[["Tier \u68C0\u67E5",n.tierCheck?`\u4ED8\u8D39\u7528\u6237 ${n.tierCheck.paidUsers}\uFF0C\u5DF2\u8FC7\u671F ${n.tierCheck.expiredUsers}\uFF0C7 \u5929\u63D0\u9192 ${n.tierCheck.expiring7Users}\uFF0C30 \u5929\u63D0\u9192 ${n.tierCheck.expiring30Users}`:"\u672A\u6267\u884C"],["Audit Logs \u6E05\u7406",n.auditLogs?`\u5019\u9009 ${n.auditLogs.countToDelete}\uFF0C\u5B9E\u9645\u5220\u9664 ${n.auditLogs.deletedCount}${n.auditLogs.skippedDeletion?n.dryRun?"\uFF08dry-run \u8DF3\u8FC7\uFF09":"\uFF08\u5F00\u53D1\u73AF\u5883\u8DF3\u8FC7\uFF09":""}`:"\u672A\u6267\u884C"],["\u975E\u6D3B\u8DC3\u7528\u6237",n.inactiveUsers?`\u5F85\u6E05\u7406 ${n.inactiveUsers.pendingUsers}\uFF08\u53D1\u4FE1 ${n.inactiveUsers.pendingNotifiedUsers}\uFF0C\u9759\u9ED8 ${n.inactiveUsers.pendingSilentUsers}\uFF09\uFF0C\u89E3\u9664\u6E05\u7406 ${n.inactiveUsers.restoredUsers}\uFF0C\u5F85\u6E05\u7406\u671F\u540E\u5220\u9664 ${n.inactiveUsers.deletedPendingUsers}\uFF0C\u672A\u9A8C\u8BC1\u76F4\u5220 ${n.inactiveUsers.deletedUnverifiedUsers}\uFF0C\u514D\u626B\u63CF\u8FC7\u671F ${n.inactiveUsers.expiredSkipUsers}${n.inactiveUsers.scanStopped?"\uFF0C\u626B\u63CF\u5DF2\u63D0\u524D\u505C\u6B62":""}`:"\u672A\u6267\u884C"],["OSS \u6E05\u7406",n.ossCleanup?`\u626B\u63CF ${n.ossCleanup.totalCount}\uFF0C\u5220\u9664 diff ${n.ossCleanup.diffCount}\uFF0C\u5220\u9664 p/v ${n.ossCleanup.pvCount}`:"\u672A\u6267\u884C"],["Redis TTL",n.redisExpire?n.redisExpire.skipped?"dry-run \u672A\u5237\u65B0":`appCheckUpdate=${n.redisExpire.appCheckUpdate}, userCheckUpdateQuota=${n.redisExpire.userCheckUpdateQuota}`:"\u672A\u6267\u884C"],["\u9519\u8BEF\u6570",n.errors.length]],r=[`# Pushy Daily Job \u65E5\u62A5${n.dryRun?" (DRY RUN)":""} - ${T.default(n.startedAt).format("YYYY-MM-DD")}`,"","## \u603B\u7ED3",`- \u5F00\u59CB\u65F6\u95F4: ${T.default(n.startedAt).format("YYYY-MM-DD HH:mm:ss")}`,`- \u7ED3\u675F\u65F6\u95F4: ${T.default(n.finishedAt).format("YYYY-MM-DD HH:mm:ss")}`,`- \u6267\u884C\u6A21\u5F0F: ${n.dryRun?"DRY_RUN\uFF08\u4EC5\u751F\u6210\u5E76\u6295\u9012\u65E5\u62A5\uFF0C\u4E0D\u6267\u884C DB/Redis/OSS \u5199\u5165\u6216\u7528\u6237\u901A\u77E5\uFF09":"LIVE"}`,`- \u603B\u4F53\u72B6\u6001: ${n.errors.length>0?"PARTIAL_FAILURE":n.dryRun?"DRY_RUN":"SUCCESS"}`,`- \u5173\u952E\u53D8\u66F4: ${i.join("\uFF0C")}`,"",_(["\u6A21\u5757","\u6458\u8981"],e)];if(n.tierCheck)r.push("","## Tier \u68C0\u67E5",_(["\u6307\u6807","\u6570\u91CF"],[["\u4ED8\u8D39\u7528\u6237\u626B\u63CF\u6570",n.tierCheck.paidUsers],["\u5DF2\u8FC7\u671F",n.tierCheck.expiredUsers],["7 \u5929\u63D0\u9192",n.tierCheck.expiring7Users],["30 \u5929\u63D0\u9192",n.tierCheck.expiring30Users]]),"","### \u5DF2\u8FC7\u671F\u8BE6\u60C5",_(["\u90AE\u7BB1","\u5957\u9910","\u6709\u6548\u671F\u81F3"],n.tierCheck.expiredEntries.map((t)=>[t.email,t.tier,t.tierExpiresAt])),"","### 7 \u5929\u63D0\u9192\u8BE6\u60C5",_(["\u90AE\u7BB1","\u5957\u9910","\u6709\u6548\u671F\u81F3"],n.tierCheck.expiring7Entries.map((t)=>[t.email,t.tier,t.tierExpiresAt])),"","### 30 \u5929\u63D0\u9192\u8BE6\u60C5",_(["\u90AE\u7BB1","\u5957\u9910","\u6709\u6548\u671F\u81F3"],n.tierCheck.expiring30Entries.map((t)=>[t.email,t.tier,t.tierExpiresAt])));if(n.auditLogs)r.push("","## Audit Logs \u6E05\u7406",_(["\u6307\u6807","\u503C"],[["\u6E05\u7406\u622A\u6B62\u65F6\u95F4",n.auditLogs.cutoffDate],["\u5019\u9009\u6570\u91CF",n.auditLogs.countToDelete],["\u5220\u9664\u6570\u91CF",n.auditLogs.deletedCount],["\u662F\u5426\u8DF3\u8FC7\u5220\u9664",n.auditLogs.skippedDeletion?n.dryRun?"\u662F\uFF08dry-run\uFF09":"\u662F":"\u5426"]]));if(n.inactiveUsers)r.push("","## \u975E\u6D3B\u8DC3\u7528\u6237\u6E05\u7406",_(["\u6307\u6807","\u6570\u91CF"],[["\u65B0\u8FDB\u5165\u5F85\u6E05\u7406",n.inactiveUsers.pendingUsers],["\u5176\u4E2D\u5DF2\u53D1\u901A\u77E5",n.inactiveUsers.pendingNotifiedUsers],["\u5176\u4E2D\u9759\u9ED8\u5165\u961F\uFF08>5\u5E74\uFF09",n.inactiveUsers.pendingSilentUsers],["\u89E3\u9664\u6E05\u7406",n.inactiveUsers.restoredUsers],["\u5F85\u6E05\u7406\u671F\u540E\u5220\u9664",n.inactiveUsers.deletedPendingUsers],["\u672A\u9A8C\u8BC1\u76F4\u5220",n.inactiveUsers.deletedUnverifiedUsers],["\u603B\u5220\u9664",n.inactiveUsers.deletedUsers],["Redis \u6E05\u7406\u79FB\u9664",n.inactiveUsers.removedUsers],["\u514D\u626B\u63CF\u8FC7\u671F",n.inactiveUsers.expiredSkipUsers],["\u626B\u63CF\u662F\u5426\u63D0\u524D\u505C\u6B62",n.inactiveUsers.scanStopped?"\u662F":"\u5426"],["\u505C\u6B62\u539F\u56E0",n.inactiveUsers.scanStopReason]]),"",Zn(n.inactiveUsers.pendingEntries),_n(n.inactiveUsers.pendingEntries),"","### \u89E3\u9664\u5F85\u6E05\u7406",Dn(n.inactiveUsers.restoredEntries,"skipUntil","\u514D\u626B\u63CF\u81F3"),"","### \u5DF2\u5220\u9664\uFF08\u5F85\u6E05\u7406\u671F\u7ED3\u675F\uFF09",Dn(n.inactiveUsers.deletedPendingEntries,"appCount","\u5E94\u7528\u6570"),"","### \u5DF2\u5220\u9664\uFF08\u672A\u9A8C\u8BC1\u8D85\u671F\uFF09",_n(n.inactiveUsers.deletedUnverifiedEntries));if(n.redisExpire)r.push("","## Redis TTL \u5237\u65B0",_(["Key","\u7ED3\u679C"],[["appCheckUpdate",n.redisExpire.skipped?"dry-run \u672A\u5237\u65B0":n.redisExpire.appCheckUpdate],["userCheckUpdateQuota",n.redisExpire.skipped?"dry-run \u672A\u5237\u65B0":n.redisExpire.userCheckUpdateQuota]]));if(n.ossCleanup)r.push("","## OSS \u6E05\u7406",_(["\u6307\u6807","\u6570\u91CF"],[["\u626B\u63CF\u5BF9\u8C61\u6570",n.ossCleanup.totalCount],["\u5220\u9664 diff \u6587\u4EF6",n.ossCleanup.diffCount],["\u5220\u9664 p/v \u6587\u4EF6",n.ossCleanup.pvCount],["\u603B\u5220\u9664\u6570",n.ossCleanup.deletedCount]]));return r.push("","## \u9519\u8BEF\u8BE6\u60C5"),r.push(_(["\u4EFB\u52A1","\u9519\u8BEF\u4FE1\u606F"],n.errors.map((t)=>[t.task,t.message]))),r.join(`
115
+ `)}async function On(n){let i=yn(n),e=Yn(n),r=n.errors.length>0?"PARTIAL_FAILURE":n.dryRun?"DRY_RUN":"SUCCESS",t=$n.resolve(process.cwd(),"stats","dailyjob"),c=`dailyjob-${T.default(n.startedAt).format("YYYY-MM-DD")}${n.dryRun?"-dry-run":""}.md`,o=$n.join(t,c);if(await Kn(t,{recursive:!0}),await Wn(o,i,"utf8"),y.length>0){let p=y.map((g)=>A(g,"adminEmails"));await k({to:p.join(","),subject:`Pushy Daily Job \u65E5\u62A5${n.dryRun?" [DRY RUN]":""} - ${T.default(n.startedAt).format("YYYY-MM-DD")}`,html:`
116
116
  <p>\u4ECA\u65E5 Pushy Daily Job ${n.dryRun?"\u5DF2\u5B8C\u6210 dry-run \u6A21\u62DF\u6267\u884C":"\u5DF2\u6267\u884C\u5B8C\u6210"}\uFF0C\u65E5\u62A5\u89C1\u9644\u4EF6\u3002</p>
117
117
  <p>\u5F00\u59CB\u65F6\u95F4\uFF1A${T.default(n.startedAt).format("YYYY-MM-DD HH:mm:ss")}</p>
118
118
  <p>\u7ED3\u675F\u65F6\u95F4\uFF1A${T.default(n.finishedAt).format("YYYY-MM-DD HH:mm:ss")}</p>
119
119
  <p>\u603B\u4F53\u72B6\u6001\uFF1A${r}</p>
120
120
  <p>\u5173\u952E\u53D8\u66F4\uFF1A${e.join("\uFF0C")}</p>
121
- `,attachments:[{filename:c,path:o,contentType:"text/markdown"}]},{throwOnError:!0})}return{filePath:o,markdown:i}}async function _n(n={}){let i=n.dryRun===!0,e=new Date,r=[],t=(u,a)=>{for(let g of a??[])r.push({task:u,message:g})};console.log(`Daily job started in ${i?"DRY_RUN":"LIVE"} mode`);let c=async(u,a)=>{try{return await a()}catch(g){let l=g instanceof Error?g.message:String(g);console.error(`Daily job step failed: ${u}`,g),r.push({task:u,message:l});return}},o=await c("tierCheck",()=>an(n));t("tierCheckMail",o?.mailErrors);let p=await c("auditLogs",()=>en(n)),C=await c("inactiveUsers",()=>Cn(n));t("inactiveUsersMail",C?.mailErrors);let m=await c("redisExpire",async()=>{if(i)return console.log("Dry-run: skip refreshing Redis TTL"),{appCheckUpdate:0,userCheckUpdateQuota:0,skipped:!0};let[u,a]=await Promise.all([E.expire(S.appCheckUpdate(),I*2),E.expire(S.userCheckUpdateQuota(),I*2)]);return{appCheckUpdate:u,userCheckUpdateQuota:a,skipped:!1}}),s=await c("ossCleanup",()=>hn(n)),f=new Date;try{await Dn({dryRun:i,startedAt:e,finishedAt:f,tierCheck:o,auditLogs:p,inactiveUsers:C,ossCleanup:s,redisExpire:m,errors:r})}catch(u){let a=u instanceof Error?u.message:String(u);console.error("Daily job report failed:",u),r.push({task:"report",message:a})}console.log(`Daily job completed in ${i?"DRY_RUN":"LIVE"} mode with ${r.length} error(s) from ${e.toISOString()} to ${f.toISOString()}`),process.exit(r.length>0?1:0)}function In(n){if(!n)return!1;return["1","true","yes","on"].includes(n.trim().toLowerCase())}function Yn(n=process.env){return{dryRun:In(n.DAILYJOB_DRY_RUN)}}_n(Yn());
121
+ `,attachments:[{filename:c,path:o,contentType:"text/markdown"}]},{throwOnError:!0})}return{filePath:o,markdown:i}}async function Rn(n={}){let i=n.dryRun===!0,e=new Date,r=[],t=(u,l)=>{for(let x of l??[])r.push({task:u,message:x})};console.log(`Daily job started in ${i?"DRY_RUN":"LIVE"} mode`);let c=async(u,l)=>{try{return await l()}catch(x){let Y=x instanceof Error?x.message:String(x);console.error(`Daily job step failed: ${u}`,x),r.push({task:u,message:Y});return}},o=await c("tierCheck",()=>En(n));t("tierCheckMail",o?.mailErrors);let p=await c("auditLogs",()=>cn(n)),g=await c("inactiveUsers",()=>ln(n));t("inactiveUsersMail",g?.mailErrors);let m=await c("redisExpire",async()=>{if(i)return console.log("Dry-run: skip refreshing Redis TTL"),{appCheckUpdate:0,userCheckUpdateQuota:0,skipped:!0};let[u,l]=await Promise.all([b.expire(U.appCheckUpdate(),I*2),b.expire(U.userCheckUpdateQuota(),I*2)]);return{appCheckUpdate:u,userCheckUpdateQuota:l,skipped:!1}}),s=await c("ossCleanup",()=>bn(n)),f=new Date;try{await On({dryRun:i,startedAt:e,finishedAt:f,tierCheck:o,auditLogs:p,inactiveUsers:g,ossCleanup:s,redisExpire:m,errors:r})}catch(u){let l=u instanceof Error?u.message:String(u);console.error("Daily job report failed:",u),r.push({task:"report",message:l})}console.log(`Daily job completed in ${i?"DRY_RUN":"LIVE"} mode with ${r.length} error(s) from ${e.toISOString()} to ${f.toISOString()}`),process.exit(r.length>0?1:0)}function In(n){if(!n)return!1;return["1","true","yes","on"].includes(n.trim().toLowerCase())}function An(n=process.env){return{dryRun:In(n.DAILYJOB_DRY_RUN)}}Rn(An());
122
122
 
123
- //# debugId=D0C6BB5AC6CA8A2964756E2164756E21
123
+ //# debugId=BEAA84477BD1186764756E2164756E21
124
124
  //# sourceMappingURL=dailyjob.js.map
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "import dayjs from 'dayjs';\nimport type { DailyJobOptions } from '../dailyjob/options';\nimport { prisma } from '../utils/prisma';\nimport { __DEV__ } from './config';\n\nexport interface AuditLogsCleanupResult {\n cutoffDate: string;\n countToDelete: number;\n deletedCount: number;\n skippedDeletion: boolean;\n}\n\n/**\n * 清理 181 天前的 audit_logs 数据\n */\nexport async function cleanAuditLogs(\n options: DailyJobOptions = {},\n): Promise<AuditLogsCleanupResult> {\n try {\n const dryRun = options.dryRun === true;\n const cutoffDate = dayjs().subtract(181, 'day').toDate();\n\n console.log(\n `Start cleaning audit_logs data before ${cutoffDate.toISOString()} (181 days ago)`,\n );\n\n const countToDelete = await prisma.audit_logs.count({\n where: {\n createdAt: {\n lt: cutoffDate,\n },\n },\n });\n\n console.log(`Found ${countToDelete} records to delete`);\n\n if (countToDelete === 0) {\n console.log('No audit_logs data to clean');\n return {\n cutoffDate: cutoffDate.toISOString(),\n countToDelete,\n deletedCount: 0,\n skippedDeletion: false,\n };\n }\n\n if (__DEV__ || dryRun) {\n if (dryRun) {\n console.log('Dry-run mode: skip actual audit_logs deletion');\n } else {\n console.log('Development mode: skip actual deletion');\n }\n return {\n cutoffDate: cutoffDate.toISOString(),\n countToDelete,\n deletedCount: 0,\n skippedDeletion: true,\n };\n }\n\n const result = await prisma.audit_logs.deleteMany({\n where: {\n createdAt: {\n lt: cutoffDate,\n },\n },\n });\n\n console.log(`Successfully deleted ${result.count} audit_logs records`);\n return {\n cutoffDate: cutoffDate.toISOString(),\n countToDelete,\n deletedCount: result.count,\n skippedDeletion: false,\n };\n } catch (error) {\n console.error('Error cleaning audit_logs:', error);\n throw error;\n }\n}\n",
6
6
  "export const __DEV__ = process.env.NODE_ENV !== 'production';\n\nexport const DIFF_RETENTION_DAYS = Number(\n process.env.DIFF_RETENTION_DAYS || 14,\n);\nexport const MAX_KEYS = Number(process.env.OSS_LIST_MAX_KEYS || 1000);\n\nexport const INACTIVE_USER_SCAN_YEARS = 3;\nexport const INACTIVE_USER_AUDIT_LOOKBACK_MONTHS = 6;\nexport const INACTIVE_USER_GRACE_DAYS = 30;\nexport const INACTIVE_USER_UNVERIFIED_DELETE_DAYS = 30;\nexport const INACTIVE_USER_SILENT_PENDING_YEARS = 5;\nexport const INACTIVE_CLEANUP_DAILY_MAIL_LIMIT = Math.max(\n 1,\n Number(process.env.INACTIVE_CLEANUP_DAILY_MAIL_LIMIT || 2000),\n);\nexport const INACTIVE_USER_QUERY_LOOKBACK_DAYS = Math.max(\n 1,\n Number(process.env.INACTIVE_USER_QUERY_LOOKBACK_DAYS || 14),\n);\n\nexport const cleanRedisKey = {\n pendingCleanupUsers: 'inactive_cleanup:pending_users',\n skipScanUsers: 'inactive_cleanup:skip_users',\n};\n\nexport interface UserZsetEntry {\n userId: number;\n score: number;\n}\n",
7
- "import dayjs from 'dayjs';\n\nimport { Prisma } from '../../generated/prisma/client';\nimport type { DailyJobOptions } from '../dailyjob/options';\nimport { sendMail } from '../modules/mail';\nimport redis from '../modules/redis';\nimport { ossClient } from '../modules/stores';\nimport { redisKey } from '../utils/constants';\nimport { assertValidEmailAddress } from '../utils/email';\nimport { isAdmin } from '../utils/isAdmin';\nimport { prisma } from '../utils/prisma';\nimport {\n __DEV__,\n cleanRedisKey,\n INACTIVE_CLEANUP_DAILY_MAIL_LIMIT,\n INACTIVE_USER_AUDIT_LOOKBACK_MONTHS,\n INACTIVE_USER_GRACE_DAYS,\n INACTIVE_USER_QUERY_LOOKBACK_DAYS,\n INACTIVE_USER_SCAN_YEARS,\n INACTIVE_USER_SILENT_PENDING_YEARS,\n INACTIVE_USER_UNVERIFIED_DELETE_DAYS,\n type UserZsetEntry,\n} from './config';\n\ninterface InactiveUserActivityState {\n latestAuditAt: Date | null;\n latestQueryAt: Date | null;\n skipUntil: Date | null;\n cleanupEligible: boolean;\n}\n\nexport interface InactiveCleanupReportEntry {\n email: string;\n createdAt: string;\n deleteAfter?: string | null;\n skipUntil?: string | null;\n appCount?: number | null;\n}\n\nexport interface InactiveUserCleanupResult {\n pendingUsers: number;\n pendingNotifiedUsers: number;\n pendingSilentUsers: number;\n restoredUsers: number;\n deletedUsers: number;\n deletedPendingUsers: number;\n deletedUnverifiedUsers: number;\n removedUsers: number;\n expiredSkipUsers: number;\n scanStopped: boolean;\n scanStopReason: string | null;\n pendingEntries: InactiveCleanupReportEntry[];\n restoredEntries: InactiveCleanupReportEntry[];\n deletedPendingEntries: InactiveCleanupReportEntry[];\n deletedUnverifiedEntries: InactiveCleanupReportEntry[];\n mailErrors: string[];\n}\n\ninterface DeletedInactiveUserData {\n email: string;\n createdAt: string | null;\n appCount: number;\n mailError?: string;\n}\n\ninterface InactiveCleanupProcessResult {\n deletedPendingUsers: number;\n deletedUnverifiedUsers: number;\n restoredUsers: number;\n removedUsers: number;\n stopScanning: boolean;\n scanStopReason: string | null;\n restoredEntries: InactiveCleanupReportEntry[];\n deletedPendingEntries: InactiveCleanupReportEntry[];\n deletedUnverifiedEntries: InactiveCleanupReportEntry[];\n mailErrors: string[];\n}\n\ninterface SkipScanState {\n expiredCount: number;\n activeSkipUserIds: number[];\n}\n\ninterface ScanInactiveUsersResult {\n pendingUsers: number;\n pendingNotifiedUsers: number;\n pendingSilentUsers: number;\n scanStopped: boolean;\n scanStopReason: string | null;\n pendingEntries: InactiveCleanupReportEntry[];\n mailErrors: string[];\n}\n\ninterface DirectCleanupUsersResult {\n deletedUnverifiedUsers: number;\n deletedUnverifiedEntries: InactiveCleanupReportEntry[];\n deletedUserIds: number[];\n}\n\ninterface DeleteInactiveUserOptions extends DailyJobOptions {\n notifyDeletedMail?: boolean;\n}\n\ninterface UserCreatedAtMetadata {\n createdAtText: string | null;\n olderThanSilentPendingCutoff: boolean;\n olderThanUnverifiedCutoff: boolean;\n}\n\ninterface UserCreatedAtMetadataRow {\n id: number;\n createdAtText: string | null;\n olderThanSilentPendingCutoff: number | bigint | string;\n olderThanUnverifiedCutoff: number | bigint | string;\n}\n\nfunction getInactiveUserScanCutoff() {\n return dayjs().subtract(INACTIVE_USER_SCAN_YEARS, 'year').toDate();\n}\n\nfunction getInactiveAuditCutoff() {\n return dayjs()\n .subtract(INACTIVE_USER_AUDIT_LOOKBACK_MONTHS, 'month')\n .toDate();\n}\n\nfunction getInactiveUnverifiedDeleteCutoff() {\n return dayjs().subtract(INACTIVE_USER_UNVERIFIED_DELETE_DAYS, 'day').toDate();\n}\n\nfunction getInactiveSilentPendingCutoff() {\n return dayjs().subtract(INACTIVE_USER_SILENT_PENDING_YEARS, 'year').toDate();\n}\n\nfunction getPendingDeleteAfterMs() {\n return dayjs().add(INACTIVE_USER_GRACE_DAYS, 'day').valueOf();\n}\n\nfunction getSafeEmail(email: string) {\n return email.replace(\n '@',\n '<span style=\"display:none\"></span>@<span style=\"display:none\"></span>',\n );\n}\n\nfunction getErrorMessage(error: unknown) {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction containsQuotaError(message: string) {\n return /quota/i.test(message);\n}\n\nfunction buildMailLimitStopReason() {\n return `清理通知邮件已达到本次扫描上限 ${INACTIVE_CLEANUP_DAILY_MAIL_LIMIT} 封`;\n}\n\nfunction buildQuotaStopReason(message: string) {\n return `清理通知邮件触发 quota,已停止本次扫描: ${message}`;\n}\n\nfunction formatDate(value: dayjs.ConfigType) {\n return dayjs(value).format('YYYY-MM-DD');\n}\n\nfunction formatDateTime(value: dayjs.ConfigType) {\n return dayjs(value).format('YYYY-MM-DD HH:mm:ss');\n}\n\nfunction buildReportEntryBase({\n email,\n createdAt,\n}: {\n email: string;\n createdAt: string | null;\n}) {\n return {\n email,\n createdAt: createdAt ?? 'INVAL',\n };\n}\n\nasync function getUserCreatedAtMetadataMap(userIds: number[]) {\n if (userIds.length === 0) {\n return new Map<number, UserCreatedAtMetadata>();\n }\n\n const silentPendingCutoff = getInactiveSilentPendingCutoff();\n const cutoff = getInactiveUnverifiedDeleteCutoff();\n const rows = await prisma.$queryRaw<UserCreatedAtMetadataRow[]>(\n Prisma.sql`\n SELECT\n id,\n NULLIF(CAST(createdAt AS CHAR(19)), '0000-00-00 00:00:00') AS createdAtText,\n CASE\n WHEN CAST(createdAt AS CHAR(19)) = '0000-00-00 00:00:00' THEN 1\n WHEN createdAt < ${silentPendingCutoff} THEN 1\n ELSE 0\n END AS olderThanSilentPendingCutoff,\n CASE\n WHEN CAST(createdAt AS CHAR(19)) = '0000-00-00 00:00:00' THEN 1\n WHEN createdAt < ${cutoff} THEN 1\n ELSE 0\n END AS olderThanUnverifiedCutoff\n FROM users\n WHERE id IN (${Prisma.join(userIds)})\n `,\n );\n\n return new Map<number, UserCreatedAtMetadata>(\n rows.map((row) => [\n row.id,\n {\n createdAtText: row.createdAtText,\n olderThanSilentPendingCutoff:\n Number(row.olderThanSilentPendingCutoff) > 0,\n olderThanUnverifiedCutoff: Number(row.olderThanUnverifiedCutoff) > 0,\n },\n ]),\n );\n}\n\nexport async function getUserZsetEntries(\n key: string,\n): Promise<UserZsetEntry[]> {\n const values = await redis.zrange(key, 0, -1, 'WITHSCORES');\n const entries: UserZsetEntry[] = [];\n\n for (let i = 0; i < values.length; i += 2) {\n const userId = Number(values[i]);\n const score = Number(values[i + 1]);\n if (!Number.isFinite(userId) || !Number.isFinite(score)) {\n continue;\n }\n entries.push({ userId, score });\n }\n\n return entries;\n}\n\nasync function getLatestQueryableQueryAt(appKeys: string[]) {\n const normalizedAppKeys = appKeys.filter(Boolean);\n if (normalizedAppKeys.length === 0) {\n return null;\n }\n\n const pipeline = redis.pipeline();\n const days = [];\n for (let i = 0; i < INACTIVE_USER_QUERY_LOOKBACK_DAYS; i++) {\n const day = dayjs().subtract(i, 'day');\n days.push(day);\n pipeline.zmscore(redisKey.appCheckUpdate(day), normalizedAppKeys);\n }\n\n const results = await pipeline.exec();\n for (let i = 0; i < days.length; i++) {\n const scores = (results?.[i]?.[1] || []) as Array<number | string | null>;\n if (scores.some((score) => Number(score || 0) > 0)) {\n return days[i].endOf('day').toDate();\n }\n }\n\n return null;\n}\n\nasync function getUserActivityState({\n userId,\n appKeys,\n}: {\n userId: number;\n appKeys: string[];\n}): Promise<InactiveUserActivityState> {\n const [recentAuditLog, latestQueryAt] = await Promise.all([\n prisma.audit_logs.findFirst({\n where: {\n userId,\n createdAt: {\n gte: getInactiveAuditCutoff(),\n },\n },\n orderBy: {\n createdAt: 'desc',\n },\n select: {\n createdAt: true,\n },\n }),\n getLatestQueryableQueryAt(appKeys),\n ]);\n\n const latestAuditAt = recentAuditLog?.createdAt ?? null;\n const skipUntilCandidates: Date[] = [];\n\n if (latestAuditAt) {\n skipUntilCandidates.push(\n dayjs(latestAuditAt)\n .add(INACTIVE_USER_AUDIT_LOOKBACK_MONTHS, 'month')\n .toDate(),\n );\n }\n\n if (latestQueryAt) {\n skipUntilCandidates.push(\n dayjs(latestQueryAt)\n .add(INACTIVE_USER_QUERY_LOOKBACK_DAYS, 'day')\n .endOf('day')\n .toDate(),\n );\n }\n\n const skipUntil =\n skipUntilCandidates.length > 0\n ? new Date(\n Math.max(...skipUntilCandidates.map((value) => value.getTime())),\n )\n : null;\n\n return {\n latestAuditAt,\n latestQueryAt,\n skipUntil,\n cleanupEligible: !latestAuditAt && !latestQueryAt,\n };\n}\n\nasync function sendInactiveCleanupMail({\n email,\n deleteAfterMs,\n}: {\n email: string;\n deleteAfterMs: number;\n}) {\n const normalizedEmail = assertValidEmailAddress(\n email,\n 'inactiveUsers.pending',\n );\n const safeEmail = getSafeEmail(normalizedEmail);\n const deleteAfter = dayjs(deleteAfterMs).format('YYYY-MM-DD');\n\n await sendMail(\n {\n to: normalizedEmail,\n subject: 'React Native中文网: 您的 Pushy 热更新账户即将被清理',\n html: `\n<p>\n <span style=\"font-size: 14px;\">亲爱的 <b>${safeEmail}</b>,您好!</span>\n</p>\n<p>\n <span style=\"font-size: 14px;\">系统检测到您的 Pushy 热更新账户注册时间已超过 3 年,最近 6 个月内没有管理操作记录;同时在当前仍可查询的应用查询统计窗口内,也没有检测到您名下应用的查询请求。</span>\n</p>\n<p>\n <span style=\"font-size: 14px;\">如果您希望继续保留账户数据,请在 <b>${deleteAfter}</b> 前登录后台并执行任意管理操作,或让您名下应用再次发起一次查询请求。</span>\n</p>\n<p>\n <span style=\"font-size: 14px;\">如果在接下来的 30 天内仍无任何操作或访问记录,系统将自动移除您的账户、应用及相关上传文件。</span>\n</p>\n<hr />\n<p style=\"margin-top: 0px; padding: 0px 0px 20px; line-height: inherit; border: 0px; list-style: none; color: rgb(51, 51, 51); font-family: 微软雅黑; font-size: 14px; white-space: normal;\">\n 这是系统自动发出的邮件,请勿回复。\n</p>\n<hr />\n<p style=\"margin-bottom: 0px; padding: 0px; font-size: 14px;\">\n React Native中文网 Pushy热更新服务\n</p>\n `,\n },\n { throwOnError: true, retryOnError: false },\n );\n}\n\nasync function sendInactiveDeletedMail({\n email,\n appCount,\n}: {\n email: string;\n appCount: number;\n}) {\n const normalizedEmail = assertValidEmailAddress(\n email,\n 'inactiveUsers.deleted',\n );\n const safeEmail = getSafeEmail(normalizedEmail);\n const deletedAt = dayjs().format('YYYY-MM-DD HH:mm:ss');\n\n await sendMail(\n {\n to: normalizedEmail,\n subject: 'React Native中文网: 您的 Pushy 热更新账户数据已被清理',\n html: `\n<p>\n <span style=\"font-size: 14px;\">亲爱的 <b>${safeEmail}</b>,您好!</span>\n</p>\n<p>\n <span style=\"font-size: 14px;\">由于您的 Pushy 热更新账户在通知后的 30 天观察期内仍无任何操作或访问记录,系统已于 <b>${deletedAt}</b> 完成数据清理。</span>\n</p>\n<p>\n <span style=\"font-size: 14px;\">本次已移除的数据包括:账户信息、名下 ${appCount} 个应用,以及相关上传文件。</span>\n</p>\n<hr />\n<p style=\"margin-top: 0px; padding: 0px 0px 20px; line-height: inherit; border: 0px; list-style: none; color: rgb(51, 51, 51); font-family: 微软雅黑; font-size: 14px; white-space: normal;\">\n 这是系统自动发出的邮件,请勿回复。\n</p>\n<hr />\n<p style=\"margin-bottom: 0px; padding: 0px; font-size: 14px;\">\n React Native中文网 Pushy热更新服务\n</p>\n `,\n },\n { throwOnError: true, retryOnError: false },\n );\n}\n\nasync function deleteUserUploads({\n userId,\n hashes,\n dryRun,\n}: {\n userId: number;\n hashes: string[];\n dryRun?: boolean;\n}) {\n if (hashes.length === 0) {\n return;\n }\n\n const [sharedPackages, sharedVersions] = await Promise.all([\n prisma.packages.findMany({\n where: {\n hash: {\n in: hashes,\n },\n apps: {\n is: {\n userId: {\n not: userId,\n },\n },\n },\n },\n select: {\n hash: true,\n },\n }),\n prisma.versions.findMany({\n where: {\n hash: {\n in: hashes,\n },\n apps: {\n is: {\n userId: {\n not: userId,\n },\n },\n },\n },\n select: {\n hash: true,\n },\n }),\n ]);\n\n const sharedHashes = new Set([\n ...sharedPackages.map((item) => item.hash),\n ...sharedVersions.map((item) => item.hash),\n ]);\n const deletableHashes = hashes.filter((hash) => !sharedHashes.has(hash));\n\n if (__DEV__ || deletableHashes.length === 0) {\n return;\n }\n\n if (dryRun) {\n console.log(\n `Dry-run: would delete ${deletableHashes.length} OSS objects for inactive user ${userId}`,\n );\n return;\n }\n\n for (let i = 0; i < deletableHashes.length; i += 1000) {\n const batch = deletableHashes.slice(i, i + 1000);\n if (batch.length === 0) {\n continue;\n }\n await ossClient.deleteMulti(batch);\n }\n}\n\nasync function deleteInactiveUserData(\n userId: number,\n options: DeleteInactiveUserOptions = {},\n): Promise<DeletedInactiveUserData | null> {\n const dryRun = options.dryRun === true;\n const notifyDeletedMail = options.notifyDeletedMail !== false;\n const user = await prisma.users.findUnique({\n where: {\n id: userId,\n },\n select: {\n id: true,\n email: true,\n apps: {\n select: {\n id: true,\n appKey: true,\n packages: {\n select: {\n hash: true,\n },\n },\n versions: {\n select: {\n hash: true,\n },\n },\n },\n },\n apiTokens: {\n select: {\n id: true,\n },\n },\n },\n });\n const createdAtMetadataByUserId = await getUserCreatedAtMetadataMap([userId]);\n const createdAtMetadata = createdAtMetadataByUserId.get(userId);\n\n if (!user) {\n if (dryRun) {\n console.log(\n `Dry-run: would remove missing inactive user ${userId} from cleanup queues`,\n );\n } else {\n await redis.zrem(cleanRedisKey.pendingCleanupUsers, String(userId));\n await redis.zrem(cleanRedisKey.skipScanUsers, String(userId));\n }\n return null;\n }\n\n if (__DEV__) {\n console.log(`Development mode: skip deleting inactive user ${userId}`);\n return null;\n }\n\n const hashes = Array.from(\n new Set(\n user.apps.flatMap((app) => [\n ...app.packages.map((pkg) => pkg.hash),\n ...app.versions.map((version) => version.hash),\n ]),\n ),\n );\n const tokenIds = user.apiTokens.map((token) => token.id);\n\n await deleteUserUploads({\n userId,\n hashes,\n dryRun,\n });\n\n if (dryRun) {\n console.log(`Dry-run: would delete inactive user ${userId}`);\n return {\n email: user.email,\n createdAt: createdAtMetadata?.createdAtText ?? null,\n appCount: user.apps.length,\n };\n }\n\n await prisma.$transaction(async (tx) => {\n if (tokenIds.length > 0) {\n await tx.audit_logs.deleteMany({\n where: {\n apiTokenId: {\n in: tokenIds,\n },\n },\n });\n }\n\n await tx.audit_logs.deleteMany({\n where: {\n userId,\n },\n });\n\n if (hashes.length > 0) {\n await tx.tasks.deleteMany({\n where: {\n OR: [\n {\n fromHash: {\n in: hashes,\n },\n },\n {\n toHash: {\n in: hashes,\n },\n },\n ],\n },\n });\n }\n\n await tx.users.delete({\n where: {\n id: userId,\n },\n });\n });\n\n let mailError: string | undefined;\n if (notifyDeletedMail) {\n try {\n await sendInactiveDeletedMail({\n email: user.email,\n appCount: user.apps.length,\n });\n } catch (error) {\n mailError = getErrorMessage(error);\n console.error('Failed to send inactive cleanup deletion email', {\n email: user.email,\n userId,\n error,\n });\n }\n }\n\n const pipeline = redis.pipeline();\n pipeline.del(`userId_${user.id}`);\n pipeline.del(`userEmail_${user.email}`);\n pipeline.del(`apps_${user.id}`);\n pipeline.zrem(cleanRedisKey.pendingCleanupUsers, String(user.id));\n pipeline.zrem(cleanRedisKey.skipScanUsers, String(user.id));\n for (const app of user.apps) {\n pipeline.del(`appId_${app.id}`);\n if (app.appKey) {\n pipeline.del(`appKey_${app.appKey}`);\n }\n }\n await pipeline.exec();\n\n return {\n email: user.email,\n createdAt: createdAtMetadata?.createdAtText ?? null,\n appCount: user.apps.length,\n mailError,\n };\n}\n\nasync function moveUserToSkipScan({\n userId,\n skipUntil,\n dryRun,\n}: {\n userId: number;\n skipUntil: Date;\n dryRun?: boolean;\n}) {\n if (dryRun) {\n console.log(\n `Dry-run: would move user ${userId} back to skip-scan until ${skipUntil.toISOString()}`,\n );\n return;\n }\n\n const pipeline = redis.pipeline();\n pipeline.zrem(cleanRedisKey.pendingCleanupUsers, String(userId));\n pipeline.zadd(\n cleanRedisKey.skipScanUsers,\n skipUntil.getTime(),\n String(userId),\n );\n await pipeline.exec();\n}\n\nexport async function processPendingCleanupUsers(\n options: DailyJobOptions = {},\n): Promise<InactiveCleanupProcessResult> {\n const dryRun = options.dryRun === true;\n const pendingUsers = await getUserZsetEntries(\n cleanRedisKey.pendingCleanupUsers,\n );\n const createdAtMetadataByUserId = await getUserCreatedAtMetadataMap(\n pendingUsers.map((entry) => entry.userId),\n );\n let deletedPendingUsers = 0;\n let deletedUnverifiedUsers = 0;\n let restoredUsers = 0;\n let removedUsers = 0;\n let stopScanning = false;\n let scanStopReason: string | null = null;\n const restoredEntries: InactiveCleanupReportEntry[] = [];\n const deletedPendingEntries: InactiveCleanupReportEntry[] = [];\n const deletedUnverifiedEntries: InactiveCleanupReportEntry[] = [];\n const mailErrors: string[] = [];\n\n for (const pendingUser of pendingUsers) {\n const user = await prisma.users.findUnique({\n where: {\n id: pendingUser.userId,\n },\n select: {\n id: true,\n email: true,\n status: true,\n apps: {\n select: {\n appKey: true,\n },\n },\n },\n });\n\n if (!user || isAdmin(user.email)) {\n if (dryRun) {\n console.log(\n `Dry-run: would remove user ${pendingUser.userId} from cleanup queues`,\n );\n } else {\n await redis.zrem(\n cleanRedisKey.pendingCleanupUsers,\n String(pendingUser.userId),\n );\n await redis.zrem(\n cleanRedisKey.skipScanUsers,\n String(pendingUser.userId),\n );\n }\n removedUsers++;\n continue;\n }\n\n const createdAtMetadata = createdAtMetadataByUserId.get(user.id);\n\n if (\n user.status === 'unverified' &&\n createdAtMetadata?.olderThanUnverifiedCutoff\n ) {\n const deleted = await deleteInactiveUserData(user.id, {\n ...options,\n notifyDeletedMail: false,\n });\n if (deleted) {\n deletedUnverifiedUsers++;\n deletedUnverifiedEntries.push({\n ...buildReportEntryBase(deleted),\n appCount: deleted.appCount,\n });\n if (deleted.mailError) {\n mailErrors.push(`deleted:${deleted.email} - ${deleted.mailError}`);\n }\n }\n continue;\n }\n\n const appKeys = user.apps\n .map((app) => app.appKey)\n .filter((appKey): appKey is string => Boolean(appKey));\n const activityState = await getUserActivityState({\n userId: user.id,\n appKeys,\n });\n\n if (!activityState.cleanupEligible && activityState.skipUntil) {\n await moveUserToSkipScan({\n userId: user.id,\n skipUntil: activityState.skipUntil,\n dryRun,\n });\n restoredUsers++;\n restoredEntries.push({\n ...buildReportEntryBase({\n email: user.email,\n createdAt: createdAtMetadata?.createdAtText ?? null,\n }),\n skipUntil: formatDateTime(activityState.skipUntil),\n });\n continue;\n }\n\n if (Date.now() < pendingUser.score) {\n continue;\n }\n\n const deleted = await deleteInactiveUserData(user.id, options);\n if (deleted) {\n deletedPendingUsers++;\n deletedPendingEntries.push({\n ...buildReportEntryBase(deleted),\n appCount: deleted.appCount,\n });\n if (deleted.mailError) {\n mailErrors.push(`deleted:${deleted.email} - ${deleted.mailError}`);\n if (!stopScanning && containsQuotaError(deleted.mailError)) {\n stopScanning = true;\n scanStopReason = buildQuotaStopReason(deleted.mailError);\n }\n }\n }\n }\n\n return {\n deletedPendingUsers,\n deletedUnverifiedUsers,\n restoredUsers,\n removedUsers,\n stopScanning,\n scanStopReason,\n restoredEntries,\n deletedPendingEntries,\n deletedUnverifiedEntries,\n mailErrors,\n };\n}\n\nexport async function cleanExpiredSkipScanUsers(\n options: DailyJobOptions = {},\n): Promise<SkipScanState> {\n const dryRun = options.dryRun === true;\n const now = Date.now();\n const skipEntries = await getUserZsetEntries(cleanRedisKey.skipScanUsers);\n const expiredUserIds = skipEntries\n .filter((entry) => entry.score <= now)\n .map((entry) => String(entry.userId));\n if (!dryRun && expiredUserIds.length > 0) {\n await redis.zremrangebyscore(cleanRedisKey.skipScanUsers, 0, now);\n }\n\n const activeSkipUserIds = skipEntries\n .filter((entry) => entry.score > now)\n .map((entry) => entry.userId);\n\n return {\n expiredCount: expiredUserIds.length,\n activeSkipUserIds,\n };\n}\n\nasync function cleanupExpiredUnverifiedUsers({\n excludedUserIds,\n dryRun,\n}: {\n excludedUserIds: number[];\n dryRun?: boolean;\n}): Promise<DirectCleanupUsersResult> {\n const users = await prisma.users.findMany({\n where: {\n status: 'unverified',\n createdAt: {\n lt: getInactiveUnverifiedDeleteCutoff(),\n },\n ...(excludedUserIds.length > 0\n ? {\n id: {\n notIn: excludedUserIds,\n },\n }\n : {}),\n },\n select: {\n id: true,\n email: true,\n },\n });\n\n let deletedUnverifiedUsers = 0;\n const deletedUnverifiedEntries: InactiveCleanupReportEntry[] = [];\n const deletedUserIds: number[] = [];\n\n for (const user of users) {\n if (isAdmin(user.email)) {\n continue;\n }\n\n deletedUserIds.push(user.id);\n const deleted = await deleteInactiveUserData(user.id, {\n dryRun,\n notifyDeletedMail: false,\n });\n if (deleted) {\n deletedUnverifiedUsers++;\n deletedUnverifiedEntries.push({\n ...buildReportEntryBase(deleted),\n appCount: deleted.appCount,\n });\n }\n }\n\n return {\n deletedUnverifiedUsers,\n deletedUnverifiedEntries,\n deletedUserIds,\n };\n}\n\nexport async function scanInactiveUsers({\n excludedUserIds,\n dryRun,\n}: {\n excludedUserIds: number[];\n dryRun?: boolean;\n}): Promise<ScanInactiveUsersResult> {\n const users = await prisma.users.findMany({\n where: {\n createdAt: {\n lt: getInactiveUserScanCutoff(),\n },\n ...(excludedUserIds.length > 0\n ? {\n id: {\n notIn: excludedUserIds,\n },\n }\n : {}),\n },\n select: {\n id: true,\n email: true,\n apps: {\n select: {\n appKey: true,\n },\n },\n },\n });\n const createdAtMetadataByUserId = await getUserCreatedAtMetadataMap(\n users.map((user) => user.id),\n );\n\n let notifiedUsers = 0;\n let silentPendingUsers = 0;\n let scanStopped = false;\n let scanStopReason: string | null = null;\n const pendingEntries: InactiveCleanupReportEntry[] = [];\n const mailErrors: string[] = [];\n\n for (const user of users) {\n if (isAdmin(user.email)) {\n continue;\n }\n\n const appKeys = user.apps\n .map((app) => app.appKey)\n .filter((appKey): appKey is string => Boolean(appKey));\n const activityState = await getUserActivityState({\n userId: user.id,\n appKeys,\n });\n\n if (!activityState.cleanupEligible) {\n if (activityState.skipUntil) {\n if (dryRun) {\n console.log(\n `Dry-run: would keep user ${user.id} in skip-scan until ${activityState.skipUntil.toISOString()}`,\n );\n } else {\n await redis.zadd(\n cleanRedisKey.skipScanUsers,\n activityState.skipUntil.getTime(),\n String(user.id),\n );\n }\n }\n continue;\n }\n\n const deleteAfterMs = getPendingDeleteAfterMs();\n const createdAtMetadata = createdAtMetadataByUserId.get(user.id);\n const shouldSkipNotification =\n createdAtMetadata?.olderThanSilentPendingCutoff === true;\n if (dryRun) {\n if (shouldSkipNotification) {\n console.log(\n `Dry-run: would queue >5y inactive user ${user.email} without warning email`,\n );\n } else {\n console.log(\n `Dry-run: would send inactive cleanup warning email to ${user.email}`,\n );\n }\n } else {\n if (shouldSkipNotification) {\n console.log(\n `Queue >5y inactive user ${user.email} without warning email`,\n );\n } else {\n if (notifiedUsers >= INACTIVE_CLEANUP_DAILY_MAIL_LIMIT) {\n scanStopped = true;\n scanStopReason = buildMailLimitStopReason();\n console.warn(scanStopReason);\n break;\n }\n\n try {\n await sendInactiveCleanupMail({\n email: user.email,\n deleteAfterMs,\n });\n } catch (error) {\n const message = getErrorMessage(error);\n console.error('Failed to send inactive cleanup warning email', {\n email: user.email,\n userId: user.id,\n error,\n });\n mailErrors.push(`pending:${user.email} - ${message}`);\n if (containsQuotaError(message)) {\n scanStopped = true;\n scanStopReason = buildQuotaStopReason(message);\n console.warn(scanStopReason);\n break;\n }\n continue;\n }\n }\n }\n if (!dryRun) {\n await redis.zadd(\n cleanRedisKey.pendingCleanupUsers,\n deleteAfterMs,\n String(user.id),\n );\n }\n if (shouldSkipNotification) {\n silentPendingUsers++;\n } else {\n notifiedUsers++;\n if (!dryRun && notifiedUsers >= INACTIVE_CLEANUP_DAILY_MAIL_LIMIT) {\n scanStopped = true;\n scanStopReason = buildMailLimitStopReason();\n }\n }\n pendingEntries.push({\n ...buildReportEntryBase({\n email: user.email,\n createdAt: createdAtMetadata?.createdAtText ?? null,\n }),\n deleteAfter: formatDate(deleteAfterMs),\n });\n\n if (scanStopped) {\n console.warn(scanStopReason);\n break;\n }\n }\n\n return {\n pendingUsers: notifiedUsers + silentPendingUsers,\n pendingNotifiedUsers: notifiedUsers,\n pendingSilentUsers: silentPendingUsers,\n scanStopped,\n scanStopReason,\n pendingEntries,\n mailErrors,\n };\n}\n\nexport async function runInactiveUserCleanupJob(\n options: DailyJobOptions = {},\n): Promise<InactiveUserCleanupResult> {\n const pendingUsers = await getUserZsetEntries(\n cleanRedisKey.pendingCleanupUsers,\n );\n const {\n deletedPendingUsers,\n deletedUnverifiedUsers: queuedDeletedUnverifiedUsers,\n restoredUsers,\n removedUsers,\n stopScanning,\n scanStopReason: preScanStopReason,\n restoredEntries,\n deletedPendingEntries,\n deletedUnverifiedEntries: queuedDeletedUnverifiedEntries,\n mailErrors: deletedMailErrors,\n } = await processPendingCleanupUsers(options);\n const { expiredCount: expiredSkipUsers, activeSkipUserIds } =\n await cleanExpiredSkipScanUsers(options);\n const {\n deletedUnverifiedUsers: directDeletedUnverifiedUsers,\n deletedUnverifiedEntries: directDeletedUnverifiedEntries,\n deletedUserIds: directDeletedUserIds,\n } = await cleanupExpiredUnverifiedUsers({\n excludedUserIds: pendingUsers.map((entry) => entry.userId),\n dryRun: options.dryRun,\n });\n const scanResult = stopScanning\n ? {\n pendingUsers: 0,\n pendingNotifiedUsers: 0,\n pendingSilentUsers: 0,\n scanStopped: true,\n scanStopReason: preScanStopReason,\n pendingEntries: [],\n mailErrors: [] as string[],\n }\n : await scanInactiveUsers({\n excludedUserIds: [\n ...pendingUsers.map((entry) => entry.userId),\n ...activeSkipUserIds,\n ...directDeletedUserIds,\n ],\n dryRun: options.dryRun,\n });\n\n const deletedUnverifiedUsers =\n queuedDeletedUnverifiedUsers + directDeletedUnverifiedUsers;\n const deletedUsers = deletedPendingUsers + deletedUnverifiedUsers;\n\n console.log(\n `Inactive cleanup processed. pending=${scanResult.pendingUsers}, notified=${scanResult.pendingNotifiedUsers}, silent=${scanResult.pendingSilentUsers}, deletedPending=${deletedPendingUsers}, deletedUnverified=${deletedUnverifiedUsers}, restored=${restoredUsers}, removed=${removedUsers}, skipExpired=${expiredSkipUsers}, scanStopped=${scanResult.scanStopped}`,\n );\n\n return {\n pendingUsers: scanResult.pendingUsers,\n pendingNotifiedUsers: scanResult.pendingNotifiedUsers,\n pendingSilentUsers: scanResult.pendingSilentUsers,\n restoredUsers,\n deletedUsers,\n deletedPendingUsers,\n deletedUnverifiedUsers,\n removedUsers,\n expiredSkipUsers,\n scanStopped: scanResult.scanStopped,\n scanStopReason: scanResult.scanStopReason,\n pendingEntries: scanResult.pendingEntries,\n restoredEntries,\n deletedPendingEntries,\n deletedUnverifiedEntries: [\n ...queuedDeletedUnverifiedEntries,\n ...directDeletedUnverifiedEntries,\n ],\n mailErrors: [...deletedMailErrors, ...scanResult.mailErrors],\n };\n}\n",
7
+ "import dayjs from 'dayjs';\n\nimport { Prisma } from '../../generated/prisma/client';\nimport type { DailyJobOptions } from '../dailyjob/options';\nimport { sendMail } from '../modules/mail';\nimport redis from '../modules/redis';\nimport { ossClient } from '../modules/stores';\nimport { redisKey } from '../utils/constants';\nimport { assertValidEmailAddress } from '../utils/email';\nimport { isAdmin } from '../utils/isAdmin';\nimport { prisma } from '../utils/prisma';\nimport {\n __DEV__,\n cleanRedisKey,\n INACTIVE_CLEANUP_DAILY_MAIL_LIMIT,\n INACTIVE_USER_AUDIT_LOOKBACK_MONTHS,\n INACTIVE_USER_GRACE_DAYS,\n INACTIVE_USER_QUERY_LOOKBACK_DAYS,\n INACTIVE_USER_SCAN_YEARS,\n INACTIVE_USER_SILENT_PENDING_YEARS,\n INACTIVE_USER_UNVERIFIED_DELETE_DAYS,\n type UserZsetEntry,\n} from './config';\n\ninterface InactiveUserActivityState {\n latestAuditAt: Date | null;\n latestQueryAt: Date | null;\n skipUntil: Date | null;\n cleanupEligible: boolean;\n}\n\nexport interface InactiveCleanupReportEntry {\n email: string;\n createdAt: string;\n deleteAfter?: string | null;\n skipUntil?: string | null;\n appCount?: number | null;\n}\n\nexport interface InactiveUserCleanupResult {\n pendingUsers: number;\n pendingNotifiedUsers: number;\n pendingSilentUsers: number;\n restoredUsers: number;\n deletedUsers: number;\n deletedPendingUsers: number;\n deletedUnverifiedUsers: number;\n removedUsers: number;\n expiredSkipUsers: number;\n scanStopped: boolean;\n scanStopReason: string | null;\n pendingEntries: InactiveCleanupReportEntry[];\n restoredEntries: InactiveCleanupReportEntry[];\n deletedPendingEntries: InactiveCleanupReportEntry[];\n deletedUnverifiedEntries: InactiveCleanupReportEntry[];\n mailErrors: string[];\n}\n\ninterface DeletedInactiveUserData {\n email: string;\n createdAt: string | null;\n appCount: number;\n mailError?: string;\n}\n\ninterface InactiveCleanupProcessResult {\n deletedPendingUsers: number;\n deletedUnverifiedUsers: number;\n restoredUsers: number;\n removedUsers: number;\n stopScanning: boolean;\n scanStopReason: string | null;\n restoredEntries: InactiveCleanupReportEntry[];\n deletedPendingEntries: InactiveCleanupReportEntry[];\n deletedUnverifiedEntries: InactiveCleanupReportEntry[];\n mailErrors: string[];\n}\n\ninterface SkipScanState {\n expiredCount: number;\n activeSkipUserIds: number[];\n}\n\ninterface ScanInactiveUsersResult {\n pendingUsers: number;\n pendingNotifiedUsers: number;\n pendingSilentUsers: number;\n scanStopped: boolean;\n scanStopReason: string | null;\n pendingEntries: InactiveCleanupReportEntry[];\n mailErrors: string[];\n}\n\ninterface DirectCleanupUsersResult {\n deletedUnverifiedUsers: number;\n deletedUnverifiedEntries: InactiveCleanupReportEntry[];\n deletedUserIds: number[];\n}\n\ninterface DeleteInactiveUserOptions extends DailyJobOptions {\n notifyDeletedMail?: boolean;\n}\n\ninterface UserCreatedAtMetadata {\n createdAtText: string | null;\n olderThanSilentPendingCutoff: boolean;\n olderThanUnverifiedCutoff: boolean;\n}\n\ninterface UserCreatedAtMetadataRow {\n id: number;\n createdAtText: string | null;\n olderThanSilentPendingCutoff: number | bigint | string;\n olderThanUnverifiedCutoff: number | bigint | string;\n}\n\nfunction getInactiveUserScanCutoff() {\n return dayjs().subtract(INACTIVE_USER_SCAN_YEARS, 'year').toDate();\n}\n\nfunction getInactiveAuditCutoff() {\n return dayjs()\n .subtract(INACTIVE_USER_AUDIT_LOOKBACK_MONTHS, 'month')\n .toDate();\n}\n\nfunction getInactiveUnverifiedDeleteCutoff() {\n return dayjs().subtract(INACTIVE_USER_UNVERIFIED_DELETE_DAYS, 'day').toDate();\n}\n\nfunction getInactiveSilentPendingCutoff() {\n return dayjs().subtract(INACTIVE_USER_SILENT_PENDING_YEARS, 'year').toDate();\n}\n\nfunction getPendingDeleteAfterMs() {\n return dayjs().add(INACTIVE_USER_GRACE_DAYS, 'day').valueOf();\n}\n\nfunction getSafeEmail(email: string) {\n return email.replace(\n '@',\n '<span style=\"display:none\"></span>@<span style=\"display:none\"></span>',\n );\n}\n\nfunction getErrorMessage(error: unknown) {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction containsQuotaError(message: string) {\n return /quota/i.test(message);\n}\n\nfunction buildMailLimitStopReason() {\n return `清理通知邮件已达到本次扫描上限 ${INACTIVE_CLEANUP_DAILY_MAIL_LIMIT} 封`;\n}\n\nfunction buildQuotaStopReason(message: string) {\n return `清理通知邮件触发 quota,已停止本次扫描: ${message}`;\n}\n\nfunction formatDate(value: dayjs.ConfigType) {\n return dayjs(value).format('YYYY-MM-DD');\n}\n\nfunction formatDateTime(value: dayjs.ConfigType) {\n return dayjs(value).format('YYYY-MM-DD HH:mm:ss');\n}\n\nfunction buildReportEntryBase({\n email,\n createdAt,\n}: {\n email: string;\n createdAt: string | null;\n}) {\n return {\n email,\n createdAt: createdAt ?? 'INVAL',\n };\n}\n\nasync function getUserCreatedAtMetadataMap(userIds: number[]) {\n if (userIds.length === 0) {\n return new Map<number, UserCreatedAtMetadata>();\n }\n\n const silentPendingCutoff = getInactiveSilentPendingCutoff();\n const cutoff = getInactiveUnverifiedDeleteCutoff();\n const rows = await prisma.$queryRaw<UserCreatedAtMetadataRow[]>(\n Prisma.sql`\n SELECT\n id,\n NULLIF(CAST(createdAt AS CHAR(19)), '0000-00-00 00:00:00') AS createdAtText,\n CASE\n WHEN CAST(createdAt AS CHAR(19)) = '0000-00-00 00:00:00' THEN 1\n WHEN createdAt < ${silentPendingCutoff} THEN 1\n ELSE 0\n END AS olderThanSilentPendingCutoff,\n CASE\n WHEN CAST(createdAt AS CHAR(19)) = '0000-00-00 00:00:00' THEN 1\n WHEN createdAt < ${cutoff} THEN 1\n ELSE 0\n END AS olderThanUnverifiedCutoff\n FROM users\n WHERE id IN (${Prisma.join(userIds)})\n `,\n );\n\n return new Map<number, UserCreatedAtMetadata>(\n rows.map((row) => [\n row.id,\n {\n createdAtText: row.createdAtText,\n olderThanSilentPendingCutoff:\n Number(row.olderThanSilentPendingCutoff) > 0,\n olderThanUnverifiedCutoff: Number(row.olderThanUnverifiedCutoff) > 0,\n },\n ]),\n );\n}\n\nexport async function getUserZsetEntries(\n key: string,\n): Promise<UserZsetEntry[]> {\n const values = await redis.zrange(key, 0, -1, 'WITHSCORES');\n const entries: UserZsetEntry[] = [];\n\n for (let i = 0; i < values.length; i += 2) {\n const userId = Number(values[i]);\n const score = Number(values[i + 1]);\n if (!Number.isFinite(userId) || !Number.isFinite(score)) {\n continue;\n }\n entries.push({ userId, score });\n }\n\n return entries;\n}\n\nasync function getLatestQueryableQueryAt(appKeys: string[]) {\n const normalizedAppKeys = appKeys.filter(Boolean);\n if (normalizedAppKeys.length === 0) {\n return null;\n }\n\n const pipeline = redis.pipeline();\n const days = [];\n for (let i = 0; i < INACTIVE_USER_QUERY_LOOKBACK_DAYS; i++) {\n const day = dayjs().subtract(i, 'day');\n days.push(day);\n pipeline.zmscore(redisKey.appCheckUpdate(day), normalizedAppKeys);\n }\n\n const results = await pipeline.exec();\n for (let i = 0; i < days.length; i++) {\n const scores = (results?.[i]?.[1] || []) as Array<number | string | null>;\n if (scores.some((score) => Number(score || 0) > 0)) {\n return days[i].endOf('day').toDate();\n }\n }\n\n return null;\n}\n\nasync function getUserActivityState({\n userId,\n appKeys,\n}: {\n userId: number;\n appKeys: string[];\n}): Promise<InactiveUserActivityState> {\n const [recentAuditLog, latestQueryAt] = await Promise.all([\n prisma.audit_logs.findFirst({\n where: {\n userId,\n createdAt: {\n gte: getInactiveAuditCutoff(),\n },\n },\n orderBy: {\n createdAt: 'desc',\n },\n select: {\n createdAt: true,\n },\n }),\n getLatestQueryableQueryAt(appKeys),\n ]);\n\n const latestAuditAt = recentAuditLog?.createdAt ?? null;\n const skipUntilCandidates: Date[] = [];\n\n if (latestAuditAt) {\n skipUntilCandidates.push(\n dayjs(latestAuditAt)\n .add(INACTIVE_USER_AUDIT_LOOKBACK_MONTHS, 'month')\n .toDate(),\n );\n }\n\n if (latestQueryAt) {\n skipUntilCandidates.push(\n dayjs(latestQueryAt)\n .add(INACTIVE_USER_QUERY_LOOKBACK_DAYS, 'day')\n .endOf('day')\n .toDate(),\n );\n }\n\n const skipUntil =\n skipUntilCandidates.length > 0\n ? new Date(\n Math.max(...skipUntilCandidates.map((value) => value.getTime())),\n )\n : null;\n\n return {\n latestAuditAt,\n latestQueryAt,\n skipUntil,\n cleanupEligible: !latestAuditAt && !latestQueryAt,\n };\n}\n\nasync function sendInactiveCleanupMail({\n email,\n deleteAfterMs,\n}: {\n email: string;\n deleteAfterMs: number;\n}) {\n const normalizedEmail = assertValidEmailAddress(\n email,\n 'inactiveUsers.pending',\n );\n const safeEmail = getSafeEmail(normalizedEmail);\n const deleteAfter = dayjs(deleteAfterMs).format('YYYY-MM-DD');\n\n await sendMail(\n {\n to: normalizedEmail,\n subject: 'React Native中文网: 您的 Pushy 热更新账户即将被清理',\n html: `\n<p>\n <span style=\"font-size: 14px;\">亲爱的 <b>${safeEmail}</b>,您好!</span>\n</p>\n<p>\n <span style=\"font-size: 14px;\">系统检测到您的 Pushy 热更新账户注册时间已超过 3 年,最近 6 个月内没有管理操作记录;同时在当前仍可查询的应用查询统计窗口内,也没有检测到您名下应用的查询请求。</span>\n</p>\n<p>\n <span style=\"font-size: 14px;\">如果您希望继续保留账户数据,请在 <b>${deleteAfter}</b> 前登录后台并执行任意管理操作,或让您名下应用再次发起一次查询请求。</span>\n</p>\n<p>\n <span style=\"font-size: 14px;\">如果在接下来的 30 天内仍无任何操作或访问记录,系统将自动移除您的账户、应用及相关上传文件。</span>\n</p>\n<hr />\n<p style=\"margin-top: 0px; padding: 0px 0px 20px; line-height: inherit; border: 0px; list-style: none; color: rgb(51, 51, 51); font-family: 微软雅黑; font-size: 14px; white-space: normal;\">\n 这是系统自动发出的邮件,请勿回复。\n</p>\n<hr />\n<p style=\"margin-bottom: 0px; padding: 0px; font-size: 14px;\">\n React Native中文网 Pushy热更新服务\n</p>\n `,\n },\n { throwOnError: true, retryOnError: false },\n );\n}\n\nasync function sendInactiveDeletedMail({\n email,\n appCount,\n}: {\n email: string;\n appCount: number;\n}) {\n const normalizedEmail = assertValidEmailAddress(\n email,\n 'inactiveUsers.deleted',\n );\n const safeEmail = getSafeEmail(normalizedEmail);\n const deletedAt = dayjs().format('YYYY-MM-DD HH:mm:ss');\n\n await sendMail(\n {\n to: normalizedEmail,\n subject: 'React Native中文网: 您的 Pushy 热更新账户数据已被清理',\n html: `\n<p>\n <span style=\"font-size: 14px;\">亲爱的 <b>${safeEmail}</b>,您好!</span>\n</p>\n<p>\n <span style=\"font-size: 14px;\">由于您的 Pushy 热更新账户在通知后的 30 天观察期内仍无任何操作或访问记录,系统已于 <b>${deletedAt}</b> 完成数据清理。</span>\n</p>\n<p>\n <span style=\"font-size: 14px;\">本次已移除的数据包括:账户信息、名下 ${appCount} 个应用,以及相关上传文件。</span>\n</p>\n<hr />\n<p style=\"margin-top: 0px; padding: 0px 0px 20px; line-height: inherit; border: 0px; list-style: none; color: rgb(51, 51, 51); font-family: 微软雅黑; font-size: 14px; white-space: normal;\">\n 这是系统自动发出的邮件,请勿回复。\n</p>\n<hr />\n<p style=\"margin-bottom: 0px; padding: 0px; font-size: 14px;\">\n React Native中文网 Pushy热更新服务\n</p>\n `,\n },\n { throwOnError: true, retryOnError: false },\n );\n}\n\nasync function deleteUserUploads({\n userId,\n hashes,\n dryRun,\n}: {\n userId: number;\n hashes: string[];\n dryRun?: boolean;\n}) {\n if (hashes.length === 0) {\n return;\n }\n\n const [sharedPackages, sharedVersions] = await Promise.all([\n prisma.packages.findMany({\n where: {\n hash: {\n in: hashes,\n },\n apps: {\n is: {\n userId: {\n not: userId,\n },\n },\n },\n },\n select: {\n hash: true,\n },\n }),\n prisma.versions.findMany({\n where: {\n hash: {\n in: hashes,\n },\n apps: {\n is: {\n userId: {\n not: userId,\n },\n },\n },\n },\n select: {\n hash: true,\n },\n }),\n ]);\n\n const sharedHashes = new Set([\n ...sharedPackages.map((item) => item.hash),\n ...sharedVersions.map((item) => item.hash),\n ]);\n const deletableHashes = hashes.filter((hash) => !sharedHashes.has(hash));\n\n if (__DEV__ || deletableHashes.length === 0) {\n return;\n }\n\n if (dryRun) {\n console.log(\n `Dry-run: would delete ${deletableHashes.length} OSS objects for inactive user ${userId}`,\n );\n return;\n }\n\n for (let i = 0; i < deletableHashes.length; i += 1000) {\n const batch = deletableHashes.slice(i, i + 1000);\n if (batch.length === 0) {\n continue;\n }\n await ossClient.deleteMulti(batch);\n }\n}\n\nasync function deleteInactiveUserData(\n userId: number,\n options: DeleteInactiveUserOptions = {},\n): Promise<DeletedInactiveUserData | null> {\n const dryRun = options.dryRun === true;\n const notifyDeletedMail = options.notifyDeletedMail !== false;\n const user = await prisma.users.findUnique({\n where: {\n id: userId,\n },\n select: {\n id: true,\n email: true,\n apps: {\n select: {\n id: true,\n appKey: true,\n packages: {\n select: {\n hash: true,\n },\n },\n versions: {\n select: {\n hash: true,\n },\n },\n },\n },\n apiTokens: {\n select: {\n id: true,\n },\n },\n },\n });\n const createdAtMetadataByUserId = await getUserCreatedAtMetadataMap([userId]);\n const createdAtMetadata = createdAtMetadataByUserId.get(userId);\n\n if (!user) {\n if (dryRun) {\n console.log(\n `Dry-run: would remove missing inactive user ${userId} from cleanup queues`,\n );\n } else {\n await redis.zrem(cleanRedisKey.pendingCleanupUsers, String(userId));\n await redis.zrem(cleanRedisKey.skipScanUsers, String(userId));\n }\n return null;\n }\n\n if (__DEV__) {\n console.log(`Development mode: skip deleting inactive user ${userId}`);\n return null;\n }\n\n const hashes = Array.from(\n new Set(\n user.apps.flatMap((app) => [\n ...app.packages.map((pkg) => pkg.hash),\n ...app.versions.map((version) => version.hash),\n ]),\n ),\n );\n const tokenIds = user.apiTokens.map((token) => token.id);\n\n await deleteUserUploads({\n userId,\n hashes,\n dryRun,\n });\n\n if (dryRun) {\n console.log(`Dry-run: would delete inactive user ${userId}`);\n return {\n email: user.email,\n createdAt: createdAtMetadata?.createdAtText ?? null,\n appCount: user.apps.length,\n };\n }\n\n await prisma.$transaction(async (tx) => {\n if (tokenIds.length > 0) {\n await tx.audit_logs.deleteMany({\n where: {\n apiTokenId: {\n in: tokenIds,\n },\n },\n });\n }\n\n await tx.audit_logs.deleteMany({\n where: {\n userId,\n },\n });\n\n if (hashes.length > 0) {\n await tx.tasks.deleteMany({\n where: {\n OR: [\n {\n fromHash: {\n in: hashes,\n },\n },\n {\n toHash: {\n in: hashes,\n },\n },\n ],\n },\n });\n }\n\n await tx.users.delete({\n where: {\n id: userId,\n },\n });\n });\n\n let mailError: string | undefined;\n if (notifyDeletedMail) {\n try {\n await sendInactiveDeletedMail({\n email: user.email,\n appCount: user.apps.length,\n });\n } catch (error) {\n mailError = getErrorMessage(error);\n console.error('Failed to send inactive cleanup deletion email', {\n email: user.email,\n userId,\n error,\n });\n }\n }\n\n const pipeline = redis.pipeline();\n pipeline.del(`userId_${user.id}`);\n pipeline.del(`userEmail_${user.email}`);\n pipeline.del(`apps_${user.id}`);\n pipeline.zrem(cleanRedisKey.pendingCleanupUsers, String(user.id));\n pipeline.zrem(cleanRedisKey.skipScanUsers, String(user.id));\n for (const app of user.apps) {\n pipeline.del(`appId_${app.id}`);\n if (app.appKey) {\n pipeline.del(`appKey_${app.appKey}`);\n }\n }\n await pipeline.exec();\n\n return {\n email: user.email,\n createdAt: createdAtMetadata?.createdAtText ?? null,\n appCount: user.apps.length,\n mailError,\n };\n}\n\nasync function moveUserToSkipScan({\n userId,\n skipUntil,\n dryRun,\n}: {\n userId: number;\n skipUntil: Date;\n dryRun?: boolean;\n}) {\n if (dryRun) {\n console.log(\n `Dry-run: would move user ${userId} back to skip-scan until ${skipUntil.toISOString()}`,\n );\n return;\n }\n\n const pipeline = redis.pipeline();\n pipeline.zrem(cleanRedisKey.pendingCleanupUsers, String(userId));\n pipeline.zadd(\n cleanRedisKey.skipScanUsers,\n skipUntil.getTime(),\n String(userId),\n );\n await pipeline.exec();\n}\n\nexport async function processPendingCleanupUsers(\n options: DailyJobOptions = {},\n): Promise<InactiveCleanupProcessResult> {\n const dryRun = options.dryRun === true;\n const pendingUsers = await getUserZsetEntries(\n cleanRedisKey.pendingCleanupUsers,\n );\n const userIds = pendingUsers.map((entry) => entry.userId);\n const [createdAtMetadataByUserId, users] = await Promise.all([\n getUserCreatedAtMetadataMap(userIds),\n userIds.length > 0\n ? prisma.users.findMany({\n where: {\n id: {\n in: userIds,\n },\n },\n select: {\n id: true,\n email: true,\n status: true,\n apps: {\n select: {\n appKey: true,\n },\n },\n },\n })\n : [],\n ]);\n const userMap = new Map(users.map((user) => [user.id, user]));\n\n let deletedPendingUsers = 0;\n let deletedUnverifiedUsers = 0;\n let restoredUsers = 0;\n let removedUsers = 0;\n let stopScanning = false;\n let scanStopReason: string | null = null;\n const restoredEntries: InactiveCleanupReportEntry[] = [];\n const deletedPendingEntries: InactiveCleanupReportEntry[] = [];\n const deletedUnverifiedEntries: InactiveCleanupReportEntry[] = [];\n const mailErrors: string[] = [];\n\n for (const pendingUser of pendingUsers) {\n const user = userMap.get(pendingUser.userId);\n\n if (!user || isAdmin(user.email)) {\n if (dryRun) {\n console.log(\n `Dry-run: would remove user ${pendingUser.userId} from cleanup queues`,\n );\n } else {\n await redis.zrem(\n cleanRedisKey.pendingCleanupUsers,\n String(pendingUser.userId),\n );\n await redis.zrem(\n cleanRedisKey.skipScanUsers,\n String(pendingUser.userId),\n );\n }\n removedUsers++;\n continue;\n }\n\n const createdAtMetadata = createdAtMetadataByUserId.get(user.id);\n\n if (\n user.status === 'unverified' &&\n createdAtMetadata?.olderThanUnverifiedCutoff\n ) {\n const deleted = await deleteInactiveUserData(user.id, {\n ...options,\n notifyDeletedMail: false,\n });\n if (deleted) {\n deletedUnverifiedUsers++;\n deletedUnverifiedEntries.push({\n ...buildReportEntryBase(deleted),\n appCount: deleted.appCount,\n });\n if (deleted.mailError) {\n mailErrors.push(`deleted:${deleted.email} - ${deleted.mailError}`);\n }\n }\n continue;\n }\n\n const appKeys = user.apps\n .map((app) => app.appKey)\n .filter((appKey): appKey is string => Boolean(appKey));\n const activityState = await getUserActivityState({\n userId: user.id,\n appKeys,\n });\n\n if (!activityState.cleanupEligible && activityState.skipUntil) {\n await moveUserToSkipScan({\n userId: user.id,\n skipUntil: activityState.skipUntil,\n dryRun,\n });\n restoredUsers++;\n restoredEntries.push({\n ...buildReportEntryBase({\n email: user.email,\n createdAt: createdAtMetadata?.createdAtText ?? null,\n }),\n skipUntil: formatDateTime(activityState.skipUntil),\n });\n continue;\n }\n\n if (Date.now() < pendingUser.score) {\n continue;\n }\n\n const deleted = await deleteInactiveUserData(user.id, options);\n if (deleted) {\n deletedPendingUsers++;\n deletedPendingEntries.push({\n ...buildReportEntryBase(deleted),\n appCount: deleted.appCount,\n });\n if (deleted.mailError) {\n mailErrors.push(`deleted:${deleted.email} - ${deleted.mailError}`);\n if (!stopScanning && containsQuotaError(deleted.mailError)) {\n stopScanning = true;\n scanStopReason = buildQuotaStopReason(deleted.mailError);\n }\n }\n }\n }\n\n return {\n deletedPendingUsers,\n deletedUnverifiedUsers,\n restoredUsers,\n removedUsers,\n stopScanning,\n scanStopReason,\n restoredEntries,\n deletedPendingEntries,\n deletedUnverifiedEntries,\n mailErrors,\n };\n}\n\nexport async function cleanExpiredSkipScanUsers(\n options: DailyJobOptions = {},\n): Promise<SkipScanState> {\n const dryRun = options.dryRun === true;\n const now = Date.now();\n const skipEntries = await getUserZsetEntries(cleanRedisKey.skipScanUsers);\n const expiredUserIds = skipEntries\n .filter((entry) => entry.score <= now)\n .map((entry) => String(entry.userId));\n if (!dryRun && expiredUserIds.length > 0) {\n await redis.zremrangebyscore(cleanRedisKey.skipScanUsers, 0, now);\n }\n\n const activeSkipUserIds = skipEntries\n .filter((entry) => entry.score > now)\n .map((entry) => entry.userId);\n\n return {\n expiredCount: expiredUserIds.length,\n activeSkipUserIds,\n };\n}\n\nasync function cleanupExpiredUnverifiedUsers({\n excludedUserIds,\n dryRun,\n}: {\n excludedUserIds: number[];\n dryRun?: boolean;\n}): Promise<DirectCleanupUsersResult> {\n const users = await prisma.users.findMany({\n where: {\n status: 'unverified',\n createdAt: {\n lt: getInactiveUnverifiedDeleteCutoff(),\n },\n ...(excludedUserIds.length > 0\n ? {\n id: {\n notIn: excludedUserIds,\n },\n }\n : {}),\n },\n select: {\n id: true,\n email: true,\n },\n });\n\n let deletedUnverifiedUsers = 0;\n const deletedUnverifiedEntries: InactiveCleanupReportEntry[] = [];\n const deletedUserIds: number[] = [];\n\n for (const user of users) {\n if (isAdmin(user.email)) {\n continue;\n }\n\n deletedUserIds.push(user.id);\n const deleted = await deleteInactiveUserData(user.id, {\n dryRun,\n notifyDeletedMail: false,\n });\n if (deleted) {\n deletedUnverifiedUsers++;\n deletedUnverifiedEntries.push({\n ...buildReportEntryBase(deleted),\n appCount: deleted.appCount,\n });\n }\n }\n\n return {\n deletedUnverifiedUsers,\n deletedUnverifiedEntries,\n deletedUserIds,\n };\n}\n\nexport async function scanInactiveUsers({\n excludedUserIds,\n dryRun,\n}: {\n excludedUserIds: number[];\n dryRun?: boolean;\n}): Promise<ScanInactiveUsersResult> {\n const users = await prisma.users.findMany({\n where: {\n createdAt: {\n lt: getInactiveUserScanCutoff(),\n },\n ...(excludedUserIds.length > 0\n ? {\n id: {\n notIn: excludedUserIds,\n },\n }\n : {}),\n },\n select: {\n id: true,\n email: true,\n apps: {\n select: {\n appKey: true,\n },\n },\n },\n });\n const createdAtMetadataByUserId = await getUserCreatedAtMetadataMap(\n users.map((user) => user.id),\n );\n\n let notifiedUsers = 0;\n let silentPendingUsers = 0;\n let scanStopped = false;\n let scanStopReason: string | null = null;\n const pendingEntries: InactiveCleanupReportEntry[] = [];\n const mailErrors: string[] = [];\n\n for (const user of users) {\n if (isAdmin(user.email)) {\n continue;\n }\n\n const appKeys = user.apps\n .map((app) => app.appKey)\n .filter((appKey): appKey is string => Boolean(appKey));\n const activityState = await getUserActivityState({\n userId: user.id,\n appKeys,\n });\n\n if (!activityState.cleanupEligible) {\n if (activityState.skipUntil) {\n if (dryRun) {\n console.log(\n `Dry-run: would keep user ${user.id} in skip-scan until ${activityState.skipUntil.toISOString()}`,\n );\n } else {\n await redis.zadd(\n cleanRedisKey.skipScanUsers,\n activityState.skipUntil.getTime(),\n String(user.id),\n );\n }\n }\n continue;\n }\n\n const deleteAfterMs = getPendingDeleteAfterMs();\n const createdAtMetadata = createdAtMetadataByUserId.get(user.id);\n const shouldSkipNotification =\n createdAtMetadata?.olderThanSilentPendingCutoff === true;\n if (dryRun) {\n if (shouldSkipNotification) {\n console.log(\n `Dry-run: would queue >5y inactive user ${user.email} without warning email`,\n );\n } else {\n console.log(\n `Dry-run: would send inactive cleanup warning email to ${user.email}`,\n );\n }\n } else {\n if (shouldSkipNotification) {\n console.log(\n `Queue >5y inactive user ${user.email} without warning email`,\n );\n } else {\n if (notifiedUsers >= INACTIVE_CLEANUP_DAILY_MAIL_LIMIT) {\n scanStopped = true;\n scanStopReason = buildMailLimitStopReason();\n console.warn(scanStopReason);\n break;\n }\n\n try {\n await sendInactiveCleanupMail({\n email: user.email,\n deleteAfterMs,\n });\n } catch (error) {\n const message = getErrorMessage(error);\n console.error('Failed to send inactive cleanup warning email', {\n email: user.email,\n userId: user.id,\n error,\n });\n mailErrors.push(`pending:${user.email} - ${message}`);\n if (containsQuotaError(message)) {\n scanStopped = true;\n scanStopReason = buildQuotaStopReason(message);\n console.warn(scanStopReason);\n break;\n }\n continue;\n }\n }\n }\n if (!dryRun) {\n await redis.zadd(\n cleanRedisKey.pendingCleanupUsers,\n deleteAfterMs,\n String(user.id),\n );\n }\n if (shouldSkipNotification) {\n silentPendingUsers++;\n } else {\n notifiedUsers++;\n if (!dryRun && notifiedUsers >= INACTIVE_CLEANUP_DAILY_MAIL_LIMIT) {\n scanStopped = true;\n scanStopReason = buildMailLimitStopReason();\n }\n }\n pendingEntries.push({\n ...buildReportEntryBase({\n email: user.email,\n createdAt: createdAtMetadata?.createdAtText ?? null,\n }),\n deleteAfter: formatDate(deleteAfterMs),\n });\n\n if (scanStopped) {\n console.warn(scanStopReason);\n break;\n }\n }\n\n return {\n pendingUsers: notifiedUsers + silentPendingUsers,\n pendingNotifiedUsers: notifiedUsers,\n pendingSilentUsers: silentPendingUsers,\n scanStopped,\n scanStopReason,\n pendingEntries,\n mailErrors,\n };\n}\n\nexport async function runInactiveUserCleanupJob(\n options: DailyJobOptions = {},\n): Promise<InactiveUserCleanupResult> {\n const pendingUsers = await getUserZsetEntries(\n cleanRedisKey.pendingCleanupUsers,\n );\n const {\n deletedPendingUsers,\n deletedUnverifiedUsers: queuedDeletedUnverifiedUsers,\n restoredUsers,\n removedUsers,\n stopScanning,\n scanStopReason: preScanStopReason,\n restoredEntries,\n deletedPendingEntries,\n deletedUnverifiedEntries: queuedDeletedUnverifiedEntries,\n mailErrors: deletedMailErrors,\n } = await processPendingCleanupUsers(options);\n const { expiredCount: expiredSkipUsers, activeSkipUserIds } =\n await cleanExpiredSkipScanUsers(options);\n const {\n deletedUnverifiedUsers: directDeletedUnverifiedUsers,\n deletedUnverifiedEntries: directDeletedUnverifiedEntries,\n deletedUserIds: directDeletedUserIds,\n } = await cleanupExpiredUnverifiedUsers({\n excludedUserIds: pendingUsers.map((entry) => entry.userId),\n dryRun: options.dryRun,\n });\n const scanResult = stopScanning\n ? {\n pendingUsers: 0,\n pendingNotifiedUsers: 0,\n pendingSilentUsers: 0,\n scanStopped: true,\n scanStopReason: preScanStopReason,\n pendingEntries: [],\n mailErrors: [] as string[],\n }\n : await scanInactiveUsers({\n excludedUserIds: [\n ...pendingUsers.map((entry) => entry.userId),\n ...activeSkipUserIds,\n ...directDeletedUserIds,\n ],\n dryRun: options.dryRun,\n });\n\n const deletedUnverifiedUsers =\n queuedDeletedUnverifiedUsers + directDeletedUnverifiedUsers;\n const deletedUsers = deletedPendingUsers + deletedUnverifiedUsers;\n\n console.log(\n `Inactive cleanup processed. pending=${scanResult.pendingUsers}, notified=${scanResult.pendingNotifiedUsers}, silent=${scanResult.pendingSilentUsers}, deletedPending=${deletedPendingUsers}, deletedUnverified=${deletedUnverifiedUsers}, restored=${restoredUsers}, removed=${removedUsers}, skipExpired=${expiredSkipUsers}, scanStopped=${scanResult.scanStopped}`,\n );\n\n return {\n pendingUsers: scanResult.pendingUsers,\n pendingNotifiedUsers: scanResult.pendingNotifiedUsers,\n pendingSilentUsers: scanResult.pendingSilentUsers,\n restoredUsers,\n deletedUsers,\n deletedPendingUsers,\n deletedUnverifiedUsers,\n removedUsers,\n expiredSkipUsers,\n scanStopped: scanResult.scanStopped,\n scanStopReason: scanResult.scanStopReason,\n pendingEntries: scanResult.pendingEntries,\n restoredEntries,\n deletedPendingEntries,\n deletedUnverifiedEntries: [\n ...queuedDeletedUnverifiedEntries,\n ...directDeletedUnverifiedEntries,\n ],\n mailErrors: [...deletedMailErrors, ...scanResult.mailErrors],\n };\n}\n",
8
8
  "import type { DailyJobOptions } from '../dailyjob/options';\nimport { ossClient } from '../modules/stores';\nimport { prisma } from '../utils/prisma';\nimport { __DEV__, DIFF_RETENTION_DAYS, MAX_KEYS } from './config';\n\ninterface OssObjectInfo {\n name: string;\n lastModified: string;\n}\n\ninterface OssCleanupStats {\n diffCount: number;\n pvCount: number;\n totalCount: number;\n deletedCount: number;\n}\n\nexport type { OssCleanupStats };\n\nfunction isDiffArtifact(name: string) {\n return /(\\.(p|ph|h)?diff)$|(\\.empty)$/.test(name);\n}\n\nasync function deleteObjects(keys: string[]) {\n if (__DEV__ || keys.length === 0) {\n return;\n }\n await ossClient.deleteMulti(keys).catch((error) => {\n console.error(\n `Failed to delete ${keys.length} objects: ${keys.join('\\n')}`,\n error,\n );\n });\n}\n\nasync function findExistingHashes(hashes: string[]) {\n if (hashes.length === 0) {\n return new Set<string>();\n }\n\n const [versions, packages] = await Promise.all([\n prisma.versions.findMany({\n where: {\n hash: { in: hashes },\n },\n select: {\n hash: true,\n },\n }),\n prisma.packages.findMany({\n where: {\n hash: { in: hashes },\n },\n select: {\n hash: true,\n },\n }),\n ]);\n\n return new Set([\n ...versions.map((version) => version.hash),\n ...packages.map((pkg) => pkg.hash),\n ]);\n}\n\nasync function processObjects(\n objects: OssObjectInfo[],\n stats: OssCleanupStats,\n options: DailyJobOptions,\n) {\n const now = Date.now();\n const expireBefore = now - DIFF_RETENTION_DAYS * 24 * 3600 * 1000;\n const diffToDelete: string[] = [];\n const pvCandidates: OssObjectInfo[] = [];\n\n for (const { name, lastModified } of objects) {\n if (isDiffArtifact(name)) {\n if (new Date(lastModified).getTime() <= expireBefore) {\n stats.deletedCount++;\n stats.diffCount++;\n console.log(\n `${stats.deletedCount} diff/${stats.diffCount} ${name} ${new Date(lastModified).toISOString()}`,\n );\n diffToDelete.push(name);\n }\n continue;\n }\n pvCandidates.push({ name, lastModified });\n }\n\n const existingHashes = await findExistingHashes(\n pvCandidates.map((pv) => pv.name),\n );\n const pvToDelete = pvCandidates.reduce((acc, pv) => {\n if (!existingHashes.has(pv.name)) {\n stats.deletedCount++;\n stats.pvCount++;\n console.log(\n `${stats.deletedCount} pv/${stats.pvCount} ${pv.name} ${new Date(pv.lastModified).toISOString()}`,\n );\n acc.push(pv.name);\n }\n return acc;\n }, [] as string[]);\n\n const keysToDelete = diffToDelete.concat(pvToDelete);\n if (options.dryRun) {\n if (keysToDelete.length > 0) {\n console.log(\n `Dry-run mode: skip deleting ${keysToDelete.length} OSS objects`,\n );\n }\n return;\n }\n\n await deleteObjects(keysToDelete);\n}\n\nasync function listAll(continuationToken?: string): Promise<OssObjectInfo[]> {\n const { objects = [], nextContinuationToken } = await ossClient.listV2(\n { 'continuation-token': continuationToken, 'max-keys': MAX_KEYS },\n {},\n );\n\n if (nextContinuationToken) {\n const rest = await listAll(nextContinuationToken);\n return [...objects, ...rest] as OssObjectInfo[];\n }\n\n return objects as OssObjectInfo[];\n}\n\nexport async function runOssCleanup(\n options: DailyJobOptions = {},\n): Promise<OssCleanupStats> {\n const stats: OssCleanupStats = {\n diffCount: 0,\n pvCount: 0,\n totalCount: 0,\n deletedCount: 0,\n };\n const allObjects = (await listAll()).filter((obj) => obj.name !== 'test');\n stats.totalCount = allObjects.length;\n\n if (allObjects.length > 0) {\n for (let i = 0; i < allObjects.length; i += 1000) {\n await processObjects(allObjects.slice(i, i + 1000), stats, options);\n }\n console.log(\n `total: ${stats.totalCount}, diff: ${stats.diffCount}, p/v: ${stats.pvCount}`,\n );\n }\n\n console.log('OSS cleanup completed');\n return stats;\n}\n",
9
9
  "import dayjs from 'dayjs';\n\nimport { PRICING_URL } from '../../config/links';\nimport { products } from '../../config/quotas';\nimport type { users } from '../../generated/prisma/client';\nimport { sendMail } from '../modules/mail';\nimport { assertValidEmailAddress } from '../utils/email';\nimport { prisma } from '../utils/prisma';\nimport type { DailyJobOptions } from './options';\n\nexport interface TierCheckEntry {\n email: string;\n tier: string;\n tierExpiresAt: string | null;\n}\n\nexport interface TierCheckResult {\n paidUsers: number;\n expiredUsers: number;\n expiring7Users: number;\n expiring30Users: number;\n expiredEntries: TierCheckEntry[];\n expiring7Entries: TierCheckEntry[];\n expiring30Entries: TierCheckEntry[];\n mailErrors: string[];\n}\n\nasync function sendExpiredMail(\n user: Pick<users, 'email' | 'tierExpiresAt' | 'tier'>,\n) {\n const email = assertValidEmailAddress(user.email, 'tierCheck.expired');\n const safeEmail = email.replace(\n '@',\n '<span style=\"display:none\"></span>@<span style=\"display:none\"></span>',\n );\n\n await sendMail(\n {\n to: email,\n subject: 'React Native中文网: 您的Pushy热更新服务已经到期',\n html: `\n <p>\n <span style=\"font-size: 14px;\">亲爱的 <b>${safeEmail}</b></span><span style=\"font-size: 14px;\">,您好!</span>\n</p>\n<p>\n <span style=\"font-size: 14px;\">&nbsp;&nbsp;&nbsp;&nbsp;</span>\n</p>\n<p>\n <span style=\"font-size: 14px;\">&nbsp;&nbsp;&nbsp;&nbsp;感谢您使用React Native中文网提供的热更新服务,您现有的产品已经过期:</span>\n</p>\n<ul>\n<li>产品名称:${products[user.tier as keyof typeof products].title}</li>\n<li>有效期至:${dayjs(user.tierExpiresAt).format('YYYY-MM-DD')}</li>\n</ul>\n<p>\n <span style=\"font-size: 14px;\">&nbsp;&nbsp;&nbsp;&nbsp;为保证您的应用热更新服务能够持续稳定运行,请您尽快<a href=\"${PRICING_URL}\">点击此处续费</a>。</span>\n</p>\n<hr />\n<p style=\"margin-top: 0px; padding: 0px 0px 20px; line-height: inherit; border: 0px; list-style: none; color: rgb(51, 51, 51); font-family: 微软雅黑; font-size: 14px; white-space: normal;\">\n &nbsp;&nbsp;&nbsp;&nbsp;这是系统自动发出的邮件,请勿回复。\n</p>\n<hr />\n<p style=\"margin-bottom: 0px; padding: 0px; font-size: 14px;\">\nReact Native中文网 Pushy热更新服务\n</p>\n `,\n },\n { throwOnError: true },\n );\n}\n\nasync function sendExpiringMail(\n user: Pick<users, 'email' | 'tierExpiresAt' | 'tier'>,\n remainingDays: number,\n) {\n const email = assertValidEmailAddress(user.email, 'tierCheck.expiring');\n const safeEmail = email.replace(\n '@',\n '<span style=\"display:none\"></span>@<span style=\"display:none\"></span>',\n );\n\n await sendMail(\n {\n to: email,\n subject: 'React Native中文网: 您的Pushy热更新服务即将到期',\n html: `\n <p>\n <span style=\"font-size: 14px;\">亲爱的 <b>${safeEmail}</b></span><span style=\"font-size: 14px;\">,您好!</span>\n</p>\n<p>\n <span style=\"font-size: 14px;\">&nbsp;&nbsp;&nbsp;&nbsp;</span>\n</p>\n<p>\n <span style=\"font-size: 14px;\">&nbsp;&nbsp;&nbsp;&nbsp;感谢您使用React Native中文网提供的热更新服务,您现有的产品有效期限已经不足${remainingDays}日:</span>\n</p>\n<ul>\n<li>产品名称:${products[user.tier as keyof typeof products].title}</li>\n<li>有效期至:${dayjs(user.tierExpiresAt).format('YYYY-MM-DD')}</li>\n</ul>\n<p>\n <span style=\"font-size: 14px;\">&nbsp;&nbsp;&nbsp;&nbsp;为保证您的应用热更新服务能够持续稳定运行,请您尽快<a href=\"${PRICING_URL}\">点击此处续费</a>。</span>\n</p>\n<hr />\n<p style=\"margin-top: 0px; padding: 0px 0px 20px; line-height: inherit; border: 0px; list-style: none; color: rgb(51, 51, 51); font-family: 微软雅黑; font-size: 14px; white-space: normal;\">\n &nbsp;&nbsp;&nbsp;&nbsp;这是系统自动发出的邮件,请勿回复。\n</p>\n<hr />\n<p style=\"margin-bottom: 0px; padding: 0px; font-size: 14px;\">\nReact Native中文网 Pushy热更新服务\n</p>\n `,\n },\n { throwOnError: true },\n );\n}\n\nfunction getErrorMessage(error: unknown) {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction toTierEntry(\n user: Pick<users, 'email' | 'tier' | 'tierExpiresAt'>,\n): TierCheckEntry {\n return {\n email: user.email,\n tier: user.tier,\n tierExpiresAt: user.tierExpiresAt\n ? dayjs(user.tierExpiresAt).format('YYYY-MM-DD')\n : null,\n };\n}\n\nexport async function runTierCheckDailyJob(\n options: DailyJobOptions = {},\n): Promise<TierCheckResult> {\n const dryRun = options.dryRun === true;\n const today = dayjs();\n const users =\n (await prisma.users.findMany({\n select: {\n id: true,\n name: true,\n email: true,\n tier: true,\n tierExpiresAt: true,\n },\n where: {\n status: 'normal',\n tier: {\n not: 'free',\n },\n },\n })) || [];\n\n console.log(`Paid users: ${users.length}`);\n\n const expiredUsers: typeof users = [];\n const expiring7Users: typeof users = [];\n const expiring30Users: typeof users = [];\n const mailErrors: string[] = [];\n\n for (const user of users) {\n if (!user.tierExpiresAt) continue;\n\n const expireDay = dayjs(user.tierExpiresAt);\n const _7beforeExpireDay = expireDay.subtract(7, 'day');\n const _30beforeExpireDay = expireDay.subtract(30, 'day');\n\n if (today.isAfter(expireDay, 'day')) {\n expiredUsers.push(user);\n } else if (today.isSame(_7beforeExpireDay, 'day')) {\n expiring7Users.push(user);\n } else if (today.isSame(_30beforeExpireDay, 'day')) {\n expiring30Users.push(user);\n }\n }\n\n const emailPromises: Promise<void>[] = [];\n\n if (dryRun) {\n for (const user of expiredUsers) {\n console.log(`Dry-run: would send expired tier email to ${user.email}`);\n }\n for (const user of expiring7Users) {\n console.log(`Dry-run: would send 7-day tier email to ${user.email}`);\n }\n for (const user of expiring30Users) {\n console.log(`Dry-run: would send 30-day tier email to ${user.email}`);\n }\n } else {\n for (const user of expiredUsers) {\n console.log(`Expired: ${user.email}`);\n emailPromises.push(\n sendExpiredMail(user).catch((error) => {\n const message = getErrorMessage(error);\n console.error('Failed to send expired tier email', {\n email: user.email,\n error,\n });\n mailErrors.push(`expired:${user.email} - ${message}`);\n }),\n );\n }\n\n for (const user of expiring7Users) {\n console.log(`Expiring in 7d: ${user.email}`);\n emailPromises.push(\n sendExpiringMail(user, 7).catch((error) => {\n const message = getErrorMessage(error);\n console.error('Failed to send 7-day tier email', {\n email: user.email,\n error,\n });\n mailErrors.push(`expiring7:${user.email} - ${message}`);\n }),\n );\n }\n\n for (const user of expiring30Users) {\n console.log(`Expiring in 30d: ${user.email}`);\n emailPromises.push(\n sendExpiringMail(user, 30).catch((error) => {\n const message = getErrorMessage(error);\n console.error('Failed to send 30-day tier email', {\n email: user.email,\n error,\n });\n mailErrors.push(`expiring30:${user.email} - ${message}`);\n }),\n );\n }\n }\n\n if (dryRun && expiredUsers.length > 0) {\n console.log(\n `Dry-run: would downgrade ${expiredUsers.length} expired users to free tier`,\n );\n } else if (expiredUsers.length > 0) {\n await prisma.users.updateMany({\n where: {\n id: { in: expiredUsers.map((user) => user.id) },\n },\n data: {\n tier: 'free',\n tierExpiresAt: null,\n },\n });\n }\n\n await Promise.all(emailPromises);\n\n console.log(`Finished tier checking.\n Expired: ${expiredUsers.length}\n Expiring in 7d: ${expiring7Users.length}\n Expiring in 30d: ${expiring30Users.length}`);\n\n return {\n paidUsers: users.length,\n expiredUsers: expiredUsers.length,\n expiring7Users: expiring7Users.length,\n expiring30Users: expiring30Users.length,\n expiredEntries: expiredUsers.map(toTierEntry),\n expiring7Entries: expiring7Users.map(toTierEntry),\n expiring30Entries: expiring30Users.map(toTierEntry),\n mailErrors,\n };\n}\n",
10
10
  "export const PRICING_URL = 'https://pushy.reactnative.cn/pricing.html';\n",
@@ -13,7 +13,7 @@
13
13
  "export interface DailyJobOptions {\n dryRun?: boolean;\n}\n\nexport const DAILYJOB_DRY_RUN_ENV = 'DAILYJOB_DRY_RUN';\n\nexport function parseDryRunEnv(value?: string) {\n if (!value) {\n return false;\n }\n\n return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());\n}\n\nexport function parseDailyJobOptions(\n env: NodeJS.ProcessEnv = process.env,\n): DailyJobOptions {\n return {\n dryRun: parseDryRunEnv(env[DAILYJOB_DRY_RUN_ENV]),\n };\n}\n",
14
14
  "#!/usr/bin/env bun\nimport { runDailyJob } from './dailyjob/main';\nimport { parseDailyJobOptions } from './dailyjob/options';\n\nrunDailyJob(parseDailyJobOptions());\n"
15
15
  ],
16
- "mappings": "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",
17
- "debugId": "D0C6BB5AC6CA8A2964756E2164756E21",
16
+ "mappings": "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",
17
+ "debugId": "BEAA84477BD1186764756E2164756E21",
18
18
  "names": []
19
19
  }