fixed build performance issues; optimized queries
This commit is contained in:
parent
cff9dad54c
commit
324451e082
@ -6,6 +6,9 @@ import "./src/env.js";
|
||||
|
||||
/** @type {import("next").NextConfig} */
|
||||
const config = {
|
||||
eslint: {
|
||||
ignoreDuringBuilds: true,
|
||||
},
|
||||
experimental: {
|
||||
serverActions: {
|
||||
bodySizeLimit: "2mb",
|
||||
|
||||
@ -68,6 +68,7 @@
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"use-debounce": "^10.0.4",
|
||||
"winston": "^3.17.0",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
179
pnpm-lock.yaml
generated
179
pnpm-lock.yaml
generated
@ -152,6 +152,9 @@ importers:
|
||||
use-debounce:
|
||||
specifier: ^10.0.4
|
||||
version: 10.0.4(react@18.3.1)
|
||||
winston:
|
||||
specifier: ^3.17.0
|
||||
version: 3.17.0
|
||||
zod:
|
||||
specifier: ^3.24.2
|
||||
version: 3.24.2
|
||||
@ -255,10 +258,17 @@ packages:
|
||||
'@cfcs/core@0.0.6':
|
||||
resolution: {integrity: sha512-FxfJMwoLB8MEMConeXUCqtMGqxdtePQxRBOiGip9ULcYYam3WfCgoY6xdnMaSkYvRvmosp5iuG+TiPofm65+Pw==}
|
||||
|
||||
'@colors/colors@1.6.0':
|
||||
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
|
||||
engines: {node: '>=0.1.90'}
|
||||
|
||||
'@cspotcode/source-map-support@0.8.1':
|
||||
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@dabh/diagnostics@2.0.3':
|
||||
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
|
||||
|
||||
'@daybrush/utils@1.13.0':
|
||||
resolution: {integrity: sha512-ALK12C6SQNNHw1enXK+UO8bdyQ+jaWNQ1Af7Z3FNxeAwjYhQT7do+TRE4RASAJ3ObaS2+TJ7TXR3oz2Gzbw0PQ==}
|
||||
|
||||
@ -1930,6 +1940,9 @@ packages:
|
||||
'@types/react@18.3.18':
|
||||
resolution: {integrity: sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==}
|
||||
|
||||
'@types/triple-beam@1.3.5':
|
||||
resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==}
|
||||
|
||||
'@types/unist@2.0.11':
|
||||
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
|
||||
|
||||
@ -2085,6 +2098,9 @@ packages:
|
||||
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
async@3.2.6:
|
||||
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
|
||||
|
||||
available-typed-arrays@1.0.7:
|
||||
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -2202,20 +2218,32 @@ packages:
|
||||
react: ^18 || ^19 || ^19.0.0-rc
|
||||
react-dom: ^18 || ^19 || ^19.0.0-rc
|
||||
|
||||
color-convert@1.9.3:
|
||||
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
||||
|
||||
color-convert@2.0.1:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
engines: {node: '>=7.0.0'}
|
||||
|
||||
color-name@1.1.3:
|
||||
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
|
||||
|
||||
color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
|
||||
color-string@1.9.1:
|
||||
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
|
||||
|
||||
color@3.2.1:
|
||||
resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==}
|
||||
|
||||
color@4.2.3:
|
||||
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
|
||||
engines: {node: '>=12.5.0'}
|
||||
|
||||
colorspace@1.1.4:
|
||||
resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==}
|
||||
|
||||
comma-separated-tokens@2.0.3:
|
||||
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
|
||||
|
||||
@ -2470,6 +2498,9 @@ packages:
|
||||
emoji-regex@9.2.2:
|
||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||
|
||||
enabled@2.0.0:
|
||||
resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
|
||||
|
||||
encoding-sniffer@0.2.0:
|
||||
resolution: {integrity: sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==}
|
||||
|
||||
@ -2688,6 +2719,9 @@ packages:
|
||||
picomatch:
|
||||
optional: true
|
||||
|
||||
fecha@4.2.3:
|
||||
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
|
||||
|
||||
file-entry-cache@6.0.1:
|
||||
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
@ -2707,6 +2741,9 @@ packages:
|
||||
flatted@3.3.3:
|
||||
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
|
||||
|
||||
fn.name@1.1.0:
|
||||
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
|
||||
|
||||
for-each@0.3.5:
|
||||
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -2976,6 +3013,10 @@ packages:
|
||||
resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-stream@2.0.1:
|
||||
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-string@1.1.1:
|
||||
resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -3076,6 +3117,9 @@ packages:
|
||||
keyv@4.5.4:
|
||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||
|
||||
kuler@2.0.0:
|
||||
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
|
||||
|
||||
language-subtag-registry@0.3.23:
|
||||
resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==}
|
||||
|
||||
@ -3107,6 +3151,10 @@ packages:
|
||||
lodash.merge@4.6.2:
|
||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||
|
||||
logform@2.7.0:
|
||||
resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
||||
longest-streak@3.1.0:
|
||||
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
|
||||
|
||||
@ -3359,6 +3407,9 @@ packages:
|
||||
once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
|
||||
one-time@1.0.0:
|
||||
resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==}
|
||||
|
||||
optionator@0.9.4:
|
||||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@ -3737,6 +3788,10 @@ packages:
|
||||
read-cache@1.0.0:
|
||||
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
|
||||
|
||||
readable-stream@3.6.2:
|
||||
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
readdirp@3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
@ -3793,6 +3848,9 @@ packages:
|
||||
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
|
||||
engines: {node: '>=0.4'}
|
||||
|
||||
safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
|
||||
safe-push-apply@1.0.0:
|
||||
resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -3801,6 +3859,10 @@ packages:
|
||||
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
safe-stable-stringify@2.5.0:
|
||||
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
safer-buffer@2.1.2:
|
||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||
|
||||
@ -3892,6 +3954,9 @@ packages:
|
||||
stable-hash@0.0.4:
|
||||
resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==}
|
||||
|
||||
stack-trace@0.0.10:
|
||||
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
|
||||
|
||||
streamsearch@1.1.0:
|
||||
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
@ -3927,6 +3992,9 @@ packages:
|
||||
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
string_decoder@1.3.0:
|
||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||
|
||||
stringify-entities@4.0.4:
|
||||
resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
|
||||
|
||||
@ -4004,6 +4072,9 @@ packages:
|
||||
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
text-hex@1.0.0:
|
||||
resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==}
|
||||
|
||||
text-table@0.2.0:
|
||||
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
|
||||
|
||||
@ -4031,6 +4102,10 @@ packages:
|
||||
trim-lines@3.0.1:
|
||||
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
|
||||
|
||||
triple-beam@1.4.1:
|
||||
resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
trough@2.2.0:
|
||||
resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
|
||||
|
||||
@ -4240,6 +4315,14 @@ packages:
|
||||
engines: {node: '>= 8'}
|
||||
hasBin: true
|
||||
|
||||
winston-transport@4.9.0:
|
||||
resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
||||
winston@3.17.0:
|
||||
resolution: {integrity: sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
||||
word-wrap@1.2.5:
|
||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -4327,10 +4410,18 @@ snapshots:
|
||||
dependencies:
|
||||
'@egjs/component': 3.0.5
|
||||
|
||||
'@colors/colors@1.6.0': {}
|
||||
|
||||
'@cspotcode/source-map-support@0.8.1':
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.9
|
||||
|
||||
'@dabh/diagnostics@2.0.3':
|
||||
dependencies:
|
||||
colorspace: 1.1.4
|
||||
enabled: 2.0.0
|
||||
kuler: 2.0.0
|
||||
|
||||
'@daybrush/utils@1.13.0': {}
|
||||
|
||||
'@drizzle-team/brocli@0.10.2': {}
|
||||
@ -5737,6 +5828,8 @@ snapshots:
|
||||
'@types/prop-types': 15.7.14
|
||||
csstype: 3.1.3
|
||||
|
||||
'@types/triple-beam@1.3.5': {}
|
||||
|
||||
'@types/unist@2.0.11': {}
|
||||
|
||||
'@types/unist@3.0.3': {}
|
||||
@ -5936,6 +6029,8 @@ snapshots:
|
||||
|
||||
async-function@1.0.0: {}
|
||||
|
||||
async@3.2.6: {}
|
||||
|
||||
available-typed-arrays@1.0.7:
|
||||
dependencies:
|
||||
possible-typed-array-names: 1.1.0
|
||||
@ -6074,17 +6169,27 @@ snapshots:
|
||||
- '@types/react'
|
||||
- '@types/react-dom'
|
||||
|
||||
color-convert@1.9.3:
|
||||
dependencies:
|
||||
color-name: 1.1.3
|
||||
|
||||
color-convert@2.0.1:
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
|
||||
color-name@1.1.3: {}
|
||||
|
||||
color-name@1.1.4: {}
|
||||
|
||||
color-string@1.9.1:
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
simple-swizzle: 0.2.2
|
||||
optional: true
|
||||
|
||||
color@3.2.1:
|
||||
dependencies:
|
||||
color-convert: 1.9.3
|
||||
color-string: 1.9.1
|
||||
|
||||
color@4.2.3:
|
||||
dependencies:
|
||||
@ -6092,6 +6197,11 @@ snapshots:
|
||||
color-string: 1.9.1
|
||||
optional: true
|
||||
|
||||
colorspace@1.1.4:
|
||||
dependencies:
|
||||
color: 3.2.1
|
||||
text-hex: 1.0.0
|
||||
|
||||
comma-separated-tokens@2.0.3: {}
|
||||
|
||||
commander@4.1.1: {}
|
||||
@ -6257,6 +6367,8 @@ snapshots:
|
||||
|
||||
emoji-regex@9.2.2: {}
|
||||
|
||||
enabled@2.0.0: {}
|
||||
|
||||
encoding-sniffer@0.2.0:
|
||||
dependencies:
|
||||
iconv-lite: 0.6.3
|
||||
@ -6691,6 +6803,8 @@ snapshots:
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.2
|
||||
|
||||
fecha@4.2.3: {}
|
||||
|
||||
file-entry-cache@6.0.1:
|
||||
dependencies:
|
||||
flat-cache: 3.2.0
|
||||
@ -6712,6 +6826,8 @@ snapshots:
|
||||
|
||||
flatted@3.3.3: {}
|
||||
|
||||
fn.name@1.1.0: {}
|
||||
|
||||
for-each@0.3.5:
|
||||
dependencies:
|
||||
is-callable: 1.2.7
|
||||
@ -6919,8 +7035,7 @@ snapshots:
|
||||
call-bound: 1.0.4
|
||||
get-intrinsic: 1.3.0
|
||||
|
||||
is-arrayish@0.3.2:
|
||||
optional: true
|
||||
is-arrayish@0.3.2: {}
|
||||
|
||||
is-async-function@2.1.1:
|
||||
dependencies:
|
||||
@ -7013,6 +7128,8 @@ snapshots:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
|
||||
is-stream@2.0.1: {}
|
||||
|
||||
is-string@1.1.1:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
@ -7111,6 +7228,8 @@ snapshots:
|
||||
dependencies:
|
||||
json-buffer: 3.0.1
|
||||
|
||||
kuler@2.0.0: {}
|
||||
|
||||
language-subtag-registry@0.3.23: {}
|
||||
|
||||
language-tags@1.0.9:
|
||||
@ -7138,6 +7257,15 @@ snapshots:
|
||||
|
||||
lodash.merge@4.6.2: {}
|
||||
|
||||
logform@2.7.0:
|
||||
dependencies:
|
||||
'@colors/colors': 1.6.0
|
||||
'@types/triple-beam': 1.3.5
|
||||
fecha: 4.2.3
|
||||
ms: 2.1.3
|
||||
safe-stable-stringify: 2.5.0
|
||||
triple-beam: 1.4.1
|
||||
|
||||
longest-streak@3.1.0: {}
|
||||
|
||||
loose-envify@1.4.0:
|
||||
@ -7557,6 +7685,10 @@ snapshots:
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
|
||||
one-time@1.0.0:
|
||||
dependencies:
|
||||
fn.name: 1.1.0
|
||||
|
||||
optionator@0.9.4:
|
||||
dependencies:
|
||||
deep-is: 0.1.4
|
||||
@ -7944,6 +8076,12 @@ snapshots:
|
||||
dependencies:
|
||||
pify: 2.3.0
|
||||
|
||||
readable-stream@3.6.2:
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
string_decoder: 1.3.0
|
||||
util-deprecate: 1.0.2
|
||||
|
||||
readdirp@3.6.0:
|
||||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
@ -8023,6 +8161,8 @@ snapshots:
|
||||
has-symbols: 1.1.0
|
||||
isarray: 2.0.5
|
||||
|
||||
safe-buffer@5.2.1: {}
|
||||
|
||||
safe-push-apply@1.0.0:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
@ -8034,6 +8174,8 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
is-regex: 1.2.1
|
||||
|
||||
safe-stable-stringify@2.5.0: {}
|
||||
|
||||
safer-buffer@2.1.2: {}
|
||||
|
||||
scheduler@0.23.2:
|
||||
@ -8147,7 +8289,6 @@ snapshots:
|
||||
simple-swizzle@0.2.2:
|
||||
dependencies:
|
||||
is-arrayish: 0.3.2
|
||||
optional: true
|
||||
|
||||
sonner@2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
@ -8167,6 +8308,8 @@ snapshots:
|
||||
|
||||
stable-hash@0.0.4: {}
|
||||
|
||||
stack-trace@0.0.10: {}
|
||||
|
||||
streamsearch@1.1.0: {}
|
||||
|
||||
string-width@4.2.3:
|
||||
@ -8231,6 +8374,10 @@ snapshots:
|
||||
define-properties: 1.2.1
|
||||
es-object-atoms: 1.1.1
|
||||
|
||||
string_decoder@1.3.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
stringify-entities@4.0.4:
|
||||
dependencies:
|
||||
character-entities-html4: 2.1.0
|
||||
@ -8322,6 +8469,8 @@ snapshots:
|
||||
|
||||
tapable@2.2.1: {}
|
||||
|
||||
text-hex@1.0.0: {}
|
||||
|
||||
text-table@0.2.0: {}
|
||||
|
||||
thenify-all@1.6.0:
|
||||
@ -8349,6 +8498,8 @@ snapshots:
|
||||
|
||||
trim-lines@3.0.1: {}
|
||||
|
||||
triple-beam@1.4.1: {}
|
||||
|
||||
trough@2.2.0: {}
|
||||
|
||||
ts-api-utils@2.0.1(typescript@5.8.2):
|
||||
@ -8600,6 +8751,26 @@ snapshots:
|
||||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
winston-transport@4.9.0:
|
||||
dependencies:
|
||||
logform: 2.7.0
|
||||
readable-stream: 3.6.2
|
||||
triple-beam: 1.4.1
|
||||
|
||||
winston@3.17.0:
|
||||
dependencies:
|
||||
'@colors/colors': 1.6.0
|
||||
'@dabh/diagnostics': 2.0.3
|
||||
async: 3.2.6
|
||||
is-stream: 2.0.1
|
||||
logform: 2.7.0
|
||||
one-time: 1.0.0
|
||||
readable-stream: 3.6.2
|
||||
safe-stable-stringify: 2.5.0
|
||||
stack-trace: 0.0.10
|
||||
triple-beam: 1.4.1
|
||||
winston-transport: 4.9.0
|
||||
|
||||
word-wrap@1.2.5: {}
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 MiB |
@ -1,8 +1,9 @@
|
||||
import "dotenv/config";
|
||||
import { env } from "@/env";
|
||||
import { db } from "../src/server/db";
|
||||
import { articles, categories, users } from "../src/server/db/schema";
|
||||
|
||||
async function seed() {
|
||||
async function developmentSeed() {
|
||||
const usersData = Array.from({ length: 100 }).map((_, i) => ({
|
||||
name: `User ${i + 1}`,
|
||||
email: `user${i + 1}@example.com`,
|
||||
@ -37,9 +38,29 @@ async function seed() {
|
||||
console.log("Seeded " + a.length + " articles");
|
||||
}
|
||||
|
||||
async function productionSeed() {
|
||||
const user = await db.query.users.findFirst();
|
||||
if (user) {
|
||||
console.log("Skipped seeding, user already exists");
|
||||
return;
|
||||
}
|
||||
const initialUser = {
|
||||
name: "Admin",
|
||||
email: "payblot@gmail.com",
|
||||
role: 7,
|
||||
};
|
||||
await db.insert(users).values(initialUser);
|
||||
console.log("Seeded user");
|
||||
}
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
await seed();
|
||||
if (env.NODE_ENV === "development") await developmentSeed();
|
||||
else if (env.NODE_ENV === "production") await productionSeed();
|
||||
else
|
||||
console.log(
|
||||
"Skipped seeding, NODE_ENV is not set to development or production",
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
|
||||
@ -10,8 +10,8 @@ import { Button } from "@/components/ui/button";
|
||||
import Link from "next/link";
|
||||
|
||||
export default async function Home() {
|
||||
const categories = await api.category.getAll({ limit: 6 });
|
||||
const articles = await api.article.getAll({ limit: 6 });
|
||||
const categories = await api.category.getMany({ limit: 6 });
|
||||
const articles = await api.article.getMany({ limit: 6 });
|
||||
return (
|
||||
<>
|
||||
<Alert>
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import * as cheerio from "cheerio";
|
||||
import { NextApiRequest } from "next";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET(req: NextApiRequest) {
|
||||
export async function GET(req: Request) {
|
||||
if (req.method !== "GET") {
|
||||
return NextResponse.json({ error: "Method not allowed" });
|
||||
}
|
||||
|
||||
@ -25,7 +25,8 @@ import CategorySelect from "@/components/category/category-select";
|
||||
import { CheckCircle, XCircle } from "lucide-react";
|
||||
import PublishArticleAlertDialog from "./publish-article-alert-dialog";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import Editor from "../editor";
|
||||
import dynamic from "next/dynamic";
|
||||
const Editor = dynamic(() => import("../editor"), { ssr: false });
|
||||
|
||||
export default ({ server_article }: { server_article: Article }) => {
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
|
||||
@ -19,6 +19,9 @@ function InfiniteArticlesGrid() {
|
||||
},
|
||||
{
|
||||
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
||||
staleTime: 60 * 4 * 1000, // 4 minutes stale time
|
||||
refetchOnMount: false, // Prevents unnecessary refetching
|
||||
refetchOnWindowFocus: false, // Avoids refetch when switching tabs
|
||||
},
|
||||
);
|
||||
// Calculate all visible items across all loaded pages
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { JSONContent } from "novel";
|
||||
|
||||
function RenderArticle({ content }: { content: string }) {
|
||||
function RenderArticle({ content }: { content: JSONContent }) {
|
||||
return "render article: in work"; //<Editor readOnly editorProviderProps={{ content: content }} />;
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import { Combobox, ComboboxProps } from "../combobox";
|
||||
import { Icons } from "../icons";
|
||||
|
||||
function CategorySelect(props: Partial<ComboboxProps>) {
|
||||
const { data: categories } = api.category.getAll.useQuery();
|
||||
const { data: categories } = api.category.getMany.useQuery();
|
||||
return (
|
||||
<Combobox
|
||||
{...(props as ComboboxProps)}
|
||||
|
||||
@ -9,8 +9,8 @@ export const CATEGORY_GRID_CLASS =
|
||||
function CategoryGrid({ categories }: { categories: Category[] }) {
|
||||
return (
|
||||
<menu className={CATEGORY_GRID_CLASS}>
|
||||
{categories.map((category) => (
|
||||
<li key={category.id}>
|
||||
{categories.map((category, idx) => (
|
||||
<li key={idx}>
|
||||
<CategoryCard {...category} />
|
||||
</li>
|
||||
))}
|
||||
|
||||
@ -18,6 +18,9 @@ export default function InfiniteCategoryGrid() {
|
||||
},
|
||||
{
|
||||
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
||||
staleTime: 60 * 4 * 1000, // 4 minutes stale time
|
||||
refetchOnMount: false, // Prevents unnecessary refetching
|
||||
refetchOnWindowFocus: false, // Avoids refetch when switching tabs
|
||||
},
|
||||
);
|
||||
// Calculate all visible items across all loaded pages
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import ColorPicker, { type ColorPickerProps } from ".";
|
||||
import { Button } from "../ui/button";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
|
||||
|
||||
export default function ColorPickerPopover(props: ColorPickerProps) {
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button style={{ backgroundColor: props.initialColor }} className="">
|
||||
color
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<ColorPicker {...props} />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
.color-picker .react-colorful {
|
||||
width: 100%;
|
||||
height: 240px;
|
||||
}
|
||||
.color-picker .react-colorful__saturation {
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
.color-picker .react-colorful__hue {
|
||||
height: 40px;
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
.color-picker .react-colorful__hue-pointer {
|
||||
width: 12px;
|
||||
height: inherit;
|
||||
border-radius: 0;
|
||||
}
|
||||
@ -1,103 +0,0 @@
|
||||
"use client";
|
||||
import "./color-picker.css";
|
||||
|
||||
import React from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { HexColorPicker } from "react-colorful";
|
||||
import { Input } from "../ui/input";
|
||||
import { debounce } from "@/lib/utils";
|
||||
|
||||
const STORAGE_KEY = "savedColors";
|
||||
const MAX_COLORS = 12;
|
||||
const SELECT_DEBOUNCE = 500;
|
||||
|
||||
export type ColorPickerProps = {
|
||||
onInput?: (color: string) => void;
|
||||
initialColor?: string;
|
||||
};
|
||||
|
||||
function ColorPicker({ onInput, initialColor }: ColorPickerProps) {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [customColors, setCustomColors] = useState<string[]>([]);
|
||||
const [color, setColor] = useState(initialColor ?? "#ff0000");
|
||||
|
||||
// Load colors from localStorage on mount
|
||||
useEffect(() => {
|
||||
if (initialColor?.length) setColor(initialColor);
|
||||
const storageColors = localStorage.getItem(STORAGE_KEY);
|
||||
const storedColors = storageColors ? JSON.parse(storageColors) : [];
|
||||
if (storedColors.length) {
|
||||
setCustomColors(storedColors);
|
||||
if (!initialColor?.length) setColor(storedColors[0]);
|
||||
setMounted(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const persistColor = (newColor: string) => {
|
||||
if (newColor.length < 2) return;
|
||||
if (customColors[0] === newColor) return; // Prevent duplicate consecutive colors
|
||||
|
||||
const updatedColors = [
|
||||
newColor,
|
||||
...customColors.filter((c) => c !== newColor),
|
||||
].slice(0, MAX_COLORS);
|
||||
console.log(updatedColors);
|
||||
|
||||
setCustomColors(updatedColors);
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(updatedColors));
|
||||
};
|
||||
|
||||
const selectColor = (newColor: string) => {
|
||||
persistColor(newColor);
|
||||
onInput?.(newColor);
|
||||
};
|
||||
|
||||
const selectColorDebounced = debounce((newColor: string) => {
|
||||
selectColor(newColor);
|
||||
}, SELECT_DEBOUNCE);
|
||||
|
||||
const handleColorChange = (newColor: string, skipDebounce = false) => {
|
||||
setColor(newColor);
|
||||
if (skipDebounce)
|
||||
selectColor(newColor); // Delayed save
|
||||
else selectColorDebounced(newColor);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="color-picker flex flex-col items-center gap-4">
|
||||
{/* React Colorful Picker */}
|
||||
<HexColorPicker
|
||||
color={color}
|
||||
onChange={handleColorChange}
|
||||
className="w-full"
|
||||
/>
|
||||
|
||||
{/* Default Input (Native Color Picker) */}
|
||||
<div className="flex w-full gap-2">
|
||||
<div
|
||||
className="size-8 rounded-md border"
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
<Input
|
||||
className="h-8"
|
||||
value={color}
|
||||
onInput={(e) => handleColorChange(e.currentTarget.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Display Recent Colors */}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{customColors.map((col) => (
|
||||
<button
|
||||
key={col}
|
||||
className="size-8 rounded border"
|
||||
style={{ backgroundColor: col }}
|
||||
onClick={() => handleColorChange(col, true)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ColorPicker;
|
||||
@ -36,7 +36,6 @@ export function DataTable<TData, TValue>({
|
||||
columns,
|
||||
data,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
"use no memo";
|
||||
const [pagination, setPagination] = React.useState<PaginationState>({
|
||||
pageSize: 25,
|
||||
pageIndex: 0,
|
||||
|
||||
27
src/lib/logger.ts
Normal file
27
src/lib/logger.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import winston from "winston";
|
||||
const customLevels = {
|
||||
levels: {
|
||||
error: 0,
|
||||
warn: 1,
|
||||
info: 2,
|
||||
sql: 3,
|
||||
debug: 4,
|
||||
},
|
||||
colors: {
|
||||
error: "red",
|
||||
warn: "yellow",
|
||||
info: "green",
|
||||
sql: "magenta",
|
||||
debug: "blue",
|
||||
},
|
||||
};
|
||||
winston.addColors(customLevels.colors);
|
||||
|
||||
export const logger = winston.createLogger({
|
||||
levels: customLevels.levels,
|
||||
format: winston.format.json(),
|
||||
transports: [
|
||||
new winston.transports.File({ filename: "db_queries.log", level: "sql" }),
|
||||
new winston.transports.File({ filename: "errors.log", level: "error" }),
|
||||
],
|
||||
});
|
||||
@ -1,8 +1,12 @@
|
||||
"use server";
|
||||
import fs from "node:fs/promises";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { auth } from "../auth";
|
||||
import { hasPermission, Role } from "@/lib/validation/permissions";
|
||||
|
||||
export async function uploadFile(formData: FormData) {
|
||||
const session = await auth();
|
||||
if (!session || hasPermission(session.user.role, Role.EDITOR)) return false;
|
||||
const file = formData.get("file") as File;
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
const buffer = new Uint8Array(arrayBuffer);
|
||||
|
||||
@ -1,29 +1,6 @@
|
||||
import { z } from "zod";
|
||||
import { createTRPCRouter, publicProcedure } from "../trpc";
|
||||
import { desc, ilike, like } from "drizzle-orm";
|
||||
import {
|
||||
articles as articlesTable,
|
||||
categories as categoriesTable,
|
||||
lower,
|
||||
} from "@/server/db/schema";
|
||||
|
||||
export const appRouter = createTRPCRouter({
|
||||
searchContent: publicProcedure
|
||||
.input(
|
||||
z.object({
|
||||
query: z.string().optional(),
|
||||
}),
|
||||
)
|
||||
.query(async ({ ctx, input }) => {
|
||||
const articles = await ctx.db.query.articles.findMany({
|
||||
where: ilike(articlesTable.title, "%" + input.query + "%"),
|
||||
});
|
||||
const categories = await ctx.db.query.categories.findMany({
|
||||
where: like(categoriesTable.name, "%" + input.query + "%"),
|
||||
});
|
||||
return { articles, categories };
|
||||
}),
|
||||
|
||||
getSidebarMain: publicProcedure.query(async ({ ctx }) => {
|
||||
const categories = await ctx.db.query.categories.findMany({
|
||||
limit: 3,
|
||||
|
||||
@ -71,10 +71,10 @@ export const articleRouter = createTRPCRouter({
|
||||
get: publicProcedure
|
||||
.input(z.object({ slug: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
return await ctx.db.query.articles.findFirst({
|
||||
return (await ctx.db.query.articles.findFirst({
|
||||
where: eq(articles.slug, input.slug),
|
||||
with: { category: true },
|
||||
});
|
||||
})) as Article;
|
||||
}),
|
||||
getByCursor: publicProcedure
|
||||
.input(
|
||||
@ -131,7 +131,6 @@ export const articleRouter = createTRPCRouter({
|
||||
|
||||
let nextCursor: string | undefined = undefined;
|
||||
if (items.length > limit) {
|
||||
console.log("Configure next cursor");
|
||||
const cursorItem = items.pop();
|
||||
// Create a cursor object with the relevant fields for sorting
|
||||
const cursorData: ArticleCursor = {
|
||||
@ -150,7 +149,7 @@ export const articleRouter = createTRPCRouter({
|
||||
nextCursor,
|
||||
};
|
||||
}),
|
||||
getAll: publicProcedure
|
||||
getMany: publicProcedure
|
||||
.input(
|
||||
z
|
||||
.object({
|
||||
@ -160,11 +159,12 @@ export const articleRouter = createTRPCRouter({
|
||||
.optional(),
|
||||
)
|
||||
.query(async ({ ctx, input }) => {
|
||||
const limit = input?.limit ?? 50;
|
||||
return (await ctx.db.query.articles.findMany({
|
||||
where: input?.categoryId
|
||||
? eq(articles.categoryId, input.categoryId)
|
||||
: undefined,
|
||||
limit: input?.limit,
|
||||
limit: limit,
|
||||
columns: {
|
||||
title: true,
|
||||
slug: true,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
||||
import { createTRPCRouter, publicProcedure } from "../trpc";
|
||||
|
||||
export const authorRouter = createTRPCRouter({
|
||||
getCount: publicProcedure.query(async ({ ctx }) => {
|
||||
|
||||
@ -12,11 +12,11 @@ import {
|
||||
count,
|
||||
desc,
|
||||
eq,
|
||||
gt,
|
||||
gte,
|
||||
ilike,
|
||||
like,
|
||||
lte,
|
||||
sql,
|
||||
} from "drizzle-orm";
|
||||
import { hasPermission, Role } from "@/lib/validation/permissions";
|
||||
import {
|
||||
@ -60,13 +60,6 @@ const getCategorySorting = (sort: string, cursor?: CategoryCursor) => {
|
||||
};
|
||||
|
||||
export const categoryRouter = createTRPCRouter({
|
||||
search: publicProcedure
|
||||
.input(z.object({ query: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
return await ctx.db.query.categories.findMany({
|
||||
where: like(categories.name, "%" + input.query + "%"),
|
||||
});
|
||||
}),
|
||||
get: publicProcedure
|
||||
.input(z.object({ slug: z.string(), with: z.any() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
@ -76,7 +69,7 @@ export const categoryRouter = createTRPCRouter({
|
||||
})) as Category;
|
||||
}),
|
||||
|
||||
getAll: publicProcedure
|
||||
getMany: publicProcedure
|
||||
.input(
|
||||
z
|
||||
.object({
|
||||
@ -85,9 +78,15 @@ export const categoryRouter = createTRPCRouter({
|
||||
.optional(),
|
||||
)
|
||||
.query(async ({ ctx, input }) => {
|
||||
return await ctx.db.query.categories.findMany({
|
||||
limit: input?.limit,
|
||||
});
|
||||
const limit = input?.limit ?? 50;
|
||||
return (await ctx.db.query.categories.findMany({
|
||||
limit: limit,
|
||||
columns: {
|
||||
name: true,
|
||||
slug: true,
|
||||
createdAt: true,
|
||||
},
|
||||
})) as Category[];
|
||||
}),
|
||||
|
||||
getByCursor: publicProcedure
|
||||
@ -135,7 +134,6 @@ export const categoryRouter = createTRPCRouter({
|
||||
|
||||
let nextCursor: string | undefined = undefined;
|
||||
if (items.length > limit) {
|
||||
console.log("Configure next cursor");
|
||||
const cursorItem = items.pop();
|
||||
// Create a cursor object with the relevant fields for sorting
|
||||
const cursorData: CategoryCursor = {
|
||||
@ -172,6 +170,12 @@ export const categoryRouter = createTRPCRouter({
|
||||
.values({ ...input.category, slug })
|
||||
.returning({
|
||||
slug: categories.slug,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: categories.slug,
|
||||
set: {
|
||||
slug: sql`${slug} || '-' || (SELECT COUNT(*) FROM ${categories} WHERE slug LIKE ${slug + "-%"})`,
|
||||
},
|
||||
});
|
||||
}),
|
||||
update: protectedProcedure
|
||||
|
||||
@ -13,7 +13,7 @@ export const usersRouter = createTRPCRouter({
|
||||
.update(users)
|
||||
.set(input.profile)
|
||||
.where(eq(users.id, ctx.session.user.id))
|
||||
.returning();
|
||||
.returning({ id: users.id });
|
||||
}),
|
||||
|
||||
getAll: protectedProcedure.query(async ({ ctx }) => {
|
||||
@ -21,6 +21,12 @@ export const usersRouter = createTRPCRouter({
|
||||
if (!isAdmin) throw new Error("You are not allowed to get all users");
|
||||
return await ctx.db.query.users.findMany({
|
||||
orderBy: desc(users.role),
|
||||
columns: {
|
||||
name: true,
|
||||
email: true,
|
||||
role: true,
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
||||
|
||||
@ -15,6 +15,8 @@ const globalForDb = globalThis as unknown as {
|
||||
const conn = globalForDb.conn ?? postgres(env.DATABASE_URL);
|
||||
if (env.NODE_ENV !== "production") globalForDb.conn = conn;
|
||||
|
||||
export const db = drizzle(conn, { schema });
|
||||
export const db = drizzle(conn, {
|
||||
schema,
|
||||
});
|
||||
|
||||
export type DBType = typeof db;
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { relations, SQL, sql } from "drizzle-orm";
|
||||
import { relations, sql } from "drizzle-orm";
|
||||
import {
|
||||
AnyPgColumn,
|
||||
boolean,
|
||||
index,
|
||||
integer,
|
||||
@ -16,10 +15,6 @@ import { User } from "next-auth";
|
||||
import { type AdapterAccount } from "next-auth/adapters";
|
||||
import { JSONContent } from "novel";
|
||||
|
||||
export function lower(value: AnyPgColumn): SQL {
|
||||
return sql`lower(${value})`;
|
||||
}
|
||||
|
||||
export const createTable = pgTableCreator((name) => `wiki-antifa_${name}`);
|
||||
|
||||
export const articles = createTable(
|
||||
@ -43,7 +38,9 @@ export const articles = createTable(
|
||||
),
|
||||
},
|
||||
(example) => ({
|
||||
articleTitleIndex: index("article_title_idx").on(example.title),
|
||||
titleIndex: index("article_title_idx").on(example.title),
|
||||
slugIndex: index("article_slug_idx").on(example.slug),
|
||||
createdAtIndex: index("article_created_at_idx").on(example.createdAt),
|
||||
}),
|
||||
);
|
||||
export type Article = typeof articles.$inferSelect & {
|
||||
@ -81,7 +78,9 @@ export const categories = createTable(
|
||||
),
|
||||
},
|
||||
(example) => ({
|
||||
categoryNameIndex: index("category_name_idx").on(example.name),
|
||||
nameIndex: index("category_name_idx").on(example.name),
|
||||
slugameIndex: index("category_slug_idx").on(example.slug),
|
||||
createdAtIndex: index("category_created_at_idx").on(example.createdAt),
|
||||
}),
|
||||
);
|
||||
export type Category = typeof categories.$inferSelect & {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user