feat: Implement structured logging with Pino across server routes
- Add Pino logger with configurable log levels - Create centralized logging utility with context-aware logging - Replace console.log/debug/error with structured logging - Enhance log messages with additional context and metadata - Configure log levels dynamically based on environment
- commit
- 2960188b03ac2e37d64fa384070a6d5969e95f57
- author
- Kit · · 2025-02-15 09:05 UTC
- parent
- afa6c9d
- signature
- SSH Unverified
SHA256:H91DFuEG4jpk5edzLA4aUKkpv4zv9Q339ipRmkph6TkThe key is unknown
19 files · +507 -146
M package.json +2 -0
| @@ -54,6 +54,8 @@ | |||
| 54 | 54 | "@libsql/client": "^0.14.0", | |
| 55 | 55 | "dotenv": "^16.4.7", | |
| 56 | 56 | "drizzle-orm": "^0.39.3", | |
| 57 | + | "pino": "^9.6.0", | |
| 58 | + | "pino-pretty": "^13.0.0", | |
| 57 | 59 | "uuid": "^11.0.5", | |
| 58 | 60 | "xp.css": "^0.2.6" | |
| 59 | 61 | } | |
| @@ -54,6 +54,8 @@ | |||||
| 54 | "@libsql/client": "^0.14.0", | 54 | "@libsql/client": "^0.14.0", | ||
| 55 | "dotenv": "^16.4.7", | 55 | "dotenv": "^16.4.7", | ||
| 56 | "drizzle-orm": "^0.39.3", | 56 | "drizzle-orm": "^0.39.3", | ||
| 57 | + | "pino": "^9.6.0", | |||
| 58 | + | "pino-pretty": "^13.0.0", | |||
| 57 | "uuid": "^11.0.5", | 59 | "uuid": "^11.0.5", | ||
| 58 | "xp.css": "^0.2.6" | 60 | "xp.css": "^0.2.6" | ||
| 59 | } | 61 | } | ||
M pnpm-lock.yaml +184 -0
| @@ -17,6 +17,12 @@ importers: | |||
| 17 | 17 | drizzle-orm: | |
| 18 | 18 | specifier: ^0.39.3 | |
| 19 | 19 | version: 0.39.3(@cloudflare/workers-types@4.20250204.0)(@libsql/client@0.14.0) | |
| 20 | + | pino: | |
| 21 | + | specifier: ^9.6.0 | |
| 22 | + | version: 9.6.0 | |
| 23 | + | pino-pretty: | |
| 24 | + | specifier: ^13.0.0 | |
| 25 | + | version: 13.0.0 | |
| 20 | 26 | uuid: | |
| 21 | 27 | specifier: ^11.0.5 | |
| 22 | 28 | version: 11.0.5 | |
| @@ -1339,6 +1345,10 @@ packages: | |||
| 1339 | 1345 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} | |
| 1340 | 1346 | engines: {node: '>=12'} | |
| 1341 | 1347 | ||
| 1348 | + | atomic-sleep@1.0.0: | |
| 1349 | + | resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} | |
| 1350 | + | engines: {node: '>=8.0.0'} | |
| 1351 | + | ||
| 1342 | 1352 | autoprefixer@10.4.20: | |
| 1343 | 1353 | resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} | |
| 1344 | 1354 | engines: {node: ^10 || ^12 || >=14} | |
| @@ -1424,6 +1434,9 @@ packages: | |||
| 1424 | 1434 | color-name@1.1.4: | |
| 1425 | 1435 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} | |
| 1426 | 1436 | ||
| 1437 | + | colorette@2.0.20: | |
| 1438 | + | resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} | |
| 1439 | + | ||
| 1427 | 1440 | commander@2.20.3: | |
| 1428 | 1441 | resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} | |
| 1429 | 1442 | ||
| @@ -1461,6 +1474,9 @@ packages: | |||
| 1461 | 1474 | resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} | |
| 1462 | 1475 | engines: {node: '>= 12'} | |
| 1463 | 1476 | ||
| 1477 | + | dateformat@4.6.3: | |
| 1478 | + | resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} | |
| 1479 | + | ||
| 1464 | 1480 | debug@4.4.0: | |
| 1465 | 1481 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} | |
| 1466 | 1482 | engines: {node: '>=6.0'} | |
| @@ -1603,6 +1619,9 @@ packages: | |||
| 1603 | 1619 | emoji-regex@9.2.2: | |
| 1604 | 1620 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} | |
| 1605 | 1621 | ||
| 1622 | + | end-of-stream@1.4.4: | |
| 1623 | + | resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} | |
| 1624 | + | ||
| 1606 | 1625 | es-module-lexer@1.6.0: | |
| 1607 | 1626 | resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} | |
| 1608 | 1627 | ||
| @@ -1736,6 +1755,9 @@ packages: | |||
| 1736 | 1755 | resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} | |
| 1737 | 1756 | engines: {node: '>=12.0.0'} | |
| 1738 | 1757 | ||
| 1758 | + | fast-copy@3.0.2: | |
| 1759 | + | resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} | |
| 1760 | + | ||
| 1739 | 1761 | fast-deep-equal@3.1.3: | |
| 1740 | 1762 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} | |
| 1741 | 1763 | ||
| @@ -1749,6 +1771,13 @@ packages: | |||
| 1749 | 1771 | fast-levenshtein@2.0.6: | |
| 1750 | 1772 | resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} | |
| 1751 | 1773 | ||
| 1774 | + | fast-redact@3.5.0: | |
| 1775 | + | resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} | |
| 1776 | + | engines: {node: '>=6'} | |
| 1777 | + | ||
| 1778 | + | fast-safe-stringify@2.1.1: | |
| 1779 | + | resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} | |
| 1780 | + | ||
| 1752 | 1781 | fastq@1.19.0: | |
| 1753 | 1782 | resolution: {integrity: sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==} | |
| 1754 | 1783 | ||
| @@ -1842,6 +1871,9 @@ packages: | |||
| 1842 | 1871 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} | |
| 1843 | 1872 | engines: {node: '>= 0.4'} | |
| 1844 | 1873 | ||
| 1874 | + | help-me@5.0.0: | |
| 1875 | + | resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} | |
| 1876 | + | ||
| 1845 | 1877 | ignore@5.3.2: | |
| 1846 | 1878 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} | |
| 1847 | 1879 | engines: {node: '>= 4'} | |
| @@ -1894,6 +1926,10 @@ packages: | |||
| 1894 | 1926 | resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} | |
| 1895 | 1927 | hasBin: true | |
| 1896 | 1928 | ||
| 1929 | + | joycon@3.1.1: | |
| 1930 | + | resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} | |
| 1931 | + | engines: {node: '>=10'} | |
| 1932 | + | ||
| 1897 | 1933 | js-base64@3.7.7: | |
| 1898 | 1934 | resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} | |
| 1899 | 1935 | ||
| @@ -1996,6 +2032,9 @@ packages: | |||
| 1996 | 2032 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} | |
| 1997 | 2033 | engines: {node: '>=16 || 14 >=14.17'} | |
| 1998 | 2034 | ||
| 2035 | + | minimist@1.2.8: | |
| 2036 | + | resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} | |
| 2037 | + | ||
| 1999 | 2038 | minipass@7.1.2: | |
| 2000 | 2039 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} | |
| 2001 | 2040 | engines: {node: '>=16 || 14 >=14.17'} | |
| @@ -2059,6 +2098,13 @@ packages: | |||
| 2059 | 2098 | ohash@1.1.4: | |
| 2060 | 2099 | resolution: {integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==} | |
| 2061 | 2100 | ||
| 2101 | + | on-exit-leak-free@2.1.2: | |
| 2102 | + | resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} | |
| 2103 | + | engines: {node: '>=14.0.0'} | |
| 2104 | + | ||
| 2105 | + | once@1.4.0: | |
| 2106 | + | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} | |
| 2107 | + | ||
| 2062 | 2108 | optionator@0.9.4: | |
| 2063 | 2109 | resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} | |
| 2064 | 2110 | engines: {node: '>= 0.8.0'} | |
| @@ -2117,6 +2163,20 @@ packages: | |||
| 2117 | 2163 | resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} | |
| 2118 | 2164 | engines: {node: '>=0.10.0'} | |
| 2119 | 2165 | ||
| 2166 | + | pino-abstract-transport@2.0.0: | |
| 2167 | + | resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} | |
| 2168 | + | ||
| 2169 | + | pino-pretty@13.0.0: | |
| 2170 | + | resolution: {integrity: sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==} | |
| 2171 | + | hasBin: true | |
| 2172 | + | ||
| 2173 | + | pino-std-serializers@7.0.0: | |
| 2174 | + | resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} | |
| 2175 | + | ||
| 2176 | + | pino@9.6.0: | |
| 2177 | + | resolution: {integrity: sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==} | |
| 2178 | + | hasBin: true | |
| 2179 | + | ||
| 2120 | 2180 | pirates@4.0.6: | |
| 2121 | 2181 | resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} | |
| 2122 | 2182 | engines: {node: '>= 6'} | |
| @@ -2266,9 +2326,15 @@ packages: | |||
| 2266 | 2326 | printable-characters@1.0.42: | |
| 2267 | 2327 | resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} | |
| 2268 | 2328 | ||
| 2329 | + | process-warning@4.0.1: | |
| 2330 | + | resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==} | |
| 2331 | + | ||
| 2269 | 2332 | promise-limit@2.7.0: | |
| 2270 | 2333 | resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==} | |
| 2271 | 2334 | ||
| 2335 | + | pump@3.0.2: | |
| 2336 | + | resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} | |
| 2337 | + | ||
| 2272 | 2338 | punycode@2.3.1: | |
| 2273 | 2339 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} | |
| 2274 | 2340 | engines: {node: '>=6'} | |
| @@ -2276,6 +2342,9 @@ packages: | |||
| 2276 | 2342 | queue-microtask@1.2.3: | |
| 2277 | 2343 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} | |
| 2278 | 2344 | ||
| 2345 | + | quick-format-unescaped@4.0.4: | |
| 2346 | + | resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} | |
| 2347 | + | ||
| 2279 | 2348 | read-cache@1.0.0: | |
| 2280 | 2349 | resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} | |
| 2281 | 2350 | ||
| @@ -2287,6 +2356,10 @@ packages: | |||
| 2287 | 2356 | resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==} | |
| 2288 | 2357 | engines: {node: '>= 14.18.0'} | |
| 2289 | 2358 | ||
| 2359 | + | real-require@0.2.0: | |
| 2360 | + | resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} | |
| 2361 | + | engines: {node: '>= 12.13.0'} | |
| 2362 | + | ||
| 2290 | 2363 | regexparam@3.0.0: | |
| 2291 | 2364 | resolution: {integrity: sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q==} | |
| 2292 | 2365 | engines: {node: '>=8'} | |
| @@ -2329,6 +2402,13 @@ packages: | |||
| 2329 | 2402 | resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} | |
| 2330 | 2403 | engines: {node: '>=6'} | |
| 2331 | 2404 | ||
| 2405 | + | safe-stable-stringify@2.5.0: | |
| 2406 | + | resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} | |
| 2407 | + | engines: {node: '>=10'} | |
| 2408 | + | ||
| 2409 | + | secure-json-parse@2.7.0: | |
| 2410 | + | resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} | |
| 2411 | + | ||
| 2332 | 2412 | semver@7.7.1: | |
| 2333 | 2413 | resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} | |
| 2334 | 2414 | engines: {node: '>=10'} | |
| @@ -2356,6 +2436,9 @@ packages: | |||
| 2356 | 2436 | resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} | |
| 2357 | 2437 | engines: {node: '>=18'} | |
| 2358 | 2438 | ||
| 2439 | + | sonic-boom@4.2.0: | |
| 2440 | + | resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} | |
| 2441 | + | ||
| 2359 | 2442 | source-map-js@1.2.1: | |
| 2360 | 2443 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} | |
| 2361 | 2444 | engines: {node: '>=0.10.0'} | |
| @@ -2371,6 +2454,10 @@ packages: | |||
| 2371 | 2454 | resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} | |
| 2372 | 2455 | deprecated: Please use @jridgewell/sourcemap-codec instead | |
| 2373 | 2456 | ||
| 2457 | + | split2@4.2.0: | |
| 2458 | + | resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} | |
| 2459 | + | engines: {node: '>= 10.x'} | |
| 2460 | + | ||
| 2374 | 2461 | stackback@0.0.2: | |
| 2375 | 2462 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} | |
| 2376 | 2463 | ||
| @@ -2455,6 +2542,9 @@ packages: | |||
| 2455 | 2542 | thenify@3.3.1: | |
| 2456 | 2543 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} | |
| 2457 | 2544 | ||
| 2545 | + | thread-stream@3.1.0: | |
| 2546 | + | resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} | |
| 2547 | + | ||
| 2458 | 2548 | tinybench@2.9.0: | |
| 2459 | 2549 | resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} | |
| 2460 | 2550 | ||
| @@ -2666,6 +2756,9 @@ packages: | |||
| 2666 | 2756 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} | |
| 2667 | 2757 | engines: {node: '>=12'} | |
| 2668 | 2758 | ||
| 2759 | + | wrappy@1.0.2: | |
| 2760 | + | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} | |
| 2761 | + | ||
| 2669 | 2762 | ws@8.18.0: | |
| 2670 | 2763 | resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} | |
| 2671 | 2764 | engines: {node: '>=10.0.0'} | |
| @@ -3585,6 +3678,8 @@ snapshots: | |||
| 3585 | 3678 | ||
| 3586 | 3679 | assertion-error@2.0.1: {} | |
| 3587 | 3680 | ||
| 3681 | + | atomic-sleep@1.0.0: {} | |
| 3682 | + | ||
| 3588 | 3683 | autoprefixer@10.4.20(postcss@8.5.2): | |
| 3589 | 3684 | dependencies: | |
| 3590 | 3685 | browserslist: 4.24.4 | |
| @@ -3672,6 +3767,8 @@ snapshots: | |||
| 3672 | 3767 | ||
| 3673 | 3768 | color-name@1.1.4: {} | |
| 3674 | 3769 | ||
| 3770 | + | colorette@2.0.20: {} | |
| 3771 | + | ||
| 3675 | 3772 | commander@2.20.3: {} | |
| 3676 | 3773 | ||
| 3677 | 3774 | commander@4.1.1: {} | |
| @@ -3696,6 +3793,8 @@ snapshots: | |||
| 3696 | 3793 | ||
| 3697 | 3794 | data-uri-to-buffer@4.0.1: {} | |
| 3698 | 3795 | ||
| 3796 | + | dateformat@4.6.3: {} | |
| 3797 | + | ||
| 3699 | 3798 | debug@4.4.0: | |
| 3700 | 3799 | dependencies: | |
| 3701 | 3800 | ms: 2.1.3 | |
| @@ -3740,6 +3839,10 @@ snapshots: | |||
| 3740 | 3839 | ||
| 3741 | 3840 | emoji-regex@9.2.2: {} | |
| 3742 | 3841 | ||
| 3842 | + | end-of-stream@1.4.4: | |
| 3843 | + | dependencies: | |
| 3844 | + | once: 1.4.0 | |
| 3845 | + | ||
| 3743 | 3846 | es-module-lexer@1.6.0: {} | |
| 3744 | 3847 | ||
| 3745 | 3848 | esbuild-register@3.6.0(esbuild@0.19.12): | |
| @@ -4007,6 +4110,8 @@ snapshots: | |||
| 4007 | 4110 | ||
| 4008 | 4111 | expect-type@1.1.0: {} | |
| 4009 | 4112 | ||
| 4113 | + | fast-copy@3.0.2: {} | |
| 4114 | + | ||
| 4010 | 4115 | fast-deep-equal@3.1.3: {} | |
| 4011 | 4116 | ||
| 4012 | 4117 | fast-glob@3.3.3: | |
| @@ -4021,6 +4126,10 @@ snapshots: | |||
| 4021 | 4126 | ||
| 4022 | 4127 | fast-levenshtein@2.0.6: {} | |
| 4023 | 4128 | ||
| 4129 | + | fast-redact@3.5.0: {} | |
| 4130 | + | ||
| 4131 | + | fast-safe-stringify@2.1.1: {} | |
| 4132 | + | ||
| 4024 | 4133 | fastq@1.19.0: | |
| 4025 | 4134 | dependencies: | |
| 4026 | 4135 | reusify: 1.0.4 | |
| @@ -4108,6 +4217,8 @@ snapshots: | |||
| 4108 | 4217 | dependencies: | |
| 4109 | 4218 | function-bind: 1.1.2 | |
| 4110 | 4219 | ||
| 4220 | + | help-me@5.0.0: {} | |
| 4221 | + | ||
| 4111 | 4222 | ignore@5.3.2: {} | |
| 4112 | 4223 | ||
| 4113 | 4224 | import-fresh@3.3.1: | |
| @@ -4151,6 +4262,8 @@ snapshots: | |||
| 4151 | 4262 | ||
| 4152 | 4263 | jiti@1.21.7: {} | |
| 4153 | 4264 | ||
| 4265 | + | joycon@3.1.1: {} | |
| 4266 | + | ||
| 4154 | 4267 | js-base64@3.7.7: {} | |
| 4155 | 4268 | ||
| 4156 | 4269 | js-yaml@4.1.0: | |
| @@ -4255,6 +4368,8 @@ snapshots: | |||
| 4255 | 4368 | dependencies: | |
| 4256 | 4369 | brace-expansion: 2.0.1 | |
| 4257 | 4370 | ||
| 4371 | + | minimist@1.2.8: {} | |
| 4372 | + | ||
| 4258 | 4373 | minipass@7.1.2: {} | |
| 4259 | 4374 | ||
| 4260 | 4375 | mlly@1.7.4: | |
| @@ -4302,6 +4417,12 @@ snapshots: | |||
| 4302 | 4417 | ||
| 4303 | 4418 | ohash@1.1.4: {} | |
| 4304 | 4419 | ||
| 4420 | + | on-exit-leak-free@2.1.2: {} | |
| 4421 | + | ||
| 4422 | + | once@1.4.0: | |
| 4423 | + | dependencies: | |
| 4424 | + | wrappy: 1.0.2 | |
| 4425 | + | ||
| 4305 | 4426 | optionator@0.9.4: | |
| 4306 | 4427 | dependencies: | |
| 4307 | 4428 | deep-is: 0.1.4 | |
| @@ -4350,6 +4471,42 @@ snapshots: | |||
| 4350 | 4471 | ||
| 4351 | 4472 | pify@2.3.0: {} | |
| 4352 | 4473 | ||
| 4474 | + | pino-abstract-transport@2.0.0: | |
| 4475 | + | dependencies: | |
| 4476 | + | split2: 4.2.0 | |
| 4477 | + | ||
| 4478 | + | pino-pretty@13.0.0: | |
| 4479 | + | dependencies: | |
| 4480 | + | colorette: 2.0.20 | |
| 4481 | + | dateformat: 4.6.3 | |
| 4482 | + | fast-copy: 3.0.2 | |
| 4483 | + | fast-safe-stringify: 2.1.1 | |
| 4484 | + | help-me: 5.0.0 | |
| 4485 | + | joycon: 3.1.1 | |
| 4486 | + | minimist: 1.2.8 | |
| 4487 | + | on-exit-leak-free: 2.1.2 | |
| 4488 | + | pino-abstract-transport: 2.0.0 | |
| 4489 | + | pump: 3.0.2 | |
| 4490 | + | secure-json-parse: 2.7.0 | |
| 4491 | + | sonic-boom: 4.2.0 | |
| 4492 | + | strip-json-comments: 3.1.1 | |
| 4493 | + | ||
| 4494 | + | pino-std-serializers@7.0.0: {} | |
| 4495 | + | ||
| 4496 | + | pino@9.6.0: | |
| 4497 | + | dependencies: | |
| 4498 | + | atomic-sleep: 1.0.0 | |
| 4499 | + | fast-redact: 3.5.0 | |
| 4500 | + | on-exit-leak-free: 2.1.2 | |
| 4501 | + | pino-abstract-transport: 2.0.0 | |
| 4502 | + | pino-std-serializers: 7.0.0 | |
| 4503 | + | process-warning: 4.0.1 | |
| 4504 | + | quick-format-unescaped: 4.0.4 | |
| 4505 | + | real-require: 0.2.0 | |
| 4506 | + | safe-stable-stringify: 2.5.0 | |
| 4507 | + | sonic-boom: 4.2.0 | |
| 4508 | + | thread-stream: 3.1.0 | |
| 4509 | + | ||
| 4353 | 4510 | pirates@4.0.6: {} | |
| 4354 | 4511 | ||
| 4355 | 4512 | pkg-types@1.3.1: | |
| @@ -4432,12 +4589,21 @@ snapshots: | |||
| 4432 | 4589 | ||
| 4433 | 4590 | printable-characters@1.0.42: {} | |
| 4434 | 4591 | ||
| 4592 | + | process-warning@4.0.1: {} | |
| 4593 | + | ||
| 4435 | 4594 | promise-limit@2.7.0: {} | |
| 4436 | 4595 | ||
| 4596 | + | pump@3.0.2: | |
| 4597 | + | dependencies: | |
| 4598 | + | end-of-stream: 1.4.4 | |
| 4599 | + | once: 1.4.0 | |
| 4600 | + | ||
| 4437 | 4601 | punycode@2.3.1: {} | |
| 4438 | 4602 | ||
| 4439 | 4603 | queue-microtask@1.2.3: {} | |
| 4440 | 4604 | ||
| 4605 | + | quick-format-unescaped@4.0.4: {} | |
| 4606 | + | ||
| 4441 | 4607 | read-cache@1.0.0: | |
| 4442 | 4608 | dependencies: | |
| 4443 | 4609 | pify: 2.3.0 | |
| @@ -4448,6 +4614,8 @@ snapshots: | |||
| 4448 | 4614 | ||
| 4449 | 4615 | readdirp@4.1.1: {} | |
| 4450 | 4616 | ||
| 4617 | + | real-require@0.2.0: {} | |
| 4618 | + | ||
| 4451 | 4619 | regexparam@3.0.0: {} | |
| 4452 | 4620 | ||
| 4453 | 4621 | resolve-from@4.0.0: {} | |
| @@ -4509,6 +4677,10 @@ snapshots: | |||
| 4509 | 4677 | dependencies: | |
| 4510 | 4678 | mri: 1.2.0 | |
| 4511 | 4679 | ||
| 4680 | + | safe-stable-stringify@2.5.0: {} | |
| 4681 | + | ||
| 4682 | + | secure-json-parse@2.7.0: {} | |
| 4683 | + | ||
| 4512 | 4684 | semver@7.7.1: {} | |
| 4513 | 4685 | ||
| 4514 | 4686 | set-cookie-parser@2.7.1: {} | |
| @@ -4529,6 +4701,10 @@ snapshots: | |||
| 4529 | 4701 | mrmime: 2.0.0 | |
| 4530 | 4702 | totalist: 3.0.1 | |
| 4531 | 4703 | ||
| 4704 | + | sonic-boom@4.2.0: | |
| 4705 | + | dependencies: | |
| 4706 | + | atomic-sleep: 1.0.0 | |
| 4707 | + | ||
| 4532 | 4708 | source-map-js@1.2.1: {} | |
| 4533 | 4709 | ||
| 4534 | 4710 | source-map-support@0.5.21: | |
| @@ -4540,6 +4716,8 @@ snapshots: | |||
| 4540 | 4716 | ||
| 4541 | 4717 | sourcemap-codec@1.4.8: {} | |
| 4542 | 4718 | ||
| 4719 | + | split2@4.2.0: {} | |
| 4720 | + | ||
| 4543 | 4721 | stackback@0.0.2: {} | |
| 4544 | 4722 | ||
| 4545 | 4723 | stacktracey@2.1.8: | |
| @@ -4670,6 +4848,10 @@ snapshots: | |||
| 4670 | 4848 | dependencies: | |
| 4671 | 4849 | any-promise: 1.3.0 | |
| 4672 | 4850 | ||
| 4851 | + | thread-stream@3.1.0: | |
| 4852 | + | dependencies: | |
| 4853 | + | real-require: 0.2.0 | |
| 4854 | + | ||
| 4673 | 4855 | tinybench@2.9.0: {} | |
| 4674 | 4856 | ||
| 4675 | 4857 | tinyexec@0.3.2: {} | |
| @@ -4877,6 +5059,8 @@ snapshots: | |||
| 4877 | 5059 | string-width: 5.1.2 | |
| 4878 | 5060 | strip-ansi: 7.1.0 | |
| 4879 | 5061 | ||
| 5062 | + | wrappy@1.0.2: {} | |
| 5063 | + | ||
| 4880 | 5064 | ws@8.18.0: {} | |
| 4881 | 5065 | ||
| 4882 | 5066 | xp.css@0.2.6: {} | |
| @@ -17,6 +17,12 @@ importers: | |||||
| 17 | drizzle-orm: | 17 | drizzle-orm: | ||
| 18 | specifier: ^0.39.3 | 18 | specifier: ^0.39.3 | ||
| 19 | version: 0.39.3(@cloudflare/workers-types@4.20250204.0)(@libsql/client@0.14.0) | 19 | version: 0.39.3(@cloudflare/workers-types@4.20250204.0)(@libsql/client@0.14.0) | ||
| 20 | + | pino: | |||
| 21 | + | specifier: ^9.6.0 | |||
| 22 | + | version: 9.6.0 | |||
| 23 | + | pino-pretty: | |||
| 24 | + | specifier: ^13.0.0 | |||
| 25 | + | version: 13.0.0 | |||
| 20 | uuid: | 26 | uuid: | ||
| 21 | specifier: ^11.0.5 | 27 | specifier: ^11.0.5 | ||
| 22 | version: 11.0.5 | 28 | version: 11.0.5 | ||
| @@ -1339,6 +1345,10 @@ packages: | |||||
| 1339 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} | 1345 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} | ||
| 1340 | engines: {node: '>=12'} | 1346 | engines: {node: '>=12'} | ||
| 1341 | 1347 | ||||
| 1348 | + | atomic-sleep@1.0.0: | |||
| 1349 | + | resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} | |||
| 1350 | + | engines: {node: '>=8.0.0'} | |||
| 1351 | + | ||||
| 1342 | autoprefixer@10.4.20: | 1352 | autoprefixer@10.4.20: | ||
| 1343 | resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} | 1353 | resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} | ||
| 1344 | engines: {node: ^10 || ^12 || >=14} | 1354 | engines: {node: ^10 || ^12 || >=14} | ||
| @@ -1424,6 +1434,9 @@ packages: | |||||
| 1424 | color-name@1.1.4: | 1434 | color-name@1.1.4: | ||
| 1425 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} | 1435 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} | ||
| 1426 | 1436 | ||||
| 1437 | + | colorette@2.0.20: | |||
| 1438 | + | resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} | |||
| 1439 | + | ||||
| 1427 | commander@2.20.3: | 1440 | commander@2.20.3: | ||
| 1428 | resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} | 1441 | resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} | ||
| 1429 | 1442 | ||||
| @@ -1461,6 +1474,9 @@ packages: | |||||
| 1461 | resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} | 1474 | resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} | ||
| 1462 | engines: {node: '>= 12'} | 1475 | engines: {node: '>= 12'} | ||
| 1463 | 1476 | ||||
| 1477 | + | dateformat@4.6.3: | |||
| 1478 | + | resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} | |||
| 1479 | + | ||||
| 1464 | debug@4.4.0: | 1480 | debug@4.4.0: | ||
| 1465 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} | 1481 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} | ||
| 1466 | engines: {node: '>=6.0'} | 1482 | engines: {node: '>=6.0'} | ||
| @@ -1603,6 +1619,9 @@ packages: | |||||
| 1603 | emoji-regex@9.2.2: | 1619 | emoji-regex@9.2.2: | ||
| 1604 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} | 1620 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} | ||
| 1605 | 1621 | ||||
| 1622 | + | end-of-stream@1.4.4: | |||
| 1623 | + | resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} | |||
| 1624 | + | ||||
| 1606 | es-module-lexer@1.6.0: | 1625 | es-module-lexer@1.6.0: | ||
| 1607 | resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} | 1626 | resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} | ||
| 1608 | 1627 | ||||
| @@ -1736,6 +1755,9 @@ packages: | |||||
| 1736 | resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} | 1755 | resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} | ||
| 1737 | engines: {node: '>=12.0.0'} | 1756 | engines: {node: '>=12.0.0'} | ||
| 1738 | 1757 | ||||
| 1758 | + | fast-copy@3.0.2: | |||
| 1759 | + | resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} | |||
| 1760 | + | ||||
| 1739 | fast-deep-equal@3.1.3: | 1761 | fast-deep-equal@3.1.3: | ||
| 1740 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} | 1762 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} | ||
| 1741 | 1763 | ||||
| @@ -1749,6 +1771,13 @@ packages: | |||||
| 1749 | fast-levenshtein@2.0.6: | 1771 | fast-levenshtein@2.0.6: | ||
| 1750 | resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} | 1772 | resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} | ||
| 1751 | 1773 | ||||
| 1774 | + | fast-redact@3.5.0: | |||
| 1775 | + | resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} | |||
| 1776 | + | engines: {node: '>=6'} | |||
| 1777 | + | ||||
| 1778 | + | fast-safe-stringify@2.1.1: | |||
| 1779 | + | resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} | |||
| 1780 | + | ||||
| 1752 | fastq@1.19.0: | 1781 | fastq@1.19.0: | ||
| 1753 | resolution: {integrity: sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==} | 1782 | resolution: {integrity: sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==} | ||
| 1754 | 1783 | ||||
| @@ -1842,6 +1871,9 @@ packages: | |||||
| 1842 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} | 1871 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} | ||
| 1843 | engines: {node: '>= 0.4'} | 1872 | engines: {node: '>= 0.4'} | ||
| 1844 | 1873 | ||||
| 1874 | + | help-me@5.0.0: | |||
| 1875 | + | resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} | |||
| 1876 | + | ||||
| 1845 | ignore@5.3.2: | 1877 | ignore@5.3.2: | ||
| 1846 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} | 1878 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} | ||
| 1847 | engines: {node: '>= 4'} | 1879 | engines: {node: '>= 4'} | ||
| @@ -1894,6 +1926,10 @@ packages: | |||||
| 1894 | resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} | 1926 | resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} | ||
| 1895 | hasBin: true | 1927 | hasBin: true | ||
| 1896 | 1928 | ||||
| 1929 | + | joycon@3.1.1: | |||
| 1930 | + | resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} | |||
| 1931 | + | engines: {node: '>=10'} | |||
| 1932 | + | ||||
| 1897 | js-base64@3.7.7: | 1933 | js-base64@3.7.7: | ||
| 1898 | resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} | 1934 | resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} | ||
| 1899 | 1935 | ||||
| @@ -1996,6 +2032,9 @@ packages: | |||||
| 1996 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} | 2032 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} | ||
| 1997 | engines: {node: '>=16 || 14 >=14.17'} | 2033 | engines: {node: '>=16 || 14 >=14.17'} | ||
| 1998 | 2034 | ||||
| 2035 | + | minimist@1.2.8: | |||
| 2036 | + | resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} | |||
| 2037 | + | ||||
| 1999 | minipass@7.1.2: | 2038 | minipass@7.1.2: | ||
| 2000 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} | 2039 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} | ||
| 2001 | engines: {node: '>=16 || 14 >=14.17'} | 2040 | engines: {node: '>=16 || 14 >=14.17'} | ||
| @@ -2059,6 +2098,13 @@ packages: | |||||
| 2059 | ohash@1.1.4: | 2098 | ohash@1.1.4: | ||
| 2060 | resolution: {integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==} | 2099 | resolution: {integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==} | ||
| 2061 | 2100 | ||||
| 2101 | + | on-exit-leak-free@2.1.2: | |||
| 2102 | + | resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} | |||
| 2103 | + | engines: {node: '>=14.0.0'} | |||
| 2104 | + | ||||
| 2105 | + | once@1.4.0: | |||
| 2106 | + | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} | |||
| 2107 | + | ||||
| 2062 | optionator@0.9.4: | 2108 | optionator@0.9.4: | ||
| 2063 | resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} | 2109 | resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} | ||
| 2064 | engines: {node: '>= 0.8.0'} | 2110 | engines: {node: '>= 0.8.0'} | ||
| @@ -2117,6 +2163,20 @@ packages: | |||||
| 2117 | resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} | 2163 | resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} | ||
| 2118 | engines: {node: '>=0.10.0'} | 2164 | engines: {node: '>=0.10.0'} | ||
| 2119 | 2165 | ||||
| 2166 | + | pino-abstract-transport@2.0.0: | |||
| 2167 | + | resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} | |||
| 2168 | + | ||||
| 2169 | + | pino-pretty@13.0.0: | |||
| 2170 | + | resolution: {integrity: sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==} | |||
| 2171 | + | hasBin: true | |||
| 2172 | + | ||||
| 2173 | + | pino-std-serializers@7.0.0: | |||
| 2174 | + | resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} | |||
| 2175 | + | ||||
| 2176 | + | pino@9.6.0: | |||
| 2177 | + | resolution: {integrity: sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==} | |||
| 2178 | + | hasBin: true | |||
| 2179 | + | ||||
| 2120 | pirates@4.0.6: | 2180 | pirates@4.0.6: | ||
| 2121 | resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} | 2181 | resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} | ||
| 2122 | engines: {node: '>= 6'} | 2182 | engines: {node: '>= 6'} | ||
| @@ -2266,9 +2326,15 @@ packages: | |||||
| 2266 | printable-characters@1.0.42: | 2326 | printable-characters@1.0.42: | ||
| 2267 | resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} | 2327 | resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} | ||
| 2268 | 2328 | ||||
| 2329 | + | process-warning@4.0.1: | |||
| 2330 | + | resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==} | |||
| 2331 | + | ||||
| 2269 | promise-limit@2.7.0: | 2332 | promise-limit@2.7.0: | ||
| 2270 | resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==} | 2333 | resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==} | ||
| 2271 | 2334 | ||||
| 2335 | + | pump@3.0.2: | |||
| 2336 | + | resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} | |||
| 2337 | + | ||||
| 2272 | punycode@2.3.1: | 2338 | punycode@2.3.1: | ||
| 2273 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} | 2339 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} | ||
| 2274 | engines: {node: '>=6'} | 2340 | engines: {node: '>=6'} | ||
| @@ -2276,6 +2342,9 @@ packages: | |||||
| 2276 | queue-microtask@1.2.3: | 2342 | queue-microtask@1.2.3: | ||
| 2277 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} | 2343 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} | ||
| 2278 | 2344 | ||||
| 2345 | + | quick-format-unescaped@4.0.4: | |||
| 2346 | + | resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} | |||
| 2347 | + | ||||
| 2279 | read-cache@1.0.0: | 2348 | read-cache@1.0.0: | ||
| 2280 | resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} | 2349 | resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} | ||
| 2281 | 2350 | ||||
| @@ -2287,6 +2356,10 @@ packages: | |||||
| 2287 | resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==} | 2356 | resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==} | ||
| 2288 | engines: {node: '>= 14.18.0'} | 2357 | engines: {node: '>= 14.18.0'} | ||
| 2289 | 2358 | ||||
| 2359 | + | real-require@0.2.0: | |||
| 2360 | + | resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} | |||
| 2361 | + | engines: {node: '>= 12.13.0'} | |||
| 2362 | + | ||||
| 2290 | regexparam@3.0.0: | 2363 | regexparam@3.0.0: | ||
| 2291 | resolution: {integrity: sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q==} | 2364 | resolution: {integrity: sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q==} | ||
| 2292 | engines: {node: '>=8'} | 2365 | engines: {node: '>=8'} | ||
| @@ -2329,6 +2402,13 @@ packages: | |||||
| 2329 | resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} | 2402 | resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} | ||
| 2330 | engines: {node: '>=6'} | 2403 | engines: {node: '>=6'} | ||
| 2331 | 2404 | ||||
| 2405 | + | safe-stable-stringify@2.5.0: | |||
| 2406 | + | resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} | |||
| 2407 | + | engines: {node: '>=10'} | |||
| 2408 | + | ||||
| 2409 | + | secure-json-parse@2.7.0: | |||
| 2410 | + | resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} | |||
| 2411 | + | ||||
| 2332 | semver@7.7.1: | 2412 | semver@7.7.1: | ||
| 2333 | resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} | 2413 | resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} | ||
| 2334 | engines: {node: '>=10'} | 2414 | engines: {node: '>=10'} | ||
| @@ -2356,6 +2436,9 @@ packages: | |||||
| 2356 | resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} | 2436 | resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} | ||
| 2357 | engines: {node: '>=18'} | 2437 | engines: {node: '>=18'} | ||
| 2358 | 2438 | ||||
| 2439 | + | sonic-boom@4.2.0: | |||
| 2440 | + | resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} | |||
| 2441 | + | ||||
| 2359 | source-map-js@1.2.1: | 2442 | source-map-js@1.2.1: | ||
| 2360 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} | 2443 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} | ||
| 2361 | engines: {node: '>=0.10.0'} | 2444 | engines: {node: '>=0.10.0'} | ||
| @@ -2371,6 +2454,10 @@ packages: | |||||
| 2371 | resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} | 2454 | resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} | ||
| 2372 | deprecated: Please use @jridgewell/sourcemap-codec instead | 2455 | deprecated: Please use @jridgewell/sourcemap-codec instead | ||
| 2373 | 2456 | ||||
| 2457 | + | split2@4.2.0: | |||
| 2458 | + | resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} | |||
| 2459 | + | engines: {node: '>= 10.x'} | |||
| 2460 | + | ||||
| 2374 | stackback@0.0.2: | 2461 | stackback@0.0.2: | ||
| 2375 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} | 2462 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} | ||
| 2376 | 2463 | ||||
| @@ -2455,6 +2542,9 @@ packages: | |||||
| 2455 | thenify@3.3.1: | 2542 | thenify@3.3.1: | ||
| 2456 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} | 2543 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} | ||
| 2457 | 2544 | ||||
| 2545 | + | thread-stream@3.1.0: | |||
| 2546 | + | resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} | |||
| 2547 | + | ||||
| 2458 | tinybench@2.9.0: | 2548 | tinybench@2.9.0: | ||
| 2459 | resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} | 2549 | resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} | ||
| 2460 | 2550 | ||||
| @@ -2666,6 +2756,9 @@ packages: | |||||
| 2666 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} | 2756 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} | ||
| 2667 | engines: {node: '>=12'} | 2757 | engines: {node: '>=12'} | ||
| 2668 | 2758 | ||||
| 2759 | + | wrappy@1.0.2: | |||
| 2760 | + | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} | |||
| 2761 | + | ||||
| 2669 | ws@8.18.0: | 2762 | ws@8.18.0: | ||
| 2670 | resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} | 2763 | resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} | ||
| 2671 | engines: {node: '>=10.0.0'} | 2764 | engines: {node: '>=10.0.0'} | ||
| @@ -3585,6 +3678,8 @@ snapshots: | |||||
| 3585 | 3678 | ||||
| 3586 | assertion-error@2.0.1: {} | 3679 | assertion-error@2.0.1: {} | ||
| 3587 | 3680 | ||||
| 3681 | + | atomic-sleep@1.0.0: {} | |||
| 3682 | + | ||||
| 3588 | autoprefixer@10.4.20(postcss@8.5.2): | 3683 | autoprefixer@10.4.20(postcss@8.5.2): | ||
| 3589 | dependencies: | 3684 | dependencies: | ||
| 3590 | browserslist: 4.24.4 | 3685 | browserslist: 4.24.4 | ||
| @@ -3672,6 +3767,8 @@ snapshots: | |||||
| 3672 | 3767 | ||||
| 3673 | color-name@1.1.4: {} | 3768 | color-name@1.1.4: {} | ||
| 3674 | 3769 | ||||
| 3770 | + | colorette@2.0.20: {} | |||
| 3771 | + | ||||
| 3675 | commander@2.20.3: {} | 3772 | commander@2.20.3: {} | ||
| 3676 | 3773 | ||||
| 3677 | commander@4.1.1: {} | 3774 | commander@4.1.1: {} | ||
| @@ -3696,6 +3793,8 @@ snapshots: | |||||
| 3696 | 3793 | ||||
| 3697 | data-uri-to-buffer@4.0.1: {} | 3794 | data-uri-to-buffer@4.0.1: {} | ||
| 3698 | 3795 | ||||
| 3796 | + | dateformat@4.6.3: {} | |||
| 3797 | + | ||||
| 3699 | debug@4.4.0: | 3798 | debug@4.4.0: | ||
| 3700 | dependencies: | 3799 | dependencies: | ||
| 3701 | ms: 2.1.3 | 3800 | ms: 2.1.3 | ||
| @@ -3740,6 +3839,10 @@ snapshots: | |||||
| 3740 | 3839 | ||||
| 3741 | emoji-regex@9.2.2: {} | 3840 | emoji-regex@9.2.2: {} | ||
| 3742 | 3841 | ||||
| 3842 | + | end-of-stream@1.4.4: | |||
| 3843 | + | dependencies: | |||
| 3844 | + | once: 1.4.0 | |||
| 3845 | + | ||||
| 3743 | es-module-lexer@1.6.0: {} | 3846 | es-module-lexer@1.6.0: {} | ||
| 3744 | 3847 | ||||
| 3745 | esbuild-register@3.6.0(esbuild@0.19.12): | 3848 | esbuild-register@3.6.0(esbuild@0.19.12): | ||
| @@ -4007,6 +4110,8 @@ snapshots: | |||||
| 4007 | 4110 | ||||
| 4008 | expect-type@1.1.0: {} | 4111 | expect-type@1.1.0: {} | ||
| 4009 | 4112 | ||||
| 4113 | + | fast-copy@3.0.2: {} | |||
| 4114 | + | ||||
| 4010 | fast-deep-equal@3.1.3: {} | 4115 | fast-deep-equal@3.1.3: {} | ||
| 4011 | 4116 | ||||
| 4012 | fast-glob@3.3.3: | 4117 | fast-glob@3.3.3: | ||
| @@ -4021,6 +4126,10 @@ snapshots: | |||||
| 4021 | 4126 | ||||
| 4022 | fast-levenshtein@2.0.6: {} | 4127 | fast-levenshtein@2.0.6: {} | ||
| 4023 | 4128 | ||||
| 4129 | + | fast-redact@3.5.0: {} | |||
| 4130 | + | ||||
| 4131 | + | fast-safe-stringify@2.1.1: {} | |||
| 4132 | + | ||||
| 4024 | fastq@1.19.0: | 4133 | fastq@1.19.0: | ||
| 4025 | dependencies: | 4134 | dependencies: | ||
| 4026 | reusify: 1.0.4 | 4135 | reusify: 1.0.4 | ||
| @@ -4108,6 +4217,8 @@ snapshots: | |||||
| 4108 | dependencies: | 4217 | dependencies: | ||
| 4109 | function-bind: 1.1.2 | 4218 | function-bind: 1.1.2 | ||
| 4110 | 4219 | ||||
| 4220 | + | help-me@5.0.0: {} | |||
| 4221 | + | ||||
| 4111 | ignore@5.3.2: {} | 4222 | ignore@5.3.2: {} | ||
| 4112 | 4223 | ||||
| 4113 | import-fresh@3.3.1: | 4224 | import-fresh@3.3.1: | ||
| @@ -4151,6 +4262,8 @@ snapshots: | |||||
| 4151 | 4262 | ||||
| 4152 | jiti@1.21.7: {} | 4263 | jiti@1.21.7: {} | ||
| 4153 | 4264 | ||||
| 4265 | + | joycon@3.1.1: {} | |||
| 4266 | + | ||||
| 4154 | js-base64@3.7.7: {} | 4267 | js-base64@3.7.7: {} | ||
| 4155 | 4268 | ||||
| 4156 | js-yaml@4.1.0: | 4269 | js-yaml@4.1.0: | ||
| @@ -4255,6 +4368,8 @@ snapshots: | |||||
| 4255 | dependencies: | 4368 | dependencies: | ||
| 4256 | brace-expansion: 2.0.1 | 4369 | brace-expansion: 2.0.1 | ||
| 4257 | 4370 | ||||
| 4371 | + | minimist@1.2.8: {} | |||
| 4372 | + | ||||
| 4258 | minipass@7.1.2: {} | 4373 | minipass@7.1.2: {} | ||
| 4259 | 4374 | ||||
| 4260 | mlly@1.7.4: | 4375 | mlly@1.7.4: | ||
| @@ -4302,6 +4417,12 @@ snapshots: | |||||
| 4302 | 4417 | ||||
| 4303 | ohash@1.1.4: {} | 4418 | ohash@1.1.4: {} | ||
| 4304 | 4419 | ||||
| 4420 | + | on-exit-leak-free@2.1.2: {} | |||
| 4421 | + | ||||
| 4422 | + | once@1.4.0: | |||
| 4423 | + | dependencies: | |||
| 4424 | + | wrappy: 1.0.2 | |||
| 4425 | + | ||||
| 4305 | optionator@0.9.4: | 4426 | optionator@0.9.4: | ||
| 4306 | dependencies: | 4427 | dependencies: | ||
| 4307 | deep-is: 0.1.4 | 4428 | deep-is: 0.1.4 | ||
| @@ -4350,6 +4471,42 @@ snapshots: | |||||
| 4350 | 4471 | ||||
| 4351 | pify@2.3.0: {} | 4472 | pify@2.3.0: {} | ||
| 4352 | 4473 | ||||
| 4474 | + | pino-abstract-transport@2.0.0: | |||
| 4475 | + | dependencies: | |||
| 4476 | + | split2: 4.2.0 | |||
| 4477 | + | ||||
| 4478 | + | pino-pretty@13.0.0: | |||
| 4479 | + | dependencies: | |||
| 4480 | + | colorette: 2.0.20 | |||
| 4481 | + | dateformat: 4.6.3 | |||
| 4482 | + | fast-copy: 3.0.2 | |||
| 4483 | + | fast-safe-stringify: 2.1.1 | |||
| 4484 | + | help-me: 5.0.0 | |||
| 4485 | + | joycon: 3.1.1 | |||
| 4486 | + | minimist: 1.2.8 | |||
| 4487 | + | on-exit-leak-free: 2.1.2 | |||
| 4488 | + | pino-abstract-transport: 2.0.0 | |||
| 4489 | + | pump: 3.0.2 | |||
| 4490 | + | secure-json-parse: 2.7.0 | |||
| 4491 | + | sonic-boom: 4.2.0 | |||
| 4492 | + | strip-json-comments: 3.1.1 | |||
| 4493 | + | ||||
| 4494 | + | pino-std-serializers@7.0.0: {} | |||
| 4495 | + | ||||
| 4496 | + | pino@9.6.0: | |||
| 4497 | + | dependencies: | |||
| 4498 | + | atomic-sleep: 1.0.0 | |||
| 4499 | + | fast-redact: 3.5.0 | |||
| 4500 | + | on-exit-leak-free: 2.1.2 | |||
| 4501 | + | pino-abstract-transport: 2.0.0 | |||
| 4502 | + | pino-std-serializers: 7.0.0 | |||
| 4503 | + | process-warning: 4.0.1 | |||
| 4504 | + | quick-format-unescaped: 4.0.4 | |||
| 4505 | + | real-require: 0.2.0 | |||
| 4506 | + | safe-stable-stringify: 2.5.0 | |||
| 4507 | + | sonic-boom: 4.2.0 | |||
| 4508 | + | thread-stream: 3.1.0 | |||
| 4509 | + | ||||
| 4353 | pirates@4.0.6: {} | 4510 | pirates@4.0.6: {} | ||
| 4354 | 4511 | ||||
| 4355 | pkg-types@1.3.1: | 4512 | pkg-types@1.3.1: | ||
| @@ -4432,12 +4589,21 @@ snapshots: | |||||
| 4432 | 4589 | ||||
| 4433 | printable-characters@1.0.42: {} | 4590 | printable-characters@1.0.42: {} | ||
| 4434 | 4591 | ||||
| 4592 | + | process-warning@4.0.1: {} | |||
| 4593 | + | ||||
| 4435 | promise-limit@2.7.0: {} | 4594 | promise-limit@2.7.0: {} | ||
| 4436 | 4595 | ||||
| 4596 | + | pump@3.0.2: | |||
| 4597 | + | dependencies: | |||
| 4598 | + | end-of-stream: 1.4.4 | |||
| 4599 | + | once: 1.4.0 | |||
| 4600 | + | ||||
| 4437 | punycode@2.3.1: {} | 4601 | punycode@2.3.1: {} | ||
| 4438 | 4602 | ||||
| 4439 | queue-microtask@1.2.3: {} | 4603 | queue-microtask@1.2.3: {} | ||
| 4440 | 4604 | ||||
| 4605 | + | quick-format-unescaped@4.0.4: {} | |||
| 4606 | + | ||||
| 4441 | read-cache@1.0.0: | 4607 | read-cache@1.0.0: | ||
| 4442 | dependencies: | 4608 | dependencies: | ||
| 4443 | pify: 2.3.0 | 4609 | pify: 2.3.0 | ||
| @@ -4448,6 +4614,8 @@ snapshots: | |||||
| 4448 | 4614 | ||||
| 4449 | readdirp@4.1.1: {} | 4615 | readdirp@4.1.1: {} | ||
| 4450 | 4616 | ||||
| 4617 | + | real-require@0.2.0: {} | |||
| 4618 | + | ||||
| 4451 | regexparam@3.0.0: {} | 4619 | regexparam@3.0.0: {} | ||
| 4452 | 4620 | ||||
| 4453 | resolve-from@4.0.0: {} | 4621 | resolve-from@4.0.0: {} | ||
| @@ -4509,6 +4677,10 @@ snapshots: | |||||
| 4509 | dependencies: | 4677 | dependencies: | ||
| 4510 | mri: 1.2.0 | 4678 | mri: 1.2.0 | ||
| 4511 | 4679 | ||||
| 4680 | + | safe-stable-stringify@2.5.0: {} | |||
| 4681 | + | ||||
| 4682 | + | secure-json-parse@2.7.0: {} | |||
| 4683 | + | ||||
| 4512 | semver@7.7.1: {} | 4684 | semver@7.7.1: {} | ||
| 4513 | 4685 | ||||
| 4514 | set-cookie-parser@2.7.1: {} | 4686 | set-cookie-parser@2.7.1: {} | ||
| @@ -4529,6 +4701,10 @@ snapshots: | |||||
| 4529 | mrmime: 2.0.0 | 4701 | mrmime: 2.0.0 | ||
| 4530 | totalist: 3.0.1 | 4702 | totalist: 3.0.1 | ||
| 4531 | 4703 | ||||
| 4704 | + | sonic-boom@4.2.0: | |||
| 4705 | + | dependencies: | |||
| 4706 | + | atomic-sleep: 1.0.0 | |||
| 4707 | + | ||||
| 4532 | source-map-js@1.2.1: {} | 4708 | source-map-js@1.2.1: {} | ||
| 4533 | 4709 | ||||
| 4534 | source-map-support@0.5.21: | 4710 | source-map-support@0.5.21: | ||
| @@ -4540,6 +4716,8 @@ snapshots: | |||||
| 4540 | 4716 | ||||
| 4541 | sourcemap-codec@1.4.8: {} | 4717 | sourcemap-codec@1.4.8: {} | ||
| 4542 | 4718 | ||||
| 4719 | + | split2@4.2.0: {} | |||
| 4720 | + | ||||
| 4543 | stackback@0.0.2: {} | 4721 | stackback@0.0.2: {} | ||
| 4544 | 4722 | ||||
| 4545 | stacktracey@2.1.8: | 4723 | stacktracey@2.1.8: | ||
| @@ -4670,6 +4848,10 @@ snapshots: | |||||
| 4670 | dependencies: | 4848 | dependencies: | ||
| 4671 | any-promise: 1.3.0 | 4849 | any-promise: 1.3.0 | ||
| 4672 | 4850 | ||||
| 4851 | + | thread-stream@3.1.0: | |||
| 4852 | + | dependencies: | |||
| 4853 | + | real-require: 0.2.0 | |||
| 4854 | + | ||||
| 4673 | tinybench@2.9.0: {} | 4855 | tinybench@2.9.0: {} | ||
| 4674 | 4856 | ||||
| 4675 | tinyexec@0.3.2: {} | 4857 | tinyexec@0.3.2: {} | ||
| @@ -4877,6 +5059,8 @@ snapshots: | |||||
| 4877 | string-width: 5.1.2 | 5059 | string-width: 5.1.2 | ||
| 4878 | strip-ansi: 7.1.0 | 5060 | strip-ansi: 7.1.0 | ||
| 4879 | 5061 | ||||
| 5062 | + | wrappy@1.0.2: {} | |||
| 5063 | + | ||||
| 4880 | ws@8.18.0: {} | 5064 | ws@8.18.0: {} | ||
| 4881 | 5065 | ||||
| 4882 | xp.css@0.2.6: {} | 5066 | xp.css@0.2.6: {} | ||
M src/hooks.server.ts +16 -13
| @@ -6,33 +6,36 @@ import db from "$lib/db/db.server"; | |||
| 6 | 6 | import { handleRateLimit } from "$lib/api/rate-limiter"; | |
| 7 | 7 | import { users } from "$lib/db/schema"; | |
| 8 | 8 | import { initializeMessageCache } from "$lib/cache/initialize-cache"; | |
| 9 | + | import { createLogger } from "$lib/utils/logger.server"; | |
| 10 | + | ||
| 11 | + | const log = createLogger('hooks-server'); | |
| 9 | 12 | ||
| 10 | 13 | async function setAllUsersOffline() { | |
| 11 | 14 | try { | |
| 12 | - | console.log("Setting all users to offline status..."); | |
| 15 | + | log.info("Setting all users to offline status..."); | |
| 13 | 16 | await db.update(users) | |
| 14 | 17 | .set({ | |
| 15 | 18 | status: "offline" | |
| 16 | 19 | }); | |
| 17 | - | console.log("Successfully set all users to offline status"); | |
| 20 | + | log.info("Successfully set all users to offline status"); | |
| 18 | 21 | } catch (error) { | |
| 19 | - | console.error("Failed to set users offline:", error); | |
| 22 | + | log.error("Failed to set users offline", { error }); | |
| 20 | 23 | } | |
| 21 | 24 | } | |
| 22 | 25 | ||
| 23 | 26 | // Initialize database when the server starts | |
| 24 | - | console.debug("Initializing database on server start..."); | |
| 27 | + | log.debug("Initializing database on server start..."); | |
| 25 | 28 | initializeDatabase() | |
| 26 | 29 | .then(async () => { | |
| 27 | 30 | await setAllUsersOffline(); | |
| 28 | 31 | await initializeMessageCache(); | |
| 29 | 32 | }) | |
| 30 | 33 | .catch(error => { | |
| 31 | - | console.error('Failed to initialize database:', error); | |
| 34 | + | log.error('Failed to initialize database', { error }); | |
| 32 | 35 | }); | |
| 33 | 36 | ||
| 34 | 37 | export const handle: Handle = async ({ event, resolve }) => { | |
| 35 | - | console.debug("Handling request:", event.url.pathname); | |
| 38 | + | log.debug("Handling request", { path: event.url.pathname }); | |
| 36 | 39 | ||
| 37 | 40 | // Identify public chat and room requests | |
| 38 | 41 | const isPublicChatRequest = | |
| @@ -69,7 +72,7 @@ export const handle: Handle = async ({ event, resolve }) => { | |||
| 69 | 72 | ||
| 70 | 73 | // Handle public chat/room requests | |
| 71 | 74 | if (isPublicChatRequest || isPublicRoomRequest) { | |
| 72 | - | console.debug("Public request accessed:", event.url.pathname); | |
| 75 | + | log.debug("Public request accessed", { path: event.url.pathname }); | |
| 73 | 76 | event.locals.user = null; | |
| 74 | 77 | event.locals.session = null; // Public access doesn't require a session | |
| 75 | 78 | const response = await resolve(event); | |
| @@ -78,11 +81,11 @@ export const handle: Handle = async ({ event, resolve }) => { | |||
| 78 | 81 | ||
| 79 | 82 | // Handle public routes: if token exists, validate and propagate | |
| 80 | 83 | if (publicRoutes.includes(event.url.pathname)) { | |
| 81 | - | console.debug("Public route accessed:", event.url.pathname); | |
| 84 | + | log.debug("Public route accessed", { path: event.url.pathname }); | |
| 82 | 85 | if (token) { | |
| 83 | 86 | const { session, user } = await validateSessionToken(token); | |
| 84 | 87 | if (session !== null) { | |
| 85 | - | console.debug("Valid session found on public route for user:", user?.nickname); | |
| 88 | + | log.debug("Valid session found on public route", { user: user?.nickname }); | |
| 86 | 89 | setSessionTokenCookie(event, token, session.expiresAt); | |
| 87 | 90 | event.locals.session = session; | |
| 88 | 91 | event.locals.user = user; | |
| @@ -94,7 +97,7 @@ export const handle: Handle = async ({ event, resolve }) => { | |||
| 94 | 97 | ||
| 95 | 98 | // Protected routes: ensure authentication | |
| 96 | 99 | if (token === null) { | |
| 97 | - | console.debug("No session token found"); | |
| 100 | + | log.debug("No session token found"); | |
| 98 | 101 | event.locals.user = null; | |
| 99 | 102 | event.locals.session = null; | |
| 100 | 103 | const response = new Response("Unauthorized", { status: 401 }); | |
| @@ -103,12 +106,12 @@ export const handle: Handle = async ({ event, resolve }) => { | |||
| 103 | 106 | ||
| 104 | 107 | const { session, user } = await validateSessionToken(token); | |
| 105 | 108 | if (session !== null) { | |
| 106 | - | console.debug("Valid session found for user:", user?.nickname); | |
| 109 | + | log.debug("Valid session found", { user: user?.nickname }); | |
| 107 | 110 | setSessionTokenCookie(event, token, session.expiresAt); | |
| 108 | 111 | event.locals.session = session; | |
| 109 | 112 | event.locals.user = user; | |
| 110 | 113 | } else { | |
| 111 | - | console.debug("Invalid session token"); | |
| 114 | + | log.debug("Invalid session token"); | |
| 112 | 115 | deleteSessionTokenCookie(event); | |
| 113 | 116 | event.locals.session = null; | |
| 114 | 117 | event.locals.user = null; | |
| @@ -118,7 +121,7 @@ export const handle: Handle = async ({ event, resolve }) => { | |||
| 118 | 121 | ||
| 119 | 122 | // After authentication, ensure SSE endpoint does not trigger rate limiting | |
| 120 | 123 | if (event.url.pathname.startsWith('/api/sse') && !event.locals.user) { | |
| 121 | - | console.debug("SSE endpoint accessed without valid session, returning 401 without rate limiting"); | |
| 124 | + | log.debug("SSE endpoint accessed without valid session, returning 401 without rate limiting"); | |
| 122 | 125 | return addDebugHeaders(new Response("Unauthorized", { status: 401 })); | |
| 123 | 126 | } | |
| 124 | 127 | ||
| @@ -6,33 +6,36 @@ import db from "$lib/db/db.server"; | |||||
| 6 | import { handleRateLimit } from "$lib/api/rate-limiter"; | 6 | import { handleRateLimit } from "$lib/api/rate-limiter"; | ||
| 7 | import { users } from "$lib/db/schema"; | 7 | import { users } from "$lib/db/schema"; | ||
| 8 | import { initializeMessageCache } from "$lib/cache/initialize-cache"; | 8 | import { initializeMessageCache } from "$lib/cache/initialize-cache"; | ||
| 9 | + | import { createLogger } from "$lib/utils/logger.server"; | |||
| 10 | + | ||||
| 11 | + | const log = createLogger('hooks-server'); | |||
| 9 | 12 | ||||
| 10 | async function setAllUsersOffline() { | 13 | async function setAllUsersOffline() { | ||
| 11 | try { | 14 | try { | ||
| 12 | - | console.log("Setting all users to offline status..."); | 15 | + | log.info("Setting all users to offline status..."); |
| 13 | await db.update(users) | 16 | await db.update(users) | ||
| 14 | .set({ | 17 | .set({ | ||
| 15 | status: "offline" | 18 | status: "offline" | ||
| 16 | }); | 19 | }); | ||
| 17 | - | console.log("Successfully set all users to offline status"); | 20 | + | log.info("Successfully set all users to offline status"); |
| 18 | } catch (error) { | 21 | } catch (error) { | ||
| 19 | - | console.error("Failed to set users offline:", error); | 22 | + | log.error("Failed to set users offline", { error }); |
| 20 | } | 23 | } | ||
| 21 | } | 24 | } | ||
| 22 | 25 | ||||
| 23 | // Initialize database when the server starts | 26 | // Initialize database when the server starts | ||
| 24 | - | console.debug("Initializing database on server start..."); | 27 | + | log.debug("Initializing database on server start..."); |
| 25 | initializeDatabase() | 28 | initializeDatabase() | ||
| 26 | .then(async () => { | 29 | .then(async () => { | ||
| 27 | await setAllUsersOffline(); | 30 | await setAllUsersOffline(); | ||
| 28 | await initializeMessageCache(); | 31 | await initializeMessageCache(); | ||
| 29 | }) | 32 | }) | ||
| 30 | .catch(error => { | 33 | .catch(error => { | ||
| 31 | - | console.error('Failed to initialize database:', error); | 34 | + | log.error('Failed to initialize database', { error }); |
| 32 | }); | 35 | }); | ||
| 33 | 36 | ||||
| 34 | export const handle: Handle = async ({ event, resolve }) => { | 37 | export const handle: Handle = async ({ event, resolve }) => { | ||
| 35 | - | console.debug("Handling request:", event.url.pathname); | 38 | + | log.debug("Handling request", { path: event.url.pathname }); |
| 36 | 39 | ||||
| 37 | // Identify public chat and room requests | 40 | // Identify public chat and room requests | ||
| 38 | const isPublicChatRequest = | 41 | const isPublicChatRequest = | ||
| @@ -69,7 +72,7 @@ export const handle: Handle = async ({ event, resolve }) => { | |||||
| 69 | 72 | ||||
| 70 | // Handle public chat/room requests | 73 | // Handle public chat/room requests | ||
| 71 | if (isPublicChatRequest || isPublicRoomRequest) { | 74 | if (isPublicChatRequest || isPublicRoomRequest) { | ||
| 72 | - | console.debug("Public request accessed:", event.url.pathname); | 75 | + | log.debug("Public request accessed", { path: event.url.pathname }); |
| 73 | event.locals.user = null; | 76 | event.locals.user = null; | ||
| 74 | event.locals.session = null; // Public access doesn't require a session | 77 | event.locals.session = null; // Public access doesn't require a session | ||
| 75 | const response = await resolve(event); | 78 | const response = await resolve(event); | ||
| @@ -78,11 +81,11 @@ export const handle: Handle = async ({ event, resolve }) => { | |||||
| 78 | 81 | ||||
| 79 | // Handle public routes: if token exists, validate and propagate | 82 | // Handle public routes: if token exists, validate and propagate | ||
| 80 | if (publicRoutes.includes(event.url.pathname)) { | 83 | if (publicRoutes.includes(event.url.pathname)) { | ||
| 81 | - | console.debug("Public route accessed:", event.url.pathname); | 84 | + | log.debug("Public route accessed", { path: event.url.pathname }); |
| 82 | if (token) { | 85 | if (token) { | ||
| 83 | const { session, user } = await validateSessionToken(token); | 86 | const { session, user } = await validateSessionToken(token); | ||
| 84 | if (session !== null) { | 87 | if (session !== null) { | ||
| 85 | - | console.debug("Valid session found on public route for user:", user?.nickname); | 88 | + | log.debug("Valid session found on public route", { user: user?.nickname }); |
| 86 | setSessionTokenCookie(event, token, session.expiresAt); | 89 | setSessionTokenCookie(event, token, session.expiresAt); | ||
| 87 | event.locals.session = session; | 90 | event.locals.session = session; | ||
| 88 | event.locals.user = user; | 91 | event.locals.user = user; | ||
| @@ -94,7 +97,7 @@ export const handle: Handle = async ({ event, resolve }) => { | |||||
| 94 | 97 | ||||
| 95 | // Protected routes: ensure authentication | 98 | // Protected routes: ensure authentication | ||
| 96 | if (token === null) { | 99 | if (token === null) { | ||
| 97 | - | console.debug("No session token found"); | 100 | + | log.debug("No session token found"); |
| 98 | event.locals.user = null; | 101 | event.locals.user = null; | ||
| 99 | event.locals.session = null; | 102 | event.locals.session = null; | ||
| 100 | const response = new Response("Unauthorized", { status: 401 }); | 103 | const response = new Response("Unauthorized", { status: 401 }); | ||
| @@ -103,12 +106,12 @@ export const handle: Handle = async ({ event, resolve }) => { | |||||
| 103 | 106 | ||||
| 104 | const { session, user } = await validateSessionToken(token); | 107 | const { session, user } = await validateSessionToken(token); | ||
| 105 | if (session !== null) { | 108 | if (session !== null) { | ||
| 106 | - | console.debug("Valid session found for user:", user?.nickname); | 109 | + | log.debug("Valid session found", { user: user?.nickname }); |
| 107 | setSessionTokenCookie(event, token, session.expiresAt); | 110 | setSessionTokenCookie(event, token, session.expiresAt); | ||
| 108 | event.locals.session = session; | 111 | event.locals.session = session; | ||
| 109 | event.locals.user = user; | 112 | event.locals.user = user; | ||
| 110 | } else { | 113 | } else { | ||
| 111 | - | console.debug("Invalid session token"); | 114 | + | log.debug("Invalid session token"); |
| 112 | deleteSessionTokenCookie(event); | 115 | deleteSessionTokenCookie(event); | ||
| 113 | event.locals.session = null; | 116 | event.locals.session = null; | ||
| 114 | event.locals.user = null; | 117 | event.locals.user = null; | ||
| @@ -118,7 +121,7 @@ export const handle: Handle = async ({ event, resolve }) => { | |||||
| 118 | 121 | ||||
| 119 | // After authentication, ensure SSE endpoint does not trigger rate limiting | 122 | // After authentication, ensure SSE endpoint does not trigger rate limiting | ||
| 120 | if (event.url.pathname.startsWith('/api/sse') && !event.locals.user) { | 123 | if (event.url.pathname.startsWith('/api/sse') && !event.locals.user) { | ||
| 121 | - | console.debug("SSE endpoint accessed without valid session, returning 401 without rate limiting"); | 124 | + | log.debug("SSE endpoint accessed without valid session, returning 401 without rate limiting"); |
| 122 | return addDebugHeaders(new Response("Unauthorized", { status: 401 })); | 125 | return addDebugHeaders(new Response("Unauthorized", { status: 401 })); | ||
| 123 | } | 126 | } | ||
| 124 | 127 | ||||
M src/lib/api/rate-limiter.ts +5 -2
| @@ -1,4 +1,7 @@ | |||
| 1 | 1 | import type { RequestEvent } from '@sveltejs/kit'; | |
| 2 | + | import { createLogger } from '$lib/utils/logger.server'; | |
| 3 | + | ||
| 4 | + | const log = createLogger('rate-limiter'); | |
| 2 | 5 | ||
| 3 | 6 | const isDev = import.meta.env.DEV; | |
| 4 | 7 | ||
| @@ -86,7 +89,7 @@ function isRateLimited(ip: string, endpointType: keyof typeof RATE_LIMITS, isAut | |||
| 86 | 89 | ||
| 87 | 90 | // Check if we're over the limit | |
| 88 | 91 | if (validRequests.length >= points) { | |
| 89 | - | console.debug(`Rate limit exceeded for IP ${ip} on ${endpointType} endpoint (${isAuthenticated ? 'authenticated' : 'unauthenticated'})`); | |
| 92 | + | log.debug(`Rate limit exceeded for IP ${ip} on ${endpointType} endpoint (${isAuthenticated ? 'authenticated' : 'unauthenticated'})`); | |
| 90 | 93 | const oldestRequest = validRequests[0]; | |
| 91 | 94 | const retryAfter = durationMs - (now - oldestRequest.timestamp); | |
| 92 | 95 | return { limited: true, retryAfter }; | |
| @@ -104,7 +107,7 @@ function isRateLimited(ip: string, endpointType: keyof typeof RATE_LIMITS, isAut | |||
| 104 | 107 | const requests = ipRequests.get(ip) || []; | |
| 105 | 108 | const validRequests = requests.filter(req => now - req.timestamp < durationMs); | |
| 106 | 109 | ||
| 107 | - | console.debug(`Rate limit check for ${ip} on ${endpointType}: ${validRequests.length}/${points} requests in the last ${durationMs/1000}s`); | |
| 110 | + | log.debug(`Rate limit check for ${ip} on ${endpointType}: ${validRequests.length}/${points} requests in the last ${durationMs/1000}s`); | |
| 108 | 111 | ||
| 109 | 112 | if (validRequests.length >= points) { | |
| 110 | 113 | const oldestRequest = validRequests[0]; | |
| @@ -1,4 +1,7 @@ | |||||
| 1 | import type { RequestEvent } from '@sveltejs/kit'; | 1 | import type { RequestEvent } from '@sveltejs/kit'; | ||
| 2 | + | import { createLogger } from '$lib/utils/logger.server'; | |||
| 3 | + | ||||
| 4 | + | const log = createLogger('rate-limiter'); | |||
| 2 | 5 | ||||
| 3 | const isDev = import.meta.env.DEV; | 6 | const isDev = import.meta.env.DEV; | ||
| 4 | 7 | ||||
| @@ -86,7 +89,7 @@ function isRateLimited(ip: string, endpointType: keyof typeof RATE_LIMITS, isAut | |||||
| 86 | 89 | ||||
| 87 | // Check if we're over the limit | 90 | // Check if we're over the limit | ||
| 88 | if (validRequests.length >= points) { | 91 | if (validRequests.length >= points) { | ||
| 89 | - | console.debug(`Rate limit exceeded for IP ${ip} on ${endpointType} endpoint (${isAuthenticated ? 'authenticated' : 'unauthenticated'})`); | 92 | + | log.debug(`Rate limit exceeded for IP ${ip} on ${endpointType} endpoint (${isAuthenticated ? 'authenticated' : 'unauthenticated'})`); |
| 90 | const oldestRequest = validRequests[0]; | 93 | const oldestRequest = validRequests[0]; | ||
| 91 | const retryAfter = durationMs - (now - oldestRequest.timestamp); | 94 | const retryAfter = durationMs - (now - oldestRequest.timestamp); | ||
| 92 | return { limited: true, retryAfter }; | 95 | return { limited: true, retryAfter }; | ||
| @@ -104,7 +107,7 @@ function isRateLimited(ip: string, endpointType: keyof typeof RATE_LIMITS, isAut | |||||
| 104 | const requests = ipRequests.get(ip) || []; | 107 | const requests = ipRequests.get(ip) || []; | ||
| 105 | const validRequests = requests.filter(req => now - req.timestamp < durationMs); | 108 | const validRequests = requests.filter(req => now - req.timestamp < durationMs); | ||
| 106 | 109 | ||||
| 107 | - | console.debug(`Rate limit check for ${ip} on ${endpointType}: ${validRequests.length}/${points} requests in the last ${durationMs/1000}s`); | 110 | + | log.debug(`Rate limit check for ${ip} on ${endpointType}: ${validRequests.length}/${points} requests in the last ${durationMs/1000}s`); |
| 108 | 111 | ||||
| 109 | if (validRequests.length >= points) { | 112 | if (validRequests.length >= points) { | ||
| 110 | const oldestRequest = validRequests[0]; | 113 | const oldestRequest = validRequests[0]; | ||
M src/lib/api/session.cookie.ts +10 -6
| @@ -1,11 +1,15 @@ | |||
| 1 | 1 | import type { Cookies } from "@sveltejs/kit"; | |
| 2 | + | import { createLogger } from "$lib/utils/logger.server"; | |
| 3 | + | ||
| 4 | + | const log = createLogger('session-cookie'); | |
| 2 | 5 | ||
| 3 | 6 | // Helper function to determine cookie domain | |
| 4 | 7 | function getCookieDomain(): string | undefined { | |
| 5 | 8 | if (process.env.NODE_ENV === 'production') { | |
| 6 | 9 | // Check for Cloudflare Pages domain | |
| 7 | 10 | if (process.env.CF_PAGES_URL) { | |
| 8 | - | console.debug("Cloudflare Pages URL:", process.env.CF_PAGES_URL); | |
| 11 | + | const url = process.env.CF_PAGES_URL; | |
| 12 | + | log.debug("Cloudflare Pages URL:", { url }); | |
| 9 | 13 | // For preview deployments, use host-only cookie | |
| 10 | 14 | if (process.env.CF_PAGES_BRANCH !== 'main') { | |
| 11 | 15 | return undefined; | |
| @@ -20,10 +24,10 @@ function getCookieDomain(): string | undefined { | |||
| 20 | 24 | } | |
| 21 | 25 | ||
| 22 | 26 | export function setSessionTokenCookie({ cookies }: { cookies: Cookies }, token: string, expiresAt: number): void { | |
| 23 | - | console.debug("Setting session cookie with expiry:", new Date(expiresAt)); | |
| 27 | + | log.debug("Setting session cookie with expiry:", new Date(expiresAt)); | |
| 24 | 28 | ||
| 25 | 29 | const domain = getCookieDomain(); | |
| 26 | - | console.debug("Setting cookie for domain:", domain); | |
| 30 | + | log.debug("Setting cookie for domain:", { domain }); | |
| 27 | 31 | ||
| 28 | 32 | cookies.set("session", token, { | |
| 29 | 33 | httpOnly: true, | |
| @@ -36,10 +40,10 @@ export function setSessionTokenCookie({ cookies }: { cookies: Cookies }, token: | |||
| 36 | 40 | } | |
| 37 | 41 | ||
| 38 | 42 | export function deleteSessionTokenCookie({ cookies }: { cookies: Cookies }): void { | |
| 39 | - | console.debug("Deleting session cookie"); | |
| 43 | + | log.debug("Deleting session cookie"); | |
| 40 | 44 | ||
| 41 | 45 | const domain = getCookieDomain(); | |
| 42 | - | console.debug("Deleting cookie for domain:", domain); | |
| 46 | + | log.debug("Deleting cookie for domain:", { domain }); | |
| 43 | 47 | ||
| 44 | 48 | cookies.set("session", "", { | |
| 45 | 49 | httpOnly: true, | |
| @@ -52,6 +56,6 @@ export function deleteSessionTokenCookie({ cookies }: { cookies: Cookies }): voi | |||
| 52 | 56 | } | |
| 53 | 57 | ||
| 54 | 58 | export function getSessionTokenCookie({ cookies }: { cookies: Cookies }): string | undefined { | |
| 55 | - | console.debug("Getting session cookie"); | |
| 59 | + | log.debug("Getting session cookie"); | |
| 56 | 60 | return cookies.get("session"); | |
| 57 | 61 | } ⏎̸ | |
| @@ -1,11 +1,15 @@ | |||||
| 1 | import type { Cookies } from "@sveltejs/kit"; | 1 | import type { Cookies } from "@sveltejs/kit"; | ||
| 2 | + | import { createLogger } from "$lib/utils/logger.server"; | |||
| 3 | + | ||||
| 4 | + | const log = createLogger('session-cookie'); | |||
| 2 | 5 | ||||
| 3 | // Helper function to determine cookie domain | 6 | // Helper function to determine cookie domain | ||
| 4 | function getCookieDomain(): string | undefined { | 7 | function getCookieDomain(): string | undefined { | ||
| 5 | if (process.env.NODE_ENV === 'production') { | 8 | if (process.env.NODE_ENV === 'production') { | ||
| 6 | // Check for Cloudflare Pages domain | 9 | // Check for Cloudflare Pages domain | ||
| 7 | if (process.env.CF_PAGES_URL) { | 10 | if (process.env.CF_PAGES_URL) { | ||
| 8 | - | console.debug("Cloudflare Pages URL:", process.env.CF_PAGES_URL); | 11 | + | const url = process.env.CF_PAGES_URL; |
| 12 | + | log.debug("Cloudflare Pages URL:", { url }); | |||
| 9 | // For preview deployments, use host-only cookie | 13 | // For preview deployments, use host-only cookie | ||
| 10 | if (process.env.CF_PAGES_BRANCH !== 'main') { | 14 | if (process.env.CF_PAGES_BRANCH !== 'main') { | ||
| 11 | return undefined; | 15 | return undefined; | ||
| @@ -20,10 +24,10 @@ function getCookieDomain(): string | undefined { | |||||
| 20 | } | 24 | } | ||
| 21 | 25 | ||||
| 22 | export function setSessionTokenCookie({ cookies }: { cookies: Cookies }, token: string, expiresAt: number): void { | 26 | export function setSessionTokenCookie({ cookies }: { cookies: Cookies }, token: string, expiresAt: number): void { | ||
| 23 | - | console.debug("Setting session cookie with expiry:", new Date(expiresAt)); | 27 | + | log.debug("Setting session cookie with expiry:", new Date(expiresAt)); |
| 24 | 28 | ||||
| 25 | const domain = getCookieDomain(); | 29 | const domain = getCookieDomain(); | ||
| 26 | - | console.debug("Setting cookie for domain:", domain); | 30 | + | log.debug("Setting cookie for domain:", { domain }); |
| 27 | 31 | ||||
| 28 | cookies.set("session", token, { | 32 | cookies.set("session", token, { | ||
| 29 | httpOnly: true, | 33 | httpOnly: true, | ||
| @@ -36,10 +40,10 @@ export function setSessionTokenCookie({ cookies }: { cookies: Cookies }, token: | |||||
| 36 | } | 40 | } | ||
| 37 | 41 | ||||
| 38 | export function deleteSessionTokenCookie({ cookies }: { cookies: Cookies }): void { | 42 | export function deleteSessionTokenCookie({ cookies }: { cookies: Cookies }): void { | ||
| 39 | - | console.debug("Deleting session cookie"); | 43 | + | log.debug("Deleting session cookie"); |
| 40 | 44 | ||||
| 41 | const domain = getCookieDomain(); | 45 | const domain = getCookieDomain(); | ||
| 42 | - | console.debug("Deleting cookie for domain:", domain); | 46 | + | log.debug("Deleting cookie for domain:", { domain }); |
| 43 | 47 | ||||
| 44 | cookies.set("session", "", { | 48 | cookies.set("session", "", { | ||
| 45 | httpOnly: true, | 49 | httpOnly: true, | ||
| @@ -52,6 +56,6 @@ export function deleteSessionTokenCookie({ cookies }: { cookies: Cookies }): voi | |||||
| 52 | } | 56 | } | ||
| 53 | 57 | ||||
| 54 | export function getSessionTokenCookie({ cookies }: { cookies: Cookies }): string | undefined { | 58 | export function getSessionTokenCookie({ cookies }: { cookies: Cookies }): string | undefined { | ||
| 55 | - | console.debug("Getting session cookie"); | 59 | + | log.debug("Getting session cookie"); |
| 56 | return cookies.get("session"); | 60 | return cookies.get("session"); | ||
| 57 | } ⏎̸ | 61 | } ⏎̸ | ||
M src/lib/api/session.server.ts +15 -12
| @@ -2,6 +2,9 @@ import db from '../db/db.server'; | |||
| 2 | 2 | import { sessions, users } from '../db/schema'; | |
| 3 | 3 | import { eq } from 'drizzle-orm'; | |
| 4 | 4 | import type { Session, User } from '../types/chat'; | |
| 5 | + | import { createLogger } from '../utils/logger.server'; | |
| 6 | + | ||
| 7 | + | const log = createLogger('session-server'); | |
| 5 | 8 | ||
| 6 | 9 | // Helper: Compute SHA-256 hash of a message and encode it as a hex string. | |
| 7 | 10 | export async function sha256(message: string): Promise<string> { | |
| @@ -41,14 +44,14 @@ export function generateSessionToken(): string { | |||
| 41 | 44 | const randomBytesArray = new Uint8Array(20); | |
| 42 | 45 | crypto.getRandomValues(randomBytesArray); | |
| 43 | 46 | const token = base32Encode(randomBytesArray); | |
| 44 | - | console.debug("Generated session token:", token); | |
| 47 | + | log.debug("Generated session token", { token }); | |
| 45 | 48 | return token; | |
| 46 | 49 | } | |
| 47 | 50 | ||
| 48 | 51 | // Create a session record once a valid token is available. | |
| 49 | 52 | // The session ID is the SHA-256 hash of the token. | |
| 50 | 53 | export async function createSession(token: string, userId: string): Promise<Session> { | |
| 51 | - | console.debug("Creating session for user:", userId); | |
| 54 | + | log.debug("Creating session", { userId }); | |
| 52 | 55 | const sessionId = await sha256(token); | |
| 53 | 56 | const createdAt = Date.now(); | |
| 54 | 57 | const expiresAt = createdAt + 24 * 60 * 60 * 1000; // 24 hours expiry | |
| @@ -58,7 +61,7 @@ export async function createSession(token: string, userId: string): Promise<Sess | |||
| 58 | 61 | createdAt, | |
| 59 | 62 | expiresAt, | |
| 60 | 63 | }); | |
| 61 | - | console.debug("Session created with id:", sessionId); | |
| 64 | + | log.debug("Session created", { sessionId }); | |
| 62 | 65 | return { id: sessionId, userId, createdAt, expiresAt }; | |
| 63 | 66 | } | |
| 64 | 67 | ||
| @@ -73,41 +76,41 @@ export type SessionValidationResult = | |||
| 73 | 76 | ||
| 74 | 77 | // Validate a session token by converting it to its SHA‑256 hash, checking expiration, and fetching the user. | |
| 75 | 78 | export async function validateSessionToken(token: string): Promise<SessionValidationResult> { | |
| 76 | - | console.debug("Validating session token:", token); | |
| 79 | + | log.debug("Validating session token"); | |
| 77 | 80 | const sessionId = await sha256(token); | |
| 78 | 81 | const session = await db.query.sessions.findFirst({ | |
| 79 | 82 | where: eq(sessions.id, sessionId) | |
| 80 | 83 | }); | |
| 81 | 84 | if (!session) { | |
| 82 | - | console.debug("Session not found for id:", sessionId); | |
| 85 | + | log.debug("Session not found", { sessionId }); | |
| 83 | 86 | return { session: null, user: null }; | |
| 84 | 87 | } | |
| 85 | 88 | const now = Date.now(); | |
| 86 | 89 | if (session.expiresAt < now) { | |
| 87 | - | console.debug("Session expired for id:", sessionId); | |
| 90 | + | log.debug("Session expired", { sessionId }); | |
| 88 | 91 | return { session: null, user: null }; | |
| 89 | 92 | } | |
| 90 | 93 | const user = await db.query.users.findFirst({ | |
| 91 | 94 | where: eq(users.id, session.userId) | |
| 92 | 95 | }); | |
| 93 | 96 | if (!user) { | |
| 94 | - | console.debug("User not found for session id:", sessionId); | |
| 97 | + | log.debug("User not found for session", { sessionId }); | |
| 95 | 98 | return { session: null, user: null }; | |
| 96 | 99 | } | |
| 97 | - | console.debug("Session validated for id:", sessionId); | |
| 100 | + | log.debug("Session validated", { sessionId, userId: user.id }); | |
| 98 | 101 | return { session, user }; | |
| 99 | 102 | } | |
| 100 | 103 | ||
| 101 | 104 | // Invalidate a specific session by deleting it from the database. | |
| 102 | 105 | export async function invalidateSession(sessionId: string): Promise<void> { | |
| 103 | - | console.debug("Invalidating session with id:", sessionId); | |
| 106 | + | log.debug("Invalidating session", { sessionId }); | |
| 104 | 107 | await db.delete(sessions).where(eq(sessions.id, sessionId)); | |
| 105 | - | console.debug("Session invalidated:", sessionId); | |
| 108 | + | log.debug("Session invalidated", { sessionId }); | |
| 106 | 109 | } | |
| 107 | 110 | ||
| 108 | 111 | // Invalidate all sessions for a given user. | |
| 109 | 112 | export async function invalidateAllSessions(userId: string): Promise<void> { | |
| 110 | - | console.debug("Invalidating all sessions for user:", userId); | |
| 113 | + | log.debug("Invalidating all sessions for user", { userId }); | |
| 111 | 114 | await db.delete(sessions).where(eq(sessions.userId, userId)); | |
| 112 | - | console.debug("All sessions invalidated for user:", userId); | |
| 115 | + | log.debug("All sessions invalidated", { userId }); | |
| 113 | 116 | } ⏎̸ | |
| @@ -2,6 +2,9 @@ import db from '../db/db.server'; | |||||
| 2 | import { sessions, users } from '../db/schema'; | 2 | import { sessions, users } from '../db/schema'; | ||
| 3 | import { eq } from 'drizzle-orm'; | 3 | import { eq } from 'drizzle-orm'; | ||
| 4 | import type { Session, User } from '../types/chat'; | 4 | import type { Session, User } from '../types/chat'; | ||
| 5 | + | import { createLogger } from '../utils/logger.server'; | |||
| 6 | + | ||||
| 7 | + | const log = createLogger('session-server'); | |||
| 5 | 8 | ||||
| 6 | // Helper: Compute SHA-256 hash of a message and encode it as a hex string. | 9 | // Helper: Compute SHA-256 hash of a message and encode it as a hex string. | ||
| 7 | export async function sha256(message: string): Promise<string> { | 10 | export async function sha256(message: string): Promise<string> { | ||
| @@ -41,14 +44,14 @@ export function generateSessionToken(): string { | |||||
| 41 | const randomBytesArray = new Uint8Array(20); | 44 | const randomBytesArray = new Uint8Array(20); | ||
| 42 | crypto.getRandomValues(randomBytesArray); | 45 | crypto.getRandomValues(randomBytesArray); | ||
| 43 | const token = base32Encode(randomBytesArray); | 46 | const token = base32Encode(randomBytesArray); | ||
| 44 | - | console.debug("Generated session token:", token); | 47 | + | log.debug("Generated session token", { token }); |
| 45 | return token; | 48 | return token; | ||
| 46 | } | 49 | } | ||
| 47 | 50 | ||||
| 48 | // Create a session record once a valid token is available. | 51 | // Create a session record once a valid token is available. | ||
| 49 | // The session ID is the SHA-256 hash of the token. | 52 | // The session ID is the SHA-256 hash of the token. | ||
| 50 | export async function createSession(token: string, userId: string): Promise<Session> { | 53 | export async function createSession(token: string, userId: string): Promise<Session> { | ||
| 51 | - | console.debug("Creating session for user:", userId); | 54 | + | log.debug("Creating session", { userId }); |
| 52 | const sessionId = await sha256(token); | 55 | const sessionId = await sha256(token); | ||
| 53 | const createdAt = Date.now(); | 56 | const createdAt = Date.now(); | ||
| 54 | const expiresAt = createdAt + 24 * 60 * 60 * 1000; // 24 hours expiry | 57 | const expiresAt = createdAt + 24 * 60 * 60 * 1000; // 24 hours expiry | ||
| @@ -58,7 +61,7 @@ export async function createSession(token: string, userId: string): Promise<Sess | |||||
| 58 | createdAt, | 61 | createdAt, | ||
| 59 | expiresAt, | 62 | expiresAt, | ||
| 60 | }); | 63 | }); | ||
| 61 | - | console.debug("Session created with id:", sessionId); | 64 | + | log.debug("Session created", { sessionId }); |
| 62 | return { id: sessionId, userId, createdAt, expiresAt }; | 65 | return { id: sessionId, userId, createdAt, expiresAt }; | ||
| 63 | } | 66 | } | ||
| 64 | 67 | ||||
| @@ -73,41 +76,41 @@ export type SessionValidationResult = | |||||
| 73 | 76 | ||||
| 74 | // Validate a session token by converting it to its SHA‑256 hash, checking expiration, and fetching the user. | 77 | // Validate a session token by converting it to its SHA‑256 hash, checking expiration, and fetching the user. | ||
| 75 | export async function validateSessionToken(token: string): Promise<SessionValidationResult> { | 78 | export async function validateSessionToken(token: string): Promise<SessionValidationResult> { | ||
| 76 | - | console.debug("Validating session token:", token); | 79 | + | log.debug("Validating session token"); |
| 77 | const sessionId = await sha256(token); | 80 | const sessionId = await sha256(token); | ||
| 78 | const session = await db.query.sessions.findFirst({ | 81 | const session = await db.query.sessions.findFirst({ | ||
| 79 | where: eq(sessions.id, sessionId) | 82 | where: eq(sessions.id, sessionId) | ||
| 80 | }); | 83 | }); | ||
| 81 | if (!session) { | 84 | if (!session) { | ||
| 82 | - | console.debug("Session not found for id:", sessionId); | 85 | + | log.debug("Session not found", { sessionId }); |
| 83 | return { session: null, user: null }; | 86 | return { session: null, user: null }; | ||
| 84 | } | 87 | } | ||
| 85 | const now = Date.now(); | 88 | const now = Date.now(); | ||
| 86 | if (session.expiresAt < now) { | 89 | if (session.expiresAt < now) { | ||
| 87 | - | console.debug("Session expired for id:", sessionId); | 90 | + | log.debug("Session expired", { sessionId }); |
| 88 | return { session: null, user: null }; | 91 | return { session: null, user: null }; | ||
| 89 | } | 92 | } | ||
| 90 | const user = await db.query.users.findFirst({ | 93 | const user = await db.query.users.findFirst({ | ||
| 91 | where: eq(users.id, session.userId) | 94 | where: eq(users.id, session.userId) | ||
| 92 | }); | 95 | }); | ||
| 93 | if (!user) { | 96 | if (!user) { | ||
| 94 | - | console.debug("User not found for session id:", sessionId); | 97 | + | log.debug("User not found for session", { sessionId }); |
| 95 | return { session: null, user: null }; | 98 | return { session: null, user: null }; | ||
| 96 | } | 99 | } | ||
| 97 | - | console.debug("Session validated for id:", sessionId); | 100 | + | log.debug("Session validated", { sessionId, userId: user.id }); |
| 98 | return { session, user }; | 101 | return { session, user }; | ||
| 99 | } | 102 | } | ||
| 100 | 103 | ||||
| 101 | // Invalidate a specific session by deleting it from the database. | 104 | // Invalidate a specific session by deleting it from the database. | ||
| 102 | export async function invalidateSession(sessionId: string): Promise<void> { | 105 | export async function invalidateSession(sessionId: string): Promise<void> { | ||
| 103 | - | console.debug("Invalidating session with id:", sessionId); | 106 | + | log.debug("Invalidating session", { sessionId }); |
| 104 | await db.delete(sessions).where(eq(sessions.id, sessionId)); | 107 | await db.delete(sessions).where(eq(sessions.id, sessionId)); | ||
| 105 | - | console.debug("Session invalidated:", sessionId); | 108 | + | log.debug("Session invalidated", { sessionId }); |
| 106 | } | 109 | } | ||
| 107 | 110 | ||||
| 108 | // Invalidate all sessions for a given user. | 111 | // Invalidate all sessions for a given user. | ||
| 109 | export async function invalidateAllSessions(userId: string): Promise<void> { | 112 | export async function invalidateAllSessions(userId: string): Promise<void> { | ||
| 110 | - | console.debug("Invalidating all sessions for user:", userId); | 113 | + | log.debug("Invalidating all sessions for user", { userId }); |
| 111 | await db.delete(sessions).where(eq(sessions.userId, userId)); | 114 | await db.delete(sessions).where(eq(sessions.userId, userId)); | ||
| 112 | - | console.debug("All sessions invalidated for user:", userId); | 115 | + | log.debug("All sessions invalidated", { userId }); |
| 113 | } ⏎̸ | 116 | } ⏎̸ | ||
M src/lib/cache/initialize-cache.ts +7 -4
| @@ -2,16 +2,19 @@ import { messageCache } from '$lib/cache/message-cache'; | |||
| 2 | 2 | import db from '$lib/db/db.server'; | |
| 3 | 3 | import { messages } from '$lib/db/schema'; | |
| 4 | 4 | import { desc } from 'drizzle-orm'; | |
| 5 | + | import { createLogger } from '$lib/utils/logger.server'; | |
| 6 | + | ||
| 7 | + | const log = createLogger('cache-initialize'); | |
| 5 | 8 | ||
| 6 | 9 | let isCacheInitialized = false; | |
| 7 | 10 | ||
| 8 | 11 | export async function initializeMessageCache(): Promise<void> { | |
| 9 | 12 | if (isCacheInitialized) { | |
| 10 | - | console.debug('[Cache] Message cache already initialized.'); | |
| 13 | + | log.debug('[Cache] Message cache already initialized.'); | |
| 11 | 14 | return; | |
| 12 | 15 | } | |
| 13 | 16 | try { | |
| 14 | - | console.debug('[Cache] Initializing message cache with pre-population.'); | |
| 17 | + | log.debug('[Cache] Initializing message cache with pre-population.'); | |
| 15 | 18 | const fetchedMessages = await db.select() | |
| 16 | 19 | .from(messages) | |
| 17 | 20 | .orderBy(desc(messages.timestamp)) | |
| @@ -20,11 +23,11 @@ export async function initializeMessageCache(): Promise<void> { | |||
| 20 | 23 | await messageCache.initialize(fetchedMessages.reverse()); | |
| 21 | 24 | isCacheInitialized = true; | |
| 22 | 25 | const stats = messageCache.getCacheStats(); | |
| 23 | - | console.debug('[Cache] Message cache initialized successfully:', { | |
| 26 | + | log.debug('[Cache] Message cache initialized successfully:', { | |
| 24 | 27 | roomCount: stats.roomCount, | |
| 25 | 28 | totalMessages: stats.totalMessages | |
| 26 | 29 | }); | |
| 27 | 30 | } catch (error) { | |
| 28 | - | console.error('[Cache] Failed to initialize message cache:', error); | |
| 31 | + | log.error('[Cache] Failed to initialize message cache:', { error: error instanceof Error ? error.message : 'Unknown error' }); | |
| 29 | 32 | } | |
| 30 | 33 | } ⏎̸ | |
| @@ -2,16 +2,19 @@ import { messageCache } from '$lib/cache/message-cache'; | |||||
| 2 | import db from '$lib/db/db.server'; | 2 | import db from '$lib/db/db.server'; | ||
| 3 | import { messages } from '$lib/db/schema'; | 3 | import { messages } from '$lib/db/schema'; | ||
| 4 | import { desc } from 'drizzle-orm'; | 4 | import { desc } from 'drizzle-orm'; | ||
| 5 | + | import { createLogger } from '$lib/utils/logger.server'; | |||
| 6 | + | ||||
| 7 | + | const log = createLogger('cache-initialize'); | |||
| 5 | 8 | ||||
| 6 | let isCacheInitialized = false; | 9 | let isCacheInitialized = false; | ||
| 7 | 10 | ||||
| 8 | export async function initializeMessageCache(): Promise<void> { | 11 | export async function initializeMessageCache(): Promise<void> { | ||
| 9 | if (isCacheInitialized) { | 12 | if (isCacheInitialized) { | ||
| 10 | - | console.debug('[Cache] Message cache already initialized.'); | 13 | + | log.debug('[Cache] Message cache already initialized.'); |
| 11 | return; | 14 | return; | ||
| 12 | } | 15 | } | ||
| 13 | try { | 16 | try { | ||
| 14 | - | console.debug('[Cache] Initializing message cache with pre-population.'); | 17 | + | log.debug('[Cache] Initializing message cache with pre-population.'); |
| 15 | const fetchedMessages = await db.select() | 18 | const fetchedMessages = await db.select() | ||
| 16 | .from(messages) | 19 | .from(messages) | ||
| 17 | .orderBy(desc(messages.timestamp)) | 20 | .orderBy(desc(messages.timestamp)) | ||
| @@ -20,11 +23,11 @@ export async function initializeMessageCache(): Promise<void> { | |||||
| 20 | await messageCache.initialize(fetchedMessages.reverse()); | 23 | await messageCache.initialize(fetchedMessages.reverse()); | ||
| 21 | isCacheInitialized = true; | 24 | isCacheInitialized = true; | ||
| 22 | const stats = messageCache.getCacheStats(); | 25 | const stats = messageCache.getCacheStats(); | ||
| 23 | - | console.debug('[Cache] Message cache initialized successfully:', { | 26 | + | log.debug('[Cache] Message cache initialized successfully:', { |
| 24 | roomCount: stats.roomCount, | 27 | roomCount: stats.roomCount, | ||
| 25 | totalMessages: stats.totalMessages | 28 | totalMessages: stats.totalMessages | ||
| 26 | }); | 29 | }); | ||
| 27 | } catch (error) { | 30 | } catch (error) { | ||
| 28 | - | console.error('[Cache] Failed to initialize message cache:', error); | 31 | + | log.error('[Cache] Failed to initialize message cache:', { error: error instanceof Error ? error.message : 'Unknown error' }); |
| 29 | } | 32 | } | ||
| 30 | } ⏎̸ | 33 | } ⏎̸ | ||
M src/lib/db/db.server.ts +8 -5
| @@ -2,30 +2,33 @@ import { drizzle } from 'drizzle-orm/libsql'; | |||
| 2 | 2 | import { createClient } from '@libsql/client'; | |
| 3 | 3 | import { TURSO_AUTH_TOKEN, TURSO_DATABASE_URL } from '$env/static/private'; | |
| 4 | 4 | import schema, { ensureDefaultChatRoom } from './schema'; | |
| 5 | + | import { createLogger } from '$lib/utils/logger.server'; | |
| 6 | + | ||
| 7 | + | const log = createLogger('db-server'); | |
| 5 | 8 | ||
| 6 | 9 | const client = createClient({ url: TURSO_DATABASE_URL, authToken: TURSO_AUTH_TOKEN }); | |
| 7 | 10 | const db = drizzle(client, { schema }); | |
| 8 | 11 | ||
| 9 | 12 | // Initialize database with required data | |
| 10 | 13 | export const initializeDatabase = async () => { | |
| 11 | - | console.debug('Initializing database...'); | |
| 14 | + | log.debug('Initializing database...'); | |
| 12 | 15 | try { | |
| 13 | 16 | await ensureDefaultChatRoom(db); | |
| 14 | - | console.debug('Database initialization complete'); | |
| 17 | + | log.debug('Database initialization complete'); | |
| 15 | 18 | } catch (error) { | |
| 16 | - | console.error('Error initializing database:', error); | |
| 19 | + | log.error('Error initializing database:', { error: error instanceof Error ? error.message : 'Unknown error' }); | |
| 17 | 20 | throw error; | |
| 18 | 21 | } | |
| 19 | 22 | }; | |
| 20 | 23 | ||
| 21 | 24 | // Call initialization when the module is loaded | |
| 22 | 25 | initializeDatabase().catch(error => { | |
| 23 | - | console.error('Failed to initialize database:', error); | |
| 26 | + | log.error('Failed to initialize database:', { error: error instanceof Error ? error.message : 'Unknown error' }); | |
| 24 | 27 | }); | |
| 25 | 28 | ||
| 26 | 29 | export const testConnection = async () => { | |
| 27 | 30 | const result = await db.query.users.findMany(); | |
| 28 | - | console.log(result); | |
| 31 | + | log.debug('Test connection result:', result); | |
| 29 | 32 | }; | |
| 30 | 33 | ||
| 31 | 34 | export default db;⏎̸ | |
| @@ -2,30 +2,33 @@ import { drizzle } from 'drizzle-orm/libsql'; | |||||
| 2 | import { createClient } from '@libsql/client'; | 2 | import { createClient } from '@libsql/client'; | ||
| 3 | import { TURSO_AUTH_TOKEN, TURSO_DATABASE_URL } from '$env/static/private'; | 3 | import { TURSO_AUTH_TOKEN, TURSO_DATABASE_URL } from '$env/static/private'; | ||
| 4 | import schema, { ensureDefaultChatRoom } from './schema'; | 4 | import schema, { ensureDefaultChatRoom } from './schema'; | ||
| 5 | + | import { createLogger } from '$lib/utils/logger.server'; | |||
| 6 | + | ||||
| 7 | + | const log = createLogger('db-server'); | |||
| 5 | 8 | ||||
| 6 | const client = createClient({ url: TURSO_DATABASE_URL, authToken: TURSO_AUTH_TOKEN }); | 9 | const client = createClient({ url: TURSO_DATABASE_URL, authToken: TURSO_AUTH_TOKEN }); | ||
| 7 | const db = drizzle(client, { schema }); | 10 | const db = drizzle(client, { schema }); | ||
| 8 | 11 | ||||
| 9 | // Initialize database with required data | 12 | // Initialize database with required data | ||
| 10 | export const initializeDatabase = async () => { | 13 | export const initializeDatabase = async () => { | ||
| 11 | - | console.debug('Initializing database...'); | 14 | + | log.debug('Initializing database...'); |
| 12 | try { | 15 | try { | ||
| 13 | await ensureDefaultChatRoom(db); | 16 | await ensureDefaultChatRoom(db); | ||
| 14 | - | console.debug('Database initialization complete'); | 17 | + | log.debug('Database initialization complete'); |
| 15 | } catch (error) { | 18 | } catch (error) { | ||
| 16 | - | console.error('Error initializing database:', error); | 19 | + | log.error('Error initializing database:', { error: error instanceof Error ? error.message : 'Unknown error' }); |
| 17 | throw error; | 20 | throw error; | ||
| 18 | } | 21 | } | ||
| 19 | }; | 22 | }; | ||
| 20 | 23 | ||||
| 21 | // Call initialization when the module is loaded | 24 | // Call initialization when the module is loaded | ||
| 22 | initializeDatabase().catch(error => { | 25 | initializeDatabase().catch(error => { | ||
| 23 | - | console.error('Failed to initialize database:', error); | 26 | + | log.error('Failed to initialize database:', { error: error instanceof Error ? error.message : 'Unknown error' }); |
| 24 | }); | 27 | }); | ||
| 25 | 28 | ||||
| 26 | export const testConnection = async () => { | 29 | export const testConnection = async () => { | ||
| 27 | const result = await db.query.users.findMany(); | 30 | const result = await db.query.users.findMany(); | ||
| 28 | - | console.log(result); | 31 | + | log.debug('Test connection result:', result); |
| 29 | }; | 32 | }; | ||
| 30 | 33 | ||||
| 31 | export default db;⏎̸ | 34 | export default db;⏎̸ | ||
M src/lib/db/schema.ts +9 -6
| @@ -4,6 +4,9 @@ import type { User, ChatRoom, Message, UserStatus, MessageType, Session } from " | |||
| 4 | 4 | import { v4 as uuidv4 } from "uuid"; | |
| 5 | 5 | import { eq } from "drizzle-orm"; | |
| 6 | 6 | import type { LibSQLDatabase } from "drizzle-orm/libsql"; | |
| 7 | + | import { createLogger } from "$lib/utils/logger.server"; | |
| 8 | + | ||
| 9 | + | const log = createLogger('schema'); | |
| 7 | 10 | ||
| 8 | 11 | // Default chat room ID - using a UUID to avoid collisions | |
| 9 | 12 | export const DEFAULT_CHAT_ROOM_ID = '00000000-0000-0000-0000-000000000001'; | |
| @@ -120,7 +123,7 @@ const schema = { | |||
| 120 | 123 | ||
| 121 | 124 | // Function to ensure default chat room exists | |
| 122 | 125 | export async function ensureDefaultChatRoom(db: LibSQLDatabase<typeof schema>) { | |
| 123 | - | console.debug('Ensuring default chat room exists...'); | |
| 126 | + | log.debug('Ensuring default chat room exists...'); | |
| 124 | 127 | try { | |
| 125 | 128 | // Use a transaction to handle concurrent creation attempts | |
| 126 | 129 | await db.transaction(async (tx) => { | |
| @@ -130,25 +133,25 @@ export async function ensureDefaultChatRoom(db: LibSQLDatabase<typeof schema>) { | |||
| 130 | 133 | .get(); | |
| 131 | 134 | ||
| 132 | 135 | if (!defaultRoom) { | |
| 133 | - | console.debug('Creating default chat room...'); | |
| 136 | + | log.debug('Creating default chat room...'); | |
| 134 | 137 | await tx.insert(chatRooms).values({ | |
| 135 | 138 | id: DEFAULT_CHAT_ROOM_ID, | |
| 136 | 139 | name: 'General', | |
| 137 | 140 | type: 'group', | |
| 138 | 141 | createdAt: Date.now() | |
| 139 | 142 | }); | |
| 140 | - | console.debug('Default chat room created successfully'); | |
| 143 | + | log.debug('Default chat room created successfully'); | |
| 141 | 144 | } else { | |
| 142 | - | console.debug('Default chat room already exists'); | |
| 145 | + | log.debug('Default chat room already exists'); | |
| 143 | 146 | } | |
| 144 | 147 | }); | |
| 145 | 148 | } catch (error: unknown) { | |
| 146 | 149 | // If the error is a unique constraint violation, another process probably created the room | |
| 147 | 150 | if (isLibSQLError(error) && error.code === 'SQLITE_CONSTRAINT') { | |
| 148 | - | console.debug('Default chat room was created by another process'); | |
| 151 | + | log.debug('Default chat room was created by another process'); | |
| 149 | 152 | return; | |
| 150 | 153 | } | |
| 151 | - | console.error('Error ensuring default chat room:', error); | |
| 154 | + | log.error('Error ensuring default chat room:', { error: error instanceof Error ? error.message : 'Unknown error' }); | |
| 152 | 155 | throw error; | |
| 153 | 156 | } | |
| 154 | 157 | } | |
| @@ -4,6 +4,9 @@ import type { User, ChatRoom, Message, UserStatus, MessageType, Session } from " | |||||
| 4 | import { v4 as uuidv4 } from "uuid"; | 4 | import { v4 as uuidv4 } from "uuid"; | ||
| 5 | import { eq } from "drizzle-orm"; | 5 | import { eq } from "drizzle-orm"; | ||
| 6 | import type { LibSQLDatabase } from "drizzle-orm/libsql"; | 6 | import type { LibSQLDatabase } from "drizzle-orm/libsql"; | ||
| 7 | + | import { createLogger } from "$lib/utils/logger.server"; | |||
| 8 | + | ||||
| 9 | + | const log = createLogger('schema'); | |||
| 7 | 10 | ||||
| 8 | // Default chat room ID - using a UUID to avoid collisions | 11 | // Default chat room ID - using a UUID to avoid collisions | ||
| 9 | export const DEFAULT_CHAT_ROOM_ID = '00000000-0000-0000-0000-000000000001'; | 12 | export const DEFAULT_CHAT_ROOM_ID = '00000000-0000-0000-0000-000000000001'; | ||
| @@ -120,7 +123,7 @@ const schema = { | |||||
| 120 | 123 | ||||
| 121 | // Function to ensure default chat room exists | 124 | // Function to ensure default chat room exists | ||
| 122 | export async function ensureDefaultChatRoom(db: LibSQLDatabase<typeof schema>) { | 125 | export async function ensureDefaultChatRoom(db: LibSQLDatabase<typeof schema>) { | ||
| 123 | - | console.debug('Ensuring default chat room exists...'); | 126 | + | log.debug('Ensuring default chat room exists...'); |
| 124 | try { | 127 | try { | ||
| 125 | // Use a transaction to handle concurrent creation attempts | 128 | // Use a transaction to handle concurrent creation attempts | ||
| 126 | await db.transaction(async (tx) => { | 129 | await db.transaction(async (tx) => { | ||
| @@ -130,25 +133,25 @@ export async function ensureDefaultChatRoom(db: LibSQLDatabase<typeof schema>) { | |||||
| 130 | .get(); | 133 | .get(); | ||
| 131 | 134 | ||||
| 132 | if (!defaultRoom) { | 135 | if (!defaultRoom) { | ||
| 133 | - | console.debug('Creating default chat room...'); | 136 | + | log.debug('Creating default chat room...'); |
| 134 | await tx.insert(chatRooms).values({ | 137 | await tx.insert(chatRooms).values({ | ||
| 135 | id: DEFAULT_CHAT_ROOM_ID, | 138 | id: DEFAULT_CHAT_ROOM_ID, | ||
| 136 | name: 'General', | 139 | name: 'General', | ||
| 137 | type: 'group', | 140 | type: 'group', | ||
| 138 | createdAt: Date.now() | 141 | createdAt: Date.now() | ||
| 139 | }); | 142 | }); | ||
| 140 | - | console.debug('Default chat room created successfully'); | 143 | + | log.debug('Default chat room created successfully'); |
| 141 | } else { | 144 | } else { | ||
| 142 | - | console.debug('Default chat room already exists'); | 145 | + | log.debug('Default chat room already exists'); |
| 143 | } | 146 | } | ||
| 144 | }); | 147 | }); | ||
| 145 | } catch (error: unknown) { | 148 | } catch (error: unknown) { | ||
| 146 | // If the error is a unique constraint violation, another process probably created the room | 149 | // If the error is a unique constraint violation, another process probably created the room | ||
| 147 | if (isLibSQLError(error) && error.code === 'SQLITE_CONSTRAINT') { | 150 | if (isLibSQLError(error) && error.code === 'SQLITE_CONSTRAINT') { | ||
| 148 | - | console.debug('Default chat room was created by another process'); | 151 | + | log.debug('Default chat room was created by another process'); |
| 149 | return; | 152 | return; | ||
| 150 | } | 153 | } | ||
| 151 | - | console.error('Error ensuring default chat room:', error); | 154 | + | log.error('Error ensuring default chat room:', { error: error instanceof Error ? error.message : 'Unknown error' }); |
| 152 | throw error; | 155 | throw error; | ||
| 153 | } | 156 | } | ||
| 154 | } | 157 | } | ||
A src/lib/utils/logger.server.ts +55 -0
| @@ -0,0 +1,55 @@ | |||
| 1 | + | import pino from 'pino'; | |
| 2 | + | ||
| 3 | + | const logLevel = process.env.NODE_ENV === 'development' ? 'debug' : 'info'; | |
| 4 | + | ||
| 5 | + | class LoggerService { | |
| 6 | + | private static instance: LoggerService; | |
| 7 | + | private logger: pino.Logger; | |
| 8 | + | private moduleLoggers: Map<string, Logger>; | |
| 9 | + | ||
| 10 | + | private constructor() { | |
| 11 | + | this.logger = pino({ | |
| 12 | + | transport: { | |
| 13 | + | target: 'pino-pretty', | |
| 14 | + | options: { | |
| 15 | + | colorize: true, | |
| 16 | + | translateTime: 'HH:MM:ss', | |
| 17 | + | ignore: 'pid,hostname', | |
| 18 | + | }, | |
| 19 | + | }, | |
| 20 | + | level: logLevel, | |
| 21 | + | }); | |
| 22 | + | this.moduleLoggers = new Map(); | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | public static getInstance(): LoggerService { | |
| 26 | + | if (!LoggerService.instance) { | |
| 27 | + | LoggerService.instance = new LoggerService(); | |
| 28 | + | } | |
| 29 | + | return LoggerService.instance; | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | public getLogger(module: string): Logger { | |
| 33 | + | if (!this.moduleLoggers.has(module)) { | |
| 34 | + | this.moduleLoggers.set(module, { | |
| 35 | + | debug: (msg: string, obj = {}) => this.logger.debug({ module, ...obj }, msg), | |
| 36 | + | info: (msg: string, obj = {}) => this.logger.info({ module, ...obj }, msg), | |
| 37 | + | warn: (msg: string, obj = {}) => this.logger.warn({ module, ...obj }, msg), | |
| 38 | + | error: (msg: string, obj = {}) => this.logger.error({ module, ...obj }, msg) | |
| 39 | + | }); | |
| 40 | + | } | |
| 41 | + | return this.moduleLoggers.get(module)!; | |
| 42 | + | } | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | export interface Logger { | |
| 46 | + | debug: (msg: string, obj?: object) => void; | |
| 47 | + | info: (msg: string, obj?: object) => void; | |
| 48 | + | warn: (msg: string, obj?: object) => void; | |
| 49 | + | error: (msg: string, obj?: object) => void; | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | // Export a function to get a logger for a specific module | |
| 53 | + | export const createLogger = (module: string): Logger => { | |
| 54 | + | return LoggerService.getInstance().getLogger(module); | |
| 55 | + | }; ⏎̸ | |
| @@ -0,0 +1,55 @@ | |||||
| 1 | + | import pino from 'pino'; | |||
| 2 | + | ||||
| 3 | + | const logLevel = process.env.NODE_ENV === 'development' ? 'debug' : 'info'; | |||
| 4 | + | ||||
| 5 | + | class LoggerService { | |||
| 6 | + | private static instance: LoggerService; | |||
| 7 | + | private logger: pino.Logger; | |||
| 8 | + | private moduleLoggers: Map<string, Logger>; | |||
| 9 | + | ||||
| 10 | + | private constructor() { | |||
| 11 | + | this.logger = pino({ | |||
| 12 | + | transport: { | |||
| 13 | + | target: 'pino-pretty', | |||
| 14 | + | options: { | |||
| 15 | + | colorize: true, | |||
| 16 | + | translateTime: 'HH:MM:ss', | |||
| 17 | + | ignore: 'pid,hostname', | |||
| 18 | + | }, | |||
| 19 | + | }, | |||
| 20 | + | level: logLevel, | |||
| 21 | + | }); | |||
| 22 | + | this.moduleLoggers = new Map(); | |||
| 23 | + | } | |||
| 24 | + | ||||
| 25 | + | public static getInstance(): LoggerService { | |||
| 26 | + | if (!LoggerService.instance) { | |||
| 27 | + | LoggerService.instance = new LoggerService(); | |||
| 28 | + | } | |||
| 29 | + | return LoggerService.instance; | |||
| 30 | + | } | |||
| 31 | + | ||||
| 32 | + | public getLogger(module: string): Logger { | |||
| 33 | + | if (!this.moduleLoggers.has(module)) { | |||
| 34 | + | this.moduleLoggers.set(module, { | |||
| 35 | + | debug: (msg: string, obj = {}) => this.logger.debug({ module, ...obj }, msg), | |||
| 36 | + | info: (msg: string, obj = {}) => this.logger.info({ module, ...obj }, msg), | |||
| 37 | + | warn: (msg: string, obj = {}) => this.logger.warn({ module, ...obj }, msg), | |||
| 38 | + | error: (msg: string, obj = {}) => this.logger.error({ module, ...obj }, msg) | |||
| 39 | + | }); | |||
| 40 | + | } | |||
| 41 | + | return this.moduleLoggers.get(module)!; | |||
| 42 | + | } | |||
| 43 | + | } | |||
| 44 | + | ||||
| 45 | + | export interface Logger { | |||
| 46 | + | debug: (msg: string, obj?: object) => void; | |||
| 47 | + | info: (msg: string, obj?: object) => void; | |||
| 48 | + | warn: (msg: string, obj?: object) => void; | |||
| 49 | + | error: (msg: string, obj?: object) => void; | |||
| 50 | + | } | |||
| 51 | + | ||||
| 52 | + | // Export a function to get a logger for a specific module | |||
| 53 | + | export const createLogger = (module: string): Logger => { | |||
| 54 | + | return LoggerService.getInstance().getLogger(module); | |||
| 55 | + | }; ⏎̸ | |||
M src/routes/api/chat/messages/+server.ts +68 -46
| @@ -15,6 +15,9 @@ import { chatRooms, DEFAULT_CHAT_ROOM_ID } from '$lib/db/schema'; | |||
| 15 | 15 | import { users } from '$lib/db/schema'; | |
| 16 | 16 | import { ensureDefaultChatRoom } from '$lib/db/schema'; | |
| 17 | 17 | import { messageCache } from '$lib/cache/message-cache'; | |
| 18 | + | import { createLogger } from '$lib/utils/logger.server'; | |
| 19 | + | ||
| 20 | + | const log = createLogger('chat-server'); | |
| 18 | 21 | ||
| 19 | 22 | // Rate limiting configuration | |
| 20 | 23 | const INITIAL_COOLDOWN = 1000; // 1 second | |
| @@ -54,7 +57,7 @@ async function initializeCache() { | |||
| 54 | 57 | if (isCacheInitialized) return; | |
| 55 | 58 | ||
| 56 | 59 | try { | |
| 57 | - | console.log('[Chat] Initializing message cache'); | |
| 60 | + | log.info('Initializing message cache'); | |
| 58 | 61 | // Fetch messages from all rooms, limited to last 100 per room | |
| 59 | 62 | const fetchedMessages = await db.select() | |
| 60 | 63 | .from(messages) | |
| @@ -66,12 +69,12 @@ async function initializeCache() { | |||
| 66 | 69 | ||
| 67 | 70 | // Log cache stats after initialization | |
| 68 | 71 | const stats = messageCache.getCacheStats(); | |
| 69 | - | console.log('[Chat] Message cache initialized successfully:', { | |
| 72 | + | log.info('Message cache initialized successfully', { | |
| 70 | 73 | roomCount: stats.roomCount, | |
| 71 | 74 | totalMessages: stats.totalMessages | |
| 72 | 75 | }); | |
| 73 | 76 | } catch (error) { | |
| 74 | - | console.error('[Chat] Failed to initialize message cache:', error); | |
| 77 | + | log.error('Failed to initialize message cache', { error }); | |
| 75 | 78 | // Don't set isCacheInitialized to true on error | |
| 76 | 79 | } | |
| 77 | 80 | } | |
| @@ -79,47 +82,44 @@ async function initializeCache() { | |||
| 79 | 82 | // GET endpoint: fetch messages from cache or DB | |
| 80 | 83 | export async function GET({ request, locals }) { | |
| 81 | 84 | const url = new URL(request.url); | |
| 82 | - | const isPublic = url.searchParams.get('public') === 'true'; | |
| 83 | - | const roomId = url.searchParams.get('roomId') || DEFAULT_CHAT_ROOM_ID; | |
| 84 | 85 | const beforeTimestamp = url.searchParams.get('before'); | |
| 85 | - | ||
| 86 | - | if (!isPublic && !locals.user) { | |
| 87 | - | throw error(401, 'Authentication required'); | |
| 88 | - | } | |
| 86 | + | const roomId = url.searchParams.get('roomId') || DEFAULT_CHAT_ROOM_ID; | |
| 87 | + | let fetchedMessages: Message[] = []; | |
| 89 | 88 | ||
| 90 | 89 | try { | |
| 91 | 90 | // Ensure default chat room exists | |
| 92 | 91 | await ensureDefaultChatRoom(db); | |
| 93 | - | ||
| 94 | - | let fetchedMessages: Message[]; | |
| 95 | - | if (isPublic) { | |
| 96 | - | if (!isCacheInitialized) { | |
| 97 | - | await initializeCache(); | |
| 98 | - | } | |
| 99 | - | ||
| 100 | - | if (beforeTimestamp) { | |
| 101 | - | // For public users requesting older messages, fetch from DB with limit | |
| 102 | - | console.log('[Chat] Fetching public messages from DB before timestamp:', beforeTimestamp); | |
| 103 | - | const query = db.select() | |
| 104 | - | .from(messages) | |
| 105 | - | .where(and( | |
| 106 | - | eq(messages.chatRoomId, roomId), | |
| 107 | - | lt(messages.timestamp, parseInt(beforeTimestamp)) | |
| 108 | - | )) | |
| 109 | - | .orderBy(desc(messages.timestamp)) | |
| 110 | - | .limit(10); | |
| 111 | - | fetchedMessages = (await query).reverse(); | |
| 112 | - | } else { | |
| 113 | - | console.log('[Chat] Fetching public messages from cache', { roomId }); | |
| 114 | - | fetchedMessages = messageCache.getMessages(roomId, 10); | |
| 92 | + | ||
| 93 | + | // Initialize cache if needed | |
| 94 | + | if (!isCacheInitialized) { | |
| 95 | + | await initializeCache(); | |
| 96 | + | } | |
| 97 | + | ||
| 98 | + | // Try to get messages from cache first | |
| 99 | + | if (!beforeTimestamp) { | |
| 100 | + | log.debug('Attempting to fetch messages from cache', { roomId }); | |
| 101 | + | const cachedMessages = messageCache.getMessages(roomId); | |
| 102 | + | if (cachedMessages && cachedMessages.length > 0) { | |
| 103 | + | log.debug('Messages found in cache', { | |
| 104 | + | roomId, | |
| 105 | + | messageCount: cachedMessages.length | |
| 106 | + | }); | |
| 107 | + | fetchedMessages = cachedMessages; | |
| 115 | 108 | } | |
| 116 | - | } else { | |
| 109 | + | } | |
| 110 | + | ||
| 111 | + | // If no cached messages or pagination request, fetch from DB | |
| 112 | + | if (fetchedMessages.length === 0) { | |
| 117 | 113 | if (!locals.user) { | |
| 114 | + | log.warn('Authentication required'); | |
| 118 | 115 | throw error(401, 'Authentication required'); | |
| 119 | 116 | } | |
| 120 | 117 | ||
| 121 | 118 | if (beforeTimestamp) { | |
| 122 | - | console.log('[Chat] Fetching messages from DB before timestamp:', beforeTimestamp); | |
| 119 | + | log.debug('Fetching messages from DB before timestamp', { | |
| 120 | + | roomId, | |
| 121 | + | beforeTimestamp | |
| 122 | + | }); | |
| 123 | 123 | const query = db.select() | |
| 124 | 124 | .from(messages) | |
| 125 | 125 | .where(and( | |
| @@ -130,7 +130,7 @@ export async function GET({ request, locals }) { | |||
| 130 | 130 | .limit(100); | |
| 131 | 131 | fetchedMessages = (await query).reverse(); | |
| 132 | 132 | } else { | |
| 133 | - | console.log('[Chat] Fetching messages from DB for instant chat', { roomId }); | |
| 133 | + | log.debug('Fetching messages from DB for instant chat', { roomId }); | |
| 134 | 134 | const query = db.select() | |
| 135 | 135 | .from(messages) | |
| 136 | 136 | .where(eq(messages.chatRoomId, roomId)) | |
| @@ -153,7 +153,10 @@ export async function GET({ request, locals }) { | |||
| 153 | 153 | } | |
| 154 | 154 | ); | |
| 155 | 155 | } catch (error) { | |
| 156 | - | console.log('[Chat] Error fetching messages:', error); | |
| 156 | + | log.error('Error fetching messages', { | |
| 157 | + | error: error instanceof Error ? error.message : 'Unknown error', | |
| 158 | + | roomId | |
| 159 | + | }); | |
| 157 | 160 | const errorResponse: GetMessagesResponse = { | |
| 158 | 161 | success: false, | |
| 159 | 162 | error: 'Failed to fetch messages' | |
| @@ -171,16 +174,20 @@ export async function GET({ request, locals }) { | |||
| 171 | 174 | // POST endpoint: receive a new message, save it in the DB, and broadcast via SSE | |
| 172 | 175 | export async function POST({ request, locals }: { request: Request, locals: App.Locals }) { | |
| 173 | 176 | if (!locals.user) { | |
| 177 | + | log.warn('Authentication required'); | |
| 174 | 178 | throw error(401, 'Authentication required'); | |
| 175 | 179 | } | |
| 176 | 180 | ||
| 177 | - | console.log('[Chat] New message received'); | |
| 181 | + | log.debug('New message received'); | |
| 178 | 182 | try { | |
| 179 | 183 | // Check rate limiting | |
| 180 | 184 | const { canSend, retryAfter } = updateUserCooldown(locals.user.id); | |
| 181 | 185 | if (!canSend) { | |
| 182 | 186 | const maskedUserId = `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}`; | |
| 183 | - | console.log('[Chat] Rate limited:', { userId: maskedUserId, retryAfter }); | |
| 187 | + | log.warn('Rate limited', { | |
| 188 | + | userId: maskedUserId, | |
| 189 | + | retryAfter | |
| 190 | + | }); | |
| 184 | 191 | const errorResponse: SendMessageResponse = { | |
| 185 | 192 | success: false, | |
| 186 | 193 | error: 'Please wait before sending another message', | |
| @@ -201,7 +208,10 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||
| 201 | 208 | ||
| 202 | 209 | const data = await request.json() as SendMessageRequest; | |
| 203 | 210 | if (!data.content || !data.userId) { | |
| 204 | - | console.log('[Chat] Invalid payload received'); | |
| 211 | + | log.warn('Invalid payload received', { | |
| 212 | + | hasContent: Boolean(data.content), | |
| 213 | + | hasUserId: Boolean(data.userId) | |
| 214 | + | }); | |
| 205 | 215 | const errorResponse: SendMessageResponse = { | |
| 206 | 216 | success: false, | |
| 207 | 217 | error: 'Invalid message payload' | |
| @@ -210,6 +220,10 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||
| 210 | 220 | } | |
| 211 | 221 | ||
| 212 | 222 | if (data.userId !== locals.user.id) { | |
| 223 | + | log.warn('Unauthorized message attempt', { | |
| 224 | + | requestedUserId: data.userId, | |
| 225 | + | actualUserId: `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}` | |
| 226 | + | }); | |
| 213 | 227 | throw error(403, 'Cannot post messages as another user'); | |
| 214 | 228 | } | |
| 215 | 229 | ||
| @@ -223,7 +237,7 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||
| 223 | 237 | .get(); | |
| 224 | 238 | ||
| 225 | 239 | if (!chatRoom) { | |
| 226 | - | console.log('[Chat] Chat room not found:', chatRoomId); | |
| 240 | + | log.warn('Chat room not found', { chatRoomId }); | |
| 227 | 241 | const errorResponse: SendMessageResponse = { | |
| 228 | 242 | success: false, | |
| 229 | 243 | error: 'Chat room not found' | |
| @@ -239,7 +253,7 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||
| 239 | 253 | ||
| 240 | 254 | if (!user) { | |
| 241 | 255 | const maskedUserId = `${data.userId.slice(0, 4)}...${data.userId.slice(-4)}`; | |
| 242 | - | console.log('[Chat] User not found:', maskedUserId); | |
| 256 | + | log.warn('User not found', { maskedUserId }); | |
| 243 | 257 | const errorResponse: SendMessageResponse = { | |
| 244 | 258 | success: false, | |
| 245 | 259 | error: 'User not found' | |
| @@ -249,7 +263,7 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||
| 249 | 263 | ||
| 250 | 264 | const newMessage: Message = { | |
| 251 | 265 | id: uuidv4(), | |
| 252 | - | chatRoomId: data.chatRoomId || DEFAULT_CHAT_ROOM_ID, | |
| 266 | + | chatRoomId, | |
| 253 | 267 | senderId: data.userId, | |
| 254 | 268 | content: data.content, | |
| 255 | 269 | type: data.type || 'chat', | |
| @@ -258,7 +272,7 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||
| 258 | 272 | ||
| 259 | 273 | // Save to DB | |
| 260 | 274 | await db.insert(messages).values(newMessage); | |
| 261 | - | console.log('[Chat] Message saved in DB', { | |
| 275 | + | log.debug('Message saved in DB', { | |
| 262 | 276 | messageId: newMessage.id, | |
| 263 | 277 | chatRoomId: newMessage.chatRoomId, | |
| 264 | 278 | type: newMessage.type, | |
| @@ -267,7 +281,7 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||
| 267 | 281 | ||
| 268 | 282 | // Update message cache | |
| 269 | 283 | messageCache.addMessage(newMessage); | |
| 270 | - | console.debug('[Chat] Message added to cache', { | |
| 284 | + | log.debug('Message added to cache', { | |
| 271 | 285 | messageId: newMessage.id, | |
| 272 | 286 | chatRoomId: newMessage.chatRoomId | |
| 273 | 287 | }); | |
| @@ -275,6 +289,12 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||
| 275 | 289 | // Broadcast the new message via the unified SSE emitter | |
| 276 | 290 | sseEmitter.emit('sse', { type: 'chatMessage', data: newMessage }); | |
| 277 | 291 | ||
| 292 | + | log.debug('Message processed successfully', { | |
| 293 | + | messageId: newMessage.id, | |
| 294 | + | userId: `${newMessage.senderId.slice(0, 4)}...${newMessage.senderId.slice(-4)}`, | |
| 295 | + | roomId: newMessage.chatRoomId | |
| 296 | + | }); | |
| 297 | + | ||
| 278 | 298 | const successResponse: SendMessageResponse = { | |
| 279 | 299 | success: true, | |
| 280 | 300 | message: newMessage | |
| @@ -287,8 +307,10 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||
| 287 | 307 | headers: { 'Content-Type': 'application/json' } | |
| 288 | 308 | } | |
| 289 | 309 | ); | |
| 290 | - | } catch { | |
| 291 | - | console.log('[Chat] Error processing message'); | |
| 310 | + | } catch (error) { | |
| 311 | + | log.error('Error processing message', { | |
| 312 | + | error: error instanceof Error ? error.message : 'Unknown error' | |
| 313 | + | }); | |
| 292 | 314 | const errorResponse: SendMessageResponse = { | |
| 293 | 315 | success: false, | |
| 294 | 316 | error: 'Failed to save message' | |
| @@ -301,4 +323,4 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||
| 301 | 323 | } | |
| 302 | 324 | ); | |
| 303 | 325 | } | |
| 304 | - | } ⏎̸ | |
| 326 | + | }⏎̸ | |
| @@ -15,6 +15,9 @@ import { chatRooms, DEFAULT_CHAT_ROOM_ID } from '$lib/db/schema'; | |||||
| 15 | import { users } from '$lib/db/schema'; | 15 | import { users } from '$lib/db/schema'; | ||
| 16 | import { ensureDefaultChatRoom } from '$lib/db/schema'; | 16 | import { ensureDefaultChatRoom } from '$lib/db/schema'; | ||
| 17 | import { messageCache } from '$lib/cache/message-cache'; | 17 | import { messageCache } from '$lib/cache/message-cache'; | ||
| 18 | + | import { createLogger } from '$lib/utils/logger.server'; | |||
| 19 | + | ||||
| 20 | + | const log = createLogger('chat-server'); | |||
| 18 | 21 | ||||
| 19 | // Rate limiting configuration | 22 | // Rate limiting configuration | ||
| 20 | const INITIAL_COOLDOWN = 1000; // 1 second | 23 | const INITIAL_COOLDOWN = 1000; // 1 second | ||
| @@ -54,7 +57,7 @@ async function initializeCache() { | |||||
| 54 | if (isCacheInitialized) return; | 57 | if (isCacheInitialized) return; | ||
| 55 | 58 | ||||
| 56 | try { | 59 | try { | ||
| 57 | - | console.log('[Chat] Initializing message cache'); | 60 | + | log.info('Initializing message cache'); |
| 58 | // Fetch messages from all rooms, limited to last 100 per room | 61 | // Fetch messages from all rooms, limited to last 100 per room | ||
| 59 | const fetchedMessages = await db.select() | 62 | const fetchedMessages = await db.select() | ||
| 60 | .from(messages) | 63 | .from(messages) | ||
| @@ -66,12 +69,12 @@ async function initializeCache() { | |||||
| 66 | 69 | ||||
| 67 | // Log cache stats after initialization | 70 | // Log cache stats after initialization | ||
| 68 | const stats = messageCache.getCacheStats(); | 71 | const stats = messageCache.getCacheStats(); | ||
| 69 | - | console.log('[Chat] Message cache initialized successfully:', { | 72 | + | log.info('Message cache initialized successfully', { |
| 70 | roomCount: stats.roomCount, | 73 | roomCount: stats.roomCount, | ||
| 71 | totalMessages: stats.totalMessages | 74 | totalMessages: stats.totalMessages | ||
| 72 | }); | 75 | }); | ||
| 73 | } catch (error) { | 76 | } catch (error) { | ||
| 74 | - | console.error('[Chat] Failed to initialize message cache:', error); | 77 | + | log.error('Failed to initialize message cache', { error }); |
| 75 | // Don't set isCacheInitialized to true on error | 78 | // Don't set isCacheInitialized to true on error | ||
| 76 | } | 79 | } | ||
| 77 | } | 80 | } | ||
| @@ -79,47 +82,44 @@ async function initializeCache() { | |||||
| 79 | // GET endpoint: fetch messages from cache or DB | 82 | // GET endpoint: fetch messages from cache or DB | ||
| 80 | export async function GET({ request, locals }) { | 83 | export async function GET({ request, locals }) { | ||
| 81 | const url = new URL(request.url); | 84 | const url = new URL(request.url); | ||
| 82 | - | const isPublic = url.searchParams.get('public') === 'true'; | |||
| 83 | - | const roomId = url.searchParams.get('roomId') || DEFAULT_CHAT_ROOM_ID; | |||
| 84 | const beforeTimestamp = url.searchParams.get('before'); | 85 | const beforeTimestamp = url.searchParams.get('before'); | ||
| 85 | - | 86 | + | const roomId = url.searchParams.get('roomId') || DEFAULT_CHAT_ROOM_ID; | |
| 86 | - | if (!isPublic && !locals.user) { | 87 | + | let fetchedMessages: Message[] = []; |
| 87 | - | throw error(401, 'Authentication required'); | |||
| 88 | - | } | |||
| 89 | 88 | ||||
| 90 | try { | 89 | try { | ||
| 91 | // Ensure default chat room exists | 90 | // Ensure default chat room exists | ||
| 92 | await ensureDefaultChatRoom(db); | 91 | await ensureDefaultChatRoom(db); | ||
| 93 | - | 92 | + | ||
| 94 | - | let fetchedMessages: Message[]; | 93 | + | // Initialize cache if needed |
| 95 | - | if (isPublic) { | 94 | + | if (!isCacheInitialized) { |
| 96 | - | if (!isCacheInitialized) { | 95 | + | await initializeCache(); |
| 97 | - | await initializeCache(); | 96 | + | } |
| 98 | - | } | 97 | + | |
| 99 | - | 98 | + | // Try to get messages from cache first | |
| 100 | - | if (beforeTimestamp) { | 99 | + | if (!beforeTimestamp) { |
| 101 | - | // For public users requesting older messages, fetch from DB with limit | 100 | + | log.debug('Attempting to fetch messages from cache', { roomId }); |
| 102 | - | console.log('[Chat] Fetching public messages from DB before timestamp:', beforeTimestamp); | 101 | + | const cachedMessages = messageCache.getMessages(roomId); |
| 103 | - | const query = db.select() | 102 | + | if (cachedMessages && cachedMessages.length > 0) { |
| 104 | - | .from(messages) | 103 | + | log.debug('Messages found in cache', { |
| 105 | - | .where(and( | 104 | + | roomId, |
| 106 | - | eq(messages.chatRoomId, roomId), | 105 | + | messageCount: cachedMessages.length |
| 107 | - | lt(messages.timestamp, parseInt(beforeTimestamp)) | 106 | + | }); |
| 108 | - | )) | 107 | + | fetchedMessages = cachedMessages; |
| 109 | - | .orderBy(desc(messages.timestamp)) | |||
| 110 | - | .limit(10); | |||
| 111 | - | fetchedMessages = (await query).reverse(); | |||
| 112 | - | } else { | |||
| 113 | - | console.log('[Chat] Fetching public messages from cache', { roomId }); | |||
| 114 | - | fetchedMessages = messageCache.getMessages(roomId, 10); | |||
| 115 | } | 108 | } | ||
| 116 | - | } else { | 109 | + | } |
| 110 | + | ||||
| 111 | + | // If no cached messages or pagination request, fetch from DB | |||
| 112 | + | if (fetchedMessages.length === 0) { | |||
| 117 | if (!locals.user) { | 113 | if (!locals.user) { | ||
| 114 | + | log.warn('Authentication required'); | |||
| 118 | throw error(401, 'Authentication required'); | 115 | throw error(401, 'Authentication required'); | ||
| 119 | } | 116 | } | ||
| 120 | 117 | ||||
| 121 | if (beforeTimestamp) { | 118 | if (beforeTimestamp) { | ||
| 122 | - | console.log('[Chat] Fetching messages from DB before timestamp:', beforeTimestamp); | 119 | + | log.debug('Fetching messages from DB before timestamp', { |
| 120 | + | roomId, | |||
| 121 | + | beforeTimestamp | |||
| 122 | + | }); | |||
| 123 | const query = db.select() | 123 | const query = db.select() | ||
| 124 | .from(messages) | 124 | .from(messages) | ||
| 125 | .where(and( | 125 | .where(and( | ||
| @@ -130,7 +130,7 @@ export async function GET({ request, locals }) { | |||||
| 130 | .limit(100); | 130 | .limit(100); | ||
| 131 | fetchedMessages = (await query).reverse(); | 131 | fetchedMessages = (await query).reverse(); | ||
| 132 | } else { | 132 | } else { | ||
| 133 | - | console.log('[Chat] Fetching messages from DB for instant chat', { roomId }); | 133 | + | log.debug('Fetching messages from DB for instant chat', { roomId }); |
| 134 | const query = db.select() | 134 | const query = db.select() | ||
| 135 | .from(messages) | 135 | .from(messages) | ||
| 136 | .where(eq(messages.chatRoomId, roomId)) | 136 | .where(eq(messages.chatRoomId, roomId)) | ||
| @@ -153,7 +153,10 @@ export async function GET({ request, locals }) { | |||||
| 153 | } | 153 | } | ||
| 154 | ); | 154 | ); | ||
| 155 | } catch (error) { | 155 | } catch (error) { | ||
| 156 | - | console.log('[Chat] Error fetching messages:', error); | 156 | + | log.error('Error fetching messages', { |
| 157 | + | error: error instanceof Error ? error.message : 'Unknown error', | |||
| 158 | + | roomId | |||
| 159 | + | }); | |||
| 157 | const errorResponse: GetMessagesResponse = { | 160 | const errorResponse: GetMessagesResponse = { | ||
| 158 | success: false, | 161 | success: false, | ||
| 159 | error: 'Failed to fetch messages' | 162 | error: 'Failed to fetch messages' | ||
| @@ -171,16 +174,20 @@ export async function GET({ request, locals }) { | |||||
| 171 | // POST endpoint: receive a new message, save it in the DB, and broadcast via SSE | 174 | // POST endpoint: receive a new message, save it in the DB, and broadcast via SSE | ||
| 172 | export async function POST({ request, locals }: { request: Request, locals: App.Locals }) { | 175 | export async function POST({ request, locals }: { request: Request, locals: App.Locals }) { | ||
| 173 | if (!locals.user) { | 176 | if (!locals.user) { | ||
| 177 | + | log.warn('Authentication required'); | |||
| 174 | throw error(401, 'Authentication required'); | 178 | throw error(401, 'Authentication required'); | ||
| 175 | } | 179 | } | ||
| 176 | 180 | ||||
| 177 | - | console.log('[Chat] New message received'); | 181 | + | log.debug('New message received'); |
| 178 | try { | 182 | try { | ||
| 179 | // Check rate limiting | 183 | // Check rate limiting | ||
| 180 | const { canSend, retryAfter } = updateUserCooldown(locals.user.id); | 184 | const { canSend, retryAfter } = updateUserCooldown(locals.user.id); | ||
| 181 | if (!canSend) { | 185 | if (!canSend) { | ||
| 182 | const maskedUserId = `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}`; | 186 | const maskedUserId = `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}`; | ||
| 183 | - | console.log('[Chat] Rate limited:', { userId: maskedUserId, retryAfter }); | 187 | + | log.warn('Rate limited', { |
| 188 | + | userId: maskedUserId, | |||
| 189 | + | retryAfter | |||
| 190 | + | }); | |||
| 184 | const errorResponse: SendMessageResponse = { | 191 | const errorResponse: SendMessageResponse = { | ||
| 185 | success: false, | 192 | success: false, | ||
| 186 | error: 'Please wait before sending another message', | 193 | error: 'Please wait before sending another message', | ||
| @@ -201,7 +208,10 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||||
| 201 | 208 | ||||
| 202 | const data = await request.json() as SendMessageRequest; | 209 | const data = await request.json() as SendMessageRequest; | ||
| 203 | if (!data.content || !data.userId) { | 210 | if (!data.content || !data.userId) { | ||
| 204 | - | console.log('[Chat] Invalid payload received'); | 211 | + | log.warn('Invalid payload received', { |
| 212 | + | hasContent: Boolean(data.content), | |||
| 213 | + | hasUserId: Boolean(data.userId) | |||
| 214 | + | }); | |||
| 205 | const errorResponse: SendMessageResponse = { | 215 | const errorResponse: SendMessageResponse = { | ||
| 206 | success: false, | 216 | success: false, | ||
| 207 | error: 'Invalid message payload' | 217 | error: 'Invalid message payload' | ||
| @@ -210,6 +220,10 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||||
| 210 | } | 220 | } | ||
| 211 | 221 | ||||
| 212 | if (data.userId !== locals.user.id) { | 222 | if (data.userId !== locals.user.id) { | ||
| 223 | + | log.warn('Unauthorized message attempt', { | |||
| 224 | + | requestedUserId: data.userId, | |||
| 225 | + | actualUserId: `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}` | |||
| 226 | + | }); | |||
| 213 | throw error(403, 'Cannot post messages as another user'); | 227 | throw error(403, 'Cannot post messages as another user'); | ||
| 214 | } | 228 | } | ||
| 215 | 229 | ||||
| @@ -223,7 +237,7 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||||
| 223 | .get(); | 237 | .get(); | ||
| 224 | 238 | ||||
| 225 | if (!chatRoom) { | 239 | if (!chatRoom) { | ||
| 226 | - | console.log('[Chat] Chat room not found:', chatRoomId); | 240 | + | log.warn('Chat room not found', { chatRoomId }); |
| 227 | const errorResponse: SendMessageResponse = { | 241 | const errorResponse: SendMessageResponse = { | ||
| 228 | success: false, | 242 | success: false, | ||
| 229 | error: 'Chat room not found' | 243 | error: 'Chat room not found' | ||
| @@ -239,7 +253,7 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||||
| 239 | 253 | ||||
| 240 | if (!user) { | 254 | if (!user) { | ||
| 241 | const maskedUserId = `${data.userId.slice(0, 4)}...${data.userId.slice(-4)}`; | 255 | const maskedUserId = `${data.userId.slice(0, 4)}...${data.userId.slice(-4)}`; | ||
| 242 | - | console.log('[Chat] User not found:', maskedUserId); | 256 | + | log.warn('User not found', { maskedUserId }); |
| 243 | const errorResponse: SendMessageResponse = { | 257 | const errorResponse: SendMessageResponse = { | ||
| 244 | success: false, | 258 | success: false, | ||
| 245 | error: 'User not found' | 259 | error: 'User not found' | ||
| @@ -249,7 +263,7 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||||
| 249 | 263 | ||||
| 250 | const newMessage: Message = { | 264 | const newMessage: Message = { | ||
| 251 | id: uuidv4(), | 265 | id: uuidv4(), | ||
| 252 | - | chatRoomId: data.chatRoomId || DEFAULT_CHAT_ROOM_ID, | 266 | + | chatRoomId, |
| 253 | senderId: data.userId, | 267 | senderId: data.userId, | ||
| 254 | content: data.content, | 268 | content: data.content, | ||
| 255 | type: data.type || 'chat', | 269 | type: data.type || 'chat', | ||
| @@ -258,7 +272,7 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||||
| 258 | 272 | ||||
| 259 | // Save to DB | 273 | // Save to DB | ||
| 260 | await db.insert(messages).values(newMessage); | 274 | await db.insert(messages).values(newMessage); | ||
| 261 | - | console.log('[Chat] Message saved in DB', { | 275 | + | log.debug('Message saved in DB', { |
| 262 | messageId: newMessage.id, | 276 | messageId: newMessage.id, | ||
| 263 | chatRoomId: newMessage.chatRoomId, | 277 | chatRoomId: newMessage.chatRoomId, | ||
| 264 | type: newMessage.type, | 278 | type: newMessage.type, | ||
| @@ -267,7 +281,7 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||||
| 267 | 281 | ||||
| 268 | // Update message cache | 282 | // Update message cache | ||
| 269 | messageCache.addMessage(newMessage); | 283 | messageCache.addMessage(newMessage); | ||
| 270 | - | console.debug('[Chat] Message added to cache', { | 284 | + | log.debug('Message added to cache', { |
| 271 | messageId: newMessage.id, | 285 | messageId: newMessage.id, | ||
| 272 | chatRoomId: newMessage.chatRoomId | 286 | chatRoomId: newMessage.chatRoomId | ||
| 273 | }); | 287 | }); | ||
| @@ -275,6 +289,12 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||||
| 275 | // Broadcast the new message via the unified SSE emitter | 289 | // Broadcast the new message via the unified SSE emitter | ||
| 276 | sseEmitter.emit('sse', { type: 'chatMessage', data: newMessage }); | 290 | sseEmitter.emit('sse', { type: 'chatMessage', data: newMessage }); | ||
| 277 | 291 | ||||
| 292 | + | log.debug('Message processed successfully', { | |||
| 293 | + | messageId: newMessage.id, | |||
| 294 | + | userId: `${newMessage.senderId.slice(0, 4)}...${newMessage.senderId.slice(-4)}`, | |||
| 295 | + | roomId: newMessage.chatRoomId | |||
| 296 | + | }); | |||
| 297 | + | ||||
| 278 | const successResponse: SendMessageResponse = { | 298 | const successResponse: SendMessageResponse = { | ||
| 279 | success: true, | 299 | success: true, | ||
| 280 | message: newMessage | 300 | message: newMessage | ||
| @@ -287,8 +307,10 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||||
| 287 | headers: { 'Content-Type': 'application/json' } | 307 | headers: { 'Content-Type': 'application/json' } | ||
| 288 | } | 308 | } | ||
| 289 | ); | 309 | ); | ||
| 290 | - | } catch { | 310 | + | } catch (error) { |
| 291 | - | console.log('[Chat] Error processing message'); | 311 | + | log.error('Error processing message', { |
| 312 | + | error: error instanceof Error ? error.message : 'Unknown error' | |||
| 313 | + | }); | |||
| 292 | const errorResponse: SendMessageResponse = { | 314 | const errorResponse: SendMessageResponse = { | ||
| 293 | success: false, | 315 | success: false, | ||
| 294 | error: 'Failed to save message' | 316 | error: 'Failed to save message' | ||
| @@ -301,4 +323,4 @@ export async function POST({ request, locals }: { request: Request, locals: App. | |||||
| 301 | } | 323 | } | ||
| 302 | ); | 324 | ); | ||
| 303 | } | 325 | } | ||
| 304 | - | } ⏎̸ | 326 | + | }⏎̸ |
M src/routes/api/rooms/[roomId]/+server.ts +10 -4
| @@ -4,7 +4,9 @@ import { messages, users } from '$lib/db/schema'; | |||
| 4 | 4 | import type { PublicRoomResponse } from '$lib/types/payloads'; | |
| 5 | 5 | import { error } from '@sveltejs/kit'; | |
| 6 | 6 | import { createSafeUser } from '$lib/types/chat'; | |
| 7 | + | import { createLogger } from '$lib/utils/logger.server'; | |
| 7 | 8 | ||
| 9 | + | const log = createLogger('rooms-server'); | |
| 8 | 10 | ||
| 9 | 11 | export async function GET({ params, url, locals }): Promise<Response> { | |
| 10 | 12 | const { roomId } = params; | |
| @@ -12,11 +14,12 @@ export async function GET({ params, url, locals }): Promise<Response> { | |||
| 12 | 14 | ||
| 13 | 15 | // Check authentication for non-public requests | |
| 14 | 16 | if (!isPublic && !locals.user) { | |
| 17 | + | log.warn('Authentication required'); | |
| 15 | 18 | throw error(401, 'Authentication required'); | |
| 16 | 19 | } | |
| 17 | 20 | ||
| 18 | 21 | try { | |
| 19 | - | console.log('[Rooms] Fetching room data:', { | |
| 22 | + | log.debug('Fetching room data', { | |
| 20 | 23 | roomId, | |
| 21 | 24 | isPublic, | |
| 22 | 25 | requestedBy: locals.user ? `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}` : 'public' | |
| @@ -47,7 +50,7 @@ export async function GET({ params, url, locals }): Promise<Response> { | |||
| 47 | 50 | buddyList: sanitizedUsers | |
| 48 | 51 | }; | |
| 49 | 52 | ||
| 50 | - | console.log('[Rooms] Successfully fetched room data:', { | |
| 53 | + | log.debug('Successfully fetched room data', { | |
| 51 | 54 | roomId, | |
| 52 | 55 | userCount: sanitizedUsers.length, | |
| 53 | 56 | isPublic | |
| @@ -57,8 +60,11 @@ export async function GET({ params, url, locals }): Promise<Response> { | |||
| 57 | 60 | status: 200, | |
| 58 | 61 | headers: { 'Content-Type': 'application/json' } | |
| 59 | 62 | }); | |
| 60 | - | } catch { | |
| 61 | - | console.log('[Rooms] Error fetching room data:', { roomId }); | |
| 63 | + | } catch (error) { | |
| 64 | + | log.error('Error fetching room data', { | |
| 65 | + | roomId, | |
| 66 | + | error: error instanceof Error ? error.message : 'Unknown error' | |
| 67 | + | }); | |
| 62 | 68 | const errorResponse: PublicRoomResponse = { | |
| 63 | 69 | success: false, | |
| 64 | 70 | error: 'Failed to fetch room data' | |
| @@ -4,7 +4,9 @@ import { messages, users } from '$lib/db/schema'; | |||||
| 4 | import type { PublicRoomResponse } from '$lib/types/payloads'; | 4 | import type { PublicRoomResponse } from '$lib/types/payloads'; | ||
| 5 | import { error } from '@sveltejs/kit'; | 5 | import { error } from '@sveltejs/kit'; | ||
| 6 | import { createSafeUser } from '$lib/types/chat'; | 6 | import { createSafeUser } from '$lib/types/chat'; | ||
| 7 | + | import { createLogger } from '$lib/utils/logger.server'; | |||
| 7 | 8 | ||||
| 9 | + | const log = createLogger('rooms-server'); | |||
| 8 | 10 | ||||
| 9 | export async function GET({ params, url, locals }): Promise<Response> { | 11 | export async function GET({ params, url, locals }): Promise<Response> { | ||
| 10 | const { roomId } = params; | 12 | const { roomId } = params; | ||
| @@ -12,11 +14,12 @@ export async function GET({ params, url, locals }): Promise<Response> { | |||||
| 12 | 14 | ||||
| 13 | // Check authentication for non-public requests | 15 | // Check authentication for non-public requests | ||
| 14 | if (!isPublic && !locals.user) { | 16 | if (!isPublic && !locals.user) { | ||
| 17 | + | log.warn('Authentication required'); | |||
| 15 | throw error(401, 'Authentication required'); | 18 | throw error(401, 'Authentication required'); | ||
| 16 | } | 19 | } | ||
| 17 | 20 | ||||
| 18 | try { | 21 | try { | ||
| 19 | - | console.log('[Rooms] Fetching room data:', { | 22 | + | log.debug('Fetching room data', { |
| 20 | roomId, | 23 | roomId, | ||
| 21 | isPublic, | 24 | isPublic, | ||
| 22 | requestedBy: locals.user ? `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}` : 'public' | 25 | requestedBy: locals.user ? `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}` : 'public' | ||
| @@ -47,7 +50,7 @@ export async function GET({ params, url, locals }): Promise<Response> { | |||||
| 47 | buddyList: sanitizedUsers | 50 | buddyList: sanitizedUsers | ||
| 48 | }; | 51 | }; | ||
| 49 | 52 | ||||
| 50 | - | console.log('[Rooms] Successfully fetched room data:', { | 53 | + | log.debug('Successfully fetched room data', { |
| 51 | roomId, | 54 | roomId, | ||
| 52 | userCount: sanitizedUsers.length, | 55 | userCount: sanitizedUsers.length, | ||
| 53 | isPublic | 56 | isPublic | ||
| @@ -57,8 +60,11 @@ export async function GET({ params, url, locals }): Promise<Response> { | |||||
| 57 | status: 200, | 60 | status: 200, | ||
| 58 | headers: { 'Content-Type': 'application/json' } | 61 | headers: { 'Content-Type': 'application/json' } | ||
| 59 | }); | 62 | }); | ||
| 60 | - | } catch { | 63 | + | } catch (error) { |
| 61 | - | console.log('[Rooms] Error fetching room data:', { roomId }); | 64 | + | log.error('Error fetching room data', { |
| 65 | + | roomId, | |||
| 66 | + | error: error instanceof Error ? error.message : 'Unknown error' | |||
| 67 | + | }); | |||
| 62 | const errorResponse: PublicRoomResponse = { | 68 | const errorResponse: PublicRoomResponse = { | ||
| 63 | success: false, | 69 | success: false, | ||
| 64 | error: 'Failed to fetch room data' | 70 | error: 'Failed to fetch room data' | ||
M src/routes/api/session/login/+server.ts +28 -8
| @@ -7,6 +7,9 @@ import { verifyPassword } from '$lib/utils/password'; | |||
| 7 | 7 | import { generateSessionToken, createSession } from '$lib/api/session.server'; | |
| 8 | 8 | import { setSessionTokenCookie } from '$lib/api/session.cookie'; | |
| 9 | 9 | import { createSafeUser } from '$lib/types/chat'; | |
| 10 | + | import { createLogger } from '$lib/utils/logger.server'; | |
| 11 | + | ||
| 12 | + | const log = createLogger('login-server'); | |
| 10 | 13 | ||
| 11 | 14 | // In-memory map to track failed login attempts per IP | |
| 12 | 15 | const loginAttempts = new Map<string, { count: number, lastAttempt: number }>(); | |
| @@ -25,9 +28,10 @@ setInterval(() => { | |||
| 25 | 28 | }, 60 * 60 * 1000); | |
| 26 | 29 | ||
| 27 | 30 | export const POST: RequestHandler = async ({ request, cookies }) => { | |
| 28 | - | console.log("[Login] New login attempt received"); | |
| 31 | + | log.debug('New login attempt received'); | |
| 29 | 32 | ||
| 30 | 33 | if (request.method !== 'POST') { | |
| 34 | + | log.warn('Invalid method used', { method: request.method }); | |
| 31 | 35 | return new Response(JSON.stringify({ error: 'Method Not Allowed' } as LoginResponseError), { status: 405 }); | |
| 32 | 36 | } | |
| 33 | 37 | ||
| @@ -40,7 +44,7 @@ export const POST: RequestHandler = async ({ request, cookies }) => { | |||
| 40 | 44 | // If the user has failed three or more times, apply exponential backoff | |
| 41 | 45 | if (attemptData.count >= MAX_ATTEMPTS) { | |
| 42 | 46 | const delay = Math.pow(2, attemptData.count - MAX_ATTEMPTS + 1) * 1000; // delay in milliseconds | |
| 43 | - | console.log(`[Login] Rate limit exceeded for IP ${maskedIp} - applying ${delay}ms delay`); | |
| 47 | + | log.warn('Rate limit exceeded', { maskedIp, delay }); | |
| 44 | 48 | await sleep(delay); | |
| 45 | 49 | } | |
| 46 | 50 | ||
| @@ -48,14 +52,17 @@ export const POST: RequestHandler = async ({ request, cookies }) => { | |||
| 48 | 52 | try { | |
| 49 | 53 | body = await request.json(); | |
| 50 | 54 | } catch { | |
| 51 | - | console.log("[Login] Invalid JSON payload received"); | |
| 55 | + | log.warn('Invalid JSON payload received'); | |
| 52 | 56 | return new Response(JSON.stringify({ error: 'Invalid JSON' } as LoginResponseError), { status: 400 }); | |
| 53 | 57 | } | |
| 54 | 58 | ||
| 55 | 59 | const { username, password } = body as { username: string, password: string }; | |
| 56 | 60 | ||
| 57 | 61 | if (typeof username !== 'string' || typeof password !== 'string') { | |
| 58 | - | console.log("Missing or invalid input fields", { username, password }); | |
| 62 | + | log.warn('Missing or invalid input fields', { | |
| 63 | + | hasUsername: typeof username === 'string', | |
| 64 | + | hasPassword: typeof password === 'string' | |
| 65 | + | }); | |
| 59 | 66 | return new Response(JSON.stringify({ error: 'Missing or invalid input fields' } as LoginResponseError), { status: 400 }); | |
| 60 | 67 | } | |
| 61 | 68 | ||
| @@ -68,7 +75,11 @@ export const POST: RequestHandler = async ({ request, cookies }) => { | |||
| 68 | 75 | attemptData.count++; | |
| 69 | 76 | attemptData.lastAttempt = now; | |
| 70 | 77 | loginAttempts.set(ip, attemptData); | |
| 71 | - | console.log(`[Login] Login failed for IP ${maskedIp}. Attempt count: ${attemptData.count}`); | |
| 78 | + | log.warn('Login failed - user not found', { | |
| 79 | + | maskedIp, | |
| 80 | + | attemptCount: attemptData.count, | |
| 81 | + | username: username.trim() | |
| 82 | + | }); | |
| 72 | 83 | const remaining = Math.max(0, MAX_ATTEMPTS - attemptData.count); | |
| 73 | 84 | return new Response(JSON.stringify({ | |
| 74 | 85 | error: `Invalid username or password. ${remaining > 0 ? remaining + " attempt(s) remaining." : "Please wait before trying again."}` | |
| @@ -82,14 +93,18 @@ export const POST: RequestHandler = async ({ request, cookies }) => { | |||
| 82 | 93 | attemptData.count++; | |
| 83 | 94 | attemptData.lastAttempt = now; | |
| 84 | 95 | loginAttempts.set(ip, attemptData); | |
| 85 | - | console.log(`[Login] Login failed for IP ${maskedIp}. Attempt count: ${attemptData.count}`); | |
| 96 | + | log.warn('Login failed - invalid password', { | |
| 97 | + | maskedIp, | |
| 98 | + | attemptCount: attemptData.count, | |
| 99 | + | userId: `${user.id.slice(0, 4)}...${user.id.slice(-4)}` | |
| 100 | + | }); | |
| 86 | 101 | const remaining = Math.max(0, MAX_ATTEMPTS - attemptData.count); | |
| 87 | 102 | return new Response(JSON.stringify({ | |
| 88 | 103 | error: `Invalid username or password. ${remaining > 0 ? remaining + " attempt(s) remaining." : "Please wait before trying again."}` | |
| 89 | 104 | } as LoginResponseError), { status: 401 }); | |
| 90 | 105 | } | |
| 91 | - | } catch { | |
| 92 | - | console.log("[Login] Error verifying password"); | |
| 106 | + | } catch (error) { | |
| 107 | + | log.error('Error verifying password', { error }); | |
| 93 | 108 | return new Response(JSON.stringify({ error: 'Internal Server Error' } as LoginResponseError), { status: 500 }); | |
| 94 | 109 | } | |
| 95 | 110 | ||
| @@ -111,6 +126,11 @@ export const POST: RequestHandler = async ({ request, cookies }) => { | |||
| 111 | 126 | // Set session cookie | |
| 112 | 127 | setSessionTokenCookie({ cookies }, token, session.expiresAt); | |
| 113 | 128 | ||
| 129 | + | log.info('Login successful', { | |
| 130 | + | userId: `${user.id.slice(0, 4)}...${user.id.slice(-4)}`, | |
| 131 | + | expiresAt: new Date(session.expiresAt).toISOString() | |
| 132 | + | }); | |
| 133 | + | ||
| 114 | 134 | return new Response(JSON.stringify({ | |
| 115 | 135 | success: true, | |
| 116 | 136 | user: createSafeUser(user) | |
| @@ -7,6 +7,9 @@ import { verifyPassword } from '$lib/utils/password'; | |||||
| 7 | import { generateSessionToken, createSession } from '$lib/api/session.server'; | 7 | import { generateSessionToken, createSession } from '$lib/api/session.server'; | ||
| 8 | import { setSessionTokenCookie } from '$lib/api/session.cookie'; | 8 | import { setSessionTokenCookie } from '$lib/api/session.cookie'; | ||
| 9 | import { createSafeUser } from '$lib/types/chat'; | 9 | import { createSafeUser } from '$lib/types/chat'; | ||
| 10 | + | import { createLogger } from '$lib/utils/logger.server'; | |||
| 11 | + | ||||
| 12 | + | const log = createLogger('login-server'); | |||
| 10 | 13 | ||||
| 11 | // In-memory map to track failed login attempts per IP | 14 | // In-memory map to track failed login attempts per IP | ||
| 12 | const loginAttempts = new Map<string, { count: number, lastAttempt: number }>(); | 15 | const loginAttempts = new Map<string, { count: number, lastAttempt: number }>(); | ||
| @@ -25,9 +28,10 @@ setInterval(() => { | |||||
| 25 | }, 60 * 60 * 1000); | 28 | }, 60 * 60 * 1000); | ||
| 26 | 29 | ||||
| 27 | export const POST: RequestHandler = async ({ request, cookies }) => { | 30 | export const POST: RequestHandler = async ({ request, cookies }) => { | ||
| 28 | - | console.log("[Login] New login attempt received"); | 31 | + | log.debug('New login attempt received'); |
| 29 | 32 | ||||
| 30 | if (request.method !== 'POST') { | 33 | if (request.method !== 'POST') { | ||
| 34 | + | log.warn('Invalid method used', { method: request.method }); | |||
| 31 | return new Response(JSON.stringify({ error: 'Method Not Allowed' } as LoginResponseError), { status: 405 }); | 35 | return new Response(JSON.stringify({ error: 'Method Not Allowed' } as LoginResponseError), { status: 405 }); | ||
| 32 | } | 36 | } | ||
| 33 | 37 | ||||
| @@ -40,7 +44,7 @@ export const POST: RequestHandler = async ({ request, cookies }) => { | |||||
| 40 | // If the user has failed three or more times, apply exponential backoff | 44 | // If the user has failed three or more times, apply exponential backoff | ||
| 41 | if (attemptData.count >= MAX_ATTEMPTS) { | 45 | if (attemptData.count >= MAX_ATTEMPTS) { | ||
| 42 | const delay = Math.pow(2, attemptData.count - MAX_ATTEMPTS + 1) * 1000; // delay in milliseconds | 46 | const delay = Math.pow(2, attemptData.count - MAX_ATTEMPTS + 1) * 1000; // delay in milliseconds | ||
| 43 | - | console.log(`[Login] Rate limit exceeded for IP ${maskedIp} - applying ${delay}ms delay`); | 47 | + | log.warn('Rate limit exceeded', { maskedIp, delay }); |
| 44 | await sleep(delay); | 48 | await sleep(delay); | ||
| 45 | } | 49 | } | ||
| 46 | 50 | ||||
| @@ -48,14 +52,17 @@ export const POST: RequestHandler = async ({ request, cookies }) => { | |||||
| 48 | try { | 52 | try { | ||
| 49 | body = await request.json(); | 53 | body = await request.json(); | ||
| 50 | } catch { | 54 | } catch { | ||
| 51 | - | console.log("[Login] Invalid JSON payload received"); | 55 | + | log.warn('Invalid JSON payload received'); |
| 52 | return new Response(JSON.stringify({ error: 'Invalid JSON' } as LoginResponseError), { status: 400 }); | 56 | return new Response(JSON.stringify({ error: 'Invalid JSON' } as LoginResponseError), { status: 400 }); | ||
| 53 | } | 57 | } | ||
| 54 | 58 | ||||
| 55 | const { username, password } = body as { username: string, password: string }; | 59 | const { username, password } = body as { username: string, password: string }; | ||
| 56 | 60 | ||||
| 57 | if (typeof username !== 'string' || typeof password !== 'string') { | 61 | if (typeof username !== 'string' || typeof password !== 'string') { | ||
| 58 | - | console.log("Missing or invalid input fields", { username, password }); | 62 | + | log.warn('Missing or invalid input fields', { |
| 63 | + | hasUsername: typeof username === 'string', | |||
| 64 | + | hasPassword: typeof password === 'string' | |||
| 65 | + | }); | |||
| 59 | return new Response(JSON.stringify({ error: 'Missing or invalid input fields' } as LoginResponseError), { status: 400 }); | 66 | return new Response(JSON.stringify({ error: 'Missing or invalid input fields' } as LoginResponseError), { status: 400 }); | ||
| 60 | } | 67 | } | ||
| 61 | 68 | ||||
| @@ -68,7 +75,11 @@ export const POST: RequestHandler = async ({ request, cookies }) => { | |||||
| 68 | attemptData.count++; | 75 | attemptData.count++; | ||
| 69 | attemptData.lastAttempt = now; | 76 | attemptData.lastAttempt = now; | ||
| 70 | loginAttempts.set(ip, attemptData); | 77 | loginAttempts.set(ip, attemptData); | ||
| 71 | - | console.log(`[Login] Login failed for IP ${maskedIp}. Attempt count: ${attemptData.count}`); | 78 | + | log.warn('Login failed - user not found', { |
| 79 | + | maskedIp, | |||
| 80 | + | attemptCount: attemptData.count, | |||
| 81 | + | username: username.trim() | |||
| 82 | + | }); | |||
| 72 | const remaining = Math.max(0, MAX_ATTEMPTS - attemptData.count); | 83 | const remaining = Math.max(0, MAX_ATTEMPTS - attemptData.count); | ||
| 73 | return new Response(JSON.stringify({ | 84 | return new Response(JSON.stringify({ | ||
| 74 | error: `Invalid username or password. ${remaining > 0 ? remaining + " attempt(s) remaining." : "Please wait before trying again."}` | 85 | error: `Invalid username or password. ${remaining > 0 ? remaining + " attempt(s) remaining." : "Please wait before trying again."}` | ||
| @@ -82,14 +93,18 @@ export const POST: RequestHandler = async ({ request, cookies }) => { | |||||
| 82 | attemptData.count++; | 93 | attemptData.count++; | ||
| 83 | attemptData.lastAttempt = now; | 94 | attemptData.lastAttempt = now; | ||
| 84 | loginAttempts.set(ip, attemptData); | 95 | loginAttempts.set(ip, attemptData); | ||
| 85 | - | console.log(`[Login] Login failed for IP ${maskedIp}. Attempt count: ${attemptData.count}`); | 96 | + | log.warn('Login failed - invalid password', { |
| 97 | + | maskedIp, | |||
| 98 | + | attemptCount: attemptData.count, | |||
| 99 | + | userId: `${user.id.slice(0, 4)}...${user.id.slice(-4)}` | |||
| 100 | + | }); | |||
| 86 | const remaining = Math.max(0, MAX_ATTEMPTS - attemptData.count); | 101 | const remaining = Math.max(0, MAX_ATTEMPTS - attemptData.count); | ||
| 87 | return new Response(JSON.stringify({ | 102 | return new Response(JSON.stringify({ | ||
| 88 | error: `Invalid username or password. ${remaining > 0 ? remaining + " attempt(s) remaining." : "Please wait before trying again."}` | 103 | error: `Invalid username or password. ${remaining > 0 ? remaining + " attempt(s) remaining." : "Please wait before trying again."}` | ||
| 89 | } as LoginResponseError), { status: 401 }); | 104 | } as LoginResponseError), { status: 401 }); | ||
| 90 | } | 105 | } | ||
| 91 | - | } catch { | 106 | + | } catch (error) { |
| 92 | - | console.log("[Login] Error verifying password"); | 107 | + | log.error('Error verifying password', { error }); |
| 93 | return new Response(JSON.stringify({ error: 'Internal Server Error' } as LoginResponseError), { status: 500 }); | 108 | return new Response(JSON.stringify({ error: 'Internal Server Error' } as LoginResponseError), { status: 500 }); | ||
| 94 | } | 109 | } | ||
| 95 | 110 | ||||
| @@ -111,6 +126,11 @@ export const POST: RequestHandler = async ({ request, cookies }) => { | |||||
| 111 | // Set session cookie | 126 | // Set session cookie | ||
| 112 | setSessionTokenCookie({ cookies }, token, session.expiresAt); | 127 | setSessionTokenCookie({ cookies }, token, session.expiresAt); | ||
| 113 | 128 | ||||
| 129 | + | log.info('Login successful', { | |||
| 130 | + | userId: `${user.id.slice(0, 4)}...${user.id.slice(-4)}`, | |||
| 131 | + | expiresAt: new Date(session.expiresAt).toISOString() | |||
| 132 | + | }); | |||
| 133 | + | ||||
| 114 | return new Response(JSON.stringify({ | 134 | return new Response(JSON.stringify({ | ||
| 115 | success: true, | 135 | success: true, | ||
| 116 | user: createSafeUser(user) | 136 | user: createSafeUser(user) | ||
M src/routes/api/session/logout/+server.ts +6 -1
| @@ -1,6 +1,9 @@ | |||
| 1 | 1 | import { json } from '@sveltejs/kit'; | |
| 2 | 2 | import { invalidateSession } from '$lib/api/session.server'; | |
| 3 | 3 | import { sha256 } from '$lib/api/session.server'; // re-use sha256 helper to compute session id | |
| 4 | + | import { createLogger } from '$lib/utils/logger.server'; | |
| 5 | + | ||
| 6 | + | const log = createLogger('logout-server'); | |
| 4 | 7 | ||
| 5 | 8 | export async function POST({ cookies }) { | |
| 6 | 9 | const token = cookies.get('session'); | |
| @@ -9,7 +12,9 @@ export async function POST({ cookies }) { | |||
| 9 | 12 | const sessionId = await sha256(token); | |
| 10 | 13 | await invalidateSession(sessionId); | |
| 11 | 14 | cookies.delete('session', { path: '/' }); | |
| 12 | - | console.debug("Logged out session with id:", sessionId); | |
| 15 | + | log.info('Session invalidated', { sessionId: `${sessionId.slice(0, 4)}...${sessionId.slice(-4)}` }); | |
| 16 | + | } else { | |
| 17 | + | log.debug('No session token found in cookies'); | |
| 13 | 18 | } | |
| 14 | 19 | ||
| 15 | 20 | return json({ success: true }); | |
| @@ -1,6 +1,9 @@ | |||||
| 1 | import { json } from '@sveltejs/kit'; | 1 | import { json } from '@sveltejs/kit'; | ||
| 2 | import { invalidateSession } from '$lib/api/session.server'; | 2 | import { invalidateSession } from '$lib/api/session.server'; | ||
| 3 | import { sha256 } from '$lib/api/session.server'; // re-use sha256 helper to compute session id | 3 | import { sha256 } from '$lib/api/session.server'; // re-use sha256 helper to compute session id | ||
| 4 | + | import { createLogger } from '$lib/utils/logger.server'; | |||
| 5 | + | ||||
| 6 | + | const log = createLogger('logout-server'); | |||
| 4 | 7 | ||||
| 5 | export async function POST({ cookies }) { | 8 | export async function POST({ cookies }) { | ||
| 6 | const token = cookies.get('session'); | 9 | const token = cookies.get('session'); | ||
| @@ -9,7 +12,9 @@ export async function POST({ cookies }) { | |||||
| 9 | const sessionId = await sha256(token); | 12 | const sessionId = await sha256(token); | ||
| 10 | await invalidateSession(sessionId); | 13 | await invalidateSession(sessionId); | ||
| 11 | cookies.delete('session', { path: '/' }); | 14 | cookies.delete('session', { path: '/' }); | ||
| 12 | - | console.debug("Logged out session with id:", sessionId); | 15 | + | log.info('Session invalidated', { sessionId: `${sessionId.slice(0, 4)}...${sessionId.slice(-4)}` }); |
| 16 | + | } else { | |||
| 17 | + | log.debug('No session token found in cookies'); | |||
| 13 | } | 18 | } | ||
| 14 | 19 | ||||
| 15 | return json({ success: true }); | 20 | return json({ success: true }); | ||
M src/routes/api/session/validate/+server.ts +12 -2
| @@ -1,17 +1,27 @@ | |||
| 1 | 1 | import { json } from '@sveltejs/kit'; | |
| 2 | 2 | import { validateSessionToken } from '$lib/api/session.server'; | |
| 3 | + | import { createLogger } from '$lib/utils/logger.server'; | |
| 4 | + | ||
| 5 | + | const log = createLogger('validate-server'); | |
| 3 | 6 | ||
| 4 | 7 | export async function GET({ cookies }) { | |
| 5 | 8 | const token = cookies.get('session'); | |
| 6 | 9 | ||
| 7 | 10 | if (!token) { | |
| 8 | - | console.debug("No session token found in cookies."); | |
| 11 | + | log.debug('No session token found in cookies'); | |
| 9 | 12 | return json({ session: null, user: null }, { status: 401 }); | |
| 10 | 13 | } | |
| 11 | 14 | ||
| 12 | 15 | const result = await validateSessionToken(token); | |
| 13 | 16 | ||
| 14 | - | console.debug("Validate endpoint result:", result); | |
| 17 | + | if (result.session && result.user) { | |
| 18 | + | log.debug('Session validated', { | |
| 19 | + | userId: `${result.user.id.slice(0, 4)}...${result.user.id.slice(-4)}`, | |
| 20 | + | sessionId: `${result.session.id.slice(0, 4)}...${result.session.id.slice(-4)}` | |
| 21 | + | }); | |
| 22 | + | } else { | |
| 23 | + | log.debug('Invalid session token'); | |
| 24 | + | } | |
| 15 | 25 | ||
| 16 | 26 | return json(result); | |
| 17 | 27 | } ⏎̸ | |
| @@ -1,17 +1,27 @@ | |||||
| 1 | import { json } from '@sveltejs/kit'; | 1 | import { json } from '@sveltejs/kit'; | ||
| 2 | import { validateSessionToken } from '$lib/api/session.server'; | 2 | import { validateSessionToken } from '$lib/api/session.server'; | ||
| 3 | + | import { createLogger } from '$lib/utils/logger.server'; | |||
| 4 | + | ||||
| 5 | + | const log = createLogger('validate-server'); | |||
| 3 | 6 | ||||
| 4 | export async function GET({ cookies }) { | 7 | export async function GET({ cookies }) { | ||
| 5 | const token = cookies.get('session'); | 8 | const token = cookies.get('session'); | ||
| 6 | 9 | ||||
| 7 | if (!token) { | 10 | if (!token) { | ||
| 8 | - | console.debug("No session token found in cookies."); | 11 | + | log.debug('No session token found in cookies'); |
| 9 | return json({ session: null, user: null }, { status: 401 }); | 12 | return json({ session: null, user: null }, { status: 401 }); | ||
| 10 | } | 13 | } | ||
| 11 | 14 | ||||
| 12 | const result = await validateSessionToken(token); | 15 | const result = await validateSessionToken(token); | ||
| 13 | 16 | ||||
| 14 | - | console.debug("Validate endpoint result:", result); | 17 | + | if (result.session && result.user) { |
| 18 | + | log.debug('Session validated', { | |||
| 19 | + | userId: `${result.user.id.slice(0, 4)}...${result.user.id.slice(-4)}`, | |||
| 20 | + | sessionId: `${result.session.id.slice(0, 4)}...${result.session.id.slice(-4)}` | |||
| 21 | + | }); | |||
| 22 | + | } else { | |||
| 23 | + | log.debug('Invalid session token'); | |||
| 24 | + | } | |||
| 15 | 25 | ||||
| 16 | return json(result); | 26 | return json(result); | ||
| 17 | } ⏎̸ | 27 | } ⏎̸ | ||
M src/routes/api/sse/+server.ts +26 -26
| @@ -10,6 +10,9 @@ import db from '$lib/db/db.server'; | |||
| 10 | 10 | import { users } from '$lib/db/schema'; | |
| 11 | 11 | import { eq, and, lt } from 'drizzle-orm'; | |
| 12 | 12 | import { createSafeUser } from '$lib/types/chat'; | |
| 13 | + | import { createLogger } from '$lib/utils/logger.server'; | |
| 14 | + | ||
| 15 | + | const log = createLogger('sse-server'); | |
| 13 | 16 | ||
| 14 | 17 | console.log('[SSE] Server initialized'); | |
| 15 | 18 | ||
| @@ -35,7 +38,7 @@ function startTimeoutCheck() { | |||
| 35 | 38 | ||
| 36 | 39 | timeoutInterval = setInterval(async () => { | |
| 37 | 40 | const timeoutThreshold = Date.now() - ONLINE_TIMEOUT_MS; | |
| 38 | - | console.log('[SSE] Checking for timed out users', { | |
| 41 | + | log.debug('Checking for timed out users', { | |
| 39 | 42 | checkTime: new Date().toISOString(), | |
| 40 | 43 | timeoutThreshold: new Date(timeoutThreshold).toISOString() | |
| 41 | 44 | }); | |
| @@ -60,13 +63,13 @@ function startTimeoutCheck() { | |||
| 60 | 63 | }); | |
| 61 | 64 | ||
| 62 | 65 | if (result.length > 0) { | |
| 63 | - | console.log('[SSE] Found timed out users:', result.map(user => ({ | |
| 64 | - | nickname: user.nickname, | |
| 65 | - | lastSeen: new Date(user.lastSeen || 0).toISOString() | |
| 66 | - | }))); | |
| 66 | + | log.info('User status updated to offline', { | |
| 67 | + | usersUpdated: result.length, | |
| 68 | + | timeoutThreshold: new Date(timeoutThreshold).toISOString() | |
| 69 | + | }); | |
| 67 | 70 | } | |
| 68 | 71 | } catch (error) { | |
| 69 | - | console.error('[SSE] Error during timeout check:', error); | |
| 72 | + | log.error('Error during timeout check', { error }); | |
| 70 | 73 | } | |
| 71 | 74 | }, CHECK_INTERVAL_MS); | |
| 72 | 75 | } | |
| @@ -81,7 +84,7 @@ function startBuddyListUpdateInterval() { | |||
| 81 | 84 | const now = Date.now(); | |
| 82 | 85 | // Only fetch and broadcast if 10 seconds have passed since last update | |
| 83 | 86 | if (now - lastBuddyListUpdate >= BUDDY_LIST_BROADCAST_INTERVAL) { | |
| 84 | - | console.log('[SSE] Broadcasting buddy list update...'); | |
| 87 | + | log.debug('Broadcasting buddy list update...'); | |
| 85 | 88 | // Query the complete buddy list from the database. | |
| 86 | 89 | const buddyList = await db.select().from(users); | |
| 87 | 90 | // Use createSafeUser to properly sanitize user data | |
| @@ -98,7 +101,7 @@ function startBuddyListUpdateInterval() { | |||
| 98 | 101 | lastBuddyListUpdate = now; | |
| 99 | 102 | } | |
| 100 | 103 | } catch (error) { | |
| 101 | - | console.error('[SSE] Error fetching buddy list for update:', error); | |
| 104 | + | log.error('Error fetching buddy list for update', { error }); | |
| 102 | 105 | } | |
| 103 | 106 | }, 1000); // Check every second but only broadcast every 10 seconds | |
| 104 | 107 | } | |
| @@ -108,9 +111,9 @@ if (!globalThis.__sseIntervalsStarted) { | |||
| 108 | 111 | startTimeoutCheck(); | |
| 109 | 112 | startBuddyListUpdateInterval(); | |
| 110 | 113 | globalThis.__sseIntervalsStarted = true; | |
| 111 | - | console.log('[SSE] Started global intervals for timeout and buddy list updates.'); | |
| 114 | + | log.info('Started global intervals for timeout and buddy list updates'); | |
| 112 | 115 | } else { | |
| 113 | - | console.log('[SSE] Global intervals already started, skipping initialization.'); | |
| 116 | + | log.debug('Global intervals already started, skipping initialization'); | |
| 114 | 117 | } | |
| 115 | 118 | ||
| 116 | 119 | // Ensure the interval is cleared if the module is reloaded | |
| @@ -134,17 +137,14 @@ if (typeof process !== 'undefined') { | |||
| 134 | 137 | }); | |
| 135 | 138 | } | |
| 136 | 139 | ||
| 137 | - | export const GET: RequestHandler = async ({ request, locals, cookies }) => { | |
| 138 | - | console.log('[SSE] Connection attempt:', { | |
| 139 | - | hasLocals: !!locals, | |
| 140 | - | hasUser: !!locals.user, | |
| 141 | - | userId: locals.user?.id, | |
| 142 | - | hasSessionCookie: !!cookies.get('session'), | |
| 143 | - | headers: Object.fromEntries(request.headers.entries()) | |
| 140 | + | export const GET: RequestHandler = async ({ request, locals, }) => { | |
| 141 | + | log.debug('Processing SSE request', { | |
| 142 | + | url: request.url, | |
| 143 | + | userId: locals.user?.id | |
| 144 | 144 | }); | |
| 145 | 145 | ||
| 146 | 146 | if (!locals.user) { | |
| 147 | - | console.log('[SSE] Authentication failed - no user in locals'); | |
| 147 | + | log.warn('Authentication failed - no user in locals'); | |
| 148 | 148 | return new Response('Unauthorized', { | |
| 149 | 149 | status: 401, | |
| 150 | 150 | headers: { | |
| @@ -168,7 +168,7 @@ export const GET: RequestHandler = async ({ request, locals, cookies }) => { | |||
| 168 | 168 | }) | |
| 169 | 169 | .where(eq(users.id, userId)); | |
| 170 | 170 | ||
| 171 | - | console.log('[SSE] User status updated to online:', { | |
| 171 | + | log.info('User status updated to online', { | |
| 172 | 172 | userId: `${userId.slice(0, 4)}...${userId.slice(-4)}`, | |
| 173 | 173 | timestamp: new Date(now).toISOString() | |
| 174 | 174 | }); | |
| @@ -182,7 +182,7 @@ export const GET: RequestHandler = async ({ request, locals, cookies }) => { | |||
| 182 | 182 | } | |
| 183 | 183 | }); | |
| 184 | 184 | } catch (error) { | |
| 185 | - | console.error('[SSE] Error updating user status:', error); | |
| 185 | + | log.error('Error updating user status', { error }); | |
| 186 | 186 | } | |
| 187 | 187 | ||
| 188 | 188 | const stream = new ReadableStream({ | |
| @@ -193,10 +193,10 @@ export const GET: RequestHandler = async ({ request, locals, cookies }) => { | |||
| 193 | 193 | `data: ${JSON.stringify(event.data)}\n\n`; | |
| 194 | 194 | controller.enqueue(encoder.encode(payload)); | |
| 195 | 195 | if (!['userStatusUpdate', 'chatMessage'].includes(event.type)) { | |
| 196 | - | console.log('[SSE] Event sent:', { type: event.type }); | |
| 196 | + | log.info('Event sent', { type: event.type }); | |
| 197 | 197 | } | |
| 198 | - | } catch { | |
| 199 | - | console.log('[SSE] Error sending event'); | |
| 198 | + | } catch (error) { | |
| 199 | + | log.error('Error sending event', { error }); | |
| 200 | 200 | } | |
| 201 | 201 | }; | |
| 202 | 202 | ||
| @@ -208,10 +208,10 @@ export const GET: RequestHandler = async ({ request, locals, cookies }) => { | |||
| 208 | 208 | request.signal.addEventListener('abort', () => { | |
| 209 | 209 | if (onSSE) { | |
| 210 | 210 | sseEmitter.removeListener('sse', onSSE); | |
| 211 | - | console.log('[SSE] Removed listener due to abort signal.'); | |
| 211 | + | log.info('Removed listener due to abort signal'); | |
| 212 | 212 | } | |
| 213 | 213 | }); | |
| 214 | - | console.log('[SSE] Added new listener'); | |
| 214 | + | log.info('Added new listener'); | |
| 215 | 215 | ||
| 216 | 216 | controller.enqueue(encoder.encode('data: Connected\n\n')); | |
| 217 | 217 | ||
| @@ -223,7 +223,7 @@ export const GET: RequestHandler = async ({ request, locals, cookies }) => { | |||
| 223 | 223 | if (keepAliveInterval) clearInterval(keepAliveInterval); | |
| 224 | 224 | if (onSSE) sseEmitter.removeListener('sse', onSSE); | |
| 225 | 225 | const maskedUserId = `${userId.slice(0, 4)}...${userId.slice(-4)}`; | |
| 226 | - | console.log('[SSE] Connection closed:', { userId: maskedUserId, reason }); | |
| 226 | + | log.info('Connection closed', { userId: maskedUserId, reason }); | |
| 227 | 227 | ||
| 228 | 228 | const now = Date.now(); | |
| 229 | 229 | await db.update(users) | |
| @@ -10,6 +10,9 @@ import db from '$lib/db/db.server'; | |||||
| 10 | import { users } from '$lib/db/schema'; | 10 | import { users } from '$lib/db/schema'; | ||
| 11 | import { eq, and, lt } from 'drizzle-orm'; | 11 | import { eq, and, lt } from 'drizzle-orm'; | ||
| 12 | import { createSafeUser } from '$lib/types/chat'; | 12 | import { createSafeUser } from '$lib/types/chat'; | ||
| 13 | + | import { createLogger } from '$lib/utils/logger.server'; | |||
| 14 | + | ||||
| 15 | + | const log = createLogger('sse-server'); | |||
| 13 | 16 | ||||
| 14 | console.log('[SSE] Server initialized'); | 17 | console.log('[SSE] Server initialized'); | ||
| 15 | 18 | ||||
| @@ -35,7 +38,7 @@ function startTimeoutCheck() { | |||||
| 35 | 38 | ||||
| 36 | timeoutInterval = setInterval(async () => { | 39 | timeoutInterval = setInterval(async () => { | ||
| 37 | const timeoutThreshold = Date.now() - ONLINE_TIMEOUT_MS; | 40 | const timeoutThreshold = Date.now() - ONLINE_TIMEOUT_MS; | ||
| 38 | - | console.log('[SSE] Checking for timed out users', { | 41 | + | log.debug('Checking for timed out users', { |
| 39 | checkTime: new Date().toISOString(), | 42 | checkTime: new Date().toISOString(), | ||
| 40 | timeoutThreshold: new Date(timeoutThreshold).toISOString() | 43 | timeoutThreshold: new Date(timeoutThreshold).toISOString() | ||
| 41 | }); | 44 | }); | ||
| @@ -60,13 +63,13 @@ function startTimeoutCheck() { | |||||
| 60 | }); | 63 | }); | ||
| 61 | 64 | ||||
| 62 | if (result.length > 0) { | 65 | if (result.length > 0) { | ||
| 63 | - | console.log('[SSE] Found timed out users:', result.map(user => ({ | 66 | + | log.info('User status updated to offline', { |
| 64 | - | nickname: user.nickname, | 67 | + | usersUpdated: result.length, |
| 65 | - | lastSeen: new Date(user.lastSeen || 0).toISOString() | 68 | + | timeoutThreshold: new Date(timeoutThreshold).toISOString() |
| 66 | - | }))); | 69 | + | }); |
| 67 | } | 70 | } | ||
| 68 | } catch (error) { | 71 | } catch (error) { | ||
| 69 | - | console.error('[SSE] Error during timeout check:', error); | 72 | + | log.error('Error during timeout check', { error }); |
| 70 | } | 73 | } | ||
| 71 | }, CHECK_INTERVAL_MS); | 74 | }, CHECK_INTERVAL_MS); | ||
| 72 | } | 75 | } | ||
| @@ -81,7 +84,7 @@ function startBuddyListUpdateInterval() { | |||||
| 81 | const now = Date.now(); | 84 | const now = Date.now(); | ||
| 82 | // Only fetch and broadcast if 10 seconds have passed since last update | 85 | // Only fetch and broadcast if 10 seconds have passed since last update | ||
| 83 | if (now - lastBuddyListUpdate >= BUDDY_LIST_BROADCAST_INTERVAL) { | 86 | if (now - lastBuddyListUpdate >= BUDDY_LIST_BROADCAST_INTERVAL) { | ||
| 84 | - | console.log('[SSE] Broadcasting buddy list update...'); | 87 | + | log.debug('Broadcasting buddy list update...'); |
| 85 | // Query the complete buddy list from the database. | 88 | // Query the complete buddy list from the database. | ||
| 86 | const buddyList = await db.select().from(users); | 89 | const buddyList = await db.select().from(users); | ||
| 87 | // Use createSafeUser to properly sanitize user data | 90 | // Use createSafeUser to properly sanitize user data | ||
| @@ -98,7 +101,7 @@ function startBuddyListUpdateInterval() { | |||||
| 98 | lastBuddyListUpdate = now; | 101 | lastBuddyListUpdate = now; | ||
| 99 | } | 102 | } | ||
| 100 | } catch (error) { | 103 | } catch (error) { | ||
| 101 | - | console.error('[SSE] Error fetching buddy list for update:', error); | 104 | + | log.error('Error fetching buddy list for update', { error }); |
| 102 | } | 105 | } | ||
| 103 | }, 1000); // Check every second but only broadcast every 10 seconds | 106 | }, 1000); // Check every second but only broadcast every 10 seconds | ||
| 104 | } | 107 | } | ||
| @@ -108,9 +111,9 @@ if (!globalThis.__sseIntervalsStarted) { | |||||
| 108 | startTimeoutCheck(); | 111 | startTimeoutCheck(); | ||
| 109 | startBuddyListUpdateInterval(); | 112 | startBuddyListUpdateInterval(); | ||
| 110 | globalThis.__sseIntervalsStarted = true; | 113 | globalThis.__sseIntervalsStarted = true; | ||
| 111 | - | console.log('[SSE] Started global intervals for timeout and buddy list updates.'); | 114 | + | log.info('Started global intervals for timeout and buddy list updates'); |
| 112 | } else { | 115 | } else { | ||
| 113 | - | console.log('[SSE] Global intervals already started, skipping initialization.'); | 116 | + | log.debug('Global intervals already started, skipping initialization'); |
| 114 | } | 117 | } | ||
| 115 | 118 | ||||
| 116 | // Ensure the interval is cleared if the module is reloaded | 119 | // Ensure the interval is cleared if the module is reloaded | ||
| @@ -134,17 +137,14 @@ if (typeof process !== 'undefined') { | |||||
| 134 | }); | 137 | }); | ||
| 135 | } | 138 | } | ||
| 136 | 139 | ||||
| 137 | - | export const GET: RequestHandler = async ({ request, locals, cookies }) => { | 140 | + | export const GET: RequestHandler = async ({ request, locals, }) => { |
| 138 | - | console.log('[SSE] Connection attempt:', { | 141 | + | log.debug('Processing SSE request', { |
| 139 | - | hasLocals: !!locals, | 142 | + | url: request.url, |
| 140 | - | hasUser: !!locals.user, | 143 | + | userId: locals.user?.id |
| 141 | - | userId: locals.user?.id, | |||
| 142 | - | hasSessionCookie: !!cookies.get('session'), | |||
| 143 | - | headers: Object.fromEntries(request.headers.entries()) | |||
| 144 | }); | 144 | }); | ||
| 145 | 145 | ||||
| 146 | if (!locals.user) { | 146 | if (!locals.user) { | ||
| 147 | - | console.log('[SSE] Authentication failed - no user in locals'); | 147 | + | log.warn('Authentication failed - no user in locals'); |
| 148 | return new Response('Unauthorized', { | 148 | return new Response('Unauthorized', { | ||
| 149 | status: 401, | 149 | status: 401, | ||
| 150 | headers: { | 150 | headers: { | ||
| @@ -168,7 +168,7 @@ export const GET: RequestHandler = async ({ request, locals, cookies }) => { | |||||
| 168 | }) | 168 | }) | ||
| 169 | .where(eq(users.id, userId)); | 169 | .where(eq(users.id, userId)); | ||
| 170 | 170 | ||||
| 171 | - | console.log('[SSE] User status updated to online:', { | 171 | + | log.info('User status updated to online', { |
| 172 | userId: `${userId.slice(0, 4)}...${userId.slice(-4)}`, | 172 | userId: `${userId.slice(0, 4)}...${userId.slice(-4)}`, | ||
| 173 | timestamp: new Date(now).toISOString() | 173 | timestamp: new Date(now).toISOString() | ||
| 174 | }); | 174 | }); | ||
| @@ -182,7 +182,7 @@ export const GET: RequestHandler = async ({ request, locals, cookies }) => { | |||||
| 182 | } | 182 | } | ||
| 183 | }); | 183 | }); | ||
| 184 | } catch (error) { | 184 | } catch (error) { | ||
| 185 | - | console.error('[SSE] Error updating user status:', error); | 185 | + | log.error('Error updating user status', { error }); |
| 186 | } | 186 | } | ||
| 187 | 187 | ||||
| 188 | const stream = new ReadableStream({ | 188 | const stream = new ReadableStream({ | ||
| @@ -193,10 +193,10 @@ export const GET: RequestHandler = async ({ request, locals, cookies }) => { | |||||
| 193 | `data: ${JSON.stringify(event.data)}\n\n`; | 193 | `data: ${JSON.stringify(event.data)}\n\n`; | ||
| 194 | controller.enqueue(encoder.encode(payload)); | 194 | controller.enqueue(encoder.encode(payload)); | ||
| 195 | if (!['userStatusUpdate', 'chatMessage'].includes(event.type)) { | 195 | if (!['userStatusUpdate', 'chatMessage'].includes(event.type)) { | ||
| 196 | - | console.log('[SSE] Event sent:', { type: event.type }); | 196 | + | log.info('Event sent', { type: event.type }); |
| 197 | } | 197 | } | ||
| 198 | - | } catch { | 198 | + | } catch (error) { |
| 199 | - | console.log('[SSE] Error sending event'); | 199 | + | log.error('Error sending event', { error }); |
| 200 | } | 200 | } | ||
| 201 | }; | 201 | }; | ||
| 202 | 202 | ||||
| @@ -208,10 +208,10 @@ export const GET: RequestHandler = async ({ request, locals, cookies }) => { | |||||
| 208 | request.signal.addEventListener('abort', () => { | 208 | request.signal.addEventListener('abort', () => { | ||
| 209 | if (onSSE) { | 209 | if (onSSE) { | ||
| 210 | sseEmitter.removeListener('sse', onSSE); | 210 | sseEmitter.removeListener('sse', onSSE); | ||
| 211 | - | console.log('[SSE] Removed listener due to abort signal.'); | 211 | + | log.info('Removed listener due to abort signal'); |
| 212 | } | 212 | } | ||
| 213 | }); | 213 | }); | ||
| 214 | - | console.log('[SSE] Added new listener'); | 214 | + | log.info('Added new listener'); |
| 215 | 215 | ||||
| 216 | controller.enqueue(encoder.encode('data: Connected\n\n')); | 216 | controller.enqueue(encoder.encode('data: Connected\n\n')); | ||
| 217 | 217 | ||||
| @@ -223,7 +223,7 @@ export const GET: RequestHandler = async ({ request, locals, cookies }) => { | |||||
| 223 | if (keepAliveInterval) clearInterval(keepAliveInterval); | 223 | if (keepAliveInterval) clearInterval(keepAliveInterval); | ||
| 224 | if (onSSE) sseEmitter.removeListener('sse', onSSE); | 224 | if (onSSE) sseEmitter.removeListener('sse', onSSE); | ||
| 225 | const maskedUserId = `${userId.slice(0, 4)}...${userId.slice(-4)}`; | 225 | const maskedUserId = `${userId.slice(0, 4)}...${userId.slice(-4)}`; | ||
| 226 | - | console.log('[SSE] Connection closed:', { userId: maskedUserId, reason }); | 226 | + | log.info('Connection closed', { userId: maskedUserId, reason }); |
| 227 | 227 | ||||
| 228 | const now = Date.now(); | 228 | const now = Date.now(); | ||
| 229 | await db.update(users) | 229 | await db.update(users) | ||
M src/routes/api/status/+server.ts +11 -3
| @@ -5,21 +5,26 @@ import { eq } from 'drizzle-orm'; | |||
| 5 | 5 | import { validateSessionToken, generateSessionToken, createSession } from '$lib/api/session.server'; | |
| 6 | 6 | import { setSessionTokenCookie } from '$lib/api/session.cookie'; | |
| 7 | 7 | import { createSafeUser } from '$lib/types/chat'; | |
| 8 | + | import { createLogger } from '$lib/utils/logger.server'; | |
| 9 | + | ||
| 10 | + | const log = createLogger('status-server'); | |
| 8 | 11 | ||
| 9 | 12 | export async function POST({ request, cookies, locals }) { | |
| 10 | 13 | if (!locals.user) { | |
| 14 | + | log.warn('Authentication required'); | |
| 11 | 15 | throw error(401, 'Authentication required'); | |
| 12 | 16 | } | |
| 13 | 17 | ||
| 14 | 18 | const { status } = await request.json(); | |
| 15 | 19 | const validStatuses = ['online', 'away', 'busy', 'offline']; | |
| 16 | 20 | if (!validStatuses.includes(status)) { | |
| 21 | + | log.warn('Invalid status received', { status }); | |
| 17 | 22 | throw error(400, 'Invalid status'); | |
| 18 | 23 | } | |
| 19 | 24 | ||
| 20 | 25 | const userId = locals.user.id; | |
| 21 | 26 | const maskedUserId = `${userId.slice(0, 4)}...${userId.slice(-4)}`; | |
| 22 | - | console.log('[Status] Updating user status:', { userId: maskedUserId, status }); | |
| 27 | + | log.debug('Updating user status', { userId: maskedUserId, status }); | |
| 23 | 28 | ||
| 24 | 29 | const now = Date.now(); | |
| 25 | 30 | ||
| @@ -33,7 +38,7 @@ export async function POST({ request, cookies, locals }) { | |||
| 33 | 38 | .where(eq(users.id, userId)) | |
| 34 | 39 | .returning() | |
| 35 | 40 | .get(); | |
| 36 | - | console.log('[Status] Database updated successfully'); | |
| 41 | + | log.debug('Database updated successfully', { userId: maskedUserId, status }); | |
| 37 | 42 | ||
| 38 | 43 | // Renew session if status is online and session is near expiry | |
| 39 | 44 | if (status === 'online') { | |
| @@ -47,7 +52,10 @@ export async function POST({ request, cookies, locals }) { | |||
| 47 | 52 | const newToken = generateSessionToken(); | |
| 48 | 53 | const newSession = await createSession(newToken, userId); | |
| 49 | 54 | setSessionTokenCookie({ cookies }, newToken, newSession.expiresAt); | |
| 50 | - | console.log('[Status] Session renewed:', { userId: maskedUserId, expiresAt: new Date(newSession.expiresAt).toISOString() }); | |
| 55 | + | log.info('Session renewed', { | |
| 56 | + | userId: maskedUserId, | |
| 57 | + | expiresAt: new Date(newSession.expiresAt).toISOString() | |
| 58 | + | }); | |
| 51 | 59 | } | |
| 52 | 60 | } | |
| 53 | 61 | } | |
| @@ -5,21 +5,26 @@ import { eq } from 'drizzle-orm'; | |||||
| 5 | import { validateSessionToken, generateSessionToken, createSession } from '$lib/api/session.server'; | 5 | import { validateSessionToken, generateSessionToken, createSession } from '$lib/api/session.server'; | ||
| 6 | import { setSessionTokenCookie } from '$lib/api/session.cookie'; | 6 | import { setSessionTokenCookie } from '$lib/api/session.cookie'; | ||
| 7 | import { createSafeUser } from '$lib/types/chat'; | 7 | import { createSafeUser } from '$lib/types/chat'; | ||
| 8 | + | import { createLogger } from '$lib/utils/logger.server'; | |||
| 9 | + | ||||
| 10 | + | const log = createLogger('status-server'); | |||
| 8 | 11 | ||||
| 9 | export async function POST({ request, cookies, locals }) { | 12 | export async function POST({ request, cookies, locals }) { | ||
| 10 | if (!locals.user) { | 13 | if (!locals.user) { | ||
| 14 | + | log.warn('Authentication required'); | |||
| 11 | throw error(401, 'Authentication required'); | 15 | throw error(401, 'Authentication required'); | ||
| 12 | } | 16 | } | ||
| 13 | 17 | ||||
| 14 | const { status } = await request.json(); | 18 | const { status } = await request.json(); | ||
| 15 | const validStatuses = ['online', 'away', 'busy', 'offline']; | 19 | const validStatuses = ['online', 'away', 'busy', 'offline']; | ||
| 16 | if (!validStatuses.includes(status)) { | 20 | if (!validStatuses.includes(status)) { | ||
| 21 | + | log.warn('Invalid status received', { status }); | |||
| 17 | throw error(400, 'Invalid status'); | 22 | throw error(400, 'Invalid status'); | ||
| 18 | } | 23 | } | ||
| 19 | 24 | ||||
| 20 | const userId = locals.user.id; | 25 | const userId = locals.user.id; | ||
| 21 | const maskedUserId = `${userId.slice(0, 4)}...${userId.slice(-4)}`; | 26 | const maskedUserId = `${userId.slice(0, 4)}...${userId.slice(-4)}`; | ||
| 22 | - | console.log('[Status] Updating user status:', { userId: maskedUserId, status }); | 27 | + | log.debug('Updating user status', { userId: maskedUserId, status }); |
| 23 | 28 | ||||
| 24 | const now = Date.now(); | 29 | const now = Date.now(); | ||
| 25 | 30 | ||||
| @@ -33,7 +38,7 @@ export async function POST({ request, cookies, locals }) { | |||||
| 33 | .where(eq(users.id, userId)) | 38 | .where(eq(users.id, userId)) | ||
| 34 | .returning() | 39 | .returning() | ||
| 35 | .get(); | 40 | .get(); | ||
| 36 | - | console.log('[Status] Database updated successfully'); | 41 | + | log.debug('Database updated successfully', { userId: maskedUserId, status }); |
| 37 | 42 | ||||
| 38 | // Renew session if status is online and session is near expiry | 43 | // Renew session if status is online and session is near expiry | ||
| 39 | if (status === 'online') { | 44 | if (status === 'online') { | ||
| @@ -47,7 +52,10 @@ export async function POST({ request, cookies, locals }) { | |||||
| 47 | const newToken = generateSessionToken(); | 52 | const newToken = generateSessionToken(); | ||
| 48 | const newSession = await createSession(newToken, userId); | 53 | const newSession = await createSession(newToken, userId); | ||
| 49 | setSessionTokenCookie({ cookies }, newToken, newSession.expiresAt); | 54 | setSessionTokenCookie({ cookies }, newToken, newSession.expiresAt); | ||
| 50 | - | console.log('[Status] Session renewed:', { userId: maskedUserId, expiresAt: new Date(newSession.expiresAt).toISOString() }); | 55 | + | log.info('Session renewed', { |
| 56 | + | userId: maskedUserId, | |||
| 57 | + | expiresAt: new Date(newSession.expiresAt).toISOString() | |||
| 58 | + | }); | |||
| 51 | } | 59 | } | ||
| 52 | } | 60 | } | ||
| 53 | } | 61 | } | ||
M src/routes/api/users/[userId]/+server.ts +12 -5
| @@ -4,10 +4,13 @@ import { users } from '$lib/db/schema'; | |||
| 4 | 4 | import { eq } from 'drizzle-orm'; | |
| 5 | 5 | import type { RequestHandler } from './$types'; | |
| 6 | 6 | import { createSafeUser } from '$lib/types/chat'; | |
| 7 | + | import { createLogger } from '$lib/utils/logger.server'; | |
| 8 | + | ||
| 9 | + | const log = createLogger('users-server'); | |
| 7 | 10 | ||
| 8 | 11 | export const GET: RequestHandler = async ({ params }) => { | |
| 9 | 12 | const maskedUserId = `${params.userId.slice(0, 4)}...${params.userId.slice(-4)}`; | |
| 10 | - | console.log('[Users] Fetching user data:', { userId: maskedUserId }); | |
| 13 | + | log.debug('Fetching user data', { userId: maskedUserId }); | |
| 11 | 14 | ||
| 12 | 15 | try { | |
| 13 | 16 | const user = await db.select() | |
| @@ -16,23 +19,27 @@ export const GET: RequestHandler = async ({ params }) => { | |||
| 16 | 19 | .get(); | |
| 17 | 20 | ||
| 18 | 21 | if (!user) { | |
| 19 | - | console.log('[Users] User not found:', { userId: maskedUserId }); | |
| 22 | + | log.warn('User not found', { userId: maskedUserId }); | |
| 20 | 23 | throw error(404, 'User not found'); | |
| 21 | 24 | } | |
| 22 | 25 | ||
| 23 | 26 | const safeUser = createSafeUser(user); | |
| 24 | 27 | ||
| 25 | - | console.log('[Users] User data retrieved:', { | |
| 28 | + | log.debug('User data retrieved', { | |
| 26 | 29 | userId: maskedUserId, | |
| 27 | 30 | status: safeUser.status, | |
| 28 | 31 | lastSeen: safeUser.lastSeen ? new Date(safeUser.lastSeen).toISOString() : null | |
| 29 | 32 | }); | |
| 33 | + | ||
| 30 | 34 | return json({ | |
| 31 | 35 | success: true, | |
| 32 | 36 | user: safeUser | |
| 33 | 37 | }); | |
| 34 | - | } catch { | |
| 35 | - | console.log('[Users] Error fetching user data:', { userId: maskedUserId }); | |
| 38 | + | } catch (err) { | |
| 39 | + | log.error('Error fetching user data', { | |
| 40 | + | userId: maskedUserId, | |
| 41 | + | error: err instanceof Error ? err.message : 'Unknown error' | |
| 42 | + | }); | |
| 36 | 43 | throw error(500, 'Failed to fetch user information'); | |
| 37 | 44 | } | |
| 38 | 45 | };⏎̸ | |
| @@ -4,10 +4,13 @@ import { users } from '$lib/db/schema'; | |||||
| 4 | import { eq } from 'drizzle-orm'; | 4 | import { eq } from 'drizzle-orm'; | ||
| 5 | import type { RequestHandler } from './$types'; | 5 | import type { RequestHandler } from './$types'; | ||
| 6 | import { createSafeUser } from '$lib/types/chat'; | 6 | import { createSafeUser } from '$lib/types/chat'; | ||
| 7 | + | import { createLogger } from '$lib/utils/logger.server'; | |||
| 8 | + | ||||
| 9 | + | const log = createLogger('users-server'); | |||
| 7 | 10 | ||||
| 8 | export const GET: RequestHandler = async ({ params }) => { | 11 | export const GET: RequestHandler = async ({ params }) => { | ||
| 9 | const maskedUserId = `${params.userId.slice(0, 4)}...${params.userId.slice(-4)}`; | 12 | const maskedUserId = `${params.userId.slice(0, 4)}...${params.userId.slice(-4)}`; | ||
| 10 | - | console.log('[Users] Fetching user data:', { userId: maskedUserId }); | 13 | + | log.debug('Fetching user data', { userId: maskedUserId }); |
| 11 | 14 | ||||
| 12 | try { | 15 | try { | ||
| 13 | const user = await db.select() | 16 | const user = await db.select() | ||
| @@ -16,23 +19,27 @@ export const GET: RequestHandler = async ({ params }) => { | |||||
| 16 | .get(); | 19 | .get(); | ||
| 17 | 20 | ||||
| 18 | if (!user) { | 21 | if (!user) { | ||
| 19 | - | console.log('[Users] User not found:', { userId: maskedUserId }); | 22 | + | log.warn('User not found', { userId: maskedUserId }); |
| 20 | throw error(404, 'User not found'); | 23 | throw error(404, 'User not found'); | ||
| 21 | } | 24 | } | ||
| 22 | 25 | ||||
| 23 | const safeUser = createSafeUser(user); | 26 | const safeUser = createSafeUser(user); | ||
| 24 | 27 | ||||
| 25 | - | console.log('[Users] User data retrieved:', { | 28 | + | log.debug('User data retrieved', { |
| 26 | userId: maskedUserId, | 29 | userId: maskedUserId, | ||
| 27 | status: safeUser.status, | 30 | status: safeUser.status, | ||
| 28 | lastSeen: safeUser.lastSeen ? new Date(safeUser.lastSeen).toISOString() : null | 31 | lastSeen: safeUser.lastSeen ? new Date(safeUser.lastSeen).toISOString() : null | ||
| 29 | }); | 32 | }); | ||
| 33 | + | ||||
| 30 | return json({ | 34 | return json({ | ||
| 31 | success: true, | 35 | success: true, | ||
| 32 | user: safeUser | 36 | user: safeUser | ||
| 33 | }); | 37 | }); | ||
| 34 | - | } catch { | 38 | + | } catch (err) { |
| 35 | - | console.log('[Users] Error fetching user data:', { userId: maskedUserId }); | 39 | + | log.error('Error fetching user data', { |
| 40 | + | userId: maskedUserId, | |||
| 41 | + | error: err instanceof Error ? err.message : 'Unknown error' | |||
| 42 | + | }); | |||
| 36 | throw error(500, 'Failed to fetch user information'); | 43 | throw error(500, 'Failed to fetch user information'); | ||
| 37 | } | 44 | } | ||
| 38 | };⏎̸ | 45 | };⏎̸ | ||
M vite.config.ts +23 -3
| @@ -2,6 +2,19 @@ import { defineConfig } from 'vitest/config'; | |||
| 2 | 2 | import { loadEnv } from 'vite'; | |
| 3 | 3 | import { sveltekit } from '@sveltejs/kit/vite'; | |
| 4 | 4 | ||
| 5 | + | /** | |
| 6 | + | * Logging configuration | |
| 7 | + | * This project uses two logging systems: | |
| 8 | + | * 1. Console logging (development/client-side) - controlled by console stripping | |
| 9 | + | * 2. Pino logging (server-side) - controlled by LOG_LEVEL | |
| 10 | + | * | |
| 11 | + | * LOG_LEVEL affects both systems: | |
| 12 | + | * - error: only error logs | |
| 13 | + | * - warn: warn and error logs | |
| 14 | + | * - info: info, warn, and error logs (default in production) | |
| 15 | + | * - debug: all logs (default in development) | |
| 16 | + | */ | |
| 17 | + | ||
| 5 | 18 | // Define which console methods to strip based on LOG_LEVEL | |
| 6 | 19 | const getConsoleFuncsToStrip = (logLevel: string) => { | |
| 7 | 20 | switch (logLevel?.toLowerCase()) { | |
| @@ -18,9 +31,11 @@ const getConsoleFuncsToStrip = (logLevel: string) => { | |||
| 18 | 31 | }; | |
| 19 | 32 | ||
| 20 | 33 | export default defineConfig(({ mode }) => { | |
| 21 | - | // Load env file based on `mode` value. mode can be 'development', 'production', etc. | |
| 34 | + | // Load env file based on `mode` value | |
| 22 | 35 | const env = loadEnv(mode, process.cwd(), ''); | |
| 23 | - | console.log(`Building with LOG_LEVEL=${env.LOG_LEVEL || 'info'} in ${mode} mode`); | |
| 36 | + | const logLevel = env.LOG_LEVEL || (mode === 'development' ? 'debug' : 'info'); | |
| 37 | + | ||
| 38 | + | console.log(`Building with LOG_LEVEL=${logLevel} in ${mode} mode`); | |
| 24 | 39 | ||
| 25 | 40 | return { | |
| 26 | 41 | plugins: [sveltekit()], | |
| @@ -31,9 +46,14 @@ export default defineConfig(({ mode }) => { | |||
| 31 | 46 | minify: 'terser', | |
| 32 | 47 | terserOptions: { | |
| 33 | 48 | compress: { | |
| 34 | - | pure_funcs: getConsoleFuncsToStrip(env.LOG_LEVEL || 'info') | |
| 49 | + | // Strip console methods based on LOG_LEVEL | |
| 50 | + | pure_funcs: getConsoleFuncsToStrip(logLevel) | |
| 35 | 51 | } | |
| 36 | 52 | }, | |
| 53 | + | }, | |
| 54 | + | // Make LOG_LEVEL available to client-side code | |
| 55 | + | define: { | |
| 56 | + | 'process.env.LOG_LEVEL': JSON.stringify(logLevel) | |
| 37 | 57 | } | |
| 38 | 58 | }; | |
| 39 | 59 | }); | |
| @@ -2,6 +2,19 @@ import { defineConfig } from 'vitest/config'; | |||||
| 2 | import { loadEnv } from 'vite'; | 2 | import { loadEnv } from 'vite'; | ||
| 3 | import { sveltekit } from '@sveltejs/kit/vite'; | 3 | import { sveltekit } from '@sveltejs/kit/vite'; | ||
| 4 | 4 | ||||
| 5 | + | /** | |||
| 6 | + | * Logging configuration | |||
| 7 | + | * This project uses two logging systems: | |||
| 8 | + | * 1. Console logging (development/client-side) - controlled by console stripping | |||
| 9 | + | * 2. Pino logging (server-side) - controlled by LOG_LEVEL | |||
| 10 | + | * | |||
| 11 | + | * LOG_LEVEL affects both systems: | |||
| 12 | + | * - error: only error logs | |||
| 13 | + | * - warn: warn and error logs | |||
| 14 | + | * - info: info, warn, and error logs (default in production) | |||
| 15 | + | * - debug: all logs (default in development) | |||
| 16 | + | */ | |||
| 17 | + | ||||
| 5 | // Define which console methods to strip based on LOG_LEVEL | 18 | // Define which console methods to strip based on LOG_LEVEL | ||
| 6 | const getConsoleFuncsToStrip = (logLevel: string) => { | 19 | const getConsoleFuncsToStrip = (logLevel: string) => { | ||
| 7 | switch (logLevel?.toLowerCase()) { | 20 | switch (logLevel?.toLowerCase()) { | ||
| @@ -18,9 +31,11 @@ const getConsoleFuncsToStrip = (logLevel: string) => { | |||||
| 18 | }; | 31 | }; | ||
| 19 | 32 | ||||
| 20 | export default defineConfig(({ mode }) => { | 33 | export default defineConfig(({ mode }) => { | ||
| 21 | - | // Load env file based on `mode` value. mode can be 'development', 'production', etc. | 34 | + | // Load env file based on `mode` value |
| 22 | const env = loadEnv(mode, process.cwd(), ''); | 35 | const env = loadEnv(mode, process.cwd(), ''); | ||
| 23 | - | console.log(`Building with LOG_LEVEL=${env.LOG_LEVEL || 'info'} in ${mode} mode`); | 36 | + | const logLevel = env.LOG_LEVEL || (mode === 'development' ? 'debug' : 'info'); |
| 37 | + | ||||
| 38 | + | console.log(`Building with LOG_LEVEL=${logLevel} in ${mode} mode`); | |||
| 24 | 39 | ||||
| 25 | return { | 40 | return { | ||
| 26 | plugins: [sveltekit()], | 41 | plugins: [sveltekit()], | ||
| @@ -31,9 +46,14 @@ export default defineConfig(({ mode }) => { | |||||
| 31 | minify: 'terser', | 46 | minify: 'terser', | ||
| 32 | terserOptions: { | 47 | terserOptions: { | ||
| 33 | compress: { | 48 | compress: { | ||
| 34 | - | pure_funcs: getConsoleFuncsToStrip(env.LOG_LEVEL || 'info') | 49 | + | // Strip console methods based on LOG_LEVEL |
| 50 | + | pure_funcs: getConsoleFuncsToStrip(logLevel) | |||
| 35 | } | 51 | } | ||
| 36 | }, | 52 | }, | ||
| 53 | + | }, | |||
| 54 | + | // Make LOG_LEVEL available to client-side code | |||
| 55 | + | define: { | |||
| 56 | + | 'process.env.LOG_LEVEL': JSON.stringify(logLevel) | |||
| 37 | } | 57 | } | ||
| 38 | }; | 58 | }; | ||
| 39 | }); | 59 | }); | ||