develop-updateui #1
@@ -5,7 +5,8 @@
|
||||
"mcp__ide__getDiagnostics",
|
||||
"Bash(bun install:*)",
|
||||
"Bash(bun preview:*)",
|
||||
"Bash(curl:*)"
|
||||
"Bash(curl:*)",
|
||||
"Bash(python -:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
19
buf.gen.yaml
Normal file
19
buf.gen.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
version: v2
|
||||
plugins:
|
||||
# - remote: buf.build/protocolbuffers/go
|
||||
# out: internal/gen/proto
|
||||
# opt:
|
||||
# - paths=source_relative
|
||||
# - remote: buf.build/grpc/go
|
||||
# out: internal/gen/proto
|
||||
# opt:
|
||||
# - paths=source_relative
|
||||
- remote: buf.build/community/stephenh-ts-proto
|
||||
out: ../stream-ui/src/server/utils/proto
|
||||
opt:
|
||||
- env=node
|
||||
- esModuleInterop=true
|
||||
- outputServices=grpc-js
|
||||
- useOptionals=all
|
||||
- forceLong=number
|
||||
- useDate=string
|
||||
9
buf.yaml
Normal file
9
buf.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
version: v2
|
||||
modules:
|
||||
- path: proto
|
||||
lint:
|
||||
use:
|
||||
- STANDARD
|
||||
breaking:
|
||||
use:
|
||||
- FILE
|
||||
74
bun.lock
74
bun.lock
@@ -5,8 +5,11 @@
|
||||
"": {
|
||||
"name": "holistream",
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^2.11.0",
|
||||
"@grpc/grpc-js": "^1.14.3",
|
||||
"@hattip/adapter-node": "^0.0.49",
|
||||
"@hono/node-server": "^1.19.11",
|
||||
"@hono/zod-validator": "^0.7.6",
|
||||
"@pinia/colada": "^0.21.7",
|
||||
"@unhead/vue": "^2.1.10",
|
||||
"@vueuse/core": "^14.2.1",
|
||||
@@ -25,6 +28,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/vite-plugin": "^1.26.0",
|
||||
"@types/bun": "^1.3.10",
|
||||
"@types/node": "^25.3.3",
|
||||
"@vitejs/plugin-vue": "^6.0.4",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.4",
|
||||
@@ -94,6 +98,8 @@
|
||||
|
||||
"@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
|
||||
|
||||
"@bufbuild/protobuf": ["@bufbuild/protobuf@2.11.0", "", {}, "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ=="],
|
||||
|
||||
"@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.2", "", {}, "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ=="],
|
||||
|
||||
"@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.14.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20260218.0" }, "optionalPeers": ["workerd"] }, "sha512-XKAkWhi1nBdNsSEoNG9nkcbyvfUrSjSf+VYVPfOto3gLTZVc3F4g6RASCMh6IixBKCG2yDgZKQIHGKtjcnLnKg=="],
|
||||
@@ -170,6 +176,10 @@
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="],
|
||||
|
||||
"@grpc/grpc-js": ["@grpc/grpc-js@1.14.3", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA=="],
|
||||
|
||||
"@grpc/proto-loader": ["@grpc/proto-loader@0.8.0", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.5.3", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ=="],
|
||||
|
||||
"@hattip/adapter-node": ["@hattip/adapter-node@0.0.49", "", { "dependencies": { "@hattip/core": "0.0.49", "@hattip/polyfills": "0.0.49", "@hattip/walk": "0.0.49" } }, "sha512-BE+Y8Q4U0YcH34FZUYU4DssGKOaZLbNL0zK57Z41UZp0m9kS79ZIolBmjjpPhTVpIlRY3Rs+uhXbVXKk7mUcJA=="],
|
||||
|
||||
"@hattip/core": ["@hattip/core@0.0.49", "", {}, "sha512-3/ZJtC17cv8m6Sph8+nw4exUp9yhEf2Shi7HK6AHSUSBtaaQXZ9rJBVxTfZj3PGNOR/P49UBXOym/52WYKFTJQ=="],
|
||||
@@ -182,6 +192,8 @@
|
||||
|
||||
"@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="],
|
||||
|
||||
"@hono/zod-validator": ["@hono/zod-validator@0.7.6", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Io1B6d011Gj1KknV4rXYz4le5+5EubcWEU/speUjuw9XMMIaP3n78yXLhjd2A3PXaXaUwEAluOiAyLqhBEJgsw=="],
|
||||
|
||||
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
|
||||
|
||||
"@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="],
|
||||
@@ -246,6 +258,8 @@
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="],
|
||||
|
||||
"@kamilkisiela/fast-url-parser": ["@kamilkisiela/fast-url-parser@1.1.4", "", {}, "sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew=="],
|
||||
|
||||
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" } }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="],
|
||||
@@ -304,6 +318,26 @@
|
||||
|
||||
"@poppinss/exception": ["@poppinss/exception@1.2.3", "", {}, "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw=="],
|
||||
|
||||
"@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
|
||||
|
||||
"@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
|
||||
|
||||
"@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="],
|
||||
|
||||
"@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="],
|
||||
|
||||
"@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="],
|
||||
|
||||
"@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
|
||||
|
||||
"@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="],
|
||||
|
||||
"@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
|
||||
|
||||
"@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
|
||||
|
||||
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="],
|
||||
|
||||
"@quansync/fs": ["@quansync/fs@1.0.0", "", { "dependencies": { "quansync": "^1.0.0" } }, "sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ=="],
|
||||
|
||||
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.6", "", { "os": "android", "cpu": "arm64" }, "sha512-kvjTSWGcrv+BaR2vge57rsKiYdVR8V8CoS0vgKrc570qRBfty4bT+1X0z3j2TaVV+kAYzA0PjeB9+mdZyqUZlg=="],
|
||||
@@ -340,6 +374,8 @@
|
||||
|
||||
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"@types/node": ["@types/node@25.3.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ=="],
|
||||
@@ -438,6 +474,10 @@
|
||||
|
||||
"acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
|
||||
|
||||
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"ast-kit": ["ast-kit@2.2.0", "", { "dependencies": { "@babel/parser": "^7.28.5", "pathe": "^2.0.3" } }, "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw=="],
|
||||
|
||||
"ast-walker-scope": ["ast-walker-scope@0.8.3", "", { "dependencies": { "@babel/parser": "^7.28.4", "ast-kit": "^2.1.3" } }, "sha512-cbdCP0PGOBq0ASG+sjnKIoYkWMKhhz+F/h9pRexUdX2Hd38+WOlBkRKlqkGOSm0YQpcFMQBJeK4WspUAkwsEdg=="],
|
||||
@@ -452,6 +492,8 @@
|
||||
|
||||
"browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg=="],
|
||||
|
||||
"busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="],
|
||||
|
||||
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
||||
@@ -460,8 +502,14 @@
|
||||
|
||||
"chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||
|
||||
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||
|
||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||
|
||||
"colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
|
||||
|
||||
"confbox": ["confbox@0.2.4", "", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="],
|
||||
@@ -492,6 +540,8 @@
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.302", "", {}, "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
|
||||
|
||||
"error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="],
|
||||
@@ -516,6 +566,8 @@
|
||||
|
||||
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||
|
||||
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
||||
|
||||
"gzip-size": ["gzip-size@6.0.0", "", { "dependencies": { "duplexer": "^0.1.2" } }, "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q=="],
|
||||
|
||||
"hono": ["hono@4.12.5", "", {}, "sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg=="],
|
||||
@@ -528,6 +580,8 @@
|
||||
|
||||
"i18next-vue": ["i18next-vue@5.4.0", "", { "peerDependencies": { "i18next": ">=23", "vue": "^3.4.38" } }, "sha512-GDj0Xvmis5Xgcvo9gMBJMgJCtewYMLZP6gAEPDDGCMjA+QeB4uS4qUf1MK79mkz/FukhaJdC+nlj0y1qk6NO2Q=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||
|
||||
"is-mobile": ["is-mobile@5.0.0", "", {}, "sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ=="],
|
||||
|
||||
"is-what": ["is-what@5.5.0", "", {}, "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="],
|
||||
@@ -568,6 +622,10 @@
|
||||
|
||||
"local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="],
|
||||
|
||||
"lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
|
||||
|
||||
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
|
||||
|
||||
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"magic-regexp": ["magic-regexp@0.10.0", "", { "dependencies": { "estree-walker": "^3.0.3", "magic-string": "^0.30.12", "mlly": "^1.7.2", "regexp-tree": "^0.1.27", "type-level-regexp": "~0.1.17", "ufo": "^1.5.4", "unplugin": "^2.0.0" } }, "sha512-Uly1Bu4lO1hwHUW0CQeSWuRtzCMNO00CmXtS8N6fyvB3B979GOEEeAkiTUDsmbYLAbvpUS/Kt5c4ibosAzVyVg=="],
|
||||
@@ -628,12 +686,16 @@
|
||||
|
||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||
|
||||
"protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="],
|
||||
|
||||
"quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
|
||||
|
||||
"readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||
|
||||
"regexp-tree": ["regexp-tree@0.1.27", "", { "bin": { "regexp-tree": "bin/regexp-tree" } }, "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA=="],
|
||||
|
||||
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
|
||||
|
||||
"rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
|
||||
|
||||
"rolldown": ["rolldown@1.0.0-rc.6", "", { "dependencies": { "@oxc-project/types": "=0.115.0", "@rolldown/pluginutils": "1.0.0-rc.6" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.6", "@rolldown/binding-darwin-arm64": "1.0.0-rc.6", "@rolldown/binding-darwin-x64": "1.0.0-rc.6", "@rolldown/binding-freebsd-x64": "1.0.0-rc.6", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.6", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.6", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.6", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.6", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.6", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.6", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.6", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.6", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.6" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-B8vFPV1ADyegoYfhg+E7RAucYKv0xdVlwYYsIJgfPNeiSxZGWNxts9RqhyGzC11ULK/VaeXyKezGCwpMiH8Ktw=="],
|
||||
@@ -652,6 +714,10 @@
|
||||
|
||||
"streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="],
|
||||
|
||||
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="],
|
||||
|
||||
"superjson": ["superjson@2.2.6", "", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="],
|
||||
@@ -720,12 +786,20 @@
|
||||
|
||||
"wrangler": ["wrangler@4.70.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.2", "@cloudflare/unenv-preset": "2.14.0", "blake3-wasm": "2.1.5", "esbuild": "0.27.3", "miniflare": "4.20260301.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260301.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260226.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-PNDZ9o4e+B5x+1bUbz62Hmwz6G9lw+I9pnYe/AguLddJFjfIyt2cmFOUOb3eOZSoXsrhcEPUg2YidYIbVwUkfw=="],
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
|
||||
"ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="],
|
||||
|
||||
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||
|
||||
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
"yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="],
|
||||
|
||||
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
||||
|
||||
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
||||
|
||||
"youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="],
|
||||
|
||||
"youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="],
|
||||
|
||||
BIN
golang.tar.gz
BIN
golang.tar.gz
Binary file not shown.
10
package.json
10
package.json
@@ -2,16 +2,19 @@
|
||||
"name": "holistream",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "bun vite",
|
||||
"build": "bun vite build",
|
||||
"preview": "bun vite preview",
|
||||
"dev": "bunx --bun vite",
|
||||
"build": "bunx --bun vite build",
|
||||
"preview": "bunx --bun vite preview",
|
||||
"deploy": "wrangler deploy",
|
||||
"cf-typegen": "wrangler types --env-interface CloudflareBindings",
|
||||
"tail": "wrangler tail"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^2.11.0",
|
||||
"@grpc/grpc-js": "^1.14.3",
|
||||
"@hattip/adapter-node": "^0.0.49",
|
||||
"@hono/node-server": "^1.19.11",
|
||||
"@hono/zod-validator": "^0.7.6",
|
||||
"@pinia/colada": "^0.21.7",
|
||||
"@unhead/vue": "^2.1.10",
|
||||
"@vueuse/core": "^14.2.1",
|
||||
@@ -30,6 +33,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/vite-plugin": "^1.26.0",
|
||||
"@types/bun": "^1.3.10",
|
||||
"@types/node": "^25.3.3",
|
||||
"@vitejs/plugin-vue": "^6.0.4",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.4",
|
||||
|
||||
46
proto/v1/common.proto
Normal file
46
proto/v1/common.proto
Normal file
@@ -0,0 +1,46 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package stream.common.v1;
|
||||
|
||||
option go_package = "stream/proto/gen/go/common/v1;commonv1";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
message RequestContext {
|
||||
string user_id = 1;
|
||||
string email = 2;
|
||||
string role = 3;
|
||||
string request_id = 4;
|
||||
string source = 5;
|
||||
}
|
||||
|
||||
message PaginationRequest {
|
||||
int32 page = 1;
|
||||
int32 page_size = 2;
|
||||
}
|
||||
|
||||
message PaginationResponse {
|
||||
int32 page = 1;
|
||||
int32 page_size = 2;
|
||||
int64 total = 3;
|
||||
}
|
||||
|
||||
message Money {
|
||||
double amount = 1;
|
||||
string currency = 2;
|
||||
}
|
||||
|
||||
message Empty {}
|
||||
|
||||
message IdRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message DeleteResponse {
|
||||
string message = 1;
|
||||
}
|
||||
|
||||
message TimestampRange {
|
||||
google.protobuf.Timestamp from = 1;
|
||||
google.protobuf.Timestamp to = 2;
|
||||
}
|
||||
130
proto/v1/user.proto
Normal file
130
proto/v1/user.proto
Normal file
@@ -0,0 +1,130 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package stream.User.v1;
|
||||
|
||||
option go_package = "stream/proto/gen/go/User/v1;Userv1";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
service UserService {
|
||||
// User CRUD
|
||||
rpc GetUser(GetUserRequest) returns (GetUserResponse);
|
||||
rpc GetUserByEmail(GetUserByEmailRequest) returns (GetUserResponse);
|
||||
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
|
||||
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
|
||||
rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse);
|
||||
rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);
|
||||
|
||||
// Preferences
|
||||
rpc GetPreferences(GetPreferencesRequest) returns (GetPreferencesResponse);
|
||||
rpc UpsertPreferences(UpsertPreferencesRequest) returns (UpsertPreferencesResponse);
|
||||
}
|
||||
|
||||
// ─── User Messages ───────────────────────────────────────────────────────────
|
||||
|
||||
message GetUserRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message GetUserByEmailRequest {
|
||||
string email = 1;
|
||||
}
|
||||
|
||||
message GetUserResponse {
|
||||
User user = 1;
|
||||
}
|
||||
|
||||
message ListUsersRequest {
|
||||
int32 page = 1;
|
||||
int32 page_size = 2;
|
||||
string role = 3; // optional filter
|
||||
}
|
||||
|
||||
message ListUsersResponse {
|
||||
repeated User users = 1;
|
||||
int32 total = 2;
|
||||
int32 page = 3;
|
||||
int32 page_size = 4;
|
||||
}
|
||||
|
||||
message CreateUserRequest {
|
||||
string email = 1;
|
||||
optional string username = 2;
|
||||
optional string password = 3;
|
||||
}
|
||||
|
||||
message CreateUserResponse {
|
||||
User user = 1;
|
||||
}
|
||||
|
||||
message UpdateUserRequest {
|
||||
string id = 1;
|
||||
optional string username = 2;
|
||||
optional string avatar = 3;
|
||||
optional string role = 4;
|
||||
optional string plan_id = 5;
|
||||
}
|
||||
|
||||
message UpdateUserResponse {
|
||||
User user = 1;
|
||||
}
|
||||
|
||||
message DeleteUserRequest {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message DeleteUserResponse {
|
||||
bool success = 1;
|
||||
}
|
||||
|
||||
// ─── Preferences Messages ────────────────────────────────────────────────────
|
||||
|
||||
message GetPreferencesRequest {
|
||||
string user_id = 1;
|
||||
}
|
||||
|
||||
message GetPreferencesResponse {
|
||||
Preferences preferences = 1;
|
||||
}
|
||||
|
||||
message UpsertPreferencesRequest {
|
||||
Preferences preferences = 1;
|
||||
}
|
||||
|
||||
message UpsertPreferencesResponse {
|
||||
Preferences preferences = 1;
|
||||
}
|
||||
|
||||
// ─── Core Models ─────────────────────────────────────────────────────────────
|
||||
|
||||
message User {
|
||||
string id = 1;
|
||||
string email = 2;
|
||||
string password = 3;
|
||||
optional string username = 4;
|
||||
optional string avatar = 5;
|
||||
optional string role = 6;
|
||||
optional string google_id = 7;
|
||||
int64 storage_used = 8;
|
||||
optional string plan_id = 9;
|
||||
optional google.protobuf.Timestamp created_at = 10;
|
||||
google.protobuf.Timestamp updated_at = 11;
|
||||
}
|
||||
|
||||
message Preferences {
|
||||
string user_id = 1;
|
||||
optional string language = 2;
|
||||
optional string locale = 3;
|
||||
optional bool email_notifications = 4;
|
||||
optional bool push_notifications = 5;
|
||||
optional bool marketing_notifications = 6;
|
||||
optional bool telegram_notifications = 7;
|
||||
optional bool autoplay = 8;
|
||||
optional bool loop = 9;
|
||||
optional bool muted = 10;
|
||||
optional bool show_controls = 11;
|
||||
optional bool pip = 12;
|
||||
optional bool airplay = 13;
|
||||
optional bool chromecast = 14;
|
||||
optional bool encrytion_m3u8 = 15;
|
||||
}
|
||||
@@ -7,12 +7,12 @@ import { registerManifestRoutes } from './server/routes/manifest';
|
||||
import { registerMergeRoutes } from './server/routes/merge';
|
||||
import { registerSSRRoutes } from './server/routes/ssr';
|
||||
import { registerWellKnownRoutes } from './server/routes/wellKnown';
|
||||
|
||||
import { setupServices } from './server/services/grpcClient';
|
||||
const app = new Hono();
|
||||
|
||||
// Global middlewares
|
||||
setupMiddlewares(app);
|
||||
|
||||
setupServices(app);
|
||||
// API proxy middleware (handles /r/*)
|
||||
app.use(apiProxyMiddleware);
|
||||
// Routes
|
||||
|
||||
@@ -1,17 +1,36 @@
|
||||
import { contextStorage } from 'hono/context-storage';
|
||||
import { cors } from 'hono/cors';
|
||||
import isMobile from 'is-mobile';
|
||||
import type { Hono } from 'hono';
|
||||
import { languageDetector } from 'hono/language';
|
||||
import { RedisClient } from "bun";
|
||||
import type { Hono } from "hono";
|
||||
import { contextStorage } from "hono/context-storage";
|
||||
import { cors } from "hono/cors";
|
||||
import { languageDetector } from "hono/language";
|
||||
import isMobile from "is-mobile";
|
||||
type AppFetch = (
|
||||
input: string | Request | URL,
|
||||
requestInit?: RequestInit
|
||||
) => Response | Promise<Response>;
|
||||
|
||||
declare module "hono" {
|
||||
interface ContextVariableMap {
|
||||
fetch: AppFetch;
|
||||
isMobile: boolean;
|
||||
redis: RedisClient;
|
||||
}
|
||||
}
|
||||
|
||||
const client = new RedisClient("redis://:pass123@47.84.62.226:6379/3");
|
||||
|
||||
export function setupMiddlewares(app: Hono) {
|
||||
app.use('*', languageDetector({
|
||||
supportedLanguages: ['vi', 'en'],
|
||||
fallbackLanguage: 'en',
|
||||
lookupCookie: 'i18next',
|
||||
lookupFromHeaderKey: 'accept-language',
|
||||
order: ['cookie', 'header'],
|
||||
}) ,contextStorage());
|
||||
app.use(
|
||||
"*",
|
||||
languageDetector({
|
||||
supportedLanguages: ["vi", "en"],
|
||||
fallbackLanguage: "en",
|
||||
lookupCookie: "i18next",
|
||||
lookupFromHeaderKey: "accept-language",
|
||||
order: ["cookie", "header"],
|
||||
}),
|
||||
contextStorage()
|
||||
);
|
||||
|
||||
app.use(cors(), async (c, next) => {
|
||||
c.set("fetch", app.request.bind(app));
|
||||
@@ -24,4 +43,15 @@ export function setupMiddlewares(app: Hono) {
|
||||
c.set("isMobile", isMobile({ ua }));
|
||||
await next();
|
||||
});
|
||||
app.use(async (c, next) => {
|
||||
client
|
||||
.connect()
|
||||
.then(() => {
|
||||
c.set("redis", client);
|
||||
return next();
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error("Failed to connect to Redis", e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
79
src/server/routes/auth.ts
Normal file
79
src/server/routes/auth.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
import { Hono } from 'hono';
|
||||
import z from 'zod';
|
||||
import { getUserServiceClient } from '../services/grpcClient';
|
||||
// authGroup := r.Group("/auth")
|
||||
// {
|
||||
// authGroup.POST("/login", authHandler.Login)
|
||||
// authGroup.POST("/register", authHandler.Register)
|
||||
// authGroup.POST("/forgot-password", authHandler.ForgotPassword)
|
||||
// authGroup.POST("/reset-password", authHandler.ResetPassword)
|
||||
// authGroup.GET("/google/login", authHandler.LoginGoogle)
|
||||
// authGroup.GET("/google/callback", authHandler.GoogleCallback)
|
||||
// }
|
||||
const authRoute = new Hono();
|
||||
authRoute.post('/login', zValidator('json', z.object({ email: z.email(), password: z.string().min(6) })), async (c) => {
|
||||
const data = c.req.valid("json")
|
||||
const user = await getUserServiceClient().getUserByEmail(data);
|
||||
if (!user) {
|
||||
return c.json({ error: 'Invalid email or password' }, 401);
|
||||
}
|
||||
if (user.password !== data.password) {
|
||||
return c.json({ error: 'Invalid email or password' }, 401);
|
||||
}
|
||||
// const user = await getUserServiceClient().getUserByEmail({ email }, (err, response) => {
|
||||
// if (err) {
|
||||
// console.error("Error fetching user by email", err);
|
||||
// return null;
|
||||
// }
|
||||
// return response;
|
||||
// });
|
||||
// return c.json({ message: 'Login endpoint' });
|
||||
});
|
||||
authRoute.post('/register', zValidator('json', z.object({ email: z.email(), password: z.string().min(6) })), async (c) => {
|
||||
return c.json({ message: 'Register endpoint' });
|
||||
});
|
||||
authRoute.post('/forgot-password', zValidator('json', z.object({ email: z.email() })), async (c) => {
|
||||
return c.json({ message: 'Forgot Password endpoint' });
|
||||
});
|
||||
authRoute.post('/reset-password', zValidator('json', z.object({ token: z.string(), password: z.string().min(6) })), async (c) => {
|
||||
return c.json({ message: 'Reset Password endpoint' });
|
||||
});
|
||||
authRoute.get('/google/login', zValidator('query', z.object({ redirect_uri: z.string().url() })), async (c) => {
|
||||
return c.json({ message: 'Google Login endpoint' });
|
||||
});
|
||||
authRoute.get('/google/callback', zValidator('query', z.object({ code: z.string(), state: z.string() })), async (c) => {
|
||||
return c.json({ message: 'Google Callback endpoint' });
|
||||
});
|
||||
export function registerAuthRoutes(app: Hono) {
|
||||
|
||||
// app.post('/merge', async (c) => {
|
||||
// try {
|
||||
// const body = await c.req.json();
|
||||
// const { filename, chunks, size } = body;
|
||||
|
||||
// if (!filename || !Array.isArray(chunks) || chunks.length === 0) {
|
||||
// return c.json({ error: 'invalid payload' }, 400);
|
||||
// }
|
||||
|
||||
// const hostError = validateChunkUrls(chunks);
|
||||
// if (hostError) return c.json({ error: hostError }, 400);
|
||||
|
||||
// const manifest = createManifest(filename, chunks, size);
|
||||
// await saveManifest(manifest);
|
||||
|
||||
// return c.json({
|
||||
// status: 'ok',
|
||||
// id: manifest.id,
|
||||
// filename: manifest.filename,
|
||||
// total_parts: manifest.total_parts,
|
||||
// size: manifest.size,
|
||||
// playback_url: `/display/${manifest.id}`,
|
||||
// play_url: `/play/index/${manifest.id}`,
|
||||
// manifest_url: `/manifest/${manifest.id}`,
|
||||
// });
|
||||
// } catch (e: any) {
|
||||
// return c.json({ error: e?.message ?? String(e) }, 500);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
60
src/server/services/grpcClient.ts
Normal file
60
src/server/services/grpcClient.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { ChannelCredentials, credentials } from "@grpc/grpc-js";
|
||||
import { tryGetContext } from "hono/context-storage";
|
||||
import { Hono } from "node_modules/hono/dist/types/hono";
|
||||
import { PromisifiedClient, promisifyClient } from "../utils/grpcHelper";
|
||||
import { UserServiceClient } from "../utils/proto/v1/user";
|
||||
declare module "hono" {
|
||||
interface ContextVariableMap {
|
||||
userServiceClient: PromisifiedClient<UserServiceClient>;
|
||||
}
|
||||
}
|
||||
const DEFAULT_GRPC_ADDRESS = '127.0.0.1:9000';
|
||||
|
||||
const grpcAddress = () => process.env.STREAM_API_GRPC_ADDR || DEFAULT_GRPC_ADDRESS;
|
||||
let sharedCredentials: ChannelCredentials | undefined;
|
||||
const getCredentials = () => {
|
||||
if (!sharedCredentials) {
|
||||
sharedCredentials = credentials.createInsecure();
|
||||
}
|
||||
return sharedCredentials;
|
||||
};
|
||||
export const getUserServiceClient = () => {
|
||||
const context = tryGetContext();
|
||||
if (context) {
|
||||
return context.get("userServiceClient");
|
||||
}
|
||||
throw new Error("No context available to get UserServiceClient");
|
||||
};
|
||||
// (method) UserServiceClient.getUserByEmail(request: GetUserByEmailRequest, callback: (error: ServiceError | null, response: GetUserResponse) => void): ClientUnaryCall (+2 overloads)
|
||||
|
||||
// const unaryCall = <TResponse>(
|
||||
// executor: (
|
||||
// metadata: Metadata,
|
||||
// options: Partial<CallOptions>,
|
||||
// callback: (error: ServiceError | null, response: TResponse) => void,
|
||||
// ) => { metadata?: Metadata; trailer?: Metadata },
|
||||
// ): Promise<TResponse> => {
|
||||
// // const { metadata } = createMetadataFromContext();
|
||||
|
||||
// return new Promise<TResponse>((resolve, reject) => {
|
||||
// executor({
|
||||
// deadline: Date.now() + 10_000,
|
||||
// }, (error, response) => {
|
||||
// if (error) {
|
||||
// reject(normalizeGrpcError(error));
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // appendSetCookiesToResponse(call.metadata?.get('set-cookie') ?? []);
|
||||
// resolve(response);
|
||||
// });
|
||||
// });
|
||||
// };
|
||||
|
||||
|
||||
export const setupServices = (app: Hono) => {
|
||||
app.use("*", async (c, next) => {
|
||||
c.set("userServiceClient", promisifyClient(new UserServiceClient(grpcAddress(), getCredentials())));
|
||||
await next();
|
||||
});
|
||||
}
|
||||
109
src/server/utils/grpcHelper.ts
Normal file
109
src/server/utils/grpcHelper.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { ClientUnaryCall, ServiceError, status } from "@grpc/grpc-js";
|
||||
type UnaryCallback<TRes> = (
|
||||
error: ServiceError | null,
|
||||
response: TRes
|
||||
) => void;
|
||||
|
||||
type UnaryLike<TReq, TRes> = (
|
||||
req: TReq,
|
||||
callback: UnaryCallback<TRes>
|
||||
) => ClientUnaryCall;
|
||||
|
||||
type RequestOf<T> = T extends (
|
||||
req: infer TReq,
|
||||
callback: UnaryCallback<any>
|
||||
) => ClientUnaryCall
|
||||
? TReq
|
||||
: never;
|
||||
|
||||
type ResponseOf<T> = T extends (
|
||||
req: any,
|
||||
callback: UnaryCallback<infer TRes>
|
||||
) => ClientUnaryCall
|
||||
? TRes
|
||||
: never;
|
||||
|
||||
/**
|
||||
* Lấy ra overload đúng dạng (req, callback) => ClientUnaryCall
|
||||
*/
|
||||
type ExtractUnaryOverload<T> = Extract<T, UnaryLike<any, any>>;
|
||||
|
||||
export type PromisifiedClient<TClient> = {
|
||||
[K in keyof TClient as ExtractUnaryOverload<TClient[K]> extends never
|
||||
? never
|
||||
: K]: (
|
||||
req: RequestOf<ExtractUnaryOverload<TClient[K]>>
|
||||
) => Promise<ResponseOf<ExtractUnaryOverload<TClient[K]>>>;
|
||||
};
|
||||
|
||||
|
||||
const grpcCodeToHttpStatus = (code?: number) => {
|
||||
switch (code) {
|
||||
case status.INVALID_ARGUMENT:
|
||||
return 400;
|
||||
case status.UNAUTHENTICATED:
|
||||
return 401;
|
||||
case status.PERMISSION_DENIED:
|
||||
return 403;
|
||||
case status.NOT_FOUND:
|
||||
return 404;
|
||||
default:
|
||||
return 500;
|
||||
}
|
||||
};
|
||||
const normalizeGrpcError = (error: ServiceError) => {
|
||||
const normalized = new Error(error.details || error.message) as Error & {
|
||||
status?: number;
|
||||
code?: number;
|
||||
body?: { code?: number; message?: string; data?: unknown };
|
||||
};
|
||||
normalized.code = error.code;
|
||||
normalized.status = grpcCodeToHttpStatus(error.code);
|
||||
|
||||
const trailerBody = error.metadata?.get('x-error-body')?.[0];
|
||||
if (typeof trailerBody === 'string' && trailerBody) {
|
||||
try {
|
||||
normalized.body = JSON.parse(trailerBody) as { code?: number; message?: string; data?: unknown };
|
||||
if (normalized.body?.message) {
|
||||
normalized.message = normalized.body.message;
|
||||
}
|
||||
if (typeof normalized.body?.code === 'number') {
|
||||
normalized.status = normalized.body.code;
|
||||
}
|
||||
} catch {
|
||||
// ignore malformed structured error payloads
|
||||
}
|
||||
}
|
||||
|
||||
return normalized;
|
||||
};
|
||||
export function promisifyClient<TClient extends object>(
|
||||
client: TClient
|
||||
): PromisifiedClient<TClient> {
|
||||
const proto = Object.getPrototypeOf(client);
|
||||
const result: Record<string, unknown> = {};
|
||||
|
||||
for (const key of Object.getOwnPropertyNames(proto)) {
|
||||
if (key === "constructor") continue;
|
||||
|
||||
const value = (client as Record<string, unknown>)[key];
|
||||
if (typeof value !== "function") continue;
|
||||
|
||||
result[key] = (req: unknown) =>
|
||||
new Promise((resolve, reject) => {
|
||||
(value as Function).call(
|
||||
client,
|
||||
req,
|
||||
(error: ServiceError | null, response: unknown) => {
|
||||
if (error) {
|
||||
reject(normalizeGrpcError(error));
|
||||
return;
|
||||
}
|
||||
resolve(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return result as PromisifiedClient<TClient>;
|
||||
}
|
||||
38
src/server/utils/index.ts
Normal file
38
src/server/utils/index.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { RedisClient } from "bun";
|
||||
import { tryGetContext } from "hono/context-storage";
|
||||
import {
|
||||
setCookie
|
||||
} from 'hono/cookie';
|
||||
import { JWTProvider } from "./token";
|
||||
export const redisClient = (): RedisClient => {
|
||||
const context = tryGetContext<any>();
|
||||
const redis = context?.get("redis") as RedisClient | undefined;
|
||||
if (!redis) {
|
||||
throw new Error("Redis client not found in context");
|
||||
}
|
||||
return redis;
|
||||
};
|
||||
|
||||
export async function generateAndSetTokens(userID: string, email: string, role: string) {
|
||||
const redis = redisClient();
|
||||
const context = tryGetContext<any>();
|
||||
await JWTProvider("your-secret-key").generateTokenPair(userID, email, role).then((td) => {
|
||||
redis.set("refresh_uuid:" + td.refreshUUID, userID, "EX", td.rtExpires - Math.floor(Date.now() / 1000));
|
||||
if (context) {
|
||||
setCookie(context, "access_token", td.accessToken, {
|
||||
expires: new Date(td.atExpires * 1000),
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
path: "/",
|
||||
});
|
||||
setCookie(context, "refresh_token", td.refreshToken, {
|
||||
expires: new Date(td.rtExpires * 1000),
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
path: "/",
|
||||
});
|
||||
}
|
||||
}).catch((e) => {
|
||||
console.error("Error generating tokens", e);
|
||||
});
|
||||
}
|
||||
231
src/server/utils/proto/google/protobuf/timestamp.ts
Normal file
231
src/server/utils/proto/google/protobuf/timestamp.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-ts_proto v2.11.4
|
||||
// protoc unknown
|
||||
// source: google/protobuf/timestamp.proto
|
||||
|
||||
/* eslint-disable */
|
||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||
|
||||
export const protobufPackage = "google.protobuf";
|
||||
|
||||
/**
|
||||
* A Timestamp represents a point in time independent of any time zone or local
|
||||
* calendar, encoded as a count of seconds and fractions of seconds at
|
||||
* nanosecond resolution. The count is relative to an epoch at UTC midnight on
|
||||
* January 1, 1970, in the proleptic Gregorian calendar which extends the
|
||||
* Gregorian calendar backwards to year one.
|
||||
*
|
||||
* All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
|
||||
* second table is needed for interpretation, using a [24-hour linear
|
||||
* smear](https://developers.google.com/time/smear).
|
||||
*
|
||||
* The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
|
||||
* restricting to that range, we ensure that we can convert to and from [RFC
|
||||
* 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
|
||||
*
|
||||
* # Examples
|
||||
*
|
||||
* Example 1: Compute Timestamp from POSIX `time()`.
|
||||
*
|
||||
* Timestamp timestamp;
|
||||
* timestamp.set_seconds(time(NULL));
|
||||
* timestamp.set_nanos(0);
|
||||
*
|
||||
* Example 2: Compute Timestamp from POSIX `gettimeofday()`.
|
||||
*
|
||||
* struct timeval tv;
|
||||
* gettimeofday(&tv, NULL);
|
||||
*
|
||||
* Timestamp timestamp;
|
||||
* timestamp.set_seconds(tv.tv_sec);
|
||||
* timestamp.set_nanos(tv.tv_usec * 1000);
|
||||
*
|
||||
* Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
|
||||
*
|
||||
* FILETIME ft;
|
||||
* GetSystemTimeAsFileTime(&ft);
|
||||
* UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
|
||||
*
|
||||
* // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
|
||||
* // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
|
||||
* Timestamp timestamp;
|
||||
* timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
|
||||
* timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
|
||||
*
|
||||
* Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
|
||||
*
|
||||
* long millis = System.currentTimeMillis();
|
||||
*
|
||||
* Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
|
||||
* .setNanos((int) ((millis % 1000) * 1000000)).build();
|
||||
*
|
||||
* Example 5: Compute Timestamp from Java `Instant.now()`.
|
||||
*
|
||||
* Instant now = Instant.now();
|
||||
*
|
||||
* Timestamp timestamp =
|
||||
* Timestamp.newBuilder().setSeconds(now.getEpochSecond())
|
||||
* .setNanos(now.getNano()).build();
|
||||
*
|
||||
* Example 6: Compute Timestamp from current time in Python.
|
||||
*
|
||||
* timestamp = Timestamp()
|
||||
* timestamp.GetCurrentTime()
|
||||
*
|
||||
* # JSON Mapping
|
||||
*
|
||||
* In JSON format, the Timestamp type is encoded as a string in the
|
||||
* [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
|
||||
* format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
|
||||
* where {year} is always expressed using four digits while {month}, {day},
|
||||
* {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
|
||||
* seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
|
||||
* are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
|
||||
* is required. A proto3 JSON serializer should always use UTC (as indicated by
|
||||
* "Z") when printing the Timestamp type and a proto3 JSON parser should be
|
||||
* able to accept both UTC and other timezones (as indicated by an offset).
|
||||
*
|
||||
* For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
|
||||
* 01:30 UTC on January 15, 2017.
|
||||
*
|
||||
* In JavaScript, one can convert a Date object to this format using the
|
||||
* standard
|
||||
* [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
|
||||
* method. In Python, a standard `datetime.datetime` object can be converted
|
||||
* to this format using
|
||||
* [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
|
||||
* the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
|
||||
* the Joda Time's [`ISODateTimeFormat.dateTime()`](
|
||||
* http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()
|
||||
* ) to obtain a formatter capable of generating timestamps in this format.
|
||||
*/
|
||||
export interface Timestamp {
|
||||
/**
|
||||
* Represents seconds of UTC time since Unix epoch
|
||||
* 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
|
||||
* 9999-12-31T23:59:59Z inclusive.
|
||||
*/
|
||||
seconds?:
|
||||
| number
|
||||
| undefined;
|
||||
/**
|
||||
* Non-negative fractions of a second at nanosecond resolution. Negative
|
||||
* second values with fractions must still have non-negative nanos values
|
||||
* that count forward in time. Must be from 0 to 999,999,999
|
||||
* inclusive.
|
||||
*/
|
||||
nanos?: number | undefined;
|
||||
}
|
||||
|
||||
function createBaseTimestamp(): Timestamp {
|
||||
return { seconds: 0, nanos: 0 };
|
||||
}
|
||||
|
||||
export const Timestamp: MessageFns<Timestamp> = {
|
||||
encode(message: Timestamp, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.seconds !== undefined && message.seconds !== 0) {
|
||||
writer.uint32(8).int64(message.seconds);
|
||||
}
|
||||
if (message.nanos !== undefined && message.nanos !== 0) {
|
||||
writer.uint32(16).int32(message.nanos);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): Timestamp {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseTimestamp();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 8) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.seconds = longToNumber(reader.int64());
|
||||
continue;
|
||||
}
|
||||
case 2: {
|
||||
if (tag !== 16) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.nanos = reader.int32();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
}
|
||||
reader.skip(tag & 7);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
fromJSON(object: any): Timestamp {
|
||||
return {
|
||||
seconds: isSet(object.seconds) ? globalThis.Number(object.seconds) : 0,
|
||||
nanos: isSet(object.nanos) ? globalThis.Number(object.nanos) : 0,
|
||||
};
|
||||
},
|
||||
|
||||
toJSON(message: Timestamp): unknown {
|
||||
const obj: any = {};
|
||||
if (message.seconds !== undefined && message.seconds !== 0) {
|
||||
obj.seconds = Math.round(message.seconds);
|
||||
}
|
||||
if (message.nanos !== undefined && message.nanos !== 0) {
|
||||
obj.nanos = Math.round(message.nanos);
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<Timestamp>, I>>(base?: I): Timestamp {
|
||||
return Timestamp.fromPartial(base ?? ({} as any));
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<Timestamp>, I>>(object: I): Timestamp {
|
||||
const message = createBaseTimestamp();
|
||||
message.seconds = object.seconds ?? 0;
|
||||
message.nanos = object.nanos ?? 0;
|
||||
return message;
|
||||
},
|
||||
};
|
||||
|
||||
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
|
||||
|
||||
export type DeepPartial<T> = T extends Builtin ? T
|
||||
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
|
||||
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
|
||||
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||
: Partial<T>;
|
||||
|
||||
type KeysOfUnion<T> = T extends T ? keyof T : never;
|
||||
export type Exact<P, I extends P> = P extends Builtin ? P
|
||||
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never };
|
||||
|
||||
function longToNumber(int64: { toString(): string }): number {
|
||||
const num = globalThis.Number(int64.toString());
|
||||
if (num > globalThis.Number.MAX_SAFE_INTEGER) {
|
||||
throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER");
|
||||
}
|
||||
if (num < globalThis.Number.MIN_SAFE_INTEGER) {
|
||||
throw new globalThis.Error("Value is smaller than Number.MIN_SAFE_INTEGER");
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
function isSet(value: any): boolean {
|
||||
return value !== null && value !== undefined;
|
||||
}
|
||||
|
||||
export interface MessageFns<T> {
|
||||
encode(message: T, writer?: BinaryWriter): BinaryWriter;
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): T;
|
||||
fromJSON(object: any): T;
|
||||
toJSON(message: T): unknown;
|
||||
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
|
||||
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
|
||||
}
|
||||
719
src/server/utils/proto/v1/common.ts
Normal file
719
src/server/utils/proto/v1/common.ts
Normal file
@@ -0,0 +1,719 @@
|
||||
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-ts_proto v2.11.4
|
||||
// protoc unknown
|
||||
// source: v1/common.proto
|
||||
|
||||
/* eslint-disable */
|
||||
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
||||
import { Timestamp } from "../google/protobuf/timestamp";
|
||||
|
||||
export const protobufPackage = "stream.common.v1";
|
||||
|
||||
export interface RequestContext {
|
||||
userId?: string | undefined;
|
||||
email?: string | undefined;
|
||||
role?: string | undefined;
|
||||
requestId?: string | undefined;
|
||||
source?: string | undefined;
|
||||
}
|
||||
|
||||
export interface PaginationRequest {
|
||||
page?: number | undefined;
|
||||
pageSize?: number | undefined;
|
||||
}
|
||||
|
||||
export interface PaginationResponse {
|
||||
page?: number | undefined;
|
||||
pageSize?: number | undefined;
|
||||
total?: number | undefined;
|
||||
}
|
||||
|
||||
export interface Money {
|
||||
amount?: number | undefined;
|
||||
currency?: string | undefined;
|
||||
}
|
||||
|
||||
export interface Empty {
|
||||
}
|
||||
|
||||
export interface IdRequest {
|
||||
id?: string | undefined;
|
||||
}
|
||||
|
||||
export interface DeleteResponse {
|
||||
message?: string | undefined;
|
||||
}
|
||||
|
||||
export interface TimestampRange {
|
||||
from?: string | undefined;
|
||||
to?: string | undefined;
|
||||
}
|
||||
|
||||
function createBaseRequestContext(): RequestContext {
|
||||
return { userId: "", email: "", role: "", requestId: "", source: "" };
|
||||
}
|
||||
|
||||
export const RequestContext: MessageFns<RequestContext> = {
|
||||
encode(message: RequestContext, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.userId !== undefined && message.userId !== "") {
|
||||
writer.uint32(10).string(message.userId);
|
||||
}
|
||||
if (message.email !== undefined && message.email !== "") {
|
||||
writer.uint32(18).string(message.email);
|
||||
}
|
||||
if (message.role !== undefined && message.role !== "") {
|
||||
writer.uint32(26).string(message.role);
|
||||
}
|
||||
if (message.requestId !== undefined && message.requestId !== "") {
|
||||
writer.uint32(34).string(message.requestId);
|
||||
}
|
||||
if (message.source !== undefined && message.source !== "") {
|
||||
writer.uint32(42).string(message.source);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): RequestContext {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseRequestContext();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 10) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.userId = reader.string();
|
||||
continue;
|
||||
}
|
||||
case 2: {
|
||||
if (tag !== 18) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.email = reader.string();
|
||||
continue;
|
||||
}
|
||||
case 3: {
|
||||
if (tag !== 26) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.role = reader.string();
|
||||
continue;
|
||||
}
|
||||
case 4: {
|
||||
if (tag !== 34) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.requestId = reader.string();
|
||||
continue;
|
||||
}
|
||||
case 5: {
|
||||
if (tag !== 42) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.source = reader.string();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
}
|
||||
reader.skip(tag & 7);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
fromJSON(object: any): RequestContext {
|
||||
return {
|
||||
userId: isSet(object.userId)
|
||||
? globalThis.String(object.userId)
|
||||
: isSet(object.user_id)
|
||||
? globalThis.String(object.user_id)
|
||||
: "",
|
||||
email: isSet(object.email) ? globalThis.String(object.email) : "",
|
||||
role: isSet(object.role) ? globalThis.String(object.role) : "",
|
||||
requestId: isSet(object.requestId)
|
||||
? globalThis.String(object.requestId)
|
||||
: isSet(object.request_id)
|
||||
? globalThis.String(object.request_id)
|
||||
: "",
|
||||
source: isSet(object.source) ? globalThis.String(object.source) : "",
|
||||
};
|
||||
},
|
||||
|
||||
toJSON(message: RequestContext): unknown {
|
||||
const obj: any = {};
|
||||
if (message.userId !== undefined && message.userId !== "") {
|
||||
obj.userId = message.userId;
|
||||
}
|
||||
if (message.email !== undefined && message.email !== "") {
|
||||
obj.email = message.email;
|
||||
}
|
||||
if (message.role !== undefined && message.role !== "") {
|
||||
obj.role = message.role;
|
||||
}
|
||||
if (message.requestId !== undefined && message.requestId !== "") {
|
||||
obj.requestId = message.requestId;
|
||||
}
|
||||
if (message.source !== undefined && message.source !== "") {
|
||||
obj.source = message.source;
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<RequestContext>, I>>(base?: I): RequestContext {
|
||||
return RequestContext.fromPartial(base ?? ({} as any));
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<RequestContext>, I>>(object: I): RequestContext {
|
||||
const message = createBaseRequestContext();
|
||||
message.userId = object.userId ?? "";
|
||||
message.email = object.email ?? "";
|
||||
message.role = object.role ?? "";
|
||||
message.requestId = object.requestId ?? "";
|
||||
message.source = object.source ?? "";
|
||||
return message;
|
||||
},
|
||||
};
|
||||
|
||||
function createBasePaginationRequest(): PaginationRequest {
|
||||
return { page: 0, pageSize: 0 };
|
||||
}
|
||||
|
||||
export const PaginationRequest: MessageFns<PaginationRequest> = {
|
||||
encode(message: PaginationRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.page !== undefined && message.page !== 0) {
|
||||
writer.uint32(8).int32(message.page);
|
||||
}
|
||||
if (message.pageSize !== undefined && message.pageSize !== 0) {
|
||||
writer.uint32(16).int32(message.pageSize);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): PaginationRequest {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBasePaginationRequest();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 8) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.page = reader.int32();
|
||||
continue;
|
||||
}
|
||||
case 2: {
|
||||
if (tag !== 16) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.pageSize = reader.int32();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
}
|
||||
reader.skip(tag & 7);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
fromJSON(object: any): PaginationRequest {
|
||||
return {
|
||||
page: isSet(object.page) ? globalThis.Number(object.page) : 0,
|
||||
pageSize: isSet(object.pageSize)
|
||||
? globalThis.Number(object.pageSize)
|
||||
: isSet(object.page_size)
|
||||
? globalThis.Number(object.page_size)
|
||||
: 0,
|
||||
};
|
||||
},
|
||||
|
||||
toJSON(message: PaginationRequest): unknown {
|
||||
const obj: any = {};
|
||||
if (message.page !== undefined && message.page !== 0) {
|
||||
obj.page = Math.round(message.page);
|
||||
}
|
||||
if (message.pageSize !== undefined && message.pageSize !== 0) {
|
||||
obj.pageSize = Math.round(message.pageSize);
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<PaginationRequest>, I>>(base?: I): PaginationRequest {
|
||||
return PaginationRequest.fromPartial(base ?? ({} as any));
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<PaginationRequest>, I>>(object: I): PaginationRequest {
|
||||
const message = createBasePaginationRequest();
|
||||
message.page = object.page ?? 0;
|
||||
message.pageSize = object.pageSize ?? 0;
|
||||
return message;
|
||||
},
|
||||
};
|
||||
|
||||
function createBasePaginationResponse(): PaginationResponse {
|
||||
return { page: 0, pageSize: 0, total: 0 };
|
||||
}
|
||||
|
||||
export const PaginationResponse: MessageFns<PaginationResponse> = {
|
||||
encode(message: PaginationResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.page !== undefined && message.page !== 0) {
|
||||
writer.uint32(8).int32(message.page);
|
||||
}
|
||||
if (message.pageSize !== undefined && message.pageSize !== 0) {
|
||||
writer.uint32(16).int32(message.pageSize);
|
||||
}
|
||||
if (message.total !== undefined && message.total !== 0) {
|
||||
writer.uint32(24).int64(message.total);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): PaginationResponse {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBasePaginationResponse();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 8) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.page = reader.int32();
|
||||
continue;
|
||||
}
|
||||
case 2: {
|
||||
if (tag !== 16) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.pageSize = reader.int32();
|
||||
continue;
|
||||
}
|
||||
case 3: {
|
||||
if (tag !== 24) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.total = longToNumber(reader.int64());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
}
|
||||
reader.skip(tag & 7);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
fromJSON(object: any): PaginationResponse {
|
||||
return {
|
||||
page: isSet(object.page) ? globalThis.Number(object.page) : 0,
|
||||
pageSize: isSet(object.pageSize)
|
||||
? globalThis.Number(object.pageSize)
|
||||
: isSet(object.page_size)
|
||||
? globalThis.Number(object.page_size)
|
||||
: 0,
|
||||
total: isSet(object.total) ? globalThis.Number(object.total) : 0,
|
||||
};
|
||||
},
|
||||
|
||||
toJSON(message: PaginationResponse): unknown {
|
||||
const obj: any = {};
|
||||
if (message.page !== undefined && message.page !== 0) {
|
||||
obj.page = Math.round(message.page);
|
||||
}
|
||||
if (message.pageSize !== undefined && message.pageSize !== 0) {
|
||||
obj.pageSize = Math.round(message.pageSize);
|
||||
}
|
||||
if (message.total !== undefined && message.total !== 0) {
|
||||
obj.total = Math.round(message.total);
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<PaginationResponse>, I>>(base?: I): PaginationResponse {
|
||||
return PaginationResponse.fromPartial(base ?? ({} as any));
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<PaginationResponse>, I>>(object: I): PaginationResponse {
|
||||
const message = createBasePaginationResponse();
|
||||
message.page = object.page ?? 0;
|
||||
message.pageSize = object.pageSize ?? 0;
|
||||
message.total = object.total ?? 0;
|
||||
return message;
|
||||
},
|
||||
};
|
||||
|
||||
function createBaseMoney(): Money {
|
||||
return { amount: 0, currency: "" };
|
||||
}
|
||||
|
||||
export const Money: MessageFns<Money> = {
|
||||
encode(message: Money, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.amount !== undefined && message.amount !== 0) {
|
||||
writer.uint32(9).double(message.amount);
|
||||
}
|
||||
if (message.currency !== undefined && message.currency !== "") {
|
||||
writer.uint32(18).string(message.currency);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): Money {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseMoney();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 9) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.amount = reader.double();
|
||||
continue;
|
||||
}
|
||||
case 2: {
|
||||
if (tag !== 18) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.currency = reader.string();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
}
|
||||
reader.skip(tag & 7);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
fromJSON(object: any): Money {
|
||||
return {
|
||||
amount: isSet(object.amount) ? globalThis.Number(object.amount) : 0,
|
||||
currency: isSet(object.currency) ? globalThis.String(object.currency) : "",
|
||||
};
|
||||
},
|
||||
|
||||
toJSON(message: Money): unknown {
|
||||
const obj: any = {};
|
||||
if (message.amount !== undefined && message.amount !== 0) {
|
||||
obj.amount = message.amount;
|
||||
}
|
||||
if (message.currency !== undefined && message.currency !== "") {
|
||||
obj.currency = message.currency;
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<Money>, I>>(base?: I): Money {
|
||||
return Money.fromPartial(base ?? ({} as any));
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<Money>, I>>(object: I): Money {
|
||||
const message = createBaseMoney();
|
||||
message.amount = object.amount ?? 0;
|
||||
message.currency = object.currency ?? "";
|
||||
return message;
|
||||
},
|
||||
};
|
||||
|
||||
function createBaseEmpty(): Empty {
|
||||
return {};
|
||||
}
|
||||
|
||||
export const Empty: MessageFns<Empty> = {
|
||||
encode(_: Empty, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): Empty {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseEmpty();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
}
|
||||
reader.skip(tag & 7);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
fromJSON(_: any): Empty {
|
||||
return {};
|
||||
},
|
||||
|
||||
toJSON(_: Empty): unknown {
|
||||
const obj: any = {};
|
||||
return obj;
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<Empty>, I>>(base?: I): Empty {
|
||||
return Empty.fromPartial(base ?? ({} as any));
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<Empty>, I>>(_: I): Empty {
|
||||
const message = createBaseEmpty();
|
||||
return message;
|
||||
},
|
||||
};
|
||||
|
||||
function createBaseIdRequest(): IdRequest {
|
||||
return { id: "" };
|
||||
}
|
||||
|
||||
export const IdRequest: MessageFns<IdRequest> = {
|
||||
encode(message: IdRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.id !== undefined && message.id !== "") {
|
||||
writer.uint32(10).string(message.id);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): IdRequest {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseIdRequest();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 10) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.id = reader.string();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
}
|
||||
reader.skip(tag & 7);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
fromJSON(object: any): IdRequest {
|
||||
return { id: isSet(object.id) ? globalThis.String(object.id) : "" };
|
||||
},
|
||||
|
||||
toJSON(message: IdRequest): unknown {
|
||||
const obj: any = {};
|
||||
if (message.id !== undefined && message.id !== "") {
|
||||
obj.id = message.id;
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<IdRequest>, I>>(base?: I): IdRequest {
|
||||
return IdRequest.fromPartial(base ?? ({} as any));
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<IdRequest>, I>>(object: I): IdRequest {
|
||||
const message = createBaseIdRequest();
|
||||
message.id = object.id ?? "";
|
||||
return message;
|
||||
},
|
||||
};
|
||||
|
||||
function createBaseDeleteResponse(): DeleteResponse {
|
||||
return { message: "" };
|
||||
}
|
||||
|
||||
export const DeleteResponse: MessageFns<DeleteResponse> = {
|
||||
encode(message: DeleteResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.message !== undefined && message.message !== "") {
|
||||
writer.uint32(10).string(message.message);
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): DeleteResponse {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseDeleteResponse();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 10) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.message = reader.string();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
}
|
||||
reader.skip(tag & 7);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
fromJSON(object: any): DeleteResponse {
|
||||
return { message: isSet(object.message) ? globalThis.String(object.message) : "" };
|
||||
},
|
||||
|
||||
toJSON(message: DeleteResponse): unknown {
|
||||
const obj: any = {};
|
||||
if (message.message !== undefined && message.message !== "") {
|
||||
obj.message = message.message;
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<DeleteResponse>, I>>(base?: I): DeleteResponse {
|
||||
return DeleteResponse.fromPartial(base ?? ({} as any));
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<DeleteResponse>, I>>(object: I): DeleteResponse {
|
||||
const message = createBaseDeleteResponse();
|
||||
message.message = object.message ?? "";
|
||||
return message;
|
||||
},
|
||||
};
|
||||
|
||||
function createBaseTimestampRange(): TimestampRange {
|
||||
return { from: undefined, to: undefined };
|
||||
}
|
||||
|
||||
export const TimestampRange: MessageFns<TimestampRange> = {
|
||||
encode(message: TimestampRange, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||
if (message.from !== undefined) {
|
||||
Timestamp.encode(toTimestamp(message.from), writer.uint32(10).fork()).join();
|
||||
}
|
||||
if (message.to !== undefined) {
|
||||
Timestamp.encode(toTimestamp(message.to), writer.uint32(18).fork()).join();
|
||||
}
|
||||
return writer;
|
||||
},
|
||||
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): TimestampRange {
|
||||
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||
const end = length === undefined ? reader.len : reader.pos + length;
|
||||
const message = createBaseTimestampRange();
|
||||
while (reader.pos < end) {
|
||||
const tag = reader.uint32();
|
||||
switch (tag >>> 3) {
|
||||
case 1: {
|
||||
if (tag !== 10) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.from = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
|
||||
continue;
|
||||
}
|
||||
case 2: {
|
||||
if (tag !== 18) {
|
||||
break;
|
||||
}
|
||||
|
||||
message.to = fromTimestamp(Timestamp.decode(reader, reader.uint32()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((tag & 7) === 4 || tag === 0) {
|
||||
break;
|
||||
}
|
||||
reader.skip(tag & 7);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
fromJSON(object: any): TimestampRange {
|
||||
return {
|
||||
from: isSet(object.from) ? globalThis.String(object.from) : undefined,
|
||||
to: isSet(object.to) ? globalThis.String(object.to) : undefined,
|
||||
};
|
||||
},
|
||||
|
||||
toJSON(message: TimestampRange): unknown {
|
||||
const obj: any = {};
|
||||
if (message.from !== undefined) {
|
||||
obj.from = message.from;
|
||||
}
|
||||
if (message.to !== undefined) {
|
||||
obj.to = message.to;
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
create<I extends Exact<DeepPartial<TimestampRange>, I>>(base?: I): TimestampRange {
|
||||
return TimestampRange.fromPartial(base ?? ({} as any));
|
||||
},
|
||||
fromPartial<I extends Exact<DeepPartial<TimestampRange>, I>>(object: I): TimestampRange {
|
||||
const message = createBaseTimestampRange();
|
||||
message.from = object.from ?? undefined;
|
||||
message.to = object.to ?? undefined;
|
||||
return message;
|
||||
},
|
||||
};
|
||||
|
||||
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
|
||||
|
||||
export type DeepPartial<T> = T extends Builtin ? T
|
||||
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
|
||||
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
|
||||
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||
: Partial<T>;
|
||||
|
||||
type KeysOfUnion<T> = T extends T ? keyof T : never;
|
||||
export type Exact<P, I extends P> = P extends Builtin ? P
|
||||
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never };
|
||||
|
||||
function toTimestamp(dateStr: string): Timestamp {
|
||||
const date = new globalThis.Date(dateStr);
|
||||
const seconds = Math.trunc(date.getTime() / 1_000);
|
||||
const nanos = (date.getTime() % 1_000) * 1_000_000;
|
||||
return { seconds, nanos };
|
||||
}
|
||||
|
||||
function fromTimestamp(t: Timestamp): string {
|
||||
let millis = (t.seconds || 0) * 1_000;
|
||||
millis += (t.nanos || 0) / 1_000_000;
|
||||
return new globalThis.Date(millis).toISOString();
|
||||
}
|
||||
|
||||
function longToNumber(int64: { toString(): string }): number {
|
||||
const num = globalThis.Number(int64.toString());
|
||||
if (num > globalThis.Number.MAX_SAFE_INTEGER) {
|
||||
throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER");
|
||||
}
|
||||
if (num < globalThis.Number.MIN_SAFE_INTEGER) {
|
||||
throw new globalThis.Error("Value is smaller than Number.MIN_SAFE_INTEGER");
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
function isSet(value: any): boolean {
|
||||
return value !== null && value !== undefined;
|
||||
}
|
||||
|
||||
export interface MessageFns<T> {
|
||||
encode(message: T, writer?: BinaryWriter): BinaryWriter;
|
||||
decode(input: BinaryReader | Uint8Array, length?: number): T;
|
||||
fromJSON(object: any): T;
|
||||
toJSON(message: T): unknown;
|
||||
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
|
||||
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
|
||||
}
|
||||
2071
src/server/utils/proto/v1/user.ts
Normal file
2071
src/server/utils/proto/v1/user.ts
Normal file
File diff suppressed because it is too large
Load Diff
111
src/server/utils/token.ts
Normal file
111
src/server/utils/token.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { randomUUID } from "crypto"
|
||||
import { sign, verify } from "hono/jwt"
|
||||
import { JWTPayload } from "hono/utils/jwt/types"
|
||||
|
||||
export interface Provider {
|
||||
generateTokenPair(
|
||||
userID: string,
|
||||
email: string,
|
||||
role: string
|
||||
): Promise<TokenPair>
|
||||
|
||||
parseToken(token: string): Promise<JWTPayload>
|
||||
|
||||
parseMapToken(token: string): Promise<Record<string, any>>
|
||||
}
|
||||
|
||||
export interface TokenPair {
|
||||
accessToken: string
|
||||
refreshToken: string
|
||||
atExpires: number
|
||||
rtExpires: number
|
||||
accessUUID: string
|
||||
refreshUUID: string
|
||||
}
|
||||
|
||||
export interface Claims {
|
||||
userID: string
|
||||
email: string
|
||||
role: string
|
||||
tokenID: string
|
||||
}
|
||||
|
||||
interface JwtClaims {
|
||||
user_id: string
|
||||
email: string
|
||||
role: string
|
||||
token_id: string
|
||||
iss: string
|
||||
exp: number
|
||||
}
|
||||
|
||||
export class JwtProvider implements Provider {
|
||||
constructor(private secret: string) {}
|
||||
|
||||
static newJWTProvider(secret: string): Provider {
|
||||
return new JwtProvider(secret)
|
||||
}
|
||||
|
||||
async generateTokenPair(
|
||||
userID: string,
|
||||
email: string,
|
||||
role: string
|
||||
): Promise<TokenPair> {
|
||||
const now = Math.floor(Date.now() / 1000)
|
||||
|
||||
const td: TokenPair = {
|
||||
accessToken: "",
|
||||
refreshToken: "",
|
||||
atExpires: now + 15 * 60,
|
||||
rtExpires: now + 7 * 24 * 60 * 60,
|
||||
accessUUID: randomUUID(),
|
||||
refreshUUID: randomUUID(),
|
||||
}
|
||||
|
||||
// ACCESS TOKEN
|
||||
const accessPayload: JWTPayload = {
|
||||
user_id: userID,
|
||||
email,
|
||||
role,
|
||||
token_id: td.accessUUID,
|
||||
iss: "stream.api",
|
||||
exp: td.atExpires,
|
||||
}
|
||||
|
||||
td.accessToken = await sign(accessPayload, this.secret)
|
||||
|
||||
// REFRESH TOKEN
|
||||
const refreshPayload = {
|
||||
refresh_uuid: td.refreshUUID,
|
||||
user_id: userID,
|
||||
exp: td.rtExpires,
|
||||
}
|
||||
|
||||
td.refreshToken = await sign(refreshPayload, this.secret)
|
||||
|
||||
return td
|
||||
}
|
||||
|
||||
async parseToken(token: string): Promise<JWTPayload> {
|
||||
const payload = (await verify(token, this.secret, "HS256"))
|
||||
|
||||
if (!payload) {
|
||||
throw new Error("invalid token")
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
async parseMapToken(token: string): Promise<JWTPayload> {
|
||||
const payload = await verify(token, this.secret, "HS256")
|
||||
|
||||
if (!payload) {
|
||||
throw new Error("invalid token")
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
}
|
||||
|
||||
export function JWTProvider(secret: string): Provider {
|
||||
return new JwtProvider(secret)
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"$schema": "node_modules/wrangler/config-schema.json",
|
||||
"name": "holistream",
|
||||
"compatibility_date": "2025-08-03",
|
||||
"main": "./src/index.tsx",
|
||||
"compatibility_flags": [
|
||||
"nodejs_compat"
|
||||
],
|
||||
"observability": {
|
||||
"enabled": true,
|
||||
"head_sampling_rate": 1,
|
||||
"logs": {
|
||||
"enabled": true,
|
||||
"head_sampling_rate": 1,
|
||||
"persist": true,
|
||||
"invocation_logs": true
|
||||
},
|
||||
"traces": {
|
||||
"enabled": true,
|
||||
"persist": true,
|
||||
"head_sampling_rate": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user