From a4e9d56b8ac63add5ef5a6ade82292f608a9d5b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E7=85=9C?= <1871263099@qq.com> Date: Thu, 1 Oct 2026 18:55:15 +0800 Subject: [PATCH] feat: add reusable private and shared account icons --- README.md | 11 +- apps/api/assets/icons/01.png | Bin 0 -> 1709 bytes apps/api/assets/icons/02.png | Bin 0 -> 1802 bytes apps/api/assets/icons/03.png | Bin 0 -> 1711 bytes apps/api/assets/icons/04.png | Bin 0 -> 1909 bytes apps/api/assets/icons/05.png | Bin 0 -> 1720 bytes apps/api/assets/icons/06.png | Bin 0 -> 2014 bytes apps/api/assets/icons/07.png | Bin 0 -> 1576 bytes apps/api/assets/icons/08.png | Bin 0 -> 1945 bytes apps/api/assets/icons/09.png | Bin 0 -> 2716 bytes apps/api/assets/icons/10.png | Bin 0 -> 1891 bytes apps/api/assets/icons/11.png | Bin 0 -> 1255 bytes apps/api/assets/icons/12.png | Bin 0 -> 1492 bytes apps/api/assets/icons/13.png | Bin 0 -> 2393 bytes apps/api/assets/icons/14.png | Bin 0 -> 1619 bytes apps/api/assets/icons/15.png | Bin 0 -> 1982 bytes apps/api/assets/icons/16.png | Bin 0 -> 1223 bytes apps/api/assets/icons/17.png | Bin 0 -> 2496 bytes apps/api/assets/icons/18.png | Bin 0 -> 926 bytes apps/api/assets/icons/19.png | Bin 0 -> 806 bytes apps/api/assets/icons/20.png | Bin 0 -> 39993 bytes apps/api/assets/icons/sources.json | 122 +++++++ apps/api/package.json | 12 +- .../005_account_icons/migration.sql | 16 + apps/api/prisma/schema.prisma | 18 + apps/api/scripts/convert-icons.py | 14 + apps/api/scripts/dev.cjs | 2 +- apps/api/scripts/download-icons.cjs | 70 ++++ apps/api/scripts/seed-icons.cjs | 41 +++ apps/api/src/backup.ts | 63 +++- apps/api/src/icons.ts | 134 +++++++ apps/api/src/main.ts | 17 +- apps/api/src/portfolio.ts | 4 + apps/api/src/validation.ts | 1 + apps/api/src/zip.ts | 20 +- apps/api/test/icons.test.ts | 199 +++++++++++ apps/api/test/zip.test.ts | 14 + apps/web/src/App.tsx | 28 +- apps/web/src/IconLibrary.tsx | 235 ++++++++++++ apps/web/src/api.ts | 1 + apps/web/src/style.css | 95 +++++ docs/acceptance.md | 6 + docs/architecture.md | 6 + pnpm-lock.yaml | 334 ++++++++++++++++++ 44 files changed, 1443 insertions(+), 20 deletions(-) create mode 100644 apps/api/assets/icons/01.png create mode 100644 apps/api/assets/icons/02.png create mode 100644 apps/api/assets/icons/03.png create mode 100644 apps/api/assets/icons/04.png create mode 100644 apps/api/assets/icons/05.png create mode 100644 apps/api/assets/icons/06.png create mode 100644 apps/api/assets/icons/07.png create mode 100644 apps/api/assets/icons/08.png create mode 100644 apps/api/assets/icons/09.png create mode 100644 apps/api/assets/icons/10.png create mode 100644 apps/api/assets/icons/11.png create mode 100644 apps/api/assets/icons/12.png create mode 100644 apps/api/assets/icons/13.png create mode 100644 apps/api/assets/icons/14.png create mode 100644 apps/api/assets/icons/15.png create mode 100644 apps/api/assets/icons/16.png create mode 100644 apps/api/assets/icons/17.png create mode 100644 apps/api/assets/icons/18.png create mode 100644 apps/api/assets/icons/19.png create mode 100644 apps/api/assets/icons/20.png create mode 100644 apps/api/assets/icons/sources.json create mode 100644 apps/api/prisma/migrations/005_account_icons/migration.sql create mode 100644 apps/api/scripts/convert-icons.py create mode 100644 apps/api/scripts/download-icons.cjs create mode 100644 apps/api/scripts/seed-icons.cjs create mode 100644 apps/api/src/icons.ts create mode 100644 apps/api/test/icons.test.ts create mode 100644 apps/web/src/IconLibrary.tsx diff --git a/README.md b/README.md index 7417bfc..8d93d9b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ if (!(Test-Path apps/api/.env)) { Copy-Item apps/api/.env.example apps/api/.env # 仅在本地 .env 设置 DATABASE_URL,先检查数据库是否存在 pnpm db:generate pnpm db:migrate +pnpm --filter @worthpath/api icons:seed # 追加预置共享图标,重复执行不覆盖已有记录 pnpm dev ``` @@ -35,10 +36,16 @@ pnpm check:staged # git add 后,提交前检查本地凭证和禁止提交的 汇率使用 [Frankfurter 公共日汇率 API](https://frankfurter.dev/)。请求使用固定币种表,不发送用户选择、身份或金额。进程运行时每小时检查,每天尝试一次;添加项目、修改本位币会重新触发检查。失败保留原币和历史汇率,显示缺失或实际使用日期,可以点击重试。自动更新保留已有同日历史导入汇率。首次使用不会自动补齐早期历史汇率,缺失日期不绘制完整总额;原币和已有汇率始终保留。 -备份 v1 最多 8 MB / 1000 项目 / 20000 条历史,导入先预览、再明确确认。首版只追加新项目并重建关系,不覆盖已有项目;按项目 ID 和导入来源识别重复;同日汇率冲突会拒绝整次导入。已有本位币保留,空空间恢复备份本位币。文件包含财务数据,应由用户妥善保存。 +新版 ZIP 备份不限制记录条数,导入先预览、再明确确认。首版只追加新项目并重建关系,不覆盖已有项目;按项目 ID 和导入来源识别重复;同日汇率冲突会拒绝整次导入。已有本位币保留,空空间恢复备份本位币。文件包含财务数据,应由用户妥善保存。 设计见 [数据模型与结构](docs/architecture.md),已验证范围与后续工作见 [首版验收](docs/acceptance.md)。生产需要 HTTPS、COOKIE_SECURE=true、WEB_ORIGIN 为准确站点地址;前端构建由反向代理托管并代理 /api。API 生产启动:在 `apps/api` 中执行 `node dist/main.js`。生产认证限速需迁移到共享存储,数据库用户需最小权限,部署与运行监控尚未配置。 -备份下载为 ZIP:manifest、settings、currencies、accounts、assets、debts、history、links、rates 各一个 JSON 文件,包含隐藏项目及全部历史,不含密码或会话凭据。导入先上传并预览,再确认追加;旧 JSON 备份继续兼容。不限制项目、历史、关联或汇率条数;上传文件最大 512 MB,ZIP 解压总计最大 1 GB,用于保护服务器资源。 +备份下载为 ZIP:manifest、settings、currencies、accounts、assets、debts、history、links、rates、icons 各一个 JSON 文件,包含隐藏项目及全部历史,不含密码或会话凭据。导入先上传并预览,再确认追加;旧 JSON 备份继续兼容。不限制项目、历史、关联或汇率条数;上传文件最大 512 MB,ZIP 解压总计最大 1 GB,用于保护服务器资源。 内网穿透临时测试:本地 `apps/api/.env` 可设置 `WEB_ORIGIN=*` 并重启 API,允许来自任意 HTTP/HTTPS 站点的浏览器写入请求,仍需登录身份并校验数据归属。缺失或无效 Origin 仍拒绝;`NODE_ENV=production` 禁止此通配符。测试结束后恢复准确的站点地址。 + +账户图标:新增或编辑账户时选择可复用图标;设置页面提供图标库及中文名称搜索。直接上传默认私有,仅当前用户能检索、读取和使用;勾选共享并明确确认公开后,所有登录用户均可搜索复用,名称必须包含中文。支持静态 PNG/JPG/WebP,单张最大 2 MB,转为最长边 256 像素的 PNG 并去除图片元数据。同一用户相同图片和可见范围会复用现有图标。账户图标通过外键关联,不复制图片。 + +预置 17 家银行及支付宝、微信、京东金融共 20 个图标,资源及来源清单在 `apps/api/assets/icons`;银行来自公开银行标识库,支付平台来自官方网站资源。图标版权与商标归相应品牌所有,用于识别账户,不代表品牌合作或授权。运行 `pnpm --filter @worthpath/api icons:seed` 初始化共享库,不会覆盖已有图标。可离线使用已提交的 PNG,无需访问外部图标网站。 + +ZIP 格式 v4 增加 icons.json(图标名称、图片、内容校验值),包含自己的全部图标及账户引用的共享图标。导入会重建关联并将图标恢复为私有,相同图片复用,避免自动公开;旧 v3 ZIP 和旧 JSON 仍可导入。清空个人数据会删除私有图标,已发布共享图标保留供其他用户使用。 diff --git a/apps/api/assets/icons/01.png b/apps/api/assets/icons/01.png new file mode 100644 index 0000000000000000000000000000000000000000..ada6b084fb07de008efbe7c4d8998b4a5465e5ce GIT binary patch literal 1709 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 FV1laIIamMy literal 0 HcmV?d00001 diff --git a/apps/api/assets/icons/04.png b/apps/api/assets/icons/04.png new file mode 100644 index 0000000000000000000000000000000000000000..44df52dd890561e62822456508286afe1d0f396f GIT binary patch literal 1909 zcmV-*2a5QKP)RCt{2TWO3`MG$Tj#RC*i5D^y`W?om#t5#z0*2F6&8gHV; z#9I>+@fMF5UGE?U6$4}ic6MI3M8#k<#zd5jMiVa#YKUjNq9Rd|OGM=|HM>hzneKPZ zo82MvCH-%@ruutb)zwwiLNw7t6HPSHL=zD)w@?_NkuEeqvkaA9N8tDTKG~b}Q-2k0va=AUk_J{!Ja3kp{4eBd{YDe#!HF(GaEt>b<%U zQ;jI3Ii;4Cp`yQGq7XZ3>EEY;HuVWPy-g3)GUF9b5&h{@GC*%8@*$v>YM{65i=}rA zs#WnxqD>m8wN;26`jniK=ldGfkI_k?KQyRzKC-8c9Jy;nMoY_3+wa?i*ol3bK?Eb| z295Lvn^JVMQ_r;`?%XHj1gMUQ{B=Ps(UPv5TPO_KhU9R9{`-5+YcZ&%XrMI_chjO7 zh=WBvVI&<9ML~`9sh0j(E?IF2>cPxW{$3jC5sr-_ko5ZWTyBu42g>>Ufg05>A^v5Z zMs-uG5Cg;HB_(Lwp zdQ@ZygKA0$IZL&a2gH$C3xc-lC+UXJog|B=i7XHliif%491ZG=*`tfY;>xWhK@0jmkUJ_X`xYlik;W@-@(Ujq2sV z^N){264GOKc@(4~r;IqkA$&P+W2bV%Xm<|KYC(*Jtf^raQa-UNDc$Oa%erdUpUBWa zQ|;TI9*8i1epckf)WfOoM2B=uaVK0vS8%q-Dq$yqsL+t#3N4!g5;CDrRh5UM}JZtGkL zvCS`g{*kc>3V^P&E9Ab6R<^Xo9JDN+%#Jh32&8|P^`niY{arfEk@8^MT#f#1u?f;h zGph;%wJdZl(6LG9y3hfDNeN;Yq_3;$C8;B}G7cZo8M+YZAD0C|mDFnrf@~oNC)8pq zW6L#bP;HG$kO6w4`h5BK zt*)vF`ZsQO1fkj(TNw@XTUAA*Ke=HE>Z*cN+d*q1C+=2+aucd?bxNH1B~ zex*?fvW708-ixh_2KA&x*tdN}P$}kV-utcRImx;B))4de@)xmECyG4L)QcTMfUl0 zhDZVf>er-xh#MB_NYKln1WD>hW1iK8B9$#dH<}!cOq}emIYD%)OBZgnv8P?>Q;`&9 z@XvH8c7KFo`d%EWH*40QKXGRXtq9{ps z*N4)Q#wR9-p^+YUC^%Jwpi20v+c5Iu}F)Ol;a#t3mdpN z7ex-IVY!T?C0(N>UEeb*UG#SIxvBSWD+B4msHGYvkURNp@&id1h+05{G7bm%dE1B) zyY;uyGlNS)j@|Qhlm@RzXdhcVh_n9=l4kZdg18UJ)$pm2&P^&iZmM8QnqVuF_Lp%* z={Q_i*EdA1F+GbJ?G?E*jhj_<^>A^XyBn_siFt<<`;(Cbkzf&+B zln{q=q+7`@rrcFskMz_uZY&quBg&q)58wO)@E_Koo~@)qLH)!j-0p{X?7voVzJ{Y; vjr2S}&^1Wsh1KJlXrhTGnrNa4(F^|q7DgzC)w&^+00000NkvXXu0mjf@$r!r literal 0 HcmV?d00001 diff --git a/apps/api/assets/icons/05.png b/apps/api/assets/icons/05.png new file mode 100644 index 0000000000000000000000000000000000000000..979cfa27c4579e002261b732ef3fea327ff7bc87 GIT binary patch literal 1720 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 literal 0 HcmV?d00001 diff --git a/apps/api/assets/icons/09.png b/apps/api/assets/icons/09.png new file mode 100644 index 0000000000000000000000000000000000000000..eb705754edc831d3d1b666cb9a5147060b40e9e1 GIT binary patch literal 2716 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 R8=n9G002ovPDHLkV1iW^RILC2 literal 0 HcmV?d00001 diff --git a/apps/api/assets/icons/12.png b/apps/api/assets/icons/12.png new file mode 100644 index 0000000000000000000000000000000000000000..fbf6c64de69af94ec37e7d38a56f01590b63e23a GIT binary patch literal 1492 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 LNkvXXu0mjfc*b-6 literal 0 HcmV?d00001 diff --git a/apps/api/assets/icons/14.png b/apps/api/assets/icons/14.png new file mode 100644 index 0000000000000000000000000000000000000000..53be6ddceff281d7e0c4334c90a5455dd4c919e1 GIT binary patch literal 1619 zcmV-Z2CVssP)LpG{ZPW^R-XsDrv8fvJah8jv=z7VJGkh!bKR&xpTQzY>% zBXKQBe1oK#-7dr^QE%!H+0lW-I|*=|0sPZQ-FKoE04@~uq8{6xK%gHppnHddGPse% zuSBiOA3y$#z{V#C^!}(|j_E}bAKh-XDc{n!__n@>EOl+CWsMOvpr(P`vZoj@1=^22 zle1D(tF}$bwh-WFB>*!xKfNTXQ3UF41A1>M;e`1KaGj_obz0V#{$aYbUmbEvi#Pp=C7GHoTH&8wI2X3ql}u$xo>-G>p487@LTnTG)bLX zRydSVQwj91<$_Zf9)6P-;*@g1xsoLQUTz2l8{muOfHSo!;2h~mpl(Vv=THTQ)$sx0 z3q}9nJiu8|ukC4;lX(`K{ExtcM&f%0^a8TgbOP08&dZ(2N!QQl%L2d~2=vni^cDkn zI7WE=Mu3JtYEf_k8|RQz^P*f!mb#iG?oJd8;5L$KR-BJyCg+-sjgJr8ivbT1sO4Nv zDGE#oN8&}{jih>Zxa;!G&8PD<2JqkH!02B7Fe91haSsb5KFq!3*l^N&f(%X(BUBSf z+&DmcC)a3(UckjF1GyOl_(P)kKV=f)U>|u<(u{Ibglt zBC}yJD}z%A)CvOpHeoP_cy*r$mE-*;8)qiA!av%3WIBzzJ(pHRHE4Tl8>9)xNWF_J zWr=DLfx0U-IG}qI$q8x7NPH+AIQWk*bJvJ!6G=5c4M2g!Cq*^NK<@Gsde6>{EiI>s zYLx-)loAk0HA_^pd`n+JQtvAb9NZnH2IDwRo9B5moxC&4b=}#n>)z*i-u;f}Jm9+S zT+j37d7d}l$vX?&yt~kK-9?V)Jm}<|#ctkR;=1lq&-0c!d1txfILWWpd8t7XHxDxc z?d{*s!cSv{*Q-&U&p+JDdwoeXH}9?$B~nuWcq4&+jimmBcQOOO)f^&6mIHAC`BGnB zOMnW>fbNPMUZD3MZ(Uc@fC#WVM*i6x^}e+ONXA*W;rhA*WI%sWitXT<0YrfB#u&j? z)gK^Ry&NM+KVNr%0xoMt53$vC2gpEfMvM^as5?MG2B$9#a3s&XI~KeVG0cZH_3DxGqIffBFA12tO+ML&;V*$LvMF zuw6x@EVjKckU=?dUtnV?hBw9FJ?e*jSI<0{D}=*VuWHBedtvzY)Ae+ zjt4SP_1N}=(0LjgQkc9q1QKtH6Lw)`!B*2br*PO5?y>q1dl5uA)jcjdmP4l)Nd5Ui z_i}y;02i?f&DgnIIQzAf#k29i!dp4#5#WbI4Vw|#5>W~25ZTe;S1lG&30j=`cxnm& z7Zp0moD!UsqEcn-TrTdCBrz~j|7`%5ib_{IPXnZ05-v4T>osV~o{-~ei)Ec1y7a@d z7^Q-P2f38NCjvyZ%D1eEeEvPjgD{~n9rPL>pjFh0+JzX!a~}fzPSN4V2Vg+0*e2!q zqQ=!kpe`m`EgtwolRP`}!!4%pkd5BQ$02x*#oOOJ`{r{Q<$mdg8fr+y(SMi457;Ir Ry#W9K002ovPDHLkV1h^R_sakP literal 0 HcmV?d00001 diff --git a/apps/api/assets/icons/15.png b/apps/api/assets/icons/15.png new file mode 100644 index 0000000000000000000000000000000000000000..dc103550e06d7cc252e173078869b4c6f06012c1 GIT binary patch literal 1982 zcmV;v2SNCWP)hO_Vg6<5jHE(ad?R201?| zVhSw3d8RHslvN@W$Xm3)9c3e@+rhUP%f6Xa3K|*fZvdy3otWO%G?jEKD>;iTYoG!A zDuP+xrUm}20kipSU#`^xx0d=|TJEG{S;)x^wNKJQE(@)+ko&cei!|8d70-+`HR+Sx z#%QXmJ3(9Ni!|5|YJp!Ez&%Bbx0G~El$^zuHBbXC52q-$D!@mx^>|vbfj0u_*RJ7o7I#` zof?-e&9${BYQX6smfIUFP8e?A7I3!qDk1NQRhgc81@upvA=RqQGu2f&x|}o zPT1EQz;CE*p#}bAKyK0kcN)OmTF8AQ)A{c$2IM*exPrEGE#y0f<5QaB=oTT;VUp3T zz>9+{H%f9NA*YeC{uH?R4M@96Pgw={M40W^gZ!;p;763sWC!8#emZNwF#)V-743#4 z6J)r3Qvh<_FC-fkK|PnT;rOx|mKo$j(z^kyXmrU0X`g7>sRp`4!?6(5Z2{X2x9?+& z9pM^Og52b-yIfilIc9kJY+psWJtTP871dvY=pe?ddM?lhDdh>C6HIixvZxw`AQ#|me2~((XVlxCE3Qx0H^*Vu+?X|%r7MCyq8C9*;G{xwPO|Jy(5~=`0Cu>~5@uPMKu*}hm+V~$a8@BX1DNyhTOAccP}AVS z$5CuzYSX8;q*7;6(n+~oV{Pp*zOv*?KyGl?%bwsS>)AK~vOV;NXd(Y8b%`m+Twwq| zcV>?gN09A4&T@a}-)TB;Q$oH?bs;725hnzo#Mph(-gHY`I|=&D!-BSlCdg~1Oh6}{ zP_o+Ql`wY^1US><9qWrA2v>M1MmtI+5JZ+vA5Rs+B{en2dg+rB_SFXD=HAvPzb}Fy zImc7ElpIkB1og5ez}E|<8~=m+9ud-Gc}@>c5Xa}dlp()RaRhY})lP}4TQEx-0;ND* zf6wK^p$Q_BHQq{mS+N9lEkFF#q|z4@(v9lT8gNF@X%lV3#(VAW^J+Y(1cEwA+NpTv zLf2z-z|ZD2G#nk8AcrJC<+;oNAE$JO2XNgY{-t#GDxAKFcs-OQFe~%@I z{PD7!Tf{P(9l0`-80+WcMnxPw#|>lIxBKOHJqTJJiz14W&LWx@l7MsPBRE0vm1zt$ zX;)zT3w3`4CrCENrih_)GRk%QN!peJ`x2Jm?V;dK8mGjnjP^S#>)ulLXu!Kz0-#by zP=XZCTo$`RO2}y;*8B{r9>%3hwgGGWIyerp>@|tp3Tk3(K$z}&E%2|X+kgqm%AjW< zKCX_k5LXbI0@bPD{zH$}8Q0f4<#L4q4yquiBo9UC) zQF@@byi$XF(uwLrrXF-CSho8bi|I;2knUH1>uPsOp_E2|Tt{^rr==J0KxyyATq#!{ zqV!2qOC~AehfY}OG7PH}=zJ$f*)P;Nw~4@>P?7govlb33=ao##wW!H~^^Hrta&>Z6 zgROL5qav-w4OA4T=BTju%0fMWN&((!So$c|*MC^R0KOE_4-5R_bga3YR`hwO`pgh& z!=th*b#_s$DgV} QW&i*H07*qoM6N<$f+!ZXcmMzZ literal 0 HcmV?d00001 diff --git a/apps/api/assets/icons/16.png b/apps/api/assets/icons/16.png new file mode 100644 index 0000000000000000000000000000000000000000..27e7892697287a69a0f8cd3b8dfb5d085495b1ca GIT binary patch literal 1223 zcmV;&1UUPNP)-A2J}}6M1#xDP8iK?7!5}6&;tVj7=o)7ZNk(uh7=*;4 zaaxdK>jBWui6!G)IT#duizJqd(~88b2SGn2dWF-9#7hr>en|8hr=O5SS~#;vwvNRj=N z`B?Fckwo$KoX&ev40rSfhgr{MhNI4>7!+5IbkD7rJ(DoFGi#hga`;=qY%Ur6OfvhU zgu~;4!TstTQ;%=z3~rVY+%WaEwdU-XH{bYsZTR{#C&17GFJZ+`HL&WhQ@5@@AC279 z_-6RFf8vp{h~bzz2^7qFUNZZzWbnO&!;_N1?<9x6Ufz(qMY49KfCJA;ZhtG8eL^yL zL|q`tQ^U8{YT!DcM=J!g)~owOl3Pa;20uv{{6Re#=?T(290t!xW`C717^+`bkJqgQ z1x>BgdSs8v*gBMOyCxa@a~6qPyBBe7W%Gnt6OHe!yEIxAsWcT?C}FmraCo{ATTi$B z;mJv`vT!S8bbM&j-EXaNw)h*%IM`ev{#K_-cOox%$-%*47t>>i1U|y_ZG^-35uqP6 zzk1=jE)L&u37vFEa1?p;z`hp_9Bi{6>el4S^4J?Ah)j%CykHL^!EqOdA0r%o?qbRj z4u5fR__s^KOFcO-Z8)B<=`_jc_)rB0_cY&aOg}|9{L#f>y=Q-X5pl}p-K!kp??Qxr zfe4+-4C7ijRjSwYNX47*Tta`&4W$J~?O8ylD&);OGX40p!P$hVeuEH>`p=Rx4#J_( z5=T|RbX(py$n*7{4ndfHm>W)&7GvN6dHxPW=pP2BO&iGTx449!NqyxIDjnh%a|*=S z;%`Kl{+lL_+V?<_a|u0faN3l{UesJUp0D?`M>naiFiKeN4h|Ze4j~d8>U!l6 ze`zBsMbGK{gk#3Vw7aQ`e$6GpYYT74LHlWoK?&jD$b1}n!Qfn#8bO)Eq0e0XeG_P& lOdfMc@T|cwnM@|o>^}^-ZI9-t$vgl6002ovPDHLkV1ko^N`wFa literal 0 HcmV?d00001 diff --git a/apps/api/assets/icons/17.png b/apps/api/assets/icons/17.png new file mode 100644 index 0000000000000000000000000000000000000000..4a40642eaafdb09087c9bdb00d1447fef64bdf90 GIT binary patch literal 2496 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 literal 0 HcmV?d00001 diff --git a/apps/api/assets/icons/20.png b/apps/api/assets/icons/20.png new file mode 100644 index 0000000000000000000000000000000000000000..86a0a8743fca1b3cf1c39d59cf24cc7270d4b351 GIT binary patch literal 39993 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

WORTHPATH / {today()}

-

{p ? p.name : labels[page]}

+

+ {p?.iconId && ( + 账户图标 + )} + {p ? p.name : labels[page]} +

{page === 'overview' ? '你的财富全貌,从这里开始。' @@ -889,7 +895,9 @@ export default function App() { >

- {p.kind === 'asset' ? ( + {p.iconId ? ( + 账户图标 + ) : p.kind === 'asset' ? ( ) : p.kind === 'debt' ? ( @@ -943,6 +951,9 @@ export default function App() { )} {page === 'settings' && ( <> +
+ +
@@ -1079,7 +1090,7 @@ export default function App() {

清空本账号数据

- 清除本账号全部账户、资产、债务、历史和汇率(包括隐藏项目);保留登录账号及个人设置。请先下载备份并确认文件已保存。 + 清除本账号全部账户、资产、债务、历史、私有图标和汇率(包括隐藏项目);保留登录账号、个人设置和已发布的共享图标。请先下载备份并确认文件已保存。

{clearStep === 0 ? (
数据备份与恢复

ZIP 内分文件保存可读 - JSON,包括全部账户、资产、债务、历史、关联、币种、设置和汇率,不限制记录条数;不包含任何认证凭据。备份含个人财务信息,请妥善保管。 + JSON,包括全部账户、资产、债务、历史、关联、币种、设置、汇率及图标,不限制记录条数;不包含任何认证凭据。备份含个人财务信息,请妥善保管。

api('/positions/' + mp!.id, 'PATCH', { ...v, + ...(f.has('iconId') ? { iconId: f.get('iconId') || null } : {}), archived: mp!.archived, hidden: f.get('hidden') === 'on', }), @@ -1305,6 +1317,7 @@ export default function App() { () => api('/positions', 'POST', { ...v, + ...(f.has('iconId') ? { iconId: f.get('iconId') || null } : {}), kind, side, hidden: f.get('hidden') === 'on', @@ -1331,6 +1344,13 @@ export default function App() { 隐藏此项目(密码验证后可查看和编辑) + {(modal.kind === 'account' || modal.p?.kind === 'account') && ( + + )} '/api/icons/' + encodeURIComponent(id) + '/image'; +export function IconLibrary({ + initialId, + picker = false, +}: { + initialId?: string | null; + picker?: boolean; +}) { + const [selected, select] = useState(initialId || ''), + [q, search] = useState(''), + [page, setPage] = useState(1), + [items, setItems] = useState([]), + [total, setTotal] = useState(0), + [loading, setLoading] = useState(false), + [name, setName] = useState(''), + [shared, setShared] = useState(false), + [confirmed, setConfirmed] = useState(false), + [busy, setBusy] = useState(false), + [error, setError] = useState(''), + [notice, setNotice] = useState(''), + [refresh, setRefresh] = useState(0); + const file = useRef(null); + useEffect(() => { + let active = true; + setLoading(true); + const timer = setTimeout(() => { + void api<{ items: Icon[]; total: number }>( + '/icons?q=' + encodeURIComponent(q) + '&page=' + page, + ) + .then((v) => { + if (active) { + setItems(v.items); + setTotal(v.total); + } + }) + .catch((e) => { + if (active) setError(e.message); + }) + .finally(() => { + if (active) setLoading(false); + }); + }, 250); + return () => { + active = false; + clearTimeout(timer); + }; + }, [q, page, refresh]); + async function upload() { + const image = file.current?.files?.[0]; + setError(''); + setNotice(''); + if (!image) { + setError('请选择图片'); + return; + } + if (!name.trim() || (shared && (!/\p{Script=Han}/u.test(name) || !confirmed))) { + setError('请填写名称;共享图标须含中文并确认公开'); + return; + } + if (image.size > 2 * 1024 * 1024) { + setError('图片不能超过 2 MB'); + return; + } + setBusy(true); + try { + const form = new FormData(); + form.append('file', image); + form.append('name', name); + form.append('shared', String(shared)); + if (shared) form.append('confirmed', 'true'); + const res = await fetch('/api/icons/upload', { + method: 'POST', + body: form, + credentials: 'same-origin', + }); + const v = await res.json(); + if (!res.ok) throw Error(v.message || '上传失败'); + if (picker) select(v.id); + setNotice( + shared ? '图标已发布,所有登录用户均可搜索和使用' : '图标已存入我的图标,仅自己可见', + ); + setName(''); + setConfirmed(false); + if (file.current) file.current.value = ''; + search(''); + setPage(1); + setRefresh((n) => n + 1); + } catch (e) { + setError((e as Error).message); + } finally { + setBusy(false); + } + } + return ( +
{ + if ( + e.key === 'Enter' && + e.target instanceof HTMLInputElement && + e.target.type !== 'checkbox' + ) + e.preventDefault(); + }} + > +

{picker ? '选择账户图标' : '图标库'}

+

+ 搜索共享图标和我的图标,选择后可在多个账户复用。直接上传默认仅自己可见。 +

+ {picker && ( +
+ + {selected ? ( + <> + 当前账户图标 + 已选择图标 + + + ) : ( + 使用默认账户图标 + )} +
+ )} + + {loading ? ( +

正在加载图标…

+ ) : ( + <> +
+ {items.map((i) => + picker ? ( + + ) : ( +
+ + {i.name} + {i.shared ? '共享' : '仅自己'} +
+ ), + )} +
+ {!items.length &&

没有匹配图标,可以在下方上传。

} +
+ 共 {total} 个图标 + + +
+ + )} +
+ 上传我的图标 / 导入共享图标 + + + + {shared && ( + + )} + +
+ {error && ( +

+ {error} +

+ )} + {notice &&

{notice}

} +
+ ); +} diff --git a/apps/web/src/api.ts b/apps/web/src/api.ts index 83b7374..36dd8c7 100644 --- a/apps/web/src/api.ts +++ b/apps/web/src/api.ts @@ -43,6 +43,7 @@ export type History = { reason: string; }; export type Position = { + iconId?: string | null; id: string; kind: string; side: string; diff --git a/apps/web/src/style.css b/apps/web/src/style.css index e25365d..1141791 100644 --- a/apps/web/src/style.css +++ b/apps/web/src/style.css @@ -901,3 +901,98 @@ footer { font-size: 11px; } } + +.position-icon img { + width: 32px; + height: 32px; + object-fit: contain; +} +.icon-library { + display: grid; + gap: 12px; + min-width: 0; +} +.icon-library h2, +.icon-library p { + margin: 0; +} +.icon-library label { + display: grid; + gap: 6px; +} +.icon-library .check-line { + display: flex; +} +.icon-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); + gap: 8px; + max-height: 340px; + overflow: auto; + padding: 4px; +} +.icon-option { + background: var(--surface, #fff); + color: inherit; + border: 1px solid #dce3e5; + padding: 10px 6px; + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + min-width: 0; +} +.icon-option img { + width: 40px; + height: 40px; + object-fit: contain; +} +.icon-option span { + font-size: 12px; + overflow-wrap: anywhere; +} +.icon-option small { + color: #64748b; +} +.icon-option:disabled { + opacity: 1; + cursor: default; +} +.icon-option.selected { + border: 2px solid #087e70; + background: #effbf7; +} +.icon-selection, +.icon-pagination { + display: flex; + flex-wrap: wrap; + gap: 10px; + align-items: center; +} +.icon-selection img { + width: 40px; + height: 40px; + object-fit: contain; +} +.icon-upload { + border-top: 1px solid #dce3e5; + padding-top: 12px; +} +.icon-upload summary { + cursor: pointer; + margin-bottom: 10px; +} +.icon-upload label { + margin-bottom: 10px; +} +.icon-error { + color: #b42318; +} + +.detail-icon { + width: 42px; + height: 42px; + object-fit: contain; + vertical-align: middle; + margin-right: 12px; +} diff --git a/docs/acceptance.md b/docs/acceptance.md index 1c92719..242af38 100644 --- a/docs/acceptance.md +++ b/docs/acceptance.md @@ -33,3 +33,9 @@ - 手动汇率写入 API 返回 404,界面入口已移除;已有历史汇率未删除。 内网穿透测试更新:支持本地 `WEB_ORIGIN=*`;HTTP/HTTPS 来源探测均通过来源校验,缺失或 null 来源拒绝。精确来源模式及生产禁止通配符的单元校验通过。此轮 14 项单元测试、2 项数据库集成测试、类型检查和构建通过;未执行外网穿透链路端到端验收。 + +## 账户图标更新 + +新增迁移 005_account_icons,不修改既有迁移或财务数据;预置 20 个图标,使用固定 ID 幂等初始化。构建/类型检查和 ZIP v3/v4 单元回归通过。新增真实数据库集成覆盖私有图标跨用户不可检索/读取/赋值、共享中文名称和公开确认、图片规范化与恶意格式拒绝、相同图片多账户复用、ZIP 图标内容/关联恢复、损坏图标整次导入拒绝、清空保留他人引用共享图标。测试仅清理随机验收用户和他们上传的图标。 + +桌面浏览器实际验收:中文检索支付宝、选择图标后保存账户、私有图片上传及共享中文名称/公开确认发布成功。390×844 手机布局无横向溢出,全部图标正常加载。原生 ZIP 下载包含 10 个 JSON 文件,manifest v4,图标内容和账户关联均存在。开发进程改为只监听 dist,避免首次加载图片处理依赖导致不必要的自动重启。 diff --git a/docs/architecture.md b/docs/architecture.md index 1c9d743..c7c70b0 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -25,3 +25,9 @@ Revision 保存按业务日期生效的绝对金额,每次金额更新新增 安全清空:先通过认证下载备份,在当前 Session 记录备份内容摘要及 10 分钟有效期。下一步必须输入精确短语“确定清空”。Serializable 事务中重新校验当前数据摘要;变化后必须重新下载。清空仅删除当前用户的项目(级联历史/关联)及汇率,保留登录身份和个人设置,撤销该用户所有会话的查看及备份确认状态。汇率请求返回后再核对当前币种,避免清空期间正在执行的网络请求重建旧汇率。 备份现使用 version=3 ZIP:manifest.json 保存格式版本、导出时间及各数据文件 SHA-256;settings/currencies/accounts/assets/debts/history/links/rates 分别保存完整数据。业务时间与 hidden 保留。旧版 v1/v2 JSON 文件仍可上传,v1 缺少 hidden 时视为未隐藏。恢复仍只追加,按业务时间和 sequence 重建顺序;空空间恢复本位币和界面/退出偏好。取消项目数、历史数、关联数和汇率数上限;文件上传最多 512 MB,ZIP 解压总计 1 GB,拒绝未知/重复路径、缺失文件、加密 ZIP、摘要不符和格式错误,不向文件系统解压。预览文件暂存在系统临时目录,15 分钟有效,确认导入令牌绑定当前用户及会话,导入/失败/过期后清理。数据库事务最长 5 分钟以容纳较大恢复。手动汇率入口和写入 API 已删除,既有历史汇率保留。 + +## 账户图标模块 + +Icon 存储 name、ownerId、shared、SHA-256、规范静态 PNG 的 MediumBlob 和可选公开来源;Position.iconId 外键 SetNull,多个账户共享同一图片。所有图标接口使用已验证身份,读取/检索/赋值均限定 shared=true 或 ownerId=当前用户,响应不返回 ownerId。个人上传默认私有;共享发布须中文名称和明确公开确认。拒绝 SVG、动图、损坏图片、超限像素/文件,重编码移除元数据。内置图标固定 ID 追加初始化;在线请求不会发送用户财务数据。 + +备份逻辑增加可选 icons 数组(旧格式缺失可兼容),v4 ZIP 将图标内容放入 icons.json;内容校验和解码在导入事务前完成,图标在事务内以当前用户私有范围重建,账户关联重映射。清空删除私有图标,公开共享图标不因发布者清空而消失。用户删除时图标 ownerId SetNull,不影响他人已引用的公共图标。 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6bc543..1096679 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,6 +56,9 @@ importers: rxjs: specifier: ^7.8.2 version: 7.8.2 + sharp: + specifier: ^0.35.5 + version: 0.35.5(@types/node@24.19.0) yauzl: specifier: ^3.4.0 version: 3.4.0 @@ -210,6 +213,9 @@ packages: '@borewit/text-codec@0.2.2': resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + '@esbuild/aix-ppc64@0.28.2': resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} engines: {node: '>=18'} @@ -366,6 +372,168 @@ packages: cpu: [x64] os: [win32] + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.5': + resolution: {integrity: sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.5': + resolution: {integrity: sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.5': + resolution: {integrity: sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.4': + resolution: {integrity: sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.4': + resolution: {integrity: sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.4': + resolution: {integrity: sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.3.4': + resolution: {integrity: sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.3.4': + resolution: {integrity: sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.3.4': + resolution: {integrity: sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.3.4': + resolution: {integrity: sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.3.4': + resolution: {integrity: sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': + resolution: {integrity: sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + resolution: {integrity: sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.35.5': + resolution: {integrity: sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.35.5': + resolution: {integrity: sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.35.5': + resolution: {integrity: sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.35.5': + resolution: {integrity: sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.35.5': + resolution: {integrity: sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.35.5': + resolution: {integrity: sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.35.5': + resolution: {integrity: sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.35.5': + resolution: {integrity: sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.35.5': + resolution: {integrity: sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.5': + resolution: {integrity: sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.5': + resolution: {integrity: sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.5': + resolution: {integrity: sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.5': + resolution: {integrity: sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -916,6 +1084,10 @@ packages: destr@2.0.5: resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + dotenv@16.6.1: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} @@ -1377,6 +1549,11 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + send@1.2.1: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} @@ -1388,6 +1565,15 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sharp@0.35.5: + resolution: {integrity: sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + side-channel-list@1.0.1: resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} @@ -1680,6 +1866,11 @@ snapshots: '@borewit/text-codec@0.2.2': {} + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + '@esbuild/aix-ppc64@0.28.2': optional: true @@ -1758,6 +1949,112 @@ snapshots: '@esbuild/win32-x64@0.28.2': optional: true + '@img/colour@1.1.0': {} + + '@img/sharp-darwin-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.4 + optional: true + + '@img/sharp-darwin-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.4 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.5': + dependencies: + '@img/sharp-wasm32': 0.35.5 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + optional: true + + '@img/sharp-linux-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.4 + optional: true + + '@img/sharp-linux-arm@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.4 + optional: true + + '@img/sharp-linux-ppc64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.4 + optional: true + + '@img/sharp-linux-riscv64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.4 + optional: true + + '@img/sharp-linux-s390x@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.4 + optional: true + + '@img/sharp-linux-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 + optional: true + + '@img/sharp-wasm32@0.35.5': + dependencies: + '@emnapi/runtime': 1.11.3 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.5': + dependencies: + '@img/sharp-wasm32': 0.35.5 + optional: true + + '@img/sharp-win32-arm64@0.35.5': + optional: true + + '@img/sharp-win32-ia32@0.35.5': + optional: true + + '@img/sharp-win32-x64@0.35.5': + optional: true + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -2256,6 +2553,8 @@ snapshots: destr@2.0.5: {} + detect-libc@2.1.2: {} + dotenv@16.6.1: {} dotenv@17.4.2: {} @@ -2774,6 +3073,8 @@ snapshots: semver@6.3.1: {} + semver@7.8.5: {} + send@1.2.1: dependencies: debug: 4.4.3 @@ -2801,6 +3102,39 @@ snapshots: setprototypeof@1.2.0: {} + sharp@0.35.5(@types/node@24.19.0): + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.5 + '@img/sharp-darwin-x64': 0.35.5 + '@img/sharp-freebsd-wasm32': 0.35.5 + '@img/sharp-libvips-darwin-arm64': 1.3.4 + '@img/sharp-libvips-darwin-x64': 1.3.4 + '@img/sharp-libvips-linux-arm': 1.3.4 + '@img/sharp-libvips-linux-arm64': 1.3.4 + '@img/sharp-libvips-linux-ppc64': 1.3.4 + '@img/sharp-libvips-linux-riscv64': 1.3.4 + '@img/sharp-libvips-linux-s390x': 1.3.4 + '@img/sharp-libvips-linux-x64': 1.3.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 + '@img/sharp-linux-arm': 0.35.5 + '@img/sharp-linux-arm64': 0.35.5 + '@img/sharp-linux-ppc64': 0.35.5 + '@img/sharp-linux-riscv64': 0.35.5 + '@img/sharp-linux-s390x': 0.35.5 + '@img/sharp-linux-x64': 0.35.5 + '@img/sharp-linuxmusl-arm64': 0.35.5 + '@img/sharp-linuxmusl-x64': 0.35.5 + '@img/sharp-webcontainers-wasm32': 0.35.5 + '@img/sharp-win32-arm64': 0.35.5 + '@img/sharp-win32-ia32': 0.35.5 + '@img/sharp-win32-x64': 0.35.5 + '@types/node': 24.19.0 + side-channel-list@1.0.1: dependencies: es-errors: 1.3.0