zen: rate limit

This commit is contained in:
Frank
2025-11-08 10:15:35 -05:00
parent 9cd465f9c0
commit 30b1ae5d4b
29 changed files with 620 additions and 419 deletions

View File

@@ -11,7 +11,7 @@
"@tsconfig/bun": "catalog:",
"husky": "9.1.7",
"prettier": "3.6.2",
"sst": "3.17.22",
"sst": "3.17.23",
"turbo": "2.5.6",
},
},
@@ -106,7 +106,10 @@
"@cloudflare/workers-types": "catalog:",
},
"devDependencies": {
"@cloudflare/workers-types": "catalog:",
"@tsconfig/node22": "22.0.2",
"@types/node": "catalog:",
"cloudflare": "5.2.0",
},
},
"packages/desktop": {
@@ -1432,6 +1435,8 @@
"@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
"@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="],
"@types/promise.allsettled": ["@types/promise.allsettled@1.0.6", "", {}, "sha512-wA0UT0HeT2fGHzIFV9kWpYz5mdoyLxKrTgMdZQM++5h6pYAFH73HXcQhefg24nD1yivUFEn5KU+EF4b+CXJ4Wg=="],
"@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="],
@@ -1518,6 +1523,8 @@
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
"ai": ["ai@5.0.8", "", { "dependencies": { "@ai-sdk/gateway": "1.0.4", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.1", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-qbnhj046UvG30V1S5WhjBn+RBGEAmi8PSZWqMhRsE3EPxvO5BcePXTZFA23e9MYyWS9zr4Vm8Mv3wQXwLmtIBw=="],
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
@@ -1742,6 +1749,8 @@
"clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="],
"cloudflare": ["cloudflare@5.2.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-dVzqDpPFYR9ApEC9e+JJshFJZXcw4HzM8W+3DHzO5oy9+8rLC53G7x6fEf9A7/gSuSCxuvndzui5qJKftfIM9A=="],
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="],
@@ -2068,7 +2077,11 @@
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
"form-data": ["form-data@2.5.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" } }, "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A=="],
"form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="],
"form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="],
"formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
@@ -2258,6 +2271,8 @@
"human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
"humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
"husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="],
"i18next": ["i18next@23.16.8", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg=="],
@@ -2716,6 +2731,8 @@
"node-addon-api": ["node-addon-api@6.1.0", "", {}, "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="],
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
@@ -3178,23 +3195,23 @@
"sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="],
"sst": ["sst@3.17.22", "", { "dependencies": { "aws-sdk": "2.1692.0", "aws4fetch": "1.0.18", "jose": "5.2.3", "opencontrol": "0.0.6", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "3.17.22", "sst-darwin-x64": "3.17.22", "sst-linux-arm64": "3.17.22", "sst-linux-x64": "3.17.22", "sst-linux-x86": "3.17.22", "sst-win32-arm64": "3.17.22", "sst-win32-x64": "3.17.22", "sst-win32-x86": "3.17.22" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-C+XMTbm6fx+7eT+ESAMATqG7qV7+pyVfxYQb6osdH3jd4u91QW1VU/xlEru+RU1rs1ZE58ixXdRP75UGPn+gog=="],
"sst": ["sst@3.17.23", "", { "dependencies": { "aws-sdk": "2.1692.0", "aws4fetch": "1.0.18", "jose": "5.2.3", "opencontrol": "0.0.6", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "3.17.23", "sst-darwin-x64": "3.17.23", "sst-linux-arm64": "3.17.23", "sst-linux-x64": "3.17.23", "sst-linux-x86": "3.17.23", "sst-win32-arm64": "3.17.23", "sst-win32-x64": "3.17.23", "sst-win32-x86": "3.17.23" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-TwKgUgDnZdc1Swe+bvCNeyO4dQnYz5cTodMpYj3jlXZdK9/KNz0PVxT1f0u5E76i1pmilXrUBL/f7iiMPw4RDg=="],
"sst-darwin-arm64": ["sst-darwin-arm64@3.17.22", "", { "os": "darwin", "cpu": "arm64" }, "sha512-B2pKq1dWc60+7HfXQ6/9etskxxNv9axxlQKveCLQAuG2a3mmtv2/jcR0Ch3mvSTGtW+KfhzUXda2kj7nZ/phBA=="],
"sst-darwin-arm64": ["sst-darwin-arm64@3.17.23", "", { "os": "darwin", "cpu": "arm64" }, "sha512-R6kvmF+rUideOoU7KBs2SdvrIupoE+b+Dor/eq9Uo4Dojj7KvYDZI/EDm8sSCbbcx/opiWeyNqKtlnLEdCxE6g=="],
"sst-darwin-x64": ["sst-darwin-x64@3.17.22", "", { "os": "darwin", "cpu": "x64" }, "sha512-flikYqXvhwwrS6x2FDOde+MQODHaZCIbUkVHYO3/gYo99rbAMQ8VpC/3LXnmnPEQkLOwWCSzLp4S4F9nG/PW2g=="],
"sst-darwin-x64": ["sst-darwin-x64@3.17.23", "", { "os": "darwin", "cpu": "x64" }, "sha512-WW4P1S35iYCifQXxD+sE3wuzcN+LHLpuKMaNoaBqEcWGZnH3IPaDJ7rpLF0arkDAo/z3jZmWWzOCkr0JuqJ8vQ=="],
"sst-linux-arm64": ["sst-linux-arm64@3.17.22", "", { "os": "linux", "cpu": "arm64" }, "sha512-+pyD8Oej9js8XeCCebiEIde02vC5hc+bLl2/jR02K+9gYkGVJ6n5bkT8AlR8zWdS4FJKPyeJYUfjliT1T33j+g=="],
"sst-linux-arm64": ["sst-linux-arm64@3.17.23", "", { "os": "linux", "cpu": "arm64" }, "sha512-TjtNqgIh7RlAWgPLFCAt0mXvIB+J7WjmRvIRrAdX0mXsndOiBJ/DMOgXSLVsIWHCfPj8MIEot/hWpnJgXgIeag=="],
"sst-linux-x64": ["sst-linux-x64@3.17.22", "", { "os": "linux", "cpu": "x64" }, "sha512-A5p941edP9wgfgsbLUMeEPvi9JExj0OSaxgtFAC6/6BYoW4zruGAPzq206Ln6dNYP3gRdo5TJbSjio3F0ot8qg=="],
"sst-linux-x64": ["sst-linux-x64@3.17.23", "", { "os": "linux", "cpu": "x64" }, "sha512-qdqJiEbYfCjZlI3F/TA6eoIU7JXVkEEI/UMILNf2JWhky0KQdCW2Xyz+wb6c0msVJCWdUM/uj+1DaiP2eXvghw=="],
"sst-linux-x86": ["sst-linux-x86@3.17.22", "", { "os": "linux", "cpu": "none" }, "sha512-pFDIi+ZwH8GOvy5He9wsbAjRGf/sTGhGE/V480w0A6itb9BC4jQ9sblJkk3Jx/fP2g27pKN2RNz+ifOU+GrUYQ=="],
"sst-linux-x86": ["sst-linux-x86@3.17.23", "", { "os": "linux", "cpu": "none" }, "sha512-aGmUujIvoNlmAABEGsOgfY1rxD9koC6hN8bnTLbDI+oI/u/zjHYh50jsbL0p3TlaHpwF/lxP3xFSuT6IKp+KgA=="],
"sst-win32-arm64": ["sst-win32-arm64@3.17.22", "", { "os": "win32", "cpu": "arm64" }, "sha512-9KaIrk+Z6hLDNi9GShf9NLrZi9jC/NNGpUAn6HvTXr8c6HUyQzg6takMH8nrISGCPn92y+IYWqdglaqbgnJTog=="],
"sst-win32-arm64": ["sst-win32-arm64@3.17.23", "", { "os": "win32", "cpu": "arm64" }, "sha512-ZxdkGqYDrrZGz98rijDCN+m5yuCcwD6Bc9/6hubLsvdpNlVorUqzpg801Ec97xSK0nIC9g6pNiRyxAcsQQstUg=="],
"sst-win32-x64": ["sst-win32-x64@3.17.22", "", { "os": "win32", "cpu": "x64" }, "sha512-cvzyet4octGHK7w05jPUSPmUdlAWyh8IzjB8Pcs873K9AUGJEtQCftOKZjXaFdIG9DTvFWCCBi9zdzClxT9jJg=="],
"sst-win32-x64": ["sst-win32-x64@3.17.23", "", { "os": "win32", "cpu": "x64" }, "sha512-yc9cor4MS49Ccy2tQCF1tf6M81yLeSGzGL+gjhUxpVKo2pN3bxl3w70eyU/mTXSEeyAmG9zEfbt6FNu4sy5cUA=="],
"sst-win32-x86": ["sst-win32-x86@3.17.22", "", { "os": "win32", "cpu": "none" }, "sha512-ol5icDJuHzG+AjbGbCIQoF8z3oiikTF9CtccdK/udqEF861DnngWzM99IY5TJvmJlN+38yOV0MY4XI5hM6SEQA=="],
"sst-win32-x86": ["sst-win32-x86@3.17.23", "", { "os": "win32", "cpu": "none" }, "sha512-DIp3s54IpNAfdYjSRt6McvkbEPQDMxUu6RUeRAd2C+FcTJgTloon/ghAPQBaDgu2VoVgymjcJARO/XyfKcCLOQ=="],
"stackframe": ["stackframe@1.3.4", "", {}, "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="],
@@ -3480,6 +3497,8 @@
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
"web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="],
"web-tree-sitter": ["web-tree-sitter@0.25.10", "", { "peerDependencies": { "@types/emscripten": "^1.40.0" }, "optionalPeers": ["@types/emscripten"] }, "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA=="],
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
@@ -3754,6 +3773,8 @@
"@slack/web-api/eventemitter3": ["eventemitter3@3.1.2", "", {}, "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q=="],
"@slack/web-api/form-data": ["form-data@2.5.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" } }, "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A=="],
"@slack/web-api/p-queue": ["p-queue@6.6.2", "", { "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" } }, "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ=="],
"@solidjs/start/shiki": ["shiki@1.29.2", "", { "dependencies": { "@shikijs/core": "1.29.2", "@shikijs/engine-javascript": "1.29.2", "@shikijs/engine-oniguruma": "1.29.2", "@shikijs/langs": "1.29.2", "@shikijs/themes": "1.29.2", "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg=="],
@@ -3814,8 +3835,6 @@
"astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"axios/form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="],
"babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
"babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
@@ -4350,6 +4369,8 @@
"@pierre/precision-diffs/shiki/@shikijs/types": ["@shikijs/types@3.14.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ=="],
"@slack/web-api/form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"@slack/web-api/p-queue/eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
"@slack/web-api/p-queue/p-timeout": ["p-timeout@3.2.0", "", { "dependencies": { "p-finally": "^1.0.0" } }, "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg=="],
@@ -4402,8 +4423,6 @@
"astro/shiki/@shikijs/types": ["@shikijs/types@3.14.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ=="],
"axios/form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"babel-plugin-module-resolver/glob/minimatch": ["minimatch@8.0.4", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA=="],
"babel-plugin-module-resolver/glob/minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
@@ -4700,6 +4719,8 @@
"@modelcontextprotocol/sdk/express/type-is/media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
"@slack/web-api/form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@2.3.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="],
"@vercel/nft/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
@@ -4708,8 +4729,6 @@
"archiver-utils/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"axios/form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"babel-plugin-module-resolver/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"babel-plugin-module-resolver/glob/path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],

2
github/sst-env.d.ts vendored
View File

@@ -6,4 +6,4 @@
/// <reference path="../sst-env.d.ts" />
import "sst"
export {}
export {}

View File

@@ -106,6 +106,7 @@ const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
const STRIPE_WEBHOOK_SECRET = new sst.Linkable("STRIPE_WEBHOOK_SECRET", {
properties: { value: stripeWebhook.secret },
})
const gatewayKv = new sst.cloudflare.Kv("GatewayKv")
////////////////
// CONSOLE
@@ -136,6 +137,16 @@ new sst.cloudflare.x.SolidStart("Console", {
EMAILOCTOPUS_API_KEY,
AWS_SES_ACCESS_KEY_ID,
AWS_SES_SECRET_ACCESS_KEY,
...($dev
? [
new sst.Secret(
"CLOUDFLARE_DEFAULT_ACCOUNT_ID",
process.env.CLOUDFLARE_DEFAULT_ACCOUNT_ID!,
),
new sst.Secret("CLOUDFLARE_API_TOKEN", process.env.CLOUDFLARE_API_TOKEN!),
]
: []),
gatewayKv,
],
environment: {
//VITE_DOCS_URL: web.url.apply((url) => url!),

View File

@@ -52,7 +52,7 @@
"@tsconfig/bun": "catalog:",
"husky": "9.1.7",
"prettier": "3.6.2",
"sst": "3.17.22",
"sst": "3.17.23",
"turbo": "2.5.6"
},
"dependencies": {

View File

@@ -3,3 +3,4 @@ export class CreditsError extends Error {}
export class MonthlyLimitError extends Error {}
export class UserLimitError extends Error {}
export class ModelError extends Error {}
export class RateLimitError extends Error {}

View File

@@ -12,14 +12,14 @@ import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
import { ModelTable } from "@opencode-ai/console-core/schema/model.sql.js"
import { ProviderTable } from "@opencode-ai/console-core/schema/provider.sql.js"
import { logger } from "./logger"
import { AuthError, CreditsError, MonthlyLimitError, UserLimitError, ModelError } from "./error"
import { AuthError, CreditsError, MonthlyLimitError, UserLimitError, ModelError, RateLimitError } from "./error"
import { createBodyConverter, createStreamPartConverter, createResponseConverter } from "./provider/provider"
import { anthropicHelper } from "./provider/anthropic"
import { openaiHelper } from "./provider/openai"
import { oaCompatHelper } from "./provider/openai-compatible"
import { createRateLimiter } from "./rateLimiter"
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
type Model = ZenData["models"][string]
export async function handler(
input: APIEvent,
@@ -28,6 +28,10 @@ export async function handler(
parseApiKey: (headers: Headers) => string | undefined
},
) {
type AuthInfo = Awaited<ReturnType<typeof authenticate>>
type ModelInfo = Awaited<ReturnType<typeof validateModel>>
type ProviderInfo = Awaited<ReturnType<typeof selectProvider>>
const FREE_WORKSPACES = [
"wrk_01K46JDFR0E75SG2Q8K172KF3Y", // frank
"wrk_01K6W1A3VE0KMNVSCQT43BG2SX", // opencode bench
@@ -35,6 +39,7 @@ export async function handler(
try {
const body = await input.request.json()
const ip = input.request.headers.get("x-real-ip") ?? ""
logger.metric({
is_tream: !!body.stream,
session: input.request.headers.get("x-opencode-session"),
@@ -42,9 +47,11 @@ export async function handler(
})
const zenData = ZenData.list()
const modelInfo = validateModel(zenData, body.model)
const providerInfo = selectProvider(zenData, modelInfo, input.request.headers.get("x-real-ip") ?? "")
const providerInfo = selectProvider(zenData, modelInfo, ip)
const authInfo = await authenticate(modelInfo, providerInfo)
validateBilling(modelInfo, authInfo)
const rateLimiter = createRateLimiter(modelInfo.id, modelInfo.rateLimit, ip)
await rateLimiter?.check()
validateBilling(authInfo, modelInfo)
validateModelSettings(authInfo)
updateProviderKey(authInfo, providerInfo)
logger.metric({ provider: providerInfo.id })
@@ -59,7 +66,7 @@ export async function handler(
}),
)
logger.debug("REQUEST URL: " + reqUrl)
logger.debug("REQUEST: " + reqBody)
logger.debug("REQUEST: " + reqBody.substring(0, 300) + "...")
const res = await fetch(reqUrl, {
method: "POST",
headers: (() => {
@@ -84,9 +91,6 @@ export async function handler(
}
}
logger.debug("STATUS: " + res.status + " " + res.statusText)
if (res.status === 400 || res.status === 503) {
logger.debug("RESPONSE: " + (await res.text()))
}
// Handle non-streaming response
if (!body.stream) {
@@ -95,6 +99,7 @@ export async function handler(
const body = JSON.stringify(responseConverter(json))
logger.metric({ response_length: body.length })
logger.debug("RESPONSE: " + body)
await rateLimiter?.track()
await trackUsage(authInfo, modelInfo, providerInfo, json.usage)
await reload(authInfo)
return new Response(body, {
@@ -123,6 +128,7 @@ export async function handler(
response_length: responseLength,
"timestamp.last_byte": Date.now(),
})
await rateLimiter?.track()
const usage = usageParser.retrieve()
if (usage) {
await trackUsage(authInfo, modelInfo, providerInfo, usage)
@@ -197,6 +203,15 @@ export async function handler(
{ status: 401 },
)
if (error instanceof RateLimitError)
return new Response(
JSON.stringify({
type: "error",
error: { type: error.constructor.name, message: error.message },
}),
{ status: 429 },
)
return new Response(
JSON.stringify({
type: "error",
@@ -221,8 +236,8 @@ export async function handler(
return { id: modelId, ...modelData }
}
function selectProvider(zenData: ZenData, model: Awaited<ReturnType<typeof validateModel>>, ip: string) {
const providers = model.providers
function selectProvider(zenData: ZenData, modelInfo: ModelInfo, ip: string) {
const providers = modelInfo.providers
.filter((provider) => !provider.disabled)
.flatMap((provider) => Array<typeof provider>(provider.weight ?? 1).fill(provider))
@@ -235,22 +250,22 @@ export async function handler(
throw new ModelError(`Provider ${provider.id} not supported`)
}
const format = zenData.providers[provider.id].format
return {
...provider,
...zenData.providers[provider.id],
...(format === "anthropic" ? anthropicHelper : format === "openai" ? openaiHelper : oaCompatHelper),
...(() => {
const format = zenData.providers[provider.id].format
if (format === "anthropic") return anthropicHelper
if (format === "openai") return openaiHelper
return oaCompatHelper
})(),
}
}
async function authenticate(
model: Awaited<ReturnType<typeof validateModel>>,
providerInfo: Awaited<ReturnType<typeof selectProvider>>,
) {
async function authenticate(modelInfo: ModelInfo, providerInfo: ProviderInfo) {
const apiKey = opts.parseApiKey(input.request.headers)
if (!apiKey) {
if (model.allowAnonymous) return
if (modelInfo.allowAnonymous) return
throw new AuthError("Missing API key.")
}
@@ -282,7 +297,7 @@ export async function handler(
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID))
.innerJoin(BillingTable, eq(BillingTable.workspaceID, KeyTable.workspaceID))
.innerJoin(UserTable, and(eq(UserTable.workspaceID, KeyTable.workspaceID), eq(UserTable.id, KeyTable.userID)))
.leftJoin(ModelTable, and(eq(ModelTable.workspaceID, KeyTable.workspaceID), eq(ModelTable.model, model.id)))
.leftJoin(ModelTable, and(eq(ModelTable.workspaceID, KeyTable.workspaceID), eq(ModelTable.model, modelInfo.id)))
.leftJoin(
ProviderTable,
and(eq(ProviderTable.workspaceID, KeyTable.workspaceID), eq(ProviderTable.provider, providerInfo.id)),
@@ -308,11 +323,11 @@ export async function handler(
}
}
function validateBilling(model: Model, authInfo: Awaited<ReturnType<typeof authenticate>>) {
function validateBilling(authInfo: AuthInfo, modelInfo: ModelInfo) {
if (!authInfo) return
if (authInfo.provider?.credentials) return
if (authInfo.isFree) return
if (model.allowAnonymous) return
if (modelInfo.allowAnonymous) return
const billing = authInfo.billing
if (!billing.paymentMethodID)
@@ -356,26 +371,18 @@ export async function handler(
}
}
function validateModelSettings(authInfo: Awaited<ReturnType<typeof authenticate>>) {
function validateModelSettings(authInfo: AuthInfo) {
if (!authInfo) return
if (authInfo.isDisabled) throw new ModelError("Model is disabled")
}
function updateProviderKey(
authInfo: Awaited<ReturnType<typeof authenticate>>,
providerInfo: Awaited<ReturnType<typeof selectProvider>>,
) {
function updateProviderKey(authInfo: AuthInfo, providerInfo: ProviderInfo) {
if (!authInfo) return
if (!authInfo.provider?.credentials) return
providerInfo.apiKey = authInfo.provider.credentials
}
async function trackUsage(
authInfo: Awaited<ReturnType<typeof authenticate>>,
modelInfo: ReturnType<typeof validateModel>,
providerInfo: Awaited<ReturnType<typeof selectProvider>>,
usage: any,
) {
async function trackUsage(authInfo: AuthInfo, modelInfo: ModelInfo, providerInfo: ProviderInfo, usage: any) {
const { inputTokens, outputTokens, reasoningTokens, cacheReadTokens, cacheWrite5mTokens, cacheWrite1hTokens } =
providerInfo.normalizeUsage(usage)
@@ -483,7 +490,7 @@ export async function handler(
)
}
async function reload(authInfo: Awaited<ReturnType<typeof authenticate>>) {
async function reload(authInfo: AuthInfo) {
if (!authInfo) return
if (authInfo.isFree) return
if (authInfo.provider?.credentials) return

View File

@@ -0,0 +1,36 @@
import { Resource } from "@opencode-ai/console-resource"
import { RateLimitError } from "./error"
import { logger } from "./logger"
export function createRateLimiter(model: string, limit: number | undefined, ip: string) {
if (!limit) return
const now = Date.now()
const currKey = `usage:${ip}:${model}:${buildYYYYMMDDHH(now)}`
const prevKey = `usage:${ip}:${model}:${buildYYYYMMDDHH(now - 3_600_000)}`
let currRate: number
let prevRate: number
return {
track: async () => {
await Resource.GatewayKv.put(currKey, currRate + 1, { expirationTtl: 3600 })
},
check: async () => {
const values = await Resource.GatewayKv.get([currKey, prevKey])
const prevValue = values?.get(prevKey)
const currValue = values?.get(currKey)
prevRate = prevValue ? parseInt(prevValue) : 0
currRate = currValue ? parseInt(currValue) : 0
logger.debug(`rate limit ${model} prev/curr: ${prevRate}/${currRate}`)
if (prevRate + currRate >= limit)
throw new RateLimitError(`Rate limit exceeded. Please try again later.`)
},
}
}
function buildYYYYMMDDHH(timestamp: number) {
return new Date(timestamp)
.toISOString()
.replace(/[^0-9]/g, "")
.substring(0, 10)
}

View File

@@ -6,4 +6,4 @@
/// <reference path="../../../sst-env.d.ts" />
import "sst"
export {}
export {}

View File

@@ -7,7 +7,6 @@ import { ZenData } from "../src/model"
const root = path.resolve(process.cwd(), "..", "..", "..")
const models = await $`bun sst secret list`.cwd(root).text()
console.log("models", models)
// read the line starting with "ZEN_MODELS"
const oldValue1 = models

View File

@@ -24,6 +24,7 @@ export namespace ZenData {
cost: ModelCostSchema,
cost200K: ModelCostSchema.optional(),
allowAnonymous: z.boolean().optional(),
rateLimit: z.number().optional(),
providers: z.array(
z.object({
id: z.string(),

View File

@@ -6,99 +6,108 @@
import "sst"
declare module "sst" {
export interface Resource {
ADMIN_SECRET: {
type: "sst.sst.Secret"
value: string
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
AUTH_API_URL: {
type: "sst.sst.Linkable"
value: string
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
AWS_SES_ACCESS_KEY_ID: {
type: "sst.sst.Secret"
value: string
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
AWS_SES_SECRET_ACCESS_KEY: {
type: "sst.sst.Secret"
value: string
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
Console: {
type: "sst.cloudflare.SolidStart"
url: string
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
Database: {
database: string
host: string
password: string
port: number
type: "sst.sst.Linkable"
username: string
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
Desktop: {
type: "sst.cloudflare.StaticSite"
url: string
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
EMAILOCTOPUS_API_KEY: {
type: "sst.sst.Secret"
value: string
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
GITHUB_APP_ID: {
type: "sst.sst.Secret"
value: string
"Desktop": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
GITHUB_APP_PRIVATE_KEY: {
type: "sst.sst.Secret"
value: string
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
GITHUB_CLIENT_ID_CONSOLE: {
type: "sst.sst.Secret"
value: string
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
GITHUB_CLIENT_SECRET_CONSOLE: {
type: "sst.sst.Secret"
value: string
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
GOOGLE_CLIENT_ID: {
type: "sst.sst.Secret"
value: string
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
HONEYCOMB_API_KEY: {
type: "sst.sst.Secret"
value: string
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
Web: {
type: "sst.cloudflare.Astro"
url: string
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
ZEN_MODELS1: {
type: "sst.sst.Secret"
value: string
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
ZEN_MODELS2: {
type: "sst.sst.Secret"
value: string
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types"
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
Api: cloudflare.Service
AuthApi: cloudflare.Service
AuthStorage: cloudflare.KVNamespace
Bucket: cloudflare.R2Bucket
LogProcessor: cloudflare.Service
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"GatewayKv": cloudflare.KVNamespace
"LogProcessor": cloudflare.Service
}
}
import "sst"
export {}
export {}

View File

@@ -6,99 +6,108 @@
import "sst"
declare module "sst" {
export interface Resource {
ADMIN_SECRET: {
type: "sst.sst.Secret"
value: string
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
AUTH_API_URL: {
type: "sst.sst.Linkable"
value: string
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
AWS_SES_ACCESS_KEY_ID: {
type: "sst.sst.Secret"
value: string
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
AWS_SES_SECRET_ACCESS_KEY: {
type: "sst.sst.Secret"
value: string
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
Console: {
type: "sst.cloudflare.SolidStart"
url: string
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
Database: {
database: string
host: string
password: string
port: number
type: "sst.sst.Linkable"
username: string
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
Desktop: {
type: "sst.cloudflare.StaticSite"
url: string
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
EMAILOCTOPUS_API_KEY: {
type: "sst.sst.Secret"
value: string
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
GITHUB_APP_ID: {
type: "sst.sst.Secret"
value: string
"Desktop": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
GITHUB_APP_PRIVATE_KEY: {
type: "sst.sst.Secret"
value: string
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
GITHUB_CLIENT_ID_CONSOLE: {
type: "sst.sst.Secret"
value: string
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
GITHUB_CLIENT_SECRET_CONSOLE: {
type: "sst.sst.Secret"
value: string
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
GOOGLE_CLIENT_ID: {
type: "sst.sst.Secret"
value: string
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
HONEYCOMB_API_KEY: {
type: "sst.sst.Secret"
value: string
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
Web: {
type: "sst.cloudflare.Astro"
url: string
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
ZEN_MODELS1: {
type: "sst.sst.Secret"
value: string
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
ZEN_MODELS2: {
type: "sst.sst.Secret"
value: string
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types"
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
Api: cloudflare.Service
AuthApi: cloudflare.Service
AuthStorage: cloudflare.KVNamespace
Bucket: cloudflare.R2Bucket
LogProcessor: cloudflare.Service
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"GatewayKv": cloudflare.KVNamespace
"LogProcessor": cloudflare.Service
}
}
import "sst"
export {}
export {}

View File

@@ -6,4 +6,4 @@
/// <reference path="../../../sst-env.d.ts" />
import "sst"
export {}
export {}

View File

@@ -13,6 +13,9 @@
}
},
"devDependencies": {
"@tsconfig/node22": "22.0.2"
"@cloudflare/workers-types": "catalog:",
"@tsconfig/node22": "22.0.2",
"@types/node": "catalog:",
"cloudflare": "5.2.0"
}
}

View File

@@ -1 +1,64 @@
export { Resource } from "sst"
import type {
KVNamespaceListOptions,
KVNamespaceListResult,
KVNamespacePutOptions,
} from "@cloudflare/workers-types"
import { Resource as ResourceBase } from "sst"
import Cloudflare from "cloudflare"
export const Resource = new Proxy(
{},
{
get(_target, prop: keyof typeof ResourceBase) {
const value = ResourceBase[prop]
// @ts-ignore
if ("type" in value && value.type === "sst.cloudflare.Kv") {
const client = new Cloudflare({
apiToken: ResourceBase.CLOUDFLARE_API_TOKEN.value,
})
// @ts-ignore
const namespaceId = value.namespaceId
const accountId = ResourceBase.CLOUDFLARE_DEFAULT_ACCOUNT_ID.value
return {
get: (k: string | string[]) => {
const isMulti = Array.isArray(k)
return client.kv.namespaces
.bulkGet(namespaceId, {
keys: Array.isArray(k) ? k : [k],
account_id: accountId,
})
.then((result) =>
isMulti ? new Map(Object.entries(result?.values ?? {})) : result?.values?.[k],
)
},
put: (k: string, v: string, opts?: KVNamespacePutOptions) =>
client.kv.namespaces.values.update(namespaceId, k, {
account_id: accountId,
value: v,
expiration: opts?.expiration,
expiration_ttl: opts?.expirationTtl,
metadata: opts?.metadata,
}),
delete: (k: string) =>
client.kv.namespaces.values.delete(namespaceId, k, {
account_id: accountId,
}),
list: (opts?: KVNamespaceListOptions): Promise<KVNamespaceListResult<unknown, string>> =>
client.kv.namespaces.keys
.list(namespaceId, {
account_id: accountId,
prefix: opts?.prefix ?? undefined,
})
.then((result) => {
return {
keys: result.result,
list_complete: true,
cacheStatus: null,
}
}),
}
}
return value
},
},
) as Record<string, any>

View File

@@ -6,99 +6,108 @@
import "sst"
declare module "sst" {
export interface Resource {
ADMIN_SECRET: {
type: "sst.sst.Secret"
value: string
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
AUTH_API_URL: {
type: "sst.sst.Linkable"
value: string
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
AWS_SES_ACCESS_KEY_ID: {
type: "sst.sst.Secret"
value: string
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
AWS_SES_SECRET_ACCESS_KEY: {
type: "sst.sst.Secret"
value: string
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
Console: {
type: "sst.cloudflare.SolidStart"
url: string
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
Database: {
database: string
host: string
password: string
port: number
type: "sst.sst.Linkable"
username: string
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
Desktop: {
type: "sst.cloudflare.StaticSite"
url: string
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
EMAILOCTOPUS_API_KEY: {
type: "sst.sst.Secret"
value: string
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
GITHUB_APP_ID: {
type: "sst.sst.Secret"
value: string
"Desktop": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
GITHUB_APP_PRIVATE_KEY: {
type: "sst.sst.Secret"
value: string
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
GITHUB_CLIENT_ID_CONSOLE: {
type: "sst.sst.Secret"
value: string
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
GITHUB_CLIENT_SECRET_CONSOLE: {
type: "sst.sst.Secret"
value: string
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
GOOGLE_CLIENT_ID: {
type: "sst.sst.Secret"
value: string
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
HONEYCOMB_API_KEY: {
type: "sst.sst.Secret"
value: string
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
Web: {
type: "sst.cloudflare.Astro"
url: string
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
ZEN_MODELS1: {
type: "sst.sst.Secret"
value: string
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
ZEN_MODELS2: {
type: "sst.sst.Secret"
value: string
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types"
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
Api: cloudflare.Service
AuthApi: cloudflare.Service
AuthStorage: cloudflare.KVNamespace
Bucket: cloudflare.R2Bucket
LogProcessor: cloudflare.Service
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"GatewayKv": cloudflare.KVNamespace
"LogProcessor": cloudflare.Service
}
}
import "sst"
export {}
export {}

View File

@@ -2,7 +2,9 @@
/* tslint:disable */
/* eslint-disable */
/// <reference types="vite/client" />
interface ImportMetaEnv {}
interface ImportMetaEnv {
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
}

View File

@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
export {}

View File

@@ -6,99 +6,108 @@
import "sst"
declare module "sst" {
export interface Resource {
ADMIN_SECRET: {
type: "sst.sst.Secret"
value: string
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
AUTH_API_URL: {
type: "sst.sst.Linkable"
value: string
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
AWS_SES_ACCESS_KEY_ID: {
type: "sst.sst.Secret"
value: string
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
AWS_SES_SECRET_ACCESS_KEY: {
type: "sst.sst.Secret"
value: string
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
Console: {
type: "sst.cloudflare.SolidStart"
url: string
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
Database: {
database: string
host: string
password: string
port: number
type: "sst.sst.Linkable"
username: string
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
Desktop: {
type: "sst.cloudflare.StaticSite"
url: string
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
EMAILOCTOPUS_API_KEY: {
type: "sst.sst.Secret"
value: string
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
GITHUB_APP_ID: {
type: "sst.sst.Secret"
value: string
"Desktop": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
GITHUB_APP_PRIVATE_KEY: {
type: "sst.sst.Secret"
value: string
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
GITHUB_CLIENT_ID_CONSOLE: {
type: "sst.sst.Secret"
value: string
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
GITHUB_CLIENT_SECRET_CONSOLE: {
type: "sst.sst.Secret"
value: string
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
GOOGLE_CLIENT_ID: {
type: "sst.sst.Secret"
value: string
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
HONEYCOMB_API_KEY: {
type: "sst.sst.Secret"
value: string
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
Web: {
type: "sst.cloudflare.Astro"
url: string
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
ZEN_MODELS1: {
type: "sst.sst.Secret"
value: string
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
ZEN_MODELS2: {
type: "sst.sst.Secret"
value: string
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types"
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
Api: cloudflare.Service
AuthApi: cloudflare.Service
AuthStorage: cloudflare.KVNamespace
Bucket: cloudflare.R2Bucket
LogProcessor: cloudflare.Service
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"GatewayKv": cloudflare.KVNamespace
"LogProcessor": cloudflare.Service
}
}
import "sst"
export {}
export {}

View File

@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
export {}

View File

@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
export {}

View File

@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
export {}

View File

@@ -6,4 +6,4 @@
/// <reference path="../../../sst-env.d.ts" />
import "sst"
export {}
export {}

View File

@@ -25,10 +25,17 @@ class Resource:
type: str
url: str
class AuthStorage:
namespaceId: str
type: str
class Bucket:
name: str
type: str
class CLOUDFLARE_API_TOKEN:
type: str
value: str
class CLOUDFLARE_DEFAULT_ACCOUNT_ID:
type: str
value: str
class Console:
type: str
url: str
@@ -60,6 +67,9 @@ class Resource:
class GOOGLE_CLIENT_ID:
type: str
value: str
class GatewayKv:
namespaceId: str
type: str
class HONEYCOMB_API_KEY:
type: str
value: str

View File

@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
export {}

View File

@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
export {}

View File

@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
export {}

View File

@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
export {}

163
sst-env.d.ts vendored
View File

@@ -5,107 +5,120 @@
declare module "sst" {
export interface Resource {
ADMIN_SECRET: {
type: "sst.sst.Secret"
value: string
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
AUTH_API_URL: {
type: "sst.sst.Linkable"
value: string
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
AWS_SES_ACCESS_KEY_ID: {
type: "sst.sst.Secret"
value: string
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
AWS_SES_SECRET_ACCESS_KEY: {
type: "sst.sst.Secret"
value: string
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
Api: {
type: "sst.cloudflare.Worker"
url: string
"Api": {
"type": "sst.cloudflare.Worker"
"url": string
}
AuthApi: {
type: "sst.cloudflare.Worker"
url: string
"AuthApi": {
"type": "sst.cloudflare.Worker"
"url": string
}
AuthStorage: {
type: "sst.cloudflare.Kv"
"AuthStorage": {
"namespaceId": string
"type": "sst.cloudflare.Kv"
}
Bucket: {
name: string
type: "sst.cloudflare.Bucket"
"Bucket": {
"name": string
"type": "sst.cloudflare.Bucket"
}
Console: {
type: "sst.cloudflare.SolidStart"
url: string
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
Database: {
database: string
host: string
password: string
port: number
type: "sst.sst.Linkable"
username: string
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
Desktop: {
type: "sst.cloudflare.StaticSite"
url: string
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
EMAILOCTOPUS_API_KEY: {
type: "sst.sst.Secret"
value: string
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
GITHUB_APP_ID: {
type: "sst.sst.Secret"
value: string
"Desktop": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
GITHUB_APP_PRIVATE_KEY: {
type: "sst.sst.Secret"
value: string
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
GITHUB_CLIENT_ID_CONSOLE: {
type: "sst.sst.Secret"
value: string
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
GITHUB_CLIENT_SECRET_CONSOLE: {
type: "sst.sst.Secret"
value: string
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
GOOGLE_CLIENT_ID: {
type: "sst.sst.Secret"
value: string
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
HONEYCOMB_API_KEY: {
type: "sst.sst.Secret"
value: string
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
LogProcessor: {
type: "sst.cloudflare.Worker"
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
"GatewayKv": {
"namespaceId": string
"type": "sst.cloudflare.Kv"
}
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
Web: {
type: "sst.cloudflare.Astro"
url: string
"LogProcessor": {
"type": "sst.cloudflare.Worker"
}
ZEN_MODELS1: {
type: "sst.sst.Secret"
value: string
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
ZEN_MODELS2: {
type: "sst.sst.Secret"
value: string
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
}
}
/// <reference path="sst-env.d.ts" />
import "sst"
export {}
export {}