initial commit
This commit is contained in:
26
api/.bruno/Create Comment.bru
Normal file
26
api/.bruno/Create Comment.bru
Normal file
@@ -0,0 +1,26 @@
|
||||
meta {
|
||||
name: Create Comment
|
||||
type: http
|
||||
seq: 7
|
||||
}
|
||||
|
||||
post {
|
||||
url: http://localhost:3000/posts/:post_id/comments
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
params:path {
|
||||
post_id:
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"text": "hey!!"
|
||||
}
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
23
api/.bruno/Create Post.bru
Normal file
23
api/.bruno/Create Post.bru
Normal file
@@ -0,0 +1,23 @@
|
||||
meta {
|
||||
name: Create Post
|
||||
type: http
|
||||
seq: 3
|
||||
}
|
||||
|
||||
post {
|
||||
url: http://localhost:3000/posts
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"description": "",
|
||||
"file_id": ""
|
||||
}
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
21
api/.bruno/Delete Comment.bru
Normal file
21
api/.bruno/Delete Comment.bru
Normal file
@@ -0,0 +1,21 @@
|
||||
meta {
|
||||
name: Delete Comment
|
||||
type: http
|
||||
seq: 9
|
||||
}
|
||||
|
||||
delete {
|
||||
url: http://localhost:3000/posts/:post_id/comments/:comment_id
|
||||
body: none
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
params:path {
|
||||
comment_id:
|
||||
post_id:
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
20
api/.bruno/Delete Post.bru
Normal file
20
api/.bruno/Delete Post.bru
Normal file
@@ -0,0 +1,20 @@
|
||||
meta {
|
||||
name: Delete Post
|
||||
type: http
|
||||
seq: 6
|
||||
}
|
||||
|
||||
delete {
|
||||
url: http://localhost:3000/posts/:id
|
||||
body: none
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
params:path {
|
||||
id:
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
20
api/.bruno/Get Comments.bru
Normal file
20
api/.bruno/Get Comments.bru
Normal file
@@ -0,0 +1,20 @@
|
||||
meta {
|
||||
name: Get Comments
|
||||
type: http
|
||||
seq: 8
|
||||
}
|
||||
|
||||
get {
|
||||
url: http://localhost:3000/posts/:post_id/comments
|
||||
body: none
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
params:path {
|
||||
post_id:
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
20
api/.bruno/Get Post.bru
Normal file
20
api/.bruno/Get Post.bru
Normal file
@@ -0,0 +1,20 @@
|
||||
meta {
|
||||
name: Get Post
|
||||
type: http
|
||||
seq: 4
|
||||
}
|
||||
|
||||
get {
|
||||
url: http://localhost:3000/posts/:id
|
||||
body: none
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
params:path {
|
||||
id:
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
16
api/.bruno/Get Posts.bru
Normal file
16
api/.bruno/Get Posts.bru
Normal file
@@ -0,0 +1,16 @@
|
||||
meta {
|
||||
name: Get Posts
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
get {
|
||||
url: http://localhost:3000/posts
|
||||
body: none
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
20
api/.bruno/Upload File.bru
Normal file
20
api/.bruno/Upload File.bru
Normal file
@@ -0,0 +1,20 @@
|
||||
meta {
|
||||
name: Upload File
|
||||
type: http
|
||||
seq: 3
|
||||
}
|
||||
|
||||
post {
|
||||
url: http://localhost:3000/posts/file
|
||||
body: multipartForm
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
body:multipart-form {
|
||||
image:
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
9
api/.bruno/bruno.json
Normal file
9
api/.bruno/bruno.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"version": "1",
|
||||
"name": "Spark",
|
||||
"type": "collection",
|
||||
"ignore": [
|
||||
"node_modules",
|
||||
".git"
|
||||
]
|
||||
}
|
||||
7
api/.bruno/collection.bru
Normal file
7
api/.bruno/collection.bru
Normal file
@@ -0,0 +1,7 @@
|
||||
headers {
|
||||
Authorization: {{TOKEN}}
|
||||
}
|
||||
|
||||
auth {
|
||||
mode: none
|
||||
}
|
||||
3
api/.bruno/environments/env.bru
Normal file
3
api/.bruno/environments/env.bru
Normal file
@@ -0,0 +1,3 @@
|
||||
vars:secret [
|
||||
TOKEN
|
||||
]
|
||||
3
api/README.md
Normal file
3
api/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
spark/api
|
||||
---
|
||||
spark's backend
|
||||
257
api/bun.lock
Normal file
257
api/bun.lock
Normal file
@@ -0,0 +1,257 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "spark-api",
|
||||
"dependencies": {
|
||||
"@prisma/adapter-pg": "^7.3.0",
|
||||
"@prisma/client": "^7.3.0",
|
||||
"hono": "^4.11.7",
|
||||
"pg": "^8.17.2",
|
||||
"zod": "^4.1.12",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"prisma": "^7.3.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@10.5.0", "", { "dependencies": { "@chevrotain/gast": "10.5.0", "@chevrotain/types": "10.5.0", "lodash": "4.17.21" } }, "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw=="],
|
||||
|
||||
"@chevrotain/gast": ["@chevrotain/gast@10.5.0", "", { "dependencies": { "@chevrotain/types": "10.5.0", "lodash": "4.17.21" } }, "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A=="],
|
||||
|
||||
"@chevrotain/types": ["@chevrotain/types@10.5.0", "", {}, "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A=="],
|
||||
|
||||
"@chevrotain/utils": ["@chevrotain/utils@10.5.0", "", {}, "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ=="],
|
||||
|
||||
"@electric-sql/pglite": ["@electric-sql/pglite@0.3.15", "", {}, "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ=="],
|
||||
|
||||
"@electric-sql/pglite-socket": ["@electric-sql/pglite-socket@0.0.20", "", { "peerDependencies": { "@electric-sql/pglite": "0.3.15" }, "bin": { "pglite-server": "dist/scripts/server.js" } }, "sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg=="],
|
||||
|
||||
"@electric-sql/pglite-tools": ["@electric-sql/pglite-tools@0.2.20", "", { "peerDependencies": { "@electric-sql/pglite": "0.3.15" } }, "sha512-BK50ZnYa3IG7ztXhtgYf0Q7zijV32Iw1cYS8C+ThdQlwx12V5VZ9KRJ42y82Hyb4PkTxZQklVQA9JHyUlex33A=="],
|
||||
|
||||
"@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="],
|
||||
|
||||
"@hono/zod-validator": ["@hono/zod-validator@0.7.4", "", { "peerDependencies": { "hono": "^4", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-biKGn3BRJVaftZlIPMyK+HCe/UHAjJ6sH0UyXe3+v0OcgVr9xfImDROTJFLtn9e3XEEAHGZIM9U6evu85abm8Q=="],
|
||||
|
||||
"@mrleebo/prisma-ast": ["@mrleebo/prisma-ast@0.13.1", "", { "dependencies": { "chevrotain": "^10.5.0", "lilconfig": "^2.1.0" } }, "sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw=="],
|
||||
|
||||
"@prisma/adapter-pg": ["@prisma/adapter-pg@7.3.0", "", { "dependencies": { "@prisma/driver-adapter-utils": "7.3.0", "pg": "^8.16.3", "postgres-array": "3.0.4" } }, "sha512-iuYQMbIPO6i9O45Fv8TB7vWu00BXhCaNAShenqF7gLExGDbnGp5BfFB4yz1K59zQ59jF6tQ9YHrg0P6/J3OoLg=="],
|
||||
|
||||
"@prisma/client": ["@prisma/client@7.3.0", "", { "dependencies": { "@prisma/client-runtime-utils": "7.3.0" }, "peerDependencies": { "prisma": "*", "typescript": ">=5.4.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-FXBIxirqQfdC6b6HnNgxGmU7ydCPEPk7maHMOduJJfnTP+MuOGa15X4omjR/zpPUUpm8ef/mEFQjJudOGkXFcQ=="],
|
||||
|
||||
"@prisma/client-runtime-utils": ["@prisma/client-runtime-utils@7.3.0", "", {}, "sha512-dG/ceD9c+tnXATPk8G+USxxYM9E6UdMTnQeQ+1SZUDxTz7SgQcfxEqafqIQHcjdlcNK/pvmmLfSwAs3s2gYwUw=="],
|
||||
|
||||
"@prisma/config": ["@prisma/config@7.3.0", "", { "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", "effect": "3.18.4", "empathic": "2.0.0" } }, "sha512-QyMV67+eXF7uMtKxTEeQqNu/Be7iH+3iDZOQZW5ttfbSwBamCSdwPszA0dum+Wx27I7anYTPLmRmMORKViSW1A=="],
|
||||
|
||||
"@prisma/debug": ["@prisma/debug@7.3.0", "", {}, "sha512-yh/tHhraCzYkffsI1/3a7SHX8tpgbJu1NPnuxS4rEpJdWAUDHUH25F1EDo6PPzirpyLNkgPPZdhojQK804BGtg=="],
|
||||
|
||||
"@prisma/dev": ["@prisma/dev@0.20.0", "", { "dependencies": { "@electric-sql/pglite": "0.3.15", "@electric-sql/pglite-socket": "0.0.20", "@electric-sql/pglite-tools": "0.2.20", "@hono/node-server": "1.19.9", "@mrleebo/prisma-ast": "0.13.1", "@prisma/get-platform": "7.2.0", "@prisma/query-plan-executor": "7.2.0", "foreground-child": "3.3.1", "get-port-please": "3.2.0", "hono": "4.11.4", "http-status-codes": "2.3.0", "pathe": "2.0.3", "proper-lockfile": "4.1.2", "remeda": "2.33.4", "std-env": "3.10.0", "valibot": "1.2.0", "zeptomatch": "2.1.0" } }, "sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ=="],
|
||||
|
||||
"@prisma/driver-adapter-utils": ["@prisma/driver-adapter-utils@7.3.0", "", { "dependencies": { "@prisma/debug": "7.3.0" } }, "sha512-Wdlezh1ck0Rq2dDINkfSkwbR53q53//Eo1vVqVLwtiZ0I6fuWDGNPxwq+SNAIHnsU+FD/m3aIJKevH3vF13U3w=="],
|
||||
|
||||
"@prisma/engines": ["@prisma/engines@7.3.0", "", { "dependencies": { "@prisma/debug": "7.3.0", "@prisma/engines-version": "7.3.0-16.9d6ad21cbbceab97458517b147a6a09ff43aa735", "@prisma/fetch-engine": "7.3.0", "@prisma/get-platform": "7.3.0" } }, "sha512-cWRQoPDXPtR6stOWuWFZf9pHdQ/o8/QNWn0m0zByxf5Kd946Q875XdEJ52pEsX88vOiXUmjuPG3euw82mwQNMg=="],
|
||||
|
||||
"@prisma/engines-version": ["@prisma/engines-version@7.3.0-16.9d6ad21cbbceab97458517b147a6a09ff43aa735", "", {}, "sha512-IH2va2ouUHihyiTTRW889LjKAl1CusZOvFfZxCDNpjSENt7g2ndFsK0vdIw/72v7+jCN6YgkHmdAP/BI7SDgyg=="],
|
||||
|
||||
"@prisma/fetch-engine": ["@prisma/fetch-engine@7.3.0", "", { "dependencies": { "@prisma/debug": "7.3.0", "@prisma/engines-version": "7.3.0-16.9d6ad21cbbceab97458517b147a6a09ff43aa735", "@prisma/get-platform": "7.3.0" } }, "sha512-Mm0F84JMqM9Vxk70pzfNpGJ1lE4hYjOeLMu7nOOD1i83nvp8MSAcFYBnHqLvEZiA6onUR+m8iYogtOY4oPO5lQ=="],
|
||||
|
||||
"@prisma/get-platform": ["@prisma/get-platform@7.2.0", "", { "dependencies": { "@prisma/debug": "7.2.0" } }, "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA=="],
|
||||
|
||||
"@prisma/query-plan-executor": ["@prisma/query-plan-executor@7.2.0", "", {}, "sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ=="],
|
||||
|
||||
"@prisma/studio-core": ["@prisma/studio-core@0.13.1", "", { "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-agdqaPEePRHcQ7CexEfkX1RvSH9uWDb6pXrZnhCRykhDFAV0/0P3d07WtfiY8hZWb7oRU4v+NkT4cGFHkQJIPg=="],
|
||||
|
||||
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.3.7", "", { "dependencies": { "bun-types": "1.3.7" } }, "sha512-lmNuMda+Z9b7tmhA0tohwy8ZWFSnmQm1UDWXtH5r9F7wZCfkeO3Jx7wKQ1EOiKq43yHts7ky6r8SDJQWRNupkA=="],
|
||||
|
||||
"@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="],
|
||||
|
||||
"@types/react": ["@types/react@19.2.10", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw=="],
|
||||
|
||||
"aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-qyschsA03Qz+gou+apt6HNl6HnI+sJJLL4wLDke4iugsE6584CMupOtTY1n+2YC9nGVrEKUlTs99jjRLKgWnjQ=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"chevrotain": ["chevrotain@10.5.0", "", { "dependencies": { "@chevrotain/cst-dts-gen": "10.5.0", "@chevrotain/gast": "10.5.0", "@chevrotain/types": "10.5.0", "@chevrotain/utils": "10.5.0", "lodash": "4.17.21", "regexp-to-ast": "0.5.0" } }, "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A=="],
|
||||
|
||||
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||
|
||||
"citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
|
||||
|
||||
"confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="],
|
||||
|
||||
"consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||
|
||||
"deepmerge-ts": ["deepmerge-ts@7.1.5", "", {}, "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw=="],
|
||||
|
||||
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||
|
||||
"denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
|
||||
|
||||
"destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="],
|
||||
|
||||
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
||||
|
||||
"effect": ["effect@3.18.4", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA=="],
|
||||
|
||||
"empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="],
|
||||
|
||||
"exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="],
|
||||
|
||||
"fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="],
|
||||
|
||||
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||
|
||||
"generate-function": ["generate-function@2.3.1", "", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="],
|
||||
|
||||
"get-port-please": ["get-port-please@3.2.0", "", {}, "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"grammex": ["grammex@3.1.12", "", {}, "sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ=="],
|
||||
|
||||
"graphmatch": ["graphmatch@1.1.0", "", {}, "sha512-0E62MaTW5rPZVRLyIJZG/YejmdA/Xr1QydHEw3Vt+qOKkMIOE8WDLc9ZX2bmAjtJFZcId4lEdrdmASsEy7D1QA=="],
|
||||
|
||||
"hono": ["hono@4.11.7", "", {}, "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw=="],
|
||||
|
||||
"http-status-codes": ["http-status-codes@2.3.0", "", {}, "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="],
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
||||
|
||||
"lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="],
|
||||
|
||||
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||
|
||||
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
|
||||
|
||||
"lru.min": ["lru.min@1.1.3", "", {}, "sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q=="],
|
||||
|
||||
"mysql2": ["mysql2@3.15.3", "", { "dependencies": { "aws-ssl-profiles": "^1.1.1", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.7.0", "long": "^5.2.1", "lru.min": "^1.0.0", "named-placeholders": "^1.1.3", "seq-queue": "^0.0.5", "sqlstring": "^2.3.2" } }, "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg=="],
|
||||
|
||||
"named-placeholders": ["named-placeholders@1.1.6", "", { "dependencies": { "lru.min": "^1.1.0" } }, "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w=="],
|
||||
|
||||
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
|
||||
|
||||
"nypm": ["nypm@0.6.4", "", { "dependencies": { "citty": "^0.2.0", "pathe": "^2.0.3", "tinyexec": "^1.0.2" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-1TvCKjZyyklN+JJj2TS3P4uSQEInrM/HkkuSXsEzm1ApPgBffOn8gFguNnZf07r/1X6vlryfIqMUkJKQMzlZiw=="],
|
||||
|
||||
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
|
||||
|
||||
"pg": ["pg@8.17.2", "", { "dependencies": { "pg-connection-string": "^2.10.1", "pg-pool": "^3.11.0", "pg-protocol": "^1.11.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.3.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-vjbKdiBJRqzcYw1fNU5KuHyYvdJ1qpcQg1CeBrHFqV1pWgHeVR6j/+kX0E1AAXfyuLUGY1ICrN2ELKA/z2HWzw=="],
|
||||
|
||||
"pg-cloudflare": ["pg-cloudflare@1.3.0", "", {}, "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ=="],
|
||||
|
||||
"pg-connection-string": ["pg-connection-string@2.10.1", "", {}, "sha512-iNzslsoeSH2/gmDDKiyMqF64DATUCWj3YJ0wP14kqcsf2TUklwimd+66yYojKwZCA7h2yRNLGug71hCBA2a4sw=="],
|
||||
|
||||
"pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="],
|
||||
|
||||
"pg-pool": ["pg-pool@3.11.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w=="],
|
||||
|
||||
"pg-protocol": ["pg-protocol@1.11.0", "", {}, "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g=="],
|
||||
|
||||
"pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
||||
|
||||
"pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="],
|
||||
|
||||
"pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
|
||||
|
||||
"postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="],
|
||||
|
||||
"postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="],
|
||||
|
||||
"postgres-bytea": ["postgres-bytea@1.0.1", "", {}, "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ=="],
|
||||
|
||||
"postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
|
||||
|
||||
"postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
||||
|
||||
"prisma": ["prisma@7.3.0", "", { "dependencies": { "@prisma/config": "7.3.0", "@prisma/dev": "0.20.0", "@prisma/engines": "7.3.0", "@prisma/studio-core": "0.13.1", "mysql2": "3.15.3", "postgres": "3.4.7" }, "peerDependencies": { "better-sqlite3": ">=9.0.0", "typescript": ">=5.4.0" }, "optionalPeers": ["better-sqlite3", "typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-ApYSOLHfMN8WftJA+vL6XwAPOh/aZ0BgUyyKPwUFgjARmG6EBI9LzDPf6SWULQMSAxydV9qn5gLj037nPNlg2w=="],
|
||||
|
||||
"proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="],
|
||||
|
||||
"pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
|
||||
|
||||
"rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="],
|
||||
|
||||
"react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
|
||||
|
||||
"react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
|
||||
|
||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||
|
||||
"regexp-to-ast": ["regexp-to-ast@0.5.0", "", {}, "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw=="],
|
||||
|
||||
"remeda": ["remeda@2.33.4", "", {}, "sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ=="],
|
||||
|
||||
"retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||
|
||||
"seq-queue": ["seq-queue@0.0.5", "", {}, "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
|
||||
|
||||
"sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="],
|
||||
|
||||
"std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="],
|
||||
|
||||
"tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
||||
|
||||
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||
|
||||
"valibot": ["valibot@1.2.0", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
|
||||
|
||||
"zeptomatch": ["zeptomatch@2.1.0", "", { "dependencies": { "grammex": "^3.1.11", "graphmatch": "^1.1.0" } }, "sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA=="],
|
||||
|
||||
"@prisma/dev/hono": ["hono@4.11.4", "", {}, "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA=="],
|
||||
|
||||
"@prisma/engines/@prisma/get-platform": ["@prisma/get-platform@7.3.0", "", { "dependencies": { "@prisma/debug": "7.3.0" } }, "sha512-N7c6m4/I0Q6JYmWKP2RCD/sM9eWiyCPY98g5c0uEktObNSZnugW2U/PO+pwL0UaqzxqTXt7gTsYsb0FnMnJNbg=="],
|
||||
|
||||
"@prisma/fetch-engine/@prisma/get-platform": ["@prisma/get-platform@7.3.0", "", { "dependencies": { "@prisma/debug": "7.3.0" } }, "sha512-N7c6m4/I0Q6JYmWKP2RCD/sM9eWiyCPY98g5c0uEktObNSZnugW2U/PO+pwL0UaqzxqTXt7gTsYsb0FnMnJNbg=="],
|
||||
|
||||
"@prisma/get-platform/@prisma/debug": ["@prisma/debug@7.2.0", "", {}, "sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw=="],
|
||||
|
||||
"nypm/citty": ["citty@0.2.0", "", {}, "sha512-8csy5IBFI2ex2hTVpaHN2j+LNE199AgiI7y4dMintrr8i0lQiFn+0AWMZrWdHKIgMOer65f8IThysYhoReqjWA=="],
|
||||
|
||||
"pg-types/postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
|
||||
|
||||
"proper-lockfile/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
||||
|
||||
"zod": ["zod@4.1.12", "", {}, "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ=="],
|
||||
}
|
||||
}
|
||||
21
api/package.json
Normal file
21
api/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "spark-api",
|
||||
"scripts": {
|
||||
"dev": "bun run --hot src/index.ts",
|
||||
"db:gen": "prisma generate",
|
||||
"db:migrate": "prisma migrate dev",
|
||||
"db:push": "prisma db push"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hono/zod-validator": "^0.7.4",
|
||||
"@prisma/adapter-pg": "^7.3.0",
|
||||
"@prisma/client": "^7.3.0",
|
||||
"hono": "^4.11.7",
|
||||
"pg": "^8.17.2",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"prisma": "^7.3.0"
|
||||
}
|
||||
}
|
||||
12
api/prisma.config.ts
Normal file
12
api/prisma.config.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// This file was generated by Prisma, and assumes you run Prisma commands using `bun --bun run prisma [command]`.
|
||||
import { defineConfig, env } from "prisma/config";
|
||||
|
||||
export default defineConfig({
|
||||
schema: "prisma/schema.prisma",
|
||||
migrations: {
|
||||
path: "prisma/migrations",
|
||||
},
|
||||
datasource: {
|
||||
url: env("DATABASE_URL"),
|
||||
},
|
||||
});
|
||||
81
api/prisma/schema.prisma
Normal file
81
api/prisma/schema.prisma
Normal file
@@ -0,0 +1,81 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
output = "../node_modules/.prisma/client/"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id
|
||||
username String @unique
|
||||
|
||||
posts Post[]
|
||||
comments Comment[]
|
||||
notifications Notification[]
|
||||
sessions Session[]
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
|
||||
model Post {
|
||||
id Int @id @default(autoincrement())
|
||||
created DateTime @default(now())
|
||||
|
||||
description String?
|
||||
images Image[]
|
||||
comments Comment[]
|
||||
|
||||
author User? @relation(fields: [author_id], references: [id], onDelete: Cascade)
|
||||
author_id String?
|
||||
|
||||
@@map("posts")
|
||||
}
|
||||
|
||||
model Image {
|
||||
id Int @id @default(autoincrement())
|
||||
filename String
|
||||
|
||||
post Post @relation(fields: [post_id], references: [id], onDelete: Cascade)
|
||||
post_id Int
|
||||
|
||||
@@map("images")
|
||||
}
|
||||
|
||||
model Comment {
|
||||
id Int @id @default(autoincrement())
|
||||
created DateTime @default(now())
|
||||
|
||||
text String
|
||||
|
||||
post Post @relation(fields: [post_id], references: [id], onDelete: Cascade)
|
||||
post_id Int
|
||||
|
||||
author User @relation(fields: [author_id], references: [id], onDelete: Cascade)
|
||||
author_id String
|
||||
|
||||
@@map("comments")
|
||||
}
|
||||
|
||||
model Notification {
|
||||
id Int @id @default(autoincrement())
|
||||
text String
|
||||
href String?
|
||||
|
||||
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
|
||||
user_id String
|
||||
|
||||
@@map("notifications")
|
||||
}
|
||||
|
||||
model Session {
|
||||
token String @id
|
||||
|
||||
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
|
||||
user_id String
|
||||
|
||||
created DateTime @default(now())
|
||||
|
||||
@@map("sessions")
|
||||
}
|
||||
14
api/src/index.ts
Normal file
14
api/src/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Hono } from 'hono'
|
||||
import { AuthGuard, AuthRoute } from './routes/auth'
|
||||
import { PostsRoute } from './routes/posts'
|
||||
|
||||
const app = new Hono()
|
||||
|
||||
.get('/', (c) => {
|
||||
return c.text('Hello Hono!')
|
||||
})
|
||||
|
||||
.route('/auth', AuthRoute)
|
||||
.route('/posts', PostsRoute)
|
||||
|
||||
export default app
|
||||
100
api/src/routes/auth.ts
Normal file
100
api/src/routes/auth.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { User } from '@prisma/client';
|
||||
import { Hono } from 'hono';
|
||||
import { createMiddleware } from 'hono/factory';
|
||||
import { getCookie, setCookie } from 'hono/cookie';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import { env } from '../util/env';
|
||||
import { prisma } from '../util/prisma';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
import z from 'zod';
|
||||
|
||||
export const AUTH_URL = env.OAUTH_AUTHORIZE_URL + '?' + new URLSearchParams({
|
||||
redirect_uri: env.OAUTH_REDIRECT_URI
|
||||
});
|
||||
|
||||
export const AuthGuard = createMiddleware<{
|
||||
Variables: {
|
||||
user: User
|
||||
}
|
||||
}>(async (c, next) => {
|
||||
const token = getCookie(c, env.AUTH_COOKIE_NAME) ?? c.req.header('Authorization')?.replace('Bearer ', '');
|
||||
if (!token) return c.json({ href: AUTH_URL }, 401);
|
||||
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
sessions: { some: { token } }
|
||||
}
|
||||
});
|
||||
if (!user) return c.json({ href: AUTH_URL }, 401);
|
||||
|
||||
c.set('user', user);
|
||||
await next();
|
||||
});
|
||||
|
||||
async function authorize(code: string) {
|
||||
const res = await fetch(env.OAUTH_VERIFY_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
redirect_uri: env.OAUTH_REDIRECT_URI,
|
||||
code
|
||||
}),
|
||||
});
|
||||
if (!res.ok) throw new Error("OAuth request failed");
|
||||
|
||||
return (await res.json()) as {
|
||||
user_id: string;
|
||||
username: string;
|
||||
};
|
||||
}
|
||||
|
||||
async function login(code: string) {
|
||||
const passport = await authorize(code);
|
||||
|
||||
const user = prisma.user.upsert({
|
||||
where: { id: passport.user_id },
|
||||
update: {},
|
||||
create: {
|
||||
id: passport.user_id,
|
||||
username: passport.username
|
||||
}
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
const AuthRoute = new Hono()
|
||||
|
||||
.get('/login',
|
||||
zValidator('query', z.object({
|
||||
code: z.string()
|
||||
})),
|
||||
async (c) => {
|
||||
const { code } = c.req.valid('query');
|
||||
if (!code) return c.text('Missing OAuth code', 400);
|
||||
|
||||
const user = await login(code);
|
||||
|
||||
const session = await prisma.session.create({
|
||||
data: {
|
||||
user_id: user.id,
|
||||
token: randomBytes(64).toString('hex')
|
||||
}
|
||||
});
|
||||
|
||||
setCookie(c, env.AUTH_COOKIE_NAME, session.token, {
|
||||
path: "/",
|
||||
secure: env.OAUTH_REDIRECT_URI.startsWith('https://')
|
||||
});
|
||||
|
||||
return c.json({
|
||||
token: session.token,
|
||||
user_id: session.user_id,
|
||||
created: session.created
|
||||
});
|
||||
}
|
||||
)
|
||||
|
||||
export { AuthRoute };
|
||||
228
api/src/routes/posts.ts
Normal file
228
api/src/routes/posts.ts
Normal file
@@ -0,0 +1,228 @@
|
||||
import { Hono } from 'hono';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
import { prisma } from '../util/prisma';
|
||||
import z from 'zod';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import * as path from 'node:path';
|
||||
import { AuthGuard } from './auth';
|
||||
import { env } from '../util/env';
|
||||
|
||||
const pendingFiles: { id: string, file: File, expires: Date }[] = [];
|
||||
|
||||
function removePendingFile(id: string) {
|
||||
pendingFiles.splice(pendingFiles.findIndex(f => f.id === id), 1);
|
||||
}
|
||||
|
||||
function cullPendingFiles() {
|
||||
for (const file of pendingFiles) {
|
||||
if (file.expires.getTime() < Date.now()) removePendingFile(file.id);
|
||||
}
|
||||
}
|
||||
|
||||
function getMIME(mime: string) {
|
||||
return env.ALLOWED_MIME.find(m => m.type === mime);
|
||||
}
|
||||
|
||||
const PostsRoute = new Hono()
|
||||
|
||||
.use(AuthGuard)
|
||||
|
||||
.get('/',
|
||||
zValidator('query', z.object({
|
||||
count: z.number().default(20),
|
||||
before_post: z.number().optional()
|
||||
})),
|
||||
async (c) => {
|
||||
const {
|
||||
count,
|
||||
before_post
|
||||
} = c.req.valid('query');
|
||||
|
||||
const posts = await prisma.post.findMany({
|
||||
where: {
|
||||
id: before_post ? { lt: before_post } : undefined,
|
||||
},
|
||||
include: { images: true, author: true },
|
||||
take: count
|
||||
});
|
||||
|
||||
return c.json({ posts });
|
||||
}
|
||||
)
|
||||
|
||||
.get('/:post_id',
|
||||
zValidator('param', z.object({
|
||||
post_id: z.string().transform(s => +s).pipe(z.number())
|
||||
})),
|
||||
async (c) => {
|
||||
const { post_id } = c.req.valid('param');
|
||||
|
||||
const post = await prisma.post.findUnique({
|
||||
where: { id: post_id },
|
||||
include: { images: true, author: true }
|
||||
});
|
||||
if (!post) return c.text("Unknown post", 404);
|
||||
|
||||
return c.json(post);
|
||||
}
|
||||
)
|
||||
|
||||
.get('/:post_id/comments',
|
||||
zValidator('param', z.object({
|
||||
post_id: z.string().transform(s => +s).pipe(z.number())
|
||||
})),
|
||||
async (c) => {
|
||||
const { post_id } = c.req.valid('param');
|
||||
|
||||
const post = await prisma.post.findUnique({
|
||||
where: { id: post_id },
|
||||
select: { comments: { include: { author: true } } },
|
||||
});
|
||||
if (!post) return c.text("Unknown post", 404);
|
||||
|
||||
return c.json({ comments: post.comments });
|
||||
}
|
||||
)
|
||||
|
||||
.post('/:post_id/comments',
|
||||
zValidator('param', z.object({
|
||||
post_id: z.string().transform(s => +s).pipe(z.number())
|
||||
})),
|
||||
zValidator('json', z.object({
|
||||
text: z.string().max(500).nonempty()
|
||||
})),
|
||||
async (c) => {
|
||||
const { post_id } = c.req.valid('param');
|
||||
const { text } = c.req.valid('json');
|
||||
const user = c.get('user');
|
||||
|
||||
try {
|
||||
const comment = await prisma.comment.create({
|
||||
data: {
|
||||
text,
|
||||
post_id,
|
||||
author_id: user.id
|
||||
},
|
||||
include: { author: true }
|
||||
});
|
||||
|
||||
return c.json(comment);
|
||||
} catch {
|
||||
return c.text("Unknown post", 404);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
.delete('/:post_id/comments/:comment_id',
|
||||
zValidator('param', z.object({
|
||||
post_id: z.string().transform(s => +s).pipe(z.number()),
|
||||
comment_id: z.string().transform(s => +s).pipe(z.number())
|
||||
})),
|
||||
async (c) => {
|
||||
const { post_id, comment_id } = c.req.valid('param');
|
||||
const user = c.get('user');
|
||||
|
||||
try {
|
||||
const comment = await prisma.comment.delete({
|
||||
where: {
|
||||
id: comment_id,
|
||||
post_id,
|
||||
author_id: user.id
|
||||
}
|
||||
});
|
||||
|
||||
return c.text(`Deleted comment ${comment.id}`);
|
||||
} catch {
|
||||
return c.text("Unknown comment", 404);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
.post('/',
|
||||
zValidator('json', z.object({
|
||||
description: z.string().max(500).nonempty().optional(),
|
||||
file_id: z.string().optional()
|
||||
})),
|
||||
async (c) => {
|
||||
cullPendingFiles();
|
||||
|
||||
const {
|
||||
description,
|
||||
file_id
|
||||
} = c.req.valid('json');
|
||||
const user = c.get('user');
|
||||
|
||||
let post = await prisma.post.create({
|
||||
data: {
|
||||
author_id: user.id,
|
||||
description
|
||||
}
|
||||
});
|
||||
|
||||
let image;
|
||||
if (file_id) {
|
||||
const file = pendingFiles.find(f => f.id === file_id);
|
||||
if (!file) return c.text("Invalid image ID");
|
||||
|
||||
const data = file.file;
|
||||
const mime = getMIME(data.type);
|
||||
if (!mime) return c.text("Something went wrong identifying the file type of your image", 500);
|
||||
|
||||
const filename = `${post.id}-${file.id}.${mime.ext}`;
|
||||
removePendingFile(file.id);
|
||||
|
||||
Bun.write(
|
||||
path.join(env.FILE_DIR, `./images/${user.id}/${filename}`),
|
||||
data
|
||||
);
|
||||
|
||||
image = await prisma.image.create({
|
||||
data: {
|
||||
filename,
|
||||
post_id: post.id
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return c.json({
|
||||
...post,
|
||||
images: image ? [image] : []
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
.post('/file', async (c) => {
|
||||
const id = randomBytes(10).toString('hex');
|
||||
const expires = new Date(Date.now() + 300000); // 5 min
|
||||
const body = await c.req.parseBody();
|
||||
|
||||
const file = body['image'];
|
||||
if (file instanceof File && getMIME(file.type)) {
|
||||
pendingFiles.push({ id, expires, file });
|
||||
return c.json({ id, expires });
|
||||
} else {
|
||||
return c.text("Invalid file body");
|
||||
}
|
||||
})
|
||||
|
||||
.delete('/:post_id',
|
||||
zValidator('param', z.object({
|
||||
post_id: z.string().transform(s => +s).pipe(z.number())
|
||||
})),
|
||||
async (c) => {
|
||||
const { post_id } = c.req.valid('param');
|
||||
const user = c.get('user');
|
||||
|
||||
try {
|
||||
const post = await prisma.post.delete({
|
||||
where: { id: post_id, author_id: user.id }
|
||||
});
|
||||
|
||||
return c.text(`Deleted post ${post.id}`);
|
||||
} catch {
|
||||
return c.text("Unknown post", 404);
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export { PostsRoute };
|
||||
18
api/src/util/env.ts
Normal file
18
api/src/util/env.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import z from 'zod';
|
||||
|
||||
const envSchema = z.object({
|
||||
DATABASE_URL: z.string(),
|
||||
FILE_DIR: z.string(),
|
||||
ALLOWED_MIME: z.string().transform(s => s.replace(' ', '').split(',').map(m => {
|
||||
const [type, ext] = m.split('=');
|
||||
return { type, ext };
|
||||
})),
|
||||
|
||||
OAUTH_AUTHORIZE_URL: z.string(),
|
||||
OAUTH_REDIRECT_URI: z.string(),
|
||||
OAUTH_VERIFY_URL: z.string(),
|
||||
|
||||
AUTH_COOKIE_NAME: z.string().default('spark-token')
|
||||
});
|
||||
|
||||
export const env = envSchema.parse(Bun.env);
|
||||
8
api/src/util/prisma.ts
Normal file
8
api/src/util/prisma.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { PrismaPg } from '@prisma/adapter-pg';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { env } from './env';
|
||||
|
||||
const adapter = new PrismaPg({ connectionString: env.DATABASE_URL });
|
||||
const prisma = new PrismaClient({ adapter });
|
||||
|
||||
export { prisma };
|
||||
7
api/tsconfig.json
Normal file
7
api/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "hono/jsx"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user