diff --git a/next.config.js b/next.config.js index 9bfe4a0..6184327 100755 --- a/next.config.js +++ b/next.config.js @@ -5,6 +5,8 @@ await import("./src/env.js"); /** @type {import("next").NextConfig} */ -const config = {}; +const config = { + +}; export default config; diff --git a/package.json b/package.json index 0d6f7a2..3cd97a0 100755 --- a/package.json +++ b/package.json @@ -20,16 +20,20 @@ "drizzle-orm": "^0.33.0", "geist": "^1.3.1", "next": "^14.2.15", + "pg": "^8.13.0", "postgres": "^3.4.4", "react": "^18.3.1", "react-dom": "^18.3.1", + "socket.io": "^4.8.0", "zod": "^3.23.8" }, "devDependencies": { "@types/eslint": "^8.56.12", "@types/node": "^20.16.13", + "@types/pg": "^8.11.10", "@types/react": "^18.3.11", "@types/react-dom": "^18.3.1", + "@types/socket.io": "^3.0.2", "@typescript-eslint/eslint-plugin": "^8.10.0", "@typescript-eslint/parser": "^8.10.0", "drizzle-kit": "^0.24.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9dd2725..b658ed9 100755 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,13 +13,16 @@ importers: version: 0.10.1(typescript@5.6.3)(zod@3.23.8) drizzle-orm: specifier: ^0.33.0 - version: 0.33.0(@types/react@18.3.11)(postgres@3.4.4)(react@18.3.1) + version: 0.33.0(@types/pg@8.11.10)(@types/react@18.3.11)(pg@8.13.0)(postgres@3.4.4)(react@18.3.1) geist: specifier: ^1.3.1 version: 1.3.1(next@14.2.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) next: specifier: ^14.2.15 version: 14.2.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + pg: + specifier: ^8.13.0 + version: 8.13.0 postgres: specifier: ^3.4.4 version: 3.4.4 @@ -29,6 +32,9 @@ importers: react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + socket.io: + specifier: ^4.8.0 + version: 4.8.0 zod: specifier: ^3.23.8 version: 3.23.8 @@ -39,12 +45,18 @@ importers: '@types/node': specifier: ^20.16.13 version: 20.16.13 + '@types/pg': + specifier: ^8.11.10 + version: 8.11.10 '@types/react': specifier: ^18.3.11 version: 18.3.11 '@types/react-dom': specifier: ^18.3.1 version: 18.3.1 + '@types/socket.io': + specifier: ^3.0.2 + version: 3.0.2 '@typescript-eslint/eslint-plugin': specifier: ^8.10.0 version: 8.10.0(@typescript-eslint/parser@8.10.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3) @@ -505,6 +517,9 @@ packages: '@rushstack/eslint-patch@1.10.4': resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==} + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} @@ -529,6 +544,12 @@ packages: typescript: optional: true + '@types/cookie@0.4.1': + resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} + + '@types/cors@2.8.17': + resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} + '@types/eslint@8.56.12': resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==} @@ -544,6 +565,9 @@ packages: '@types/node@20.16.13': resolution: {integrity: sha512-GjQ7im10B0labo8ZGXDGROUl9k0BNyDgzfGpb4g/cl+4yYDWVKcozANF4FGr4/p0O/rAkQClM6Wiwkije++1Tg==} + '@types/pg@8.11.10': + resolution: {integrity: sha512-LczQUW4dbOQzsH2RQ5qoeJ6qJPdrcM/DcMLoqWQkMLMsq83J5lAX3LXjdkWdpscFy67JSOWDnh7Ny/sPFykmkg==} + '@types/prop-types@15.7.13': resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==} @@ -553,6 +577,10 @@ packages: '@types/react@18.3.11': resolution: {integrity: sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==} + '@types/socket.io@3.0.2': + resolution: {integrity: sha512-pu0sN9m5VjCxBZVK8hW37ZcMe8rjn4HHggBN5CbaRTvFwv5jOmuIRZEuddsBPa9Th0ts0SIo3Niukq+95cMBbQ==} + deprecated: This is a stub types definition. socket.io provides its own type definitions, so you do not need this installed. + '@typescript-eslint/eslint-plugin@8.10.0': resolution: {integrity: sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -613,6 +641,10 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -709,6 +741,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64id@2.0.0: + resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} + engines: {node: ^4.5.0 || >= 5.9} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -770,6 +806,14 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -941,6 +985,14 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + engine.io@6.6.2: + resolution: {integrity: sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==} + engines: {node: '>=10.2.0'} + enhanced-resolve@5.17.1: resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} engines: {node: '>=10.13.0'} @@ -1475,6 +1527,14 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -1503,6 +1563,10 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + next@14.2.15: resolution: {integrity: sha512-h9ctmOokpoDphRvMGnwOJAedT6zKhwqyZML9mDtspgf4Rh3Pn7UTYKqePNoDvhsWBAO5GoPNYshnAUGIazVGmw==} engines: {node: '>=18.17.0'} @@ -1561,6 +1625,9 @@ packages: resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} + obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -1602,6 +1669,48 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + + pg-connection-string@2.7.0: + resolution: {integrity: sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-numeric@1.0.2: + resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} + engines: {node: '>=4'} + + pg-pool@3.7.0: + resolution: {integrity: sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.7.0: + resolution: {integrity: sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg-types@4.0.2: + resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==} + engines: {node: '>=10'} + + pg@8.13.0: + resolution: {integrity: sha512-34wkUTh3SxTClfoHB3pQ7bIMvw9dpFU1audQQeZG837fmHfHpr14n/AELVDoOYVDW2h5RDWU78tFjkD+erSBsw==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1666,6 +1775,41 @@ packages: resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-array@3.0.2: + resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==} + engines: {node: '>=12'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-bytea@3.0.0: + resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==} + engines: {node: '>= 6'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-date@2.1.0: + resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==} + engines: {node: '>=12'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + postgres-interval@3.0.0: + resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==} + engines: {node: '>=12'} + + postgres-range@1.1.4: + resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==} + postgres@3.4.4: resolution: {integrity: sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==} engines: {node: '>=12'} @@ -1842,6 +1986,17 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + socket.io-adapter@2.5.5: + resolution: {integrity: sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + + socket.io@4.8.0: + resolution: {integrity: sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==} + engines: {node: '>=10.2.0'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1853,6 +2008,10 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -2008,6 +2167,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} @@ -2043,6 +2206,22 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + yaml@2.6.0: resolution: {integrity: sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==} engines: {node: '>= 14'} @@ -2321,6 +2500,8 @@ snapshots: '@rushstack/eslint-patch@1.10.4': {} + '@socket.io/component-emitter@3.1.2': {} + '@swc/counter@0.1.3': {} '@swc/helpers@0.5.5': @@ -2341,6 +2522,12 @@ snapshots: optionalDependencies: typescript: 5.6.3 + '@types/cookie@0.4.1': {} + + '@types/cors@2.8.17': + dependencies: + '@types/node': 20.16.13 + '@types/eslint@8.56.12': dependencies: '@types/estree': 1.0.6 @@ -2356,6 +2543,12 @@ snapshots: dependencies: undici-types: 6.19.8 + '@types/pg@8.11.10': + dependencies: + '@types/node': 20.16.13 + pg-protocol: 1.7.0 + pg-types: 4.0.2 + '@types/prop-types@15.7.13': {} '@types/react-dom@18.3.1': @@ -2367,6 +2560,14 @@ snapshots: '@types/prop-types': 15.7.13 csstype: 3.1.3 + '@types/socket.io@3.0.2': + dependencies: + socket.io: 4.8.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + '@typescript-eslint/eslint-plugin@8.10.0(@typescript-eslint/parser@8.10.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)': dependencies: '@eslint-community/regexpp': 4.11.1 @@ -2450,6 +2651,11 @@ snapshots: '@ungap/structured-clone@1.2.0': {} + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + acorn-jsx@5.3.2(acorn@8.13.0): dependencies: acorn: 8.13.0 @@ -2563,6 +2769,8 @@ snapshots: balanced-match@1.0.2: {} + base64id@2.0.0: {} + binary-extensions@2.3.0: {} brace-expansion@1.1.11: @@ -2627,6 +2835,13 @@ snapshots: concat-map@0.0.1: {} + cookie@0.7.2: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 @@ -2700,9 +2915,11 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.33.0(@types/react@18.3.11)(postgres@3.4.4)(react@18.3.1): + drizzle-orm@0.33.0(@types/pg@8.11.10)(@types/react@18.3.11)(pg@8.13.0)(postgres@3.4.4)(react@18.3.1): optionalDependencies: + '@types/pg': 8.11.10 '@types/react': 18.3.11 + pg: 8.13.0 postgres: 3.4.4 react: 18.3.1 @@ -2712,6 +2929,25 @@ snapshots: emoji-regex@9.2.2: {} + engine.io-parser@5.2.3: {} + + engine.io@6.6.2: + dependencies: + '@types/cookie': 0.4.1 + '@types/cors': 2.8.17 + '@types/node': 20.16.13 + accepts: 1.3.8 + base64id: 2.0.0 + cookie: 0.7.2 + cors: 2.8.5 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + enhanced-resolve@5.17.1: dependencies: graceful-fs: 4.2.11 @@ -3444,6 +3680,12 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -3468,6 +3710,8 @@ snapshots: natural-compare@1.4.0: {} + negotiator@0.6.3: {} + next@14.2.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@next/env': 14.2.15 @@ -3535,6 +3779,8 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 + obuf@1.1.2: {} + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -3575,6 +3821,53 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + pg-cloudflare@1.1.1: + optional: true + + pg-connection-string@2.7.0: {} + + pg-int8@1.0.1: {} + + pg-numeric@1.0.2: {} + + pg-pool@3.7.0(pg@8.13.0): + dependencies: + pg: 8.13.0 + + pg-protocol@1.7.0: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + + pg-types@4.0.2: + dependencies: + pg-int8: 1.0.1 + pg-numeric: 1.0.2 + postgres-array: 3.0.2 + postgres-bytea: 3.0.0 + postgres-date: 2.1.0 + postgres-interval: 3.0.0 + postgres-range: 1.1.4 + + pg@8.13.0: + dependencies: + pg-connection-string: 2.7.0 + pg-pool: 3.7.0(pg@8.13.0) + pg-protocol: 1.7.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -3628,6 +3921,28 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postgres-array@2.0.0: {} + + postgres-array@3.0.2: {} + + postgres-bytea@1.0.0: {} + + postgres-bytea@3.0.0: + dependencies: + obuf: 1.1.2 + + postgres-date@1.0.7: {} + + postgres-date@2.1.0: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + + postgres-interval@3.0.0: {} + + postgres-range@1.1.4: {} + postgres@3.4.4: {} prelude-ls@1.2.1: {} @@ -3763,6 +4078,36 @@ snapshots: signal-exit@4.1.0: {} + socket.io-adapter@2.5.5: + dependencies: + debug: 4.3.7 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + socket.io@4.8.0: + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.5 + debug: 4.3.7 + engine.io: 6.6.2 + socket.io-adapter: 2.5.5 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + source-map-js@1.2.1: {} source-map-support@0.5.21: @@ -3772,6 +4117,8 @@ snapshots: source-map@0.6.1: {} + split2@4.2.0: {} + streamsearch@1.1.0: {} string-width@4.2.3: @@ -3977,6 +4324,8 @@ snapshots: util-deprecate@1.0.2: {} + vary@1.1.2: {} + which-boxed-primitive@1.0.2: dependencies: is-bigint: 1.0.4 @@ -4035,6 +4384,10 @@ snapshots: wrappy@1.0.2: {} + ws@8.17.1: {} + + xtend@4.0.2: {} + yaml@2.6.0: {} yocto-queue@0.1.0: {} diff --git a/src/app/api/relationships/countdown/get/route.ts b/src/app/api/relationships/countdown/get/route.ts new file mode 100644 index 0000000..d85dc24 --- /dev/null +++ b/src/app/api/relationships/countdown/get/route.ts @@ -0,0 +1,37 @@ +'use server'; +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; +import { getCountdown } from '~/server/functions'; +import { middleware } from '~/middleware'; +import type { Countdown } from '~/server/types'; + +export const GET = async (request: NextRequest) => { + const middlewareResponse = await middleware(request); + if (middlewareResponse) return middlewareResponse; + try { + const url = new URL(request.url); + const userId = Number.parseInt(url.searchParams.get('userId') ?? ''); + if (!userId || isNaN(userId)) + return NextResponse.json( + { message: 'Missing userId' }, { status: 400 } + ); + const countdown: Countdown | null = await getCountdown(userId); + if (!countdown) { + return NextResponse.json( + { message: 'No countdown found' }, { status: 404 } + ); + } + return NextResponse.json(countdown); + } catch (error) { + console.error('Error getting countdown:', error); + if (error instanceof Error) { + return NextResponse.json( + { message: error.message }, { status: 500 } + ); + } else { + return NextResponse.json( + { message: 'Unknown error occurred' }, { status: 500 } + ); + } + } +}; diff --git a/src/app/api/relationships/countdown/set/route.ts b/src/app/api/relationships/countdown/set/route.ts new file mode 100644 index 0000000..35ef0e7 --- /dev/null +++ b/src/app/api/relationships/countdown/set/route.ts @@ -0,0 +1,40 @@ +'use server'; +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; +import { setCountdown } from '~/server/functions'; +import { middleware } from '~/middleware'; +import type { Countdown } from '~/server/types'; + +export const POST = async (request: NextRequest) => { + const middlewareResponse = await middleware(request); + if (middlewareResponse) return middlewareResponse; + try { + const body = await request.json() as { + userId: number; + countdown: Countdown; + }; + const { userId, countdown } = body; + if (!userId || !countdown || isNaN(userId)) + return NextResponse.json( + { message: 'Missing userId or countdown' }, { status: 400 } + ); + const newCountdown: Countdown = await setCountdown(userId, countdown); + if (!newCountdown) { + return NextResponse.json( + { message: 'Error setting countdown' }, { status: 500 } + ); + } + return NextResponse.json(newCountdown); + } catch (error) { + console.error('Error setting countdown:', error); + if (error instanceof Error) { + return NextResponse.json( + { message: error.message }, { status: 500 } + ); + } else { + return NextResponse.json( + { message: 'Unknown error occurred' }, { status: 500 } + ); + } + } +}; diff --git a/src/app/api/websocket/messages/route.ts b/src/app/api/websocket/messages/route.ts new file mode 100644 index 0000000..b0e6b62 --- /dev/null +++ b/src/app/api/websocket/messages/route.ts @@ -0,0 +1,80 @@ +// WebSocket for client to receive messages +//import { Server } from 'socket.io'; +//import { Client, Notification } from 'pg'; +//import { NextResponse } from 'next/server'; +//import type { NextRequest } from 'next/server'; +//import type { Message } from '~/server/types'; + +//let isInitialized = false; +//const io = new Server(); + +//const pgClient = new Client({ + //connectionString: process.env.DATABASE_URL, +//}); + +//const initializePostgres = async () => { + //if (isInitialized) return; + //try { + //await pgClient.connect(); + //console.log('Connected to PostgreSQL'); + //await pgClient.query('LISTEN new_message'); + + //pgClient.on('notification', (msg: Notification) => { + //try { + //const newMessage: Message = JSON.parse(msg.payload ?? '{}') as Message; + //const { receiverId, text, id } = newMessage; + //if (receiverId && typeof receiverId === 'number') { + //io.to(receiverId.toString()).emit('message', newMessage); + //console.log(`Message sent to room ${receiverId} with text: ${text}`); + //} else { + //console.error('Invalid receiverId:', receiverId); + //} + //} catch (error) { + //console.error('Error parsing notification payload:', error); + //} + //}); + //isInitialized = true; + //} catch (error) { + //console.error('Error connecting to PostgreSQL:', error); + //throw error; + //} +//}; + +//io.on('connection', (socket) => { + //console.log('WebSocket client connected', socket.id); + + //socket.on('join', async (userId: number) => { + //const roomId = userId.toString(); + //await socket.join(roomId); + //console.log(`WebSocket client joined room ${userId}`); + //}); + + //socket.on('error', (error) => { + //console.error('WebSocket error:', error); + //}); + + //socket.on('disconnect', () => { + //console.log('WebSocket client disconnected'); + //}); +//}); + +//export const runtime = 'edge'; + +//export const GET = async (request: NextRequest) => { + //try { + //await initializePostgres(); + //// @ts-expect-error: Socket.IO types conflict with Next.js Request + //const upgrade = await io.handleUpgrade(request); + + //if (!upgrade || !upgrade.headers) + //throw new Error('Failed to upgrade connection'); + + //return new NextResponse(null, { + //status: 101, + //headers: upgrade.headers, + //}); + //} catch (error) { + //console.error('Error handling upgrade:', error); + //return NextResponse.json({ message: 'Internal server error' }, { status: 500 }); + //} +//}; diff --git a/src/server/functions.ts b/src/server/functions.ts index d5348b8..50f0657 100755 --- a/src/server/functions.ts +++ b/src/server/functions.ts @@ -26,6 +26,29 @@ export const getUser = async (userId: number) => { } }; +export const getRelationship = async (userId: number) => { + try { + const user = await getUser(userId); + if (!user) throw new Error("User not found"); + const userRelationship = await db.select() + .from(schema.userRelationships) + .where(eq(schema.userRelationships.userId, user.id)) + .limit(1) + .then(results => results[0]); + if (!userRelationship) throw new Error('No relationships found for user'); + const relationship = await db.select() + .from(schema.relationships) + .where(eq(schema.relationships.id, userRelationship.relationshipId)) + .limit(1) + .then(results => results[0] as Relationship); + if (!relationship) throw new Error('Relationship not found'); + return relationship; + } catch (error) { + console.error('Error getting relationship:', error); + throw error; + } +}; + export const getInitialDataByAppleId = async (appleId: string) => { try { const users = await db.select().from(schema.users) @@ -358,3 +381,53 @@ export const sendMessage = async (message: Message) => { throw error; } }; + +export const setCountdown = async (userId: number, countdown: Countdown) => { + try { + const user = await getUser(userId); + if (!user) throw new Error("User not found"); + const relationship = await getRelationship(userId); + if (!relationship) throw new Error("Relationship not found"); + const existingCountdown: Countdown | null = await getCountdown(userId); + let result; + if (existingCountdown !== null) { + result = await db.update(schema.countdowns) + .set({ title: countdown.title, date: countdown.date }) + .where(eq(schema.countdowns.id, existingCountdown.id)) + .returning(); + } else { + result = await db.insert(schema.countdowns) + .values({ + relationshipId: relationship.id, + title: countdown.title, + date: countdown.date, + }).returning(); + } + return result[0] as Countdown; + } catch (error) { + console.error('Error setting countdown:', error); + throw error; + } +}; + +export const getCountdown = async (userId: number) => { + try { + const user = await getUser(userId); + if (!user) throw new Error("User not found"); + const relationship = await getRelationship(userId); + if (!relationship) throw new Error("Relationship not found"); + const countdown = await db.select() + .from(schema.countdowns) + .where(eq(schema.countdowns.relationshipId, relationship.id)) + .limit(1) + .then(results => results[0] as Countdown); + if (!countdown) { + console.log('No countdown found'); + return null; + } + return countdown; + } catch (error) { + console.error('Error getting countdown:', error); + throw error; + } +};