Compare commits
7 Commits
2c2cb819e1
...
dev
Author | SHA1 | Date | |
---|---|---|---|
303ca85e2f | |||
7fc6ca0457 | |||
dd26a3db33 | |||
e2c65425af | |||
4886bc5b1f | |||
f8152c6db8 | |||
3d09199702 |
17
.env.example
@ -1,17 +0,0 @@
|
|||||||
# Since the ".env" file is gitignored, you can use the ".env.example" file to
|
|
||||||
# build a new ".env" file when you clone the repo. Keep this file up-to-date
|
|
||||||
# when you add new variables to `.env`.
|
|
||||||
|
|
||||||
# This file will be committed to version control, so make sure not to have any
|
|
||||||
# secrets in it. If you are cloning this repo, create a copy of this file named
|
|
||||||
# ".env" and populate it with your secrets.
|
|
||||||
|
|
||||||
# When adding additional environment variables, the schema in "/src/env.js"
|
|
||||||
# should be updated accordingly.
|
|
||||||
|
|
||||||
# Drizzle
|
|
||||||
DATABASE_URL="postgresql://postgres:password@localhost:5432/wavelength_server"
|
|
||||||
|
|
||||||
# Example:
|
|
||||||
# SERVERVAR="foo"
|
|
||||||
# NEXT_PUBLIC_CLIENTVAR="bar"
|
|
0
.eslintrc.cjs
Executable file → Normal file
4
.gitignore
vendored
Executable file → Normal file
@ -1,9 +1,12 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
/public/uploads/*
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
/node_modules
|
/node_modules
|
||||||
/.pnp
|
/.pnp
|
||||||
.pnp.js
|
.pnp.js
|
||||||
|
*/node_modules
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/coverage
|
||||||
@ -35,6 +38,7 @@ yarn-error.log*
|
|||||||
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
|
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
|
||||||
.env
|
.env
|
||||||
.env*.local
|
.env*.local
|
||||||
|
*/.env
|
||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
|
0
drizzle.config.ts
Executable file → Normal file
@ -18,3 +18,75 @@ $$;
|
|||||||
CREATE TRIGGER message_insert_trigger
|
CREATE TRIGGER message_insert_trigger
|
||||||
AFTER INSERT ON messages
|
AFTER INSERT ON messages
|
||||||
FOR EACH ROW EXECUTE FUNCTION notify_new_message();
|
FOR EACH ROW EXECUTE FUNCTION notify_new_message();
|
||||||
|
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION notify_countdown_update()
|
||||||
|
RETURNS trigger LANGUAGE plpgsql AS $$
|
||||||
|
BEGIN
|
||||||
|
PERFORM pg_notify(
|
||||||
|
'countdown_update',
|
||||||
|
json_build_object(
|
||||||
|
'id', NEW.id,
|
||||||
|
'relationshipId', NEW.relationship_id,
|
||||||
|
'title', NEW.title,
|
||||||
|
'date', NEW.date
|
||||||
|
)::text
|
||||||
|
);
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE TRIGGER countdown_update_trigger
|
||||||
|
AFTER INSERT OR UPDATE ON countdowns
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION notify_countdown_update();
|
||||||
|
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION notify_relationship_status_change()
|
||||||
|
RETURNS trigger LANGUAGE plpgsql AS $$
|
||||||
|
BEGIN
|
||||||
|
IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
|
||||||
|
PERFORM pg_notify(
|
||||||
|
'relationship_status_change',
|
||||||
|
json_build_object(
|
||||||
|
'id', NEW.id,
|
||||||
|
'relationshipId', NEW.id,
|
||||||
|
'status', NEW.is_accepted
|
||||||
|
)::text
|
||||||
|
);
|
||||||
|
ELSIF (TG_OP = 'DELETE') THEN
|
||||||
|
PERFORM pg_notify(
|
||||||
|
'relationship_status_change',
|
||||||
|
json_build_object(
|
||||||
|
'id', OLD.id,
|
||||||
|
'relationshipId', OLD.id,
|
||||||
|
'status', 'deleted'
|
||||||
|
)::text
|
||||||
|
);
|
||||||
|
END IF;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE TRIGGER relationship_status_change_trigger
|
||||||
|
AFTER INSERT OR UPDATE OR DELETE ON relationships
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION notify_relationship_status_change();
|
||||||
|
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION notify_profile_picture_update()
|
||||||
|
RETURNS trigger LANGUAGE plpgsql AS $$
|
||||||
|
BEGIN
|
||||||
|
PERFORM pg_notify(
|
||||||
|
'profile_picture_update',
|
||||||
|
json_build_object(
|
||||||
|
'id', NEW.id,
|
||||||
|
'userId', NEW.id,
|
||||||
|
'pictureUrl', NEW.pfp_url
|
||||||
|
)::text
|
||||||
|
);
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE TRIGGER profile_picture_update_trigger
|
||||||
|
AFTER UPDATE OF pfp_url ON users
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION notify_profile_picture_update();
|
||||||
|
4
next.config.js
Executable file → Normal file
@ -5,6 +5,8 @@
|
|||||||
await import("./src/env.js");
|
await import("./src/env.js");
|
||||||
|
|
||||||
/** @type {import("next").NextConfig} */
|
/** @type {import("next").NextConfig} */
|
||||||
const config = {};
|
const config = {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
4
package.json
Executable file → Normal file
@ -20,16 +20,20 @@
|
|||||||
"drizzle-orm": "^0.33.0",
|
"drizzle-orm": "^0.33.0",
|
||||||
"geist": "^1.3.1",
|
"geist": "^1.3.1",
|
||||||
"next": "^14.2.15",
|
"next": "^14.2.15",
|
||||||
|
"pg": "^8.13.0",
|
||||||
"postgres": "^3.4.4",
|
"postgres": "^3.4.4",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
|
"socket.io": "^4.8.0",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/eslint": "^8.56.12",
|
"@types/eslint": "^8.56.12",
|
||||||
"@types/node": "^20.16.13",
|
"@types/node": "^20.16.13",
|
||||||
|
"@types/pg": "^8.11.10",
|
||||||
"@types/react": "^18.3.11",
|
"@types/react": "^18.3.11",
|
||||||
"@types/react-dom": "^18.3.1",
|
"@types/react-dom": "^18.3.1",
|
||||||
|
"@types/socket.io": "^3.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.10.0",
|
"@typescript-eslint/eslint-plugin": "^8.10.0",
|
||||||
"@typescript-eslint/parser": "^8.10.0",
|
"@typescript-eslint/parser": "^8.10.0",
|
||||||
"drizzle-kit": "^0.24.2",
|
"drizzle-kit": "^0.24.2",
|
||||||
|
357
pnpm-lock.yaml
generated
Executable file → Normal file
@ -13,13 +13,16 @@ importers:
|
|||||||
version: 0.10.1(typescript@5.6.3)(zod@3.23.8)
|
version: 0.10.1(typescript@5.6.3)(zod@3.23.8)
|
||||||
drizzle-orm:
|
drizzle-orm:
|
||||||
specifier: ^0.33.0
|
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:
|
geist:
|
||||||
specifier: ^1.3.1
|
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))
|
version: 1.3.1(next@14.2.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
||||||
next:
|
next:
|
||||||
specifier: ^14.2.15
|
specifier: ^14.2.15
|
||||||
version: 14.2.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
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:
|
postgres:
|
||||||
specifier: ^3.4.4
|
specifier: ^3.4.4
|
||||||
version: 3.4.4
|
version: 3.4.4
|
||||||
@ -29,6 +32,9 @@ importers:
|
|||||||
react-dom:
|
react-dom:
|
||||||
specifier: ^18.3.1
|
specifier: ^18.3.1
|
||||||
version: 18.3.1(react@18.3.1)
|
version: 18.3.1(react@18.3.1)
|
||||||
|
socket.io:
|
||||||
|
specifier: ^4.8.0
|
||||||
|
version: 4.8.0
|
||||||
zod:
|
zod:
|
||||||
specifier: ^3.23.8
|
specifier: ^3.23.8
|
||||||
version: 3.23.8
|
version: 3.23.8
|
||||||
@ -39,12 +45,18 @@ importers:
|
|||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^20.16.13
|
specifier: ^20.16.13
|
||||||
version: 20.16.13
|
version: 20.16.13
|
||||||
|
'@types/pg':
|
||||||
|
specifier: ^8.11.10
|
||||||
|
version: 8.11.10
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^18.3.11
|
specifier: ^18.3.11
|
||||||
version: 18.3.11
|
version: 18.3.11
|
||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
specifier: ^18.3.1
|
specifier: ^18.3.1
|
||||||
version: 18.3.1
|
version: 18.3.1
|
||||||
|
'@types/socket.io':
|
||||||
|
specifier: ^3.0.2
|
||||||
|
version: 3.0.2
|
||||||
'@typescript-eslint/eslint-plugin':
|
'@typescript-eslint/eslint-plugin':
|
||||||
specifier: ^8.10.0
|
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)
|
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':
|
'@rushstack/eslint-patch@1.10.4':
|
||||||
resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==}
|
resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==}
|
||||||
|
|
||||||
|
'@socket.io/component-emitter@3.1.2':
|
||||||
|
resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
|
||||||
|
|
||||||
'@swc/counter@0.1.3':
|
'@swc/counter@0.1.3':
|
||||||
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
|
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
|
||||||
|
|
||||||
@ -529,6 +544,12 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
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':
|
'@types/eslint@8.56.12':
|
||||||
resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==}
|
resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==}
|
||||||
|
|
||||||
@ -544,6 +565,9 @@ packages:
|
|||||||
'@types/node@20.16.13':
|
'@types/node@20.16.13':
|
||||||
resolution: {integrity: sha512-GjQ7im10B0labo8ZGXDGROUl9k0BNyDgzfGpb4g/cl+4yYDWVKcozANF4FGr4/p0O/rAkQClM6Wiwkije++1Tg==}
|
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':
|
'@types/prop-types@15.7.13':
|
||||||
resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==}
|
resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==}
|
||||||
|
|
||||||
@ -553,6 +577,10 @@ packages:
|
|||||||
'@types/react@18.3.11':
|
'@types/react@18.3.11':
|
||||||
resolution: {integrity: sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==}
|
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':
|
'@typescript-eslint/eslint-plugin@8.10.0':
|
||||||
resolution: {integrity: sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==}
|
resolution: {integrity: sha512-phuB3hoP7FFKbRXxjl+DRlQDuJqhpOnm5MmtROXyWi3uS/Xg2ZXqiQfcG2BJHiN4QKyzdOJi3NEn/qTnjUlkmQ==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
@ -613,6 +641,10 @@ packages:
|
|||||||
'@ungap/structured-clone@1.2.0':
|
'@ungap/structured-clone@1.2.0':
|
||||||
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
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:
|
acorn-jsx@5.3.2:
|
||||||
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -709,6 +741,10 @@ packages:
|
|||||||
balanced-match@1.0.2:
|
balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
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:
|
binary-extensions@2.3.0:
|
||||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -770,6 +806,14 @@ packages:
|
|||||||
concat-map@0.0.1:
|
concat-map@0.0.1:
|
||||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
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:
|
cross-spawn@7.0.3:
|
||||||
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@ -941,6 +985,14 @@ packages:
|
|||||||
emoji-regex@9.2.2:
|
emoji-regex@9.2.2:
|
||||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
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:
|
enhanced-resolve@5.17.1:
|
||||||
resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==}
|
resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
@ -1475,6 +1527,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||||
engines: {node: '>=8.6'}
|
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:
|
minimatch@3.1.2:
|
||||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||||
|
|
||||||
@ -1503,6 +1563,10 @@ packages:
|
|||||||
natural-compare@1.4.0:
|
natural-compare@1.4.0:
|
||||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||||
|
|
||||||
|
negotiator@0.6.3:
|
||||||
|
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
next@14.2.15:
|
next@14.2.15:
|
||||||
resolution: {integrity: sha512-h9ctmOokpoDphRvMGnwOJAedT6zKhwqyZML9mDtspgf4Rh3Pn7UTYKqePNoDvhsWBAO5GoPNYshnAUGIazVGmw==}
|
resolution: {integrity: sha512-h9ctmOokpoDphRvMGnwOJAedT6zKhwqyZML9mDtspgf4Rh3Pn7UTYKqePNoDvhsWBAO5GoPNYshnAUGIazVGmw==}
|
||||||
engines: {node: '>=18.17.0'}
|
engines: {node: '>=18.17.0'}
|
||||||
@ -1561,6 +1625,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==}
|
resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
obuf@1.1.2:
|
||||||
|
resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==}
|
||||||
|
|
||||||
once@1.4.0:
|
once@1.4.0:
|
||||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||||
|
|
||||||
@ -1602,6 +1669,48 @@ packages:
|
|||||||
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
|
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
|
||||||
engines: {node: '>=16 || 14 >=14.18'}
|
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:
|
picocolors@1.1.1:
|
||||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||||
|
|
||||||
@ -1666,6 +1775,41 @@ packages:
|
|||||||
resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==}
|
resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==}
|
||||||
engines: {node: ^10 || ^12 || >=14}
|
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:
|
postgres@3.4.4:
|
||||||
resolution: {integrity: sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==}
|
resolution: {integrity: sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@ -1842,6 +1986,17 @@ packages:
|
|||||||
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
||||||
engines: {node: '>=14'}
|
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:
|
source-map-js@1.2.1:
|
||||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -1853,6 +2008,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
split2@4.2.0:
|
||||||
|
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
|
||||||
|
engines: {node: '>= 10.x'}
|
||||||
|
|
||||||
streamsearch@1.1.0:
|
streamsearch@1.1.0:
|
||||||
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
|
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
@ -2008,6 +2167,10 @@ packages:
|
|||||||
util-deprecate@1.0.2:
|
util-deprecate@1.0.2:
|
||||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
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:
|
which-boxed-primitive@1.0.2:
|
||||||
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
|
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
|
||||||
|
|
||||||
@ -2043,6 +2206,22 @@ packages:
|
|||||||
wrappy@1.0.2:
|
wrappy@1.0.2:
|
||||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
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:
|
yaml@2.6.0:
|
||||||
resolution: {integrity: sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==}
|
resolution: {integrity: sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
@ -2321,6 +2500,8 @@ snapshots:
|
|||||||
|
|
||||||
'@rushstack/eslint-patch@1.10.4': {}
|
'@rushstack/eslint-patch@1.10.4': {}
|
||||||
|
|
||||||
|
'@socket.io/component-emitter@3.1.2': {}
|
||||||
|
|
||||||
'@swc/counter@0.1.3': {}
|
'@swc/counter@0.1.3': {}
|
||||||
|
|
||||||
'@swc/helpers@0.5.5':
|
'@swc/helpers@0.5.5':
|
||||||
@ -2341,6 +2522,12 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.6.3
|
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':
|
'@types/eslint@8.56.12':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree': 1.0.6
|
'@types/estree': 1.0.6
|
||||||
@ -2356,6 +2543,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 6.19.8
|
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/prop-types@15.7.13': {}
|
||||||
|
|
||||||
'@types/react-dom@18.3.1':
|
'@types/react-dom@18.3.1':
|
||||||
@ -2367,6 +2560,14 @@ snapshots:
|
|||||||
'@types/prop-types': 15.7.13
|
'@types/prop-types': 15.7.13
|
||||||
csstype: 3.1.3
|
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)':
|
'@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:
|
dependencies:
|
||||||
'@eslint-community/regexpp': 4.11.1
|
'@eslint-community/regexpp': 4.11.1
|
||||||
@ -2450,6 +2651,11 @@ snapshots:
|
|||||||
|
|
||||||
'@ungap/structured-clone@1.2.0': {}
|
'@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):
|
acorn-jsx@5.3.2(acorn@8.13.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.13.0
|
acorn: 8.13.0
|
||||||
@ -2563,6 +2769,8 @@ snapshots:
|
|||||||
|
|
||||||
balanced-match@1.0.2: {}
|
balanced-match@1.0.2: {}
|
||||||
|
|
||||||
|
base64id@2.0.0: {}
|
||||||
|
|
||||||
binary-extensions@2.3.0: {}
|
binary-extensions@2.3.0: {}
|
||||||
|
|
||||||
brace-expansion@1.1.11:
|
brace-expansion@1.1.11:
|
||||||
@ -2627,6 +2835,13 @@ snapshots:
|
|||||||
|
|
||||||
concat-map@0.0.1: {}
|
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:
|
cross-spawn@7.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
path-key: 3.1.1
|
path-key: 3.1.1
|
||||||
@ -2700,9 +2915,11 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- 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:
|
optionalDependencies:
|
||||||
|
'@types/pg': 8.11.10
|
||||||
'@types/react': 18.3.11
|
'@types/react': 18.3.11
|
||||||
|
pg: 8.13.0
|
||||||
postgres: 3.4.4
|
postgres: 3.4.4
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
|
|
||||||
@ -2712,6 +2929,25 @@ snapshots:
|
|||||||
|
|
||||||
emoji-regex@9.2.2: {}
|
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:
|
enhanced-resolve@5.17.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
@ -3444,6 +3680,12 @@ snapshots:
|
|||||||
braces: 3.0.3
|
braces: 3.0.3
|
||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
|
|
||||||
|
mime-db@1.52.0: {}
|
||||||
|
|
||||||
|
mime-types@2.1.35:
|
||||||
|
dependencies:
|
||||||
|
mime-db: 1.52.0
|
||||||
|
|
||||||
minimatch@3.1.2:
|
minimatch@3.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 1.1.11
|
brace-expansion: 1.1.11
|
||||||
@ -3468,6 +3710,8 @@ snapshots:
|
|||||||
|
|
||||||
natural-compare@1.4.0: {}
|
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):
|
next@14.2.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 14.2.15
|
'@next/env': 14.2.15
|
||||||
@ -3535,6 +3779,8 @@ snapshots:
|
|||||||
define-properties: 1.2.1
|
define-properties: 1.2.1
|
||||||
es-object-atoms: 1.0.0
|
es-object-atoms: 1.0.0
|
||||||
|
|
||||||
|
obuf@1.1.2: {}
|
||||||
|
|
||||||
once@1.4.0:
|
once@1.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
wrappy: 1.0.2
|
wrappy: 1.0.2
|
||||||
@ -3575,6 +3821,53 @@ snapshots:
|
|||||||
lru-cache: 10.4.3
|
lru-cache: 10.4.3
|
||||||
minipass: 7.1.2
|
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: {}
|
picocolors@1.1.1: {}
|
||||||
|
|
||||||
picomatch@2.3.1: {}
|
picomatch@2.3.1: {}
|
||||||
@ -3628,6 +3921,28 @@ snapshots:
|
|||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
source-map-js: 1.2.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: {}
|
postgres@3.4.4: {}
|
||||||
|
|
||||||
prelude-ls@1.2.1: {}
|
prelude-ls@1.2.1: {}
|
||||||
@ -3763,6 +4078,36 @@ snapshots:
|
|||||||
|
|
||||||
signal-exit@4.1.0: {}
|
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-js@1.2.1: {}
|
||||||
|
|
||||||
source-map-support@0.5.21:
|
source-map-support@0.5.21:
|
||||||
@ -3772,6 +4117,8 @@ snapshots:
|
|||||||
|
|
||||||
source-map@0.6.1: {}
|
source-map@0.6.1: {}
|
||||||
|
|
||||||
|
split2@4.2.0: {}
|
||||||
|
|
||||||
streamsearch@1.1.0: {}
|
streamsearch@1.1.0: {}
|
||||||
|
|
||||||
string-width@4.2.3:
|
string-width@4.2.3:
|
||||||
@ -3977,6 +4324,8 @@ snapshots:
|
|||||||
|
|
||||||
util-deprecate@1.0.2: {}
|
util-deprecate@1.0.2: {}
|
||||||
|
|
||||||
|
vary@1.1.2: {}
|
||||||
|
|
||||||
which-boxed-primitive@1.0.2:
|
which-boxed-primitive@1.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-bigint: 1.0.4
|
is-bigint: 1.0.4
|
||||||
@ -4035,6 +4384,10 @@ snapshots:
|
|||||||
|
|
||||||
wrappy@1.0.2: {}
|
wrappy@1.0.2: {}
|
||||||
|
|
||||||
|
ws@8.17.1: {}
|
||||||
|
|
||||||
|
xtend@4.0.2: {}
|
||||||
|
|
||||||
yaml@2.6.0: {}
|
yaml@2.6.0: {}
|
||||||
|
|
||||||
yocto-queue@0.1.0: {}
|
yocto-queue@0.1.0: {}
|
||||||
|
0
postcss.config.cjs
Executable file → Normal file
0
prettier.config.js
Executable file → Normal file
0
public/favicon.png
Executable file → Normal file
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
0
public/uploads/profile-pictures/2_1729280510919.jpg
Executable file → Normal file
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
BIN
public/uploads/profile-pictures/3_1729898560745.jpg
Normal file
After Width: | Height: | Size: 24 KiB |
0
public/uploads/profile-pictures/madeline.jpg
Executable file → Normal file
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
0
src/app/api/relationships/checkStatus/route.ts
Executable file → Normal file
37
src/app/api/relationships/countdown/get/route.ts
Normal file
@ -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 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
40
src/app/api/relationships/countdown/set/route.ts
Normal file
@ -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 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
0
src/app/api/relationships/createRequest/route.ts
Executable file → Normal file
0
src/app/api/relationships/updateStatus/route.ts
Executable file → Normal file
0
src/app/api/users/createUser/route.ts
Executable file → Normal file
0
src/app/api/users/getInitialDataByAppleId/route.ts
Executable file → Normal file
0
src/app/api/users/search/route.ts
Executable file → Normal file
0
src/app/api/users/updatePfp/route.ts
Executable file → Normal file
10
src/app/api/users/updatePushToken/route.ts
Executable file → Normal file
@ -1,7 +1,7 @@
|
|||||||
'use server';
|
'use server';
|
||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import type { NextRequest } from 'next/server';
|
import type { NextRequest } from 'next/server';
|
||||||
import { updatePushToken } from '~/server/functions';
|
import { addPushToken } from '~/server/functions';
|
||||||
import { middleware } from '~/middleware';
|
import { middleware } from '~/middleware';
|
||||||
|
|
||||||
export const POST = async (request: NextRequest) => {
|
export const POST = async (request: NextRequest) => {
|
||||||
@ -15,17 +15,17 @@ export const POST = async (request: NextRequest) => {
|
|||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: 'Missing userId or pushToken' }, { status: 400 }
|
{ message: 'Missing userId or pushToken' }, { status: 400 }
|
||||||
);
|
);
|
||||||
const result = await updatePushToken(userId, pushToken);
|
const result = await addPushToken(userId, pushToken);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: 'Error updating push token' }, { status: 500 }
|
{ message: 'Error adding push token' }, { status: 500 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: 'Push token updated successfully' }, { status: 200 }
|
{ message: 'Push token added successfully' }, { status: 200 }
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating push token:', error);
|
console.error('Error adding push token:', error);
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ message: error.message }, { status: 500 }
|
{ message: error.message }, { status: 500 }
|
||||||
|
0
src/app/layout.tsx
Executable file → Normal file
0
src/app/page.tsx
Executable file → Normal file
0
src/env.js
Executable file → Normal file
0
src/middleware.ts
Executable file → Normal file
0
src/server/db/index.ts
Executable file → Normal file
16
src/server/db/schema.ts
Executable file → Normal file
@ -23,7 +23,6 @@ export const users = pgTable(
|
|||||||
email: varchar('email', { length: 100 }).unique().notNull(),
|
email: varchar('email', { length: 100 }).unique().notNull(),
|
||||||
fullName: varchar('full_name', { length: 100 }).notNull(),
|
fullName: varchar('full_name', { length: 100 }).notNull(),
|
||||||
pfpUrl: varchar('pfp_url', { length: 255 }),
|
pfpUrl: varchar('pfp_url', { length: 255 }),
|
||||||
pushToken: varchar('push_token', { length: 100 }).unique().notNull(),
|
|
||||||
createdAt: timestamp('created_at', { withTimezone: true })
|
createdAt: timestamp('created_at', { withTimezone: true })
|
||||||
.default(sql`CURRENT_TIMESTAMP`).notNull(),
|
.default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||||
metadata: jsonb('metadata'),
|
metadata: jsonb('metadata'),
|
||||||
@ -35,6 +34,21 @@ export const users = pgTable(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const pushTokens = pgTable(
|
||||||
|
'push_tokens',
|
||||||
|
{
|
||||||
|
id: serial('id').primaryKey(),
|
||||||
|
userId: integer('user_id').references(() => users.id).notNull(),
|
||||||
|
token: varchar('token', { length: 100 }).unique().notNull(),
|
||||||
|
createdAt: timestamp('created_at', { withTimezone: true })
|
||||||
|
.default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||||
|
},
|
||||||
|
(table) => ({
|
||||||
|
userIdIndex: index('push_tokens_user_id_idx').on(table.userId),
|
||||||
|
tokenIndex: index('push_tokens_token_idx').on(table.token),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
export const relationships = pgTable(
|
export const relationships = pgTable(
|
||||||
'relationships',
|
'relationships',
|
||||||
{
|
{
|
||||||
|
170
src/server/functions.ts
Executable file → Normal file
@ -2,7 +2,8 @@ import 'server-only';
|
|||||||
import { db } from '~/server/db';
|
import { db } from '~/server/db';
|
||||||
import * as schema from '~/server/db/schema';
|
import * as schema from '~/server/db/schema';
|
||||||
import { eq, and, or, like, not, desc, sql } from 'drizzle-orm';
|
import { eq, and, or, like, not, desc, sql } from 'drizzle-orm';
|
||||||
import { User,
|
import type {
|
||||||
|
User,
|
||||||
Relationship,
|
Relationship,
|
||||||
UserRelationship,
|
UserRelationship,
|
||||||
RelationshipData,
|
RelationshipData,
|
||||||
@ -18,23 +19,70 @@ import { User,
|
|||||||
export const getUser = async (userId: number) => {
|
export const getUser = async (userId: number) => {
|
||||||
try {
|
try {
|
||||||
const users = await db.select().from(schema.users)
|
const users = await db.select().from(schema.users)
|
||||||
.where(eq(schema.users.id, userId))
|
.where(eq(schema.users.id, userId));
|
||||||
return (users.length > 0) ? users[0] as User : null;
|
|
||||||
|
if (users.length === 0) return null;
|
||||||
|
|
||||||
|
const user = users[0];
|
||||||
|
// Get push tokens for user
|
||||||
|
const tokens = await db.select()
|
||||||
|
.from(schema.pushTokens)
|
||||||
|
.where(eq(schema.pushTokens.userId, userId));
|
||||||
|
|
||||||
|
return {
|
||||||
|
...user,
|
||||||
|
pushTokens: tokens.map(t => t.token)
|
||||||
|
} as User;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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) => {
|
export const getInitialDataByAppleId = async (appleId: string) => {
|
||||||
try {
|
try {
|
||||||
const users = await db.select().from(schema.users)
|
const users = await db.select().from(schema.users)
|
||||||
.where(eq(schema.users.appleId, appleId))
|
.where(eq(schema.users.appleId, appleId));
|
||||||
if (users.length === 0) return null;
|
if (users.length === 0) return null;
|
||||||
const user = users[0] as User;
|
|
||||||
|
const user = users[0];
|
||||||
|
// Get push tokens for user
|
||||||
|
const tokens = await db.select()
|
||||||
|
.from(schema.pushTokens)
|
||||||
|
.where(eq(schema.pushTokens.userId, user.id));
|
||||||
|
|
||||||
|
const userWithTokens = {
|
||||||
|
...user,
|
||||||
|
pushTokens: tokens.map(t => t.token)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rest of the function remains the same, but use userWithTokens instead of user
|
||||||
const userRelationships = await db.select()
|
const userRelationships = await db.select()
|
||||||
.from(schema.userRelationships)
|
.from(schema.userRelationships)
|
||||||
.where(eq(schema.userRelationships.userId, user.id))
|
.where(eq(schema.userRelationships.userId, userWithTokens.id));
|
||||||
|
|
||||||
let relationshipData: RelationshipData | undefined;
|
let relationshipData: RelationshipData | undefined;
|
||||||
let countdown: Countdown | undefined;
|
let countdown: Countdown | undefined;
|
||||||
@ -54,7 +102,7 @@ export const getInitialDataByAppleId = async (appleId: string) => {
|
|||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(schema.userRelationships.relationshipId, relationship.id),
|
eq(schema.userRelationships.relationshipId, relationship.id),
|
||||||
not(eq(schema.userRelationships.userId, user.id))
|
not(eq(schema.userRelationships.userId, userWithTokens.id))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if (partners.length > 0) {
|
if (partners.length > 0) {
|
||||||
@ -75,7 +123,7 @@ export const getInitialDataByAppleId = async (appleId: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const initialData: InitialData = {
|
const initialData: InitialData = {
|
||||||
user,
|
user: userWithTokens,
|
||||||
relationshipData,
|
relationshipData,
|
||||||
countdown,
|
countdown,
|
||||||
};
|
};
|
||||||
@ -91,31 +139,29 @@ export const createUser = async (
|
|||||||
fullName: string, pushToken: string
|
fullName: string, pushToken: string
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
console.log(appleId, email, fullName, pushToken);
|
|
||||||
|
|
||||||
if (!appleId || !email || !fullName || !pushToken) {
|
if (!appleId || !email || !fullName || !pushToken) {
|
||||||
throw new Error("Error: All required fields must be filled");
|
throw new Error("Error: All required fields must be filled");
|
||||||
}
|
}
|
||||||
// Check if username or email is already taken
|
|
||||||
const existingUser = await db.select().from(schema.users)
|
const existingUser = await db.select().from(schema.users)
|
||||||
.where(or(eq(schema.users.appleId, appleId), eq(schema.users.email, email)));
|
.where(or(eq(schema.users.appleId, appleId), eq(schema.users.email, email)));
|
||||||
|
|
||||||
if (existingUser.length > 0) {
|
if (existingUser.length > 0) {
|
||||||
throw new Error("Username or email is already in use");
|
throw new Error("Username or email is already in use");
|
||||||
}
|
}
|
||||||
console.log('right before we add the user');
|
|
||||||
|
|
||||||
const newUsers: User[] = await db.insert(schema.users).values({
|
const newUsers = await db.insert(schema.users).values({
|
||||||
appleId, email, fullName, pushToken
|
appleId, email, fullName
|
||||||
}).returning() as User[]; // return the newly created user
|
}).returning();
|
||||||
|
|
||||||
const newUser: User | undefined = newUsers[0];
|
const newUser = newUsers[0];
|
||||||
|
|
||||||
if (!newUsers.length || !newUsers[0]?.id)
|
if (!newUsers.length || !newUsers[0]?.id)
|
||||||
throw new Error("Failed to create new user");
|
throw new Error("Failed to create new user");
|
||||||
|
|
||||||
return newUser;
|
await addPushToken(newUser.id, pushToken);
|
||||||
|
|
||||||
|
return newUser;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
throw new Error(`Failed to create new user: ${error.message}`);
|
throw new Error(`Failed to create new user: ${error.message}`);
|
||||||
@ -125,25 +171,30 @@ export const createUser = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updatePushToken = async (userId: number, pushToken: string): Promise<boolean> => {
|
export const addPushToken = async (userId: number, token: string): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
const result = await db.update(schema.users)
|
await db.insert(schema.pushTokens)
|
||||||
.set({ pushToken: pushToken })
|
.values({ userId, token })
|
||||||
.where(
|
.onConflictDoNothing();
|
||||||
and(
|
return true;
|
||||||
eq(schema.users.id, userId),
|
|
||||||
not(eq(schema.users.pushToken, pushToken))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.returning({ updatedId: schema.users.id });
|
|
||||||
|
|
||||||
return result.length > 0;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating push token:', error);
|
console.error('Error adding push token:', error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getPushTokens = async (userId: number): Promise<string[]> => {
|
||||||
|
try {
|
||||||
|
const tokens = await db.select()
|
||||||
|
.from(schema.pushTokens)
|
||||||
|
.where(eq(schema.pushTokens.userId, userId));
|
||||||
|
return tokens.map(t => t.token);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting push tokens:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const getPfpUrl = async (userId: number) => {
|
export const getPfpUrl = async (userId: number) => {
|
||||||
try {
|
try {
|
||||||
const users = await db.select().from(schema.users)
|
const users = await db.select().from(schema.users)
|
||||||
@ -256,6 +307,8 @@ export const updateRelationshipStatus = async (
|
|||||||
} else if (status === 'rejected') {
|
} else if (status === 'rejected') {
|
||||||
await db.delete(schema.userRelationships)
|
await db.delete(schema.userRelationships)
|
||||||
.where(eq(schema.userRelationships.relationshipId, relationship.id));
|
.where(eq(schema.userRelationships.relationshipId, relationship.id));
|
||||||
|
await db.delete(schema.countdowns)
|
||||||
|
.where(eq(schema.countdowns.relationshipId, relationship.id));
|
||||||
await db.delete(schema.relationships)
|
await db.delete(schema.relationships)
|
||||||
.where(eq(schema.relationships.id, relationship.id));
|
.where(eq(schema.relationships.id, relationship.id));
|
||||||
return null;
|
return null;
|
||||||
@ -358,3 +411,60 @@ export const sendMessage = async (message: Message) => {
|
|||||||
throw error;
|
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);
|
||||||
|
|
||||||
|
// Ensure the date is a valid Date object
|
||||||
|
const countdownDate = new Date(countdown.date);
|
||||||
|
if (isNaN(countdownDate.getTime())) {
|
||||||
|
throw new Error("Invalid date provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
let result;
|
||||||
|
if (existingCountdown !== null) {
|
||||||
|
result = await db.update(schema.countdowns)
|
||||||
|
.set({ title: countdown.title, date: countdownDate })
|
||||||
|
.where(eq(schema.countdowns.id, existingCountdown.id))
|
||||||
|
.returning();
|
||||||
|
} else {
|
||||||
|
result = await db.insert(schema.countdowns)
|
||||||
|
.values({
|
||||||
|
relationshipId: relationship.id,
|
||||||
|
title: countdown.title,
|
||||||
|
date: countdownDate,
|
||||||
|
}).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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
2
src/server/types.ts
Executable file → Normal file
@ -7,9 +7,9 @@ export type User = {
|
|||||||
email: string;
|
email: string;
|
||||||
fullName: string;
|
fullName: string;
|
||||||
pfpUrl: string | null;
|
pfpUrl: string | null;
|
||||||
pushToken: string;
|
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
metadata?: Record<string, string>;
|
metadata?: Record<string, string>;
|
||||||
|
pushTokens?: string[];
|
||||||
};
|
};
|
||||||
// Relationship Table in DB
|
// Relationship Table in DB
|
||||||
export type Relationship = {
|
export type Relationship = {
|
||||||
|
0
src/styles/globals.css
Executable file → Normal file
0
tailwind.config.ts
Executable file → Normal file
0
tsconfig.json
Executable file → Normal file
102
websocket_server/dist/server.js
vendored
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
"use strict";
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const express_1 = __importDefault(require("express"));
|
||||||
|
const socket_io_1 = require("socket.io");
|
||||||
|
const http_1 = require("http");
|
||||||
|
const pg_1 = require("pg");
|
||||||
|
const dotenv_1 = __importDefault(require("dotenv"));
|
||||||
|
dotenv_1.default.config();
|
||||||
|
const app = (0, express_1.default)();
|
||||||
|
const httpServer = (0, http_1.createServer)(app);
|
||||||
|
// Create new Socket.IO server
|
||||||
|
const io = new socket_io_1.Server(httpServer);
|
||||||
|
const PORT = process.env.PORT || 3030;
|
||||||
|
const pgClient = new pg_1.Client({
|
||||||
|
connectionString: process.env.DATABASE_URL,
|
||||||
|
});
|
||||||
|
const handleNotification = (msg) => {
|
||||||
|
var _a;
|
||||||
|
const payload = JSON.parse((_a = msg.payload) !== null && _a !== void 0 ? _a : '{}');
|
||||||
|
switch (msg.channel) {
|
||||||
|
case 'new_message':
|
||||||
|
handleNewMessage(payload);
|
||||||
|
break;
|
||||||
|
case 'countdown_update':
|
||||||
|
handleCountdownUpdate(payload);
|
||||||
|
break;
|
||||||
|
case 'relationship_status_change':
|
||||||
|
handleRelationshipStatusChange(payload);
|
||||||
|
break;
|
||||||
|
case 'profile_picture_update':
|
||||||
|
handleProfilePictureUpdate(payload);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log('Unhandled notification:', msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleNewMessage = (payload) => {
|
||||||
|
const { receiverId, text, id } = payload;
|
||||||
|
io.to(`user:${receiverId}`).emit('new_message', payload);
|
||||||
|
console.log(`Message sent to room ${receiverId} with text: "${text}"`);
|
||||||
|
};
|
||||||
|
const handleCountdownUpdate = (payload) => {
|
||||||
|
const { relationshipId, countdown } = payload;
|
||||||
|
io.to(`relationship:${relationshipId}`).emit('countdown_update', countdown);
|
||||||
|
console.log(`Countdown update sent to room ${relationshipId}`);
|
||||||
|
};
|
||||||
|
const handleRelationshipStatusChange = (payload) => {
|
||||||
|
const { relationshipId, status } = payload;
|
||||||
|
io.to(`relationship:${relationshipId}`).emit('relationship_status_change', status);
|
||||||
|
console.log(`Relationship status change sent to room ${relationshipId}, status: ${status}`);
|
||||||
|
};
|
||||||
|
const handleProfilePictureUpdate = (payload) => {
|
||||||
|
const { userId, profilePictureUrl } = payload;
|
||||||
|
io.to(`user:${userId}`).emit('profile_picture_update', profilePictureUrl);
|
||||||
|
console.log(`Profile picture update sent to room ${userId}`);
|
||||||
|
};
|
||||||
|
// Connect the PostgreSQL client
|
||||||
|
pgClient.connect()
|
||||||
|
.then(() => {
|
||||||
|
console.log('Connected to PostgreSQL');
|
||||||
|
// Listen for NOTIFY events on the 'new_message' channel
|
||||||
|
pgClient.query('LISTEN new_message');
|
||||||
|
pgClient.query('LISTEN countdown_update');
|
||||||
|
pgClient.query('LISTEN relationship_status_change');
|
||||||
|
pgClient.query('LISTEN profile_picture_update');
|
||||||
|
pgClient.on('notification', handleNotification);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('Failed to connect to PostgreSQL:', err);
|
||||||
|
});
|
||||||
|
// Handle WebSocket connections
|
||||||
|
io.on('connection', (socket) => {
|
||||||
|
console.log('WebSocket client connected:', socket.id);
|
||||||
|
socket.on('join', (roomInfo) => {
|
||||||
|
//console.log('Attempting to join room:', roomInfo);
|
||||||
|
if (typeof roomInfo === 'object') {
|
||||||
|
if (roomInfo.userId) {
|
||||||
|
socket.join(`user:${roomInfo.userId}`);
|
||||||
|
console.log(`User ${roomInfo.userId} joined room`);
|
||||||
|
}
|
||||||
|
else if (roomInfo.relationshipId) {
|
||||||
|
socket.join(`relationship:${roomInfo.relationshipId}`);
|
||||||
|
console.log(`Relationship ${roomInfo.relationshipId} joined room`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log('Socket object at connection:', socket);
|
||||||
|
});
|
||||||
|
socket.on('error', (error) => {
|
||||||
|
console.error('WebSocket error:', error);
|
||||||
|
});
|
||||||
|
// Handle disconnection of WebSocket clients
|
||||||
|
socket.on('disconnect', () => {
|
||||||
|
console.log('WebSocket client disconnected:', socket.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// Start the WebSocket server
|
||||||
|
httpServer.listen(PORT, () => {
|
||||||
|
console.log(`WebSocket server is running on port ${PORT}`);
|
||||||
|
});
|
1384
websocket_server/package-lock.json
generated
Normal file
32
websocket_server/package.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "websocket-server",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "tsc",
|
||||||
|
"start": "node dist/server.js",
|
||||||
|
"dev": "tsc-watch --onSuccess \"node dist/server.js\"",
|
||||||
|
"go": "tsc-watch --onSuccess \"node dist/server.js\""
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
|
"express": "^4.21.1",
|
||||||
|
"pg": "^8.13.0",
|
||||||
|
"socket.io": "^4.8.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/cors": "^2.8.17",
|
||||||
|
"@types/express": "^5.0.0",
|
||||||
|
"@types/node": "^22.7.8",
|
||||||
|
"@types/pg": "^8.11.10",
|
||||||
|
"@types/socket.io": "^3.0.2",
|
||||||
|
"tsc-watch": "^6.2.0",
|
||||||
|
"typescript": "^5.6.3"
|
||||||
|
}
|
||||||
|
}
|
1134
websocket_server/pnpm-lock.yaml
generated
Normal file
112
websocket_server/src/server.ts
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import { Server } from 'socket.io';
|
||||||
|
import { createServer } from 'http';
|
||||||
|
import { Client } from 'pg';
|
||||||
|
import type { Notification } from 'pg';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const httpServer = createServer(app);
|
||||||
|
|
||||||
|
// Create new Socket.IO server
|
||||||
|
const io = new Server(httpServer);
|
||||||
|
|
||||||
|
const PORT = process.env.PORT ?? 3020;
|
||||||
|
|
||||||
|
const pgClient = new Client({
|
||||||
|
connectionString: process.env.DATABASE_URL,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleNotification = (msg: Notification) => {
|
||||||
|
const payload = JSON.parse(msg.payload ?? '{}');
|
||||||
|
switch (msg.channel) {
|
||||||
|
case 'new_message':
|
||||||
|
handleNewMessage(payload);
|
||||||
|
break;
|
||||||
|
case 'countdown_update':
|
||||||
|
handleCountdownUpdate(payload);
|
||||||
|
break;
|
||||||
|
case 'relationship_status_change':
|
||||||
|
handleRelationshipStatusChange(payload);
|
||||||
|
break;
|
||||||
|
case 'profile_picture_update':
|
||||||
|
handleProfilePictureUpdate(payload);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log('Unhandled notification:', msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNewMessage = (payload: any) => {
|
||||||
|
const {receiverId, text, id} = payload;
|
||||||
|
io.to(`user:${receiverId}`).emit('new_message', payload);
|
||||||
|
console.log(`Message sent to room ${receiverId} with text: "${text}"`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCountdownUpdate = (payload: any) => {
|
||||||
|
const { relationshipId, countdown } = payload;
|
||||||
|
io.to(`relationship:${relationshipId}`).emit('countdown_update', countdown);
|
||||||
|
console.log(`Countdown update sent to room ${relationshipId}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRelationshipStatusChange = (payload: any) => {
|
||||||
|
const { relationshipId, status } = payload;
|
||||||
|
io.to(`relationship:${relationshipId}`).emit('relationship_status_change', status);
|
||||||
|
console.log(`Relationship status change sent to room ${relationshipId}, status: ${status}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleProfilePictureUpdate = (payload: any) => {
|
||||||
|
const { userId, profilePictureUrl } = payload;
|
||||||
|
io.to(`user:${userId}`).emit('profile_picture_update', profilePictureUrl);
|
||||||
|
console.log(`Profile picture update sent to room ${userId}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Connect the PostgreSQL client
|
||||||
|
pgClient.connect()
|
||||||
|
.then(() => {
|
||||||
|
console.log('Connected to PostgreSQL');
|
||||||
|
// Listen for NOTIFY events on the 'new_message' channel
|
||||||
|
pgClient.query('LISTEN new_message');
|
||||||
|
pgClient.query('LISTEN countdown_update');
|
||||||
|
pgClient.query('LISTEN relationship_status_change');
|
||||||
|
pgClient.query('LISTEN profile_picture_update');
|
||||||
|
|
||||||
|
pgClient.on('notification', handleNotification);
|
||||||
|
})
|
||||||
|
.catch((err: unknown) => {
|
||||||
|
console.error('Failed to connect to PostgreSQL:', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle WebSocket connections
|
||||||
|
io.on('connection', (socket) => {
|
||||||
|
console.log('WebSocket client connected:', socket.id);
|
||||||
|
|
||||||
|
socket.on('join', (roomInfo) => {
|
||||||
|
//console.log('Attempting to join room:', roomInfo);
|
||||||
|
if (typeof roomInfo === 'object') {
|
||||||
|
if (roomInfo.userId) {
|
||||||
|
socket.join(`user:${roomInfo.userId}`);
|
||||||
|
console.log(`User ${roomInfo.userId} joined room`);
|
||||||
|
} else if (roomInfo.relationshipId) {
|
||||||
|
socket.join(`relationship:${roomInfo.relationshipId}`);
|
||||||
|
console.log(`Relationship ${roomInfo.relationshipId} joined room`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log('Socket object at connection:', socket);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('error', (error) => {
|
||||||
|
console.error('WebSocket error:', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle disconnection of WebSocket clients
|
||||||
|
socket.on('disconnect', () => {
|
||||||
|
console.log('WebSocket client disconnected:', socket.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start the WebSocket server
|
||||||
|
httpServer.listen(PORT, () => {
|
||||||
|
console.log(`WebSocket server is running on port ${PORT}`);
|
||||||
|
});
|
110
websocket_server/tsconfig.json
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||||
|
|
||||||
|
/* Projects */
|
||||||
|
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||||
|
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||||
|
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||||
|
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||||
|
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||||
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||||
|
|
||||||
|
/* Language and Environment */
|
||||||
|
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||||
|
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||||
|
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||||
|
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||||
|
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||||
|
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||||
|
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||||
|
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||||
|
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||||
|
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||||
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||||
|
|
||||||
|
/* Modules */
|
||||||
|
"module": "commonjs", /* Specify what module code is generated. */
|
||||||
|
"rootDir": "./src", /* Specify the root folder within your source files. */
|
||||||
|
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||||
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||||
|
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||||
|
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||||
|
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||||
|
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||||
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
|
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||||
|
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||||
|
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||||
|
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||||
|
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||||
|
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
|
||||||
|
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||||
|
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||||
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||||
|
|
||||||
|
/* JavaScript Support */
|
||||||
|
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||||
|
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||||
|
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||||
|
|
||||||
|
/* Emit */
|
||||||
|
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||||
|
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||||
|
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||||
|
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||||
|
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||||
|
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||||
|
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||||
|
"outDir": "./dist", /* Specify an output folder for all emitted files. */
|
||||||
|
// "removeComments": true, /* Disable emitting comments. */
|
||||||
|
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||||
|
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||||
|
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||||
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||||
|
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||||
|
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||||
|
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||||
|
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||||
|
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||||
|
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||||
|
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||||
|
|
||||||
|
/* Interop Constraints */
|
||||||
|
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||||
|
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||||
|
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
|
||||||
|
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||||
|
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||||
|
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||||
|
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||||
|
|
||||||
|
/* Type Checking */
|
||||||
|
"strict": true, /* Enable all strict type-checking options. */
|
||||||
|
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||||
|
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||||
|
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||||
|
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||||
|
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||||
|
// "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
|
||||||
|
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||||
|
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||||
|
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||||
|
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||||
|
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||||
|
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||||
|
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||||
|
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||||
|
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||||
|
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||||
|
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||||
|
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||||
|
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||||
|
|
||||||
|
/* Completeness */
|
||||||
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||||
|
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||||
|
}
|
||||||
|
}
|