Merge pull request #5 from k-puig/feature/full-endpoint
Feature/full endpoint
This commit is contained in:
10
concord-server/.editorconfig
Normal file
10
concord-server/.editorconfig
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# EditorConfig helps maintain consistent coding styles
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
1
concord-server/.gitignore
vendored
1
concord-server/.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
# deps
|
# deps
|
||||||
node_modules/
|
node_modules/
|
||||||
|
generated/
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
To install dependencies:
|
To install dependencies:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bun install
|
bun install
|
||||||
```
|
```
|
||||||
|
|
||||||
To run:
|
To run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bun run dev
|
bun run dev
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -4,93 +4,240 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "concord-server",
|
"name": "concord-server",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hono/standard-validator": "^0.1.5",
|
||||||
|
"@hono/zod-validator": "^0.7.3",
|
||||||
|
"@prisma/client": "^6.16.2",
|
||||||
|
"@scalar/hono-api-reference": "^0.9.19",
|
||||||
|
"@socket.io/bun-engine": "^0.0.3",
|
||||||
"hono": "^4.9.9",
|
"hono": "^4.9.9",
|
||||||
|
"hono-openapi": "^1.1.0",
|
||||||
"prisma": "^6.16.2",
|
"prisma": "^6.16.2",
|
||||||
|
"socket.io": "^4.8.1",
|
||||||
|
"zod": "^4.1.11",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
|
"prisma-zod-generator": "^1.22.1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"@prisma/config": ["@prisma/config@6.16.2", "", { "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", "effect": "3.16.12", "empathic": "2.0.0" } }, "sha512-mKXSUrcqXj0LXWPmJsK2s3p9PN+aoAbyMx7m5E1v1FufofR1ZpPoIArjjzOIm+bJRLLvYftoNYLx1tbHgF9/yg=="],
|
"@hono/standard-validator": ["@hono/standard-validator@0.1.5", "", { "peerDependencies": { "@standard-schema/spec": "1.0.0", "hono": ">=3.9.0" } }, "sha512-EIyZPPwkyLn6XKwFj5NBEWHXhXbgmnVh2ceIFo5GO7gKI9WmzTjPDKnppQB0KrqKeAkq3kpoW4SIbu5X1dgx3w=="],
|
||||||
|
|
||||||
"@prisma/debug": ["@prisma/debug@6.16.2", "", {}, "sha512-bo4/gA/HVV6u8YK2uY6glhNsJ7r+k/i5iQ9ny/3q5bt9ijCj7WMPUwfTKPvtEgLP+/r26Z686ly11hhcLiQ8zA=="],
|
"@hono/zod-validator": ["@hono/zod-validator@0.7.3", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-uYGdgVib3RlGD698WR5dVM0zB3UuPY5vHKXffGUbUh7r4xY+mFIhF3/v4AcQVLrU5CQdBso8BJr4wuVoCrjTuQ=="],
|
||||||
|
|
||||||
"@prisma/engines": ["@prisma/engines@6.16.2", "", { "dependencies": { "@prisma/debug": "6.16.2", "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", "@prisma/fetch-engine": "6.16.2", "@prisma/get-platform": "6.16.2" } }, "sha512-7yf3AjfPUgsg/l7JSu1iEhsmZZ/YE00yURPjTikqm2z4btM0bCl2coFtTGfeSOWbQMmq45Jab+53yGUIAT1sjA=="],
|
"@prisma/client": ["@prisma/client@6.16.2", "", { "peerDependencies": { "prisma": "*", "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"] }, "sha512-E00PxBcalMfYO/TWnXobBVUai6eW/g5OsifWQsQDzJYm7yaY+IRLo7ZLsaefi0QkTpxfuhFcQ/w180i6kX3iJw=="],
|
||||||
|
|
||||||
"@prisma/engines-version": ["@prisma/engines-version@6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", "", {}, "sha512-ThvlDaKIVrnrv97ujNFDYiQbeMQpLa0O86HFA2mNoip4mtFqM7U5GSz2ie1i2xByZtvPztJlNRgPsXGeM/kqAA=="],
|
"@prisma/config": ["@prisma/config@6.16.2", "", { "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", "effect": "3.16.12", "empathic": "2.0.0" } }, ""],
|
||||||
|
|
||||||
"@prisma/fetch-engine": ["@prisma/fetch-engine@6.16.2", "", { "dependencies": { "@prisma/debug": "6.16.2", "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", "@prisma/get-platform": "6.16.2" } }, "sha512-wPnZ8DMRqpgzye758ZvfAMiNJRuYpz+rhgEBZi60ZqDIgOU2694oJxiuu3GKFeYeR/hXxso4/2oBC243t/whxQ=="],
|
"@prisma/debug": ["@prisma/debug@6.16.2", "", {}, ""],
|
||||||
|
|
||||||
"@prisma/get-platform": ["@prisma/get-platform@6.16.2", "", { "dependencies": { "@prisma/debug": "6.16.2" } }, "sha512-U/P36Uke5wS7r1+omtAgJpEB94tlT4SdlgaeTc6HVTTT93pXj7zZ+B/cZnmnvjcNPfWddgoDx8RLjmQwqGDYyA=="],
|
"@prisma/dmmf": ["@prisma/dmmf@6.16.2", "", {}, "sha512-o9ztgdbj2KZXl6DL+oP56TTC0poTLPns9/MeU761b49E1IQ/fd0jwdov1bidlNOiwio8Nsou23xNrYE/db10aA=="],
|
||||||
|
|
||||||
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
|
"@prisma/driver-adapter-utils": ["@prisma/driver-adapter-utils@6.16.2", "", { "dependencies": { "@prisma/debug": "6.16.2" } }, "sha512-DMgfafnG0zPd+QoAQOC0Trn1xlb0fVAfQi2MpkpzSf641KiVkVPkJRXDSbcTbxGxO2HRdd0vI9U6LlesWad4XA=="],
|
||||||
|
|
||||||
|
"@prisma/engines": ["@prisma/engines@6.16.2", "", { "dependencies": { "@prisma/debug": "6.16.2", "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", "@prisma/fetch-engine": "6.16.2", "@prisma/get-platform": "6.16.2" } }, ""],
|
||||||
|
|
||||||
|
"@prisma/engines-version": ["@prisma/engines-version@6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", "", {}, ""],
|
||||||
|
|
||||||
|
"@prisma/fetch-engine": ["@prisma/fetch-engine@6.16.2", "", { "dependencies": { "@prisma/debug": "6.16.2", "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", "@prisma/get-platform": "6.16.2" } }, ""],
|
||||||
|
|
||||||
|
"@prisma/generator": ["@prisma/generator@6.16.2", "", {}, "sha512-7bwRmtMIgfe1rUynh1p9VlmYvEiidbRO6aBphPBS6YGEGSvNe8+QExbRpsqFlFBvIX76BhZCxuEj7ZwALMYRKQ=="],
|
||||||
|
|
||||||
|
"@prisma/generator-helper": ["@prisma/generator-helper@6.16.2", "", { "dependencies": { "@prisma/debug": "6.16.2", "@prisma/dmmf": "6.16.2", "@prisma/generator": "6.16.2" } }, "sha512-8tVnWM8ETJNrvI5CT9eKCW23+aPLNkidC+g9NJn7ghXm60Q7GGlLX5tmvn5dE8tXvs/FSX3MN7KNmNJpOr89Hw=="],
|
||||||
|
|
||||||
|
"@prisma/get-platform": ["@prisma/get-platform@6.16.2", "", { "dependencies": { "@prisma/debug": "6.16.2" } }, ""],
|
||||||
|
|
||||||
|
"@prisma/internals": ["@prisma/internals@6.16.2", "", { "dependencies": { "@prisma/config": "6.16.2", "@prisma/debug": "6.16.2", "@prisma/dmmf": "6.16.2", "@prisma/driver-adapter-utils": "6.16.2", "@prisma/engines": "6.16.2", "@prisma/fetch-engine": "6.16.2", "@prisma/generator": "6.16.2", "@prisma/generator-helper": "6.16.2", "@prisma/get-platform": "6.16.2", "@prisma/prisma-schema-wasm": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", "@prisma/schema-engine-wasm": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", "@prisma/schema-files-loader": "6.16.2", "arg": "5.0.2", "prompts": "2.4.2" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"] }, "sha512-gwmWl7H8iTbi+58RXua5Lsus5LDbIZGO2wQ4RoSX9YtEbKWHwRP8TUzTVLwRNeJ2DHwfnzhTLrUnybwotqiACg=="],
|
||||||
|
|
||||||
|
"@prisma/prisma-schema-wasm": ["@prisma/prisma-schema-wasm@6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", "", {}, "sha512-DvYi0zKqzPd49Z5japS3FawyMylscaoUmlXNhnRAXb8HZryG4Q7TM1FLX8OIAfCgLmoWS1c/Zf4UZznBXkvWSg=="],
|
||||||
|
|
||||||
|
"@prisma/schema-engine-wasm": ["@prisma/schema-engine-wasm@6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", "", {}, "sha512-HDgFE0um5OHkk2pkQbAgARR284i2VpoM+7GYRAT0zxoTagsdaZ6yquJF2LEZuAKfibib0Ct7JZxRCB8eN/Ru6g=="],
|
||||||
|
|
||||||
|
"@prisma/schema-files-loader": ["@prisma/schema-files-loader@6.16.2", "", { "dependencies": { "@prisma/prisma-schema-wasm": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", "fs-extra": "11.3.0" } }, "sha512-TN77DUFgOxT/WL6wuxVhn7qVDvwVRl0TEzhFfRh5vKQsuZ5itLzA7Ki4TgOs4Pk18wwZnti6ZKdzR3Y7cu2KsA=="],
|
||||||
|
|
||||||
|
"@scalar/core": ["@scalar/core@0.3.17", "", { "dependencies": { "@scalar/types": "0.2.16" } }, "sha512-G6tP+2oorFA90szI8DGiEQ23SmobiuhN93GfTJNpFMhz/kdEtC4lcYawAxL1tWkZhlK/QYRcaCZcVzmpTUgBCA=="],
|
||||||
|
|
||||||
|
"@scalar/hono-api-reference": ["@scalar/hono-api-reference@0.9.19", "", { "dependencies": { "@scalar/core": "0.3.17" }, "peerDependencies": { "hono": "^4.0.0" } }, "sha512-MeBdIwuAAhwvsUt1f9174wASu0C8b4WrXuVdC/6FjrsTxLHSbJVx+XFzZAKF5ex+1kaXjvYrm3fiovRFHLyfJA=="],
|
||||||
|
|
||||||
|
"@scalar/openapi-types": ["@scalar/openapi-types@0.3.7", "", { "dependencies": { "zod": "3.24.1" } }, "sha512-QHSvHBVDze3+dUwAhIGq6l1iOev4jdoqdBK7QpfeN1Q4h+6qpVEw3EEqBiH0AXUSh/iWwObBv4uMgfIx0aNZ5g=="],
|
||||||
|
|
||||||
|
"@scalar/types": ["@scalar/types@0.2.16", "", { "dependencies": { "@scalar/openapi-types": "0.3.7", "nanoid": "5.1.5", "zod": "3.24.1" } }, "sha512-XWff9jWfYaj6q3ww94x66S6Q58u/3kA1sDOUhLAwb9va7r58bzk3NRwLOkEEdJmyEns1MEJAM53mY8KRWX6elA=="],
|
||||||
|
|
||||||
|
"@socket.io/bun-engine": ["@socket.io/bun-engine@0.0.3", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-OK2ZObq9hKsxyAcV7xDTcWGubBmfEY3Lt4nb04K+HlYl9G5PDgrY9hxJm9uV+B0xo3MhKFrgdg9VQsVZ3pbT/g=="],
|
||||||
|
|
||||||
|
"@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="],
|
||||||
|
|
||||||
|
"@standard-community/standard-json": ["@standard-community/standard-json@0.3.5", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "@types/json-schema": "^7.0.15", "@valibot/to-json-schema": "^1.3.0", "arktype": "^2.1.20", "effect": "^3.16.8", "quansync": "^0.2.11", "sury": "^10.0.0", "typebox": "^1.0.17", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.5" }, "optionalPeers": ["@valibot/to-json-schema", "arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-to-json-schema"] }, "sha512-4+ZPorwDRt47i+O7RjyuaxHRK/37QY/LmgxlGrRrSTLYoFatEOzvqIc85GTlM18SFZ5E91C+v0o/M37wZPpUHA=="],
|
||||||
|
|
||||||
|
"@standard-community/standard-openapi": ["@standard-community/standard-openapi@0.2.8", "", { "peerDependencies": { "@standard-community/standard-json": "^0.3.5", "@standard-schema/spec": "^1.0.0", "arktype": "^2.1.20", "effect": "^3.17.14", "openapi-types": "^12.1.3", "sury": "^10.0.0", "typebox": "^1.0.0", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-openapi": "^4" }, "optionalPeers": ["arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-openapi"] }, "sha512-80ap74p5oy/SU4al5HkPwO5+NbN2wH/FBr6kwaE5ROq7AvcDFaxzUfTazewroNaCotbvdGcvzXb9oEoOIyfC/Q=="],
|
||||||
|
|
||||||
|
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, ""],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.2.22", "", { "dependencies": { "bun-types": "1.2.22" } }, "sha512-5A/KrKos2ZcN0c6ljRSOa1fYIyCKhZfIVYeuyb4snnvomnpFqC0tTsEkdqNxbAgExV384OETQ//WAjl3XbYqQA=="],
|
"@types/bun": ["@types/bun@1.2.22", "", { "dependencies": { "bun-types": "1.2.22" } }, "sha512-5A/KrKos2ZcN0c6ljRSOa1fYIyCKhZfIVYeuyb4snnvomnpFqC0tTsEkdqNxbAgExV384OETQ//WAjl3XbYqQA=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@24.5.2", "", { "dependencies": { "undici-types": "~7.12.0" } }, "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ=="],
|
"@types/cors": ["@types/cors@2.8.19", "", { "dependencies": { "@types/node": "*" } }, "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg=="],
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.1.14", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-ukd93VGzaNPMAUPy0gRDSC57UuQbnH9Kussp7HBjM06YFi9uZTFhOvMSO2OKqXm1rSgzOE+pVx1k1PYHGwlc8Q=="],
|
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.2.22", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA=="],
|
"@types/node": ["@types/node@24.5.2", "", { "dependencies": { "undici-types": "~7.12.0" } }, ""],
|
||||||
|
|
||||||
"c12": ["c12@3.1.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^16.6.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.4.2", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw=="],
|
"@types/react": ["@types/react@19.1.14", "", { "dependencies": { "csstype": "^3.0.2" } }, ""],
|
||||||
|
|
||||||
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
"accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
|
||||||
|
|
||||||
"citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
|
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
||||||
|
|
||||||
"confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="],
|
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
|
||||||
|
|
||||||
"consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
|
"arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
|
||||||
|
|
||||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
"base64id": ["base64id@2.0.0", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="],
|
||||||
|
|
||||||
"deepmerge-ts": ["deepmerge-ts@7.1.5", "", {}, "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw=="],
|
"bun-types": ["bun-types@1.2.22", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, ""],
|
||||||
|
|
||||||
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
"c12": ["c12@3.1.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^16.6.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.4.2", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, ""],
|
||||||
|
|
||||||
"destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="],
|
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, ""],
|
||||||
|
|
||||||
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
"citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, ""],
|
||||||
|
|
||||||
"effect": ["effect@3.16.12", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg=="],
|
"confbox": ["confbox@0.2.2", "", {}, ""],
|
||||||
|
|
||||||
"empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="],
|
"consola": ["consola@3.4.2", "", {}, ""],
|
||||||
|
|
||||||
"exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="],
|
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
||||||
|
|
||||||
"fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="],
|
"cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
|
||||||
|
|
||||||
"giget": ["giget@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="],
|
"csstype": ["csstype@3.1.3", "", {}, ""],
|
||||||
|
|
||||||
"hono": ["hono@4.9.9", "", {}, "sha512-Hxw4wT6zjJGZJdkJzAx9PyBdf7ZpxaTSA0NfxqjLghwMrLBX8p33hJBzoETRakF3UJu6OdNQBZAlNSkGqKFukw=="],
|
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
||||||
|
|
||||||
"jiti": ["jiti@2.6.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ=="],
|
"debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
||||||
|
|
||||||
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
|
"deepmerge-ts": ["deepmerge-ts@7.1.5", "", {}, ""],
|
||||||
|
|
||||||
"nypm": ["nypm@0.6.2", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.2", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "tinyexec": "^1.0.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g=="],
|
"defu": ["defu@6.1.4", "", {}, ""],
|
||||||
|
|
||||||
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
|
"destr": ["destr@2.0.5", "", {}, ""],
|
||||||
|
|
||||||
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
"dotenv": ["dotenv@16.6.1", "", {}, ""],
|
||||||
|
|
||||||
"perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
|
"effect": ["effect@3.16.12", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, ""],
|
||||||
|
|
||||||
"pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
|
"empathic": ["empathic@2.0.0", "", {}, ""],
|
||||||
|
|
||||||
"prisma": ["prisma@6.16.2", "", { "dependencies": { "@prisma/config": "6.16.2", "@prisma/engines": "6.16.2" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-aRvldGE5UUJTtVmFiH3WfNFNiqFlAtePUxcI0UEGlnXCX7DqhiMT5TRYwncHFeA/Reca5W6ToXXyCMTeFPdSXA=="],
|
"engine.io": ["engine.io@6.6.4", "", { "dependencies": { "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", "ws": "~8.17.1" } }, "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g=="],
|
||||||
|
|
||||||
"pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
|
"engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="],
|
||||||
|
|
||||||
"rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="],
|
"exsolve": ["exsolve@1.0.7", "", {}, ""],
|
||||||
|
|
||||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
"fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, ""],
|
||||||
|
|
||||||
"tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="],
|
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||||
|
|
||||||
"undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="],
|
"fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
|
||||||
|
|
||||||
|
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
||||||
|
|
||||||
|
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
||||||
|
|
||||||
|
"fs-extra": ["fs-extra@11.3.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew=="],
|
||||||
|
|
||||||
|
"giget": ["giget@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": "dist/cli.mjs" }, ""],
|
||||||
|
|
||||||
|
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||||
|
|
||||||
|
"hono": ["hono@4.9.9", "", {}, ""],
|
||||||
|
|
||||||
|
"hono-openapi": ["hono-openapi@1.1.0", "", { "peerDependencies": { "@hono/standard-validator": "^0.1.2", "@standard-community/standard-json": "^0.3.5", "@standard-community/standard-openapi": "^0.2.8", "@types/json-schema": "^7.0.15", "hono": "^4.8.3", "openapi-types": "^12.1.3" }, "optionalPeers": ["@hono/standard-validator", "hono"] }, "sha512-eA5hN8D2O30EkPPUxWFilcZcThAe81TShbH38Y183ZZp8WkgMh4BrPEDeZ/EFN2tyDi3cmTgKTa3+oStyJX0UA=="],
|
||||||
|
|
||||||
|
"jiti": ["jiti@2.6.0", "", { "bin": "lib/jiti-cli.mjs" }, ""],
|
||||||
|
|
||||||
|
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||||
|
|
||||||
|
"jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="],
|
||||||
|
|
||||||
|
"kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
|
||||||
|
|
||||||
|
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||||
|
|
||||||
|
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||||
|
|
||||||
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
|
"nanoid": ["nanoid@5.1.5", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw=="],
|
||||||
|
|
||||||
|
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||||
|
|
||||||
|
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
||||||
|
|
||||||
|
"node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
||||||
|
|
||||||
|
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, ""],
|
||||||
|
|
||||||
|
"nypm": ["nypm@0.6.2", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.2", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "tinyexec": "^1.0.1" }, "bin": "dist/cli.mjs" }, ""],
|
||||||
|
|
||||||
|
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||||
|
|
||||||
|
"ohash": ["ohash@2.0.11", "", {}, ""],
|
||||||
|
|
||||||
|
"openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
|
||||||
|
|
||||||
|
"pathe": ["pathe@2.0.3", "", {}, ""],
|
||||||
|
|
||||||
|
"perfect-debounce": ["perfect-debounce@1.0.0", "", {}, ""],
|
||||||
|
|
||||||
|
"pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, ""],
|
||||||
|
|
||||||
|
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
|
||||||
|
|
||||||
|
"prisma": ["prisma@6.16.2", "", { "dependencies": { "@prisma/config": "6.16.2", "@prisma/engines": "6.16.2" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": "build/index.js" }, ""],
|
||||||
|
|
||||||
|
"prisma-zod-generator": ["prisma-zod-generator@1.22.1", "", { "dependencies": { "@prisma/client": "^6.16.2", "@prisma/generator-helper": "^6.16.2", "@prisma/internals": "^6.16.2", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "node-fetch": "^3.3.2", "prettier": "^3.6.2", "tslib": "^2.8.1" }, "peerDependencies": { "zod": ">=3.25.0 <5" }, "bin": { "prisma-zod-generator": "lib/generator.js" } }, "sha512-nBr00sfR8onGCD5eIDLHoFrpeJTSuZxSeaO61Zg6CAEyXPR51gpkO1ev9huG7+tsV+mm8me8VNl8hMcVtWl8FA=="],
|
||||||
|
|
||||||
|
"prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="],
|
||||||
|
|
||||||
|
"pure-rand": ["pure-rand@6.1.0", "", {}, ""],
|
||||||
|
|
||||||
|
"quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
|
||||||
|
|
||||||
|
"rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, ""],
|
||||||
|
|
||||||
|
"readdirp": ["readdirp@4.1.2", "", {}, ""],
|
||||||
|
|
||||||
|
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||||
|
|
||||||
|
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
||||||
|
|
||||||
|
"socket.io": ["socket.io@4.8.1", "", { "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" } }, "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg=="],
|
||||||
|
|
||||||
|
"socket.io-adapter": ["socket.io-adapter@2.5.5", "", { "dependencies": { "debug": "~4.3.4", "ws": "~8.17.1" } }, "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg=="],
|
||||||
|
|
||||||
|
"socket.io-parser": ["socket.io-parser@4.2.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" } }, "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew=="],
|
||||||
|
|
||||||
|
"tinyexec": ["tinyexec@1.0.1", "", {}, ""],
|
||||||
|
|
||||||
|
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
|
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@7.12.0", "", {}, ""],
|
||||||
|
|
||||||
|
"universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
|
||||||
|
|
||||||
|
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||||
|
|
||||||
|
"web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
|
||||||
|
|
||||||
|
"ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="],
|
||||||
|
|
||||||
|
"zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
|
||||||
|
|
||||||
|
"@scalar/openapi-types/zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="],
|
||||||
|
|
||||||
|
"@scalar/types/zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "public"."Instance" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"icon" TEXT,
|
||||||
|
|
||||||
|
CONSTRAINT "Instance_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "public"."User" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"username" TEXT NOT NULL,
|
||||||
|
"nickname" TEXT,
|
||||||
|
"bio" TEXT,
|
||||||
|
"picture" TEXT,
|
||||||
|
"banner" TEXT,
|
||||||
|
"admin" BOOLEAN NOT NULL,
|
||||||
|
"status" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "public"."Role" (
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"instanceId" TEXT NOT NULL,
|
||||||
|
"type" TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "public"."UserAuth" (
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"password" TEXT NOT NULL,
|
||||||
|
"token" TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "public"."Category" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"instanceId" TEXT,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"position" INTEGER NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Category_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "public"."Channel" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"type" TEXT NOT NULL,
|
||||||
|
"categoryId" TEXT,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"description" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Channel_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "public"."Message" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"channelId" TEXT NOT NULL,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"deleted" BOOLEAN NOT NULL,
|
||||||
|
"text" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "Message_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "public"."Reply" (
|
||||||
|
"messageId" TEXT NOT NULL,
|
||||||
|
"repliesToId" TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "public"."MessagePing" (
|
||||||
|
"messageId" TEXT NOT NULL,
|
||||||
|
"pingsUserId" TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Role_userId_instanceId_key" ON "public"."Role"("userId", "instanceId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "UserAuth_userId_key" ON "public"."UserAuth"("userId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Reply_messageId_key" ON "public"."Reply"("messageId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Reply_repliesToId_key" ON "public"."Reply"("repliesToId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "Reply_messageId_repliesToId_key" ON "public"."Reply"("messageId", "repliesToId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "MessagePing_messageId_pingsUserId_key" ON "public"."MessagePing"("messageId", "pingsUserId");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "public"."Role" ADD CONSTRAINT "Role_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "public"."Role" ADD CONSTRAINT "Role_instanceId_fkey" FOREIGN KEY ("instanceId") REFERENCES "public"."Instance"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "public"."UserAuth" ADD CONSTRAINT "UserAuth_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "public"."Category" ADD CONSTRAINT "Category_instanceId_fkey" FOREIGN KEY ("instanceId") REFERENCES "public"."Instance"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "public"."Channel" ADD CONSTRAINT "Channel_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "public"."Category"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "public"."Message" ADD CONSTRAINT "Message_channelId_fkey" FOREIGN KEY ("channelId") REFERENCES "public"."Channel"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "public"."Message" ADD CONSTRAINT "Message_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "public"."Reply" ADD CONSTRAINT "Reply_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "public"."Message"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "public"."Reply" ADD CONSTRAINT "Reply_repliesToId_fkey" FOREIGN KEY ("repliesToId") REFERENCES "public"."Message"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "public"."MessagePing" ADD CONSTRAINT "MessagePing_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "public"."Message"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "public"."MessagePing" ADD CONSTRAINT "MessagePing_pingsUserId_fkey" FOREIGN KEY ("pingsUserId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `updatedAt` to the `Category` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `pinnedId` to the `Channel` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `updatedAt` to the `Channel` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `updatedAt` to the `Instance` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `updatedAt` to the `Message` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `updatedAt` to the `Reply` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `updatedAt` to the `Role` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `updatedAt` to the `User` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `updatedAt` to the `UserAuth` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."Category" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."Channel" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ADD COLUMN "pinnedId" TEXT,
|
||||||
|
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."Instance" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."Message" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."MessagePing" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."Reply" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."Role" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."User" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."UserAuth" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "public"."ChannelPin" (
|
||||||
|
"messageId" TEXT NOT NULL,
|
||||||
|
"channelId" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "ChannelPin_messageId_key" ON "public"."ChannelPin"("messageId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "ChannelPin_channelId_key" ON "public"."ChannelPin"("channelId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "ChannelPin_messageId_channelId_key" ON "public"."ChannelPin"("messageId", "channelId");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "public"."ChannelPin" ADD CONSTRAINT "ChannelPin_messageId_fkey" FOREIGN KEY ("messageId") REFERENCES "public"."Message"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "public"."ChannelPin" ADD CONSTRAINT "ChannelPin_channelId_fkey" FOREIGN KEY ("channelId") REFERENCES "public"."Channel"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."Category" ALTER COLUMN "updatedAt" DROP DEFAULT;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."Channel" ALTER COLUMN "updatedAt" DROP DEFAULT;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."Channel" ALTER COLUMN "pinnedId" DROP NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."Instance" ALTER COLUMN "updatedAt" DROP DEFAULT;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."Message" ALTER COLUMN "updatedAt" DROP DEFAULT;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."Reply" ALTER COLUMN "updatedAt" DROP DEFAULT;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."Role" ALTER COLUMN "updatedAt" DROP DEFAULT;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."User" ALTER COLUMN "updatedAt" DROP DEFAULT;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "public"."UserAuth" ALTER COLUMN "updatedAt" DROP DEFAULT;
|
||||||
3
concord-server/migrations/migration_lock.toml
Normal file
3
concord-server/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (e.g., Git)
|
||||||
|
provider = "postgresql"
|
||||||
390
concord-server/package-lock.json
generated
Normal file
390
concord-server/package-lock.json
generated
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
{
|
||||||
|
"name": "concord-server",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "concord-server",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/client": "^6.16.2",
|
||||||
|
"hono": "^4.9.9",
|
||||||
|
"prisma": "^6.16.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/client": {
|
||||||
|
"version": "6.16.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.16.2.tgz",
|
||||||
|
"integrity": "sha512-E00PxBcalMfYO/TWnXobBVUai6eW/g5OsifWQsQDzJYm7yaY+IRLo7ZLsaefi0QkTpxfuhFcQ/w180i6kX3iJw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"prisma": "*",
|
||||||
|
"typescript": ">=5.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"prisma": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/config": {
|
||||||
|
"version": "6.16.2",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"c12": "3.1.0",
|
||||||
|
"deepmerge-ts": "7.1.5",
|
||||||
|
"effect": "3.16.12",
|
||||||
|
"empathic": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/debug": {
|
||||||
|
"version": "6.16.2",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/engines": {
|
||||||
|
"version": "6.16.2",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "6.16.2",
|
||||||
|
"@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43",
|
||||||
|
"@prisma/fetch-engine": "6.16.2",
|
||||||
|
"@prisma/get-platform": "6.16.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/engines-version": {
|
||||||
|
"version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/fetch-engine": {
|
||||||
|
"version": "6.16.2",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "6.16.2",
|
||||||
|
"@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43",
|
||||||
|
"@prisma/get-platform": "6.16.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/get-platform": {
|
||||||
|
"version": "6.16.2",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "6.16.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@standard-schema/spec": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/bun": {
|
||||||
|
"version": "1.2.22",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.2.22.tgz",
|
||||||
|
"integrity": "sha512-5A/KrKos2ZcN0c6ljRSOa1fYIyCKhZfIVYeuyb4snnvomnpFqC0tTsEkdqNxbAgExV384OETQ//WAjl3XbYqQA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bun-types": "1.2.22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "24.5.2",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/react": {
|
||||||
|
"version": "19.1.14",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"csstype": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/bun-types": {
|
||||||
|
"version": "1.2.22",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^19"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/c12": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"chokidar": "^4.0.3",
|
||||||
|
"confbox": "^0.2.2",
|
||||||
|
"defu": "^6.1.4",
|
||||||
|
"dotenv": "^16.6.1",
|
||||||
|
"exsolve": "^1.0.7",
|
||||||
|
"giget": "^2.0.0",
|
||||||
|
"jiti": "^2.4.2",
|
||||||
|
"ohash": "^2.0.11",
|
||||||
|
"pathe": "^2.0.3",
|
||||||
|
"perfect-debounce": "^1.0.0",
|
||||||
|
"pkg-types": "^2.2.0",
|
||||||
|
"rc9": "^2.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"magicast": "^0.3.5"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"magicast": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chokidar": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"readdirp": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.16.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/citty": {
|
||||||
|
"version": "0.1.6",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"consola": "^3.2.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/confbox": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/consola": {
|
||||||
|
"version": "3.4.2",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.18.0 || >=16.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/csstype": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/deepmerge-ts": {
|
||||||
|
"version": "7.1.5",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/defu": {
|
||||||
|
"version": "6.1.4",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/destr": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "16.6.1",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://dotenvx.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/effect": {
|
||||||
|
"version": "3.16.12",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@standard-schema/spec": "^1.0.0",
|
||||||
|
"fast-check": "^3.23.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/empathic": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/exsolve": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/fast-check": {
|
||||||
|
"version": "3.23.2",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/dubzzz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fast-check"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pure-rand": "^6.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/giget": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"citty": "^0.1.6",
|
||||||
|
"consola": "^3.4.0",
|
||||||
|
"defu": "^6.1.4",
|
||||||
|
"node-fetch-native": "^1.6.6",
|
||||||
|
"nypm": "^0.6.0",
|
||||||
|
"pathe": "^2.0.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"giget": "dist/cli.mjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/hono": {
|
||||||
|
"version": "4.9.9",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jiti": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"jiti": "lib/jiti-cli.mjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-fetch-native": {
|
||||||
|
"version": "1.6.7",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/nypm": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"citty": "^0.1.6",
|
||||||
|
"consola": "^3.4.2",
|
||||||
|
"pathe": "^2.0.3",
|
||||||
|
"pkg-types": "^2.3.0",
|
||||||
|
"tinyexec": "^1.0.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"nypm": "dist/cli.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.16.0 || >=16.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ohash": {
|
||||||
|
"version": "2.0.11",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/pathe": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/perfect-debounce": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/pkg-types": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"confbox": "^0.2.2",
|
||||||
|
"exsolve": "^1.0.7",
|
||||||
|
"pathe": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prisma": {
|
||||||
|
"version": "6.16.2",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/config": "6.16.2",
|
||||||
|
"@prisma/engines": "6.16.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"prisma": "build/index.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=5.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pure-rand": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/dubzzz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fast-check"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/rc9": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"defu": "^6.1.4",
|
||||||
|
"destr": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/readdirp": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.18.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tinyexec": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "7.12.0",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,10 +4,19 @@
|
|||||||
"dev": "bun run --hot src/index.ts"
|
"dev": "bun run --hot src/index.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hono/standard-validator": "^0.1.5",
|
||||||
|
"@hono/zod-validator": "^0.7.3",
|
||||||
|
"@prisma/client": "^6.16.2",
|
||||||
|
"@scalar/hono-api-reference": "^0.9.19",
|
||||||
|
"@socket.io/bun-engine": "^0.0.3",
|
||||||
"hono": "^4.9.9",
|
"hono": "^4.9.9",
|
||||||
"prisma": "^6.16.2"
|
"hono-openapi": "^1.1.0",
|
||||||
|
"prisma": "^6.16.2",
|
||||||
|
"socket.io": "^4.8.1",
|
||||||
|
"zod": "^4.1.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest"
|
"@types/bun": "latest",
|
||||||
|
"prisma-zod-generator": "^1.22.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
135
concord-server/schema.prisma
Normal file
135
concord-server/schema.prisma
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
generator zod {
|
||||||
|
provider = "prisma-zod-generator"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Instance {
|
||||||
|
id String @id @default(uuid(7))
|
||||||
|
name String
|
||||||
|
icon String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
Role Role[]
|
||||||
|
Category Category[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id String @id @default(uuid(7))
|
||||||
|
username String
|
||||||
|
nickname String?
|
||||||
|
bio String?
|
||||||
|
picture String?
|
||||||
|
banner String?
|
||||||
|
admin Boolean
|
||||||
|
status String // online/offline/dnd/idle/invis
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
Role Role[]
|
||||||
|
UserAuth UserAuth?
|
||||||
|
Message Message[]
|
||||||
|
MessagePing MessagePing[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model Role {
|
||||||
|
User User @relation(fields: [userId], references: [id])
|
||||||
|
userId String
|
||||||
|
Instance Instance @relation(fields: [instanceId], references: [id])
|
||||||
|
instanceId String
|
||||||
|
type String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@unique([userId, instanceId])
|
||||||
|
}
|
||||||
|
|
||||||
|
model UserAuth {
|
||||||
|
User User @relation(fields: [userId], references: [id])
|
||||||
|
userId String
|
||||||
|
password String // HASHED PASSWORD AS STRING USING SHA-256
|
||||||
|
token String? // current user token
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@unique([userId])
|
||||||
|
}
|
||||||
|
|
||||||
|
model Category {
|
||||||
|
id String @id @default(uuid(7))
|
||||||
|
Instance Instance? @relation(fields: [instanceId], references: [id])
|
||||||
|
instanceId String?
|
||||||
|
name String
|
||||||
|
position Int
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
Channel Channel[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model Channel {
|
||||||
|
id String @id @default(uuid(7))
|
||||||
|
type String
|
||||||
|
Category Category? @relation(fields: [categoryId], references: [id])
|
||||||
|
categoryId String?
|
||||||
|
name String
|
||||||
|
description String
|
||||||
|
pinnedId String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
Message Message[]
|
||||||
|
ChannelPin ChannelPin?
|
||||||
|
}
|
||||||
|
|
||||||
|
model ChannelPin {
|
||||||
|
messageId String @unique
|
||||||
|
channelId String @unique
|
||||||
|
|
||||||
|
Message Message @relation(fields: [messageId], references: [id])
|
||||||
|
Channel Channel @relation(fields: [channelId], references: [id])
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
@@unique([messageId, channelId])
|
||||||
|
}
|
||||||
|
|
||||||
|
model Message {
|
||||||
|
id String @id @default(uuid(7))
|
||||||
|
Channel Channel @relation(fields: [channelId], references: [id])
|
||||||
|
channelId String
|
||||||
|
User User @relation(fields: [userId], references: [id])
|
||||||
|
userId String
|
||||||
|
deleted Boolean
|
||||||
|
text String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
replies Reply? @relation("MessageToReply")
|
||||||
|
repliedTo Reply? @relation("ReplyToMessage")
|
||||||
|
MessagePing MessagePing[]
|
||||||
|
ChannelPin ChannelPin?
|
||||||
|
}
|
||||||
|
|
||||||
|
model Reply {
|
||||||
|
message Message @relation("MessageToReply", fields: [messageId], references: [id]) //message text
|
||||||
|
messageId String @unique //message id of the reply
|
||||||
|
repliesTo Message @relation("ReplyToMessage", fields: [repliesToId], references: [id]) //message id that this message replies to
|
||||||
|
repliesToId String @unique //replies to this message id
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@unique([messageId, repliesToId])
|
||||||
|
}
|
||||||
|
|
||||||
|
model MessagePing {
|
||||||
|
Message Message @relation(fields: [messageId], references: [id])
|
||||||
|
messageId String
|
||||||
|
PingsUser User @relation(fields: [pingsUserId], references: [id])
|
||||||
|
pingsUserId String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
@@unique([messageId, pingsUserId])
|
||||||
|
}
|
||||||
40
concord-server/src/controller/categoryController.ts
Normal file
40
concord-server/src/controller/categoryController.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import {
|
||||||
|
createCategory,
|
||||||
|
getCategory,
|
||||||
|
getCategoriesByInstance,
|
||||||
|
updateCategory,
|
||||||
|
deleteCategory,
|
||||||
|
deleteAllCategoriesFromInstance,
|
||||||
|
} from "../services/channelService";
|
||||||
|
import {
|
||||||
|
CreateCategoryInput,
|
||||||
|
UpdateCategoryInput,
|
||||||
|
DeleteCategoryInput,
|
||||||
|
DeleteCategoriesByInstanceIdInput,
|
||||||
|
} from "../validators/categoryValidator";
|
||||||
|
|
||||||
|
export async function createNewCategory(data: CreateCategoryInput) {
|
||||||
|
return await createCategory(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchCategoryData(id: string) {
|
||||||
|
return await getCategory(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchCategoriesByInstance(instanceId: string) {
|
||||||
|
return await getCategoriesByInstance(instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateExistingCategory(data: UpdateCategoryInput) {
|
||||||
|
return await updateCategory(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteExistingCategory(data: DeleteCategoryInput) {
|
||||||
|
return await deleteCategory(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteAllCategoriesByInstance(
|
||||||
|
data: DeleteCategoriesByInstanceIdInput,
|
||||||
|
) {
|
||||||
|
return await deleteAllCategoriesFromInstance(data);
|
||||||
|
}
|
||||||
40
concord-server/src/controller/channelController.ts
Normal file
40
concord-server/src/controller/channelController.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import {
|
||||||
|
createChannel,
|
||||||
|
getChannel,
|
||||||
|
getChannelsByCategory,
|
||||||
|
updateChannel,
|
||||||
|
deleteChannel,
|
||||||
|
deleteAllChannelsFromCategory,
|
||||||
|
} from "../services/channelService";
|
||||||
|
import {
|
||||||
|
CreateChannelInput,
|
||||||
|
UpdateChannelInput,
|
||||||
|
DeleteChannelInput,
|
||||||
|
DeleteChannelsByCategoryIdInput,
|
||||||
|
} from "../validators/channelValidator";
|
||||||
|
|
||||||
|
export async function createNewChannel(data: CreateChannelInput) {
|
||||||
|
return await createChannel(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchChannelData(id: string) {
|
||||||
|
return await getChannel(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchChannelsByCategory(categoryId: string) {
|
||||||
|
return await getChannelsByCategory(categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateExistingChannel(data: UpdateChannelInput) {
|
||||||
|
return await updateChannel(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteExistingChannel(data: DeleteChannelInput) {
|
||||||
|
return await deleteChannel(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteAllChannelsByCategory(
|
||||||
|
data: DeleteChannelsByCategoryIdInput,
|
||||||
|
) {
|
||||||
|
return await deleteAllChannelsFromCategory(data);
|
||||||
|
}
|
||||||
10
concord-server/src/controller/instanceController.ts
Normal file
10
concord-server/src/controller/instanceController.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { createInstance, getAllInstances } from "../services/instanceService";
|
||||||
|
import { CreateInstanceRequest } from "../validators/instanceValidator";
|
||||||
|
|
||||||
|
export async function createInstanceReq(data: CreateInstanceRequest) {
|
||||||
|
return await createInstance(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllInstancesReq() {
|
||||||
|
return await getAllInstances();
|
||||||
|
}
|
||||||
29
concord-server/src/controller/messageController.ts
Normal file
29
concord-server/src/controller/messageController.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import {
|
||||||
|
getMessageInformation,
|
||||||
|
getMessagesBefore,
|
||||||
|
sendMessageToChannel,
|
||||||
|
} from "../services/messageService";
|
||||||
|
|
||||||
|
export async function fetchMessageData(id: string) {
|
||||||
|
return await getMessageInformation(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchMessagesBefore(date: string, channelId: string) {
|
||||||
|
return getMessagesBefore(date, channelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendMessage(
|
||||||
|
channelId: string,
|
||||||
|
userId: string,
|
||||||
|
content: string,
|
||||||
|
token: string,
|
||||||
|
repliedMessageId: string | null,
|
||||||
|
) {
|
||||||
|
return await sendMessageToChannel(
|
||||||
|
channelId,
|
||||||
|
userId,
|
||||||
|
content,
|
||||||
|
token,
|
||||||
|
repliedMessageId,
|
||||||
|
);
|
||||||
|
}
|
||||||
126
concord-server/src/controller/realtime.ts
Normal file
126
concord-server/src/controller/realtime.ts
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import { Context } from "hono";
|
||||||
|
import {
|
||||||
|
sendMessageToChannel,
|
||||||
|
removeMessageFromChannel,
|
||||||
|
} from "../services/realtime.js";
|
||||||
|
import { success } from "zod";
|
||||||
|
|
||||||
|
export async function postMessageToChannel(io: any, c: Context) {
|
||||||
|
try {
|
||||||
|
io = c.get("io");
|
||||||
|
|
||||||
|
const instanceId = c.req.param("instanceId");
|
||||||
|
const categoryId = c.req.param("categoryId");
|
||||||
|
const channelId = c.req.param("channelId");
|
||||||
|
const message = await c.req.json();
|
||||||
|
|
||||||
|
const result = await sendMessageToChannel(
|
||||||
|
instanceId,
|
||||||
|
categoryId,
|
||||||
|
channelId,
|
||||||
|
message,
|
||||||
|
"new_channel_message",
|
||||||
|
io,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result === "Event not implemented") {
|
||||||
|
console.log(
|
||||||
|
"controller::realtime::postMessageToChannel - Failed to send message",
|
||||||
|
);
|
||||||
|
return c.json({
|
||||||
|
success: false,
|
||||||
|
message: "Event not implemented or recognized",
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result === "no acknowledgment") {
|
||||||
|
console.log(
|
||||||
|
"controller::realtime::postMessageToChannel - No acknowledgment received from client",
|
||||||
|
);
|
||||||
|
return c.json({
|
||||||
|
success: false,
|
||||||
|
message: "No acknowledgment received from client",
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error("failed to send message");
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
message: "Message sent successfully",
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
const errMessage = err as Error;
|
||||||
|
console.log("controller::realtime::postMessageToChannel - ", errMessage);
|
||||||
|
return c.json({
|
||||||
|
success: false,
|
||||||
|
message: errMessage.message,
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteMessageFromChannel(io: any, c: Context) {
|
||||||
|
try {
|
||||||
|
io = c.get("io");
|
||||||
|
|
||||||
|
const instanceId = c.req.param("instanceId");
|
||||||
|
const categoryId = c.req.param("categoryId");
|
||||||
|
const channelId = c.req.param("channelId");
|
||||||
|
const messageId = c.req.param("messageId");
|
||||||
|
|
||||||
|
const result = await removeMessageFromChannel(
|
||||||
|
instanceId,
|
||||||
|
categoryId,
|
||||||
|
channelId,
|
||||||
|
messageId,
|
||||||
|
"delete_channel_message",
|
||||||
|
io,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result === "event not implemented") {
|
||||||
|
console.log(
|
||||||
|
"controller::realtime::deleteMessageFromChannel - Event not implemented",
|
||||||
|
);
|
||||||
|
return c.json({
|
||||||
|
success: false,
|
||||||
|
message: "Event not implemented or recognized",
|
||||||
|
status: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result === "no acknowledgment") {
|
||||||
|
console.log(
|
||||||
|
"controller::realtime::deleteMessageFromChannel - No acknowledgment received from client",
|
||||||
|
);
|
||||||
|
return c.json({
|
||||||
|
success: false,
|
||||||
|
message: "No acknowledgment received from client",
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw new Error("failed to delete message");
|
||||||
|
}
|
||||||
|
|
||||||
|
c.json({
|
||||||
|
success: true,
|
||||||
|
message: "Message deleted successfully",
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
const errMessage = err as Error;
|
||||||
|
console.log("services::realtime::deleteMessageFromChannel - ", errMessage);
|
||||||
|
return c.json({
|
||||||
|
success: false,
|
||||||
|
message: errMessage.message,
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
18
concord-server/src/controller/userController.ts
Normal file
18
concord-server/src/controller/userController.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import {
|
||||||
|
getAllUsersFrom,
|
||||||
|
getUserInformation,
|
||||||
|
createUser,
|
||||||
|
} from "../services/userService";
|
||||||
|
import { CreateUserInput } from "../validators/userValidator";
|
||||||
|
|
||||||
|
export async function fetchUserData(id: string) {
|
||||||
|
return await getUserInformation(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchAllUsers(instanceId: string) {
|
||||||
|
return await getAllUsersFrom(instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createNewUser(data: CreateUserInput) {
|
||||||
|
return await createUser(data);
|
||||||
|
}
|
||||||
5
concord-server/src/helper/hashing.ts
Normal file
5
concord-server/src/helper/hashing.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import * as crypto from "crypto";
|
||||||
|
|
||||||
|
export default function shaHash(data: string, salt: string): string {
|
||||||
|
return crypto.createHmac("sha256", salt).update(data).digest("hex");
|
||||||
|
}
|
||||||
@@ -1,9 +1,71 @@
|
|||||||
import { Hono } from 'hono'
|
import { Hono } from "hono";
|
||||||
|
import { cors } from "hono/cors";
|
||||||
|
import { Server as Engine } from "@socket.io/bun-engine";
|
||||||
|
import { Server } from "socket.io";
|
||||||
|
import routes from "./routes/index";
|
||||||
|
import { Scalar } from "@scalar/hono-api-reference";
|
||||||
|
import { openAPIRouteHandler } from "hono-openapi";
|
||||||
|
|
||||||
const app = new Hono()
|
//initialize socket.io server
|
||||||
|
const io = new Server();
|
||||||
|
|
||||||
app.get('/', (c) => {
|
//initialize bun engine
|
||||||
return c.text('Hello Hono!')
|
//then bind to socket.io server
|
||||||
})
|
const engine = new Engine();
|
||||||
|
io.bind(engine);
|
||||||
|
|
||||||
export default app
|
io.on("connection", (socket) => {
|
||||||
|
//get userId and clientId from query params
|
||||||
|
const userId = socket.handshake.query.userId;
|
||||||
|
const clientId = socket.handshake.query.clientId;
|
||||||
|
if (!userId || Array.isArray(userId)) {
|
||||||
|
socket.disconnect();
|
||||||
|
throw new Error("Invalid user ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!clientId || Array.isArray(clientId)) {
|
||||||
|
socket.disconnect();
|
||||||
|
throw new Error("Invalid client ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.join(userId);
|
||||||
|
console.log(
|
||||||
|
`User ${userId} connected. Client ID ${clientId} on socket ${socket.id}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
socket.on("disconnect", () => {
|
||||||
|
console.log(`User ${userId} disconnected from socket ${socket.id}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = new Hono();
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
"*",
|
||||||
|
cors({
|
||||||
|
origin: "http://localhost:5173",
|
||||||
|
allowHeaders: ["Content-Type", "Authorization"],
|
||||||
|
allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
||||||
|
credentials: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
app.route("/api", routes);
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
"/openapi",
|
||||||
|
openAPIRouteHandler(app, {
|
||||||
|
documentation: {
|
||||||
|
info: {
|
||||||
|
title: "Hono API",
|
||||||
|
version: "1.0.0",
|
||||||
|
description: "Greeting API",
|
||||||
|
},
|
||||||
|
servers: [{ url: "http://localhost:3000", description: "Local Server" }],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
app.get("/scalar", Scalar({ url: "/openapi" }));
|
||||||
|
|
||||||
|
export default app;
|
||||||
|
|||||||
315
concord-server/src/routes/categoryRoutes.ts
Normal file
315
concord-server/src/routes/categoryRoutes.ts
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
import {
|
||||||
|
createNewCategory,
|
||||||
|
fetchCategoryData,
|
||||||
|
fetchCategoriesByInstance,
|
||||||
|
updateExistingCategory,
|
||||||
|
deleteExistingCategory,
|
||||||
|
deleteAllCategoriesByInstance,
|
||||||
|
} from "../controller/categoryController";
|
||||||
|
|
||||||
|
import {
|
||||||
|
createCategorySchema,
|
||||||
|
getCategorySchema,
|
||||||
|
getCategoriesByInstanceIdSchema,
|
||||||
|
updateCategorySchema,
|
||||||
|
deleteCategorySchema,
|
||||||
|
deleteCategoriesByInstanceIdSchema,
|
||||||
|
CreateCategoryInput,
|
||||||
|
GetCategoryInput,
|
||||||
|
GetCategoriesByInstanceIdInput,
|
||||||
|
UpdateCategoryInput,
|
||||||
|
DeleteCategoryInput,
|
||||||
|
DeleteCategoriesByInstanceIdInput,
|
||||||
|
} from "../validators/categoryValidator";
|
||||||
|
import { zValidator } from "@hono/zod-validator";
|
||||||
|
import { Hono } from "hono";
|
||||||
|
import { describeRoute, resolver } from "hono-openapi";
|
||||||
|
const categoryRoutes = new Hono();
|
||||||
|
|
||||||
|
// Create a new category
|
||||||
|
categoryRoutes.post(
|
||||||
|
"/",
|
||||||
|
describeRoute({
|
||||||
|
description: "Create a new category",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success creating category",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(createCategorySchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: "Bad Request - Invalid input data",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(createCategorySchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: "Unauthorized - Admin access required",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(createCategorySchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "User Id not found",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(createCategorySchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("json", createCategorySchema),
|
||||||
|
async (c) => {
|
||||||
|
const data = c.req.valid("json") as CreateCategoryInput;
|
||||||
|
const categoryData = await createNewCategory(data);
|
||||||
|
if (categoryData) {
|
||||||
|
return c.json(categoryData);
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "Failed to create category" }, 400);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get a category by ID
|
||||||
|
categoryRoutes.get(
|
||||||
|
"/:id",
|
||||||
|
describeRoute({
|
||||||
|
description: "Get category by id",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success getting category",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(getCategorySchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "Category id not found",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(getCategorySchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (c) => {
|
||||||
|
const id = c.req.param("id");
|
||||||
|
const categoryData = await fetchCategoryData(id);
|
||||||
|
if (categoryData) {
|
||||||
|
return c.json(categoryData);
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "Category not found" }, 404);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get all categories by instance ID
|
||||||
|
categoryRoutes.get(
|
||||||
|
"/instance/:instanceId",
|
||||||
|
describeRoute({
|
||||||
|
description: "Get all categories by instance id",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success getting all categories in instance",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(getCategoriesByInstanceIdSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: "Bad Request - Missing instance ID",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(getCategoriesByInstanceIdSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (c) => {
|
||||||
|
const instanceId = c.req.param("instanceId");
|
||||||
|
if (!instanceId) {
|
||||||
|
return c.json({ error: "No instance id provided" }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const categoryData = await fetchCategoriesByInstance(instanceId);
|
||||||
|
if (categoryData) {
|
||||||
|
return c.json(categoryData);
|
||||||
|
} else {
|
||||||
|
return c.json(
|
||||||
|
{ error: "Error getting all categories from instance" },
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update a category
|
||||||
|
categoryRoutes.put(
|
||||||
|
"/:id",
|
||||||
|
describeRoute({
|
||||||
|
description: "Update an existing category",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success updating category",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(updateCategorySchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: "Bad Request - Invalid input data",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(updateCategorySchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: "Unauthorized - Admin access required",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(updateCategorySchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "Category id or User Id not found",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(updateCategorySchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("json", updateCategorySchema),
|
||||||
|
async (c) => {
|
||||||
|
const id = c.req.param("id");
|
||||||
|
const data = c.req.valid("json") as UpdateCategoryInput;
|
||||||
|
|
||||||
|
// Ensure the ID in the path matches the one in the body
|
||||||
|
if (data.id && data.id !== id) {
|
||||||
|
return c.json({ error: "ID in path does not match ID in body" }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set ID from path if not in body
|
||||||
|
if (!data.id) {
|
||||||
|
data.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const categoryData = await updateExistingCategory(data);
|
||||||
|
if (categoryData) {
|
||||||
|
return c.json(categoryData);
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "Failed to update category" }, 400);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Delete a specific category
|
||||||
|
categoryRoutes.delete(
|
||||||
|
"/:id",
|
||||||
|
describeRoute({
|
||||||
|
description: "Delete an existing category",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success deleting category",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(deleteCategorySchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: "Bad Request - Invalid input data",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(deleteCategorySchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: "Unauthorized - Admin access required",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(deleteCategorySchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "Category id or User Id not found",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(deleteCategorySchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("json", deleteCategorySchema),
|
||||||
|
async (c) => {
|
||||||
|
const id = c.req.param("id");
|
||||||
|
const data = c.req.valid("json") as DeleteCategoryInput;
|
||||||
|
|
||||||
|
// Ensure the ID in the path matches the one in the body
|
||||||
|
if (data.id !== id) {
|
||||||
|
return c.json({ error: "ID in path does not match ID in body" }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const categoryData = await deleteExistingCategory(data);
|
||||||
|
if (categoryData) {
|
||||||
|
return c.json(categoryData);
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "Failed to delete category" }, 400);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Delete all categories by instance ID
|
||||||
|
categoryRoutes.delete(
|
||||||
|
"/instance/:instanceId",
|
||||||
|
describeRoute({
|
||||||
|
description: "Delete all categories by instance id",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success deleting all categories in instance",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(deleteCategoriesByInstanceIdSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: "Bad Request - Invalid input data",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(deleteCategoriesByInstanceIdSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: "Unauthorized - Admin access required",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(deleteCategoriesByInstanceIdSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "Instance id or User Id not found",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(deleteCategoriesByInstanceIdSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("json", deleteCategoriesByInstanceIdSchema),
|
||||||
|
async (c) => {
|
||||||
|
const instanceId = c.req.param("instanceId");
|
||||||
|
const data = c.req.valid("json") as DeleteCategoriesByInstanceIdInput;
|
||||||
|
|
||||||
|
// Ensure the instanceId in the path matches the one in the body
|
||||||
|
if (data.instanceId !== instanceId) {
|
||||||
|
return c.json(
|
||||||
|
{ error: "Instance ID in path does not match Instance ID in body" },
|
||||||
|
400,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const categoryData = await deleteAllCategoriesByInstance(data);
|
||||||
|
if (categoryData) {
|
||||||
|
return c.json(categoryData);
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "Failed to delete categories" }, 400);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export { categoryRoutes };
|
||||||
313
concord-server/src/routes/channelRoutes.ts
Normal file
313
concord-server/src/routes/channelRoutes.ts
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
import {
|
||||||
|
createNewChannel,
|
||||||
|
fetchChannelData,
|
||||||
|
fetchChannelsByCategory,
|
||||||
|
updateExistingChannel,
|
||||||
|
deleteExistingChannel,
|
||||||
|
deleteAllChannelsByCategory,
|
||||||
|
} from "../controller/channelController";
|
||||||
|
|
||||||
|
import {
|
||||||
|
createChannelSchema,
|
||||||
|
getChannelSchema,
|
||||||
|
getChannelsByCategoryIdSchema,
|
||||||
|
updateChannelSchema,
|
||||||
|
deleteChannelSchema,
|
||||||
|
deleteChannelsByCategoryIdSchema,
|
||||||
|
CreateChannelInput,
|
||||||
|
GetChannelInput,
|
||||||
|
GetChannelsByCategoryIdInput,
|
||||||
|
UpdateChannelInput,
|
||||||
|
DeleteChannelInput,
|
||||||
|
DeleteChannelsByCategoryIdInput,
|
||||||
|
} from "../validators/channelValidator";
|
||||||
|
import { zValidator } from "@hono/zod-validator";
|
||||||
|
import { Hono } from "hono";
|
||||||
|
import { describeRoute, resolver } from "hono-openapi";
|
||||||
|
|
||||||
|
const channelRoutes = new Hono();
|
||||||
|
|
||||||
|
// Create a new channel
|
||||||
|
channelRoutes.post(
|
||||||
|
"/",
|
||||||
|
describeRoute({
|
||||||
|
description: "Create a new channel",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success creating channel",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(createChannelSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: "Bad Request - Invalid input data",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(createChannelSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: "Unauthorized - Admin access required",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(createChannelSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "User Id not found",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(createChannelSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("json", createChannelSchema),
|
||||||
|
async (c) => {
|
||||||
|
const data = c.req.valid("json") as CreateChannelInput;
|
||||||
|
const channelData = await createNewChannel(data);
|
||||||
|
if (channelData) {
|
||||||
|
return c.json(channelData);
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "Failed to create channel" }, 400);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get a channel by ID
|
||||||
|
channelRoutes.get(
|
||||||
|
"/:id",
|
||||||
|
describeRoute({
|
||||||
|
description: "Get channel by id",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success getting channel",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(getChannelSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "Channel id not found",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(getChannelSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (c) => {
|
||||||
|
const id = c.req.param("id");
|
||||||
|
const channelData = await fetchChannelData(id);
|
||||||
|
if (channelData) {
|
||||||
|
return c.json(channelData);
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "Channel not found" }, 404);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get all channels by category ID
|
||||||
|
channelRoutes.get(
|
||||||
|
"/category/:categoryId",
|
||||||
|
describeRoute({
|
||||||
|
description: "Get all channels by category id",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success getting all channels in category",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(getChannelsByCategoryIdSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: "Bad Request - Missing category ID",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(getChannelsByCategoryIdSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (c) => {
|
||||||
|
const categoryId = c.req.param("categoryId");
|
||||||
|
if (!categoryId) {
|
||||||
|
return c.json({ error: "No category id provided" }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const channels = await fetchChannelsByCategory(categoryId);
|
||||||
|
if (channels) {
|
||||||
|
return c.json(channels);
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "Error getting channels from category" }, 500);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update a channel
|
||||||
|
channelRoutes.put(
|
||||||
|
"/:id",
|
||||||
|
describeRoute({
|
||||||
|
description: "Update an existing channel",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success updating channel",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(updateChannelSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: "Bad Request - Invalid input data",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(updateChannelSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: "Unauthorized - Admin access required",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(updateChannelSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "Channel id or User Id not found",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(updateChannelSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("json", updateChannelSchema),
|
||||||
|
async (c) => {
|
||||||
|
const id = c.req.param("id");
|
||||||
|
const data = c.req.valid("json") as UpdateChannelInput;
|
||||||
|
|
||||||
|
// Ensure the ID in the path matches the one in the body
|
||||||
|
if (data.id && data.id !== id) {
|
||||||
|
return c.json({ error: "ID in path does not match ID in body" }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set ID from path if not in body
|
||||||
|
if (!data.id) {
|
||||||
|
data.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await updateExistingChannel(data);
|
||||||
|
if (result) {
|
||||||
|
return c.json(result);
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "Failed to update channel" }, 400);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Delete a specific channel
|
||||||
|
channelRoutes.delete(
|
||||||
|
"/:id",
|
||||||
|
describeRoute({
|
||||||
|
description: "Delete an existing channel",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success deleting channel",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(deleteChannelSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: "Bad Request - Invalid input data",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(deleteChannelSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: "Unauthorized - Admin access required",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(deleteChannelSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "Channel id or User Id not found",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(deleteChannelSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("json", deleteChannelSchema),
|
||||||
|
async (c) => {
|
||||||
|
const id = c.req.param("id");
|
||||||
|
const data = c.req.valid("json") as DeleteChannelInput;
|
||||||
|
|
||||||
|
// Ensure the ID in the path matches the one in the body
|
||||||
|
if (data.id !== id) {
|
||||||
|
return c.json({ error: "ID in path does not match ID in body" }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await deleteExistingChannel(data);
|
||||||
|
if (result) {
|
||||||
|
return c.json({ success: true });
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "Failed to delete channel" }, 400);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Delete all channels by category ID
|
||||||
|
channelRoutes.delete(
|
||||||
|
"/category/:categoryId",
|
||||||
|
describeRoute({
|
||||||
|
description: "Delete all channels by category id",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success deleting all channels in category",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(deleteChannelsByCategoryIdSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: "Bad Request - Invalid input data",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(deleteChannelsByCategoryIdSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: "Unauthorized - Admin access required",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(deleteChannelsByCategoryIdSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "Category id or User Id not found",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(deleteChannelsByCategoryIdSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("json", deleteChannelsByCategoryIdSchema),
|
||||||
|
async (c) => {
|
||||||
|
const categoryId = c.req.param("categoryId");
|
||||||
|
const data = c.req.valid("json") as DeleteChannelsByCategoryIdInput;
|
||||||
|
|
||||||
|
// Ensure the categoryId in the path matches the one in the body
|
||||||
|
if (data.categoryId !== categoryId) {
|
||||||
|
return c.json(
|
||||||
|
{ error: "Category ID in path does not match Category ID in body" },
|
||||||
|
400,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await deleteAllChannelsByCategory(data);
|
||||||
|
if (result) {
|
||||||
|
return c.json({ success: true });
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "Failed to delete channels" }, 400);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export { channelRoutes };
|
||||||
17
concord-server/src/routes/index.ts
Normal file
17
concord-server/src/routes/index.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
//place exported routes below this line
|
||||||
|
import { Hono } from "hono";
|
||||||
|
import userRoutes from "./userRoutes";
|
||||||
|
import messageRoutes from "./messageRoutes";
|
||||||
|
import { channelRoutes } from "./channelRoutes";
|
||||||
|
import instanceRoutes from "./instanceRoutes";
|
||||||
|
import { categoryRoutes } from "./categoryRoutes";
|
||||||
|
|
||||||
|
const routes = new Hono();
|
||||||
|
|
||||||
|
routes.route("/user", userRoutes);
|
||||||
|
routes.route("/message", messageRoutes);
|
||||||
|
routes.route("/channel", channelRoutes);
|
||||||
|
routes.route("/instance", instanceRoutes);
|
||||||
|
routes.route("/category", categoryRoutes);
|
||||||
|
|
||||||
|
export default routes;
|
||||||
77
concord-server/src/routes/instanceRoutes.ts
Normal file
77
concord-server/src/routes/instanceRoutes.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import { Hono } from "hono";
|
||||||
|
import { describeRoute, resolver } from "hono-openapi";
|
||||||
|
import {
|
||||||
|
createInstanceRequestSchema,
|
||||||
|
getAllInstancesResponseSchema,
|
||||||
|
} from "../validators/instanceValidator";
|
||||||
|
import { zValidator } from "@hono/zod-validator";
|
||||||
|
import {
|
||||||
|
createInstanceReq,
|
||||||
|
getAllInstancesReq,
|
||||||
|
} from "../controller/instanceController";
|
||||||
|
|
||||||
|
const instanceRoutes = new Hono();
|
||||||
|
|
||||||
|
instanceRoutes.post(
|
||||||
|
"",
|
||||||
|
describeRoute({
|
||||||
|
description: "Create instance",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Instance created",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(createInstanceRequestSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: "Invalid request",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("json", createInstanceRequestSchema),
|
||||||
|
async (c) => {
|
||||||
|
const data = await c.req.json();
|
||||||
|
if (!data) {
|
||||||
|
return c.json({ error: "could not parse data" }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const instance = await createInstanceReq(data);
|
||||||
|
return c.json(instance, 201);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
instanceRoutes.get(
|
||||||
|
"",
|
||||||
|
describeRoute({
|
||||||
|
description: "Get all instances",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "List of all instances",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(getAllInstancesResponseSchema),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
500: {
|
||||||
|
description: "Server error",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (c) => {
|
||||||
|
const instances = await getAllInstancesReq();
|
||||||
|
if (instances.success) {
|
||||||
|
return c.json(instances, 200);
|
||||||
|
} else {
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
error: instances.error || "Failed to fetch instances",
|
||||||
|
},
|
||||||
|
500,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export default instanceRoutes;
|
||||||
146
concord-server/src/routes/messageRoutes.ts
Normal file
146
concord-server/src/routes/messageRoutes.ts
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
import { Hono } from "hono";
|
||||||
|
import { describeResponse, describeRoute, resolver } from "hono-openapi";
|
||||||
|
import {
|
||||||
|
getMessageByIdSchema,
|
||||||
|
getMessagesBeforeDate,
|
||||||
|
sendMessageSchema,
|
||||||
|
} from "../validators/messageValidator";
|
||||||
|
import { zValidator } from "@hono/zod-validator";
|
||||||
|
import {
|
||||||
|
fetchMessageData,
|
||||||
|
fetchMessagesBefore,
|
||||||
|
sendMessage,
|
||||||
|
} from "../controller/messageController";
|
||||||
|
|
||||||
|
const messageRoutes = new Hono();
|
||||||
|
|
||||||
|
messageRoutes.get(
|
||||||
|
"/:id",
|
||||||
|
describeRoute({
|
||||||
|
description: "Get message by id",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success getting message",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(getMessageByIdSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "Message id not found",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(getMessageByIdSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("param", getMessageByIdSchema),
|
||||||
|
async (c) => {
|
||||||
|
const id = c.req.param("id");
|
||||||
|
const messageData = await fetchMessageData(id);
|
||||||
|
|
||||||
|
if (messageData) {
|
||||||
|
return c.json(messageData, 200);
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "Message not found" }, 404);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
messageRoutes.get(
|
||||||
|
"",
|
||||||
|
describeRoute({
|
||||||
|
description: "Get up to 50 messages prior to given datetime",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success getting up to 50 messages",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(getMessagesBeforeDate) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("query", getMessagesBeforeDate),
|
||||||
|
async (c) => {
|
||||||
|
const date = c.req.query("date");
|
||||||
|
if (!date) {
|
||||||
|
return c.json({ error: "date not provided" }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const channelId = c.req.query("channelId");
|
||||||
|
if (!channelId) {
|
||||||
|
return c.json({ error: "channelId not provided" }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const messagesArr = await fetchMessagesBefore(date, channelId);
|
||||||
|
|
||||||
|
if (messagesArr) {
|
||||||
|
return c.json(messagesArr, 200);
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "Failed to fetch messages" }, 500);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
messageRoutes.post(
|
||||||
|
"",
|
||||||
|
describeRoute({
|
||||||
|
description: "Send a message to a channel",
|
||||||
|
responses: {
|
||||||
|
201: {
|
||||||
|
description: "Message sent successfully",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(sendMessageSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
401: {
|
||||||
|
description: "Unauthorized - invalid token or user credentials",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: {
|
||||||
|
type: "object",
|
||||||
|
properties: { error: { type: "string" } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
500: {
|
||||||
|
description: "Server error",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: {
|
||||||
|
type: "object",
|
||||||
|
properties: { error: { type: "string" } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("json", sendMessageSchema),
|
||||||
|
async (c) => {
|
||||||
|
const { channelId, userId, content, token, repliedMessageId } =
|
||||||
|
await c.req.json();
|
||||||
|
|
||||||
|
const result = await sendMessage(
|
||||||
|
channelId,
|
||||||
|
userId,
|
||||||
|
content,
|
||||||
|
token,
|
||||||
|
repliedMessageId || null,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return c.json(result, 201);
|
||||||
|
} else {
|
||||||
|
return c.json(
|
||||||
|
{
|
||||||
|
error:
|
||||||
|
"Failed to send message. Check your credentials and try again.",
|
||||||
|
},
|
||||||
|
401,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export default messageRoutes;
|
||||||
29
concord-server/src/routes/realtime.ts
Normal file
29
concord-server/src/routes/realtime.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Hono } from "hono";
|
||||||
|
import { zValidator } from "@hono/zod-validator";
|
||||||
|
import { describeRoute, resolver } from "hono-openapi";
|
||||||
|
import {
|
||||||
|
postMessageToChannel,
|
||||||
|
deleteMessageFromChannel,
|
||||||
|
} from "../controller/realtime";
|
||||||
|
|
||||||
|
const app = new Hono();
|
||||||
|
|
||||||
|
app.post(
|
||||||
|
"message/",
|
||||||
|
zValidator({
|
||||||
|
body: z.object({
|
||||||
|
content: z.string().min(1).max(500),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
async (c) => {
|
||||||
|
const { instanceId, categoryId, channelId } = c.req.params;
|
||||||
|
const { content } = c.req.body;
|
||||||
|
|
||||||
|
return postMessageToChannel(c.get("io"), {
|
||||||
|
instanceId,
|
||||||
|
categoryId,
|
||||||
|
channelId,
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
110
concord-server/src/routes/userRoutes.ts
Normal file
110
concord-server/src/routes/userRoutes.ts
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import { Hono } from "hono";
|
||||||
|
import {
|
||||||
|
fetchAllUsers,
|
||||||
|
fetchUserData,
|
||||||
|
createNewUser,
|
||||||
|
} from "../controller/userController";
|
||||||
|
import {
|
||||||
|
createUserSchema,
|
||||||
|
queryAllUsersByInstanceId,
|
||||||
|
queryUserByIdSchema,
|
||||||
|
} from "../validators/userValidator";
|
||||||
|
import { zValidator } from "@hono/zod-validator";
|
||||||
|
import { describeRoute, resolver } from "hono-openapi";
|
||||||
|
const userRoutes = new Hono();
|
||||||
|
|
||||||
|
userRoutes.get(
|
||||||
|
"/:id",
|
||||||
|
describeRoute({
|
||||||
|
description: "Get user by id",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success getting user",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(queryUserByIdSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
404: {
|
||||||
|
description: "User id not found",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(queryUserByIdSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("param", queryUserByIdSchema),
|
||||||
|
async (c) => {
|
||||||
|
const id = c.req.param("id");
|
||||||
|
const userData = await fetchUserData(id);
|
||||||
|
if (userData) {
|
||||||
|
return c.json(userData);
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "User not found" }, 404);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
userRoutes.get(
|
||||||
|
"",
|
||||||
|
describeRoute({
|
||||||
|
description: "Get all users by instance id",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Success getting all users in instance",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(queryAllUsersByInstanceId) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("query", queryAllUsersByInstanceId),
|
||||||
|
async (c) => {
|
||||||
|
const instanceId = c.req.query("instanceId");
|
||||||
|
if (!instanceId) {
|
||||||
|
return c.json({ error: "No instance id provided" }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const userData = await fetchAllUsers(instanceId);
|
||||||
|
if (userData) {
|
||||||
|
return c.json(userData);
|
||||||
|
} else {
|
||||||
|
return c.json({ error: "Error getting all users from instance" }, 500);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
userRoutes.post(
|
||||||
|
"",
|
||||||
|
describeRoute({
|
||||||
|
description: "Create a new user",
|
||||||
|
responses: {
|
||||||
|
201: {
|
||||||
|
description: "Success",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(createUserSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
description: "Bad request (user exists)",
|
||||||
|
content: {
|
||||||
|
"application/json": { schema: resolver(createUserSchema) },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator("json", createUserSchema),
|
||||||
|
async (c) => {
|
||||||
|
try {
|
||||||
|
const data = await c.req.json();
|
||||||
|
const newUser = await createNewUser(data);
|
||||||
|
if (!newUser) {
|
||||||
|
return c.json({ error: "User already exists" }, 400);
|
||||||
|
}
|
||||||
|
return c.json(newUser, 201);
|
||||||
|
} catch (error) {
|
||||||
|
return c.json({ error: "Error creating user" }, 500);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export default userRoutes;
|
||||||
457
concord-server/src/services/channelService.ts
Normal file
457
concord-server/src/services/channelService.ts
Normal file
@@ -0,0 +1,457 @@
|
|||||||
|
import { Channel, Category } from "@prisma/client";
|
||||||
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
import { getUserInformation, getUserCredentials } from "./userService";
|
||||||
|
import {
|
||||||
|
CreateChannelInput,
|
||||||
|
UpdateChannelInput,
|
||||||
|
DeleteChannelInput,
|
||||||
|
DeleteChannelsByCategoryIdInput,
|
||||||
|
} from "../validators/channelValidator";
|
||||||
|
import {
|
||||||
|
UpdateCategoryInput,
|
||||||
|
DeleteCategoryInput,
|
||||||
|
DeleteCategoriesByInstanceIdInput,
|
||||||
|
CreateCategoryInput,
|
||||||
|
} from "../validators/categoryValidator";
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
export async function createCategory(
|
||||||
|
data: CreateCategoryInput,
|
||||||
|
): Promise<Category | null> {
|
||||||
|
try {
|
||||||
|
//Confirm if user exists and is admin
|
||||||
|
const requestingUser = await getUserInformation(data.requestingUserId);
|
||||||
|
const requestingUserCredentials = await getUserCredentials(
|
||||||
|
data.requestingUserId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!requestingUser ||
|
||||||
|
!requestingUserCredentials ||
|
||||||
|
!requestingUser.admin ||
|
||||||
|
requestingUserCredentials.token == null ||
|
||||||
|
data.requestingUserToken != requestingUserCredentials.token
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newCategory = await prisma.category.create({
|
||||||
|
data: {
|
||||||
|
name: data.name,
|
||||||
|
position: data.position,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!newCategory) {
|
||||||
|
throw new Error("could not create category");
|
||||||
|
}
|
||||||
|
|
||||||
|
let curInstance;
|
||||||
|
if (data.instanceId) {
|
||||||
|
curInstance = await prisma.instance.findUnique({
|
||||||
|
where: {
|
||||||
|
id: data.instanceId,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
Category: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!curInstance) {
|
||||||
|
throw new Error("could not find instance to add category to");
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.category.update({
|
||||||
|
where: {
|
||||||
|
id: newCategory.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
instanceId: curInstance.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return newCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newCategory;
|
||||||
|
} catch (err) {
|
||||||
|
console.log("services::channelService::createCategory - ", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCategory(
|
||||||
|
categoryId: string,
|
||||||
|
): Promise<Category | null> {
|
||||||
|
try {
|
||||||
|
const category = await prisma.category.findUnique({
|
||||||
|
where: {
|
||||||
|
id: categoryId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!category) {
|
||||||
|
throw new Error("could not find category");
|
||||||
|
}
|
||||||
|
|
||||||
|
return category;
|
||||||
|
} catch (err) {
|
||||||
|
console.log("services::channelService::getCategory - ", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCategoriesByInstance(
|
||||||
|
instanceId: string,
|
||||||
|
): Promise<Category[] | null> {
|
||||||
|
try {
|
||||||
|
const categories = await prisma.category.findMany({
|
||||||
|
where: {
|
||||||
|
instanceId: instanceId,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
Channel: true,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
position: "asc",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!categories) {
|
||||||
|
throw new Error("could not find categories for instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
return categories;
|
||||||
|
} catch (err) {
|
||||||
|
console.log("services::channelService::getCategoriesByInstance - ", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateCategory(
|
||||||
|
data: UpdateCategoryInput,
|
||||||
|
): Promise<Category | null> {
|
||||||
|
try {
|
||||||
|
//Confirm if user exists and is admin
|
||||||
|
const requestingUser = await getUserInformation(data.requestingUserId);
|
||||||
|
const requestingUserCredentials = await getUserCredentials(
|
||||||
|
data.requestingUserId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!requestingUser ||
|
||||||
|
!requestingUserCredentials ||
|
||||||
|
!requestingUser.admin ||
|
||||||
|
requestingUserCredentials.token == null ||
|
||||||
|
data.requestingUserToken != requestingUserCredentials.token
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedCategory = await prisma.category.update({
|
||||||
|
where: {
|
||||||
|
id: data.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
name: data.name,
|
||||||
|
position: data.position,
|
||||||
|
Channel: data.channels ? { set: data.channels } : undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!updatedCategory) {
|
||||||
|
throw new Error("could not update category");
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedCategory;
|
||||||
|
} catch (err) {
|
||||||
|
console.log("services::channelService::updateCategory - ", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteCategory(
|
||||||
|
data: DeleteCategoryInput,
|
||||||
|
): Promise<boolean | null> {
|
||||||
|
try {
|
||||||
|
//Confirm if user exists and is admin
|
||||||
|
const requestingUser = await getUserInformation(data.requestingUserId);
|
||||||
|
const requestingUserCredentials = await getUserCredentials(
|
||||||
|
data.requestingUserId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!requestingUser ||
|
||||||
|
!requestingUserCredentials ||
|
||||||
|
!requestingUser.admin ||
|
||||||
|
requestingUserCredentials.token == null ||
|
||||||
|
data.requestingUserToken != requestingUserCredentials.token
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteAllChannels = await prisma.channel.deleteMany({
|
||||||
|
where: {
|
||||||
|
categoryId: data.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (deleteAllChannels.count === 0) {
|
||||||
|
throw new Error("could not delete channels from category");
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletedCategory = await prisma.category.delete({
|
||||||
|
where: {
|
||||||
|
id: data.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!deletedCategory) {
|
||||||
|
throw new Error("could not delete category");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.log("services::channelService::deleteCategory - ", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteAllCategoriesFromInstance(
|
||||||
|
data: DeleteCategoriesByInstanceIdInput,
|
||||||
|
): Promise<boolean | null> {
|
||||||
|
try {
|
||||||
|
//Confirm if user exists and is admin
|
||||||
|
const requestingUser = await getUserInformation(data.requestingUserId);
|
||||||
|
const requestingUserCredentials = await getUserCredentials(
|
||||||
|
data.requestingUserId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!requestingUser ||
|
||||||
|
!requestingUserCredentials ||
|
||||||
|
!requestingUser.admin ||
|
||||||
|
requestingUserCredentials.token == null ||
|
||||||
|
data.requestingUserToken != requestingUserCredentials.token
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletedCategories = await prisma.category.deleteMany({
|
||||||
|
where: {
|
||||||
|
instanceId: data.instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (deletedCategories.count === 0) {
|
||||||
|
throw new Error("could not delete categories from instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(
|
||||||
|
"services::channelService::deleteAllCategoriesFromInstance - ",
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createChannel(
|
||||||
|
data: CreateChannelInput,
|
||||||
|
): Promise<Channel | null> {
|
||||||
|
try {
|
||||||
|
//Confirm if user exists and is admin
|
||||||
|
const requestingUser = await getUserInformation(data.requestingUserId);
|
||||||
|
const requestingUserCredentials = await getUserCredentials(
|
||||||
|
data.requestingUserId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!requestingUser ||
|
||||||
|
!requestingUserCredentials ||
|
||||||
|
!requestingUser.admin ||
|
||||||
|
requestingUserCredentials.token == null ||
|
||||||
|
data.requestingUserToken != requestingUserCredentials.token
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newChannel = await prisma.channel.create({
|
||||||
|
data: {
|
||||||
|
type: data.type,
|
||||||
|
name: data.name,
|
||||||
|
description: data.description,
|
||||||
|
categoryId: data.categoryId ? data.categoryId : null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!newChannel) {
|
||||||
|
throw new Error("could not create channel");
|
||||||
|
}
|
||||||
|
|
||||||
|
return newChannel;
|
||||||
|
} catch (err) {
|
||||||
|
console.log("services::channelService::createChannel - ", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getChannel(channelId: string): Promise<Channel | null> {
|
||||||
|
try {
|
||||||
|
const channel = await prisma.channel.findUnique({
|
||||||
|
where: {
|
||||||
|
id: channelId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!channel) {
|
||||||
|
throw new Error("could not find channel");
|
||||||
|
}
|
||||||
|
|
||||||
|
return channel;
|
||||||
|
} catch (err) {
|
||||||
|
console.log("services::channelService::getChannel - ", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getChannelsByCategory(
|
||||||
|
categoryId: string,
|
||||||
|
): Promise<Channel[] | null> {
|
||||||
|
try {
|
||||||
|
const channels = await prisma.channel.findMany({
|
||||||
|
where: {
|
||||||
|
categoryId: categoryId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!channels) {
|
||||||
|
throw new Error("could not find channels for category");
|
||||||
|
}
|
||||||
|
return channels;
|
||||||
|
} catch (err) {
|
||||||
|
console.log("services::channelService::getChannelsByCategory - ", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateChannel(
|
||||||
|
data: UpdateChannelInput,
|
||||||
|
): Promise<Channel | null> {
|
||||||
|
try {
|
||||||
|
//Confirm if user exists and is admin
|
||||||
|
const requestingUser = await getUserInformation(data.requestingUserId);
|
||||||
|
const requestingUserCredentials = await getUserCredentials(
|
||||||
|
data.requestingUserId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!requestingUser ||
|
||||||
|
!requestingUserCredentials ||
|
||||||
|
!requestingUser.admin ||
|
||||||
|
requestingUserCredentials.token == null ||
|
||||||
|
data.requestingUserToken != requestingUserCredentials.token
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedChannel = await prisma.channel.update({
|
||||||
|
where: {
|
||||||
|
id: data.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
name: data.name,
|
||||||
|
description: data.description,
|
||||||
|
categoryId: data.categoryId ? data.categoryId : undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!updatedChannel) {
|
||||||
|
throw new Error("could not update channel");
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedChannel;
|
||||||
|
} catch (err) {
|
||||||
|
console.log("services::channelService::updateChannel - ", err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteChannel(
|
||||||
|
data: DeleteChannelInput,
|
||||||
|
): Promise<boolean | null> {
|
||||||
|
try {
|
||||||
|
//Confirm if user exists and is admin
|
||||||
|
const requestingUser = await getUserInformation(data.requestingUserId);
|
||||||
|
const requestingUserCredentials = await getUserCredentials(
|
||||||
|
data.requestingUserId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!requestingUser ||
|
||||||
|
!requestingUserCredentials ||
|
||||||
|
!requestingUser.admin ||
|
||||||
|
requestingUserCredentials.token == null ||
|
||||||
|
data.requestingUserToken != requestingUserCredentials.token
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletedChannel = await prisma.channel.delete({
|
||||||
|
where: {
|
||||||
|
id: data.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!deletedChannel) {
|
||||||
|
throw new Error("could not delete channel");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.log("services::channelService::deleteChannel - ", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteAllChannelsFromCategory(
|
||||||
|
data: DeleteChannelsByCategoryIdInput,
|
||||||
|
): Promise<boolean | null> {
|
||||||
|
try {
|
||||||
|
//Confirm if user exists and is admin
|
||||||
|
const requestingUser = await getUserInformation(data.requestingUserId);
|
||||||
|
const requestingUserCredentials = await getUserCredentials(
|
||||||
|
data.requestingUserId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!requestingUser ||
|
||||||
|
!requestingUserCredentials ||
|
||||||
|
!requestingUser.admin ||
|
||||||
|
requestingUserCredentials.token == null ||
|
||||||
|
data.requestingUserToken != requestingUserCredentials.token
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletedChannels = await prisma.channel.deleteMany({
|
||||||
|
where: {
|
||||||
|
categoryId: data.categoryId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (deletedChannels.count === 0) {
|
||||||
|
throw new Error("could not delete channels from category");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(
|
||||||
|
"services::channelService::deleteAllChannelsFromCategory - ",
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
concord-server/src/services/instanceService.ts
Normal file
59
concord-server/src/services/instanceService.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
import { CreateInstanceRequest } from "../validators/instanceValidator";
|
||||||
|
import { getUserCredentials, getUserInformation } from "./userService";
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
export async function createInstance(data: CreateInstanceRequest) {
|
||||||
|
try {
|
||||||
|
const creds = await getUserCredentials(data.requestingUserId);
|
||||||
|
const user = await getUserInformation(data.requestingUserId);
|
||||||
|
if (
|
||||||
|
!creds ||
|
||||||
|
creds.token != data.requestingUserToken ||
|
||||||
|
!user ||
|
||||||
|
!user.admin
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newInstance = await prisma.instance.create({
|
||||||
|
data: {
|
||||||
|
name: data.name,
|
||||||
|
icon: data.icon,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: newInstance,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error creating instance:", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: "Failed to create instance",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllInstances() {
|
||||||
|
try {
|
||||||
|
const instances = await prisma.instance.findMany({
|
||||||
|
orderBy: {
|
||||||
|
createdAt: "desc",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: instances,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching instances:", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: "Failed to fetch instances",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
230
concord-server/src/services/messageService.ts
Normal file
230
concord-server/src/services/messageService.ts
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
import { getUserCredentials } from "./userService";
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
export async function getMessageInformation(id: string): Promise<{
|
||||||
|
id: string;
|
||||||
|
channelId: string;
|
||||||
|
userId: string;
|
||||||
|
text: string;
|
||||||
|
deleted: boolean;
|
||||||
|
replies: null | {
|
||||||
|
messageId: string;
|
||||||
|
repliesToId: string;
|
||||||
|
repliesToText: string;
|
||||||
|
};
|
||||||
|
} | null> {
|
||||||
|
try {
|
||||||
|
if (!id) {
|
||||||
|
throw new Error("missing messageId");
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = await prisma.message.findUnique({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!message) {
|
||||||
|
throw new Error("could not find message");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this message is a reply to another message
|
||||||
|
const replyData = await prisma.reply.findFirst({
|
||||||
|
where: {
|
||||||
|
messageId: id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let originalMessage = null;
|
||||||
|
if (replyData) {
|
||||||
|
originalMessage = await prisma.message.findUnique({
|
||||||
|
where: {
|
||||||
|
id: replyData.repliesToId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: message.id,
|
||||||
|
channelId: message.channelId!,
|
||||||
|
userId: message.userId!,
|
||||||
|
text: message.text,
|
||||||
|
deleted: message.deleted,
|
||||||
|
replies: originalMessage
|
||||||
|
? {
|
||||||
|
messageId: message.id,
|
||||||
|
repliesToId: originalMessage.id,
|
||||||
|
repliesToText: originalMessage.text,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
const errMessage = err as Error;
|
||||||
|
|
||||||
|
if (errMessage.message === "missing messageId") {
|
||||||
|
console.log(
|
||||||
|
"services::actions::getMessageInformation - missing messageId",
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errMessage.message === "could not find message") {
|
||||||
|
console.log(
|
||||||
|
"services::actions::getMessageInformation - unable to find message",
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"services::actions::getMessageInformation - unknown error",
|
||||||
|
errMessage,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMessagesBefore(date: string, channelId: string) {
|
||||||
|
try {
|
||||||
|
if (!date || !channelId) {
|
||||||
|
throw new Error("missing date or channelId");
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = await prisma.message.findMany({
|
||||||
|
where: {
|
||||||
|
channelId: channelId,
|
||||||
|
createdAt: {
|
||||||
|
lt: new Date(date),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: "desc",
|
||||||
|
},
|
||||||
|
take: 50,
|
||||||
|
});
|
||||||
|
|
||||||
|
const messageInformationPromises = messages.map(async (message) => {
|
||||||
|
const replyData = await prisma.reply.findFirst({
|
||||||
|
where: {
|
||||||
|
messageId: message.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let originalMessage = null;
|
||||||
|
if (replyData) {
|
||||||
|
originalMessage = await prisma.message.findUnique({
|
||||||
|
where: {
|
||||||
|
id: replyData.repliesToId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: message.id,
|
||||||
|
channelId: message.channelId!,
|
||||||
|
userId: message.userId!,
|
||||||
|
text: message.text,
|
||||||
|
deleted: message.deleted,
|
||||||
|
replies: originalMessage
|
||||||
|
? {
|
||||||
|
messageId: message.id,
|
||||||
|
repliesToId: originalMessage.id,
|
||||||
|
repliesToText: originalMessage.text,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(messageInformationPromises);
|
||||||
|
} catch (err) {
|
||||||
|
const errMessage = err as Error;
|
||||||
|
|
||||||
|
if (errMessage.message === "missing date or channelId") {
|
||||||
|
console.log(
|
||||||
|
"services::actions::getMessagesBefore - missing date or channelId",
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"services::actions::getMessagesBefore - unknown error",
|
||||||
|
errMessage,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendMessageToChannel(
|
||||||
|
channelId: string,
|
||||||
|
userId: string,
|
||||||
|
content: string,
|
||||||
|
token: string,
|
||||||
|
repliedMessageId: string | null,
|
||||||
|
): Promise<{
|
||||||
|
id: string;
|
||||||
|
channelId: string;
|
||||||
|
userId: string;
|
||||||
|
text: string;
|
||||||
|
deleted: boolean;
|
||||||
|
replies: null | {
|
||||||
|
messageId: string;
|
||||||
|
repliesToId: string;
|
||||||
|
repliesToText: string;
|
||||||
|
};
|
||||||
|
} | null> {
|
||||||
|
try {
|
||||||
|
const userCreds = await getUserCredentials(userId);
|
||||||
|
if (!userCreds || userCreds.token != token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newMessage = await prisma.message.create({
|
||||||
|
data: {
|
||||||
|
channelId: channelId,
|
||||||
|
userId: userId,
|
||||||
|
text: content,
|
||||||
|
deleted: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!newMessage) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let origMessage;
|
||||||
|
if (repliedMessageId) {
|
||||||
|
origMessage = await prisma.message.findUnique({
|
||||||
|
where: {
|
||||||
|
id: repliedMessageId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!origMessage) {
|
||||||
|
throw new Error("could not find original message to reply to");
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.reply.create({
|
||||||
|
data: {
|
||||||
|
messageId: newMessage.id,
|
||||||
|
repliesToId: origMessage.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...newMessage,
|
||||||
|
channelId: newMessage.channelId!,
|
||||||
|
userId: newMessage.userId!,
|
||||||
|
replies: origMessage
|
||||||
|
? {
|
||||||
|
messageId: newMessage.id,
|
||||||
|
repliesToId: origMessage?.id,
|
||||||
|
repliesToText: origMessage?.text,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
90
concord-server/src/services/realtime.ts
Normal file
90
concord-server/src/services/realtime.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import { readonly } from "zod";
|
||||||
|
|
||||||
|
const EVENTS = {
|
||||||
|
NEW_CHANNEL_MESSAGE: "new_channel_message",
|
||||||
|
DELETE_CHANNEL_MESSAGE: "delete_channel_message",
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function sendMessageToChannel(
|
||||||
|
instanceId: string,
|
||||||
|
categoryId: string,
|
||||||
|
channelId: string,
|
||||||
|
message: any,
|
||||||
|
event: string,
|
||||||
|
io: any,
|
||||||
|
): Promise<string | boolean> {
|
||||||
|
try {
|
||||||
|
//TODO: implement middleware to replace this
|
||||||
|
if (EVENTS.NEW_CHANNEL_MESSAGE === event) {
|
||||||
|
throw new Error("Event not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: add prisma to save channel message to DB
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
io.to(instanceId).emit(event, message, (ack: any) => {
|
||||||
|
if (ack && ack.status === "received") {
|
||||||
|
console.log(`Message ${ack.messageId} acknowledged by client.`);
|
||||||
|
resolve(true);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
"services::realtime::sendMessageToChannel No acknowledgment received from client.",
|
||||||
|
);
|
||||||
|
resolve("no acknowledgment");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
const errMessage = err as Error;
|
||||||
|
if (errMessage.message === "Event not implemented") {
|
||||||
|
console.log(
|
||||||
|
`services::realtime::sendMessageToChannel - Event not implemented. Attempted event: ${event}`,
|
||||||
|
);
|
||||||
|
return "event not implemented";
|
||||||
|
}
|
||||||
|
console.log("services::realtime::sendMessageToChannel - ", errMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeMessageFromChannel(
|
||||||
|
instanceId: string,
|
||||||
|
categoryId: string,
|
||||||
|
channelId: string,
|
||||||
|
messageId: string,
|
||||||
|
event: string,
|
||||||
|
io: any,
|
||||||
|
): Promise<string | boolean> {
|
||||||
|
try {
|
||||||
|
//TODO: implement middleware to replace this
|
||||||
|
if (EVENTS.DELETE_CHANNEL_MESSAGE === event) {
|
||||||
|
throw new Error("event not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: add prisma to flag a channel message as deleted
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
io.to(instanceId).emit(event, { messageId }, (ack: any) => {
|
||||||
|
if (ack && ack.status === "received") {
|
||||||
|
console.log(`Message ${ack.messageId} acknowledged by client.`);
|
||||||
|
resolve(true);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
"services::realtime::deleteMessageFromChannel No acknowledgment received from client.",
|
||||||
|
);
|
||||||
|
resolve("no acknowledgment");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
const errMessage = err as Error;
|
||||||
|
if (errMessage.message === "Event not implemented") {
|
||||||
|
console.log(
|
||||||
|
`services::realtime::deleteMessageFromChannel - Event not implemented. Attempted event: ${event}`,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
console.log("services::realtime::deleteMessageFromChannel - ", errMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
297
concord-server/src/services/userService.ts
Normal file
297
concord-server/src/services/userService.ts
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
import {
|
||||||
|
Message,
|
||||||
|
MessagePing,
|
||||||
|
PrismaClient,
|
||||||
|
Role,
|
||||||
|
UserAuth,
|
||||||
|
} from "@prisma/client";
|
||||||
|
import { CreateUserInput } from "../validators/userValidator";
|
||||||
|
import shaHash from "../helper/hashing";
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
export async function createUser(data: CreateUserInput): Promise<{
|
||||||
|
username: string;
|
||||||
|
nickname: string | null;
|
||||||
|
bio: string | null;
|
||||||
|
picture: string | null;
|
||||||
|
banner: string | null;
|
||||||
|
status: string;
|
||||||
|
admin: boolean;
|
||||||
|
} | null> {
|
||||||
|
const requestingUser = await getUserInformation(data.requestingUserId);
|
||||||
|
const requestingUserCredentials = await getUserCredentials(
|
||||||
|
data.requestingUserId,
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
!requestingUser ||
|
||||||
|
!requestingUserCredentials ||
|
||||||
|
!requestingUser.admin ||
|
||||||
|
requestingUserCredentials.token == null ||
|
||||||
|
data.requestingUserToken != requestingUserCredentials.token
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((await prisma.user.count({ where: { username: data.username } })) >= 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userData = await prisma.user.create({
|
||||||
|
data: {
|
||||||
|
username: data.username,
|
||||||
|
nickname: data.nickname,
|
||||||
|
bio: data.bio,
|
||||||
|
picture: data.picture,
|
||||||
|
banner: data.banner,
|
||||||
|
status: data.status,
|
||||||
|
admin: data.admin,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
!(await prisma.userAuth.create({
|
||||||
|
data: {
|
||||||
|
userId: userData.id,
|
||||||
|
password: shaHash(data.passwordhash, userData.id),
|
||||||
|
token: null,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return userData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserCredentials(userId: string): Promise<{
|
||||||
|
userId: string;
|
||||||
|
password: string;
|
||||||
|
token: string | null;
|
||||||
|
} | null> {
|
||||||
|
try {
|
||||||
|
if (!userId) {
|
||||||
|
throw new Error("missing userId");
|
||||||
|
}
|
||||||
|
|
||||||
|
const userAuth = await prisma.userAuth.findUnique({
|
||||||
|
where: {
|
||||||
|
userId: userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!userAuth) {
|
||||||
|
throw new Error("could not find user credentials");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
userId: userAuth.userId,
|
||||||
|
password: userAuth.password,
|
||||||
|
token: userAuth.token,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
const errMessage = err as Error;
|
||||||
|
|
||||||
|
if (errMessage.message === "missing userId") {
|
||||||
|
console.log("services::actions::getUserCredentials - missing userId");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errMessage.message === "could not find user credentials") {
|
||||||
|
console.log(
|
||||||
|
"services::actions::getUserCredentials - unable to find user credentials",
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"services::actions::getUserCredentials - unknown error",
|
||||||
|
errMessage,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserInformation(userId: string): Promise<{
|
||||||
|
id: string;
|
||||||
|
userName: string;
|
||||||
|
nickName: string | null;
|
||||||
|
bio: string | null;
|
||||||
|
picture: string | null;
|
||||||
|
banner: string | null;
|
||||||
|
admin: boolean;
|
||||||
|
status: "online" | "offline" | "dnd" | "idle" | "invis";
|
||||||
|
role: Role[];
|
||||||
|
} | null> {
|
||||||
|
try {
|
||||||
|
if (!userId) {
|
||||||
|
throw new Error("missing userId");
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new Error("could not find user");
|
||||||
|
}
|
||||||
|
|
||||||
|
const userRoles = await prisma.role.findMany({
|
||||||
|
where: {
|
||||||
|
userId: userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: userId,
|
||||||
|
userName: user.username,
|
||||||
|
nickName: user.nickname,
|
||||||
|
bio: user.bio,
|
||||||
|
picture: user.picture,
|
||||||
|
banner: user.banner,
|
||||||
|
admin: user.admin,
|
||||||
|
status: (["online", "offline", "dnd", "idle", "invis"] as const).includes(
|
||||||
|
user.status as any,
|
||||||
|
)
|
||||||
|
? (user.status as "online" | "offline" | "dnd" | "idle" | "invis")
|
||||||
|
: "offline",
|
||||||
|
role: userRoles,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
const errMessage = err as Error;
|
||||||
|
|
||||||
|
if (errMessage.message === "missing userId") {
|
||||||
|
console.log("services::actions::getUserInformation - missing userId");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errMessage.message === "could not find user") {
|
||||||
|
console.log(
|
||||||
|
"services::actions::getUserInformation - unable to find user",
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"services::actions::getUserInformation - unknown error",
|
||||||
|
errMessage,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllUsersFrom(instanceId: string): Promise<
|
||||||
|
| {
|
||||||
|
id: string;
|
||||||
|
userName: string;
|
||||||
|
nickName: string | null;
|
||||||
|
bio: string | null;
|
||||||
|
picture: string | null;
|
||||||
|
banner: string | null;
|
||||||
|
admin: boolean;
|
||||||
|
status: "online" | "offline" | "dnd" | "idle" | "invis";
|
||||||
|
role: {
|
||||||
|
userId: string;
|
||||||
|
instanceId: string;
|
||||||
|
}[];
|
||||||
|
}[]
|
||||||
|
| null
|
||||||
|
> {
|
||||||
|
try {
|
||||||
|
const instances = await prisma.instance.count({
|
||||||
|
where: {
|
||||||
|
id: instanceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (instances < 1) {
|
||||||
|
throw new Error("could not find given instance id");
|
||||||
|
}
|
||||||
|
|
||||||
|
const users = await prisma.user.findMany({
|
||||||
|
where: {
|
||||||
|
Role: {
|
||||||
|
some: {
|
||||||
|
instanceId: instanceId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!users) {
|
||||||
|
throw new Error("could not get all users in instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
const admins = await prisma.user.findMany({
|
||||||
|
where: {
|
||||||
|
admin: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!admins) {
|
||||||
|
throw new Error("could not get all admins");
|
||||||
|
}
|
||||||
|
const adminData = await Promise.all(
|
||||||
|
admins.map(async (u) => {
|
||||||
|
const adminRoles = await prisma.role.findMany({
|
||||||
|
where: {
|
||||||
|
userId: u.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!adminRoles) {
|
||||||
|
throw new Error("could not get admin roles for admin " + u.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: u.id,
|
||||||
|
userName: u.username,
|
||||||
|
nickName: u.nickname,
|
||||||
|
bio: u.bio,
|
||||||
|
picture: u.picture,
|
||||||
|
banner: u.banner,
|
||||||
|
admin: u.admin,
|
||||||
|
status: (
|
||||||
|
["online", "offline", "dnd", "idle", "invis"] as const
|
||||||
|
).includes(u.status as any)
|
||||||
|
? (u.status as "online" | "offline" | "dnd" | "idle" | "invis")
|
||||||
|
: "offline",
|
||||||
|
role: adminRoles,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const userData = await Promise.all(
|
||||||
|
users.map(async (u) => {
|
||||||
|
const userRoles = await prisma.role.findMany({
|
||||||
|
where: {
|
||||||
|
userId: u.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!userRoles) {
|
||||||
|
throw new Error("could not get user roles for user " + u.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: u.id,
|
||||||
|
userName: u.username,
|
||||||
|
nickName: u.nickname,
|
||||||
|
bio: u.bio,
|
||||||
|
picture: u.picture,
|
||||||
|
banner: u.banner,
|
||||||
|
admin: u.admin,
|
||||||
|
status: (
|
||||||
|
["online", "offline", "dnd", "idle", "invis"] as const
|
||||||
|
).includes(u.status as any)
|
||||||
|
? (u.status as "online" | "offline" | "dnd" | "idle" | "invis")
|
||||||
|
: "offline",
|
||||||
|
role: userRoles,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return userData.concat(adminData);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
61
concord-server/src/validators/categoryValidator.ts
Normal file
61
concord-server/src/validators/categoryValidator.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
//category validators
|
||||||
|
|
||||||
|
export const createCategorySchema = z.object({
|
||||||
|
name: z.string().min(1).max(50),
|
||||||
|
position: z.number().min(0),
|
||||||
|
instanceId: z.uuidv7().optional(),
|
||||||
|
admin: z.boolean(),
|
||||||
|
requestingUserId: z.uuidv7(),
|
||||||
|
requestingUserToken: z.uuidv4(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getCategorySchema = z.object({
|
||||||
|
id: z.uuidv7(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getCategoriesByInstanceIdSchema = z.object({
|
||||||
|
instanceId: z.uuidv7(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateCategorySchema = z.object({
|
||||||
|
id: z.uuidv7(),
|
||||||
|
name: z.string().min(1).max(50).optional(),
|
||||||
|
position: z.number().min(0).optional(),
|
||||||
|
channels: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
id: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
admin: z.boolean(),
|
||||||
|
requestingUserId: z.uuidv7(),
|
||||||
|
requestingUserToken: z.uuidv4(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteCategorySchema = z.object({
|
||||||
|
id: z.uuidv7(),
|
||||||
|
admin: z.boolean(),
|
||||||
|
requestingUserId: z.uuidv7(),
|
||||||
|
requestingUserToken: z.uuidv4(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteCategoriesByInstanceIdSchema = z.object({
|
||||||
|
instanceId: z.uuidv7(),
|
||||||
|
admin: z.boolean(),
|
||||||
|
requestingUserId: z.uuidv7(),
|
||||||
|
requestingUserToken: z.uuidv4(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type CreateCategoryInput = z.infer<typeof createCategorySchema>;
|
||||||
|
export type GetCategoryInput = z.infer<typeof getCategorySchema>;
|
||||||
|
export type GetCategoriesByInstanceIdInput = z.infer<
|
||||||
|
typeof getCategoriesByInstanceIdSchema
|
||||||
|
>;
|
||||||
|
export type UpdateCategoryInput = z.infer<typeof updateCategorySchema>;
|
||||||
|
export type DeleteCategoryInput = z.infer<typeof deleteCategorySchema>;
|
||||||
|
export type DeleteCategoriesByInstanceIdInput = z.infer<
|
||||||
|
typeof deleteCategoriesByInstanceIdSchema
|
||||||
|
>;
|
||||||
56
concord-server/src/validators/channelValidator.ts
Normal file
56
concord-server/src/validators/channelValidator.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
//channel validators
|
||||||
|
|
||||||
|
export const createChannelSchema = z.object({
|
||||||
|
type: z.enum(["text", "voice"]),
|
||||||
|
name: z.string().min(1).max(50),
|
||||||
|
description: z.string().max(255),
|
||||||
|
categoryId: z.uuidv7().optional(),
|
||||||
|
admin: z.boolean(),
|
||||||
|
requestingUserId: z.uuidv7(),
|
||||||
|
requestingUserToken: z.uuidv4(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getChannelSchema = z.object({
|
||||||
|
id: z.uuidv7(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getChannelsByCategoryIdSchema = z.object({
|
||||||
|
categoryId: z.uuidv7(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateChannelSchema = z.object({
|
||||||
|
id: z.uuidv7(),
|
||||||
|
name: z.string().min(1).max(50).optional(),
|
||||||
|
description: z.string().max(255).optional(),
|
||||||
|
categoryId: z.uuidv7().optional(),
|
||||||
|
admin: z.boolean(),
|
||||||
|
requestingUserId: z.uuidv7(),
|
||||||
|
requestingUserToken: z.uuidv4(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteChannelSchema = z.object({
|
||||||
|
id: z.uuidv7(),
|
||||||
|
admin: z.boolean(),
|
||||||
|
requestingUserId: z.uuidv7(),
|
||||||
|
requestingUserToken: z.uuidv4(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteChannelsByCategoryIdSchema = z.object({
|
||||||
|
categoryId: z.uuidv7(),
|
||||||
|
admin: z.boolean(),
|
||||||
|
requestingUserId: z.uuidv7(),
|
||||||
|
requestingUserToken: z.uuidv4(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type CreateChannelInput = z.infer<typeof createChannelSchema>;
|
||||||
|
export type GetChannelInput = z.infer<typeof getChannelSchema>;
|
||||||
|
export type GetChannelsByCategoryIdInput = z.infer<
|
||||||
|
typeof getChannelsByCategoryIdSchema
|
||||||
|
>;
|
||||||
|
export type UpdateChannelInput = z.infer<typeof updateChannelSchema>;
|
||||||
|
export type DeleteChannelInput = z.infer<typeof deleteChannelSchema>;
|
||||||
|
export type DeleteChannelsByCategoryIdInput = z.infer<
|
||||||
|
typeof deleteChannelsByCategoryIdSchema
|
||||||
|
>;
|
||||||
33
concord-server/src/validators/instanceValidator.ts
Normal file
33
concord-server/src/validators/instanceValidator.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const createInstanceRequestSchema = z.object({
|
||||||
|
name: z.string().min(1, "Instance name cannot be empty"),
|
||||||
|
icon: z.url().optional(),
|
||||||
|
requestingUserId: z.uuidv7(),
|
||||||
|
requestingUserToken: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getAllInstancesResponseSchema = z.object({
|
||||||
|
success: z.boolean(),
|
||||||
|
data: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
id: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
icon: z.string().nullable(),
|
||||||
|
createdAt: z.string().refine((val) => !isNaN(Date.parse(val)), {
|
||||||
|
message: "Invalid date string format",
|
||||||
|
}),
|
||||||
|
updatedAt: z.string().refine((val) => !isNaN(Date.parse(val)), {
|
||||||
|
message: "Invalid date string format",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
error: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type CreateInstanceRequest = z.infer<typeof createInstanceRequestSchema>;
|
||||||
|
export type GetAllInstancesResponse = z.infer<
|
||||||
|
typeof getAllInstancesResponseSchema
|
||||||
|
>;
|
||||||
20
concord-server/src/validators/messageValidator.ts
Normal file
20
concord-server/src/validators/messageValidator.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const getMessageByIdSchema = z.object({
|
||||||
|
id: z.uuidv7(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getMessagesBeforeDate = z.object({
|
||||||
|
date: z.string().refine((val) => !isNaN(Date.parse(val)), {
|
||||||
|
message: "Invalid date string format",
|
||||||
|
}),
|
||||||
|
channelId: z.uuidv7(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const sendMessageSchema = z.object({
|
||||||
|
channelId: z.uuidv7(),
|
||||||
|
userId: z.uuidv7(),
|
||||||
|
content: z.string(),
|
||||||
|
token: z.string(),
|
||||||
|
repliedMessageId: z.uuidv7().nullable().optional(),
|
||||||
|
});
|
||||||
30
concord-server/src/validators/userValidator.ts
Normal file
30
concord-server/src/validators/userValidator.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const queryUserByIdSchema = z.object({
|
||||||
|
id: z.uuidv7(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const queryAllUsersByInstanceId = z.object({
|
||||||
|
instanceId: z.uuidv7(),
|
||||||
|
});
|
||||||
|
import { is } from "zod/v4/locales";
|
||||||
|
export const createUserSchema = z.object({
|
||||||
|
username: z.string().min(3).max(30),
|
||||||
|
nickname: z.string().min(1).max(30).optional(),
|
||||||
|
bio: z.string().max(500).optional(),
|
||||||
|
picture: z.url().optional(),
|
||||||
|
banner: z.url().optional(),
|
||||||
|
status: z
|
||||||
|
.enum(["online", "offline", "dnd", "idle", "invis"])
|
||||||
|
.default("online"),
|
||||||
|
admin: z.boolean().default(false),
|
||||||
|
requestingUserId: z.uuidv7(),
|
||||||
|
requestingUserToken: z.uuidv4(),
|
||||||
|
passwordhash: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type QueryUserByIdInput = z.infer<typeof queryUserByIdSchema>;
|
||||||
|
export type QueryAllUsersByInstanceIdInput = z.infer<
|
||||||
|
typeof queryAllUsersByInstanceId
|
||||||
|
>;
|
||||||
|
export type CreateUserInput = z.infer<typeof createUserSchema>;
|
||||||
@@ -4,4 +4,4 @@
|
|||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"jsxImportSource": "hono/jsx"
|
"jsxImportSource": "hono/jsx"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user